Merge "Set max_microversion 2.35 for nova security group tests"
diff --git a/.zuul.yaml b/.zuul.yaml
index e5e093a..8dcb935 100644
--- a/.zuul.yaml
+++ b/.zuul.yaml
@@ -45,6 +45,19 @@
         ENABLE_FILE_INJECTION: true
 
 - job:
+    name: tempest-full-parallel
+    parent: tempest-full
+    voting: false
+    branches:
+      - master
+    description: |
+      Base integration test with Neutron networking and py27.
+      It includes all scenarios as it was in the past.
+      This job runs all scenario tests in parallel!
+    vars:
+      tox_envlist: full-parallel
+
+- job:
     name: tempest-full-py3
     parent: devstack-tempest
     branches:
@@ -68,6 +81,26 @@
         # without Swift, c-bak cannot run (in the Gate at least)
         c-bak: false
 
+- nodeset:
+    name: openstack-bionic-node
+    nodes:
+      - name: controller
+        label: ubuntu-bionic
+    groups:
+      - name: tempest
+        nodes:
+          - controller
+
+- job:
+    name: tempest-full-py36
+    parent: tempest-full-py3
+    nodeset: openstack-bionic-node
+    branches:
+      - master
+    description: |
+      Base integration test with Neutron networking and py36.
+    voting: false
+
 - job:
     name: tempest-full-queens
     parent: tempest-full
@@ -175,6 +208,26 @@
               - ^roles/
               - ^.zuul.yaml$
         - nova-multiattach
+        - tempest-full-parallel:
+            irrelevant-files:
+              - ^(test-|)requirements.txt$
+              - ^.*\.rst$
+              - ^doc/.*$
+              - ^etc/.*$
+              - ^releasenotes/.*$
+              - ^setup.cfg$
+              - ^tempest/hacking/.*$
+              - ^tempest/tests/.*$
+        - tempest-full-py36:
+            irrelevant-files:
+              - ^(test-|)requirements.txt$
+              - ^.*\.rst$
+              - ^doc/.*$
+              - ^etc/.*$
+              - ^releasenotes/.*$
+              - ^setup.cfg$
+              - ^tempest/hacking/.*$
+              - ^tempest/tests/.*$
         - tempest-full-queens:
             irrelevant-files:
               - ^(test-|)requirements.txt$
@@ -221,6 +274,16 @@
               - ^setup.cfg$
               - ^tempest/hacking/.*$
               - ^tempest/tests/.*$
+        - nova-live-migration:
+            irrelevant-files:
+              - ^(test-|)requirements.txt$
+              - ^.*\.rst$
+              - ^doc/.*$
+              - ^etc/.*$
+              - ^releasenotes/.*$
+              - ^setup.cfg$
+              - ^tempest/hacking/.*$
+              - ^tempest/tests/.*$
     periodic-stable:
       jobs:
         - tempest-full-queens
diff --git a/HACKING.rst b/HACKING.rst
index f961884..bb55ac5 100644
--- a/HACKING.rst
+++ b/HACKING.rst
@@ -363,13 +363,24 @@
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 When adding tests for new features that were not in previous releases of the
-projects the new test has to be properly skipped with a feature flag. Whether
-this is just as simple as using the @utils.requires_ext() decorator to
-check if the required extension (or discoverable optional API) is enabled or
+projects the new test has to be properly skipped with a feature flag. This can
+be just as simple as using the ``@utils.requires_ext()`` or
+``testtools.skipUnless`` decorators to check if the required extension (or
+discoverable optional API) or feature is enabled or can be as difficult as
 adding a new config option to the appropriate section. If there isn't a method
 of selecting the new **feature** from the config file then there won't be a
-mechanism to disable the test with older stable releases and the new test won't
-be able to merge.
+mechanism to disable the test with older stable releases and the new test
+won't be able to merge.
+
+Introduction of a new feature flag requires specifying a default value for
+the corresponding config option that is appropriate in the latest OpenStack
+release. Because Tempest is branchless, the feature flag's default value will
+need to be overridden to a value that is appropriate in earlier releases
+in which the feature isn't available. In DevStack, this can be accomplished
+by modifying Tempest's `lib installation script`_ for previous branches
+(because DevStack is branched).
+
+.. _lib installation script: http://git.openstack.org/cgit/openstack-dev/devstack/tree/lib/tempest
 
 2. Bug fix on core project needing Tempest changes
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/README.rst b/README.rst
index f8059ae..044ae09 100644
--- a/README.rst
+++ b/README.rst
@@ -15,7 +15,7 @@
 
 This is a set of integration tests to be run against a live OpenStack
 cluster. Tempest has batteries of tests for OpenStack API validation,
-Scenarios, and other specific tests useful in validating an OpenStack
+scenarios, and other specific tests useful in validating an OpenStack
 deployment.
 
 Design Principles
diff --git a/REVIEWING.rst b/REVIEWING.rst
index 766d0c6..a880181 100644
--- a/REVIEWING.rst
+++ b/REVIEWING.rst
@@ -42,8 +42,8 @@
 
 API Stability
 -------------
-Tests should only be added for a published stable APIs. If a patch contains
-tests for an API which hasn't been marked as stable or for an API that which
+Tests should only be added for published stable APIs. If a patch contains
+tests for an API which hasn't been marked as stable or for an API which
 doesn't conform to the `API stability guidelines
 <https://wiki.openstack.org/wiki/Governance/Approved/APIStability>`_ then it
 should not be approved.
diff --git a/doc/source/microversion_testing.rst b/doc/source/microversion_testing.rst
index 942f969..3bc1d0c 100644
--- a/doc/source/microversion_testing.rst
+++ b/doc/source/microversion_testing.rst
@@ -306,10 +306,18 @@
 
   .. _2.6: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id5
 
+  * `2.9`_
+
+  .. _2.9: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id8
+
   * `2.10`_
 
   .. _2.10: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id9
 
+  * `2.19`_
+
+  .. _2.19: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id17
+
   * `2.20`_
 
   .. _2.20: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id18
@@ -322,6 +330,10 @@
 
   .. _2.25: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#maximum-in-mitaka
 
+  * `2.26`_
+
+  .. _2.26: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id23
+
   * `2.32`_
 
   .. _2.32: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id29
@@ -342,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/bp-application-credentials-df69b1f617db1bb9.yaml b/releasenotes/notes/bp-application-credentials-df69b1f617db1bb9.yaml
new file mode 100644
index 0000000..53125ef
--- /dev/null
+++ b/releasenotes/notes/bp-application-credentials-df69b1f617db1bb9.yaml
@@ -0,0 +1,9 @@
+---
+features:
+  - |
+    [`blueprint application-credentials <https://blueprints.launchpad.net/keystone/+spec/application-credentials>`_]
+    Tempest can test keystone's application credentials interface. A new client
+    library is added for application credentials, and a new config option,
+    ``[identity-feature-enabled]/application_credentials``, can control whether
+    the application credentials feature is tested (defaults to False,
+    indicating the feature is not enabled in the cloud under test).
diff --git a/releasenotes/notes/identity-v3-project-tags-client-36683c6a8644e54b.yaml b/releasenotes/notes/identity-v3-project-tags-client-36683c6a8644e54b.yaml
new file mode 100644
index 0000000..dfbcc7d
--- /dev/null
+++ b/releasenotes/notes/identity-v3-project-tags-client-36683c6a8644e54b.yaml
@@ -0,0 +1,12 @@
+---
+features:
+  - |
+    Add ``project_tags_client`` to the identity v3 library. This feature
+    enables the possibility of invoking the following API actions:
+
+    * update_project_tag
+    * list_project_tags
+    * update_all_project_tags
+    * check_project_tag_existence
+    * delete_project_tag
+    * delete_all_project_tags
diff --git a/releasenotes/notes/vnc-hardcoded-server-name-removed-6f8d1e90a175dc08.yaml b/releasenotes/notes/vnc-hardcoded-server-name-removed-6f8d1e90a175dc08.yaml
new file mode 100644
index 0000000..0da2ddc
--- /dev/null
+++ b/releasenotes/notes/vnc-hardcoded-server-name-removed-6f8d1e90a175dc08.yaml
@@ -0,0 +1,10 @@
+---
+features:
+  - |
+    New string configuration option ``vnc_server_header`` is added
+    to ``compute-feature-enabled`` section. It offers to provide VNC server
+    name that is to be expected in the responce header. For example, obvious
+    at hand names is 'WebSockify', 'nginx'.
+fixes:
+  - |
+    Fix VNC server response header issue when it is behind reverse proxy
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/requirements.txt b/requirements.txt
index 38f4a2a..7520d42 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -7,7 +7,7 @@
 testtools>=2.2.0 # MIT
 paramiko>=2.0.0 # LGPLv2.1+
 netaddr>=0.7.18 # BSD
-oslo.concurrency>=3.25.0 # Apache-2.0
+oslo.concurrency>=3.26.0 # Apache-2.0
 oslo.config>=5.2.0 # Apache-2.0
 oslo.log>=3.36.0 # Apache-2.0
 stestr>=1.0.0 # Apache-2.0
diff --git a/setup.cfg b/setup.cfg
index c981370..96ee7ea 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -47,12 +47,5 @@
 oslo.config.opts =
     tempest.config = tempest.config:list_opts
 
-[build_sphinx]
-all-files = 1
-# warning can be generated by using GENERATE_TEMPEST_PLUGIN_LIST='False'
-warning-is-error = 0
-build-dir = doc/build
-source-dir = doc/source
-
 [wheel]
 universal = 1
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_aggregates_negative.py b/tempest/api/compute/admin/test_aggregates_negative.py
index 6df8410..a6e0efa 100644
--- a/tempest/api/compute/admin/test_aggregates_negative.py
+++ b/tempest/api/compute/admin/test_aggregates_negative.py
@@ -27,15 +27,16 @@
     def setup_clients(cls):
         super(AggregatesAdminNegativeTestJSON, cls).setup_clients()
         cls.client = cls.os_admin.aggregates_client
-        cls.hyper_client = cls.os_admin.hypervisor_client
+        cls.services_client = cls.os_admin.services_client
 
     @classmethod
     def resource_setup(cls):
         super(AggregatesAdminNegativeTestJSON, cls).resource_setup()
         cls.aggregate_name_prefix = 'test_aggregate'
 
-        hyper_list = cls.hyper_client.list_hypervisors()['hypervisors']
-        cls.hosts = [v['hypervisor_hostname'] for v in hyper_list
+        svc_list = cls.services_client.list_services(
+            binary='nova-compute')['services']
+        cls.hosts = [v['host'] for v in svc_list
                      if v['status'] == 'enabled' and v['state'] == 'up']
 
     def _create_test_aggregate(self):
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_floating_ips_bulk.py b/tempest/api/compute/admin/test_floating_ips_bulk.py
index ba19937..72d09ed 100644
--- a/tempest/api/compute/admin/test_floating_ips_bulk.py
+++ b/tempest/api/compute/admin/test_floating_ips_bulk.py
@@ -31,6 +31,7 @@
     API documentation - http://docs.openstack.org/api/openstack-compute/2/
     content/ext-os-floating-ips-bulk.html
     """
+    max_microversion = '2.35'
 
     @classmethod
     def setup_clients(cls):
diff --git a/tempest/api/compute/admin/test_live_migration.py b/tempest/api/compute/admin/test_live_migration.py
index 2398cf1..bc38144 100644
--- a/tempest/api/compute/admin/test_live_migration.py
+++ b/tempest/api/compute/admin/test_live_migration.py
@@ -147,6 +147,7 @@
     @testtools.skipIf(not CONF.compute_feature_enabled.
                       block_migrate_cinder_iscsi,
                       'Block Live migration not configured for iSCSI')
+    @utils.services('volume')
     def test_iscsi_volume(self):
         server = self.create_test_server(wait_until="ACTIVE")
         server_id = server['id']
diff --git a/tempest/api/compute/admin/test_servers.py b/tempest/api/compute/admin/test_servers.py
index 3f06c4e..cdfc44a 100644
--- a/tempest/api/compute/admin/test_servers.py
+++ b/tempest/api/compute/admin/test_servers.py
@@ -16,6 +16,7 @@
 from tempest.common import waiters
 from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
+from tempest.lib import exceptions as lib_exc
 
 
 class ServersAdminTestJSON(base.BaseV2ComputeAdminTest):
@@ -61,9 +62,13 @@
     @decorators.idempotent_id('d56e9540-73ed-45e0-9b88-98fc419087eb')
     def test_list_servers_detailed_filter_by_invalid_status(self):
         params = {'status': 'invalid_status'}
-        body = self.client.list_servers(detail=True, **params)
-        servers = body['servers']
-        self.assertEmpty(servers)
+        if self.is_requested_microversion_compatible('2.37'):
+            body = self.client.list_servers(detail=True, **params)
+            servers = body['servers']
+            self.assertEmpty(servers)
+        else:
+            self.assertRaises(lib_exc.BadRequest, self.client.list_servers,
+                              detail=True, **params)
 
     @decorators.idempotent_id('51717b38-bdc1-458b-b636-1cf82d99f62f')
     def test_list_servers_by_admin(self):
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/base.py b/tempest/api/compute/base.py
index 9759be7..975728c 100644
--- a/tempest/api/compute/base.py
+++ b/tempest/api/compute/base.py
@@ -127,6 +127,36 @@
         cls.image_ssh_password = CONF.validation.image_ssh_password
 
     @classmethod
+    def is_requested_microversion_compatible(cls, max_version):
+        """Check the compatibility of selected request microversion
+
+        This method will check if selected request microversion
+        (cls.request_microversion) for test is compatible with respect
+        to 'max_version'. Compatible means if selected request microversion
+        is in the range(<=) of 'max_version'.
+
+        :param max_version: maximum microversion to compare for compatibility.
+            Example: '2.30'
+        :returns: True if selected request microversion is compatible with
+            'max_version'. False in other case.
+        """
+        try:
+            req_version_obj = api_version_request.APIVersionRequest(
+                cls.request_microversion)
+        # NOTE(gmann): This is case where this method is used before calling
+        # resource_setup(), where cls.request_microversion is set. There may
+        # not be any such case but still we can handle this case.
+        except AttributeError:
+            request_microversion = (
+                api_version_utils.select_request_microversion(
+                    cls.min_microversion,
+                    CONF.compute.min_microversion))
+            req_version_obj = api_version_request.APIVersionRequest(
+                request_microversion)
+        max_version_obj = api_version_request.APIVersionRequest(max_version)
+        return req_version_obj <= max_version_obj
+
+    @classmethod
     def server_check_teardown(cls):
         """Checks is the shared server clean enough for subsequent test.
 
@@ -532,10 +562,10 @@
     def get_host_other_than(self, server_id):
         source_host = self.get_host_for_server(server_id)
 
-        hypers = self.os_admin.hypervisor_client.list_hypervisors(
-            )['hypervisors']
-        hosts = [hyper['hypervisor_hostname'] for hyper in hypers
-                 if hyper['state'] == 'up' and hyper['status'] == 'enabled']
+        svcs = self.os_admin.services_client.list_services(
+            binary='nova-compute')['services']
+        hosts = [svc['host'] for svc in svcs
+                 if svc['state'] == 'up' and svc['status'] == 'enabled']
 
         for target_host in hosts:
             if source_host != target_host:
diff --git a/tempest/api/compute/servers/test_create_server.py b/tempest/api/compute/servers/test_create_server.py
index c660821..122c4f5 100644
--- a/tempest/api/compute/servers/test_create_server.py
+++ b/tempest/api/compute/servers/test_create_server.py
@@ -135,8 +135,13 @@
             servers_client=self.client)
         hostname = linux_client.exec_command("hostname").rstrip()
         msg = ('Failed while verifying servername equals hostname. Expected '
-               'hostname "%s" but got "%s".' % (self.name, hostname))
-        self.assertEqual(self.name.lower(), hostname, msg)
+               'hostname "%s" but got "%s".' %
+               (self.name, hostname.split(".")[0]))
+        # NOTE(zhufl): Some images will add postfix for the hostname, e.g.,
+        # if hostname is "aaa", postfix ".novalocal" may be added to it, and
+        # the hostname will be "aaa.novalocal" then, so we should ignore the
+        # postfix when checking whether hostname equals self.name.
+        self.assertEqual(self.name.lower(), hostname.split(".")[0], msg)
 
 
 class ServersTestManualDisk(ServersTestJSON):
diff --git a/tempest/api/compute/servers/test_device_tagging.py b/tempest/api/compute/servers/test_device_tagging.py
index 43046ca..1213a04 100644
--- a/tempest/api/compute/servers/test_device_tagging.py
+++ b/tempest/api/compute/servers/test_device_tagging.py
@@ -47,8 +47,8 @@
             raise cls.skipException('Neutron is required')
         if not CONF.validation.run_validation:
             raise cls.skipException('Validation must be enabled')
-        if (not CONF.compute_feature_enabled.config_drive
-            and not CONF.compute_feature_enabled.metadata_service):
+        if (not CONF.compute_feature_enabled.config_drive and
+                not CONF.compute_feature_enabled.metadata_service):
             raise cls.skipException('One of metadata or config drive must be '
                                     'enabled')
 
@@ -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_list_servers_negative.py b/tempest/api/compute/servers/test_list_servers_negative.py
index 393e68f..18a78f0 100644
--- a/tempest/api/compute/servers/test_list_servers_negative.py
+++ b/tempest/api/compute/servers/test_list_servers_negative.py
@@ -79,10 +79,16 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('fcdf192d-0f74-4d89-911f-1ec002b822c4')
     def test_list_servers_status_non_existing(self):
-        # Return an empty list when invalid status is specified
-        body = self.client.list_servers(status='non_existing_status')
-        servers = body['servers']
-        self.assertEmpty(servers)
+        # When invalid status is specified, up to microversion 2.37,
+        # an empty list is returnd, and starting from microversion 2.38,
+        # a 400 error is returned in that case.
+        if self.is_requested_microversion_compatible('2.37'):
+            body = self.client.list_servers(status='non_existing_status')
+            servers = body['servers']
+            self.assertEmpty(servers)
+        else:
+            self.assertRaises(lib_exc.BadRequest, self.client.list_servers,
+                              status='non_existing_status')
 
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('d47c17fb-eebd-4287-8e95-f20a7e627b18')
diff --git a/tempest/api/compute/servers/test_novnc.py b/tempest/api/compute/servers/test_novnc.py
index d9581e3..1dfd0f9 100644
--- a/tempest/api/compute/servers/test_novnc.py
+++ b/tempest/api/compute/servers/test_novnc.py
@@ -151,11 +151,22 @@
         self.assertTrue(
             self._websocket.response.startswith(b'HTTP/1.1 101 Switching '
                                                 b'Protocols\r\n'),
-            'Did not get the expected 101 on the websockify call: '
-            + six.text_type(self._websocket.response))
-        self.assertTrue(
-            self._websocket.response.find(b'Server: WebSockify') > 0,
-            'Did not get the expected WebSocket HTTP Response.')
+            'Did not get the expected 101 on the {} call: {}'.format(
+                CONF.compute_feature_enabled.vnc_server_header,
+                six.text_type(self._websocket.response)
+            )
+        )
+        # Since every other server type returns Headers with different case
+        # (for example 'nginx'), lowercase must be applied to eliminate issues.
+        _desired_header = "server: {0}".format(
+            CONF.compute_feature_enabled.vnc_server_header
+        ).lower()
+        _response = six.text_type(self._websocket.response).lower()
+        self.assertIn(
+            _desired_header,
+            _response,
+            'Did not get the expected WebSocket HTTP Response.'
+        )
 
     @decorators.idempotent_id('c640fdff-8ab4-45a4-a5d8-7e6146cbd0dc')
     def test_novnc(self):
diff --git a/tempest/api/compute/servers/test_server_actions.py b/tempest/api/compute/servers/test_server_actions.py
index 5c3e9f0..bbec30c 100644
--- a/tempest/api/compute/servers/test_server_actions.py
+++ b/tempest/api/compute/servers/test_server_actions.py
@@ -586,7 +586,7 @@
             server_info = self.client.show_server(self.server_id)['server']
             if 'SHELVED' in server_info['status']:
                 self.client.unshelve_server(self.server_id)
-        self.addOnException(_unshelve_server)
+        self.addCleanup(_unshelve_server)
 
         server = self.client.show_server(self.server_id)['server']
         image_name = server['name'] + '-shelved'
diff --git a/tempest/api/compute/servers/test_servers.py b/tempest/api/compute/servers/test_servers.py
index 2904976..543fa1c 100644
--- a/tempest/api/compute/servers/test_servers.py
+++ b/tempest/api/compute/servers/test_servers.py
@@ -184,8 +184,28 @@
     min_microversion = '2.47'
     max_microversion = 'latest'
 
+    # NOTE(gmann): This test tests the server APIs response schema
+    # Along with 2.47 microversion schema this test class tests the
+    # other microversions 2.9, 2.19 and 2.26 server APIs response schema
+    # also. 2.47 APIs schema are on top of 2.9->2.19->2.26 schema so
+    # below tests cover all of the schema.
+
     @decorators.idempotent_id('88b0bdb2-494c-11e7-a919-92ebcb67fe33')
     def test_show_server(self):
         server = self.create_test_server()
         # All fields will be checked by API schema
         self.servers_client.show_server(server['id'])
+
+    @decorators.idempotent_id('8de397c2-57d0-4b90-aa30-e5d668f21a8b')
+    def test_update_rebuild_list_server(self):
+        server = self.create_test_server()
+        # Checking update API response schema
+        self.servers_client.update_server(server['id'])
+        waiters.wait_for_server_status(self.servers_client, server['id'],
+                                       'ACTIVE')
+        # Checking rebuild API response schema
+        self.servers_client.rebuild_server(server['id'], self.image_ref_alt)
+        waiters.wait_for_server_status(self.servers_client,
+                                       server['id'], 'ACTIVE')
+        # Checking list details API response schema
+        self.servers_client.list_servers(detail=True)
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/servers/test_servers_negative.py b/tempest/api/compute/servers/test_servers_negative.py
index 9b545af..e944c28 100644
--- a/tempest/api/compute/servers/test_servers_negative.py
+++ b/tempest/api/compute/servers/test_servers_negative.py
@@ -481,7 +481,7 @@
             server_info = self.client.show_server(self.server_id)['server']
             if 'SHELVED' in server_info['status']:
                 self.client.unshelve_server(self.server_id)
-        self.addOnException(_unshelve_server)
+        self.addCleanup(_unshelve_server)
 
         server = self.client.show_server(self.server_id)['server']
         image_name = server['name'] + '-shelved'
diff --git a/tempest/api/compute/test_tenant_networks.py b/tempest/api/compute/test_tenant_networks.py
index b55e2c0..f4eada0 100644
--- a/tempest/api/compute/test_tenant_networks.py
+++ b/tempest/api/compute/test_tenant_networks.py
@@ -18,6 +18,7 @@
 
 
 class ComputeTenantNetworksTest(base.BaseV2ComputeTest):
+    max_microversion = '2.35'
 
     @classmethod
     def resource_setup(cls):
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_application_credentials.py b/tempest/api/identity/admin/v3/test_application_credentials.py
new file mode 100644
index 0000000..4a74ef8
--- /dev/null
+++ b/tempest/api/identity/admin/v3/test_application_credentials.py
@@ -0,0 +1,48 @@
+# Copyright 2018 SUSE Linux GmbH
+#
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from tempest.api.identity import base
+from tempest import config
+from tempest.lib import decorators
+
+
+CONF = config.CONF
+
+
+class ApplicationCredentialsV3AdminTest(base.BaseApplicationCredentialsV3Test,
+                                        base.BaseIdentityV3AdminTest):
+
+    @decorators.idempotent_id('3b3dd48f-3388-406a-a9e6-4d078a552d0e')
+    def test_create_application_credential_with_roles(self):
+        role = self.setup_test_role()
+        self.os_admin.roles_v3_client.create_user_role_on_project(
+            self.project_id,
+            self.user_id,
+            role['id']
+        )
+
+        app_cred = self.create_application_credential(
+            roles=[{'id': role['id']}])
+        secret = app_cred['secret']
+
+        # Check that the application credential is functional
+        token_id, resp = self.non_admin_token.get_token(
+            app_cred_id=app_cred['id'],
+            app_cred_secret=secret,
+            auth_data=True
+        )
+        self.assertEqual(resp['project']['id'], self.project_id)
+        self.assertEqual(resp['roles'][0]['id'], role['id'])
diff --git a/tempest/api/identity/admin/v3/test_project_tags.py b/tempest/api/identity/admin/v3/test_project_tags.py
new file mode 100644
index 0000000..d05173b
--- /dev/null
+++ b/tempest/api/identity/admin/v3/test_project_tags.py
@@ -0,0 +1,66 @@
+# Copyright 2018 AT&T Corporation.
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+import testtools
+
+from tempest.api.identity import base
+from tempest import config
+from tempest.lib.common.utils import data_utils
+from tempest.lib import decorators
+from tempest.lib import exceptions as lib_exc
+
+CONF = config.CONF
+
+
+class IdentityV3ProjectTagsTest(base.BaseIdentityV3AdminTest):
+
+    @decorators.idempotent_id('7c123aac-999d-416a-a0fb-84b915ab10de')
+    @testtools.skipUnless(CONF.identity_feature_enabled.project_tags,
+                          'Project tags not available.')
+    def test_list_update_delete_project_tags(self):
+        project = self.setup_test_project()
+
+        # Create a tag for testing.
+        tag = data_utils.rand_name('tag')
+        # NOTE(felipemonteiro): The response body for create is empty.
+        self.project_tags_client.update_project_tag(project['id'], tag)
+
+        # Verify that the tag was created.
+        self.project_tags_client.check_project_tag_existence(
+            project['id'], tag)
+
+        # Verify that updating the project tags works.
+        tags_to_update = [data_utils.rand_name('tag') for _ in range(3)]
+        updated_tags = self.project_tags_client.update_all_project_tags(
+            project['id'], tags_to_update)['tags']
+        self.assertEqual(sorted(tags_to_update), sorted(updated_tags))
+
+        # Verify that listing project tags works.
+        retrieved_tags = self.project_tags_client.list_project_tags(
+            project['id'])['tags']
+        self.assertEqual(sorted(tags_to_update), sorted(retrieved_tags))
+
+        # Verify that deleting a project tag works.
+        self.project_tags_client.delete_project_tag(
+            project['id'], tags_to_update[0])
+        self.assertRaises(lib_exc.NotFound,
+                          self.project_tags_client.check_project_tag_existence,
+                          project['id'], tags_to_update[0])
+
+        # Verify that deleting all project tags works.
+        self.project_tags_client.delete_all_project_tags(project['id'])
+        retrieved_tags = self.project_tags_client.list_project_tags(
+            project['id'])['tags']
+        self.assertEmpty(retrieved_tags)
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/identity/base.py b/tempest/api/identity/base.py
index 9edccbb..68f2c07 100644
--- a/tempest/api/identity/base.py
+++ b/tempest/api/identity/base.py
@@ -190,6 +190,8 @@
         cls.non_admin_catalog_client = cls.os_primary.catalog_client
         cls.non_admin_versions_client =\
             cls.os_primary.identity_versions_v3_client
+        cls.non_admin_app_creds_client = \
+            cls.os_primary.application_credentials_client
 
 
 class BaseIdentityV3AdminTest(BaseIdentityV3Test):
@@ -228,6 +230,7 @@
         cls.domain_config_client = cls.os_admin.domain_config_client
         cls.endpoint_filter_client = cls.os_admin.endpoint_filter_client
         cls.endpoint_groups_client = cls.os_admin.endpoint_groups_client
+        cls.project_tags_client = cls.os_admin.project_tags_client
 
         if CONF.identity.admin_domain_scope:
             # NOTE(andreaf) When keystone policy requires it, the identity
@@ -288,3 +291,30 @@
             test_utils.call_and_ignore_notfound_exc,
             self.delete_domain, domain['id'])
         return domain
+
+
+class BaseApplicationCredentialsV3Test(BaseIdentityV3Test):
+
+    @classmethod
+    def skip_checks(cls):
+        super(BaseApplicationCredentialsV3Test, cls).skip_checks()
+        if not CONF.identity_feature_enabled.application_credentials:
+            raise cls.skipException("Application credentials are not available"
+                                    " in this environment")
+
+    @classmethod
+    def resource_setup(cls):
+        super(BaseApplicationCredentialsV3Test, cls).resource_setup()
+        cls.user_id = cls.os_primary.credentials.user_id
+        cls.project_id = cls.os_primary.credentials.project_id
+
+    def create_application_credential(self, name=None, **kwargs):
+        name = name or data_utils.rand_name('application_credential')
+        application_credential = (
+            self.non_admin_app_creds_client.create_application_credential(
+                self.user_id, name=name, **kwargs))['application_credential']
+        self.addCleanup(
+            self.non_admin_app_creds_client.delete_application_credential,
+            self.user_id,
+            application_credential['id'])
+        return application_credential
diff --git a/tempest/api/identity/v3/test_application_credentials.py b/tempest/api/identity/v3/test_application_credentials.py
new file mode 100644
index 0000000..caf0b1e
--- /dev/null
+++ b/tempest/api/identity/v3/test_application_credentials.py
@@ -0,0 +1,85 @@
+# Copyright 2018 SUSE Linux GmbH
+#
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+import datetime
+
+from oslo_utils import timeutils
+
+from tempest.api.identity import base
+from tempest import config
+from tempest.lib import decorators
+
+
+CONF = config.CONF
+
+
+class ApplicationCredentialsV3Test(base.BaseApplicationCredentialsV3Test):
+
+    def _list_app_creds(self, name=None):
+        kwargs = dict(user_id=self.user_id)
+        if name:
+            kwargs.update(name=name)
+        return self.non_admin_app_creds_client.list_application_credentials(
+            **kwargs)['application_credentials']
+
+    @decorators.idempotent_id('8080c75c-eddc-4786-941a-c2da7039ae61')
+    def test_create_application_credential(self):
+        app_cred = self.create_application_credential()
+
+        # Check that the secret appears in the create response
+        secret = app_cred['secret']
+
+        # Check that the secret is not retrievable after initial create
+        app_cred = self.non_admin_app_creds_client.show_application_credential(
+            user_id=self.user_id,
+            application_credential_id=app_cred['id']
+        )['application_credential']
+        self.assertNotIn('secret', app_cred)
+
+        # Check that the application credential is functional
+        token_id, resp = self.non_admin_token.get_token(
+            app_cred_id=app_cred['id'],
+            app_cred_secret=secret,
+            auth_data=True
+        )
+        self.assertEqual(resp['project']['id'], self.project_id)
+
+    @decorators.idempotent_id('852daf0c-42b5-4239-8466-d193d0543ed3')
+    def test_create_application_credential_expires(self):
+        expires_at = timeutils.utcnow() + datetime.timedelta(hours=1)
+
+        app_cred = self.create_application_credential(expires_at=expires_at)
+
+        expires_str = expires_at.isoformat()
+        self.assertEqual(expires_str, app_cred['expires_at'])
+
+    @decorators.idempotent_id('ff0cd457-6224-46e7-b79e-0ada4964a8a6')
+    def test_list_application_credentials(self):
+        self.create_application_credential()
+        self.create_application_credential()
+
+        app_creds = self._list_app_creds()
+        self.assertEqual(2, len(app_creds))
+
+    @decorators.idempotent_id('9bb5e5cc-5250-493a-8869-8b665f6aa5f6')
+    def test_query_application_credentials(self):
+        self.create_application_credential()
+        app_cred_two = self.create_application_credential()
+        app_cred_two_name = app_cred_two['name']
+
+        app_creds = self._list_app_creds(name=app_cred_two_name)
+        self.assertEqual(1, len(app_creds))
+        self.assertEqual(app_cred_two_name, app_creds[0]['name'])
diff --git a/tempest/api/image/v1/test_images.py b/tempest/api/image/v1/test_images.py
index 76723f4..2432c8b 100644
--- a/tempest/api/image/v1/test_images.py
+++ b/tempest/api/image/v1/test_images.py
@@ -132,8 +132,8 @@
     @classmethod
     def skip_checks(cls):
         super(ListImagesTest, cls).skip_checks()
-        if (len(CONF.image.container_formats) < 2
-           or len(CONF.image.disk_formats) < 2):
+        if (len(CONF.image.container_formats) < 2 or
+                len(CONF.image.disk_formats) < 2):
             skip_msg = ("%s skipped as multiple container formats "
                         "or disk formats are not available." % cls.__name__)
             raise cls.skipException(skip_msg)
@@ -227,8 +227,8 @@
             self.assertEqual(image['disk_format'], self.disk_format_alt)
         result_set = set(map(lambda x: x['id'], images_list))
         self.assertTrue(self.same_disk_format_set <= result_set)
-        self.assertFalse(self.created_set - self.same_disk_format_set
-                         <= result_set)
+        self.assertFalse(self.created_set - self.same_disk_format_set <=
+                         result_set)
 
     @decorators.idempotent_id('2143655d-96d9-4bec-9188-8674206b4b3b')
     def test_index_container_format(self):
@@ -238,8 +238,8 @@
             self.assertEqual(image['container_format'], self.container_format)
         result_set = set(map(lambda x: x['id'], images_list))
         self.assertTrue(self.same_container_format_set <= result_set)
-        self.assertFalse(self.created_set - self.same_container_format_set
-                         <= result_set)
+        self.assertFalse(self.created_set - self.same_container_format_set <=
+                         result_set)
 
     @decorators.idempotent_id('feb32ac6-22bb-4a16-afd8-9454bb714b14')
     def test_index_max_size(self):
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/admin/test_volume_hosts.py b/tempest/api/volume/admin/test_volume_hosts.py
index ce0cbd2..7e53ce8 100644
--- a/tempest/api/volume/admin/test_volume_hosts.py
+++ b/tempest/api/volume/admin/test_volume_hosts.py
@@ -46,8 +46,8 @@
         # show host API should fail (return code: 404). The cinder-volume host
         # is presented in format: <host-name>@driver-name.
         c_vol_hosts = [host['host_name'] for host in hosts
-                       if (host['service'] == 'cinder-volume'
-                           and host['service-state'] == 'enabled')]
+                       if (host['service'] == 'cinder-volume' and
+                           host['service-state'] == 'enabled')]
         self.assertNotEmpty(c_vol_hosts,
                             "No available cinder-volume host is found, "
                             "all hosts that found are: %s" % hosts)
diff --git a/tempest/api/volume/admin/test_volume_retype_with_migration.py b/tempest/api/volume/admin/test_volume_retype_with_migration.py
index f0b3a4f..025c1be 100644
--- a/tempest/api/volume/admin/test_volume_retype_with_migration.py
+++ b/tempest/api/volume/admin/test_volume_retype_with_migration.py
@@ -46,13 +46,10 @@
         extra_specs_src = {"volume_backend_name": backend_src}
         extra_specs_dst = {"volume_backend_name": backend_dst}
 
-        src_vol_type = cls.create_volume_type(extra_specs=extra_specs_src)
+        cls.src_vol_type = cls.create_volume_type(extra_specs=extra_specs_src)
         cls.dst_vol_type = cls.create_volume_type(extra_specs=extra_specs_dst)
 
-        cls.src_vol = cls.create_volume(volume_type=src_vol_type['name'])
-
-    @classmethod
-    def resource_cleanup(cls):
+    def _wait_for_internal_volume_cleanup(self, vol):
         # When retyping a volume, Cinder creates an internal volume in the
         # target backend. The volume in the source backend is deleted after
         # the migration, so we need to wait for Cinder delete this volume
@@ -60,40 +57,37 @@
 
         # This list should return 2 volumes until the copy and cleanup
         # process is finished.
-        fetched_list = cls.admin_volume_client.list_volumes(
+        fetched_list = self.admin_volume_client.list_volumes(
             params={'all_tenants': True,
-                    'display_name': cls.src_vol['name']})['volumes']
+                    'display_name': vol['name']})['volumes']
 
         for fetched_vol in fetched_list:
-            if fetched_vol['id'] != cls.src_vol['id']:
+            if fetched_vol['id'] != vol['id']:
                 # This is the Cinder internal volume
                 LOG.debug('Waiting for internal volume %s deletion',
                           fetched_vol['id'])
-                cls.admin_volume_client.wait_for_resource_deletion(
+                self.admin_volume_client.wait_for_resource_deletion(
                     fetched_vol['id'])
                 break
 
-        super(VolumeRetypeWithMigrationTest, cls).resource_cleanup()
-
-    @decorators.idempotent_id('a1a41f3f-9dad-493e-9f09-3ff197d477cd')
-    def test_available_volume_retype_with_migration(self):
-
+    def _retype_volume(self, volume):
         keys_with_no_change = ('id', 'size', 'description', 'name', 'user_id',
                                'os-vol-tenant-attr:tenant_id')
         keys_with_change = ('volume_type', 'os-vol-host-attr:host')
 
         volume_source = self.admin_volume_client.show_volume(
-            self.src_vol['id'])['volume']
+            volume['id'])['volume']
 
         self.volumes_client.retype_volume(
-            self.src_vol['id'],
+            volume['id'],
             new_type=self.dst_vol_type['name'],
             migration_policy='on-demand')
-
-        waiters.wait_for_volume_retype(self.volumes_client, self.src_vol['id'],
+        self.addCleanup(self._wait_for_internal_volume_cleanup, volume)
+        waiters.wait_for_volume_retype(self.volumes_client, volume['id'],
                                        self.dst_vol_type['name'])
+
         volume_dest = self.admin_volume_client.show_volume(
-            self.src_vol['id'])['volume']
+            volume['id'])['volume']
 
         # Check the volume information after the migration.
         self.assertEqual('success',
@@ -105,3 +99,27 @@
 
         for key in keys_with_change:
             self.assertNotEqual(volume_source[key], volume_dest[key])
+
+    @decorators.idempotent_id('a1a41f3f-9dad-493e-9f09-3ff197d477cd')
+    def test_available_volume_retype_with_migration(self):
+        src_vol = self.create_volume(volume_type=self.src_vol_type['name'])
+        self._retype_volume(src_vol)
+
+    @decorators.idempotent_id('d0d9554f-e7a5-4104-8973-f35b27ccb60d')
+    def test_volume_from_snapshot_retype_with_migration(self):
+        # Create a volume in the first backend
+        src_vol = self.create_volume(volume_type=self.src_vol_type['name'])
+
+        # Create a volume snapshot
+        snapshot = self.create_snapshot(src_vol['id'])
+
+        # Create a volume from the snapshot
+        src_vol = self.create_volume(volume_type=self.src_vol_type['name'],
+                                     snapshot_id=snapshot['id'])
+
+        # Delete the snapshot
+        self.snapshots_client.delete_snapshot(snapshot['id'])
+        self.snapshots_client.wait_for_resource_deletion(snapshot['id'])
+
+        # Migrate the volume from snapshot to the second backend
+        self._retype_volume(src_vol)
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/api/volume/test_volumes_extend.py b/tempest/api/volume/test_volumes_extend.py
index 54052ae..5d339c4 100644
--- a/tempest/api/volume/test_volumes_extend.py
+++ b/tempest/api/volume/test_volumes_extend.py
@@ -32,7 +32,7 @@
     @decorators.idempotent_id('9a36df71-a257-43a5-9555-dc7c88e66e0e')
     def test_volume_extend(self):
         # Extend Volume Test.
-        volume = self.create_volume()
+        volume = self.create_volume(image_ref=self.image_ref)
         extend_size = volume['size'] + 1
         self.volumes_client.extend_volume(volume['id'],
                                           new_size=extend_size)
@@ -44,7 +44,6 @@
     @decorators.idempotent_id('86be1cba-2640-11e5-9c82-635fb964c912')
     @testtools.skipUnless(CONF.volume_feature_enabled.snapshot,
                           "Cinder volume snapshots are disabled")
-    @decorators.skip_because(bug='1687044')
     def test_volume_extend_when_volume_has_snapshot(self):
         volume = self.create_volume()
         self.create_snapshot(volume['id'])
diff --git a/tempest/api/volume/test_volumes_snapshots.py b/tempest/api/volume/test_volumes_snapshots.py
index dcd3518..52114bc 100644
--- a/tempest/api/volume/test_volumes_snapshots.py
+++ b/tempest/api/volume/test_volumes_snapshots.py
@@ -41,16 +41,19 @@
     def test_snapshot_create_delete_with_volume_in_use(self):
         # Create a test instance
         server = self.create_server()
-        self.attach_volume(server['id'], self.volume_origin['id'])
+        # NOTE(zhufl) Here we create volume from self.image_ref for adding
+        # coverage for "creating snapshot from non-blank volume".
+        volume = self.create_volume(image_ref=self.image_ref)
+        self.attach_volume(server['id'], volume['id'])
 
         # Snapshot a volume which attached to an instance with force=False
         self.assertRaises(lib_exc.BadRequest, self.create_snapshot,
-                          self.volume_origin['id'], force=False)
+                          volume['id'], force=False)
 
         # Snapshot a volume attached to an instance
-        snapshot1 = self.create_snapshot(self.volume_origin['id'], force=True)
-        snapshot2 = self.create_snapshot(self.volume_origin['id'], force=True)
-        snapshot3 = self.create_snapshot(self.volume_origin['id'], force=True)
+        snapshot1 = self.create_snapshot(volume['id'], force=True)
+        snapshot2 = self.create_snapshot(volume['id'], force=True)
+        snapshot3 = self.create_snapshot(volume['id'], force=True)
 
         # Delete the snapshots. Some snapshot implementations can take
         # different paths according to order they are deleted.
diff --git a/tempest/api/volume/test_volumes_snapshots_list.py b/tempest/api/volume/test_volumes_snapshots_list.py
index 507df1f..f12bfd8 100644
--- a/tempest/api/volume/test_volumes_snapshots_list.py
+++ b/tempest/api/volume/test_volumes_snapshots_list.py
@@ -28,13 +28,11 @@
     @classmethod
     def resource_setup(cls):
         super(VolumesSnapshotListTestJSON, cls).resource_setup()
-        cls.snapshot_id_list = []
         volume_origin = cls.create_volume()
 
         # Create snapshots with params
         for _ in range(3):
             snapshot = cls.create_snapshot(volume_origin['id'])
-            cls.snapshot_id_list.append(snapshot['id'])
         cls.snapshot = snapshot
 
     def _list_by_param_values_and_assert(self, with_detail=False, **params):
@@ -151,10 +149,14 @@
     @decorators.idempotent_id('05489dde-44bc-4961-a1f5-3ce7ee7824f7')
     def test_snapshot_list_param_marker(self):
         # The list of snapshots should end before the provided marker
-        params = {'marker': self.snapshot_id_list[1]}
+        snap_list = self.snapshots_client.list_snapshots()['snapshots']
+        # list_snapshots will take the reverse order as they are created.
+        snapshot_id_list = [snap['id'] for snap in snap_list][::-1]
+
+        params = {'marker': snapshot_id_list[1]}
         snap_list = self.snapshots_client.list_snapshots(**params)['snapshots']
         fetched_list_id = [snap['id'] for snap in snap_list]
         # Verify the list of snapshots ends before the provided
         # marker(second snapshot), therefore only the first snapshot
         # should displayed.
-        self.assertEqual(self.snapshot_id_list[:1], fetched_list_id)
+        self.assertEqual(snapshot_id_list[:1], fetched_list_id)
diff --git a/tempest/clients.py b/tempest/clients.py
index b06eafb..707127c 100644
--- a/tempest/clients.py
+++ b/tempest/clients.py
@@ -197,6 +197,10 @@
         self.endpoint_groups_client = self.identity_v3.EndPointGroupsClient(
             **params_v3)
         self.catalog_client = self.identity_v3.CatalogClient(**params_v3)
+        self.project_tags_client = self.identity_v3.ProjectTagsClient(
+            **params_v3)
+        self.application_credentials_client = \
+            self.identity_v3.ApplicationCredentialsClient(**params_v3)
 
         # Token clients do not use the catalog. They only need default_params.
         # They read auth_url, so they should only be set if the corresponding
@@ -267,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()
@@ -282,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/cmd/cleanup_service.py b/tempest/cmd/cleanup_service.py
index 025959a..27e1bc1 100644
--- a/tempest/cmd/cleanup_service.py
+++ b/tempest/cmd/cleanup_service.py
@@ -104,11 +104,11 @@
             self.tenant_filter['tenant_id'] = self.tenant_id
 
     def _filter_by_tenant_id(self, item_list):
-        if (item_list is None
-                or not item_list
-                or not hasattr(self, 'tenant_id')
-                or self.tenant_id is None
-                or 'tenant_id' not in item_list[0]):
+        if (item_list is None or
+                not item_list or
+                not hasattr(self, 'tenant_id') or
+                self.tenant_id is None or
+                'tenant_id' not in item_list[0]):
             return item_list
 
         return [item for item in item_list
@@ -816,8 +816,8 @@
             if not self.is_save_state:
                 roles = [role for role in roles if
                          (role['id'] not in
-                          self.saved_state_json['roles'].keys()
-                          and role['name'] != CONF.identity.admin_role)]
+                          self.saved_state_json['roles'].keys() and
+                          role['name'] != CONF.identity.admin_role)]
                 LOG.debug("List count, %s Roles after reconcile", len(roles))
             return roles
         except Exception:
@@ -852,13 +852,16 @@
     def list(self):
         projects = self.client.list_projects()['projects']
         if not self.is_save_state:
-            projects = [project for project in projects if (project['id']
-                        not in self.saved_state_json['projects'].keys()
-                        and project['name'] != CONF.auth.admin_project_name)]
+            project_ids = self.saved_state_json['projects']
+            projects = [project
+                        for project in projects
+                        if (project['id'] not in project_ids and
+                            project['name'] != CONF.auth.admin_project_name)]
 
         if self.is_preserve:
-            projects = [project for project in projects if project['name']
-                        not in CONF_PROJECTS]
+            projects = [project
+                        for project in projects
+                        if project['name'] not in CONF_PROJECTS]
 
         LOG.debug("List count, %s Projects after reconcile", len(projects))
         return projects
diff --git a/tempest/cmd/init.py b/tempest/cmd/init.py
index 9a85d89..84c8631 100644
--- a/tempest/cmd/init.py
+++ b/tempest/cmd/init.py
@@ -136,7 +136,7 @@
         if not os.path.isdir(local_dir):
             LOG.debug('Creating local working dir: %s', local_dir)
             os.mkdir(local_dir)
-        elif not os.listdir(local_dir) == []:
+        elif os.listdir(local_dir):
             raise OSError("Directory you are trying to initialize already "
                           "exists and is not empty: %s" % local_dir)
 
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 5d27efd..1fb5c8e 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -240,7 +240,13 @@
                      'settings enabled?'),
     cfg.BoolOpt('project_tags',
                 default=False,
-                help='Is the project tags identity v3 API available?')
+                help='Is the project tags identity v3 API available?'),
+    # Application credentials is a default feature in Queens. This config
+    # option can removed once Pike is EOL.
+    cfg.BoolOpt('application_credentials',
+                default=False,
+                help='Does the environment have application credentials '
+                     'enabled?')
 ]
 
 compute_group = cfg.OptGroup(name='compute',
@@ -407,6 +413,10 @@
                 default=False,
                 help='Enable VNC console. This configuration value should '
                      'be same as [nova.vnc]->vnc_enabled in nova.conf'),
+    cfg.StrOpt('vnc_server_header',
+               default='WebSockify',
+               help='Expected VNC server name (WebSockify, nginx, etc) '
+                    'in response header.'),
     cfg.BoolOpt('spice_console',
                 default=False,
                 help='Enable Spice console. This configuration value should '
@@ -742,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='',
@@ -1068,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/checks.py b/tempest/hacking/checks.py
index aae685c..b6e7f8c 100644
--- a/tempest/hacking/checks.py
+++ b/tempest/hacking/checks.py
@@ -228,12 +228,12 @@
     if 'tempest/lib/' not in filename:
         return
 
-    if not ('from tempest' in logical_line
-            or 'import tempest' in logical_line):
+    if not ('from tempest' in logical_line or
+            'import tempest' in logical_line):
         return
 
-    if ('from tempest.lib' in logical_line
-        or 'import tempest.lib' in logical_line):
+    if ('from tempest.lib' in logical_line or
+            'import tempest.lib' in logical_line):
         return
 
     msg = ("T112: tempest.lib should not import local tempest code to avoid "
@@ -266,9 +266,9 @@
     if 'tempest/lib/' not in filename:
         return
 
-    if ('tempest.config' in logical_line
-        or 'from tempest import config' in logical_line
-        or 'oslo_config' in logical_line):
+    if ('tempest.config' in logical_line or
+            'from tempest import config' in logical_line or
+            'oslo_config' in logical_line):
         msg = ('T114: tempest.lib can not have any dependency on tempest '
                'config.')
         yield(0, msg)
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_19/servers.py b/tempest/lib/api_schema/response/compute/v2_19/servers.py
index 05cc32c..fd9e933 100644
--- a/tempest/lib/api_schema/response/compute/v2_19/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_19/servers.py
@@ -14,9 +14,9 @@
 
 import copy
 
-from tempest.lib.api_schema.response.compute.v2_1 import servers as serversv21
 from tempest.lib.api_schema.response.compute.v2_16 import servers \
     as serversv216
+from tempest.lib.api_schema.response.compute.v2_9 import servers as serversv29
 
 list_servers = copy.deepcopy(serversv216.list_servers)
 
@@ -32,20 +32,20 @@
 list_servers_detail['response_body']['properties']['servers']['items'][
     'required'].append('description')
 
-update_server = copy.deepcopy(serversv21.update_server)
+update_server = copy.deepcopy(serversv29.update_server)
 update_server['response_body']['properties']['server'][
     'properties'].update({'description': {'type': ['string', 'null']}})
 update_server['response_body']['properties']['server'][
     'required'].append('description')
 
-rebuild_server = copy.deepcopy(serversv21.rebuild_server)
+rebuild_server = copy.deepcopy(serversv29.rebuild_server)
 rebuild_server['response_body']['properties']['server'][
     'properties'].update({'description': {'type': ['string', 'null']}})
 rebuild_server['response_body']['properties']['server'][
     'required'].append('description')
 
 rebuild_server_with_admin_pass = copy.deepcopy(
-    serversv21.rebuild_server_with_admin_pass)
+    serversv29.rebuild_server_with_admin_pass)
 rebuild_server_with_admin_pass['response_body']['properties']['server'][
     'properties'].update({'description': {'type': ['string', 'null']}})
 rebuild_server_with_admin_pass['response_body']['properties']['server'][
diff --git a/tempest/lib/api_schema/response/compute/v2_26/servers.py b/tempest/lib/api_schema/response/compute/v2_26/servers.py
index b03bdf6..5c35eab 100644
--- a/tempest/lib/api_schema/response/compute/v2_26/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_26/servers.py
@@ -43,6 +43,25 @@
 list_servers_detail['response_body']['properties']['servers']['items'][
     'required'].append('tags')
 
+update_server = copy.deepcopy(servers219.update_server)
+update_server['response_body']['properties']['server'][
+    'properties'].update({'tags': tag_items})
+update_server['response_body']['properties']['server'][
+    'required'].append('tags')
+
+rebuild_server = copy.deepcopy(servers219.rebuild_server)
+rebuild_server['response_body']['properties']['server'][
+    'properties'].update({'tags': tag_items})
+rebuild_server['response_body']['properties']['server'][
+    'required'].append('tags')
+
+rebuild_server_with_admin_pass = copy.deepcopy(
+    servers219.rebuild_server_with_admin_pass)
+rebuild_server_with_admin_pass['response_body']['properties']['server'][
+    'properties'].update({'tags': tag_items})
+rebuild_server_with_admin_pass['response_body']['properties']['server'][
+    'required'].append('tags')
+
 # list response schema wasn't changed for v2.26 so use v2.1
 
 list_servers = copy.deepcopy(servers21.list_servers)
diff --git a/tempest/lib/api_schema/response/compute/v2_47/servers.py b/tempest/lib/api_schema/response/compute/v2_47/servers.py
index 37a084f..935be70 100644
--- a/tempest/lib/api_schema/response/compute/v2_47/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_47/servers.py
@@ -37,3 +37,19 @@
 get_server = copy.deepcopy(servers226.get_server)
 get_server['response_body']['properties']['server'][
     'properties'].update({'flavor': flavor})
+list_servers_detail = copy.deepcopy(servers226.list_servers_detail)
+list_servers_detail['response_body']['properties']['servers']['items'][
+    'properties'].update({'flavor': flavor})
+
+update_server = copy.deepcopy(servers226.update_server)
+update_server['response_body']['properties']['server'][
+    'properties'].update({'flavor': flavor})
+
+rebuild_server = copy.deepcopy(servers226.rebuild_server)
+rebuild_server['response_body']['properties']['server'][
+    'properties'].update({'flavor': flavor})
+
+rebuild_server_with_admin_pass = copy.deepcopy(
+    servers226.rebuild_server_with_admin_pass)
+rebuild_server_with_admin_pass['response_body']['properties']['server'][
+    'properties'].update({'flavor': flavor})
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/api_schema/response/compute/v2_9/servers.py b/tempest/lib/api_schema/response/compute/v2_9/servers.py
index e260e48..7df02d5 100644
--- a/tempest/lib/api_schema/response/compute/v2_9/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_9/servers.py
@@ -14,6 +14,7 @@
 
 import copy
 
+from tempest.lib.api_schema.response.compute.v2_1 import servers as servers_21
 from tempest.lib.api_schema.response.compute.v2_6 import servers
 
 list_servers = copy.deepcopy(servers.list_servers)
@@ -29,3 +30,22 @@
     'properties'].update({'locked': {'type': 'boolean'}})
 list_servers_detail['response_body']['properties']['servers']['items'][
     'required'].append('locked')
+
+update_server = copy.deepcopy(servers_21.update_server)
+update_server['response_body']['properties']['server'][
+    'properties'].update({'locked': {'type': 'boolean'}})
+update_server['response_body']['properties']['server'][
+    'required'].append('locked')
+
+rebuild_server = copy.deepcopy(servers_21.rebuild_server)
+rebuild_server['response_body']['properties']['server'][
+    'properties'].update({'locked': {'type': 'boolean'}})
+rebuild_server['response_body']['properties']['server'][
+    'required'].append('locked')
+
+rebuild_server_with_admin_pass = copy.deepcopy(
+    servers_21.rebuild_server_with_admin_pass)
+rebuild_server_with_admin_pass['response_body']['properties']['server'][
+    'properties'].update({'locked': {'type': 'boolean'}})
+rebuild_server_with_admin_pass['response_body']['properties']['server'][
+    'required'].append('locked')
diff --git a/tempest/lib/base.py b/tempest/lib/base.py
index 33a32ee..3be55c0 100644
--- a/tempest/lib/base.py
+++ b/tempest/lib/base.py
@@ -43,8 +43,7 @@
         super(BaseTestCase, self).setUp()
         if not self.setUpClassCalled:
             raise RuntimeError("setUpClass does not calls the super's "
-                               "setUpClass in the "
-                               + self.__class__.__name__)
+                               "setUpClass in {!r}".format(type(self)))
         test_timeout = os.environ.get('OS_TEST_TIMEOUT', 0)
         try:
             test_timeout = int(test_timeout)
@@ -62,7 +61,7 @@
             stderr = self.useFixture(fixtures.StringStream('stderr')).stream
             self.useFixture(fixtures.MonkeyPatch('sys.stderr', stderr))
         if (os.environ.get('OS_LOG_CAPTURE') != 'False' and
-            os.environ.get('OS_LOG_CAPTURE') != '0'):
+                os.environ.get('OS_LOG_CAPTURE') != '0'):
             self.useFixture(fixtures.LoggerFixture(nuke_handlers=False,
                                                    format=self.log_format,
                                                    level=None))
diff --git a/tempest/lib/cli/output_parser.py b/tempest/lib/cli/output_parser.py
index 2edd5c1..a7d5e49 100644
--- a/tempest/lib/cli/output_parser.py
+++ b/tempest/lib/cli/output_parser.py
@@ -37,8 +37,8 @@
     items = []
     tables_ = tables(output_lines)
     for table_ in tables_:
-        if ('Property' not in table_['headers']
-            or 'Value' not in table_['headers']):
+        if ('Property' not in table_['headers'] or
+                'Value' not in table_['headers']):
             raise exceptions.InvalidStructure()
         item = {}
         for value in table_['values']:
diff --git a/tempest/lib/cmd/check_uuid.py b/tempest/lib/cmd/check_uuid.py
index 101d692..d1f0888 100755
--- a/tempest/lib/cmd/check_uuid.py
+++ b/tempest/lib/cmd/check_uuid.py
@@ -103,7 +103,7 @@
     def _modules_search(self):
         """Recursive search for python modules in base package"""
         modules = []
-        for root, dirs, files in os.walk(self.base_path):
+        for root, _, files in os.walk(self.base_path):
             if not os.path.exists(os.path.join(root, '__init__.py')):
                 continue
             root_package = self._path_to_package(root)
@@ -121,10 +121,10 @@
         idempotent_id = None
         for decorator in test_node.decorator_list:
             if (hasattr(decorator, 'func') and
-                hasattr(decorator.func, 'attr') and
-                decorator.func.attr == DECORATOR_NAME and
-                hasattr(decorator.func, 'value') and
-                decorator.func.value.id == DECORATOR_MODULE):
+                    hasattr(decorator.func, 'attr') and
+                    decorator.func.attr == DECORATOR_NAME and
+                    hasattr(decorator.func, 'value') and
+                    decorator.func.value.id == DECORATOR_MODULE):
                 for arg in decorator.args:
                     idempotent_id = ast.literal_eval(arg)
         return idempotent_id
@@ -165,8 +165,8 @@
 
     @staticmethod
     def _is_test_method(node):
-        return (node.__class__ is ast.FunctionDef
-                and node.name.startswith('test_'))
+        return (node.__class__ is ast.FunctionDef and
+                node.name.startswith('test_'))
 
     @staticmethod
     def _next_node(body, node):
diff --git a/tempest/lib/common/dynamic_creds.py b/tempest/lib/common/dynamic_creds.py
index 4f1a883..f27e926 100644
--- a/tempest/lib/common/dynamic_creds.py
+++ b/tempest/lib/common/dynamic_creds.py
@@ -338,15 +338,15 @@
                 credentials = self._create_creds(roles=credential_type)
             self._creds[str(credential_type)] = credentials
             # Maintained until tests are ported
-            LOG.info("Acquired dynamic creds:\n credentials: %s", credentials)
-            if (self.neutron_available and
-                self.create_networks):
+            LOG.info("Acquired dynamic creds:\n"
+                     " credentials: %s", credentials)
+            if (self.neutron_available and self.create_networks):
                 network, subnet, router = self._create_network_resources(
                     credentials.tenant_id)
                 credentials.set_resources(network=network, subnet=subnet,
                                           router=router)
-                LOG.info("Created isolated network resources for : \n"
-                         + " credentials: %s", credentials)
+                LOG.info("Created isolated network resources for:\n"
+                         " credentials: %s", credentials)
         return credentials
 
     def get_primary_creds(self):
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 09bccab..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):
@@ -156,11 +158,11 @@
 
         url = 'servers'
         schema = self.get_schema(self.schema_versions_info)
-        _schema = schema.list_servers
-
         if detail:
             url += '/detail'
             _schema = schema.list_servers_detail
+        else:
+            _schema = schema.list_servers
         if params:
             url += '?%s' % urllib.urlencode(params)
 
diff --git a/tempest/lib/services/identity/v3/__init__.py b/tempest/lib/services/identity/v3/__init__.py
index a539d08..da1c51c 100644
--- a/tempest/lib/services/identity/v3/__init__.py
+++ b/tempest/lib/services/identity/v3/__init__.py
@@ -12,6 +12,8 @@
 # License for the specific language governing permissions and limitations under
 # the License.
 
+from tempest.lib.services.identity.v3.application_credentials_client import \
+    ApplicationCredentialsClient
 from tempest.lib.services.identity.v3.catalog_client import \
     CatalogClient
 from tempest.lib.services.identity.v3.credentials_client import \
@@ -33,6 +35,8 @@
 from tempest.lib.services.identity.v3.oauth_token_client import \
     OAUTHTokenClient
 from tempest.lib.services.identity.v3.policies_client import PoliciesClient
+from tempest.lib.services.identity.v3.project_tags_client import \
+    ProjectTagsClient
 from tempest.lib.services.identity.v3.projects_client import ProjectsClient
 from tempest.lib.services.identity.v3.regions_client import RegionsClient
 from tempest.lib.services.identity.v3.role_assignments_client import \
@@ -44,11 +48,11 @@
 from tempest.lib.services.identity.v3.users_client import UsersClient
 from tempest.lib.services.identity.v3.versions_client import VersionsClient
 
-__all__ = ['CatalogClient', 'CredentialsClient', 'DomainsClient',
-           'DomainConfigurationClient', 'EndPointGroupsClient',
-           'EndPointsClient', 'EndPointsFilterClient', 'GroupsClient',
-           'IdentityClient', 'InheritedRolesClient', 'OAUTHConsumerClient',
-           'OAUTHTokenClient', 'PoliciesClient', 'ProjectsClient',
-           'RegionsClient', 'RoleAssignmentsClient', 'RolesClient',
-           'ServicesClient', 'V3TokenClient', 'TrustsClient', 'UsersClient',
-           'VersionsClient']
+__all__ = ['ApplicationCredentialsClient', 'CatalogClient',
+           'CredentialsClient', 'DomainsClient', 'DomainConfigurationClient',
+           'EndPointGroupsClient', 'EndPointsClient', 'EndPointsFilterClient',
+           'GroupsClient', 'IdentityClient', 'InheritedRolesClient',
+           'OAUTHConsumerClient', 'OAUTHTokenClient', 'PoliciesClient',
+           'ProjectsClient', 'ProjectTagsClient', 'RegionsClient',
+           'RoleAssignmentsClient', 'RolesClient', 'ServicesClient',
+           'V3TokenClient', 'TrustsClient', 'UsersClient', 'VersionsClient']
diff --git a/tempest/lib/services/identity/v3/application_credentials_client.py b/tempest/lib/services/identity/v3/application_credentials_client.py
new file mode 100644
index 0000000..557aa9e
--- /dev/null
+++ b/tempest/lib/services/identity/v3/application_credentials_client.py
@@ -0,0 +1,83 @@
+# Copyright 2018 SUSE Linux GmbH
+#
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+"""
+https://developer.openstack.org/api-ref/identity/v3/index.html#application-credentials
+"""
+
+from oslo_serialization import jsonutils as json
+from six.moves.urllib import parse as urllib
+
+from tempest.lib.common import rest_client
+
+
+class ApplicationCredentialsClient(rest_client.RestClient):
+    api_version = "v3"
+
+    def create_application_credential(self, user_id, **kwargs):
+        """Creates an application credential.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/identity/v3/index.html#create-application-credential
+        """
+        post_body = json.dumps({'application_credential': kwargs})
+        resp, body = self.post('users/%s/application_credentials' % user_id,
+                               post_body)
+        self.expected_success(201, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_application_credential(self, user_id, application_credential_id):
+        """Gets details of an application credential.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/identity/v3/index.html#show-application-credential-details
+        """
+        resp, body = self.get('users/%s/application_credentials/%s' %
+                              (user_id, application_credential_id))
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def list_application_credentials(self, user_id, **params):
+        """Lists out all of a user's application credentials.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/identity/v3/index.html#list-application-credentials
+        """
+        url = 'users/%s/application_credentials' % user_id
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+        resp, body = self.get(url)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_application_credential(self, user_id,
+                                      application_credential_id):
+        """Deletes an application credential.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/identity/v3/index.html#delete-application-credential
+        """
+        resp, body = self.delete('users/%s/application_credentials/%s' %
+                                 (user_id, application_credential_id))
+        self.expected_success(204, resp.status)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/identity/v3/project_tags_client.py b/tempest/lib/services/identity/v3/project_tags_client.py
new file mode 100644
index 0000000..dd1a2a5
--- /dev/null
+++ b/tempest/lib/services/identity/v3/project_tags_client.py
@@ -0,0 +1,80 @@
+# Copyright 2018 AT&T Corporation.
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from oslo_serialization import jsonutils as json
+
+from tempest.lib.common import rest_client
+
+
+class ProjectTagsClient(rest_client.RestClient):
+    api_version = "v3"
+
+    def update_project_tag(self, project_id, tag):
+        """Updates the specified tag and adds it to the project's list of tags.
+
+        """
+        url = 'projects/%s/tags/%s' % (project_id, tag)
+        resp, body = self.put(url, '{}')
+        # NOTE(felipemonteiro): This API endpoint returns 201 AND an empty
+        # response body, which is consistent with the spec:
+        # https://specs.openstack.org/openstack/api-wg/guidelines/tags.html#addressing-individual-tags
+        self.expected_success(201, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def list_project_tags(self, project_id):
+        """List tags for a project."""
+        url = "projects/%s/tags" % project_id
+        resp, body = self.get(url)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_all_project_tags(self, project_id, tags, **kwargs):
+        """Updates all the tags for a project.
+
+        Any existing tags not specified will be deleted.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/identity/v3/#modify-tag-list-for-a-project
+        """
+        body = {'tags': tags}
+        if kwargs:
+            body.update(kwargs)
+        put_body = json.dumps(body)
+        resp, body = self.put('projects/%s/tags' % project_id, put_body)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def check_project_tag_existence(self, project_id, tag):
+        """Check if a project contains a tag."""
+        url = 'projects/%s/tags/%s' % (project_id, tag)
+        resp, body = self.get(url)
+        self.expected_success(204, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_project_tag(self, project_id, tag):
+        """Delete a project tag."""
+        url = 'projects/%s/tags/%s' % (project_id, tag)
+        resp, body = self.delete(url)
+        self.expected_success(204, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_all_project_tags(self, project_id):
+        """Delete all tags from a project."""
+        resp, body = self.delete('projects/%s/tags' % project_id)
+        self.expected_success(204, resp.status)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/identity/v3/token_client.py b/tempest/lib/services/identity/v3/token_client.py
index 33f6f16..d591f03 100644
--- a/tempest/lib/services/identity/v3/token_client.py
+++ b/tempest/lib/services/identity/v3/token_client.py
@@ -51,7 +51,8 @@
     def auth(self, user_id=None, username=None, password=None, project_id=None,
              project_name=None, user_domain_id=None, user_domain_name=None,
              project_domain_id=None, project_domain_name=None, domain_id=None,
-             domain_name=None, token=None):
+             domain_name=None, token=None, app_cred_id=None,
+             app_cred_secret=None):
         """Obtains a token from the authentication service
 
         :param user_id: user id
@@ -109,6 +110,13 @@
             if _domain:
                 id_obj['password']['user']['domain'] = _domain
 
+        if app_cred_id and app_cred_secret:
+            id_obj['methods'].append('application_credential')
+            id_obj['application_credential'] = {
+                'id': app_cred_id,
+                'secret': app_cred_secret,
+            }
+
         if (project_id or project_name):
             _project = dict()
 
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 c78646f..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)
@@ -662,8 +692,8 @@
                 addresses = (server['addresses'][network['name']]
                              if network else [])
             for address in addresses:
-                if (address['version'] == CONF.validation.ip_version_for_ssh
-                        and address['OS-EXT-IPS:type'] == 'fixed'):
+                if (address['version'] == CONF.validation.ip_version_for_ssh and  # noqa
+                        address['OS-EXT-IPS:type'] == 'fixed'):
                     return address['addr']
             raise exceptions.ServerUnreachable(server_id=server['id'])
         else:
@@ -792,8 +822,8 @@
         port_map = [(p["id"], fxip["ip_address"])
                     for p in ports
                     for fxip in p["fixed_ips"]
-                    if netutils.is_valid_ipv4(fxip["ip_address"])
-                    and p['status'] in p_status]
+                    if (netutils.is_valid_ipv4(fxip["ip_address"]) and
+                        p['status'] in p_status)]
         inactive = [p for p in ports if p['status'] != 'ACTIVE']
         if inactive:
             LOG.warning("Instance has ports that are not ACTIVE: %s", inactive)
diff --git a/tempest/scenario/test_aggregates_basic_ops.py b/tempest/scenario/test_aggregates_basic_ops.py
index f762995..b515639 100644
--- a/tempest/scenario/test_aggregates_basic_ops.py
+++ b/tempest/scenario/test_aggregates_basic_ops.py
@@ -37,7 +37,7 @@
         super(TestAggregatesBasicOps, cls).setup_clients()
         # Use admin client by default
         cls.aggregates_client = cls.os_admin.aggregates_client
-        cls.hyper_client = cls.os_admin.hypervisor_client
+        cls.services_client = cls.os_admin.services_client
 
     def _create_aggregate(self, **kwargs):
         aggregate = (self.aggregates_client.create_aggregate(**kwargs)
@@ -51,9 +51,10 @@
         return aggregate
 
     def _get_host_name(self):
-        hyper_list = self.hyper_client.list_hypervisors()['hypervisors']
-        self.assertNotEmpty(hyper_list)
-        return hyper_list[0]['hypervisor_hostname']
+        svc_list = self.services_client.list_services(
+            binary='nova-compute')['services']
+        self.assertNotEmpty(svc_list)
+        return svc_list[0]['host']
 
     def _add_host(self, aggregate_id, host):
         aggregate = (self.aggregates_client.add_host(aggregate_id, host=host)
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_network_advanced_server_ops.py b/tempest/scenario/test_network_advanced_server_ops.py
index e4ab11c..87ce951 100644
--- a/tempest/scenario/test_network_advanced_server_ops.py
+++ b/tempest/scenario/test_network_advanced_server_ops.py
@@ -43,8 +43,8 @@
     @classmethod
     def skip_checks(cls):
         super(TestNetworkAdvancedServerOps, cls).skip_checks()
-        if not (CONF.network.project_networks_reachable
-                or CONF.network.public_network_id):
+        if not (CONF.network.project_networks_reachable or
+                CONF.network.public_network_id):
             msg = ('Either project_networks_reachable must be "true", or '
                    'public_network_id must be defined.')
             raise cls.skipException(msg)
diff --git a/tempest/scenario/test_network_basic_ops.py b/tempest/scenario/test_network_basic_ops.py
index fd9c985..bcd4ddb 100644
--- a/tempest/scenario/test_network_basic_ops.py
+++ b/tempest/scenario/test_network_basic_ops.py
@@ -81,8 +81,8 @@
     @classmethod
     def skip_checks(cls):
         super(TestNetworkBasicOps, cls).skip_checks()
-        if not (CONF.network.project_networks_reachable
-                or CONF.network.public_network_id):
+        if not (CONF.network.project_networks_reachable or
+                CONF.network.public_network_id):
             msg = ('Either project_networks_reachable must be "true", or '
                    'public_network_id must be defined.')
             raise cls.skipException(msg)
diff --git a/tempest/scenario/test_network_v6.py b/tempest/scenario/test_network_v6.py
index 9f4e62b..e4e39c3 100644
--- a/tempest/scenario/test_network_v6.py
+++ b/tempest/scenario/test_network_v6.py
@@ -38,11 +38,11 @@
     @classmethod
     def skip_checks(cls):
         super(TestGettingAddress, cls).skip_checks()
-        if not (CONF.network_feature_enabled.ipv6
-                and CONF.network_feature_enabled.ipv6_subnet_attributes):
+        if not (CONF.network_feature_enabled.ipv6 and
+                CONF.network_feature_enabled.ipv6_subnet_attributes):
             raise cls.skipException('IPv6 or its attributes not supported')
-        if not (CONF.network.project_networks_reachable
-                or CONF.network.public_network_id):
+        if not (CONF.network.project_networks_reachable or
+                CONF.network.public_network_id):
             msg = ('Either project_networks_reachable must be "true", or '
                    'public_network_id must be defined.')
             raise cls.skipException(msg)
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 7ceae89..2d024e9 100644
--- a/tempest/scenario/test_volume_boot_pattern.py
+++ b/tempest/scenario/test_volume_boot_pattern.py
@@ -193,38 +193,54 @@
     @testtools.skipUnless(CONF.volume_feature_enabled.snapshot,
                           'Cinder volume snapshots are disabled')
     @utils.services('compute', 'volume', 'image')
-    def test_create_ebs_image_and_check_boot(self):
-        # create an instance from volume
+    def test_image_defined_boot_from_volume(self):
+        # create an instance from image-backed volume
         volume_origin = self._create_volume_from_image()
         instance = self._boot_instance_from_resource(
             source_id=volume_origin['id'],
             source_type='volume',
             delete_on_termination=True)
-        # create EBS image
+        # Create a snapshot image from the volume-backed server.
+        # The compute service will have the block service create a snapshot of
+        # the root volume and store its metadata in the image.
         image = self.create_server_snapshot(instance)
 
-        # delete instance
+        # Delete the first server which will also delete the first image-backed
+        # volume.
         self._delete_server(instance)
 
-        # boot instance from EBS image
+        # Create a server from the image snapshot which has an
+        # "image-defined block device mapping (BDM)" in it, i.e. the metadata
+        # about the volume snapshot. The compute service will use this to
+        # create a volume from the volume snapshot and use that as the root
+        # disk for the server.
         instance = self.create_server(image_id=image['id'])
 
-        # Verify the server was created from the image
-        created_volume = instance['os-extended-volumes:volumes_attached']
-        self.assertNotEmpty(created_volume, "No volume attachment found.")
-        created_volume_info = self.volumes_client.show_volume(
-            created_volume[0]['id'])['volume']
+        # Verify the server was created from the image-defined BDM.
+        volume_attachments = instance['os-extended-volumes:volumes_attached']
+        self.assertEqual(1, len(volume_attachments),
+                         "No volume attachment found.")
+        created_volume = self.volumes_client.show_volume(
+            volume_attachments[0]['id'])['volume']
+        # Assert that the volume service also shows the server attachment.
+        self.assertEqual(1, len(created_volume['attachments']),
+                         "No server attachment found for volume: %s" %
+                         created_volume)
         self.assertEqual(instance['id'],
-                         created_volume_info['attachments'][0]['server_id'])
-        self.assertEqual(created_volume[0]['id'],
-                         created_volume_info['attachments'][0]['volume_id'])
+                         created_volume['attachments'][0]['server_id'])
+        self.assertEqual(volume_attachments[0]['id'],
+                         created_volume['attachments'][0]['volume_id'])
         self.assertEqual(
             volume_origin['volume_image_metadata']['image_id'],
-            created_volume_info['volume_image_metadata']['image_id'])
+            created_volume['volume_image_metadata']['image_id'])
 
-        # delete instance
+        # Delete the second server which should also delete the second volume
+        # created from the volume snapshot.
         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/test.py b/tempest/test.py
index 27e0165..f2babbb 100644
--- a/tempest/test.py
+++ b/tempest/test.py
@@ -68,9 +68,9 @@
 def validate_tearDownClass():
     if at_exit_set:
         LOG.error(
-            "tearDownClass does not call the super's "
-            "tearDownClass in these classes: \n"
-            + str(at_exit_set))
+            "tearDownClass does not call the super's tearDownClass in "
+            "these classes:\n"
+            "  %s", at_exit_set)
 
 
 atexit.register(validate_tearDownClass)
@@ -582,8 +582,8 @@
         super(BaseTestCase, self).setUp()
         if not self.__setupclass_called:
             raise RuntimeError("setUpClass does not calls the super's"
-                               "setUpClass in the "
-                               + self.__class__.__name__)
+                               "setUpClass in the " +
+                               self.__class__.__name__)
         at_exit_set.add(self.__class__)
         test_timeout = os.environ.get('OS_TEST_TIMEOUT', 0)
         try:
@@ -602,7 +602,7 @@
             stderr = self.useFixture(fixtures.StringStream('stderr')).stream
             self.useFixture(fixtures.MonkeyPatch('sys.stderr', stderr))
         if (os.environ.get('OS_LOG_CAPTURE') != 'False' and
-            os.environ.get('OS_LOG_CAPTURE') != '0'):
+                os.environ.get('OS_LOG_CAPTURE') != '0'):
             self.useFixture(fixtures.LoggerFixture(nuke_handlers=False,
                                                    format=self.log_format,
                                                    level=None))
diff --git a/tempest/tests/api/compute/test_base.py b/tempest/tests/api/compute/test_base.py
index 5024100..47f4ad6 100644
--- a/tempest/tests/api/compute/test_base.py
+++ b/tempest/tests/api/compute/test_base.py
@@ -173,3 +173,20 @@
         # make our assertions
         wait_for_image_status.assert_called_once_with(
             compute_images_client, image_id, 'SAVING')
+
+    def _test_version_compatible(self, max_version, expected=True):
+        actual = (compute_base.BaseV2ComputeTest.
+                  is_requested_microversion_compatible(max_version))
+        self.assertEqual(expected, actual)
+
+    def test_check_lower_version(self):
+        compute_base.BaseV2ComputeTest.request_microversion = '2.8'
+        self._test_version_compatible('2.40')
+
+    def test_check_euqal_version(self):
+        compute_base.BaseV2ComputeTest.request_microversion = '2.40'
+        self._test_version_compatible('2.40')
+
+    def test_check_higher_version(self):
+        compute_base.BaseV2ComputeTest.request_microversion = '2.41'
+        self._test_version_compatible('2.40', expected=False)
diff --git a/tempest/tests/base.py b/tempest/tests/base.py
index ca81d4d..0b53b45 100644
--- a/tempest/tests/base.py
+++ b/tempest/tests/base.py
@@ -18,7 +18,7 @@
 
 class TestCase(base.BaseTestCase):
 
-    def patch(self, target, **kwargs):
+    def patch(self, target, *args, **kwargs):
         """Returns a started `mock.patch` object for the supplied target.
 
         The caller may then call the returned patcher to create a mock object.
@@ -27,23 +27,35 @@
         patcher object, as this method automatically adds a cleanup
         to the test class to stop the patcher.
 
-        :param target: String module.class or module.object expression to patch
-        :param **kwargs: Passed as-is to `mock.patch`. See mock documentation
-                         for details.
+        :param target: string module.class or module.object expression to patch
+        :param *args: passed as-is to `mock.patch`.
+        :param **kwargs: passed as-is to `mock.patch`.
+
+        See mock documentation for more details:
+        https://docs.python.org/3.5/library/unittest.mock.html#unittest.mock.patch
         """
-        p = mock.patch(target, **kwargs)
+
+        p = mock.patch(target, *args, **kwargs)
         m = p.start()
         self.addCleanup(p.stop)
         return m
 
-    def patchobject(self, target, attribute, new=mock.DEFAULT):
+    def patchobject(self, target, attribute, *args, **kwargs):
         """Convenient wrapper around `mock.patch.object`
 
         Returns a started mock that will be automatically stopped after the
         test ran.
+
+        :param target: object to have the attribute patched
+        :param attribute: name of the attribute to be patched
+        :param *args: passed as-is to `mock.patch.object`.
+        :param **kwargs: passed as-is to `mock.patch.object`.
+
+        See mock documentation for more details:
+        https://docs.python.org/3.5/library/unittest.mock.html#unittest.mock.patch.object
         """
 
-        p = mock.patch.object(target, attribute, new)
+        p = mock.patch.object(target, attribute, *args, **kwargs)
         m = p.start()
         self.addCleanup(p.stop)
         return m
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/identity/v3/test_application_credentials_client.py b/tempest/tests/lib/services/identity/v3/test_application_credentials_client.py
new file mode 100644
index 0000000..9bf9b68
--- /dev/null
+++ b/tempest/tests/lib/services/identity/v3/test_application_credentials_client.py
@@ -0,0 +1,156 @@
+# Copyright 2018 SUSE Linux GmbH
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from tempest.lib.services.identity.v3 import application_credentials_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestApplicationCredentialsClient(base.BaseServiceTest):
+    FAKE_CREATE_APP_CRED = {
+        "application_credential": {
+            "description": "fake application credential",
+            "roles": [
+                {
+                    "id": "c60fdd45",
+                    "domain_id": None,
+                    "name": "Member"
+                }
+            ],
+            "expires_at": "2019-02-27T18:30:59.999999Z",
+            "secret": "_BVq0xU5L",
+            "unrestricted": None,
+            "project_id": "ddef321",
+            "id": "5499a186",
+            "name": "one"
+        }
+    }
+
+    FAKE_LIST_APP_CREDS = {
+        "application_credentials": [
+            {
+                "description": "fake application credential",
+                "roles": [
+                    {
+                        "domain_id": None,
+                        "name": "Member",
+                        "id": "c60fdd45",
+                    }
+                ],
+                "expires_at": "2018-02-27T18:30:59.999999Z",
+                "unrestricted": None,
+                "project_id": "ddef321",
+                "id": "5499a186",
+                "name": "one"
+            },
+            {
+                "description": None,
+                "roles": [
+                    {
+                        "id": "0f1837c8",
+                        "domain_id": None,
+                        "name": "anotherrole"
+                    },
+                    {
+                        "id": "c60fdd45",
+                        "domain_id": None,
+                        "name": "Member"
+                    }
+                ],
+                "expires_at": None,
+                "unrestricted": None,
+                "project_id": "c5403d938",
+                "id": "d441c904f",
+                "name": "two"
+            }
+        ]
+    }
+
+    FAKE_APP_CRED_INFO = {
+        "application_credential": {
+            "description": None,
+            "roles": [
+                {
+                    "domain_id": None,
+                    "name": "Member",
+                    "id": "c60fdd45",
+                }
+            ],
+            "expires_at": None,
+            "unrestricted": None,
+            "project_id": "ddef321",
+            "id": "5499a186",
+            "name": "one"
+        }
+    }
+
+    def setUp(self):
+        super(TestApplicationCredentialsClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = \
+            application_credentials_client.ApplicationCredentialsClient(
+                fake_auth, 'identity', 'regionOne')
+
+    def _test_create_app_cred(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.create_application_credential,
+            'tempest.lib.common.rest_client.RestClient.post',
+            self.FAKE_CREATE_APP_CRED,
+            bytes_body,
+            status=201,
+            user_id="123456")
+
+    def _test_show_app_cred(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.show_application_credential,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_APP_CRED_INFO,
+            bytes_body,
+            user_id="123456",
+            application_credential_id="5499a186")
+
+    def _test_list_app_creds(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_application_credentials,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_LIST_APP_CREDS,
+            bytes_body,
+            user_id="123456")
+
+    def test_create_application_credential_with_str_body(self):
+        self._test_create_app_cred()
+
+    def test_create_application_credential_with_bytes_body(self):
+        self._test_create_app_cred(bytes_body=True)
+
+    def test_show_application_credential_with_str_body(self):
+        self._test_show_app_cred()
+
+    def test_show_application_credential_with_bytes_body(self):
+        self._test_show_app_cred(bytes_body=True)
+
+    def test_list_application_credential_with_str_body(self):
+        self._test_list_app_creds()
+
+    def test_list_application_credential_with_bytes_body(self):
+        self._test_list_app_creds(bytes_body=True)
+
+    def test_delete_trust(self):
+        self.check_service_client_function(
+            self.client.delete_application_credential,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {},
+            user_id="123456",
+            application_credential_id="5499a186",
+            status=204)
diff --git a/tempest/tests/lib/services/identity/v3/test_project_tags_client.py b/tempest/tests/lib/services/identity/v3/test_project_tags_client.py
new file mode 100644
index 0000000..2d65a29
--- /dev/null
+++ b/tempest/tests/lib/services/identity/v3/test_project_tags_client.py
@@ -0,0 +1,104 @@
+# Copyright 2018 AT&T Corporation.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from tempest.lib.services.identity.v3 import project_tags_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestProjectTagsClient(base.BaseServiceTest):
+
+    FAKE_PROJECT_ID = "0c4e939acacf4376bdcd1129f1a054ad"
+
+    FAKE_PROJECT_TAG = "foo"
+
+    FAKE_PROJECT_TAGS = ["foo", "bar"]
+
+    def setUp(self):
+        super(TestProjectTagsClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = project_tags_client.ProjectTagsClient(fake_auth,
+                                                            'identity',
+                                                            'regionOne')
+
+    def _test_update_project_tag(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.update_project_tag,
+            'tempest.lib.common.rest_client.RestClient.put',
+            {},
+            bytes_body,
+            project_id=self.FAKE_PROJECT_ID,
+            tag=self.FAKE_PROJECT_TAG,
+            status=201)
+
+    def _test_list_project_tags(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_project_tags,
+            'tempest.lib.common.rest_client.RestClient.get',
+            {"tags": self.FAKE_PROJECT_TAGS},
+            bytes_body,
+            project_id=self.FAKE_PROJECT_ID)
+
+    def _test_update_all_project_tags(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.update_all_project_tags,
+            'tempest.lib.common.rest_client.RestClient.put',
+            {"tags": self.FAKE_PROJECT_TAGS},
+            bytes_body,
+            project_id=self.FAKE_PROJECT_ID,
+            tags=self.FAKE_PROJECT_TAGS)
+
+    def test_update_project_tag_with_str_body(self):
+        self._test_update_project_tag()
+
+    def test_update_project_tag_with_bytes_body(self):
+        self._test_update_project_tag(bytes_body=True)
+
+    def test_list_project_tags_with_str_body(self):
+        self._test_list_project_tags()
+
+    def test_list_project_tags_with_bytes_body(self):
+        self._test_list_project_tags(bytes_body=True)
+
+    def test_update_all_project_tags_with_str_body(self):
+        self._test_update_all_project_tags()
+
+    def test_update_all_project_tags_with_bytes_body(self):
+        self._test_update_all_project_tags(bytes_body=True)
+
+    def test_check_project_project_tag_existence(self):
+        self.check_service_client_function(
+            self.client.check_project_tag_existence,
+            'tempest.lib.common.rest_client.RestClient.get',
+            {},
+            project_id=self.FAKE_PROJECT_ID,
+            tag=self.FAKE_PROJECT_TAG,
+            status=204)
+
+    def test_delete_project_tag(self):
+        self.check_service_client_function(
+            self.client.delete_project_tag,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {},
+            project_id=self.FAKE_PROJECT_ID,
+            tag=self.FAKE_PROJECT_TAG,
+            status=204)
+
+    def test_delete_all_project_tags(self):
+        self.check_service_client_function(
+            self.client.delete_all_project_tags,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {},
+            project_id=self.FAKE_PROJECT_ID,
+            status=204)
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)
 
diff --git a/tools/generate-tempest-plugins-list.py b/tools/generate-tempest-plugins-list.py
index dd05438..bbb9019 100644
--- a/tools/generate-tempest-plugins-list.py
+++ b/tools/generate-tempest-plugins-list.py
@@ -75,11 +75,13 @@
 # json library won't choke.
 projects = sorted(filter(is_in_openstack_namespace, json.loads(r.read()[4:])))
 
-# Retrieve projects having no deb, ui or spec namespace as those namespaces
-# do not contains tempest plugins.
-projects_list = [i for i in projects if not (i.startswith('openstack/deb-') or
-                                             i.endswith('-ui') or
-                                             i.endswith('-specs'))]
+# Retrieve projects having no deb, puppet, ui or spec namespace as those
+# namespaces do not contains tempest plugins.
+projects_list = [i for i in projects if not (
+    i.startswith('openstack/deb-') or
+    i.startswith('openstack/puppet-') or
+    i.endswith('-ui') or
+    i.endswith('-specs'))]
 
 found_plugins = list(filter(has_tempest_plugin, projects_list))
 
diff --git a/tox.ini b/tox.ini
index 9103175..da0233a 100644
--- a/tox.ini
+++ b/tox.ini
@@ -83,6 +83,16 @@
     tempest run --regex '(?!.*\[.*\bslow\b.*\])(^tempest\.api)' {posargs}
     tempest run --combine --serial --regex '(?!.*\[.*\bslow\b.*\])(^tempest\.scenario)' {posargs}
 
+[testenv:full-parallel]
+envdir = .tox/tempest
+sitepackages = {[tempestenv]sitepackages}
+setenv = {[tempestenv]setenv}
+deps = {[tempestenv]deps}
+# The regex below is used to select all tempest scenario and including the non slow api tests
+commands =
+    find . -type f -name "*.pyc" -delete
+    tempest run --regex '(^tempest\.scenario.*)|(?!.*\[.*\bslow\b.*\])(^tempest\.api)' {posargs}
+
 [testenv:full-serial]
 envdir = .tox/tempest
 sitepackages = {[tempestenv]sitepackages}