Merge "Fix nits and log from I996d257e089f0676e316f2b14207c343063ccf8e"
diff --git a/.gitignore b/.gitignore
index 7cb052f..06a2281 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,9 +3,8 @@
 *.pyc
 __pycache__/
 etc/accounts.yaml
-etc/tempest.conf
+etc/*.conf
 etc/tempest.conf.sample
-etc/logging.conf
 include/swift_objects/swift_small
 include/swift_objects/swift_medium
 include/swift_objects/swift_large
diff --git a/.stestr.conf b/.stestr.conf
index e3201c1..818c743 100644
--- a/.stestr.conf
+++ b/.stestr.conf
@@ -1,4 +1,3 @@
 [DEFAULT]
 test_path=./tempest/test_discover
 group_regex=([^\.]*\.)*
-
diff --git a/.testr.conf b/.testr.conf
deleted file mode 100644
index 95a4fb4..0000000
--- a/.testr.conf
+++ /dev/null
@@ -1,9 +0,0 @@
-[DEFAULT]
-test_command=OS_STDOUT_CAPTURE=${OS_STDOUT_CAPTURE:-1} \
-             OS_STDERR_CAPTURE=${OS_STDERR_CAPTURE:-1} \
-             OS_TEST_TIMEOUT=${OS_TEST_TIMEOUT:-500} \
-             OS_TEST_LOCK_PATH=${OS_TEST_LOCK_PATH:-${TMPDIR:-'/tmp'}} \
-             ${PYTHON:-python} -m subunit.run discover -t ${OS_TOP_LEVEL:-./} ${OS_TEST_PATH:-./tempest/test_discover} $LISTOPT $IDOPTION
-test_id_option=--load-list $IDFILE
-test_list_option=--list
-group_regex=([^\.]*\.)*
diff --git a/.zuul.yaml b/.zuul.yaml
index f2ca000..17ec1fc 100644
--- a/.zuul.yaml
+++ b/.zuul.yaml
@@ -1,26 +1,50 @@
 - job:
     name: devstack-tempest
     parent: devstack
-    description: Base Tempest job.
+    nodeset: openstack-single-node
+    description: |
+      Base Tempest job.
+
+      This Tempest job provides the base for both the single and multi-node
+      test setup. To run a multi-node test inherit from devstack-tempest and
+      set the nodeset to a multi-node one.
     required-projects:
-      - openstack/tempest
+      - git.openstack.org/openstack/tempest
     timeout: 7200
     roles:
-      - zuul: openstack-dev/devstack
+      - zuul: git.openstack.org/openstack-dev/devstack
     vars:
       devstack_services:
         tempest: true
+      devstack_local_conf:
+        test-config:
+          $TEMPEST_CONFIG:
+            compute:
+              min_compute_nodes: "{{ groups['compute'] | default(['controller']) | length }}"
+      test_results_stage_name: 'test_results'
+      zuul_copy_output:
+        '{{ devstack_base_dir }}/tempest/etc/tempest.conf': 'logs'
+        '{{ devstack_base_dir }}/tempest/etc/accounts.yaml': 'logs'
+        '{{ devstack_base_dir }}/tempest/tempest.log': 'logs'
+        '{{ stage_dir }}/{{ test_results_stage_name }}.subunit': 'logs'
+        '{{ stage_dir }}/{{ test_results_stage_name }}.html': 'logs'
+        '{{ stage_dir }}/stackviz': 'logs'
+      extensions_to_txt:
+        conf: True
+        log: True
+        yaml: True
+        yml: True
     run: playbooks/devstack-tempest.yaml
     post-run: playbooks/post-tempest.yaml
 
 - job:
     name: tempest-full
     parent: devstack-tempest
-    # This currently only works on the master branch.
-    # NOTE(andreaf) Only run on master for now.
-    # The negative lookup is redudant but it's a
-    # reminder that we don't want the job running there.
-    branches: ^(?!driverfixes/)master$
+    # This currently works from stable/pike on.
+    branches:
+      - master
+      - stable/queens
+      - stable/pike
     description: |
       Base integration test with Neutron networking and py27.
       Former names for this job where:
@@ -32,9 +56,24 @@
         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: ^(?!driverfixes/)master$
+    branches:
+      - master
+      - stable/queens
     description: |
       Base integration test with Neutron networking and py3.
       Former names for this job where:
@@ -54,6 +93,102 @@
         c-bak: false
 
 - job:
+    name: tempest-multinode-full
+    parent: devstack-tempest
+    nodeset: openstack-two-node
+    # Until the devstack changes are backported, only run this on master
+    branches:
+      - master
+    description: |
+      Base multinode integration test with Neutron networking and py27.
+      Former names for this job were:
+        * neutron-tempest-multinode-full
+        * legacy-tempest-dsvm-neutron-multinode-full
+        * gate-tempest-dsvm-neutron-multinode-full-ubuntu-xenial-nv
+      This job includes two nodes, controller / tempest plus a subnode, but
+      it can be used with different topologies, as long as a controller node
+      and a tempest one exist.
+    timeout: 10800
+    vars:
+      tox_envlist: full
+      devstack_localrc:
+        FORCE_CONFIG_DRIVE: False
+        NOVA_ALLOW_MOVE_TO_SAME_HOST: false
+        LIVE_MIGRATION_AVAILABLE: true
+        USE_BLOCK_MIGRATION_FOR_LIVE_MIGRATION: true
+    group-vars:
+      peers:
+        devstack_localrc:
+          NOVA_ALLOW_MOVE_TO_SAME_HOST: false
+          LIVE_MIGRATION_AVAILABLE: true
+          USE_BLOCK_MIGRATION_FOR_LIVE_MIGRATION: true
+
+- 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-slow
+    parent: tempest-multinode-full
+    branches:
+      - master
+    description: |
+      This multinode integration job will run all the tests tagged as slow.
+      It enables the lvm multibackend setup to cover few scenario tests.
+      This job will run only slow tests(API or Scenario) serially.
+
+      Former names for this job were:
+        * legacy-tempest-dsvm-neutron-scenario-multinode-lvm-multibackend
+        * tempest-scenario-multinode-lvm-multibackend
+    timeout: 10800
+    vars:
+      tox_envlist: slow-serial
+      devstack_localrc:
+        CINDER_ENABLED_BACKENDS: lvm:lvmdriver-1,lvm:lvmdriver-2
+      tempest_concurrency: 2
+
+- job:
+    name: tempest-full-rocky
+    parent: tempest-full
+    override-checkout: stable/rocky
+
+- job:
+    name: tempest-full-rocky-py3
+    parent: tempest-full-py3
+    override-checkout: stable/rocky
+
+- job:
+    name: tempest-full-queens
+    parent: tempest-full
+    override-checkout: stable/queens
+
+- job:
+    name: tempest-full-queens-py3
+    parent: tempest-full-py3
+    override-checkout: stable/queens
+
+- job:
+    name: tempest-full-pike
+    parent: tempest-full
+    override-checkout: stable/pike
+
+- job:
     name: tempest-tox-plugin-sanity-check
     parent: tox
     description: |
@@ -63,73 +198,95 @@
       tox_envlist: plugin-sanity-check
     voting: false
     timeout: 5000
+    irrelevant-files:
+      - ^.*\.rst$
+      - ^doc/.*$
+      - ^etc/.*$
+      - ^releasenotes/.*$
+      - ^tempest/hacking/.*$
+      - ^tempest/tests/.*$
     required-projects:
-      - openstack/almanach
-      - openstack/aodh
-      - openstack/barbican-tempest-plugin
-      - openstack/ceilometer
-      - openstack/cinder
-      - openstack/congress
-      - openstack/designate-tempest-plugin
-      - openstack/ec2-api
-      - openstack/freezer
-      - openstack/freezer-api
-      - openstack/freezer-tempest-plugin
-      - openstack/gce-api
-      - openstack/glare
-      - openstack/heat
-      - openstack/intel-nfv-ci-tests
-      - openstack/ironic
-      - openstack/ironic-inspector
-      - openstack/keystone-tempest-plugin
-      - openstack/kingbird
-      - openstack/kuryr-tempest-plugin
-      - openstack/magnum
-      - openstack/magnum-tempest-plugin
-      - openstack/manila
-      - openstack/manila-tempest-plugin
-      - openstack/mistral
-      - openstack/mogan
-      - openstack/monasca-api
-      - openstack/monasca-log-api
-      - openstack/murano
-      - openstack/networking-bgpvpn
-      - openstack/networking-cisco
-      - openstack/networking-fortinet
-      - openstack/networking-generic-switch
-      - openstack/networking-l2gw
-      - openstack/networking-midonet
-      - openstack/networking-plumgrid
-      - openstack/networking-sfc
-      - openstack/neutron
-      - openstack/neutron-dynamic-routing
-      - openstack/neutron-fwaas
-      - openstack/neutron-lbaas
-      - openstack/neutron-tempest-plugin
-      - openstack/neutron-vpnaas
-      - openstack/nova-lxd
-      - openstack/novajoin-tempest-plugin
-      - openstack/octavia-tempest-plugin
-      - openstack/oswin-tempest-plugin
-      - openstack/panko
-      - openstack/patrole
-      - openstack/qinling
-      - openstack/requirements
-      - openstack/sahara-tests
-      - openstack/senlin
-      - openstack/senlin-tempest-plugin
-      - openstack/tap-as-a-service
-      - openstack/tempest-horizon
-      - openstack/trio2o
-      - openstack/trove
-      - openstack/valet
-      - openstack/vitrage
-      - openstack/vmware-nsx-tempest-plugin
-      - openstack/watcher-tempest-plugin
-      - openstack/zaqar-tempest-plugin
-      - openstack/zun-tempest-plugin
+      - git.openstack.org/openstack/almanach
+      - git.openstack.org/openstack/aodh
+      - git.openstack.org/openstack/barbican-tempest-plugin
+      - git.openstack.org/openstack/ceilometer
+      - git.openstack.org/openstack/cinder
+      - git.openstack.org/openstack/congress
+      - git.openstack.org/openstack/designate-tempest-plugin
+      - git.openstack.org/openstack/ec2-api
+      - git.openstack.org/openstack/freezer
+      - git.openstack.org/openstack/freezer-api
+      - git.openstack.org/openstack/freezer-tempest-plugin
+      - git.openstack.org/openstack/gce-api
+      - git.openstack.org/openstack/glare
+      - git.openstack.org/openstack/heat
+      - git.openstack.org/openstack/intel-nfv-ci-tests
+      - git.openstack.org/openstack/ironic
+      - git.openstack.org/openstack/ironic-inspector
+      - git.openstack.org/openstack/keystone-tempest-plugin
+      - git.openstack.org/openstack/kingbird
+      - git.openstack.org/openstack/kuryr-tempest-plugin
+      - git.openstack.org/openstack/magnum
+      - git.openstack.org/openstack/magnum-tempest-plugin
+      - git.openstack.org/openstack/manila
+      - git.openstack.org/openstack/manila-tempest-plugin
+      - git.openstack.org/openstack/mistral
+      - git.openstack.org/openstack/mogan
+      - git.openstack.org/openstack/monasca-api
+      - git.openstack.org/openstack/monasca-log-api
+      - git.openstack.org/openstack/murano
+      - git.openstack.org/openstack/networking-bgpvpn
+      - git.openstack.org/openstack/networking-cisco
+      - git.openstack.org/openstack/networking-fortinet
+      - git.openstack.org/openstack/networking-generic-switch
+      - git.openstack.org/openstack/networking-l2gw
+      - git.openstack.org/openstack/networking-midonet
+      - git.openstack.org/openstack/networking-plumgrid
+      - git.openstack.org/openstack/networking-sfc
+      - git.openstack.org/openstack/neutron
+      - git.openstack.org/openstack/neutron-dynamic-routing
+      - git.openstack.org/openstack/neutron-fwaas
+      - git.openstack.org/openstack/neutron-lbaas
+      - git.openstack.org/openstack/neutron-tempest-plugin
+      - git.openstack.org/openstack/neutron-vpnaas
+      - git.openstack.org/openstack/nova-lxd
+      - git.openstack.org/openstack/novajoin-tempest-plugin
+      - git.openstack.org/openstack/octavia-tempest-plugin
+      - git.openstack.org/openstack/oswin-tempest-plugin
+      - git.openstack.org/openstack/panko
+      - git.openstack.org/openstack/patrole
+      - git.openstack.org/openstack/qinling
+      - git.openstack.org/openstack/requirements
+      - git.openstack.org/openstack/sahara-tests
+      - git.openstack.org/openstack/senlin
+      - git.openstack.org/openstack/senlin-tempest-plugin
+      - git.openstack.org/openstack/tap-as-a-service
+      - git.openstack.org/openstack/tempest-horizon
+      - git.openstack.org/openstack/trio2o
+      - git.openstack.org/openstack/trove
+      - git.openstack.org/openstack/valet
+      - git.openstack.org/openstack/vitrage
+      - git.openstack.org/openstack/vmware-nsx-tempest-plugin
+      - git.openstack.org/openstack/watcher-tempest-plugin
+      - git.openstack.org/openstack/zaqar-tempest-plugin
+      - git.openstack.org/openstack/zun-tempest-plugin
+
+- job:
+    name: tempest-cinder-v2-api
+    parent: devstack-tempest
+    branches:
+      - master
+    description: |
+      This job runs the cinder API test against v2 endpoint.
+    vars:
+      tox_envlist: all
+      tempest_test_regex: api.*volume
+      devstack_localrc:
+        TEMPEST_VOLUME_TYPE: volumev2
 
 - project:
+    templates:
+      - openstack-python36-jobs
     check:
       jobs:
         - devstack-tempest:
@@ -137,8 +294,166 @@
               - ^playbooks/
               - ^roles/
               - ^.zuul.yaml$
-        - nova-multiattach
+        - nova-multiattach:
+            irrelevant-files:
+              - ^(test-|)requirements.txt$
+              - ^.*\.rst$
+              - ^doc/.*$
+              - ^etc/.*$
+              - ^releasenotes/.*$
+              - ^setup.cfg$
+              - ^tempest/hacking/.*$
+              - ^tempest/tests/.*$
+        - 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-rocky:
+            irrelevant-files:
+              - ^(test-|)requirements.txt$
+              - ^.*\.rst$
+              - ^doc/.*$
+              - ^etc/.*$
+              - ^releasenotes/.*$
+              - ^setup.cfg$
+              - ^tempest/hacking/.*$
+              - ^tempest/tests/.*$
+        - tempest-full-rocky-py3:
+            irrelevant-files:
+              - ^(test-|)requirements.txt$
+              - ^.*\.rst$
+              - ^doc/.*$
+              - ^etc/.*$
+              - ^releasenotes/.*$
+              - ^setup.cfg$
+              - ^tempest/hacking/.*$
+              - ^tempest/tests/.*$
+        - tempest-full-queens:
+            irrelevant-files:
+              - ^(test-|)requirements.txt$
+              - ^.*\.rst$
+              - ^doc/.*$
+              - ^etc/.*$
+              - ^releasenotes/.*$
+              - ^setup.cfg$
+              - ^tempest/hacking/.*$
+              - ^tempest/tests/.*$
+        - tempest-full-queens-py3:
+            irrelevant-files:
+              - ^(test-|)requirements.txt$
+              - ^.*\.rst$
+              - ^doc/.*$
+              - ^etc/.*$
+              - ^releasenotes/.*$
+              - ^setup.cfg$
+              - ^tempest/hacking/.*$
+              - ^tempest/tests/.*$
+        - tempest-full-pike:
+            irrelevant-files:
+              - ^(test-|)requirements.txt$
+              - ^.*\.rst$
+              - ^doc/.*$
+              - ^etc/.*$
+              - ^releasenotes/.*$
+              - ^setup.cfg$
+              - ^tempest/hacking/.*$
+              - ^tempest/tests/.*$
+        - tempest-multinode-full:
+            irrelevant-files:
+              - ^(test-|)requirements.txt$
+              - ^.*\.rst$
+              - ^doc/.*$
+              - ^etc/.*$
+              - ^releasenotes/.*$
+              - ^setup.cfg$
+              - ^tempest/hacking/.*$
+              - ^tempest/tests/.*$
         - tempest-tox-plugin-sanity-check
+        - tempest-slow:
+            irrelevant-files:
+              - ^(test-|)requirements.txt$
+              - ^.*\.rst$
+              - ^doc/.*$
+              - ^etc/.*$
+              - ^releasenotes/.*$
+              - ^setup.cfg$
+              - ^tempest/hacking/.*$
+              - ^tempest/tests/.*$
+        - nova-cells-v1:
+            irrelevant-files:
+              - ^(test-|)requirements.txt$
+              - ^.*\.rst$
+              - ^doc/.*$
+              - ^etc/.*$
+              - ^releasenotes/.*$
+              - ^setup.cfg$
+              - ^tempest/hacking/.*$
+              - ^tempest/tests/.*$
+        - nova-live-migration:
+            voting: false
+            irrelevant-files:
+              - ^(test-|)requirements.txt$
+              - ^.*\.rst$
+              - ^doc/.*$
+              - ^etc/.*$
+              - ^releasenotes/.*$
+              - ^setup.cfg$
+              - ^tempest/hacking/.*$
+              - ^tempest/tests/.*$
     gate:
       jobs:
-        - nova-multiattach
+        - nova-multiattach:
+            irrelevant-files:
+              - ^(test-|)requirements.txt$
+              - ^.*\.rst$
+              - ^doc/.*$
+              - ^etc/.*$
+              - ^releasenotes/.*$
+              - ^setup.cfg$
+              - ^tempest/hacking/.*$
+              - ^tempest/tests/.*$
+        - tempest-slow:
+            irrelevant-files:
+              - ^(test-|)requirements.txt$
+              - ^.*\.rst$
+              - ^doc/.*$
+              - ^etc/.*$
+              - ^releasenotes/.*$
+              - ^setup.cfg$
+              - ^tempest/hacking/.*$
+              - ^tempest/tests/.*$
+    experimental:
+      jobs:
+        - tempest-cinder-v2-api:
+            irrelevant-files:
+              - ^(test-|)requirements.txt$
+              - ^.*\.rst$
+              - ^doc/.*$
+              - ^etc/.*$
+              - ^releasenotes/.*$
+              - ^setup.cfg$
+              - ^tempest/hacking/.*$
+              - ^tempest/tests/.*$
+    periodic-stable:
+      jobs:
+        - tempest-full-rocky
+        - tempest-full-rocky-py3
+        - tempest-full-queens
+        - tempest-full-queens-py3
+        - tempest-full-pike
diff --git a/HACKING.rst b/HACKING.rst
index f961884..5b9c0f1 100644
--- a/HACKING.rst
+++ b/HACKING.rst
@@ -25,6 +25,8 @@
 - [T115] Check that admin tests should exist under admin path
 - [N322] Method's default argument shouldn't be mutable
 - [T116] Unsupported 'message' Exception attribute in PY3
+- [T117] Check negative tests have ``@decorators.attr(type=['negative'])``
+  applied.
 
 Test Data/Configuration
 -----------------------
@@ -106,7 +108,7 @@
 test method. You specify the services with the ``tempest.common.utils.services``
 decorator. For example:
 
-@utils.services('compute', 'image')
+``@utils.services('compute', 'image')``
 
 Valid service tag names are the same as the list of directories in tempest.api
 that have tests.
@@ -118,6 +120,54 @@
 in ``tempest.api.compute`` would require a service tag for those services,
 however they do not need to be tagged as ``compute``.
 
+Test Attributes
+---------------
+Tempest leverages `test attributes`_ which are a simple but effective way of
+distinguishing between different "types" of API tests. A test can be "tagged"
+with such attributes using the ``decorators.attr`` decorator, for example::
+
+    @decorators.attr(type=['negative'])
+    def test_aggregate_create_aggregate_name_length_less_than_1(self):
+        [...]
+
+These test attributes can be used for test selection via regular expressions.
+For example, ``(?!.*\[.*\bslow\b.*\])(^tempest\.scenario)`` runs all the tests
+in the ``scenario`` test module, *except* for those tagged with the ``slow``
+attribute (via a negative lookahead in the regular expression). These
+attributes are used in Tempest's ``tox.ini`` as well as Tempest's Zuul job
+definitions for specifying particular batches of Tempest test suites to run.
+
+.. _test attributes: https://testtools.readthedocs.io/en/latest/for-test-authors.html?highlight=attr#test-attributes
+
+Negative Attribute
+^^^^^^^^^^^^^^^^^^
+The ``type='negative'`` attribute is used to signify that a test is a negative
+test, which is a test that handles invalid input gracefully. This attribute
+should be applied to all negative test scenarios.
+
+This attribute must be applied to each test that belongs to a negative test
+class, i.e. a test class name ending with "Negative.*" substring.
+
+Slow Attribute
+^^^^^^^^^^^^^^
+The ``type='slow'`` attribute is used to signify that a test takes a long time
+to run, relatively speaking. This attribute is usually applied to
+:ref:`scenario tests <scenario_field_guide>`, which involve a complicated
+series of API operations, the total runtime of which can be relatively long.
+This long runtime has performance implications on `Zuul`_ jobs, which is why
+the ``slow`` attribute is leveraged to run slow tests on a selective basis,
+to keep total `Zuul`_ job runtime down to a reasonable time frame.
+
+.. _Zuul: https://docs.openstack.org/infra/zuul/
+
+Smoke Attribute
+^^^^^^^^^^^^^^^
+The ``type='smoke'`` attribute is used to signify that a test is a so-called
+smoke test, which is a type of test that tests the most vital OpenStack
+functionality, like listing servers or flavors or creating volumes. The
+attribute should be sparingly applied to only the tests that sanity-check the
+most essential functionality of an OpenStack cloud.
+
 Test fixtures and resources
 ---------------------------
 Test level resources should be cleaned-up after the test execution. Clean-up
@@ -363,13 +413,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 242f4d5..0a130dc 100644
--- a/README.rst
+++ b/README.rst
@@ -2,7 +2,7 @@
 Team and repository tags
 ========================
 
-.. image:: https://governance.openstack.org/badges/tempest.svg
+.. image:: https://governance.openstack.org/tc/badges/tempest.svg
     :target: https://governance.openstack.org/tc/reference/tags/index.html
 
 .. Change things from this point on
@@ -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
@@ -95,10 +95,12 @@
    command. Tempest is expecting a ``tempest.conf`` file in etc/ so if only a
    sample exists you must rename or copy it to tempest.conf before making
    any changes to it otherwise Tempest will not know how to load it. For
-   details on configuring Tempest refer to the :ref:`tempest-configuration`.
+   details on configuring Tempest refer to the
+   `Tempest Configuration <https://docs.openstack.org/tempest/latest/configuration.html#tempest-configuration>`_
 
 #. Once the configuration is done you're now ready to run Tempest. This can
-   be done using the :ref:`tempest_run` command. This can be done by either
+   be done using the `Tempest Run <https://docs.openstack.org/tempest/latest/run.html#tempest-run>`_
+   command. This can be done by either
    running::
 
     $ tempest run
@@ -109,15 +111,18 @@
 
     $ tempest run --workspace cloud-01
 
-   There is also the option to use testr directly, or any `testr`_ based test
-   runner, like `ostestr`_. For example, from the workspace dir run::
+   There is also the option to use `stestr`_ directly. For example, from
+   the workspace dir run::
 
-    $ ostestr --regex '(?!.*\[.*\bslow\b.*\])(^tempest\.(api|scenario))'
+    $ stestr run --black-regex '\[.*\bslow\b.*\]' '^tempest\.(api|scenario)'
 
-   will run the same set of tests as the default gate jobs.
+   will run the same set of tests as the default gate jobs. Or you can
+   use `unittest`_ compatible test runners such as `testr`_, `pytest`_ etc.
 
+.. _unittest: https://docs.python.org/3/library/unittest.html
 .. _testr: https://testrepository.readthedocs.org/en/latest/MANUAL.html
-.. _ostestr: https://docs.openstack.org/os-testr/latest/
+.. _stestr: https://stestr.readthedocs.org/en/latest/MANUAL.html
+.. _pytest: https://docs.pytest.org/en/latest/
 
 Library
 -------
@@ -129,7 +134,8 @@
 stable interface and there are no guarantees on the Python API unless otherwise
 stated.
 
-For more details refer to the library documentation here: :ref:`library`
+For more details refer to the `library documentation
+<https://docs.openstack.org/tempest/latest/library.html#library>`_
 
 Release Versioning
 ------------------
@@ -165,8 +171,10 @@
 -------------
 
 Detailed configuration of Tempest is beyond the scope of this
-document see :ref:`tempest-configuration` for more details on configuring
-Tempest. The ``etc/tempest.conf.sample`` attempts to be a self-documenting
+document, see `Tempest Configuration Documentation
+<https://docs.openstack.org/tempest/latest/configuration.html#tempest-configuration>`_
+for more details on configuring Tempest.
+The ``etc/tempest.conf.sample`` attempts to be a self-documenting
 version of the configuration.
 
 You can generate a new sample tempest.conf file, run the following
@@ -212,18 +220,6 @@
 For more information on these options and details about stestr, please see the
 `stestr documentation <http://stestr.readthedocs.io/en/latest/MANUAL.html>`_.
 
-Python 2.6
-----------
-
-Starting in the Kilo release the OpenStack services dropped all support for
-python 2.6. This change has been mirrored in Tempest, starting after the
-tempest-2 tag. This means that proposed changes to Tempest which only fix
-python 2.6 compatibility will be rejected, and moving forward more features not
-present in python 2.6 will be used. If you're running your OpenStack services
-on an earlier release with python 2.6 you can easily run Tempest against it
-from a remote system running python 2.7. (or deploy a cloud guest in your cloud
-that has python 2.7)
-
 Python 3.x
 ----------
 
diff --git a/REVIEWING.rst b/REVIEWING.rst
index 766d0c6..8a1e152 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.
@@ -99,6 +99,39 @@
 scenario tests this is up to the reviewers discretion whether a docstring is
 required or not.
 
+
+Test Removal and Refactoring
+----------------------------
+Make sure that any test that is renamed, relocated (e.g. moved to another
+class), or removed does not belong to the `interop`_ testing suite -- which
+includes a select suite of Tempest tests for the purposes of validating that
+OpenStack vendor clouds are interoperable -- or a project's `whitelist`_ or
+`blacklist`_ files.
+
+It is of critical importance that no interop, whitelist or blacklist test
+reference be broken by a patch set introduced to Tempest that renames,
+relocates or removes a referenced test.
+
+Please check the existence of code which references Tempest tests with:
+http://codesearch.openstack.org/
+
+Interop
+^^^^^^^
+Make sure that modifications to an `interop`_ test are backwards-compatible.
+This means that code modifications to tests should not undermine the quality of
+the validation currently performed by the test or significantly alter the
+behavior of the test.
+
+Removal
+^^^^^^^
+Reference the :ref:`test-removal` guidelines for understanding best practices
+associated with test removal.
+
+.. _interop: https://www.openstack.org/brand/interop
+.. _whitelist: https://docs.openstack.org/tempest/latest/run.html#test-selection
+.. _blacklist: https://docs.openstack.org/tempest/latest/run.html#test-selection
+
+
 Release Notes
 -------------
 Release notes are how we indicate to users and other consumers of Tempest what
@@ -113,16 +146,18 @@
 
 .. _reno: https://docs.openstack.org/reno/latest/
 
+
 Deprecated Code
 ---------------
 Sometimes we have some bugs in deprecated code. Basically, we leave it. Because
 we don't need to maintain it. However, if the bug is critical, we might need to
 fix it. When it will happen, we will deal with it on a case-by-case basis.
 
+
 When to approve
 ---------------
 * Every patch needs two +2s before being approved.
-* Its ok to hold off on an approval until a subject matter expert reviews it
+* It's ok to hold off on an approval until a subject matter expert reviews it
 * If a patch has already been approved but requires a trivial rebase to merge,
   you do not have to wait for a second +2, since the patch has already had
   two +2s.
diff --git a/doc/requirements.txt b/doc/requirements.txt
index 555b7d2..d959d44 100644
--- a/doc/requirements.txt
+++ b/doc/requirements.txt
@@ -1,6 +1,6 @@
 # The order of packages is significant, because pip processes them in the order
 # of appearance. Changing the order has an impact on the overall integration
 # process, which may cause wedges in the gate later.
-openstackdocstheme>=1.17.0 # Apache-2.0
+openstackdocstheme>=1.18.1 # Apache-2.0
 reno>=2.5.0 # Apache-2.0
-sphinx!=1.6.6,>=1.6.2 # BSD
+sphinx!=1.6.6,!=1.6.7,>=1.6.2 # BSD
diff --git a/doc/source/conf.py b/doc/source/conf.py
index 0a061b8..c2ea628 100644
--- a/doc/source/conf.py
+++ b/doc/source/conf.py
@@ -64,7 +64,7 @@
 # openstackdocstheme options
 repository_name = 'openstack/tempest'
 bug_project = 'tempest'
-bug_tag = ''
+bug_tag = 'doc'
 
 # Must set this variable to include year, month, day, hours, and minutes.
 html_last_updated_fmt = '%Y-%m-%d %H:%M'
diff --git a/doc/source/configuration.rst b/doc/source/configuration.rst
index d0d7320..2e5f706 100644
--- a/doc/source/configuration.rst
+++ b/doc/source/configuration.rst
@@ -172,7 +172,7 @@
 resize test).
 
 Using a smaller flavor is generally recommended. When larger flavors are used,
-the extra time required to bring up servers will likely affect total run time
+the extra time required to bring up servers will likely affect the total run time
 and probably require tweaking timeout values to ensure tests have ample time to
 finish.
 
@@ -207,7 +207,7 @@
 
 The behavior of these options is a bit convoluted (which will likely be fixed in
 future versions). You first need to specify ``img_dir``, which is the directory
-in which Tempest will look for the image files. First it will check if the
+in which Tempest will look for the image files. First, it will check if the
 filename set for ``img_file`` could be found in ``img_dir``. If it is found then
 the ``img_container_format`` and ``img_disk_format`` options are used to upload
 that image to glance. However, if it is not found, Tempest will look for the
@@ -239,7 +239,7 @@
 """"""""""""""""""""""""""""""""""
 When Tempest creates servers for testing, some tests require being able to
 connect those servers. Depending on the configuration of the cloud, the methods
-for doing this can be different. In certain configurations it is required to
+for doing this can be different. In certain configurations, it is required to
 specify a single network with server create calls. Accordingly, Tempest provides
 a few different methods for providing this information in configuration to try
 and ensure that regardless of the cloud's configuration it'll still be able to
@@ -297,10 +297,10 @@
 ''''''''''''''''''''''''
 With dynamic credentials enabled and using nova-network, your only option for
 configuration is to either set a fixed network name or not. However, in most
-cases it shouldn't matter because nova-network should have no problem booting a
+cases, it shouldn't matter because nova-network should have no problem booting a
 server with multiple networks. If this is not the case for your cloud then using
 an accounts file is recommended because it provides the necessary flexibility to
-describe your configuration. Dynamic credentials is not able to dynamically
+describe your configuration. Dynamic credentials are not able to dynamically
 allocate things as necessary if Neutron is not enabled.
 
 With Neutron and dynamic credentials enabled there should not be any additional
@@ -352,7 +352,7 @@
 OpenStack is really a constellation of several different projects which
 are running together to create a cloud. However which projects you're running
 is not set in stone, and which services are running is up to the deployer.
-Tempest however needs to know which services are available so it can figure
+Tempest, however, needs to know which services are available so it can figure
 out which tests it is able to run and certain setup steps which differ based
 on the available services.
 
@@ -390,8 +390,8 @@
 
 .. note::
 
-    Tempest does not serve all kinds of fancy URLs in the service catalog.  The
-    service catalog should be in a standard format (which is going to be
+    Tempest does not serve all kinds of fancy URLs in the service catalog.
+    The service catalog should be in a standard format (which is going to be
     standardized at the Keystone level).
     Tempest expects URLs in the Service catalog in the following format:
 
@@ -413,10 +413,10 @@
 certain operations and features aren't supported depending on the configuration.
 These features may or may not be discoverable from the API so the burden is
 often on the user to figure out what is supported by the cloud they're talking
-to.  Besides the obvious interoperability issues with this it also leaves
+to. Besides the obvious interoperability issues with this, it also leaves
 Tempest in an interesting situation trying to figure out which tests are
 expected to work. However, Tempest tests do not rely on dynamic API discovery
-for a feature (assuming one exists). Instead Tempest has to be explicitly
+for a feature (assuming one exists). Instead, Tempest has to be explicitly
 configured as to which optional features are enabled. This is in order to
 prevent bugs in the discovery mechanisms from masking failures.
 
@@ -432,8 +432,8 @@
 ^^^^^^^^^^^^^^
 The service feature-enabled sections often contain an ``api-extensions`` option
 (or in the case of Swift a ``discoverable_apis`` option). This is used to tell
-Tempest which api extensions (or configurable middleware) is used in your
+Tempest which API extensions (or configurable middleware) is used in your
 deployment. It has two valid config states: either it contains a single value
-``all`` (which is the default) which means that every api extension is assumed
+``all`` (which is the default) which means that every API extension is assumed
 to be enabled, or it is set to a list of each individual extension that is
 enabled for that service.
diff --git a/doc/source/data/tempest-plugins-registry.header b/doc/source/data/tempest-plugins-registry.header
index 9821e8e..0de12b7 100644
--- a/doc/source/data/tempest-plugins-registry.header
+++ b/doc/source/data/tempest-plugins-registry.header
@@ -17,7 +17,3 @@
 The following are plugins that a script has found in the openstack/
 namespace, which includes but is not limited to official OpenStack
 projects.
-
-+----------------------------+-------------------------------------------------------------------------+
-|Plugin Name                 |URL                                                                      |
-+----------------------------+-------------------------------------------------------------------------+
diff --git a/doc/source/index.rst b/doc/source/index.rst
index f562850..fecf98a 100644
--- a/doc/source/index.rst
+++ b/doc/source/index.rst
@@ -80,6 +80,14 @@
 
    library
 
+Support Policy
+--------------
+
+.. toctree::
+   :maxdepth: 2
+
+   stable_branch_support_policy
+
 Indices and tables
 ==================
 
diff --git a/doc/source/library.rst b/doc/source/library.rst
index 14415ae..6a12c45 100644
--- a/doc/source/library.rst
+++ b/doc/source/library.rst
@@ -4,12 +4,12 @@
 =============================
 
 Tempest provides a stable library interface that provides external tools or
-test suites an interface for reusing pieces of tempest code. Any public
-interface that lives in tempest/lib in the tempest repo is treated as a stable
+test suites an interface for reusing pieces of Tempest code. Any public
+interface that lives in tempest/lib in the Tempest repo is treated as a stable
 public interface and it should be safe to external consume that. Every effort
 goes into maintaining backwards compatibility with any change.
 The library is self contained and doesn't have any dependency on
-other tempest internals outside of lib (including no usage of tempest
+other Tempest internals outside of lib (including no usage of Tempest
 configuration).
 
 Stability
@@ -32,7 +32,7 @@
 Making changes
 ''''''''''''''
 When making changes to tempest/lib you have to be conscious of the effect of
-any changes on external consumers. If your proposed changeset will change the
+any changes on external consumers. If your proposed change set will change the
 default behaviour of any interface, or make something which previously worked
 not after your change, then it is not acceptable. Every effort needs to go into
 preserving backwards compatibility in changes.
@@ -40,8 +40,8 @@
 Reviewing
 '''''''''
 When reviewing a proposed change to tempest/lib code we need to be careful to
-ensure that we don't break backwards compatibility. For patches that change
-existing interfaces we have to be careful to make sure we don't break any
+ensure that we don't break backward compatibility. For patches that change
+existing interfaces, we have to be careful to make sure we don't break any
 external consumers. Some common red flags are:
 
 * a change to an existing API requires a change outside the library directory
@@ -52,7 +52,7 @@
 '''''''
 When adding a new interface to the library we need to at a minimum have unit
 test coverage. A proposed change to add an interface to tempest/lib that
-doesn't have unit tests shouldn't be accepted. Ideally these unit tests will
+doesn't have unit tests shouldn't be accepted. Ideally, these unit tests will
 provide sufficient coverage to ensure a stable interface moving forward.
 
 Current Library APIs
diff --git a/doc/source/microversion_testing.rst b/doc/source/microversion_testing.rst
index 942f969..983fa24 100644
--- a/doc/source/microversion_testing.rst
+++ b/doc/source/microversion_testing.rst
@@ -306,10 +306,22 @@
 
   .. _2.6: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id5
 
+  * `2.8`_
+
+  .. _2.8: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id7
+
+  * `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,14 +334,34 @@
 
   .. _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.28`_

+

+  .. _2.28: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id25
+
   * `2.32`_
 
   .. _2.32: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id29
 
+  * `2.36`_
+
+  .. _2.36: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#microversion
+
   * `2.37`_
 
   .. _2.37: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id34
 
+  * `2.39`_
+
+  .. _2.39: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id35
+
+  * `2.41`_
+
+  .. _2.41: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id37
+
   * `2.42`_
 
   .. _2.42: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#maximum-in-ocata
@@ -342,9 +374,37 @@
 
   .. _2.48: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id43
 
+  * `2.49`_
+
+  .. _2.49: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id44
+
+  * `2.53`_
+
+  .. _2.53: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#maximum-in-pike
+
+  * `2.54`_
+
+  .. _2.54: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id49
+
+  * `2.55`_
+
+  .. _2.55: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id50
+
+  * `2.57`_
+
+  .. _2.57: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id52
+
   * `2.60`_
 
-  .. _2.60: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id54
+  .. _2.60: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#maximum-in-queens
+
+  * `2.61`_
+
+  .. _2.61: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id55
+
+  * `2.63`_
+
+  .. _2.63: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id57
 
 * Volume
 
diff --git a/doc/source/plugin.rst b/doc/source/plugin.rst
index 6f6621d..9958792 100644
--- a/doc/source/plugin.rst
+++ b/doc/source/plugin.rst
@@ -5,24 +5,24 @@
 =============================
 
 Tempest has an external test plugin interface which enables anyone to integrate
-an external test suite as part of a tempest run. This will let any project
-leverage being run with the rest of the tempest suite while not requiring the
-tests live in the tempest tree.
+an external test suite as part of a Tempest run. This will let any project
+leverage being run with the rest of the Tempest suite while not requiring the
+tests live in the Tempest tree.
 
 Creating a plugin
 =================
 
 Creating a plugin is fairly straightforward and doesn't require much additional
 effort on top of creating a test suite using tempest.lib. One thing to note with
-doing this is that the interfaces exposed by tempest are not considered stable
-(with the exception of configuration variables which ever effort goes into
-ensuring backwards compatibility). You should not need to import anything from
-tempest itself except where explicitly noted.
+doing this is that the interfaces exposed by Tempest are not considered stable
+(with the exception of configuration variables whichever effort goes into
+ensuring backward compatibility). You should not need to import anything from
+Tempest itself except where explicitly noted.
 
 Stable Tempest APIs plugins may use
 -----------------------------------
 
-As noted above, several tempest APIs are acceptable to use from plugins, while
+As noted above, several Tempest APIs are acceptable to use from plugins, while
 others are not. A list of stable APIs available to plugins is provided below:
 
 * tempest.lib.*
@@ -32,7 +32,7 @@
 * tempest.clients
 * tempest.test
 
-If there is an interface from tempest that you need to rely on in your plugin
+If there is an interface from Tempest that you need to rely on in your plugin
 which is not listed above, it likely needs to be migrated to tempest.lib. In
 that situation, file a bug, push a migration patch, etc. to expedite providing
 the interface in a reliable manner.
@@ -62,7 +62,7 @@
 -----------
 
 Once you've created your plugin class you need to add an entry point to your
-project to enable tempest to find the plugin. The entry point must be added
+project to enable Tempest to find the plugin. The entry point must be added
 to the "tempest.test_plugins" namespace.
 
 If you are using pbr this is fairly straightforward, in the setup.cfg just add
@@ -77,9 +77,9 @@
 Standalone Plugin vs In-repo Plugin
 -----------------------------------
 
-Since all that's required for a plugin to be detected by tempest is a valid
+Since all that's required for a plugin to be detected by Tempest is a valid
 setuptools entry point in the proper namespace there is no difference from the
-tempest perspective on either creating a separate python package to
+Tempest perspective on either creating a separate python package to
 house the plugin or adding the code to an existing python project. However,
 there are tradeoffs to consider when deciding which approach to take when
 creating a new plugin.
@@ -91,9 +91,9 @@
 single version of the test code across project release boundaries (see the
 `Branchless Tempest Spec`_ for more details on this). It also greatly
 simplifies the install time story for external users. Instead of having to
-install the right version of a project in the same python namespace as tempest
+install the right version of a project in the same python namespace as Tempest
 they simply need to pip install the plugin in that namespace. It also means
-that users don't have to worry about inadvertently installing a tempest plugin
+that users don't have to worry about inadvertently installing a Tempest plugin
 when they install another package.
 
 .. _Branchless Tempest Spec: http://specs.openstack.org/openstack/qa-specs/specs/tempest/implemented/branchless-tempest.html
@@ -108,9 +108,9 @@
 Plugin Class
 ============
 
-To provide tempest with all the required information it needs to be able to run
-your plugin you need to create a plugin class which tempest will load and call
-to get information when it needs. To simplify creating this tempest provides an
+To provide Tempest with all the required information it needs to be able to run
+your plugin you need to create a plugin class which Tempest will load and call
+to get information when it needs. To simplify creating this Tempest provides an
 abstract class that should be used as the parent for your plugin. To use this
 you would do something like the following:
 
@@ -147,7 +147,7 @@
       services/
         client.py
 
-That will mirror what people expect from tempest. The file
+That will mirror what people expect from Tempest. The file
 
 * **config.py**: contains any plugin specific configuration variables
 * **plugin.py**: contains the plugin class used for the entry point
@@ -156,14 +156,14 @@
 * **services**: where the plugin specific service clients are
 
 Additionally, when you're creating the plugin you likely want to follow all
-of the tempest developer and reviewer documentation to ensure that the tests
-being added in the plugin act and behave like the rest of tempest.
+of the Tempest developer and reviewer documentation to ensure that the tests
+being added in the plugin act and behave like the rest of Tempest.
 
 Dealing with configuration options
 ----------------------------------
 
-Historically Tempest didn't provide external guarantees on its configuration
-options. However, with the introduction of the plugin interface this is no
+Historically, Tempest didn't provide external guarantees on its configuration
+options. However, with the introduction of the plugin interface, this is no
 longer the case. An external plugin can rely on using any configuration option
 coming from Tempest, there will be at least a full deprecation cycle for any
 option before it's removed. However, just the options provided by Tempest
@@ -171,7 +171,7 @@
 configuration options you should use the ``register_opts`` and
 ``get_opt_lists`` methods to pass them to Tempest when the plugin is loaded.
 When adding configuration options the ``register_opts`` method gets passed the
-CONF object from tempest. This enables the plugin to add options to both
+CONF object from Tempest. This enables the plugin to add options to both
 existing sections and also create new configuration sections for new options.
 
 Service Clients
@@ -325,23 +325,23 @@
 
 Tempest will automatically discover any installed plugins when it is run. So by
 just installing the python packages which contain your plugin you'll be using
-them with tempest, nothing else is really required.
+them with Tempest, nothing else is really required.
 
 However, you should take care when installing plugins. By their very nature
-there are no guarantees when running tempest with plugins enabled about the
+there are no guarantees when running Tempest with plugins enabled about the
 quality of the plugin. Additionally, while there is no limitation on running
-with multiple plugins it's worth noting that poorly written plugins might not
+with multiple plugins, it's worth noting that poorly written plugins might not
 properly isolate their tests which could cause unexpected cross interactions
 between plugins.
 
 Notes for using plugins with virtualenvs
 ----------------------------------------
 
-When using a tempest inside a virtualenv (like when running under tox) you have
+When using a Tempest inside a virtualenv (like when running under tox) you have
 to ensure that the package that contains your plugin is either installed in the
 venv too or that you have system site-packages enabled. The virtualenv will
-isolate the tempest install from the rest of your system so just installing the
-plugin package on your system and then running tempest inside a venv will not
+isolate the Tempest install from the rest of your system so just installing the
+plugin package on your system and then running Tempest inside a venv will not
 work.
 
 Tempest also exposes a tox job, all-plugin, which will setup a tox virtualenv
diff --git a/doc/source/stable_branch_support_policy.rst b/doc/source/stable_branch_support_policy.rst
new file mode 100644
index 0000000..87e3ad1
--- /dev/null
+++ b/doc/source/stable_branch_support_policy.rst
@@ -0,0 +1,30 @@
+Stable Branch Support Policy
+============================
+
+Since the `Extended Maintenance policy`_ for stable branches was adopted
+OpenStack projects will keep stable branches around after a "stable" or
+"maintained" period for a phase of indeterminate length called "Extended
+Maintenance". Prior to this resolution Tempest supported all stable branches
+which were supported upstream. This policy does not scale under the new model
+as Tempest would be responsible for gating proposed changes against an ever
+increasing number of branches. Therefore due to resource constraints, Tempest
+will only provide support for branches in the "Maintained" phase from the
+documented `Support Phases`_. When a branch moves from the *Maintained* to the
+*Extended Maintenance* phase, Tempest will tag the removal of support for that
+branch as it has in the past when a branch goes end of life.
+
+The expectation for *Extended Maintenance* phase branches is that they will continue
+running Tempest during that phase of support. Since the REST APIs are stable
+interfaces across release boundaries, branches in these phases should run
+Tempest from master as long as possible. But, because we won't be actively
+testing branches in these phases, it's possible that we'll introduce changes to
+Tempest on master which will break support on *Extended Maintenance* phase
+branches. When this happens the expectation for those branches is to either
+switch to running Tempest from a tag with support for the branch, or blacklist
+a newly introduced test (if that is the cause of the issue). Tempest will not
+be creating stable branches to support *Extended Maintenance* phase branches, as
+the burden is on the *Extended Maintenance* phase branche maintainers, not the Tempest
+project, to support that branch.
+
+.. _Extended Maintenance policy: https://governance.openstack.org/tc/resolutions/20180301-stable-branch-eol.html
+.. _Support Phases: https://docs.openstack.org/project-team-guide/stable-branches.html#maintenance-phases
diff --git a/doc/source/test_removal.rst b/doc/source/test_removal.rst
index ddae6e2..e249bdd 100644
--- a/doc/source/test_removal.rst
+++ b/doc/source/test_removal.rst
@@ -1,21 +1,23 @@
+.. _test-removal:
+
 Tempest Test Removal Procedure
 ==============================
 
-Historically tempest was the only way of doing functional testing and
-integration testing in OpenStack. This was mostly only an artifact of tempest
+Historically, Tempest was the only way of doing functional testing and
+integration testing in OpenStack. This was mostly only an artifact of Tempest
 being the only proven pattern for doing this, not an artifact of a design
-decision. However, moving forward as functional testing is being spun up in
-each individual project we really only want tempest to be the integration test
-suite it was intended to be; testing the high level interactions between
-projects through REST API requests. In this model there are probably existing
-tests that aren't the best fit living in tempest. However, since tempest is
+decision. However, moving forward, as functional testing is being spun up in
+each individual project, we really only want Tempest to be the integration test
+suite it was intended to be: testing the high-level interactions between
+projects through REST API requests. In this model, there are probably existing
+tests that aren't the best fit living in Tempest. However, since Tempest is
 largely still the only gating test suite in this space we can't carelessly rip
 out everything from the tree. This document outlines the procedure which was
 developed to ensure we minimize the risk for removing something of value from
-the tempest tree.
+the Tempest tree.
 
-This procedure might seem overly conservative and slow paced, but this is by
-design to try and ensure we don't remove something that is actually providing
+This procedure might seem overly conservative and slow-paced, but this is by
+design to try to ensure we don't remove something that is actually providing
 value. Having potential duplication between testing is not a big deal
 especially compared to the alternative of removing something which is actually
 providing value and is actively catching bugs, or blocking incorrect patches
@@ -27,24 +29,24 @@
 3 prong rule for removal
 ^^^^^^^^^^^^^^^^^^^^^^^^
 
-In the proposal etherpad we'll be looking for answers to 3 questions
+In the proposal etherpad we'll be looking for answers to 3 questions:
 
 #. The tests proposed for removal must have equiv. coverage in a different
    project's test suite (whether this is another gating test project, or an in
    tree functional test suite). For API tests preferably the other project will
-   have a similar source of friction in place to prevent breaking api changes
-   so that we don't regress and let breaking api changes slip through the
+   have a similar source of friction in place to prevent breaking API changes
+   so that we don't regress and let breaking API changes slip through the
    gate.
 #. The test proposed for removal has a failure rate <  0.50% in the gate over
    the past release (the value and interval will likely be adjusted in the
    future)
 
    .. _`prong #3`:
-#. There must not be an external user/consumer of tempest
+#. There must not be an external user/consumer of Tempest
    that depends on the test proposed for removal
 
 The answers to 1 and 2 are easy to verify. For 1 just provide a link to the new
-test location. If you are linking to the tempest removal patch please also put
+test location. If you are linking to the Tempest removal patch please also put
 a Depends-On in the commit message for the commit which moved the test into
 another repo.
 
@@ -91,32 +93,35 @@
 #. paste the output table with numbers and the mysql command you ran to
    generate it into the etherpad.
 
-Eventually a cli interface will be created to make that a bit more friendly.
+Eventually, a CLI interface will be created to make that a bit more friendly.
 Also a dashboard is in the works so we don't need to manually run the command.
 
 The intent of the 2nd prong is to verify that moving the test into a project
-specific testing is preventing bugs (assuming the tempest tests were catching
-issues) from bubbling up a layer into tempest jobs. If we're seeing failure
+specific testing is preventing bugs (assuming the Tempest tests were catching
+issues) from bubbling up a layer into Tempest jobs. If we're seeing failure
 rates above a certain threshold in the gate checks that means the functional
 testing isn't really being effective in catching that bug (and therefore
-blocking it from landing) and having the testing run in tempest still has
+blocking it from landing) and having the testing run in Tempest still has
 value.
 
-However for the 3rd prong verification is a bit more subjective. The original
+However, for the 3rd prong verification is a bit more subjective. The original
 intent of this prong was mostly for refstack/defcore and also for things that
 running on the stable branches. We don't want to remove any tests if that
-would break our api consistency checking between releases, or something that
-defcore/refstack is depending on being in tempest. It's worth pointing out
-that if a test is used in defcore as part of interop testing then it will
-probably have continuing value being in tempest as part of the
+would break our API consistency checking between releases, or something that
+defcore/refstack is depending on being in Tempest. It's worth pointing out
+that if a test is used in `defcore`_ as part of `interop`_ testing then it will
+probably have continuing value being in Tempest as part of the
 integration/integrated tests in general. This is one area where some overlap
-is expected between testing in projects and tempest, which is not a bad thing.
+is expected between testing in projects and Tempest, which is not a bad thing.
+
+.. _defcore: https://wiki.openstack.org/wiki/Governance/InteropWG
+.. _interop: https://www.openstack.org/brand/interop
 
 Discussing the 3rd prong
 """"""""""""""""""""""""
 
 There are 2 approaches to addressing the 3rd prong. Either it can be raised
-during a qa meeting during the tempest discussion. Please put it on the agenda
+during a QA meeting during the Tempest discussion. Please put it on the agenda
 well ahead of the scheduled meeting. Since the meeting time will be well known
 ahead of time anyone who depends on the tests will have ample time beforehand
 to outline any concerns on the before the meeting. To give ample time for
@@ -133,19 +138,19 @@
 Exceptions to this procedure
 ----------------------------
 
-For the most part all tempest test removals have to go through this procedure
+For the most part, all Tempest test removals have to go through this procedure
 there are a couple of exceptions though:
 
-#. The class of testing has been decided to be outside the scope of tempest.
+#. The class of testing has been decided to be outside the scope of Tempest.
 #. A revert for a patch which added a broken test, or testing which didn't
    actually run in the gate (basically any revert for something which
    shouldn't have been added)
 #. Tests that would become out of scope as a consequence of an API change,
    as described in `API Compatibility`_.
    Such tests cannot live in Tempest because of the branchless nature of
-   Tempest. Such test must still honor `prong #3`_.
+   Tempest. Such tests must still honor `prong #3`_.
 
-For the first exception type the only types of testing in tree which have been
+For the first exception type, the only types of testing in the tree which have been
 declared out of scope at this point are:
 
 * The CLI tests (which should be completely removed at this point)
@@ -154,14 +159,14 @@
 * XML API Tests (which should be completely removed at this point)
 * EC2 API/boto tests (which should be completely removed at this point)
 
-For tests that fit into this category the only criteria for removal is that
+For tests that fit into this category, the only criteria for removal is that
 there is equivalent testing elsewhere.
 
 Tempest Scope
 ^^^^^^^^^^^^^
 
-Starting in the liberty cycle tempest has defined a set of projects which
-are defined as in scope for direct testing in tempest. As of today that list
+Starting in the liberty cycle Tempest, has defined a set of projects which
+are defined as in scope for direct testing in Tempest. As of today that list
 is:
 
 * Keystone
@@ -171,23 +176,23 @@
 * Neutron
 * Swift
 
-anything that lives in tempest which doesn't test one of these projects can be
+Anything that lives in Tempest which doesn't test one of these projects can be
 removed assuming there is equivalent testing elsewhere. Preferably using the
 `tempest plugin mechanism`_
-to maintain continuity after migrating the tests out of tempest.
+to maintain continuity after migrating the tests out of Tempest.
 
 .. _tempest plugin mechanism: https://docs.openstack.org/tempest/latest/plugin.html
 
 API Compatibility
 """""""""""""""""
 
-If an API introduces a non-discoverable, backward incompatible change, and
-such change is not backported to all versions supported by Tempest, tests for
+If an API introduces a non-discoverable, backward-incompatible change, and
+such a change is not backported to all versions supported by Tempest, tests for
 that API cannot live in Tempest anymore.
 This is because tests would not be able to know or control which API response
 to expect, and thus would not be able to enforce a specific behavior.
 
-If a test exists in Tempest that would meet this criteria as consequence of a
-change, the test must be removed according to the procedure discussed into
+If a test exists in Tempest that would meet these criteria as a consequence of a
+change, the test must be removed according to the procedure discussed in
 this document. The API change should not be merged until all conditions
 required for test removal can be met.
diff --git a/doc/source/write_tests.rst b/doc/source/write_tests.rst
index fff2405..0a29b7b 100644
--- a/doc/source/write_tests.rst
+++ b/doc/source/write_tests.rst
@@ -4,7 +4,7 @@
 ##########################
 
 This guide serves as a starting point for developers working on writing new
-Tempest tests. At a high level tests in Tempest are just tests that conform to
+Tempest tests. At a high level, tests in Tempest are just tests that conform to
 the standard python `unit test`_ framework. But there are several aspects of
 that are unique to Tempest and its role as an integration test suite running
 against a real cloud.
diff --git a/playbooks/devstack-tempest.yaml b/playbooks/devstack-tempest.yaml
index a684984..01155a8 100644
--- a/playbooks/devstack-tempest.yaml
+++ b/playbooks/devstack-tempest.yaml
@@ -3,7 +3,7 @@
 # avoid zuul retrying on legitimate failures.
 - hosts: all
   roles:
-    - run-devstack
+    - orchestrate-devstack
 
 # We run tests only on one node, regardless how many nodes are in the system
 - hosts: tempest
diff --git a/playbooks/post-tempest.yaml b/playbooks/post-tempest.yaml
index 70dac09..6e0bcad 100644
--- a/playbooks/post-tempest.yaml
+++ b/playbooks/post-tempest.yaml
@@ -1,23 +1,6 @@
-- hosts: all
+- hosts: tempest
   become: true
-  vars:
-    logs_root: "{{ devstack_base_dir|default('/opt/stack') }}"
-    test_results_stage_name: 'test_results'
   roles:
-    - role: process-test-results
-      test_results_dir: '{{ logs_root }}/tempest'
-      tox_envdir: tempest
+    - role: fetch-subunit-output
+      zuul_work_dir: '{{ devstack_base_dir }}/tempest'
     - role: process-stackviz
-    - role: stage-output
-      zuul_copy_output:
-        { '{{ logs_root }}/tempest/etc/tempest.conf': 'logs',
-          '{{ logs_root }}/tempest/etc/accounts.yaml': 'logs',
-          '{{ logs_root }}/tempest/tempest.log': 'logs',
-          '{{ stage_dir }}/{{ test_results_stage_name }}.subunit': 'logs',
-          '{{ stage_dir }}/{{ test_results_stage_name }}.html': 'logs',
-          '{{ stage_dir }}/stackviz': 'logs' }
-      extensions_to_txt:
-        - conf
-        - log
-        - yaml
-        - yml
diff --git a/releasenotes/notes/12/12.2.0-service_client_config-8a1d7b4de769c633.yaml b/releasenotes/notes/12/12.2.0-service_client_config-8a1d7b4de769c633.yaml
index 3e43f9a..985acb0 100644
--- a/releasenotes/notes/12/12.2.0-service_client_config-8a1d7b4de769c633.yaml
+++ b/releasenotes/notes/12/12.2.0-service_client_config-8a1d7b4de769c633.yaml
@@ -3,4 +3,4 @@
   - A new helper method `service_client_config` has been added
     to the stable module config.py that returns extracts from
     configuration into a dictionary the configuration settings
-    relevant for the initisialisation of a service client.
+    relevant for the initialization of a service client.
diff --git a/releasenotes/notes/16/16.0.0-add-content-type-without-spaces-b2c9b91b257814f3.yaml b/releasenotes/notes/16/16.0.0-add-content-type-without-spaces-b2c9b91b257814f3.yaml
index 0075a36..9ae46fd 100644
--- a/releasenotes/notes/16/16.0.0-add-content-type-without-spaces-b2c9b91b257814f3.yaml
+++ b/releasenotes/notes/16/16.0.0-add-content-type-without-spaces-b2c9b91b257814f3.yaml
@@ -2,8 +2,8 @@
 upgrade:
   - The ``JSON_ENC`` and ``TXT_ENC`` option in the ``_error_checker``
     section have been added with additional content-type which are
-    defined in RFC7231 but missing in the currnt rest_client.py file.
+    defined in RFC7231 but missing in the current rest_client.py file.
     The lack of these additional content-type will cause defcore test
     to fail for OpenStack public cloud which uses tomcat module in the
     api gateway. The additions are ``application/json;charset=utf-8``,
-    ``text/html;charset=utf-8``,``text/plain;charset=utf-8``
\ No newline at end of file
+    ``text/html;charset=utf-8``, ``text/plain;charset=utf-8``
diff --git a/releasenotes/notes/16/16.0.0-add-tempest-run-combine-option-e94c1049ba8985d5.yaml b/releasenotes/notes/16/16.0.0-add-tempest-run-combine-option-e94c1049ba8985d5.yaml
index 73900ca..e9c3694 100644
--- a/releasenotes/notes/16/16.0.0-add-tempest-run-combine-option-e94c1049ba8985d5.yaml
+++ b/releasenotes/notes/16/16.0.0-add-tempest-run-combine-option-e94c1049ba8985d5.yaml
@@ -1,6 +1,6 @@
 ---
 features:
   - |
-    Adds a new cli option to tempest run, --combine, which is used to indicate
-    you want the subunit stream output combined with the previous run's in
-    the testr repository
+    Adds a new cli option to tempest run, ``--combine``, which is used
+    to indicate you want the subunit stream output combined with the
+    previous run's in the testr repository
diff --git a/releasenotes/notes/16/16.0.0-create-server-tags-client-8c0042a77e859af6.yaml b/releasenotes/notes/16/16.0.0-create-server-tags-client-8c0042a77e859af6.yaml
index 9927971..18ad5b9 100644
--- a/releasenotes/notes/16/16.0.0-create-server-tags-client-8c0042a77e859af6.yaml
+++ b/releasenotes/notes/16/16.0.0-create-server-tags-client-8c0042a77e859af6.yaml
@@ -2,7 +2,7 @@
 features:
   - |
     Add server tags APIs to the servers_client library.
-    This feature enables the possibility of upating, deleting
+    This feature enables the possibility of updating, deleting
     and checking existence of a tag on a server, as well
     as updating and deleting all tags on a server.
 
diff --git a/releasenotes/notes/16/16.0.0-deprecate-resources-prefix-option-ad490c0a30a0266b.yaml b/releasenotes/notes/16/16.0.0-deprecate-resources-prefix-option-ad490c0a30a0266b.yaml
index f679208..0b45d0d 100644
--- a/releasenotes/notes/16/16.0.0-deprecate-resources-prefix-option-ad490c0a30a0266b.yaml
+++ b/releasenotes/notes/16/16.0.0-deprecate-resources-prefix-option-ad490c0a30a0266b.yaml
@@ -6,5 +6,5 @@
 deprecations:
   - The resources_prefix is marked as deprecated because it is
     enough to set 'tempest' as the prefix on rand_name() to
-    ideintify resources which are created by Tempest and no
+    identify resources which are created by Tempest and no
     projects set this option on OpenStack dev community.
diff --git a/releasenotes/notes/16/16.0.0-remove-deprecated-allow_port_security_disabled-option-d0ffaeb2e7817707.yaml b/releasenotes/notes/16/16.0.0-remove-deprecated-allow_port_security_disabled-option-d0ffaeb2e7817707.yaml
index 9d7102f..5b4a96d 100644
--- a/releasenotes/notes/16/16.0.0-remove-deprecated-allow_port_security_disabled-option-d0ffaeb2e7817707.yaml
+++ b/releasenotes/notes/16/16.0.0-remove-deprecated-allow_port_security_disabled-option-d0ffaeb2e7817707.yaml
@@ -1,5 +1,5 @@
 ---
 upgrade:
   - |
-    The deprecated config option 'allow_port_security_disabled' from compute_feature_enabled
-    group has been removed.
+    The deprecated config option ``allow_port_security_disabled`` from
+    ``compute_feature_enabled`` group has been removed.
diff --git a/releasenotes/notes/16/16.0.0-remove-deprecated-compute-validation-config-options-part-2-5cd17b6e0e6cb8a3.yaml b/releasenotes/notes/16/16.0.0-remove-deprecated-compute-validation-config-options-part-2-5cd17b6e0e6cb8a3.yaml
index b4e4dd1..c8b0ca8 100644
--- a/releasenotes/notes/16/16.0.0-remove-deprecated-compute-validation-config-options-part-2-5cd17b6e0e6cb8a3.yaml
+++ b/releasenotes/notes/16/16.0.0-remove-deprecated-compute-validation-config-options-part-2-5cd17b6e0e6cb8a3.yaml
@@ -8,4 +8,4 @@
     - ``compute.ssh_user`` (available as ``validation.image_ssh_user``)
     - ``scenario.ssh_user`` (available as ``validation.image_ssh_user``)
     - ``compute.network_for_ssh`` (available as ``validation.network_for_ssh``)
-    - ``compute.ping_timeout `` (available as ``validation.ping_timeout``)
+    - ``compute.ping_timeout`` (available as ``validation.ping_timeout``)
diff --git a/releasenotes/notes/add-additional-methods-to-policy-client-library-b8279c18335588c9.yaml b/releasenotes/notes/add-additional-methods-to-policy-client-library-b8279c18335588c9.yaml
new file mode 100644
index 0000000..cd5284d
--- /dev/null
+++ b/releasenotes/notes/add-additional-methods-to-policy-client-library-b8279c18335588c9.yaml
@@ -0,0 +1,6 @@
+---
+features:
+  - |
+    Add ``v3-ext/OS-ENDPOINT-POLICY`` API calls to support creation, deletion and
+    retrieval of associations between service endpoints and policies. Such associations
+    enable an endpoint to request its policy.
diff --git a/releasenotes/notes/add-extra-apis-to-volume-v3-services-client-bf9b235cf5a611fe.yaml b/releasenotes/notes/add-extra-apis-to-volume-v3-services-client-bf9b235cf5a611fe.yaml
new file mode 100644
index 0000000..03d0ae8
--- /dev/null
+++ b/releasenotes/notes/add-extra-apis-to-volume-v3-services-client-bf9b235cf5a611fe.yaml
@@ -0,0 +1,6 @@
+---
+features:
+  - |
+    Add ``enable_service``, ``disable_service`` , ``disable_log_reason``,
+    ``freeze_host`` and ``thaw_host`` API endpoints to volume v3
+    ``services_client``.
diff --git a/releasenotes/notes/add-load-list-cmd-35a4a2e6ea0a36fd.yaml b/releasenotes/notes/add-load-list-cmd-35a4a2e6ea0a36fd.yaml
index 403bbad..145e7dd 100644
--- a/releasenotes/notes/add-load-list-cmd-35a4a2e6ea0a36fd.yaml
+++ b/releasenotes/notes/add-load-list-cmd-35a4a2e6ea0a36fd.yaml
@@ -1,7 +1,7 @@
 ---
 features:
   - |
-    Adds a new cli option to tempest run, --load-list <list-file>
+    Adds a new cli option to tempest run, ``--load-list <list-file>``
     to specify target tests to run from a list-file. The list-file
-    supports the output format of the tempest run --list-tests
+    supports the output format of the tempest run ``--list-tests``
     command.
diff --git a/releasenotes/notes/add-port-profile-config-option-2610b2fa67027960.yaml b/releasenotes/notes/add-port-profile-config-option-2610b2fa67027960.yaml
new file mode 100644
index 0000000..19d47d1
--- /dev/null
+++ b/releasenotes/notes/add-port-profile-config-option-2610b2fa67027960.yaml
@@ -0,0 +1,9 @@
+---
+features:
+  - A new config option 'port_profile' is added to the section
+    'network' to specify capabilities of the port.
+    By default this is set to {}. When using OVS HW offload
+    feature we need to create Neutron port with a certain
+    capability. This is done by creating Neutron port with
+    binding profile. To be able to test this we need profile
+    capability support in Tempest as well.
diff --git a/releasenotes/notes/add-save-state-option-5ea67858cbaca969.yaml b/releasenotes/notes/add-save-state-option-5ea67858cbaca969.yaml
index 8fdf4f0..abd2610 100644
--- a/releasenotes/notes/add-save-state-option-5ea67858cbaca969.yaml
+++ b/releasenotes/notes/add-save-state-option-5ea67858cbaca969.yaml
@@ -1,4 +1,5 @@
 ---
 features:
   - |
-    Add --save-state option to allow saving state of cloud before tempest run.
+    Add ``--save-state`` option to allow saving state of cloud before
+    tempest run.
diff --git a/releasenotes/notes/add-show-encryption-specs-item-api-to-v2-encryption-types-client-290b421cd4bc0c0e.yaml b/releasenotes/notes/add-show-encryption-specs-item-api-to-v2-encryption-types-client-290b421cd4bc0c0e.yaml
new file mode 100644
index 0000000..9e13afc
--- /dev/null
+++ b/releasenotes/notes/add-show-encryption-specs-item-api-to-v2-encryption-types-client-290b421cd4bc0c0e.yaml
@@ -0,0 +1,6 @@
+---
+features:
+  - |
+    Add show encryption specs item API to v2 encryption_types_client library.
+    This feature enables the possibility to show specific encryption specs for
+    a volume type.
diff --git a/releasenotes/notes/add-show-quota-details-api-to-network-quotas-client-3fffd302cc5d335f.yaml b/releasenotes/notes/add-show-quota-details-api-to-network-quotas-client-3fffd302cc5d335f.yaml
new file mode 100644
index 0000000..6c44ba0
--- /dev/null
+++ b/releasenotes/notes/add-show-quota-details-api-to-network-quotas-client-3fffd302cc5d335f.yaml
@@ -0,0 +1,7 @@
+---
+features:
+  - |
+    Add extension API show quota details to network quotas_client library.
+    This feature enables the possibility to show a quota set for a specified
+    project that includes the quota's used, limit and reserved counts per
+    resource.
diff --git a/releasenotes/notes/add-storyboard-in-skip-because-decorator-3e139aa8a4f7970f.yaml b/releasenotes/notes/add-storyboard-in-skip-because-decorator-3e139aa8a4f7970f.yaml
new file mode 100644
index 0000000..dd4a90b
--- /dev/null
+++ b/releasenotes/notes/add-storyboard-in-skip-because-decorator-3e139aa8a4f7970f.yaml
@@ -0,0 +1,17 @@
+---
+features:
+  - |
+    Add a new parameter called ``bug_type`` to
+    ``tempest.lib.decorators.related_bug`` and
+    ``tempest.lib.decorators.skip_because`` decorators, which accepts
+    2 values:
+
+    * launchpad
+    * storyboard
+
+    This offers the possibility of tracking bugs related to tests using
+    launchpad or storyboard references. The default value is launchpad
+    for backward compatibility.
+
+    Passing in a non-digit ``bug`` value to either decorator will raise
+    a ``InvalidParam`` exception (previously ``ValueError``).
diff --git a/releasenotes/notes/add-support-args-kwargs-in-call-until-true-a91k592h5a64exf7.yaml b/releasenotes/notes/add-support-args-kwargs-in-call-until-true-a91k592h5a64exf7.yaml
index e23abe3..9c30a0c 100644
--- a/releasenotes/notes/add-support-args-kwargs-in-call-until-true-a91k592h5a64exf7.yaml
+++ b/releasenotes/notes/add-support-args-kwargs-in-call-until-true-a91k592h5a64exf7.yaml
@@ -2,4 +2,4 @@
 features:
   - Add support of args and kwargs when calling func in call_until_true,
     also to log the cost time when call_until_true returns True or False
-    for debuggin.
+    for debugging.
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/cinder-use-os-endpoint-type-c11f63fd468ceb4c.yaml b/releasenotes/notes/cinder-use-os-endpoint-type-c11f63fd468ceb4c.yaml
new file mode 100644
index 0000000..1dda4e1
--- /dev/null
+++ b/releasenotes/notes/cinder-use-os-endpoint-type-c11f63fd468ceb4c.yaml
@@ -0,0 +1,6 @@
+---
+upgrade:
+  - |
+    Cinder CLI calls have now been updated to use the ``--os-endpoint-type``
+    option instead of ``--endpoint-type``. The latter had been deprecated in
+    Cinder and has been removed in the Rocky release.
diff --git a/releasenotes/notes/cli-tests-v3fixes-fb38189cefd64213.yaml b/releasenotes/notes/cli-tests-v3fixes-fb38189cefd64213.yaml
index e3443c8..2a0a86c 100644
--- a/releasenotes/notes/cli-tests-v3fixes-fb38189cefd64213.yaml
+++ b/releasenotes/notes/cli-tests-v3fixes-fb38189cefd64213.yaml
@@ -2,8 +2,8 @@
 other:
   - |
     The CLIClient class, when it calls a command line client, uses
-    --os-project-name instead of --os-tenant-name for the project, and
-    passes --os-identity-api-version (default empty).
+    ``--os-project-name`` instead of ``--os-tenant-name`` for the
+    project, and passes ``--os-identity-api-version`` (default empty).
     All CLI clients still available in supported releases of OpenStack
-    which are wrapped by the cmd_with_auth() method support those
+    which are wrapped by the ``cmd_with_auth()`` method support those
     switches.
diff --git a/releasenotes/notes/compare-header-version-func-de5139b2161b3627.yaml b/releasenotes/notes/compare-header-version-func-de5139b2161b3627.yaml
index 305e756..cc6c51b 100644
--- a/releasenotes/notes/compare-header-version-func-de5139b2161b3627.yaml
+++ b/releasenotes/notes/compare-header-version-func-de5139b2161b3627.yaml
@@ -3,7 +3,7 @@
   - |
     Add a new function called ``compare_version_header_to_response`` to
     ``tempest.lib.common.api_version_utils``, which compares the API
-    micoversion in the response header to another microversion using the
+    microversion in the response header to another microversion using the
     comparators defined in
     ``tempest.lib.common.api_version_request.APIVersionRequest``.
 
diff --git a/releasenotes/notes/create-mount-config-drive-to-lib-1a6e912b8afbcc7e.yaml b/releasenotes/notes/create-mount-config-drive-to-lib-1a6e912b8afbcc7e.yaml
new file mode 100644
index 0000000..f92cd78
--- /dev/null
+++ b/releasenotes/notes/create-mount-config-drive-to-lib-1a6e912b8afbcc7e.yaml
@@ -0,0 +1,4 @@
+---
+features:
+  - A function has been added to the common library to allow mounting and
+    unmounting of the config drive consistently.
diff --git a/releasenotes/notes/deprecate-volume-api-selection-config-options-b95c5c0ccbf38916.yaml b/releasenotes/notes/deprecate-volume-api-selection-config-options-b95c5c0ccbf38916.yaml
new file mode 100644
index 0000000..1bea6d0
--- /dev/null
+++ b/releasenotes/notes/deprecate-volume-api-selection-config-options-b95c5c0ccbf38916.yaml
@@ -0,0 +1,19 @@
+---
+deprecations:
+  - |
+    The v2 volume API has been deprecated since Pike release.
+    Volume v3 API is current and Tempest volume tests can
+    be run against v2 or v3 API based on config option
+    ``CONF.volume.catalog_type``. If catalog_type is ``volumev2``, then
+    all the volume tests will run against v2 API. If catalog_type is
+    ``volumev3`` which is default in Tempest, then all the volume
+    tests will run against v3 API.
+    That makes below config options unusable in Tempest which used to
+    select the target volume API for volume tests.
+
+    * ``CONF.volume-feature-enabled.api_v2``
+    * ``CONF.volume-feature-enabled.api_v3``
+
+    Tempest deprecate the above two config options in Rocky release
+    and will be removed in future. Alternatively ``CONF.volume.catalog_type``
+    can be used to run the Tempest against volume v2 or v3 API.
diff --git a/releasenotes/notes/fix-show-image-file-expected-code-92d97342d0f6d60e.yaml b/releasenotes/notes/fix-show-image-file-expected-code-92d97342d0f6d60e.yaml
new file mode 100644
index 0000000..a4050a5
--- /dev/null
+++ b/releasenotes/notes/fix-show-image-file-expected-code-92d97342d0f6d60e.yaml
@@ -0,0 +1,6 @@
+---
+fixes:
+  - |
+    Fix show_image_file interface in v2 ImagesClient: Bug#1756264.
+    The expected success code of show_image_file is changed from
+    ``200`` to ``[200, 204, 206]``.
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/omit_X-Subject-Token_from_log-1bf5fef88c80334b.yaml b/releasenotes/notes/omit_X-Subject-Token_from_log-1bf5fef88c80334b.yaml
new file mode 100644
index 0000000..51c8f79
--- /dev/null
+++ b/releasenotes/notes/omit_X-Subject-Token_from_log-1bf5fef88c80334b.yaml
@@ -0,0 +1,7 @@
+---
+security:
+  - |
+    The x-subject-token of a response header is ommitted from log,
+    but clients specify the same token on a request header on
+    Keystone API and that was not omitted. In this release,
+    that has been omitted for a security reason.
diff --git a/releasenotes/notes/removal-deprecated-volume-config-options-21c4412f3c600923.yaml b/releasenotes/notes/removal-deprecated-volume-config-options-21c4412f3c600923.yaml
new file mode 100644
index 0000000..32147c7
--- /dev/null
+++ b/releasenotes/notes/removal-deprecated-volume-config-options-21c4412f3c600923.yaml
@@ -0,0 +1,24 @@
+---
+upgrade:
+  - |
+    Below config option was deprecated for removal since juno release.
+    It's time to remove it as all supported stable branches and Tempest plugins
+    are good to handle it.
+
+    * ``[volume_feature_enabled].api_v1``
+
+    Also Tempest removes the below corresponding service clients alias from
+    client.py which were being set based on above removed config option.
+
+    * self.backups_client
+    * self.encryption_types_client
+    * self.snapshots_client
+    * self.volume_availability_zone_client
+    * self.volume_hosts_client
+    * self.volume_limits_client
+    * self.volume_qos_client
+    * self.volume_quotas_client
+    * self.volume_services_client
+    * self.volume_types_client
+    * self.volumes_client
+    * self.volumes_extension_client
diff --git a/releasenotes/notes/remove-allow_tenant_isolation-option-03f0d998eb498d44.yaml b/releasenotes/notes/remove-allow_tenant_isolation-option-03f0d998eb498d44.yaml
new file mode 100644
index 0000000..4f4516b
--- /dev/null
+++ b/releasenotes/notes/remove-allow_tenant_isolation-option-03f0d998eb498d44.yaml
@@ -0,0 +1,6 @@
+---
+upgrade:
+  - |
+    Remove deprecated config option ``allow_tenant_isolation`` from
+    ``auth`` and ``compute`` groups. Use ``use_dynamic_credentials`` directly
+    instead of the removed option.
diff --git a/releasenotes/notes/start-of-queens-support-fea9051ba1d85fc7.yaml b/releasenotes/notes/start-of-queens-support-fea9051ba1d85fc7.yaml
new file mode 100644
index 0000000..10b2300
--- /dev/null
+++ b/releasenotes/notes/start-of-queens-support-fea9051ba1d85fc7.yaml
@@ -0,0 +1,12 @@
+---
+prelude: >
+    This release marks the start of Queens release support in Tempest.
+    This release also marks the end of support for Newton in Tempest.
+other:
+    - OpenStack Releases supported after this release are **Queens**, **Pike**,
+      and **Ocata**.
+
+      The release under current development of this tag is Rocky, meaning
+      that every Tempest commit is also tested against master during the Rocky
+      cycle. However, this does not necessarily mean that using Tempest as of
+      this tag will work against a Rocky (or future release) cloud.
diff --git a/releasenotes/notes/switch-to-stestr-8c9f834b3f5a55d8.yaml b/releasenotes/notes/switch-to-stestr-8c9f834b3f5a55d8.yaml
new file mode 100644
index 0000000..9e2f1ba
--- /dev/null
+++ b/releasenotes/notes/switch-to-stestr-8c9f834b3f5a55d8.yaml
@@ -0,0 +1,13 @@
+---
+features:
+- The Tempest CLI commands have switched from calling testrepository internally
+  to use stestr instead. This means that all of the features and bug fixes from
+  moving to stestr are available to the tempest commands.
+
+upgrade:
+- Tempest CLI commands will no long rely on anything from testr. This means any
+  data in existing testr internals that were being exposed are no longer
+  present. For example things like the .testr directories will be silently
+  ignored. There is a potential incompatibility for existing users who are
+  relying on test results being stored by testr. Anything relying on previous
+  testr behavior will need to be updated to handle stestr.
diff --git a/releasenotes/notes/tempest-lib-compute-update-service-6019d2dcfe4a1c5d.yaml b/releasenotes/notes/tempest-lib-compute-update-service-6019d2dcfe4a1c5d.yaml
new file mode 100644
index 0000000..d67cdb8
--- /dev/null
+++ b/releasenotes/notes/tempest-lib-compute-update-service-6019d2dcfe4a1c5d.yaml
@@ -0,0 +1,11 @@
+---
+features:
+  - |
+    The ``update_service`` API is added to the ``services_client`` compute
+    library. This API is introduced in microversion 2.53 and supersedes
+    the following APIs:
+
+    * ``PUT /os-services/disable`` (``disable_service``)
+    * ``PUT /os-services/disable-log-reason`` (``disable_log_reason``)
+    * ``PUT /os-services/enable`` (``enable_service``)
+    * ``PUT /os-services/force-down`` (``update_forced_down``)
diff --git a/releasenotes/notes/tempest-rocky-release-0fc3312053923380.yaml b/releasenotes/notes/tempest-rocky-release-0fc3312053923380.yaml
new file mode 100644
index 0000000..e9c77a6
--- /dev/null
+++ b/releasenotes/notes/tempest-rocky-release-0fc3312053923380.yaml
@@ -0,0 +1,16 @@
+---
+prelude: >
+    This release is to tag the Tempest for OpenStack Rocky release.
+    After this release, Tempest will support below OpenStack Releases:
+
+      * Rocky
+      * Queens
+      * Pike
+      * Ocata
+
+    Current development of Tempest is for OpenStack Stein development
+    cycle. Every Tempest commit is also tested against master during
+    the Stein cycle. However, this does not necessarily mean that using
+    Tempest as of this tag will work against a Stein (or future release)
+    cloud.
+    To be on safe side, use this tag to test the OpenStack Rocky release.
\ No newline at end of file
diff --git a/releasenotes/notes/tempest-run-fix-updates-564b41706decbba1.yaml b/releasenotes/notes/tempest-run-fix-updates-564b41706decbba1.yaml
new file mode 100644
index 0000000..0f9a0f6
--- /dev/null
+++ b/releasenotes/notes/tempest-run-fix-updates-564b41706decbba1.yaml
@@ -0,0 +1,9 @@
+---
+features:
+  - |
+    Adds a new CLI arg in tempest run, ``--black-regex``, which is a
+    regex to exclude the tests that match it.
+fixes:
+  - |
+    Fixes tempest run CLI args mutually exclusive behavior which should not
+    be the case anymore (Bug#1751201).
diff --git a/releasenotes/notes/tempest-workspace-delete-directory-feature-74d6d157a5a05561.yaml b/releasenotes/notes/tempest-workspace-delete-directory-feature-74d6d157a5a05561.yaml
index ec21098..c69ed50 100644
--- a/releasenotes/notes/tempest-workspace-delete-directory-feature-74d6d157a5a05561.yaml
+++ b/releasenotes/notes/tempest-workspace-delete-directory-feature-74d6d157a5a05561.yaml
@@ -1,5 +1,5 @@
 ---
 features:
   - |
-    Added tempest workspace remove --name <workspace_name> --rmdir
+    Added tempest workspace remove ``--name <workspace_name> --rmdir``
     feature to delete the workspace directory as well as entry.
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..d3a6851
--- /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 allows the expected
+    VNC server name in the response header to be specified. For example,
+    obvious at hand names are '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/releasenotes/source/index.rst b/releasenotes/source/index.rst
index df1de46..3be014f 100644
--- a/releasenotes/source/index.rst
+++ b/releasenotes/source/index.rst
@@ -2,19 +2,23 @@
  Tempest Release Notes
 ===========================
 
- .. toctree::
-    :maxdepth: 1
+.. toctree::
+   :maxdepth: 1
 
-    unreleased
-    v17.0.0
-    v16.1.0
-    v16.0.0
-    v15.0.0
-    v14.0.0
-    v13.0.0
-    v12.0.0
-    v11.0.0
-    v10.0.0
+   unreleased
+   v19.0.0
+   v18.0.0
+   v17.2.0
+   v17.1.0
+   v17.0.0
+   v16.1.0
+   v16.0.0
+   v15.0.0
+   v14.0.0
+   v13.0.0
+   v12.0.0
+   v11.0.0
+   v10.0.0
 
 Indices and tables
 ==================
diff --git a/releasenotes/source/v17.1.0.rst b/releasenotes/source/v17.1.0.rst
new file mode 100644
index 0000000..b8fd570
--- /dev/null
+++ b/releasenotes/source/v17.1.0.rst
@@ -0,0 +1,6 @@
+=====================
+v17.1.0 Release Notes
+=====================
+
+.. release-notes:: 17.1.0 Release Notes
+   :version: 17.1.0
diff --git a/releasenotes/source/v17.2.0.rst b/releasenotes/source/v17.2.0.rst
new file mode 100644
index 0000000..8566ae4
--- /dev/null
+++ b/releasenotes/source/v17.2.0.rst
@@ -0,0 +1,6 @@
+=====================
+v17.2.0 Release Notes
+=====================
+
+.. release-notes:: 17.2.0 Release Notes
+   :version: 17.2.0
diff --git a/releasenotes/source/v18.0.0.rst b/releasenotes/source/v18.0.0.rst
new file mode 100644
index 0000000..aa05db3
--- /dev/null
+++ b/releasenotes/source/v18.0.0.rst
@@ -0,0 +1,6 @@
+=====================
+v18.0.0 Release Notes
+=====================
+
+.. release-notes:: 18.0.0 Release Notes
+   :version: 18.0.0
diff --git a/releasenotes/source/v19.0.0.rst b/releasenotes/source/v19.0.0.rst
new file mode 100644
index 0000000..bcffe5d
--- /dev/null
+++ b/releasenotes/source/v19.0.0.rst
@@ -0,0 +1,6 @@
+=====================
+v19.0.0 Release Notes
+=====================
+
+.. release-notes:: 19.0.0 Release Notes
+   :version: 19.0.0
diff --git a/requirements.txt b/requirements.txt
index c02cd05..7520d42 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -7,19 +7,18 @@
 testtools>=2.2.0 # MIT
 paramiko>=2.0.0 # LGPLv2.1+
 netaddr>=0.7.18 # BSD
-testrepository>=0.0.18 # Apache-2.0/BSD
-oslo.concurrency>=3.25.0 # Apache-2.0
-oslo.config>=5.1.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
 oslo.serialization!=2.19.1,>=2.18.0 # Apache-2.0
 oslo.utils>=3.33.0 # Apache-2.0
 six>=1.10.0 # MIT
 fixtures>=3.0.0 # Apache-2.0/BSD
-PyYAML>=3.10 # MIT
+PyYAML>=3.12 # MIT
 python-subunit>=1.0.0 # Apache-2.0/BSD
 stevedore>=1.20.0 # Apache-2.0
 PrettyTable<0.8,>=0.7.1 # BSD
-os-testr>=1.0.0 # Apache-2.0
 urllib3>=1.21.1 # MIT
 debtcollector>=1.2.0 # Apache-2.0
 unittest2>=1.1.0 # BSD
diff --git a/roles/process-stackviz/README.rst b/roles/process-stackviz/README.rst
index 54c217b..a8447d2 100644
--- a/roles/process-stackviz/README.rst
+++ b/roles/process-stackviz/README.rst
@@ -16,7 +16,7 @@
    The stage directory where the input data can be found and
    the output will be produced.
 
-.. zuul:rolevar:: test_results_stage_name
-   :default: test_results
+.. zuul:rolevar:: zuul_work_dir
+   :default: {{ devstack_base_dir }}/tempest
 
-   The name of the subunit file to be used as input.
+   Directory to work in. It has to be a fully qualified path.
diff --git a/roles/process-stackviz/defaults/main.yaml b/roles/process-stackviz/defaults/main.yaml
index c6a64d1..f3bc32b 100644
--- a/roles/process-stackviz/defaults/main.yaml
+++ b/roles/process-stackviz/defaults/main.yaml
@@ -1,3 +1,3 @@
 devstack_base_dir: /opt/stack
 stage_dir: "{{ ansible_user_dir }}"
-test_results_stage_name: test_results
+zuul_work_dir: "{{ devstack_base_dir }}/tempest"
diff --git a/roles/process-stackviz/tasks/main.yaml b/roles/process-stackviz/tasks/main.yaml
index 09de606..3724e0e 100644
--- a/roles/process-stackviz/tasks/main.yaml
+++ b/roles/process-stackviz/tasks/main.yaml
@@ -9,11 +9,11 @@
 
 - name: Check if subunit data exists
   stat:
-    path: "{{ stage_dir }}/{{ test_results_stage_name }}.subunit"
+    path: "{{ zuul_work_dir }}/testrepository.subunit"
   register: subunit_input
 
 - debug:
-    msg: "Subunit file could not be found at {{ stage_dir }}/{{ test_results_stage_name }}.subunit"
+    msg: "Subunit file could not be found at {{ zuul_work_dir }}/testrepository.subunit"
   when: not subunit_input.stat.exists
 
 - name: Install stackviz
@@ -50,6 +50,7 @@
     - stackviz_archive.stat.exists
     - subunit_input.stat.exists
     - dstat_input.stat.exists
+  failed_when: False
 
 - name: Run stackviz without dstat
   shell: |
@@ -61,3 +62,4 @@
     - stackviz_archive.stat.exists
     - subunit_input.stat.exists
     - not dstat_input.stat.exists
+  failed_when: False
diff --git a/roles/run-tempest/README.rst b/roles/run-tempest/README.rst
index 33dcce9..71b8e4f 100644
--- a/roles/run-tempest/README.rst
+++ b/roles/run-tempest/README.rst
@@ -29,7 +29,30 @@
                # Line with only a comment.
                (tempest\.(api|scenario|thirdparty)).*$    # Run only api scenario and third party
 
+.. zuul:rolevar:: tempest_test_blacklist
+
+   Specifies a blacklist file to skip tests that are not needed.
+
+   Pass a full path to the file.
+
 .. zuul:rolevar:: tox_envlist
    :default: smoke
 
    The Tempest tox environment to run.
+
+.. zuul:rolevar:: tempest_black_regex
+   :default: ''
+
+   A regular expression used to skip the tests.
+
+   It works only when used with some specific tox environments
+   ('all', 'all-plugin'.)
+
+   Multi-line and commented regexs can be achieved by doing this:
+
+       ::
+           vars:
+             tempest_black_regex: |
+               (?x)    # Ignore comments and whitespaces
+               # Line with only a comment.
+               (tempest.api.identity).*$
diff --git a/roles/run-tempest/defaults/main.yaml b/roles/run-tempest/defaults/main.yaml
index 85e94f2..c89eb93 100644
--- a/roles/run-tempest/defaults/main.yaml
+++ b/roles/run-tempest/defaults/main.yaml
@@ -1,3 +1,4 @@
 devstack_base_dir: /opt/stack
 tempest_test_regex: ''
 tox_envlist: smoke
+tempest_black_regex: ''
diff --git a/roles/run-tempest/tasks/main.yaml b/roles/run-tempest/tasks/main.yaml
index 87898db..54ddc71 100644
--- a/roles/run-tempest/tasks/main.yaml
+++ b/roles/run-tempest/tasks/main.yaml
@@ -20,8 +20,24 @@
     default_concurrency: "{{ num_cores|int // 2 }}"
   when: num_cores|int > 3
 
+- when:
+    - tempest_test_blacklist is defined
+  block:
+    - name: Check for test blacklist file
+      stat:
+        path: "{{ tempest_test_blacklist }}"
+      register:
+        blacklist_stat
+
+    - name: Build blacklist option
+      set_fact:
+        blacklist_option: "--blacklist-file={{ tempest_test_blacklist|quote }}"
+      when: blacklist_stat.stat.exists
+
 - name: Run Tempest
-  command: tox -e {{tox_envlist}} -- {{tempest_test_regex|quote}} --concurrency={{tempest_concurrency|default(default_concurrency)}}
+  command: tox -e {{tox_envlist}} -- {{tempest_test_regex|quote}} {{blacklist_option|default('')}} \
+            --concurrency={{tempest_concurrency|default(default_concurrency)}} \
+            --black-regex={{tempest_black_regex|quote}}
   args:
     chdir: "{{devstack_base_dir}}/tempest"
   become: true
diff --git a/setup.cfg b/setup.cfg
index 04bb29f..96ee7ea 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -47,11 +47,5 @@
 oslo.config.opts =
     tempest.config = tempest.config:list_opts
 
-[build_sphinx]
-all-files = 1
-warning-is-error = 1
-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.py b/tempest/api/compute/admin/test_aggregates.py
index 90aca81..7a3bfdf 100644
--- a/tempest/api/compute/admin/test_aggregates.py
+++ b/tempest/api/compute/admin/test_aggregates.py
@@ -25,17 +25,17 @@
 CONF = config.CONF
 
 
-class AggregatesAdminTestJSON(base.BaseV2ComputeAdminTest):
+class AggregatesAdminTestBase(base.BaseV2ComputeAdminTest):
     """Tests Aggregates API that require admin privileges"""
 
     @classmethod
     def setup_clients(cls):
-        super(AggregatesAdminTestJSON, cls).setup_clients()
+        super(AggregatesAdminTestBase, cls).setup_clients()
         cls.client = cls.os_admin.aggregates_client
 
     @classmethod
     def resource_setup(cls):
-        super(AggregatesAdminTestJSON, cls).resource_setup()
+        super(AggregatesAdminTestBase, cls).resource_setup()
         cls.aggregate_name_prefix = 'test_aggregate'
         cls.az_name_prefix = 'test_az'
 
@@ -69,6 +69,9 @@
 
         return aggregate
 
+
+class AggregatesAdminTestJSON(AggregatesAdminTestBase):
+
     @decorators.idempotent_id('0d148aa3-d54c-4317-aa8d-42040a475e20')
     def test_aggregate_create_delete(self):
         # Create and delete an aggregate.
@@ -223,8 +226,32 @@
 
         self.client.add_host(aggregate['id'], host=host)
         self.addCleanup(self.client.remove_host, aggregate['id'], host=host)
-        admin_servers_client = self.os_admin.servers_client
         server = self.create_test_server(availability_zone=az_name,
                                          wait_until='ACTIVE')
-        body = admin_servers_client.show_server(server['id'])['server']
-        self.assertEqual(host, body['OS-EXT-SRV-ATTR:host'])
+        server_host = self.get_host_for_server(server['id'])
+        self.assertEqual(host, server_host)
+
+
+class AggregatesAdminTestV241(AggregatesAdminTestBase):
+    min_microversion = '2.41'
+
+    # NOTE(gmann): This test tests the Aggregate APIs response schema
+    # for 2.41 microversion. No specific assert or behaviour verification
+    # is needed.
+
+    @decorators.idempotent_id('fdf24d9e-8afa-4700-b6aa-9c498351504f')
+    def test_create_update_show_aggregate_add_remove_host(self):
+        # Update and add a host to the given aggregate and get details.
+        self.useFixture(fixtures.LockFixture('availability_zone'))
+        # Checking create aggregate API response schema
+        aggregate = self._create_test_aggregate()
+
+        new_aggregate_name = data_utils.rand_name(self.aggregate_name_prefix)
+        # Checking update aggregate API response schema
+        self.client.update_aggregate(aggregate['id'], name=new_aggregate_name)
+        # Checking show aggregate API response schema
+        self.client.show_aggregate(aggregate['id'])['aggregate']
+        # Checking add host to aggregate API response schema
+        self.client.add_host(aggregate['id'], host=self.host)
+        # Checking rempve host from aggregate API response schema
+        self.client.remove_host(aggregate['id'], host=self.host)
diff --git a/tempest/api/compute/admin/test_aggregates_negative.py b/tempest/api/compute/admin/test_aggregates_negative.py
index 36ff09e..a6e0efa 100644
--- a/tempest/api/compute/admin/test_aggregates_negative.py
+++ b/tempest/api/compute/admin/test_aggregates_negative.py
@@ -27,16 +27,17 @@
     def setup_clients(cls):
         super(AggregatesAdminNegativeTestJSON, cls).setup_clients()
         cls.client = cls.os_admin.aggregates_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'
 
-        hosts_all = cls.os_admin.hosts_client.list_hosts()['hosts']
-        hosts = ([host['host_name']
-                 for host in hosts_all if host['service'] == 'compute'])
-        cls.host = hosts[0]
+        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):
         aggregate_name = data_utils.rand_name(self.aggregate_name_prefix)
@@ -123,11 +124,9 @@
     @decorators.idempotent_id('0ef07828-12b4-45ba-87cc-41425faf5711')
     def test_aggregate_add_non_exist_host(self):
         # Adding a non-exist host to an aggregate should raise exceptions.
-        hosts_all = self.os_admin.hosts_client.list_hosts()['hosts']
-        hosts = map(lambda x: x['host_name'], hosts_all)
         while True:
             non_exist_host = data_utils.rand_name('nonexist_host')
-            if non_exist_host not in hosts:
+            if non_exist_host not in self.hosts:
                 break
         aggregate = self._create_test_aggregate()
         self.assertRaises(lib_exc.NotFound, self.client.add_host,
@@ -140,7 +139,7 @@
         aggregate = self._create_test_aggregate()
         self.assertRaises(lib_exc.Forbidden,
                           self.aggregates_client.add_host,
-                          aggregate['id'], host=self.host)
+                          aggregate['id'], host=self.hosts[0])
 
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('19dd44e1-c435-4ee1-a402-88c4f90b5950')
@@ -148,12 +147,12 @@
         self.useFixture(fixtures.LockFixture('availability_zone'))
         aggregate = self._create_test_aggregate()
 
-        self.client.add_host(aggregate['id'], host=self.host)
+        self.client.add_host(aggregate['id'], host=self.hosts[0])
         self.addCleanup(self.client.remove_host, aggregate['id'],
-                        host=self.host)
+                        host=self.hosts[0])
 
         self.assertRaises(lib_exc.Conflict, self.client.add_host,
-                          aggregate['id'], host=self.host)
+                          aggregate['id'], host=self.hosts[0])
 
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('7a53af20-137a-4e44-a4ae-e19260e626d9')
@@ -162,13 +161,13 @@
         self.useFixture(fixtures.LockFixture('availability_zone'))
         aggregate = self._create_test_aggregate()
 
-        self.client.add_host(aggregate['id'], host=self.host)
+        self.client.add_host(aggregate['id'], host=self.hosts[0])
         self.addCleanup(self.client.remove_host, aggregate['id'],
-                        host=self.host)
+                        host=self.hosts[0])
 
         self.assertRaises(lib_exc.Forbidden,
                           self.aggregates_client.remove_host,
-                          aggregate['id'], host=self.host)
+                          aggregate['id'], host=self.hosts[0])
 
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('95d6a6fa-8da9-4426-84d0-eec0329f2e4d')
diff --git a/tempest/api/compute/admin/test_auto_allocate_network.py b/tempest/api/compute/admin/test_auto_allocate_network.py
index c4d5768..e8011a6 100644
--- a/tempest/api/compute/admin/test_auto_allocate_network.py
+++ b/tempest/api/compute/admin/test_auto_allocate_network.py
@@ -148,9 +148,7 @@
     def test_server_create_no_allocate(self):
         """Tests that no networking is allocated for the server."""
         # create the server with no networking
-        server, _ = compute.create_test_server(
-            self.os_primary, networks='none', wait_until='ACTIVE')
-        self.addCleanup(self.delete_server, server['id'])
+        server = self.create_test_server(networks='none', wait_until='ACTIVE')
         # get the server ips
         addresses = self.servers_client.list_addresses(
             server['id'])['addresses']
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..31b9217
--- /dev/null
+++ b/tempest/api/compute/admin/test_flavors_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.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)
+        # Checking update API response schema
+        self.admin_flavors_client.update_flavor(new_flavor_id,
+                                                description='new')
+        # Checking list details API response schema
+        self.flavors_client.list_flavors(detail=True)
+        # Checking list API response schema
+        self.flavors_client.list_flavors()
+
+
+class FlavorsV261TestJSON(FlavorsV255TestJSON):
+    min_microversion = '2.61'
+    max_microversion = 'latest'
+
+    # NOTE(gmann): This class tests the flavors APIs
+    # response schema for the 2.61 microversion.
diff --git a/tempest/api/compute/admin/test_floating_ips_bulk.py b/tempest/api/compute/admin/test_floating_ips_bulk.py
index ba19937..2d7e1a7 100644
--- a/tempest/api/compute/admin/test_floating_ips_bulk.py
+++ b/tempest/api/compute/admin/test_floating_ips_bulk.py
@@ -25,12 +25,16 @@
 CONF = config.CONF
 
 
+# TODO(stephenfin): Remove this test class once the nova queens branch goes
+# into extended maintenance mode.
 class FloatingIPsBulkAdminTestJSON(base.BaseV2ComputeAdminTest):
     """Tests Floating IPs Bulk APIs that require admin privileges.
 
     API documentation - http://docs.openstack.org/api/openstack-compute/2/
     content/ext-os-floating-ips-bulk.html
     """
+    max_microversion = '2.35'
+    depends_on_nova_network = True
 
     @classmethod
     def setup_clients(cls):
diff --git a/tempest/api/compute/admin/test_hosts.py b/tempest/api/compute/admin/test_hosts.py
index 00f3256..c246685 100644
--- a/tempest/api/compute/admin/test_hosts.py
+++ b/tempest/api/compute/admin/test_hosts.py
@@ -20,6 +20,8 @@
 class HostsAdminTestJSON(base.BaseV2ComputeAdminTest):
     """Tests hosts API using admin privileges."""
 
+    max_microversion = '2.42'
+
     @classmethod
     def setup_clients(cls):
         super(HostsAdminTestJSON, cls).setup_clients()
diff --git a/tempest/api/compute/admin/test_hosts_negative.py b/tempest/api/compute/admin/test_hosts_negative.py
index 5bd8104..8a91ae2 100644
--- a/tempest/api/compute/admin/test_hosts_negative.py
+++ b/tempest/api/compute/admin/test_hosts_negative.py
@@ -20,6 +20,8 @@
 class HostsAdminNegativeTestJSON(base.BaseV2ComputeAdminTest):
     """Tests hosts API using admin privileges."""
 
+    max_microversion = '2.42'
+
     @classmethod
     def setup_clients(cls):
         super(HostsAdminNegativeTestJSON, cls).setup_clients()
diff --git a/tempest/api/compute/admin/test_hypervisor.py b/tempest/api/compute/admin/test_hypervisor.py
index 404fd94..14f51e9 100644
--- a/tempest/api/compute/admin/test_hypervisor.py
+++ b/tempest/api/compute/admin/test_hypervisor.py
@@ -17,12 +17,12 @@
 from tempest.lib import decorators
 
 
-class HypervisorAdminTestJSON(base.BaseV2ComputeAdminTest):
+class HypervisorAdminTestBase(base.BaseV2ComputeAdminTest):
     """Tests Hypervisors API that require admin privileges"""
 
     @classmethod
     def setup_clients(cls):
-        super(HypervisorAdminTestJSON, cls).setup_clients()
+        super(HypervisorAdminTestBase, cls).setup_clients()
         cls.client = cls.os_admin.hypervisor_client
 
     def _list_hypervisors(self):
@@ -30,6 +30,10 @@
         hypers = self.client.list_hypervisors()['hypervisors']
         return hypers
 
+
+class HypervisorAdminTestJSON(HypervisorAdminTestBase):
+    """Tests Hypervisors API that require admin privileges"""
+
     @decorators.idempotent_id('7f0ceacd-c64d-4e96-b8ee-d02943142cc5')
     def test_get_hypervisor_list(self):
         # List of hypervisor and available hypervisors hostname
@@ -53,17 +57,6 @@
         self.assertEqual(details['hypervisor_hostname'],
                          hypers[0]['hypervisor_hostname'])
 
-    @decorators.idempotent_id('e81bba3f-6215-4e39-a286-d52d2f906862')
-    def test_get_hypervisor_show_servers(self):
-        # Show instances about the specific hypervisors
-        hypers = self._list_hypervisors()
-        self.assertNotEmpty(hypers, "No hypervisors found.")
-
-        hostname = hypers[0]['hypervisor_hostname']
-        hypervisors = (self.client.list_servers_on_hypervisor(hostname)
-                       ['hypervisors'])
-        self.assertNotEmpty(hypervisors)
-
     @decorators.idempotent_id('797e4f28-b6e0-454d-a548-80cc77c00816')
     def test_get_hypervisor_stats(self):
         # Verify the stats of the all hypervisor
@@ -110,6 +103,34 @@
             has_valid_uptime,
             "None of the hypervisors had a valid uptime: %s" % hypers)
 
+
+class HypervisorAdminV228Test(HypervisorAdminTestBase):
+    min_microversion = '2.28'
+
+    @decorators.idempotent_id('d46bab64-0fbe-4eb8-9133-e6ee56188cc5')
+    def test_get_list_hypervisor_details(self):
+        # NOTE(zhufl): This test tests the hypervisor APIs response schema
+        # for 2.28 microversion. No specific assert or behaviour verification
+        # is needed.
+        hypers = self._list_hypervisors()
+        self.assertNotEmpty(hypers, "No hypervisors found.")
+        self.client.show_hypervisor(hypers[0]['id'])
+
+
+class HypervisorAdminUnderV252Test(HypervisorAdminTestBase):
+    max_microversion = '2.52'
+
+    @decorators.idempotent_id('e81bba3f-6215-4e39-a286-d52d2f906862')
+    def test_get_hypervisor_show_servers(self):
+        # Show instances about the specific hypervisors
+        hypers = self._list_hypervisors()
+        self.assertNotEmpty(hypers, "No hypervisors found.")
+
+        hostname = hypers[0]['hypervisor_hostname']
+        hypervisors = (self.client.list_servers_on_hypervisor(hostname)
+                       ['hypervisors'])
+        self.assertNotEmpty(hypervisors)
+
     @decorators.idempotent_id('d7e1805b-3b14-4a3b-b6fd-50ec6d9f361f')
     def test_search_hypervisor(self):
         hypers = self._list_hypervisors()
diff --git a/tempest/api/compute/admin/test_hypervisor_negative.py b/tempest/api/compute/admin/test_hypervisor_negative.py
index 431e823..0056376 100644
--- a/tempest/api/compute/admin/test_hypervisor_negative.py
+++ b/tempest/api/compute/admin/test_hypervisor_negative.py
@@ -19,12 +19,12 @@
 from tempest.lib import exceptions as lib_exc
 
 
-class HypervisorAdminNegativeTestJSON(base.BaseV2ComputeAdminTest):
+class HypervisorAdminNegativeTestBase(base.BaseV2ComputeAdminTest):
     """Tests Hypervisors API that require admin privileges"""
 
     @classmethod
     def setup_clients(cls):
-        super(HypervisorAdminNegativeTestJSON, cls).setup_clients()
+        super(HypervisorAdminNegativeTestBase, cls).setup_clients()
         cls.client = cls.os_admin.hypervisor_client
         cls.non_adm_client = cls.hypervisor_client
 
@@ -33,6 +33,10 @@
         hypers = self.client.list_hypervisors()['hypervisors']
         return hypers
 
+
+class HypervisorAdminNegativeTestJSON(HypervisorAdminNegativeTestBase):
+    """Tests Hypervisors API that require admin privileges"""
+
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('c136086a-0f67-4b2b-bc61-8482bd68989f')
     def test_show_nonexistent_hypervisor(self):
@@ -55,27 +59,6 @@
             hypers[0]['id'])
 
     @decorators.attr(type=['negative'])
-    @decorators.idempotent_id('2a0a3938-832e-4859-95bf-1c57c236b924')
-    def test_show_servers_with_non_admin_user(self):
-        hypers = self._list_hypervisors()
-        self.assertNotEmpty(hypers)
-
-        self.assertRaises(
-            lib_exc.Forbidden,
-            self.non_adm_client.list_servers_on_hypervisor,
-            hypers[0]['id'])
-
-    @decorators.attr(type=['negative'])
-    @decorators.idempotent_id('02463d69-0ace-4d33-a4a8-93d7883a2bba')
-    def test_show_servers_with_nonexistent_hypervisor(self):
-        nonexistent_hypervisor_id = data_utils.rand_uuid()
-
-        self.assertRaises(
-            lib_exc.NotFound,
-            self.client.list_servers_on_hypervisor,
-            nonexistent_hypervisor_id)
-
-    @decorators.attr(type=['negative'])
     @decorators.idempotent_id('e2b061bb-13f9-40d8-9d6e-d5bf17595849')
     def test_get_hypervisor_stats_with_non_admin_user(self):
         self.assertRaises(
@@ -119,13 +102,30 @@
             lib_exc.Forbidden,
             self.non_adm_client.list_hypervisors, detail=True)
 
+
+class HypervisorAdminNegativeUnderV252Test(HypervisorAdminNegativeTestBase):
+    max_microversion = '2.52'
+
     @decorators.attr(type=['negative'])
-    @decorators.idempotent_id('19a45cc1-1000-4055-b6d2-28e8b2ec4faa')
-    def test_search_nonexistent_hypervisor(self):
+    @decorators.idempotent_id('2a0a3938-832e-4859-95bf-1c57c236b924')
+    def test_show_servers_with_non_admin_user(self):
+        hypers = self._list_hypervisors()
+        self.assertNotEmpty(hypers)
+
+        self.assertRaises(
+            lib_exc.Forbidden,
+            self.non_adm_client.list_servers_on_hypervisor,
+            hypers[0]['id'])
+
+    @decorators.attr(type=['negative'])
+    @decorators.idempotent_id('02463d69-0ace-4d33-a4a8-93d7883a2bba')
+    def test_show_servers_with_nonexistent_hypervisor(self):
+        nonexistent_hypervisor_id = data_utils.rand_uuid()
+
         self.assertRaises(
             lib_exc.NotFound,
-            self.client.search_hypervisor,
-            'nonexistent_hypervisor_name')
+            self.client.list_servers_on_hypervisor,
+            nonexistent_hypervisor_id)
 
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('5b6a6c79-5dc1-4fa5-9c58-9c8085948e74')
@@ -137,3 +137,11 @@
             lib_exc.Forbidden,
             self.non_adm_client.search_hypervisor,
             hypers[0]['hypervisor_hostname'])
+
+    @decorators.attr(type=['negative'])
+    @decorators.idempotent_id('19a45cc1-1000-4055-b6d2-28e8b2ec4faa')
+    def test_search_nonexistent_hypervisor(self):
+        self.assertRaises(
+            lib_exc.NotFound,
+            self.client.search_hypervisor,
+            'nonexistent_hypervisor_name')
diff --git a/tempest/api/compute/admin/test_live_migration.py b/tempest/api/compute/admin/test_live_migration.py
index 9e897e3..8350f7c 100644
--- a/tempest/api/compute/admin/test_live_migration.py
+++ b/tempest/api/compute/admin/test_live_migration.py
@@ -66,7 +66,8 @@
         kwargs = dict()
         block_migration = getattr(self, 'block_migration', None)
         if self.block_migration is None:
-            kwargs['disk_over_commit'] = False
+            if self.is_requested_microversion_compatible('2.24'):
+                kwargs['disk_over_commit'] = False
             block_migration = (CONF.compute_feature_enabled.
                                block_migration_for_live_migration and
                                not volume_backed)
@@ -147,6 +148,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']
@@ -158,14 +160,11 @@
         self.attach_volume(server, volume, device='/dev/xvdb')
         server = self.admin_servers_client.show_server(server_id)['server']
         volume_id1 = server["os-extended-volumes:volumes_attached"][0]["id"]
-        self._migrate_server_to(server_id, target_host)
-        waiters.wait_for_server_status(self.servers_client,
-                                       server_id, 'ACTIVE')
+        self._live_migrate(server_id, target_host, 'ACTIVE')
 
         server = self.admin_servers_client.show_server(server_id)['server']
         volume_id2 = server["os-extended-volumes:volumes_attached"][0]["id"]
 
-        self.assertEqual(target_host, self.get_host_for_server(server_id))
         self.assertEqual(volume_id1, volume_id2)
 
 
@@ -204,10 +203,7 @@
         self._verify_console_interaction(server01_id)
         self._verify_console_interaction(server02_id)
 
-        self._migrate_server_to(server01_id, host02_id)
-        waiters.wait_for_server_status(self.servers_client,
-                                       server01_id, 'ACTIVE')
-        self.assertEqual(host02_id, self.get_host_for_server(server01_id))
+        self._live_migrate(server01_id, host02_id, 'ACTIVE')
         self._verify_console_interaction(server01_id)
         # At this point, both instances have a valid serial console
         # connection, which means the ports got updated.
diff --git a/tempest/api/compute/admin/test_live_migration_negative.py b/tempest/api/compute/admin/test_live_migration_negative.py
index deabbc2..8327a3b 100644
--- a/tempest/api/compute/admin/test_live_migration_negative.py
+++ b/tempest/api/compute/admin/test_live_migration_negative.py
@@ -32,9 +32,10 @@
 
     def _migrate_server_to(self, server_id, dest_host):
         bmflm = CONF.compute_feature_enabled.block_migration_for_live_migration
-        self.admin_servers_client.live_migrate_server(
-            server_id, host=dest_host, block_migration=bmflm,
-            disk_over_commit=False)
+        kwargs = dict(host=dest_host, block_migration=bmflm)
+        if self.is_requested_microversion_compatible('2.24'):
+            kwargs['disk_over_commit'] = False
+        self.admin_servers_client.live_migrate_server(server_id, **kwargs)
 
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('7fb7856e-ae92-44c9-861a-af62d7830bcb')
diff --git a/tempest/api/compute/admin/test_migrations.py b/tempest/api/compute/admin/test_migrations.py
index a6b71b2..83f2e61 100644
--- a/tempest/api/compute/admin/test_migrations.py
+++ b/tempest/api/compute/admin/test_migrations.py
@@ -106,7 +106,7 @@
                                        'ACTIVE')
 
         server = self.servers_client.show_server(server['id'])['server']
-        self.assertEqual(flavor['id'], server['flavor']['id'])
+        self.assert_flavor_equal(flavor['id'], server['flavor'])
 
     def _test_cold_migrate_server(self, revert=False):
         if CONF.compute.min_compute_nodes < 2:
@@ -114,8 +114,7 @@
             raise self.skipException(msg)
 
         server = self.create_test_server(wait_until="ACTIVE")
-        src_host = self.admin_servers_client.show_server(
-            server['id'])['server']['OS-EXT-SRV-ATTR:host']
+        src_host = self.get_host_for_server(server['id'])
 
         self.admin_servers_client.migrate_server(server['id'])
 
@@ -131,8 +130,7 @@
 
         waiters.wait_for_server_status(self.servers_client,
                                        server['id'], 'ACTIVE')
-        dst_host = self.admin_servers_client.show_server(
-            server['id'])['server']['OS-EXT-SRV-ATTR:host']
+        dst_host = self.get_host_for_server(server['id'])
         assert_func(src_host, dst_host)
 
     @decorators.idempotent_id('4bf0be52-3b6f-4746-9a27-3143636fe30d')
diff --git a/tempest/api/compute/admin/test_networks.py b/tempest/api/compute/admin/test_networks.py
index 87ce39d..99907a8 100644
--- a/tempest/api/compute/admin/test_networks.py
+++ b/tempest/api/compute/admin/test_networks.py
@@ -26,6 +26,7 @@
     API docs:
     https://developer.openstack.org/api-ref/compute/#networks-os-networks-deprecated
     """
+    max_microversion = '2.35'
 
     @classmethod
     def setup_clients(cls):
diff --git a/tempest/api/compute/admin/test_quotas.py b/tempest/api/compute/admin/test_quotas.py
index c2bdf7e..12c7255 100644
--- a/tempest/api/compute/admin/test_quotas.py
+++ b/tempest/api/compute/admin/test_quotas.py
@@ -25,72 +25,20 @@
 LOG = logging.getLogger(__name__)
 
 
-class QuotasAdminTestJSON(base.BaseV2ComputeAdminTest):
+class QuotasAdminTestBase(base.BaseV2ComputeAdminTest):
     force_tenant_isolation = True
 
     def setUp(self):
         # NOTE(mriedem): Avoid conflicts with os-quota-class-sets tests.
         self.useFixture(fixtures.LockFixture('compute_quotas'))
-        super(QuotasAdminTestJSON, self).setUp()
+        super(QuotasAdminTestBase, self).setUp()
 
     @classmethod
     def setup_clients(cls):
-        super(QuotasAdminTestJSON, cls).setup_clients()
+        super(QuotasAdminTestBase, cls).setup_clients()
         cls.adm_client = cls.os_admin.quotas_client
 
-    @classmethod
-    def resource_setup(cls):
-        super(QuotasAdminTestJSON, cls).resource_setup()
-
-        # NOTE(afazekas): these test cases should always create and use a new
-        # tenant most of them should be skipped if we can't do that
-        cls.demo_tenant_id = cls.quotas_client.tenant_id
-
-        cls.default_quota_set = set(('injected_file_content_bytes',
-                                     'metadata_items', 'injected_files',
-                                     'ram', 'floating_ips',
-                                     'fixed_ips', 'key_pairs',
-                                     'injected_file_path_bytes',
-                                     'instances', 'security_group_rules',
-                                     'cores', 'security_groups'))
-
-    @decorators.idempotent_id('3b0a7c8f-cf58-46b8-a60c-715a32a8ba7d')
-    def test_get_default_quotas(self):
-        # Admin can get the default resource quota set for a tenant
-        expected_quota_set = self.default_quota_set | set(['id'])
-        quota_set = self.adm_client.show_default_quota_set(
-            self.demo_tenant_id)['quota_set']
-        self.assertEqual(quota_set['id'], self.demo_tenant_id)
-        for quota in expected_quota_set:
-            self.assertIn(quota, quota_set.keys())
-
-    @decorators.idempotent_id('55fbe2bf-21a9-435b-bbd2-4162b0ed799a')
-    def test_update_all_quota_resources_for_tenant(self):
-        # Admin can update all the resource quota limits for a tenant
-        default_quota_set = self.adm_client.show_default_quota_set(
-            self.demo_tenant_id)['quota_set']
-        new_quota_set = {'injected_file_content_bytes': 20480,
-                         'metadata_items': 256, 'injected_files': 10,
-                         'ram': 10240, 'floating_ips': 20, 'fixed_ips': 10,
-                         'key_pairs': 200, 'injected_file_path_bytes': 512,
-                         'instances': 20, 'security_group_rules': 20,
-                         'cores': 2, 'security_groups': 20,
-                         'server_groups': 20, 'server_group_members': 20}
-        # Update limits for all quota resources
-        quota_set = self.adm_client.update_quota_set(
-            self.demo_tenant_id,
-            force=True,
-            **new_quota_set)['quota_set']
-
-        default_quota_set.pop('id')
-        self.addCleanup(self.adm_client.update_quota_set,
-                        self.demo_tenant_id, **default_quota_set)
-        for quota in new_quota_set:
-            self.assertIn(quota, quota_set.keys())
-
-    # TODO(afazekas): merge these test cases
-    @decorators.idempotent_id('ce9e0815-8091-4abd-8345-7fe5b85faa1d')
-    def test_get_updated_quotas(self):
+    def _get_updated_quotas(self):
         # Verify that GET shows the updated quota set of project
         project_name = data_utils.rand_name('cpu_quota_project')
         project_desc = project_name + '-desc'
@@ -101,8 +49,11 @@
                         project_id)
 
         self.adm_client.update_quota_set(project_id, ram='5120')
-        quota_set = self.adm_client.show_quota_set(project_id)['quota_set']
-        self.assertEqual(5120, quota_set['ram'])
+        # Call show_quota_set with detail=true to cover the
+        # get_quota_set_details response schema for microversion tests
+        quota_set = self.adm_client.show_quota_set(
+            project_id, detail=True)['quota_set']
+        self.assertEqual(5120, quota_set['ram']['limit'])
 
         # Verify that GET shows the updated quota set of user
         user_name = data_utils.rand_name('cpu_quota_user')
@@ -122,6 +73,75 @@
             project_id, user_id=user_id)['quota_set']
         self.assertEqual(2048, quota_set['ram'])
 
+    @classmethod
+    def resource_setup(cls):
+        super(QuotasAdminTestBase, cls).resource_setup()
+
+        # NOTE(afazekas): these test cases should always create and use a new
+        # tenant most of them should be skipped if we can't do that
+        cls.demo_tenant_id = cls.quotas_client.tenant_id
+
+        cls.default_quota_set = set(('metadata_items', 'ram', 'key_pairs',
+                                     'instances', 'cores',
+                                     'server_group_members', 'server_groups'))
+        if cls.is_requested_microversion_compatible('2.35'):
+            cls.default_quota_set = \
+                cls.default_quota_set | set(['fixed_ips', 'floating_ips',
+                                             'security_group_rules',
+                                             'security_groups'])
+        if cls.is_requested_microversion_compatible('2.56'):
+            cls.default_quota_set = \
+                cls.default_quota_set | set(['injected_file_content_bytes',
+                                             'injected_file_path_bytes',
+                                             'injected_files'])
+
+
+class QuotasAdminTestJSON(QuotasAdminTestBase):
+    @decorators.idempotent_id('3b0a7c8f-cf58-46b8-a60c-715a32a8ba7d')
+    def test_get_default_quotas(self):
+        # Admin can get the default resource quota set for a tenant
+        expected_quota_set = self.default_quota_set | set(['id'])
+        quota_set = self.adm_client.show_default_quota_set(
+            self.demo_tenant_id)['quota_set']
+        self.assertEqual(quota_set['id'], self.demo_tenant_id)
+        for quota in expected_quota_set:
+            self.assertIn(quota, quota_set.keys())
+
+    @decorators.idempotent_id('55fbe2bf-21a9-435b-bbd2-4162b0ed799a')
+    def test_update_all_quota_resources_for_tenant(self):
+        # Admin can update all the resource quota limits for a tenant
+        default_quota_set = self.adm_client.show_default_quota_set(
+            self.demo_tenant_id)['quota_set']
+        new_quota_set = {'metadata_items': 256, 'ram': 10240,
+                         'key_pairs': 200, 'instances': 20,
+                         'server_groups': 20,
+                         'server_group_members': 20, 'cores': 2}
+        if self.is_requested_microversion_compatible('2.35'):
+            new_quota_set.update({'fixed_ips': 10, 'floating_ips': 20,
+                                  'security_group_rules': 20,
+                                  'security_groups': 20})
+        if self.is_requested_microversion_compatible('2.56'):
+            new_quota_set.update({'injected_file_content_bytes': 20480,
+                                  'injected_file_path_bytes': 512,
+                                  'injected_files': 10})
+
+        # Update limits for all quota resources
+        quota_set = self.adm_client.update_quota_set(
+            self.demo_tenant_id,
+            force=True,
+            **new_quota_set)['quota_set']
+
+        default_quota_set.pop('id')
+        self.addCleanup(self.adm_client.update_quota_set,
+                        self.demo_tenant_id, **default_quota_set)
+        for quota in new_quota_set:
+            self.assertIn(quota, quota_set.keys())
+
+    # TODO(afazekas): merge these test cases
+    @decorators.idempotent_id('ce9e0815-8091-4abd-8345-7fe5b85faa1d')
+    def test_get_updated_quotas(self):
+        self._get_updated_quotas()
+
     @decorators.idempotent_id('389d04f0-3a41-405f-9317-e5f86e3c44f0')
     def test_delete_quota(self):
         # Admin can delete the resource quota set for a project
@@ -144,6 +164,30 @@
         self.assertEqual(ram_default, quota_set_new['ram'])
 
 
+class QuotasAdminTestV236(QuotasAdminTestBase):
+    min_microversion = '2.36'
+    # NOTE(gmann): This test tests the Quota APIs response schema
+    # for 2.36 microversion. No specific assert or behaviour verification
+    # is needed.
+
+    @decorators.idempotent_id('4268b5c9-92e5-4adc-acf1-3a2798f3d803')
+    def test_get_updated_quotas(self):
+        # Checking Quota update, get, get details APIs response schema
+        self._get_updated_quotas()
+
+
+class QuotasAdminTestV257(QuotasAdminTestBase):
+    min_microversion = '2.57'
+    # NOTE(gmann): This test tests the Quota APIs response schema
+    # for 2.57 microversion. No specific assert or behaviour verification
+    # is needed.
+
+    @decorators.idempotent_id('e641e6c6-e86c-41a4-9e5c-9493c0ae47ad')
+    def test_get_updated_quotas(self):
+        # Checking Quota update, get, get details APIs response schema
+        self._get_updated_quotas()
+
+
 class QuotaClassesAdminTestJSON(base.BaseV2ComputeAdminTest):
     """Tests the os-quota-class-sets API to update default quotas."""
 
diff --git a/tempest/api/compute/admin/test_quotas_negative.py b/tempest/api/compute/admin/test_quotas_negative.py
index 5ef7ee4..f90ff92 100644
--- a/tempest/api/compute/admin/test_quotas_negative.py
+++ b/tempest/api/compute/admin/test_quotas_negative.py
@@ -22,12 +22,12 @@
 CONF = config.CONF
 
 
-class QuotasAdminNegativeTestJSON(base.BaseV2ComputeAdminTest):
+class QuotasAdminNegativeTestBase(base.BaseV2ComputeAdminTest):
     force_tenant_isolation = True
 
     @classmethod
     def setup_clients(cls):
-        super(QuotasAdminNegativeTestJSON, cls).setup_clients()
+        super(QuotasAdminNegativeTestBase, cls).setup_clients()
         cls.client = cls.os_primary.quotas_client
         cls.adm_client = cls.os_admin.quotas_client
         cls.sg_client = cls.security_groups_client
@@ -35,7 +35,7 @@
 
     @classmethod
     def resource_setup(cls):
-        super(QuotasAdminNegativeTestJSON, cls).resource_setup()
+        super(QuotasAdminNegativeTestBase, cls).resource_setup()
         # NOTE(afazekas): these test cases should always create and use a new
         # tenant most of them should be skipped if we can't do that
         cls.demo_tenant_id = cls.client.tenant_id
@@ -51,6 +51,9 @@
         self.addCleanup(self.adm_client.update_quota_set, self.demo_tenant_id,
                         **{quota_item: default_quota_value})
 
+
+class QuotasAdminNegativeTest(QuotasAdminNegativeTestBase):
+
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('733abfe8-166e-47bb-8363-23dbd7ff3476')
     def test_update_quota_normal_user(self):
@@ -85,6 +88,10 @@
         self.assertRaises((lib_exc.Forbidden, lib_exc.OverLimit),
                           self.create_test_server)
 
+
+class QuotasSecurityGroupAdminNegativeTest(QuotasAdminNegativeTestBase):
+    max_microversion = '2.35'
+
     @decorators.skip_because(bug="1186354",
                              condition=CONF.service_available.neutron)
     @decorators.attr(type=['negative'])
diff --git a/tempest/api/compute/admin/test_security_group_default_rules.py b/tempest/api/compute/admin/test_security_group_default_rules.py
index f2f3b57..bca6a22 100644
--- a/tempest/api/compute/admin/test_security_group_default_rules.py
+++ b/tempest/api/compute/admin/test_security_group_default_rules.py
@@ -23,6 +23,7 @@
 
 
 class SecurityGroupDefaultRulesTest(base.BaseV2ComputeAdminTest):
+    max_microversion = '2.35'
 
     @classmethod
     # TODO(GMann): Once Bug# 1311500 is fixed, these test can run
diff --git a/tempest/api/compute/admin/test_security_groups.py b/tempest/api/compute/admin/test_security_groups.py
index ff9caa3..f0178aa 100644
--- a/tempest/api/compute/admin/test_security_groups.py
+++ b/tempest/api/compute/admin/test_security_groups.py
@@ -20,6 +20,7 @@
 
 
 class SecurityGroupsTestAdminJSON(base.BaseV2ComputeAdminTest):
+    max_microversion = '2.35'
 
     @classmethod
     def setup_clients(cls):
diff --git a/tempest/api/compute/admin/test_servers.py b/tempest/api/compute/admin/test_servers.py
index 3f06c4e..170b2cc 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):
@@ -171,7 +176,7 @@
         self.assertEqual(self.s1_id, rebuilt_server['id'])
         rebuilt_image_id = rebuilt_server['image']['id']
         self.assertEqual(self.image_ref_alt, rebuilt_image_id)
-        self.assertEqual(self.flavor_ref, rebuilt_server['flavor']['id'])
+        self.assert_flavor_equal(self.flavor_ref, rebuilt_server['flavor'])
         waiters.wait_for_server_status(self.non_admin_client,
                                        rebuilt_server['id'], 'ACTIVE',
                                        raise_on_error=False)
diff --git a/tempest/api/compute/admin/test_servers_on_multinodes.py b/tempest/api/compute/admin/test_servers_on_multinodes.py
index 2e7b07b..5cd98f4 100644
--- a/tempest/api/compute/admin/test_servers_on_multinodes.py
+++ b/tempest/api/compute/admin/test_servers_on_multinodes.py
@@ -28,7 +28,7 @@
     def resource_setup(cls):
         super(ServersOnMultiNodesTest, cls).resource_setup()
         cls.server01 = cls.create_test_server(wait_until='ACTIVE')['id']
-        cls.host01 = cls._get_host(cls.server01)
+        cls.host01 = cls.get_host_for_server(cls.server01)
 
     @classmethod
     def skip_checks(cls):
@@ -38,57 +38,8 @@
             raise cls.skipException(
                 "Less than 2 compute nodes, skipping multi-nodes test.")
 
-    @classmethod
-    def _get_host(cls, server_id):
-        return cls.os_admin.servers_client.show_server(
-            server_id)['server']['OS-EXT-SRV-ATTR:host']
-
-    @decorators.idempotent_id('26a9d5df-6890-45f2-abc4-a659290cb130')
-    @testtools.skipUnless(
-        compute.is_scheduler_filter_enabled("SameHostFilter"),
-        'SameHostFilter is not available.')
-    def test_create_servers_on_same_host(self):
-        hints = {'same_host': self.server01}
-        server02 = self.create_test_server(scheduler_hints=hints,
-                                           wait_until='ACTIVE')['id']
-        host02 = self._get_host(server02)
-        self.assertEqual(self.host01, host02)
-
-    @decorators.idempotent_id('cc7ca884-6e3e-42a3-a92f-c522fcf25e8e')
-    @testtools.skipUnless(
-        compute.is_scheduler_filter_enabled("DifferentHostFilter"),
-        'DifferentHostFilter is not available.')
-    def test_create_servers_on_different_hosts(self):
-        hints = {'different_host': self.server01}
-        server02 = self.create_test_server(scheduler_hints=hints,
-                                           wait_until='ACTIVE')['id']
-        host02 = self._get_host(server02)
-        self.assertNotEqual(self.host01, host02)
-
-    @decorators.idempotent_id('7869cc84-d661-4e14-9f00-c18cdc89cf57')
-    @testtools.skipUnless(
-        compute.is_scheduler_filter_enabled("DifferentHostFilter"),
-        'DifferentHostFilter is not available.')
-    def test_create_servers_on_different_hosts_with_list_of_servers(self):
-        # This scheduler-hint supports list of servers also.
-        hints = {'different_host': [self.server01]}
-        server02 = self.create_test_server(scheduler_hints=hints,
-                                           wait_until='ACTIVE')['id']
-        host02 = self._get_host(server02)
-        self.assertNotEqual(self.host01, host02)
-
-    @decorators.idempotent_id('f8bd0867-e459-45f5-ba53-59134552fe04')
-    @testtools.skipUnless(
-        compute.is_scheduler_filter_enabled("ServerGroupAntiAffinityFilter"),
-        'ServerGroupAntiAffinityFilter is not available.')
-    def test_create_server_with_scheduler_hint_group_anti_affinity(self):
-        """Tests the ServerGroupAntiAffinityFilter
-
-        Creates two servers in an anti-affinity server group and
-        asserts the servers are in the group and on different hosts.
-        """
-        group_id = self.create_test_server_group(
-            policy=['anti-affinity'])['id']
+    def _create_servers_with_group(self, policy):
+        group_id = self.create_test_server_group(policy=[policy])['id']
         hints = {'group': group_id}
         reservation_id = self.create_test_server(
             scheduler_hints=hints, wait_until='ACTIVE', min_count=2,
@@ -105,9 +56,70 @@
         hosts = {}
         for server in servers:
             self.assertIn(server['id'], server_group['members'])
-            hosts[server['id']] = self._get_host(server['id'])
+            hosts[server['id']] = self.get_host_for_server(server['id'])
 
-        # Assert the servers are on different hosts.
-        hostnames = list(hosts.values())
+        return hosts
+
+    @decorators.idempotent_id('26a9d5df-6890-45f2-abc4-a659290cb130')
+    @testtools.skipUnless(
+        compute.is_scheduler_filter_enabled("SameHostFilter"),
+        'SameHostFilter is not available.')
+    def test_create_servers_on_same_host(self):
+        hints = {'same_host': self.server01}
+        server02 = self.create_test_server(scheduler_hints=hints,
+                                           wait_until='ACTIVE')['id']
+        host02 = self.get_host_for_server(server02)
+        self.assertEqual(self.host01, host02)
+
+    @decorators.idempotent_id('cc7ca884-6e3e-42a3-a92f-c522fcf25e8e')
+    @testtools.skipUnless(
+        compute.is_scheduler_filter_enabled("DifferentHostFilter"),
+        'DifferentHostFilter is not available.')
+    def test_create_servers_on_different_hosts(self):
+        hints = {'different_host': self.server01}
+        server02 = self.create_test_server(scheduler_hints=hints,
+                                           wait_until='ACTIVE')['id']
+        host02 = self.get_host_for_server(server02)
+        self.assertNotEqual(self.host01, host02)
+
+    @decorators.idempotent_id('7869cc84-d661-4e14-9f00-c18cdc89cf57')
+    @testtools.skipUnless(
+        compute.is_scheduler_filter_enabled("DifferentHostFilter"),
+        'DifferentHostFilter is not available.')
+    def test_create_servers_on_different_hosts_with_list_of_servers(self):
+        # This scheduler-hint supports list of servers also.
+        hints = {'different_host': [self.server01]}
+        server02 = self.create_test_server(scheduler_hints=hints,
+                                           wait_until='ACTIVE')['id']
+        host02 = self.get_host_for_server(server02)
+        self.assertNotEqual(self.host01, host02)
+
+    @decorators.idempotent_id('f8bd0867-e459-45f5-ba53-59134552fe04')
+    @testtools.skipUnless(
+        compute.is_scheduler_filter_enabled("ServerGroupAntiAffinityFilter"),
+        'ServerGroupAntiAffinityFilter is not available.')
+    def test_create_server_with_scheduler_hint_group_anti_affinity(self):
+        """Tests the ServerGroupAntiAffinityFilter
+
+        Creates two servers in an anti-affinity server group and
+        asserts the servers are in the group and on different hosts.
+        """
+        hosts = self._create_servers_with_group('anti-affinity')
+        hostnames = hosts.values()
         self.assertNotEqual(hostnames[0], hostnames[1],
                             'Servers are on the same host: %s' % hosts)
+
+    @decorators.idempotent_id('9d2e924a-baf4-11e7-b856-fa163e65f5ce')
+    @testtools.skipUnless(
+        compute.is_scheduler_filter_enabled("ServerGroupAffinityFilter"),
+        'ServerGroupAffinityFilter is not available.')
+    def test_create_server_with_scheduler_hint_group_affinity(self):
+        """Tests the ServerGroupAffinityFilter
+
+        Creates two servers in an affinity server group and
+        asserts the servers are in the group and on same host.
+        """
+        hosts = self._create_servers_with_group('affinity')
+        hostnames = hosts.values()
+        self.assertEqual(hostnames[0], hostnames[1],
+                         'Servers are on the different hosts: %s' % hosts)
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/admin/test_services_negative.py b/tempest/api/compute/admin/test_services_negative.py
index 201670a..d264829 100644
--- a/tempest/api/compute/admin/test_services_negative.py
+++ b/tempest/api/compute/admin/test_services_negative.py
@@ -13,12 +13,14 @@
 #    under the License.
 
 from tempest.api.compute import base
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
 
 
 class ServicesAdminNegativeTestJSON(base.BaseV2ComputeAdminTest):
     """Tests Services API. List and Enable/Disable require admin privileges."""
+    max_microversion = '2.52'
 
     @classmethod
     def setup_clients(cls):
@@ -35,7 +37,8 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('d0884a69-f693-4e79-a9af-232d15643bf7')
     def test_get_service_by_invalid_params(self):
-        # return all services if send the request with invalid parameter
+        # Expect all services to be returned when the request contains invalid
+        # parameters.
         services = self.client.list_services()['services']
         services_xxx = (self.client.list_services(xxx='nova-compute')
                         ['services'])
@@ -58,3 +61,43 @@
         services = self.client.list_services(host='xxx',
                                              binary=binary_name)['services']
         self.assertEmpty(services)
+
+
+class ServicesAdminNegativeV253TestJSON(ServicesAdminNegativeTestJSON):
+    min_microversion = '2.53'
+    max_microversion = 'latest'
+
+    # NOTE(felipemonteiro): This class tests the services APIs response schema
+    # for the 2.53 microversion. Schema testing is done for `list_services`
+    # tests.
+
+    @classmethod
+    def resource_setup(cls):
+        super(ServicesAdminNegativeV253TestJSON, cls).resource_setup()
+        cls.fake_service_id = data_utils.rand_uuid()
+
+    @decorators.attr(type=['negative'])
+    @decorators.idempotent_id('508671aa-c929-4479-bd10-8680d40dd0a6')
+    def test_enable_service_with_invalid_service_id(self):
+        self.assertRaises(lib_exc.NotFound,
+                          self.client.update_service,
+                          service_id=self.fake_service_id,
+                          status='enabled')
+
+    @decorators.attr(type=['negative'])
+    @decorators.idempotent_id('a9eeeade-42b3-419f-87aa-c9342aa068cf')
+    def test_disable_service_with_invalid_service_id(self):
+        self.assertRaises(lib_exc.NotFound,
+                          self.client.update_service,
+                          service_id=self.fake_service_id,
+                          status='disabled')
+
+    @decorators.attr(type=['negative'])
+    @decorators.idempotent_id('f46a9d91-1e85-4b96-8e7a-db7706fa2e9a')
+    def test_disable_log_reason_with_invalid_service_id(self):
+        # disabled_reason requires that status='disabled' be provided.
+        self.assertRaises(lib_exc.NotFound,
+                          self.client.update_service,
+                          service_id=self.fake_service_id,
+                          status='disabled',
+                          disabled_reason='maintenance')
diff --git a/tempest/api/compute/admin/test_volume_swap.py b/tempest/api/compute/admin/test_volume_swap.py
index 99bad8f..ed8cf20 100644
--- a/tempest/api/compute/admin/test_volume_swap.py
+++ b/tempest/api/compute/admin/test_volume_swap.py
@@ -126,13 +126,13 @@
         self.assertIn(volume1['id'], vol_attachments[0]['volumeId'])
 
 
-class AttachVolumeMultiAttachTest(TestVolumeSwapBase):
+class TestMultiAttachVolumeSwap(TestVolumeSwapBase):
     min_microversion = '2.60'
     max_microversion = 'latest'
 
     @classmethod
     def skip_checks(cls):
-        super(AttachVolumeMultiAttachTest, cls).skip_checks()
+        super(TestMultiAttachVolumeSwap, cls).skip_checks()
         if not CONF.compute_feature_enabled.volume_multiattach:
             raise cls.skipException('Volume multi-attach is not available.')
 
diff --git a/tempest/api/compute/base.py b/tempest/api/compute/base.py
index 9759be7..ff2f99c 100644
--- a/tempest/api/compute/base.py
+++ b/tempest/api/compute/base.py
@@ -108,6 +108,35 @@
                 raise lib_exc.InvalidConfiguration(
                     'Either api_v1 or api_v2 must be True in '
                     '[image-feature-enabled].')
+        cls._check_depends_on_nova_network()
+
+    @classmethod
+    def _check_depends_on_nova_network(cls):
+        # Since nova-network APIs were removed from Nova in the Rocky release,
+        # determine, based on the max version from the version document, if
+        # the compute API is >Queens and if so, skip tests that rely on
+        # nova-network.
+        if not getattr(cls, 'depends_on_nova_network', False):
+            return
+        versions = cls.versions_client.list_versions()['versions']
+        # Find the v2.1 version which will tell us our max version for the
+        # compute API we're testing against.
+        for version in versions:
+            if version['id'] == 'v2.1':
+                max_version = api_version_request.APIVersionRequest(
+                    version['version'])
+                break
+        else:
+            LOG.warning(
+                'Unable to determine max v2.1 compute API version: %s',
+                versions)
+            return
+
+        # The max compute API version in Queens is 2.60 so we cap
+        # at that version.
+        queens = api_version_request.APIVersionRequest('2.60')
+        if max_version > queens:
+            raise cls.skipException('nova-network is gone')
 
     @classmethod
     def resource_setup(cls):
@@ -127,6 +156,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.
 
@@ -149,19 +208,6 @@
                 raise
 
     @classmethod
-    def clear_resources(cls, resource_name, resources, resource_del_func):
-        LOG.debug('Clearing %s: %s', resource_name,
-                  ','.join(map(str, resources)))
-        for res_id in resources:
-            try:
-                test_utils.call_and_ignore_notfound_exc(
-                    resource_del_func, res_id)
-            except Exception as exc:
-                LOG.exception('Exception raised deleting %s: %s',
-                              resource_name, res_id)
-                LOG.exception(exc)
-
-    @classmethod
     def create_test_server(cls, validatable=False, volume_backed=False,
                            validation_resources=None, **kwargs):
         """Wrapper utility that returns a test server.
@@ -369,21 +415,16 @@
         except Exception:
             LOG.exception('Failed to delete server %s', server_id)
 
-    @classmethod
-    def resize_server(cls, server_id, new_flavor_id, **kwargs):
+    def resize_server(self, server_id, new_flavor_id, **kwargs):
         """resize and confirm_resize an server, waits for it to be ACTIVE."""
-        cls.servers_client.resize_server(server_id, new_flavor_id, **kwargs)
-        waiters.wait_for_server_status(cls.servers_client, server_id,
+        self.servers_client.resize_server(server_id, new_flavor_id, **kwargs)
+        waiters.wait_for_server_status(self.servers_client, server_id,
                                        'VERIFY_RESIZE')
-        cls.servers_client.confirm_resize_server(server_id)
-        waiters.wait_for_server_status(cls.servers_client, server_id, 'ACTIVE')
-        server = cls.servers_client.show_server(server_id)['server']
-        # Nova API > 2.46 no longer includes flavor.id
-        if server['flavor'].get('id'):
-            if new_flavor_id != server['flavor']['id']:
-                msg = ('Flavor id of %s is not equal to new_flavor_id.'
-                       % server_id)
-                raise lib_exc.TempestException(msg)
+        self.servers_client.confirm_resize_server(server_id)
+        waiters.wait_for_server_status(
+            self.servers_client, server_id, 'ACTIVE')
+        server = self.servers_client.show_server(server_id)['server']
+        self.assert_flavor_equal(new_flavor_id, server['flavor'])
 
     @classmethod
     def delete_volume(cls, volume_id):
@@ -471,7 +512,7 @@
             # is already detached.
             pass
 
-    def attach_volume(self, server, volume, device=None):
+    def attach_volume(self, server, volume, device=None, tag=None):
         """Attaches volume to server and waits for 'in-use' volume status.
 
         The volume will be detached when the test tears down.
@@ -480,10 +521,14 @@
         :param volume: The volume to attach.
         :param device: Optional mountpoint for the attached volume. Note that
             this is not guaranteed for all hypervisors and is not recommended.
+        :param tag: Optional device role tag to apply to the volume.
         """
         attach_kwargs = dict(volumeId=volume['id'])
         if device:
             attach_kwargs['device'] = device
+        if tag:
+            attach_kwargs['tag'] = tag
+
         attachment = self.servers_client.attach_volume(
             server['id'], **attach_kwargs)['volumeAttachment']
         # On teardown detach the volume and wait for it to be available. This
@@ -498,6 +543,27 @@
                                                 volume['id'], 'in-use')
         return attachment
 
+    def assert_flavor_equal(self, flavor_id, server_flavor):
+        """Check whether server_flavor equals to flavor.
+
+        :param flavor_id: flavor id
+        :param server_flavor: flavor info returned by show_server.
+        """
+        # Nova API > 2.46 no longer includes flavor.id, and schema check
+        # will cover whether 'id' should be in flavor
+        if server_flavor.get('id'):
+            msg = ('server flavor is not same as flavor!')
+            self.assertEqual(flavor_id, server_flavor['id'], msg)
+        else:
+            flavor = self.flavors_client.show_flavor(flavor_id)['flavor']
+            self.assertEqual(flavor['name'], server_flavor['original_name'],
+                             "original_name in server flavor is not same as "
+                             "flavor name!")
+            for key in ['ram', 'vcpus', 'disk']:
+                msg = ('attribute %s in server flavor is not same as '
+                       'flavor!' % key)
+                self.assertEqual(flavor[key], server_flavor[key], msg)
+
 
 class BaseV2ComputeAdminTest(BaseV2ComputeTest):
     """Base test case class for Compute Admin API tests."""
@@ -525,17 +591,18 @@
         self.addCleanup(client.delete_flavor, flavor['id'])
         return flavor
 
-    def get_host_for_server(self, server_id):
-        server_details = self.admin_servers_client.show_server(server_id)
+    @classmethod
+    def get_host_for_server(cls, server_id):
+        server_details = cls.admin_servers_client.show_server(server_id)
         return server_details['server']['OS-EXT-SRV-ATTR:host']
 
     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/images/test_image_metadata.py b/tempest/api/compute/images/test_image_metadata.py
index b497626..1f3af5f 100644
--- a/tempest/api/compute/images/test_image_metadata.py
+++ b/tempest/api/compute/images/test_image_metadata.py
@@ -28,6 +28,7 @@
 
 
 class ImagesMetadataTestJSON(base.BaseV2ComputeTest):
+    max_microversion = '2.38'
 
     @classmethod
     def skip_checks(cls):
diff --git a/tempest/api/compute/images/test_image_metadata_negative.py b/tempest/api/compute/images/test_image_metadata_negative.py
index 03d0789..407fb08 100644
--- a/tempest/api/compute/images/test_image_metadata_negative.py
+++ b/tempest/api/compute/images/test_image_metadata_negative.py
@@ -20,6 +20,7 @@
 
 
 class ImagesMetadataNegativeTestJSON(base.BaseV2ComputeTest):
+    max_microversion = '2.38'
 
     @classmethod
     def setup_clients(cls):
diff --git a/tempest/api/compute/images/test_images_oneserver_negative.py b/tempest/api/compute/images/test_images_oneserver_negative.py
index a2e58c9..bebc6ca 100644
--- a/tempest/api/compute/images/test_images_oneserver_negative.py
+++ b/tempest/api/compute/images/test_images_oneserver_negative.py
@@ -33,8 +33,11 @@
 
     def tearDown(self):
         """Terminate test instances created after a test is executed."""
-        self.server_check_teardown()
         super(ImagesOneServerNegativeTestJSON, self).tearDown()
+        # NOTE(zhufl): Because server_check_teardown will raise Exception
+        # which will prevent other cleanup steps from being executed, so
+        # server_check_teardown should be called after super's tearDown.
+        self.server_check_teardown()
 
     def setUp(self):
         # NOTE(afazekas): Normally we use the same server with all test cases,
diff --git a/tempest/api/compute/keypairs/test_keypairs_negative.py b/tempest/api/compute/keypairs/test_keypairs_negative.py
index f9050a8..81635ca 100644
--- a/tempest/api/compute/keypairs/test_keypairs_negative.py
+++ b/tempest/api/compute/keypairs/test_keypairs_negative.py
@@ -84,6 +84,6 @@
     @decorators.idempotent_id('45fbe5e0-acb5-49aa-837a-ff8d0719db91')
     def test_create_keypair_invalid_name(self):
         # Keypairs with name being an invalid name should not be created
-        k_name = 'key_/.\@:'
+        k_name = r'key_/.\@:'
         self.assertRaises(lib_exc.BadRequest, self.create_keypair,
                           k_name)
diff --git a/tempest/api/compute/limits/test_absolute_limits.py b/tempest/api/compute/limits/test_absolute_limits.py
index 0585fec..8c2202e 100644
--- a/tempest/api/compute/limits/test_absolute_limits.py
+++ b/tempest/api/compute/limits/test_absolute_limits.py
@@ -18,6 +18,7 @@
 
 
 class AbsoluteLimitsTestJSON(base.BaseV2ComputeTest):
+    max_microversion = '2.56'
 
     @classmethod
     def setup_clients(cls):
@@ -26,22 +27,14 @@
 
     @decorators.idempotent_id('b54c66af-6ab6-4cf0-a9e5-a0cb58d75e0b')
     def test_absLimits_get(self):
-        # To check if all limits are present in the response
-        limits = self.client.show_limits()['limits']
-        absolute_limits = limits['absolute']
-        expected_elements = ['maxImageMeta', 'maxPersonality',
-                             'maxPersonalitySize',
-                             'maxServerMeta', 'maxTotalCores',
-                             'maxTotalFloatingIps', 'maxSecurityGroups',
-                             'maxSecurityGroupRules', 'maxTotalInstances',
-                             'maxTotalKeypairs', 'maxTotalRAMSize',
-                             'maxServerGroups', 'maxServerGroupMembers',
-                             'totalCoresUsed', 'totalFloatingIpsUsed',
-                             'totalSecurityGroupsUsed', 'totalInstancesUsed',
-                             'totalRAMUsed', 'totalServerGroupsUsed']
-        # check whether all expected elements exist
-        missing_elements =\
-            [ele for ele in expected_elements if ele not in absolute_limits]
-        self.assertEmpty(missing_elements,
-                         "Failed to find element %s in absolute limits list"
-                         % ', '.join(ele for ele in missing_elements))
+        # To check if all limits are present in the response (will be checked
+        # by schema)
+        self.client.show_limits()
+
+
+class AbsoluteLimitsV257TestJSON(base.BaseV2ComputeTest):
+    min_microversion = '2.57'
+    max_microversion = 'latest'
+
+    # NOTE(felipemonteiro): This class tests the Absolute Limits APIs
+    # response schema for the 2.57 microversion.
diff --git a/tempest/api/compute/limits/test_absolute_limits_negative.py b/tempest/api/compute/limits/test_absolute_limits_negative.py
index bef4eb5..500638a 100644
--- a/tempest/api/compute/limits/test_absolute_limits_negative.py
+++ b/tempest/api/compute/limits/test_absolute_limits_negative.py
@@ -33,15 +33,15 @@
 
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('215cd465-d8ae-49c9-bf33-9c911913a5c8')
-    def test_max_image_meta_exceed_limit(self):
-        # We should not create vm with image meta over maxImageMeta limit
+    def test_max_metadata_exceed_limit(self):
+        # We should not create vm with metadata over maxServerMeta limit
         # Get max limit value
         limits = self.client.show_limits()['limits']
-        max_meta = limits['absolute']['maxImageMeta']
+        max_meta = limits['absolute']['maxServerMeta']
 
         # No point in running this test if there is no limit.
         if max_meta == -1:
-            raise self.skipException('no limit for maxImageMeta')
+            raise self.skipException('no limit for maxServerMeta')
 
         # Create server should fail, since we are passing > metadata Limit!
         max_meta_data = max_meta + 1
diff --git a/tempest/api/compute/security_groups/base.py b/tempest/api/compute/security_groups/base.py
index 54a6da8..49125d1 100644
--- a/tempest/api/compute/security_groups/base.py
+++ b/tempest/api/compute/security_groups/base.py
@@ -22,6 +22,7 @@
 
 
 class BaseSecurityGroupsTest(base.BaseV2ComputeTest):
+    max_microversion = '2.35'
 
     @classmethod
     def skip_checks(cls):
diff --git a/tempest/api/compute/servers/test_attach_interfaces.py b/tempest/api/compute/servers/test_attach_interfaces.py
index 0e8f681..0636ee4 100644
--- a/tempest/api/compute/servers/test_attach_interfaces.py
+++ b/tempest/api/compute/servers/test_attach_interfaces.py
@@ -29,11 +29,11 @@
 CONF = config.CONF
 
 
-class AttachInterfacesTestJSON(base.BaseV2ComputeTest):
+class AttachInterfacesTestBase(base.BaseV2ComputeTest):
 
     @classmethod
     def skip_checks(cls):
-        super(AttachInterfacesTestJSON, cls).skip_checks()
+        super(AttachInterfacesTestBase, cls).skip_checks()
         if not CONF.service_available.neutron:
             raise cls.skipException("Neutron is required")
         if not CONF.compute_feature_enabled.interface_attach:
@@ -43,14 +43,26 @@
     def setup_credentials(cls):
         # This test class requires network and subnet
         cls.set_network_resources(network=True, subnet=True)
-        super(AttachInterfacesTestJSON, cls).setup_credentials()
+        super(AttachInterfacesTestBase, cls).setup_credentials()
 
     @classmethod
     def setup_clients(cls):
-        super(AttachInterfacesTestJSON, cls).setup_clients()
+        super(AttachInterfacesTestBase, cls).setup_clients()
         cls.subnets_client = cls.os_primary.subnets_client
         cls.ports_client = cls.os_primary.ports_client
 
+    def _create_server_get_interfaces(self):
+        server = self.create_test_server(wait_until='ACTIVE')
+        ifs = (self.interfaces_client.list_interfaces(server['id'])
+               ['interfaceAttachments'])
+        body = waiters.wait_for_interface_status(
+            self.interfaces_client, server['id'], ifs[0]['port_id'], 'ACTIVE')
+        ifs[0]['port_state'] = body['port_state']
+        return server, ifs
+
+
+class AttachInterfacesTestJSON(AttachInterfacesTestBase):
+
     def wait_for_port_detach(self, port_id):
         """Waits for the port's device_id to be unset.
 
@@ -92,15 +104,6 @@
         if mac_addr:
             self.assertEqual(iface['mac_addr'], mac_addr)
 
-    def _create_server_get_interfaces(self):
-        server = self.create_test_server(wait_until='ACTIVE')
-        ifs = (self.interfaces_client.list_interfaces(server['id'])
-               ['interfaceAttachments'])
-        body = waiters.wait_for_interface_status(
-            self.interfaces_client, server['id'], ifs[0]['port_id'], 'ACTIVE')
-        ifs[0]['port_state'] = body['port_state']
-        return server, ifs
-
     def _test_create_interface(self, server):
         iface = (self.interfaces_client.create_interface(server['id'])
                  ['interfaceAttachment'])
@@ -185,7 +188,7 @@
 
     @decorators.idempotent_id('73fe8f02-590d-4bf1-b184-e9ca81065051')
     @utils.services('network')
-    def test_create_list_show_delete_interfaces(self):
+    def test_create_list_show_delete_interfaces_by_network_port(self):
         server, ifs = self._create_server_get_interfaces()
         interface_count = len(ifs)
         self.assertGreater(interface_count, 0)
@@ -206,6 +209,42 @@
         iface = self._test_create_interface_by_port_id(server, ifs)
         ifs.append(iface)
 
+        _ifs = (self.interfaces_client.list_interfaces(server['id'])
+                ['interfaceAttachments'])
+        self._compare_iface_list(ifs, _ifs)
+
+        self._test_show_interface(server, ifs)
+
+        _ifs = self._test_delete_interface(server, ifs)
+        self.assertEqual(len(ifs) - 1, len(_ifs))
+
+    @decorators.idempotent_id('d290c06c-f5b3-11e7-8ec8-002293781009')
+    @utils.services('network')
+    def test_create_list_show_delete_interfaces_by_fixed_ip(self):
+        # NOTE(zhufl) By default only project that is admin or network owner
+        # or project with role advsvc is authorised to create interfaces with
+        # fixed-ip, so if we don't create network for each project, do not
+        # test _test_create_interface_by_fixed_ips.
+        if not (CONF.auth.use_dynamic_credentials and
+                CONF.auth.create_isolated_networks and
+                not CONF.network.shared_physical_network):
+                raise self.skipException("Only owner network supports "
+                                         "creating interface by fixed ip.")
+
+        server, ifs = self._create_server_get_interfaces()
+        interface_count = len(ifs)
+        self.assertGreater(interface_count, 0)
+
+        try:
+            iface = self._test_create_interface(server)
+        except lib_exc.BadRequest as e:
+            msg = ('Multiple possible networks found, use a Network ID to be '
+                   'more specific.')
+            if not CONF.compute.fixed_network_name and six.text_type(e) == msg:
+                raise
+        else:
+            ifs.append(iface)
+
         iface = self._test_create_interface_by_fixed_ips(server, ifs)
         ifs.append(iface)
 
@@ -218,30 +257,6 @@
         _ifs = self._test_delete_interface(server, ifs)
         self.assertEqual(len(ifs) - 1, len(_ifs))
 
-    @decorators.attr(type='smoke')
-    @decorators.idempotent_id('c7e0e60b-ee45-43d0-abeb-8596fd42a2f9')
-    @utils.services('network')
-    def test_add_remove_fixed_ip(self):
-        # Add and Remove the fixed IP to server.
-        server, ifs = self._create_server_get_interfaces()
-        interface_count = len(ifs)
-        self.assertGreater(interface_count, 0)
-        network_id = ifs[0]['net_id']
-        self.servers_client.add_fixed_ip(server['id'], networkId=network_id)
-        # Remove the fixed IP from server.
-        server_detail = self.os_primary.servers_client.show_server(
-            server['id'])['server']
-        # Get the Fixed IP from server.
-        fixed_ip = None
-        for ip_set in server_detail['addresses']:
-            for ip in server_detail['addresses'][ip_set]:
-                if ip['OS-EXT-IPS:type'] == 'fixed':
-                    fixed_ip = ip['addr']
-                    break
-            if fixed_ip is not None:
-                break
-        self.servers_client.remove_fixed_ip(server['id'], address=fixed_ip)
-
     @decorators.idempotent_id('2f3a0127-95c7-4977-92d2-bc5aec602fb4')
     def test_reassign_port_between_servers(self):
         """Tests the following:
@@ -278,3 +293,31 @@
             # API so we have to poll the port until the device_id is unset.
             self.interfaces_client.delete_interface(server['id'], port_id)
             self.wait_for_port_detach(port_id)
+
+
+class AttachInterfacesUnderV243Test(AttachInterfacesTestBase):
+    max_microversion = '2.43'
+
+    @decorators.attr(type='smoke')
+    @decorators.idempotent_id('c7e0e60b-ee45-43d0-abeb-8596fd42a2f9')
+    @utils.services('network')
+    def test_add_remove_fixed_ip(self):
+        # Add and Remove the fixed IP to server.
+        server, ifs = self._create_server_get_interfaces()
+        interface_count = len(ifs)
+        self.assertGreater(interface_count, 0)
+        network_id = ifs[0]['net_id']
+        self.servers_client.add_fixed_ip(server['id'], networkId=network_id)
+        # Remove the fixed IP from server.
+        server_detail = self.os_primary.servers_client.show_server(
+            server['id'])['server']
+        # Get the Fixed IP from server.
+        fixed_ip = None
+        for ip_set in server_detail['addresses']:
+            for ip in server_detail['addresses'][ip_set]:
+                if ip['OS-EXT-IPS:type'] == 'fixed':
+                    fixed_ip = ip['addr']
+                    break
+            if fixed_ip is not None:
+                break
+        self.servers_client.remove_fixed_ip(server['id'], address=fixed_ip)
diff --git a/tempest/api/compute/servers/test_create_server.py b/tempest/api/compute/servers/test_create_server.py
index c660821..4f0dbad 100644
--- a/tempest/api/compute/servers/test_create_server.py
+++ b/tempest/api/compute/servers/test_create_server.py
@@ -80,7 +80,7 @@
             self.assertEqual("", self.server['image'])
         else:
             self.assertEqual(self.image_ref, self.server['image']['id'])
-        self.assertEqual(self.flavor_ref, self.server['flavor']['id'])
+        self.assert_flavor_equal(self.flavor_ref, self.server['flavor'])
         self.assertEqual(self.meta, self.server['metadata'])
 
     @decorators.attr(type='smoke')
@@ -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 d857fcb..40681cb 100644
--- a/tempest/api/compute/servers/test_device_tagging.py
+++ b/tempest/api/compute/servers/test_device_tagging.py
@@ -19,6 +19,7 @@
 from tempest.api.compute import base
 from tempest.common import utils
 from tempest.common.utils.linux import remote_client
+from tempest.common import waiters
 from tempest import config
 from tempest.lib.common.utils import data_utils
 from tempest.lib.common.utils import test_utils
@@ -31,30 +32,23 @@
 LOG = logging.getLogger(__name__)
 
 
-class DeviceTaggingTest(base.BaseV2ComputeTest):
-
-    min_microversion = '2.32'
-    # NOTE(mriedem): max_version looks odd but it's actually correct. Due to a
-    # bug in the 2.32 microversion, tags on block devices only worked with the
-    # 2.32 microversion specifically. And tags on networks only worked between
-    # 2.32 and 2.36 inclusive; the 2.37 microversion broke tags for networks.
-    max_microversion = '2.32'
+class DeviceTaggingBase(base.BaseV2ComputeTest):
 
     @classmethod
     def skip_checks(cls):
-        super(DeviceTaggingTest, cls).skip_checks()
+        super(DeviceTaggingBase, cls).skip_checks()
         if not CONF.service_available.neutron:
             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')
 
     @classmethod
     def setup_clients(cls):
-        super(DeviceTaggingTest, cls).setup_clients()
+        super(DeviceTaggingBase, cls).setup_clients()
         cls.networks_client = cls.os_primary.networks_client
         cls.ports_client = cls.os_primary.ports_client
         cls.subnets_client = cls.os_primary.subnets_client
@@ -64,7 +58,57 @@
     def setup_credentials(cls):
         cls.set_network_resources(network=True, subnet=True, router=True,
                                   dhcp=True)
-        super(DeviceTaggingTest, cls).setup_credentials()
+        super(DeviceTaggingBase, cls).setup_credentials()
+
+    def verify_metadata_from_api(self, server, ssh_client, verify_method):
+        md_url = 'http://169.254.169.254/openstack/latest/meta_data.json'
+        LOG.info('Attempting to verify tagged devices in server %s via '
+                 'the metadata service: %s', server['id'], md_url)
+
+        def get_and_verify_metadata():
+            try:
+                ssh_client.exec_command('curl -V')
+            except exceptions.SSHExecCommandFailed:
+                if not CONF.compute_feature_enabled.config_drive:
+                    raise self.skipException('curl not found in guest '
+                                             'and config drive is '
+                                             'disabled')
+                LOG.warning('curl was not found in the guest, device '
+                            'tagging metadata was not checked in the '
+                            'metadata API')
+                return True
+            cmd = 'curl %s' % md_url
+            md_json = ssh_client.exec_command(cmd)
+            return verify_method(md_json)
+        # NOTE(gmann) Keep refreshing the metadata info until the metadata
+        # cache is refreshed. For safer side, we will go with wait loop of
+        # build_interval till build_timeout. verify_method() above will return
+        # True if all metadata verification is done as expected.
+        if not test_utils.call_until_true(get_and_verify_metadata,
+                                          CONF.compute.build_timeout,
+                                          CONF.compute.build_interval):
+            raise exceptions.TimeoutException('Timeout while verifying '
+                                              'metadata on server.')
+
+    def verify_metadata_on_config_drive(self, server, ssh_client,
+                                        verify_method):
+        LOG.info('Attempting to verify tagged devices in server %s via '
+                 'the config drive.', server['id'])
+        ssh_client.mount_config_drive()
+        cmd_md = 'sudo cat /mnt/openstack/latest/meta_data.json'
+        md_json = ssh_client.exec_command(cmd_md)
+        verify_method(md_json)
+        ssh_client.unmount_config_drive()
+
+
+class TaggedBootDevicesTest(DeviceTaggingBase):
+
+    min_microversion = '2.32'
+    # NOTE(mriedem): max_version looks odd but it's actually correct. Due to a
+    # bug in the 2.32 microversion, tags on block devices only worked with the
+    # 2.32 microversion specifically. And tags on networks only worked between
+    # 2.32 and 2.36 inclusive; the 2.37 microversion broke tags for networks.
+    max_microversion = '2.32'
 
     def verify_device_metadata(self, md_json):
         md_dict = json.loads(md_json)
@@ -79,18 +123,24 @@
                 if d['mac'] == self.net_2_200_mac:
                     self.assertEqual(d['tags'], ['net-2-200'])
 
-            # A hypervisor may present multiple paths to a tagged disk, so
-            # there may be duplicated tags in the metadata, use set() to
-            # remove duplicated tags.
-            found_devices = [d['tags'][0] for d in md_dict['devices']]
+        # A hypervisor may present multiple paths to a tagged disk, so
+        # there may be duplicated tags in the metadata, use set() to
+        # remove duplicated tags.
+        # Some hypervisors might report devices with no tags as well.
+        found_devices = [d['tags'][0] for d in md_dict['devices']
+                         if d.get('tags')]
+        try:
             self.assertEqual(set(found_devices), set(['port-1', 'port-2',
                                                       'net-1', 'net-2-100',
                                                       'net-2-200', 'boot',
                                                       'other']))
+            return True
+        except Exception:
+            return False
 
     @decorators.idempotent_id('a2e65a6c-66f1-4442-aaa8-498c31778d96')
     @utils.services('network', 'volume', 'image')
-    def test_device_tagging(self):
+    def test_tagged_boot_devices(self):
         # Create volumes
         # The create_volume methods waits for the volumes to be available and
         # the base class will clean them up on tearDown.
@@ -132,7 +182,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)
@@ -142,7 +191,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
@@ -207,11 +255,10 @@
         self.addCleanup(self.delete_server, server['id'])
 
         server = self.servers_client.show_server(server['id'])['server']
-        self.ssh_client = remote_client.RemoteClient(
+        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)
 
@@ -231,58 +278,112 @@
         self.assertTrue(self.net_2_100_mac)
         self.assertTrue(self.net_2_200_mac)
 
-        # Verify metadata from metadata service
+        # Verify metadata from metadata API
         if CONF.compute_feature_enabled.metadata_service:
-            md_url = 'http://169.254.169.254/openstack/latest/meta_data.json'
-            LOG.info('Attempting to verify tagged devices in server %s via '
-                     'the metadata service: %s', server['id'], md_url)
-
-            def get_and_verify_metadata():
-                try:
-                    self.ssh_client.exec_command('curl -V')
-                except exceptions.SSHExecCommandFailed:
-                    if not CONF.compute_feature_enabled.config_drive:
-                        raise self.skipException('curl not found in guest '
-                                                 'and config drive is '
-                                                 'disabled')
-                    LOG.warning('curl was not found in the guest, device '
-                                'tagging metadata was not checked in the '
-                                'metadata API')
-                    return True
-                cmd = 'curl %s' % md_url
-                md_json = self.ssh_client.exec_command(cmd)
-                self.verify_device_metadata(md_json)
-                return True
-
-            if not test_utils.call_until_true(get_and_verify_metadata,
-                                              CONF.compute.build_timeout,
-                                              CONF.compute.build_interval):
-                raise exceptions.TimeoutException('Timeout while verifying '
-                                                  'metadata on server.')
+            self.verify_metadata_from_api(server, ssh_client,
+                                          self.verify_device_metadata)
 
         # Verify metadata on config drive
         if CONF.compute_feature_enabled.config_drive:
-            cmd_blkid = 'blkid -t LABEL=config-2 -o device'
-            LOG.info('Attempting to verify tagged devices in server %s via '
-                     'the config drive.', server['id'])
-            dev_name = self.ssh_client.exec_command(cmd_blkid)
-            dev_name = dev_name.rstrip()
-            try:
-                self.ssh_client.exec_command('sudo mount %s /mnt' % dev_name)
-            except exceptions.SSHExecCommandFailed:
-                # So the command failed, let's try to know why and print some
-                # useful information.
-                lsblk = self.ssh_client.exec_command('sudo lsblk --fs --ascii')
-                LOG.error("Mounting %s on /mnt failed. Right after the "
-                          "failure 'lsblk' in the guest reported:\n%s",
-                          dev_name, lsblk)
-                raise
-
-            cmd_md = 'sudo cat /mnt/openstack/latest/meta_data.json'
-            md_json = self.ssh_client.exec_command(cmd_md)
-            self.verify_device_metadata(md_json)
+            self.verify_metadata_on_config_drive(server, ssh_client,
+                                                 self.verify_device_metadata)
 
 
-class DeviceTaggingTestV2_42(DeviceTaggingTest):
+class TaggedBootDevicesTest_v242(TaggedBootDevicesTest):
     min_microversion = '2.42'
     max_microversion = 'latest'
+
+
+class TaggedAttachmentsTest(DeviceTaggingBase):
+
+    min_microversion = '2.49'
+    max_microversion = 'latest'
+
+    @classmethod
+    def skip_checks(cls):
+        super(TaggedAttachmentsTest, cls).skip_checks()
+        if not CONF.compute_feature_enabled.metadata_service:
+            raise cls.skipException('Metadata API must be enabled')
+
+    def verify_device_metadata(self, md_json):
+        md_dict = json.loads(md_json)
+        found_devices = [d['tags'][0] for d in md_dict['devices']
+                         if d.get('tags')]
+        try:
+            self.assertItemsEqual(found_devices, ['nic-tag', 'volume-tag'])
+            return True
+        except Exception:
+            return False
+
+    def verify_empty_devices(self, md_json):
+        md_dict = json.loads(md_json)
+        try:
+            self.assertEmpty(md_dict['devices'])
+            return True
+        except AssertionError:
+            LOG.debug("Related bug 1775947. Devices dict is not empty: %s",
+                      md_dict['devices'])
+            return False
+
+    @decorators.idempotent_id('3e41c782-2a89-4922-a9d2-9a188c4e7c7c')
+    @utils.services('network', 'volume', 'image')
+    def test_tagged_attachment(self):
+        # Create network
+        net = self.networks_client.create_network(
+            name=data_utils.rand_name(
+                'tagged-attachments-test-net'))['network']
+        self.addCleanup(self.networks_client.delete_network, net['id'])
+
+        # Create subnet
+        subnet = self.subnets_client.create_subnet(
+            network_id=net['id'],
+            cidr='10.10.10.0/24',
+            ip_version=4)['subnet']
+        self.addCleanup(self.subnets_client.delete_subnet, subnet['id'])
+
+        # Create volume
+        volume = self.create_volume()
+
+        # Boot test server
+        config_drive_enabled = CONF.compute_feature_enabled.config_drive
+        validation_resources = self.get_test_validation_resources(
+            self.os_primary)
+
+        server = self.create_test_server(
+            validatable=True,
+            validation_resources=validation_resources,
+            config_drive=config_drive_enabled,
+            name=data_utils.rand_name('device-tagging-server'),
+            networks=[{'uuid': self.get_tenant_network()['id']}])
+        self.addCleanup(self.delete_server, server['id'])
+
+        # Attach tagged nic and volume
+        interface = self.interfaces_client.create_interface(
+            server['id'], net_id=net['id'],
+            tag='nic-tag')['interfaceAttachment']
+        self.attach_volume(server, volume, tag='volume-tag')
+
+        ssh_client = remote_client.RemoteClient(
+            self.get_server_ip(server, validation_resources),
+            CONF.validation.image_ssh_user,
+            pkey=validation_resources['keypair']['private_key'],
+            server=server,
+            servers_client=self.servers_client)
+
+        self.verify_metadata_from_api(server, ssh_client,
+                                      self.verify_device_metadata)
+
+        # Detach tagged nic and volume
+        self.servers_client.detach_volume(server['id'], volume['id'])
+        waiters.wait_for_volume_resource_status(self.volumes_client,
+                                                volume['id'], 'available')
+        self.interfaces_client.delete_interface(server['id'],
+                                                interface['port_id'])
+        waiters.wait_for_interface_detach(self.interfaces_client,
+                                          server['id'],
+                                          interface['port_id'])
+        # FIXME(mriedem): The assertion that the tagged devices are removed
+        # from the metadata for the server is being skipped until bug 1775947
+        # is fixed.
+        # self.verify_metadata_from_api(server, ssh_client,
+        #                               self.verify_empty_devices)
diff --git a/tempest/api/compute/servers/test_list_server_filters.py b/tempest/api/compute/servers/test_list_server_filters.py
index 14aecfd..3dffd01 100644
--- a/tempest/api/compute/servers/test_list_server_filters.py
+++ b/tempest/api/compute/servers/test_list_server_filters.py
@@ -227,7 +227,7 @@
     @decorators.idempotent_id('24a89b0c-0d55-4a28-847f-45075f19b27b')
     def test_list_servers_filtered_by_name_regex(self):
         # list of regex that should match s1, s2 and s3
-        regexes = ['^.*\-instance\-[0-9]+$', '^.*\-instance\-.*$']
+        regexes = [r'^.*\-instance\-[0-9]+$', r'^.*\-instance\-.*$']
         for regex in regexes:
             params = {'name': regex}
             body = self.client.list_servers(**params)
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..5801db1 100644
--- a/tempest/api/compute/servers/test_novnc.py
+++ b/tempest/api/compute/servers/test_novnc.py
@@ -44,10 +44,13 @@
         self._websocket = None
 
     def tearDown(self):
-        self.server_check_teardown()
         super(NoVNCConsoleTestJSON, self).tearDown()
         if self._websocket is not None:
             self._websocket.close()
+        # NOTE(zhufl): Because server_check_teardown will raise Exception
+        # which will prevent other cleanup steps from being executed, so
+        # server_check_teardown should be called after super's tearDown.
+        self.server_check_teardown()
 
     @classmethod
     def setup_clients(cls):
@@ -58,6 +61,9 @@
     def resource_setup(cls):
         super(NoVNCConsoleTestJSON, cls).resource_setup()
         cls.server = cls.create_test_server(wait_until="ACTIVE")
+        cls.use_get_remote_console = False
+        if not cls.is_requested_microversion_compatible('2.5'):
+            cls.use_get_remote_console = True
 
     def _validate_novnc_html(self, vnc_url):
         """Verify we can connect to novnc and get back the javascript."""
@@ -151,16 +157,32 @@
         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):
-        body = self.client.get_vnc_console(self.server['id'],
-                                           type='novnc')['console']
+        if self.use_get_remote_console:
+            body = self.client.get_remote_console(
+                self.server['id'], console_type='novnc',
+                protocol='vnc')['remote_console']
+        else:
+            body = self.client.get_vnc_console(self.server['id'],
+                                               type='novnc')['console']
         self.assertEqual('novnc', body['type'])
         # Do the initial HTTP Request to novncproxy to get the NoVNC JavaScript
         self._validate_novnc_html(body['url'])
@@ -173,8 +195,13 @@
 
     @decorators.idempotent_id('f9c79937-addc-4aaa-9e0e-841eef02aeb7')
     def test_novnc_bad_token(self):
-        body = self.client.get_vnc_console(self.server['id'],
-                                           type='novnc')['console']
+        if self.use_get_remote_console:
+            body = self.client.get_remote_console(
+                self.server['id'], console_type='novnc',
+                protocol='vnc')['remote_console']
+        else:
+            body = self.client.get_vnc_console(self.server['id'],
+                                               type='novnc')['console']
         self.assertEqual('novnc', body['type'])
         # Do the WebSockify HTTP Request to novncproxy with a bad token
         url = body['url'].replace('token=', 'token=bad')
diff --git a/tempest/api/compute/servers/test_server_actions.py b/tempest/api/compute/servers/test_server_actions.py
index 5c3e9f0..f6494b5 100644
--- a/tempest/api/compute/servers/test_server_actions.py
+++ b/tempest/api/compute/servers/test_server_actions.py
@@ -59,8 +59,11 @@
                 self.server_id, validatable=True)
 
     def tearDown(self):
-        self.server_check_teardown()
         super(ServerActionsTestJSON, self).tearDown()
+        # NOTE(zhufl): Because server_check_teardown will raise Exception
+        # which will prevent other cleanup steps from being executed, so
+        # server_check_teardown should be called after super's tearDown.
+        self.server_check_teardown()
 
     @classmethod
     def setup_credentials(cls):
@@ -197,7 +200,7 @@
         self.assertEqual(self.server_id, rebuilt_server['id'])
         rebuilt_image_id = rebuilt_server['image']['id']
         self.assertTrue(self.image_ref_alt.endswith(rebuilt_image_id))
-        self.assertEqual(self.flavor_ref, rebuilt_server['flavor']['id'])
+        self.assert_flavor_equal(self.flavor_ref, rebuilt_server['flavor'])
 
         # Verify the server properties after the rebuild completes
         waiters.wait_for_server_status(self.client,
@@ -251,7 +254,7 @@
         self.assertEqual(self.server_id, rebuilt_server['id'])
         rebuilt_image_id = rebuilt_server['image']['id']
         self.assertEqual(new_image, rebuilt_image_id)
-        self.assertEqual(self.flavor_ref, rebuilt_server['flavor']['id'])
+        self.assert_flavor_equal(self.flavor_ref, rebuilt_server['flavor'])
 
         # Verify the server properties after the rebuild completes
         waiters.wait_for_server_status(self.client,
@@ -303,7 +306,7 @@
                                        expected_status)
 
         server = self.client.show_server(server_id)['server']
-        self.assertEqual(self.flavor_ref_alt, server['flavor']['id'])
+        self.assert_flavor_equal(self.flavor_ref_alt, server['flavor'])
 
         if stop:
             # NOTE(mriedem): tearDown requires the server to be started.
@@ -367,7 +370,43 @@
         waiters.wait_for_server_status(self.client, self.server_id, 'ACTIVE')
 
         server = self.client.show_server(self.server_id)['server']
-        self.assertEqual(self.flavor_ref, server['flavor']['id'])
+        self.assert_flavor_equal(self.flavor_ref, server['flavor'])
+
+    @decorators.idempotent_id('fbbf075f-a812-4022-bc5c-ccb8047eef12')
+    @decorators.related_bug('1737599')
+    @testtools.skipUnless(CONF.compute_feature_enabled.resize,
+                          'Resize not available.')
+    @utils.services('volume')
+    def test_resize_server_revert_with_volume_attached(self):
+        # Tests attaching a volume to a server instance and then resizing
+        # the instance. Once the instance is resized, revert the resize which
+        # should move the instance and volume attachment back to the original
+        # compute host.
+
+        # Create a blank volume and attach it to the server created in setUp.
+        volume = self.create_volume()
+        server = self.client.show_server(self.server_id)['server']
+        self.attach_volume(server, volume)
+        # Now resize the server with the blank volume attached.
+        self.client.resize_server(self.server_id, self.flavor_ref_alt)
+        # Explicitly delete the server to get a new one for later
+        # tests. Avoids resize down race issues.
+        self.addCleanup(self.delete_server, self.server_id)
+        waiters.wait_for_server_status(
+            self.client, self.server_id, 'VERIFY_RESIZE')
+        # Now revert the resize which should move the instance and it's volume
+        # attachment back to the original source compute host.
+        self.client.revert_resize_server(self.server_id)
+        waiters.wait_for_server_status(self.client, self.server_id, 'ACTIVE')
+        # Make sure everything still looks OK.
+        server = self.client.show_server(self.server_id)['server']
+        # The flavor id is not returned in the server response after
+        # microversion 2.46 so handle that gracefully.
+        if server['flavor'].get('id'):
+            self.assertEqual(self.flavor_ref, server['flavor']['id'])
+        attached_volumes = server['os-extended-volumes:volumes_attached']
+        self.assertEqual(1, len(attached_volumes))
+        self.assertEqual(volume['id'], attached_volumes[0]['id'])
 
     @decorators.idempotent_id('b963d4f1-94b3-4c40-9e97-7b583f46e470')
     @testtools.skipUnless(CONF.compute_feature_enabled.snapshot,
@@ -393,7 +432,7 @@
         resp = self.client.create_backup(self.server_id,
                                          backup_type='daily',
                                          rotation=2,
-                                         name=backup1).response
+                                         name=backup1)
         oldest_backup_exist = True
 
         # the oldest one should be deleted automatically in this test
@@ -409,10 +448,10 @@
                                 "deleted during rotation.", oldest_backup)
 
         if api_version_utils.compare_version_header_to_response(
-                "OpenStack-API-Version", "compute 2.45", resp, "lt"):
+                "OpenStack-API-Version", "compute 2.45", resp.response, "lt"):
             image1_id = resp['image_id']
         else:
-            image1_id = data_utils.parse_image_id(resp['location'])
+            image1_id = data_utils.parse_image_id(resp.response['location'])
         self.addCleanup(_clean_oldest_backup, image1_id)
         waiters.wait_for_image_status(glance_client,
                                       image1_id, 'active')
@@ -422,12 +461,12 @@
         resp = self.client.create_backup(self.server_id,
                                          backup_type='daily',
                                          rotation=2,
-                                         name=backup2).response
+                                         name=backup2)
         if api_version_utils.compare_version_header_to_response(
-                "OpenStack-API-Version", "compute 2.45", resp, "lt"):
+                "OpenStack-API-Version", "compute 2.45", resp.response, "lt"):
             image2_id = resp['image_id']
         else:
-            image2_id = data_utils.parse_image_id(resp['location'])
+            image2_id = data_utils.parse_image_id(resp.response['location'])
         self.addCleanup(glance_client.delete_image, image2_id)
         waiters.wait_for_image_status(glance_client,
                                       image2_id, 'active')
@@ -465,12 +504,12 @@
         resp = self.client.create_backup(self.server_id,
                                          backup_type='daily',
                                          rotation=2,
-                                         name=backup3).response
+                                         name=backup3)
         if api_version_utils.compare_version_header_to_response(
-                "OpenStack-API-Version", "compute 2.45", resp, "lt"):
+                "OpenStack-API-Version", "compute 2.45", resp.response, "lt"):
             image3_id = resp['image_id']
         else:
-            image3_id = data_utils.parse_image_id(resp['location'])
+            image3_id = data_utils.parse_image_id(resp.response['location'])
         self.addCleanup(glance_client.delete_image, image3_id)
         # the first back up should be deleted
         waiters.wait_for_server_status(self.client, self.server_id, 'ACTIVE')
@@ -491,10 +530,10 @@
 
     def _get_output(self):
         output = self.client.get_console_output(
-            self.server_id, length=10)['output']
+            self.server_id, length=3)['output']
         self.assertTrue(output, "Console output was empty.")
         lines = len(output.split('\n'))
-        self.assertEqual(lines, 10)
+        self.assertEqual(lines, 3)
 
     @decorators.idempotent_id('4b8867e6-fffa-4d54-b1d1-6fdda57be2f3')
     @testtools.skipUnless(CONF.compute_feature_enabled.console_output,
@@ -525,8 +564,8 @@
 
             # NOTE: This test tries to get full length console log, and the
             # length should be bigger than the one of test_get_console_output.
-            self.assertGreater(lines, 10, "Cannot get enough console log "
-                                          "length. (lines: %s)" % lines)
+            self.assertGreater(lines, 3, "Cannot get enough console log "
+                                         "length. (lines: %s)" % lines)
 
         self.wait_for(_check_full_length_console_log)
 
@@ -586,7 +625,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'
@@ -603,6 +642,20 @@
         waiters.wait_for_server_status(self.client, self.server_id, 'ACTIVE')
         glance_client.wait_for_resource_deletion(images[0]['id'])
 
+    @decorators.idempotent_id('8cf9f450-a871-42cf-9bef-77eba189c0b0')
+    @decorators.related_bug('1745529')
+    @testtools.skipUnless(CONF.compute_feature_enabled.shelve,
+                          'Shelve is not available.')
+    @testtools.skipUnless(CONF.compute_feature_enabled.pause,
+                          'Pause is not available.')
+    def test_shelve_paused_server(self):
+        server = self.create_test_server(wait_until='ACTIVE')
+        self.client.pause_server(server['id'])
+        waiters.wait_for_server_status(self.client, server['id'], 'PAUSED')
+        # Check if Shelve operation is successful on paused server.
+        compute.shelve_server(self.client, server['id'],
+                              force_shelve_offload=True)
+
     @decorators.idempotent_id('af8eafd4-38a7-4a4b-bdbc-75145a580560')
     def test_stop_start_server(self):
         self.client.stop_server(self.server_id)
@@ -640,8 +693,13 @@
         # Get the VNC console of type 'novnc' and 'xvpvnc'
         console_types = ['novnc', 'xvpvnc']
         for console_type in console_types:
-            body = self.client.get_vnc_console(self.server_id,
-                                               type=console_type)['console']
+            if self.is_requested_microversion_compatible('2.5'):
+                body = self.client.get_vnc_console(
+                    self.server_id, type=console_type)['console']
+            else:
+                body = self.client.get_remote_console(
+                    self.server_id, console_type=console_type,
+                    protocol='vnc')['remote_console']
             self.assertEqual(console_type, body['type'])
             self.assertNotEqual('', body['url'])
             self._validate_url(body['url'])
diff --git a/tempest/api/compute/servers/test_server_group.py b/tempest/api/compute/servers/test_server_group.py
index 5286c8f..1b7cb96 100644
--- a/tempest/api/compute/servers/test_server_group.py
+++ b/tempest/api/compute/servers/test_server_group.py
@@ -47,8 +47,16 @@
         super(ServerGroupTestJSON, cls).resource_setup()
         cls.policy = ['affinity']
 
-        cls.created_server_group = cls.create_test_server_group(
-            policy=cls.policy)
+    def setUp(self):
+        super(ServerGroupTestJSON, self).setUp()
+        # TODO(zhufl): After microversion 2.13 project_id and user_id are
+        # added to the body of server_group, and microversion is not used
+        # in resource_setup for now, so we should create server group in setUp
+        # in order to use the same microversion as in testcases till
+        # microversion support in resource_setup is fulfilled.
+        if not hasattr(self, 'created_server_group'):
+            self.__class__.created_server_group = \
+                self.create_test_server_group(policy=self.policy)
 
     def _create_server_group(self, name, policy):
         # create the test server-group with given policy
diff --git a/tempest/api/compute/servers/test_servers.py b/tempest/api/compute/servers/test_servers.py
index c9ee671..56d973e 100644
--- a/tempest/api/compute/servers/test_servers.py
+++ b/tempest/api/compute/servers/test_servers.py
@@ -167,13 +167,76 @@
         server = self.client.show_server(server['id'])['server']
         self.assertEqual('2001:2001::3', server['accessIPv6'])
 
+    @decorators.related_bug('1730756')
+    @decorators.idempotent_id('defbaca5-d611-49f5-ae21-56ee25d2db49')
+    def test_create_server_specify_multibyte_character_name(self):
+        # prefix character is:
+        # http://unicode.org/cldr/utility/character.jsp?a=20A1
+
+        # We use a string with 3 byte utf-8 character due to nova
+        # will return 400(Bad Request) if we attempt to send a name which has
+        # 4 byte utf-8 character.
+        utf8_name = data_utils.rand_name(b'\xe2\x82\xa1'.decode('utf-8'))
+        self.create_test_server(name=utf8_name, wait_until='ACTIVE')
+
 
 class ServerShowV247Test(base.BaseV2ComputeTest):
     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)
+
+
+class ServerShowV263Test(base.BaseV2ComputeTest):
+    min_microversion = '2.63'
+    max_microversion = 'latest'
+
+    @decorators.idempotent_id('71b8e3d5-11d2-494f-b917-b094a4afed3c')
+    def test_show_update_rebuild_list_server(self):
+        trusted_certs = ['test-cert-1', 'test-cert-2']
+        server = self.create_test_server(
+            trusted_image_certificates=trusted_certs,
+            wait_until='ACTIVE')
+
+        # Check show API response schema
+        self.servers_client.show_server(server['id'])['server']
+
+        # Check update API response schema
+        self.servers_client.update_server(server['id'])
+        waiters.wait_for_server_status(self.servers_client,
+                                       server['id'], 'ACTIVE')
+
+        # Check 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')
+
+        # Check list details API response schema
+        params = {'trusted_image_certificates': trusted_certs}
+        servers = self.servers_client.list_servers(
+            detail=True, **params)['servers']
+        self.assertNotEmpty(servers)
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..2434884
--- /dev/null
+++ b/tempest/api/compute/servers/test_servers_microversions.py
@@ -0,0 +1,66 @@
+# 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')
+
+
+class ServerShowV257Test(base.BaseV2ComputeTest):
+    min_microversion = '2.57'
+    max_microversion = 'latest'
+
+    @decorators.idempotent_id('803df848-080a-4261-8f11-b020cd9b6f60')
+    def test_rebuild_server(self):
+        server = self.create_test_server(wait_until='ACTIVE')
+        user_data = "ZWNobyAiaGVsbG8gd29ybGQi"
+        # Checking rebuild API response schema
+        self.servers_client.rebuild_server(server['id'], self.image_ref_alt,
+                                           user_data=user_data)
+        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..0c1c05c 100644
--- a/tempest/api/compute/servers/test_servers_negative.py
+++ b/tempest/api/compute/servers/test_servers_negative.py
@@ -40,8 +40,11 @@
             self.__class__.server_id = self.recreate_server(self.server_id)
 
     def tearDown(self):
-        self.server_check_teardown()
         super(ServersNegativeTestJSON, self).tearDown()
+        # NOTE(zhufl): Because server_check_teardown will raise Exception
+        # which will prevent other cleanup steps from being executed, so
+        # server_check_teardown should be called after super's tearDown.
+        self.server_check_teardown()
 
     @classmethod
     def setup_clients(cls):
@@ -481,7 +484,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_virtual_interfaces.py b/tempest/api/compute/servers/test_virtual_interfaces.py
index 90f04ff..f810ec5 100644
--- a/tempest/api/compute/servers/test_virtual_interfaces.py
+++ b/tempest/api/compute/servers/test_virtual_interfaces.py
@@ -25,7 +25,12 @@
 CONF = config.CONF
 
 
+# TODO(mriedem): Remove this test class once the nova queens branch goes into
+# extended maintenance mode.
 class VirtualInterfacesTestJSON(base.BaseV2ComputeTest):
+    max_microversion = '2.43'
+
+    depends_on_nova_network = True
 
     @classmethod
     def setup_credentials(cls):
@@ -50,8 +55,6 @@
         # for a given server_id
 
         if CONF.service_available.neutron:
-            # TODO(mriedem): After a microversion implements the API for
-            # neutron, a 400 should be a failure for nova-network and neutron.
             with testtools.ExpectedException(exceptions.BadRequest):
                 self.client.list_virtual_interfaces(self.server['id'])
         else:
diff --git a/tempest/api/compute/servers/test_virtual_interfaces_negative.py b/tempest/api/compute/servers/test_virtual_interfaces_negative.py
index c4e2400..f6e8bc9 100644
--- a/tempest/api/compute/servers/test_virtual_interfaces_negative.py
+++ b/tempest/api/compute/servers/test_virtual_interfaces_negative.py
@@ -20,7 +20,12 @@
 from tempest.lib import exceptions as lib_exc
 
 
+# TODO(mriedem): Remove this test class once the nova queens branch goes into
+# extended maintenance mode.
 class VirtualInterfacesNegativeTestJSON(base.BaseV2ComputeTest):
+    max_microversion = '2.43'
+
+    depends_on_nova_network = True
 
     @classmethod
     def setup_credentials(cls):
diff --git a/tempest/api/compute/test_networks.py b/tempest/api/compute/test_networks.py
index b8c79d7..76131e2 100644
--- a/tempest/api/compute/test_networks.py
+++ b/tempest/api/compute/test_networks.py
@@ -20,6 +20,8 @@
 
 
 class ComputeNetworksTest(base.BaseV2ComputeTest):
+    max_microversion = '2.35'
+
     @classmethod
     def skip_checks(cls):
         super(ComputeNetworksTest, cls).skip_checks()
diff --git a/tempest/api/compute/test_quotas.py b/tempest/api/compute/test_quotas.py
index 7cf90ae..a62492d 100644
--- a/tempest/api/compute/test_quotas.py
+++ b/tempest/api/compute/test_quotas.py
@@ -43,14 +43,19 @@
         super(QuotasTestJSON, cls).resource_setup()
         cls.tenant_id = cls.client.tenant_id
         cls.user_id = cls.client.user_id
-        cls.default_quota_set = set(('injected_file_content_bytes',
-                                     'metadata_items', 'injected_files',
-                                     'ram', 'floating_ips',
-                                     'fixed_ips', 'key_pairs',
-                                     'injected_file_path_bytes',
-                                     'instances', 'security_group_rules',
-                                     'cores', 'security_groups',
+        cls.default_quota_set = set(('metadata_items', 'ram', 'key_pairs',
+                                     'instances', 'cores',
                                      'server_group_members', 'server_groups'))
+        if cls.is_requested_microversion_compatible('2.35'):
+            cls.default_quota_set = \
+                cls.default_quota_set | set(['fixed_ips', 'floating_ips',
+                                             'security_group_rules',
+                                             'security_groups'])
+        if cls.is_requested_microversion_compatible('2.56'):
+            cls.default_quota_set = \
+                cls.default_quota_set | set(['injected_file_content_bytes',
+                                             'injected_file_path_bytes',
+                                             'injected_files'])
 
     @decorators.idempotent_id('f1ef0a97-dbbb-4cca-adc5-c9fbc4f76107')
     def test_get_quotas(self):
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_domains.py b/tempest/api/identity/admin/v3/test_domains.py
index ca6b03e..72b6be4 100644
--- a/tempest/api/identity/admin/v3/test_domains.py
+++ b/tempest/api/identity/admin/v3/test_domains.py
@@ -18,6 +18,7 @@
 from tempest.lib.common.utils import data_utils
 from tempest.lib.common.utils import test_utils
 from tempest.lib import decorators
+from tempest.lib import exceptions
 
 CONF = config.CONF
 
@@ -115,6 +116,22 @@
         domains_list = [d['id'] for d in body]
         self.assertNotIn(domain['id'], domains_list)
 
+    @decorators.idempotent_id('d8d318b7-d1b3-4c37-94c5-3c5ba0b121ea')
+    def test_domain_delete_cascades_content(self):
+        # Create a domain with a user and a group in it
+        domain = self.setup_test_domain()
+        user = self.create_test_user(domain_id=domain['id'])
+        group = self.setup_test_group(domain_id=domain['id'])
+        # Delete the domain
+        self.delete_domain(domain['id'])
+        # Check the domain, its users and groups are gone
+        self.assertRaises(exceptions.NotFound,
+                          self.domains_client.show_domain, domain['id'])
+        self.assertRaises(exceptions.NotFound,
+                          self.users_client.show_user, user['id'])
+        self.assertRaises(exceptions.NotFound,
+                          self.groups_client.show_group, group['id'])
+
     @decorators.idempotent_id('036df86e-bb5d-42c0-a7c2-66b9db3a6046')
     def test_create_domain_with_disabled_status(self):
         # Create domain with enabled status as false
diff --git a/tempest/api/identity/admin/v3/test_endpoints.py b/tempest/api/identity/admin/v3/test_endpoints.py
index 874aaa4..2cd8906 100644
--- a/tempest/api/identity/admin/v3/test_endpoints.py
+++ b/tempest/api/identity/admin/v3/test_endpoints.py
@@ -20,6 +20,10 @@
 
 
 class EndPointsTestJSON(base.BaseIdentityV3AdminTest):
+    # NOTE: force_tenant_isolation is true in the base class by default but
+    # overridden to false here to allow test execution for clouds using the
+    # pre-provisioned credentials provider.
+    force_tenant_isolation = False
 
     @classmethod
     def setup_clients(cls):
diff --git a/tempest/api/identity/admin/v3/test_endpoints_negative.py b/tempest/api/identity/admin/v3/test_endpoints_negative.py
index d54e222..4c3eb1c 100644
--- a/tempest/api/identity/admin/v3/test_endpoints_negative.py
+++ b/tempest/api/identity/admin/v3/test_endpoints_negative.py
@@ -1,4 +1,3 @@
-
 # Copyright 2013 IBM Corp.
 # All Rights Reserved.
 #
@@ -21,6 +20,10 @@
 
 
 class EndpointsNegativeTestJSON(base.BaseIdentityV3AdminTest):
+    # NOTE: force_tenant_isolation is true in the base class by default but
+    # overridden to false here to allow test execution for clouds using the
+    # pre-provisioned credentials provider.
+    force_tenant_isolation = False
 
     @classmethod
     def setup_clients(cls):
diff --git a/tempest/api/identity/admin/v3/test_groups.py b/tempest/api/identity/admin/v3/test_groups.py
index 507810b..37ce266 100644
--- a/tempest/api/identity/admin/v3/test_groups.py
+++ b/tempest/api/identity/admin/v3/test_groups.py
@@ -30,50 +30,46 @@
 
     @decorators.idempotent_id('2e80343b-6c81-4ac3-88c7-452f3e9d5129')
     def test_group_create_update_get(self):
+        # Verify group creation works.
         name = data_utils.rand_name('Group')
         description = data_utils.rand_name('Description')
-        group = self.groups_client.create_group(
-            name=name, domain_id=self.domain['id'],
-            description=description)['group']
-        self.addCleanup(self.groups_client.delete_group, group['id'])
+        group = self.setup_test_group(name=name, domain_id=self.domain['id'],
+                                      description=description)
         self.assertEqual(group['name'], name)
         self.assertEqual(group['description'], description)
+        self.assertEqual(self.domain['id'], group['domain_id'])
 
-        new_name = data_utils.rand_name('UpdateGroup')
-        new_desc = data_utils.rand_name('UpdateDescription')
+        # Verify updating name and description works.
+        first_name_update = data_utils.rand_name('UpdateGroup')
+        first_desc_update = data_utils.rand_name('UpdateDescription')
         updated_group = self.groups_client.update_group(
-            group['id'], name=new_name, description=new_desc)['group']
-        self.assertEqual(updated_group['name'], new_name)
-        self.assertEqual(updated_group['description'], new_desc)
+            group['id'], name=first_name_update,
+            description=first_desc_update)['group']
+        self.assertEqual(updated_group['name'], first_name_update)
+        self.assertEqual(updated_group['description'], first_desc_update)
 
+        # Verify that the updated values are reflected after performing show.
         new_group = self.groups_client.show_group(group['id'])['group']
         self.assertEqual(group['id'], new_group['id'])
-        self.assertEqual(new_name, new_group['name'])
-        self.assertEqual(new_desc, new_group['description'])
+        self.assertEqual(first_name_update, new_group['name'])
+        self.assertEqual(first_desc_update, new_group['description'])
 
-    @decorators.idempotent_id('b66eb441-b08a-4a6d-81ab-fef71baeb26c')
-    def test_group_update_with_few_fields(self):
-        name = data_utils.rand_name('Group')
-        old_description = data_utils.rand_name('Description')
-        group = self.groups_client.create_group(
-            name=name, domain_id=self.domain['id'],
-            description=old_description)['group']
-        self.addCleanup(self.groups_client.delete_group, group['id'])
-
-        new_name = data_utils.rand_name('UpdateGroup')
+        # Verify that updating a single field for a group (name) leaves the
+        # other fields (description, domain_id) unchanged.
+        second_name_update = data_utils.rand_name(
+            self.__class__.__name__ + 'UpdateGroup')
         updated_group = self.groups_client.update_group(
-            group['id'], name=new_name)['group']
-        self.assertEqual(new_name, updated_group['name'])
-        # Verify that 'description' is not being updated or deleted.
-        self.assertEqual(old_description, updated_group['description'])
+            group['id'], name=second_name_update)['group']
+        self.assertEqual(second_name_update, updated_group['name'])
+        # Verify that 'description' and 'domain_id' were not updated or
+        # deleted.
+        self.assertEqual(first_desc_update, updated_group['description'])
+        self.assertEqual(self.domain['id'], updated_group['domain_id'])
 
     @decorators.attr(type='smoke')
     @decorators.idempotent_id('1598521a-2f36-4606-8df9-30772bd51339')
     def test_group_users_add_list_delete(self):
-        name = data_utils.rand_name('Group')
-        group = self.groups_client.create_group(
-            name=name, domain_id=self.domain['id'])['group']
-        self.addCleanup(self.groups_client.delete_group, group['id'])
+        group = self.setup_test_group(domain_id=self.domain['id'])
         # add user into group
         users = []
         for _ in range(3):
@@ -100,11 +96,8 @@
         # create two groups, and add user into them
         groups = []
         for _ in range(2):
-            name = data_utils.rand_name('Group')
-            group = self.groups_client.create_group(
-                name=name, domain_id=self.domain['id'])['group']
+            group = self.setup_test_group(domain_id=self.domain['id'])
             groups.append(group)
-            self.addCleanup(self.groups_client.delete_group, group['id'])
             self.groups_client.add_group_user(group['id'], user['id'])
         # list groups which user belongs to
         user_groups = self.users_client.list_user_groups(user['id'])['groups']
@@ -118,12 +111,7 @@
         group_ids = list()
         fetched_ids = list()
         for _ in range(3):
-            name = data_utils.rand_name('Group')
-            description = data_utils.rand_name('Description')
-            group = self.groups_client.create_group(
-                name=name, domain_id=self.domain['id'],
-                description=description)['group']
-            self.addCleanup(self.groups_client.delete_group, group['id'])
+            group = self.setup_test_group(domain_id=self.domain['id'])
             group_ids.append(group['id'])
         # List and Verify Groups
         # When domain specific drivers are enabled the operations
diff --git a/tempest/api/identity/admin/v3/test_list_projects.py b/tempest/api/identity/admin/v3/test_list_projects.py
index 82664e8..148b368 100644
--- a/tempest/api/identity/admin/v3/test_list_projects.py
+++ b/tempest/api/identity/admin/v3/test_list_projects.py
@@ -18,7 +18,19 @@
 from tempest.lib import decorators
 
 
-class ListProjectsTestJSON(base.BaseIdentityV3AdminTest):
+class BaseListProjectsTestJSON(base.BaseIdentityV3AdminTest):
+
+    def _list_projects_with_params(self, included, excluded, params, key):
+        # Validate that projects in ``included`` belongs to the projects
+        # returned that match ``params`` but not projects in ``excluded``
+        body = self.projects_client.list_projects(params)['projects']
+        for p in included:
+            self.assertIn(p[key], map(lambda x: x[key], body))
+        for p in excluded:
+            self.assertNotIn(p[key], map(lambda x: x[key], body))
+
+
+class ListProjectsTestJSON(BaseListProjectsTestJSON):
 
     @classmethod
     def resource_setup(cls):
@@ -61,17 +73,20 @@
     def test_list_projects_with_domains(self):
         # List projects with domain
         self._list_projects_with_params(
-            {'domain_id': self.domain['id']}, 'domain_id')
+            [self.p1], [self.p2, self.p3], {'domain_id': self.domain['id']},
+            'domain_id')
 
     @decorators.idempotent_id('0fe7a334-675a-4509-b00e-1c4b95d5dae8')
     def test_list_projects_with_enabled(self):
         # List the projects with enabled
-        self._list_projects_with_params({'enabled': False}, 'enabled')
+        self._list_projects_with_params(
+            [self.p1], [self.p2, self.p3], {'enabled': False}, 'enabled')
 
     @decorators.idempotent_id('fa178524-4e6d-4925-907c-7ab9f42c7e26')
     def test_list_projects_with_name(self):
         # List projects with name
-        self._list_projects_with_params({'name': self.p1_name}, 'name')
+        self._list_projects_with_params(
+            [self.p1], [self.p2, self.p3], {'name': self.p1_name}, 'name')
 
     @decorators.idempotent_id('6edc66f5-2941-4a17-9526-4073311c1fac')
     def test_list_projects_with_parent(self):
@@ -82,8 +97,3 @@
         self.assertNotEmpty(fetched_projects)
         for project in fetched_projects:
             self.assertEqual(self.p3['parent_id'], project['parent_id'])
-
-    def _list_projects_with_params(self, params, key):
-        body = self.projects_client.list_projects(params)['projects']
-        self.assertIn(self.p1[key], map(lambda x: x[key], body))
-        self.assertNotIn(self.p2[key], map(lambda x: x[key], body))
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/admin/v3/test_roles.py b/tempest/api/identity/admin/v3/test_roles.py
index 69cac33..62ced19 100644
--- a/tempest/api/identity/admin/v3/test_roles.py
+++ b/tempest/api/identity/admin/v3/test_roles.py
@@ -384,12 +384,23 @@
 
         rules = self.roles_client.list_all_role_inference_rules()[
             'role_inferences']
+
+        # NOTE(jaosorior): With the work related to the define-default-roles
+        # blueprint, we now have 'admin', 'member' and 'reader' by default. So
+        # we filter every other implied role to only take into account the ones
+        # relates to this test.
+        relevant_roles = (self.roles[0]['id'], self.roles[1]['id'],
+                          self.roles[2]['id'], self.role['id'])
+
+        def is_implied_role_relevant(rule):
+            return any(r for r in rule['implies'] if r['id'] in relevant_roles)
+
+        relevant_rules = filter(is_implied_role_relevant, rules)
         # Sort the rules by the number of inferences, since there should be 1
         # inference between "roles[2]" and "role" and 2 inferences for
         # "roles[0]": between "roles[1]" and "roles[2]".
-        sorted_rules = sorted(rules, key=lambda r: len(r['implies']))
+        sorted_rules = sorted(relevant_rules, key=lambda r: len(r['implies']))
 
-        # Check that 2 sets of rules are returned.
         self.assertEqual(2, len(sorted_rules))
         # Check that only 1 inference rule exists between "roles[2]" and "role"
         self.assertEqual(1, len(sorted_rules[0]['implies']))
diff --git a/tempest/api/identity/admin/v3/test_tokens.py b/tempest/api/identity/admin/v3/test_tokens.py
index 0845407..8ae43d6 100644
--- a/tempest/api/identity/admin/v3/test_tokens.py
+++ b/tempest/api/identity/admin/v3/test_tokens.py
@@ -19,7 +19,6 @@
 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
 
@@ -28,30 +27,6 @@
 
     credentials = ['primary', 'admin', 'alt']
 
-    @decorators.idempotent_id('0f9f5a5f-d5cd-4a86-8a5b-c5ded151f212')
-    def test_tokens(self):
-        # Valid user's token is authenticated
-        # Create a User
-        u_name = data_utils.rand_name('user')
-        u_desc = '%s-description' % u_name
-        u_password = data_utils.rand_password()
-        user = self.create_test_user(
-            name=u_name, description=u_desc, password=u_password)
-        # Perform Authentication
-        resp = self.token.auth(user_id=user['id'],
-                               password=u_password).response
-        subject_token = resp['x-subject-token']
-        self.client.check_token_existence(subject_token)
-        # Perform GET Token
-        token_details = self.client.show_token(subject_token)['token']
-        self.assertEqual(resp['x-subject-token'], subject_token)
-        self.assertEqual(token_details['user']['id'], user['id'])
-        self.assertEqual(token_details['user']['name'], u_name)
-        # Perform Delete Token
-        self.client.delete_token(subject_token)
-        self.assertRaises(lib_exc.NotFound, self.client.check_token_existence,
-                          subject_token)
-
     @decorators.idempotent_id('565fa210-1da1-4563-999b-f7b5b67cf112')
     def test_rescope_token(self):
         """Rescope a token.
@@ -201,10 +176,7 @@
             role_id = self.setup_test_role()['id']
 
             # Create a group.
-            group_name = data_utils.rand_name('Group')
-            group_id = self.groups_client.create_group(
-                name=group_name, domain_id=domain_id)['group']['id']
-            self.addCleanup(self.groups_client.delete_group, group_id)
+            group_id = self.setup_test_group(domain_id=domain_id)['id']
 
             # Add the alt user to the group.
             self.groups_client.add_group_user(group_id, alt_user_id)
diff --git a/tempest/api/identity/base.py b/tempest/api/identity/base.py
index 9edccbb..282343c 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,44 @@
             test_utils.call_and_ignore_notfound_exc,
             self.delete_domain, domain['id'])
         return domain
+
+    def setup_test_group(self, **kwargs):
+        """Set up a test group."""
+        if 'name' not in kwargs:
+            kwargs['name'] = data_utils.rand_name(
+                self.__class__.__name__ + '_test_project')
+        if 'description' not in kwargs:
+            kwargs['description'] = data_utils.rand_name(
+                self.__class__.__name__ + '_test_description')
+        group = self.groups_client.create_group(**kwargs)['group']
+        self.addCleanup(
+            test_utils.call_and_ignore_notfound_exc,
+            self.groups_client.delete_group, group['id'])
+        return group
+
+
+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/v2/test_users.py b/tempest/api/identity/v2/test_users.py
index 9c77fff..158dfb3 100644
--- a/tempest/api/identity/v2/test_users.py
+++ b/tempest/api/identity/v2/test_users.py
@@ -84,7 +84,7 @@
         new_pass = data_utils.rand_password()
         user_id = self.creds.user_id
 
-        # to change password back. important for allow_tenant_isolation = false
+        # to change password back. important for use_dynamic_credentials=false
         self.addCleanup(self._restore_password, user_id, old_pass, new_pass)
 
         # user updates own password
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/identity/v3/test_tokens.py b/tempest/api/identity/v3/test_tokens.py
index 4c72d82..f13aa10 100644
--- a/tempest/api/identity/v3/test_tokens.py
+++ b/tempest/api/identity/v3/test_tokens.py
@@ -91,3 +91,28 @@
             self.assertIsNotNone(subject_name, 'Expected user name in token.')
 
         self.assertEqual(resp['methods'][0], 'password')
+
+    @decorators.idempotent_id('0f9f5a5f-d5cd-4a86-8a5b-c5ded151f212')
+    def test_token_auth_creation_existence_deletion(self):
+        # Tests basic token auth functionality in a way that is compatible with
+        # pre-provisioned credentials. The default user is used for token
+        # authentication.
+
+        # Valid user's token is authenticated
+        user = self.os_primary.credentials
+        # Perform Authentication
+        resp = self.non_admin_token.auth(
+            user_id=user.user_id, password=user.password).response
+        subject_token = resp['x-subject-token']
+        self.non_admin_client.check_token_existence(subject_token)
+        # Perform GET Token
+        token_details = self.non_admin_client.show_token(
+            subject_token)['token']
+        self.assertEqual(resp['x-subject-token'], subject_token)
+        self.assertEqual(token_details['user']['id'], user.user_id)
+        self.assertEqual(token_details['user']['name'], user.username)
+        # Perform Delete Token
+        self.non_admin_client.delete_token(subject_token)
+        self.assertRaises(lib_exc.NotFound,
+                          self.non_admin_client.check_token_existence,
+                          subject_token)
diff --git a/tempest/api/identity/v3/test_users.py b/tempest/api/identity/v3/test_users.py
index 1f099df..6d6baca 100644
--- a/tempest/api/identity/v3/test_users.py
+++ b/tempest/api/identity/v3/test_users.py
@@ -82,7 +82,7 @@
         old_token = self.non_admin_client.token
         new_pass = data_utils.rand_password()
 
-        # to change password back. important for allow_tenant_isolation = false
+        # to change password back. important for use_dynamic_credentials=false
         self.addCleanup(self._restore_password, old_pass, new_pass)
 
         # user updates own password
diff --git a/tempest/api/image/base.py b/tempest/api/image/base.py
index 142e3c2..ae7b3e4 100644
--- a/tempest/api/image/base.py
+++ b/tempest/api/image/base.py
@@ -142,16 +142,17 @@
         cls.schemas_client = cls.os_primary.schemas_client
         cls.versions_client = cls.os_primary.image_versions_client
 
-    def create_namespace(cls, namespace_name=None, visibility='public',
+    def create_namespace(self, namespace_name=None, visibility='public',
                          description='Tempest', protected=False,
                          **kwargs):
         if not namespace_name:
             namespace_name = data_utils.rand_name('test-ns')
         kwargs.setdefault('display_name', namespace_name)
-        namespace = cls.namespaces_client.create_namespace(
+        namespace = self.namespaces_client.create_namespace(
             namespace=namespace_name, visibility=visibility,
             description=description, protected=protected, **kwargs)
-        cls.addCleanup(cls.namespaces_client.delete_namespace, namespace_name)
+        self.addCleanup(self.namespaces_client.delete_namespace,
+                        namespace_name)
         return namespace
 
 
@@ -183,3 +184,13 @@
                                          disk_format='raw')
         self.addCleanup(self.client.delete_image, image['id'])
         return image['id']
+
+
+class BaseV2ImageAdminTest(BaseV2ImageTest):
+
+    credentials = ['admin', 'primary']
+
+    @classmethod
+    def setup_clients(cls):
+        super(BaseV2ImageAdminTest, cls).setup_clients()
+        cls.admin_client = cls.os_admin.image_client_v2
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/tests/lib/services/volume/v2/__init__.py b/tempest/api/image/v2/admin/__init__.py
similarity index 100%
rename from tempest/tests/lib/services/volume/v2/__init__.py
rename to tempest/api/image/v2/admin/__init__.py
diff --git a/tempest/api/image/v2/admin/test_images.py b/tempest/api/image/v2/admin/test_images.py
new file mode 100644
index 0000000..dbb8c58
--- /dev/null
+++ b/tempest/api/image/v2/admin/test_images.py
@@ -0,0 +1,51 @@
+# 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.api.image import base
+from tempest.lib.common.utils import data_utils
+from tempest.lib import decorators
+
+
+class BasicOperationsImagesAdminTest(base.BaseV2ImageAdminTest):
+
+    @decorators.related_bug('1420008')
+    @decorators.idempotent_id('646a6eaa-135f-4493-a0af-12583021224e')
+    def test_create_image_owner_param(self):
+        # NOTE: Create image with owner different from tenant owner by
+        # using "owner" parameter requires an admin privileges.
+        random_id = data_utils.rand_uuid_hex()
+        image = self.admin_client.create_image(
+            container_format='bare', disk_format='raw', owner=random_id)
+        self.addCleanup(self.admin_client.delete_image, image['id'])
+        image_info = self.admin_client.show_image(image['id'])
+        self.assertEqual(random_id, image_info['owner'])
+
+    @decorators.related_bug('1420008')
+    @decorators.idempotent_id('525ba546-10ef-4aad-bba1-1858095ce553')
+    def test_update_image_owner_param(self):
+        random_id_1 = data_utils.rand_uuid_hex()
+        image = self.admin_client.create_image(
+            container_format='bare', disk_format='raw', owner=random_id_1)
+        self.addCleanup(self.admin_client.delete_image, image['id'])
+        created_image_info = self.admin_client.show_image(image['id'])
+
+        random_id_2 = data_utils.rand_uuid_hex()
+        self.admin_client.update_image(
+            image['id'], [dict(replace="/owner", value=random_id_2)])
+        updated_image_info = self.admin_client.show_image(image['id'])
+
+        self.assertEqual(random_id_2, updated_image_info['owner'])
+        self.assertNotEqual(created_image_info['owner'],
+                            updated_image_info['owner'])
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/image/v2/test_images_metadefs_namespace_tags.py b/tempest/api/image/v2/test_images_metadefs_namespace_tags.py
index 69bebfe..482e808 100644
--- a/tempest/api/image/v2/test_images_metadefs_namespace_tags.py
+++ b/tempest/api/image/v2/test_images_metadefs_namespace_tags.py
@@ -49,7 +49,7 @@
         # List namespace tags
         body = self.namespace_tags_client.list_namespace_tags(
             namespace['namespace'])
-        self.assertTrue(3, len(body['tags']))
+        self.assertEqual(3, len(body['tags']))
         self.assertIn(body['tags'][0]['name'], self.tag_list)
         self.assertIn(body['tags'][1]['name'], self.tag_list)
         self.assertIn(body['tags'][2]['name'], self.tag_list)
diff --git a/tempest/api/network/admin/test_dhcp_agent_scheduler.py b/tempest/api/network/admin/test_dhcp_agent_scheduler.py
index 8315c5d..4631ea9 100644
--- a/tempest/api/network/admin/test_dhcp_agent_scheduler.py
+++ b/tempest/api/network/admin/test_dhcp_agent_scheduler.py
@@ -63,7 +63,6 @@
     def test_add_remove_network_from_dhcp_agent(self):
         # The agent is now bound to the network, we can free the port
         self.ports_client.delete_port(self.port['id'])
-        self.ports.remove(self.port)
         agent = dict()
         agent['agent_type'] = None
         body = self.admin_agents_client.list_agents()
diff --git a/tempest/api/network/admin/test_external_network_extension.py b/tempest/api/network/admin/test_external_network_extension.py
index 49a9cdb..7e8cc8e 100644
--- a/tempest/api/network/admin/test_external_network_extension.py
+++ b/tempest/api/network/admin/test_external_network_extension.py
@@ -13,6 +13,7 @@
 import testtools
 
 from tempest.api.network import base
+from tempest.common import utils
 from tempest import config
 from tempest.lib.common.utils import data_utils
 from tempest.lib.common.utils import test_utils
@@ -117,8 +118,15 @@
         self.addCleanup(test_utils.call_and_ignore_notfound_exc,
                         self.admin_floating_ips_client.delete_floatingip,
                         created_floating_ip['id'])
-        floatingip_list = self.admin_floating_ips_client.list_floatingips(
-            network=external_network['id'])
+        if utils.is_extension_enabled('filter-validation', 'network'):
+            floatingip_list = self.admin_floating_ips_client.list_floatingips(
+                floating_network_id=external_network['id'])
+        else:
+            # NOTE(hongbin): This is for testing the backward-compatibility
+            # of neutron API although the parameter is a wrong filter
+            # for listing floating IPs.
+            floatingip_list = self.admin_floating_ips_client.list_floatingips(
+                invalid_filter=external_network['id'])
         self.assertIn(created_floating_ip['id'],
                       (f['id'] for f in floatingip_list['floatingips']))
         self.admin_networks_client.delete_network(external_network['id'])
diff --git a/tempest/api/network/admin/test_floating_ips_admin_actions.py b/tempest/api/network/admin/test_floating_ips_admin_actions.py
index 5aa337c..be0c4c6 100644
--- a/tempest/api/network/admin/test_floating_ips_admin_actions.py
+++ b/tempest/api/network/admin/test_floating_ips_admin_actions.py
@@ -22,7 +22,6 @@
 
 
 class FloatingIPAdminTestJSON(base.BaseAdminNetworkTest):
-    force_tenant_isolation = True
     credentials = ['primary', 'alt', 'admin']
 
     @classmethod
diff --git a/tempest/api/network/admin/test_negative_quotas.py b/tempest/api/network/admin/test_negative_quotas.py
index 6849653..e79f8c3 100644
--- a/tempest/api/network/admin/test_negative_quotas.py
+++ b/tempest/api/network/admin/test_negative_quotas.py
@@ -59,7 +59,7 @@
         # Try to create a third network while the quota is two
         with self.assertRaisesRegex(
                 lib_exc.Conflict,
-                "Quota exceeded for resources: \['network'\].*"):
+                r"Quota exceeded for resources: \['network'\].*"):
             n3 = self.networks_client.create_network()
             self.addCleanup(self.networks_client.delete_network,
                             n3['network']['id'])
diff --git a/tempest/api/network/admin/test_quotas.py b/tempest/api/network/admin/test_quotas.py
index 57a28bf..b1e4a58 100644
--- a/tempest/api/network/admin/test_quotas.py
+++ b/tempest/api/network/admin/test_quotas.py
@@ -13,6 +13,8 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import testtools
+
 from tempest.api.network import base
 from tempest.common import identity
 from tempest.common import utils
@@ -89,3 +91,15 @@
     def test_quotas(self):
         new_quotas = {'network': 0, 'port': 0}
         self._check_quotas(new_quotas)
+
+    @testtools.skipUnless(utils.is_extension_enabled(
+        'quota_details', 'network'), 'Quota details extension not enabled.')
+    @decorators.idempotent_id('7b05ec5f-bf44-43cb-b28f-ddd72a824288')
+    def test_show_quota_details(self):
+        # Show quota details for an existing project
+        quota_details = self.admin_quotas_client.show_quota_details(
+            self.admin_quotas_client.tenant_id)['quota']
+        expected_keys = ['used', 'limit', 'reserved']
+        for resource_type in quota_details:
+            for key in expected_keys:
+                self.assertIn(key, quota_details[resource_type])
diff --git a/tempest/api/network/admin/test_routers.py b/tempest/api/network/admin/test_routers.py
index 8cdb41e..a7355f3 100644
--- a/tempest/api/network/admin/test_routers.py
+++ b/tempest/api/network/admin/test_routers.py
@@ -32,7 +32,6 @@
 
     def _cleanup_router(self, router):
         self.delete_router(router)
-        self.routers.remove(router)
 
     def _create_router(self, name=None, admin_state_up=False,
                        external_network_id=None, enable_snat=None):
diff --git a/tempest/api/network/base.py b/tempest/api/network/base.py
index 8670165..9032fdc 100644
--- a/tempest/api/network/base.py
+++ b/tempest/api/network/base.py
@@ -88,9 +88,6 @@
     @classmethod
     def resource_setup(cls):
         super(BaseNetworkTest, cls).resource_setup()
-        cls.subnets = []
-        cls.ports = []
-        cls.routers = []
         cls.ethertype = "IPv" + str(cls._ip_version)
         if cls._ip_version == 4:
             cls.cidr = netaddr.IPNetwork(CONF.network.project_network_cidr)
@@ -155,7 +152,6 @@
         cls.addClassResourceCleanup(test_utils.call_and_ignore_notfound_exc,
                                     cls.subnets_client.delete_subnet,
                                     subnet['id'])
-        cls.subnets.append(subnet)
         return subnet
 
     @classmethod
@@ -166,7 +162,6 @@
         port = body['port']
         cls.addClassResourceCleanup(test_utils.call_and_ignore_notfound_exc,
                                     cls.ports_client.delete_port, port['id'])
-        cls.ports.append(port)
         return port
 
     @classmethod
@@ -194,7 +189,6 @@
         router = body['router']
         cls.addClassResourceCleanup(test_utils.call_and_ignore_notfound_exc,
                                     cls.delete_router, router)
-        cls.routers.append(router)
         return router
 
     @classmethod
diff --git a/tempest/api/network/test_dhcp_ipv6.py b/tempest/api/network/test_dhcp_ipv6.py
index 9d6d700..0730d58 100644
--- a/tempest/api/network/test_dhcp_ipv6.py
+++ b/tempest/api/network/test_dhcp_ipv6.py
@@ -56,6 +56,9 @@
     def resource_setup(cls):
         super(NetworksTestDHCPv6, cls).resource_setup()
         cls.network = cls.create_network()
+        cls.ports = []
+        cls.subnets = []
+        cls.routers = []
 
     def _remove_from_list_by_index(self, things_list, elem):
         for index, i in enumerate(things_list):
@@ -90,8 +93,10 @@
 
     def _get_ips_from_subnet(self, **kwargs):
         subnet = self.create_subnet(self.network, **kwargs)
+        self.subnets.append(subnet)
         port_mac = data_utils.rand_mac_address()
         port = self.create_port(self.network, mac_address=port_mac)
+        self.ports.append(port)
         real_ip = next(iter(port['fixed_ips']), None)['ip_address']
         eui_ip = str(netutils.get_ipv6_addr_by_EUI64(
             subnet['cidr'], port_mac))
@@ -182,16 +187,21 @@
                 kwargs_dhcp = {'ipv6_address_mode': 'dhcpv6-stateful'}
                 if order == "slaac_first":
                     subnet_slaac = self.create_subnet(self.network, **kwargs)
+                    self.subnets.append(subnet_slaac)
                     subnet_dhcp = self.create_subnet(
                         self.network, **kwargs_dhcp)
+                    self.subnets.append(subnet_dhcp)
                 else:
                     subnet_dhcp = self.create_subnet(
                         self.network, **kwargs_dhcp)
+                    self.subnets.append(subnet_dhcp)
                     subnet_slaac = self.create_subnet(self.network, **kwargs)
+                    self.subnets.append(subnet_slaac)
                 port_mac = data_utils.rand_mac_address()
                 eui_ip = str(netutils.get_ipv6_addr_by_EUI64(
                     subnet_slaac['cidr'], port_mac))
                 port = self.create_port(self.network, mac_address=port_mac)
+                self.ports.append(port)
                 real_ips = dict([(k['subnet_id'], k['ip_address'])
                                  for k in port['fixed_ips']])
                 real_dhcp_ip, real_eui_ip = [real_ips[sub['id']]
@@ -228,16 +238,21 @@
                           'ipv6_address_mode': add_mode}
                 if order == "slaac_first":
                     subnet_slaac = self.create_subnet(self.network, **kwargs)
+                    self.subnets.append(subnet_slaac)
                     subnet_dhcp = self.create_subnet(
                         self.network, ip_version=4)
+                    self.subnets.append(subnet_dhcp)
                 else:
                     subnet_dhcp = self.create_subnet(
                         self.network, ip_version=4)
+                    self.subnets.append(subnet_dhcp)
                     subnet_slaac = self.create_subnet(self.network, **kwargs)
+                    self.subnets.append(subnet_slaac)
                 port_mac = data_utils.rand_mac_address()
                 eui_ip = str(netutils.get_ipv6_addr_by_EUI64(
                     subnet_slaac['cidr'], port_mac))
                 port = self.create_port(self.network, mac_address=port_mac)
+                self.ports.append(port)
                 real_ips = dict([(k['subnet_id'], k['ip_address'])
                                  for k in port['fixed_ips']])
                 real_dhcp_ip, real_eui_ip = [real_ips[sub['id']]
@@ -267,7 +282,9 @@
                       'ipv6_address_mode': add_mode}
             kwargs = dict((k, v) for k, v in kwargs.items() if v)
             subnet = self.create_subnet(self.network, **kwargs)
+            self.subnets.append(subnet)
             port = self.create_port(self.network)
+            self.ports.append(port)
             port_ip = next(iter(port['fixed_ips']), None)['ip_address']
             self._clean_network()
             msg = ('Real IP address is {0} and it is NOT on '
@@ -289,6 +306,7 @@
                       'ipv6_address_mode': add_mode}
             kwargs = dict((k, v) for k, v in kwargs.items() if v)
             subnet = self.create_subnet(self.network, **kwargs)
+            self.subnets.append(subnet)
             ip_range = netaddr.IPRange(subnet["allocation_pools"][0]["start"],
                                        subnet["allocation_pools"][0]["end"])
             ip = netaddr.IPAddress(random.randrange(ip_range.first,
@@ -296,6 +314,7 @@
             port = self.create_port(self.network,
                                     fixed_ips=[{'subnet_id': subnet['id'],
                                                 'ip_address': ip}])
+            self.ports.append(port)
             port_ip = next(iter(port['fixed_ips']), None)['ip_address']
             self._clean_network()
             self.assertEqual(port_ip, ip,
@@ -310,6 +329,7 @@
         kwargs = {'ipv6_ra_mode': 'dhcpv6-stateful',
                   'ipv6_address_mode': 'dhcpv6-stateful'}
         subnet = self.create_subnet(self.network, **kwargs)
+        self.subnets.append(subnet)
         ip_range = netaddr.IPRange(subnet["allocation_pools"][0]["start"],
                                    subnet["allocation_pools"][0]["end"])
         ip = netaddr.IPAddress(random.randrange(
@@ -327,14 +347,16 @@
         kwargs = {'ipv6_ra_mode': 'dhcpv6-stateful',
                   'ipv6_address_mode': 'dhcpv6-stateful'}
         subnet = self.create_subnet(self.network, **kwargs)
+        self.subnets.append(subnet)
         ip_range = netaddr.IPRange(subnet["allocation_pools"][0]["start"],
                                    subnet["allocation_pools"][0]["end"])
         ip = netaddr.IPAddress(random.randrange(
             ip_range.first, ip_range.last)).format()
-        self.create_port(self.network,
-                         fixed_ips=[
-                             {'subnet_id': subnet['id'],
-                              'ip_address': ip}])
+        port = self.create_port(self.network,
+                                fixed_ips=[
+                                    {'subnet_id': subnet['id'],
+                                     'ip_address': ip}])
+        self.ports.append(port)
         self.assertRaisesRegex(lib_exc.Conflict,
                                "IpAddressAlreadyAllocated|IpAddressInUse",
                                self.create_port,
@@ -344,7 +366,9 @@
 
     def _create_subnet_router(self, kwargs):
         subnet = self.create_subnet(self.network, **kwargs)
+        self.subnets.append(subnet)
         router = self.create_router(admin_state_up=True)
+        self.routers.append(router)
         port = self.create_router_interface(router['id'],
                                             subnet['id'])
         body = self.ports_client.show_port(port['port_id'])
diff --git a/tempest/api/network/test_floating_ips.py b/tempest/api/network/test_floating_ips.py
index ef4a23a..504bfa8 100644
--- a/tempest/api/network/test_floating_ips.py
+++ b/tempest/api/network/test_floating_ips.py
@@ -15,6 +15,7 @@
 
 from tempest.api.network import base
 from tempest.common import utils
+from tempest.common.utils import data_utils
 from tempest.common.utils import net_utils
 from tempest import config
 from tempest.lib import decorators
@@ -63,8 +64,10 @@
         cls.router = cls.create_router(external_network_id=cls.ext_net_id)
         cls.create_router_interface(cls.router['id'], cls.subnet['id'])
         # Create two ports one each for Creation and Updating of floatingIP
+        cls.ports = []
         for i in range(2):
-            cls.create_port(cls.network)
+            port = cls.create_port(cls.network)
+            cls.ports.append(port)
 
     @decorators.attr(type='smoke')
     @decorators.idempotent_id('62595970-ab1c-4b7f-8fcc-fddfe55e8718')
@@ -158,11 +161,21 @@
         self.addCleanup(self.floating_ips_client.delete_floatingip,
                         created_floating_ip['id'])
         self.assertEqual(created_floating_ip['router_id'], self.router['id'])
-        network2 = self.create_network()
+        network_name = data_utils.rand_name(self.__class__.__name__)
+        network2 = self.networks_client.create_network(
+            name=network_name)['network']
+        self.addCleanup(self.networks_client.delete_network,
+                        network2['id'])
         subnet2 = self.create_subnet(network2)
+        self.addCleanup(self.subnets_client.delete_subnet, subnet2['id'])
         router2 = self.create_router(external_network_id=self.ext_net_id)
+        self.addCleanup(self.routers_client.delete_router, router2['id'])
         self.create_router_interface(router2['id'], subnet2['id'])
+        self.addCleanup(self.routers_client.remove_router_interface,
+                        router2['id'], subnet_id=subnet2['id'])
         port_other_router = self.create_port(network2)
+        self.addCleanup(self.ports_client.delete_port,
+                        port_other_router['id'])
         # Associate floating IP to the other port on another router
         floating_ip = self.floating_ips_client.update_floatingip(
             created_floating_ip['id'],
diff --git a/tempest/api/network/test_ports.py b/tempest/api/network/test_ports.py
index 5168423..246a5c3 100644
--- a/tempest/api/network/test_ports.py
+++ b/tempest/api/network/test_ports.py
@@ -52,6 +52,21 @@
         ports_list = body['ports']
         self.assertFalse(port_id in [n['id'] for n in ports_list])
 
+    def _create_subnet(self, network, gateway='',
+                       cidr=None, mask_bits=None, **kwargs):
+        subnet = self.create_subnet(network, gateway, cidr, mask_bits)
+        self.addCleanup(self.subnets_client.delete_subnet, subnet['id'])
+        return subnet
+
+    def _create_network(self, network_name=None, **kwargs):
+        network_name = network_name or data_utils.rand_name(
+            self.__class__.__name__)
+        network = self.networks_client.create_network(
+            name=network_name, **kwargs)['network']
+        self.addCleanup(self.networks_client.delete_network,
+                        network['id'])
+        return network
+
     @decorators.attr(type='smoke')
     @decorators.idempotent_id('c72c1c0c-2193-4aca-aaa4-b1442640f51c')
     def test_create_update_delete_port(self):
@@ -73,7 +88,7 @@
     @decorators.idempotent_id('67f1b811-f8db-43e2-86bd-72c074d4a42c')
     def test_create_bulk_port(self):
         network1 = self.network
-        network2 = self.create_network()
+        network2 = self._create_network()
         network_list = [network1['id'], network2['id']]
         port_list = [{'network_id': net_id} for net_id in network_list]
         body = self.ports_client.create_bulk_ports(ports=port_list)
@@ -90,7 +105,7 @@
     @decorators.attr(type='smoke')
     @decorators.idempotent_id('0435f278-40ae-48cb-a404-b8a087bc09b1')
     def test_create_port_in_allowed_allocation_pools(self):
-        network = self.create_network()
+        network = self._create_network()
         net_id = network['id']
         address = self.cidr
         address.prefixlen = self.mask_bits
@@ -100,10 +115,9 @@
             raise exceptions.InvalidConfiguration(msg)
         allocation_pools = {'allocation_pools': [{'start': str(address[2]),
                                                   'end': str(address[-2])}]}
-        subnet = self.create_subnet(network, cidr=address,
-                                    mask_bits=address.prefixlen,
-                                    **allocation_pools)
-        self.addCleanup(self.subnets_client.delete_subnet, subnet['id'])
+        self._create_subnet(network, cidr=address,
+                            mask_bits=address.prefixlen,
+                            **allocation_pools)
         body = self.ports_client.create_port(network_id=net_id)
         self.addCleanup(self.ports_client.delete_port, body['port']['id'])
         port = body['port']
@@ -153,9 +167,8 @@
     @decorators.idempotent_id('e7fe260b-1e79-4dd3-86d9-bec6a7959fc5')
     def test_port_list_filter_by_ip(self):
         # Create network and subnet
-        network = self.create_network()
-        subnet = self.create_subnet(network)
-        self.addCleanup(self.subnets_client.delete_subnet, subnet['id'])
+        network = self._create_network()
+        self._create_subnet(network)
         # Create two ports
         port_1 = self.ports_client.create_port(network_id=network['id'])
         self.addCleanup(self.ports_client.delete_port, port_1['port']['id'])
@@ -187,10 +200,8 @@
         'ip-substring-filtering extension not enabled.')
     def test_port_list_filter_by_ip_substr(self):
         # Create network and subnet
-        network = self.create_network()
-        subnet = self.create_subnet(network)
-        self.addCleanup(self.subnets_client.delete_subnet, subnet['id'])
-
+        network = self._create_network()
+        subnet = self._create_subnet(network)
         # Get two IP addresses
         ip_address_1 = None
         ip_address_2 = None
@@ -261,10 +272,8 @@
     @decorators.idempotent_id('5ad01ed0-0e6e-4c5d-8194-232801b15c72')
     def test_port_list_filter_by_router_id(self):
         # Create a router
-        network = self.create_network()
-        self.addCleanup(self.networks_client.delete_network, network['id'])
-        subnet = self.create_subnet(network)
-        self.addCleanup(self.subnets_client.delete_subnet, subnet['id'])
+        network = self._create_network()
+        self._create_subnet(network)
         router = self.create_router()
         self.addCleanup(self.routers_client.delete_router, router['id'])
         port = self.ports_client.create_port(network_id=network['id'])
@@ -294,12 +303,9 @@
     @decorators.idempotent_id('63aeadd4-3b49-427f-a3b1-19ca81f06270')
     def test_create_update_port_with_second_ip(self):
         # Create a network with two subnets
-        network = self.create_network()
-        self.addCleanup(self.networks_client.delete_network, network['id'])
-        subnet_1 = self.create_subnet(network)
-        self.addCleanup(self.subnets_client.delete_subnet, subnet_1['id'])
-        subnet_2 = self.create_subnet(network)
-        self.addCleanup(self.subnets_client.delete_subnet, subnet_2['id'])
+        network = self._create_network()
+        subnet_1 = self._create_subnet(network)
+        subnet_2 = self._create_subnet(network)
         fixed_ip_1 = [{'subnet_id': subnet_1['id']}]
         fixed_ip_2 = [{'subnet_id': subnet_2['id']}]
 
@@ -323,8 +329,7 @@
         self.assertEqual(2, len(port['fixed_ips']))
 
     def _update_port_with_security_groups(self, security_groups_names):
-        subnet_1 = self.create_subnet(self.network)
-        self.addCleanup(self.subnets_client.delete_subnet, subnet_1['id'])
+        subnet_1 = self._create_subnet(self.network)
         fixed_ip_1 = [{'subnet_id': subnet_1['id']}]
 
         security_groups_list = list()
@@ -413,10 +418,8 @@
         utils.is_extension_enabled('security-group', 'network'),
         'security-group extension not enabled.')
     def test_create_port_with_no_securitygroups(self):
-        network = self.create_network()
-        self.addCleanup(self.networks_client.delete_network, network['id'])
-        subnet = self.create_subnet(network)
-        self.addCleanup(self.subnets_client.delete_subnet, subnet['id'])
+        network = self._create_network()
+        self._create_subnet(network)
         port = self.create_port(network, security_groups=[])
         self.addCleanup(self.ports_client.delete_port, port['id'])
         self.assertIsNotNone(port['security_groups'])
diff --git a/tempest/api/network/test_routers.py b/tempest/api/network/test_routers.py
index abbb779..8b03631 100644
--- a/tempest/api/network/test_routers.py
+++ b/tempest/api/network/test_routers.py
@@ -29,7 +29,6 @@
 
     def _cleanup_router(self, router):
         self.delete_router(router)
-        self.routers.remove(router)
 
     def _create_router(self, name=None, admin_state_up=False,
                        external_network_id=None, enable_snat=None):
@@ -39,6 +38,11 @@
         self.addCleanup(self._cleanup_router, router)
         return router
 
+    def _create_subnet(self, network, gateway='', cidr=None):
+        subnet = self.create_subnet(network, gateway, cidr)
+        self.addCleanup(self.subnets_client.delete_subnet, subnet['id'])
+        return subnet
+
     def _add_router_interface_with_subnet_id(self, router_id, subnet_id):
         interface = self.routers_client.add_router_interface(
             router_id, subnet_id=subnet_id)
@@ -65,12 +69,12 @@
                           'The public_network_id option must be specified.')
     def test_create_show_list_update_delete_router(self):
         # Create a router
-        name = data_utils.rand_name(self.__class__.__name__ + '-router')
+        router_name = data_utils.rand_name(self.__class__.__name__ + '-router')
         router = self._create_router(
-            name=name,
+            name=router_name,
             admin_state_up=False,
             external_network_id=CONF.network.public_network_id)
-        self.assertEqual(router['name'], name)
+        self.assertEqual(router['name'], router_name)
         self.assertEqual(router['admin_state_up'], False)
         self.assertEqual(
             router['external_gateway_info']['network_id'],
@@ -97,8 +101,12 @@
     @decorators.attr(type='smoke')
     @decorators.idempotent_id('b42e6e39-2e37-49cc-a6f4-8467e940900a')
     def test_add_remove_router_interface_with_subnet_id(self):
-        network = self.create_network()
-        subnet = self.create_subnet(network)
+        network_name = data_utils.rand_name(self.__class__.__name__)
+        network = self.networks_client.create_network(
+            name=network_name)['network']
+        self.addCleanup(self.networks_client.delete_network,
+                        network['id'])
+        subnet = self._create_subnet(network)
         router = self._create_router()
         # Add router interface with subnet id
         interface = self.routers_client.add_router_interface(
@@ -116,8 +124,12 @@
     @decorators.attr(type='smoke')
     @decorators.idempotent_id('2b7d2f37-6748-4d78-92e5-1d590234f0d5')
     def test_add_remove_router_interface_with_port_id(self):
-        network = self.create_network()
-        self.create_subnet(network)
+        network_name = data_utils.rand_name(self.__class__.__name__)
+        network = self.networks_client.create_network(
+            name=network_name)['network']
+        self.addCleanup(self.networks_client.delete_network,
+                        network['id'])
+        self._create_subnet(network)
         router = self._create_router()
         port_body = self.ports_client.create_port(
             network_id=network['id'])
@@ -183,13 +195,18 @@
         # Update router extra route, second ip of the range is
         # used as next hop
         for i in range(routes_num):
-            network = self.create_network()
+            network_name = data_utils.rand_name(self.__class__.__name__)
+            network = self.networks_client.create_network(
+                name=network_name)['network']
+            self.addCleanup(self.networks_client.delete_network,
+                            network['id'])
             subnet = self.create_subnet(network, cidr=next_cidr)
             next_cidr = next_cidr.next()
 
             # Add router interface with subnet id
             self.create_router_interface(router['id'], subnet['id'])
-
+            self.addCleanup(self._remove_router_interface_with_subnet_id,
+                            router['id'], subnet['id'])
             cidr = netaddr.IPNetwork(subnet['cidr'])
             next_hop = str(cidr[2])
             destination = str(subnet['cidr'])
@@ -242,13 +259,18 @@
     @decorators.attr(type='smoke')
     @decorators.idempotent_id('802c73c9-c937-4cef-824b-2191e24a6aab')
     def test_add_multiple_router_interfaces(self):
-        network01 = self.create_network(
-            network_name=data_utils.rand_name('router-network01-'))
-        network02 = self.create_network(
-            network_name=data_utils.rand_name('router-network02-'))
-        subnet01 = self.create_subnet(network01)
+        network_name = data_utils.rand_name(self.__class__.__name__)
+        network01 = self.networks_client.create_network(
+            name=network_name)['network']
+        self.addCleanup(self.networks_client.delete_network,
+                        network01['id'])
+        network02 = self.networks_client.create_network(
+            name=data_utils.rand_name(self.__class__.__name__))['network']
+        self.addCleanup(self.networks_client.delete_network,
+                        network02['id'])
+        subnet01 = self._create_subnet(network01)
         sub02_cidr = self.cidr.next()
-        subnet02 = self.create_subnet(network02, cidr=sub02_cidr)
+        subnet02 = self._create_subnet(network02, cidr=sub02_cidr)
         router = self._create_router()
         interface01 = self._add_router_interface_with_subnet_id(router['id'],
                                                                 subnet01['id'])
@@ -261,8 +283,12 @@
 
     @decorators.idempotent_id('96522edf-b4b5-45d9-8443-fa11c26e6eff')
     def test_router_interface_port_update_with_fixed_ip(self):
-        network = self.create_network()
-        subnet = self.create_subnet(network)
+        network_name = data_utils.rand_name(self.__class__.__name__)
+        network = self.networks_client.create_network(
+            name=network_name)['network']
+        self.addCleanup(self.networks_client.delete_network,
+                        network['id'])
+        subnet = self._create_subnet(network)
         router = self._create_router()
         fixed_ip = [{'subnet_id': subnet['id']}]
         interface = self._add_router_interface_with_subnet_id(router['id'],
diff --git a/tempest/api/volume/admin/test_group_snapshots.py b/tempest/api/volume/admin/test_group_snapshots.py
index 45f4caa..731a055 100644
--- a/tempest/api/volume/admin/test_group_snapshots.py
+++ b/tempest/api/volume/admin/test_group_snapshots.py
@@ -157,6 +157,57 @@
         waiters.wait_for_volume_resource_status(
             self.groups_client, grp2['id'], 'available')
 
+    @decorators.idempotent_id('7d7fc000-0b4c-4376-a372-544116d2e127')
+    @decorators.related_bug('1739031')
+    def test_delete_group_snapshots_following_updated_volumes(self):
+        volume_type = self.create_volume_type()
+
+        group_type = self.create_group_type()
+
+        # Create a volume group
+        grp = self.create_group(group_type=group_type['id'],
+                                volume_types=[volume_type['id']])
+
+        # Note: When dealing with consistency groups all volumes must
+        # reside on the same backend. Adding volumes to the same consistency
+        # group from multiple backends isn't supported. In order to ensure all
+        # volumes share the same backend, all volumes must share same
+        # volume-type and group id.
+        volume_list = []
+        for _ in range(2):
+            volume = self.create_volume(volume_type=volume_type['id'],
+                                        group_id=grp['id'])
+            volume_list.append(volume['id'])
+
+        for vol in volume_list:
+            self.groups_client.update_group(grp['id'],
+                                            remove_volumes=vol)
+            waiters.wait_for_volume_resource_status(
+                self.groups_client, grp['id'], 'available')
+
+            self.groups_client.update_group(grp['id'],
+                                            add_volumes=vol)
+            waiters.wait_for_volume_resource_status(
+                self.groups_client, grp['id'], 'available')
+
+        # Verify the created volumes are associated with consistency group
+        vols = self.volumes_client.list_volumes(detail=True)['volumes']
+        grp_vols = [v for v in vols if v['group_id'] == grp['id']]
+        self.assertEqual(2, len(grp_vols))
+
+        # Create a snapshot group
+        group_snapshot = self._create_group_snapshot(group_id=grp['id'])
+        snapshots = self.snapshots_client.list_snapshots(
+            detail=True)['snapshots']
+
+        for snap in snapshots:
+            if snap['volume_id'] in volume_list:
+                waiters.wait_for_volume_resource_status(
+                    self.snapshots_client, snap['id'], 'available')
+
+        # Delete a snapshot group
+        self._delete_group_snapshot(group_snapshot)
+
 
 class GroupSnapshotsV319Test(BaseGroupSnapshotsTest):
     _api_version = 3
diff --git a/tempest/api/volume/admin/test_multi_backend.py b/tempest/api/volume/admin/test_multi_backend.py
index c0891e4..c5c70d2 100644
--- a/tempest/api/volume/admin/test_multi_backend.py
+++ b/tempest/api/volume/admin/test_multi_backend.py
@@ -29,6 +29,10 @@
         if not CONF.volume_feature_enabled.multi_backend:
             raise cls.skipException("Cinder multi-backend feature disabled")
 
+        if len(set(CONF.volume.backend_names)) < 2:
+            raise cls.skipException("Requires at least two different "
+                                    "backend names")
+
     @classmethod
     def resource_setup(cls):
         super(VolumeMultiBackendTest, cls).resource_setup()
@@ -41,9 +45,6 @@
 
         # Volume/Type creation (uses volume_backend_name)
         # It is not allowed to create the same backend name twice
-        if len(backend_names) < 2:
-            raise cls.skipException("Requires at least two different "
-                                    "backend names")
         for backend_name in backend_names:
             # Volume/Type creation (uses backend_name)
             cls._create_type_and_volume(backend_name, False)
diff --git a/tempest/api/volume/admin/test_user_messages.py b/tempest/api/volume/admin/test_user_messages.py
index 20c3538..9907497 100644
--- a/tempest/api/volume/admin/test_user_messages.py
+++ b/tempest/api/volume/admin/test_user_messages.py
@@ -62,8 +62,16 @@
         return message_id
 
     @decorators.idempotent_id('50f29e6e-f363-42e1-8ad1-f67ae7fd4d5a')
-    def test_list_messages(self):
-        self._create_user_message()
+    def test_list_show_messages(self):
+        message_id = self._create_user_message()
+        self.addCleanup(self.messages_client.delete_message, message_id)
+
+        # show message
+        message = self.messages_client.show_message(message_id)['message']
+        for key in MESSAGE_KEYS:
+            self.assertIn(key, message.keys(), 'Missing expected key %s' % key)
+
+        # list messages
         messages = self.messages_client.list_messages()['messages']
         self.assertIsInstance(messages, list)
         for message in messages:
@@ -71,16 +79,6 @@
                 self.assertIn(key, message.keys(),
                               'Missing expected key %s' % key)
 
-    @decorators.idempotent_id('55a4a61e-c7b2-4ba0-a05d-b914bdef3070')
-    def test_show_message(self):
-        message_id = self._create_user_message()
-        self.addCleanup(self.messages_client.delete_message, message_id)
-
-        message = self.messages_client.show_message(message_id)['message']
-
-        for key in MESSAGE_KEYS:
-            self.assertIn(key, message.keys(), 'Missing expected key %s' % key)
-
     @decorators.idempotent_id('c6eb6901-cdcc-490f-b735-4fe251842aed')
     def test_delete_message(self):
         message_id = self._create_user_message()
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_quotas.py b/tempest/api/volume/admin/test_volume_quotas.py
index 6f9daa8..61a6df4 100644
--- a/tempest/api/volume/admin/test_volume_quotas.py
+++ b/tempest/api/volume/admin/test_volume_quotas.py
@@ -13,10 +13,8 @@
 #    under the License.
 
 from tempest.api.volume import base
-from tempest.common import identity
 from tempest.common import tempest_fixtures as fixtures
 from tempest.common import waiters
-from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 
 QUOTA_KEYS = ['gigabytes', 'snapshots', 'volumes', 'backups',
@@ -25,8 +23,6 @@
 
 
 class BaseVolumeQuotasAdminTestJSON(base.BaseVolumeAdminTest):
-    force_tenant_isolation = True
-
     credentials = ['primary', 'alt', 'admin']
 
     def setUp(self):
@@ -42,8 +38,21 @@
     @classmethod
     def setup_clients(cls):
         super(BaseVolumeQuotasAdminTestJSON, cls).setup_clients()
-        cls.transfer_client = cls.os_primary.volume_transfers_v2_client
-        cls.alt_transfer_client = cls.os_alt.volume_transfers_v2_client
+        cls.transfer_client = cls.os_primary.volume_transfers_client_latest
+        cls.alt_transfer_client = cls.os_alt.volume_transfers_client_latest
+
+    @classmethod
+    def resource_setup(cls):
+        super(BaseVolumeQuotasAdminTestJSON, cls).resource_setup()
+
+        # Save the current set of quotas so that some tests may use it
+        # to restore the quotas to their original values after they are
+        # done.
+        cls.original_quota_set = (cls.admin_quotas_client.show_quota_set(
+            cls.demo_tenant_id)['quota_set'])
+        cls.cleanup_quota_set = dict(
+            (k, v) for k, v in cls.original_quota_set.items()
+            if k in QUOTA_KEYS)
 
     @decorators.idempotent_id('59eada70-403c-4cef-a2a3-a8ce2f1b07a0')
     def test_list_quotas(self):
@@ -62,8 +71,6 @@
     @decorators.idempotent_id('3d45c99e-cc42-4424-a56e-5cbd212b63a6')
     def test_update_all_quota_resources_for_tenant(self):
         # Admin can update all the resource quota limits for a tenant
-        default_quota_set = self.admin_quotas_client.show_default_quota_set(
-            self.demo_tenant_id)['quota_set']
         new_quota_set = {'gigabytes': 1009,
                          'volumes': 11,
                          'snapshots': 11,
@@ -76,11 +83,9 @@
             self.demo_tenant_id,
             **new_quota_set)['quota_set']
 
-        cleanup_quota_set = dict(
-            (k, v) for k, v in default_quota_set.items()
-            if k in QUOTA_KEYS)
         self.addCleanup(self.admin_quotas_client.update_quota_set,
-                        self.demo_tenant_id, **cleanup_quota_set)
+                        self.demo_tenant_id, **self.cleanup_quota_set)
+
         # test that the specific values we set are actually in
         # the final result. There is nothing here that ensures there
         # would be no other values in there.
@@ -96,6 +101,25 @@
             for usage_key in QUOTA_USAGE_KEYS:
                 self.assertIn(usage_key, quota_usage[key])
 
+    @decorators.idempotent_id('874b35a9-51f1-4258-bec5-cd561b6690d3')
+    def test_delete_quota(self):
+        # Admin can delete the resource quota set for a project
+
+        self.addCleanup(self.admin_quotas_client.update_quota_set,
+                        self.demo_tenant_id, **self.cleanup_quota_set)
+
+        quota_set_default = self.admin_quotas_client.show_default_quota_set(
+            self.demo_tenant_id)['quota_set']
+        volume_default = quota_set_default['volumes']
+
+        self.admin_quotas_client.update_quota_set(
+            self.demo_tenant_id, volumes=(volume_default + 5))
+
+        self.admin_quotas_client.delete_quota_set(self.demo_tenant_id)
+        quota_set_new = (self.admin_quotas_client.show_quota_set(
+            self.demo_tenant_id)['quota_set'])
+        self.assertEqual(volume_default, quota_set_new['volumes'])
+
     @decorators.idempotent_id('ae8b6091-48ad-4bfa-a188-bbf5cc02115f')
     def test_quota_usage(self):
         quota_usage = self.admin_quotas_client.show_quota_set(
@@ -115,28 +139,6 @@
                          volume["size"],
                          new_quota_usage['gigabytes']['in_use'])
 
-    @decorators.idempotent_id('874b35a9-51f1-4258-bec5-cd561b6690d3')
-    def test_delete_quota(self):
-        # Admin can delete the resource quota set for a project
-        project_name = data_utils.rand_name('quota_tenant')
-        description = data_utils.rand_name('desc_')
-        project = identity.identity_utils(self.os_admin).create_project(
-            project_name, description=description)
-        project_id = project['id']
-        self.addCleanup(identity.identity_utils(self.os_admin).delete_project,
-                        project_id)
-        quota_set_default = self.admin_quotas_client.show_default_quota_set(
-            project_id)['quota_set']
-        volume_default = quota_set_default['volumes']
-
-        self.admin_quotas_client.update_quota_set(
-            project_id, volumes=(volume_default + 5))
-
-        self.admin_quotas_client.delete_quota_set(project_id)
-        quota_set_new = (self.admin_quotas_client.show_quota_set(project_id)
-                         ['quota_set'])
-        self.assertEqual(volume_default, quota_set_new['volumes'])
-
     @decorators.idempotent_id('8911036f-9d54-4720-80cc-a1c9796a8805')
     def test_quota_usage_after_volume_transfer(self):
         # Create a volume for transfer
diff --git a/tempest/api/volume/admin/test_volume_quotas_negative.py b/tempest/api/volume/admin/test_volume_quotas_negative.py
index d127b5f..f50f336 100644
--- a/tempest/api/volume/admin/test_volume_quotas_negative.py
+++ b/tempest/api/volume/admin/test_volume_quotas_negative.py
@@ -19,10 +19,11 @@
 from tempest.lib import exceptions as lib_exc
 
 CONF = config.CONF
+QUOTA_KEYS = ['gigabytes', 'snapshots', 'volumes', 'backups',
+              'backup_gigabytes', 'per_volume_gigabytes']
 
 
 class BaseVolumeQuotasNegativeTestJSON(base.BaseVolumeAdminTest):
-    force_tenant_isolation = True
 
     @classmethod
     def setup_credentials(cls):
@@ -32,11 +33,23 @@
     @classmethod
     def resource_setup(cls):
         super(BaseVolumeQuotasNegativeTestJSON, cls).resource_setup()
+
+        # Save the current set of quotas, then set up the cleanup method
+        # to restore the quotas to their original values after the tests
+        # from this class are done. This is needed just in case Tempest is
+        # configured to use pre-provisioned projects/user accounts.
+        cls.original_quota_set = (cls.admin_quotas_client.show_quota_set(
+            cls.demo_tenant_id)['quota_set'])
+        cls.cleanup_quota_set = dict(
+            (k, v) for k, v in cls.original_quota_set.items()
+            if k in QUOTA_KEYS)
+        cls.addClassResourceCleanup(cls.admin_quotas_client.update_quota_set,
+                                    cls.demo_tenant_id,
+                                    **cls.cleanup_quota_set)
+
         cls.shared_quota_set = {'gigabytes': 2 * CONF.volume.volume_size,
                                 'volumes': 1}
 
-        # NOTE(gfidente): no need to restore original quota set
-        # after the tests as they only work with dynamic credentials.
         cls.admin_quotas_client.update_quota_set(
             cls.demo_tenant_id,
             **cls.shared_quota_set)
diff --git a/tempest/api/volume/admin/test_volume_retype.py b/tempest/api/volume/admin/test_volume_retype.py
new file mode 100644
index 0000000..1c56eb2
--- /dev/null
+++ b/tempest/api/volume/admin/test_volume_retype.py
@@ -0,0 +1,180 @@
+#    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 abc
+
+from oslo_log import log as logging
+
+from tempest.api.volume import base
+from tempest.common import waiters
+from tempest import config
+from tempest.lib import decorators
+
+CONF = config.CONF
+
+LOG = logging.getLogger(__name__)
+
+
+class VolumeRetypeTest(base.BaseVolumeAdminTest):
+
+    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
+        # before deleting the types we've created.
+
+        # This list should return 2 volumes until the copy and cleanup
+        # process is finished.
+        fetched_list = self.admin_volume_client.list_volumes(
+            params={'all_tenants': True,
+                    'display_name': vol['name']})['volumes']
+
+        for fetched_vol in fetched_list:
+            if fetched_vol['id'] != vol['id']:
+                # This is the Cinder internal volume
+                LOG.debug('Waiting for internal volume %s deletion',
+                          fetched_vol['id'])
+                self.admin_volume_client.wait_for_resource_deletion(
+                    fetched_vol['id'])
+                break
+
+    @abc.abstractmethod
+    def _verify_migration(self, source_vol, dest_vol):
+        pass
+
+    def _create_volume_from_snapshot(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'])
+
+        return src_vol
+
+    def _retype_volume(self, volume, migration_policy):
+
+        volume_source = self.admin_volume_client.show_volume(
+            volume['id'])['volume']
+
+        self.volumes_client.retype_volume(
+            volume['id'],
+            new_type=self.dst_vol_type['name'],
+            migration_policy=migration_policy)
+        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(
+            volume['id'])['volume']
+
+        self._verify_migration(volume_source, volume_dest)
+
+
+class VolumeRetypeWithMigrationTest(VolumeRetypeTest):
+
+    @classmethod
+    def skip_checks(cls):
+        super(VolumeRetypeTest, cls).skip_checks()
+
+        if not CONF.volume_feature_enabled.multi_backend:
+            raise cls.skipException("Cinder multi-backend feature disabled.")
+
+        if len(set(CONF.volume.backend_names)) < 2:
+            raise cls.skipException("Requires at least two different "
+                                    "backend names")
+
+    @classmethod
+    def resource_setup(cls):
+        super(VolumeRetypeWithMigrationTest, cls).resource_setup()
+        # read backend name from a list.
+        backend_src = CONF.volume.backend_names[0]
+        backend_dst = CONF.volume.backend_names[1]
+
+        extra_specs_src = {"volume_backend_name": backend_src}
+        extra_specs_dst = {"volume_backend_name": backend_dst}
+
+        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)
+
+    def _verify_migration(self, volume_source, volume_dest):
+
+        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')
+
+        # Check the volume information after the migration.
+        self.assertEqual('success',
+                         volume_dest['os-vol-mig-status-attr:migstat'])
+        self.assertEqual('success', volume_dest['migration_status'])
+
+        for key in keys_with_no_change:
+            self.assertEqual(volume_source[key], volume_dest[key])
+
+        for key in keys_with_change:
+            self.assertNotEqual(volume_source[key], volume_dest[key])
+
+        self.assertEqual(volume_dest['volume_type'], self.dst_vol_type['name'])
+
+    @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, migration_policy='on-demand')
+
+    @decorators.idempotent_id('d0d9554f-e7a5-4104-8973-f35b27ccb60d')
+    def test_volume_from_snapshot_retype_with_migration(self):
+        src_vol = self._create_volume_from_snapshot()
+
+        # Migrate the volume from snapshot to the second backend
+        self._retype_volume(src_vol, migration_policy='on-demand')
+
+
+class VolumeRetypeWithoutMigrationTest(VolumeRetypeTest):
+
+    @classmethod
+    def resource_setup(cls):
+        super(VolumeRetypeWithoutMigrationTest, cls).resource_setup()
+        cls.src_vol_type = cls.create_volume_type('volume-type-1')
+        cls.dst_vol_type = cls.create_volume_type('volume-type-2')
+
+    def _verify_migration(self, volume_source, volume_dest):
+
+        keys_with_no_change = ('id', 'size', 'description', 'name',
+                               'user_id', 'os-vol-tenant-attr:tenant_id',
+                               'os-vol-host-attr:host')
+        keys_with_change = ('volume_type',)
+
+        # Check the volume information after the retype
+        self.assertIsNone(volume_dest['os-vol-mig-status-attr:migstat'])
+        self.assertIsNone(volume_dest['migration_status'])
+
+        for key in keys_with_no_change:
+            self.assertEqual(volume_source[key], volume_dest[key])
+
+        for key in keys_with_change:
+            self.assertNotEqual(volume_source[key], volume_dest[key])
+
+        self.assertEqual(volume_dest['volume_type'], self.dst_vol_type['name'])
+
+    @decorators.idempotent_id('b90412ee-465d-46e9-b249-ec84a47d5f25')
+    def test_available_volume_retype(self):
+        src_vol = self.create_volume(volume_type=self.src_vol_type['name'])
+
+        # Retype the volume from snapshot
+        self._retype_volume(src_vol, migration_policy='never')
diff --git a/tempest/api/volume/admin/test_volume_retype_with_migration.py b/tempest/api/volume/admin/test_volume_retype_with_migration.py
deleted file mode 100644
index f0b3a4f..0000000
--- a/tempest/api/volume/admin/test_volume_retype_with_migration.py
+++ /dev/null
@@ -1,107 +0,0 @@
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
-#    not use this file except in compliance with the License. You may obtain
-#    a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-#    License for the specific language governing permissions and limitations
-#    under the License.
-
-
-from oslo_log import log as logging
-
-from tempest.api.volume import base
-from tempest.common import waiters
-from tempest import config
-from tempest.lib import decorators
-
-CONF = config.CONF
-
-LOG = logging.getLogger(__name__)
-
-
-class VolumeRetypeWithMigrationTest(base.BaseVolumeAdminTest):
-
-    @classmethod
-    def skip_checks(cls):
-        super(VolumeRetypeWithMigrationTest, cls).skip_checks()
-
-        if not CONF.volume_feature_enabled.multi_backend:
-            raise cls.skipException("Cinder multi-backend feature disabled.")
-
-        if len(set(CONF.volume.backend_names)) < 2:
-            raise cls.skipException("Requires at least two different "
-                                    "backend names")
-
-    @classmethod
-    def resource_setup(cls):
-        super(VolumeRetypeWithMigrationTest, cls).resource_setup()
-        # read backend name from a list.
-        backend_src = CONF.volume.backend_names[0]
-        backend_dst = CONF.volume.backend_names[1]
-
-        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.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):
-        # 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
-        # before deleting the types we've created.
-
-        # This list should return 2 volumes until the copy and cleanup
-        # process is finished.
-        fetched_list = cls.admin_volume_client.list_volumes(
-            params={'all_tenants': True,
-                    'display_name': cls.src_vol['name']})['volumes']
-
-        for fetched_vol in fetched_list:
-            if fetched_vol['id'] != cls.src_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(
-                    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):
-
-        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']
-
-        self.volumes_client.retype_volume(
-            self.src_vol['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.dst_vol_type['name'])
-        volume_dest = self.admin_volume_client.show_volume(
-            self.src_vol['id'])['volume']
-
-        # Check the volume information after the migration.
-        self.assertEqual('success',
-                         volume_dest['os-vol-mig-status-attr:migstat'])
-        self.assertEqual('success', volume_dest['migration_status'])
-
-        for key in keys_with_no_change:
-            self.assertEqual(volume_source[key], volume_dest[key])
-
-        for key in keys_with_change:
-            self.assertNotEqual(volume_source[key], volume_dest[key])
diff --git a/tempest/api/volume/admin/test_volume_services_negative.py b/tempest/api/volume/admin/test_volume_services_negative.py
new file mode 100644
index 0000000..3a863a1
--- /dev/null
+++ b/tempest/api/volume/admin/test_volume_services_negative.py
@@ -0,0 +1,64 @@
+# Copyright 2018 FiberHome Telecommunication Technologies CO.,LTD
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from tempest.api.volume import base
+from tempest.lib import decorators
+from tempest.lib import exceptions as lib_exc
+
+
+class VolumeServicesNegativeTest(base.BaseVolumeAdminTest):
+
+    @classmethod
+    def resource_setup(cls):
+        super(VolumeServicesNegativeTest, cls).resource_setup()
+        services = cls.admin_volume_services_client.list_services()['services']
+        cls.host = services[0]['host']
+        cls.binary = services[0]['binary']
+
+    @decorators.attr(type='negative')
+    @decorators.idempotent_id('3246ce65-ba70-4159-aa3b-082c28e4b484')
+    def test_enable_service_with_invalid_host(self):
+        self.assertRaises(lib_exc.NotFound,
+                          self.admin_volume_services_client.enable_service,
+                          host='invalid_host', binary=self.binary)
+
+    @decorators.attr(type='negative')
+    @decorators.idempotent_id('c571f179-c6e6-4c50-a0ab-368b628a8ac1')
+    def test_disable_service_with_invalid_binary(self):
+        self.assertRaises(lib_exc.NotFound,
+                          self.admin_volume_services_client.disable_service,
+                          host=self.host, binary='invalid_binary')
+
+    @decorators.attr(type='negative')
+    @decorators.idempotent_id('77767b36-5e8f-4c68-a0b5-2308cc21ec64')
+    def test_disable_log_reason_with_no_reason(self):
+        self.assertRaises(lib_exc.BadRequest,
+                          self.admin_volume_services_client.disable_log_reason,
+                          host=self.host, binary=self.binary,
+                          disabled_reason=None)
+
+    @decorators.attr(type='negative')
+    @decorators.idempotent_id('712bfab8-1f44-4eb5-a632-fa70bf78f05e')
+    def test_freeze_host_with_invalid_host(self):
+        self.assertRaises(lib_exc.BadRequest,
+                          self.admin_volume_services_client.freeze_host,
+                          host='invalid_host')
+
+    @decorators.attr(type='negative')
+    @decorators.idempotent_id('7c6287c9-d655-47e1-9a11-76f6657a6dce')
+    def test_thaw_host_with_invalid_host(self):
+        self.assertRaises(lib_exc.BadRequest,
+                          self.admin_volume_services_client.thaw_host,
+                          host='invalid_host')
diff --git a/tempest/api/volume/admin/test_volume_snapshot_quotas_negative.py b/tempest/api/volume/admin/test_volume_snapshot_quotas_negative.py
index 0f4e90f..74eb792 100644
--- a/tempest/api/volume/admin/test_volume_snapshot_quotas_negative.py
+++ b/tempest/api/volume/admin/test_volume_snapshot_quotas_negative.py
@@ -19,10 +19,11 @@
 from tempest.lib import exceptions as lib_exc
 
 CONF = config.CONF
+QUOTA_KEYS = ['gigabytes', 'snapshots', 'volumes', 'backups',
+              'backup_gigabytes', 'per_volume_gigabytes']
 
 
 class VolumeSnapshotQuotasNegativeTestJSON(base.BaseVolumeAdminTest):
-    force_tenant_isolation = True
 
     @classmethod
     def skip_checks(cls):
@@ -38,12 +39,24 @@
     @classmethod
     def resource_setup(cls):
         super(VolumeSnapshotQuotasNegativeTestJSON, cls).resource_setup()
+
+        # Save the current set of quotas, then set up the cleanup method
+        # to restore the quotas to their original values after the tests
+        # from this class are done. This is needed just in case Tempest is
+        # configured to use pre-provisioned projects/user accounts.
+        cls.original_quota_set = (cls.admin_quotas_client.show_quota_set(
+            cls.demo_tenant_id)['quota_set'])
+        cls.cleanup_quota_set = dict(
+            (k, v) for k, v in cls.original_quota_set.items()
+            if k in QUOTA_KEYS)
+        cls.addClassResourceCleanup(cls.admin_quotas_client.update_quota_set,
+                                    cls.demo_tenant_id,
+                                    **cls.cleanup_quota_set)
+
         cls.default_volume_size = CONF.volume.volume_size
         cls.shared_quota_set = {'gigabytes': 3 * cls.default_volume_size,
                                 'volumes': 1, 'snapshots': 1}
 
-        # NOTE(gfidente): no need to restore original quota set
-        # after the tests as they only work with tenant isolation.
         cls.admin_quotas_client.update_quota_set(
             cls.demo_tenant_id,
             **cls.shared_quota_set)
diff --git a/tempest/api/volume/admin/test_volume_types.py b/tempest/api/volume/admin/test_volume_types.py
index af1024c..1077524 100644
--- a/tempest/api/volume/admin/test_volume_types.py
+++ b/tempest/api/volume/admin/test_volume_types.py
@@ -161,6 +161,12 @@
                              'The fetched encryption_type %s is different '
                              'from the updated encryption_type' % key)
 
+        # Get encryption specs item
+        key = 'cipher'
+        item = self.admin_encryption_types_client.show_encryption_specs_item(
+            encrypt_type_id, key)
+        self.assertEqual(update_kwargs[key], item[key])
+
         # Delete encryption type
         self.admin_encryption_types_client.delete_encryption_type(
             encrypt_type_id)
diff --git a/tempest/api/volume/admin/test_volumes_backup.py b/tempest/api/volume/admin/test_volumes_backup.py
index 375aacb..45060d0 100644
--- a/tempest/api/volume/admin/test_volumes_backup.py
+++ b/tempest/api/volume/admin/test_volumes_backup.py
@@ -59,9 +59,9 @@
         volume = self.create_volume()
         # Create backup
         backup_name = data_utils.rand_name(self.__class__.__name__ + '-Backup')
-        backup = (self.create_backup(backup_client=self.admin_backups_client,
-                                     volume_id=volume['id'],
-                                     name=backup_name))
+        backup = self.create_backup(volume_id=volume['id'], name=backup_name)
+        waiters.wait_for_volume_resource_status(self.volumes_client,
+                                                volume['id'], 'available')
         self.assertEqual(backup_name, backup['name'])
 
         # Export Backup
@@ -103,21 +103,22 @@
         self.assertIn(new_id, [b['id'] for b in backups])
 
         # Restore backup
-        restore = self.admin_backups_client.restore_backup(
-            backup['id'])['restore']
-        self.addCleanup(self.admin_volume_client.delete_volume,
+        restore = self.backups_client.restore_backup(backup['id'])['restore']
+        self.addCleanup(self.volumes_client.delete_volume,
                         restore['volume_id'])
         self.assertEqual(backup['id'], restore['backup_id'])
-        waiters.wait_for_volume_resource_status(self.admin_volume_client,
-                                                restore['volume_id'],
-                                                'available')
+
+        # When restore operation is performed then, backup['id']
+        # goes to 'restoring' state so we need to wait for
+        # backup['id'] to become 'available'.
+        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')
 
         # Verify if restored volume is there in volume list
-        volumes = self.admin_volume_client.list_volumes()['volumes']
+        volumes = self.volumes_client.list_volumes()['volumes']
         self.assertIn(restore['volume_id'], [v['id'] for v in volumes])
-        waiters.wait_for_volume_resource_status(self.admin_backups_client,
-                                                import_backup['id'],
-                                                'available')
 
     @decorators.idempotent_id('47a35425-a891-4e13-961c-c45deea21e94')
     def test_volume_backup_reset_status(self):
@@ -126,12 +127,12 @@
         # Create a backup
         backup_name = data_utils.rand_name(
             self.__class__.__name__ + '-Backup')
-        backup = self.create_backup(backup_client=self.admin_backups_client,
-                                    volume_id=volume['id'],
-                                    name=backup_name)
+        backup = self.create_backup(volume_id=volume['id'], name=backup_name)
+        waiters.wait_for_volume_resource_status(self.volumes_client,
+                                                volume['id'], 'available')
         self.assertEqual(backup_name, backup['name'])
         # Reset backup status to error
         self.admin_backups_client.reset_backup_status(backup_id=backup['id'],
                                                       status="error")
-        waiters.wait_for_volume_resource_status(self.admin_backups_client,
+        waiters.wait_for_volume_resource_status(self.backups_client,
                                                 backup['id'], 'error')
diff --git a/tempest/api/volume/base.py b/tempest/api/volume/base.py
index 81fd6e6..64fe29a 100644
--- a/tempest/api/volume/base.py
+++ b/tempest/api/volume/base.py
@@ -74,24 +74,19 @@
         if CONF.service_available.glance:
             cls.images_client = cls.os_primary.image_client_v2
 
-        if cls._api_version == 3:
-            cls.backups_client = cls.os_primary.backups_v3_client
-            cls.volumes_client = cls.os_primary.volumes_v3_client
-            cls.messages_client = cls.os_primary.volume_v3_messages_client
-            cls.versions_client = cls.os_primary.volume_v3_versions_client
-            cls.groups_client = cls.os_primary.groups_v3_client
-            cls.group_snapshots_client = (
-                cls.os_primary.group_snapshots_v3_client)
-        else:
-            cls.backups_client = cls.os_primary.backups_v2_client
-            cls.volumes_client = cls.os_primary.volumes_v2_client
-
-        cls.snapshots_client = cls.os_primary.snapshots_v2_client
+        cls.backups_client = cls.os_primary.backups_client_latest
+        cls.volumes_client = cls.os_primary.volumes_client_latest
+        cls.messages_client = cls.os_primary.volume_messages_client_latest
+        cls.versions_client = cls.os_primary.volume_versions_client_latest
+        cls.groups_client = cls.os_primary.groups_client_latest
+        cls.group_snapshots_client = (
+            cls.os_primary.group_snapshots_client_latest)
+        cls.snapshots_client = cls.os_primary.snapshots_client_latest
         cls.volumes_extension_client =\
-            cls.os_primary.volumes_v2_extension_client
+            cls.os_primary.volumes_extension_client_latest
         cls.availability_zone_client = (
-            cls.os_primary.volume_v2_availability_zone_client)
-        cls.volume_limits_client = cls.os_primary.volume_v2_limits_client
+            cls.os_primary.volume_availability_zone_client_latest)
+        cls.volume_limits_client = cls.os_primary.volume_limits_client_latest
 
     def setUp(self):
         super(BaseVolumeTest, self).setUp()
@@ -247,34 +242,34 @@
     def setup_clients(cls):
         super(BaseVolumeAdminTest, cls).setup_clients()
 
-        cls.admin_volume_qos_client = cls.os_admin.volume_qos_v2_client
+        cls.admin_volume_qos_client = cls.os_admin.volume_qos_client_latest
         cls.admin_volume_services_client = \
-            cls.os_admin.volume_services_v2_client
-        cls.admin_volume_types_client = cls.os_admin.volume_types_v2_client
-        cls.admin_volume_manage_client = cls.os_admin.volume_manage_v2_client
-        cls.admin_volume_client = cls.os_admin.volumes_v2_client
-        if cls._api_version == 3:
-            cls.admin_volume_client = cls.os_admin.volumes_v3_client
-            cls.admin_groups_client = cls.os_admin.groups_v3_client
-            cls.admin_messages_client = cls.os_admin.volume_v3_messages_client
-            cls.admin_group_snapshots_client = \
-                cls.os_admin.group_snapshots_v3_client
-            cls.admin_group_types_client = cls.os_admin.group_types_v3_client
-        cls.admin_hosts_client = cls.os_admin.volume_hosts_v2_client
+            cls.os_admin.volume_services_client_latest
+        cls.admin_volume_types_client = cls.os_admin.volume_types_client_latest
+        cls.admin_volume_manage_client = (
+            cls.os_admin.volume_manage_client_latest)
+        cls.admin_volume_client = cls.os_admin.volumes_client_latest
+        cls.admin_groups_client = cls.os_admin.groups_client_latest
+        cls.admin_messages_client = cls.os_admin.volume_messages_client_latest
+        cls.admin_group_snapshots_client = \
+            cls.os_admin.group_snapshots_client_latest
+        cls.admin_group_types_client = cls.os_admin.group_types_client_latest
+        cls.admin_hosts_client = cls.os_admin.volume_hosts_client_latest
         cls.admin_snapshot_manage_client = \
-            cls.os_admin.snapshot_manage_v2_client
-        cls.admin_snapshots_client = cls.os_admin.snapshots_v2_client
-        cls.admin_backups_client = cls.os_admin.backups_v2_client
+            cls.os_admin.snapshot_manage_client_latest
+        cls.admin_snapshots_client = cls.os_admin.snapshots_client_latest
+        cls.admin_backups_client = cls.os_admin.backups_client_latest
         cls.admin_encryption_types_client = \
-            cls.os_admin.encryption_types_v2_client
+            cls.os_admin.encryption_types_client_latest
         cls.admin_quota_classes_client = \
-            cls.os_admin.volume_quota_classes_v2_client
-        cls.admin_quotas_client = cls.os_admin.volume_quotas_v2_client
-        cls.admin_volume_limits_client = cls.os_admin.volume_v2_limits_client
+            cls.os_admin.volume_quota_classes_client_latest
+        cls.admin_quotas_client = cls.os_admin.volume_quotas_client_latest
+        cls.admin_volume_limits_client = (
+            cls.os_admin.volume_limits_client_latest)
         cls.admin_capabilities_client = \
-            cls.os_admin.volume_capabilities_v2_client
+            cls.os_admin.volume_capabilities_client_latest
         cls.admin_scheduler_stats_client = \
-            cls.os_admin.volume_scheduler_stats_v2_client
+            cls.os_admin.volume_scheduler_stats_client_latest
 
     @classmethod
     def create_test_qos_specs(cls, name=None, consumer=None, **kwargs):
diff --git a/tempest/api/volume/test_volume_absolute_limits.py b/tempest/api/volume/test_volume_absolute_limits.py
index 4018468..00a3375 100644
--- a/tempest/api/volume/test_volume_absolute_limits.py
+++ b/tempest/api/volume/test_volume_absolute_limits.py
@@ -17,7 +17,6 @@
 from tempest import config
 from tempest.lib import decorators
 
-
 CONF = config.CONF
 
 
@@ -32,9 +31,16 @@
     @classmethod
     def resource_setup(cls):
         super(AbsoluteLimitsTests, cls).resource_setup()
+
         # Create a shared volume for tests
         cls.volume = cls.create_volume()
 
+    @classmethod
+    def skip_checks(cls):
+        super(AbsoluteLimitsTests, cls).skip_checks()
+        if not CONF.auth.use_dynamic_credentials:
+            raise cls.skipException("Must use dynamic credentials.")
+
     @decorators.idempotent_id('8e943f53-e9d6-4272-b2e9-adcf2f7c29ad')
     def test_get_volume_absolute_limits(self):
         # get volume limit for a tenant
diff --git a/tempest/api/volume/test_volume_transfers.py b/tempest/api/volume/test_volume_transfers.py
index 4108da5..c85e0bc 100644
--- a/tempest/api/volume/test_volume_transfers.py
+++ b/tempest/api/volume/test_volume_transfers.py
@@ -15,6 +15,7 @@
 
 from tempest.api.volume import base
 from tempest.common import waiters
+from tempest.lib.common.utils import test_utils
 from tempest.lib import decorators
 
 
@@ -26,10 +27,10 @@
     def setup_clients(cls):
         super(VolumesTransfersTest, cls).setup_clients()
 
-        cls.client = cls.os_primary.volume_transfers_v2_client
-        cls.alt_client = cls.os_alt.volume_transfers_v2_client
-        cls.alt_volumes_client = cls.os_alt.volumes_v2_client
-        cls.adm_volumes_client = cls.os_admin.volumes_v2_client
+        cls.client = cls.os_primary.volume_transfers_client_latest
+        cls.alt_client = cls.os_alt.volume_transfers_client_latest
+        cls.alt_volumes_client = cls.os_alt.volumes_client_latest
+        cls.adm_volumes_client = cls.os_admin.volumes_client_latest
 
     @decorators.idempotent_id('4d75b645-a478-48b1-97c8-503f64242f1a')
     def test_create_get_list_accept_volume_transfer(self):
@@ -43,6 +44,9 @@
         transfer = self.client.create_volume_transfer(
             volume_id=volume['id'])['transfer']
         transfer_id = transfer['id']
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self.client.delete_volume_transfer,
+                        transfer_id)
         auth_key = transfer['auth_key']
         waiters.wait_for_volume_resource_status(
             self.volumes_client, volume['id'], 'awaiting-transfer')
@@ -81,6 +85,9 @@
         # Create a volume transfer
         transfer_id = self.client.create_volume_transfer(
             volume_id=volume['id'])['transfer']['id']
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self.client.delete_volume_transfer,
+                        transfer_id)
         waiters.wait_for_volume_resource_status(
             self.volumes_client, volume['id'], 'awaiting-transfer')
 
diff --git a/tempest/api/volume/test_volumes_backup.py b/tempest/api/volume/test_volumes_backup.py
index 552b231..c178272 100644
--- a/tempest/api/volume/test_volumes_backup.py
+++ b/tempest/api/volume/test_volumes_backup.py
@@ -117,6 +117,8 @@
             self.__class__.__name__ + '-Backup')
         backup = self.create_backup(volume_id=volume['id'],
                                     name=backup_name, force=True)
+        waiters.wait_for_volume_resource_status(self.volumes_client,
+                                                volume['id'], 'in-use')
         self.assertEqual(backup_name, backup['name'])
 
     @decorators.idempotent_id('2a8ba340-dff2-4511-9db7-646f07156b15')
@@ -128,10 +130,12 @@
 
         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'])
+        waiters.wait_for_volume_resource_status(self.volumes_client,
+                                                volume['id'], 'available')
 
         # Restore the backup
         restored_volume_id = self.restore_backup(backup['id'])['volume_id']
@@ -140,7 +144,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):
@@ -160,6 +164,8 @@
         # Create volume and backup
         volume = self.create_volume()
         backup = self.create_backup(volume_id=volume['id'])
+        waiters.wait_for_volume_resource_status(self.volumes_client,
+                                                volume['id'], 'available')
 
         # Update backup and assert response body for update_backup method
         update_kwargs = {
diff --git a/tempest/api/volume/test_volumes_extend.py b/tempest/api/volume/test_volumes_extend.py
index 54052ae..ac9a9c7 100644
--- a/tempest/api/volume/test_volumes_extend.py
+++ b/tempest/api/volume/test_volumes_extend.py
@@ -32,8 +32,8 @@
     @decorators.idempotent_id('9a36df71-a257-43a5-9555-dc7c88e66e0e')
     def test_volume_extend(self):
         # Extend Volume Test.
-        volume = self.create_volume()
-        extend_size = volume['size'] + 1
+        volume = self.create_volume(image_ref=self.image_ref)
+        extend_size = volume['size'] * 2
         self.volumes_client.extend_volume(volume['id'],
                                           new_size=extend_size)
         waiters.wait_for_volume_resource_status(self.volumes_client,
@@ -44,12 +44,11 @@
     @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'])
 
-        extend_size = volume['size'] + 1
+        extend_size = volume['size'] * 2
         self.volumes_client.extend_volume(volume['id'], new_size=extend_size)
 
         waiters.wait_for_volume_resource_status(self.volumes_client,
diff --git a/tempest/api/volume/test_volumes_negative.py b/tempest/api/volume/test_volumes_negative.py
index f139283..866bd87 100644
--- a/tempest/api/volume/test_volumes_negative.py
+++ b/tempest/api/volume/test_volumes_negative.py
@@ -103,21 +103,24 @@
     def test_create_volume_with_nonexistent_volume_type(self):
         # Should not be able to create volume with non-existent volume type
         self.assertRaises(lib_exc.NotFound, self.volumes_client.create_volume,
-                          size='1', volume_type=data_utils.rand_uuid())
+                          size=CONF.volume.volume_size,
+                          volume_type=data_utils.rand_uuid())
 
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('0c36f6ae-4604-4017-b0a9-34fdc63096f9')
     def test_create_volume_with_nonexistent_snapshot_id(self):
         # Should not be able to create volume with non-existent snapshot
         self.assertRaises(lib_exc.NotFound, self.volumes_client.create_volume,
-                          size='1', snapshot_id=data_utils.rand_uuid())
+                          size=CONF.volume.volume_size,
+                          snapshot_id=data_utils.rand_uuid())
 
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('47c73e08-4be8-45bb-bfdf-0c4e79b88344')
     def test_create_volume_with_nonexistent_source_volid(self):
         # Should not be able to create volume with non-existent source volume
         self.assertRaises(lib_exc.NotFound, self.volumes_client.create_volume,
-                          size='1', source_volid=data_utils.rand_uuid())
+                          size=CONF.volume.volume_size,
+                          source_volid=data_utils.rand_uuid())
 
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('0186422c-999a-480e-a026-6a665744c30c')
diff --git a/tempest/api/volume/test_volumes_snapshots.py b/tempest/api/volume/test_volumes_snapshots.py
index dcd3518..4b97b80 100644
--- a/tempest/api/volume/test_volumes_snapshots.py
+++ b/tempest/api/volume/test_volumes_snapshots.py
@@ -15,6 +15,7 @@
 
 from tempest.api.volume import base
 from tempest.common import utils
+from tempest.common import waiters
 from tempest import config
 from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
@@ -41,16 +42,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.
@@ -151,6 +155,30 @@
         self.assertEqual(volume['snapshot_id'], src_snap['id'])
         self.assertEqual(volume['size'], src_size + 1)
 
+    @decorators.idempotent_id('053d8870-8282-4fff-9dbb-99cb58bb5e0a')
+    def test_volume_from_snapshot_no_size(self):
+        # Creates a volume from a snapshot defaulting to original size
+        src_size = CONF.volume.volume_size
+
+        src_vol = self.create_volume(size=src_size)
+        src_snap = self.create_snapshot(src_vol['id'])
+        # Destination volume without specifying a size
+        dst_vol = self.create_volume(snapshot_id=src_snap['id'])
+
+        # NOTE(zhufl): dst_vol is created based on snapshot, so dst_vol
+        # should be deleted before deleting snapshot, otherwise deleting
+        # snapshot will end with status 'error-deleting'. This depends on
+        # the implementation mechanism of vendors, generally speaking,
+        # some verdors will use "virtual disk clone" which will promote
+        # disk clone speed, and in this situation the "disk clone"
+        # is just a relationship between volume and snapshot.
+        self.addCleanup(self.delete_volume, self.volumes_client, dst_vol['id'])
+
+        volume = self.volumes_client.show_volume(dst_vol['id'])['volume']
+        # Should allow
+        self.assertEqual(volume['snapshot_id'], src_snap['id'])
+        self.assertEqual(volume['size'], src_size)
+
     @decorators.idempotent_id('bbcfa285-af7f-479e-8c1a-8c34fc16543c')
     @testtools.skipUnless(CONF.volume_feature_enabled.backup,
                           "Cinder backup is disabled")
@@ -160,6 +188,8 @@
 
         backup = self.create_backup(volume_id=self.volume_origin['id'],
                                     snapshot_id=snapshot['id'])
+        waiters.wait_for_volume_resource_status(self.snapshots_client,
+                                                snapshot['id'], 'available')
         backup_info = self.backups_client.show_backup(backup['id'])['backup']
         self.assertEqual(self.volume_origin['id'], backup_info['volume_id'])
         self.assertEqual(snapshot['id'], backup_info['snapshot_id'])
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/api/volume/test_volumes_snapshots_negative.py b/tempest/api/volume/test_volumes_snapshots_negative.py
index ea5f036..0453c0a 100644
--- a/tempest/api/volume/test_volumes_snapshots_negative.py
+++ b/tempest/api/volume/test_volumes_snapshots_negative.py
@@ -50,7 +50,7 @@
     @decorators.idempotent_id('677863d1-34f9-456d-b6ac-9924f667a7f4')
     def test_volume_from_snapshot_decreasing_size(self):
         # Creates a volume a snapshot passing a size different from the source
-        src_size = CONF.volume.volume_size + 1
+        src_size = CONF.volume.volume_size * 2
 
         src_vol = self.create_volume(size=src_size)
         src_snap = self.create_snapshot(src_vol['id'])
@@ -58,7 +58,7 @@
         # Destination volume smaller than source
         self.assertRaises(lib_exc.BadRequest,
                           self.volumes_client.create_volume,
-                          size=src_size - 1,
+                          size=CONF.volume.volume_size,
                           snapshot_id=src_snap['id'])
 
     @decorators.attr(type=['negative'])
diff --git a/tempest/clients.py b/tempest/clients.py
index b06eafb..204ce08 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
@@ -218,55 +222,76 @@
 
     def _set_volume_clients(self):
 
-        if CONF.volume_feature_enabled.api_v1:
-            self.backups_client = self.volume_v1.BackupsClient()
-            self.encryption_types_client = \
-                self.volume_v1.EncryptionTypesClient()
-            self.snapshots_client = self.volume_v1.SnapshotsClient()
-            self.volume_availability_zone_client = \
-                self.volume_v1.AvailabilityZoneClient()
-            self.volume_hosts_client = self.volume_v1.HostsClient()
-            self.volume_limits_client = self.volume_v1.LimitsClient()
-            self.volume_qos_client = self.volume_v1.QosSpecsClient()
-            self.volume_quotas_client = self.volume_v1.QuotasClient()
-            self.volume_services_client = self.volume_v1.ServicesClient()
-            self.volume_types_client = self.volume_v1.TypesClient()
-            self.volumes_client = self.volume_v1.VolumesClient()
-            self.volumes_extension_client = self.volume_v1.ExtensionsClient()
-
         # if only api_v3 is enabled, all these clients should be available
         if (CONF.volume_feature_enabled.api_v2 or
             CONF.volume_feature_enabled.api_v3):
-            self.backups_v2_client = self.volume_v2.BackupsClient()
-            self.encryption_types_v2_client = \
-                self.volume_v2.EncryptionTypesClient()
-            self.snapshot_manage_v2_client = \
-                self.volume_v2.SnapshotManageClient()
-            self.snapshots_v2_client = self.volume_v2.SnapshotsClient()
-            self.volume_capabilities_v2_client = \
-                self.volume_v2.CapabilitiesClient()
-            self.volume_manage_v2_client = self.volume_v2.VolumeManageClient()
-            self.volume_qos_v2_client = self.volume_v2.QosSpecsClient()
-            self.volume_services_v2_client = self.volume_v2.ServicesClient()
-            self.volume_types_v2_client = self.volume_v2.TypesClient()
-            self.volume_hosts_v2_client = self.volume_v2.HostsClient()
-            self.volume_quotas_v2_client = self.volume_v2.QuotasClient()
-            self.volume_quota_classes_v2_client = \
-                self.volume_v2.QuotaClassesClient()
-            self.volume_scheduler_stats_v2_client = \
-                self.volume_v2.SchedulerStatsClient()
-            self.volume_transfers_v2_client = \
-                self.volume_v2.TransfersClient()
-            self.volume_v2_availability_zone_client = \
-                self.volume_v2.AvailabilityZoneClient()
-            self.volume_v2_limits_client = self.volume_v2.LimitsClient()
-            self.volumes_v2_client = self.volume_v2.VolumesClient()
-            self.volumes_v2_extension_client = \
-                self.volume_v2.ExtensionsClient()
+            self.backups_client_latest = self.volume_v3.BackupsClient()
+            self.encryption_types_client_latest = \
+                self.volume_v3.EncryptionTypesClient()
+            self.snapshot_manage_client_latest = \
+                self.volume_v3.SnapshotManageClient()
+            self.snapshots_client_latest = self.volume_v3.SnapshotsClient()
+            self.volume_capabilities_client_latest = \
+                self.volume_v3.CapabilitiesClient()
+            self.volume_manage_client_latest = (
+                self.volume_v3.VolumeManageClient())
+            self.volume_qos_client_latest = self.volume_v3.QosSpecsClient()
+            self.volume_services_client_latest = (
+                self.volume_v3.ServicesClient())
+            self.volume_types_client_latest = self.volume_v3.TypesClient()
+            self.volume_hosts_client_latest = self.volume_v3.HostsClient()
+            self.volume_quotas_client_latest = self.volume_v3.QuotasClient()
+            self.volume_quota_classes_client_latest = \
+                self.volume_v3.QuotaClassesClient()
+            self.volume_scheduler_stats_client_latest = \
+                self.volume_v3.SchedulerStatsClient()
+            self.volume_transfers_client_latest = \
+                self.volume_v3.TransfersClient()
+            self.volume_availability_zone_client_latest = \
+                self.volume_v3.AvailabilityZoneClient()
+            self.volume_limits_client_latest = self.volume_v3.LimitsClient()
+            self.volumes_client_latest = self.volume_v3.VolumesClient()
+            self.volumes_extension_client_latest = \
+                self.volume_v3.ExtensionsClient()
+            self.group_types_client_latest = self.volume_v3.GroupTypesClient()
+            self.groups_client_latest = self.volume_v3.GroupsClient()
+            self.group_snapshots_client_latest = \
+                self.volume_v3.GroupSnapshotsClient()
+            self.volume_messages_client_latest = (
+                self.volume_v3.MessagesClient())
+            self.volume_versions_client_latest = (
+                self.volume_v3.VersionsClient())
 
-            # 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
+            # TODO(gmann): Below alias for service clients have been
+            # deprecated and will be removed in future. Start using the alias
+            # defined above with suffix _latest.
+            # ****************Deprecated alias start from here***************
+            self.backups_v2_client = self.volume_v3.BackupsClient()
+            self.encryption_types_v2_client = \
+                self.volume_v3.EncryptionTypesClient()
+            self.snapshot_manage_v2_client = \
+                self.volume_v3.SnapshotManageClient()
+            self.snapshots_v2_client = self.volume_v3.SnapshotsClient()
+            self.volume_capabilities_v2_client = \
+                self.volume_v3.CapabilitiesClient()
+            self.volume_manage_v2_client = self.volume_v3.VolumeManageClient()
+            self.volume_qos_v2_client = self.volume_v3.QosSpecsClient()
+            self.volume_services_v2_client = self.volume_v3.ServicesClient()
+            self.volume_types_v2_client = self.volume_v3.TypesClient()
+            self.volume_hosts_v2_client = self.volume_v3.HostsClient()
+            self.volume_quotas_v2_client = self.volume_v3.QuotasClient()
+            self.volume_quota_classes_v2_client = \
+                self.volume_v3.QuotaClassesClient()
+            self.volume_scheduler_stats_v2_client = \
+                self.volume_v3.SchedulerStatsClient()
+            self.volume_transfers_v2_client = \
+                self.volume_v3.TransfersClient()
+            self.volume_v2_availability_zone_client = \
+                self.volume_v3.AvailabilityZoneClient()
+            self.volume_v2_limits_client = self.volume_v3.LimitsClient()
+            self.volumes_v2_client = self.volume_v3.VolumesClient()
+            self.volumes_v2_extension_client = \
+                self.volume_v3.ExtensionsClient()
 
         if CONF.volume_feature_enabled.api_v3:
             self.backups_v3_client = self.volume_v3.BackupsClient()
@@ -278,10 +303,7 @@
             self.volume_v3_messages_client = self.volume_v3.MessagesClient()
             self.volume_v3_versions_client = self.volume_v3.VersionsClient()
             self.volumes_v3_client = self.volume_v3.VolumesClient()
-
-            # 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
+            # ****************Deprecated alias end here***********************
 
     def _set_object_storage_clients(self):
         self.account_client = self.object_storage.AccountClient()
diff --git a/tempest/cmd/account_generator.py b/tempest/cmd/account_generator.py
index 1c671ec..9be8ee2 100755
--- a/tempest/cmd/account_generator.py
+++ b/tempest/cmd/account_generator.py
@@ -311,5 +311,6 @@
         resources.extend(generate_resources(cred_provider, opts.admin))
     dump_accounts(resources, opts.identity_version, opts.accounts)
 
+
 if __name__ == "__main__":
     main()
diff --git a/tempest/cmd/cleanup_service.py b/tempest/cmd/cleanup_service.py
index 025959a..83cf42c 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
@@ -315,7 +315,7 @@
 class VolumeQuotaService(BaseService):
     def __init__(self, manager, **kwargs):
         super(VolumeQuotaService, self).__init__(kwargs)
-        self.client = manager.volume_quotas_v2_client
+        self.client = manager.volume_quotas_client_latest
 
     def delete(self):
         client = self.client
@@ -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 7634d9e..d84f3a3 100644
--- a/tempest/cmd/init.py
+++ b/tempest/cmd/init.py
@@ -20,19 +20,15 @@
 from oslo_config import generator
 from oslo_log import log as logging
 from six import moves
-from testrepository import commands
+from stestr import commands
 
 from tempest.cmd import workspace
 
 LOG = logging.getLogger(__name__)
 
-TESTR_CONF = """[DEFAULT]
-test_command=OS_STDOUT_CAPTURE=${OS_STDOUT_CAPTURE:-1} \\
-    OS_STDERR_CAPTURE=${OS_STDERR_CAPTURE:-1} \\
-    OS_TEST_TIMEOUT=${OS_TEST_TIMEOUT:-500} \\
-    ${PYTHON:-python} -m subunit.run discover -t %s %s $LISTOPT $IDOPTION
-test_id_option=--load-list $IDFILE
-test_list_option=--list
+STESTR_CONF = r"""[DEFAULT]
+test_path=%s
+top_dir=%s
 group_regex=([^\.]*\.)*
 """
 
@@ -84,13 +80,13 @@
                                  "is ~/.tempest/workspace.yaml")
         return parser
 
-    def generate_testr_conf(self, local_path):
-        testr_conf_path = os.path.join(local_path, '.testr.conf')
+    def generate_stestr_conf(self, local_path):
+        stestr_conf_path = os.path.join(local_path, '.stestr.conf')
         top_level_path = os.path.dirname(os.path.dirname(__file__))
         discover_path = os.path.join(top_level_path, 'test_discover')
-        testr_conf = TESTR_CONF % (top_level_path, discover_path)
-        with open(testr_conf_path, 'w+') as testr_conf_file:
-            testr_conf_file.write(testr_conf)
+        stestr_conf = STESTR_CONF % (discover_path, top_level_path)
+        with open(stestr_conf_path, 'w+') as stestr_conf_file:
+            stestr_conf_file.write(stestr_conf)
 
     def get_configparser(self, conf_path):
         config_parse = moves.configparser.ConfigParser()
@@ -140,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)
 
@@ -148,7 +144,7 @@
         etc_dir = os.path.join(local_dir, 'etc')
         config_path = os.path.join(etc_dir, 'tempest.conf')
         log_dir = os.path.join(local_dir, 'logs')
-        testr_dir = os.path.join(local_dir, '.testrepository')
+        stestr_dir = os.path.join(local_dir, '.stestr')
         # Create lock dir
         if not os.path.isdir(lock_dir):
             LOG.debug('Creating lock dir: %s', lock_dir)
@@ -163,12 +159,11 @@
         self.generate_sample_config(local_dir)
         # Update local confs to reflect local paths
         self.update_local_conf(config_path, lock_dir, log_dir)
-        # Generate a testr conf file
-        self.generate_testr_conf(local_dir)
-        # setup local testr working dir
-        if not os.path.isdir(testr_dir):
-            commands.run_argv(['testr', 'init', '-d', local_dir], sys.stdin,
-                              sys.stdout, sys.stderr)
+        # Generate a stestr conf file
+        self.generate_stestr_conf(local_dir)
+        # setup local stestr working dir
+        if not os.path.isdir(stestr_dir):
+            commands.init_command(repo_url=local_dir)
 
     def take_action(self, parsed_args):
         workspace_manager = workspace.WorkspaceManager(
diff --git a/tempest/cmd/run.py b/tempest/cmd/run.py
index 6435717..a27425c 100644
--- a/tempest/cmd/run.py
+++ b/tempest/cmd/run.py
@@ -19,9 +19,11 @@
 ==============
 Tempest run has several options:
 
-* ``--regex, -r``: This is a selection regex like what testr uses. It will run
-  any tests that match on re.match() with the regex
-* ``--smoke, -s``: Run all the tests tagged as smoke
+ * **--regex/-r**: This is a selection regex like what stestr uses. It will run
+                   any tests that match on re.match() with the regex
+ * **--smoke/-s**: Run all the tests tagged as smoke
+ * **--black-regex**: It allows to do simple test exclusion via passing a
+                      rejection/black regexp
 
 There are also the ``--blacklist-file`` and ``--whitelist-file`` options that
 let you pass a filepath to tempest run with the file format being a line
@@ -32,17 +34,9 @@
     ^regex1 # Match these tests
     .*regex2 # Match those tests
 
-The blacklist file will be used to construct a negative lookahead regex and
-the whitelist file will simply OR all the regexes in the file. The whitelist
-and blacklist file options are mutually exclusive so you can't use them
-together. However, you can combine either with a normal regex or the *--smoke*
-flag. When used with a blacklist file the generated regex will be combined to
-something like::
-
-    ^((?!black_regex1|black_regex2).)*$cli_regex1
-
-When combined with a whitelist file all the regexes from the file and the CLI
-regexes will be ORed.
+These arguments are just passed into stestr, you can refer to the stestr
+selection docs for more details on how these operate:
+http://stestr.readthedocs.io/en/latest/MANUAL.html#test-selection
 
 You can also use the ``--list-tests`` option in conjunction with selection
 arguments to list which tests will be run.
@@ -74,9 +68,9 @@
 ---------------------
 Tempest run provides you with an option to execute tempest from anywhere on
 your system. You are required to provide a config file in this case with the
-``--config-file`` option. When run tempest will create a .testrepository
-directory and a .testr.conf file in your current working directory. This way
-you can use testr commands directly to inspect the state of the previous run.
+``--config-file`` option. When run tempest will create a .stestr
+directory and a .stestr.conf file in your current working directory. This way
+you can use stestr commands directly to inspect the state of the previous run.
 
 Test Output
 ===========
@@ -94,18 +88,13 @@
 the current run's results with the previous runs.
 """
 
-import io
 import os
 import sys
-import tempfile
-import threading
 
 from cliff import command
-from os_testr import regex_builder
-from os_testr import subunit_trace
 from oslo_serialization import jsonutils as json
 import six
-from testrepository.commands import run_argv
+from stestr import commands
 
 from tempest import clients
 from tempest.cmd import cleanup_service
@@ -124,35 +113,27 @@
     def _set_env(self, config_file=None):
         if config_file:
             CONF.set_config_path(os.path.abspath(config_file))
-        # NOTE(mtreinish): This is needed so that testr doesn't gobble up any
+        # NOTE(mtreinish): This is needed so that stestr doesn't gobble up any
         # stacktraces on failure.
         if 'TESTR_PDB' in os.environ:
             return
         else:
             os.environ["TESTR_PDB"] = ""
-        # NOTE(dims): most of our .testr.conf try to test for PYTHON
+        # NOTE(dims): most of our .stestr.conf try to test for PYTHON
         # environment variable and fall back to "python", under python3
         # if it does not exist. we should set it to the python3 executable
         # to deal with this situation better for now.
         if six.PY3 and 'PYTHON' not in os.environ:
             os.environ['PYTHON'] = sys.executable
 
-    def _create_testrepository(self):
-        if not os.path.isdir('.testrepository'):
-            returncode = run_argv(['testr', 'init'], sys.stdin, sys.stdout,
-                                  sys.stderr)
-            if returncode:
-                sys.exit(returncode)
-
-    def _create_testr_conf(self):
+    def _create_stestr_conf(self):
         top_level_path = os.path.dirname(os.path.dirname(__file__))
         discover_path = os.path.join(top_level_path, 'test_discover')
-        file_contents = init.TESTR_CONF % (top_level_path, discover_path)
-        with open('.testr.conf', 'w+') as testr_conf_file:
-            testr_conf_file.write(file_contents)
+        file_contents = init.STESTR_CONF % (discover_path, top_level_path)
+        with open('.stestr.conf', 'w+') as stestr_conf_file:
+            stestr_conf_file.write(file_contents)
 
     def take_action(self, parsed_args):
-        returncode = 0
         if parsed_args.config_file:
             self._set_env(parsed_args.config_file)
         else:
@@ -169,52 +150,39 @@
                     "register the workspace." %
                     (parsed_args.workspace, workspace_mgr.path))
             os.chdir(path)
-            # NOTE(mtreinish): tempest init should create a .testrepository dir
-            # but since workspaces can be imported let's sanity check and
-            # ensure that one is created
-            self._create_testrepository()
-        # Local execution mode
-        elif os.path.isfile('.testr.conf'):
-            # If you're running in local execution mode and there is not a
-            # testrepository dir create one
-            self._create_testrepository()
+            if not os.path.isfile('.stestr.conf'):
+                self._create_stestr_conf()
         # local execution with config file mode
-        elif parsed_args.config_file:
-            self._create_testr_conf()
-            self._create_testrepository()
-        else:
-            print("No .testr.conf file was found for local execution")
+        elif parsed_args.config_file and not os.path.isfile('.stestr.conf'):
+            self._create_stestr_conf()
+        elif not os.path.isfile('.stestr.conf'):
+            print("No .stestr.conf file was found for local execution")
             sys.exit(2)
         if parsed_args.state:
             self._init_state()
         else:
             pass
 
-        if parsed_args.combine:
-            temp_stream = tempfile.NamedTemporaryFile()
-            return_code = run_argv(['tempest', 'last', '--subunit'], sys.stdin,
-                                   temp_stream, sys.stderr)
-            if return_code > 0:
-                sys.exit(return_code)
-
         regex = self._build_regex(parsed_args)
+        return_code = 0
         if parsed_args.list_tests:
-            argv = ['tempest', 'list-tests', regex]
-            returncode = run_argv(argv, sys.stdin, sys.stdout, sys.stderr)
-        else:
-            options = self._build_options(parsed_args)
-            returncode = self._run(regex, options)
-            if returncode > 0:
-                sys.exit(returncode)
+            return_code = commands.list_command(
+                filters=regex, whitelist_file=parsed_args.whitelist_file,
+                blacklist_file=parsed_args.blacklist_file,
+                black_regex=parsed_args.black_regex)
 
-        if parsed_args.combine:
-            return_code = run_argv(['tempest', 'last', '--subunit'], sys.stdin,
-                                   temp_stream, sys.stderr)
+        else:
+            serial = not parsed_args.parallel
+            return_code = commands.run_command(
+                filters=regex, subunit_out=parsed_args.subunit,
+                serial=serial, concurrency=parsed_args.concurrency,
+                blacklist_file=parsed_args.blacklist_file,
+                whitelist_file=parsed_args.whitelist_file,
+                black_regex=parsed_args.black_regex,
+                load_list=parsed_args.load_list, combine=parsed_args.combine)
             if return_code > 0:
                 sys.exit(return_code)
-            returncode = run_argv(['tempest', 'load', temp_stream.name],
-                                  sys.stdin, sys.stdout, sys.stderr)
-        sys.exit(returncode)
+        return return_code
 
     def get_description(self):
         return 'Run tempest'
@@ -262,23 +230,24 @@
         regex.add_argument('--smoke', '-s', action='store_true',
                            help="Run the smoke tests only")
         regex.add_argument('--regex', '-r', default='',
-                           help='A normal testr selection regex used to '
+                           help='A normal stestr selection regex used to '
                                 'specify a subset of tests to run')
-        list_selector = parser.add_mutually_exclusive_group()
-        list_selector.add_argument('--whitelist-file', '--whitelist_file',
-                                   help="Path to a whitelist file, this file "
-                                        "contains a separate regex on each "
-                                        "newline.")
-        list_selector.add_argument('--blacklist-file', '--blacklist_file',
-                                   help='Path to a blacklist file, this file '
-                                        'contains a separate regex exclude on '
-                                        'each newline')
-        list_selector.add_argument('--load-list', '--load_list',
-                                   help='Path to a non-regex whitelist file, '
-                                        'this file contains a seperate test '
-                                        'on each newline. This command'
-                                        'supports files created by the tempest'
-                                        'run ``--list-tests`` command')
+        parser.add_argument('--black-regex', dest='black_regex',
+                            help='A regex to exclude tests that match it')
+        parser.add_argument('--whitelist-file', '--whitelist_file',
+                            help="Path to a whitelist file, this file "
+                            "contains a separate regex on each "
+                            "newline.")
+        parser.add_argument('--blacklist-file', '--blacklist_file',
+                            help='Path to a blacklist file, this file '
+                                 'contains a separate regex exclude on '
+                                 'each newline')
+        parser.add_argument('--load-list', '--load_list',
+                            help='Path to a non-regex whitelist file, '
+                                 'this file contains a separate test '
+                                 'on each newline. This command'
+                                 'supports files created by the tempest'
+                                 'run ``--list-tests`` command')
         # list only args
         parser.add_argument('--list-tests', '-l', action='store_true',
                             help='List tests',
@@ -305,62 +274,15 @@
         parser.add_argument("--combine", action='store_true',
                             help='Combine the output of this run with the '
                                  "previous run's as a combined stream in the "
-                                 "testr repository after it finish")
+                                 "stestr repository after it finish")
 
         parser.set_defaults(parallel=True)
         return parser
 
     def _build_regex(self, parsed_args):
-        regex = ''
+        regex = None
         if parsed_args.smoke:
-            regex = 'smoke'
+            regex = ['smoke']
         elif parsed_args.regex:
-            regex = parsed_args.regex
-        if parsed_args.whitelist_file or parsed_args.blacklist_file:
-            regex = regex_builder.construct_regex(parsed_args.blacklist_file,
-                                                  parsed_args.whitelist_file,
-                                                  regex, False)
+            regex = parsed_args.regex.split()
         return regex
-
-    def _build_options(self, parsed_args):
-        options = []
-        if parsed_args.subunit:
-            options.append("--subunit")
-        if parsed_args.parallel:
-            options.append("--parallel")
-        if parsed_args.concurrency:
-            options.append("--concurrency=%s" % parsed_args.concurrency)
-        if parsed_args.load_list:
-            options.append("--load-list=%s" % parsed_args.load_list)
-        return options
-
-    def _run(self, regex, options):
-        returncode = 0
-        argv = ['tempest', 'run', regex] + options
-        if '--subunit' in options:
-            returncode = run_argv(argv, sys.stdin, sys.stdout, sys.stderr)
-        else:
-            argv.append('--subunit')
-            stdin = io.StringIO()
-            stdout_r, stdout_w = os.pipe()
-            subunit_w = os.fdopen(stdout_w, 'wt')
-            subunit_r = os.fdopen(stdout_r)
-            returncodes = {}
-
-            def run_argv_thread():
-                returncodes['testr'] = run_argv(argv, stdin, subunit_w,
-                                                sys.stderr)
-                subunit_w.close()
-
-            run_thread = threading.Thread(target=run_argv_thread)
-            run_thread.start()
-            returncodes['subunit-trace'] = subunit_trace.trace(
-                subunit_r, sys.stdout, post_fails=True, print_failures=True)
-            run_thread.join()
-            subunit_r.close()
-            # python version of pipefail
-            if returncodes['testr']:
-                returncode = returncodes['testr']
-            elif returncodes['subunit-trace']:
-                returncode = returncodes['subunit-trace']
-        return returncode
diff --git a/tempest/cmd/subunit_describe_calls.py b/tempest/cmd/subunit_describe_calls.py
index f0ade7e..8dcf575 100644
--- a/tempest/cmd/subunit_describe_calls.py
+++ b/tempest/cmd/subunit_describe_calls.py
@@ -29,6 +29,9 @@
   written to. This contains more information than is present in stdout.
 * ``--ports, -p``: (Optional) The path to a JSON file describing the ports
   being used by different services
+* ``--verbose, -v``: (Optional) Print Request and Response Headers and Body
+  data to stdout
+
 
 Usage
 -----
@@ -92,7 +95,7 @@
     ip_re = re.compile(r'(^|[^0-9])[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]'
                        '{1,3}([^0-9]|$)')
     url_re = re.compile(r'.*INFO.*Request \((?P<name>.*)\): (?P<code>[\d]{3}) '
-                        '(?P<verb>\w*) (?P<url>.*) .*')
+                        r'(?P<verb>\w*) (?P<url>.*) .*')
     port_re = re.compile(r'.*:(?P<port>\d+).*')
     path_re = re.compile(r'http[s]?://[^/]*/(?P<path>.*)')
     request_re = re.compile(r'.* Request - Headers: (?P<headers>.*)')
@@ -262,6 +265,10 @@
             "-p", "--ports", metavar="<ports file>", default=None,
             help="A JSON file describing the ports for each service.")
 
+        self.add_argument(
+            "-v", "--verbose", action='store_true', default=False,
+            help="Add Request and Response header and body data to stdout.")
+
 
 def parse(stream, non_subunit_name, ports):
     if ports is not None and os.path.exists(ports):
@@ -286,7 +293,7 @@
     return url_parser
 
 
-def output(url_parser, output_file):
+def output(url_parser, output_file, verbose):
     if output_file is not None:
         with open(output_file, "w") as outfile:
             outfile.write(json.dumps(url_parser.test_logs))
@@ -302,13 +309,22 @@
             sys.stdout.write('\t- {0} {1} request for {2} to {3}\n'.format(
                 item.get('status_code'), item.get('verb'),
                 item.get('service'), item.get('url')))
+            if verbose:
+                sys.stdout.write('\t\t- request headers: {0}\n'.format(
+                    item.get('request_headers')))
+                sys.stdout.write('\t\t- request body: {0}\n'.format(
+                    item.get('request_body')))
+                sys.stdout.write('\t\t- response headers: {0}\n'.format(
+                    item.get('response_headers')))
+                sys.stdout.write('\t\t- response body: {0}\n'.format(
+                    item.get('response_body')))
         sys.stdout.write('\n')
 
 
 def entry_point():
     cl_args = ArgumentParser().parse_args()
     parser = parse(cl_args.subunit, cl_args.non_subunit_name, cl_args.ports)
-    output(parser, cl_args.output_file)
+    output(parser, cl_args.output_file, cl_args.verbose)
 
 
 if __name__ == "__main__":
diff --git a/tempest/cmd/verify_tempest_config.py b/tempest/cmd/verify_tempest_config.py
index 15af271..6c2fee8 100644
--- a/tempest/cmd/verify_tempest_config.py
+++ b/tempest/cmd/verify_tempest_config.py
@@ -205,10 +205,6 @@
 def verify_cinder_api_versions(os, update):
     # Check cinder api versions
     versions = _get_api_versions(os, 'cinder')
-    if (CONF.volume_feature_enabled.api_v1 !=
-            contains_version('v1.', versions)):
-        print_and_or_update('api_v1', 'volume-feature-enabled',
-                            not CONF.volume_feature_enabled.api_v1, update)
     if (CONF.volume_feature_enabled.api_v2 !=
             contains_version('v2.', versions)):
         print_and_or_update('api_v2', 'volume-feature-enabled',
@@ -283,6 +279,9 @@
     if not results.get(service):
         results[service] = {}
     extensions_opt = get_enabled_extensions(service)
+    if not extensions_opt:
+        LOG.info("'%s' has no api_extensions set.", service)
+        return results
     if extensions_opt[0] == 'all':
         results[service]['extensions'] = extensions
         return results
@@ -488,5 +487,6 @@
             traceback.print_exc()
             raise
 
+
 if __name__ == "__main__":
     main()
diff --git a/tempest/common/compute.py b/tempest/common/compute.py
index 638ad9b..f2730b3 100644
--- a/tempest/common/compute.py
+++ b/tempest/common/compute.py
@@ -165,7 +165,7 @@
 
     if volume_backed:
         volume_name = data_utils.rand_name(__name__ + '-volume')
-        volumes_client = clients.volumes_v2_client
+        volumes_client = clients.volumes_client_latest
         params = {'name': volume_name,
                   'imageRef': image_id,
                   'size': CONF.volume.volume_size}
@@ -289,7 +289,12 @@
 
 def create_websocket(url):
     url = urlparse.urlparse(url)
-    for res in socket.getaddrinfo(url.hostname, url.port,
+
+    # NOTE(mnaser): It is possible that there is no port specified, so fall
+    #               back to the default port based on the scheme.
+    port = url.port or (443 if url.scheme == 'https' else 80)
+
+    for res in socket.getaddrinfo(url.hostname, port,
                                   socket.AF_UNSPEC, socket.SOCK_STREAM):
         af, socktype, proto, _, sa = res
         client_socket = socket.socket(af, socktype, proto)
@@ -382,7 +387,12 @@
         """Upgrade the HTTP connection to a WebSocket and verify."""
         # The real request goes to the /websockify URI always
         reqdata = 'GET /websockify HTTP/1.1\r\n'
-        reqdata += 'Host: %s:%s\r\n' % (url.hostname, url.port)
+        reqdata += 'Host: %s' % url.hostname
+        # Add port only if we have one specified
+        if url.port:
+            reqdata += ':%s' % url.port
+        # Line-ending for Host header
+        reqdata += '\r\n'
         # Tell the HTTP Server to Upgrade the connection to a WebSocket
         reqdata += 'Upgrade: websocket\r\nConnection: Upgrade\r\n'
         # The token=xxx is sent as a Cookie not in the URI
diff --git a/tempest/common/credentials_factory.py b/tempest/common/credentials_factory.py
index 75db155..c6e5dcb 100644
--- a/tempest/common/credentials_factory.py
+++ b/tempest/common/credentials_factory.py
@@ -210,6 +210,7 @@
     except exceptions.InvalidConfiguration:
         return False
 
+
 # === Credentials
 
 # Type of credentials available from configuration
diff --git a/tempest/common/custom_matchers.py b/tempest/common/custom_matchers.py
index ed11b21..c702d88 100644
--- a/tempest/common/custom_matchers.py
+++ b/tempest/common/custom_matchers.py
@@ -225,9 +225,9 @@
             elif key in ('content-type', 'date', 'last-modified',
                          'x-copied-from-last-modified') and not value:
                 return InvalidFormat(key, value)
-            elif key == 'x-timestamp' and not re.match("^\d+\.?\d*\Z", value):
+            elif key == 'x-timestamp' and not re.match(r"^\d+\.?\d*\Z", value):
                 return InvalidFormat(key, value)
-            elif key == 'x-copied-from' and not re.match("\S+/\S+", value):
+            elif key == 'x-copied-from' and not re.match(r"\S+/\S+", value):
                 return InvalidFormat(key, value)
             elif key == 'x-trans-id' and \
                 not re.match("^tx[0-9a-f]{21}-[0-9a-f]{10}.*", value):
diff --git a/tempest/common/utils/__init__.py b/tempest/common/utils/__init__.py
index 225a713..167bf5b 100644
--- a/tempest/common/utils/__init__.py
+++ b/tempest/common/utils/__init__.py
@@ -40,6 +40,7 @@
         self.__dict__[attr] = attr_obj
         return attr_obj
 
+
 data_utils = DataUtils()
 
 
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 4b7ace2..8314e1e 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -61,11 +61,7 @@
                      "users. This option requires that OpenStack Identity "
                      "API admin credentials are known. If false, isolated "
                      "test cases and parallel execution, can still be "
-                     "achieved configuring a list of test accounts",
-                deprecated_opts=[cfg.DeprecatedOpt('allow_tenant_isolation',
-                                                   group='auth'),
-                                 cfg.DeprecatedOpt('allow_tenant_isolation',
-                                                   group='compute')]),
+                     "achieved configuring a list of test accounts"),
     cfg.ListOpt('tempest_roles',
                 help="Roles to assign to all users created by tempest",
                 default=[]),
@@ -104,6 +100,7 @@
                secret=True,
                deprecated_group='identity'),
     cfg.StrOpt('admin_domain_name',
+               default='Default',
                help="Admin domain name for authentication (Keystone V3)."
                     "The same domain applies to user and project",
                deprecated_group='identity'),
@@ -236,7 +233,16 @@
     cfg.BoolOpt('security_compliance',
                 default=False,
                 help='Does the environment have the security compliance '
-                     'settings enabled?')
+                     'settings enabled?'),
+    cfg.BoolOpt('project_tags',
+                default=False,
+                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',
@@ -403,6 +409,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 '
@@ -608,10 +618,14 @@
                      " for subnet creation"),
     cfg.StrOpt('port_vnic_type',
                choices=[None, 'normal', 'direct', 'macvtap'],
-               help="vnic_type to use when Launching instances"
+               help="vnic_type to use when launching instances"
                     " with pre-configured ports."
                     " Supported ports are:"
                     " ['normal','direct','macvtap']"),
+    cfg.DictOpt('port_profile',
+                default={},
+                help="port profile to use when launching instances"
+                     " with pre-configured ports."),
     cfg.ListOpt('default_network',
                 default=["1.0.0.0/16", "2.0.0.0/16"],
                 help="List of ip pools"
@@ -734,7 +748,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='',
@@ -822,19 +836,31 @@
                 help='A list of enabled volume extensions with a special '
                      'entry all which indicates every extension is enabled. '
                      'Empty list indicates all extensions are disabled'),
-    cfg.BoolOpt('api_v1',
-                default=False,
-                help="Is the v1 volume API enabled",
-                deprecated_for_removal=True,
-                deprecated_reason="The v1 volume API has been deprecated "
-                                  "since Juno release, and the API will be "
-                                  "removed."),
     cfg.BoolOpt('api_v2',
                 default=True,
-                help="Is the v2 volume API enabled"),
+                help="Is the v2 volume API enabled",
+                deprecated_for_removal=True,
+                deprecated_reason="The v2 volume API has been deprecated "
+                                  "since Pike release. Now Tempest run all "
+                                  "the volume tests against v2 or v3 API "
+                                  "based on CONF.volume.catalog_type which "
+                                  "makes this config option unusable. If "
+                                  "catalog_type is volumev2, then all the "
+                                  "volume tests will run against v2 API. "
+                                  "Use ``CONF.volume.catalog_type`` to run "
+                                  "the Tempest against volume v2 or v3 API"),
     cfg.BoolOpt('api_v3',
                 default=True,
-                help="Is the v3 volume API enabled"),
+                help="Is the v3 volume API enabled",
+                deprecated_for_removal=True,
+                deprecated_reason="Tempest run all the volume tests against "
+                                  "v2 or v3 API based on "
+                                  "CONF.volume.catalog_type which makes this "
+                                  "config option unusable. If catalog_type is "
+                                  "volumev3 which is default, then all the "
+                                  "volume tests will run against v3 API. "
+                                  "Use ``CONF.volume.catalog_type`` to run "
+                                  "the Tempest against volume v2 or v3 API"),
     cfg.BoolOpt('extend_attached_volume',
                 default=False,
                 help='Does the cloud support extending the size of a volume '
@@ -1060,7 +1086,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..2c40cb1 100644
--- a/tempest/hacking/checks.py
+++ b/tempest/hacking/checks.py
@@ -15,7 +15,7 @@
 import os
 import re
 
-import pep8
+import pycodestyle
 
 
 PYTHON_CLIENTS = ['cinder', 'glance', 'keystone', 'nova', 'swift', 'neutron',
@@ -34,6 +34,9 @@
 METHOD_DELETE_RESOURCE = re.compile(r"^\s*def delete_.+")
 CLASS = re.compile(r"^class .+")
 EX_ATTRIBUTE = re.compile(r'(\s+|\()(e|ex|exc|exception).message(\s+|\))')
+NEGATIVE_TEST_DECORATOR = re.compile(
+    r'\s*@decorators\.attr\(type=.*negative.*\)')
+_HAVE_NEGATIVE_DECORATOR = False
 
 
 def import_no_clients_in_api_and_scenario_tests(physical_line, filename):
@@ -66,7 +69,7 @@
 
 def no_setup_teardown_class_for_tests(physical_line, filename):
 
-    if pep8.noqa(physical_line):
+    if pycodestyle.noqa(physical_line):
         return
 
     if 'tempest/test.py' in filename or 'tempest/lib/' in filename:
@@ -161,7 +164,7 @@
     if not METHOD.match(physical_line):
         return False
 
-    if pep8.noqa(physical_line):
+    if pycodestyle.noqa(physical_line):
         return False
 
     return True
@@ -228,12 +231,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 +269,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)
@@ -284,13 +287,13 @@
     if 'tempest/api/' not in filename:
         return
 
-    if pep8.noqa(physical_line):
+    if pycodestyle.noqa(physical_line):
         return
 
-    if not re.match('class .*Test.*\(.*Admin.*\):', logical_line):
+    if not re.match(r'class .*Test.*\(.*Admin.*\):', logical_line):
         return
 
-    if not re.match('.\/tempest\/api\/.*\/admin\/.*', filename):
+    if not re.match(r'.\/tempest\/api\/.*\/admin\/.*', filename):
         msg = 'T115: All admin tests should exist under admin path.'
         yield(0, msg)
 
@@ -306,6 +309,29 @@
         yield(0, msg)
 
 
+def negative_test_attribute_always_applied_to_negative_tests(physical_line,
+                                                             filename):
+    """Check ``@decorators.attr(type=['negative'])`` applied to negative tests.
+
+    T117
+    """
+    global _HAVE_NEGATIVE_DECORATOR
+
+    if re.match(r'.\/tempest\/api\/.*_negative.*', filename):
+
+        if NEGATIVE_TEST_DECORATOR.match(physical_line):
+            _HAVE_NEGATIVE_DECORATOR = True
+            return
+
+        if TEST_DEFINITION.match(physical_line):
+            if not _HAVE_NEGATIVE_DECORATOR:
+                return (
+                    0, "T117: Must apply `@decorators.attr(type=['negative'])`"
+                       " to all negative API tests"
+                )
+            _HAVE_NEGATIVE_DECORATOR = False
+
+
 def factory(register):
     register(import_no_clients_in_api_and_scenario_tests)
     register(scenario_tests_need_service_tags)
@@ -322,3 +348,4 @@
     register(use_rand_uuid_instead_of_uuid4)
     register(dont_put_admin_tests_on_nonadmin_path)
     register(unsupported_exception_attribute_PY3)
+    register(negative_test_attribute_always_applied_to_negative_tests)
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..bd5e3d6 100644
--- a/tempest/lib/api_schema/response/compute/v2_1/flavors.py
+++ b/tempest/lib/api_schema/response/compute/v2_1/flavors.py
@@ -60,7 +60,7 @@
     },
     'additionalProperties': False,
     # 'OS-FLV-DISABLED', 'os-flavor-access', 'rxtx_factor' and
-    # 'OS-FLV-EXT-DATA' are API extensions. So they are not 'required'.
+    # 'OS-FLV-EXT-DATA' are API extensions, so they are not 'required'.
     'required': ['name', 'links', 'ram', 'vcpus', 'swap', 'disk', 'id']
 }
 
@@ -74,7 +74,7 @@
                 'items': common_flavor_info
             },
             # NOTE(gmann): flavors_links attribute is not necessary
-            # to be present always So it is not 'required'.
+            # to be present always so it is not 'required'.
             'flavors_links': parameter_types.links
         },
         'additionalProperties': False,
@@ -82,11 +82,7 @@
     }
 }
 
-unset_flavor_extra_specs = {
-    '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_1/flavors_extra_specs.py b/tempest/lib/api_schema/response/compute/v2_1/flavors_extra_specs.py
index a438d48..3aa1eda 100644
--- a/tempest/lib/api_schema/response/compute/v2_1/flavors_extra_specs.py
+++ b/tempest/lib/api_schema/response/compute/v2_1/flavors_extra_specs.py
@@ -20,7 +20,7 @@
             'extra_specs': {
                 'type': 'object',
                 'patternProperties': {
-                    '^[a-zA-Z0-9_\-\. :]+$': {'type': 'string'}
+                    r'^[a-zA-Z0-9_\-\. :]+$': {'type': 'string'}
                 }
             }
         },
@@ -29,12 +29,16 @@
     }
 }
 
+unset_flavor_extra_specs = {
+    'status_code': [200]
+}
+
 set_get_flavor_extra_specs_key = {
     'status_code': [200],
     'response_body': {
         'type': 'object',
         'patternProperties': {
-            '^[a-zA-Z0-9_\-\. :]+$': {'type': 'string'}
+            r'^[a-zA-Z0-9_\-\. :]+$': {'type': 'string'}
         }
     }
 }
diff --git a/tempest/lib/api_schema/response/compute/v2_1/server_groups.py b/tempest/lib/api_schema/response/compute/v2_1/server_groups.py
new file mode 100644
index 0000000..01db20b
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_1/server_groups.py
@@ -0,0 +1,65 @@
+# Copyright 2017 NTT 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.
+
+common_server_group = {
+    'type': 'object',
+    'properties': {
+        'id': {'type': 'string'},
+        'name': {'type': 'string'},
+        'policies': {
+            'type': 'array',
+            'items': {'type': 'string'}
+        },
+        # 'members' attribute contains the array of instance's UUID of
+        # instances present in server group
+        'members': {
+            'type': 'array',
+            'items': {'type': 'string'}
+        },
+        'metadata': {'type': 'object'}
+    },
+    'additionalProperties': False,
+    'required': ['id', 'name', 'policies', 'members', 'metadata']
+}
+
+create_show_server_group = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'server_group': common_server_group
+        },
+        'additionalProperties': False,
+        'required': ['server_group']
+    }
+}
+
+delete_server_group = {
+    'status_code': [204]
+}
+
+list_server_groups = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'server_groups': {
+                'type': 'array',
+                'items': common_server_group
+            }
+        },
+        'additionalProperties': False,
+        'required': ['server_groups']
+    }
+}
diff --git a/tempest/lib/api_schema/response/compute/v2_1/servers.py b/tempest/lib/api_schema/response/compute/v2_1/servers.py
index 2954de0..3300298 100644
--- a/tempest/lib/api_schema/response/compute/v2_1/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_1/servers.py
@@ -345,58 +345,6 @@
     }
 }
 
-common_server_group = {
-    'type': 'object',
-    'properties': {
-        'id': {'type': 'string'},
-        'name': {'type': 'string'},
-        'policies': {
-            'type': 'array',
-            'items': {'type': 'string'}
-        },
-        # 'members' attribute contains the array of instance's UUID of
-        # instances present in server group
-        'members': {
-            'type': 'array',
-            'items': {'type': 'string'}
-        },
-        'metadata': {'type': 'object'}
-    },
-    'additionalProperties': False,
-    'required': ['id', 'name', 'policies', 'members', 'metadata']
-}
-
-create_show_server_group = {
-    'status_code': [200],
-    'response_body': {
-        'type': 'object',
-        'properties': {
-            'server_group': common_server_group
-        },
-        'additionalProperties': False,
-        'required': ['server_group']
-    }
-}
-
-delete_server_group = {
-    'status_code': [204]
-}
-
-list_server_groups = {
-    'status_code': [200],
-    'response_body': {
-        'type': 'object',
-        'properties': {
-            'server_groups': {
-                'type': 'array',
-                'items': common_server_group
-            }
-        },
-        'additionalProperties': False,
-        'required': ['server_groups']
-    }
-}
-
 instance_actions = {
     'type': 'object',
     'properties': {
@@ -430,8 +378,9 @@
             'traceback': {'type': ['string', 'null']}
         },
         'additionalProperties': False,
-        'required': ['event', 'start_time', 'finish_time', 'result',
-                     'traceback']
+        # NOTE(zhufl): events.traceback can only be seen by admin users
+        # with default policy.json, so it shouldn't be a required field.
+        'required': ['event', 'start_time', 'finish_time', 'result']
     }
 }
 
diff --git a/tempest/lib/api_schema/response/compute/v2_11/services.py b/tempest/lib/api_schema/response/compute/v2_11/services.py
index 18b833b..9ece1f9 100644
--- a/tempest/lib/api_schema/response/compute/v2_11/services.py
+++ b/tempest/lib/api_schema/response/compute/v2_11/services.py
@@ -44,3 +44,10 @@
         'required': ['service']
     }
 }
+
+# **** Schemas unchanged in microversion 2.11 since microversion 2.1 ****
+# Note(felipemonteiro): 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.
+enable_disable_service = copy.deepcopy(services.enable_disable_service)
+disable_log_reason = copy.deepcopy(services.disable_log_reason)
diff --git a/tempest/lib/api_schema/response/compute/v2_13/servers.py b/tempest/lib/api_schema/response/compute/v2_13/server_groups.py
similarity index 69%
rename from tempest/lib/api_schema/response/compute/v2_13/servers.py
rename to tempest/lib/api_schema/response/compute/v2_13/server_groups.py
index a90f3e4..5cb4241 100644
--- a/tempest/lib/api_schema/response/compute/v2_13/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_13/server_groups.py
@@ -14,21 +14,24 @@
 
 import copy
 
-from tempest.lib.api_schema.response.compute.v2_1 import servers
+from tempest.lib.api_schema.response.compute.v2_1 import server_groups
 
-
-common_server_group = copy.deepcopy(servers.common_server_group)
+# Compute microversion 2.13:
+# 1. New attributes in 'server_group' dict.
+#      'project_id', 'user_id'
+common_server_group = copy.deepcopy(server_groups.common_server_group)
 common_server_group['properties']['project_id'] = {'type': 'string'}
 common_server_group['properties']['user_id'] = {'type': 'string'}
 common_server_group['required'].append('project_id')
 common_server_group['required'].append('user_id')
 
-create_show_server_group = copy.deepcopy(servers.create_show_server_group)
+create_show_server_group = copy.deepcopy(
+    server_groups.create_show_server_group)
 create_show_server_group['response_body']['properties'][
     'server_group'] = common_server_group
 
-delete_server_group = copy.deepcopy(servers.delete_server_group)
+delete_server_group = copy.deepcopy(server_groups.delete_server_group)
 
-list_server_groups = copy.deepcopy(servers.list_server_groups)
+list_server_groups = copy.deepcopy(server_groups.list_server_groups)
 list_server_groups['response_body']['properties']['server_groups'][
     'items'] = common_server_group
diff --git a/tempest/lib/api_schema/response/compute/v2_16/servers.py b/tempest/lib/api_schema/response/compute/v2_16/servers.py
index 3eb658f..72b84f5 100644
--- a/tempest/lib/api_schema/response/compute/v2_16/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_16/servers.py
@@ -122,7 +122,7 @@
     '^[a-zA-Z0-9-_.]+$']['items']['properties'].update({
         'OS-EXT-IPS:type': {'type': 'string'},
         'OS-EXT-IPS-MAC:mac_addr': parameter_types.mac_address})
-# NOTE(gmann)dd: Update OS-EXT-IPS:type and OS-EXT-IPS-MAC:mac_addr
+# NOTE(gmann): Update OS-EXT-IPS:type and OS-EXT-IPS-MAC:mac_addr
 # attributes in server address. Those are API extension,
 # and some environments return a response without
 # these attributes. So they are not 'required'.
@@ -157,4 +157,14 @@
     }
 }
 
+# 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.
+# ****** Schemas unchanged since microversion 2.9 ******
 list_servers = copy.deepcopy(servers.list_servers)
+update_server = copy.deepcopy(servers.update_server)
+rebuild_server = copy.deepcopy(servers.rebuild_server)
+rebuild_server_with_admin_pass = copy.deepcopy(
+    servers.rebuild_server_with_admin_pass)
+show_server_diagnostics = copy.deepcopy(servers.show_server_diagnostics)
+get_remote_consoles = copy.deepcopy(servers.get_remote_consoles)
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..e3e8ad1 100644
--- a/tempest/lib/api_schema/response/compute/v2_19/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_19/servers.py
@@ -14,12 +14,12 @@
 
 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
 
-list_servers = copy.deepcopy(serversv216.list_servers)
-
+# Compute microversion 2.19:
+# 1. New attributes in 'server' dict.
+#      'description'
 get_server = copy.deepcopy(serversv216.get_server)
 get_server['response_body']['properties']['server'][
     'properties'].update({'description': {'type': ['string', 'null']}})
@@ -32,21 +32,29 @@
 list_servers_detail['response_body']['properties']['servers']['items'][
     'required'].append('description')
 
-update_server = copy.deepcopy(serversv21.update_server)
+update_server = copy.deepcopy(serversv216.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(serversv216.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)
+    serversv216.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'][
     'required'].append('description')
+
+# 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.
+# ****** Schemas unchanged since microversion 2.16 ******
+list_servers = copy.deepcopy(serversv216.list_servers)
+show_server_diagnostics = copy.deepcopy(serversv216.show_server_diagnostics)
+get_remote_consoles = copy.deepcopy(serversv216.get_remote_consoles)
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..8e62dc3 100644
--- a/tempest/lib/api_schema/response/compute/v2_26/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_26/servers.py
@@ -15,7 +15,6 @@
 
 import copy
 
-from tempest.lib.api_schema.response.compute.v2_1 import servers as servers21
 from tempest.lib.api_schema.response.compute.v2_19 import servers as servers219
 
 # The 2.26 microversion changes the server GET and (detailed) LIST responses to
@@ -43,9 +42,24 @@
 list_servers_detail['response_body']['properties']['servers']['items'][
     'required'].append('tags')
 
-# list response schema wasn't changed for v2.26 so use v2.1
+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')
 
-list_servers = copy.deepcopy(servers21.list_servers)
+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_tags = {
     'status_code': [200],
@@ -79,3 +93,11 @@
 }
 
 delete_tag = {'status_code': [204]}
+
+# 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.
+# ****** Schemas unchanged since microversion 2.19 ******
+list_servers = copy.deepcopy(servers219.list_servers)
+show_server_diagnostics = copy.deepcopy(servers219.show_server_diagnostics)
+get_remote_consoles = copy.deepcopy(servers219.get_remote_consoles)
diff --git a/tempest/tests/lib/services/volume/v2/__init__.py b/tempest/lib/api_schema/response/compute/v2_28/__init__.py
similarity index 100%
copy from tempest/tests/lib/services/volume/v2/__init__.py
copy to tempest/lib/api_schema/response/compute/v2_28/__init__.py
diff --git a/tempest/lib/api_schema/response/compute/v2_28/hypervisors.py b/tempest/lib/api_schema/response/compute/v2_28/hypervisors.py
new file mode 100644
index 0000000..8ea9ff8
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_28/hypervisors.py
@@ -0,0 +1,40 @@
+# Copyright 2018 ZTE 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 copy
+
+from tempest.lib.api_schema.response.compute.v2_1 \
+    import hypervisors as hypervisorsv21
+
+# hypervisor.cpu_info change from string to JSON object.
+hypervisor_detail = copy.deepcopy(hypervisorsv21.hypervisor_detail)
+hypervisor_detail['properties'].update({'cpu_info': {'type': 'object'}})
+
+list_hypervisors_detail = copy.deepcopy(hypervisorsv21.list_hypervisors_detail)
+list_hypervisors_detail['response_body']['properties']['hypervisors'].update(
+    {'items': hypervisor_detail})
+
+get_hypervisor = copy.deepcopy(hypervisorsv21.get_hypervisor)
+get_hypervisor['response_body']['properties'].update(
+    {'hypervisor': hypervisor_detail})
+
+# NOTE(zhufl): 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.
+# ****** Schemas unchanged since microversion 2.1 ***
+get_hypervisor_statistics = \
+    copy.deepcopy(hypervisorsv21.get_hypervisor_statistics)
+list_search_hypervisors = copy.deepcopy(hypervisorsv21.list_search_hypervisors)
+get_hypervisor_uptime = copy.deepcopy(hypervisorsv21.get_hypervisor_uptime)
+get_hypervisors_servers = copy.deepcopy(hypervisorsv21.get_hypervisors_servers)
diff --git a/tempest/lib/api_schema/response/compute/v2_3/servers.py b/tempest/lib/api_schema/response/compute/v2_3/servers.py
index f24103e..18fb352 100644
--- a/tempest/lib/api_schema/response/compute/v2_3/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_3/servers.py
@@ -128,7 +128,7 @@
     '^[a-zA-Z0-9-_.]+$']['items']['properties'].update({
         'OS-EXT-IPS:type': {'type': 'string'},
         'OS-EXT-IPS-MAC:mac_addr': parameter_types.mac_address})
-# NOTE(gmann)dd: Update OS-EXT-IPS:type and OS-EXT-IPS-MAC:mac_addr
+# NOTE(gmann): Update OS-EXT-IPS:type and OS-EXT-IPS-MAC:mac_addr
 # attributes in server address. Those are API extension,
 # and some environments return a response without
 # these attributes. So they are not 'required'.
@@ -163,4 +163,13 @@
     }
 }
 
+# NOTE: 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.
+# ****** Schemas unchanged since microversion 2.1 ***
 list_servers = copy.deepcopy(servers.list_servers)
+update_server = copy.deepcopy(servers.update_server)
+rebuild_server = copy.deepcopy(servers.rebuild_server)
+rebuild_server_with_admin_pass = copy.deepcopy(
+    servers.rebuild_server_with_admin_pass)
+show_server_diagnostics = copy.deepcopy(servers.show_server_diagnostics)
diff --git a/tempest/tests/lib/services/volume/v2/__init__.py b/tempest/lib/api_schema/response/compute/v2_36/__init__.py
similarity index 100%
copy from tempest/tests/lib/services/volume/v2/__init__.py
copy to tempest/lib/api_schema/response/compute/v2_36/__init__.py
diff --git a/tempest/lib/api_schema/response/compute/v2_36/limits.py b/tempest/lib/api_schema/response/compute/v2_36/limits.py
new file mode 100644
index 0000000..8e94690
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_36/limits.py
@@ -0,0 +1,35 @@
+# Copyright 2018 ZTE 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 copy
+
+from tempest.lib.api_schema.response.compute.v2_1 import limits as limitv21
+
+# Compute microversion 2.36:
+# remove attributes in get_limit:
+#    'maxSecurityGroupRules',
+#    'maxSecurityGroups',
+#    'maxTotalFloatingIps',
+#    'totalFloatingIpsUsed',
+#    'totalSecurityGroupsUsed'
+
+get_limit = copy.deepcopy(limitv21.get_limit)
+
+for item in ['maxSecurityGroupRules', 'maxSecurityGroups',
+             'maxTotalFloatingIps', 'totalFloatingIpsUsed',
+             'totalSecurityGroupsUsed']:
+    get_limit['response_body']['properties']['limits']['properties'][
+        'absolute']['properties'].pop(item)
+    get_limit['response_body']['properties']['limits']['properties'][
+        'absolute']['required'].remove(item)
diff --git a/tempest/lib/api_schema/response/compute/v2_36/quotas.py b/tempest/lib/api_schema/response/compute/v2_36/quotas.py
new file mode 100644
index 0000000..f191ed1
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_36/quotas.py
@@ -0,0 +1,54 @@
+# Copyright 2018 ZTE 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 copy
+
+from tempest.lib.api_schema.response.compute.v2_1 import quotas as quotasv21
+
+# Compute microversion 2.36:
+# remove attributes in quota_set:
+#    'fixed_ips',
+#    'floating_ips',
+#    'security_group_rules',
+#    'security_groups'
+
+remove_item_list = ['fixed_ips', 'floating_ips',
+                    'security_group_rules', 'security_groups']
+
+update_quota_set = copy.deepcopy(quotasv21.update_quota_set)
+for item in remove_item_list:
+    update_quota_set['response_body']['properties']['quota_set'][
+        'properties'].pop(item)
+    update_quota_set['response_body']['properties']['quota_set'][
+        'required'].remove(item)
+
+get_quota_set = copy.deepcopy(quotasv21.get_quota_set)
+for item in remove_item_list:
+    get_quota_set['response_body']['properties']['quota_set'][
+        'properties'].pop(item)
+    get_quota_set['response_body']['properties']['quota_set'][
+        'required'].remove(item)
+
+get_quota_set_details = copy.deepcopy(quotasv21.get_quota_set_details)
+for item in remove_item_list:
+    get_quota_set_details['response_body']['properties']['quota_set'][
+        'properties'].pop(item)
+    get_quota_set_details['response_body']['properties']['quota_set'][
+        'required'].remove(item)
+
+# NOTE(zhufl): 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.
+# ****** Schemas unchanged since microversion 2.1 ***
+delete_quota = copy.deepcopy(quotasv21.delete_quota)
diff --git a/tempest/tests/lib/services/volume/v2/__init__.py b/tempest/lib/api_schema/response/compute/v2_39/__init__.py
similarity index 100%
copy from tempest/tests/lib/services/volume/v2/__init__.py
copy to tempest/lib/api_schema/response/compute/v2_39/__init__.py
diff --git a/tempest/lib/api_schema/response/compute/v2_39/limits.py b/tempest/lib/api_schema/response/compute/v2_39/limits.py
new file mode 100644
index 0000000..3df6616
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_39/limits.py
@@ -0,0 +1,29 @@
+# Copyright 2018 ZTE 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 copy
+
+from tempest.lib.api_schema.response.compute.v2_36 import limits as limitv236
+
+# Compute microversion 2.39:
+# remove attributes in get_limit:
+#    'maxImageMeta'
+
+get_limit = copy.deepcopy(limitv236.get_limit)
+
+get_limit['response_body']['properties']['limits']['properties']['absolute'][
+    'properties'].pop('maxImageMeta')
+
+get_limit['response_body']['properties']['limits']['properties']['absolute'][
+    'required'].remove('maxImageMeta')
diff --git a/tempest/tests/lib/services/volume/v2/__init__.py b/tempest/lib/api_schema/response/compute/v2_41/__init__.py
similarity index 100%
copy from tempest/tests/lib/services/volume/v2/__init__.py
copy to tempest/lib/api_schema/response/compute/v2_41/__init__.py
diff --git a/tempest/lib/api_schema/response/compute/v2_41/aggregates.py b/tempest/lib/api_schema/response/compute/v2_41/aggregates.py
new file mode 100644
index 0000000..036bd83
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_41/aggregates.py
@@ -0,0 +1,54 @@
+# Copyright 2018 ZTE 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 copy
+
+from tempest.lib.api_schema.response.compute.v2_1 import aggregates
+
+# 'uuid' of an aggregate is returned in microversion 2.41
+aggregate_for_create = copy.deepcopy(aggregates.aggregate_for_create)
+aggregate_for_create['properties'].update({'uuid': {'type': 'string',
+                                                    'format': 'uuid'}})
+aggregate_for_create['required'].append('uuid')
+
+common_aggregate_info = copy.deepcopy(aggregates.common_aggregate_info)
+common_aggregate_info['properties'].update({'uuid': {'type': 'string',
+                                                     'format': 'uuid'}})
+common_aggregate_info['required'].append('uuid')
+
+list_aggregates = copy.deepcopy(aggregates.list_aggregates)
+list_aggregates['response_body']['properties']['aggregates'].update(
+    {'items': common_aggregate_info})
+
+get_aggregate = copy.deepcopy(aggregates.get_aggregate)
+get_aggregate['response_body']['properties'].update(
+    {'aggregate': common_aggregate_info})
+
+aggregate_set_metadata = get_aggregate
+
+update_aggregate = copy.deepcopy(aggregates.update_aggregate)
+update_aggregate['response_body']['properties'].update(
+    {'aggregate': common_aggregate_info})
+
+create_aggregate = copy.deepcopy(aggregates.create_aggregate)
+create_aggregate['response_body']['properties'].update(
+    {'aggregate': aggregate_for_create})
+
+aggregate_add_remove_host = get_aggregate
+
+# NOTE(zhufl): 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.
+# ****** Schemas unchanged since microversion 2.1 ***
+delete_aggregate = copy.deepcopy(aggregates.delete_aggregate)
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..0fbacd3 100644
--- a/tempest/lib/api_schema/response/compute/v2_47/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_47/servers.py
@@ -26,7 +26,7 @@
         'extra_specs': {
             'type': 'object',
             'patternProperties': {
-                '^[a-zA-Z0-9_\-\. :]+$': {'type': 'string'}
+                r'^[a-zA-Z0-9_\-\. :]+$': {'type': 'string'}
             }
         }
     },
@@ -37,3 +37,32 @@
 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})
+
+# NOTE(zhufl): 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.
+show_server_diagnostics = copy.deepcopy(servers226.show_server_diagnostics)
+get_remote_consoles = copy.deepcopy(servers226.get_remote_consoles)
+list_tags = copy.deepcopy(servers226.list_tags)
+update_all_tags = copy.deepcopy(servers226.update_all_tags)
+delete_all_tags = copy.deepcopy(servers226.delete_all_tags)
+check_tag_existence = copy.deepcopy(servers226.check_tag_existence)
+update_tag = copy.deepcopy(servers226.update_tag)
+delete_tag = copy.deepcopy(servers226.delete_tag)
+list_servers = copy.deepcopy(servers226.list_servers)
diff --git a/tempest/lib/api_schema/response/compute/v2_48/servers.py b/tempest/lib/api_schema/response/compute/v2_48/servers.py
index 5904758..84b5a2a 100644
--- a/tempest/lib/api_schema/response/compute/v2_48/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_48/servers.py
@@ -112,4 +112,20 @@
     }
 }
 
+# NOTE(zhufl): 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.
+list_servers = copy.deepcopy(servers247.list_servers)
+get_remote_consoles = copy.deepcopy(servers247.get_remote_consoles)
+list_tags = copy.deepcopy(servers247.list_tags)
+update_all_tags = copy.deepcopy(servers247.update_all_tags)
+delete_all_tags = copy.deepcopy(servers247.delete_all_tags)
+check_tag_existence = copy.deepcopy(servers247.check_tag_existence)
+update_tag = copy.deepcopy(servers247.update_tag)
+delete_tag = copy.deepcopy(servers247.delete_tag)
 get_server = copy.deepcopy(servers247.get_server)
+list_servers_detail = copy.deepcopy(servers247.list_servers_detail)
+update_server = copy.deepcopy(servers247.update_server)
+rebuild_server = copy.deepcopy(servers247.rebuild_server)
+rebuild_server_with_admin_pass = copy.deepcopy(
+    servers247.rebuild_server_with_admin_pass)
diff --git a/tempest/tests/lib/services/volume/v2/__init__.py b/tempest/lib/api_schema/response/compute/v2_53/__init__.py
similarity index 100%
copy from tempest/tests/lib/services/volume/v2/__init__.py
copy to tempest/lib/api_schema/response/compute/v2_53/__init__.py
diff --git a/tempest/lib/api_schema/response/compute/v2_53/services.py b/tempest/lib/api_schema/response/compute/v2_53/services.py
new file mode 100644
index 0000000..97b0c72
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_53/services.py
@@ -0,0 +1,71 @@
+# 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 copy
+
+from tempest.lib.api_schema.response.compute.v2_1 import parameter_types
+from tempest.lib.api_schema.response.compute.v2_11 import services \
+    as servicesv211
+
+# ***************** Schemas changed in microversion 2.53 *****************
+
+# NOTE(felipemonteiro): This is schema for microversion 2.53 which includes:
+#
+# * changing the service 'id' to 'string' type only
+# * adding update_service which supersedes enable_service, disable_service,
+#   disable_log_reason, update_forced_down.
+
+list_services = copy.deepcopy(servicesv211.list_services)
+# The ID of the service is a uuid, so v2.1 pattern does not apply.
+list_services['response_body']['properties']['services']['items'][
+    'properties']['id'] = {'type': 'string', 'format': 'uuid'}
+
+update_service = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'service': {
+                'type': 'object',
+                'properties': {
+                    'id': {'type': 'string', 'format': 'uuid'},
+                    'binary': {'type': 'string'},
+                    # disabled_reason can be null when status is enabled.
+                    'disabled_reason': {'type': ['string', 'null']},
+                    'host': {'type': 'string'},
+                    'state': {'type': 'string'},
+                    'status': {'type': 'string'},
+                    'updated_at': parameter_types.date_time,
+                    'zone': {'type': 'string'},
+                    'forced_down': {'type': 'boolean'}
+                },
+                'additionalProperties': False,
+                'required': ['id', 'binary', 'disabled_reason', 'host',
+                             'state', 'status', 'updated_at', 'zone',
+                             'forced_down']
+            }
+        },
+        'additionalProperties': False,
+        'required': ['service']
+    }
+}
+
+# **** Schemas unchanged in microversion 2.53 since microversion 2.11 ****
+# Note(felipemonteiro): 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.
+enable_disable_service = copy.deepcopy(servicesv211.enable_disable_service)
+update_forced_down = copy.deepcopy(servicesv211.update_forced_down)
+disable_log_reason = copy.deepcopy(servicesv211.disable_log_reason)
diff --git a/tempest/tests/lib/services/volume/v2/__init__.py b/tempest/lib/api_schema/response/compute/v2_54/__init__.py
similarity index 100%
copy from tempest/tests/lib/services/volume/v2/__init__.py
copy to 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..099e1b8
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_54/servers.py
@@ -0,0 +1,57 @@
+#    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_48 import servers as servers248
+# ****** 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(servers248.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(
+    servers248.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')
+
+# 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.
+# ****** Schemas unchanged in microversion 2.54 since microversion 2.48 ***
+get_server = copy.deepcopy(servers248.get_server)
+list_servers_detail = copy.deepcopy(servers248.list_servers_detail)
+update_server = copy.deepcopy(servers248.update_server)
+list_servers = copy.deepcopy(servers248.list_servers)
+show_server_diagnostics = copy.deepcopy(servers248.show_server_diagnostics)
+get_remote_consoles = copy.deepcopy(servers248.get_remote_consoles)
+list_tags = copy.deepcopy(servers248.list_tags)
+update_all_tags = copy.deepcopy(servers248.update_all_tags)
+delete_all_tags = copy.deepcopy(servers248.delete_all_tags)
+check_tag_existence = copy.deepcopy(servers248.check_tag_existence)
+update_tag = copy.deepcopy(servers248.update_tag)
+delete_tag = copy.deepcopy(servers248.delete_tag)
diff --git a/tempest/tests/lib/services/volume/v2/__init__.py b/tempest/lib/api_schema/response/compute/v2_55/__init__.py
similarity index 100%
copy from tempest/tests/lib/services/volume/v2/__init__.py
copy to 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..554f43b
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_55/flavors.py
@@ -0,0 +1,120 @@
+# 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.
+import copy
+
+from tempest.lib.api_schema.response.compute.v2_1 import flavors as flavorsv21
+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']
+    }
+}
+
+# Note(zhufl): 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.
+# ****** Schemas unchanged since microversion 2.1 ***
+delete_flavor = copy.deepcopy(flavorsv21.delete_flavor)
diff --git a/tempest/tests/lib/services/volume/v2/__init__.py b/tempest/lib/api_schema/response/compute/v2_57/__init__.py
similarity index 100%
copy from tempest/tests/lib/services/volume/v2/__init__.py
copy to tempest/lib/api_schema/response/compute/v2_57/__init__.py
diff --git a/tempest/lib/api_schema/response/compute/v2_57/limits.py b/tempest/lib/api_schema/response/compute/v2_57/limits.py
new file mode 100644
index 0000000..dcb8b3d
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_57/limits.py
@@ -0,0 +1,30 @@
+# Copyright 2018 ZTE 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 copy
+
+from tempest.lib.api_schema.response.compute.v2_39 import limits as limitv239
+
+# Compute microversion 2.57:
+# remove attributes in get_limit:
+#    'maxPersonality',
+#    'maxPersonalitySize'
+
+get_limit = copy.deepcopy(limitv239.get_limit)
+
+for item in ['maxPersonality', 'maxPersonalitySize']:
+    get_limit['response_body']['properties']['limits']['properties'][
+        'absolute']['properties'].pop(item)
+    get_limit['response_body']['properties']['limits']['properties'][
+        'absolute']['required'].remove(item)
diff --git a/tempest/lib/api_schema/response/compute/v2_57/quotas.py b/tempest/lib/api_schema/response/compute/v2_57/quotas.py
new file mode 100644
index 0000000..4664a1a
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_57/quotas.py
@@ -0,0 +1,53 @@
+# Copyright 2018 ZTE 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 copy
+
+from tempest.lib.api_schema.response.compute.v2_36 import quotas as quotasv236
+
+# Compute microversion 2.57:
+# remove attributes in quota_set:
+#    'injected_file_content_bytes',
+#    'injected_file_path_bytes',
+#    'injected_files'
+
+remove_item_list = ['injected_file_content_bytes', 'injected_file_path_bytes',
+                    'injected_files']
+
+update_quota_set = copy.deepcopy(quotasv236.update_quota_set)
+for item in remove_item_list:
+    update_quota_set['response_body']['properties']['quota_set'][
+        'properties'].pop(item)
+    update_quota_set['response_body']['properties']['quota_set'][
+        'required'].remove(item)
+
+get_quota_set = copy.deepcopy(quotasv236.get_quota_set)
+for item in remove_item_list:
+    get_quota_set['response_body']['properties']['quota_set'][
+        'properties'].pop(item)
+    get_quota_set['response_body']['properties']['quota_set'][
+        'required'].remove(item)
+
+get_quota_set_details = copy.deepcopy(quotasv236.get_quota_set_details)
+for item in remove_item_list:
+    get_quota_set_details['response_body']['properties']['quota_set'][
+        'properties'].pop(item)
+    get_quota_set_details['response_body']['properties']['quota_set'][
+        'required'].remove(item)
+
+# NOTE(zhufl): 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.
+# ****** Schemas unchanged since microversion 2.1 ***
+delete_quota = copy.deepcopy(quotasv236.delete_quota)
diff --git a/tempest/lib/api_schema/response/compute/v2_57/servers.py b/tempest/lib/api_schema/response/compute/v2_57/servers.py
new file mode 100644
index 0000000..0099a2b
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_57/servers.py
@@ -0,0 +1,61 @@
+#    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_54 import servers as servers254
+# ****** Schemas changed in microversion 2.57 *****************
+
+# Note(gmann): This is schema for microversion 2.57 which includes the
+# 'user_data' in the Response body of the following APIs:
+#    - ``POST '/servers/{server_id}/action (rebuild)``
+
+user_data = {
+    'oneOf': [
+        {
+            'type': 'string',
+            'format': 'base64',
+            'maxLength': 65535
+        },
+        {'type': 'null'}
+    ]
+}
+
+rebuild_server = copy.deepcopy(servers254.rebuild_server)
+rebuild_server['response_body']['properties']['server'][
+    'properties'].update({'user_data': user_data})
+rebuild_server['response_body']['properties']['server'][
+    'required'].append('user_data')
+
+rebuild_server_with_admin_pass = copy.deepcopy(
+    servers254.rebuild_server_with_admin_pass)
+rebuild_server_with_admin_pass['response_body']['properties']['server'][
+    'properties'].update({'user_data': user_data})
+rebuild_server_with_admin_pass['response_body']['properties']['server'][
+    'required'].append('user_data')
+
+# 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.
+# ****** Schemas unchanged in microversion 2.57 since microversion 2.54 ***
+get_server = copy.deepcopy(servers254.get_server)
+list_servers_detail = copy.deepcopy(servers254.list_servers_detail)
+update_server = copy.deepcopy(servers254.update_server)
+list_servers = copy.deepcopy(servers254.list_servers)
+show_server_diagnostics = copy.deepcopy(servers254.show_server_diagnostics)
+get_remote_consoles = copy.deepcopy(servers254.get_remote_consoles)
+list_tags = copy.deepcopy(servers254.list_tags)
+update_all_tags = copy.deepcopy(servers254.update_all_tags)
+delete_all_tags = copy.deepcopy(servers254.delete_all_tags)
+check_tag_existence = copy.deepcopy(servers254.check_tag_existence)
+update_tag = copy.deepcopy(servers254.update_tag)
+delete_tag = copy.deepcopy(servers254.delete_tag)
diff --git a/tempest/lib/api_schema/response/compute/v2_6/servers.py b/tempest/lib/api_schema/response/compute/v2_6/servers.py
index 29b3e86..d5774de 100644
--- a/tempest/lib/api_schema/response/compute/v2_6/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_6/servers.py
@@ -16,9 +16,18 @@
 
 from tempest.lib.api_schema.response.compute.v2_3 import servers
 
+# NOTE: 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.
+# ****** Schemas unchanged since microversion 2.3 ******
 list_servers = copy.deepcopy(servers.list_servers)
 get_server = copy.deepcopy(servers.get_server)
 list_servers_detail = copy.deepcopy(servers.list_servers_detail)
+update_server = copy.deepcopy(servers.update_server)
+rebuild_server = copy.deepcopy(servers.rebuild_server)
+rebuild_server_with_admin_pass = copy.deepcopy(
+    servers.rebuild_server_with_admin_pass)
+show_server_diagnostics = copy.deepcopy(servers.show_server_diagnostics)
 
 # NOTE: The consolidated remote console API got introduced with v2.6
 # with bp/consolidate-console-api. See Nova commit 578bafeda
@@ -31,7 +40,7 @@
                 'type': 'object',
                 'properties': {
                     'protocol': {'enum': ['vnc', 'rdp', 'serial', 'spice']},
-                    'type': {'enum': ['novnc', 'xpvnc', 'rdp-html5',
+                    'type': {'enum': ['novnc', 'xvpvnc', 'rdp-html5',
                                       'spice-html5', 'serial']},
                     'url': {
                         'type': 'string',
diff --git a/tempest/tests/lib/services/volume/v2/__init__.py b/tempest/lib/api_schema/response/compute/v2_61/__init__.py
similarity index 100%
copy from tempest/tests/lib/services/volume/v2/__init__.py
copy to tempest/lib/api_schema/response/compute/v2_61/__init__.py
diff --git a/tempest/lib/api_schema/response/compute/v2_61/flavors.py b/tempest/lib/api_schema/response/compute/v2_61/flavors.py
new file mode 100644
index 0000000..5119466
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_61/flavors.py
@@ -0,0 +1,106 @@
+# 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.
+
+import copy
+
+from tempest.lib.api_schema.response.compute.v2_1 import parameter_types
+from tempest.lib.api_schema.response.compute.v2_55 import flavors \
+    as flavorsv255
+
+# ****** Schemas changed in microversion 2.61 *****************
+
+# Note(gmann): This is schema for microversion 2.61 which includes the
+# Flavor extra_specs in the Response body of the following APIs:
+#    - ``PUT /flavors/{flavor_id}``
+#    - ``GET /flavors/detail``
+#    - ``GET /flavors/{flavor_id}``
+#    - ``POST /flavors``
+
+flavor_description = {
+    'type': ['string', 'null'],
+    'minLength': 0, 'maxLength': 65535
+}
+
+flavor_extra_specs = {
+    'type': 'object',
+    'patternProperties': {
+        '^[a-zA-Z0-9-_:. ]{1,255}$': {'type': 'string'}
+    }
+}
+
+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,
+        'extra_specs': flavor_extra_specs
+    },
+    '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']
+    }
+}
+
+# ****** Schemas unchanged in microversion 2.61 since microversion 2.55 ***
+# 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.
+# ****** Schemas unchanged since microversion 2.55 ***
+list_flavors = copy.deepcopy(flavorsv255.list_flavors)
+
+# ****** Schemas unchanged since microversion 2.1 ***
+delete_flavor = copy.deepcopy(flavorsv255.delete_flavor)
diff --git a/tempest/tests/lib/services/volume/v2/__init__.py b/tempest/lib/api_schema/response/compute/v2_63/__init__.py
similarity index 100%
copy from tempest/tests/lib/services/volume/v2/__init__.py
copy to tempest/lib/api_schema/response/compute/v2_63/__init__.py
diff --git a/tempest/lib/api_schema/response/compute/v2_63/servers.py b/tempest/lib/api_schema/response/compute/v2_63/servers.py
new file mode 100644
index 0000000..3c3d41c
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_63/servers.py
@@ -0,0 +1,75 @@
+#    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_57 import servers as servers257
+
+# Nova microversion 2.63 adds 'trusted_image_certificates' (a list of
+# certificate IDs) to the server rebuild and servers details responses.
+
+
+trusted_certs = {
+    'type': ['array', 'null'],
+    'minItems': 1,
+    'maxItems': 50,
+    'uniqueItems': True,
+    'items': {
+        'type': 'string',
+        'minLength': 1
+    }
+}
+
+list_servers_detail = copy.deepcopy(servers257.list_servers_detail)
+list_servers_detail['response_body']['properties']['servers']['items'][
+    'properties'].update({'trusted_image_certificates': trusted_certs})
+list_servers_detail['response_body']['properties']['servers']['items'][
+    'required'].append('trusted_image_certificates')
+
+rebuild_server = copy.deepcopy(servers257.rebuild_server)
+rebuild_server['response_body']['properties']['server'][
+    'properties'].update({'trusted_image_certificates': trusted_certs})
+rebuild_server['response_body']['properties']['server'][
+    'required'].append('trusted_image_certificates')
+
+rebuild_server_with_admin_pass = copy.deepcopy(
+    servers257.rebuild_server_with_admin_pass)
+rebuild_server_with_admin_pass['response_body']['properties']['server'][
+    'properties'].update({'trusted_image_certificates': trusted_certs})
+rebuild_server_with_admin_pass['response_body']['properties']['server'][
+    'required'].append('trusted_image_certificates')
+
+update_server = copy.deepcopy(servers257.update_server)
+update_server['response_body']['properties']['server'][
+    'properties'].update({'trusted_image_certificates': trusted_certs})
+update_server['response_body']['properties']['server'][
+    'required'].append('trusted_image_certificates')
+
+get_server = copy.deepcopy(servers257.get_server)
+get_server['response_body']['properties']['server'][
+    'properties'].update({'trusted_image_certificates': trusted_certs})
+get_server['response_body']['properties']['server'][
+    'required'].append('trusted_image_certificates')
+
+# NOTE(zhufl): 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.
+# ****** Schemas unchanged since microversion 2.57 ***
+list_servers = copy.deepcopy(servers257.list_servers)
+show_server_diagnostics = copy.deepcopy(servers257.show_server_diagnostics)
+get_remote_consoles = copy.deepcopy(servers257.get_remote_consoles)
+list_tags = copy.deepcopy(servers257.list_tags)
+update_all_tags = copy.deepcopy(servers257.update_all_tags)
+delete_all_tags = copy.deepcopy(servers257.delete_all_tags)
+check_tag_existence = copy.deepcopy(servers257.check_tag_existence)
+update_tag = copy.deepcopy(servers257.update_tag)
+delete_tag = copy.deepcopy(servers257.delete_tag)
diff --git a/tempest/tests/lib/services/volume/v2/__init__.py b/tempest/lib/api_schema/response/compute/v2_8/__init__.py
similarity index 100%
copy from tempest/tests/lib/services/volume/v2/__init__.py
copy to tempest/lib/api_schema/response/compute/v2_8/__init__.py
diff --git a/tempest/lib/api_schema/response/compute/v2_8/servers.py b/tempest/lib/api_schema/response/compute/v2_8/servers.py
new file mode 100644
index 0000000..df7847f
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_8/servers.py
@@ -0,0 +1,37 @@
+# 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 copy
+
+from tempest.lib.api_schema.response.compute.v2_6 import servers
+
+# 2.8: Add 'mks' protocol and 'webmks' type for remote consoles.
+get_remote_consoles = copy.deepcopy(servers.get_remote_consoles)
+get_remote_consoles['response_body']['properties']['remote_console'][
+    'properties']['protocol']['enum'].append('mks')
+get_remote_consoles['response_body']['properties']['remote_console'][
+    'properties']['type']['enum'].append('webmks')
+
+# NOTE: 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.
+# ****** Schemas unchanged since microversion 2.6 ******
+list_servers = copy.deepcopy(servers.list_servers)
+get_server = copy.deepcopy(servers.get_server)
+list_servers_detail = copy.deepcopy(servers.list_servers_detail)
+update_server = copy.deepcopy(servers.update_server)
+rebuild_server = copy.deepcopy(servers.rebuild_server)
+rebuild_server_with_admin_pass = copy.deepcopy(
+    servers.rebuild_server_with_admin_pass)
+show_server_diagnostics = copy.deepcopy(servers.show_server_diagnostics)
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..55f8e75 100644
--- a/tempest/lib/api_schema/response/compute/v2_9/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_9/servers.py
@@ -14,9 +14,7 @@
 
 import copy
 
-from tempest.lib.api_schema.response.compute.v2_6 import servers
-
-list_servers = copy.deepcopy(servers.list_servers)
+from tempest.lib.api_schema.response.compute.v2_8 import servers
 
 get_server = copy.deepcopy(servers.get_server)
 get_server['response_body']['properties']['server'][
@@ -29,3 +27,30 @@
     'properties'].update({'locked': {'type': 'boolean'}})
 list_servers_detail['response_body']['properties']['servers']['items'][
     'required'].append('locked')
+
+update_server = copy.deepcopy(servers.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.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.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')
+
+# NOTE: 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.
+# ****** Schemas unchanged since microversion 2.8 ******
+list_servers = copy.deepcopy(servers.list_servers)
+show_server_diagnostics = copy.deepcopy(servers.show_server_diagnostics)
+get_remote_consoles = copy.deepcopy(servers.get_remote_consoles)
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/base.py b/tempest/lib/cli/base.py
index 3fb56ec..d8c776b 100644
--- a/tempest/lib/cli/base.py
+++ b/tempest/lib/cli/base.py
@@ -269,7 +269,7 @@
         :param merge_stderr: if True the stderr buffer is merged into stdout
         :type merge_stderr: boolean
         """
-        flags += ' --endpoint-type %s' % endpoint_type
+        flags += ' --os-endpoint-type %s' % endpoint_type
         return self.cmd_with_auth(
             'cinder', action, flags, params, fail_ok, merge_stderr)
 
diff --git a/tempest/lib/cli/output_parser.py b/tempest/lib/cli/output_parser.py
index 2edd5c1..45d41c7 100644
--- a/tempest/lib/cli/output_parser.py
+++ b/tempest/lib/cli/output_parser.py
@@ -25,7 +25,7 @@
 LOG = logging.getLogger(__name__)
 
 
-delimiter_line = re.compile('^\+\-[\+\-]+\-\+$')
+delimiter_line = re.compile(r'^\+\-[\+\-]+\-\+$')
 
 
 def details_multiple(output_lines, with_label=False):
@@ -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..82fcd0b 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):
@@ -358,5 +358,6 @@
                  "Run 'tox -v -e uuidgen' to automatically fix tests with\n"
                  "missing @decorators.idempotent_id decorators.")
 
+
 if __name__ == '__main__':
     run()
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/common/jsonschema_validator.py b/tempest/lib/common/jsonschema_validator.py
index bbdf382..9a35b76 100644
--- a/tempest/lib/common/jsonschema_validator.py
+++ b/tempest/lib/common/jsonschema_validator.py
@@ -12,9 +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.
+import base64
 
 import jsonschema
 from oslo_utils import timeutils
+import six
 
 # JSON Schema validator and format checker used for JSON Schema validation
 JSONSCHEMA_VALIDATOR = jsonschema.Draft4Validator
@@ -37,3 +39,19 @@
         return False
     else:
         return True
+
+
+@jsonschema.FormatChecker.cls_checks('base64')
+def _validate_base64_format(instance):
+    try:
+        if isinstance(instance, six.text_type):
+            instance = instance.encode('utf-8')
+        base64.decodestring(instance)
+    except base64.binascii.Error:
+        return False
+    except TypeError:
+        # The name must be string type. If instance isn't string type, the
+        # TypeError will be raised at here.
+        return False
+
+    return True
diff --git a/tempest/lib/common/rest_client.py b/tempest/lib/common/rest_client.py
index 22276d4..e2fd722 100644
--- a/tempest/lib/common/rest_client.py
+++ b/tempest/lib/common/rest_client.py
@@ -416,6 +416,8 @@
                           resp_body=None, extra=None):
         if 'X-Auth-Token' in req_headers:
             req_headers['X-Auth-Token'] = '<omitted>'
+        if 'X-Subject-Token' in req_headers:
+            req_headers['X-Subject-Token'] = '<omitted>'
         # A shallow copy is sufficient
         resp_log = resp.copy()
         if 'x-subject-token' in resp_log:
@@ -568,8 +570,10 @@
 
         :param str url: Full url to send the request
         :param str method: The HTTP verb to use for the request
-        :param str headers: Headers to use for the request if none are specifed
-                            the headers
+        :param dict headers: Headers to use for the request. If none are
+                             specified, then the headers returned from the
+                             get_headers() method are used. If the request
+                             explicitly requires no headers use an empty dict.
         :param str body: Body to send with the request
         :param bool chunked: sends the body with chunked encoding
         :rtype: tuple
@@ -603,8 +607,8 @@
                                    returned by the get_headers() method are to
                                    be used but additional headers are needed in
                                    the request pass them in as a dict.
-        :param dict headers: Headers to use for the request if none are
-                             specifed the headers returned from the
+        :param dict headers: Headers to use for the request. If none are
+                             specified, then the headers returned from the
                              get_headers() method are used. If the request
                              explicitly requires no headers use an empty dict.
         :param str body: Body to send with the request
@@ -621,10 +625,13 @@
         :raises Gone: If a 410 response code is received
         :raises Conflict: If a 409 response code is received
         :raises PreconditionFailed: If a 412 response code is received
-        :raises OverLimit: If a 413 response code is received and over_limit is
-                          not in the response body
+        :raises OverLimit: If a 413 response code is received and retry-after
+                           is not in the response body or its retry operation
+                           exceeds the limits defined by the server
         :raises RateLimitExceeded: If a 413 response code is received and
-                                   over_limit is in the response body
+                                   retry-after is in the response body and
+                                   its retry operation does not exceeds the
+                                   limits defined by the server
         :raises InvalidContentType: If a 415 response code is received
         :raises UnprocessableEntity: If a 422 response code is received
         :raises InvalidHTTPResponseBody: The response body wasn't valid JSON
diff --git a/tempest/lib/common/thread.py b/tempest/lib/common/thread.py
new file mode 100644
index 0000000..510fc36
--- /dev/null
+++ b/tempest/lib/common/thread.py
@@ -0,0 +1,29 @@
+# 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.
+
+# This make disable relative module import
+from __future__ import absolute_import
+
+
+import six
+
+if six.PY2:
+    # module thread is removed in Python 3
+    from thread import get_ident  # noqa: H237,F401
+
+else:
+    # On Python3 thread module has been deprecated and get_ident has been moved
+    # to threading module
+    from threading import get_ident  # noqa: F401
diff --git a/tempest/lib/common/utils/linux/remote_client.py b/tempest/lib/common/utils/linux/remote_client.py
index 94fab00..8ac1d38 100644
--- a/tempest/lib/common/utils/linux/remote_client.py
+++ b/tempest/lib/common/utils/linux/remote_client.py
@@ -11,6 +11,7 @@
 #    under the License.
 
 import functools
+import re
 import sys
 
 import netaddr
@@ -126,3 +127,27 @@
             cmd = 'sudo {cmd} -I {nic}'.format(cmd=cmd, nic=nic)
         cmd += ' -c{0} -w{0} -s{1} {2}'.format(count, size, host)
         return self.exec_command(cmd)
+
+    def mount_config_drive(self):
+        """Mount the config drive inside a virtual machine
+
+        This method will not unmount the config drive, so unmount_config_drive
+        must be used for cleanup.
+        """
+        cmd_blkid = 'blkid | grep -i config-2'
+        result = self.exec_command(cmd_blkid)
+        dev_name = re.match('([^:]+)', result).group()
+
+        try:
+            self.exec_command('sudo mount %s /mnt' % dev_name)
+        except tempest.lib.exceptions.SSHExecCommandFailed:
+            # So the command failed, let's try to know why and print some
+            # useful information.
+            lsblk = self.exec_command('sudo lsblk --fs --ascii')
+            LOG.error("Mounting %s on /mnt failed. Right after the "
+                      "failure 'lsblk' in the guest reported:\n%s",
+                      dev_name, lsblk)
+            raise
+
+    def unmount_config_drive(self):
+        self.exec_command('sudo umount /mnt')
diff --git a/tempest/lib/decorators.py b/tempest/lib/decorators.py
index e99dd24..b399aa0 100644
--- a/tempest/lib/decorators.py
+++ b/tempest/lib/decorators.py
@@ -19,39 +19,83 @@
 import six
 import testtools
 
+from tempest.lib import exceptions as lib_exc
+
 LOG = logging.getLogger(__name__)
 
+_SUPPORTED_BUG_TYPES = {
+    'launchpad': 'https://launchpad.net/bugs/%s',
+    'storyboard': 'https://storyboard.openstack.org/#!/story/%s',
+}
+
+
+def _validate_bug_and_bug_type(bug, bug_type):
+    """Validates ``bug`` and ``bug_type`` values.
+
+    :param bug: bug number causing the test to skip (launchpad or storyboard)
+    :param bug_type: 'launchpad' or 'storyboard', default 'launchpad'
+    :raises: InvalidParam if ``bug`` is not a digit or ``bug_type`` is not
+        a valid value
+    """
+    if not bug.isdigit():
+        invalid_param = '%s must be a valid %s number' % (bug, bug_type)
+        raise lib_exc.InvalidParam(invalid_param=invalid_param)
+    if bug_type not in _SUPPORTED_BUG_TYPES:
+        invalid_param = 'bug_type "%s" must be one of: %s' % (
+            bug_type, ', '.join(_SUPPORTED_BUG_TYPES.keys()))
+        raise lib_exc.InvalidParam(invalid_param=invalid_param)
+
+
+def _get_bug_url(bug, bug_type='launchpad'):
+    """Get the bug URL based on the ``bug_type`` and ``bug``
+
+    :param bug: The launchpad/storyboard bug number causing the test
+    :param bug_type: 'launchpad' or 'storyboard', default 'launchpad'
+    :returns: Bug URL corresponding to ``bug_type`` value
+    """
+    _validate_bug_and_bug_type(bug, bug_type)
+    return _SUPPORTED_BUG_TYPES[bug_type] % bug
+
 
 def skip_because(*args, **kwargs):
     """A decorator useful to skip tests hitting known bugs
 
-    @param bug: bug number causing the test to skip
-    @param condition: optional condition to be True for the skip to have place
+    ``bug`` must be a number and ``condition`` must be true for the test to
+    skip.
+
+    :param bug: bug number causing the test to skip (launchpad or storyboard)
+    :param bug_type: 'launchpad' or 'storyboard', default 'launchpad'
+    :param condition: optional condition to be True for the skip to have place
+    :raises: testtools.TestCase.skipException if ``condition`` is True and
+        ``bug`` is included
     """
     def decorator(f):
         @functools.wraps(f)
         def wrapper(*func_args, **func_kwargs):
             skip = False
+            msg = ''
             if "condition" in kwargs:
                 if kwargs["condition"] is True:
                     skip = True
             else:
                 skip = True
             if "bug" in kwargs and skip is True:
-                if not kwargs['bug'].isdigit():
-                    raise ValueError('bug must be a valid bug number')
-                msg = "Skipped until Bug: %s is resolved." % kwargs["bug"]
+                bug = kwargs['bug']
+                bug_type = kwargs.get('bug_type', 'launchpad')
+                bug_url = _get_bug_url(bug, bug_type)
+                msg = "Skipped until bug: %s is resolved." % bug_url
                 raise testtools.TestCase.skipException(msg)
             return f(*func_args, **func_kwargs)
         return wrapper
     return decorator
 
 
-def related_bug(bug, status_code=None):
-    """A decorator useful to know solutions from launchpad bug reports
+def related_bug(bug, status_code=None, bug_type='launchpad'):
+    """A decorator useful to know solutions from launchpad/storyboard reports
 
-    @param bug: The launchpad bug number causing the test
-    @param status_code: The status code related to the bug report
+    :param bug: The launchpad/storyboard bug number causing the test bug
+    :param bug_type: 'launchpad' or 'storyboard', default 'launchpad'
+    :param status_code: The status code related to the bug report
     """
     def decorator(f):
         @functools.wraps(f)
@@ -61,9 +105,10 @@
             except Exception as exc:
                 exc_status_code = getattr(exc, 'status_code', None)
                 if status_code is None or status_code == exc_status_code:
-                    LOG.error('Hints: This test was made for the bug %s. '
-                              'The failure could be related to '
-                              'https://launchpad.net/bugs/%s', bug, bug)
+                    if bug:
+                        LOG.error('Hints: This test was made for the bug_type '
+                                  '%s. The failure could be related to '
+                                  '%s', bug, _get_bug_url(bug, bug_type))
                 raise exc
         return wrapper
     return decorator
diff --git a/tempest/lib/services/clients.py b/tempest/lib/services/clients.py
index 8918a8c..833cfd6 100644
--- a/tempest/lib/services/clients.py
+++ b/tempest/lib/services/clients.py
@@ -331,7 +331,7 @@
         self.region = region
         # Check if passed or default credentials are valid
         if not self.credentials.is_valid():
-            raise exceptions.InvalidCredentials()
+            raise exceptions.InvalidCredentials(credentials)
         # Get the identity classes matching the provided credentials
         # TODO(andreaf) Define a new interface in Credentials to get
         # the API version from an instance
@@ -340,7 +340,9 @@
                     isinstance(self.credentials, auth.IDENTITY_VERSION[k][0])]
         # Zero matches or more than one are both not valid.
         if len(identity) != 1:
-            raise exceptions.InvalidCredentials()
+            msg = "Zero or %d ambiguous auth provider found. identity: %s, " \
+                "credentials: %s" % (len(identity), identity, credentials)
+            raise exceptions.InvalidCredentials(msg)
         self.auth_version, auth_provider_class = identity[0]
         self.dscv = disable_ssl_certificate_validation
         self.ca_certs = ca_certs
diff --git a/tempest/lib/services/compute/aggregates_client.py b/tempest/lib/services/compute/aggregates_client.py
index 713d7a3..57f5e4e 100644
--- a/tempest/lib/services/compute/aggregates_client.py
+++ b/tempest/lib/services/compute/aggregates_client.py
@@ -15,7 +15,10 @@
 
 from oslo_serialization import jsonutils as json
 
-from tempest.lib.api_schema.response.compute.v2_1 import aggregates as schema
+from tempest.lib.api_schema.response.compute.v2_1 \
+    import aggregates as schema
+from tempest.lib.api_schema.response.compute.v2_41 \
+    import aggregates as schemav241
 from tempest.lib.common import rest_client
 from tempest.lib import exceptions as lib_exc
 from tempest.lib.services.compute import base_compute_client
@@ -23,10 +26,15 @@
 
 class AggregatesClient(base_compute_client.BaseComputeClient):
 
+    schema_versions_info = [
+        {'min': None, 'max': '2.40', 'schema': schema},
+        {'min': '2.41', 'max': None, 'schema': schemav241}]
+
     def list_aggregates(self):
         """Get aggregate list."""
         resp, body = self.get("os-aggregates")
         body = json.loads(body)
+        schema = self.get_schema(self.schema_versions_info)
         self.validate_response(schema.list_aggregates, resp, body)
         return rest_client.ResponseBody(resp, body)
 
@@ -34,6 +42,7 @@
         """Get details of the given aggregate."""
         resp, body = self.get("os-aggregates/%s" % aggregate_id)
         body = json.loads(body)
+        schema = self.get_schema(self.schema_versions_info)
         self.validate_response(schema.get_aggregate, resp, body)
         return rest_client.ResponseBody(resp, body)
 
@@ -48,6 +57,7 @@
         resp, body = self.post('os-aggregates', post_body)
 
         body = json.loads(body)
+        schema = self.get_schema(self.schema_versions_info)
         self.validate_response(schema.create_aggregate, resp, body)
         return rest_client.ResponseBody(resp, body)
 
@@ -62,12 +72,14 @@
         resp, body = self.put('os-aggregates/%s' % aggregate_id, put_body)
 
         body = json.loads(body)
+        schema = self.get_schema(self.schema_versions_info)
         self.validate_response(schema.update_aggregate, resp, body)
         return rest_client.ResponseBody(resp, body)
 
     def delete_aggregate(self, aggregate_id):
         """Delete the given aggregate."""
         resp, body = self.delete("os-aggregates/%s" % aggregate_id)
+        schema = self.get_schema(self.schema_versions_info)
         self.validate_response(schema.delete_aggregate, resp, body)
         return rest_client.ResponseBody(resp, body)
 
@@ -94,6 +106,7 @@
         resp, body = self.post('os-aggregates/%s/action' % aggregate_id,
                                post_body)
         body = json.loads(body)
+        schema = self.get_schema(self.schema_versions_info)
         self.validate_response(schema.aggregate_add_remove_host, resp, body)
         return rest_client.ResponseBody(resp, body)
 
@@ -108,6 +121,7 @@
         resp, body = self.post('os-aggregates/%s/action' % aggregate_id,
                                post_body)
         body = json.loads(body)
+        schema = self.get_schema(self.schema_versions_info)
         self.validate_response(schema.aggregate_add_remove_host, resp, body)
         return rest_client.ResponseBody(resp, body)
 
@@ -122,5 +136,6 @@
         resp, body = self.post('os-aggregates/%s/action' % aggregate_id,
                                post_body)
         body = json.loads(body)
+        schema = self.get_schema(self.schema_versions_info)
         self.validate_response(schema.aggregate_set_metadata, resp, body)
         return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/compute/flavors_client.py b/tempest/lib/services/compute/flavors_client.py
index 0fb1991..2fad0a4 100644
--- a/tempest/lib/services/compute/flavors_client.py
+++ b/tempest/lib/services/compute/flavors_client.py
@@ -21,12 +21,21 @@
     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.api_schema.response.compute.v2_61 import flavors \
+    as schemav261
 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': '2.60', 'schema': schemav255},
+        {'min': '2.61', 'max': None, 'schema': schemav261}]
+
     def list_flavors(self, detail=False, **params):
         """Lists flavors.
 
@@ -36,11 +45,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 +68,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 +89,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):
@@ -175,7 +205,8 @@
         """
         resp, body = self.delete('flavors/%s/os-extra_specs/%s' %
                                  (flavor_id, key))
-        self.validate_response(schema.unset_flavor_extra_specs, resp, body)
+        self.validate_response(schema_extra_specs.unset_flavor_extra_specs,
+                               resp, body)
         return rest_client.ResponseBody(resp, body)
 
     def list_flavor_access(self, flavor_id):
diff --git a/tempest/lib/services/compute/hypervisor_client.py b/tempest/lib/services/compute/hypervisor_client.py
index 23c304e..1cbfcc3 100644
--- a/tempest/lib/services/compute/hypervisor_client.py
+++ b/tempest/lib/services/compute/hypervisor_client.py
@@ -15,16 +15,24 @@
 
 from oslo_serialization import jsonutils as json
 
-from tempest.lib.api_schema.response.compute.v2_1 import hypervisors as schema
+from tempest.lib.api_schema.response.compute.v2_1 \
+    import hypervisors as schemav21
+from tempest.lib.api_schema.response.compute.v2_28 \
+    import hypervisors as schemav228
 from tempest.lib.common import rest_client
 from tempest.lib.services.compute import base_compute_client
 
 
 class HypervisorClient(base_compute_client.BaseComputeClient):
 
+    schema_versions_info = [
+        {'min': None, 'max': '2.27', 'schema': schemav21},
+        {'min': '2.28', 'max': None, 'schema': schemav228}]
+
     def list_hypervisors(self, detail=False):
         """List hypervisors information."""
         url = 'os-hypervisors'
+        schema = self.get_schema(self.schema_versions_info)
         _schema = schema.list_search_hypervisors
         if detail:
             url += '/detail'
@@ -39,6 +47,7 @@
         """Display the details of the specified hypervisor."""
         resp, body = self.get('os-hypervisors/%s' % hypervisor_id)
         body = json.loads(body)
+        schema = self.get_schema(self.schema_versions_info)
         self.validate_response(schema.get_hypervisor, resp, body)
         return rest_client.ResponseBody(resp, body)
 
@@ -46,6 +55,7 @@
         """List instances belonging to the specified hypervisor."""
         resp, body = self.get('os-hypervisors/%s/servers' % hypervisor_name)
         body = json.loads(body)
+        schema = self.get_schema(self.schema_versions_info)
         self.validate_response(schema.get_hypervisors_servers, resp, body)
         return rest_client.ResponseBody(resp, body)
 
@@ -53,6 +63,7 @@
         """Get hypervisor statistics over all compute nodes."""
         resp, body = self.get('os-hypervisors/statistics')
         body = json.loads(body)
+        schema = self.get_schema(self.schema_versions_info)
         self.validate_response(schema.get_hypervisor_statistics, resp, body)
         return rest_client.ResponseBody(resp, body)
 
@@ -60,6 +71,7 @@
         """Display the uptime of the specified hypervisor."""
         resp, body = self.get('os-hypervisors/%s/uptime' % hypervisor_id)
         body = json.loads(body)
+        schema = self.get_schema(self.schema_versions_info)
         self.validate_response(schema.get_hypervisor_uptime, resp, body)
         return rest_client.ResponseBody(resp, body)
 
@@ -69,5 +81,6 @@
         """Search specified hypervisor."""
         resp, body = self.get('os-hypervisors/%s/search' % hypervisor_name)
         body = json.loads(body)
+        schema = self.get_schema(self.schema_versions_info)
         self.validate_response(schema.list_search_hypervisors, resp, body)
         return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/compute/limits_client.py b/tempest/lib/services/compute/limits_client.py
index efe9889..9af80c4 100644
--- a/tempest/lib/services/compute/limits_client.py
+++ b/tempest/lib/services/compute/limits_client.py
@@ -15,15 +15,25 @@
 
 from oslo_serialization import jsonutils as json
 
-from tempest.lib.api_schema.response.compute.v2_1 import limits as schema
+from tempest.lib.api_schema.response.compute.v2_1 import limits as schemav21
+from tempest.lib.api_schema.response.compute.v2_36 import limits as schemav236
+from tempest.lib.api_schema.response.compute.v2_39 import limits as schemav239
+from tempest.lib.api_schema.response.compute.v2_57 import limits as schemav257
 from tempest.lib.common import rest_client
 from tempest.lib.services.compute import base_compute_client
 
 
 class LimitsClient(base_compute_client.BaseComputeClient):
 
+    schema_versions_info = [
+        {'min': None, 'max': '2.35', 'schema': schemav21},
+        {'min': '2.36', 'max': '2.38', 'schema': schemav236},
+        {'min': '2.39', 'max': '2.56', 'schema': schemav239},
+        {'min': '2.57', 'max': None, 'schema': schemav257}]
+
     def show_limits(self):
         resp, body = self.get("limits")
         body = json.loads(body)
+        schema = self.get_schema(self.schema_versions_info)
         self.validate_response(schema.get_limit, resp, body)
         return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/compute/quotas_client.py b/tempest/lib/services/compute/quotas_client.py
index 12df895..99c8d0f 100644
--- a/tempest/lib/services/compute/quotas_client.py
+++ b/tempest/lib/services/compute/quotas_client.py
@@ -17,12 +17,19 @@
 from six.moves.urllib import parse as urllib
 
 from tempest.lib.api_schema.response.compute.v2_1 import quotas as schema
+from tempest.lib.api_schema.response.compute.v2_36 import quotas as schemav236
+from tempest.lib.api_schema.response.compute.v2_57 import quotas as schemav257
 from tempest.lib.common import rest_client
 from tempest.lib.services.compute import base_compute_client
 
 
 class QuotasClient(base_compute_client.BaseComputeClient):
 
+    schema_versions_info = [
+        {'min': None, 'max': '2.35', 'schema': schema},
+        {'min': '2.36', 'max': '2.56', 'schema': schemav236},
+        {'min': '2.57', 'max': None, 'schema': schemav257}]
+
     def show_quota_set(self, tenant_id, user_id=None, detail=False):
         """List the quota set for a tenant.
 
@@ -42,6 +49,7 @@
             url += '?%s' % urllib.urlencode(params)
         resp, body = self.get(url)
         body = json.loads(body)
+        schema = self.get_schema(self.schema_versions_info)
         if detail:
             self.validate_response(schema.get_quota_set_details, resp, body)
         else:
@@ -57,6 +65,7 @@
         url = 'os-quota-sets/%s/defaults' % tenant_id
         resp, body = self.get(url)
         body = json.loads(body)
+        schema = self.get_schema(self.schema_versions_info)
         self.validate_response(schema.get_quota_set, resp, body)
         return rest_client.ResponseBody(resp, body)
 
@@ -78,6 +87,7 @@
                                   post_body)
 
         body = json.loads(body)
+        schema = self.get_schema(self.schema_versions_info)
         self.validate_response(schema.update_quota_set, resp, body)
         return rest_client.ResponseBody(resp, body)
 
@@ -87,5 +97,6 @@
         https://developer.openstack.org/api-ref/compute/#revert-quotas-to-defaults
         """
         resp, body = self.delete('os-quota-sets/%s' % tenant_id)
+        schema = self.get_schema(self.schema_versions_info)
         self.validate_response(schema.delete_quota, resp, body)
         return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/compute/server_groups_client.py b/tempest/lib/services/compute/server_groups_client.py
index 03cd645..0d440d5 100644
--- a/tempest/lib/services/compute/server_groups_client.py
+++ b/tempest/lib/services/compute/server_groups_client.py
@@ -16,8 +16,10 @@
 
 from oslo_serialization import jsonutils as json
 
-from tempest.lib.api_schema.response.compute.v2_1 import servers as schema
-from tempest.lib.api_schema.response.compute.v2_13 import servers as schemav213
+from tempest.lib.api_schema.response.compute.v2_1 import server_groups \
+    as schema
+from tempest.lib.api_schema.response.compute.v2_13 import server_groups \
+    as schemav213
 from tempest.lib.common import rest_client
 from tempest.lib.services.compute import base_compute_client
 
diff --git a/tempest/lib/services/compute/servers_client.py b/tempest/lib/services/compute/servers_client.py
index 09bccab..9eed4b3 100644
--- a/tempest/lib/services/compute/servers_client.py
+++ b/tempest/lib/services/compute/servers_client.py
@@ -29,7 +29,11 @@
 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_57 import servers as schemav257
 from tempest.lib.api_schema.response.compute.v2_6 import servers as schemav26
+from tempest.lib.api_schema.response.compute.v2_63 import servers as schemav263
+from tempest.lib.api_schema.response.compute.v2_8 import servers as schemav28
 from tempest.lib.api_schema.response.compute.v2_9 import servers as schemav29
 from tempest.lib.common import rest_client
 from tempest.lib.services.compute import base_compute_client
@@ -41,13 +45,17 @@
     schema_versions_info = [
         {'min': None, 'max': '2.2', 'schema': schema},
         {'min': '2.3', 'max': '2.5', 'schema': schemav23},
-        {'min': '2.6', 'max': '2.8', 'schema': schemav26},
+        {'min': '2.6', 'max': '2.7', 'schema': schemav26},
+        {'min': '2.8', 'max': '2.8', 'schema': schemav28},
         {'min': '2.9', 'max': '2.15', 'schema': schemav29},
         {'min': '2.16', 'max': '2.18', 'schema': schemav216},
         {'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': '2.56', 'schema': schemav254},
+        {'min': '2.57', 'max': '2.62', 'schema': schemav257},
+        {'min': '2.63', 'max': None, 'schema': schemav263}]
 
     def __init__(self, auth_provider, service, region,
                  enable_instance_password=True, **kwargs):
@@ -156,11 +164,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/compute/services_client.py b/tempest/lib/services/compute/services_client.py
index b046c35..d52de3a 100644
--- a/tempest/lib/services/compute/services_client.py
+++ b/tempest/lib/services/compute/services_client.py
@@ -20,6 +20,8 @@
 from tempest.lib.api_schema.response.compute.v2_1 import services as schema
 from tempest.lib.api_schema.response.compute.v2_11 import services \
     as schemav211
+from tempest.lib.api_schema.response.compute.v2_53 import services \
+    as schemav253
 from tempest.lib.common import rest_client
 from tempest.lib.services.compute import base_compute_client
 
@@ -28,7 +30,8 @@
 
     schema_versions_info = [
         {'min': None, 'max': '2.10', 'schema': schema},
-        {'min': '2.11', 'max': None, 'schema': schemav211}]
+        {'min': '2.11', 'max': '2.52', 'schema': schemav211},
+        {'min': '2.53', 'max': None, 'schema': schemav253}]
 
     def list_services(self, **params):
         """Lists all running Compute services for a tenant.
@@ -47,9 +50,30 @@
         self.validate_response(_schema.list_services, resp, body)
         return rest_client.ResponseBody(resp, body)
 
+    def update_service(self, service_id, **kwargs):
+        """Update a compute service.
+
+        Update a compute service to enable or disable scheduling, including
+        recording a reason why a compute service was disabled from scheduling.
+
+        This API is available starting with microversion 2.53.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/compute/#update-compute-service
+        """
+        put_body = json.dumps(kwargs)
+        resp, body = self.put('os-services/%s' % service_id, put_body)
+        body = json.loads(body)
+        _schema = self.get_schema(self.schema_versions_info)
+        self.validate_response(_schema.update_service, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
     def enable_service(self, **kwargs):
         """Enable service on a host.
 
+        ``update_service`` supersedes this API starting with microversion 2.53.
+
         For a full list of available parameters, please refer to the official
         API reference:
         https://developer.openstack.org/api-ref/compute/#enable-scheduling-for-a-compute-service
@@ -63,6 +87,8 @@
     def disable_service(self, **kwargs):
         """Disable service on a host.
 
+        ``update_service`` supersedes this API starting with microversion 2.53.
+
         For a full list of available parameters, please refer to the official
         API reference:
         https://developer.openstack.org/api-ref/compute/#disable-scheduling-for-a-compute-service
@@ -76,6 +102,8 @@
     def disable_log_reason(self, **kwargs):
         """Disables scheduling for a Compute service and logs reason.
 
+        ``update_service`` supersedes this API starting with microversion 2.53.
+
         For a full list of available parameters, please refer to the official
         API reference:
         https://developer.openstack.org/api-ref/compute/#disable-scheduling-for-a-compute-service-and-log-disabled-reason
@@ -89,6 +117,8 @@
     def update_forced_down(self, **kwargs):
         """Set or unset ``forced_down`` flag for the service.
 
+        ``update_service`` supersedes this API starting with microversion 2.53.
+
         For a full list of available parameters, please refer to the official
         API reference:
         https://developer.openstack.org/api-ref/compute/#update-forced-down
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/policies_client.py b/tempest/lib/services/identity/v3/policies_client.py
index d4560e2..ca8dbbd 100644
--- a/tempest/lib/services/identity/v3/policies_client.py
+++ b/tempest/lib/services/identity/v3/policies_client.py
@@ -73,3 +73,115 @@
         resp, body = self.delete(url)
         self.expected_success(204, resp.status)
         return rest_client.ResponseBody(resp, body)
+
+    def update_policy_association_for_endpoint(self, policy_id, endpoint_id):
+        """Create policy association with endpoint.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/identity/v3-ext/index.html#associate-policy-and-endpoint
+        """
+        url = "policies/{0}/OS-ENDPOINT-POLICY/endpoints/{1}"\
+              .format(policy_id, endpoint_id)
+        resp, body = self.put(url, '{}')
+        self.expected_success(204, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_policy_association_for_endpoint(self, policy_id, endpoint_id):
+        """Get policy association of endpoint.
+
+        API reference:
+        https://developer.openstack.org/api-ref/identity/v3-ext/index.html#verify-a-policy-and-endpoint-association
+        """
+        url = "policies/{0}/OS-ENDPOINT-POLICY/endpoints/{1}"\
+              .format(policy_id, endpoint_id)
+        resp, body = self.get(url)
+        self.expected_success(204, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_policy_association_for_endpoint(self, policy_id, endpoint_id):
+        """Delete policy association with endpoint.
+
+        API reference:
+        https://developer.openstack.org/api-ref/identity/v3-ext/index.html#delete-a-policy-and-endpoint-association
+        """
+        url = "policies/{0}/OS-ENDPOINT-POLICY/endpoints/{1}"\
+              .format(policy_id, endpoint_id)
+        resp, body = self.delete(url)
+        self.expected_success(204, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_policy_association_for_service(self, policy_id, service_id):
+        """Create policy association with service.
+
+        API reference:
+        https://developer.openstack.org/api-ref/identity/v3-ext/index.html#associate-policy-and-service-type-endpoint
+        """
+        url = "policies/{0}/OS-ENDPOINT-POLICY/services/{1}"\
+              .format(policy_id, service_id)
+        resp, body = self.put(url, '{}')
+        self.expected_success(204, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_policy_association_for_service(self, policy_id, service_id):
+        """Get policy association of service.
+
+        API Reference:
+        https://developer.openstack.org/api-ref/identity/v3-ext/index.html#verify-a-policy-and-service-type-endpoint-association
+        """
+        url = "policies/{0}/OS-ENDPOINT-POLICY/services/{1}"\
+              .format(policy_id, service_id)
+        resp, body = self.get(url)
+        self.expected_success(204, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_policy_association_for_service(self, policy_id, service_id):
+        """Delete policy association with service.
+
+        API reference:
+        https://developer.openstack.org/api-ref/identity/v3-ext/index.html#delete-a-policy-and-service-type-endpoint-association
+        """
+        url = "policies/{0}/OS-ENDPOINT-POLICY/services/{1}"\
+              .format(policy_id, service_id)
+        resp, body = self.delete(url)
+        self.expected_success(204, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_policy_association_for_region_and_service(
+            self, policy_id, service_id, region_id):
+        """Create policy association with service and region.
+
+        API reference:
+        https://developer.openstack.org/api-ref/identity/v3-ext/index.html#associate-policy-and-service-type-endpoint-in-a-region
+        """
+        url = "policies/{0}/OS-ENDPOINT-POLICY/services/{1}/regions/{2}"\
+              .format(policy_id, service_id, region_id)
+        resp, body = self.put(url, '{}')
+        self.expected_success(204, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_policy_association_for_region_and_service(
+        self, policy_id, service_id, region_id):
+        """Get policy association of service and region.
+
+        API reference:
+        https://developer.openstack.org/api-ref/identity/v3-ext/index.html#verify-a-policy-and-service-type-endpoint-in-a-region-association
+        """
+        url = "policies/{0}/OS-ENDPOINT-POLICY/services/{1}/regions/{2}"\
+              .format(policy_id, service_id, region_id)
+        resp, body = self.get(url)
+        self.expected_success(204, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_policy_association_for_region_and_service(
+        self, policy_id, service_id, region_id):
+        """Delete policy association with service and region.
+
+        API reference:
+        https://developer.openstack.org/api-ref/identity/v3-ext/index.html#delete-a-policy-and-service-type-endpoint-in-a-region-association
+        """
+        url = "policies/{0}/OS-ENDPOINT-POLICY/services/{1}/regions/{2}"\
+              .format(policy_id, service_id, region_id)
+        resp, body = self.delete(url)
+        self.expected_success(204, resp.status)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/identity/v3/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/image/v2/images_client.py b/tempest/lib/services/image/v2/images_client.py
index bcdae44..3c38dba 100644
--- a/tempest/lib/services/image/v2/images_client.py
+++ b/tempest/lib/services/image/v2/images_client.py
@@ -32,7 +32,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref/image/v2/index.html#update-an-image
+        https://developer.openstack.org/api-ref/image/v2/#update-image
         """
         data = json.dumps(patch)
         headers = {"Content-Type": "application/openstack-images-v2.0"
@@ -47,7 +47,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref/image/v2/index.html#create-an-image
+        https://developer.openstack.org/api-ref/image/v2/#create-image
         """
         data = json.dumps(kwargs)
         resp, body = self.post('images', data)
@@ -84,7 +84,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref/image/v2/#delete-an-image
+        https://developer.openstack.org/api-ref/image/v2/#delete-image
          """
         url = 'images/%s' % image_id
         resp, _ = self.delete(url)
@@ -96,7 +96,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref/image/v2/#show-images
+        https://developer.openstack.org/api-ref/image/v2/#list-images
         """
         url = 'images'
 
@@ -113,7 +113,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        http://developer.openstack.org/api-ref/image/v2/#show-image-details
+        https://developer.openstack.org/api-ref/image/v2/#show-image
         """
         url = 'images/%s' % image_id
         resp, body = self.get(url)
@@ -161,7 +161,7 @@
         """
         url = 'images/%s/file' % image_id
         resp, body = self.get(url)
-        self.expected_success(200, resp.status)
+        self.expected_success([200, 204, 206], resp.status)
         return rest_client.ResponseBodyData(resp, body)
 
     def add_image_tag(self, image_id, tag):
diff --git a/tempest/lib/services/network/agents_client.py b/tempest/lib/services/network/agents_client.py
index 9bdf090..5068121 100644
--- a/tempest/lib/services/network/agents_client.py
+++ b/tempest/lib/services/network/agents_client.py
@@ -18,35 +18,62 @@
 class AgentsClient(base.BaseNetworkClient):
 
     def update_agent(self, agent_id, **kwargs):
-        """Update agent."""
-        # 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/1526673
+        """Update an agent.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/network/v2/#update-agent
+        """
         uri = '/agents/%s' % agent_id
         return self.update_resource(uri, kwargs)
 
     def show_agent(self, agent_id, **fields):
+        """Show details for an agent.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/network/v2/#show-agent-details
+        """
         uri = '/agents/%s' % agent_id
         return self.show_resource(uri, **fields)
 
     def list_agents(self, **filters):
+        """List all agents.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/network/v2/#list-all-agents
+        """
         uri = '/agents'
         return self.list_resources(uri, **filters)
 
     def list_routers_on_l3_agent(self, agent_id):
+        """List routers that an l3 agent hosts.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/network/v2/#list-routers-hosted-by-an-l3-agent
+        """
         uri = '/agents/%s/l3-routers' % agent_id
         return self.list_resources(uri)
 
     def create_router_on_l3_agent(self, agent_id, **kwargs):
-        # 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/1526670
+        """Add a router to an l3 agent.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/network/v2/#schedule-router-to-an-l3-agent
+        """
         uri = '/agents/%s/l3-routers' % agent_id
         return self.create_resource(uri, kwargs, expect_empty_body=True)
 
     def delete_router_from_l3_agent(self, agent_id, router_id):
+        """Remove a router to an l3 agent.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/network/v2/#remove-l3-router-from-an-l3-agent
+        """
         uri = '/agents/%s/l3-routers/%s' % (agent_id, router_id)
         return self.delete_resource(uri)
 
@@ -60,9 +87,11 @@
         return self.delete_resource(uri)
 
     def add_dhcp_agent_to_network(self, agent_id, **kwargs):
-        # 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/1526212
+        """Schedule a network to a DHCP agent.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/network/v2/#schedule-a-network-to-a-dhcp-agent
+        """
         uri = '/agents/%s/dhcp-networks' % agent_id
         return self.create_resource(uri, kwargs, expect_empty_body=True)
diff --git a/tempest/lib/services/network/quotas_client.py b/tempest/lib/services/network/quotas_client.py
index f23af88..e9666de 100644
--- a/tempest/lib/services/network/quotas_client.py
+++ b/tempest/lib/services/network/quotas_client.py
@@ -46,3 +46,8 @@
         """List default quotas for a project."""
         uri = '/quotas/%s/default' % tenant_id
         return self.show_resource(uri)
+
+    def show_quota_details(self, tenant_id):
+        """Show quota details for a project."""
+        uri = '/quotas/%s/details.json' % tenant_id
+        return self.show_resource(uri)
diff --git a/tempest/lib/services/volume/v1/encryption_types_client.py b/tempest/lib/services/volume/v1/encryption_types_client.py
index 0fac6bd..1fde79f 100644
--- a/tempest/lib/services/volume/v1/encryption_types_client.py
+++ b/tempest/lib/services/volume/v1/encryption_types_client.py
@@ -38,7 +38,7 @@
     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.
+        :param volume_type_id: Id of volume type.
         """
         url = "/types/%s/encryption" % volume_type_id
         resp, body = self.get(url)
@@ -61,7 +61,7 @@
         return rest_client.ResponseBody(resp, body)
 
     def delete_encryption_type(self, volume_type_id):
-        """Delete the encryption type for the specified volume-type."""
+        """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)
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 20f3356..875e59e 100644
--- a/tempest/lib/services/volume/v2/encryption_types_client.py
+++ b/tempest/lib/services/volume/v2/encryption_types_client.py
@@ -13,71 +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 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..f2d2d21 100644
--- a/tempest/lib/services/volume/v3/backups_client.py
+++ b/tempest/lib/services/volume/v3/backups_client.py
@@ -14,14 +14,28 @@
 #    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 +49,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/base_client.py b/tempest/lib/services/volume/v3/base_client.py
index e78380b..617da2e 100644
--- a/tempest/lib/services/volume/v3/base_client.py
+++ b/tempest/lib/services/volume/v3/base_client.py
@@ -20,4 +20,3 @@
 
 BaseClient = moves.moved_class(base_client.BaseClient, 'BaseClient', __name__,
                                version="Pike", removal_version='?')
-BaseClient.api_version = 'v3'
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..03de187
--- /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.
+
+        :param 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/group_snapshots_client.py b/tempest/lib/services/volume/v3/group_snapshots_client.py
index 6e53e3e..16412d3 100644
--- a/tempest/lib/services/volume/v3/group_snapshots_client.py
+++ b/tempest/lib/services/volume/v3/group_snapshots_client.py
@@ -23,7 +23,6 @@
 
 class GroupSnapshotsClient(base_client.BaseClient):
     """Client class to send CRUD Volume Group Snapshot API requests"""
-    api_version = 'v3'
 
     def create_group_snapshot(self, **kwargs):
         """Creates a group snapshot.
diff --git a/tempest/lib/services/volume/v3/group_types_client.py b/tempest/lib/services/volume/v3/group_types_client.py
index 1b47201..1ccb9f8 100644
--- a/tempest/lib/services/volume/v3/group_types_client.py
+++ b/tempest/lib/services/volume/v3/group_types_client.py
@@ -22,7 +22,6 @@
 
 class GroupTypesClient(base_client.BaseClient):
     """Client class to send CRUD Volume V3 Group Types API requests"""
-    api_version = 'v3'
 
     @property
     def resource_type(self):
@@ -94,7 +93,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        https://developer.openstack.org/api-ref/block-storage/v3/#create-group-specs-for-a-group-type
+        https://developer.openstack.org/api-ref/block-storage/v3/#create-or-update-group-specs-for-a-group-type
         """
         url = "group_types/%s/group_specs" % group_type_id
         post_body = json.dumps({'group_specs': group_specs})
diff --git a/tempest/lib/services/volume/v3/groups_client.py b/tempest/lib/services/volume/v3/groups_client.py
index e2e477d..3cf1e6a 100644
--- a/tempest/lib/services/volume/v3/groups_client.py
+++ b/tempest/lib/services/volume/v3/groups_client.py
@@ -23,7 +23,6 @@
 
 class GroupsClient(base_client.BaseClient):
     """Client class to send CRUD Volume Group API requests"""
-    api_version = 'v3'
 
     def create_group(self, **kwargs):
         """Creates a group.
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/messages_client.py b/tempest/lib/services/volume/v3/messages_client.py
index 0127271..47538cd 100644
--- a/tempest/lib/services/volume/v3/messages_client.py
+++ b/tempest/lib/services/volume/v3/messages_client.py
@@ -22,7 +22,6 @@
 
 class MessagesClient(base_client.BaseClient):
     """Client class to send user messages API requests."""
-    api_version = 'v3'
 
     def show_message(self, message_id):
         """Show details for a single message."""
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..22155a9
--- /dev/null
+++ b/tempest/lib/services/volume/v3/services_client.py
@@ -0,0 +1,102 @@
+# 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 Services API requests"""
+
+    def list_services(self, **params):
+        """List all Cinder services.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/block-storage/v3/#list-all-cinder-services
+        """
+        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)
+
+    def enable_service(self, **kwargs):
+        """Enable service on a host.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/block-storage/v3/#enable-a-cinder-service
+        """
+        put_body = json.dumps(kwargs)
+        resp, body = self.put('os-services/enable', put_body)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def disable_service(self, **kwargs):
+        """Disable service on a host.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/block-storage/v3/#disable-a-cinder-service
+        """
+        put_body = json.dumps(kwargs)
+        resp, body = self.put('os-services/disable', put_body)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def disable_log_reason(self, **kwargs):
+        """Disable scheduling for a volume service and log disabled reason.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/block-storage/v3/#log-disabled-cinder-service-information
+        """
+        put_body = json.dumps(kwargs)
+        resp, body = self.put('os-services/disable-log-reason', put_body)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def freeze_host(self, **kwargs):
+        """Freeze a Cinder backend host.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/block-storage/v3/#freeze-a-cinder-backend-host
+        """
+        put_body = json.dumps(kwargs)
+        resp, _ = self.put('os-services/freeze', put_body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp)
+
+    def thaw_host(self, **kwargs):
+        """Thaw a Cinder backend host.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/block-storage/v3/#thaw-a-cinder-backend-host
+        """
+        put_body = json.dumps(kwargs)
+        resp, _ = self.put('os-services/thaw', put_body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp)
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..cae65b2 100644
--- a/tempest/lib/services/volume/v3/snapshots_client.py
+++ b/tempest/lib/services/volume/v3/snapshots_client.py
@@ -13,9 +13,198 @@
 #    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"
+
+    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(202, 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 status of 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/#update-status-of-a-snapshot
+        """
+        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 for the snapshot for a specific key.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/block-storage/v3/#update-a-snapshot-s-metadata-for-a-specific-key
+        """
+        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..13ecd15
--- /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 Types 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 type extra specs created.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/block-storage/v3/#show-all-extra-specifications-for-volume-type
+        """
+        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 new volume type extra specs.
+
+        :param volume_type_id: Id of volume type.
+        :param 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.
+
+        :param volume_type_id: Id of volume type.
+        :param extra_spec_name: Name of the extra spec to be updated.
+        :param extra_specs: 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/versions_client.py b/tempest/lib/services/volume/v3/versions_client.py
index 5702f95..57629bd 100644
--- a/tempest/lib/services/volume/v3/versions_client.py
+++ b/tempest/lib/services/volume/v3/versions_client.py
@@ -22,7 +22,6 @@
 
 
 class VersionsClient(base_client.BaseClient):
-    api_version = 'v3'
 
     def list_versions(self):
         """List API versions
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..11c5767 100644
--- a/tempest/lib/services/volume/v3/volumes_client.py
+++ b/tempest/lib/services/volume/v3/volumes_client.py
@@ -14,15 +14,94 @@
 #    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 +117,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 06b4b59..dff50a9 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
     #
@@ -93,6 +94,10 @@
         if not client:
             client = self.ports_client
         name = data_utils.rand_name(self.__class__.__name__)
+        if CONF.network.port_vnic_type and 'binding:vnic_type' not in kwargs:
+            kwargs['binding:vnic_type'] = CONF.network.port_vnic_type
+        if CONF.network.port_profile and 'binding:profile' not in kwargs:
+            kwargs['binding:profile'] = CONF.network.port_profile
         result = client.create_port(
             name=name,
             network_id=network_id,
@@ -139,13 +144,20 @@
             name = data_utils.rand_name(self.__class__.__name__ + "-server")
 
         vnic_type = CONF.network.port_vnic_type
+        profile = CONF.network.port_profile
 
-        # If vnic_type is configured create port for
+        # If vnic_type or profile are configured create port for
         # every network
-        if vnic_type:
+        if vnic_type or profile:
             ports = []
+            create_port_body = {}
 
-            create_port_body = {'binding:vnic_type': vnic_type}
+            if vnic_type:
+                create_port_body['binding:vnic_type'] = vnic_type
+
+            if profile:
+                create_port_body['binding:profile'] = profile
+
             if kwargs:
                 # Convert security group names to security group ids
                 # to pass to create_port
@@ -237,6 +249,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(
@@ -252,15 +295,15 @@
         self.addCleanup(self.snapshots_client.delete_snapshot, snapshot['id'])
         waiters.wait_for_volume_resource_status(self.snapshots_client,
                                                 snapshot['id'], 'available')
+        snapshot = self.snapshots_client.show_snapshot(
+            snapshot['id'])['snapshot']
         return snapshot
 
     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)
+            client = self.os_admin.volume_types_client_latest
+        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)
@@ -404,7 +447,9 @@
                                        disk_format=img_disk_format,
                                        properties=img_properties)
         except IOError:
-            LOG.debug("A qcow2 image was not found. Try to get a uec image.")
+            LOG.warning(
+                "A(n) %s image was not found. Retrying with uec image.",
+                img_disk_format)
             kernel = self._image_create('scenario-aki', 'aki', aki_img_path)
             ramdisk = self._image_create('scenario-ari', 'ari', ari_img_path)
             properties = {'kernel_id': kernel, 'ramdisk_id': ramdisk}
@@ -502,7 +547,7 @@
                                                 volume['id'], 'available')
 
     def ping_ip_address(self, ip_address, should_succeed=True,
-                        ping_timeout=None, mtu=None):
+                        ping_timeout=None, mtu=None, server=None):
         timeout = ping_timeout or CONF.validation.ping_timeout
         cmd = ['ping', '-c1', '-w1']
 
@@ -536,12 +581,16 @@
                       'caller': caller, 'ip': ip_address, 'timeout': timeout,
                       'result': 'expected' if result else 'unexpected'
                   })
+        if server:
+            self._log_console_output([server])
         return result
 
     def check_vm_connectivity(self, ip_address,
                               username=None,
                               private_key=None,
                               should_connect=True,
+                              extra_msg="",
+                              server=None,
                               mtu=None):
         """Check server connectivity
 
@@ -551,43 +600,36 @@
         :param should_connect: True/False indicates positive/negative test
             positive - attempt ping and ssh
             negative - attempt ping and fail if succeed
+        :param extra_msg: Message to help with debugging if ``ping_ip_address``
+            fails
+        :param server: The server whose console to log for debugging
         :param mtu: network MTU to use for connectivity validation
 
         :raises: AssertError if the result of the connectivity check does
             not match the value of the should_connect param
         """
+        LOG.debug('checking network connections to IP %s with user: %s',
+                  ip_address, username)
         if should_connect:
             msg = "Timed out waiting for %s to become reachable" % ip_address
         else:
             msg = "ip address %s is reachable" % ip_address
+        if extra_msg:
+            msg = "%s\n%s" % (extra_msg, msg)
         self.assertTrue(self.ping_ip_address(ip_address,
                                              should_succeed=should_connect,
-                                             mtu=mtu),
+                                             mtu=mtu, server=server),
                         msg=msg)
         if should_connect:
             # no need to check ssh for negative connectivity
-            self.get_remote_client(ip_address, username, private_key)
-
-    def check_public_network_connectivity(self, ip_address, username,
-                                          private_key, should_connect=True,
-                                          msg=None, servers=None, mtu=None):
-        # The target login is assumed to have been configured for
-        # key-based authentication by cloud-init.
-        LOG.debug('checking network connections to IP %s with user: %s',
-                  ip_address, username)
-        try:
-            self.check_vm_connectivity(ip_address,
-                                       username,
-                                       private_key,
-                                       should_connect=should_connect,
-                                       mtu=mtu)
-        except Exception:
-            ex_msg = 'Public network connectivity check failed'
-            if msg:
-                ex_msg += ": " + msg
-            LOG.exception(ex_msg)
-            self._log_console_output(servers)
-            raise
+            try:
+                self.get_remote_client(ip_address, username, private_key,
+                                       server=server)
+            except Exception:
+                if not extra_msg:
+                    extra_msg = 'Failed to ssh to %s' % ip_address
+                LOG.exception(extra_msg)
+                raise
 
     def create_floating_ip(self, thing, pool_name=None):
         """Create a floating IP and associates to a server on Nova"""
@@ -604,9 +646,10 @@
         return floating_ip
 
     def create_timestamp(self, ip_address, dev_name=None, mount_path='/mnt',
-                         private_key=None):
+                         private_key=None, server=None):
         ssh_client = self.get_remote_client(ip_address,
-                                            private_key=private_key)
+                                            private_key=private_key,
+                                            server=server)
         if dev_name is not None:
             ssh_client.make_fs(dev_name)
             ssh_client.exec_command('sudo mount /dev/%s %s' % (dev_name,
@@ -620,9 +663,10 @@
         return timestamp
 
     def get_timestamp(self, ip_address, dev_name=None, mount_path='/mnt',
-                      private_key=None):
+                      private_key=None, server=None):
         ssh_client = self.get_remote_client(ip_address,
-                                            private_key=private_key)
+                                            private_key=private_key,
+                                            server=server)
         if dev_name is not None:
             ssh_client.mount(dev_name, mount_path)
         timestamp = ssh_client.exec_command('sudo cat %s/timestamp'
@@ -653,13 +697,18 @@
                 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:
             raise lib_exc.InvalidConfiguration()
 
+    @classmethod
+    def get_host_for_server(cls, server_id):
+        server_details = cls.os_admin.servers_client.show_server(server_id)
+        return server_details['server']['OS-EXT-SRV-ATTR:host']
+
 
 class NetworkScenarioTest(ScenarioTest):
     """Base class for network scenario tests.
@@ -768,8 +817,13 @@
         return subnet
 
     def _get_server_port_id_and_ip4(self, server, ip_addr=None):
-        ports = self.os_admin.ports_client.list_ports(
-            device_id=server['id'], fixed_ip=ip_addr)['ports']
+        if ip_addr:
+            ports = self.os_admin.ports_client.list_ports(
+                device_id=server['id'],
+                fixed_ips='ip_address=%s' % ip_addr)['ports']
+        else:
+            ports = self.os_admin.ports_client.list_ports(
+                device_id=server['id'])['ports']
         # A port can have more than one IP address in some cases.
         # If the network is dual-stack (IPv4 + IPv6), this port is associated
         # with 2 subnets
@@ -783,8 +837,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)
@@ -1159,9 +1213,9 @@
     @classmethod
     def setup_clients(cls):
         super(EncryptionScenarioTest, cls).setup_clients()
-        cls.admin_volume_types_client = cls.os_admin.volume_types_v2_client
+        cls.admin_volume_types_client = cls.os_admin.volume_types_client_latest
         cls.admin_encryption_types_client =\
-            cls.os_admin.encryption_types_v2_client
+            cls.os_admin.encryption_types_client_latest
 
     def create_encryption_type(self, client=None, type_id=None, provider=None,
                                key_size=None, cipher=None,
@@ -1174,7 +1228,7 @@
         LOG.debug("Creating an encryption type for volume type: %s", type_id)
         client.create_encryption_type(
             type_id, provider=provider, key_size=key_size, cipher=cipher,
-            control_location=control_location)['encryption']
+            control_location=control_location)
 
     def create_encrypted_volume(self, encryption_provider, volume_type,
                                 key_size=256, cipher='aes-xts-plain64',
diff --git a/tempest/scenario/test_aggregates_basic_ops.py b/tempest/scenario/test_aggregates_basic_ops.py
index 9ff6227..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.hosts_client = cls.os_admin.hosts_client
+        cls.services_client = cls.os_admin.services_client
 
     def _create_aggregate(self, **kwargs):
         aggregate = (self.aggregates_client.create_aggregate(**kwargs)
@@ -51,10 +51,10 @@
         return aggregate
 
     def _get_host_name(self):
-        hosts = self.hosts_client.list_hosts()['hosts']
-        self.assertNotEmpty(hosts)
-        computes = [x for x in hosts if x['service'] == 'compute']
-        return computes[0]['host_name']
+        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_minimum_basic.py b/tempest/scenario/test_minimum_basic.py
index 0df26ea..2b35e45 100644
--- a/tempest/scenario/test_minimum_basic.py
+++ b/tempest/scenario/test_minimum_basic.py
@@ -127,6 +127,7 @@
             floating_ip = self.create_floating_ip(server)
             # fetch the server again to make sure the addresses were refreshed
             # after associating the floating IP
+            server = self.servers_client.show_server(server['id'])['server']
             address = self._get_floating_ip_in_server_addresses(
                 floating_ip, server)
             self.assertIsNotNone(
diff --git a/tempest/scenario/test_network_advanced_server_ops.py b/tempest/scenario/test_network_advanced_server_ops.py
index e4ab11c..8827610 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)
@@ -90,9 +90,10 @@
         floating_ip_addr = floating_ip['floating_ip_address']
         # Check FloatingIP status before checking the connectivity
         self.check_floating_ip_status(floating_ip, 'ACTIVE')
-        self.check_public_network_connectivity(floating_ip_addr, username,
-                                               private_key, should_connect,
-                                               servers=[server])
+        self.check_vm_connectivity(floating_ip_addr, username,
+                                   private_key, should_connect,
+                                   'Public network connectivity check failed',
+                                   server)
 
     def _wait_server_status_and_check_network_connectivity(self, server,
                                                            keypair,
@@ -101,10 +102,6 @@
                                        'ACTIVE')
         self._check_network_connectivity(server, keypair, floating_ip)
 
-    def _get_host_for_server(self, server_id):
-        body = self.admin_servers_client.show_server(server_id)['server']
-        return body['OS-EXT-SRV-ATTR:host']
-
     @decorators.idempotent_id('61f1aa9a-1573-410e-9054-afa557cab021')
     @decorators.attr(type='slow')
     @utils.services('compute', 'network')
@@ -196,7 +193,14 @@
                                        'VERIFY_RESIZE')
         self.servers_client.confirm_resize_server(server['id'])
         server = self.servers_client.show_server(server['id'])['server']
-        self.assertEqual(resize_flavor, server['flavor']['id'])
+        # Nova API > 2.46 no longer includes flavor.id, and schema check
+        # will cover whether 'id' should be in flavor
+        if server['flavor'].get('id'):
+            self.assertEqual(resize_flavor, server['flavor']['id'])
+        else:
+            flavor = self.flavors_client.show_flavor(resize_flavor)['flavor']
+            for key in ['original_name', 'ram', 'vcpus', 'disk']:
+                self.assertEqual(flavor[key], server['flavor'][key])
         self._wait_server_status_and_check_network_connectivity(
             server, keypair, floating_ip)
 
@@ -212,7 +216,7 @@
         keypair = self.create_keypair()
         server = self._setup_server(keypair)
         floating_ip = self._setup_network(server, keypair)
-        src_host = self._get_host_for_server(server['id'])
+        src_host = self.get_host_for_server(server['id'])
         self._wait_server_status_and_check_network_connectivity(
             server, keypair, floating_ip)
 
@@ -222,7 +226,7 @@
         self.servers_client.confirm_resize_server(server['id'])
         self._wait_server_status_and_check_network_connectivity(
             server, keypair, floating_ip)
-        dst_host = self._get_host_for_server(server['id'])
+        dst_host = self.get_host_for_server(server['id'])
 
         self.assertNotEqual(src_host, dst_host)
 
@@ -238,7 +242,7 @@
         keypair = self.create_keypair()
         server = self._setup_server(keypair)
         floating_ip = self._setup_network(server, keypair)
-        src_host = self._get_host_for_server(server['id'])
+        src_host = self.get_host_for_server(server['id'])
         self._wait_server_status_and_check_network_connectivity(
             server, keypair, floating_ip)
 
@@ -248,6 +252,6 @@
         self.servers_client.revert_resize_server(server['id'])
         self._wait_server_status_and_check_network_connectivity(
             server, keypair, floating_ip)
-        dst_host = self._get_host_for_server(server['id'])
+        dst_host = self.get_host_for_server(server['id'])
 
         self.assertEqual(src_host, dst_host)
diff --git a/tempest/scenario/test_network_basic_ops.py b/tempest/scenario/test_network_basic_ops.py
index ff8837f..c1132cf 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)
@@ -175,7 +175,7 @@
     def _get_server_key(self, server):
         return self.keypairs[server['key_name']]['private_key']
 
-    def check_public_network_connectivity(
+    def _check_public_network_connectivity(
             self, should_connect=True, msg=None,
             should_check_floating_ip_status=True, mtu=None):
         """Verifies connectivty to a VM via public network and floating IP
@@ -199,13 +199,18 @@
         if should_connect:
             private_key = self._get_server_key(server)
             floatingip_status = 'ACTIVE'
+
         # Check FloatingIP Status before initiating a connection
         if should_check_floating_ip_status:
             self.check_floating_ip_status(floating_ip, floatingip_status)
-        # call the common method in the parent class
-        super(TestNetworkBasicOps, self).check_public_network_connectivity(
-            ip_address, ssh_login, private_key, should_connect, msg,
-            self.servers, mtu=mtu)
+
+        message = 'Public network connectivity check failed'
+        if msg:
+            message += '. Reason: %s' % msg
+
+        self.check_vm_connectivity(
+            ip_address, ssh_login, private_key, should_connect,
+            message, server, mtu=mtu)
 
     def _disassociate_floating_ips(self):
         floating_ip, _ = self.floating_ip_tuple
@@ -304,16 +309,19 @@
 
         - ping internal gateway and DHCP port, implying in-tenant connectivity
         pinging both, because L3 and DHCP agents might be on different nodes
+        - ping internal compute port, implying connectivity to other VMs on
+        this network
         """
         floating_ip, server = self.floating_ip_tuple
         # get internal ports' ips:
-        # get all network ports in the new network
+        # get all network and compute ports in the new network
         internal_ips = (
             p['fixed_ips'][0]['ip_address'] for p in
             self.os_admin.ports_client.list_ports(
                 tenant_id=server['tenant_id'],
                 network_id=network['id'])['ports']
-            if p['device_owner'].startswith('network')
+            if p['device_owner'].startswith('network') or
+            p['device_owner'].startswith('compute')
         )
 
         self._check_server_connectivity(floating_ip,
@@ -401,17 +409,17 @@
 
         """
         self._setup_network_and_servers()
-        self.check_public_network_connectivity(should_connect=True)
+        self._check_public_network_connectivity(should_connect=True)
         self._check_network_internal_connectivity(network=self.network)
         self._check_network_external_connectivity()
         self._disassociate_floating_ips()
-        self.check_public_network_connectivity(should_connect=False,
-                                               msg="after disassociate "
-                                                   "floating ip")
+        self._check_public_network_connectivity(should_connect=False,
+                                                msg="after disassociate "
+                                                    "floating ip")
         self._reassociate_floating_ips()
-        self.check_public_network_connectivity(should_connect=True,
-                                               msg="after re-associate "
-                                                   "floating ip")
+        self._check_public_network_connectivity(should_connect=True,
+                                                msg="after re-associate "
+                                                    "floating ip")
 
     @decorators.idempotent_id('b158ea55-472e-4086-8fa9-c64ac0c6c1d0')
     @testtools.skipUnless(utils.is_extension_enabled('net-mtu', 'network'),
@@ -422,17 +430,16 @@
         """Validate that network MTU sized frames fit through."""
         self._setup_network_and_servers()
         # first check that connectivity works in general for the instance
-        self.check_public_network_connectivity(should_connect=True)
+        self._check_public_network_connectivity(should_connect=True)
         # now that we checked general connectivity, test that full size frames
         # can also pass between nodes
-        self.check_public_network_connectivity(
+        self._check_public_network_connectivity(
             should_connect=True, mtu=self.network['mtu'])
 
     @decorators.idempotent_id('1546850e-fbaa-42f5-8b5f-03d8a6a95f15')
     @testtools.skipIf(CONF.network.shared_physical_network,
                       'Connectivity can only be tested when in a '
                       'multitenant network environment')
-    @decorators.skip_because(bug="1610994")
     @decorators.attr(type='slow')
     @utils.services('compute', 'network')
     def test_connectivity_between_vms_on_different_networks(self):
@@ -465,7 +472,7 @@
 
         """
         self._setup_network_and_servers()
-        self.check_public_network_connectivity(should_connect=True)
+        self._check_public_network_connectivity(should_connect=True)
         self._check_network_internal_connectivity(network=self.network)
         self._check_network_external_connectivity()
         self._create_new_network(create_gateway=True)
@@ -500,7 +507,7 @@
 
         """
         self._setup_network_and_servers()
-        self.check_public_network_connectivity(should_connect=True)
+        self._check_public_network_connectivity(should_connect=True)
         self._create_new_network()
         self._hotplug_server()
         self._check_network_internal_connectivity(network=self.new_net)
@@ -522,19 +529,19 @@
                 admin_state_up attribute of router to True
         """
         self._setup_network_and_servers()
-        self.check_public_network_connectivity(
+        self._check_public_network_connectivity(
             should_connect=True, msg="before updating "
             "admin_state_up of router to False")
         self._update_router_admin_state(self.router, False)
         # TODO(alokmaurya): Remove should_check_floating_ip_status=False check
         # once bug 1396310 is fixed
 
-        self.check_public_network_connectivity(
+        self._check_public_network_connectivity(
             should_connect=False, msg="after updating "
             "admin_state_up of router to False",
             should_check_floating_ip_status=False)
         self._update_router_admin_state(self.router, True)
-        self.check_public_network_connectivity(
+        self._check_public_network_connectivity(
             should_connect=True, msg="after updating "
             "admin_state_up of router to True")
 
@@ -579,7 +586,7 @@
         renew_timeout = CONF.network.build_timeout
 
         self._setup_network_and_servers(dns_nameservers=[initial_dns_server])
-        self.check_public_network_connectivity(should_connect=True)
+        self._check_public_network_connectivity(should_connect=True)
 
         floating_ip, server = self.floating_ip_tuple
         ip_address = floating_ip['floating_ip_address']
@@ -654,20 +661,20 @@
                                             private_key=private_key,
                                             server=server2)
 
-        self.check_public_network_connectivity(
+        self._check_public_network_connectivity(
             should_connect=True, msg="before updating "
             "admin_state_up of instance port to False")
         self.check_remote_connectivity(ssh_client, dest=server_pip,
                                        should_succeed=True)
         self.ports_client.update_port(port_id, admin_state_up=False)
-        self.check_public_network_connectivity(
+        self._check_public_network_connectivity(
             should_connect=False, msg="after updating "
             "admin_state_up of instance port to False",
             should_check_floating_ip_status=False)
         self.check_remote_connectivity(ssh_client, dest=server_pip,
                                        should_succeed=False)
         self.ports_client.update_port(port_id, admin_state_up=True)
-        self.check_public_network_connectivity(
+        self._check_public_network_connectivity(
             should_connect=True, msg="after updating "
             "admin_state_up of instance port to True")
         self.check_remote_connectivity(ssh_client, dest=server_pip,
@@ -764,7 +771,7 @@
             msg = "Rescheduling test does not apply to distributed routers."
             raise self.skipException(msg)
 
-        self.check_public_network_connectivity(should_connect=True)
+        self._check_public_network_connectivity(should_connect=True)
 
         # remove resource from agents
         hosting_agents = set(a["id"] for a in
@@ -781,7 +788,7 @@
                              'unscheduling router failed')
 
         # verify resource is un-functional
-        self.check_public_network_connectivity(
+        self._check_public_network_connectivity(
             should_connect=False,
             msg='after router unscheduling',
         )
@@ -798,7 +805,7 @@
             "target agent")
 
         # verify resource is functional
-        self.check_public_network_connectivity(
+        self._check_public_network_connectivity(
             should_connect=True,
             msg='After router rescheduling')
 
@@ -832,7 +839,7 @@
 
         # Create server
         self._setup_network_and_servers()
-        self.check_public_network_connectivity(should_connect=True)
+        self._check_public_network_connectivity(should_connect=True)
         self._create_new_network()
         self._hotplug_server()
         fip, server = self.floating_ip_tuple
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_security_groups_basic_ops.py b/tempest/scenario/test_security_groups_basic_ops.py
index e39afe0..28a2d64 100644
--- a/tempest/scenario/test_security_groups_basic_ops.py
+++ b/tempest/scenario/test_security_groups_basic_ops.py
@@ -282,11 +282,8 @@
 
         # Verify servers are on different compute nodes
         if self.multi_node:
-            adm_get_server = self.os_admin.servers_client.show_server
-            new_host = adm_get_server(server["id"])["server"][
-                "OS-EXT-SRV-ATTR:host"]
-            host_list = [adm_get_server(s)["server"]["OS-EXT-SRV-ATTR:host"]
-                         for s in self.servers]
+            new_host = self.get_host_for_server(server["id"])
+            host_list = [self.get_host_for_server(s) for s in self.servers]
             self.assertNotIn(new_host, host_list,
                              message="Failed to boot servers on different "
                                      "Compute nodes.")
@@ -369,7 +366,8 @@
             self.floating_ips[tenant.access_point['id']]['floating_ip_address']
         private_key = tenant.keypair['private_key']
         access_point_ssh = self.get_remote_client(
-            access_point_ssh, private_key=private_key)
+            access_point_ssh, private_key=private_key,
+            server=tenant.access_point)
         return access_point_ssh
 
     def _test_in_tenant_block(self, tenant):
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_server_basic_ops.py b/tempest/scenario/test_server_basic_ops.py
index 1be8625..1671216 100644
--- a/tempest/scenario/test_server_basic_ops.py
+++ b/tempest/scenario/test_server_basic_ops.py
@@ -14,7 +14,6 @@
 #    under the License.
 
 import json
-import re
 
 from tempest.common import utils
 from tempest.common import waiters
@@ -94,22 +93,13 @@
             result = self.servers_client.show_password(self.instance['id'])
             self.assertEqual(data, result['password'])
 
-    def _mount_config_drive(self):
-        cmd_blkid = 'blkid | grep -i config-2'
-        result = self.ssh_client.exec_command(cmd_blkid)
-        dev_name = re.match('([^:]+)', result).group()
-        self.ssh_client.exec_command('sudo mount %s /mnt' % dev_name)
-
-    def _unmount_config_drive(self):
-        self.ssh_client.exec_command('sudo umount /mnt')
-
     def verify_metadata_on_config_drive(self):
         if self.run_ssh and CONF.compute_feature_enabled.config_drive:
             # Verify metadata on config_drive
-            self._mount_config_drive()
+            self.ssh_client.mount_config_drive()
             cmd_md = 'sudo cat /mnt/openstack/latest/meta_data.json'
             result = self.ssh_client.exec_command(cmd_md)
-            self._unmount_config_drive()
+            self.ssh_client.unmount_config_drive()
             result = json.loads(result)
             self.assertIn('meta', result)
             msg = ('Failed while verifying metadata on config_drive on server.'
@@ -119,10 +109,10 @@
     def verify_networkdata_on_config_drive(self):
         if self.run_ssh and CONF.compute_feature_enabled.config_drive:
             # Verify network data on config_drive
-            self._mount_config_drive()
+            self.ssh_client.mount_config_drive()
             cmd_md = 'sudo cat /mnt/openstack/latest/network_data.json'
             result = self.ssh_client.exec_command(cmd_md)
-            self._unmount_config_drive()
+            self.ssh_client.unmount_config_drive()
             result = json.loads(result)
             self.assertIn('services', result)
             self.assertIn('links', result)
diff --git a/tempest/scenario/test_shelve_instance.py b/tempest/scenario/test_shelve_instance.py
index 68f18d1..d6b6d14 100644
--- a/tempest/scenario/test_shelve_instance.py
+++ b/tempest/scenario/test_shelve_instance.py
@@ -63,7 +63,8 @@
 
         instance_ip = self.get_server_ip(server)
         timestamp = self.create_timestamp(instance_ip,
-                                          private_key=keypair['private_key'])
+                                          private_key=keypair['private_key'],
+                                          server=server)
 
         # Prevent bug #1257594 from coming back
         # Unshelve used to boot the instance with the original image, not
@@ -71,7 +72,8 @@
         self._shelve_then_unshelve_server(server)
 
         timestamp2 = self.get_timestamp(instance_ip,
-                                        private_key=keypair['private_key'])
+                                        private_key=keypair['private_key'],
+                                        server=server)
         self.assertEqual(timestamp, timestamp2)
 
     @decorators.attr(type='slow')
diff --git a/tempest/scenario/test_snapshot_pattern.py b/tempest/scenario/test_snapshot_pattern.py
index b51a781..a33d4d4 100644
--- a/tempest/scenario/test_snapshot_pattern.py
+++ b/tempest/scenario/test_snapshot_pattern.py
@@ -57,7 +57,8 @@
 
         instance_ip = self.get_server_ip(server)
         timestamp = self.create_timestamp(instance_ip,
-                                          private_key=keypair['private_key'])
+                                          private_key=keypair['private_key'],
+                                          server=server)
 
         # snapshot the instance
         snapshot_image = self.create_server_snapshot(server=server)
@@ -71,5 +72,6 @@
         # check the existence of the timestamp file in the second instance
         server_from_snapshot_ip = self.get_server_ip(server_from_snapshot)
         timestamp2 = self.get_timestamp(server_from_snapshot_ip,
-                                        private_key=keypair['private_key'])
+                                        private_key=keypair['private_key'],
+                                        server=server_from_snapshot)
         self.assertEqual(timestamp, timestamp2)
diff --git a/tempest/scenario/test_stamp_pattern.py b/tempest/scenario/test_stamp_pattern.py
index ef369d6..2782119 100644
--- a/tempest/scenario/test_stamp_pattern.py
+++ b/tempest/scenario/test_stamp_pattern.py
@@ -96,7 +96,8 @@
                                                       keypair['private_key'])
         timestamp = self.create_timestamp(ip_for_server,
                                           CONF.compute.volume_device_name,
-                                          private_key=keypair['private_key'])
+                                          private_key=keypair['private_key'],
+                                          server=server)
         self.nova_volume_detach(server, volume)
 
         # snapshot the volume
@@ -126,5 +127,6 @@
         # check the existence of the timestamp file in the volume2
         timestamp2 = self.get_timestamp(ip_for_snapshot,
                                         CONF.compute.volume_device_name,
-                                        private_key=keypair['private_key'])
+                                        private_key=keypair['private_key'],
+                                        server=server_from_snapshot)
         self.assertEqual(timestamp, timestamp2)
diff --git a/tempest/scenario/test_volume_backup_restore.py b/tempest/scenario/test_volume_backup_restore.py
new file mode 100644
index 0000000..8a8c54e
--- /dev/null
+++ b/tempest/scenario/test_volume_backup_restore.py
@@ -0,0 +1,94 @@
+# 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.common import waiters
+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'])
+        waiters.wait_for_volume_resource_status(self.volumes_client,
+                                                volume['id'], 'available')
+
+        # 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 beb039c..810480b 100644
--- a/tempest/scenario/test_volume_boot_pattern.py
+++ b/tempest/scenario/test_volume_boot_pattern.py
@@ -11,6 +11,7 @@
 #    under the License.
 
 from oslo_log import log as logging
+from oslo_serialization import jsonutils
 import testtools
 
 from tempest.common import utils
@@ -30,12 +31,6 @@
     # breathing room to get through deletes in the time allotted.
     TIMEOUT_SCALING_FACTOR = 2
 
-    @classmethod
-    def skip_checks(cls):
-        super(TestVolumeBootPattern, cls).skip_checks()
-        if not CONF.volume_feature_enabled.snapshot:
-            raise cls.skipException("Cinder volume snapshots are disabled")
-
     def _create_volume_from_image(self):
         img_uuid = CONF.compute.image_ref
         vol_name = data_utils.rand_name(
@@ -55,7 +50,8 @@
                                      source_type,
                                      keypair=None,
                                      security_group=None,
-                                     delete_on_termination=False):
+                                     delete_on_termination=False,
+                                     name=None):
         create_kwargs = dict()
         if keypair:
             create_kwargs['key_name'] = keypair['name']
@@ -66,6 +62,8 @@
             source_id,
             source_type,
             delete_on_termination=delete_on_termination))
+        if name:
+            create_kwargs['name'] = name
 
         return self.create_server(image_id='', **create_kwargs)
 
@@ -73,9 +71,18 @@
         self.servers_client.delete_server(server['id'])
         waiters.wait_for_server_termination(self.servers_client, server['id'])
 
+    def _delete_snapshot(self, snapshot_id):
+        self.snapshots_client.delete_snapshot(snapshot_id)
+        self.snapshots_client.wait_for_resource_deletion(snapshot_id)
+
     @decorators.idempotent_id('557cd2c2-4eb8-4dce-98be-f86765ff311b')
+    # Note: This test is being skipped based on 'public_network_id'.
+    # It is being used in create_floating_ip() method which gets called
+    # from get_server_ip() method
     @testtools.skipUnless(CONF.network.public_network_id,
                           'The public_network_id option must be specified.')
+    @testtools.skipUnless(CONF.volume_feature_enabled.snapshot,
+                          'Cinder volume snapshots are disabled')
     @utils.services('compute', 'volume', 'image')
     def test_volume_boot_pattern(self):
 
@@ -109,7 +116,8 @@
         LOG.info("Setting timestamp in instance %s", instance_1st)
         ip_instance_1st = self.get_server_ip(instance_1st)
         timestamp = self.create_timestamp(ip_instance_1st,
-                                          private_key=keypair['private_key'])
+                                          private_key=keypair['private_key'],
+                                          server=instance_1st)
 
         # delete instance
         LOG.info("Deleting first instance: %s", instance_1st)
@@ -127,7 +135,8 @@
         LOG.info("Getting timestamp in instance %s", instance_2nd)
         ip_instance_2nd = self.get_server_ip(instance_2nd)
         timestamp2 = self.get_timestamp(ip_instance_2nd,
-                                        private_key=keypair['private_key'])
+                                        private_key=keypair['private_key'],
+                                        server=instance_2nd)
         self.assertEqual(timestamp, timestamp2)
 
         # snapshot a volume
@@ -151,11 +160,14 @@
                  server_from_snapshot)
         server_from_snapshot_ip = self.get_server_ip(server_from_snapshot)
         timestamp3 = self.get_timestamp(server_from_snapshot_ip,
-                                        private_key=keypair['private_key'])
+                                        private_key=keypair['private_key'],
+                                        server=server_from_snapshot)
         self.assertEqual(timestamp, timestamp3)
 
     @decorators.idempotent_id('05795fb2-b2a7-4c9f-8fac-ff25aedb1489')
     @decorators.attr(type='slow')
+    @testtools.skipUnless(CONF.volume_feature_enabled.snapshot,
+                          'Cinder volume snapshots are disabled')
     @utils.services('compute', 'image', 'volume')
     def test_create_server_from_volume_snapshot(self):
         # Create a volume from an image
@@ -192,38 +204,76 @@
                          created_volume_info['attachments'][0]['volume_id'])
 
     @decorators.idempotent_id('36c34c67-7b54-4b59-b188-02a2f458a63b')
+    @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(
+        name = data_utils.rand_name(self.__class__.__name__ +
+                                    '-volume-backed-server')
+        instance1 = self._boot_instance_from_resource(
             source_id=volume_origin['id'],
             source_type='volume',
-            delete_on_termination=True)
-        # create EBS image
-        image = self.create_server_snapshot(instance)
+            delete_on_termination=True,
+            name=name)
+        # 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(instance1)
 
-        # delete instance
-        self._delete_server(instance)
+        # 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.
+        name = data_utils.rand_name(self.__class__.__name__ +
+                                    '-image-snapshot-server')
+        instance2 = self.create_server(image_id=image['id'], name=name)
 
-        # boot instance from EBS image
-        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']
-        self.assertEqual(instance['id'],
-                         created_volume_info['attachments'][0]['server_id'])
-        self.assertEqual(created_volume[0]['id'],
-                         created_volume_info['attachments'][0]['volume_id'])
+        # Verify the server was created from the image-defined BDM.
+        volume_attachments = instance2['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(instance2['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
-        self._delete_server(instance)
+        # Delete the second server which should also delete the second volume
+        # created from the volume snapshot.
+        self._delete_server(instance2)
+
+        # Assert that the underlying volume is gone.
+        self.volumes_client.wait_for_resource_deletion(created_volume['id'])
+
+        # Delete the volume snapshot. We must do this before deleting the first
+        # server created in this test because the snapshot depends on the first
+        # instance's underlying volume (volume_origin).
+        # In glance v2, the image properties are flattened and in glance v1,
+        # the image properties are under the 'properties' key.
+        bdms = image.get('block_device_mapping')
+        if not bdms:
+            bdms = image['properties']['block_device_mapping']
+        bdms = jsonutils.loads(bdms)
+        snapshot_id = bdms[0]['snapshot_id']
+        self._delete_snapshot(snapshot_id)
+
+        # Now, delete the first server which will also delete the first
+        # image-backed volume.
+        self._delete_server(instance1)
+
+        # Assert that the underlying volume is gone.
+        self.volumes_client.wait_for_resource_deletion(volume_origin['id'])
 
     @decorators.idempotent_id('cb78919a-e553-4bab-b73b-10cf4d2eb125')
     @testtools.skipUnless(CONF.compute_feature_enabled.attach_encrypted_volume,
diff --git a/tempest/scenario/test_volume_migrate_attached.py b/tempest/scenario/test_volume_migrate_attached.py
index ff7996a..c54bb38 100644
--- a/tempest/scenario/test_volume_migrate_attached.py
+++ b/tempest/scenario/test_volume_migrate_attached.py
@@ -40,7 +40,7 @@
     @classmethod
     def setup_clients(cls):
         super(TestVolumeMigrateRetypeAttached, cls).setup_clients()
-        cls.admin_volumes_client = cls.os_admin.volumes_v2_client
+        cls.admin_volumes_client = cls.os_admin.volumes_client_latest
 
     @classmethod
     def skip_checks(cls):
@@ -114,7 +114,8 @@
         LOG.info("Setting timestamp in instance %s", instance['id'])
         ip_instance = self.get_server_ip(instance)
         timestamp = self.create_timestamp(ip_instance,
-                                          private_key=keypair['private_key'])
+                                          private_key=keypair['private_key'],
+                                          server=instance)
 
         # retype volume with migration from backend #1 to backend #2
         LOG.info("Retyping Volume %s to new type %s", volume_origin['id'],
@@ -125,5 +126,6 @@
         LOG.info("Getting timestamp in postmigrated instance %s",
                  instance['id'])
         timestamp2 = self.get_timestamp(ip_instance,
-                                        private_key=keypair['private_key'])
+                                        private_key=keypair['private_key'],
+                                        server=instance)
         self.assertEqual(timestamp, timestamp2)
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/cmd/test_run.py b/tempest/tests/cmd/test_run.py
index 0485e14..f55df30 100644
--- a/tempest/tests/cmd/test_run.py
+++ b/tempest/tests/cmd/test_run.py
@@ -21,6 +21,7 @@
 
 import fixtures
 import mock
+import six
 
 from tempest.cmd import run
 from tempest.tests import base
@@ -35,70 +36,25 @@
         super(TestTempestRun, self).setUp()
         self.run_cmd = run.TempestRun(None, None)
 
-    def test_build_options(self):
-        args = mock.Mock(spec=argparse.Namespace)
-        setattr(args, "subunit", True)
-        setattr(args, "parallel", False)
-        setattr(args, "concurrency", 10)
-        setattr(args, "load_list", '')
-        options = self.run_cmd._build_options(args)
-        self.assertEqual(['--subunit',
-                          '--concurrency=10'],
-                         options)
-
     def test__build_regex_default(self):
         args = mock.Mock(spec=argparse.Namespace)
         setattr(args, 'smoke', False)
         setattr(args, 'regex', '')
-        setattr(args, 'whitelist_file', None)
-        setattr(args, 'blacklist_file', None)
-        self.assertEqual('', self.run_cmd._build_regex(args))
+        self.assertIsNone(None, self.run_cmd._build_regex(args))
 
     def test__build_regex_smoke(self):
         args = mock.Mock(spec=argparse.Namespace)
         setattr(args, "smoke", True)
         setattr(args, 'regex', '')
-        setattr(args, 'whitelist_file', None)
-        setattr(args, 'blacklist_file', None)
-        self.assertEqual('smoke', self.run_cmd._build_regex(args))
+        self.assertEqual(['smoke'], self.run_cmd._build_regex(args))
 
     def test__build_regex_regex(self):
         args = mock.Mock(spec=argparse.Namespace)
         setattr(args, 'smoke', False)
         setattr(args, "regex", 'i_am_a_fun_little_regex')
-        setattr(args, 'whitelist_file', None)
-        setattr(args, 'blacklist_file', None)
-        self.assertEqual('i_am_a_fun_little_regex',
+        self.assertEqual(['i_am_a_fun_little_regex'],
                          self.run_cmd._build_regex(args))
 
-    def test__build_whitelist_file(self):
-        args = mock.Mock(spec=argparse.Namespace)
-        setattr(args, 'smoke', False)
-        setattr(args, 'regex', None)
-        self.tests = tempfile.NamedTemporaryFile(
-            prefix='whitelist', delete=False)
-        self.tests.write(b"volume \n compute")
-        self.tests.close()
-        setattr(args, 'whitelist_file', self.tests.name)
-        setattr(args, 'blacklist_file', None)
-        self.assertEqual("volume|compute",
-                         self.run_cmd._build_regex(args))
-        os.unlink(self.tests.name)
-
-    def test__build_blacklist_file(self):
-        args = mock.Mock(spec=argparse.Namespace)
-        setattr(args, 'smoke', False)
-        setattr(args, 'regex', None)
-        self.tests = tempfile.NamedTemporaryFile(
-            prefix='blacklist', delete=False)
-        self.tests.write(b"volume \n compute")
-        self.tests.close()
-        setattr(args, 'whitelist_file', None)
-        setattr(args, 'blacklist_file', self.tests.name)
-        self.assertEqual("^((?!compute|volume).)*$",
-                         self.run_cmd._build_regex(args))
-        os.unlink(self.tests.name)
-
 
 class TestRunReturnCode(base.TestCase):
     def setUp(self):
@@ -109,13 +65,13 @@
         self.test_dir = os.path.join(self.directory, 'tests')
         os.mkdir(self.test_dir)
         # Setup Test files
-        self.testr_conf_file = os.path.join(self.directory, '.testr.conf')
+        self.stestr_conf_file = os.path.join(self.directory, '.stestr.conf')
         self.setup_cfg_file = os.path.join(self.directory, 'setup.cfg')
         self.passing_file = os.path.join(self.test_dir, 'test_passing.py')
         self.failing_file = os.path.join(self.test_dir, 'test_failing.py')
         self.init_file = os.path.join(self.test_dir, '__init__.py')
         self.setup_py = os.path.join(self.directory, 'setup.py')
-        shutil.copy('tempest/tests/files/testr-conf', self.testr_conf_file)
+        shutil.copy('tempest/tests/files/testr-conf', self.stestr_conf_file)
         shutil.copy('tempest/tests/files/passing-tests', self.passing_file)
         shutil.copy('tempest/tests/files/failing-tests', self.failing_file)
         shutil.copy('setup.py', self.setup_py)
@@ -132,29 +88,57 @@
         msg = ("Running %s got an unexpected returncode\n"
                "Stdout: %s\nStderr: %s" % (' '.join(cmd), out, err))
         self.assertEqual(p.returncode, expected, msg)
+        return out, err
 
     def test_tempest_run_passes(self):
-        # Git init is required for the pbr testr command. pbr requires a git
-        # version or an sdist to work. so make the test directory a git repo
-        # too.
-        subprocess.call(['git', 'init'], stderr=DEVNULL)
         self.assertRunExit(['tempest', 'run', '--regex', 'passing'], 0)
 
-    def test_tempest_run_passes_with_testrepository(self):
-        # Git init is required for the pbr testr command. pbr requires a git
-        # version or an sdist to work. so make the test directory a git repo
-        # too.
-        subprocess.call(['git', 'init'], stderr=DEVNULL)
-        subprocess.call(['testr', 'init'])
+    def test_tempest_run_passes_with_stestr_repository(self):
+        subprocess.call(['stestr', 'init'])
         self.assertRunExit(['tempest', 'run', '--regex', 'passing'], 0)
 
     def test_tempest_run_fails(self):
-        # Git init is required for the pbr testr command. pbr requires a git
-        # version or an sdist to work. so make the test directory a git repo
-        # too.
-        subprocess.call(['git', 'init'], stderr=DEVNULL)
         self.assertRunExit(['tempest', 'run'], 1)
 
+    def test_run_list(self):
+        subprocess.call(['stestr', 'init'])
+        out, err = self.assertRunExit(['tempest', 'run', '-l'], 0)
+        tests = out.split()
+        tests = sorted([six.text_type(x.rstrip()) for x in tests if x])
+        result = [
+            six.text_type('tests.test_failing.FakeTestClass.test_pass'),
+            six.text_type('tests.test_failing.FakeTestClass.test_pass_list'),
+            six.text_type('tests.test_passing.FakeTestClass.test_pass'),
+            six.text_type('tests.test_passing.FakeTestClass.test_pass_list'),
+        ]
+        # NOTE(mtreinish): on python 3 the subprocess prints b'' around
+        # stdout.
+        if six.PY3:
+            result = ["b\'" + x + "\'" for x in result]
+        self.assertEqual(result, tests)
+
+    def test_tempest_run_with_whitelist(self):
+        fd, path = tempfile.mkstemp()
+        self.addCleanup(os.remove, path)
+        whitelist_file = os.fdopen(fd, 'wb', 0)
+        self.addCleanup(whitelist_file.close)
+        whitelist_file.write('passing'.encode('utf-8'))
+        self.assertRunExit(['tempest', 'run', '--whitelist-file=%s' % path], 0)
+
+    def test_tempest_run_with_whitelist_with_regex(self):
+        fd, path = tempfile.mkstemp()
+        self.addCleanup(os.remove, path)
+        whitelist_file = os.fdopen(fd, 'wb', 0)
+        self.addCleanup(whitelist_file.close)
+        whitelist_file.write('passing'.encode('utf-8'))
+        self.assertRunExit(['tempest', 'run', '--whitelist-file=%s' % path,
+                            '--regex', 'fail'], 1)
+
+    def test_tempest_run_passes_with_config_file(self):
+        self.assertRunExit(['tempest', 'run',
+                            '--config-file', self.stestr_conf_file,
+                            '--regex', 'passing'], 0)
+
 
 class TestTakeAction(base.TestCase):
     def test_workspace_not_registered(self):
@@ -183,3 +167,27 @@
         self.assertRaises(Exception_, tempest_run.take_action, parsed_args)
         exit_msg = m_exit.call_args[0][0]
         self.assertIn(workspace, exit_msg)
+
+    def test_config_file_specified(self):
+        # Setup test dirs
+        self.directory = tempfile.mkdtemp(prefix='tempest-unit')
+        self.addCleanup(shutil.rmtree, self.directory)
+        self.test_dir = os.path.join(self.directory, 'tests')
+        os.mkdir(self.test_dir)
+        # Change directory, run wrapper and check result
+        self.addCleanup(os.chdir, os.path.abspath(os.curdir))
+        os.chdir(self.directory)
+
+        tempest_run = run.TempestRun(app=mock.Mock(), app_args=mock.Mock())
+        parsed_args = mock.Mock()
+        parsed_args.config_file = []
+
+        parsed_args.workspace = None
+        parsed_args.state = None
+        parsed_args.list_tests = False
+        parsed_args.config_file = '.stestr.conf'
+
+        with mock.patch('stestr.commands.run_command') as m:
+            m.return_value = 0
+            self.assertEqual(0, tempest_run.take_action(parsed_args))
+            m.assert_called()
diff --git a/tempest/tests/cmd/test_subunit_describe_calls.py b/tempest/tests/cmd/test_subunit_describe_calls.py
index 5f3d770..cb34ba6 100644
--- a/tempest/tests/cmd/test_subunit_describe_calls.py
+++ b/tempest/tests/cmd/test_subunit_describe_calls.py
@@ -33,15 +33,42 @@
         p.communicate()
         self.assertEqual(0, p.returncode)
 
+    def test_verbose(self):
+        subunit_file = os.path.join(
+            os.path.dirname(os.path.abspath(__file__)),
+            'sample_streams/calls.subunit')
+        p = subprocess.Popen([
+            'subunit-describe-calls', '-s', subunit_file,
+            '-v'], stdin=subprocess.PIPE, stdout=subprocess.PIPE)
+        stdout = p.communicate()
+        self.assertEqual(0, p.returncode)
+        self.assertIn(b'- request headers:', stdout[0])
+        self.assertIn(b'- request body:', stdout[0])
+        self.assertIn(b'- response headers:', stdout[0])
+        self.assertIn(b'- response body:', stdout[0])
+
     def test_return_code_no_output(self):
         subunit_file = os.path.join(
             os.path.dirname(os.path.abspath(__file__)),
             'sample_streams/calls.subunit')
         p = subprocess.Popen([
             'subunit-describe-calls', '-s', subunit_file],
-            stdin=subprocess.PIPE)
-        p.communicate()
+            stdin=subprocess.PIPE, stdout=subprocess.PIPE)
+        stdout = p.communicate()
         self.assertEqual(0, p.returncode)
+        self.assertIn(b'foo', stdout[0])
+        self.assertIn(b'- 200 POST request for Nova to v2.1/<id>/',
+                      stdout[0])
+        self.assertIn(b'- 200 DELETE request for Nova to v2.1/<id>/',
+                      stdout[0])
+        self.assertIn(b'- 200 GET request for Nova to v2.1/<id>/',
+                      stdout[0])
+        self.assertIn(b'- 404 DELETE request for Nova to v2.1/<id>/',
+                      stdout[0])
+        self.assertNotIn(b'- request headers:', stdout[0])
+        self.assertNotIn(b'- request body:', stdout[0])
+        self.assertNotIn(b'- response headers:', stdout[0])
+        self.assertNotIn(b'- response body:', stdout[0])
 
     def test_parse(self):
         subunit_file = os.path.join(
diff --git a/tempest/tests/cmd/test_tempest_init.py b/tempest/tests/cmd/test_tempest_init.py
index 79510be..9042b12 100644
--- a/tempest/tests/cmd/test_tempest_init.py
+++ b/tempest/tests/cmd/test_tempest_init.py
@@ -22,21 +22,21 @@
 
 class TestTempestInit(base.TestCase):
 
-    def test_generate_testr_conf(self):
+    def test_generate_stestr_conf(self):
         # Create fake conf dir
         conf_dir = self.useFixture(fixtures.TempDir())
 
         init_cmd = init.TempestInit(None, None)
-        init_cmd.generate_testr_conf(conf_dir.path)
+        init_cmd.generate_stestr_conf(conf_dir.path)
 
         # Generate expected file contents
         top_level_path = os.path.dirname(os.path.dirname(init.__file__))
         discover_path = os.path.join(top_level_path, 'test_discover')
-        testr_conf_file = init.TESTR_CONF % (top_level_path, discover_path)
+        stestr_conf_file = init.STESTR_CONF % (discover_path, top_level_path)
 
-        conf_path = conf_dir.join('.testr.conf')
+        conf_path = conf_dir.join('.stestr.conf')
         with open(conf_path, 'r') as conf_file:
-            self.assertEqual(conf_file.read(), testr_conf_file)
+            self.assertEqual(conf_file.read(), stestr_conf_file)
 
     def test_generate_sample_config(self):
         local_dir = self.useFixture(fixtures.TempDir())
@@ -125,18 +125,18 @@
         lock_path = os.path.join(fake_local_dir.path, 'tempest_lock')
         etc_dir = os.path.join(fake_local_dir.path, 'etc')
         log_dir = os.path.join(fake_local_dir.path, 'logs')
-        testr_dir = os.path.join(fake_local_dir.path, '.testrepository')
+        stestr_dir = os.path.join(fake_local_dir.path, '.stestr')
         self.assertTrue(os.path.isdir(lock_path))
         self.assertTrue(os.path.isdir(etc_dir))
         self.assertTrue(os.path.isdir(log_dir))
-        self.assertTrue(os.path.isdir(testr_dir))
+        self.assertTrue(os.path.isdir(stestr_dir))
         # Assert file creation
         fake_file_moved = os.path.join(etc_dir, 'conf_file.conf')
         local_conf_file = os.path.join(etc_dir, 'tempest.conf')
-        local_testr_conf = os.path.join(fake_local_dir.path, '.testr.conf')
+        local_stestr_conf = os.path.join(fake_local_dir.path, '.stestr.conf')
         self.assertTrue(os.path.isfile(fake_file_moved))
         self.assertTrue(os.path.isfile(local_conf_file))
-        self.assertTrue(os.path.isfile(local_testr_conf))
+        self.assertTrue(os.path.isfile(local_stestr_conf))
 
     def test_take_action_fails(self):
         class ParsedArgs(object):
diff --git a/tempest/tests/cmd/test_verify_tempest_config.py b/tempest/tests/cmd/test_verify_tempest_config.py
index 8641b63..c260343 100644
--- a/tempest/tests/cmd/test_verify_tempest_config.py
+++ b/tempest/tests/cmd/test_verify_tempest_config.py
@@ -225,7 +225,7 @@
         # This test verifies that wrong config api_v2 = True is detected
         class FakeClient(object):
             def get_versions(self):
-                return (None, ['v1.0'])
+                return (None, ['v1.1'])
 
         fake_os = mock.MagicMock()
         fake_module = mock.MagicMock()
@@ -343,6 +343,24 @@
         self.assertEqual(sorted(['fake1', 'fake2', 'not_fake']),
                          sorted(results['neutron']['extensions']))
 
+    def test_verify_extensions_neutron_none(self):
+        def fake_list_extensions():
+            return {'extensions': []}
+        fake_os = mock.MagicMock()
+        fake_client = mock.MagicMock()
+        fake_client.list_extensions = fake_list_extensions
+        self.useFixture(fixtures.MockPatchObject(
+            verify_tempest_config, 'get_extension_client',
+            return_value=fake_client))
+        self.useFixture(fixtures.MockPatchObject(
+            verify_tempest_config, 'get_enabled_extensions',
+            return_value=(['all'])))
+        results = verify_tempest_config.verify_extensions(fake_os,
+                                                          'neutron', {})
+        self.assertIn('neutron', results)
+        self.assertIn('extensions', results['neutron'])
+        self.assertEqual([], results['neutron']['extensions'])
+
     def test_verify_extensions_cinder(self):
         def fake_list_extensions():
             return {'extensions': [{'alias': 'fake1'},
@@ -391,6 +409,24 @@
         self.assertEqual(sorted(['fake1', 'fake2', 'not_fake']),
                          sorted(results['cinder']['extensions']))
 
+    def test_verify_extensions_cinder_none(self):
+        def fake_list_extensions():
+            return {'extensions': []}
+        fake_os = mock.MagicMock()
+        fake_client = mock.MagicMock()
+        fake_client.list_extensions = fake_list_extensions
+        self.useFixture(fixtures.MockPatchObject(
+            verify_tempest_config, 'get_extension_client',
+            return_value=fake_client))
+        self.useFixture(fixtures.MockPatchObject(
+            verify_tempest_config, 'get_enabled_extensions',
+            return_value=(['all'])))
+        results = verify_tempest_config.verify_extensions(fake_os,
+                                                          'cinder', {})
+        self.assertIn('cinder', results)
+        self.assertIn('extensions', results['cinder'])
+        self.assertEqual([], results['cinder']['extensions'])
+
     def test_verify_extensions_nova(self):
         def fake_list_extensions():
             return ([{'alias': 'fake1'}, {'alias': 'fake2'},
@@ -437,6 +473,24 @@
         self.assertEqual(sorted(['fake1', 'fake2', 'not_fake']),
                          sorted(results['nova']['extensions']))
 
+    def test_verify_extensions_nova_none(self):
+        def fake_list_extensions():
+            return ({'extensions': []})
+        fake_os = mock.MagicMock()
+        fake_client = mock.MagicMock()
+        fake_client.list_extensions = fake_list_extensions
+        self.useFixture(fixtures.MockPatchObject(
+            verify_tempest_config, 'get_extension_client',
+            return_value=fake_client))
+        self.useFixture(fixtures.MockPatchObject(
+            verify_tempest_config, 'get_enabled_extensions',
+            return_value=(['all'])))
+        results = verify_tempest_config.verify_extensions(fake_os,
+                                                          'nova', {})
+        self.assertIn('nova', results)
+        self.assertIn('extensions', results['nova'])
+        self.assertEqual([], results['nova']['extensions'])
+
     def test_verify_extensions_swift(self):
         def fake_list_extensions():
             return {'fake1': 'metadata',
@@ -485,6 +539,24 @@
         self.assertEqual(sorted(['not_fake', 'fake1', 'fake2']),
                          sorted(results['swift']['extensions']))
 
+    def test_verify_extensions_swift_none(self):
+        def fake_list_extensions():
+            return {'swift': 'metadata'}
+        fake_os = mock.MagicMock()
+        fake_client = mock.MagicMock()
+        fake_client.list_capabilities = fake_list_extensions
+        self.useFixture(fixtures.MockPatchObject(
+            verify_tempest_config, 'get_extension_client',
+            return_value=fake_client))
+        self.useFixture(fixtures.MockPatchObject(
+            verify_tempest_config, 'get_enabled_extensions',
+            return_value=(['all'])))
+        results = verify_tempest_config.verify_extensions(fake_os,
+                                                          'swift', {})
+        self.assertIn('swift', results)
+        self.assertIn('extensions', results['swift'])
+        self.assertEqual([], results['swift']['extensions'])
+
     def test_get_extension_client(self):
         creds = credentials_factory.get_credentials(
             fill_in=False, username='fake_user', project_name='fake_project',
diff --git a/tempest/tests/cmd/test_workspace.py b/tempest/tests/cmd/test_workspace.py
index a1c8c53..3ed8a10 100644
--- a/tempest/tests/cmd/test_workspace.py
+++ b/tempest/tests/cmd/test_workspace.py
@@ -17,6 +17,11 @@
 import subprocess
 import tempfile
 
+from mock import patch
+try:
+    from StringIO import StringIO
+except ImportError:
+    from io import StringIO
 from tempest.cmd import workspace
 from tempest.lib.common.utils import data_utils
 from tempest.tests import base
@@ -140,3 +145,42 @@
         self.addCleanup(shutil.rmtree, path, ignore_errors=True)
         self.workspace_manager.register_new_workspace(name, path)
         self.assertIsNotNone(self.workspace_manager.get_workspace(name))
+
+    def test_workspace_name_not_exists(self):
+        nonexistent_name = data_utils.rand_uuid()
+        with patch('sys.stdout', new_callable=StringIO) as mock_stdout:
+            ex = self.assertRaises(SystemExit,
+                                   self.workspace_manager._name_exists,
+                                   nonexistent_name)
+        self.assertEqual(1, ex.code)
+        self.assertEqual(mock_stdout.getvalue(),
+                         "A workspace was not found with name: %s\n" %
+                         nonexistent_name)
+
+    def test_workspace_name_already_exists(self):
+        duplicate_name = self.name
+        with patch('sys.stdout', new_callable=StringIO) as mock_stdout:
+            ex = self.assertRaises(SystemExit,
+                                   self.workspace_manager.
+                                   _workspace_name_exists,
+                                   duplicate_name)
+        self.assertEqual(1, ex.code)
+        self.assertEqual(mock_stdout.getvalue(),
+                         "A workspace already exists with name: %s.\n"
+                         % duplicate_name)
+
+    def test_workspace_manager_path_not_exist(self):
+        fake_path = "fake_path"
+        with patch('sys.stdout', new_callable=StringIO) as mock_stdout:
+            ex = self.assertRaises(SystemExit,
+                                   self.workspace_manager._validate_path,
+                                   fake_path)
+        self.assertEqual(1, ex.code)
+        self.assertEqual(mock_stdout.getvalue(),
+                         "Path does not exist.\n")
+
+    def test_workspace_manager_list_workspaces(self):
+        listed = self.workspace_manager.list_workspaces()
+        self.assertEqual(1, len(listed))
+        self.assertIn(self.name, listed)
+        self.assertEqual(self.path, listed.get(self.name))
diff --git a/tempest/tests/common/test_waiters.py b/tempest/tests/common/test_waiters.py
index bc197b5..938d226 100644
--- a/tempest/tests/common/test_waiters.py
+++ b/tempest/tests/common/test_waiters.py
@@ -72,3 +72,79 @@
         mock_show.assert_has_calls([mock.call(volume_id),
                                     mock.call(volume_id)])
         mock_sleep.assert_called_once_with(1)
+
+
+class TestInterfaceWaiters(base.TestCase):
+
+    build_timeout = 1.
+    build_interval = 1
+    port_down = {'interfaceAttachment': {'port_state': 'DOWN'}}
+    port_active = {'interfaceAttachment': {'port_state': 'ACTIVE'}}
+
+    def mock_client(self, **kwargs):
+        return mock.MagicMock(
+            build_timeout=self.build_timeout,
+            build_interval=self.build_interval,
+            **kwargs)
+
+    def test_wait_for_interface_status(self):
+        show_interface = mock.Mock(
+            side_effect=[self.port_down, self.port_active])
+        client = self.mock_client(show_interface=show_interface)
+        self.patch('time.time', return_value=0.)
+        sleep = self.patch('time.sleep')
+
+        result = waiters.wait_for_interface_status(
+            client, 'server_id', 'port_id', 'ACTIVE')
+
+        self.assertIs(self.port_active['interfaceAttachment'], result)
+        show_interface.assert_has_calls([mock.call('server_id', 'port_id'),
+                                         mock.call('server_id', 'port_id')])
+        sleep.assert_called_once_with(client.build_interval)
+
+    def test_wait_for_interface_status_timeout(self):
+        show_interface = mock.MagicMock(return_value=self.port_down)
+        client = self.mock_client(show_interface=show_interface)
+        self.patch('time.time', side_effect=[0., client.build_timeout + 1.])
+        sleep = self.patch('time.sleep')
+
+        self.assertRaises(lib_exc.TimeoutException,
+                          waiters.wait_for_interface_status,
+                          client, 'server_id', 'port_id', 'ACTIVE')
+
+        show_interface.assert_has_calls([mock.call('server_id', 'port_id'),
+                                         mock.call('server_id', 'port_id')])
+        sleep.assert_called_once_with(client.build_interval)
+
+    one_interface = {'interfaceAttachments': [{'port_id': 'port_one'}]}
+    two_interfaces = {'interfaceAttachments': [{'port_id': 'port_one'},
+                                               {'port_id': 'port_two'}]}
+
+    def test_wait_for_interface_detach(self):
+        list_interfaces = mock.MagicMock(
+            side_effect=[self.two_interfaces, self.one_interface])
+        client = self.mock_client(list_interfaces=list_interfaces)
+        self.patch('time.time', return_value=0.)
+        sleep = self.patch('time.sleep')
+
+        result = waiters.wait_for_interface_detach(
+            client, 'server_id', 'port_two')
+
+        self.assertIs(self.one_interface['interfaceAttachments'], result)
+        list_interfaces.assert_has_calls([mock.call('server_id'),
+                                          mock.call('server_id')])
+        sleep.assert_called_once_with(client.build_interval)
+
+    def test_wait_for_interface_detach_timeout(self):
+        list_interfaces = mock.MagicMock(return_value=self.one_interface)
+        client = self.mock_client(list_interfaces=list_interfaces)
+        self.patch('time.time', side_effect=[0., client.build_timeout + 1.])
+        sleep = self.patch('time.sleep')
+
+        self.assertRaises(lib_exc.TimeoutException,
+                          waiters.wait_for_interface_detach,
+                          client, 'server_id', 'port_one')
+
+        list_interfaces.assert_has_calls([mock.call('server_id'),
+                                          mock.call('server_id')])
+        sleep.assert_called_once_with(client.build_interval)
diff --git a/tempest/tests/common/utils/linux/test_remote_client.py b/tempest/tests/common/utils/linux/test_remote_client.py
index 739357b..1f0080f 100644
--- a/tempest/tests/common/utils/linux/test_remote_client.py
+++ b/tempest/tests/common/utils/linux/test_remote_client.py
@@ -77,7 +77,7 @@
 
     def test_write_to_console_special_chars(self):
         self._test_write_to_console_helper(
-            '\`',
+            r'\`',
             'sudo sh -c "echo \\"\\\\\\`\\" >/dev/console"')
         self.conn.write_to_console('$')
         self._assert_exec_called_with(
diff --git a/tempest/tests/fake_config.py b/tempest/tests/fake_config.py
index 4a2fff4..be54130 100644
--- a/tempest/tests/fake_config.py
+++ b/tempest/tests/fake_config.py
@@ -59,6 +59,7 @@
         self._set_attrs()
         self.lock_path = cfg.CONF.oslo_concurrency.lock_path
 
+
 fake_service1_group = cfg.OptGroup(name='fake-service1', title='Fake service1')
 
 FakeService1Group = [
diff --git a/tempest/tests/files/setup.cfg b/tempest/tests/files/setup.cfg
index f6f9f73..bd68708 100644
--- a/tempest/tests/files/setup.cfg
+++ b/tempest/tests/files/setup.cfg
@@ -4,7 +4,7 @@
 summary = Fake Project for testing wrapper scripts
 author = OpenStack
 author-email = openstack-dev@lists.openstack.org
-home-page = http://www.openstack.org/
+home-page = https://docs.openstack.org/tempest/latest/
 classifier =
     Intended Audience :: Information Technology
     Intended Audience :: System Administrators
diff --git a/tempest/tests/files/testr-conf b/tempest/tests/files/testr-conf
index d5ad083..63b3c44 100644
--- a/tempest/tests/files/testr-conf
+++ b/tempest/tests/files/testr-conf
@@ -1,5 +1,3 @@
 [DEFAULT]
-test_command=${PYTHON:-python} -m subunit.run discover -t ./ ./tests $LISTOPT $IDOPTION
-test_id_option=--load-list $IDFILE
-test_list_option=--list
+test_path=./tests
 group_regex=([^\.]*\.)*
diff --git a/tempest/tests/lib/common/test_http.py b/tempest/tests/lib/common/test_http.py
index a292209..02436e0 100644
--- a/tempest/tests/lib/common/test_http.py
+++ b/tempest/tests/lib/common/test_http.py
@@ -12,57 +12,158 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import urllib3
+
 from tempest.lib.common import http
 from tempest.tests import base
 
 
+CERT_NONE = 'CERT_NONE'
+CERT_REQUIRED = 'CERT_REQUIRED'
+CERT_LOCATION = '/etc/ssl/certs/ca-certificates.crt'
+PROXY_URL = 'http://myproxy:3128'
+REQUEST_URL = 'http://10.0.0.107:5000/v2.0'
+REQUEST_METHOD = 'GET'
+
+
 class TestClosingHttp(base.TestCase):
-    def setUp(self):
-        super(TestClosingHttp, self).setUp()
-        self.cert_none = "CERT_NONE"
-        self.cert_location = "/etc/ssl/certs/ca-certificates.crt"
 
-    def test_constructor_invalid_ca_certs_and_timeout(self):
-        connection = http.ClosingHttp(
-            disable_ssl_certificate_validation=False,
-            ca_certs=None,
-            timeout=None)
-        for attr in ('cert_reqs', 'ca_certs', 'timeout'):
-            self.assertNotIn(attr, connection.connection_pool_kw)
+    def closing_http(self, **kwargs):
+        return http.ClosingHttp(**kwargs)
 
-    def test_constructor_valid_ca_certs(self):
-        cert_required = 'CERT_REQUIRED'
-        connection = http.ClosingHttp(
-            disable_ssl_certificate_validation=False,
-            ca_certs=self.cert_location,
-            timeout=None)
-        self.assertEqual(cert_required,
+    def test_closing_http(self):
+        connection = self.closing_http()
+
+        self.assertNotIn('cert_reqs', connection.connection_pool_kw)
+        self.assertNotIn('ca_certs', connection.connection_pool_kw)
+        self.assertNotIn('timeout', connection.connection_pool_kw)
+
+    def test_closing_http_with_ca_certs(self):
+        connection = self.closing_http(ca_certs=CERT_LOCATION)
+
+        self.assertEqual(CERT_REQUIRED,
                          connection.connection_pool_kw['cert_reqs'])
-        self.assertEqual(self.cert_location,
+        self.assertEqual(CERT_LOCATION,
                          connection.connection_pool_kw['ca_certs'])
-        self.assertNotIn('timeout',
+
+    def test_closing_http_with_dscv(self):
+        connection = self.closing_http(
+            disable_ssl_certificate_validation=True)
+
+        self.assertEqual(CERT_NONE,
+                         connection.connection_pool_kw['cert_reqs'])
+        self.assertNotIn('ca_certs',
                          connection.connection_pool_kw)
 
-    def test_constructor_ssl_cert_validation_disabled(self):
-        connection = http.ClosingHttp(
+    def test_closing_http_with_ca_certs_and_dscv(self):
+        connection = self.closing_http(
             disable_ssl_certificate_validation=True,
-            ca_certs=None,
-            timeout=30)
-        self.assertEqual(self.cert_none,
+            ca_certs=CERT_LOCATION)
+
+        self.assertEqual(CERT_NONE,
                          connection.connection_pool_kw['cert_reqs'])
-        self.assertEqual(30,
+        self.assertNotIn('ca_certs',
+                         connection.connection_pool_kw)
+
+    def test_closing_http_with_timeout(self):
+        timeout = 30
+        connection = self.closing_http(timeout=timeout)
+        self.assertEqual(timeout,
                          connection.connection_pool_kw['timeout'])
-        self.assertNotIn('ca_certs',
-                         connection.connection_pool_kw)
 
-    def test_constructor_ssl_cert_validation_disabled_and_ca_certs(self):
-        connection = http.ClosingHttp(
-            disable_ssl_certificate_validation=True,
-            ca_certs=self.cert_location,
-            timeout=None)
-        self.assertNotIn('timeout',
-                         connection.connection_pool_kw)
-        self.assertEqual(self.cert_none,
-                         connection.connection_pool_kw['cert_reqs'])
-        self.assertNotIn('ca_certs',
-                         connection.connection_pool_kw)
+    def test_request(self):
+        # Given
+        connection = self.closing_http()
+        http_response = urllib3.HTTPResponse()
+        request = self.patch('urllib3.PoolManager.request',
+                             return_value=http_response)
+        retry = self.patch('urllib3.util.Retry')
+
+        # When
+        response, data = connection.request(
+            method=REQUEST_METHOD,
+            url=REQUEST_URL)
+
+        # Then
+        request.assert_called_once_with(
+            REQUEST_METHOD,
+            REQUEST_URL,
+            headers={'connection': 'close'},
+            retries=retry(raise_on_redirect=False, redirect=5))
+        self.assertEqual(
+            {'content-location': REQUEST_URL,
+             'status': str(http_response.status)},
+            response)
+        self.assertEqual(http_response.status, response.status)
+        self.assertEqual(http_response.reason, response.reason)
+        self.assertEqual(http_response.version, response.version)
+        self.assertEqual(http_response.data, data)
+
+    def test_request_with_fields(self):
+        # Given
+        connection = self.closing_http()
+        http_response = urllib3.HTTPResponse()
+        request = self.patch('urllib3.PoolManager.request',
+                             return_value=http_response)
+        retry = self.patch('urllib3.util.Retry')
+        fields = object()
+
+        # When
+        connection.request(
+            method=REQUEST_METHOD,
+            url=REQUEST_URL,
+            fields=fields)
+
+        # Then
+        request.assert_called_once_with(
+            REQUEST_METHOD,
+            REQUEST_URL,
+            fields=fields,
+            headers=dict(connection='close'),
+            retries=retry(raise_on_redirect=False, redirect=5))
+
+    def test_request_with_headers(self):
+        # Given
+        connection = self.closing_http()
+        headers = {'Xtra Key': 'Xtra Value'}
+        http_response = urllib3.HTTPResponse(headers=headers)
+        request = self.patch('urllib3.PoolManager.request',
+                             return_value=http_response)
+        retry = self.patch('urllib3.util.Retry')
+
+        # When
+        response, _ = connection.request(
+            method=REQUEST_METHOD,
+            url=REQUEST_URL,
+            headers=headers)
+
+        # Then
+        request.assert_called_once_with(
+            REQUEST_METHOD,
+            REQUEST_URL,
+            headers=dict(headers, connection='close'),
+            retries=retry(raise_on_redirect=False, redirect=5))
+        self.assertEqual(
+            {'content-location': REQUEST_URL,
+             'status': str(http_response.status),
+             'xtra key': 'Xtra Value'},
+            response)
+
+
+class TestClosingProxyHttp(TestClosingHttp):
+
+    def closing_http(self, proxy_url=PROXY_URL, **kwargs):
+        connection = http.ClosingProxyHttp(proxy_url=proxy_url, **kwargs)
+        self.assertHasProxy(connection, proxy_url)
+        return connection
+
+    def test_class_without_proxy_url(self):
+        self.assertRaises(ValueError, http.ClosingProxyHttp, None)
+
+    def assertHasProxy(self, connection, proxy_url):
+        self.assertIsInstance(connection, http.ClosingProxyHttp)
+        proxy = connection.proxy
+        self.assertEqual(proxy_url,
+                         '%s://%s:%i' % (proxy.scheme,
+                                         proxy.host,
+                                         proxy.port))
diff --git a/tempest/tests/lib/common/utils/test_data_utils.py b/tempest/tests/lib/common/utils/test_data_utils.py
index b8385b2..a0267d0 100644
--- a/tempest/tests/lib/common/utils/test_data_utils.py
+++ b/tempest/tests/lib/common/utils/test_data_utils.py
@@ -88,7 +88,7 @@
     def test_rand_url(self):
         actual = data_utils.rand_url()
         self.assertIsInstance(actual, str)
-        self.assertRegex(actual, "^https://url-[0-9]*\.com$")
+        self.assertRegex(actual, r"^https://url-[0-9]*\.com$")
         actual2 = data_utils.rand_url()
         self.assertNotEqual(actual, actual2)
 
diff --git a/tempest/tests/lib/common/utils/test_test_utils.py b/tempest/tests/lib/common/utils/test_test_utils.py
index f638ba6..865767b 100644
--- a/tempest/tests/lib/common/utils/test_test_utils.py
+++ b/tempest/tests/lib/common/utils/test_test_utils.py
@@ -12,12 +12,15 @@
 #    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 time
+
 import mock
 
+from tempest.lib.common import thread
 from tempest.lib.common.utils import test_utils
 from tempest.lib import exceptions
 from tempest.tests import base
-from tempest.tests import utils
 
 
 class TestTestUtils(base.TestCase):
@@ -78,47 +81,126 @@
             42, test_utils.call_and_ignore_notfound_exc(m, *args, **kwargs))
         m.assert_called_once_with(*args, **kwargs)
 
-    @mock.patch('time.sleep')
-    @mock.patch('time.time')
-    def test_call_until_true_when_f_never_returns_true(self, m_time, m_sleep):
-        def set_value(bool_value):
-            return bool_value
-        timeout = 42  # The value doesn't matter as we mock time.time()
-        sleep = 60  # The value doesn't matter as we mock time.sleep()
-        m_time.side_effect = utils.generate_timeout_series(timeout)
-        self.assertEqual(
-            False, test_utils.call_until_true(set_value, timeout, sleep, False)
-        )
-        m_sleep.call_args_list = [mock.call(sleep)] * 2
-        m_time.call_args_list = [mock.call()] * 2
 
-    @mock.patch('time.sleep')
-    @mock.patch('time.time')
-    def test_call_until_true_when_f_returns_true(self, m_time, m_sleep):
-        def set_value(bool_value=False):
-            return bool_value
-        timeout = 42  # The value doesn't matter as we mock time.time()
-        sleep = 60  # The value doesn't matter as we mock time.sleep()
-        m_time.return_value = 0
-        self.assertEqual(
-            True, test_utils.call_until_true(set_value, timeout, sleep,
-                                             bool_value=True)
-        )
-        self.assertEqual(0, m_sleep.call_count)
-        # when logging cost time we need to acquire current time.
-        self.assertEqual(2, m_time.call_count)
+class TestCallUntilTrue(base.TestCase):
 
-    @mock.patch('time.sleep')
-    @mock.patch('time.time')
-    def test_call_until_true_when_f_returns_true_no_param(
-            self, m_time, m_sleep):
-        def set_value(bool_value=False):
-            return bool_value
-        timeout = 42  # The value doesn't matter as we mock time.time()
-        sleep = 60  # The value doesn't matter as we mock time.sleep()
-        m_time.side_effect = utils.generate_timeout_series(timeout)
-        self.assertEqual(
-            False, test_utils.call_until_true(set_value, timeout, sleep)
-        )
-        m_sleep.call_args_list = [mock.call(sleep)] * 2
-        m_time.call_args_list = [mock.call()] * 2
+    def test_call_until_true_when_true_at_first_call(self):
+        """func returns True at first call
+
+        """
+        self._test_call_until_true(return_values=[True],
+                                   duration=30.,
+                                   time_sequence=[10., 60.])
+
+    def test_call_until_true_when_true_before_timeout(self):
+        """func returns false at first call, then True before timeout
+
+        """
+        self._test_call_until_true(return_values=[False, True],
+                                   duration=30.,
+                                   time_sequence=[10., 39., 41.])
+
+    def test_call_until_true_when_never_true_before_timeout(self):
+        """func returns false, then false, just before timeout
+
+        """
+        self._test_call_until_true(return_values=[False, False],
+                                   duration=30.,
+                                   time_sequence=[10., 39., 41.])
+
+    def test_call_until_true_with_params(self):
+        """func is called using given parameters
+
+        """
+        self._test_call_until_true(return_values=[False, True],
+                                   duration=30.,
+                                   time_sequence=[10., 30., 60.],
+                                   args=(1, 2),
+                                   kwargs=dict(foo='bar', bar='foo'))
+
+    def _test_call_until_true(self, return_values, duration, time_sequence,
+                              args=None, kwargs=None):
+        """Test call_until_true function
+
+        :param return_values: list of booleans values to be returned
+        each time given function is called. If any of these values
+        is not consumed by calling the function the test fails.
+        The list must contain a sequence of False items terminated
+        by a single True or False
+        :param duration: parameter passed to call_until_true function
+        (a floating point value).
+        :param time_sequence: sequence of time values returned by
+        mocked time.time function used to trigger call_until_true
+        behavior when handling timeout condition. The sequence must
+        contain the exact number of values expected to be consumed by
+        each time call_until_true calls time.time function.
+        :param args: sequence of positional arguments to be passed
+        to call_until_true function.
+        :param kwargs: sequence of named arguments to be passed
+        to call_until_true function.
+        """
+
+        # all values except the last are False
+        self.assertEqual([False] * len(return_values[:-1]), return_values[:-1])
+        # last value can be True or False
+        self.assertIn(return_values[-1], [True, False])
+
+        # GIVEN
+        func = mock.Mock(side_effect=return_values)
+        sleep = 10.  # this value has no effect as time.sleep is being mocked
+        sleep_func = self.patch('time.sleep')
+        time_func = self._patch_time(time_sequence)
+        args = args or tuple()
+        kwargs = kwargs or dict()
+
+        # WHEN
+        result = test_utils.call_until_true(func, duration, sleep,
+                                            *args, **kwargs)
+        # THEN
+
+        # It must return last returned value
+        self.assertIs(return_values[-1], result)
+
+        self._test_func_calls(func, return_values, *args, **kwargs)
+        self._test_sleep_calls(sleep_func, return_values, sleep)
+        # The number of times time.time is called is not relevant as a
+        # requirement of call_until_true. What is instead relevant is that
+        # call_until_true use a mocked function to make the test reliable
+        # and the test actually provide the right sequence of numbers to
+        # reproduce the behavior has to be tested
+        self._assert_called_n_times(time_func, len(time_sequence))
+
+    def _patch_time(self, time_sequence):
+        # Iterator over time sequence
+        time_iterator = iter(time_sequence)
+        # Preserve original time.time() behavior for other threads
+        original_time = time.time
+        thread_id = thread.get_ident()
+
+        def mocked_time():
+            if thread.get_ident() == thread_id:
+                # Test thread => return time sequence values
+                return next(time_iterator)
+            else:
+                # Other threads => call original time function
+                return original_time()
+
+        return self.patch('time.time', side_effect=mocked_time)
+
+    def _test_func_calls(self, func, return_values, *args, **kwargs):
+        self._assert_called_n_times(func, len(return_values), *args, **kwargs)
+
+    def _test_sleep_calls(self, sleep_func, return_values, sleep):
+        # count first consecutive False
+        expected_count = 0
+        for value in return_values:
+            if value:
+                break
+            expected_count += 1
+        self._assert_called_n_times(sleep_func, expected_count, sleep)
+
+    def _assert_called_n_times(self, mock_func, expected_count, *args,
+                               **kwargs):
+        calls = [mock.call(*args, **kwargs)] * expected_count
+        self.assertEqual(expected_count, mock_func.call_count)
+        mock_func.assert_has_calls(calls)
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/compute/test_services_client.py b/tempest/tests/lib/services/compute/test_services_client.py
index 2dd981c..ba432e3 100644
--- a/tempest/tests/lib/services/compute/test_services_client.py
+++ b/tempest/tests/lib/services/compute/test_services_client.py
@@ -56,6 +56,20 @@
         }
     }
 
+    FAKE_UPDATE_SERVICE = {
+        "service": {
+            "id": "e81d66a4-ddd3-4aba-8a84-171d1cb4d339",
+            "binary": "nova-compute",
+            "disabled_reason": "test2",
+            "host": "host1",
+            "state": "down",
+            "status": "disabled",
+            "updated_at": "2012-10-29T13:42:05.000000",
+            "forced_down": False,
+            "zone": "nova"
+        }
+    }
+
     def setUp(self):
         super(TestServicesClient, self).setUp()
         fake_auth = fake_auth_provider.FakeAuthProvider()
@@ -119,6 +133,28 @@
             binary="controller",
             disabled_reason='test reason')
 
+    def _test_update_service(self, bytes_body=False, status=None,
+                             disabled_reason=None, forced_down=None):
+        resp_body = copy.deepcopy(self.FAKE_UPDATE_SERVICE)
+        kwargs = {}
+
+        if status is not None:
+            kwargs['status'] = status
+        if disabled_reason is not None:
+            kwargs['disabled_reason'] = disabled_reason
+        if forced_down is not None:
+            kwargs['forced_down'] = forced_down
+
+        resp_body['service'].update(kwargs)
+
+        self.check_service_client_function(
+            self.client.update_service,
+            'tempest.lib.common.rest_client.RestClient.put',
+            resp_body,
+            bytes_body,
+            service_id=resp_body['service']['id'],
+            **kwargs)
+
     def test_log_reason_disabled_service_with_str_body(self):
         self._test_log_reason_disabled_service()
 
@@ -144,3 +180,36 @@
                        new_callable=mock.PropertyMock(return_value='2.11'))
     def test_update_forced_down_with_bytes_body(self, _):
         self._test_update_forced_down(bytes_body=True)
+
+    @mock.patch.object(base_compute_client, 'COMPUTE_MICROVERSION',
+                       new_callable=mock.PropertyMock(return_value='2.53'))
+    def test_update_service_disable_scheduling_with_str_body(self, _):
+        self._test_update_service(status='disabled',
+                                  disabled_reason='maintenance')
+
+    @mock.patch.object(base_compute_client, 'COMPUTE_MICROVERSION',
+                       new_callable=mock.PropertyMock(return_value='2.53'))
+    def test_update_service_disable_scheduling_with_bytes_body(self, _):
+        self._test_update_service(status='disabled',
+                                  disabled_reason='maintenance',
+                                  bytes_body=True)
+
+    @mock.patch.object(base_compute_client, 'COMPUTE_MICROVERSION',
+                       new_callable=mock.PropertyMock(return_value='2.53'))
+    def test_update_service_enable_scheduling_with_str_body(self, _):
+        self._test_update_service(status='enabled')
+
+    @mock.patch.object(base_compute_client, 'COMPUTE_MICROVERSION',
+                       new_callable=mock.PropertyMock(return_value='2.53'))
+    def test_update_service_enable_scheduling_with_bytes_body(self, _):
+        self._test_update_service(status='enabled', bytes_body=True)
+
+    @mock.patch.object(base_compute_client, 'COMPUTE_MICROVERSION',
+                       new_callable=mock.PropertyMock(return_value='2.53'))
+    def test_update_service_forced_down_with_str_body(self, _):
+        self._test_update_service(forced_down=True)
+
+    @mock.patch.object(base_compute_client, 'COMPUTE_MICROVERSION',
+                       new_callable=mock.PropertyMock(return_value='2.53'))
+    def test_update_service_forced_down_with_bytes_body(self, _):
+        self._test_update_service(forced_down=True, bytes_body=True)
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_policies_client.py b/tempest/tests/lib/services/identity/v3/test_policies_client.py
index 66c3d65..0237475 100644
--- a/tempest/tests/lib/services/identity/v3/test_policies_client.py
+++ b/tempest/tests/lib/services/identity/v3/test_policies_client.py
@@ -81,6 +81,10 @@
                 }
             ]
     }
+    FAKE_ENDPOINT_ID = "234789"
+    FAKE_SERVICE_ID = "556782"
+    FAKE_POLICY_ID = "717273"
+    FAKE_REGION_ID = "73"
 
     def setUp(self):
         super(TestPoliciesClient, self).setUp()
@@ -150,3 +154,87 @@
             {},
             policy_id="717273",
             status=204)
+
+    def test_update_policy_association_for_endpoint(self):
+        self.check_service_client_function(
+            self.client.update_policy_association_for_endpoint,
+            'tempest.lib.common.rest_client.RestClient.put',
+            {},
+            policy_id=self.FAKE_POLICY_ID,
+            endpoint_id=self.FAKE_ENDPOINT_ID,
+            status=204)
+
+    def test_show_policy_association_for_endpoint(self):
+        self.check_service_client_function(
+            self.client.show_policy_association_for_endpoint,
+            'tempest.lib.common.rest_client.RestClient.get',
+            {},
+            policy_id=self.FAKE_POLICY_ID,
+            endpoint_id=self.FAKE_ENDPOINT_ID,
+            status=204)
+
+    def test_delete_policy_association_for_endpoint(self):
+        self.check_service_client_function(
+            self.client.delete_policy_association_for_endpoint,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {},
+            policy_id=self.FAKE_POLICY_ID,
+            endpoint_id=self.FAKE_ENDPOINT_ID,
+            status=204)
+
+    def test_update_policy_association_for_service(self):
+        self.check_service_client_function(
+            self.client.update_policy_association_for_service,
+            'tempest.lib.common.rest_client.RestClient.put',
+            {},
+            policy_id=self.FAKE_POLICY_ID,
+            service_id=self.FAKE_SERVICE_ID,
+            status=204)
+
+    def test_show_policy_association_for_service(self):
+        self.check_service_client_function(
+            self.client.show_policy_association_for_service,
+            'tempest.lib.common.rest_client.RestClient.get',
+            {},
+            policy_id=self.FAKE_POLICY_ID,
+            service_id=self.FAKE_SERVICE_ID,
+            status=204)
+
+    def test_delete_policy_association_for_service(self):
+        self.check_service_client_function(
+            self.client.delete_policy_association_for_service,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {},
+            policy_id=self.FAKE_POLICY_ID,
+            service_id=self.FAKE_SERVICE_ID,
+            status=204)
+
+    def test_update_policy_association_for_region_and_service(self):
+        self.check_service_client_function(
+            self.client.update_policy_association_for_region_and_service,
+            'tempest.lib.common.rest_client.RestClient.put',
+            {},
+            policy_id=self.FAKE_POLICY_ID,
+            service_id=self.FAKE_SERVICE_ID,
+            region_id=self.FAKE_REGION_ID,
+            status=204)
+
+    def test_show_policy_association_for_region_and_service(self):
+        self.check_service_client_function(
+            self.client.show_policy_association_for_region_and_service,
+            'tempest.lib.common.rest_client.RestClient.get',
+            {},
+            policy_id=self.FAKE_POLICY_ID,
+            service_id=self.FAKE_SERVICE_ID,
+            region_id=self.FAKE_REGION_ID,
+            status=204)
+
+    def test_delete_policy_association_for_region_and_service(self):
+        self.check_service_client_function(
+            self.client.delete_policy_association_for_region_and_service,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {},
+            policy_id=self.FAKE_POLICY_ID,
+            service_id=self.FAKE_SERVICE_ID,
+            region_id=self.FAKE_REGION_ID,
+            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/network/test_quotas_client.py b/tempest/tests/lib/services/network/test_quotas_client.py
index 5a09911..aa6c1a1 100644
--- a/tempest/tests/lib/services/network/test_quotas_client.py
+++ b/tempest/tests/lib/services/network/test_quotas_client.py
@@ -54,6 +54,46 @@
 
     FAKE_QUOTA_TENANT_ID = "bab7d5c60cd041a0a36f7c4b6e1dd978"
 
+    FAKE_QUOTA_DETAILS = {
+        "quota": {
+            "rbac_policy": {
+                "used": 4,
+                "limit": 10,
+                "reserved": 0
+            },
+            "subnetpool": {
+                "used": 2,
+                "limit": -1,
+                "reserved": 0
+            },
+            "security_group_rule": {
+                "used": 10,
+                "limit": 100,
+                "reserved": 1
+            },
+            "security_group": {
+                "used": 3,
+                "limit": 10,
+                "reserved": 0
+            },
+            "subnet": {
+                "used": 3,
+                "limit": 100,
+                "reserved": 0
+            },
+            "port": {
+                "used": 21,
+                "limit": 500,
+                "reserved": 3
+            },
+            "network": {
+                "used": 9,
+                "limit": 100,
+                "reserved": 2
+            }
+        }
+    }
+
     def setUp(self):
         super(TestQuotasClient, self).setUp()
         fake_auth = fake_auth_provider.FakeAuthProvider()
@@ -95,6 +135,15 @@
             200,
             tenant_id=self.FAKE_QUOTA_TENANT_ID)
 
+    def _test_show_quota_details(self, bytes_body=False):
+        self.check_service_client_function(
+            self.quotas_client.show_quota_details,
+            "tempest.lib.common.rest_client.RestClient.get",
+            self.FAKE_QUOTA_DETAILS,
+            bytes_body,
+            200,
+            tenant_id=self.FAKE_QUOTA_TENANT_ID)
+
     def test_reset_quotas(self):
         self.check_service_client_function(
             self.quotas_client.reset_quotas,
@@ -126,3 +175,9 @@
 
     def test_update_quotas_with_bytes_body(self):
         self._test_update_quotas(bytes_body=True)
+
+    def test_show_quota_details_with_str_body(self):
+        self._test_show_quota_details()
+
+    def test_show_quota_details_with_bytes_body(self):
+        self._test_show_quota_details(bytes_body=True)
diff --git a/tempest/tests/lib/services/volume/v2/test_backups_client.py b/tempest/tests/lib/services/volume/v2/test_backups_client.py
deleted file mode 100644
index 14e5fb0..0000000
--- a/tempest/tests/lib/services/volume/v2/test_backups_client.py
+++ /dev/null
@@ -1,117 +0,0 @@
-# Copyright 2017 FiberHome Telecommunication Technologies CO.,LTD
-# All Rights Reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
-#    not use this file except in compliance with the License. You may obtain
-#    a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-#    License for the specific language governing permissions and limitations
-#    under the License.
-
-from tempest.lib.services.volume.v2 import backups_client
-from tempest.tests.lib import fake_auth_provider
-from tempest.tests.lib.services import base
-
-
-class TestBackupsClient(base.BaseServiceTest):
-
-    FAKE_BACKUP_LIST = {
-        "backups": [
-            {
-                "id": "2ef47aee-8844-490c-804d-2a8efe561c65",
-                "links": [
-                    {
-                        "href": "fake-url-1",
-                        "rel": "self"
-                    },
-                    {
-                        "href": "fake-url-2",
-                        "rel": "bookmark"
-                    }
-                ],
-                "name": "backup001"
-            }
-        ]
-    }
-
-    FAKE_BACKUP_LIST_WITH_DETAIL = {
-        "backups": [
-            {
-                "availability_zone": "az1",
-                "container": "volumebackups",
-                "created_at": "2013-04-02T10:35:27.000000",
-                "description": None,
-                "fail_reason": None,
-                "id": "2ef47aee-8844-490c-804d-2a8efe561c65",
-                "links": [
-                    {
-                        "href": "fake-url-1",
-                        "rel": "self"
-                    },
-                    {
-                        "href": "fake-url-2",
-                        "rel": "bookmark"
-                    }
-                ],
-                "name": "backup001",
-                "object_count": 22,
-                "size": 1,
-                "status": "available",
-                "volume_id": "e5185058-943a-4cb4-96d9-72c184c337d6",
-                "is_incremental": True,
-                "has_dependent_backups": False
-            }
-        ]
-    }
-
-    def setUp(self):
-        super(TestBackupsClient, self).setUp()
-        fake_auth = fake_auth_provider.FakeAuthProvider()
-        self.client = backups_client.BackupsClient(fake_auth,
-                                                   'volume',
-                                                   'regionOne')
-
-    def _test_list_backups(self, detail=False, mock_args='backups',
-                           bytes_body=False, **params):
-        if detail:
-            resp_body = self.FAKE_BACKUP_LIST_WITH_DETAIL
-        else:
-            resp_body = self.FAKE_BACKUP_LIST
-        self.check_service_client_function(
-            self.client.list_backups,
-            'tempest.lib.common.rest_client.RestClient.get',
-            resp_body,
-            to_utf=bytes_body,
-            mock_args=[mock_args],
-            detail=detail,
-            **params)
-
-    def test_list_backups_with_str_body(self):
-        self._test_list_backups()
-
-    def test_list_backups_with_bytes_body(self):
-        self._test_list_backups(bytes_body=True)
-
-    def test_list_backups_with_detail_with_str_body(self):
-        mock_args = "backups/detail"
-        self._test_list_backups(detail=True, mock_args=mock_args)
-
-    def test_list_backups_with_detail_with_bytes_body(self):
-        mock_args = "backups/detail"
-        self._test_list_backups(detail=True, mock_args=mock_args,
-                                bytes_body=True)
-
-    def test_list_backups_with_params(self):
-        # Run the test separately for each param, to avoid assertion error
-        # resulting from randomized params order.
-        mock_args = 'backups?sort_key=name'
-        self._test_list_backups(mock_args=mock_args, sort_key='name')
-
-        mock_args = 'backups/detail?limit=10'
-        self._test_list_backups(detail=True, mock_args=mock_args,
-                                bytes_body=True, limit=10)
diff --git a/tempest/tests/lib/services/volume/v2/test_volumes_client.py b/tempest/tests/lib/services/volume/v2/test_volumes_client.py
deleted file mode 100644
index d7b042e..0000000
--- a/tempest/tests/lib/services/volume/v2/test_volumes_client.py
+++ /dev/null
@@ -1,127 +0,0 @@
-# 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.services.volume.v2 import volumes_client
-from tempest.tests.lib import fake_auth_provider
-from tempest.tests.lib.services import base
-
-
-class TestVolumesClient(base.BaseServiceTest):
-
-    FAKE_VOLUME_METADATA_ITEM = {
-        "meta": {
-            "key1": "value1"
-        }
-    }
-
-    FAKE_VOLUME_IMAGE_METADATA = {
-        "metadata": {
-            "container_format": "bare",
-            "min_ram": "0",
-            "disk_format": "raw",
-            "image_name": "xly-ubuntu16-server",
-            "image_id": "3e087b0c-10c5-4255-b147-6e8e9dbad6fc",
-            "checksum": "008f5d22fe3cb825d714da79607a90f9",
-            "min_disk": "0",
-            "size": "8589934592"
-        }
-    }
-
-    def setUp(self):
-        super(TestVolumesClient, self).setUp()
-        fake_auth = fake_auth_provider.FakeAuthProvider()
-        self.client = volumes_client.VolumesClient(fake_auth,
-                                                   'volume',
-                                                   'regionOne')
-
-    def _test_retype_volume(self, bytes_body=False):
-        kwargs = {
-            "new_type": "dedup-tier-replication",
-            "migration_policy": "never"
-        }
-
-        self.check_service_client_function(
-            self.client.retype_volume,
-            'tempest.lib.common.rest_client.RestClient.post',
-            {},
-            to_utf=bytes_body,
-            status=202,
-            volume_id="a3be971b-8de5-4bdf-bdb8-3d8eb0fb69f8",
-            **kwargs
-        )
-
-    def _test_force_detach_volume(self, bytes_body=False):
-        kwargs = {
-            'attachment_id': '6980e295-920f-412e-b189-05c50d605acd',
-            'connector': {
-                'initiator': 'iqn.2017-04.org.fake:01'
-            }
-        }
-
-        self.check_service_client_function(
-            self.client.force_detach_volume,
-            'tempest.lib.common.rest_client.RestClient.post',
-            {},
-            to_utf=bytes_body,
-            status=202,
-            volume_id="a3be971b-8de5-4bdf-bdb8-3d8eb0fb69f8",
-            **kwargs
-        )
-
-    def _test_show_volume_metadata_item(self, bytes_body=False):
-        self.check_service_client_function(
-            self.client.show_volume_metadata_item,
-            'tempest.lib.common.rest_client.RestClient.get',
-            self.FAKE_VOLUME_METADATA_ITEM,
-            to_utf=bytes_body,
-            volume_id="a3be971b-8de5-4bdf-bdb8-3d8eb0fb69f8",
-            id="key1")
-
-    def _test_show_volume_image_metadata(self, bytes_body=False):
-        fake_volume_id = "a3be971b-8de5-4bdf-bdb8-3d8eb0fb69f8"
-        self.check_service_client_function(
-            self.client.show_volume_image_metadata,
-            'tempest.lib.common.rest_client.RestClient.post',
-            self.FAKE_VOLUME_IMAGE_METADATA,
-            to_utf=bytes_body,
-            mock_args=['volumes/%s/action' % fake_volume_id,
-                       json.dumps({"os-show_image_metadata": {}})],
-            volume_id=fake_volume_id)
-
-    def test_force_detach_volume_with_str_body(self):
-        self._test_force_detach_volume()
-
-    def test_force_detach_volume_with_bytes_body(self):
-        self._test_force_detach_volume(bytes_body=True)
-
-    def test_show_volume_metadata_item_with_str_body(self):
-        self._test_show_volume_metadata_item()
-
-    def test_show_volume_metadata_item_with_bytes_body(self):
-        self._test_show_volume_metadata_item(bytes_body=True)
-
-    def test_show_volume_image_metadata_with_str_body(self):
-        self._test_show_volume_image_metadata()
-
-    def test_show_volume_image_metadata_with_bytes_body(self):
-        self._test_show_volume_image_metadata(bytes_body=True)
-
-    def test_retype_volume_with_str_body(self):
-        self._test_retype_volume()
-
-    def test_retype_volume_with_bytes_body(self):
-        self._test_retype_volume(bytes_body=True)
diff --git a/tempest/tests/lib/services/volume/v2/test_availability_zone_client.py b/tempest/tests/lib/services/volume/v3/test_availability_zone_client.py
similarity index 96%
rename from tempest/tests/lib/services/volume/v2/test_availability_zone_client.py
rename to tempest/tests/lib/services/volume/v3/test_availability_zone_client.py
index 770565c..4827326 100644
--- a/tempest/tests/lib/services/volume/v2/test_availability_zone_client.py
+++ b/tempest/tests/lib/services/volume/v3/test_availability_zone_client.py
@@ -13,7 +13,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest.lib.services.volume.v2 import availability_zone_client
+from tempest.lib.services.volume.v3 import availability_zone_client
 from tempest.tests.lib import fake_auth_provider
 from tempest.tests.lib.services import base
 
diff --git a/tempest/tests/lib/services/volume/v3/test_backups_client.py b/tempest/tests/lib/services/volume/v3/test_backups_client.py
index f1ce987..5412064 100644
--- a/tempest/tests/lib/services/volume/v3/test_backups_client.py
+++ b/tempest/tests/lib/services/volume/v3/test_backups_client.py
@@ -20,6 +20,55 @@
 
 class TestBackupsClient(base.BaseServiceTest):
 
+    FAKE_BACKUP_LIST = {
+        "backups": [
+            {
+                "id": "2ef47aee-8844-490c-804d-2a8efe561c65",
+                "links": [
+                    {
+                        "href": "fake-url-1",
+                        "rel": "self"
+                    },
+                    {
+                        "href": "fake-url-2",
+                        "rel": "bookmark"
+                    }
+                ],
+                "name": "backup001"
+            }
+        ]
+    }
+
+    FAKE_BACKUP_LIST_WITH_DETAIL = {
+        "backups": [
+            {
+                "availability_zone": "az1",
+                "container": "volumebackups",
+                "created_at": "2013-04-02T10:35:27.000000",
+                "description": None,
+                "fail_reason": None,
+                "id": "2ef47aee-8844-490c-804d-2a8efe561c65",
+                "links": [
+                    {
+                        "href": "fake-url-1",
+                        "rel": "self"
+                    },
+                    {
+                        "href": "fake-url-2",
+                        "rel": "bookmark"
+                    }
+                ],
+                "name": "backup001",
+                "object_count": 22,
+                "size": 1,
+                "status": "available",
+                "volume_id": "e5185058-943a-4cb4-96d9-72c184c337d6",
+                "is_incremental": True,
+                "has_dependent_backups": False
+            }
+        ]
+    }
+
     FAKE_BACKUP_UPDATE = {
         "backup": {
             "id": "4c65c15f-a5c5-464b-b92a-90e4c04636a7",
@@ -35,6 +84,46 @@
                                                    'volume',
                                                    'regionOne')
 
+    def _test_list_backups(self, detail=False, mock_args='backups',
+                           bytes_body=False, **params):
+        if detail:
+            resp_body = self.FAKE_BACKUP_LIST_WITH_DETAIL
+        else:
+            resp_body = self.FAKE_BACKUP_LIST
+        self.check_service_client_function(
+            self.client.list_backups,
+            'tempest.lib.common.rest_client.RestClient.get',
+            resp_body,
+            to_utf=bytes_body,
+            mock_args=[mock_args],
+            detail=detail,
+            **params)
+
+    def test_list_backups_with_str_body(self):
+        self._test_list_backups()
+
+    def test_list_backups_with_bytes_body(self):
+        self._test_list_backups(bytes_body=True)
+
+    def test_list_backups_with_detail_with_str_body(self):
+        mock_args = "backups/detail"
+        self._test_list_backups(detail=True, mock_args=mock_args)
+
+    def test_list_backups_with_detail_with_bytes_body(self):
+        mock_args = "backups/detail"
+        self._test_list_backups(detail=True, mock_args=mock_args,
+                                bytes_body=True)
+
+    def test_list_backups_with_params(self):
+        # Run the test separately for each param, to avoid assertion error
+        # resulting from randomized params order.
+        mock_args = 'backups?sort_key=name'
+        self._test_list_backups(mock_args=mock_args, sort_key='name')
+
+        mock_args = 'backups/detail?limit=10'
+        self._test_list_backups(detail=True, mock_args=mock_args,
+                                bytes_body=True, limit=10)
+
     def _test_update_backup(self, bytes_body=False):
         self.check_service_client_function(
             self.client.update_backup,
diff --git a/tempest/tests/lib/services/volume/v2/test_capabilities_client.py b/tempest/tests/lib/services/volume/v3/test_capabilities_client.py
similarity index 97%
rename from tempest/tests/lib/services/volume/v2/test_capabilities_client.py
rename to tempest/tests/lib/services/volume/v3/test_capabilities_client.py
index 3d3f1e1..7efe1ff 100644
--- a/tempest/tests/lib/services/volume/v2/test_capabilities_client.py
+++ b/tempest/tests/lib/services/volume/v3/test_capabilities_client.py
@@ -13,7 +13,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest.lib.services.volume.v2 import capabilities_client
+from tempest.lib.services.volume.v3 import capabilities_client
 from tempest.tests.lib import fake_auth_provider
 from tempest.tests.lib.services import base
 
diff --git a/tempest/tests/lib/services/volume/v2/test_encryption_types_client.py b/tempest/tests/lib/services/volume/v3/test_encryption_types_client.py
similarity index 81%
rename from tempest/tests/lib/services/volume/v2/test_encryption_types_client.py
rename to tempest/tests/lib/services/volume/v3/test_encryption_types_client.py
index d029091..c788181 100644
--- a/tempest/tests/lib/services/volume/v2/test_encryption_types_client.py
+++ b/tempest/tests/lib/services/volume/v3/test_encryption_types_client.py
@@ -12,7 +12,7 @@
 # License for the specific language governing permissions and limitations
 # under the License.
 
-from tempest.lib.services.volume.v2 import encryption_types_client
+from tempest.lib.services.volume.v3 import encryption_types_client
 from tempest.tests.lib import fake_auth_provider
 from tempest.tests.lib.services import base
 
@@ -43,6 +43,10 @@
         }
     }
 
+    FAKE_ENCRYPTION_SPECS_ITEM = {
+        "cipher": "aes-xts-plain64"
+    }
+
     def setUp(self):
         super(TestEncryptionTypesClient, self).setUp()
         fake_auth = fake_auth_provider.FakeAuthProvider()
@@ -65,6 +69,13 @@
             self.FAKE_INFO_ENCRYPTION_TYPE,
             bytes_body, volume_type_id="cbc36478b0bd8e67e89")
 
+    def _test_show_encryption_specs_item(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.show_encryption_specs_item,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_ENCRYPTION_SPECS_ITEM,
+            bytes_body, volume_type_id="cbc36478b0bd8e67e89", key="cipher")
+
     def test_create_encryption_type_with_str_body(self):
         self._test_create_encryption()
 
@@ -77,6 +88,12 @@
     def test_show_encryption_type_with_bytes_body(self):
         self._test_show_encryption_type(bytes_body=True)
 
+    def test_show_encryption_specs_item_with_str_body(self):
+        self._test_show_encryption_specs_item()
+
+    def test_show_encryption_specs_item_with_bytes_body(self):
+        self._test_show_encryption_specs_item(bytes_body=True)
+
     def test_delete_encryption_type(self):
         self.check_service_client_function(
             self.client.delete_encryption_type,
diff --git a/tempest/tests/lib/services/volume/v2/test_extensions_client.py b/tempest/tests/lib/services/volume/v3/test_extensions_client.py
similarity index 97%
rename from tempest/tests/lib/services/volume/v2/test_extensions_client.py
rename to tempest/tests/lib/services/volume/v3/test_extensions_client.py
index c0ee421..a8bbffd 100644
--- a/tempest/tests/lib/services/volume/v2/test_extensions_client.py
+++ b/tempest/tests/lib/services/volume/v3/test_extensions_client.py
@@ -13,7 +13,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest.lib.services.volume.v2 import extensions_client
+from tempest.lib.services.volume.v3 import extensions_client
 from tempest.tests.lib import fake_auth_provider
 from tempest.tests.lib.services import base
 
diff --git a/tempest/tests/lib/services/volume/v2/test_hosts_client.py b/tempest/tests/lib/services/volume/v3/test_hosts_client.py
similarity index 98%
rename from tempest/tests/lib/services/volume/v2/test_hosts_client.py
rename to tempest/tests/lib/services/volume/v3/test_hosts_client.py
index e107910..67ae4fd 100644
--- a/tempest/tests/lib/services/volume/v2/test_hosts_client.py
+++ b/tempest/tests/lib/services/volume/v3/test_hosts_client.py
@@ -13,7 +13,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest.lib.services.volume.v2 import hosts_client
+from tempest.lib.services.volume.v3 import hosts_client
 from tempest.tests.lib import fake_auth_provider
 from tempest.tests.lib.services import base
 
diff --git a/tempest/tests/lib/services/volume/v2/test_limits_client.py b/tempest/tests/lib/services/volume/v3/test_limits_client.py
similarity index 97%
rename from tempest/tests/lib/services/volume/v2/test_limits_client.py
rename to tempest/tests/lib/services/volume/v3/test_limits_client.py
index 202054c..f94fbe1 100644
--- a/tempest/tests/lib/services/volume/v2/test_limits_client.py
+++ b/tempest/tests/lib/services/volume/v3/test_limits_client.py
@@ -13,7 +13,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest.lib.services.volume.v2 import limits_client
+from tempest.lib.services.volume.v3 import limits_client
 from tempest.tests.lib import fake_auth_provider
 from tempest.tests.lib.services import base
 
diff --git a/tempest/tests/lib/services/volume/v2/test_quota_classes_client.py b/tempest/tests/lib/services/volume/v3/test_quota_classes_client.py
similarity index 97%
rename from tempest/tests/lib/services/volume/v2/test_quota_classes_client.py
rename to tempest/tests/lib/services/volume/v3/test_quota_classes_client.py
index e715fcc..6190733 100644
--- a/tempest/tests/lib/services/volume/v2/test_quota_classes_client.py
+++ b/tempest/tests/lib/services/volume/v3/test_quota_classes_client.py
@@ -15,7 +15,7 @@
 
 import copy
 
-from tempest.lib.services.volume.v2 import quota_classes_client
+from tempest.lib.services.volume.v3 import quota_classes_client
 from tempest.tests.lib import fake_auth_provider
 from tempest.tests.lib.services import base
 
diff --git a/tempest/tests/lib/services/volume/v2/test_quotas_client.py b/tempest/tests/lib/services/volume/v3/test_quotas_client.py
similarity index 97%
rename from tempest/tests/lib/services/volume/v2/test_quotas_client.py
rename to tempest/tests/lib/services/volume/v3/test_quotas_client.py
index 6384350..aa5d251 100644
--- a/tempest/tests/lib/services/volume/v2/test_quotas_client.py
+++ b/tempest/tests/lib/services/volume/v3/test_quotas_client.py
@@ -12,7 +12,7 @@
 # License for the specific language governing permissions and limitations
 # under the License.
 
-from tempest.lib.services.volume.v2 import quotas_client
+from tempest.lib.services.volume.v3 import quotas_client
 from tempest.tests.lib import fake_auth_provider
 from tempest.tests.lib.services import base
 
diff --git a/tempest/tests/lib/services/volume/v2/test_scheduler_stats_client.py b/tempest/tests/lib/services/volume/v3/test_scheduler_stats_client.py
similarity index 97%
rename from tempest/tests/lib/services/volume/v2/test_scheduler_stats_client.py
rename to tempest/tests/lib/services/volume/v3/test_scheduler_stats_client.py
index 8a5f25f..e0f5566 100644
--- a/tempest/tests/lib/services/volume/v2/test_scheduler_stats_client.py
+++ b/tempest/tests/lib/services/volume/v3/test_scheduler_stats_client.py
@@ -13,7 +13,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest.lib.services.volume.v2 import scheduler_stats_client
+from tempest.lib.services.volume.v3 import scheduler_stats_client
 from tempest.tests.lib import fake_auth_provider
 from tempest.tests.lib.services import base
 
diff --git a/tempest/tests/lib/services/volume/v3/test_services_client.py b/tempest/tests/lib/services/volume/v3/test_services_client.py
new file mode 100644
index 0000000..f65228f
--- /dev/null
+++ b/tempest/tests/lib/services/volume/v3/test_services_client.py
@@ -0,0 +1,214 @@
+# Copyright 2018 FiberHome Telecommunication Technologies CO.,LTD
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+import copy
+
+import mock
+from oslo_serialization import jsonutils as json
+
+from tempest.lib.services.volume.v3 import services_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestServicesClient(base.BaseServiceTest):
+
+    FAKE_SERVICE_LIST = {
+        "services": [
+            {
+                "status": "enabled",
+                "binary": "cinder-backup",
+                "zone": "nova",
+                "state": "up",
+                "updated_at": "2017-07-20T07:20:17.000000",
+                "host": "fake-host",
+                "disabled_reason": None
+            },
+            {
+                "status": "enabled",
+                "binary": "cinder-scheduler",
+                "zone": "nova",
+                "state": "up",
+                "updated_at": "2017-07-20T07:20:24.000000",
+                "host": "fake-host",
+                "disabled_reason": None
+            },
+            {
+                "status": "enabled",
+                "binary": "cinder-volume",
+                "zone": "nova",
+                "frozen": False,
+                "state": "up",
+                "updated_at": "2017-07-20T07:20:20.000000",
+                "host": "fake-host@lvm",
+                "replication_status": "disabled",
+                "active_backend_id": None,
+                "disabled_reason": None
+            }
+        ]
+    }
+
+    FAKE_SERVICE_REQUEST = {
+        "host": "fake-host",
+        "binary": "cinder-volume"
+    }
+
+    FAKE_SERVICE_RESPONSE = {
+        "disabled": False,
+        "status": "enabled",
+        "host": "fake-host@lvm",
+        "service": "",
+        "binary": "cinder-volume",
+        "disabled_reason": None
+    }
+
+    def setUp(self):
+        super(TestServicesClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = services_client.ServicesClient(fake_auth,
+                                                     'volume',
+                                                     'regionOne')
+
+    def _test_list_services(self, bytes_body=False,
+                            mock_args='os-services', **params):
+        self.check_service_client_function(
+            self.client.list_services,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_SERVICE_LIST,
+            to_utf=bytes_body,
+            mock_args=[mock_args],
+            **params)
+
+    def _test_enable_service(self, bytes_body=False):
+        resp_body = self.FAKE_SERVICE_RESPONSE
+        kwargs = self.FAKE_SERVICE_REQUEST
+        payload = json.dumps(kwargs, sort_keys=True)
+        json_dumps = json.dumps
+
+        # 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(services_client.json, 'dumps') as mock_dumps:
+            mock_dumps.side_effect = lambda d: json_dumps(d, sort_keys=True)
+
+            self.check_service_client_function(
+                self.client.enable_service,
+                'tempest.lib.common.rest_client.RestClient.put',
+                resp_body,
+                to_utf=bytes_body,
+                mock_args=['os-services/enable', payload],
+                **kwargs)
+
+    def _test_disable_service(self, bytes_body=False):
+        resp_body = copy.deepcopy(self.FAKE_SERVICE_RESPONSE)
+        resp_body.pop('disabled_reason')
+        resp_body['disabled'] = True
+        resp_body['status'] = 'disabled'
+        kwargs = self.FAKE_SERVICE_REQUEST
+        payload = json.dumps(kwargs, sort_keys=True)
+        json_dumps = json.dumps
+
+        # 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(services_client.json, 'dumps') as mock_dumps:
+            mock_dumps.side_effect = lambda d: json_dumps(d, sort_keys=True)
+
+            self.check_service_client_function(
+                self.client.disable_service,
+                'tempest.lib.common.rest_client.RestClient.put',
+                resp_body,
+                to_utf=bytes_body,
+                mock_args=['os-services/disable', payload],
+                **kwargs)
+
+    def _test_disable_log_reason(self, bytes_body=False):
+        resp_body = copy.deepcopy(self.FAKE_SERVICE_RESPONSE)
+        resp_body['disabled_reason'] = "disabled for test"
+        resp_body['disabled'] = True
+        resp_body['status'] = 'disabled'
+        kwargs = copy.deepcopy(self.FAKE_SERVICE_REQUEST)
+        kwargs.update({"disabled_reason": "disabled for test"})
+        payload = json.dumps(kwargs, sort_keys=True)
+        json_dumps = json.dumps
+
+        # 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(services_client.json, 'dumps') as mock_dumps:
+            mock_dumps.side_effect = lambda d: json_dumps(d, sort_keys=True)
+
+            self.check_service_client_function(
+                self.client.disable_log_reason,
+                'tempest.lib.common.rest_client.RestClient.put',
+                resp_body,
+                to_utf=bytes_body,
+                mock_args=['os-services/disable-log-reason', payload],
+                **kwargs)
+
+    def _test_freeze_host(self, bytes_body=False):
+        kwargs = {'host': 'host1@lvm'}
+        self.check_service_client_function(
+            self.client.freeze_host,
+            'tempest.lib.common.rest_client.RestClient.put',
+            {},
+            bytes_body,
+            **kwargs)
+
+    def _test_thaw_host(self, bytes_body=False):
+        kwargs = {'host': 'host1@lvm'}
+        self.check_service_client_function(
+            self.client.thaw_host,
+            'tempest.lib.common.rest_client.RestClient.put',
+            {},
+            bytes_body,
+            **kwargs)
+
+    def test_list_services_with_str_body(self):
+        self._test_list_services()
+
+    def test_list_services_with_bytes_body(self):
+        self._test_list_services(bytes_body=True)
+
+    def test_list_services_with_params(self):
+        mock_args = 'os-services?host=fake-host'
+        self._test_list_services(mock_args=mock_args, host='fake-host')
+
+    def test_enable_service_with_str_body(self):
+        self._test_enable_service()
+
+    def test_enable_service_with_bytes_body(self):
+        self._test_enable_service(bytes_body=True)
+
+    def test_disable_service_with_str_body(self):
+        self._test_disable_service()
+
+    def test_disable_service_with_bytes_body(self):
+        self._test_disable_service(bytes_body=True)
+
+    def test_disable_log_reason_with_str_body(self):
+        self._test_disable_log_reason()
+
+    def test_disable_log_reason_with_bytes_body(self):
+        self._test_disable_log_reason(bytes_body=True)
+
+    def test_freeze_host_with_str_body(self):
+        self._test_freeze_host()
+
+    def test_freeze_host_with_bytes_body(self):
+        self._test_freeze_host(bytes_body=True)
+
+    def test_thaw_host_with_str_body(self):
+        self._test_thaw_host()
+
+    def test_thaw_host_with_bytes_body(self):
+        self._test_thaw_host(bytes_body=True)
diff --git a/tempest/tests/lib/services/volume/v2/test_snapshot_manage_client.py b/tempest/tests/lib/services/volume/v3/test_snapshot_manage_client.py
similarity index 97%
rename from tempest/tests/lib/services/volume/v2/test_snapshot_manage_client.py
rename to tempest/tests/lib/services/volume/v3/test_snapshot_manage_client.py
index 3fe8970..1b88020 100644
--- a/tempest/tests/lib/services/volume/v2/test_snapshot_manage_client.py
+++ b/tempest/tests/lib/services/volume/v3/test_snapshot_manage_client.py
@@ -17,7 +17,7 @@
 
 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
 from tempest.tests.lib import fake_auth_provider
 from tempest.tests.lib.services import base
 
diff --git a/tempest/tests/lib/services/volume/v2/test_snapshots_client.py b/tempest/tests/lib/services/volume/v3/test_snapshots_client.py
similarity index 99%
rename from tempest/tests/lib/services/volume/v2/test_snapshots_client.py
rename to tempest/tests/lib/services/volume/v3/test_snapshots_client.py
index c9f57a0..2efd2e6 100644
--- a/tempest/tests/lib/services/volume/v2/test_snapshots_client.py
+++ b/tempest/tests/lib/services/volume/v3/test_snapshots_client.py
@@ -12,7 +12,7 @@
 # License for the specific language governing permissions and limitations
 # under the License.
 
-from tempest.lib.services.volume.v2 import snapshots_client
+from tempest.lib.services.volume.v3 import snapshots_client
 from tempest.tests.lib import fake_auth_provider
 from tempest.tests.lib.services import base
 
diff --git a/tempest/tests/lib/services/volume/v2/test_transfers_client.py b/tempest/tests/lib/services/volume/v3/test_transfers_client.py
similarity index 98%
rename from tempest/tests/lib/services/volume/v2/test_transfers_client.py
rename to tempest/tests/lib/services/volume/v3/test_transfers_client.py
index 84f4992..d631fe7 100644
--- a/tempest/tests/lib/services/volume/v2/test_transfers_client.py
+++ b/tempest/tests/lib/services/volume/v3/test_transfers_client.py
@@ -18,7 +18,7 @@
 import mock
 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
 from tempest.tests.lib import fake_auth_provider
 from tempest.tests.lib.services import base
 
diff --git a/tempest/tests/lib/services/volume/v2/test_volume_manage_client.py b/tempest/tests/lib/services/volume/v3/test_volume_manage_client.py
similarity index 98%
rename from tempest/tests/lib/services/volume/v2/test_volume_manage_client.py
rename to tempest/tests/lib/services/volume/v3/test_volume_manage_client.py
index ea4a9f9..902f027 100644
--- a/tempest/tests/lib/services/volume/v2/test_volume_manage_client.py
+++ b/tempest/tests/lib/services/volume/v3/test_volume_manage_client.py
@@ -17,7 +17,7 @@
 
 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
 from tempest.tests.lib import fake_auth_provider
 from tempest.tests.lib.services import base
 
diff --git a/tempest/tests/lib/services/volume/v3/test_volumes_client.py b/tempest/tests/lib/services/volume/v3/test_volumes_client.py
index a515fd3..1250536 100644
--- a/tempest/tests/lib/services/volume/v3/test_volumes_client.py
+++ b/tempest/tests/lib/services/volume/v3/test_volumes_client.py
@@ -13,6 +13,8 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from oslo_serialization import jsonutils as json
+
 from tempest.lib.services.volume.v3 import volumes_client
 from tempest.tests.lib import fake_auth_provider
 from tempest.tests.lib.services import base
@@ -27,6 +29,25 @@
         }
     }
 
+    FAKE_VOLUME_METADATA_ITEM = {
+        "meta": {
+            "key1": "value1"
+        }
+    }
+
+    FAKE_VOLUME_IMAGE_METADATA = {
+        "metadata": {
+            "container_format": "bare",
+            "min_ram": "0",
+            "disk_format": "raw",
+            "image_name": "xly-ubuntu16-server",
+            "image_id": "3e087b0c-10c5-4255-b147-6e8e9dbad6fc",
+            "checksum": "008f5d22fe3cb825d714da79607a90f9",
+            "min_disk": "0",
+            "size": "8589934592"
+        }
+    }
+
     def setUp(self):
         super(TestVolumesClient, self).setUp()
         fake_auth = fake_auth_provider.FakeAuthProvider()
@@ -34,6 +55,60 @@
                                                    'volume',
                                                    'regionOne')
 
+    def _test_retype_volume(self, bytes_body=False):
+        kwargs = {
+            "new_type": "dedup-tier-replication",
+            "migration_policy": "never"
+        }
+
+        self.check_service_client_function(
+            self.client.retype_volume,
+            'tempest.lib.common.rest_client.RestClient.post',
+            {},
+            to_utf=bytes_body,
+            status=202,
+            volume_id="a3be971b-8de5-4bdf-bdb8-3d8eb0fb69f8",
+            **kwargs
+        )
+
+    def _test_force_detach_volume(self, bytes_body=False):
+        kwargs = {
+            'attachment_id': '6980e295-920f-412e-b189-05c50d605acd',
+            'connector': {
+                'initiator': 'iqn.2017-04.org.fake:01'
+            }
+        }
+
+        self.check_service_client_function(
+            self.client.force_detach_volume,
+            'tempest.lib.common.rest_client.RestClient.post',
+            {},
+            to_utf=bytes_body,
+            status=202,
+            volume_id="a3be971b-8de5-4bdf-bdb8-3d8eb0fb69f8",
+            **kwargs
+        )
+
+    def _test_show_volume_metadata_item(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.show_volume_metadata_item,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_VOLUME_METADATA_ITEM,
+            to_utf=bytes_body,
+            volume_id="a3be971b-8de5-4bdf-bdb8-3d8eb0fb69f8",
+            id="key1")
+
+    def _test_show_volume_image_metadata(self, bytes_body=False):
+        fake_volume_id = "a3be971b-8de5-4bdf-bdb8-3d8eb0fb69f8"
+        self.check_service_client_function(
+            self.client.show_volume_image_metadata,
+            'tempest.lib.common.rest_client.RestClient.post',
+            self.FAKE_VOLUME_IMAGE_METADATA,
+            to_utf=bytes_body,
+            mock_args=['volumes/%s/action' % fake_volume_id,
+                       json.dumps({"os-show_image_metadata": {}})],
+            volume_id=fake_volume_id)
+
     def _test_show_volume_summary(self, bytes_body=False):
         self.check_service_client_function(
             self.client.show_volume_summary,
@@ -41,6 +116,30 @@
             self.FAKE_VOLUME_SUMMARY,
             bytes_body)
 
+    def test_force_detach_volume_with_str_body(self):
+        self._test_force_detach_volume()
+
+    def test_force_detach_volume_with_bytes_body(self):
+        self._test_force_detach_volume(bytes_body=True)
+
+    def test_show_volume_metadata_item_with_str_body(self):
+        self._test_show_volume_metadata_item()
+
+    def test_show_volume_metadata_item_with_bytes_body(self):
+        self._test_show_volume_metadata_item(bytes_body=True)
+
+    def test_show_volume_image_metadata_with_str_body(self):
+        self._test_show_volume_image_metadata()
+
+    def test_show_volume_image_metadata_with_bytes_body(self):
+        self._test_show_volume_image_metadata(bytes_body=True)
+
+    def test_retype_volume_with_str_body(self):
+        self._test_retype_volume()
+
+    def test_retype_volume_with_bytes_body(self):
+        self._test_retype_volume(bytes_body=True)
+
     def test_show_volume_summary_with_str_body(self):
         self._test_show_volume_summary()
 
diff --git a/tempest/tests/lib/test_decorators.py b/tempest/tests/lib/test_decorators.py
index ed0eea3..0b1a599 100644
--- a/tempest/tests/lib/test_decorators.py
+++ b/tempest/tests/lib/test_decorators.py
@@ -19,6 +19,7 @@
 from tempest.lib import base as test
 from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
+from tempest.lib import exceptions as lib_exc
 from tempest.tests import base
 
 
@@ -62,21 +63,40 @@
 
         t = TestFoo('test_bar')
         if expected_to_skip:
-            self.assertRaises(testtools.TestCase.skipException, t.test_bar)
+            e = self.assertRaises(testtools.TestCase.skipException, t.test_bar)
+            bug = decorator_args['bug']
+            bug_type = decorator_args.get('bug_type', 'launchpad')
+            self.assertRegex(
+                str(e),
+                r'Skipped until bug\: %s.*' % decorators._get_bug_url(
+                    bug, bug_type)
+            )
         else:
             # assert that test_bar returned 0
             self.assertEqual(TestFoo('test_bar').test_bar(), 0)
 
-    def test_skip_because_bug(self):
+    def test_skip_because_launchpad_bug(self):
         self._test_skip_because_helper(bug='12345')
 
-    def test_skip_because_bug_and_condition_true(self):
+    def test_skip_because_launchpad_bug_and_condition_true(self):
         self._test_skip_because_helper(bug='12348', condition=True)
 
-    def test_skip_because_bug_and_condition_false(self):
+    def test_skip_because_launchpad_bug_and_condition_false(self):
         self._test_skip_because_helper(expected_to_skip=False,
                                        bug='12349', condition=False)
 
+    def test_skip_because_storyboard_bug(self):
+        self._test_skip_because_helper(bug='1992', bug_type='storyboard')
+
+    def test_skip_because_storyboard_bug_and_condition_true(self):
+        self._test_skip_because_helper(bug='1992', bug_type='storyboard',
+                                       condition=True)
+
+    def test_skip_because_storyboard_bug_and_condition_false(self):
+        self._test_skip_because_helper(expected_to_skip=False,
+                                       bug='1992', bug_type='storyboard',
+                                       condition=False)
+
     def test_skip_because_bug_without_bug_never_skips(self):
         """Never skip without a bug parameter."""
         self._test_skip_because_helper(expected_to_skip=False,
@@ -84,8 +104,8 @@
         self._test_skip_because_helper(expected_to_skip=False)
 
     def test_skip_because_invalid_bug_number(self):
-        """Raise ValueError if with an invalid bug number"""
-        self.assertRaises(ValueError, self._test_skip_because_helper,
+        """Raise InvalidParam if with an invalid bug number"""
+        self.assertRaises(lib_exc.InvalidParam, self._test_skip_because_helper,
                           bug='critical_bug')
 
 
@@ -126,6 +146,13 @@
 
 
 class TestRelatedBugDecorator(base.TestCase):
+
+    def _get_my_exception(self):
+        class MyException(Exception):
+            def __init__(self, status_code):
+                self.status_code = status_code
+        return MyException
+
     def test_relatedbug_when_no_exception(self):
         f = mock.Mock()
         sentinel = object()
@@ -137,10 +164,9 @@
         test_foo(sentinel)
         f.assert_called_once_with(sentinel)
 
-    def test_relatedbug_when_exception(self):
-        class MyException(Exception):
-            def __init__(self, status_code):
-                self.status_code = status_code
+    def test_relatedbug_when_exception_with_launchpad_bug_type(self):
+        """Validate related_bug decorator with bug_type == 'launchpad'"""
+        MyException = self._get_my_exception()
 
         def f(self):
             raise MyException(status_code=500)
@@ -152,4 +178,53 @@
         with mock.patch.object(decorators.LOG, 'error') as m_error:
             self.assertRaises(MyException, test_foo, object())
 
-        m_error.assert_called_once_with(mock.ANY, '1234', '1234')
+        m_error.assert_called_once_with(
+            mock.ANY, '1234', 'https://launchpad.net/bugs/1234')
+
+    def test_relatedbug_when_exception_with_storyboard_bug_type(self):
+        """Validate related_bug decorator with bug_type == 'storyboard'"""
+        MyException = self._get_my_exception()
+
+        def f(self):
+            raise MyException(status_code=500)
+
+        @decorators.related_bug(bug="1234", status_code=500,
+                                bug_type='storyboard')
+        def test_foo(self):
+            f(self)
+
+        with mock.patch.object(decorators.LOG, 'error') as m_error:
+            self.assertRaises(MyException, test_foo, object())
+
+        m_error.assert_called_once_with(
+            mock.ANY, '1234', 'https://storyboard.openstack.org/#!/story/1234')
+
+    def test_relatedbug_when_exception_invalid_bug_type(self):
+        """Check related_bug decorator raises exc when bug_type is not valid"""
+        MyException = self._get_my_exception()
+
+        def f(self):
+            raise MyException(status_code=500)
+
+        @decorators.related_bug(bug="1234", status_code=500,
+                                bug_type=mock.sentinel.invalid)
+        def test_foo(self):
+            f(self)
+
+        with mock.patch.object(decorators.LOG, 'error'):
+            self.assertRaises(lib_exc.InvalidParam, test_foo, object())
+
+    def test_relatedbug_when_exception_invalid_bug_number(self):
+        """Check related_bug decorator raises exc when bug_number != digit"""
+        MyException = self._get_my_exception()
+
+        def f(self):
+            raise MyException(status_code=500)
+
+        @decorators.related_bug(bug="not a digit", status_code=500,
+                                bug_type='launchpad')
+        def test_foo(self):
+            f(self)
+
+        with mock.patch.object(decorators.LOG, 'error'):
+            self.assertRaises(lib_exc.InvalidParam, test_foo, object())
diff --git a/tempest/tests/services/object_storage/test_object_client.py b/tempest/tests/services/object_storage/test_object_client.py
deleted file mode 100644
index 86535f9..0000000
--- a/tempest/tests/services/object_storage/test_object_client.py
+++ /dev/null
@@ -1,108 +0,0 @@
-# Copyright 2016 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.
-
-
-import mock
-
-from tempest.lib import exceptions
-from tempest.services.object_storage import object_client
-from tempest.tests import base
-from tempest.tests.lib import fake_auth_provider
-
-
-class TestObjectClient(base.TestCase):
-
-    def setUp(self):
-        super(TestObjectClient, self).setUp()
-        self.fake_auth = fake_auth_provider.FakeAuthProvider()
-        self.url = self.fake_auth.base_url(None)
-        self.object_client = object_client.ObjectClient(self.fake_auth,
-                                                        'swift', 'region1')
-
-    @mock.patch.object(object_client, '_create_connection')
-    def test_create_object_continue_no_data(self, mock_poc):
-        self._validate_create_object_continue(None, mock_poc)
-
-    @mock.patch.object(object_client, '_create_connection')
-    def test_create_object_continue_with_data(self, mock_poc):
-        self._validate_create_object_continue('hello', mock_poc)
-
-    @mock.patch.object(object_client, '_create_connection')
-    def test_create_continue_with_no_continue_received(self, mock_poc):
-        self._validate_create_object_continue('hello', mock_poc,
-                                              initial_status=201)
-
-    def _validate_create_object_continue(self, req_data,
-                                         mock_poc, initial_status=100):
-
-        expected_hdrs = {
-            'X-Auth-Token': self.fake_auth.get_token(),
-            'content-length': 0 if req_data is None else len(req_data),
-            'Expect': '100-continue'}
-
-        # Setup the Mocks prior to invoking the object creation
-        mock_resp_cls = mock.Mock()
-        mock_resp_cls._read_status.return_value = ("1", initial_status, "OK")
-
-        mock_poc.return_value.response_class.return_value = mock_resp_cls
-
-        # This is the final expected return value
-        mock_poc.return_value.getresponse.return_value.status = 201
-        mock_poc.return_value.getresponse.return_value.reason = 'OK'
-
-        # Call method to PUT object using expect:100-continue
-        cnt = "container1"
-        obj = "object1"
-        path = "/%s/%s" % (cnt, obj)
-
-        # If the expected initial status is not 100, then an exception
-        # should be thrown and the connection closed
-        if initial_status is 100:
-            status, reason = \
-                self.object_client.create_object_continue(cnt, obj, req_data)
-        else:
-            self.assertRaises(exceptions.UnexpectedResponseCode,
-                              self.object_client.create_object_continue, cnt,
-                              obj, req_data)
-            mock_poc.return_value.close.assert_called_once_with()
-
-        # Verify that putrequest is called 1 time with the appropriate values
-        mock_poc.return_value.putrequest.assert_called_once_with('PUT', path)
-
-        # Verify that headers were written, including "Expect:100-continue"
-        calls = []
-
-        for header, value in expected_hdrs.items():
-            calls.append(mock.call(header, value))
-
-        mock_poc.return_value.putheader.assert_has_calls(calls, False)
-        mock_poc.return_value.endheaders.assert_called_once_with()
-
-        # The following steps are only taken if the initial status is 100
-        if initial_status is 100:
-            # Verify that the method returned what it was supposed to
-            self.assertEqual(status, 201)
-
-            # Verify that _safe_read was called once to remove the CRLF
-            # after the 100 response
-            mock_rc = mock_poc.return_value.response_class.return_value
-            mock_rc._safe_read.assert_called_once_with(2)
-
-            # Verify the actual data was written via send
-            mock_poc.return_value.send.assert_called_once_with(req_data)
-
-            # Verify that the getresponse method was called to receive
-            # the final
-            mock_poc.return_value.getresponse.assert_called_once_with()
diff --git a/tempest/tests/test_hacking.py b/tempest/tests/test_hacking.py
index bc3a753..9534ce8 100644
--- a/tempest/tests/test_hacking.py
+++ b/tempest/tests/test_hacking.py
@@ -193,3 +193,60 @@
             "raise TestCase.failureException(exception.message)"))), 1)
         self.assertEqual(len(list(checks.unsupported_exception_attribute_PY3(
             "raise TestCase.failureException(ee.message)"))), 0)
+
+    def _test_no_negatve_test_attribute_applied_to_negative_test(
+            self, filename, with_other_decorators=False,
+            with_negative_decorator=True, expected_success=True):
+        check = checks.negative_test_attribute_always_applied_to_negative_tests
+        other_decorators = [
+            "@decorators.idempotent_id(123)",
+            "@utils.requires_ext(extension='ext', service='svc')"
+        ]
+
+        if with_other_decorators:
+            # Include multiple decorators to verify that this check works with
+            # arbitrarily many decorators. These insert decorators above the
+            # @decorators.attr(type=['negative']) decorator.
+            for decorator in other_decorators:
+                self.assertIsNone(check(" %s" % decorator, filename))
+        if with_negative_decorator:
+            self.assertIsNone(
+                check("@decorators.attr(type=['negative'])", filename))
+        if with_other_decorators:
+            # Include multiple decorators to verify that this check works with
+            # arbitrarily many decorators. These insert decorators between
+            # the test and the @decorators.attr(type=['negative']) decorator.
+            for decorator in other_decorators:
+                self.assertIsNone(check(" %s" % decorator, filename))
+        final_result = check(" def test_some_negative_case", filename)
+        if expected_success:
+            self.assertIsNone(final_result)
+        else:
+            self.assertIsInstance(final_result, tuple)
+            self.assertFalse(final_result[0])
+
+    def test_no_negatve_test_attribute_applied_to_negative_test(self):
+        # Check negative filename, negative decorator passes
+        self._test_no_negatve_test_attribute_applied_to_negative_test(
+            "./tempest/api/test_something_negative.py")
+        # Check negative filename, negative decorator, other decorators passes
+        self._test_no_negatve_test_attribute_applied_to_negative_test(
+            "./tempest/api/test_something_negative.py",
+            with_other_decorators=True)
+
+        # Check non-negative filename skips check, causing pass
+        self._test_no_negatve_test_attribute_applied_to_negative_test(
+            "./tempest/api/test_something.py")
+
+        # Check negative filename, no negative decorator fails
+        self._test_no_negatve_test_attribute_applied_to_negative_test(
+            "./tempest/api/test_something_negative.py",
+            with_negative_decorator=False,
+            expected_success=False)
+        # Check negative filename, no negative decorator, other decorators
+        # fails
+        self._test_no_negatve_test_attribute_applied_to_negative_test(
+            "./tempest/api/test_something_negative.py",
+            with_other_decorators=True,
+            with_negative_decorator=False,
+            expected_success=False)
diff --git a/tempest/tests/test_list_tests.py b/tempest/tests/test_list_tests.py
index 4af7463..1cc9c9a 100644
--- a/tempest/tests/test_list_tests.py
+++ b/tempest/tests/test_list_tests.py
@@ -34,7 +34,7 @@
                          "error on import %s" % ids)
         ids = six.text_type(ids).split('\n')
         for test_id in ids:
-            if re.match('(\w+\.){3}\w+', test_id):
+            if re.match(r'(\w+\.){3}\w+', test_id):
                 if not test_id.startswith('tempest.'):
                     parts = test_id.partition('tempest')
                     fail_id = parts[1] + parts[2]
diff --git a/test-requirements.txt b/test-requirements.txt
index e33f207..196387c 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -1,7 +1,7 @@
 # The order of packages is significant, because pip processes them in the order
 # of appearance. Changing the order has an impact on the overall integration
 # process, which may cause wedges in the gate later.
-hacking!=0.13.0,<0.14,>=0.12.0 # Apache-2.0
+hacking>=1.1.0,<1.2.0 # Apache-2.0
 mock>=2.0.0 # BSD
 coverage!=4.4,>=4.0 # Apache-2.0
 oslotest>=3.2.0 # Apache-2.0
diff --git a/tools/check_logs.py b/tools/check_logs.py
index b80ccc0..de7e41d 100755
--- a/tools/check_logs.py
+++ b/tools/check_logs.py
@@ -96,7 +96,7 @@
 def collect_url_logs(url):
     page = urlreq.urlopen(url)
     content = page.read()
-    logs = re.findall('(screen-[\w-]+\.txt\.gz)</a>', content)
+    logs = re.findall(r'(screen-[\w-]+\.txt\.gz)</a>', content)
     return logs
 
 
@@ -162,6 +162,7 @@
     print("ok")
     return 0
 
+
 usage = """
 Find non-white-listed log errors in log files from a devstack-gate run.
 Log files will be searched for ERROR or CRITICAL messages. If any
diff --git a/tools/generate-tempest-plugins-list.py b/tools/generate-tempest-plugins-list.py
index dd05438..4eb78fb 100644
--- a/tools/generate-tempest-plugins-list.py
+++ b/tools/generate-tempest-plugins-list.py
@@ -63,23 +63,26 @@
     except HTTPError as err:
         if err.code == 404:
             return False
-    p = re.compile('^tempest\.test_plugins', re.M)
+    p = re.compile(r'^tempest\.test_plugins', re.M)
     if p.findall(r.read().decode('utf-8')):
         return True
     else:
         False
 
+
 r = urllib.urlopen(url)
 # Gerrit prepends 4 garbage octets to the JSON, in order to counter
 # cross-site scripting attacks.  Therefore we must discard it so the
 # 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/tools/generate-tempest-plugins-list.sh b/tools/generate-tempest-plugins-list.sh
index 20c99b2..b27b23a 100755
--- a/tools/generate-tempest-plugins-list.sh
+++ b/tools/generate-tempest-plugins-list.sh
@@ -49,13 +49,37 @@
 
 sorted_plugins=$(python tools/generate-tempest-plugins-list.py)
 
-for k in ${sorted_plugins}; do
-    project=${k:0:28}
-    giturl="git://git.openstack.org/openstack/${k:0:26}"
-    printf "|%-28s|%-73s|\n" "${project}" "${giturl}"
-    printf "+----------------------------+-------------------------------------------------------------------------+\n"
+name_col_len=$(echo "${sorted_plugins}" | wc -L)
+name_col_len=$(( name_col_len + 20 ))
+
+# Print the title underline for a RST table.
+function title_underline {
+    printf "== "
+    local len=$1
+    while [[ $len -gt 0 ]]; do
+        printf "="
+        len=$(( len - 1))
+    done
+    printf " ===\n"
+}
+
+printf "\n\n"
+title_underline ${name_col_len}
+printf "%-3s %-${name_col_len}s %s\n" "SR" "Plugin Name" "URL"
+title_underline ${name_col_len}
+
+i=0
+for plugin in ${sorted_plugins}; do
+    i=$((i+1))
+    giturl="git://git.openstack.org/openstack/${plugin}"
+    gitlink="https://git.openstack.org/cgit/openstack/${plugin}"
+    printf "%-3s %-${name_col_len}s %s\n" "$i" "${plugin}" "\`${giturl} <${gitlink}>\`__"
 done
 
+title_underline ${name_col_len}
+
+printf "\n\n"
+
 if [[ -r doc/source/data/tempest-plugins-registry.footer ]]; then
     cat doc/source/data/tempest-plugins-registry.footer
 fi
diff --git a/tox.ini b/tox.ini
index 892b6f4..970f415 100644
--- a/tox.ini
+++ b/tox.ini
@@ -16,7 +16,10 @@
 setenv =
     VIRTUAL_ENV={envdir}
     OS_LOG_CAPTURE=1
-    PYTHONWARNINGS=default::DeprecationWarning
+    OS_STDOUT_CAPTURE=1
+    OS_STDERR_CAPTURE=1
+    OS_TEST_TIMEOUT=160
+    PYTHONWARNINGS=default::DeprecationWarning,ignore::DeprecationWarning:distutils,ignore::DeprecationWarning:site
 passenv = OS_STDOUT_CAPTURE OS_STDERR_CAPTURE OS_TEST_TIMEOUT OS_TEST_LOCK_PATH TEMPEST_CONFIG TEMPEST_CONFIG_DIR http_proxy HTTP_PROXY https_proxy HTTPS_PROXY no_proxy NO_PROXY ZUUL_CACHE_DIR REQUIREMENTS_PIP_LOCATION GENERATE_TEMPEST_PLUGIN_LIST
 usedevelop = True
 install_command = pip install {opts} {packages}
@@ -75,11 +78,22 @@
 deps = {[tempestenv]deps}
 # The regex below is used to select which tests to run and exclude the slow tag:
 # See the testrepository bug: https://bugs.launchpad.net/testrepository/+bug/1208610
+# FIXME: We can replace it with the `--black-regex` option to exclude tests now.
 commands =
     find . -type f -name "*.pyc" -delete
     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}
@@ -87,6 +101,7 @@
 deps = {[tempestenv]deps}
 # The regex below is used to select which tests to run and exclude the slow tag:
 # See the testrepository bug: https://bugs.launchpad.net/testrepository/+bug/1208610
+# FIXME: We can replace it with the `--black-regex` option to exclude tests now.
 commands =
     find . -type f -name "*.pyc" -delete
     tempest run --serial --regex '(?!.*\[.*\bslow\b.*\])(^tempest\.(api|scenario))' {posargs}
@@ -122,6 +137,16 @@
     find . -type f -name "*.pyc" -delete
     tempest run --serial --regex '\[.*\bsmoke\b.*\]' {posargs}
 
+[testenv:slow-serial]
+envdir = .tox/tempest
+sitepackages = {[tempestenv]sitepackages}
+setenv = {[tempestenv]setenv}
+deps = {[tempestenv]deps}
+# The regex below is used to select the slow tagged tests to run serially:
+commands =
+    find . -type f -name "*.pyc" -delete
+    tempest run --serial --regex '\[.*\bslow\b.*\]' {posargs}
+
 [testenv:venv]
 deps =
   -c{env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt}
@@ -143,7 +168,7 @@
   -r{toxinidir}/doc/requirements.txt
 commands =
   rm -rf doc/build
-  sphinx-build -b html doc/source doc/build/html
+  sphinx-build -W -b html doc/source doc/build/html
 whitelist_externals = rm
 
 [testenv:pep8]
@@ -163,7 +188,8 @@
 # E125 is a won't fix until https://github.com/jcrocholl/pep8/issues/126 is resolved.  For further detail see https://review.openstack.org/#/c/36788/
 # E123 skipped because it is ignored by default in the default pep8
 # E129 skipped because it is too limiting when combined with other rules
-ignore = E125,E123,E129
+# W504 skipped because it is overeager and unnecessary
+ignore = E125,E123,E129,W504
 show-source = True
 exclude = .git,.venv,.tox,dist,doc,*egg,build
 enable-extensions = H106,H203,H904