Merge "Migrate legacy scenario job to tempest in-tree"
diff --git a/doc/source/microversion_testing.rst b/doc/source/microversion_testing.rst
index c22888d..3bc1d0c 100644
--- a/doc/source/microversion_testing.rst
+++ b/doc/source/microversion_testing.rst
@@ -354,6 +354,14 @@
 
   .. _2.48: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id43
 
+  * `2.54`_
+
+  .. _2.54: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id4
+
+  * `2.55`_
+
+  .. _2.55: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id49
+
   * `2.60`_
 
   .. _2.60: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id54
diff --git a/releasenotes/notes/add-update-flavor--api-to-flavors-client-a859542fe54aab7c.yaml b/releasenotes/notes/add-update-flavor--api-to-flavors-client-a859542fe54aab7c.yaml
new file mode 100644
index 0000000..222a99f
--- /dev/null
+++ b/releasenotes/notes/add-update-flavor--api-to-flavors-client-a859542fe54aab7c.yaml
@@ -0,0 +1,4 @@
+---
+features:
+  - |
+    Add update flavor API to compute flavors_client library.
diff --git a/releasenotes/notes/volume-service-testing-default-to-v3-endpoints-20b86895a590925d.yaml b/releasenotes/notes/volume-service-testing-default-to-v3-endpoints-20b86895a590925d.yaml
new file mode 100644
index 0000000..ea69293
--- /dev/null
+++ b/releasenotes/notes/volume-service-testing-default-to-v3-endpoints-20b86895a590925d.yaml
@@ -0,0 +1,8 @@
+---
+upgrade:
+  - |
+    The volume config option ``catalog_type`` default is changed to
+    ``volumev3`` which is v3 API endpoint configured in devstack.
+    With this change Tempest will be testing v3 API as default.
+    User who want to test v2 API can still test by configuring the
+    ``catalog_type`` to v2 endpoint.
diff --git a/releasenotes/notes/volume-v3-service-clients-a863a6336af56cca.yaml b/releasenotes/notes/volume-v3-service-clients-a863a6336af56cca.yaml
new file mode 100644
index 0000000..b572a34
--- /dev/null
+++ b/releasenotes/notes/volume-v3-service-clients-a863a6336af56cca.yaml
@@ -0,0 +1,12 @@
+---
+features:
+  - |
+    Adds volume service clients for v3 APIs. As v3 base API should be
+    identical to v2 APIs, we just copy all existing v2 service client
+    for v3 API.
+deprecations:
+  - |
+    Deprecates the volume service clients for v2 APIs. Volume v2 APIs
+    are deprecated in all supported stable branches, so it's time
+    to deprecate the tempest service clients for v2 APIs and remove in future
+    release.
diff --git a/tempest/README.rst b/tempest/README.rst
index 62821de..a5f4a92 100644
--- a/tempest/README.rst
+++ b/tempest/README.rst
@@ -12,12 +12,12 @@
 and guidelines. Below is the overview of the Tempest respository structure
 to make this clear.
 
- .. code-block:: console
+.. code-block:: console
 
-    tempest/
-       api/ - API tests
-       scenario/ - complex scenario tests
-       tests/ - unit tests for Tempest internals
+   tempest/
+      api/ - API tests
+      scenario/ - complex scenario tests
+      tests/ - unit tests for Tempest internals
 
 Each of these directories contains different types of tests. What
 belongs in each directory, the rules and examples for good tests, are
diff --git a/tempest/api/compute/admin/test_flavors_microversions.py b/tempest/api/compute/admin/test_flavors_microversions.py
new file mode 100644
index 0000000..027af25
--- /dev/null
+++ b/tempest/api/compute/admin/test_flavors_microversions.py
@@ -0,0 +1,43 @@
+# Copyright 2018 NEC Corporation.
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+from tempest.api.compute import base
+from tempest.lib.common.utils import data_utils
+from tempest.lib import decorators
+
+
+class FlavorsV255TestJSON(base.BaseV2ComputeAdminTest):
+    min_microversion = '2.55'
+    max_microversion = 'latest'
+
+    # NOTE(gmann): This class tests the flavors APIs
+    # response schema for the 2.55 microversion.
+
+    @decorators.idempotent_id('61976b25-488d-41dc-9dcb-cb9693a7b075')
+    def test_crud_flavor(self):
+        flavor_id = data_utils.rand_int_id(start=1000)
+        # Checking create API response schema
+        new_flavor_id = self.create_flavor(ram=512,
+                                           vcpus=1,
+                                           disk=10,
+                                           id=flavor_id)['id']
+        # Checking show API response schema
+        self.flavors_client.show_flavor(new_flavor_id)['flavor']
+        # Checking update API response schema
+        self.admin_flavors_client.update_flavor(new_flavor_id,
+                                                description='new')['flavor']
+        # Checking list details API response schema
+        self.flavors_client.list_flavors(detail=True)['flavors']
+        # Checking list API response schema
+        self.flavors_client.list_flavors()['flavors']
diff --git a/tempest/api/compute/admin/test_quotas_negative.py b/tempest/api/compute/admin/test_quotas_negative.py
index 5ef7ee4..f90ff92 100644
--- a/tempest/api/compute/admin/test_quotas_negative.py
+++ b/tempest/api/compute/admin/test_quotas_negative.py
@@ -22,12 +22,12 @@
 CONF = config.CONF
 
 
-class QuotasAdminNegativeTestJSON(base.BaseV2ComputeAdminTest):
+class QuotasAdminNegativeTestBase(base.BaseV2ComputeAdminTest):
     force_tenant_isolation = True
 
     @classmethod
     def setup_clients(cls):
-        super(QuotasAdminNegativeTestJSON, cls).setup_clients()
+        super(QuotasAdminNegativeTestBase, cls).setup_clients()
         cls.client = cls.os_primary.quotas_client
         cls.adm_client = cls.os_admin.quotas_client
         cls.sg_client = cls.security_groups_client
@@ -35,7 +35,7 @@
 
     @classmethod
     def resource_setup(cls):
-        super(QuotasAdminNegativeTestJSON, cls).resource_setup()
+        super(QuotasAdminNegativeTestBase, cls).resource_setup()
         # NOTE(afazekas): these test cases should always create and use a new
         # tenant most of them should be skipped if we can't do that
         cls.demo_tenant_id = cls.client.tenant_id
@@ -51,6 +51,9 @@
         self.addCleanup(self.adm_client.update_quota_set, self.demo_tenant_id,
                         **{quota_item: default_quota_value})
 
+
+class QuotasAdminNegativeTest(QuotasAdminNegativeTestBase):
+
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('733abfe8-166e-47bb-8363-23dbd7ff3476')
     def test_update_quota_normal_user(self):
@@ -85,6 +88,10 @@
         self.assertRaises((lib_exc.Forbidden, lib_exc.OverLimit),
                           self.create_test_server)
 
+
+class QuotasSecurityGroupAdminNegativeTest(QuotasAdminNegativeTestBase):
+    max_microversion = '2.35'
+
     @decorators.skip_because(bug="1186354",
                              condition=CONF.service_available.neutron)
     @decorators.attr(type=['negative'])
diff --git a/tempest/api/compute/admin/test_security_group_default_rules.py b/tempest/api/compute/admin/test_security_group_default_rules.py
index f2f3b57..bca6a22 100644
--- a/tempest/api/compute/admin/test_security_group_default_rules.py
+++ b/tempest/api/compute/admin/test_security_group_default_rules.py
@@ -23,6 +23,7 @@
 
 
 class SecurityGroupDefaultRulesTest(base.BaseV2ComputeAdminTest):
+    max_microversion = '2.35'
 
     @classmethod
     # TODO(GMann): Once Bug# 1311500 is fixed, these test can run
diff --git a/tempest/api/compute/admin/test_security_groups.py b/tempest/api/compute/admin/test_security_groups.py
index ff9caa3..f0178aa 100644
--- a/tempest/api/compute/admin/test_security_groups.py
+++ b/tempest/api/compute/admin/test_security_groups.py
@@ -20,6 +20,7 @@
 
 
 class SecurityGroupsTestAdminJSON(base.BaseV2ComputeAdminTest):
+    max_microversion = '2.35'
 
     @classmethod
     def setup_clients(cls):
diff --git a/tempest/api/compute/admin/test_services.py b/tempest/api/compute/admin/test_services.py
index f3eb597..73e191b 100644
--- a/tempest/api/compute/admin/test_services.py
+++ b/tempest/api/compute/admin/test_services.py
@@ -56,15 +56,3 @@
         # sort the lists before comparing, to take out dependency
         # on order.
         self.assertEqual(sorted(s1), sorted(s2))
-
-    @decorators.idempotent_id('39397f6f-37b8-4234-8671-281e44c74025')
-    def test_get_service_by_service_and_host_name(self):
-        services = self.client.list_services()['services']
-        host_name = services[0]['host']
-        binary_name = services[0]['binary']
-
-        services = self.client.list_services(host=host_name,
-                                             binary=binary_name)['services']
-        self.assertEqual(1, len(services))
-        self.assertEqual(host_name, services[0]['host'])
-        self.assertEqual(binary_name, services[0]['binary'])
diff --git a/tempest/api/compute/security_groups/base.py b/tempest/api/compute/security_groups/base.py
index 54a6da8..49125d1 100644
--- a/tempest/api/compute/security_groups/base.py
+++ b/tempest/api/compute/security_groups/base.py
@@ -22,6 +22,7 @@
 
 
 class BaseSecurityGroupsTest(base.BaseV2ComputeTest):
+    max_microversion = '2.35'
 
     @classmethod
     def skip_checks(cls):
diff --git a/tempest/api/compute/servers/test_device_tagging.py b/tempest/api/compute/servers/test_device_tagging.py
index b0d527c..1213a04 100644
--- a/tempest/api/compute/servers/test_device_tagging.py
+++ b/tempest/api/compute/servers/test_device_tagging.py
@@ -134,7 +134,6 @@
         self.addCleanup(self.ports_client.delete_port, self.port2['id'])
 
         # Create server
-        admin_pass = data_utils.rand_password()
         config_drive_enabled = CONF.compute_feature_enabled.config_drive
         validation_resources = self.get_test_validation_resources(
             self.os_primary)
@@ -144,7 +143,6 @@
             wait_until='ACTIVE',
             validation_resources=validation_resources,
             config_drive=config_drive_enabled,
-            adminPass=admin_pass,
             name=data_utils.rand_name('device-tagging-server'),
             networks=[
                 # Validation network for ssh
@@ -212,8 +210,7 @@
         self.ssh_client = remote_client.RemoteClient(
             self.get_server_ip(server, validation_resources),
             CONF.validation.image_ssh_user,
-            admin_pass,
-            validation_resources['keypair']['private_key'],
+            pkey=validation_resources['keypair']['private_key'],
             server=server,
             servers_client=self.servers_client)
 
diff --git a/tempest/api/compute/servers/test_servers_microversions.py b/tempest/api/compute/servers/test_servers_microversions.py
new file mode 100644
index 0000000..f3863f1
--- /dev/null
+++ b/tempest/api/compute/servers/test_servers_microversions.py
@@ -0,0 +1,51 @@
+# Copyright 2018 NEC Corporation.
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from tempest.api.compute import base
+from tempest.common import waiters
+from tempest.lib.common.utils import data_utils
+from tempest.lib import decorators
+
+# NOTE(gmann): This file is to write the tests which mainly
+# tests newly added microversion schema related to servers APIs.
+# As per (https://docs.openstack.org/tempest/latest/microversion_testing.
+# html#tempest-scope-for-microversion-testing),
+# we need to fill the API response schema gaps which gets modified
+# during microversion change. To cover the testing of such schema
+# we need to have operation schema test which just test
+# the microversion schemas.
+# If you are adding server APIs microversion schema file without
+# their integration tests, you can add tests to cover those schema
+# in this file.
+
+
+class ServerShowV254Test(base.BaseV2ComputeTest):
+    min_microversion = '2.54'
+    max_microversion = 'latest'
+
+    @decorators.idempotent_id('09170a98-4940-4637-add7-1a35121f1a5a')
+    def test_rebuild_server(self):
+        server = self.create_test_server(wait_until='ACTIVE')
+        keypair_name = data_utils.rand_name(
+            self.__class__.__name__ + '-keypair')
+        kwargs = {'name': keypair_name}
+        self.keypairs_client.create_keypair(**kwargs)
+        self.addCleanup(self.keypairs_client.delete_keypair,
+                        keypair_name)
+        # Checking rebuild API response schema
+        self.servers_client.rebuild_server(server['id'], self.image_ref_alt,
+                                           key_name=keypair_name)
+        waiters.wait_for_server_status(self.servers_client,
+                                       server['id'], 'ACTIVE')
diff --git a/tempest/api/compute/volumes/test_attach_volume_negative.py b/tempest/api/compute/volumes/test_attach_volume_negative.py
index 7a74869..8618148 100644
--- a/tempest/api/compute/volumes/test_attach_volume_negative.py
+++ b/tempest/api/compute/volumes/test_attach_volume_negative.py
@@ -56,3 +56,16 @@
 
         self.assertRaises(lib_exc.BadRequest,
                           self.attach_volume, server, volume)
+
+    @decorators.attr(type=['negative'])
+    @decorators.idempotent_id('ee37a796-2afb-11e7-bc0f-fa163e65f5ce')
+    def test_attach_attached_volume_to_different_server(self):
+        server1 = self.create_test_server(wait_until='ACTIVE')
+        volume = self.create_volume()
+
+        self.attach_volume(server1, volume)
+
+        # Create server2 and attach in-use volume
+        server2 = self.create_test_server(wait_until='ACTIVE')
+        self.assertRaises(lib_exc.BadRequest,
+                          self.attach_volume, server2, volume)
diff --git a/tempest/api/identity/admin/v2/test_tenants.py b/tempest/api/identity/admin/v2/test_tenants.py
index 0f955bf..cda721c 100644
--- a/tempest/api/identity/admin/v2/test_tenants.py
+++ b/tempest/api/identity/admin/v2/test_tenants.py
@@ -59,11 +59,9 @@
         # Create a tenant that is enabled
         tenant = self.setup_test_tenant(enabled=True)
         tenant_id = tenant['id']
-        en1 = tenant['enabled']
-        self.assertTrue(en1, 'Enable should be True in response')
+        self.assertTrue(tenant['enabled'], 'Enable should be True in response')
         body = self.tenants_client.show_tenant(tenant_id)['tenant']
-        en2 = body['enabled']
-        self.assertTrue(en2, 'Enable should be True in lookup')
+        self.assertTrue(body['enabled'], 'Enable should be True in lookup')
         self.tenants_client.delete_tenant(tenant_id)
 
     @decorators.idempotent_id('3be22093-b30f-499d-b772-38340e5e16fb')
@@ -71,12 +69,10 @@
         # Create a tenant that is not enabled
         tenant = self.setup_test_tenant(enabled=False)
         tenant_id = tenant['id']
-        en1 = tenant['enabled']
-        self.assertEqual('false', str(en1).lower(),
+        self.assertFalse(tenant['enabled'],
                          'Enable should be False in response')
         body = self.tenants_client.show_tenant(tenant_id)['tenant']
-        en2 = body['enabled']
-        self.assertEqual('false', str(en2).lower(),
+        self.assertFalse(body['enabled'],
                          'Enable should be False in lookup')
         self.tenants_client.delete_tenant(tenant_id)
 
@@ -143,7 +139,7 @@
         resp3_en = body['enabled']
 
         self.assertNotEqual(resp1_en, resp3_en)
-        self.assertEqual('false', str(resp1_en).lower())
+        self.assertFalse(tenant['enabled'])
         self.assertEqual(resp2_en, resp3_en)
 
         self.tenants_client.delete_tenant(t_id)
diff --git a/tempest/api/identity/admin/v3/test_projects.py b/tempest/api/identity/admin/v3/test_projects.py
index ac23067..bc94a8e 100644
--- a/tempest/api/identity/admin/v3/test_projects.py
+++ b/tempest/api/identity/admin/v3/test_projects.py
@@ -101,22 +101,19 @@
         # Create a project that is enabled
         project = self.setup_test_project(enabled=True)
         project_id = project['id']
-        en1 = project['enabled']
-        self.assertTrue(en1, 'Enable should be True in response')
+        self.assertTrue(project['enabled'],
+                        'Enable should be True in response')
         body = self.projects_client.show_project(project_id)['project']
-        en2 = body['enabled']
-        self.assertTrue(en2, 'Enable should be True in lookup')
+        self.assertTrue(body['enabled'], 'Enable should be True in lookup')
 
     @decorators.idempotent_id('78f96a9c-e0e0-4ee6-a3ba-fbf6dfd03207')
     def test_project_create_not_enabled(self):
         # Create a project that is not enabled
         project = self.setup_test_project(enabled=False)
-        en1 = project['enabled']
-        self.assertEqual('false', str(en1).lower(),
+        self.assertFalse(project['enabled'],
                          'Enable should be False in response')
         body = self.projects_client.show_project(project['id'])['project']
-        en2 = body['enabled']
-        self.assertEqual('false', str(en2).lower(),
+        self.assertFalse(body['enabled'],
                          'Enable should be False in lookup')
 
     @decorators.idempotent_id('f608f368-048c-496b-ad63-d286c26dab6b')
@@ -178,7 +175,7 @@
         resp3_en = body['enabled']
 
         self.assertNotEqual(resp1_en, resp3_en)
-        self.assertEqual('false', str(resp1_en).lower())
+        self.assertFalse(project['enabled'])
         self.assertEqual(resp2_en, resp3_en)
 
     @decorators.idempotent_id('59398d4a-5dc5-4f86-9a4c-c26cc804d6c6')
diff --git a/tempest/api/image/v2/test_images.py b/tempest/api/image/v2/test_images.py
index ce5bd3e..aa57daf 100644
--- a/tempest/api/image/v2/test_images.py
+++ b/tempest/api/image/v2/test_images.py
@@ -71,8 +71,12 @@
         self.assertEqual(1024, body.get('size'))
 
         # Now try get image file
+        # NOTE: This Glance API returns different status codes for image
+        # condition. In this non-empty data case, Glance should return 200,
+        # so here should check the status code.
         body = self.client.show_image_file(image['id'])
         self.assertEqual(file_content, body.data)
+        self.assertEqual(200, body.response.status)
 
     @decorators.attr(type='smoke')
     @decorators.idempotent_id('f848bb94-1c6e-45a4-8726-39e3a5b23535')
@@ -111,6 +115,13 @@
                                   visibility='private')
         self.assertEqual('queued', image['status'])
 
+        # NOTE: This Glance API returns different status codes for image
+        # condition. In this empty data case, Glance should return 204,
+        # so here should check the status code.
+        image_file = self.client.show_image_file(image['id'])
+        self.assertEqual(0, len(image_file.data))
+        self.assertEqual(204, image_file.response.status)
+
         # Now try uploading an image file
         image_file = six.BytesIO(data_utils.random_bytes())
         self.client.store_image_file(image['id'], image_file)
diff --git a/tempest/api/volume/test_volumes_backup.py b/tempest/api/volume/test_volumes_backup.py
index 552b231..07cfad5 100644
--- a/tempest/api/volume/test_volumes_backup.py
+++ b/tempest/api/volume/test_volumes_backup.py
@@ -128,7 +128,7 @@
 
         volume_details = self.volumes_client.show_volume(
             volume['id'])['volume']
-        self.assertEqual('true', volume_details['bootable'])
+        self.assertTrue(volume_details['bootable'])
 
         # Create a backup
         backup = self.create_backup(volume_id=volume['id'])
@@ -140,7 +140,7 @@
         restored_volume_info = self.volumes_client.show_volume(
             restored_volume_id)['volume']
 
-        self.assertEqual('true', restored_volume_info['bootable'])
+        self.assertTrue(restored_volume_info['bootable'])
 
 
 class VolumesBackupsV39Test(base.BaseVolumeTest):
diff --git a/tempest/clients.py b/tempest/clients.py
index 0d16748..707127c 100644
--- a/tempest/clients.py
+++ b/tempest/clients.py
@@ -271,6 +271,7 @@
             # Set default client for users that don't need explicit version
             self.volumes_client_latest = self.volumes_v2_client
             self.snapshots_client_latest = self.snapshots_v2_client
+            self.backups_client_latest = self.backups_v2_client
 
         if CONF.volume_feature_enabled.api_v3:
             self.backups_v3_client = self.volume_v3.BackupsClient()
@@ -286,6 +287,7 @@
             # Set default client for users that don't need explicit version
             self.volumes_client_latest = self.volumes_v3_client
             self.snapshots_client_latest = self.snapshots_v3_client
+            self.backups_client_latest = self.backups_v3_client
 
     def _set_object_storage_clients(self):
         self.account_client = self.object_storage.AccountClient()
diff --git a/tempest/common/waiters.py b/tempest/common/waiters.py
index 08e2a12..0e86f05 100644
--- a/tempest/common/waiters.py
+++ b/tempest/common/waiters.py
@@ -287,3 +287,24 @@
             raise lib_exc.TimeoutException(message)
 
     return body
+
+
+def wait_for_interface_detach(client, server_id, port_id):
+    """Waits for an interface to be detached from a server."""
+    body = client.list_interfaces(server_id)['interfaceAttachments']
+    ports = [iface['port_id'] for iface in body]
+    start = int(time.time())
+
+    while port_id in ports:
+        time.sleep(client.build_interval)
+        body = client.list_interfaces(server_id)['interfaceAttachments']
+        ports = [iface['port_id'] for iface in body]
+        if port_id not in ports:
+            return body
+
+        timed_out = int(time.time()) - start >= client.build_timeout
+        if timed_out:
+            message = ('Interface %s failed to detach from server %s within '
+                       'the required time (%s s)' % (port_id, server_id,
+                                                     client.build_timeout))
+            raise lib_exc.TimeoutException(message)
diff --git a/tempest/config.py b/tempest/config.py
index 7133b3d..1fb5c8e 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -752,7 +752,7 @@
                help='Timeout in seconds to wait for a volume to become '
                     'available.'),
     cfg.StrOpt('catalog_type',
-               default='volume',
+               default='volumev3',
                help="Catalog type of the Volume Service"),
     cfg.StrOpt('region',
                default='',
@@ -1078,7 +1078,7 @@
     return opt_list
 
 
-# this should never be called outside of this class
+# This should never be called outside of this module
 class TempestConfigPrivate(object):
     """Provides OpenStack configuration information."""
 
diff --git a/tempest/hacking/ignored_list_T110.txt b/tempest/hacking/ignored_list_T110.txt
deleted file mode 100644
index 0e7e894..0000000
--- a/tempest/hacking/ignored_list_T110.txt
+++ /dev/null
@@ -1 +0,0 @@
-./tempest/services/object_storage/object_client.py
diff --git a/tempest/lib/api_schema/response/compute/v2_1/flavors.py b/tempest/lib/api_schema/response/compute/v2_1/flavors.py
index 547d94d..af5e67f 100644
--- a/tempest/lib/api_schema/response/compute/v2_1/flavors.py
+++ b/tempest/lib/api_schema/response/compute/v2_1/flavors.py
@@ -86,7 +86,7 @@
     'status_code': [200]
 }
 
-create_get_flavor_details = {
+create_update_get_flavor_details = {
     'status_code': [200],
     'response_body': {
         'type': 'object',
diff --git a/tempest/lib/api_schema/response/compute/v2_54/__init__.py b/tempest/lib/api_schema/response/compute/v2_54/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_54/__init__.py
diff --git a/tempest/lib/api_schema/response/compute/v2_54/servers.py b/tempest/lib/api_schema/response/compute/v2_54/servers.py
new file mode 100644
index 0000000..c084696
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_54/servers.py
@@ -0,0 +1,49 @@
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+import copy
+
+from tempest.lib.api_schema.response.compute.v2_47 import servers as servers247
+# ****** Schemas changed in microversion 2.54 *****************
+
+# Note(gmann): This is schema for microversion 2.54 which includes the
+# 'key_name' in the Response body of the following APIs:
+#    - ``POST '/servers/{server_id}/action (rebuild)``
+
+key_name = {
+    'oneOf': [
+        {'type': 'string', 'minLength': 1, 'maxLength': 255},
+        {'type': 'null'},
+    ]
+}
+
+rebuild_server = copy.deepcopy(servers247.rebuild_server)
+rebuild_server['response_body']['properties']['server'][
+    'properties'].update({'key_name': key_name})
+rebuild_server['response_body']['properties']['server'][
+    'required'].append('key_name')
+
+rebuild_server_with_admin_pass = copy.deepcopy(
+    servers247.rebuild_server_with_admin_pass)
+rebuild_server_with_admin_pass['response_body']['properties']['server'][
+    'properties'].update({'key_name': key_name})
+rebuild_server_with_admin_pass['response_body']['properties']['server'][
+    'required'].append('key_name')
+
+# ****** Schemas unchanged in microversion 2.54 since microversion 2.47 ***
+
+# NOTE(gmann): Below are the unchanged schema in this microversion. We need
+# to keep this schema in this file to have the generic way to select the
+# right schema based on self.schema_versions_info mapping in service client.
+get_server = copy.deepcopy(servers247.get_server)
+list_servers_detail = copy.deepcopy(servers247.list_servers_detail)
+update_server = copy.deepcopy(servers247.update_server)
diff --git a/tempest/lib/api_schema/response/compute/v2_55/__init__.py b/tempest/lib/api_schema/response/compute/v2_55/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_55/__init__.py
diff --git a/tempest/lib/api_schema/response/compute/v2_55/flavors.py b/tempest/lib/api_schema/response/compute/v2_55/flavors.py
new file mode 100644
index 0000000..823190a
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_55/flavors.py
@@ -0,0 +1,112 @@
+# Copyright 2018 NEC Corporation.  All rights reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from tempest.lib.api_schema.response.compute.v2_1 import parameter_types
+
+# Note(gmann): This is schema for microversion 2.55 which includes the
+# following changes:
+# Add new PUT API
+# Adds a ``description`` field to the following APIs response:
+#    - ``GET /flavors``
+#    - ``GET /flavors/detail``
+#    - ``GET /flavors/{flavor_id}``
+#    - ``POST /flavors``
+
+flavor_description = {
+    'type': ['string', 'null'],
+    'minLength': 0, 'maxLength': 65535
+}
+
+list_flavors = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'flavors': {
+                'type': 'array',
+                'items': {
+                    'type': 'object',
+                    'properties': {
+                        'name': {'type': 'string'},
+                        'links': parameter_types.links,
+                        'id': {'type': 'string'},
+                        'description': flavor_description
+                    },
+                    'additionalProperties': False,
+                    'required': ['name', 'links', 'id', 'description']
+                }
+            },
+            'flavors_links': parameter_types.links
+        },
+        'additionalProperties': False,
+        # NOTE(gmann): flavors_links attribute is not necessary
+        # to be present always So it is not 'required'.
+        'required': ['flavors']
+    }
+}
+
+common_flavor_info = {
+    'type': 'object',
+    'properties': {
+        'name': {'type': 'string'},
+        'links': parameter_types.links,
+        'ram': {'type': 'integer'},
+        'vcpus': {'type': 'integer'},
+        # 'swap' attributes comes as integer value but if it is empty
+        # it comes as "". So defining type of as string and integer.
+        'swap': {'type': ['integer', 'string']},
+        'disk': {'type': 'integer'},
+        'id': {'type': 'string'},
+        'OS-FLV-DISABLED:disabled': {'type': 'boolean'},
+        'os-flavor-access:is_public': {'type': 'boolean'},
+        'rxtx_factor': {'type': 'number'},
+        'OS-FLV-EXT-DATA:ephemeral': {'type': 'integer'},
+        'description': flavor_description
+    },
+    'additionalProperties': False,
+    # 'OS-FLV-DISABLED', 'os-flavor-access', 'rxtx_factor' and
+    # 'OS-FLV-EXT-DATA' are API extensions. So they are not 'required'.
+    'required': ['name', 'links', 'ram', 'vcpus', 'swap', 'disk', 'id',
+                 'description']
+}
+
+list_flavors_details = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'flavors': {
+                'type': 'array',
+                'items': common_flavor_info
+            },
+            # NOTE(gmann): flavors_links attribute is not necessary
+            # to be present always So it is not 'required'.
+            'flavors_links': parameter_types.links
+        },
+        'additionalProperties': False,
+        'required': ['flavors']
+    }
+}
+
+create_update_get_flavor_details = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'flavor': common_flavor_info
+        },
+        'additionalProperties': False,
+        'required': ['flavor']
+    }
+}
diff --git a/tempest/lib/services/compute/flavors_client.py b/tempest/lib/services/compute/flavors_client.py
index 0fb1991..4923d7e 100644
--- a/tempest/lib/services/compute/flavors_client.py
+++ b/tempest/lib/services/compute/flavors_client.py
@@ -21,12 +21,18 @@
     as schema_access
 from tempest.lib.api_schema.response.compute.v2_1 import flavors_extra_specs \
     as schema_extra_specs
+from tempest.lib.api_schema.response.compute.v2_55 import flavors \
+    as schemav255
 from tempest.lib.common import rest_client
 from tempest.lib.services.compute import base_compute_client
 
 
 class FlavorsClient(base_compute_client.BaseComputeClient):
 
+    schema_versions_info = [
+        {'min': None, 'max': '2.54', 'schema': schema},
+        {'min': '2.55', 'max': None, 'schema': schemav255}]
+
     def list_flavors(self, detail=False, **params):
         """Lists flavors.
 
@@ -36,11 +42,12 @@
         https://developer.openstack.org/api-ref/compute/#list-flavors-with-details
         """
         url = 'flavors'
-        _schema = schema.list_flavors
-
+        schema = self.get_schema(self.schema_versions_info)
         if detail:
             url += '/detail'
             _schema = schema.list_flavors_details
+        else:
+            _schema = schema.list_flavors
         if params:
             url += '?%s' % urllib.urlencode(params)
 
@@ -58,7 +65,9 @@
         """
         resp, body = self.get("flavors/%s" % flavor_id)
         body = json.loads(body)
-        self.validate_response(schema.create_get_flavor_details, resp, body)
+        schema = self.get_schema(self.schema_versions_info)
+        self.validate_response(schema.create_update_get_flavor_details,
+                               resp, body)
         return rest_client.ResponseBody(resp, body)
 
     def create_flavor(self, **kwargs):
@@ -77,7 +86,25 @@
         resp, body = self.post('flavors', post_body)
 
         body = json.loads(body)
-        self.validate_response(schema.create_get_flavor_details, resp, body)
+        schema = self.get_schema(self.schema_versions_info)
+        self.validate_response(schema.create_update_get_flavor_details,
+                               resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_flavor(self, flavor_id, **kwargs):
+        """Uodate the flavor or instance type.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/compute/#update-flavor-description
+        """
+        put_body = json.dumps({'flavor': kwargs})
+        resp, body = self.put("flavors/%s" % flavor_id, put_body)
+
+        body = json.loads(body)
+        schema = self.get_schema(self.schema_versions_info)
+        self.validate_response(schema.create_update_get_flavor_details,
+                               resp, body)
         return rest_client.ResponseBody(resp, body)
 
     def delete_flavor(self, flavor_id):
diff --git a/tempest/lib/services/compute/servers_client.py b/tempest/lib/services/compute/servers_client.py
index c85af1f..e75cdb5 100644
--- a/tempest/lib/services/compute/servers_client.py
+++ b/tempest/lib/services/compute/servers_client.py
@@ -29,6 +29,7 @@
 from tempest.lib.api_schema.response.compute.v2_3 import servers as schemav23
 from tempest.lib.api_schema.response.compute.v2_47 import servers as schemav247
 from tempest.lib.api_schema.response.compute.v2_48 import servers as schemav248
+from tempest.lib.api_schema.response.compute.v2_54 import servers as schemav254
 from tempest.lib.api_schema.response.compute.v2_6 import servers as schemav26
 from tempest.lib.api_schema.response.compute.v2_9 import servers as schemav29
 from tempest.lib.common import rest_client
@@ -47,7 +48,8 @@
         {'min': '2.19', 'max': '2.25', 'schema': schemav219},
         {'min': '2.26', 'max': '2.46', 'schema': schemav226},
         {'min': '2.47', 'max': '2.47', 'schema': schemav247},
-        {'min': '2.48', 'max': None, 'schema': schemav248}]
+        {'min': '2.48', 'max': '2.53', 'schema': schemav248},
+        {'min': '2.54', 'max': None, 'schema': schemav254}]
 
     def __init__(self, auth_provider, service, region,
                  enable_instance_password=True, **kwargs):
diff --git a/tempest/lib/services/volume/v2/availability_zone_client.py b/tempest/lib/services/volume/v2/availability_zone_client.py
index bb4a357..bdb2304 100644
--- a/tempest/lib/services/volume/v2/availability_zone_client.py
+++ b/tempest/lib/services/volume/v2/availability_zone_client.py
@@ -13,16 +13,11 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from oslo_serialization import jsonutils as json
+from debtcollector import moves
 
-from tempest.lib.common import rest_client
+from tempest.lib.services.volume.v3 import availability_zone_client
 
 
-class AvailabilityZoneClient(rest_client.RestClient):
-    api_version = "v2"
-
-    def list_availability_zones(self):
-        resp, body = self.get('os-availability-zone')
-        body = json.loads(body)
-        self.expected_success(200, resp.status)
-        return rest_client.ResponseBody(resp, body)
+AvailabilityZoneClient = moves.moved_class(
+    availability_zone_client.AvailabilityZoneClient, 'AvailabilityZoneClient',
+    __name__, version="Rocky", removal_version='?')
diff --git a/tempest/lib/services/volume/v2/backups_client.py b/tempest/lib/services/volume/v2/backups_client.py
index adfa6a6..80b3631 100644
--- a/tempest/lib/services/volume/v2/backups_client.py
+++ b/tempest/lib/services/volume/v2/backups_client.py
@@ -12,108 +12,11 @@
 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 #    License for the specific language governing permissions and limitations
 #    under the License.
+from debtcollector import moves
 
-from oslo_serialization import jsonutils as json
-from six.moves.urllib import parse as urllib
-
-from tempest.lib.common import rest_client
-from tempest.lib import exceptions as lib_exc
-from tempest.lib.services.volume import base_client
+from tempest.lib.services.volume.v3 import backups_client
 
 
-class BackupsClient(base_client.BaseClient):
-    """Volume V2 Backups client"""
-    api_version = "v2"
-
-    def create_backup(self, **kwargs):
-        """Creates a backup of volume.
-
-        For a full list of available parameters, please refer to the official
-        API reference:
-        https://developer.openstack.org/api-ref/block-storage/v2/index.html#create-backup
-        """
-        post_body = json.dumps({'backup': kwargs})
-        resp, body = self.post('backups', post_body)
-        body = json.loads(body)
-        self.expected_success(202, resp.status)
-        return rest_client.ResponseBody(resp, body)
-
-    def restore_backup(self, backup_id, **kwargs):
-        """Restore volume from backup.
-
-        For a full list of available parameters, please refer to the official
-        API reference:
-        https://developer.openstack.org/api-ref/block-storage/v2/index.html#restore-backup
-        """
-        post_body = json.dumps({'restore': kwargs})
-        resp, body = self.post('backups/%s/restore' % (backup_id), post_body)
-        body = json.loads(body)
-        self.expected_success(202, resp.status)
-        return rest_client.ResponseBody(resp, body)
-
-    def delete_backup(self, backup_id):
-        """Delete a backup of volume."""
-        resp, body = self.delete('backups/%s' % backup_id)
-        self.expected_success(202, resp.status)
-        return rest_client.ResponseBody(resp, body)
-
-    def show_backup(self, backup_id):
-        """Returns the details of a single backup."""
-        url = "backups/%s" % backup_id
-        resp, body = self.get(url)
-        body = json.loads(body)
-        self.expected_success(200, resp.status)
-        return rest_client.ResponseBody(resp, body)
-
-    def list_backups(self, detail=False, **params):
-        """List all the tenant's backups.
-
-        For a full list of available parameters, please refer to the official
-        API reference:
-        http://developer.openstack.org/api-ref/block-storage/v2/#list-backups
-        http://developer.openstack.org/api-ref/block-storage/v2/#list-backups-with-details
-        """
-        url = "backups"
-        if detail:
-            url += "/detail"
-        if params:
-            url += '?%s' % urllib.urlencode(params)
-        resp, body = self.get(url)
-        body = json.loads(body)
-        self.expected_success(200, resp.status)
-        return rest_client.ResponseBody(resp, body)
-
-    def export_backup(self, backup_id):
-        """Export backup metadata record."""
-        url = "backups/%s/export_record" % backup_id
-        resp, body = self.get(url)
-        body = json.loads(body)
-        self.expected_success(200, resp.status)
-        return rest_client.ResponseBody(resp, body)
-
-    def import_backup(self, **kwargs):
-        """Import backup metadata record."""
-        post_body = json.dumps({'backup-record': kwargs})
-        resp, body = self.post("backups/import_record", post_body)
-        body = json.loads(body)
-        self.expected_success(201, resp.status)
-        return rest_client.ResponseBody(resp, body)
-
-    def reset_backup_status(self, backup_id, status):
-        """Reset the specified backup's status."""
-        post_body = json.dumps({'os-reset_status': {"status": status}})
-        resp, body = self.post('backups/%s/action' % backup_id, post_body)
-        self.expected_success(202, resp.status)
-        return rest_client.ResponseBody(resp, body)
-
-    def is_resource_deleted(self, id):
-        try:
-            self.show_backup(id)
-        except lib_exc.NotFound:
-            return True
-        return False
-
-    @property
-    def resource_type(self):
-        """Returns the primary type of resource this client works with."""
-        return 'backup'
+BackupsClient = moves.moved_class(
+    backups_client.BackupsClient, 'BackupsClient',
+    __name__, version="Rocky", removal_version='?')
diff --git a/tempest/lib/services/volume/v2/capabilities_client.py b/tempest/lib/services/volume/v2/capabilities_client.py
index 240be13..d8cf806 100644
--- a/tempest/lib/services/volume/v2/capabilities_client.py
+++ b/tempest/lib/services/volume/v2/capabilities_client.py
@@ -13,23 +13,11 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from oslo_serialization import jsonutils as json
+from debtcollector import moves
 
-from tempest.lib.common import rest_client
+from tempest.lib.services.volume.v3 import capabilities_client
 
 
-class CapabilitiesClient(rest_client.RestClient):
-    api_version = "v2"
-
-    def show_backend_capabilities(self, host):
-        """Shows capabilities for a storage back end.
-
-        For a full list of available parameters, please refer to the official
-        API reference:
-        https://developer.openstack.org/api-ref/block-storage/v2/index.html#show-back-end-capabilities
-        """
-        url = 'capabilities/%s' % host
-        resp, body = self.get(url)
-        body = json.loads(body)
-        self.expected_success(200, resp.status)
-        return rest_client.ResponseBody(resp, body)
+CapabilitiesClient = moves.moved_class(
+    capabilities_client.CapabilitiesClient, 'CapabilitiesClient',
+    __name__, version="Queens", removal_version='?')
diff --git a/tempest/lib/services/volume/v2/encryption_types_client.py b/tempest/lib/services/volume/v2/encryption_types_client.py
index b99d1fe..875e59e 100644
--- a/tempest/lib/services/volume/v2/encryption_types_client.py
+++ b/tempest/lib/services/volume/v2/encryption_types_client.py
@@ -13,79 +13,11 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from oslo_serialization import jsonutils as json
+from debtcollector import moves
 
-from tempest.lib.common import rest_client
-from tempest.lib import exceptions as lib_exc
+from tempest.lib.services.volume.v3 import encryption_types_client
 
 
-class EncryptionTypesClient(rest_client.RestClient):
-    api_version = "v2"
-
-    def is_resource_deleted(self, id):
-        try:
-            body = self.show_encryption_type(id)
-            if not body:
-                return True
-        except lib_exc.NotFound:
-            return True
-        return False
-
-    @property
-    def resource_type(self):
-        """Returns the primary type of resource this client works with."""
-        return 'encryption-type'
-
-    def show_encryption_type(self, volume_type_id):
-        """Get the volume encryption type for the specified volume type.
-
-        volume_type_id: Id of volume_type.
-        """
-        url = "/types/%s/encryption" % volume_type_id
-        resp, body = self.get(url)
-        body = json.loads(body)
-        self.expected_success(200, resp.status)
-        return rest_client.ResponseBody(resp, body)
-
-    def show_encryption_specs_item(self, volume_type_id, key):
-        """Get the encryption specs item for the specified volume type."""
-        url = "/types/%s/encryption/%s" % (volume_type_id, key)
-        resp, body = self.get(url)
-        body = json.loads(body)
-        self.expected_success(200, resp.status)
-        return rest_client.ResponseBody(resp, body)
-
-    def create_encryption_type(self, volume_type_id, **kwargs):
-        """Create encryption type.
-
-        For a full list of available parameters, please refer to the official
-        API reference:
-        https://developer.openstack.org/api-ref/block-storage/v2/#create-an-encryption-type-for-v2
-        """
-        url = "/types/%s/encryption" % volume_type_id
-        post_body = json.dumps({'encryption': kwargs})
-        resp, body = self.post(url, post_body)
-        body = json.loads(body)
-        self.expected_success(200, resp.status)
-        return rest_client.ResponseBody(resp, body)
-
-    def delete_encryption_type(self, volume_type_id):
-        """Delete the encryption type for the specified volume-type."""
-        resp, body = self.delete(
-            "/types/%s/encryption/provider" % volume_type_id)
-        self.expected_success(202, resp.status)
-        return rest_client.ResponseBody(resp, body)
-
-    def update_encryption_type(self, volume_type_id, **kwargs):
-        """Update an encryption type for an existing volume type.
-
-        For a full list of available parameters, please refer to the official
-        API reference:
-        https://developer.openstack.org/api-ref/block-storage/v2/#update-an-encryption-type-for-v2
-        """
-        url = "/types/%s/encryption/provider" % volume_type_id
-        put_body = json.dumps({'encryption': kwargs})
-        resp, body = self.put(url, put_body)
-        body = json.loads(body)
-        self.expected_success(200, resp.status)
-        return rest_client.ResponseBody(resp, body)
+EncryptionTypesClient = moves.moved_class(
+    encryption_types_client.EncryptionTypesClient, 'EncryptionTypesClient',
+    __name__, version="Rocky", removal_version='?')
diff --git a/tempest/lib/services/volume/v2/extensions_client.py b/tempest/lib/services/volume/v2/extensions_client.py
index 09279d5..6316ef5 100644
--- a/tempest/lib/services/volume/v2/extensions_client.py
+++ b/tempest/lib/services/volume/v2/extensions_client.py
@@ -12,19 +12,11 @@
 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 #    License for the specific language governing permissions and limitations
 #    under the License.
+from debtcollector import moves
 
-from oslo_serialization import jsonutils as json
-
-from tempest.lib.common import rest_client
+from tempest.lib.services.volume.v3 import extensions_client
 
 
-class ExtensionsClient(rest_client.RestClient):
-    """Volume V2 extensions client."""
-    api_version = "v2"
-
-    def list_extensions(self):
-        url = 'extensions'
-        resp, body = self.get(url)
-        body = json.loads(body)
-        self.expected_success(200, resp.status)
-        return rest_client.ResponseBody(resp, body)
+ExtensionsClient = moves.moved_class(
+    extensions_client.ExtensionsClient, 'ExtensionsClient',
+    __name__, version="Rocky", removal_version='?')
diff --git a/tempest/lib/services/volume/v2/hosts_client.py b/tempest/lib/services/volume/v2/hosts_client.py
index f44bda3..38f1b38 100644
--- a/tempest/lib/services/volume/v2/hosts_client.py
+++ b/tempest/lib/services/volume/v2/hosts_client.py
@@ -12,37 +12,11 @@
 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 #    License for the specific language governing permissions and limitations
 #    under the License.
+from debtcollector import moves
 
-from oslo_serialization import jsonutils as json
-from six.moves.urllib import parse as urllib
-
-from tempest.lib.common import rest_client
+from tempest.lib.services.volume.v3 import hosts_client
 
 
-class HostsClient(rest_client.RestClient):
-    """Client class to send CRUD Volume V2 API requests"""
-    api_version = "v2"
-
-    def list_hosts(self, **params):
-        """Lists all hosts.
-
-        For a full list of available parameters, please refer to the official
-        API reference:
-        https://developer.openstack.org/api-ref/block-storage/v2/#list-all-hosts
-        """
-        url = 'os-hosts'
-        if params:
-            url += '?%s' % urllib.urlencode(params)
-
-        resp, body = self.get(url)
-        body = json.loads(body)
-        self.expected_success(200, resp.status)
-        return rest_client.ResponseBody(resp, body)
-
-    def show_host(self, host_name):
-        """Show host details."""
-        url = 'os-hosts/%s' % host_name
-        resp, body = self.get(url)
-        self.expected_success(200, resp.status)
-        body = json.loads(body)
-        return rest_client.ResponseBody(resp, body)
+HostsClient = moves.moved_class(
+    hosts_client.HostsClient, 'HostsClient',
+    __name__, version="Rocky", removal_version='?')
diff --git a/tempest/lib/services/volume/v2/limits_client.py b/tempest/lib/services/volume/v2/limits_client.py
index ce9fba9..a6b8c5a 100644
--- a/tempest/lib/services/volume/v2/limits_client.py
+++ b/tempest/lib/services/volume/v2/limits_client.py
@@ -12,21 +12,11 @@
 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 #    License for the specific language governing permissions and limitations
 #    under the License.
+from debtcollector import moves
 
-from oslo_serialization import jsonutils as json
-
-from tempest.lib.common import rest_client
+from tempest.lib.services.volume.v3 import limits_client
 
 
-class LimitsClient(rest_client.RestClient):
-    """Volume V2 limits client."""
-
-    api_version = "v2"
-
-    def show_limits(self):
-        """Returns the details of a volume absolute limits."""
-        url = "limits"
-        resp, body = self.get(url)
-        body = json.loads(body)
-        self.expected_success(200, resp.status)
-        return rest_client.ResponseBody(resp, body)
+LimitsClient = moves.moved_class(
+    limits_client.LimitsClient, 'LimitsClient',
+    __name__, version="Rocky", removal_version='?')
diff --git a/tempest/lib/services/volume/v2/qos_client.py b/tempest/lib/services/volume/v2/qos_client.py
index 47d3914..b81384e 100644
--- a/tempest/lib/services/volume/v2/qos_client.py
+++ b/tempest/lib/services/volume/v2/qos_client.py
@@ -11,123 +11,11 @@
 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 #    License for the specific language governing permissions and limitations
 #    under the License.
+from debtcollector import moves
 
-from oslo_serialization import jsonutils as json
-
-from tempest.lib.common import rest_client
-from tempest.lib import exceptions as lib_exc
+from tempest.lib.services.volume.v3 import qos_client
 
 
-class QosSpecsClient(rest_client.RestClient):
-    """Volume V2 QoS client.
-
-       Client class to send CRUD QoS API requests
-    """
-
-    api_version = "v2"
-
-    def is_resource_deleted(self, qos_id):
-        try:
-            self.show_qos(qos_id)
-        except lib_exc.NotFound:
-            return True
-        return False
-
-    @property
-    def resource_type(self):
-        """Returns the primary type of resource this client works with."""
-        return 'qos'
-
-    def create_qos(self, **kwargs):
-        """Create a QoS Specification.
-
-        For a full list of available parameters, please refer to the official
-        API reference:
-        http://developer.openstack.org/api-ref/block-storage/v2/#create-qos-specification
-        """
-        post_body = json.dumps({'qos_specs': kwargs})
-        resp, body = self.post('qos-specs', post_body)
-        self.expected_success(200, resp.status)
-        body = json.loads(body)
-        return rest_client.ResponseBody(resp, body)
-
-    def delete_qos(self, qos_id, force=False):
-        """Delete the specified QoS specification."""
-        resp, body = self.delete(
-            "qos-specs/%s?force=%s" % (qos_id, force))
-        self.expected_success(202, resp.status)
-        return rest_client.ResponseBody(resp, body)
-
-    def list_qos(self):
-        """List all the QoS specifications created."""
-        url = 'qos-specs'
-        resp, body = self.get(url)
-        body = json.loads(body)
-        self.expected_success(200, resp.status)
-        return rest_client.ResponseBody(resp, body)
-
-    def show_qos(self, qos_id):
-        """Get the specified QoS specification."""
-        url = "qos-specs/%s" % qos_id
-        resp, body = self.get(url)
-        body = json.loads(body)
-        self.expected_success(200, resp.status)
-        return rest_client.ResponseBody(resp, body)
-
-    def set_qos_key(self, qos_id, **kwargs):
-        """Set the specified keys/values of QoS specification.
-
-        For a full list of available parameters, please refer to the official
-        API reference:
-        http://developer.openstack.org/api-ref/block-storage/v2/#set-keys-in-qos-specification
-        """
-        put_body = json.dumps({"qos_specs": kwargs})
-        resp, body = self.put('qos-specs/%s' % qos_id, put_body)
-        body = json.loads(body)
-        self.expected_success(200, resp.status)
-        return rest_client.ResponseBody(resp, body)
-
-    def unset_qos_key(self, qos_id, keys):
-        """Unset the specified keys of QoS specification.
-
-        :param keys: keys to delete from the QoS specification.
-
-        For a full list of available parameters, please refer to the official
-        API reference:
-        http://developer.openstack.org/api-ref/block-storage/v2/#unset-keys-in-qos-specification
-        """
-        put_body = json.dumps({'keys': keys})
-        resp, body = self.put('qos-specs/%s/delete_keys' % qos_id, put_body)
-        self.expected_success(202, resp.status)
-        return rest_client.ResponseBody(resp, body)
-
-    def associate_qos(self, qos_id, vol_type_id):
-        """Associate the specified QoS with specified volume-type."""
-        url = "qos-specs/%s/associate" % qos_id
-        url += "?vol_type_id=%s" % vol_type_id
-        resp, body = self.get(url)
-        self.expected_success(202, resp.status)
-        return rest_client.ResponseBody(resp, body)
-
-    def show_association_qos(self, qos_id):
-        """Get the association of the specified QoS specification."""
-        url = "qos-specs/%s/associations" % qos_id
-        resp, body = self.get(url)
-        body = json.loads(body)
-        self.expected_success(200, resp.status)
-        return rest_client.ResponseBody(resp, body)
-
-    def disassociate_qos(self, qos_id, vol_type_id):
-        """Disassociate the specified QoS with specified volume-type."""
-        url = "qos-specs/%s/disassociate" % qos_id
-        url += "?vol_type_id=%s" % vol_type_id
-        resp, body = self.get(url)
-        self.expected_success(202, resp.status)
-        return rest_client.ResponseBody(resp, body)
-
-    def disassociate_all_qos(self, qos_id):
-        """Disassociate the specified QoS with all associations."""
-        url = "qos-specs/%s/disassociate_all" % qos_id
-        resp, body = self.get(url)
-        self.expected_success(202, resp.status)
-        return rest_client.ResponseBody(resp, body)
+QosSpecsClient = moves.moved_class(
+    qos_client.QosSpecsClient, 'QosSpecsClient',
+    __name__, version="Rocky", removal_version='?')
diff --git a/tempest/lib/services/volume/v2/quota_classes_client.py b/tempest/lib/services/volume/v2/quota_classes_client.py
index 733b1ac..24aab89 100644
--- a/tempest/lib/services/volume/v2/quota_classes_client.py
+++ b/tempest/lib/services/volume/v2/quota_classes_client.py
@@ -12,40 +12,11 @@
 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 #    License for the specific language governing permissions and limitations
 #    under the License.
+from debtcollector import moves
 
-from oslo_serialization import jsonutils as json
-
-from tempest.lib.common import rest_client
+from tempest.lib.services.volume.v3 import quota_classes_client
 
 
-class QuotaClassesClient(rest_client.RestClient):
-    """Volume quota class V2 client."""
-
-    api_version = "v2"
-
-    def show_quota_class_set(self, quota_class_id):
-        """List quotas for a quota class.
-
-        For a full list of available parameters, please refer to the official
-        API reference:
-        https://developer.openstack.org/api-ref/block-storage/v2/index.html#show-quota-classes
-        """
-        url = 'os-quota-class-sets/%s' % quota_class_id
-        resp, body = self.get(url)
-        self.expected_success(200, resp.status)
-        body = json.loads(body)
-        return rest_client.ResponseBody(resp, body)
-
-    def update_quota_class_set(self, quota_class_id, **kwargs):
-        """Update quotas for a quota class.
-
-        For a full list of available parameters, please refer to the official
-        API reference:
-        https://developer.openstack.org/api-ref/block-storage/v2/index.html#update-quota-classes
-        """
-        url = 'os-quota-class-sets/%s' % quota_class_id
-        put_body = json.dumps({'quota_class_set': kwargs})
-        resp, body = self.put(url, put_body)
-        self.expected_success(200, resp.status)
-        body = json.loads(body)
-        return rest_client.ResponseBody(resp, body)
+QuotaClassesClient = moves.moved_class(
+    quota_classes_client.QuotaClassesClient, 'QuotaClassesClient',
+    __name__, version="Rocky", removal_version='?')
diff --git a/tempest/lib/services/volume/v2/quotas_client.py b/tempest/lib/services/volume/v2/quotas_client.py
index e4b2895..6f9f61c 100644
--- a/tempest/lib/services/volume/v2/quotas_client.py
+++ b/tempest/lib/services/volume/v2/quotas_client.py
@@ -12,53 +12,11 @@
 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 #    License for the specific language governing permissions and limitations
 #    under the License.
+from debtcollector import moves
 
-from oslo_serialization import jsonutils
-from six.moves.urllib import parse as urllib
-
-from tempest.lib.common import rest_client
+from tempest.lib.services.volume.v3 import quotas_client
 
 
-class QuotasClient(rest_client.RestClient):
-    """Client class to send CRUD Volume Quotas API V2 requests"""
-    api_version = "v2"
-
-    def show_default_quota_set(self, tenant_id):
-        """List the default volume quota set for a tenant."""
-
-        url = 'os-quota-sets/%s/defaults' % tenant_id
-        resp, body = self.get(url)
-        self.expected_success(200, resp.status)
-        body = jsonutils.loads(body)
-        return rest_client.ResponseBody(resp, body)
-
-    def show_quota_set(self, tenant_id, params=None):
-        """List the quota set for a tenant."""
-
-        url = 'os-quota-sets/%s' % tenant_id
-        if params:
-            url += '?%s' % urllib.urlencode(params)
-
-        resp, body = self.get(url)
-        self.expected_success(200, resp.status)
-        body = jsonutils.loads(body)
-        return rest_client.ResponseBody(resp, body)
-
-    def update_quota_set(self, tenant_id, **kwargs):
-        """Updates quota set
-
-        For a full list of available parameters, please refer to the official
-        API reference:
-        https://developer.openstack.org/api-ref/block-storage/v2/index.html#update-quotas
-        """
-        put_body = jsonutils.dumps({'quota_set': kwargs})
-        resp, body = self.put('os-quota-sets/%s' % tenant_id, put_body)
-        self.expected_success(200, resp.status)
-        body = jsonutils.loads(body)
-        return rest_client.ResponseBody(resp, body)
-
-    def delete_quota_set(self, tenant_id):
-        """Delete the tenant's quota set."""
-        resp, body = self.delete('os-quota-sets/%s' % tenant_id)
-        self.expected_success(200, resp.status)
-        return rest_client.ResponseBody(resp, body)
+QuotasClient = moves.moved_class(
+    quotas_client.QuotasClient, 'QuotasClient',
+    __name__, version="Rocky", removal_version='?')
diff --git a/tempest/lib/services/volume/v2/scheduler_stats_client.py b/tempest/lib/services/volume/v2/scheduler_stats_client.py
index 0d04f85..a5adb34 100644
--- a/tempest/lib/services/volume/v2/scheduler_stats_client.py
+++ b/tempest/lib/services/volume/v2/scheduler_stats_client.py
@@ -12,26 +12,11 @@
 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 #    License for the specific language governing permissions and limitations
 #    under the License.
+from debtcollector import moves
 
-from oslo_serialization import jsonutils as json
-
-from tempest.lib.common import rest_client
+from tempest.lib.services.volume.v3 import scheduler_stats_client
 
 
-class SchedulerStatsClient(rest_client.RestClient):
-    api_version = "v2"
-
-    def list_pools(self, detail=False):
-        """List all the volumes pools (hosts).
-
-        For a full list of available parameters, please refer to the official
-        API reference:
-        https://developer.openstack.org/api-ref/block-storage/v2/index.html#list-back-end-storage-pools
-        """
-        url = 'scheduler-stats/get_pools'
-        if detail:
-            url += '?detail=True'
-        resp, body = self.get(url)
-        body = json.loads(body)
-        self.expected_success(200, resp.status)
-        return rest_client.ResponseBody(resp, body)
+SchedulerStatsClient = moves.moved_class(
+    scheduler_stats_client.SchedulerStatsClient, 'SchedulerStatsClient',
+    __name__, version="Rocky", removal_version='?')
diff --git a/tempest/lib/services/volume/v2/services_client.py b/tempest/lib/services/volume/v2/services_client.py
index bc55469..a4491d3 100644
--- a/tempest/lib/services/volume/v2/services_client.py
+++ b/tempest/lib/services/volume/v2/services_client.py
@@ -12,23 +12,11 @@
 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 #    License for the specific language governing permissions and limitations
 #    under the License.
+from debtcollector import moves
 
-from oslo_serialization import jsonutils as json
-from six.moves.urllib import parse as urllib
-
-from tempest.lib.common import rest_client
+from tempest.lib.services.volume.v3 import services_client
 
 
-class ServicesClient(rest_client.RestClient):
-    """Client class to send CRUD Volume V2 API requests"""
-    api_version = "v2"
-
-    def list_services(self, **params):
-        url = 'os-services'
-        if params:
-            url += '?%s' % urllib.urlencode(params)
-
-        resp, body = self.get(url)
-        body = json.loads(body)
-        self.expected_success(200, resp.status)
-        return rest_client.ResponseBody(resp, body)
+ServicesClient = moves.moved_class(
+    services_client.ServicesClient, 'ServicesClient',
+    __name__, version="Rocky", removal_version='?')
diff --git a/tempest/lib/services/volume/v2/snapshot_manage_client.py b/tempest/lib/services/volume/v2/snapshot_manage_client.py
index aecd30b..132209f 100644
--- a/tempest/lib/services/volume/v2/snapshot_manage_client.py
+++ b/tempest/lib/services/volume/v2/snapshot_manage_client.py
@@ -12,22 +12,11 @@
 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 #    License for the specific language governing permissions and limitations
 #    under the License.
+from debtcollector import moves
 
-from oslo_serialization import jsonutils as json
-
-from tempest.lib.common import rest_client
+from tempest.lib.services.volume.v3 import snapshot_manage_client
 
 
-class SnapshotManageClient(rest_client.RestClient):
-    """Snapshot manage V2 client."""
-
-    api_version = "v2"
-
-    def manage_snapshot(self, **kwargs):
-        """Manage a snapshot."""
-        post_body = json.dumps({'snapshot': kwargs})
-        url = 'os-snapshot-manage'
-        resp, body = self.post(url, post_body)
-        self.expected_success(202, resp.status)
-        body = json.loads(body)
-        return rest_client.ResponseBody(resp, body)
+SnapshotManageClient = moves.moved_class(
+    snapshot_manage_client.SnapshotManageClient, 'SnapshotManageClient',
+    __name__, version="Rocky", removal_version='?')
diff --git a/tempest/lib/services/volume/v2/snapshots_client.py b/tempest/lib/services/volume/v2/snapshots_client.py
index 4bc2842..3a72cc1 100644
--- a/tempest/lib/services/volume/v2/snapshots_client.py
+++ b/tempest/lib/services/volume/v2/snapshots_client.py
@@ -9,200 +9,11 @@
 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 #    License for the specific language governing permissions and limitations
 #    under the License.
+from debtcollector import moves
 
-from oslo_serialization import jsonutils as json
-from six.moves.urllib import parse as urllib
-
-from tempest.lib.common import rest_client
-from tempest.lib import exceptions as lib_exc
+from tempest.lib.services.volume.v3 import snapshots_client
 
 
-class SnapshotsClient(rest_client.RestClient):
-    """Client class to send CRUD Volume V2 API requests."""
-    api_version = "v2"
-    create_resp = 202
-
-    def list_snapshots(self, detail=False, **params):
-        """List all the snapshot.
-
-        For a full list of available parameters, please refer to the official
-        API reference:
-        http://developer.openstack.org/api-ref/block-storage/v2/#list-snapshots-with-details
-        http://developer.openstack.org/api-ref/block-storage/v2/#list-snapshots
-        """
-        url = 'snapshots'
-        if detail:
-            url += '/detail'
-        if params:
-            url += '?%s' % urllib.urlencode(params)
-
-        resp, body = self.get(url)
-        body = json.loads(body)
-        self.expected_success(200, resp.status)
-        return rest_client.ResponseBody(resp, body)
-
-    def show_snapshot(self, snapshot_id):
-        """Returns the details of a single snapshot.
-
-        For a full list of available parameters, please refer to the official
-        API reference:
-        http://developer.openstack.org/api-ref/block-storage/v2/#show-snapshot-details
-        """
-        url = "snapshots/%s" % snapshot_id
-        resp, body = self.get(url)
-        body = json.loads(body)
-        self.expected_success(200, resp.status)
-        return rest_client.ResponseBody(resp, body)
-
-    def create_snapshot(self, **kwargs):
-        """Creates a new snapshot.
-
-        For a full list of available parameters, please refer to the official
-        API reference:
-        http://developer.openstack.org/api-ref/block-storage/v2/#create-snapshot
-        """
-        post_body = json.dumps({'snapshot': kwargs})
-        resp, body = self.post('snapshots', post_body)
-        body = json.loads(body)
-        self.expected_success(self.create_resp, resp.status)
-        return rest_client.ResponseBody(resp, body)
-
-    def update_snapshot(self, snapshot_id, **kwargs):
-        """Updates a snapshot.
-
-        For a full list of available parameters, please refer to the official
-        API reference:
-        http://developer.openstack.org/api-ref/block-storage/v2/#update-snapshot
-        """
-        put_body = json.dumps({'snapshot': kwargs})
-        resp, body = self.put('snapshots/%s' % snapshot_id, put_body)
-        body = json.loads(body)
-        self.expected_success(200, resp.status)
-        return rest_client.ResponseBody(resp, body)
-
-    def delete_snapshot(self, snapshot_id):
-        """Delete Snapshot.
-
-        For a full list of available parameters, please refer to the official
-        API reference:
-        http://developer.openstack.org/api-ref/block-storage/v2/#delete-snapshot
-        """
-        resp, body = self.delete("snapshots/%s" % snapshot_id)
-        self.expected_success(202, resp.status)
-        return rest_client.ResponseBody(resp, body)
-
-    def is_resource_deleted(self, id):
-        try:
-            self.show_snapshot(id)
-        except lib_exc.NotFound:
-            return True
-        return False
-
-    @property
-    def resource_type(self):
-        """Returns the primary type of resource this client works with."""
-        return 'volume-snapshot'
-
-    def reset_snapshot_status(self, snapshot_id, status):
-        """Reset the specified snapshot's status."""
-        post_body = json.dumps({'os-reset_status': {"status": status}})
-        resp, body = self.post('snapshots/%s/action' % snapshot_id, post_body)
-        self.expected_success(202, resp.status)
-        return rest_client.ResponseBody(resp, body)
-
-    def update_snapshot_status(self, snapshot_id, **kwargs):
-        """Update the specified snapshot's status."""
-        # TODO(gmann): api-site doesn't contain doc ref
-        # for this API. After fixing the api-site, we need to
-        # add the link here.
-        # Bug https://bugs.launchpad.net/openstack-api-site/+bug/1532645
-
-        post_body = json.dumps({'os-update_snapshot_status': kwargs})
-        url = 'snapshots/%s/action' % snapshot_id
-        resp, body = self.post(url, post_body)
-        self.expected_success(202, resp.status)
-        return rest_client.ResponseBody(resp, body)
-
-    def create_snapshot_metadata(self, snapshot_id, metadata):
-        """Create metadata for the snapshot.
-
-        For a full list of available parameters, please refer to the official
-        API reference:
-        http://developer.openstack.org/api-ref/block-storage/v2/#create-snapshot-metadata
-        """
-        put_body = json.dumps({'metadata': metadata})
-        url = "snapshots/%s/metadata" % snapshot_id
-        resp, body = self.post(url, put_body)
-        body = json.loads(body)
-        self.expected_success(200, resp.status)
-        return rest_client.ResponseBody(resp, body)
-
-    def show_snapshot_metadata(self, snapshot_id):
-        """Get metadata of the snapshot.
-
-        For a full list of available parameters, please refer to the official
-        API reference:
-        http://developer.openstack.org/api-ref/block-storage/v2/#show-snapshot-metadata
-        """
-        url = "snapshots/%s/metadata" % snapshot_id
-        resp, body = self.get(url)
-        body = json.loads(body)
-        self.expected_success(200, resp.status)
-        return rest_client.ResponseBody(resp, body)
-
-    def update_snapshot_metadata(self, snapshot_id, **kwargs):
-        """Update metadata for the snapshot.
-
-        For a full list of available parameters, please refer to the official
-        API reference:
-        http://developer.openstack.org/api-ref/block-storage/v2/#update-snapshot-metadata
-        """
-        put_body = json.dumps(kwargs)
-        url = "snapshots/%s/metadata" % snapshot_id
-        resp, body = self.put(url, put_body)
-        body = json.loads(body)
-        self.expected_success(200, resp.status)
-        return rest_client.ResponseBody(resp, body)
-
-    def show_snapshot_metadata_item(self, snapshot_id, id):
-        """Show metadata item for the snapshot."""
-        url = "snapshots/%s/metadata/%s" % (snapshot_id, id)
-        resp, body = self.get(url)
-        body = json.loads(body)
-        self.expected_success(200, resp.status)
-        return rest_client.ResponseBody(resp, body)
-
-    def update_snapshot_metadata_item(self, snapshot_id, id, **kwargs):
-        """Update metadata item for the snapshot."""
-        # TODO(piyush): Current api-site doesn't contain this API description.
-        # After fixing the api-site, we need to fix here also for putting the
-        # link to api-site.
-        # LP: https://bugs.launchpad.net/openstack-api-site/+bug/1529064
-        put_body = json.dumps(kwargs)
-        url = "snapshots/%s/metadata/%s" % (snapshot_id, id)
-        resp, body = self.put(url, put_body)
-        body = json.loads(body)
-        self.expected_success(200, resp.status)
-        return rest_client.ResponseBody(resp, body)
-
-    def delete_snapshot_metadata_item(self, snapshot_id, id):
-        """Delete metadata item for the snapshot."""
-        url = "snapshots/%s/metadata/%s" % (snapshot_id, id)
-        resp, body = self.delete(url)
-        self.expected_success(200, resp.status)
-        return rest_client.ResponseBody(resp, body)
-
-    def force_delete_snapshot(self, snapshot_id):
-        """Force Delete Snapshot."""
-        post_body = json.dumps({'os-force_delete': {}})
-        resp, body = self.post('snapshots/%s/action' % snapshot_id, post_body)
-        self.expected_success(202, resp.status)
-        return rest_client.ResponseBody(resp, body)
-
-    def unmanage_snapshot(self, snapshot_id):
-        """Unmanage a snapshot."""
-        post_body = json.dumps({'os-unmanage': {}})
-        url = 'snapshots/%s/action' % (snapshot_id)
-        resp, body = self.post(url, post_body)
-        self.expected_success(202, resp.status)
-        return rest_client.ResponseBody(resp, body)
+SnapshotsClient = moves.moved_class(
+    snapshots_client.SnapshotsClient, 'SnapshotsClient',
+    __name__, version="Rocky", removal_version='?')
diff --git a/tempest/lib/services/volume/v2/transfers_client.py b/tempest/lib/services/volume/v2/transfers_client.py
index 2dfbe7b..701d0ae 100644
--- a/tempest/lib/services/volume/v2/transfers_client.py
+++ b/tempest/lib/services/volume/v2/transfers_client.py
@@ -12,72 +12,11 @@
 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 #    License for the specific language governing permissions and limitations
 #    under the License.
+from debtcollector import moves
 
-from oslo_serialization import jsonutils as json
-from six.moves.urllib import parse as urllib
-
-from tempest.lib.common import rest_client
+from tempest.lib.services.volume.v3 import transfers_client
 
 
-class TransfersClient(rest_client.RestClient):
-    """Client class to send CRUD Volume Transfer V2 API requests"""
-    api_version = "v2"
-
-    def create_volume_transfer(self, **kwargs):
-        """Create a volume transfer.
-
-        For a full list of available parameters, please refer to the official
-        API reference:
-        https://developer.openstack.org/api-ref/block-storage/v2/#create-volume-transfer
-        """
-        post_body = json.dumps({'transfer': kwargs})
-        resp, body = self.post('os-volume-transfer', post_body)
-        body = json.loads(body)
-        self.expected_success(202, resp.status)
-        return rest_client.ResponseBody(resp, body)
-
-    def show_volume_transfer(self, transfer_id):
-        """Returns the details of a volume transfer."""
-        url = "os-volume-transfer/%s" % transfer_id
-        resp, body = self.get(url)
-        body = json.loads(body)
-        self.expected_success(200, resp.status)
-        return rest_client.ResponseBody(resp, body)
-
-    def list_volume_transfers(self, detail=False, **params):
-        """List all the volume transfers created.
-
-        For a full list of available parameters, please refer to the official
-        API reference:
-        https://developer.openstack.org/api-ref/block-storage/v2/#list-volume-transfers
-        https://developer.openstack.org/api-ref/block-storage/v2/#list-volume-transfers-with-details
-        """
-        url = 'os-volume-transfer'
-        if detail:
-            url += '/detail'
-        if params:
-            url += '?%s' % urllib.urlencode(params)
-        resp, body = self.get(url)
-        body = json.loads(body)
-        self.expected_success(200, resp.status)
-        return rest_client.ResponseBody(resp, body)
-
-    def delete_volume_transfer(self, transfer_id):
-        """Delete a volume transfer."""
-        resp, body = self.delete("os-volume-transfer/%s" % transfer_id)
-        self.expected_success(202, resp.status)
-        return rest_client.ResponseBody(resp, body)
-
-    def accept_volume_transfer(self, transfer_id, **kwargs):
-        """Accept a volume transfer.
-
-        For a full list of available parameters, please refer to the official
-        API reference:
-        https://developer.openstack.org/api-ref/block-storage/v2/#accept-volume-transfer
-        """
-        url = 'os-volume-transfer/%s/accept' % transfer_id
-        post_body = json.dumps({'accept': kwargs})
-        resp, body = self.post(url, post_body)
-        body = json.loads(body)
-        self.expected_success(202, resp.status)
-        return rest_client.ResponseBody(resp, body)
+TransfersClient = moves.moved_class(
+    transfers_client.TransfersClient, 'TransfersClient',
+    __name__, version="Rocky", removal_version='?')
diff --git a/tempest/lib/services/volume/v2/types_client.py b/tempest/lib/services/volume/v2/types_client.py
index af4fd8c..8457f91 100644
--- a/tempest/lib/services/volume/v2/types_client.py
+++ b/tempest/lib/services/volume/v2/types_client.py
@@ -12,194 +12,11 @@
 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 #    License for the specific language governing permissions and limitations
 #    under the License.
+from debtcollector import moves
 
-from oslo_serialization import jsonutils as json
-from six.moves.urllib import parse as urllib
-
-from tempest.lib.common import rest_client
-from tempest.lib import exceptions as lib_exc
+from tempest.lib.services.volume.v3 import types_client
 
 
-class TypesClient(rest_client.RestClient):
-    """Client class to send CRUD Volume V2 API requests"""
-    api_version = "v2"
-
-    def is_resource_deleted(self, id):
-        try:
-            self.show_volume_type(id)
-        except lib_exc.NotFound:
-            return True
-        return False
-
-    @property
-    def resource_type(self):
-        """Returns the primary type of resource this client works with."""
-        return 'volume-type'
-
-    def list_volume_types(self, **params):
-        """List all the volume_types created.
-
-        For a full list of available parameters, please refer to the official
-        API reference:
-        https://developer.openstack.org/api-ref/block-storage/v2/#list-all-volume-types-for-v2
-        """
-        url = 'types'
-        if params:
-            url += '?%s' % urllib.urlencode(params)
-
-        resp, body = self.get(url)
-        body = json.loads(body)
-        self.expected_success(200, resp.status)
-        return rest_client.ResponseBody(resp, body)
-
-    def show_volume_type(self, volume_type_id):
-        """Returns the details of a single volume_type.
-
-        For a full list of available parameters, please refer to the official
-        API reference:
-        https://developer.openstack.org/api-ref/block-storage/v2/#show-volume-type-details-for-v2
-        """
-        url = "types/%s" % volume_type_id
-        resp, body = self.get(url)
-        body = json.loads(body)
-        self.expected_success(200, resp.status)
-        return rest_client.ResponseBody(resp, body)
-
-    def create_volume_type(self, **kwargs):
-        """Create volume type.
-
-        For a full list of available parameters, please refer to the official
-        API reference:
-        https://developer.openstack.org/api-ref/block-storage/v2/#create-volume-type-for-v2
-        """
-        post_body = json.dumps({'volume_type': kwargs})
-        resp, body = self.post('types', post_body)
-        body = json.loads(body)
-        self.expected_success(200, resp.status)
-        return rest_client.ResponseBody(resp, body)
-
-    def delete_volume_type(self, volume_type_id):
-        """Deletes the Specified Volume_type.
-
-        For a full list of available parameters, please refer to the official
-        API reference:
-        http://developer.openstack.org/api-ref/block-storage/v2/#delete-volume-type
-        """
-        resp, body = self.delete("types/%s" % volume_type_id)
-        self.expected_success(202, resp.status)
-        return rest_client.ResponseBody(resp, body)
-
-    def list_volume_types_extra_specs(self, volume_type_id, **params):
-        """List all the volume_types extra specs created.
-
-        TODO: Current api-site doesn't contain this API description.
-        After fixing the api-site, we need to fix here also for putting
-        the link to api-site.
-        """
-        url = 'types/%s/extra_specs' % volume_type_id
-        if params:
-            url += '?%s' % urllib.urlencode(params)
-
-        resp, body = self.get(url)
-        body = json.loads(body)
-        self.expected_success(200, resp.status)
-        return rest_client.ResponseBody(resp, body)
-
-    def show_volume_type_extra_specs(self, volume_type_id, extra_specs_name):
-        """Returns the details of a single volume_type extra spec."""
-        url = "types/%s/extra_specs/%s" % (volume_type_id, extra_specs_name)
-        resp, body = self.get(url)
-        body = json.loads(body)
-        self.expected_success(200, resp.status)
-        return rest_client.ResponseBody(resp, body)
-
-    def create_volume_type_extra_specs(self, volume_type_id, extra_specs):
-        """Creates a new Volume_type extra spec.
-
-        volume_type_id: Id of volume_type.
-        extra_specs: A dictionary of values to be used as extra_specs.
-        """
-        url = "types/%s/extra_specs" % volume_type_id
-        post_body = json.dumps({'extra_specs': extra_specs})
-        resp, body = self.post(url, post_body)
-        body = json.loads(body)
-        self.expected_success(200, resp.status)
-        return rest_client.ResponseBody(resp, body)
-
-    def delete_volume_type_extra_specs(self, volume_type_id, extra_spec_name):
-        """Deletes the Specified Volume_type extra spec."""
-        resp, body = self.delete("types/%s/extra_specs/%s" % (
-            volume_type_id, extra_spec_name))
-        self.expected_success(202, resp.status)
-        return rest_client.ResponseBody(resp, body)
-
-    def update_volume_type(self, volume_type_id, **kwargs):
-        """Updates volume type name, description, and/or is_public.
-
-        For a full list of available parameters, please refer to the official
-        API reference:
-        http://developer.openstack.org/api-ref/block-storage/v2/#update-volume-type
-        """
-        put_body = json.dumps({'volume_type': kwargs})
-        resp, body = self.put('types/%s' % volume_type_id, put_body)
-        body = json.loads(body)
-        self.expected_success(200, resp.status)
-        return rest_client.ResponseBody(resp, body)
-
-    def update_volume_type_extra_specs(self, volume_type_id, extra_spec_name,
-                                       extra_specs):
-        """Update a volume_type extra spec.
-
-        volume_type_id: Id of volume_type.
-        extra_spec_name: Name of the extra spec to be updated.
-        extra_spec: A dictionary of with key as extra_spec_name and the
-                     updated value.
-        For a full list of available parameters, please refer to the official
-        API reference:
-        http://developer.openstack.org/api-ref/block-storage/v2/#update-extra-specs-for-a-volume-type
-        """
-        url = "types/%s/extra_specs/%s" % (volume_type_id, extra_spec_name)
-        put_body = json.dumps(extra_specs)
-        resp, body = self.put(url, put_body)
-        body = json.loads(body)
-        self.expected_success(200, resp.status)
-        return rest_client.ResponseBody(resp, body)
-
-    def add_type_access(self, volume_type_id, **kwargs):
-        """Adds volume type access for the given project.
-
-        For a full list of available parameters, please refer to the official
-        API reference:
-        http://developer.openstack.org/api-ref/block-storage/v2/#add-private-volume-type-access
-        """
-        post_body = json.dumps({'addProjectAccess': kwargs})
-        url = 'types/%s/action' % volume_type_id
-        resp, body = self.post(url, post_body)
-        self.expected_success(202, resp.status)
-        return rest_client.ResponseBody(resp, body)
-
-    def remove_type_access(self, volume_type_id, **kwargs):
-        """Removes volume type access for the given project.
-
-        For a full list of available parameters, please refer to the official
-        API reference:
-        http://developer.openstack.org/api-ref/block-storage/v2/#remove-private-volume-type-access
-        """
-        post_body = json.dumps({'removeProjectAccess': kwargs})
-        url = 'types/%s/action' % volume_type_id
-        resp, body = self.post(url, post_body)
-        self.expected_success(202, resp.status)
-        return rest_client.ResponseBody(resp, body)
-
-    def list_type_access(self, volume_type_id):
-        """Print access information about the given volume type.
-
-        For a full list of available parameters, please refer to the official
-        API reference:
-        http://developer.openstack.org/api-ref/block-storage/v2/#list-private-volume-type-access-details
-        """
-        url = 'types/%s/os-volume-type-access' % volume_type_id
-        resp, body = self.get(url)
-        body = json.loads(body)
-        self.expected_success(200, resp.status)
-        return rest_client.ResponseBody(resp, body)
+TypesClient = moves.moved_class(
+    types_client.TypesClient, 'TypesClient',
+    __name__, version="Rocky", removal_version='?')
diff --git a/tempest/lib/services/volume/v2/volume_manage_client.py b/tempest/lib/services/volume/v2/volume_manage_client.py
index 12f4240..0669326 100644
--- a/tempest/lib/services/volume/v2/volume_manage_client.py
+++ b/tempest/lib/services/volume/v2/volume_manage_client.py
@@ -12,26 +12,11 @@
 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 #    License for the specific language governing permissions and limitations
 #    under the License.
+from debtcollector import moves
 
-from oslo_serialization import jsonutils as json
-
-from tempest.lib.common import rest_client
+from tempest.lib.services.volume.v3 import volume_manage_client
 
 
-class VolumeManageClient(rest_client.RestClient):
-    """Volume manage V2 client."""
-
-    api_version = "v2"
-
-    def manage_volume(self, **kwargs):
-        """Manage existing volume.
-
-        For a full list of available parameters, please refer to the official
-        API reference:
-        https://developer.openstack.org/api-ref/block-storage/v2/#manage-existing-volume
-        """
-        post_body = json.dumps({'volume': kwargs})
-        resp, body = self.post('os-volume-manage', post_body)
-        self.expected_success(202, resp.status)
-        body = json.loads(body)
-        return rest_client.ResponseBody(resp, body)
+VolumeManageClient = moves.moved_class(
+    volume_manage_client.VolumeManageClient, 'VolumeManageClient',
+    __name__, version="Rocky", removal_version='?')
diff --git a/tempest/lib/services/volume/v2/volumes_client.py b/tempest/lib/services/volume/v2/volumes_client.py
index da3f2b5..f5f9e6e 100644
--- a/tempest/lib/services/volume/v2/volumes_client.py
+++ b/tempest/lib/services/volume/v2/volumes_client.py
@@ -12,341 +12,11 @@
 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 #    License for the specific language governing permissions and limitations
 #    under the License.
+from debtcollector import moves
 
-from oslo_serialization import jsonutils as json
-import six
-from six.moves.urllib import parse as urllib
-
-from tempest.lib.common import rest_client
-from tempest.lib import exceptions as lib_exc
-from tempest.lib.services.volume import base_client
+from tempest.lib.services.volume.v3 import volumes_client
 
 
-class VolumesClient(base_client.BaseClient):
-    """Client class to send CRUD Volume V2 API requests"""
-    api_version = "v2"
-
-    def _prepare_params(self, params):
-        """Prepares params for use in get or _ext_get methods.
-
-        If params is a string it will be left as it is, but if it's not it will
-        be urlencoded.
-        """
-        if isinstance(params, six.string_types):
-            return params
-        return urllib.urlencode(params)
-
-    def list_volumes(self, detail=False, params=None):
-        """List all the volumes created.
-
-        Params can be a string (must be urlencoded) or a dictionary.
-        For a full list of available parameters, please refer to the official
-        API reference:
-        http://developer.openstack.org/api-ref/block-storage/v2/#list-volumes-with-details
-        http://developer.openstack.org/api-ref/block-storage/v2/#list-volumes
-        """
-        url = 'volumes'
-        if detail:
-            url += '/detail'
-        if params:
-            url += '?%s' % self._prepare_params(params)
-
-        resp, body = self.get(url)
-        body = json.loads(body)
-        self.expected_success(200, resp.status)
-        return rest_client.ResponseBody(resp, body)
-
-    def show_volume(self, volume_id):
-        """Returns the details of a single volume."""
-        url = "volumes/%s" % volume_id
-        resp, body = self.get(url)
-        body = json.loads(body)
-        self.expected_success(200, resp.status)
-        return rest_client.ResponseBody(resp, body)
-
-    def create_volume(self, **kwargs):
-        """Creates a new Volume.
-
-        For a full list of available parameters, please refer to the official
-        API reference:
-        http://developer.openstack.org/api-ref/block-storage/v2/#create-volume
-        """
-        post_body = json.dumps({'volume': kwargs})
-        resp, body = self.post('volumes', post_body)
-        body = json.loads(body)
-        self.expected_success(202, resp.status)
-        return rest_client.ResponseBody(resp, body)
-
-    def update_volume(self, volume_id, **kwargs):
-        """Updates the Specified Volume.
-
-        For a full list of available parameters, please refer to the official
-        API reference:
-        http://developer.openstack.org/api-ref/block-storage/v2/#update-volume
-        """
-        put_body = json.dumps({'volume': kwargs})
-        resp, body = self.put('volumes/%s' % volume_id, put_body)
-        body = json.loads(body)
-        self.expected_success(200, resp.status)
-        return rest_client.ResponseBody(resp, body)
-
-    def delete_volume(self, volume_id, **params):
-        """Deletes the Specified Volume.
-
-        For a full list of available parameters, please refer to the official
-        API reference:
-        https://developer.openstack.org/api-ref/block-storage/v2/#delete-volume
-        """
-        url = 'volumes/%s' % volume_id
-        if params:
-            url += '?%s' % urllib.urlencode(params)
-        resp, body = self.delete(url)
-        self.expected_success(202, resp.status)
-        return rest_client.ResponseBody(resp, body)
-
-    def upload_volume(self, volume_id, **kwargs):
-        """Uploads a volume in Glance."""
-        post_body = json.dumps({'os-volume_upload_image': kwargs})
-        url = 'volumes/%s/action' % (volume_id)
-        resp, body = self.post(url, post_body)
-        body = json.loads(body)
-        self.expected_success(202, resp.status)
-        return rest_client.ResponseBody(resp, body)
-
-    def attach_volume(self, volume_id, **kwargs):
-        """Attaches a volume to a given instance on a given mountpoint.
-
-        For a full list of available parameters, please refer to the official
-        API reference:
-        http://developer.openstack.org/api-ref/block-storage/v2/#attach-volume-to-server
-        """
-        post_body = json.dumps({'os-attach': kwargs})
-        url = 'volumes/%s/action' % (volume_id)
-        resp, body = self.post(url, post_body)
-        self.expected_success(202, resp.status)
-        return rest_client.ResponseBody(resp, body)
-
-    def set_bootable_volume(self, volume_id, **kwargs):
-        """Set a bootable flag for a volume - true or false.
-
-        For a full list of available parameters, please refer to the official
-        API reference:
-        http://developer.openstack.org/api-ref/block-storage/v2/#update-volume-bootable-status
-        """
-        post_body = json.dumps({'os-set_bootable': kwargs})
-        url = 'volumes/%s/action' % (volume_id)
-        resp, body = self.post(url, post_body)
-        self.expected_success(200, resp.status)
-        return rest_client.ResponseBody(resp, body)
-
-    def detach_volume(self, volume_id):
-        """Detaches a volume from an instance."""
-        post_body = json.dumps({'os-detach': {}})
-        url = 'volumes/%s/action' % (volume_id)
-        resp, body = self.post(url, post_body)
-        self.expected_success(202, resp.status)
-        return rest_client.ResponseBody(resp, body)
-
-    def reserve_volume(self, volume_id):
-        """Reserves a volume."""
-        post_body = json.dumps({'os-reserve': {}})
-        url = 'volumes/%s/action' % (volume_id)
-        resp, body = self.post(url, post_body)
-        self.expected_success(202, resp.status)
-        return rest_client.ResponseBody(resp, body)
-
-    def unreserve_volume(self, volume_id):
-        """Restore a reserved volume ."""
-        post_body = json.dumps({'os-unreserve': {}})
-        url = 'volumes/%s/action' % (volume_id)
-        resp, body = self.post(url, post_body)
-        self.expected_success(202, resp.status)
-        return rest_client.ResponseBody(resp, body)
-
-    def is_resource_deleted(self, id):
-        """Check the specified resource is deleted or not.
-
-        :param id: A checked resource id
-        :raises lib_exc.DeleteErrorException: If the specified resource is on
-        the status the delete was failed.
-        """
-        try:
-            volume = self.show_volume(id)
-        except lib_exc.NotFound:
-            return True
-        if volume["volume"]["status"] == "error_deleting":
-            raise lib_exc.DeleteErrorException(resource_id=id)
-        return False
-
-    @property
-    def resource_type(self):
-        """Returns the primary type of resource this client works with."""
-        return 'volume'
-
-    def extend_volume(self, volume_id, **kwargs):
-        """Extend a volume.
-
-        For a full list of available parameters, please refer to the official
-        API reference:
-        http://developer.openstack.org/api-ref/block-storage/v2/#extend-volume-size
-        """
-        post_body = json.dumps({'os-extend': kwargs})
-        url = 'volumes/%s/action' % (volume_id)
-        resp, body = self.post(url, post_body)
-        self.expected_success(202, resp.status)
-        return rest_client.ResponseBody(resp, body)
-
-    def reset_volume_status(self, volume_id, **kwargs):
-        """Reset the Specified Volume's Status.
-
-        For a full list of available parameters, please refer to the official
-        API reference:
-        http://developer.openstack.org/api-ref/block-storage/v2/#reset-volume-statuses
-        """
-        post_body = json.dumps({'os-reset_status': kwargs})
-        resp, body = self.post('volumes/%s/action' % volume_id, post_body)
-        self.expected_success(202, resp.status)
-        return rest_client.ResponseBody(resp, body)
-
-    def update_volume_readonly(self, volume_id, **kwargs):
-        """Update the Specified Volume readonly."""
-        post_body = json.dumps({'os-update_readonly_flag': kwargs})
-        url = 'volumes/%s/action' % (volume_id)
-        resp, body = self.post(url, post_body)
-        self.expected_success(202, resp.status)
-        return rest_client.ResponseBody(resp, body)
-
-    def force_delete_volume(self, volume_id):
-        """Force Delete Volume."""
-        post_body = json.dumps({'os-force_delete': {}})
-        resp, body = self.post('volumes/%s/action' % volume_id, post_body)
-        self.expected_success(202, resp.status)
-        return rest_client.ResponseBody(resp, body)
-
-    def create_volume_metadata(self, volume_id, metadata):
-        """Create metadata for the volume.
-
-        For a full list of available parameters, please refer to the official
-        API reference:
-        http://developer.openstack.org/api-ref/block-storage/v2/#create-volume-metadata
-        """
-        put_body = json.dumps({'metadata': metadata})
-        url = "volumes/%s/metadata" % volume_id
-        resp, body = self.post(url, put_body)
-        body = json.loads(body)
-        self.expected_success(200, resp.status)
-        return rest_client.ResponseBody(resp, body)
-
-    def show_volume_metadata(self, volume_id):
-        """Get metadata of the volume."""
-        url = "volumes/%s/metadata" % volume_id
-        resp, body = self.get(url)
-        body = json.loads(body)
-        self.expected_success(200, resp.status)
-        return rest_client.ResponseBody(resp, body)
-
-    def update_volume_metadata(self, volume_id, metadata):
-        """Update metadata for the volume.
-
-        For a full list of available parameters, please refer to the official
-        API reference:
-        http://developer.openstack.org/api-ref/block-storage/v2/#update-volume-metadata
-        """
-        put_body = json.dumps({'metadata': metadata})
-        url = "volumes/%s/metadata" % volume_id
-        resp, body = self.put(url, put_body)
-        body = json.loads(body)
-        self.expected_success(200, resp.status)
-        return rest_client.ResponseBody(resp, body)
-
-    def show_volume_metadata_item(self, volume_id, id):
-        """Show metadata item for the volume."""
-        url = "volumes/%s/metadata/%s" % (volume_id, id)
-        resp, body = self.get(url)
-        body = json.loads(body)
-        self.expected_success(200, resp.status)
-        return rest_client.ResponseBody(resp, body)
-
-    def update_volume_metadata_item(self, volume_id, id, meta_item):
-        """Update metadata item for the volume."""
-        put_body = json.dumps({'meta': meta_item})
-        url = "volumes/%s/metadata/%s" % (volume_id, id)
-        resp, body = self.put(url, put_body)
-        body = json.loads(body)
-        self.expected_success(200, resp.status)
-        return rest_client.ResponseBody(resp, body)
-
-    def delete_volume_metadata_item(self, volume_id, id):
-        """Delete metadata item for the volume."""
-        url = "volumes/%s/metadata/%s" % (volume_id, id)
-        resp, body = self.delete(url)
-        self.expected_success(200, resp.status)
-        return rest_client.ResponseBody(resp, body)
-
-    def retype_volume(self, volume_id, **kwargs):
-        """Updates volume with new volume type.
-
-        For a full list of available parameters, please refer to the official
-        API reference:
-        https://developer.openstack.org/api-ref/block-storage/v2/#retype-volume
-        """
-        post_body = json.dumps({'os-retype': kwargs})
-        resp, body = self.post('volumes/%s/action' % volume_id, post_body)
-        self.expected_success(202, resp.status)
-        return rest_client.ResponseBody(resp, body)
-
-    def force_detach_volume(self, volume_id, **kwargs):
-        """Force detach a volume.
-
-        For a full list of available parameters, please refer to the official
-        API reference:
-        https://developer.openstack.org/api-ref/block-storage/v2/#force-detach-volume
-        """
-        post_body = json.dumps({'os-force_detach': kwargs})
-        url = 'volumes/%s/action' % volume_id
-        resp, body = self.post(url, post_body)
-        self.expected_success(202, resp.status)
-        return rest_client.ResponseBody(resp, body)
-
-    def update_volume_image_metadata(self, volume_id, **kwargs):
-        """Update image metadata for the volume.
-
-        For a full list of available parameters, please refer to the official
-        API reference:
-        http://developer.openstack.org/api-ref/block-storage/v2/#set-image-metadata-for-volume
-        """
-        post_body = json.dumps({'os-set_image_metadata': {'metadata': kwargs}})
-        url = "volumes/%s/action" % (volume_id)
-        resp, body = self.post(url, post_body)
-        body = json.loads(body)
-        self.expected_success(200, resp.status)
-        return rest_client.ResponseBody(resp, body)
-
-    def delete_volume_image_metadata(self, volume_id, key_name):
-        """Delete image metadata item for the volume."""
-        post_body = json.dumps({'os-unset_image_metadata': {'key': key_name}})
-        url = "volumes/%s/action" % (volume_id)
-        resp, body = self.post(url, post_body)
-        self.expected_success(200, resp.status)
-        return rest_client.ResponseBody(resp, body)
-
-    def show_volume_image_metadata(self, volume_id):
-        """Show image metadata for the volume."""
-        post_body = json.dumps({'os-show_image_metadata': {}})
-        url = "volumes/%s/action" % volume_id
-        resp, body = self.post(url, post_body)
-        body = json.loads(body)
-        self.expected_success(200, resp.status)
-        return rest_client.ResponseBody(resp, body)
-
-    def unmanage_volume(self, volume_id):
-        """Unmanage volume.
-
-        For a full list of available parameters, please refer to the official
-        API reference:
-        https://developer.openstack.org/api-ref/block-storage/v2/#unmanage-volume
-        """
-        post_body = json.dumps({'os-unmanage': {}})
-        resp, body = self.post('volumes/%s/action' % volume_id, post_body)
-        self.expected_success(202, resp.status)
-        return rest_client.ResponseBody(resp, body)
+VolumesClient = moves.moved_class(
+    volumes_client.VolumesClient, 'VolumesClient',
+    __name__, version="Rocky", removal_version='?')
diff --git a/tempest/lib/services/volume/v3/__init__.py b/tempest/lib/services/volume/v3/__init__.py
index 2d85553..a1b7de3 100644
--- a/tempest/lib/services/volume/v3/__init__.py
+++ b/tempest/lib/services/volume/v3/__init__.py
@@ -11,19 +11,44 @@
 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 # License for the specific language governing permissions and limitations under
 # the License.
-
+from tempest.lib.services.volume.v3.availability_zone_client \
+    import AvailabilityZoneClient
 from tempest.lib.services.volume.v3.backups_client import BackupsClient
 from tempest.lib.services.volume.v3.base_client import BaseClient
+from tempest.lib.services.volume.v3.capabilities_client import \
+    CapabilitiesClient
+from tempest.lib.services.volume.v3.encryption_types_client import \
+    EncryptionTypesClient
+from tempest.lib.services.volume.v3.extensions_client import ExtensionsClient
 from tempest.lib.services.volume.v3.group_snapshots_client import \
     GroupSnapshotsClient
 from tempest.lib.services.volume.v3.group_types_client import GroupTypesClient
 from tempest.lib.services.volume.v3.groups_client import GroupsClient
+from tempest.lib.services.volume.v3.hosts_client import HostsClient
+from tempest.lib.services.volume.v3.limits_client import LimitsClient
 from tempest.lib.services.volume.v3.messages_client import MessagesClient
+from tempest.lib.services.volume.v3.qos_client import QosSpecsClient
+from tempest.lib.services.volume.v3.quota_classes_client import \
+    QuotaClassesClient
+from tempest.lib.services.volume.v3.quotas_client import QuotasClient
+from tempest.lib.services.volume.v3.scheduler_stats_client import \
+    SchedulerStatsClient
+from tempest.lib.services.volume.v3.services_client import ServicesClient
+from tempest.lib.services.volume.v3.snapshot_manage_client import \
+    SnapshotManageClient
 from tempest.lib.services.volume.v3.snapshots_client import SnapshotsClient
+from tempest.lib.services.volume.v3.transfers_client import TransfersClient
+from tempest.lib.services.volume.v3.types_client import TypesClient
 from tempest.lib.services.volume.v3.versions_client import VersionsClient
+from tempest.lib.services.volume.v3.volume_manage_client import \
+    VolumeManageClient
 from tempest.lib.services.volume.v3.volumes_client import VolumesClient
 
-__all__ = ['BackupsClient', 'BaseClient', 'GroupsClient',
-           'GroupSnapshotsClient', 'GroupTypesClient',
-           'MessagesClient', 'SnapshotsClient', 'VersionsClient',
-           'VolumesClient']
+__all__ = ['AvailabilityZoneClient', 'BackupsClient', 'BaseClient',
+           'CapabilitiesClient', 'EncryptionTypesClient', 'ExtensionsClient',
+           'GroupSnapshotsClient', 'GroupTypesClient', 'GroupsClient',
+           'HostsClient', 'LimitsClient', 'MessagesClient', 'QosSpecsClient',
+           'QuotaClassesClient', 'QuotasClient', 'SchedulerStatsClient',
+           'ServicesClient', 'SnapshotManageClient', 'SnapshotsClient',
+           'TransfersClient', 'TypesClient', 'VersionsClient',
+           'VolumeManageClient', 'VolumesClient']
diff --git a/tempest/lib/services/volume/v3/availability_zone_client.py b/tempest/lib/services/volume/v3/availability_zone_client.py
new file mode 100644
index 0000000..147e4c6
--- /dev/null
+++ b/tempest/lib/services/volume/v3/availability_zone_client.py
@@ -0,0 +1,27 @@
+# Copyright 2014 IBM Corp.
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from oslo_serialization import jsonutils as json
+
+from tempest.lib.common import rest_client
+
+
+class AvailabilityZoneClient(rest_client.RestClient):
+
+    def list_availability_zones(self):
+        resp, body = self.get('os-availability-zone')
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/volume/v3/backups_client.py b/tempest/lib/services/volume/v3/backups_client.py
index e742e39..10538b0 100644
--- a/tempest/lib/services/volume/v3/backups_client.py
+++ b/tempest/lib/services/volume/v3/backups_client.py
@@ -14,15 +14,30 @@
 #    under the License.
 
 from oslo_serialization import jsonutils as json
+from six.moves.urllib import parse as urllib
 
 from tempest.lib.common import rest_client
-from tempest.lib.services.volume.v2 import backups_client
+from tempest.lib import exceptions as lib_exc
+from tempest.lib.services.volume import base_client
 
 
-class BackupsClient(backups_client.BackupsClient):
+class BackupsClient(base_client.BaseClient):
     """Volume V3 Backups client"""
     api_version = "v3"
 
+    def create_backup(self, **kwargs):
+        """Creates a backup of volume.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/block-storage/v3/index.html#create-a-backup
+        """
+        post_body = json.dumps({'backup': kwargs})
+        resp, body = self.post('backups', post_body)
+        body = json.loads(body)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
     def update_backup(self, backup_id, **kwargs):
         """Updates the specified volume backup.
 
@@ -35,3 +50,83 @@
         body = json.loads(body)
         self.expected_success(200, resp.status)
         return rest_client.ResponseBody(resp, body)
+
+    def restore_backup(self, backup_id, **kwargs):
+        """Restore volume from backup.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/block-storage/v3/index.html#restore-a-backup
+        """
+        post_body = json.dumps({'restore': kwargs})
+        resp, body = self.post('backups/%s/restore' % (backup_id), post_body)
+        body = json.loads(body)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_backup(self, backup_id):
+        """Delete a backup of volume."""
+        resp, body = self.delete('backups/%s' % backup_id)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_backup(self, backup_id):
+        """Returns the details of a single backup."""
+        url = "backups/%s" % backup_id
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def list_backups(self, detail=False, **params):
+        """List all the tenant's backups.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/block-storage/v3/index.html#list-backups-for-project
+        https://developer.openstack.org/api-ref/block-storage/v3/index.html#list-backups-with-detail
+        """
+        url = "backups"
+        if detail:
+            url += "/detail"
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def export_backup(self, backup_id):
+        """Export backup metadata record."""
+        url = "backups/%s/export_record" % backup_id
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def import_backup(self, **kwargs):
+        """Import backup metadata record."""
+        post_body = json.dumps({'backup-record': kwargs})
+        resp, body = self.post("backups/import_record", post_body)
+        body = json.loads(body)
+        self.expected_success(201, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def reset_backup_status(self, backup_id, status):
+        """Reset the specified backup's status."""
+        post_body = json.dumps({'os-reset_status': {"status": status}})
+        resp, body = self.post('backups/%s/action' % backup_id, post_body)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def is_resource_deleted(self, id):
+        try:
+            self.show_backup(id)
+        except lib_exc.NotFound:
+            return True
+        return False
+
+    @property
+    def resource_type(self):
+        """Returns the primary type of resource this client works with."""
+        return 'backup'
diff --git a/tempest/lib/services/volume/v3/capabilities_client.py b/tempest/lib/services/volume/v3/capabilities_client.py
new file mode 100644
index 0000000..7ebcd69
--- /dev/null
+++ b/tempest/lib/services/volume/v3/capabilities_client.py
@@ -0,0 +1,34 @@
+# Copyright 2016 Red Hat, Inc.
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from oslo_serialization import jsonutils as json
+
+from tempest.lib.common import rest_client
+
+
+class CapabilitiesClient(rest_client.RestClient):
+
+    def show_backend_capabilities(self, host):
+        """Shows capabilities for a storage back end.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/block-storage/v2/index.html#show-back-end-capabilities
+        """
+        url = 'capabilities/%s' % host
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/volume/v3/encryption_types_client.py b/tempest/lib/services/volume/v3/encryption_types_client.py
new file mode 100644
index 0000000..7443a87
--- /dev/null
+++ b/tempest/lib/services/volume/v3/encryption_types_client.py
@@ -0,0 +1,90 @@
+# Copyright 2012 OpenStack Foundation
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from oslo_serialization import jsonutils as json
+
+from tempest.lib.common import rest_client
+from tempest.lib import exceptions as lib_exc
+
+
+class EncryptionTypesClient(rest_client.RestClient):
+
+    def is_resource_deleted(self, id):
+        try:
+            body = self.show_encryption_type(id)
+            if not body:
+                return True
+        except lib_exc.NotFound:
+            return True
+        return False
+
+    @property
+    def resource_type(self):
+        """Returns the primary type of resource this client works with."""
+        return 'encryption-type'
+
+    def show_encryption_type(self, volume_type_id):
+        """Get the volume encryption type for the specified volume type.
+
+        volume_type_id: Id of volume_type.
+        """
+        url = "/types/%s/encryption" % volume_type_id
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_encryption_specs_item(self, volume_type_id, key):
+        """Get the encryption specs item for the specified volume type."""
+        url = "/types/%s/encryption/%s" % (volume_type_id, key)
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def create_encryption_type(self, volume_type_id, **kwargs):
+        """Create encryption type.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/block-storage/v3/index.html#create-an-encryption-type
+        """
+        url = "/types/%s/encryption" % volume_type_id
+        post_body = json.dumps({'encryption': kwargs})
+        resp, body = self.post(url, post_body)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_encryption_type(self, volume_type_id):
+        """Delete the encryption type for the specified volume-type."""
+        resp, body = self.delete(
+            "/types/%s/encryption/provider" % volume_type_id)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_encryption_type(self, volume_type_id, **kwargs):
+        """Update an encryption type for an existing volume type.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/block-storage/v3/index.html#update-an-encryption-type
+        """
+        url = "/types/%s/encryption/provider" % volume_type_id
+        put_body = json.dumps({'encryption': kwargs})
+        resp, body = self.put(url, put_body)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/volume/v3/extensions_client.py b/tempest/lib/services/volume/v3/extensions_client.py
new file mode 100644
index 0000000..45b7a56
--- /dev/null
+++ b/tempest/lib/services/volume/v3/extensions_client.py
@@ -0,0 +1,29 @@
+# Copyright 2014 IBM Corp.
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from oslo_serialization import jsonutils as json
+
+from tempest.lib.common import rest_client
+
+
+class ExtensionsClient(rest_client.RestClient):
+    """Volume extensions client."""
+
+    def list_extensions(self):
+        url = 'extensions'
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/volume/v3/hosts_client.py b/tempest/lib/services/volume/v3/hosts_client.py
new file mode 100644
index 0000000..8b65805
--- /dev/null
+++ b/tempest/lib/services/volume/v3/hosts_client.py
@@ -0,0 +1,47 @@
+# Copyright 2014 OpenStack Foundation
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from oslo_serialization import jsonutils as json
+from six.moves.urllib import parse as urllib
+
+from tempest.lib.common import rest_client
+
+
+class HostsClient(rest_client.RestClient):
+    """Client class to send CRUD Volume API requests"""
+
+    def list_hosts(self, **params):
+        """Lists all hosts.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/block-storage/v3/index.html#list-all-hosts-for-a-project
+        """
+        url = 'os-hosts'
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_host(self, host_name):
+        """Show host details."""
+        url = 'os-hosts/%s' % host_name
+        resp, body = self.get(url)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/volume/v3/limits_client.py b/tempest/lib/services/volume/v3/limits_client.py
new file mode 100644
index 0000000..9500254
--- /dev/null
+++ b/tempest/lib/services/volume/v3/limits_client.py
@@ -0,0 +1,30 @@
+# Copyright 2016 Red Hat, Inc.
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from oslo_serialization import jsonutils as json
+
+from tempest.lib.common import rest_client
+
+
+class LimitsClient(rest_client.RestClient):
+    """Volume limits client."""
+
+    def show_limits(self):
+        """Returns the details of a volume absolute limits."""
+        url = "limits"
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/volume/v3/qos_client.py b/tempest/lib/services/volume/v3/qos_client.py
new file mode 100644
index 0000000..8f4d37f
--- /dev/null
+++ b/tempest/lib/services/volume/v3/qos_client.py
@@ -0,0 +1,131 @@
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from oslo_serialization import jsonutils as json
+
+from tempest.lib.common import rest_client
+from tempest.lib import exceptions as lib_exc
+
+
+class QosSpecsClient(rest_client.RestClient):
+    """Volume QoS client.
+
+       Client class to send CRUD QoS API requests
+    """
+
+    def is_resource_deleted(self, qos_id):
+        try:
+            self.show_qos(qos_id)
+        except lib_exc.NotFound:
+            return True
+        return False
+
+    @property
+    def resource_type(self):
+        """Returns the primary type of resource this client works with."""
+        return 'qos'
+
+    def create_qos(self, **kwargs):
+        """Create a QoS Specification.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/block-storage/v3/index.html#create-a-qos-specification
+        """
+        post_body = json.dumps({'qos_specs': kwargs})
+        resp, body = self.post('qos-specs', post_body)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_qos(self, qos_id, force=False):
+        """Delete the specified QoS specification."""
+        resp, body = self.delete(
+            "qos-specs/%s?force=%s" % (qos_id, force))
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def list_qos(self):
+        """List all the QoS specifications created."""
+        url = 'qos-specs'
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_qos(self, qos_id):
+        """Get the specified QoS specification."""
+        url = "qos-specs/%s" % qos_id
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def set_qos_key(self, qos_id, **kwargs):
+        """Set the specified keys/values of QoS specification.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/block-storage/v3/index.html#set-keys-in-a-qos-specification
+        """
+        put_body = json.dumps({"qos_specs": kwargs})
+        resp, body = self.put('qos-specs/%s' % qos_id, put_body)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def unset_qos_key(self, qos_id, keys):
+        """Unset the specified keys of QoS specification.
+
+        :param keys: keys to delete from the QoS specification.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/block-storage/v3/index.html#unset-keys-in-a-qos-specification
+        """
+        put_body = json.dumps({'keys': keys})
+        resp, body = self.put('qos-specs/%s/delete_keys' % qos_id, put_body)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def associate_qos(self, qos_id, vol_type_id):
+        """Associate the specified QoS with specified volume-type."""
+        url = "qos-specs/%s/associate" % qos_id
+        url += "?vol_type_id=%s" % vol_type_id
+        resp, body = self.get(url)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_association_qos(self, qos_id):
+        """Get the association of the specified QoS specification."""
+        url = "qos-specs/%s/associations" % qos_id
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def disassociate_qos(self, qos_id, vol_type_id):
+        """Disassociate the specified QoS with specified volume-type."""
+        url = "qos-specs/%s/disassociate" % qos_id
+        url += "?vol_type_id=%s" % vol_type_id
+        resp, body = self.get(url)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def disassociate_all_qos(self, qos_id):
+        """Disassociate the specified QoS with all associations."""
+        url = "qos-specs/%s/disassociate_all" % qos_id
+        resp, body = self.get(url)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/volume/v3/quota_classes_client.py b/tempest/lib/services/volume/v3/quota_classes_client.py
new file mode 100644
index 0000000..a8eb536
--- /dev/null
+++ b/tempest/lib/services/volume/v3/quota_classes_client.py
@@ -0,0 +1,49 @@
+# Copyright 2017 FiberHome Telecommunication Technologies CO.,LTD
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from oslo_serialization import jsonutils as json
+
+from tempest.lib.common import rest_client
+
+
+class QuotaClassesClient(rest_client.RestClient):
+    """Volume quota class client."""
+
+    def show_quota_class_set(self, quota_class_id):
+        """List quotas for a quota class.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/block-storage/v3/index.html#show-quota-classes-for-a-project
+        """
+        url = 'os-quota-class-sets/%s' % quota_class_id
+        resp, body = self.get(url)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_quota_class_set(self, quota_class_id, **kwargs):
+        """Update quotas for a quota class.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/block-storage/v3/index.html#update-quota-classes-for-a-project
+        """
+        url = 'os-quota-class-sets/%s' % quota_class_id
+        put_body = json.dumps({'quota_class_set': kwargs})
+        resp, body = self.put(url, put_body)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/volume/v3/quotas_client.py b/tempest/lib/services/volume/v3/quotas_client.py
new file mode 100644
index 0000000..538a915
--- /dev/null
+++ b/tempest/lib/services/volume/v3/quotas_client.py
@@ -0,0 +1,63 @@
+# Copyright 2014 OpenStack Foundation
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from oslo_serialization import jsonutils
+from six.moves.urllib import parse as urllib
+
+from tempest.lib.common import rest_client
+
+
+class QuotasClient(rest_client.RestClient):
+    """Client class to send CRUD Volume Quotas API requests"""
+
+    def show_default_quota_set(self, tenant_id):
+        """List the default volume quota set for a tenant."""
+
+        url = 'os-quota-sets/%s/defaults' % tenant_id
+        resp, body = self.get(url)
+        self.expected_success(200, resp.status)
+        body = jsonutils.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_quota_set(self, tenant_id, params=None):
+        """List the quota set for a tenant."""
+
+        url = 'os-quota-sets/%s' % tenant_id
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+
+        resp, body = self.get(url)
+        self.expected_success(200, resp.status)
+        body = jsonutils.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_quota_set(self, tenant_id, **kwargs):
+        """Updates quota set
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/block-storage/v3/index.html#update-quotas-for-a-project
+        """
+        put_body = jsonutils.dumps({'quota_set': kwargs})
+        resp, body = self.put('os-quota-sets/%s' % tenant_id, put_body)
+        self.expected_success(200, resp.status)
+        body = jsonutils.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_quota_set(self, tenant_id):
+        """Delete the tenant's quota set."""
+        resp, body = self.delete('os-quota-sets/%s' % tenant_id)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/volume/v3/scheduler_stats_client.py b/tempest/lib/services/volume/v3/scheduler_stats_client.py
new file mode 100644
index 0000000..9b80851
--- /dev/null
+++ b/tempest/lib/services/volume/v3/scheduler_stats_client.py
@@ -0,0 +1,36 @@
+# Copyright 2016 Red Hat, Inc.
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from oslo_serialization import jsonutils as json
+
+from tempest.lib.common import rest_client
+
+
+class SchedulerStatsClient(rest_client.RestClient):
+
+    def list_pools(self, detail=False):
+        """List all the volumes pools (hosts).
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/block-storage/v3/index.html#list-all-back-end-storage-pools
+        """
+        url = 'scheduler-stats/get_pools'
+        if detail:
+            url += '?detail=True'
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/volume/v3/services_client.py b/tempest/lib/services/volume/v3/services_client.py
new file mode 100644
index 0000000..09036a4
--- /dev/null
+++ b/tempest/lib/services/volume/v3/services_client.py
@@ -0,0 +1,33 @@
+# Copyright 2014 OpenStack Foundation
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from oslo_serialization import jsonutils as json
+from six.moves.urllib import parse as urllib
+
+from tempest.lib.common import rest_client
+
+
+class ServicesClient(rest_client.RestClient):
+    """Client class to send CRUD Volume API requests"""
+
+    def list_services(self, **params):
+        url = 'os-services'
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/volume/v3/snapshot_manage_client.py b/tempest/lib/services/volume/v3/snapshot_manage_client.py
new file mode 100644
index 0000000..43fd328
--- /dev/null
+++ b/tempest/lib/services/volume/v3/snapshot_manage_client.py
@@ -0,0 +1,31 @@
+# Copyright 2016 Red Hat, Inc.
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from oslo_serialization import jsonutils as json
+
+from tempest.lib.common import rest_client
+
+
+class SnapshotManageClient(rest_client.RestClient):
+    """Snapshot manage client."""
+
+    def manage_snapshot(self, **kwargs):
+        """Manage a snapshot."""
+        post_body = json.dumps({'snapshot': kwargs})
+        url = 'os-snapshot-manage'
+        resp, body = self.post(url, post_body)
+        self.expected_success(202, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/volume/v3/snapshots_client.py b/tempest/lib/services/volume/v3/snapshots_client.py
index 88c094f..298925a 100644
--- a/tempest/lib/services/volume/v3/snapshots_client.py
+++ b/tempest/lib/services/volume/v3/snapshots_client.py
@@ -13,9 +13,199 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest.lib.services.volume.v2 import snapshots_client
+from oslo_serialization import jsonutils as json
+from six.moves.urllib import parse as urllib
+
+from tempest.lib.common import rest_client
+from tempest.lib import exceptions as lib_exc
 
 
-class SnapshotsClient(snapshots_client.SnapshotsClient):
+class SnapshotsClient(rest_client.RestClient):
     """Client class to send CRUD Volume Snapshot V3 API requests."""
     api_version = "v3"
+    create_resp = 202
+
+    def list_snapshots(self, detail=False, **params):
+        """List all the snapshot.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/block-storage/v3/index.html#list-accessible-snapshots
+        https://developer.openstack.org/api-ref/block-storage/v3/index.html#list-snapshots-and-details
+        """
+        url = 'snapshots'
+        if detail:
+            url += '/detail'
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_snapshot(self, snapshot_id):
+        """Returns the details of a single snapshot.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/block-storage/v3/index.html#show-a-snapshot-s-details
+        """
+        url = "snapshots/%s" % snapshot_id
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def create_snapshot(self, **kwargs):
+        """Creates a new snapshot.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/block-storage/v3/index.html#create-a-snapshot
+        """
+        post_body = json.dumps({'snapshot': kwargs})
+        resp, body = self.post('snapshots', post_body)
+        body = json.loads(body)
+        self.expected_success(self.create_resp, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_snapshot(self, snapshot_id, **kwargs):
+        """Updates a snapshot.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/block-storage/v3/index.html#update-a-snapshot
+        """
+        put_body = json.dumps({'snapshot': kwargs})
+        resp, body = self.put('snapshots/%s' % snapshot_id, put_body)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_snapshot(self, snapshot_id):
+        """Delete Snapshot.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/block-storage/v3/index.html#delete-a-snapshot
+        """
+        resp, body = self.delete("snapshots/%s" % snapshot_id)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def is_resource_deleted(self, id):
+        try:
+            self.show_snapshot(id)
+        except lib_exc.NotFound:
+            return True
+        return False
+
+    @property
+    def resource_type(self):
+        """Returns the primary type of resource this client works with."""
+        return 'volume-snapshot'
+
+    def reset_snapshot_status(self, snapshot_id, status):
+        """Reset the specified snapshot's status."""
+        post_body = json.dumps({'os-reset_status': {"status": status}})
+        resp, body = self.post('snapshots/%s/action' % snapshot_id, post_body)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_snapshot_status(self, snapshot_id, **kwargs):
+        """Update the specified snapshot's status."""
+        # TODO(gmann): api-site doesn't contain doc ref
+        # for this API. After fixing the api-site, we need to
+        # add the link here.
+        # Bug https://bugs.launchpad.net/openstack-api-site/+bug/1532645
+
+        post_body = json.dumps({'os-update_snapshot_status': kwargs})
+        url = 'snapshots/%s/action' % snapshot_id
+        resp, body = self.post(url, post_body)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def create_snapshot_metadata(self, snapshot_id, metadata):
+        """Create metadata for the snapshot.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/block-storage/v3/index.html#create-a-snapshot-s-metadata
+        """
+        put_body = json.dumps({'metadata': metadata})
+        url = "snapshots/%s/metadata" % snapshot_id
+        resp, body = self.post(url, put_body)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_snapshot_metadata(self, snapshot_id):
+        """Get metadata of the snapshot.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/block-storage/v3/index.html#show-a-snapshot-s-metadata
+        """
+        url = "snapshots/%s/metadata" % snapshot_id
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_snapshot_metadata(self, snapshot_id, **kwargs):
+        """Update metadata for the snapshot.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/block-storage/v3/index.html#update-a-snapshot-s-metadata
+        """
+        put_body = json.dumps(kwargs)
+        url = "snapshots/%s/metadata" % snapshot_id
+        resp, body = self.put(url, put_body)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_snapshot_metadata_item(self, snapshot_id, id):
+        """Show metadata item for the snapshot."""
+        url = "snapshots/%s/metadata/%s" % (snapshot_id, id)
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_snapshot_metadata_item(self, snapshot_id, id, **kwargs):
+        """Update metadata item for the snapshot."""
+        # TODO(piyush): Current api-site doesn't contain this API description.
+        # After fixing the api-site, we need to fix here also for putting the
+        # link to api-site.
+        # LP: https://bugs.launchpad.net/openstack-api-site/+bug/1529064
+        put_body = json.dumps(kwargs)
+        url = "snapshots/%s/metadata/%s" % (snapshot_id, id)
+        resp, body = self.put(url, put_body)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_snapshot_metadata_item(self, snapshot_id, id):
+        """Delete metadata item for the snapshot."""
+        url = "snapshots/%s/metadata/%s" % (snapshot_id, id)
+        resp, body = self.delete(url)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def force_delete_snapshot(self, snapshot_id):
+        """Force Delete Snapshot."""
+        post_body = json.dumps({'os-force_delete': {}})
+        resp, body = self.post('snapshots/%s/action' % snapshot_id, post_body)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def unmanage_snapshot(self, snapshot_id):
+        """Unmanage a snapshot."""
+        post_body = json.dumps({'os-unmanage': {}})
+        url = 'snapshots/%s/action' % (snapshot_id)
+        resp, body = self.post(url, post_body)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/volume/v3/transfers_client.py b/tempest/lib/services/volume/v3/transfers_client.py
new file mode 100644
index 0000000..97c5597
--- /dev/null
+++ b/tempest/lib/services/volume/v3/transfers_client.py
@@ -0,0 +1,82 @@
+# Copyright 2012 OpenStack Foundation
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from oslo_serialization import jsonutils as json
+from six.moves.urllib import parse as urllib
+
+from tempest.lib.common import rest_client
+
+
+class TransfersClient(rest_client.RestClient):
+    """Client class to send CRUD Volume Transfer API requests"""
+
+    def create_volume_transfer(self, **kwargs):
+        """Create a volume transfer.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/block-storage/v3/index.html#create-a-volume-transfer
+        """
+        post_body = json.dumps({'transfer': kwargs})
+        resp, body = self.post('os-volume-transfer', post_body)
+        body = json.loads(body)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_volume_transfer(self, transfer_id):
+        """Returns the details of a volume transfer."""
+        url = "os-volume-transfer/%s" % transfer_id
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def list_volume_transfers(self, detail=False, **params):
+        """List all the volume transfers created.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/block-storage/v3/index.html#list-volume-transfers-for-a-project
+        https://developer.openstack.org/api-ref/block-storage/v3/index.html#list-volume-transfers-and-details
+        """
+        url = 'os-volume-transfer'
+        if detail:
+            url += '/detail'
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_volume_transfer(self, transfer_id):
+        """Delete a volume transfer."""
+        resp, body = self.delete("os-volume-transfer/%s" % transfer_id)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def accept_volume_transfer(self, transfer_id, **kwargs):
+        """Accept a volume transfer.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/block-storage/v3/index.html#accept-a-volume-transfer
+        """
+        url = 'os-volume-transfer/%s/accept' % transfer_id
+        post_body = json.dumps({'accept': kwargs})
+        resp, body = self.post(url, post_body)
+        body = json.loads(body)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/volume/v3/types_client.py b/tempest/lib/services/volume/v3/types_client.py
new file mode 100644
index 0000000..6d9d03a
--- /dev/null
+++ b/tempest/lib/services/volume/v3/types_client.py
@@ -0,0 +1,204 @@
+# Copyright 2012 OpenStack Foundation
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from oslo_serialization import jsonutils as json
+from six.moves.urllib import parse as urllib
+
+from tempest.lib.common import rest_client
+from tempest.lib import exceptions as lib_exc
+
+
+class TypesClient(rest_client.RestClient):
+    """Client class to send CRUD Volume API requests"""
+
+    def is_resource_deleted(self, id):
+        try:
+            self.show_volume_type(id)
+        except lib_exc.NotFound:
+            return True
+        return False
+
+    @property
+    def resource_type(self):
+        """Returns the primary type of resource this client works with."""
+        return 'volume-type'
+
+    def list_volume_types(self, **params):
+        """List all the volume_types created.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/block-storage/v3/index.html#list-all-volume-types
+        """
+        url = 'types'
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_volume_type(self, volume_type_id):
+        """Returns the details of a single volume_type.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/block-storage/v3/index.html#show-volume-type-detail
+        """
+        url = "types/%s" % volume_type_id
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def create_volume_type(self, **kwargs):
+        """Create volume type.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/block-storage/v3/index.html#create-a-volume-type
+        """
+        post_body = json.dumps({'volume_type': kwargs})
+        resp, body = self.post('types', post_body)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_volume_type(self, volume_type_id):
+        """Deletes the Specified Volume_type.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/block-storage/v3/index.html#delete-a-volume-type
+        """
+        resp, body = self.delete("types/%s" % volume_type_id)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def list_volume_types_extra_specs(self, volume_type_id, **params):
+        """List all the volume_types extra specs created.
+
+        TODO: Current api-site doesn't contain this API description.
+        After fixing the api-site, we need to fix here also for putting
+        the link to api-site.
+        """
+        url = 'types/%s/extra_specs' % volume_type_id
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_volume_type_extra_specs(self, volume_type_id, extra_specs_name):
+        """Returns the details of a single volume_type extra spec."""
+        url = "types/%s/extra_specs/%s" % (volume_type_id, extra_specs_name)
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def create_volume_type_extra_specs(self, volume_type_id, extra_specs):
+        """Creates a new Volume_type extra spec.
+
+        volume_type_id: Id of volume_type.
+        extra_specs: A dictionary of values to be used as extra_specs.
+        """
+        url = "types/%s/extra_specs" % volume_type_id
+        post_body = json.dumps({'extra_specs': extra_specs})
+        resp, body = self.post(url, post_body)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_volume_type_extra_specs(self, volume_type_id, extra_spec_name):
+        """Deletes the Specified Volume_type extra spec."""
+        resp, body = self.delete("types/%s/extra_specs/%s" % (
+            volume_type_id, extra_spec_name))
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_volume_type(self, volume_type_id, **kwargs):
+        """Updates volume type name, description, and/or is_public.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/block-storage/v3/index.html#update-a-volume-type
+        """
+        put_body = json.dumps({'volume_type': kwargs})
+        resp, body = self.put('types/%s' % volume_type_id, put_body)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_volume_type_extra_specs(self, volume_type_id, extra_spec_name,
+                                       extra_specs):
+        """Update a volume_type extra spec.
+
+        volume_type_id: Id of volume_type.
+        extra_spec_name: Name of the extra spec to be updated.
+        extra_spec: A dictionary of with key as extra_spec_name and the
+                     updated value.
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/block-storage/v3/index.html#update-extra-specification-for-volume-type
+        """
+        url = "types/%s/extra_specs/%s" % (volume_type_id, extra_spec_name)
+        put_body = json.dumps(extra_specs)
+        resp, body = self.put(url, put_body)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def add_type_access(self, volume_type_id, **kwargs):
+        """Adds volume type access for the given project.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/block-storage/v3/index.html#add-private-volume-type-access-to-project
+        """
+        post_body = json.dumps({'addProjectAccess': kwargs})
+        url = 'types/%s/action' % volume_type_id
+        resp, body = self.post(url, post_body)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def remove_type_access(self, volume_type_id, **kwargs):
+        """Removes volume type access for the given project.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/block-storage/v3/index.html#remove-private-volume-type-access-from-project
+        """
+        post_body = json.dumps({'removeProjectAccess': kwargs})
+        url = 'types/%s/action' % volume_type_id
+        resp, body = self.post(url, post_body)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def list_type_access(self, volume_type_id):
+        """Print access information about the given volume type.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/block-storage/v3/index.html#list-private-volume-type-access-detail
+        """
+        url = 'types/%s/os-volume-type-access' % volume_type_id
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/volume/v3/volume_manage_client.py b/tempest/lib/services/volume/v3/volume_manage_client.py
new file mode 100644
index 0000000..349e11d
--- /dev/null
+++ b/tempest/lib/services/volume/v3/volume_manage_client.py
@@ -0,0 +1,35 @@
+# Copyright 2017 FiberHome Telecommunication Technologies CO.,LTD
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from oslo_serialization import jsonutils as json
+
+from tempest.lib.common import rest_client
+
+
+class VolumeManageClient(rest_client.RestClient):
+    """Volume manage client."""
+
+    def manage_volume(self, **kwargs):
+        """Manage existing volume.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/block-storage/v3/index.html#manage-an-existing-volume
+        """
+        post_body = json.dumps({'volume': kwargs})
+        resp, body = self.post('os-volume-manage', post_body)
+        self.expected_success(202, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/volume/v3/volumes_client.py b/tempest/lib/services/volume/v3/volumes_client.py
index 5f4b278..a1185c4 100644
--- a/tempest/lib/services/volume/v3/volumes_client.py
+++ b/tempest/lib/services/volume/v3/volumes_client.py
@@ -14,16 +14,96 @@
 #    under the License.
 
 from oslo_serialization import jsonutils as json
+import six
 from six.moves.urllib import parse as urllib
 
 from tempest.lib.common import rest_client
-from tempest.lib.services.volume.v2 import volumes_client
+from tempest.lib import exceptions as lib_exc
+from tempest.lib.services.volume import base_client
 
 
-class VolumesClient(volumes_client.VolumesClient):
+class VolumesClient(base_client.BaseClient):
     """Client class to send CRUD Volume V3 API requests"""
     api_version = "v3"
 
+    def _prepare_params(self, params):
+        """Prepares params for use in get or _ext_get methods.
+
+        If params is a string it will be left as it is, but if it's not it will
+        be urlencoded.
+        """
+        if isinstance(params, six.string_types):
+            return params
+        return urllib.urlencode(params)
+
+    def list_volumes(self, detail=False, params=None):
+        """List all the volumes created.
+
+        Params can be a string (must be urlencoded) or a dictionary.
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/block-storage/v3/index.html#list-accessible-volumes-with-details
+        https://developer.openstack.org/api-ref/block-storage/v3/index.html#list-accessible-volumes
+        """
+        url = 'volumes'
+        if detail:
+            url += '/detail'
+        if params:
+            url += '?%s' % self._prepare_params(params)
+
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_volume(self, volume_id):
+        """Returns the details of a single volume."""
+        url = "volumes/%s" % volume_id
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def create_volume(self, **kwargs):
+        """Creates a new Volume.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/block-storage/v3/index.html#create-a-volume
+        """
+        post_body = json.dumps({'volume': kwargs})
+        resp, body = self.post('volumes', post_body)
+        body = json.loads(body)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_volume(self, volume_id, **kwargs):
+        """Updates the Specified Volume.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/block-storage/v3/index.html#update-a-volume
+        """
+        put_body = json.dumps({'volume': kwargs})
+        resp, body = self.put('volumes/%s' % volume_id, put_body)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_volume(self, volume_id, **params):
+        """Deletes the Specified Volume.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/block-storage/v3/index.html#delete-a-volume
+        """
+        url = 'volumes/%s' % volume_id
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+        resp, body = self.delete(url)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
     def show_volume_summary(self, **params):
         """Get volumes summary.
 
@@ -38,3 +118,250 @@
         body = json.loads(body)
         self.expected_success(200, resp.status)
         return rest_client.ResponseBody(resp, body)
+
+    def upload_volume(self, volume_id, **kwargs):
+        """Uploads a volume in Glance."""
+        post_body = json.dumps({'os-volume_upload_image': kwargs})
+        url = 'volumes/%s/action' % (volume_id)
+        resp, body = self.post(url, post_body)
+        body = json.loads(body)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def attach_volume(self, volume_id, **kwargs):
+        """Attaches a volume to a given instance on a given mountpoint.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/block-storage/v3/index.html#attach-volume-to-a-server
+        """
+        post_body = json.dumps({'os-attach': kwargs})
+        url = 'volumes/%s/action' % (volume_id)
+        resp, body = self.post(url, post_body)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def set_bootable_volume(self, volume_id, **kwargs):
+        """Set a bootable flag for a volume - true or false.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/block-storage/v3/index.html#update-a-volume-s-bootable-status
+        """
+        post_body = json.dumps({'os-set_bootable': kwargs})
+        url = 'volumes/%s/action' % (volume_id)
+        resp, body = self.post(url, post_body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def detach_volume(self, volume_id):
+        """Detaches a volume from an instance."""
+        post_body = json.dumps({'os-detach': {}})
+        url = 'volumes/%s/action' % (volume_id)
+        resp, body = self.post(url, post_body)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def reserve_volume(self, volume_id):
+        """Reserves a volume."""
+        post_body = json.dumps({'os-reserve': {}})
+        url = 'volumes/%s/action' % (volume_id)
+        resp, body = self.post(url, post_body)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def unreserve_volume(self, volume_id):
+        """Restore a reserved volume ."""
+        post_body = json.dumps({'os-unreserve': {}})
+        url = 'volumes/%s/action' % (volume_id)
+        resp, body = self.post(url, post_body)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def is_resource_deleted(self, id):
+        """Check the specified resource is deleted or not.
+
+        :param id: A checked resource id
+        :raises lib_exc.DeleteErrorException: If the specified resource is on
+        the status the delete was failed.
+        """
+        try:
+            volume = self.show_volume(id)
+        except lib_exc.NotFound:
+            return True
+        if volume["volume"]["status"] == "error_deleting":
+            raise lib_exc.DeleteErrorException(resource_id=id)
+        return False
+
+    @property
+    def resource_type(self):
+        """Returns the primary type of resource this client works with."""
+        return 'volume'
+
+    def extend_volume(self, volume_id, **kwargs):
+        """Extend a volume.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/block-storage/v3/index.html#extend-a-volume-size
+        """
+        post_body = json.dumps({'os-extend': kwargs})
+        url = 'volumes/%s/action' % (volume_id)
+        resp, body = self.post(url, post_body)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def reset_volume_status(self, volume_id, **kwargs):
+        """Reset the Specified Volume's Status.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/block-storage/v3/index.html#reset-a-volume-s-statuses
+        """
+        post_body = json.dumps({'os-reset_status': kwargs})
+        resp, body = self.post('volumes/%s/action' % volume_id, post_body)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_volume_readonly(self, volume_id, **kwargs):
+        """Update the Specified Volume readonly."""
+        post_body = json.dumps({'os-update_readonly_flag': kwargs})
+        url = 'volumes/%s/action' % (volume_id)
+        resp, body = self.post(url, post_body)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def force_delete_volume(self, volume_id):
+        """Force Delete Volume."""
+        post_body = json.dumps({'os-force_delete': {}})
+        resp, body = self.post('volumes/%s/action' % volume_id, post_body)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def create_volume_metadata(self, volume_id, metadata):
+        """Create metadata for the volume.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/block-storage/v3/index.html#create-metadata-for-volume
+        """
+        put_body = json.dumps({'metadata': metadata})
+        url = "volumes/%s/metadata" % volume_id
+        resp, body = self.post(url, put_body)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_volume_metadata(self, volume_id):
+        """Get metadata of the volume."""
+        url = "volumes/%s/metadata" % volume_id
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_volume_metadata(self, volume_id, metadata):
+        """Update metadata for the volume.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/block-storage/v3/index.html#update-a-volume-s-metadata
+        """
+        put_body = json.dumps({'metadata': metadata})
+        url = "volumes/%s/metadata" % volume_id
+        resp, body = self.put(url, put_body)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_volume_metadata_item(self, volume_id, id):
+        """Show metadata item for the volume."""
+        url = "volumes/%s/metadata/%s" % (volume_id, id)
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_volume_metadata_item(self, volume_id, id, meta_item):
+        """Update metadata item for the volume."""
+        put_body = json.dumps({'meta': meta_item})
+        url = "volumes/%s/metadata/%s" % (volume_id, id)
+        resp, body = self.put(url, put_body)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_volume_metadata_item(self, volume_id, id):
+        """Delete metadata item for the volume."""
+        url = "volumes/%s/metadata/%s" % (volume_id, id)
+        resp, body = self.delete(url)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def retype_volume(self, volume_id, **kwargs):
+        """Updates volume with new volume type.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/block-storage/v3/index.html#retype-a-volume
+        """
+        post_body = json.dumps({'os-retype': kwargs})
+        resp, body = self.post('volumes/%s/action' % volume_id, post_body)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def force_detach_volume(self, volume_id, **kwargs):
+        """Force detach a volume.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/block-storage/v3/index.html#force-delete-a-volume
+        """
+        post_body = json.dumps({'os-force_detach': kwargs})
+        url = 'volumes/%s/action' % volume_id
+        resp, body = self.post(url, post_body)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_volume_image_metadata(self, volume_id, **kwargs):
+        """Update image metadata for the volume.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/block-storage/v3/index.html#set-image-metadata-for-a-volume
+        """
+        post_body = json.dumps({'os-set_image_metadata': {'metadata': kwargs}})
+        url = "volumes/%s/action" % (volume_id)
+        resp, body = self.post(url, post_body)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_volume_image_metadata(self, volume_id, key_name):
+        """Delete image metadata item for the volume."""
+        post_body = json.dumps({'os-unset_image_metadata': {'key': key_name}})
+        url = "volumes/%s/action" % (volume_id)
+        resp, body = self.post(url, post_body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_volume_image_metadata(self, volume_id):
+        """Show image metadata for the volume."""
+        post_body = json.dumps({'os-show_image_metadata': {}})
+        url = "volumes/%s/action" % volume_id
+        resp, body = self.post(url, post_body)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def unmanage_volume(self, volume_id):
+        """Unmanage volume.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/block-storage/v3/index.html#unmanage-a-volume
+        """
+        post_body = json.dumps({'os-unmanage': {}})
+        resp, body = self.post('volumes/%s/action' % volume_id, post_body)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index cf53b67..9965fe5 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -83,6 +83,7 @@
         if CONF.service_available.cinder:
             cls.volumes_client = cls.os_primary.volumes_client_latest
             cls.snapshots_client = cls.os_primary.snapshots_client_latest
+            cls.backups_client = cls.os_primary.backups_client_latest
 
     # ## Test functions library
     #
@@ -244,6 +245,37 @@
         volume = self.volumes_client.show_volume(volume['id'])['volume']
         return volume
 
+    def create_backup(self, volume_id, name=None, description=None,
+                      force=False, snapshot_id=None, incremental=False,
+                      container=None):
+
+        name = name or data_utils.rand_name(
+            self.__class__.__name__ + "-backup")
+        kwargs = {'name': name,
+                  'description': description,
+                  'force': force,
+                  'snapshot_id': snapshot_id,
+                  'incremental': incremental,
+                  'container': container}
+        backup = self.backups_client.create_backup(volume_id=volume_id,
+                                                   **kwargs)['backup']
+        self.addCleanup(self.backups_client.delete_backup, backup['id'])
+        waiters.wait_for_volume_resource_status(self.backups_client,
+                                                backup['id'], 'available')
+        return backup
+
+    def restore_backup(self, backup_id):
+        restore = self.backups_client.restore_backup(backup_id)['restore']
+        self.addCleanup(self.volumes_client.delete_volume,
+                        restore['volume_id'])
+        waiters.wait_for_volume_resource_status(self.backups_client,
+                                                backup_id, 'available')
+        waiters.wait_for_volume_resource_status(self.volumes_client,
+                                                restore['volume_id'],
+                                                'available')
+        self.assertEqual(backup_id, restore['backup_id'])
+        return restore
+
     def create_volume_snapshot(self, volume_id, name=None, description=None,
                                metadata=None, force=False):
         name = name or data_utils.rand_name(
@@ -266,10 +298,8 @@
     def create_volume_type(self, client=None, name=None, backend_name=None):
         if not client:
             client = self.os_admin.volume_types_v2_client
-        if not name:
-            class_name = self.__class__.__name__
-            name = data_utils.rand_name(class_name + '-volume-type')
-        randomized_name = data_utils.rand_name('scenario-type-' + name)
+        randomized_name = name or data_utils.rand_name(
+            'volume-type-' + self.__class__.__name__)
 
         LOG.debug("Creating a volume type: %s on backend %s",
                   randomized_name, backend_name)
diff --git a/tempest/scenario/test_encrypted_cinder_volumes.py b/tempest/scenario/test_encrypted_cinder_volumes.py
index b5220e9..8c210d5 100644
--- a/tempest/scenario/test_encrypted_cinder_volumes.py
+++ b/tempest/scenario/test_encrypted_cinder_volumes.py
@@ -57,8 +57,7 @@
     @utils.services('compute', 'volume', 'image')
     def test_encrypted_cinder_volumes_luks(self):
         server = self.launch_instance()
-        volume = self.create_encrypted_volume('nova.volume.encryptors.'
-                                              'luks.LuksEncryptor',
+        volume = self.create_encrypted_volume('luks',
                                               volume_type='luks')
         self.attach_detach_volume(server, volume)
 
@@ -67,7 +66,6 @@
     @utils.services('compute', 'volume', 'image')
     def test_encrypted_cinder_volumes_cryptsetup(self):
         server = self.launch_instance()
-        volume = self.create_encrypted_volume('nova.volume.encryptors.'
-                                              'cryptsetup.CryptsetupEncryptor',
+        volume = self.create_encrypted_volume('plain',
                                               volume_type='cryptsetup')
         self.attach_detach_volume(server, volume)
diff --git a/tempest/scenario/test_server_advanced_ops.py b/tempest/scenario/test_server_advanced_ops.py
index 89b9fdd..8aa729b 100644
--- a/tempest/scenario/test_server_advanced_ops.py
+++ b/tempest/scenario/test_server_advanced_ops.py
@@ -32,7 +32,6 @@
     """The test suite for server advanced operations
 
     This test case stresses some advanced server instance operations:
-     * Resizing a volume-backed instance
      * Sequence suspend resume
     """
 
diff --git a/tempest/scenario/test_volume_backup_restore.py b/tempest/scenario/test_volume_backup_restore.py
new file mode 100644
index 0000000..c23b564
--- /dev/null
+++ b/tempest/scenario/test_volume_backup_restore.py
@@ -0,0 +1,91 @@
+# Copyright 2018 Red Hat, Inc.
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from tempest.common import utils
+from tempest import config
+from tempest.lib import decorators
+from tempest.scenario import manager
+
+CONF = config.CONF
+
+
+class TestVolumeBackupRestore(manager.ScenarioTest):
+    """Test cinder backup and restore
+
+    This testcase verifies content preservation after backup and restore
+    operations by booting a server from a restored backup and check the
+    connectivity to it.
+
+    The following is the scenario outline:
+    1. Create volume from image.
+    2. Create a backup for the volume.
+    3. Restore the backup.
+    4. Boot a server from the restored backup.
+    5. Create a floating ip.
+    6. Check server connectivity.
+    """
+
+    @classmethod
+    def skip_checks(cls):
+        super(TestVolumeBackupRestore, cls).skip_checks()
+        if not CONF.volume_feature_enabled.backup:
+            raise cls.skipException('Backup is not enable.')
+
+    @decorators.idempotent_id('2ce5e55c-4085-43c1-98c6-582525334ad7')
+    @decorators.attr(type='slow')
+    @utils.services('compute', 'volume', 'image')
+    def test_volume_backup_restore(self):
+        # Create volume from image
+        img_uuid = CONF.compute.image_ref
+        volume = self.create_volume(imageRef=img_uuid)
+        volume_details = self.volumes_client.show_volume(
+            volume['id'])['volume']
+        self.assertEqual('true', volume_details['bootable'])
+
+        # Create a backup
+        backup = self.create_backup(volume_id=volume['id'])
+
+        # Restore the backup
+        restored_volume_id = self.restore_backup(backup['id'])['volume_id']
+
+        # Verify the restored backup volume is bootable
+        restored_volume_info = self.volumes_client.show_volume(
+            restored_volume_id)['volume']
+        self.assertEqual('true', restored_volume_info['bootable'])
+
+        # Create keypair and security group
+        keypair = self.create_keypair()
+        security_group = self._create_security_group()
+
+        # Boot a server from the restored backup
+        bd_map_v2 = [{
+            'uuid': restored_volume_id,
+            'source_type': 'volume',
+            'destination_type': 'volume',
+            'boot_index': 0}]
+        server = self.create_server(image_id='',
+                                    block_device_mapping_v2=bd_map_v2,
+                                    key_name=keypair['name'],
+                                    security_groups=[
+                                        {'name': security_group['name']}])
+
+        # Create a floating ip
+        floating_ip = self.create_floating_ip(server)
+
+        # Check server connectivity
+        self.check_vm_connectivity(floating_ip['ip'],
+                                   username=CONF.validation.image_ssh_user,
+                                   private_key=keypair['private_key'],
+                                   should_connect=True)
diff --git a/tempest/scenario/test_volume_boot_pattern.py b/tempest/scenario/test_volume_boot_pattern.py
index 1fc57e7..2d024e9 100644
--- a/tempest/scenario/test_volume_boot_pattern.py
+++ b/tempest/scenario/test_volume_boot_pattern.py
@@ -236,16 +236,11 @@
 
         # Delete the second server which should also delete the second volume
         # created from the volume snapshot.
-        # TODO(mriedem): Currently, the compute service fails to delete the
-        # volume it created because the volume still has the snapshot
-        # associated with it, and the cleanups for the volume snapshot which
-        # were added in create_server_snapshot above, don't run until after
-        # this is called. So we need to delete the volume snapshot and wait for
-        # it to be gone here first before deleting the server, and then we can
-        # also assert that the underlying volume is deleted when the server is
-        # deleted.
         self._delete_server(instance)
 
+        # Assert that the underlying volume is gone.
+        self.volumes_client.wait_for_resource_deletion(created_volume['id'])
+
     @decorators.idempotent_id('cb78919a-e553-4bab-b73b-10cf4d2eb125')
     @testtools.skipUnless(CONF.compute_feature_enabled.attach_encrypted_volume,
                           'Encrypted volume attach is not supported')
diff --git a/tempest/tests/common/test_waiters.py b/tempest/tests/common/test_waiters.py
index bc197b5..eb1e2b6 100644
--- a/tempest/tests/common/test_waiters.py
+++ b/tempest/tests/common/test_waiters.py
@@ -72,3 +72,64 @@
         mock_show.assert_has_calls([mock.call(volume_id),
                                     mock.call(volume_id)])
         mock_sleep.assert_called_once_with(1)
+
+
+class TestInterfaceWaiters(base.TestCase):
+    def setUp(self):
+        super(TestInterfaceWaiters, self).setUp()
+        self.client = mock.MagicMock()
+        self.client.build_timeout = 1
+        self.client.build_interval = 1
+
+    def _port_down(self):
+        return {'interfaceAttachment': {'port_state': 'DOWN'}}
+
+    def _port_active(self):
+        return {'interfaceAttachment': {'port_state': 'ACTIVE'}}
+
+    def test_wait_for_interface_status(self):
+        self.client.show_interface.side_effect = [self._port_down(),
+                                                  self._port_active()]
+        with mock.patch.object(time, 'sleep') as sleep_mock:
+            start_time = int(time.time())
+            waiters.wait_for_interface_status(self.client, 'server_id',
+                                              'port_id', 'ACTIVE')
+            end_time = int(time.time())
+            self.assertLess(end_time, (start_time + self.client.build_timeout))
+            sleep_mock.assert_called_once_with(self.client.build_interval)
+
+    def test_wait_for_interface_status_timeout(self):
+        time_mock = self.patch('time.time')
+        time_mock.side_effect = utils.generate_timeout_series(1)
+
+        self.client.show_interface.return_value = self._port_down()
+        self.assertRaises(lib_exc.TimeoutException,
+                          waiters.wait_for_interface_status,
+                          self.client, 'server_id', 'port_id', 'ACTIVE')
+
+    def _one_interface(self):
+        return {'interfaceAttachments': [{'port_id': 'port_one'}]}
+
+    def _two_interfaces(self):
+        return {'interfaceAttachments': [{'port_id': 'port_one'},
+                                         {'port_id': 'port_two'}]}
+
+    def test_wait_for_interface_detach(self):
+        self.client.list_interfaces.side_effect = [self._two_interfaces(),
+                                                   self._one_interface()]
+        with mock.patch.object(time, 'sleep') as sleep_mock:
+            start_time = int(time.time())
+            waiters.wait_for_interface_detach(self.client, 'server_id',
+                                              'port_two')
+            end_time = int(time.time())
+            self.assertLess(end_time, (start_time + self.client.build_timeout))
+            sleep_mock.assert_called_once_with(self.client.build_interval)
+
+    def test_wait_for_interface_detach_timeout(self):
+        time_mock = self.patch('time.time')
+        time_mock.side_effect = utils.generate_timeout_series(1)
+
+        self.client.list_interfaces.return_value = self._one_interface()
+        self.assertRaises(lib_exc.TimeoutException,
+                          waiters.wait_for_interface_detach,
+                          self.client, 'server_id', 'port_one')
diff --git a/tempest/tests/lib/services/compute/test_flavors_client.py b/tempest/tests/lib/services/compute/test_flavors_client.py
index cbd17c6..5325036 100644
--- a/tempest/tests/lib/services/compute/test_flavors_client.py
+++ b/tempest/tests/lib/services/compute/test_flavors_client.py
@@ -17,6 +17,7 @@
 import fixtures
 from oslo_serialization import jsonutils as json
 
+from tempest.api.compute import api_microversion_fixture
 from tempest.lib.services.compute import flavors_client
 from tempest.tests.lib import fake_auth_provider
 from tempest.tests.lib import fake_http
@@ -39,6 +40,21 @@
         "vcpus": 1
     }
 
+    FAKE_FLAVOR_UPDATE = {
+        "disk": 1,
+        "id": "1",
+        "links": [{
+            "href": "http://openstack.example.com/v2/openstack/flavors/1",
+            "rel": "self"}, {
+            "href": "http://openstack.example.com/openstack/flavors/1",
+            "rel": "bookmark"}],
+        "name": "m1.tiny",
+        "ram": 512,
+        "swap": 1,
+        "vcpus": 1,
+        "description": 'new'
+    }
+
     EXTRA_SPECS = {"extra_specs": {
         "key1": "value1",
         "key2": "value2"}
@@ -106,6 +122,25 @@
     def test_create_flavor__byte_body(self):
         self._test_create_flavor(bytes_body=True)
 
+    def _test_update_flavor(self, bytes_body=False):
+        self.useFixture(api_microversion_fixture.APIMicroversionFixture(
+            '2.55'))
+        expected = {"flavor": TestFlavorsClient.FAKE_FLAVOR_UPDATE}
+        request = {"flavor": {"description": "updated description"}}
+        self.check_service_client_function(
+            self.client.update_flavor,
+            'tempest.lib.common.rest_client.RestClient.put',
+            expected,
+            bytes_body,
+            flavor_id='8c7aae5a-d315-4216-875b-ed9b6a5bcfc6',
+            **request)
+
+    def test_update_flavor_str_body(self):
+        self._test_update_flavor(bytes_body=False)
+
+    def test_update_flavor__byte_body(self):
+        self._test_update_flavor(bytes_body=True)
+
     def test_delete_flavor(self):
         self.check_service_client_function(
             self.client.delete_flavor,
diff --git a/tempest/tests/lib/services/volume/v2/test_snapshot_manage_client.py b/tempest/tests/lib/services/volume/v2/test_snapshot_manage_client.py
index 3fe8970..e03a8eb 100644
--- a/tempest/tests/lib/services/volume/v2/test_snapshot_manage_client.py
+++ b/tempest/tests/lib/services/volume/v2/test_snapshot_manage_client.py
@@ -18,6 +18,8 @@
 from oslo_serialization import jsonutils as json
 
 from tempest.lib.services.volume.v2 import snapshot_manage_client
+from tempest.lib.services.volume.v3 import snapshot_manage_client \
+    as snapshot_manage_clientv3
 from tempest.tests.lib import fake_auth_provider
 from tempest.tests.lib.services import base
 
@@ -63,7 +65,7 @@
 
         # NOTE: Use sort_keys for json.dumps so that the expected and actual
         # payloads are guaranteed to be identical for mock_args assert check.
-        with mock.patch.object(snapshot_manage_client.json,
+        with mock.patch.object(snapshot_manage_clientv3.json,
                                'dumps') as mock_dumps:
             mock_dumps.side_effect = lambda d: json_dumps(d, sort_keys=True)
 
diff --git a/tempest/tests/lib/services/volume/v2/test_transfers_client.py b/tempest/tests/lib/services/volume/v2/test_transfers_client.py
index 84f4992..8e7c6f4 100644
--- a/tempest/tests/lib/services/volume/v2/test_transfers_client.py
+++ b/tempest/tests/lib/services/volume/v2/test_transfers_client.py
@@ -19,6 +19,8 @@
 from oslo_serialization import jsonutils as json
 
 from tempest.lib.services.volume.v2 import transfers_client
+from tempest.lib.services.volume.v3 import transfers_client \
+    as transfers_clientv3
 from tempest.tests.lib import fake_auth_provider
 from tempest.tests.lib.services import base
 
@@ -63,7 +65,7 @@
 
         # NOTE: Use sort_keys for json.dumps so that the expected and actual
         # payloads are guaranteed to be identical for mock_args assert check.
-        with mock.patch.object(transfers_client.json, 'dumps') as mock_dumps:
+        with mock.patch.object(transfers_clientv3.json, 'dumps') as mock_dumps:
             mock_dumps.side_effect = lambda d: json_dumps(d, sort_keys=True)
 
             self.check_service_client_function(
@@ -84,7 +86,7 @@
 
         # NOTE: Use sort_keys for json.dumps so that the expected and actual
         # payloads are guaranteed to be identical for mock_args assert check.
-        with mock.patch.object(transfers_client.json, 'dumps') as mock_dumps:
+        with mock.patch.object(transfers_clientv3.json, 'dumps') as mock_dumps:
             mock_dumps.side_effect = lambda d: json_dumps(d, sort_keys=True)
 
             self.check_service_client_function(
diff --git a/tempest/tests/lib/services/volume/v2/test_volume_manage_client.py b/tempest/tests/lib/services/volume/v2/test_volume_manage_client.py
index ea4a9f9..0fb66bb 100644
--- a/tempest/tests/lib/services/volume/v2/test_volume_manage_client.py
+++ b/tempest/tests/lib/services/volume/v2/test_volume_manage_client.py
@@ -18,6 +18,8 @@
 from oslo_serialization import jsonutils as json
 
 from tempest.lib.services.volume.v2 import volume_manage_client
+from tempest.lib.services.volume.v3 import volume_manage_client \
+    as volume_manage_clientv3
 from tempest.tests.lib import fake_auth_provider
 from tempest.tests.lib.services import base
 
@@ -91,7 +93,7 @@
 
         # NOTE: Use sort_keys for json.dumps so that the expected and actual
         # payloads are guaranteed to be identical for mock_args assert check.
-        with mock.patch.object(volume_manage_client.json,
+        with mock.patch.object(volume_manage_clientv3.json,
                                'dumps') as mock_dumps:
             mock_dumps.side_effect = lambda d: json_dumps(d, sort_keys=True)