Merge "Handle 'path' query parameter for test_novnc"
diff --git a/.gitreview b/.gitreview
index 84b5114..a475594 100644
--- a/.gitreview
+++ b/.gitreview
@@ -1,4 +1,4 @@
 [gerrit]
-host=review.openstack.org
+host=review.opendev.org
 port=29418
 project=openstack/tempest.git
diff --git a/.zuul.yaml b/.zuul.yaml
index 9753656..462501e 100644
--- a/.zuul.yaml
+++ b/.zuul.yaml
@@ -8,10 +8,10 @@
       test setup. To run a multi-node test inherit from devstack-tempest and
       set the nodeset to a multi-node one.
     required-projects:
-      - git.openstack.org/openstack/tempest
+      - opendev.org/openstack/tempest
     timeout: 7200
     roles:
-      - zuul: git.openstack.org/openstack-dev/devstack
+      - zuul: opendev.org/openstack/devstack
     vars:
       devstack_services:
         tempest: true
@@ -55,10 +55,10 @@
     description: |
       Base Tempest IPv6 job.
     required-projects:
-      - git.openstack.org/openstack/tempest
+      - opendev.org/openstack/tempest
     timeout: 7200
     roles:
-      - zuul: git.openstack.org/openstack-dev/devstack
+      - zuul: opendev.org/openstack/devstack
     vars:
       devstack_services:
         tempest: true
@@ -113,24 +113,24 @@
       periodic-tempest-dsvm-oslo-latest-full-master.
     timeout: 10800
     required-projects:
-      - git.openstack.org/openstack/oslo.cache
-      - git.openstack.org/openstack/oslo.concurrency
-      - git.openstack.org/openstack/oslo.config
-      - git.openstack.org/openstack/oslo.context
-      - git.openstack.org/openstack/oslo.db
-      - git.openstack.org/openstack/oslo.i18n
-      - git.openstack.org/openstack/oslo.log
-      - git.openstack.org/openstack/oslo.messaging
-      - git.openstack.org/openstack/oslo.middleware
-      - git.openstack.org/openstack/oslo.policy
-      - git.openstack.org/openstack/oslo.privsep
-      - git.openstack.org/openstack/oslo.reports
-      - git.openstack.org/openstack/oslo.rootwrap
-      - git.openstack.org/openstack/oslo.serialization
-      - git.openstack.org/openstack/oslo.service
-      - git.openstack.org/openstack/oslo.utils
-      - git.openstack.org/openstack/oslo.versionedobjects
-      - git.openstack.org/openstack/oslo.vmware
+      - opendev.org/openstack/oslo.cache
+      - opendev.org/openstack/oslo.concurrency
+      - opendev.org/openstack/oslo.config
+      - opendev.org/openstack/oslo.context
+      - opendev.org/openstack/oslo.db
+      - opendev.org/openstack/oslo.i18n
+      - opendev.org/openstack/oslo.log
+      - opendev.org/openstack/oslo.messaging
+      - opendev.org/openstack/oslo.middleware
+      - opendev.org/openstack/oslo.policy
+      - opendev.org/openstack/oslo.privsep
+      - opendev.org/openstack/oslo.reports
+      - opendev.org/openstack/oslo.rootwrap
+      - opendev.org/openstack/oslo.serialization
+      - opendev.org/openstack/oslo.service
+      - opendev.org/openstack/oslo.utils
+      - opendev.org/openstack/oslo.versionedobjects
+      - opendev.org/openstack/oslo.vmware
 
 - job:
     name: tempest-full-parallel
@@ -247,30 +247,10 @@
       devstack_localrc:
         USE_PYTHON3: true
 
-- nodeset:
-    name: openstack-bionic-node
-    nodes:
-      - name: controller
-        label: ubuntu-bionic
-    groups:
-      - name: tempest
-        nodes:
-          - controller
-
-- nodeset:
-    name: openstack-opensuse150-node
-    nodes:
-      - name: controller
-        label: opensuse-150
-    groups:
-      - name: tempest
-        nodes:
-          - controller
-
 - job:
     name: tempest-full-py3-opensuse150
     parent: tempest-full-py3
-    nodeset: openstack-opensuse150-node
+    nodeset: devstack-single-node-opensuse-150
     description: |
       Base integration test with Neutron networking and py36 running
       on openSUSE Leap 15.0
@@ -317,6 +297,16 @@
         c-bak: false
 
 - job:
+    name: tempest-full-stein
+    parent: tempest-full
+    override-checkout: stable/stein
+
+- job:
+    name: tempest-full-stein-py3
+    parent: tempest-full-py3
+    override-checkout: stable/stein
+
+- job:
     name: tempest-full-rocky
     parent: tempest-full
     nodeset: openstack-single-node-xenial
@@ -351,7 +341,7 @@
     parent: tox
     description: |
       Run tempest plugin sanity check script using tox.
-    nodeset: ubuntu-xenial
+    nodeset: ubuntu-bionic
     vars:
       tox_envlist: plugin-sanity-check
     voting: false
@@ -364,82 +354,82 @@
       - ^tempest/hacking/.*$
       - ^tempest/tests/.*$
     required-projects:
-      - git.openstack.org/openstack/airship-tempest-plugin
-      - git.openstack.org/openstack/almanach
-      - git.openstack.org/openstack/aodh
-      - git.openstack.org/openstack/barbican-tempest-plugin
-      - git.openstack.org/openstack/blazar-tempest-plugin
-      - git.openstack.org/openstack/ceilometer
-      - git.openstack.org/openstack/cinder-tempest-plugin
-      - git.openstack.org/openstack/cloudkitty-tempest-plugin
-      - git.openstack.org/openstack/congress-tempest-plugin
-      - git.openstack.org/openstack/designate-tempest-plugin
-      - git.openstack.org/openstack/ec2api-tempest-plugin
-      - git.openstack.org/openstack/freezer
-      - git.openstack.org/openstack/freezer-api
-      - git.openstack.org/openstack/freezer-tempest-plugin
-      - git.openstack.org/openstack/gabbi-tempest
-      - git.openstack.org/openstack/gce-api
-      - git.openstack.org/openstack/glare
-      - git.openstack.org/openstack/heat-tempest-plugin
-      - git.openstack.org/openstack/intel-nfv-ci-tests
-      - git.openstack.org/openstack/ironic-tempest-plugin
-      - 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-tempest-plugin
-      - git.openstack.org/openstack/mogan
-      - git.openstack.org/openstack/monasca-api
-      - git.openstack.org/openstack/monasca-log-api
-      - git.openstack.org/openstack/monasca-tempest-plugin
-      - git.openstack.org/openstack/murano-tempest-plugin
-      - git.openstack.org/openstack/networking-ansible
-      - 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-tempest-plugin
-      - git.openstack.org/openstack/networking-midonet
-      - git.openstack.org/openstack/networking-sfc
-      - git.openstack.org/openstack/networking-spp
-      - 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
-      - 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/python-watcherclient
-      - 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/solum-tempest-plugin
-      - git.openstack.org/openstack/tap-as-a-service
-      - git.openstack.org/openstack/telemetry-tempest-plugin
-      - git.openstack.org/openstack/tempest-horizon
-      - git.openstack.org/openstack/tobiko
-      - git.openstack.org/openstack/trio2o
-      - git.openstack.org/openstack/tripleo-common-tempest-plugin
-      - git.openstack.org/openstack/trove-tempest-plugin
-      - git.openstack.org/openstack/valet
-      - git.openstack.org/openstack/vitrage-tempest-plugin
-      - 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
+      - opendev.org/airship/tempest-plugin
+      - opendev.org/x/almanach
+      - opendev.org/openstack/aodh
+      - opendev.org/openstack/barbican-tempest-plugin
+      - opendev.org/openstack/blazar-tempest-plugin
+      - opendev.org/openstack/ceilometer
+      - opendev.org/openstack/cinder-tempest-plugin
+      - opendev.org/openstack/cloudkitty-tempest-plugin
+      - opendev.org/openstack/congress-tempest-plugin
+      - opendev.org/openstack/cyborg-tempest-plugin
+      - opendev.org/openstack/designate-tempest-plugin
+      - opendev.org/openstack/ec2api-tempest-plugin
+      - opendev.org/openstack/freezer
+      - opendev.org/openstack/freezer-api
+      - opendev.org/openstack/freezer-tempest-plugin
+      - opendev.org/x/gabbi-tempest
+      - opendev.org/x/gce-api
+      - opendev.org/x/glare
+      - opendev.org/openstack/heat-tempest-plugin
+      - opendev.org/x/intel-nfv-ci-tests
+      - opendev.org/openstack/ironic-tempest-plugin
+      - opendev.org/openstack/ironic-inspector
+      - opendev.org/openstack/keystone-tempest-plugin
+      - opendev.org/x/kingbird
+      - opendev.org/openstack/kuryr-tempest-plugin
+      - opendev.org/openstack/magnum
+      - opendev.org/openstack/magnum-tempest-plugin
+      - opendev.org/openstack/manila
+      - opendev.org/openstack/manila-tempest-plugin
+      - opendev.org/openstack/mistral-tempest-plugin
+      - opendev.org/x/mogan
+      - opendev.org/openstack/monasca-api
+      - opendev.org/openstack/monasca-log-api
+      - opendev.org/openstack/monasca-tempest-plugin
+      - opendev.org/openstack/murano-tempest-plugin
+      - opendev.org/x/networking-ansible
+      - opendev.org/openstack/networking-bgpvpn
+      - opendev.org/x/networking-cisco
+      - opendev.org/x/networking-fortinet
+      - opendev.org/openstack/networking-generic-switch
+      - opendev.org/openstack/networking-l2gw-tempest-plugin
+      - opendev.org/openstack/networking-midonet
+      - opendev.org/openstack/networking-sfc
+      - opendev.org/x/networking-spp
+      - opendev.org/openstack/neutron
+      - opendev.org/openstack/neutron-dynamic-routing
+      - opendev.org/openstack/neutron-fwaas
+      - opendev.org/openstack/neutron-lbaas
+      - opendev.org/openstack/neutron-tempest-plugin
+      - opendev.org/openstack/neutron-vpnaas
+      - opendev.org/x/nova-lxd
+      - opendev.org/x/novajoin-tempest-plugin
+      - opendev.org/openstack/octavia-tempest-plugin
+      - opendev.org/openstack/oswin-tempest-plugin
+      - opendev.org/openstack/panko
+      - opendev.org/openstack/patrole
+      - opendev.org/openstack/python-watcherclient
+      - opendev.org/openstack/qinling
+      - opendev.org/openstack/requirements
+      - opendev.org/openstack/sahara-tests
+      - opendev.org/openstack/senlin
+      - opendev.org/openstack/senlin-tempest-plugin
+      - opendev.org/openstack/solum-tempest-plugin
+      - opendev.org/x/tap-as-a-service
+      - opendev.org/openstack/telemetry-tempest-plugin
+      - opendev.org/openstack/tempest-horizon
+      - opendev.org/x/tobiko
+      - opendev.org/x/trio2o
+      - opendev.org/openstack/tripleo-common-tempest-plugin
+      - opendev.org/openstack/trove-tempest-plugin
+      - opendev.org/x/valet
+      - opendev.org/openstack/vitrage-tempest-plugin
+      - opendev.org/x/vmware-nsx-tempest-plugin
+      - opendev.org/openstack/watcher-tempest-plugin
+      - opendev.org/openstack/zaqar-tempest-plugin
+      - opendev.org/openstack/zun-tempest-plugin
 
 - job:
     name: tempest-cinder-v2-api
@@ -520,7 +510,6 @@
         - tempest-full-parallel:
             # Define list of irrelevant files to use everywhere else
             irrelevant-files: &tempest-irrelevant-files
-              - ^(test-|)requirements.txt$
               - ^.*\.rst$
               - ^doc/.*$
               - ^etc/.*$
@@ -534,6 +523,10 @@
         - tempest-full-py3-ipv6:
             voting: false
             irrelevant-files: *tempest-irrelevant-files
+        - tempest-full-stein:
+            irrelevant-files: *tempest-irrelevant-files
+        - tempest-full-stein-py3:
+            irrelevant-files: *tempest-irrelevant-files
         - tempest-full-rocky:
             irrelevant-files: *tempest-irrelevant-files
         - tempest-full-rocky-py3:
@@ -550,7 +543,6 @@
             irrelevant-files: *tempest-irrelevant-files
         - tempest-tox-plugin-sanity-check:
             irrelevant-files:
-              - ^(test-|)requirements.txt$
               - ^.*\.rst$
               - ^doc/.*$
               - ^etc/.*$
@@ -589,8 +581,6 @@
             irrelevant-files: *tempest-irrelevant-files
         - neutron-tempest-dvr:
             irrelevant-files: *tempest-irrelevant-files
-        - legacy-tempest-dsvm-neutron-full-ocata:
-            irrelevant-files: *tempest-irrelevant-files
         - tempest-full:
             irrelevant-files: *tempest-irrelevant-files
         - interop-tempest-consistency:
@@ -623,9 +613,7 @@
             irrelevant-files: *tempest-irrelevant-files
         - neutron-tempest-dvr-ha-multinode-full:
             irrelevant-files: *tempest-irrelevant-files
-        - nova-cells-v1:
-            irrelevant-files: *tempest-irrelevant-files
-        - legacy-tempest-dsvm-nova-v20-api:
+        - nova-tempest-v2-api:
             irrelevant-files: *tempest-irrelevant-files
         - legacy-tempest-dsvm-lvm-multibackend:
             irrelevant-files: *tempest-irrelevant-files
@@ -637,12 +625,13 @@
             irrelevant-files: *tempest-irrelevant-files
     periodic-stable:
       jobs:
+        - tempest-full-stein
+        - tempest-full-stein-py3
         - tempest-full-rocky
         - tempest-full-rocky-py3
         - tempest-full-queens
         - tempest-full-queens-py3
         - tempest-full-pike
-        - legacy-periodic-tempest-dsvm-neutron-full-ocata
     periodic:
       jobs:
         - tempest-all
diff --git a/HACKING.rst b/HACKING.rst
index eb6551a..204b3c7 100644
--- a/HACKING.rst
+++ b/HACKING.rst
@@ -28,6 +28,8 @@
 - [T117] Check negative tests have ``@decorators.attr(type=['negative'])``
   applied.
 
+It is recommended to use ``tox -eautopep8`` before submitting a patch.
+
 Test Data/Configuration
 -----------------------
 - Assume nothing about existing test data
@@ -455,7 +457,7 @@
 by modifying Tempest's `lib installation script`_ for previous branches
 (because DevStack is branched).
 
-.. _lib installation script: https://git.openstack.org/cgit/openstack-dev/devstack/tree/lib/tempest
+.. _lib installation script: https://opendev.org/openstack/devstack/src/branch/master/lib/tempest
 
 2. Bug fix on core project needing Tempest changes
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/README.rst b/README.rst
index 73930f1..841fae6 100644
--- a/README.rst
+++ b/README.rst
@@ -61,7 +61,7 @@
 #. You first need to install Tempest. This is done with pip after you check out
    the Tempest repo::
 
-    $ git clone https://git.openstack.org/openstack/tempest
+    $ git clone https://opendev.org/openstack/tempest
     $ pip install tempest/
 
    This can be done within a venv, but the assumption for this guide is that
@@ -119,6 +119,17 @@
    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.
 
+   Tox also contains several existing job configurations. For example::
+
+    $ tox -e full
+
+   which will run the same set of tests as the OpenStack gate. (it's exactly how
+   the gate invokes Tempest) Or::
+
+    $ tox -e smoke
+
+   to run the tests tagged as smoke.
+
 .. _unittest: https://docs.python.org/3/library/unittest.html
 .. _testr: https://testrepository.readthedocs.org/en/latest/MANUAL.html
 .. _stestr: https://stestr.readthedocs.org/en/latest/MANUAL.html
@@ -270,14 +281,3 @@
 To run one single test serially ::
 
     $ testr run tempest.api.compute.servers.test_servers_negative.ServersNegativeTestJSON.test_reboot_non_existent_server
-
-Tox also contains several existing job configurations. For example::
-
-    $ tox -e full
-
-which will run the same set of tests as the OpenStack gate. (it's exactly how
-the gate invokes Tempest) Or::
-
-    $ tox -e smoke
-
-to run the tests tagged as smoke.
diff --git a/REVIEWING.rst b/REVIEWING.rst
index 31fedce..498ce66 100644
--- a/REVIEWING.rst
+++ b/REVIEWING.rst
@@ -187,4 +187,4 @@
   Note that such a policy should be used judiciously, as we should strive to
   have two +2's on each patch set, prior to approval.
 
-.. _example: https://review.openstack.org/#/c/611032/
+.. _example: https://review.opendev.org/#/c/611032/
diff --git a/doc/source/microversion_testing.rst b/doc/source/microversion_testing.rst
index 4b1c145..b4f06e3 100644
--- a/doc/source/microversion_testing.rst
+++ b/doc/source/microversion_testing.rst
@@ -406,6 +406,14 @@
 
   .. _2.63: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id57
 
+  * `2.70`_
+
+  .. _2.70: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id63
+
+  * `2.71`_
+
+  .. _2.71: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id64
+
 * Volume
 
   * `3.3`_
diff --git a/doc/source/plugin.rst b/doc/source/plugin.rst
index dc0e94c..a9e2059 100644
--- a/doc/source/plugin.rst
+++ b/doc/source/plugin.rst
@@ -43,7 +43,7 @@
 In order to create the basic structure with base classes and test directories
 you can use the tempest-plugin-cookiecutter project::
 
-  > pip install -U cookiecutter && cookiecutter https://git.openstack.org/openstack/tempest-plugin-cookiecutter
+  > pip install -U cookiecutter && cookiecutter https://opendev.org/openstack/tempest-plugin-cookiecutter
 
   Cloning into 'tempest-plugin-cookiecutter'...
   remote: Counting objects: 17, done.
diff --git a/playbooks/devstack-tempest.yaml b/playbooks/devstack-tempest.yaml
index b51e701..5f87abd 100644
--- a/playbooks/devstack-tempest.yaml
+++ b/playbooks/devstack-tempest.yaml
@@ -11,7 +11,7 @@
     # This enviroment variable is used by the optional tempest-gabbi
     # job provided by the gabbi-tempest plugin. It can be safely ignored
     # if that plugin is not being used.
-    GABBI_TEMPEST_PATH: "{{ gabbi_tempest_path }}"
+    GABBI_TEMPEST_PATH: "{{ gabbi_tempest_path | default('') }}"
   roles:
     - setup-tempest-run-dir
     - setup-tempest-data-dir
diff --git a/releasenotes/notes/QoS-client-for-placement-based-minimum-bw-allocation-8e5854d5754cec68.yaml b/releasenotes/notes/QoS-client-for-placement-based-minimum-bw-allocation-8e5854d5754cec68.yaml
new file mode 100644
index 0000000..b66ea3a
--- /dev/null
+++ b/releasenotes/notes/QoS-client-for-placement-based-minimum-bw-allocation-8e5854d5754cec68.yaml
@@ -0,0 +1,25 @@
+---
+features:
+  - |
+    Add ``qos-policies`` and ``qos-minimum-bandwidth-rule`` clients
+    to Tempest to make possible the testing of the placement based
+    bandwidth allocation feature.
+    The following API calls are available for tempest from now:
+
+    ``QoS policies`` client:
+
+    * GET /qos/policies
+    * POST /qos/policies
+    * GET /qos/policies/{policy_id}
+    * PUT /qos/policies/{policy_id}
+    * DELETE /qos/policies/{policy_id}
+
+
+    ``QoS minimum bandwidth rules`` client:
+
+    * GET qos/policies/{policy_id}/minimum_bandwidth_rules
+    * POST /qos/policies/{policy_id}/minimum_bandwidth_rules
+    * GET qos/policies/{policy_id}/minimum_bandwidth_rules/{rule_id}
+    * PUT qos/policies/{policy_id}/minimum_bandwidth_rules/{rule_id}
+    * DELETE /qos/policies/{policy_id}/minimum_bandwidth_rules/{rule_id}
+
diff --git a/releasenotes/notes/add-migrate-volume-and-list-hosts-to-v3-volume-client-library-ad3529260db58f00.yaml b/releasenotes/notes/add-migrate-volume-and-list-hosts-to-v3-volume-client-library-ad3529260db58f00.yaml
new file mode 100644
index 0000000..ca6a78d
--- /dev/null
+++ b/releasenotes/notes/add-migrate-volume-and-list-hosts-to-v3-volume-client-library-ad3529260db58f00.yaml
@@ -0,0 +1,8 @@
+---
+features:
+  - |
+    Add list host API support to the volume v3 client library.
+    This feature enables callers to list all hosts for a given project.
+  - |
+    Add migrate volume API support to the volume v3 client library.
+    This features allows callers to migrate volumes between backends.
diff --git a/releasenotes/notes/add-profiler-config-options-db7c4ae6d338ee5c.yaml b/releasenotes/notes/add-profiler-config-options-db7c4ae6d338ee5c.yaml
new file mode 100644
index 0000000..2245044
--- /dev/null
+++ b/releasenotes/notes/add-profiler-config-options-db7c4ae6d338ee5c.yaml
@@ -0,0 +1,10 @@
+---
+features:
+  - |
+    Add support of `OSProfiler library`_ for profiling and distributed
+    tracing of OpenStack. A new config option ``key`` in section ``profiler``
+    is added, the option sets the secret key used to enable profiling in
+    OpenStack services. The value needs to correspond to the one specified
+    in [profiler]/hmac_keys option of OpenStack services.
+
+    .. _OSProfiler library: https://docs.openstack.org/osprofiler/
diff --git a/releasenotes/notes/add-unstable_test-decorator-a73cf97d4ffcc796.yaml b/releasenotes/notes/add-unstable_test-decorator-a73cf97d4ffcc796.yaml
new file mode 100644
index 0000000..2203fd1
--- /dev/null
+++ b/releasenotes/notes/add-unstable_test-decorator-a73cf97d4ffcc796.yaml
@@ -0,0 +1,11 @@
+---
+features:
+  - |
+    New decorator ``unstable_test`` is added to ``tempest.lib.decorators``.
+    It can be used to mark some test as unstable thus it will be still run
+    by tempest but job will not fail if this test will fail. Such test will
+    be skipped in case of failure.
+    It can be used for example when there is known bug related which cause
+    irregular tests failures. Marking such test as unstable will help other
+    developers to get their job done and still run this test to get additional
+    debug data or to confirm if some potential fix really solved the issue.
diff --git a/releasenotes/notes/bug-1808473-54ada26ab78e7b02.yaml b/releasenotes/notes/bug-1808473-54ada26ab78e7b02.yaml
new file mode 100644
index 0000000..c280198
--- /dev/null
+++ b/releasenotes/notes/bug-1808473-54ada26ab78e7b02.yaml
@@ -0,0 +1,7 @@
+---
+fixes:
+  - |
+    Fixed bug #1808473. ``tempest run`` CLI will error if a non-exist config file is
+    input to parameter --config-file. Earlier non-exist config value was silently
+    getting ignored and the default config file was used instead which used to give
+    false behavior to the user on using the passed config file.
diff --git a/releasenotes/notes/correct-port-profile-config-option-d67f5cb31f1bc34c.yaml b/releasenotes/notes/correct-port-profile-config-option-d67f5cb31f1bc34c.yaml
index 7510d47..2830aa2 100644
--- a/releasenotes/notes/correct-port-profile-config-option-d67f5cb31f1bc34c.yaml
+++ b/releasenotes/notes/correct-port-profile-config-option-d67f5cb31f1bc34c.yaml
@@ -1,7 +1,7 @@
 ---
 fixes:
   - |
-    Patch https://review.openstack.org/#/c/499575/ introduced
+    Patch https://review.opendev.org/#/c/499575/ introduced
     support creating Neutron port with certain capabilities.
     Currently capabilities list interpreted as string this change
     fix it.
diff --git a/releasenotes/notes/deprecate-dns_servers-option-0xf2f297ee47a5ff.yaml b/releasenotes/notes/deprecate-dns_servers-option-0xf2f297ee47a5ff.yaml
new file mode 100644
index 0000000..30551cb
--- /dev/null
+++ b/releasenotes/notes/deprecate-dns_servers-option-0xf2f297ee47a5ff.yaml
@@ -0,0 +1,6 @@
+---
+deprecations:
+  - |
+    The config option ``CONF.network.dns_servers`` is no longer used
+    anywhere, so it is deprecated and will be removed in the future.
+
diff --git a/releasenotes/notes/lib_api_microversion_fixture-f52308fc6b6b89f2.yaml b/releasenotes/notes/lib_api_microversion_fixture-f52308fc6b6b89f2.yaml
new file mode 100644
index 0000000..d707fc7
--- /dev/null
+++ b/releasenotes/notes/lib_api_microversion_fixture-f52308fc6b6b89f2.yaml
@@ -0,0 +1,7 @@
+---
+features:
+  - |
+    New library interface to set the API microversion on Service Clients.
+    ``APIMicroversionFixture,`` can be used to set the API microversion
+    on multiple services. This Fixture will take care of reseting the service
+    microversion to None once test is finished.
diff --git a/releasenotes/notes/remove-some-deprecated-auth-and-identity-options-xa1xd9b8fb948g4f.yaml b/releasenotes/notes/remove-some-deprecated-auth-and-identity-options-xa1xd9b8fb948g4f.yaml
new file mode 100644
index 0000000..fa21afd
--- /dev/null
+++ b/releasenotes/notes/remove-some-deprecated-auth-and-identity-options-xa1xd9b8fb948g4f.yaml
@@ -0,0 +1,8 @@
+upgrade:
+  - |
+    Remove deprecated config option ``endpoint_type`` from
+    ``identity`` group. Use ``v2_public_endpoint_type`` from
+    ``identity`` group instead.
+    Remove deprecated config option ``tenant_isolation_domain_name``
+    from ``auth`` group. Use ``default_credentials_domain_name`` from
+    ``auth`` group instead.
diff --git a/releasenotes/notes/remove-some-deprecated-identity-options-0ffxd1b8db928e43.yaml b/releasenotes/notes/remove-some-deprecated-identity-options-0ffxd1b8db928e43.yaml
new file mode 100644
index 0000000..e9e9444
--- /dev/null
+++ b/releasenotes/notes/remove-some-deprecated-identity-options-0ffxd1b8db928e43.yaml
@@ -0,0 +1,11 @@
+upgrade:
+  - |
+    Remove deprecated config option ``admin_username`` from
+    ``identity`` groups. Use ``admin_username`` from ``auth`` instead.
+    Remove deprecated config option ``admin_tenant_name`` from
+    ``auth`` and ``identity`` groups. Use ``admin_project_name`` from
+    ``auth`` instead.
+    Remove deprecated config option ``admin_password`` from
+    ``identity`` groups. Use ``admin_password`` from ``auth`` instead.
+    Remove deprecated config option ``admin_domain_name`` from
+    ``identity`` groups. Use ``admin_domain_name`` from ``auth`` instead.
\ No newline at end of file
diff --git a/releasenotes/notes/support-microversion-in-scenario-test-b4fbfdd3a977fc58.yaml b/releasenotes/notes/support-microversion-in-scenario-test-b4fbfdd3a977fc58.yaml
new file mode 100644
index 0000000..4d0a3dd
--- /dev/null
+++ b/releasenotes/notes/support-microversion-in-scenario-test-b4fbfdd3a977fc58.yaml
@@ -0,0 +1,14 @@
+---
+features:
+  - |
+    Add microversion support for scenario tests. Scenario test calls
+    multiple service API within same test and many services like compute,
+    volume and placement etc support API microversion. With microversion
+    support in scenario test, we can call different service API with
+    different microvesion. Which means we can implement scenario tests
+    for microversion also.
+    Currently Scenario manager support below services microversion:
+
+    * Compute
+    * Volume
+    * Placement
diff --git a/releasenotes/notes/tempest-stein-release-18bad34136a2e6ef.yaml b/releasenotes/notes/tempest-stein-release-18bad34136a2e6ef.yaml
new file mode 100644
index 0000000..212cf7d
--- /dev/null
+++ b/releasenotes/notes/tempest-stein-release-18bad34136a2e6ef.yaml
@@ -0,0 +1,18 @@
+---
+prelude: >
+    This release is to tag the Tempest for OpenStack Stein release.
+    This release marks the start of Stein release support in Tempest and
+    the end of support for Ocata in Tempest.
+    After this release, Tempest will support below OpenStack Releases:
+
+      * Stein
+      * Rocky
+      * Queens
+      * Pike
+
+    Current development of Tempest is for OpenStack Train development
+    cycle. Every Tempest commit is also tested against master during
+    the Train cycle. However, this does not necessarily mean that using
+    Tempest as of this tag will work against a Train (or future release)
+    cloud.
+    To be on safe side, use this tag to test the OpenStack Stein release.
diff --git a/releasenotes/source/index.rst b/releasenotes/source/index.rst
index 3be014f..e5d5bfe 100644
--- a/releasenotes/source/index.rst
+++ b/releasenotes/source/index.rst
@@ -6,6 +6,7 @@
    :maxdepth: 1
 
    unreleased
+   v20.0.0
    v19.0.0
    v18.0.0
    v17.2.0
diff --git a/releasenotes/source/v20.0.0.rst b/releasenotes/source/v20.0.0.rst
new file mode 100644
index 0000000..28c5431
--- /dev/null
+++ b/releasenotes/source/v20.0.0.rst
@@ -0,0 +1,6 @@
+=====================
+v20.0.0 Release Notes
+=====================
+
+.. release-notes:: 20.0.0 Release Notes
+   :version: 20.0.0
diff --git a/requirements.txt b/requirements.txt
index 7520d42..bf38fae 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -3,7 +3,7 @@
 # process, which may cause wedges in the gate later.
 pbr!=2.1.0,>=2.0.0 # Apache-2.0
 cliff!=2.9.0,>=2.8.0 # Apache-2.0
-jsonschema<3.0.0,>=2.6.0 # MIT
+jsonschema>=2.6.0 # MIT
 testtools>=2.2.0 # MIT
 paramiko>=2.0.0 # LGPLv2.1+
 netaddr>=0.7.18 # BSD
diff --git a/tempest/api/compute/admin/test_quotas.py b/tempest/api/compute/admin/test_quotas.py
index 12c7255..0060ffe 100644
--- a/tempest/api/compute/admin/test_quotas.py
+++ b/tempest/api/compute/admin/test_quotas.py
@@ -212,7 +212,7 @@
     # 'danger' flag.
     @decorators.idempotent_id('7932ab0f-5136-4075-b201-c0e2338df51a')
     def test_update_default_quotas(self):
-        LOG.debug("get the current 'default' quota class values")
+        # get the current 'default' quota class values
         body = (self.adm_client.show_quota_class_set('default')
                 ['quota_class_set'])
         self.assertEqual('default', body.pop('id'))
@@ -224,9 +224,14 @@
             # there is a real chance that we go from -1 (unlimited)
             # to a very small number which causes issues.
             body[quota] = default + 100
-        LOG.debug("update limits for the default quota class set")
+        # update limits for the default quota class set
         update_body = self.adm_client.update_quota_class_set(
             'default', **body)['quota_class_set']
-        LOG.debug("assert that the response has all of the changed values")
+        # assert that the response has all of the changed values
         self.assertThat(update_body.items(),
                         matchers.ContainsAll(body.items()))
+        # check quota values are changed
+        show_body = self.adm_client.show_quota_class_set(
+            'default')['quota_class_set']
+        self.assertThat(show_body.items(),
+                        matchers.ContainsAll(body.items()))
diff --git a/tempest/api/compute/admin/test_volume_swap.py b/tempest/api/compute/admin/test_volume_swap.py
index cc83c04..371b506 100644
--- a/tempest/api/compute/admin/test_volume_swap.py
+++ b/tempest/api/compute/admin/test_volume_swap.py
@@ -142,6 +142,12 @@
         if not CONF.compute_feature_enabled.volume_multiattach:
             raise cls.skipException('Volume multi-attach is not available.')
 
+    @classmethod
+    def setup_clients(cls):
+        super(TestMultiAttachVolumeSwap, cls).setup_clients()
+        # Need this to set readonly volumes.
+        cls.admin_volumes_client = cls.os_admin.volumes_client_latest
+
     # NOTE(mriedem): This is an uncommon scenario to call the compute API
     # to swap volumes directly; swap volume is primarily only for volume
     # live migration and retype callbacks from the volume service, and is slow
@@ -162,6 +168,13 @@
         # volumes cleanup can happen successfully irrespective of which volume
         # is attached to server.
         volume1 = self.create_volume(multiattach=True)
+        # Make volume1 read-only since you can't swap from a volume with
+        # multiple read/write attachments, and you can't change the readonly
+        # flag on an in-use volume so we have to do this before attaching
+        # volume1 to anything. If the compute API ever supports per-attachment
+        # attach modes, then we can handle this differently.
+        self.admin_volumes_client.update_volume_readonly(
+            volume1['id'], readonly=True)
         volume2 = self.create_volume(multiattach=True)
 
         # Create two servers and wait for them to be ACTIVE.
diff --git a/tempest/api/compute/base.py b/tempest/api/compute/base.py
index 624a99e..e71e642 100644
--- a/tempest/api/compute/base.py
+++ b/tempest/api/compute/base.py
@@ -17,11 +17,11 @@
 
 from oslo_log import log as logging
 
-from tempest.api.compute import api_microversion_fixture
 from tempest.common import compute
 from tempest.common import waiters
 from tempest import config
 from tempest import exceptions
+from tempest.lib.common import api_microversion_fixture
 from tempest.lib.common import api_version_request
 from tempest.lib.common import api_version_utils
 from tempest.lib.common.utils import data_utils
@@ -470,7 +470,7 @@
     def setUp(self):
         super(BaseV2ComputeTest, self).setUp()
         self.useFixture(api_microversion_fixture.APIMicroversionFixture(
-            self.request_microversion))
+            compute_microversion=self.request_microversion))
 
     @classmethod
     def create_volume(cls, image_ref=None, **kwargs):
diff --git a/tempest/api/compute/servers/test_attach_interfaces.py b/tempest/api/compute/servers/test_attach_interfaces.py
index bea23d9..eeb58d6 100644
--- a/tempest/api/compute/servers/test_attach_interfaces.py
+++ b/tempest/api/compute/servers/test_attach_interfaces.py
@@ -331,6 +331,16 @@
     @decorators.idempotent_id('c7e0e60b-ee45-43d0-abeb-8596fd42a2f9')
     @utils.services('network')
     def test_add_remove_fixed_ip(self):
+        # NOTE(zhufl) By default only project that is admin or network owner
+        # or project with role advsvc is authorised to add interfaces with
+        # fixed-ip, so if we don't create network for each project, do not
+        # test
+        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.")
+
         # Add and Remove the fixed IP to server.
         server, ifs = self._create_server_get_interfaces()
         original_interface_count = len(ifs)  # This is the number of ports.
diff --git a/tempest/api/compute/servers/test_novnc.py b/tempest/api/compute/servers/test_novnc.py
index 4d93bb7..50ffb21 100644
--- a/tempest/api/compute/servers/test_novnc.py
+++ b/tempest/api/compute/servers/test_novnc.py
@@ -145,7 +145,7 @@
         data_length = len(data) if data is not None else 0
         self.assertFalse(data_length <= 24 or
                          data_length != (struct.unpack(">L",
-                                         data[20:24])[0] + 24),
+                                                       data[20:24])[0] + 24),
                          'Server initialization was not the right format.')
         # Since the rest of the data on the screen is arbitrary, we will
         # close the socket and end our validation of the data at this point
@@ -153,7 +153,7 @@
         # initialization was the right format
         self.assertFalse(data_length <= 24 or
                          data_length != (struct.unpack(">L",
-                                         data[20:24])[0] + 24))
+                                                       data[20:24])[0] + 24))
 
     def _validate_websocket_upgrade(self):
         self.assertTrue(
diff --git a/tempest/api/compute/servers/test_server_actions.py b/tempest/api/compute/servers/test_server_actions.py
index f3d7476..f6c3e73 100644
--- a/tempest/api/compute/servers/test_server_actions.py
+++ b/tempest/api/compute/servers/test_server_actions.py
@@ -92,6 +92,7 @@
             validatable=True,
             validation_resources=validation_resources,
             wait_until='ACTIVE')
+        self.addCleanup(self.delete_server, newserver['id'])
         # The server's password should be set to the provided password
         new_password = 'Newpass1234'
         self.client.change_password(newserver['id'], adminPass=new_password)
@@ -288,6 +289,17 @@
         self.assertEqual('in-use', vol_after_rebuild['status'])
         self.assertEqual(self.server_id,
                          vol_after_rebuild['attachments'][0]['server_id'])
+        if CONF.validation.run_validation:
+            validation_resources = self.get_class_validation_resources(
+                self.os_primary)
+            linux_client = remote_client.RemoteClient(
+                self.get_server_ip(server, validation_resources),
+                self.ssh_user,
+                password=None,
+                pkey=validation_resources['keypair']['private_key'],
+                server=server,
+                servers_client=self.client)
+            linux_client.validate_authentication()
 
     def _test_resize_server_confirm(self, server_id, stop=False):
         # The server's RAM and disk space should be modified to that of
diff --git a/tempest/api/compute/servers/test_servers.py b/tempest/api/compute/servers/test_servers.py
index 56d973e..e8b1161 100644
--- a/tempest/api/compute/servers/test_servers.py
+++ b/tempest/api/compute/servers/test_servers.py
@@ -19,7 +19,6 @@
 from tempest.common import waiters
 from tempest import config
 from tempest.lib.common.utils import data_utils
-from tempest.lib.common.utils import test_utils
 from tempest.lib import decorators
 
 CONF = config.CONF
@@ -40,11 +39,7 @@
         # If an admin password is provided on server creation, the server's
         # root password should be set to that password.
         server = self.create_test_server(adminPass='testpassword')
-        self.addCleanup(waiters.wait_for_server_termination,
-                        self.servers_client, server['id'])
-        self.addCleanup(
-            test_utils.call_and_ignore_notfound_exc,
-            self.servers_client.delete_server, server['id'])
+        self.addCleanup(self.delete_server, server['id'])
 
         # Verify the password is set correctly in the response
         self.assertEqual('testpassword', server['adminPass'])
@@ -59,19 +54,11 @@
         server = self.create_test_server(name=server_name,
                                          wait_until='ACTIVE')
         id1 = server['id']
-        self.addCleanup(waiters.wait_for_server_termination,
-                        self.servers_client, id1)
-        self.addCleanup(
-            test_utils.call_and_ignore_notfound_exc,
-            self.servers_client.delete_server, id1)
+        self.addCleanup(self.delete_server, id1)
         server = self.create_test_server(name=server_name,
                                          wait_until='ACTIVE')
         id2 = server['id']
-        self.addCleanup(waiters.wait_for_server_termination,
-                        self.servers_client, id2)
-        self.addCleanup(
-            test_utils.call_and_ignore_notfound_exc,
-            self.servers_client.delete_server, id2)
+        self.addCleanup(self.delete_server, id2)
         self.assertNotEqual(id1, id2, "Did not create a new server")
         server = self.client.show_server(id1)['server']
         name1 = server['name']
@@ -87,13 +74,9 @@
         self.keypairs_client.create_keypair(name=key_name)
         self.addCleanup(self.keypairs_client.delete_keypair, key_name)
         self.keypairs_client.list_keypairs()
-        server = self.create_test_server(key_name=key_name)
-        self.addCleanup(waiters.wait_for_server_termination,
-                        self.servers_client, server['id'])
-        self.addCleanup(
-            test_utils.call_and_ignore_notfound_exc,
-            self.servers_client.delete_server, server['id'])
-        waiters.wait_for_server_status(self.client, server['id'], 'ACTIVE')
+        server = self.create_test_server(key_name=key_name,
+                                         wait_until='ACTIVE')
+        self.addCleanup(self.delete_server, server['id'])
         server = self.client.show_server(server['id'])['server']
         self.assertEqual(key_name, server['key_name'])
 
@@ -115,11 +98,7 @@
     def test_update_server_name(self):
         # The server name should be changed to the provided value
         server = self.create_test_server(wait_until='ACTIVE')
-        self.addCleanup(waiters.wait_for_server_termination,
-                        self.servers_client, server['id'])
-        self.addCleanup(
-            test_utils.call_and_ignore_notfound_exc,
-            self.servers_client.delete_server, server['id'])
+        self.addCleanup(self.delete_server, server['id'])
         # Update instance name with non-ASCII characters
         prefix_name = u'\u00CD\u00F1st\u00E1\u00F1c\u00E9'
         self._update_server_name(server['id'], 'ACTIVE', prefix_name)
@@ -137,11 +116,7 @@
     def test_update_access_server_address(self):
         # The server's access addresses should reflect the provided values
         server = self.create_test_server(wait_until='ACTIVE')
-        self.addCleanup(waiters.wait_for_server_termination,
-                        self.servers_client, server['id'])
-        self.addCleanup(
-            test_utils.call_and_ignore_notfound_exc,
-            self.servers_client.delete_server, server['id'])
+        self.addCleanup(self.delete_server, server['id'])
 
         # Update the IPv4 and IPv6 access addresses
         self.client.update_server(server['id'],
@@ -157,13 +132,9 @@
     @decorators.idempotent_id('38fb1d02-c3c5-41de-91d3-9bc2025a75eb')
     def test_create_server_with_ipv6_addr_only(self):
         # Create a server without an IPv4 address(only IPv6 address).
-        server = self.create_test_server(accessIPv6='2001:2001::3')
-        self.addCleanup(waiters.wait_for_server_termination,
-                        self.servers_client, server['id'])
-        self.addCleanup(
-            test_utils.call_and_ignore_notfound_exc,
-            self.servers_client.delete_server, server['id'])
-        waiters.wait_for_server_status(self.client, server['id'], 'ACTIVE')
+        server = self.create_test_server(accessIPv6='2001:2001::3',
+                                         wait_until='ACTIVE')
+        self.addCleanup(self.delete_server, server['id'])
         server = self.client.show_server(server['id'])['server']
         self.assertEqual('2001:2001::3', server['accessIPv6'])
 
diff --git a/tempest/api/compute/test_extensions.py b/tempest/api/compute/test_extensions.py
index 34faf5f..12e7fea 100644
--- a/tempest/api/compute/test_extensions.py
+++ b/tempest/api/compute/test_extensions.py
@@ -37,7 +37,7 @@
         ext = CONF.compute_feature_enabled.api_extensions[0]
 
         # Log extensions list
-        extension_list = map(lambda x: x['alias'], extensions)
+        extension_list = [x['alias'] for x in extensions]
         LOG.debug("Nova extensions: %s", ','.join(extension_list))
 
         if ext == 'all':
diff --git a/tempest/api/compute/volumes/test_attach_volume.py b/tempest/api/compute/volumes/test_attach_volume.py
index 8bb4eaa..f83e62c 100644
--- a/tempest/api/compute/volumes/test_attach_volume.py
+++ b/tempest/api/compute/volumes/test_attach_volume.py
@@ -126,7 +126,7 @@
     @decorators.idempotent_id('7fa563fe-f0f7-43eb-9e22-a1ece036b513')
     def test_list_get_volume_attachments(self):
         # List volume attachment of the server
-        server, _ = self._create_server()
+        server, validation_resources = self._create_server()
         volume_1st = self.create_volume()
         attachment_1st = self.attach_volume(server, volume_1st)
         body = self.servers_client.list_volume_attachments(
@@ -149,6 +149,16 @@
             server['id'])['volumeAttachments']
         self.assertEqual(2, len(body))
 
+        if CONF.validation.run_validation:
+            linux_client = remote_client.RemoteClient(
+                self.get_server_ip(server, validation_resources),
+                self.image_ssh_user,
+                self.image_ssh_password,
+                validation_resources['keypair']['private_key'],
+                server=server,
+                servers_client=self.servers_client)
+            linux_client.validate_authentication()
+
         for attachment in [attachment_1st, attachment_2nd]:
             body = self.servers_client.show_volume_attachment(
                 server['id'], attachment['id'])['volumeAttachment']
diff --git a/tempest/api/identity/admin/v3/test_endpoint_groups.py b/tempest/api/identity/admin/v3/test_endpoint_groups.py
index 625568d..7d85dc9 100644
--- a/tempest/api/identity/admin/v3/test_endpoint_groups.py
+++ b/tempest/api/identity/admin/v3/test_endpoint_groups.py
@@ -69,6 +69,7 @@
     @decorators.idempotent_id('7c69e7a1-f865-402d-a2ea-44493017315a')
     def test_create_list_show_check_delete_endpoint_group(self):
         service_id = self._create_service()
+        self.addCleanup(self.services_client.delete_service, service_id)
         name = data_utils.rand_name('service_group')
         description = data_utils.rand_name('description')
         filters = {'service_id': service_id}
@@ -129,6 +130,7 @@
         # Creating an endpoint group so as to check update endpoint group
         # with new values
         service1_id = self._create_service()
+        self.addCleanup(self.services_client.delete_service, service1_id)
         name = data_utils.rand_name('service_group')
         description = data_utils.rand_name('description')
         filters = {'service_id': service1_id}
diff --git a/tempest/api/identity/admin/v3/test_inherits.py b/tempest/api/identity/admin/v3/test_inherits.py
index acc5a8c..2672f71 100644
--- a/tempest/api/identity/admin/v3/test_inherits.py
+++ b/tempest/api/identity/admin/v3/test_inherits.py
@@ -9,14 +9,22 @@
 #    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.common import utils
+from tempest import config
 from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 
+CONF = config.CONF
+
 
 class InheritsV3TestJSON(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 skip_checks(cls):
@@ -43,18 +51,26 @@
             domain_id=cls.domain['id'])['group']
         cls.addClassResourceCleanup(cls.groups_client.delete_group,
                                     cls.group['id'])
-        cls.user = cls.users_client.create_user(
-            name=u_name, description=u_desc, password=u_password,
-            email=u_email, project_id=cls.project['id'],
-            domain_id=cls.domain['id'])['user']
-        cls.addClassResourceCleanup(cls.users_client.delete_user,
-                                    cls.user['id'])
+        if not CONF.identity_feature_enabled.immutable_user_source:
+            cls.user = cls.users_client.create_user(
+                name=u_name,
+                description=u_desc,
+                password=u_password,
+                email=u_email,
+                project_id=cls.project['id'],
+                domain_id=cls.domain['id']
+            )['user']
+            cls.addClassResourceCleanup(cls.users_client.delete_user,
+                                        cls.user['id'])
 
     def _list_assertions(self, body, fetched_role_ids, role_id):
         self.assertEqual(len(body), 1)
         self.assertIn(role_id, fetched_role_ids)
 
     @decorators.idempotent_id('4e6f0366-97c8-423c-b2be-41eae6ac91c8')
+    @testtools.skipIf(CONF.identity_feature_enabled.immutable_user_source,
+                      'Skipped because environment has an immutable user '
+                      'source and solely provides read-only access to users.')
     def test_inherit_assign_list_check_revoke_roles_on_domains_user(self):
         # Create role
         src_role = self.setup_test_role()
@@ -103,6 +119,9 @@
             self.domain['id'], self.group['id'], src_role['id'])
 
     @decorators.idempotent_id('18b70e45-7687-4b72-8277-b8f1a47d7591')
+    @testtools.skipIf(CONF.identity_feature_enabled.immutable_user_source,
+                      'Skipped because environment has an immutable user '
+                      'source and solely provides read-only access to users.')
     def test_inherit_assign_check_revoke_roles_on_projects_user(self):
         # Create role
         src_role = self.setup_test_role()
@@ -134,6 +153,9 @@
              self.project['id'], self.group['id'], src_role['id']))
 
     @decorators.idempotent_id('3acf666e-5354-42ac-8e17-8b68893bcd36')
+    @testtools.skipIf(CONF.identity_feature_enabled.immutable_user_source,
+                      'Skipped because environment has an immutable user '
+                      'source and solely provides read-only access to users.')
     def test_inherit_assign_list_revoke_user_roles_on_domain(self):
         # Create role
         src_role = self.setup_test_role()
@@ -178,6 +200,9 @@
         self.assertEmpty(assignments)
 
     @decorators.idempotent_id('9f02ccd9-9b57-46b4-8f77-dd5a736f3a06')
+    @testtools.skipIf(CONF.identity_feature_enabled.immutable_user_source,
+                      'Skipped because environment has an immutable user '
+                      'source and solely provides read-only access to users.')
     def test_inherit_assign_list_revoke_user_roles_on_project_tree(self):
         # Create role
         src_role = self.setup_test_role()
diff --git a/tempest/api/identity/admin/v3/test_list_projects.py b/tempest/api/identity/admin/v3/test_list_projects.py
index 50f3186..299a618 100644
--- a/tempest/api/identity/admin/v3/test_list_projects.py
+++ b/tempest/api/identity/admin/v3/test_list_projects.py
@@ -13,11 +13,14 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from oslo_log import log as logging
+
 from tempest.api.identity import base
 from tempest import config
 from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 
+LOG = logging.getLogger(__name__)
 CONF = config.CONF
 
 
@@ -26,6 +29,9 @@
     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``
+        all_projects = self.projects_client.list_projects()['projects']
+        LOG.debug("Complete list of projects available in keystone: %s",
+                  all_projects)
         body = self.projects_client.list_projects(params)['projects']
         for p in included:
             self.assertIn(p[key], map(lambda x: x[key], body))
@@ -39,13 +45,12 @@
     def resource_setup(cls):
         super(ListProjectsTestJSON, cls).resource_setup()
         cls.project_ids = list()
-        # Create a domain
-        cls.domain = cls.create_domain()
+        cls.domain_id = cls.os_admin.credentials.domain_id
         # Create project with domain
         cls.p1_name = data_utils.rand_name('project')
         cls.p1 = cls.projects_client.create_project(
             cls.p1_name, enabled=False,
-            domain_id=cls.domain['id'])['project']
+            domain_id=cls.domain_id)['project']
         cls.addClassResourceCleanup(cls.projects_client.delete_project,
                                     cls.p1['id'])
         cls.project_ids.append(cls.p1['id'])
diff --git a/tempest/api/identity/admin/v3/test_roles.py b/tempest/api/identity/admin/v3/test_roles.py
index 47f663c..5ba4c9f 100644
--- a/tempest/api/identity/admin/v3/test_roles.py
+++ b/tempest/api/identity/admin/v3/test_roles.py
@@ -12,15 +12,23 @@
 #    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.common.utils import test_utils
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
 
+CONF = config.CONF
+
 
 class RolesV3TestJSON(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 resource_setup(cls):
@@ -48,16 +56,21 @@
             domain_id=cls.domain['id'])['group']
         cls.addClassResourceCleanup(cls.groups_client.delete_group,
                                     cls.group_body['id'])
-        cls.user_body = cls.users_client.create_user(
-            name=u_name, description=u_desc, password=cls.u_password,
-            email=u_email, project_id=cls.project['id'],
-            domain_id=cls.domain['id'])['user']
-        cls.addClassResourceCleanup(cls.users_client.delete_user,
-                                    cls.user_body['id'])
         cls.role = cls.roles_client.create_role(
             name=data_utils.rand_name('Role'))['role']
         cls.addClassResourceCleanup(cls.roles_client.delete_role,
                                     cls.role['id'])
+        if not CONF.identity_feature_enabled.immutable_user_source:
+            cls.user_body = cls.users_client.create_user(
+                name=u_name,
+                description=u_desc,
+                email=u_email,
+                password=cls.u_password,
+                domain_id=cls.domain['id'],
+                project_id=cls.project['id']
+            )['user']
+            cls.addClassResourceCleanup(cls.users_client.delete_user,
+                                        cls.user_body['id'])
 
     @decorators.attr(type='smoke')
     @decorators.idempotent_id('18afc6c0-46cf-4911-824e-9989cc056c3a')
@@ -84,6 +97,9 @@
         self.assertIn(role['id'], [r['id'] for r in roles])
 
     @decorators.idempotent_id('c6b80012-fe4a-498b-9ce8-eb391c05169f')
+    @testtools.skipIf(CONF.identity_feature_enabled.immutable_user_source,
+                      'Skipped because environment has an immutable user '
+                      'source and solely provides read-only access to users.')
     def test_grant_list_revoke_role_to_user_on_project(self):
         self.roles_client.create_user_role_on_project(self.project['id'],
                                                       self.user_body['id'],
@@ -102,6 +118,9 @@
             self.project['id'], self.user_body['id'], self.role['id'])
 
     @decorators.idempotent_id('6c9a2940-3625-43a3-ac02-5dcec62ef3bd')
+    @testtools.skipIf(CONF.identity_feature_enabled.immutable_user_source,
+                      'Skipped because environment has an immutable user '
+                      'source and solely provides read-only access to users.')
     def test_grant_list_revoke_role_to_user_on_domain(self):
         self.roles_client.create_user_role_on_domain(
             self.domain['id'], self.user_body['id'], self.role['id'])
@@ -119,6 +138,9 @@
             self.domain['id'], self.user_body['id'], self.role['id'])
 
     @decorators.idempotent_id('cbf11737-1904-4690-9613-97bcbb3df1c4')
+    @testtools.skipIf(CONF.identity_feature_enabled.immutable_user_source,
+                      'Skipped because environment has an immutable user '
+                      'source and solely provides read-only access to users.')
     def test_grant_list_revoke_role_to_group_on_project(self):
         # Grant role to group on project
         self.roles_client.create_group_role_on_project(
@@ -254,6 +276,9 @@
         self.assertIn(self.roles[2]['id'], implies_ids)
 
     @decorators.idempotent_id('c8828027-df48-4021-95df-b65b92c7429e')
+    @testtools.skipIf(CONF.identity_feature_enabled.immutable_user_source,
+                      'Skipped because environment has an immutable user '
+                      'source and solely provides read-only access to users.')
     def test_assignments_for_implied_roles_create_delete(self):
         # Create a grant using "roles[0]"
         self.roles_client.create_user_role_on_project(
@@ -344,6 +369,9 @@
             domain_role1['id'])
 
     @decorators.idempotent_id('3859df7e-5b78-4e4d-b10e-214c8953842a')
+    @testtools.skipIf(CONF.identity_feature_enabled.immutable_user_source,
+                      'Skipped because environment has an immutable user '
+                      'source and solely provides read-only access to users.')
     def test_assignments_for_domain_roles(self):
         domain_role = self.setup_test_role(domain_id=self.domain['id'])
 
diff --git a/tempest/api/identity/v2/test_users.py b/tempest/api/identity/v2/test_users.py
index 158dfb3..2eea860 100644
--- a/tempest/api/identity/v2/test_users.py
+++ b/tempest/api/identity/v2/test_users.py
@@ -15,6 +15,8 @@
 
 import time
 
+import testtools
+
 from tempest.api.identity import base
 from tempest import config
 from tempest.lib.common.utils import data_utils
@@ -78,6 +80,10 @@
         self.non_admin_users_client.auth_provider.set_auth()
 
     @decorators.idempotent_id('165859c9-277f-4124-9479-a7d1627b0ca7')
+    @testtools.skipIf(CONF.identity_feature_enabled.immutable_user_source,
+                      'Skipped because environment has an '
+                      'immutable user source and solely '
+                      'provides read-only access to users.')
     def test_user_update_own_password(self):
         old_pass = self.creds.password
         old_token = self.non_admin_users_client.token
diff --git a/tempest/api/identity/v3/test_catalog.py b/tempest/api/identity/v3/test_catalog.py
index deec2dc..bc95f0d 100644
--- a/tempest/api/identity/v3/test_catalog.py
+++ b/tempest/api/identity/v3/test_catalog.py
@@ -22,8 +22,8 @@
 
     @decorators.idempotent_id('56b57ced-22b8-4127-9b8a-565dfb0207e2')
     def test_catalog_standardization(self):
-        # http://git.openstack.org/cgit/openstack/service-types-authority
-        # /tree/service-types.yaml
+        # https://opendev.org/openstack/service-types-authority
+        # /src/branch/master/service-types.yaml
         standard_service_values = [{'name': 'keystone', 'type': 'identity'},
                                    {'name': 'nova', 'type': 'compute'},
                                    {'name': 'glance', 'type': 'image'},
diff --git a/tempest/api/identity/v3/test_users.py b/tempest/api/identity/v3/test_users.py
index 13b5161..d4e7612 100644
--- a/tempest/api/identity/v3/test_users.py
+++ b/tempest/api/identity/v3/test_users.py
@@ -77,6 +77,10 @@
         self.non_admin_users_client.auth_provider.set_auth()
 
     @decorators.idempotent_id('ad71bd23-12ad-426b-bb8b-195d2b635f27')
+    @testtools.skipIf(CONF.identity_feature_enabled.immutable_user_source,
+                      'Skipped because environment has an '
+                      'immutable user source and solely '
+                      'provides read-only access to users.')
     def test_user_update_own_password(self):
         old_pass = self.creds.password
         old_token = self.non_admin_client.token
@@ -102,6 +106,10 @@
     @testtools.skipUnless(CONF.identity_feature_enabled.security_compliance,
                           'Security compliance not available.')
     @decorators.idempotent_id('941784ee-5342-4571-959b-b80dd2cea516')
+    @testtools.skipIf(CONF.identity_feature_enabled.immutable_user_source,
+                      'Skipped because environment has an '
+                      'immutable user source and solely '
+                      'provides read-only access to users.')
     def test_password_history_check_self_service_api(self):
         old_pass = self.creds.password
         new_pass1 = data_utils.rand_password()
diff --git a/tempest/api/network/admin/test_external_network_extension.py b/tempest/api/network/admin/test_external_network_extension.py
index 7e8cc8e..5bd3fce 100644
--- a/tempest/api/network/admin/test_external_network_extension.py
+++ b/tempest/api/network/admin/test_external_network_extension.py
@@ -36,6 +36,7 @@
         body = self.admin_networks_client.create_network(**post_body)
         network = body['network']
         self.addCleanup(
+            test_utils.call_and_ignore_notfound_exc,
             self.admin_networks_client.delete_network, network['id'])
         return network
 
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 be0c4c6..adc4dda 100644
--- a/tempest/api/network/admin/test_floating_ips_admin_actions.py
+++ b/tempest/api/network/admin/test_floating_ips_admin_actions.py
@@ -16,6 +16,7 @@
 from tempest.api.network import base
 from tempest.common import utils
 from tempest import config
+from tempest.lib.common.utils import test_utils
 from tempest.lib import decorators
 
 CONF = config.CONF
@@ -57,14 +58,18 @@
         # Create floating ip from admin user
         floating_ip_admin = self.admin_floating_ips_client.create_floatingip(
             floating_network_id=self.ext_net_id)
-        self.addCleanup(self.admin_floating_ips_client.delete_floatingip,
-                        floating_ip_admin['floatingip']['id'])
+        self.addCleanup(
+            test_utils.call_and_ignore_notfound_exc,
+            self.admin_floating_ips_client.delete_floatingip,
+            floating_ip_admin['floatingip']['id'])
         # Create floating ip from alt user
         body = self.alt_floating_ips_client.create_floatingip(
             floating_network_id=self.ext_net_id)
         floating_ip_alt = body['floatingip']
-        self.addCleanup(self.alt_floating_ips_client.delete_floatingip,
-                        floating_ip_alt['id'])
+        self.addCleanup(
+            test_utils.call_and_ignore_notfound_exc,
+            self.alt_floating_ips_client.delete_floatingip,
+            floating_ip_alt['id'])
         # List floating ips from admin
         body = self.admin_floating_ips_client.list_floatingips()
         floating_ip_ids_admin = [f['id'] for f in body['floatingips']]
@@ -91,8 +96,10 @@
             tenant_id=self.network['tenant_id'],
             port_id=self.port['id'])
         created_floating_ip = body['floatingip']
-        self.addCleanup(self.floating_ips_client.delete_floatingip,
-                        created_floating_ip['id'])
+        self.addCleanup(
+            test_utils.call_and_ignore_notfound_exc,
+            self.floating_ips_client.delete_floatingip,
+            created_floating_ip['id'])
         self.assertIsNotNone(created_floating_ip['id'])
         self.assertIsNotNone(created_floating_ip['tenant_id'])
         self.assertIsNotNone(created_floating_ip['floating_ip_address'])
diff --git a/tempest/api/network/admin/test_negative_quotas.py b/tempest/api/network/admin/test_negative_quotas.py
index 9d1e2a7..0db038d 100644
--- a/tempest/api/network/admin/test_negative_quotas.py
+++ b/tempest/api/network/admin/test_negative_quotas.py
@@ -17,6 +17,7 @@
 from tempest.common import identity
 from tempest.common import utils
 from tempest.lib.common.utils import data_utils
+from tempest.lib.common.utils import test_utils
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
 
@@ -58,11 +59,13 @@
         # Create two networks
         n1 = self.admin_networks_client.create_network(
             tenant_id=self.project['id'])
-        self.addCleanup(self.admin_networks_client.delete_network,
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self.admin_networks_client.delete_network,
                         n1['network']['id'])
         n2 = self.admin_networks_client.create_network(
             tenant_id=self.project['id'])
-        self.addCleanup(self.admin_networks_client.delete_network,
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self.admin_networks_client.delete_network,
                         n2['network']['id'])
 
         # Try to create a third network while the quota is two
@@ -71,5 +74,6 @@
                 r"Quota exceeded for resources: \['network'\].*"):
             n3 = self.admin_networks_client.create_network(
                 tenant_id=self.project['id'])
-            self.addCleanup(self.admin_networks_client.delete_network,
+            self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                            self.admin_networks_client.delete_network,
                             n3['network']['id'])
diff --git a/tempest/api/network/admin/test_ports.py b/tempest/api/network/admin/test_ports.py
index 05363db..edfda6e 100644
--- a/tempest/api/network/admin/test_ports.py
+++ b/tempest/api/network/admin/test_ports.py
@@ -14,6 +14,7 @@
 #    under the License.
 
 from tempest.api.network import base
+from tempest.lib.common.utils import test_utils
 from tempest.lib import decorators
 
 
@@ -37,7 +38,9 @@
                      "binding:host_id": self.host_id}
         body = self.admin_ports_client.create_port(**post_body)
         port = body['port']
-        self.addCleanup(self.admin_ports_client.delete_port, port['id'])
+        self.addCleanup(
+            test_utils.call_and_ignore_notfound_exc,
+            self.admin_ports_client.delete_port, port['id'])
         host_id = port['binding:host_id']
         self.assertIsNotNone(host_id)
         self.assertEqual(self.host_id, host_id)
@@ -47,7 +50,9 @@
         post_body = {"network_id": self.network['id']}
         body = self.admin_ports_client.create_port(**post_body)
         port = body['port']
-        self.addCleanup(self.admin_ports_client.delete_port, port['id'])
+        self.addCleanup(
+            test_utils.call_and_ignore_notfound_exc,
+            self.admin_ports_client.delete_port, port['id'])
         update_body = {"binding:host_id": self.host_id}
         body = self.admin_ports_client.update_port(port['id'], **update_body)
         updated_port = body['port']
@@ -61,7 +66,9 @@
         post_body = {"network_id": self.network['id']}
         body = self.admin_ports_client.create_port(**post_body)
         port = body['port']
-        self.addCleanup(self.admin_ports_client.delete_port, port['id'])
+        self.addCleanup(
+            test_utils.call_and_ignore_notfound_exc,
+            self.admin_ports_client.delete_port, port['id'])
 
         # Update the port's binding attributes so that is now 'bound'
         # to a host
@@ -85,7 +92,8 @@
         body = self.admin_ports_client.create_port(
             network_id=self.network['id'])
         port = body['port']
-        self.addCleanup(self.admin_ports_client.delete_port, port['id'])
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self.admin_ports_client.delete_port, port['id'])
         body = self.admin_ports_client.show_port(port['id'])
         show_port = body['port']
         self.assertEqual(port['binding:host_id'],
diff --git a/tempest/api/network/admin/test_routers.py b/tempest/api/network/admin/test_routers.py
index 6ce86fb..a4a057c 100644
--- a/tempest/api/network/admin/test_routers.py
+++ b/tempest/api/network/admin/test_routers.py
@@ -20,6 +20,7 @@
 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
 from tempest.lib import decorators
 
 CONF = config.CONF
@@ -38,7 +39,8 @@
         # associate a cleanup with created routers to avoid quota limits
         router = self.create_router(name, admin_state_up,
                                     external_network_id, enable_snat)
-        self.addCleanup(self._cleanup_router, router)
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self._cleanup_router, router)
         return router
 
     @classmethod
@@ -62,7 +64,8 @@
         name = data_utils.rand_name('router-')
         create_body = self.admin_routers_client.create_router(
             name=name, tenant_id=project_id)
-        self.addCleanup(self.admin_routers_client.delete_router,
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self.admin_routers_client.delete_router,
                         create_body['router']['id'])
         self.assertEqual(project_id, create_body['router']['tenant_id'])
 
@@ -92,7 +95,8 @@
                 'enable_snat': enable_snat}
             create_body = self.admin_routers_client.create_router(
                 name=name, external_gateway_info=external_gateway_info)
-            self.addCleanup(self.admin_routers_client.delete_router,
+            self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                            self.admin_routers_client.delete_router,
                             create_body['router']['id'])
             # Verify snat attributes after router creation
             self._verify_router_gateway(create_body['router']['id'],
diff --git a/tempest/api/network/admin/test_routers_dvr.py b/tempest/api/network/admin/test_routers_dvr.py
index 93478e6..270f802 100644
--- a/tempest/api/network/admin/test_routers_dvr.py
+++ b/tempest/api/network/admin/test_routers_dvr.py
@@ -18,6 +18,7 @@
 from tempest.api.network import base
 from tempest.common import utils
 from tempest.lib.common.utils import data_utils
+from tempest.lib.common.utils import test_utils
 from tempest.lib import decorators
 
 
@@ -62,7 +63,8 @@
         name = data_utils.rand_name('router')
         router = self.admin_routers_client.create_router(name=name,
                                                          distributed=True)
-        self.addCleanup(self.admin_routers_client.delete_router,
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self.admin_routers_client.delete_router,
                         router['router']['id'])
         self.assertTrue(router['router']['distributed'])
 
@@ -82,7 +84,8 @@
         name = data_utils.rand_name('router')
         router = self.admin_routers_client.create_router(name=name,
                                                          distributed=False)
-        self.addCleanup(self.admin_routers_client.delete_router,
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self.admin_routers_client.delete_router,
                         router['router']['id'])
         self.assertFalse(router['router']['distributed'])
 
@@ -112,8 +115,8 @@
                                                          ha=False,
                                                          tenant_id=tenant_id)
         router_id = router['router']['id']
-        self.addCleanup(self.admin_routers_client.delete_router,
-                        router_id)
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self.admin_routers_client.delete_router, router_id)
         self.assertFalse(router['router']['distributed'])
         router = self.admin_routers_client.update_router(
             router_id, distributed=True)
diff --git a/tempest/api/network/admin/test_routers_negative.py b/tempest/api/network/admin/test_routers_negative.py
index 9356bcc..fdcc977 100644
--- a/tempest/api/network/admin/test_routers_negative.py
+++ b/tempest/api/network/admin/test_routers_negative.py
@@ -18,6 +18,7 @@
 from tempest.api.network import base
 from tempest.common import utils
 from tempest import config
+from tempest.lib.common.utils import test_utils
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
 
@@ -42,7 +43,8 @@
         # At first create a address from public_network_id
         port = self.admin_ports_client.create_port(
             network_id=CONF.network.public_network_id)['port']
-        self.addCleanup(self.admin_ports_client.delete_port,
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self.admin_ports_client.delete_port,
                         port_id=port['id'])
         # Add used ip and subnet_id in external_fixed_ips
         fixed_ip = {
diff --git a/tempest/api/network/base_security_groups.py b/tempest/api/network/base_security_groups.py
index b8d677a..32f2cdd 100644
--- a/tempest/api/network/base_security_groups.py
+++ b/tempest/api/network/base_security_groups.py
@@ -15,6 +15,7 @@
 
 from tempest.api.network import base
 from tempest.lib.common.utils import data_utils
+from tempest.lib.common.utils import test_utils
 
 
 class BaseSecGroupTest(base.BaseNetworkTest):
@@ -24,7 +25,8 @@
         name = data_utils.rand_name('secgroup-')
         group_create_body = (
             self.security_groups_client.create_security_group(name=name))
-        self.addCleanup(self._delete_security_group,
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self._delete_security_group,
                         group_create_body['security_group']['id'])
         self.assertEqual(group_create_body['security_group']['name'], name)
         return group_create_body, name
diff --git a/tempest/api/network/test_allowed_address_pair.py b/tempest/api/network/test_allowed_address_pair.py
index dec3413..d393207 100644
--- a/tempest/api/network/test_allowed_address_pair.py
+++ b/tempest/api/network/test_allowed_address_pair.py
@@ -17,6 +17,7 @@
 
 from tempest.api.network import base
 from tempest.common import utils
+from tempest.lib.common.utils import test_utils
 from tempest.lib import decorators
 
 
@@ -62,7 +63,8 @@
             network_id=self.network['id'],
             allowed_address_pairs=allowed_address_pairs)
         port_id = body['port']['id']
-        self.addCleanup(self.ports_client.delete_port, port_id)
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self.ports_client.delete_port, port_id)
 
         # Confirm port was created with allowed address pair attribute
         body = self.ports_client.list_ports()
@@ -76,7 +78,8 @@
         # Create a port without allowed address pair
         body = self.ports_client.create_port(network_id=self.network['id'])
         port_id = body['port']['id']
-        self.addCleanup(self.ports_client.delete_port, port_id)
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self.ports_client.delete_port, port_id)
         if mac_address is None:
             mac_address = self.mac_address
 
@@ -106,7 +109,8 @@
         # Create an ip _address and mac_address through port create
         resp = self.ports_client.create_port(network_id=self.network['id'])
         newportid = resp['port']['id']
-        self.addCleanup(self.ports_client.delete_port, newportid)
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self.ports_client.delete_port, newportid)
         ipaddress = resp['port']['fixed_ips'][0]['ip_address']
         macaddress = resp['port']['mac_address']
 
diff --git a/tempest/api/network/test_dhcp_ipv6.py b/tempest/api/network/test_dhcp_ipv6.py
index 3ab2909..eb31ed3 100644
--- a/tempest/api/network/test_dhcp_ipv6.py
+++ b/tempest/api/network/test_dhcp_ipv6.py
@@ -206,7 +206,7 @@
                                  for k in port['fixed_ips']])
                 real_dhcp_ip, real_eui_ip = [real_ips[sub['id']]
                                              for sub in [subnet_dhcp,
-                                             subnet_slaac]]
+                                                         subnet_slaac]]
                 self.ports_client.delete_port(port['id'])
                 self.ports.pop()
                 body = self.ports_client.list_ports()
@@ -257,7 +257,7 @@
                                  for k in port['fixed_ips']])
                 real_dhcp_ip, real_eui_ip = [real_ips[sub['id']]
                                              for sub in [subnet_dhcp,
-                                             subnet_slaac]]
+                                                         subnet_slaac]]
                 self._clean_network()
                 self.assertEqual(real_eui_ip,
                                  eui_ip,
diff --git a/tempest/api/network/test_extra_dhcp_options.py b/tempest/api/network/test_extra_dhcp_options.py
index 0d42033..8e94429 100644
--- a/tempest/api/network/test_extra_dhcp_options.py
+++ b/tempest/api/network/test_extra_dhcp_options.py
@@ -16,6 +16,7 @@
 from tempest.api.network import base
 from tempest.common import utils
 from tempest.lib.common.utils import data_utils
+from tempest.lib.common.utils import test_utils
 from tempest.lib import decorators
 
 
@@ -62,7 +63,8 @@
             network_id=self.network['id'],
             extra_dhcp_opts=self.extra_dhcp_opts)
         port_id = body['port']['id']
-        self.addCleanup(self.ports_client.delete_port, port_id)
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self.ports_client.delete_port, port_id)
 
         # Confirm port created has Extra DHCP Options
         body = self.ports_client.list_ports()
diff --git a/tempest/api/network/test_floating_ips.py b/tempest/api/network/test_floating_ips.py
index 504bfa8..9704c73 100644
--- a/tempest/api/network/test_floating_ips.py
+++ b/tempest/api/network/test_floating_ips.py
@@ -18,6 +18,7 @@
 from tempest.common.utils import data_utils
 from tempest.common.utils import net_utils
 from tempest import config
+from tempest.lib.common.utils import test_utils
 from tempest.lib import decorators
 
 CONF = config.CONF
@@ -77,8 +78,10 @@
             floating_network_id=self.ext_net_id,
             port_id=self.ports[0]['id'])
         created_floating_ip = body['floatingip']
-        self.addCleanup(self.floating_ips_client.delete_floatingip,
-                        created_floating_ip['id'])
+        self.addCleanup(
+            test_utils.call_and_ignore_notfound_exc,
+            self.floating_ips_client.delete_floatingip,
+            created_floating_ip['id'])
         self.assertIsNotNone(created_floating_ip['id'])
         self.assertIsNotNone(created_floating_ip['tenant_id'])
         self.assertIsNotNone(created_floating_ip['floating_ip_address'])
@@ -125,14 +128,19 @@
         self.assertIsNone(updated_floating_ip['fixed_ip_address'])
         self.assertIsNone(updated_floating_ip['router_id'])
 
+        # Explicity test deletion of floating IP
+        self.floating_ips_client.delete_floatingip(created_floating_ip['id'])
+
     @decorators.idempotent_id('e1f6bffd-442f-4668-b30e-df13f2705e77')
     def test_floating_ip_delete_port(self):
         # Create a floating IP
         body = self.floating_ips_client.create_floatingip(
             floating_network_id=self.ext_net_id)
         created_floating_ip = body['floatingip']
-        self.addCleanup(self.floating_ips_client.delete_floatingip,
-                        created_floating_ip['id'])
+        self.addCleanup(
+            test_utils.call_and_ignore_notfound_exc,
+            self.floating_ips_client.delete_floatingip,
+            created_floating_ip['id'])
         # Create a port
         port = self.ports_client.create_port(network_id=self.network['id'])
         created_port = port['port']
@@ -158,24 +166,36 @@
             floating_network_id=self.ext_net_id,
             port_id=self.ports[1]['id'])
         created_floating_ip = body['floatingip']
-        self.addCleanup(self.floating_ips_client.delete_floatingip,
-                        created_floating_ip['id'])
+        self.addCleanup(
+            test_utils.call_and_ignore_notfound_exc,
+            self.floating_ips_client.delete_floatingip,
+            created_floating_ip['id'])
         self.assertEqual(created_floating_ip['router_id'], self.router['id'])
         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'])
+        self.addCleanup(
+            test_utils.call_and_ignore_notfound_exc,
+            self.networks_client.delete_network,
+            network2['id'])
         subnet2 = self.create_subnet(network2)
-        self.addCleanup(self.subnets_client.delete_subnet, subnet2['id'])
+        self.addCleanup(
+            test_utils.call_and_ignore_notfound_exc,
+            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.addCleanup(
+            test_utils.call_and_ignore_notfound_exc,
+            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'])
+        self.addCleanup(
+            test_utils.call_and_ignore_notfound_exc,
+            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'])
+        self.addCleanup(
+            test_utils.call_and_ignore_notfound_exc,
+            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'],
@@ -194,8 +214,10 @@
             port_id=self.ports[1]['id'],
             fixed_ip_address=self.ports[1]['fixed_ips'][0]['ip_address'])
         created_floating_ip = body['floatingip']
-        self.addCleanup(self.floating_ips_client.delete_floatingip,
-                        created_floating_ip['id'])
+        self.addCleanup(
+            test_utils.call_and_ignore_notfound_exc,
+            self.floating_ips_client.delete_floatingip,
+            created_floating_ip['id'])
         self.assertIsNotNone(created_floating_ip['id'])
         self.assertEqual(created_floating_ip['fixed_ip_address'],
                          self.ports[1]['fixed_ips'][0]['ip_address'])
@@ -218,14 +240,16 @@
         body = self.ports_client.create_port(network_id=self.network['id'],
                                              fixed_ips=fixed_ips)
         port = body['port']
-        self.addCleanup(self.ports_client.delete_port, port['id'])
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self.ports_client.delete_port, port['id'])
         # Create floating ip
         body = self.floating_ips_client.create_floatingip(
             floating_network_id=self.ext_net_id,
             port_id=port['id'],
             fixed_ip_address=list_ips[0])
         floating_ip = body['floatingip']
-        self.addCleanup(self.floating_ips_client.delete_floatingip,
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self.floating_ips_client.delete_floatingip,
                         floating_ip['id'])
         self.assertIsNotNone(floating_ip['id'])
         self.assertEqual(floating_ip['fixed_ip_address'], list_ips[0])
diff --git a/tempest/api/network/test_floating_ips_negative.py b/tempest/api/network/test_floating_ips_negative.py
index e904a81..1688c9d 100644
--- a/tempest/api/network/test_floating_ips_negative.py
+++ b/tempest/api/network/test_floating_ips_negative.py
@@ -17,6 +17,7 @@
 from tempest.api.network import base
 from tempest.common import utils
 from tempest import config
+from tempest.lib.common.utils import test_utils
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
 
@@ -81,6 +82,7 @@
             floating_network_id=self.ext_net_id)
         floating_ip = body['floatingip']
         self.addCleanup(
+            test_utils.call_and_ignore_notfound_exc,
             self.floating_ips_client.delete_floatingip, floating_ip['id'])
         # Associate floating IP to the other port
         self.assertRaises(
diff --git a/tempest/api/network/test_networks.py b/tempest/api/network/test_networks.py
index 7345fd1..eba1f6c 100644
--- a/tempest/api/network/test_networks.py
+++ b/tempest/api/network/test_networks.py
@@ -160,7 +160,8 @@
     def test_create_update_delete_network_subnet(self):
         # Create a network
         network = self.create_network()
-        self.addCleanup(self.networks_client.delete_network, network['id'])
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self.networks_client.delete_network, network['id'])
         net_id = network['id']
         self.assertEqual('ACTIVE', network['status'])
         # Verify network update
@@ -176,6 +177,8 @@
         body = self.subnets_client.update_subnet(subnet_id, name=new_name)
         updated_subnet = body['subnet']
         self.assertEqual(updated_subnet['name'], new_name)
+        # Verify network delete
+        self.networks_client.delete_network(network['id'])
 
     @decorators.attr(type='smoke')
     @decorators.idempotent_id('2bf13842-c93f-4a69-83ed-717d2ec3b44e')
@@ -313,11 +316,12 @@
     @decorators.idempotent_id('3d3852eb-3009-49ec-97ac-5ce83b73010a')
     def test_update_subnet_gw_dns_host_routes_dhcp(self):
         network = self.create_network()
-        self.addCleanup(self.networks_client.delete_network, network['id'])
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self.networks_client.delete_network, network['id'])
 
         subnet = self.create_subnet(
             network, **self.subnet_dict(['gateway', 'host_routes',
-                                        'dns_nameservers',
+                                         'dns_nameservers',
                                          'allocation_pools']))
         subnet_id = subnet['id']
         new_gateway = str(netaddr.IPAddress(
diff --git a/tempest/api/network/test_ports.py b/tempest/api/network/test_ports.py
index 2c9159c..93a4631 100644
--- a/tempest/api/network/test_ports.py
+++ b/tempest/api/network/test_ports.py
@@ -23,6 +23,7 @@
 from tempest.common import custom_matchers
 from tempest.common import utils
 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
 
@@ -52,7 +53,8 @@
     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'])
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self.subnets_client.delete_subnet, subnet['id'])
         return subnet
 
     def _create_network(self, network_name=None, **kwargs):
@@ -60,7 +62,8 @@
             self.__class__.__name__)
         network = self.networks_client.create_network(
             name=network_name, **kwargs)['network']
-        self.addCleanup(self.networks_client.delete_network,
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self.networks_client.delete_network,
                         network['id'])
         return network
 
@@ -107,7 +110,7 @@
         address = self.cidr
         address.prefixlen = self.mask_bits
         if ((address.version == 4 and address.prefixlen >= 30) or
-           (address.version == 6 and address.prefixlen >= 126)):
+            (address.version == 6 and address.prefixlen >= 126)):
             msg = ("Subnet %s isn't large enough for the test" % address.cidr)
             raise exceptions.InvalidConfiguration(msg)
         allocation_pools = {'allocation_pools': [{'start': str(address[2]),
@@ -116,13 +119,15 @@
                             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'])
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self.ports_client.delete_port, body['port']['id'])
         port = body['port']
         ip_address = port['fixed_ips'][0]['ip_address']
         start_ip_address = allocation_pools['allocation_pools'][0]['start']
         end_ip_address = allocation_pools['allocation_pools'][0]['end']
         ip_range = netaddr.IPRange(start_ip_address, end_ip_address)
         self.assertIn(ip_address, ip_range)
+        self.ports_client.delete_port(port['id'])
 
     @decorators.attr(type='smoke')
     @decorators.idempotent_id('c9a685bd-e83f-499c-939f-9f7863ca259f')
@@ -168,9 +173,11 @@
         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'])
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self.ports_client.delete_port, port_1['port']['id'])
         port_2 = self.ports_client.create_port(network_id=network['id'])
-        self.addCleanup(self.ports_client.delete_port, port_2['port']['id'])
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self.ports_client.delete_port, port_2['port']['id'])
         # List ports filtered by fixed_ips
         port_1_fixed_ip = port_1['port']['fixed_ips'][0]['ip_address']
         fixed_ips = 'ip_address=' + port_1_fixed_ip
@@ -219,11 +226,13 @@
         fixed_ips = [{'subnet_id': subnet['id'], 'ip_address': ip_address_1}]
         port_1 = self.ports_client.create_port(network_id=network['id'],
                                                fixed_ips=fixed_ips)
-        self.addCleanup(self.ports_client.delete_port, port_1['port']['id'])
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self.ports_client.delete_port, port_1['port']['id'])
         fixed_ips = [{'subnet_id': subnet['id'], 'ip_address': ip_address_2}]
         port_2 = self.ports_client.create_port(network_id=network['id'],
                                                fixed_ips=fixed_ips)
-        self.addCleanup(self.ports_client.delete_port, port_2['port']['id'])
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self.ports_client.delete_port, port_2['port']['id'])
 
         # Scenario 1: List port1 (port2 is filtered out)
         if ip_address_1[:-1] != ip_address_2[:-1]:
@@ -272,12 +281,14 @@
         network = self._create_network()
         self._create_subnet(network)
         router = self.create_router()
-        self.addCleanup(self.routers_client.delete_router, router['id'])
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self.routers_client.delete_router, router['id'])
         port = self.ports_client.create_port(network_id=network['id'])
         # Add router interface to port created above
         self.routers_client.add_router_interface(router['id'],
                                                  port_id=port['port']['id'])
-        self.addCleanup(self.routers_client.remove_router_interface,
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self.routers_client.remove_router_interface,
                         router['id'], port_id=port['port']['id'])
         # List ports filtered by router_id
         port_list = self.ports_client.list_ports(device_id=router['id'])
@@ -311,7 +322,8 @@
         # Create a port with multiple IP addresses
         port = self.create_port(network,
                                 fixed_ips=fixed_ips)
-        self.addCleanup(self.ports_client.delete_port, port['id'])
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self.ports_client.delete_port, port['id'])
         self.assertEqual(2, len(port['fixed_ips']))
         check_fixed_ips = [subnet_1['id'], subnet_2['id']]
         for item in port['fixed_ips']:
@@ -334,7 +346,8 @@
         for name in security_groups_names:
             group_create_body = sec_grps_client.create_security_group(
                 name=name)
-            self.addCleanup(self.security_groups_client.delete_security_group,
+            self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                            self.security_groups_client.delete_security_group,
                             group_create_body['security_group']['id'])
             security_groups_list.append(group_create_body['security_group']
                                         ['id'])
@@ -342,7 +355,8 @@
         sec_grp_name = data_utils.rand_name('secgroup')
         security_group = sec_grps_client.create_security_group(
             name=sec_grp_name)
-        self.addCleanup(self.security_groups_client.delete_security_group,
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self.security_groups_client.delete_security_group,
                         security_group['security_group']['id'])
         post_body = {
             "name": data_utils.rand_name('port-'),
@@ -351,7 +365,8 @@
             "admin_state_up": True,
             "fixed_ips": fixed_ip_1}
         body = self.ports_client.create_port(**post_body)
-        self.addCleanup(self.ports_client.delete_port, body['port']['id'])
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self.ports_client.delete_port, body['port']['id'])
         port = body['port']
 
         # Update the port with security groups
@@ -402,7 +417,8 @@
         # Create a new port with user defined mac
         body = self.ports_client.create_port(network_id=self.network['id'],
                                              mac_address=free_mac_address)
-        self.addCleanup(self.ports_client.delete_port, body['port']['id'])
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self.ports_client.delete_port, body['port']['id'])
         port = body['port']
         body = self.ports_client.show_port(port['id'])
         show_port = body['port']
@@ -418,7 +434,8 @@
         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.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self.ports_client.delete_port, port['id'])
         self.assertIsNotNone(port['security_groups'])
         self.assertEmpty(port['security_groups'])
 
diff --git a/tempest/api/network/test_routers.py b/tempest/api/network/test_routers.py
index be3cf65..f223fa4 100644
--- a/tempest/api/network/test_routers.py
+++ b/tempest/api/network/test_routers.py
@@ -20,6 +20,7 @@
 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
 from tempest.lib import decorators
 
 CONF = config.CONF
@@ -89,10 +90,11 @@
         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.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self.networks_client.delete_network, network['id'])
         subnet = self.create_subnet(network)
-        self.addCleanup(self.subnets_client.delete_subnet, subnet['id'])
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self.subnets_client.delete_subnet, subnet['id'])
         router = self.create_router()
         self.addCleanup(self.delete_router, router)
         # Add router interface with subnet id
@@ -114,8 +116,8 @@
         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.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self.networks_client.delete_network, network['id'])
         subnet = self.create_subnet(network)
         self.addCleanup(self.subnets_client.delete_subnet, subnet['id'])
         router = self.create_router()
@@ -126,7 +128,8 @@
         interface = self.routers_client.add_router_interface(
             router['id'],
             port_id=port_body['port']['id'])
-        self.addCleanup(self.routers_client.remove_router_interface,
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self.routers_client.remove_router_interface,
                         router['id'], port_id=port_body['port']['id'])
         self.assertIn('subnet_id', interface.keys())
         self.assertIn('port_id', interface.keys())
@@ -135,6 +138,8 @@
             interface['port_id'])
         self.assertEqual(show_port_body['port']['device_id'],
                          router['id'])
+        self.routers_client.remove_router_interface(
+            router['id'], port_id=port_body['port']['id'])
 
     @decorators.idempotent_id('cbe42f84-04c2-11e7-8adb-fa163e4fa634')
     @utils.requires_ext(extension='ext-gw-mode', service='network')
@@ -160,7 +165,8 @@
         # Create a router and set gateway to fixed_ip
         router = self.admin_routers_client.create_router(
             external_gateway_info=external_gateway_info)['router']
-        self.addCleanup(self.admin_routers_client.delete_router,
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self.admin_routers_client.delete_router,
                         router_id=router['id'])
         # Examine router's gateway is equal to fixed_ip
         self.assertEqual(router['external_gateway_info'][
@@ -188,10 +194,12 @@
             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,
+            self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                            self.networks_client.delete_network,
                             network['id'])
             subnet = self.create_subnet(network, cidr=next_cidr)
-            self.addCleanup(self.subnets_client.delete_subnet, subnet['id'])
+            self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                            self.subnets_client.delete_subnet, subnet['id'])
             next_cidr = next_cidr.next()
 
             # Add router interface with subnet id
@@ -254,18 +262,20 @@
         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'])
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self.networks_client.delete_network, network01['id'])
         network_name = data_utils.rand_name(self.__class__.__name__)
         network02 = self.networks_client.create_network(
             name=network_name)['network']
-        self.addCleanup(self.networks_client.delete_network,
-                        network02['id'])
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self.networks_client.delete_network, network02['id'])
         subnet01 = self.create_subnet(network01)
-        self.addCleanup(self.subnets_client.delete_subnet, subnet01['id'])
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self.subnets_client.delete_subnet, subnet01['id'])
         sub02_cidr = self.cidr.next()
         subnet02 = self.create_subnet(network02, cidr=sub02_cidr)
-        self.addCleanup(self.subnets_client.delete_subnet, subnet02['id'])
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self.subnets_client.delete_subnet, subnet02['id'])
         router = self.create_router()
         self.addCleanup(self.delete_router, router)
         interface01 = self._add_router_interface_with_subnet_id(router['id'],
@@ -282,10 +292,11 @@
         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.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self.networks_client.delete_network, network['id'])
         subnet = self.create_subnet(network)
-        self.addCleanup(self.subnets_client.delete_subnet, subnet['id'])
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self.subnets_client.delete_subnet, subnet['id'])
         router = self.create_router()
         self.addCleanup(self.delete_router, router)
         fixed_ip = [{'subnet_id': subnet['id']}]
diff --git a/tempest/api/network/test_security_groups.py b/tempest/api/network/test_security_groups.py
index ffc1fca..ef19122 100644
--- a/tempest/api/network/test_security_groups.py
+++ b/tempest/api/network/test_security_groups.py
@@ -16,6 +16,7 @@
 from tempest.api.network import base_security_groups as base
 from tempest.common import utils
 from tempest.lib.common.utils import data_utils
+from tempest.lib.common.utils import test_utils
 from tempest.lib import decorators
 
 
@@ -49,8 +50,8 @@
         )
 
         sec_group_rule = rule_create_body['security_group_rule']
-        self.addCleanup(self._delete_security_group_rule,
-                        sec_group_rule['id'])
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self._delete_security_group_rule, sec_group_rule['id'])
 
         expected = {'direction': direction, 'protocol': protocol,
                     'ethertype': ethertype, 'port_range_min': port_range_min,
@@ -104,6 +105,8 @@
         self.assertEqual(show_body['security_group']['name'], new_name)
         self.assertEqual(show_body['security_group']['description'],
                          new_description)
+        # Delete security group
+        self._delete_security_group(group_create_body['security_group']['id'])
 
     @decorators.attr(type='smoke')
     @decorators.idempotent_id('cfb99e0e-7410-4a3d-8a0c-959a63ee77e9')
@@ -138,6 +141,8 @@
                          for rule in rule_list_body['security_group_rules']]
             self.assertIn(rule_create_body['security_group_rule']['id'],
                           rule_list)
+            self._delete_security_group_rule(
+                rule_create_body['security_group_rule']['id'])
 
     @decorators.idempotent_id('87dfbcf9-1849-43ea-b1e4-efa3eeae9f71')
     def test_create_security_group_rule_with_additional_args(self):
@@ -170,7 +175,14 @@
 
         sg_id = group_create_body['security_group']['id']
         direction = 'ingress'
-        protocol = 'icmp'
+        # The Neutron API accepts 'icmp', 'icmpv6' and 'ipv6-icmp' for
+        # IPv6 ICMP protocol names, but the latter is preferred and the
+        # others considered "legacy".  Use 'ipv6-icmp' as the API could
+        # change to return only that value, see
+        # https://review.opendev.org/#/c/453346/
+        # The neutron-tempest-plugin API tests pass all three and verify
+        # the output, so there is no need to duplicate that here.
+        protocol = 'ipv6-icmp' if self._ip_version == 6 else 'icmp'
         icmp_type_codes = [(3, 2), (3, 0), (8, 0), (0, 0), (11, None)]
         for icmp_type, icmp_code in icmp_type_codes:
             self._create_verify_security_group_rule(sg_id, direction,
diff --git a/tempest/api/volume/admin/test_volume_retype.py b/tempest/api/volume/admin/test_volume_retype.py
index 1c56eb2..9136139 100644
--- a/tempest/api/volume/admin/test_volume_retype.py
+++ b/tempest/api/volume/admin/test_volume_retype.py
@@ -36,7 +36,7 @@
         # process is finished.
         fetched_list = self.admin_volume_client.list_volumes(
             params={'all_tenants': True,
-                    'display_name': vol['name']})['volumes']
+                    'name': vol['name']})['volumes']
 
         for fetched_vol in fetched_list:
             if fetched_vol['id'] != vol['id']:
diff --git a/tempest/api/volume/test_volumes_backup.py b/tempest/api/volume/test_volumes_backup.py
index c178272..6ce5d3e 100644
--- a/tempest/api/volume/test_volumes_backup.py
+++ b/tempest/api/volume/test_volumes_backup.py
@@ -50,6 +50,7 @@
                                                 'available')
         return restored_volume
 
+    @decorators.skip_because(bug="1483434")
     @testtools.skipIf(CONF.volume.storage_protocol == 'ceph',
                       'ceph does not support arbitrary container names')
     @decorators.idempotent_id('a66eb488-8ee1-47d4-8e9f-575a095728c6')
diff --git a/tempest/api/volume/test_volumes_extend.py b/tempest/api/volume/test_volumes_extend.py
index ac9a9c7..c3f44e2 100644
--- a/tempest/api/volume/test_volumes_extend.py
+++ b/tempest/api/volume/test_volumes_extend.py
@@ -32,7 +32,7 @@
     @decorators.idempotent_id('9a36df71-a257-43a5-9555-dc7c88e66e0e')
     def test_volume_extend(self):
         # Extend Volume Test.
-        volume = self.create_volume(image_ref=self.image_ref)
+        volume = self.create_volume(imageRef=self.image_ref)
         extend_size = volume['size'] * 2
         self.volumes_client.extend_volume(volume['id'],
                                           new_size=extend_size)
diff --git a/tempest/api/volume/test_volumes_snapshots.py b/tempest/api/volume/test_volumes_snapshots.py
index 1855386..72e7290 100644
--- a/tempest/api/volume/test_volumes_snapshots.py
+++ b/tempest/api/volume/test_volumes_snapshots.py
@@ -44,7 +44,7 @@
         server = self.create_server()
         # 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)
+        volume = self.create_volume(imageRef=self.image_ref)
         self.attach_volume(server['id'], volume['id'])
 
         # Snapshot a volume which attached to an instance with force=False
diff --git a/tempest/clients.py b/tempest/clients.py
index e5d5be1..f7a83be 100644
--- a/tempest/clients.py
+++ b/tempest/clients.py
@@ -44,6 +44,7 @@
         self._set_object_storage_clients()
         self._set_image_clients()
         self._set_network_clients()
+        self.placement_client = self.placement.PlacementClient()
         # TODO(andreaf) This is maintained for backward compatibility
         # with plugins, but it should removed eventually, since it was
         # never a stable interface and it's not useful anyways
@@ -68,6 +69,8 @@
         self.network_versions_client = self.network.NetworkVersionsClient()
         self.service_providers_client = self.network.ServiceProvidersClient()
         self.tags_client = self.network.TagsClient()
+        self.qos_client = self.network.QosClient()
+        self.qos_min_bw_client = self.network.QosMinimumBandwidthRulesClient()
 
     def _set_image_clients(self):
         if CONF.service_available.glance:
diff --git a/tempest/cmd/cleanup.py b/tempest/cmd/cleanup.py
index f6af0ba..e6db2e9 100644
--- a/tempest/cmd/cleanup.py
+++ b/tempest/cmd/cleanup.py
@@ -310,8 +310,8 @@
             svc.run()
 
         with open(SAVED_STATE_JSON, 'w+') as f:
-            f.write(json.dumps(data,
-                    sort_keys=True, indent=2, separators=(',', ': ')))
+            f.write(json.dumps(data, sort_keys=True,
+                               indent=2, separators=(',', ': ')))
 
     def _load_json(self, saved_state_json=SAVED_STATE_JSON):
         try:
diff --git a/tempest/cmd/cleanup_service.py b/tempest/cmd/cleanup_service.py
index 3aed4e8..104958a 100644
--- a/tempest/cmd/cleanup_service.py
+++ b/tempest/cmd/cleanup_service.py
@@ -158,7 +158,7 @@
             try:
                 client.delete_snapshot(snap['id'])
             except Exception:
-                LOG.exception("Delete Snapshot exception.")
+                LOG.exception("Delete Snapshot %s exception.", snap['id'])
 
     def dry_run(self):
         snaps = self.list()
@@ -195,7 +195,7 @@
             try:
                 client.delete_server(server['id'])
             except Exception:
-                LOG.exception("Delete Server exception.")
+                LOG.exception("Delete Server %s exception.", server['id'])
 
     def dry_run(self):
         servers = self.list()
@@ -227,7 +227,7 @@
             try:
                 client.delete_server_group(sg['id'])
             except Exception:
-                LOG.exception("Delete Server Group exception.")
+                LOG.exception("Delete Server Group %s exception.", sg['id'])
 
     def dry_run(self):
         sgs = self.list()
@@ -260,11 +260,11 @@
         client = self.client
         keypairs = self.list()
         for k in keypairs:
+            name = k['keypair']['name']
             try:
-                name = k['keypair']['name']
                 client.delete_keypair(name)
             except Exception:
-                LOG.exception("Delete Keypairs exception.")
+                LOG.exception("Delete Keypair %s exception.", name)
 
     def dry_run(self):
         keypairs = self.list()
@@ -300,7 +300,7 @@
             try:
                 client.delete_volume(v['id'])
             except Exception:
-                LOG.exception("Delete Volume exception.")
+                LOG.exception("Delete Volume %s exception.", v['id'])
 
     def dry_run(self):
         vols = self.list()
@@ -323,7 +323,8 @@
         try:
             client.delete_quota_set(self.project_id)
         except Exception:
-            LOG.exception("Delete Volume Quotas exception.")
+            LOG.exception("Delete Volume Quotas exception for 'project %s'.",
+                          self.project_id)
 
     def dry_run(self):
         quotas = self.client.show_quota_set(
@@ -342,7 +343,8 @@
         try:
             client.delete_quota_set(self.project_id)
         except Exception:
-            LOG.exception("Delete Quotas exception.")
+            LOG.exception("Delete Quotas exception for 'project %s'.",
+                          self.project_id)
 
     def dry_run(self):
         client = self.limits_client
@@ -397,7 +399,7 @@
             try:
                 client.delete_network(n['id'])
             except Exception:
-                LOG.exception("Delete Network exception.")
+                LOG.exception("Delete Network %s exception.", n['id'])
 
     def dry_run(self):
         networks = self.list()
@@ -431,7 +433,8 @@
             try:
                 client.delete_floatingip(flip['id'])
             except Exception:
-                LOG.exception("Delete Network Floating IP exception.")
+                LOG.exception("Delete Network Floating IP %s exception.",
+                              flip['id'])
 
     def dry_run(self):
         flips = self.list()
@@ -467,16 +470,20 @@
         ports_client = self.ports_client
         routers = self.list()
         for router in routers:
-            try:
-                rid = router['id']
-                ports = [port for port
-                         in ports_client.list_ports(device_id=rid)['ports']
-                         if net_info.is_router_interface_port(port)]
-                for port in ports:
+            rid = router['id']
+            ports = [port for port
+                     in ports_client.list_ports(device_id=rid)['ports']
+                     if net_info.is_router_interface_port(port)]
+            for port in ports:
+                try:
                     client.remove_router_interface(rid, port_id=port['id'])
+                except Exception:
+                    LOG.exception("Delete Router Interface exception for "
+                                  "'port %s' of 'router %s'.", port['id'], rid)
+            try:
                 client.delete_router(rid)
             except Exception:
-                LOG.exception("Delete Router exception.")
+                LOG.exception("Delete Router %s exception.", rid)
 
     def dry_run(self):
         routers = self.list()
@@ -511,7 +518,8 @@
             try:
                 client.delete_metering_label_rule(rule['id'])
             except Exception:
-                LOG.exception("Delete Metering Label Rule exception.")
+                LOG.exception("Delete Metering Label Rule %s exception.",
+                              rule['id'])
 
     def dry_run(self):
         rules = self.list()
@@ -546,7 +554,8 @@
             try:
                 client.delete_metering_label(label['id'])
             except Exception:
-                LOG.exception("Delete Metering Label exception.")
+                LOG.exception("Delete Metering Label %s exception.",
+                              label['id'])
 
     def dry_run(self):
         labels = self.list()
@@ -585,7 +594,7 @@
             try:
                 client.delete_port(port['id'])
             except Exception:
-                LOG.exception("Delete Port exception.")
+                LOG.exception("Delete Port %s exception.", port['id'])
 
     def dry_run(self):
         ports = self.list()
@@ -626,7 +635,8 @@
             try:
                 client.delete_security_group(secgroup['id'])
             except Exception:
-                LOG.exception("Delete security_group exception.")
+                LOG.exception("Delete security_group %s exception.",
+                              secgroup['id'])
 
     def dry_run(self):
         secgroups = self.list()
@@ -661,7 +671,7 @@
             try:
                 client.delete_subnet(subnet['id'])
             except Exception:
-                LOG.exception("Delete Subnet exception.")
+                LOG.exception("Delete Subnet %s exception.", subnet['id'])
 
     def dry_run(self):
         subnets = self.list()
@@ -696,7 +706,7 @@
             try:
                 client.delete_subnetpool(pool['id'])
             except Exception:
-                LOG.exception("Delete Subnet Pool exception.")
+                LOG.exception("Delete Subnet Pool %s exception.", pool['id'])
 
     def dry_run(self):
         pools = self.list()
@@ -736,7 +746,7 @@
             try:
                 client.delete_flavor(flavor['id'])
             except Exception:
-                LOG.exception("Delete Flavor exception.")
+                LOG.exception("Delete Flavor %s exception.", flavor['id'])
 
     def dry_run(self):
         flavors = self.list()
@@ -773,7 +783,7 @@
             try:
                 client.delete_image(image['id'])
             except Exception:
-                LOG.exception("Delete Image exception.")
+                LOG.exception("Delete Image %s exception.", image['id'])
 
     def dry_run(self):
         images = self.list()
@@ -816,7 +826,7 @@
             try:
                 self.client.delete_user(user['id'])
             except Exception:
-                LOG.exception("Delete User exception.")
+                LOG.exception("Delete User %s exception.", user['id'])
 
     def dry_run(self):
         users = self.list()
@@ -856,7 +866,7 @@
             try:
                 self.client.delete_role(role['id'])
             except Exception:
-                LOG.exception("Delete Role exception.")
+                LOG.exception("Delete Role %s exception.", role['id'])
 
     def dry_run(self):
         roles = self.list()
@@ -898,7 +908,7 @@
             try:
                 self.client.delete_project(project['id'])
             except Exception:
-                LOG.exception("Delete project exception.")
+                LOG.exception("Delete project %s exception.", project['id'])
 
     def dry_run(self):
         projects = self.list()
@@ -935,7 +945,7 @@
                 client.update_domain(domain['id'], enabled=False)
                 client.delete_domain(domain['id'])
             except Exception:
-                LOG.exception("Delete Domain exception.")
+                LOG.exception("Delete Domain %s exception.", domain['id'])
 
     def dry_run(self):
         domains = self.list()
diff --git a/tempest/cmd/run.py b/tempest/cmd/run.py
index 823ed11..77d4496 100644
--- a/tempest/cmd/run.py
+++ b/tempest/cmd/run.py
@@ -103,6 +103,9 @@
 from tempest.common import credentials_factory as credentials
 from tempest import config
 
+if six.PY2:
+    # Python 2 has not FileNotFoundError exception
+    FileNotFoundError = IOError
 
 CONF = config.CONF
 SAVED_STATE_JSON = "saved_state.json"
@@ -112,7 +115,12 @@
 
     def _set_env(self, config_file=None):
         if config_file:
-            CONF.set_config_path(os.path.abspath(config_file))
+            if os.path.exists(os.path.abspath(config_file)):
+                CONF.set_config_path(os.path.abspath(config_file))
+            else:
+                raise FileNotFoundError(
+                    "Config file: %s doesn't exist" % config_file)
+
         # NOTE(mtreinish): This is needed so that stestr doesn't gobble up any
         # stacktraces on failure.
         if 'TESTR_PDB' in os.environ:
@@ -202,8 +210,8 @@
             svc.run()
 
         with open(SAVED_STATE_JSON, 'w+') as f:
-            f.write(json.dumps(data,
-                    sort_keys=True, indent=2, separators=(',', ': ')))
+            f.write(json.dumps(data, sort_keys=True,
+                               indent=2, separators=(',', ': ')))
 
     def get_parser(self, prog_name):
         parser = super(TempestRun, self).get_parser(prog_name)
diff --git a/tempest/common/identity.py b/tempest/common/identity.py
index 525110b..cd6d058 100644
--- a/tempest/common/identity.py
+++ b/tempest/common/identity.py
@@ -26,7 +26,7 @@
         if project['name'] == project_name:
             return project
     raise lib_exc.NotFound('No such project(%s) in %s' % (project_name,
-                           projects))
+                                                          projects))
 
 
 def get_tenant_by_name(client, tenant_name):
diff --git a/tempest/common/utils/linux/remote_client.py b/tempest/common/utils/linux/remote_client.py
index 49d9742..d76a323 100644
--- a/tempest/common/utils/linux/remote_client.py
+++ b/tempest/common/utils/linux/remote_client.py
@@ -156,3 +156,53 @@
             cmd_why = 'sudo ls -lR /dev'
             LOG.info("Contents of /dev: %s", self.exec_command(cmd_why))
             raise
+
+    def nc_listen_host(self, port=80, protocol='tcp'):
+        """Creates persistent nc server listening on the given TCP / UDP port
+
+        :port: the port to start listening on.
+        :protocol: the protocol used by the server. TCP by default.
+        """
+        udp = '-u' if protocol.lower() == 'udp' else ''
+        cmd = "sudo nc %(udp)s -p %(port)s -lk -e echo foolish &" % {
+            'udp': udp, 'port': port}
+        return self.exec_command(cmd)
+
+    def nc_host(self, host, port=80, protocol='tcp', expected_response=None):
+        """Check connectivity to TCP / UDP port at host via nc
+
+        :host: an IP against which the connectivity will be tested.
+        :port: the port to check connectivity against.
+        :protocol: the protocol used by nc to send packets. TCP by default.
+        :expected_response: string representing the expected response
+            from server.
+        :raises SSHExecCommandFailed: if an expected response is given and it
+            does not match the actual server response.
+        """
+        udp = '-u' if protocol.lower() == 'udp' else ''
+        cmd = 'echo "bar" | nc -w 1 %(udp)s %(host)s %(port)s' % {
+            'udp': udp, 'host': host, 'port': port}
+        response = self.exec_command(cmd)
+
+        # sending an UDP packet will always succeed. we need to check
+        # the response.
+        if (expected_response is not None and
+                expected_response != response.strip()):
+            raise tempest.lib.exceptions.SSHExecCommandFailed(
+                command=cmd, exit_status=0, stdout=response, stderr='')
+        return response
+
+    def icmp_check(self, host, nic=None):
+        """Wrapper for icmp connectivity checks"""
+        return self.ping_host(host, nic=nic)
+
+    def udp_check(self, host, **kwargs):
+        """Wrapper for udp connectivity checks."""
+        kwargs.pop('nic', None)
+        return self.nc_host(host, protocol='udp', expected_response='foolish',
+                            **kwargs)
+
+    def tcp_check(self, host, **kwargs):
+        """Wrapper for tcp connectivity checks."""
+        kwargs.pop('nic', None)
+        return self.nc_host(host, **kwargs)
diff --git a/tempest/common/utils/net_utils.py b/tempest/common/utils/net_utils.py
index 867b3dd..b697ef1 100644
--- a/tempest/common/utils/net_utils.py
+++ b/tempest/common/utils/net_utils.py
@@ -19,7 +19,6 @@
 
 def get_unused_ip_addresses(ports_client, subnets_client,
                             network_id, subnet_id, count):
-
     """Return a list with the specified number of unused IP addresses
 
     This method uses the given ports_client to find the specified number of
diff --git a/tempest/common/waiters.py b/tempest/common/waiters.py
old mode 100644
new mode 100755
index 0e86f05..e1b8cf5
--- a/tempest/common/waiters.py
+++ b/tempest/common/waiters.py
@@ -104,8 +104,8 @@
         body = client.show_server(server_id)['server']
     except lib_exc.NotFound:
         return
-    old_status = server_status = body['status']
-    old_task_state = task_state = _get_task_state(body)
+    old_status = body['status']
+    old_task_state = _get_task_state(body)
     start_time = int(time.time())
     while True:
         time.sleep(client.build_interval)
@@ -202,6 +202,8 @@
                 resource_name=resource_name, resource_id=resource_id)
         if resource_name == 'volume' and resource_status == 'error_restoring':
             raise exceptions.VolumeRestoreErrorException(volume_id=resource_id)
+        if resource_status == 'error_extending' and resource_status != status:
+            raise exceptions.VolumeExtendErrorException(volume_id=resource_id)
 
         if int(time.time()) - start >= client.build_timeout:
             message = ('%s %s failed to reach %s status (current %s) '
@@ -213,6 +215,31 @@
              resource_name, resource_id, status, time.time() - start)
 
 
+def wait_for_volume_migration(client, volume_id, new_host):
+    """Waits for a Volume to move to a new host."""
+    body = client.show_volume(volume_id)['volume']
+    host = body['os-vol-host-attr:host']
+    migration_status = body['migration_status']
+    start = int(time.time())
+
+    # new_host is hostname@backend while current_host is hostname@backend#type
+    while migration_status != 'success' or new_host not in host:
+        time.sleep(client.build_interval)
+        body = client.show_volume(volume_id)['volume']
+        host = body['os-vol-host-attr:host']
+        migration_status = body['migration_status']
+
+        if migration_status == 'error':
+            message = ('volume %s failed to migrate.' % (volume_id))
+            raise lib_exc.TempestException(message)
+
+        if int(time.time()) - start >= client.build_timeout:
+            message = ('Volume %s failed to migrate to %s (current %s) '
+                       'within the required time (%s s).' %
+                       (volume_id, new_host, host, client.build_timeout))
+            raise lib_exc.TimeoutException(message)
+
+
 def wait_for_volume_retype(client, volume_id, new_volume_type):
     """Waits for a Volume to have a new volume type."""
     body = client.show_volume(volume_id)['volume']
diff --git a/tempest/config.py b/tempest/config.py
index fbe18a3..9e4718b 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -69,10 +69,7 @@
     cfg.StrOpt('default_credentials_domain_name',
                default='Default',
                help="Default domain used when getting v3 credentials. "
-                    "This is the name keystone uses for v2 compatibility.",
-               deprecated_opts=[cfg.DeprecatedOpt(
-                                'tenant_isolation_domain_name',
-                                group='auth')]),
+                    "This is the name keystone uses for v2 compatibility."),
     cfg.BoolOpt('create_isolated_networks',
                 default=True,
                 help="If use_dynamic_credentials is set to True and Neutron "
@@ -84,27 +81,20 @@
     cfg.StrOpt('admin_username',
                help="Username for an administrative user. This is needed for "
                     "authenticating requests made by project isolation to "
-                    "create users and projects",
-               deprecated_group='identity'),
+                    "create users and projects"),
     cfg.StrOpt('admin_project_name',
                help="Project name to use for an administrative user. This is "
                     "needed for authenticating requests made by project "
-                    "isolation to create users and projects",
-               deprecated_opts=[cfg.DeprecatedOpt('admin_tenant_name',
-                                                  group='auth'),
-                                cfg.DeprecatedOpt('admin_tenant_name',
-                                                  group='identity')]),
+                    "isolation to create users and projects"),
     cfg.StrOpt('admin_password',
                help="Password to use for an administrative user. This is "
                     "needed for authenticating requests made by project "
                     "isolation to create users and projects",
-               secret=True,
-               deprecated_group='identity'),
+               secret=True),
     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'),
+                    "The same domain applies to user and project"),
 ]
 
 identity_group = cfg.OptGroup(name='identity',
@@ -146,9 +136,7 @@
                choices=['public', 'admin', 'internal',
                         'publicURL', 'adminURL', 'internalURL'],
                help="The public endpoint type to use for OpenStack Identity "
-                    "(Keystone) API v2",
-               deprecated_opts=[cfg.DeprecatedOpt('endpoint_type',
-                                                  group='identity')]),
+                    "(Keystone) API v2"),
     cfg.StrOpt('v3_endpoint_type',
                default='adminURL',
                choices=['public', 'admin', 'internal',
@@ -685,7 +673,10 @@
     cfg.ListOpt('dns_servers',
                 default=["8.8.8.8", "8.8.4.4"],
                 help="List of dns servers which should be used"
-                     " for subnet creation"),
+                     " for subnet creation",
+                deprecated_for_removal=True,
+                deprecated_reason="This config option is no longer "
+                                  "used anywhere, so it can be removed."),
     cfg.StrOpt('port_vnic_type',
                choices=[None, 'normal', 'direct', 'macvtap'],
                help="vnic_type to use when launching instances"
@@ -1049,7 +1040,12 @@
                choices=["udhcpc", "dhclient", ""],
                help='DHCP client used by images to renew DCHP lease. '
                     'If left empty, update operation will be skipped. '
-                    'Supported clients: "udhcpc", "dhclient"')
+                    'Supported clients: "udhcpc", "dhclient"'),
+    cfg.StrOpt('protocol',
+               default='icmp',
+               choices=('icmp', 'tcp', 'udp'),
+               help='The protocol used in security groups tests to check '
+                    'connectivity.'),
 ]
 
 
@@ -1100,6 +1096,18 @@
 """)
 ]
 
+
+profiler_group = cfg.OptGroup(name="profiler",
+                              title="OpenStack Profiler")
+
+ProfilerGroup = [
+    cfg.StrOpt('key',
+               help="The secret key to enable OpenStack Profiler. The value "
+                    "should match the one configured in OpenStack services "
+                    "under `[profiler]/hmac_keys` property. The default empty "
+                    "value keeps profiling disabled"),
+]
+
 DefaultGroup = [
     cfg.BoolOpt('pause_teardown',
                 default=False,
@@ -1132,6 +1140,7 @@
     (service_available_group, ServiceAvailableGroup),
     (debug_group, DebugGroup),
     (placement_group, PlacementGroup),
+    (profiler_group, ProfilerGroup),
     (None, DefaultGroup)
 ]
 
@@ -1249,7 +1258,7 @@
 
         logging_cfg_path = "%s/logging.conf" % os.path.dirname(path)
         if ((not hasattr(_CONF, 'log_config_append') or
-            _CONF.log_config_append is None) and
+             _CONF.log_config_append is None) and
             os.path.isfile(logging_cfg_path)):
             # if logging conf is in place we need to set log_config_append
             _CONF.log_config_append = logging_cfg_path
diff --git a/tempest/exceptions.py b/tempest/exceptions.py
old mode 100644
new mode 100755
index a430d5d..c05e7a6
--- a/tempest/exceptions.py
+++ b/tempest/exceptions.py
@@ -42,6 +42,11 @@
     message = "Volume %(volume_id)s failed to restore and is in ERROR status"
 
 
+class VolumeExtendErrorException(exceptions.TempestException):
+    message = ("Volume %(volume_id)s failed to extend and "
+               "is in error_extending status")
+
+
 class StackBuildErrorException(exceptions.TempestException):
     message = ("Stack %(stack_identifier)s is in %(stack_status)s status "
                "due to '%(stack_status_reason)s'")
diff --git a/tempest/lib/api_schema/response/compute/v2_1/volumes.py b/tempest/lib/api_schema/response/compute/v2_1/volumes.py
index c35dae9..d367f2a 100644
--- a/tempest/lib/api_schema/response/compute/v2_1/volumes.py
+++ b/tempest/lib/api_schema/response/compute/v2_1/volumes.py
@@ -50,7 +50,8 @@
                             # If it would come as empty array "[]" then,
                             # those elements can be defined as 'required'.
                         }
-                    }
+                    },
+                    'os-vol-host-attr:host': {'type': 'string'},
                 },
                 'additionalProperties': False,
                 'required': ['id', 'status', 'displayName', 'availabilityZone',
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 72b84f5..fc81ff7 100644
--- a/tempest/lib/api_schema/response/compute/v2_16/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_16/servers.py
@@ -168,3 +168,6 @@
     servers.rebuild_server_with_admin_pass)
 show_server_diagnostics = copy.deepcopy(servers.show_server_diagnostics)
 get_remote_consoles = copy.deepcopy(servers.get_remote_consoles)
+attach_volume = copy.deepcopy(servers.attach_volume)
+show_volume_attachment = copy.deepcopy(servers.show_volume_attachment)
+list_volume_attachments = copy.deepcopy(servers.list_volume_attachments)
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 e3e8ad1..b6c3c14 100644
--- a/tempest/lib/api_schema/response/compute/v2_19/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_19/servers.py
@@ -58,3 +58,6 @@
 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)
+attach_volume = copy.deepcopy(serversv216.attach_volume)
+show_volume_attachment = copy.deepcopy(serversv216.show_volume_attachment)
+list_volume_attachments = copy.deepcopy(serversv216.list_volume_attachments)
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 8e62dc3..5a0f987 100644
--- a/tempest/lib/api_schema/response/compute/v2_26/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_26/servers.py
@@ -101,3 +101,6 @@
 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)
+attach_volume = copy.deepcopy(servers219.attach_volume)
+show_volume_attachment = copy.deepcopy(servers219.show_volume_attachment)
+list_volume_attachments = copy.deepcopy(servers219.list_volume_attachments)
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 18fb352..1674c1b 100644
--- a/tempest/lib/api_schema/response/compute/v2_3/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_3/servers.py
@@ -173,3 +173,6 @@
 rebuild_server_with_admin_pass = copy.deepcopy(
     servers.rebuild_server_with_admin_pass)
 show_server_diagnostics = copy.deepcopy(servers.show_server_diagnostics)
+attach_volume = copy.deepcopy(servers.attach_volume)
+show_volume_attachment = copy.deepcopy(servers.show_volume_attachment)
+list_volume_attachments = copy.deepcopy(servers.list_volume_attachments)
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 0fbacd3..d580f2c 100644
--- a/tempest/lib/api_schema/response/compute/v2_47/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_47/servers.py
@@ -66,3 +66,6 @@
 update_tag = copy.deepcopy(servers226.update_tag)
 delete_tag = copy.deepcopy(servers226.delete_tag)
 list_servers = copy.deepcopy(servers226.list_servers)
+attach_volume = copy.deepcopy(servers226.attach_volume)
+show_volume_attachment = copy.deepcopy(servers226.show_volume_attachment)
+list_volume_attachments = copy.deepcopy(servers226.list_volume_attachments)
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 84b5a2a..e2e45bc 100644
--- a/tempest/lib/api_schema/response/compute/v2_48/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_48/servers.py
@@ -129,3 +129,6 @@
 rebuild_server = copy.deepcopy(servers247.rebuild_server)
 rebuild_server_with_admin_pass = copy.deepcopy(
     servers247.rebuild_server_with_admin_pass)
+attach_volume = copy.deepcopy(servers247.attach_volume)
+show_volume_attachment = copy.deepcopy(servers247.show_volume_attachment)
+list_volume_attachments = copy.deepcopy(servers247.list_volume_attachments)
diff --git a/tempest/lib/api_schema/response/compute/v2_54/servers.py b/tempest/lib/api_schema/response/compute/v2_54/servers.py
index 099e1b8..2c2bff0 100644
--- a/tempest/lib/api_schema/response/compute/v2_54/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_54/servers.py
@@ -55,3 +55,6 @@
 check_tag_existence = copy.deepcopy(servers248.check_tag_existence)
 update_tag = copy.deepcopy(servers248.update_tag)
 delete_tag = copy.deepcopy(servers248.delete_tag)
+attach_volume = copy.deepcopy(servers248.attach_volume)
+show_volume_attachment = copy.deepcopy(servers248.show_volume_attachment)
+list_volume_attachments = copy.deepcopy(servers248.list_volume_attachments)
diff --git a/tempest/lib/api_schema/response/compute/v2_57/servers.py b/tempest/lib/api_schema/response/compute/v2_57/servers.py
index 0099a2b..aa57d25 100644
--- a/tempest/lib/api_schema/response/compute/v2_57/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_57/servers.py
@@ -59,3 +59,6 @@
 check_tag_existence = copy.deepcopy(servers254.check_tag_existence)
 update_tag = copy.deepcopy(servers254.update_tag)
 delete_tag = copy.deepcopy(servers254.delete_tag)
+attach_volume = copy.deepcopy(servers254.attach_volume)
+show_volume_attachment = copy.deepcopy(servers254.show_volume_attachment)
+list_volume_attachments = copy.deepcopy(servers254.list_volume_attachments)
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 d5774de..922bf79 100644
--- a/tempest/lib/api_schema/response/compute/v2_6/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_6/servers.py
@@ -28,6 +28,9 @@
 rebuild_server_with_admin_pass = copy.deepcopy(
     servers.rebuild_server_with_admin_pass)
 show_server_diagnostics = copy.deepcopy(servers.show_server_diagnostics)
+attach_volume = copy.deepcopy(servers.attach_volume)
+show_volume_attachment = copy.deepcopy(servers.show_volume_attachment)
+list_volume_attachments = copy.deepcopy(servers.list_volume_attachments)
 
 # NOTE: The consolidated remote console API got introduced with v2.6
 # with bp/consolidate-console-api. See Nova commit 578bafeda
diff --git a/tempest/lib/api_schema/response/compute/v2_63/servers.py b/tempest/lib/api_schema/response/compute/v2_63/servers.py
index 3c3d41c..01910aa 100644
--- a/tempest/lib/api_schema/response/compute/v2_63/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_63/servers.py
@@ -73,3 +73,6 @@
 check_tag_existence = copy.deepcopy(servers257.check_tag_existence)
 update_tag = copy.deepcopy(servers257.update_tag)
 delete_tag = copy.deepcopy(servers257.delete_tag)
+attach_volume = copy.deepcopy(servers257.attach_volume)
+show_volume_attachment = copy.deepcopy(servers257.show_volume_attachment)
+list_volume_attachments = copy.deepcopy(servers257.list_volume_attachments)
diff --git a/tempest/lib/api_schema/response/compute/v2_70/__init__.py b/tempest/lib/api_schema/response/compute/v2_70/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_70/__init__.py
diff --git a/tempest/lib/api_schema/response/compute/v2_70/servers.py b/tempest/lib/api_schema/response/compute/v2_70/servers.py
new file mode 100644
index 0000000..5ca4cc8
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_70/servers.py
@@ -0,0 +1,80 @@
+#    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 servers as servers2_1
+from tempest.lib.api_schema.response.compute.v2_63 import servers as servers263
+
+
+###########################################################################
+#
+# 2.70:
+#
+# Exposes virtual device tags for volume attachments and virtual interfaces
+# (ports). A tag parameter is added to the response body for the following
+# APIs:
+#
+# Volumes
+#
+# - GET /servers/{server_id}/os-volume_attachments (list)
+# - GET /servers/{server_id}/os-volume_attachments/{volume_id} (show)
+# - POST /servers/{server_id}/os-volume_attachments (attach)
+#
+# Ports
+#
+# - GET /servers/{server_id}/os-interface (list)
+# - GET /servers/{server_id}/os-interface/{port_id} (show)
+# - POST /servers/{server_id}/os-interface (attach)
+#
+###########################################################################
+
+attach_volume = copy.deepcopy(servers2_1.attach_volume)
+attach_volume['response_body']['properties']['volumeAttachment'][
+    'properties'].update({'tag': {'type': ['string', 'null']}})
+attach_volume['response_body']['properties']['volumeAttachment'][
+    'required'].append('tag')
+
+show_volume_attachment = copy.deepcopy(servers2_1.show_volume_attachment)
+show_volume_attachment['response_body']['properties']['volumeAttachment'][
+    'properties'].update({'tag': {'type': ['string', 'null']}})
+show_volume_attachment['response_body']['properties'][
+    'volumeAttachment']['required'].append('tag')
+
+list_volume_attachments = copy.deepcopy(servers2_1.list_volume_attachments)
+list_volume_attachments['response_body']['properties']['volumeAttachments'][
+    'items']['properties'].update({'tag': {'type': ['string', 'null']}})
+list_volume_attachments['response_body']['properties'][
+    'volumeAttachments']['items']['required'].append('tag')
+
+# TODO(mriedem): Handle the os-interface changes when there is a test that
+# needs them from this microversion onward.
+
+# NOTE(lajoskatona): 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.63 ***
+list_servers_detail = copy.deepcopy(servers263.list_servers_detail)
+rebuild_server = copy.deepcopy(servers263.rebuild_server)
+rebuild_server_with_admin_pass = copy.deepcopy(
+    servers263.rebuild_server_with_admin_pass)
+update_server = copy.deepcopy(servers263.update_server)
+get_server = copy.deepcopy(servers263.get_server)
+list_servers = copy.deepcopy(servers263.list_servers)
+show_server_diagnostics = copy.deepcopy(servers263.show_server_diagnostics)
+get_remote_consoles = copy.deepcopy(servers263.get_remote_consoles)
+list_tags = copy.deepcopy(servers263.list_tags)
+update_all_tags = copy.deepcopy(servers263.update_all_tags)
+delete_all_tags = copy.deepcopy(servers263.delete_all_tags)
+check_tag_existence = copy.deepcopy(servers263.check_tag_existence)
+update_tag = copy.deepcopy(servers263.update_tag)
+delete_tag = copy.deepcopy(servers263.delete_tag)
diff --git a/tempest/lib/api_schema/response/compute/v2_71/__init__.py b/tempest/lib/api_schema/response/compute/v2_71/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_71/__init__.py
diff --git a/tempest/lib/api_schema/response/compute/v2_71/servers.py b/tempest/lib/api_schema/response/compute/v2_71/servers.py
new file mode 100644
index 0000000..0c526fb
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_71/servers.py
@@ -0,0 +1,81 @@
+#    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_70 import servers as servers270
+
+
+###########################################################################
+#
+# 2.71:
+#
+# The server_groups parameter will be in the response body of the following
+# APIs to list the server groups to which the server belongs:
+#
+# - GET /servers/{server_id} (show)
+# - PUT /servers/{server_id} (update)
+# - POST /servers/{server_id}/action (rebuild)
+#
+###########################################################################
+
+# The "server_groups" parameter will always be present and contain at most one
+# UUID entry.
+server_groups = {
+    'type': 'array',
+    'minItems': 0,
+    'maxItems': 1,
+    'items': {
+        'type': 'string',
+        'format': 'uuid'
+    }
+}
+
+rebuild_server = copy.deepcopy(servers270.rebuild_server)
+rebuild_server['response_body']['properties']['server'][
+    'properties'].update({'server_groups': server_groups})
+rebuild_server['response_body']['properties']['server'][
+    'required'].append('server_groups')
+
+rebuild_server_with_admin_pass = copy.deepcopy(
+    servers270.rebuild_server_with_admin_pass)
+rebuild_server_with_admin_pass['response_body']['properties']['server'][
+    'properties'].update({'server_groups': server_groups})
+rebuild_server_with_admin_pass['response_body']['properties']['server'][
+    'required'].append('server_groups')
+
+update_server = copy.deepcopy(servers270.update_server)
+update_server['response_body']['properties']['server'][
+    'properties'].update({'server_groups': server_groups})
+update_server['response_body']['properties']['server'][
+    'required'].append('server_groups')
+
+get_server = copy.deepcopy(servers270.get_server)
+get_server['response_body']['properties']['server'][
+    'properties'].update({'server_groups': server_groups})
+get_server['response_body']['properties']['server'][
+    'required'].append('server_groups')
+
+# NOTE(lajoskatona): 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.70 ***
+list_servers_details = copy.deepcopy(servers270.list_servers_detail)
+list_servers = copy.deepcopy(servers270.list_servers)
+show_server_diagnostics = copy.deepcopy(servers270.show_server_diagnostics)
+get_remote_consoles = copy.deepcopy(servers270.get_remote_consoles)
+list_tags = copy.deepcopy(servers270.list_tags)
+update_all_tags = copy.deepcopy(servers270.update_all_tags)
+delete_all_tags = copy.deepcopy(servers270.delete_all_tags)
+check_tag_existence = copy.deepcopy(servers270.check_tag_existence)
+update_tag = copy.deepcopy(servers270.update_tag)
+delete_tag = copy.deepcopy(servers270.delete_tag)
diff --git a/tempest/lib/api_schema/response/compute/v2_8/servers.py b/tempest/lib/api_schema/response/compute/v2_8/servers.py
index df7847f..3dbab3f 100644
--- a/tempest/lib/api_schema/response/compute/v2_8/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_8/servers.py
@@ -35,3 +35,6 @@
 rebuild_server_with_admin_pass = copy.deepcopy(
     servers.rebuild_server_with_admin_pass)
 show_server_diagnostics = copy.deepcopy(servers.show_server_diagnostics)
+attach_volume = copy.deepcopy(servers.attach_volume)
+show_volume_attachment = copy.deepcopy(servers.show_volume_attachment)
+list_volume_attachments = copy.deepcopy(servers.list_volume_attachments)
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 55f8e75..ee0313d 100644
--- a/tempest/lib/api_schema/response/compute/v2_9/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_9/servers.py
@@ -54,3 +54,6 @@
 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)
+attach_volume = copy.deepcopy(servers.attach_volume)
+show_volume_attachment = copy.deepcopy(servers.show_volume_attachment)
+list_volume_attachments = copy.deepcopy(servers.list_volume_attachments)
diff --git a/tempest/lib/cmd/check_uuid.py b/tempest/lib/cmd/check_uuid.py
index ac40eef..71ecb32 100755
--- a/tempest/lib/cmd/check_uuid.py
+++ b/tempest/lib/cmd/check_uuid.py
@@ -110,7 +110,7 @@
             for item in files:
                 if item.endswith('.py'):
                     module_name = '.'.join((root_package,
-                                           os.path.splitext(item)[0]))
+                                            os.path.splitext(item)[0]))
                     if not module_name.startswith(UNIT_TESTS_EXCLUDE):
                         modules.append(module_name)
         return modules
diff --git a/tempest/lib/common/api_microversion_fixture.py b/tempest/lib/common/api_microversion_fixture.py
new file mode 100644
index 0000000..3837138
--- /dev/null
+++ b/tempest/lib/common/api_microversion_fixture.py
@@ -0,0 +1,82 @@
+# Copyright 2019 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 fixtures
+
+from tempest.lib.services.compute import base_compute_client
+from tempest.lib.services.placement import base_placement_client
+from tempest.lib.services.volume import base_client as base_volume_client
+
+
+class APIMicroversionFixture(fixtures.Fixture):
+    """API Microversion Fixture to set service microversion.
+
+    This class provides the fixture to set and reset the microversion
+    on service client. Service client has global variable to set the
+    microversion for that service API request.
+    For example: base_compute_client.COMPUTE_MICROVERSION
+    Global variable is always risky to set directly which can affect the
+    other test's API request also. This class provides a way to reset the
+    service microversion once test finish the API request.
+    This class can be used with useFixture: Example::
+
+        def setUp(self):
+            super(BaseV2ComputeTest, self).setUp()
+            self.useFixture(api_microversion_fixture.APIMicroversionFixture(
+                compute_microversion=self.compute_request_microversion))
+
+    Or you can set microversion on multiple services together::
+
+        def setUp(self):
+            super(ScenarioTest, self).setUp()
+            self.useFixture(api_microversion_fixture.APIMicroversionFixture(
+                compute_microversion=self.compute_request_microversion,
+                volume_microversion=self.volume_request_microversion))
+
+    Current supported services:
+    - Compute
+    - Volume
+    - Placement
+
+    :param str compute_microversion: microvesion to be set on compute
+                                     service clients
+    :param str volume_microversion: microvesion to be set on volume
+                                    service clients
+    :param str placement_microversion: microvesion to be set on placement
+                                       service clients
+    """
+
+    def __init__(self, compute_microversion=None, volume_microversion=None,
+                 placement_microversion=None):
+        self.compute_microversion = compute_microversion
+        self.volume_microversion = volume_microversion
+        self.placement_microversion = placement_microversion
+
+    def _setUp(self):
+        super(APIMicroversionFixture, self)._setUp()
+        if self.compute_microversion:
+            base_compute_client.COMPUTE_MICROVERSION = (
+                self.compute_microversion)
+        if self.volume_microversion:
+            base_volume_client.VOLUME_MICROVERSION = self.volume_microversion
+        if self.placement_microversion:
+            base_placement_client.PLACEMENT_MICROVERSION = (
+                self.placement_microversion)
+
+        self.addCleanup(self._reset_microversion)
+
+    def _reset_microversion(self):
+        base_compute_client.COMPUTE_MICROVERSION = None
+        base_volume_client.VOLUME_MICROVERSION = None
+        base_placement_client.PLACEMENT_MICROVERSION = None
diff --git a/tempest/lib/common/api_version_utils.py b/tempest/lib/common/api_version_utils.py
index bcb076b..d29362d 100644
--- a/tempest/lib/common/api_version_utils.py
+++ b/tempest/lib/common/api_version_utils.py
@@ -12,6 +12,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import six
 import testtools
 
 from tempest.lib.common import api_version_request
@@ -54,7 +55,7 @@
     config_min_version = api_version_request.APIVersionRequest(cfg_min_version)
     config_max_version = api_version_request.APIVersionRequest(cfg_max_version)
     if ((min_version > max_version) or
-       (config_min_version > config_max_version)):
+        (config_min_version > config_max_version)):
         msg = ("Test Class versions [%s - %s]. "
                "Configuration versions [%s - %s]."
                % (min_version.get_string(),
@@ -108,10 +109,12 @@
 
     :param api_microversion_header_name: Microversion header name
             Example- "X-OpenStack-Nova-API-Version"
-    :param api_microversion: Microversion number like "2.10"
+    :param api_microversion: Microversion number like "2.10", type str.
     :param response_header: Response header where microversion is
             expected to be present.
     """
+    if not isinstance(api_microversion, six.string_types):
+        raise TypeError('api_microversion must be a string')
     api_microversion_header_name = api_microversion_header_name.lower()
     if (api_microversion_header_name not in response_header or
         api_microversion != response_header[api_microversion_header_name]):
diff --git a/tempest/lib/common/preprov_creds.py b/tempest/lib/common/preprov_creds.py
index fcdeb17..1011504 100644
--- a/tempest/lib/common/preprov_creds.py
+++ b/tempest/lib/common/preprov_creds.py
@@ -273,7 +273,7 @@
             # NOTE(andreaf) Not all fields may be available on all credentials
             # so defaulting to None for that case.
             if all([getattr(creds, k, None) == hash_attributes.get(k, None) for
-                   k in init_attributes]):
+                    k in init_attributes]):
                 return _hash
         raise AttributeError('Invalid credentials %s' % creds)
 
diff --git a/tempest/lib/common/profiler.py b/tempest/lib/common/profiler.py
new file mode 100644
index 0000000..1544337
--- /dev/null
+++ b/tempest/lib/common/profiler.py
@@ -0,0 +1,64 @@
+#    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 base64
+import hashlib
+import hmac
+import json
+
+from oslo_utils import encodeutils
+from oslo_utils import uuidutils
+
+_profiler = {}
+
+
+def enable(profiler_key, trace_id=None):
+    """Enable global profiler instance
+
+    :param profiler_key: the secret key used to enable profiling in services
+    :param trace_id: unique id of the trace, if empty the id is generated
+        automatically
+    """
+    _profiler['key'] = profiler_key
+    _profiler['uuid'] = trace_id or uuidutils.generate_uuid()
+
+
+def disable():
+    """Disable global profiler instance"""
+    _profiler.clear()
+
+
+def serialize_as_http_headers():
+    """Serialize profiler state as HTTP headers
+
+    This function corresponds to the one from osprofiler library.
+    :return: dictionary with 2 keys `X-Trace-Info` and `X-Trace-HMAC`.
+    """
+    p = _profiler
+    if not p:  # profiler is not enabled
+        return {}
+
+    info = {'base_id': p['uuid'], 'parent_id': p['uuid']}
+    trace_info = base64.urlsafe_b64encode(
+        encodeutils.to_utf8(json.dumps(info)))
+    trace_hmac = _sign(trace_info, p['key'])
+
+    return {
+        'X-Trace-Info': trace_info,
+        'X-Trace-HMAC': trace_hmac,
+    }
+
+
+def _sign(trace_info, key):
+    h = hmac.new(encodeutils.to_utf8(key), digestmod=hashlib.sha1)
+    h.update(trace_info)
+    return h.hexdigest()
diff --git a/tempest/lib/common/rest_client.py b/tempest/lib/common/rest_client.py
index 3be441e..f076727 100644
--- a/tempest/lib/common/rest_client.py
+++ b/tempest/lib/common/rest_client.py
@@ -27,6 +27,7 @@
 
 from tempest.lib.common import http
 from tempest.lib.common import jsonschema_validator
+from tempest.lib.common import profiler
 from tempest.lib.common.utils import test_utils
 from tempest.lib import exceptions
 
@@ -131,8 +132,10 @@
             accept_type = 'json'
         if send_type is None:
             send_type = 'json'
-        return {'Content-Type': 'application/%s' % send_type,
-                'Accept': 'application/%s' % accept_type}
+        headers = {'Content-Type': 'application/%s' % send_type,
+                   'Accept': 'application/%s' % accept_type}
+        headers.update(profiler.serialize_as_http_headers())
+        return headers
 
     def __str__(self):
         STRING_LIMIT = 80
diff --git a/tempest/lib/common/utils/data_utils.py b/tempest/lib/common/utils/data_utils.py
index 3483c51..7f94612 100644
--- a/tempest/lib/common/utils/data_utils.py
+++ b/tempest/lib/common/utils/data_utils.py
@@ -170,7 +170,7 @@
     :rtype: string
     """
     return b''.join([six.int2byte(random.randint(0, 255))
-                    for i in range(size)])
+                     for i in range(size)])
 
 
 # Courtesy of http://stackoverflow.com/a/312464
diff --git a/tempest/lib/decorators.py b/tempest/lib/decorators.py
index 4064401..808e0fb 100644
--- a/tempest/lib/decorators.py
+++ b/tempest/lib/decorators.py
@@ -154,3 +154,45 @@
         return f
 
     return decorator
+
+
+def unstable_test(*args, **kwargs):
+    """A decorator useful to run tests hitting known bugs and skip it if fails
+
+    This decorator can be used in cases like:
+
+    * We have skipped tests with some bug and now bug is claimed to be fixed.
+      Now we want to check the test stability so we use this decorator.
+      The number of skipped cases with that bug can be counted to mark test
+      stable again.
+    * There is test which is failing often, but not always. If there is known
+      bug related to it, and someone is working on fix, this decorator can be
+      used instead of "skip_because". That will ensure that test is still run
+      so new debug data can be collected from jobs' logs but it will not make
+      life of other developers harder by forcing them to recheck jobs more
+      often.
+
+    ``bug`` must be a number 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'
+    :raises: testtools.TestCase.skipException if test actually fails,
+        and ``bug`` is included
+    """
+    def decor(f):
+        @functools.wraps(f)
+        def inner(self, *func_args, **func_kwargs):
+            try:
+                return f(self, *func_args, **func_kwargs)
+            except Exception as e:
+                if "bug" in kwargs:
+                    bug = kwargs['bug']
+                    bug_type = kwargs.get('bug_type', 'launchpad')
+                    bug_url = _get_bug_url(bug, bug_type)
+                    msg = ("Marked as unstable and skipped because of bug: "
+                           "%s, failure was: %s") % (bug_url, e)
+                    raise testtools.TestCase.skipException(msg)
+                else:
+                    raise e
+        return inner
+    return decor
diff --git a/tempest/lib/services/compute/flavors_client.py b/tempest/lib/services/compute/flavors_client.py
index 2fad0a4..5d2dd46 100644
--- a/tempest/lib/services/compute/flavors_client.py
+++ b/tempest/lib/services/compute/flavors_client.py
@@ -172,7 +172,7 @@
         https://developer.openstack.org/api-ref/compute/#show-an-extra-spec-for-a-flavor
         """
         resp, body = self.get('flavors/%s/os-extra_specs/%s' % (flavor_id,
-                              key))
+                                                                key))
         body = json.loads(body)
         self.validate_response(
             schema_extra_specs.set_get_flavor_extra_specs_key,
diff --git a/tempest/lib/services/compute/servers_client.py b/tempest/lib/services/compute/servers_client.py
index 9eed4b3..f2270f8 100644
--- a/tempest/lib/services/compute/servers_client.py
+++ b/tempest/lib/services/compute/servers_client.py
@@ -33,6 +33,8 @@
 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_70 import servers as schemav270
+from tempest.lib.api_schema.response.compute.v2_71 import servers as schemav271
 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
@@ -55,7 +57,9 @@
         {'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}]
+        {'min': '2.63', 'max': '2.69', 'schema': schemav263},
+        {'min': '2.70', 'max': '2.70', 'schema': schemav270},
+        {'min': '2.71', 'max': None, 'schema': schemav271}]
 
     def __init__(self, auth_provider, service, region,
                  enable_instance_password=True, **kwargs):
@@ -426,6 +430,7 @@
         resp, body = self.post('servers/%s/os-volume_attachments' % server_id,
                                post_body)
         body = json.loads(body)
+        schema = self.get_schema(self.schema_versions_info)
         self.validate_response(schema.attach_volume, resp, body)
         return rest_client.ResponseBody(resp, body)
 
@@ -460,6 +465,7 @@
         resp, body = self.get('servers/%s/os-volume_attachments/%s' % (
             server_id, volume_id))
         body = json.loads(body)
+        schema = self.get_schema(self.schema_versions_info)
         self.validate_response(schema.show_volume_attachment, resp, body)
         return rest_client.ResponseBody(resp, body)
 
@@ -473,6 +479,7 @@
         resp, body = self.get('servers/%s/os-volume_attachments' % (
             server_id))
         body = json.loads(body)
+        schema = self.get_schema(self.schema_versions_info)
         self.validate_response(schema.list_volume_attachments, resp, body)
         return rest_client.ResponseBody(resp, body)
 
@@ -636,7 +643,7 @@
     def list_virtual_interfaces(self, server_id):
         """List the virtual interfaces used in an instance."""
         resp, body = self.get('/'.join(['servers', server_id,
-                              'os-virtual-interfaces']))
+                                        'os-virtual-interfaces']))
         body = json.loads(body)
         self.validate_response(schema.list_virtual_interfaces, resp, body)
         return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/network/__init__.py b/tempest/lib/services/network/__init__.py
index 419e593..69f178e 100644
--- a/tempest/lib/services/network/__init__.py
+++ b/tempest/lib/services/network/__init__.py
@@ -21,6 +21,9 @@
     MeteringLabelsClient
 from tempest.lib.services.network.networks_client import NetworksClient
 from tempest.lib.services.network.ports_client import PortsClient
+from tempest.lib.services.network.qos_client import QosClient
+from tempest.lib.services.network.qos_minimum_bandwidth_rules_client import \
+    QosMinimumBandwidthRulesClient
 from tempest.lib.services.network.quotas_client import QuotasClient
 from tempest.lib.services.network.routers_client import RoutersClient
 from tempest.lib.services.network.security_group_rules_client import \
@@ -37,6 +40,7 @@
 __all__ = ['AgentsClient', 'ExtensionsClient', 'FloatingIPsClient',
            'MeteringLabelRulesClient', 'MeteringLabelsClient',
            'NetworksClient', 'NetworkVersionsClient', 'PortsClient',
-           'QuotasClient', 'RoutersClient', 'SecurityGroupRulesClient',
-           'SecurityGroupsClient', 'ServiceProvidersClient',
-           'SubnetpoolsClient', 'SubnetsClient', 'TagsClient']
+           'QosClient', 'QosMinimumBandwidthRulesClient', 'QuotasClient',
+           'RoutersClient', 'SecurityGroupRulesClient', 'SecurityGroupsClient',
+           'ServiceProvidersClient', 'SubnetpoolsClient', 'SubnetsClient',
+           'TagsClient']
diff --git a/tempest/lib/services/network/qos_client.py b/tempest/lib/services/network/qos_client.py
new file mode 100644
index 0000000..bcd1066
--- /dev/null
+++ b/tempest/lib/services/network/qos_client.py
@@ -0,0 +1,70 @@
+# Copyright (c) 2019 Ericsson
+#
+#    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.network import base
+
+
+class QosClient(base.BaseNetworkClient):
+
+    def create_qos_policy(self, **kwargs):
+        """Creates a QoS policy.
+
+        For full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/network/v2/index.html#create-qos-policy
+        """
+        uri = '/qos/policies'
+        post_data = {'policy': kwargs}
+        return self.create_resource(uri, post_data)
+
+    def update_qos_policy(self, qos_policy_id, **kwargs):
+        """Updates a QoS policy.
+
+        For full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/network/v2/index.html#update-qos-policy
+        """
+        uri = '/qos/policies/%s' % qos_policy_id
+        post_data = {'policy': kwargs}
+        return self.update_resource(uri, post_data)
+
+    def show_qos_policy(self, qos_policy_id, **fields):
+        """Show details of a QoS policy.
+
+        For full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/network/v2/index.html#show-qos-policy-details
+        """
+        uri = '/qos/policies/%s' % qos_policy_id
+        return self.show_resource(uri, **fields)
+
+    def delete_qos_policy(self, qos_policy_id):
+        """Deletes a QoS policy.
+
+        For full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/network/v2/index.html#delete-qos-policy
+        """
+        uri = '/qos/policies/%s' % qos_policy_id
+        return self.delete_resource(uri)
+
+    def list_qos_policies(self, **filters):
+        """Lists QoS policies.
+
+        For full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/network/v2/index.html#list-qos-policies
+        """
+        uri = '/qos/policies'
+        return self.list_resources(uri, **filters)
diff --git a/tempest/lib/services/network/qos_minimum_bandwidth_rules_client.py b/tempest/lib/services/network/qos_minimum_bandwidth_rules_client.py
new file mode 100644
index 0000000..4f4ee3f
--- /dev/null
+++ b/tempest/lib/services/network/qos_minimum_bandwidth_rules_client.py
@@ -0,0 +1,73 @@
+# Copyright (c) 2019 Ericsson
+#
+#    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.network import base
+
+
+class QosMinimumBandwidthRulesClient(base.BaseNetworkClient):
+
+    def create_minimum_bandwidth_rule(self, qos_policy_id, **kwargs):
+        """Creates a minimum bandwidth rule for a QoS policy.
+
+        For full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/network/v2/index.html#create-minimum-bandwidth-rule
+        """
+        uri = '/qos/policies/%s/minimum_bandwidth_rules' % qos_policy_id
+        post_data = {'minimum_bandwidth_rule': kwargs}
+        return self.create_resource(uri, post_data)
+
+    def update_minimum_bandwidth_rule(self, qos_policy_id, rule_id, **kwargs):
+        """Updates a minimum bandwidth rule.
+
+        For full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/network/v2/index.html#update-minimum-bandwidth-rule
+        """
+        uri = '/qos/policies/%s/minimum_bandwidth_rules/%s' % (
+            qos_policy_id, rule_id)
+        post_data = {'minimum_bandwidth_rule': kwargs}
+        return self.update_resource(uri, post_data)
+
+    def show_minimum_bandwidth_rule(self, qos_policy_id, rule_id, **fields):
+        """Show details of a minimum bandwidth rule.
+
+        For full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/network/v2/index.html#show-minimum-bandwidth-rule-details
+        """
+        uri = '/qos/policies/%s/minimum_bandwidth_rules/%s' % (
+            qos_policy_id, rule_id)
+        return self.show_resource(uri, **fields)
+
+    def delete_minimum_bandwidth_rule(self, qos_policy_id, rule_id):
+        """Deletes a minimum bandwidth rule for a QoS policy.
+
+        For full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/network/v2/index.html#delete-minimum-bandwidth-rule
+        """
+        uri = '/qos/policies/%s/minimum_bandwidth_rules/%s' % (
+            qos_policy_id, rule_id)
+        return self.delete_resource(uri)
+
+    def list_minimum_bandwidth_rules(self, qos_policy_id, **filters):
+        """Lists all minimum bandwidth rules for a QoS policy.
+
+        For full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/network/v2/index.html#list-minimum-bandwidth-rules-for-qos-policy
+        """
+        uri = '/qos/policies/%s/minimum_bandwidth_rules' % qos_policy_id
+        return self.list_resources(uri, **filters)
diff --git a/tempest/lib/services/volume/v3/volumes_client.py b/tempest/lib/services/volume/v3/volumes_client.py
index fec2950..2dbdd11 100644
--- a/tempest/lib/services/volume/v3/volumes_client.py
+++ b/tempest/lib/services/volume/v3/volumes_client.py
@@ -35,6 +35,16 @@
             return params
         return urllib.urlencode(params)
 
+    def list_hosts(self):
+        """Lists all hosts summary info that is not disabled.
+
+        https://developer.openstack.org/api-ref/block-storage/v3/index.html#list-all-hosts-for-a-project
+        """
+        resp, body = self.get('os-hosts')
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
     def list_volumes(self, detail=False, params=None):
         """List all the volumes created.
 
@@ -55,6 +65,19 @@
         self.expected_success(200, resp.status)
         return rest_client.ResponseBody(resp, body)
 
+    def migrate_volume(self, volume_id, **kwargs):
+        """Migrate a volume to a new backend
+
+        For a full list of available parameters please refer to the offical
+        API reference:
+
+        https://developer.openstack.org/api-ref/block-storage/v3/index.html#migrate-a-volume
+        """
+        post_body = json.dumps({'os-migrate_volume': 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 show_volume(self, volume_id):
         """Returns the details of a single volume."""
         url = "volumes/%s" % volume_id
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index dbb9acc..87d7e76 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -28,6 +28,8 @@
 from tempest.common import waiters
 from tempest import config
 from tempest import exceptions
+from tempest.lib.common import api_microversion_fixture
+from tempest.lib.common import api_version_utils
 from tempest.lib.common.utils import data_utils
 from tempest.lib.common.utils import test_utils
 from tempest.lib import exceptions as lib_exc
@@ -37,12 +39,57 @@
 
 LOG = log.getLogger(__name__)
 
+LATEST_MICROVERSION = 'latest'
+
 
 class ScenarioTest(tempest.test.BaseTestCase):
     """Base class for scenario tests. Uses tempest own clients. """
 
     credentials = ['primary']
 
+    compute_min_microversion = None
+    compute_max_microversion = LATEST_MICROVERSION
+    volume_min_microversion = None
+    volume_max_microversion = LATEST_MICROVERSION
+    placement_min_microversion = None
+    placement_max_microversion = LATEST_MICROVERSION
+
+    @classmethod
+    def skip_checks(cls):
+        super(ScenarioTest, cls).skip_checks()
+        api_version_utils.check_skip_with_microversion(
+            cls.compute_min_microversion, cls.compute_max_microversion,
+            CONF.compute.min_microversion, CONF.compute.max_microversion)
+        api_version_utils.check_skip_with_microversion(
+            cls.volume_min_microversion, cls.volume_max_microversion,
+            CONF.volume.min_microversion, CONF.volume.max_microversion)
+        api_version_utils.check_skip_with_microversion(
+            cls.placement_min_microversion, cls.placement_max_microversion,
+            CONF.placement.min_microversion, CONF.placement.max_microversion)
+
+    @classmethod
+    def resource_setup(cls):
+        super(ScenarioTest, cls).resource_setup()
+        cls.compute_request_microversion = (
+            api_version_utils.select_request_microversion(
+                cls.compute_min_microversion,
+                CONF.compute.min_microversion))
+        cls.volume_request_microversion = (
+            api_version_utils.select_request_microversion(
+                cls.volume_min_microversion,
+                CONF.volume.min_microversion))
+        cls.placement_request_microversion = (
+            api_version_utils.select_request_microversion(
+                cls.placement_min_microversion,
+                CONF.placement.min_microversion))
+
+    def setUp(self):
+        super(ScenarioTest, self).setUp()
+        self.useFixture(api_microversion_fixture.APIMicroversionFixture(
+            compute_microversion=self.compute_request_microversion,
+            volume_microversion=self.volume_request_microversion,
+            placement_microversion=self.placement_request_microversion))
+
     @classmethod
     def setup_clients(cls):
         super(ScenarioTest, cls).setup_clients()
@@ -125,6 +172,27 @@
         returns a test server. The purpose of this wrapper is to minimize
         the impact on the code of the tests already using this
         function.
+
+        :param **kwargs:
+            See extra parameters below
+
+        :Keyword Arguments:
+            * *vnic_type* (``string``) --
+              used when launching instances with pre-configured ports.
+              Examples:
+                normal: a traditional virtual port that is either attached
+                        to a linux bridge or an openvswitch bridge on a
+                        compute node.
+                direct: an SR-IOV port that is directly attached to a VM
+                macvtap: an SR-IOV port that is attached to a VM via a macvtap
+                         device.
+              Defaults to ``CONF.network.port_vnic_type``.
+            * *port_profile* (``dict``) --
+              This attribute is a dictionary that can be used (with admin
+              credentials) to supply information influencing the binding of
+              the port.
+              example: port_profile = "capabilities:[switchdev]"
+              Defaults to ``CONF.network.port_profile``.
         """
 
         # NOTE(jlanoux): As a first step, ssh checks in the scenario
@@ -143,8 +211,8 @@
         if name is None:
             name = data_utils.rand_name(self.__class__.__name__ + "-server")
 
-        vnic_type = CONF.network.port_vnic_type
-        profile = CONF.network.port_profile
+        vnic_type = kwargs.pop('vnic_type', CONF.network.port_vnic_type)
+        profile = kwargs.pop('port_profile', CONF.network.port_profile)
 
         # If vnic_type or profile are configured create port for
         # every network
@@ -166,7 +234,7 @@
                         clients.security_groups_client.list_security_groups(
                         ).get('security_groups')
                     sec_dict = dict([(s['name'], s['id'])
-                                    for s in security_groups])
+                                     for s in security_groups])
 
                     sec_groups_names = [s['name'] for s in kwargs.pop(
                         'security_groups')]
@@ -303,11 +371,32 @@
             snapshot['id'])['snapshot']
         return snapshot
 
+    def _cleanup_volume_type(self, volume_type):
+        """Clean up a given volume type.
+
+        Ensuring all volumes associated to a type are first removed before
+        attempting to remove the type itself. This includes any image volume
+        cache volumes stored in a separate tenant to the original volumes
+        created from the type.
+        """
+        admin_volume_type_client = self.os_admin.volume_types_client_latest
+        admin_volumes_client = self.os_admin.volumes_client_latest
+        volumes = admin_volumes_client.list_volumes(
+            detail=True, params={'all_tenants': 1})['volumes']
+        type_name = volume_type['name']
+        for volume in [v for v in volumes if v['volume_type'] == type_name]:
+            test_utils.call_and_ignore_notfound_exc(
+                admin_volumes_client.delete_volume, volume['id'])
+            admin_volumes_client.wait_for_resource_deletion(volume['id'])
+        admin_volume_type_client.delete_volume_type(volume_type['id'])
+
     def create_volume_type(self, client=None, name=None, backend_name=None):
         if not client:
             client = self.os_admin.volume_types_client_latest
-        randomized_name = name or data_utils.rand_name(
-            'volume-type-' + self.__class__.__name__)
+        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)
 
         LOG.debug("Creating a volume type: %s on backend %s",
                   randomized_name, backend_name)
@@ -317,7 +406,7 @@
 
         volume_type = client.create_volume_type(
             name=randomized_name, extra_specs=extra_specs)['volume_type']
-        self.addCleanup(client.delete_volume_type, volume_type['id'])
+        self.addCleanup(self._cleanup_volume_type, volume_type)
         return volume_type
 
     def _create_loginable_secgroup_rule(self, secgroup_id=None):
@@ -937,24 +1026,33 @@
             raise
 
     def check_remote_connectivity(self, source, dest, should_succeed=True,
-                                  nic=None):
-        """assert ping server via source ssh connection
+                                  nic=None, protocol='icmp'):
+        """check server connectivity via source ssh connection
 
-        :param source: RemoteClient: an ssh connection from which to ping
-        :param dest: an IP to ping against
-        :param should_succeed: boolean: should ping succeed or not
-        :param nic: specific network interface to ping from
+        :param source: RemoteClient: an ssh connection from which to execute
+            the check
+        :param dest: an IP to check connectivity against
+        :param should_succeed: boolean should connection succeed or not
+        :param nic: specific network interface to test connectivity from
+        :param protocol: the protocol used to test connectivity with.
+        :returns: True, if the connection succeeded and it was expected to
+            succeed. False otherwise.
         """
-        def ping_remote():
+        method_name = '%s_check' % protocol
+        connectivity_checker = getattr(source, method_name)
+
+        def connect_remote():
             try:
-                source.ping_host(dest, nic=nic)
+                connectivity_checker(dest, nic=nic)
             except lib_exc.SSHExecCommandFailed:
-                LOG.warning('Failed to ping IP: %s via a ssh connection '
-                            'from: %s.', dest, source.ssh_client.host)
+                LOG.warning('Failed to check %(protocol)s connectivity for '
+                            'IP %(dest)s via a ssh connection from: %(src)s.',
+                            dict(protocol=protocol, dest=dest,
+                                 src=source.ssh_client.host))
                 return not should_succeed
             return should_succeed
 
-        result = test_utils.call_until_true(ping_remote,
+        result = test_utils.call_until_true(connect_remote,
                                             CONF.validation.ping_timeout, 1)
         if result:
             return
diff --git a/tempest/scenario/test_minimum_basic.py b/tempest/scenario/test_minimum_basic.py
index 2b35e45..cee543b 100644
--- a/tempest/scenario/test_minimum_basic.py
+++ b/tempest/scenario/test_minimum_basic.py
@@ -48,6 +48,7 @@
     10. Check SSH connection to instance after reboot
 
     """
+
     def nova_show(self, server):
         got_server = (self.servers_client.show_server(server['id'])
                       ['server'])
diff --git a/tempest/scenario/test_network_basic_ops.py b/tempest/scenario/test_network_basic_ops.py
index 7992585..f46c7e8 100644
--- a/tempest/scenario/test_network_basic_ops.py
+++ b/tempest/scenario/test_network_basic_ops.py
@@ -292,11 +292,14 @@
                                               % CONF.network.build_timeout)
 
         _, new_nic = self.diff_list[0]
-        ssh_client.exec_command("sudo ip addr add %s/%s dev %s" % (
-                                new_port['fixed_ips'][0]['ip_address'],
-                                CONF.network.project_network_mask_bits,
-                                new_nic))
-        ssh_client.exec_command("sudo ip link set %s up" % new_nic)
+        ip_output = ssh_client.exec_command('ip a')
+        ip_address = new_port['fixed_ips'][0]['ip_address']
+        ip_mask = CONF.network.project_network_mask_bits
+        # check if the address is not already in use, if not, set it
+        if ' ' + ip_address + '/' + str(ip_mask) not in ip_output:
+            ssh_client.exec_command("sudo ip addr add %s/%s dev %s" % (
+                                    ip_address, ip_mask, new_nic))
+            ssh_client.exec_command("sudo ip link set %s up" % new_nic)
 
     def _get_server_nics(self, ssh_client):
         reg = re.compile(r'(?P<num>\d+): (?P<nic_name>\w+)[@]?.*:')
diff --git a/tempest/scenario/test_network_v6.py b/tempest/scenario/test_network_v6.py
index 438ee01..8de6614 100644
--- a/tempest/scenario/test_network_v6.py
+++ b/tempest/scenario/test_network_v6.py
@@ -166,9 +166,10 @@
         if self._sysconfig_network_scripts_dir_exists(ssh):
             try:
                 ssh.exec_command(
-                    'echo -e "DEVICE=%(nic)s\\nIPV6INIT=yes" | '
+                    'echo -e "DEVICE=%(nic)s\\nNAME=%(nic)s\\nIPV6INIT=yes" | '
                     'sudo tee /etc/sysconfig/network-scripts/ifcfg-%(nic)s; '
-                    'sudo /sbin/service network restart' % {'nic': nic})
+                    'sudo nmcli connection reload' % {'nic': nic})
+                ssh.exec_command('sudo nmcli connection up %s' % nic)
             except exceptions.SSHExecCommandFailed as e:
                 # NOTE(slaweq): Sometimes it can happen that this SSH command
                 # will fail because of some error from network manager in
diff --git a/tempest/scenario/test_security_groups_basic_ops.py b/tempest/scenario/test_security_groups_basic_ops.py
index 2b7926a..9cbd831 100644
--- a/tempest/scenario/test_security_groups_basic_ops.py
+++ b/tempest/scenario/test_security_groups_basic_ops.py
@@ -395,24 +395,22 @@
             self.check_remote_connectivity(source=access_point_ssh,
                                            dest=self._get_server_ip(server))
 
-    def _test_cross_tenant_block(self, source_tenant, dest_tenant):
+    def _test_cross_tenant_block(self, source_tenant, dest_tenant, ruleset):
         # if public router isn't defined, then dest_tenant access is via
         # floating-ip
+        protocol = ruleset['protocol']
         access_point_ssh = self._connect_to_access_point(source_tenant)
         ip = self._get_server_ip(dest_tenant.access_point,
                                  floating=self.floating_ip_access)
         self.check_remote_connectivity(source=access_point_ssh, dest=ip,
-                                       should_succeed=False)
+                                       should_succeed=False, protocol=protocol)
 
-    def _test_cross_tenant_allow(self, source_tenant, dest_tenant):
+    def _test_cross_tenant_allow(self, source_tenant, dest_tenant, ruleset):
         """check for each direction:
 
         creating rule for tenant incoming traffic enables only 1way traffic
         """
-        ruleset = dict(
-            protocol='icmp',
-            direction='ingress'
-        )
+        protocol = ruleset['protocol']
         sec_group_rules_client = (
             dest_tenant.manager.security_group_rules_client)
         self._create_security_group_rule(
@@ -423,10 +421,10 @@
         access_point_ssh = self._connect_to_access_point(source_tenant)
         ip = self._get_server_ip(dest_tenant.access_point,
                                  floating=self.floating_ip_access)
-        self.check_remote_connectivity(access_point_ssh, ip)
+        self.check_remote_connectivity(access_point_ssh, ip, protocol=protocol)
 
         # test that reverse traffic is still blocked
-        self._test_cross_tenant_block(dest_tenant, source_tenant)
+        self._test_cross_tenant_block(dest_tenant, source_tenant, ruleset)
 
         # allow reverse traffic and check
         sec_group_rules_client = (
@@ -440,7 +438,8 @@
         access_point_ssh_2 = self._connect_to_access_point(dest_tenant)
         ip = self._get_server_ip(source_tenant.access_point,
                                  floating=self.floating_ip_access)
-        self.check_remote_connectivity(access_point_ssh_2, ip)
+        self.check_remote_connectivity(access_point_ssh_2, ip,
+                                       protocol=protocol)
 
     def _verify_mac_addr(self, tenant):
         """Verify that VM has the same ip, mac as listed in port"""
@@ -470,6 +469,17 @@
                 self._log_console_output(
                     servers=[tenant.access_point], client=client)
 
+    def _create_protocol_ruleset(self, protocol, port=80):
+        if protocol == 'icmp':
+            ruleset = dict(protocol='icmp',
+                           direction='ingress')
+        else:
+            ruleset = dict(protocol=protocol,
+                           port_range_min=port,
+                           port_range_max=port,
+                           direction='ingress')
+        return ruleset
+
     @decorators.idempotent_id('e79f879e-debb-440c-a7e4-efeda05b6848')
     @utils.services('compute', 'network')
     def test_cross_tenant_traffic(self):
@@ -484,8 +494,18 @@
             # cross tenant check
             source_tenant = self.primary_tenant
             dest_tenant = self.alt_tenant
-            self._test_cross_tenant_block(source_tenant, dest_tenant)
-            self._test_cross_tenant_allow(source_tenant, dest_tenant)
+
+            protocol = CONF.scenario.protocol
+            LOG.debug("Testing cross tenant traffic for %s protocol",
+                      protocol)
+            if protocol in ['udp', 'tcp']:
+                for tenant in [source_tenant, dest_tenant]:
+                    access_point = self._connect_to_access_point(tenant)
+                    access_point.nc_listen_host(protocol=protocol)
+
+            ruleset = self._create_protocol_ruleset(protocol)
+            self._test_cross_tenant_block(source_tenant, dest_tenant, ruleset)
+            self._test_cross_tenant_allow(source_tenant, dest_tenant, ruleset)
         except Exception:
             self._log_console_output_for_all_tenants()
             raise
diff --git a/tempest/scenario/test_volume_boot_pattern.py b/tempest/scenario/test_volume_boot_pattern.py
index e5730e7c..6ed7e30 100644
--- a/tempest/scenario/test_volume_boot_pattern.py
+++ b/tempest/scenario/test_volume_boot_pattern.py
@@ -86,7 +86,6 @@
                           'Cinder volume snapshots are disabled')
     @utils.services('compute', 'volume', 'image')
     def test_volume_boot_pattern(self):
-
         """This test case attempts to reproduce the following steps:
 
         * Create in Cinder some bootable volume importing a Glance image
diff --git a/tempest/scenario/test_volume_migrate_attached.py b/tempest/scenario/test_volume_migrate_attached.py
index c54bb38..106500e 100644
--- a/tempest/scenario/test_volume_migrate_attached.py
+++ b/tempest/scenario/test_volume_migrate_attached.py
@@ -33,6 +33,9 @@
      * Write to the volume
      * Perform a cinder retype --on-demand of the volume to type of backend #2
      * Check written content of migrated volume
+     * Check the type of the volume has been updated.
+     * Check the volume is still in-use and the migration was successful.
+     * Check that the same volume is attached to the instance.
     """
 
     credentials = ['primary', 'admin']
@@ -78,7 +81,8 @@
                                         'src_backend': backend_source,
                                         'dst': dest_body['name'],
                                         'dst_backend': backend_dest})
-        return source_body['name'], dest_body['name']
+        return ({'name': source_body['name'], 'host': backend_source},
+                {'name': dest_body['name'], 'host': backend_dest})
 
     def _volume_retype_with_migration(self, volume_id, new_volume_type):
         # NOTE: The 'on-demand' migration requires admin operation, so
@@ -93,7 +97,7 @@
     @decorators.attr(type='slow')
     @decorators.idempotent_id('deadd2c2-beef-4dce-98be-f86765ff311b')
     @utils.services('compute', 'volume')
-    def test_volume_migrate_attached(self):
+    def test_volume_retype_attached(self):
         LOG.info("Creating keypair and security group")
         keypair = self.create_keypair()
         security_group = self._create_security_group()
@@ -104,11 +108,11 @@
 
         # create an instance from volume
         LOG.info("Booting instance from volume")
-        volume_origin = self.create_volume(imageRef=CONF.compute.image_ref,
-                                           volume_type=source_type)
+        volume_id = self.create_volume(imageRef=CONF.compute.image_ref,
+                                       volume_type=source_type['name'])['id']
 
-        instance = self._boot_instance_from_volume(volume_origin['id'],
-                                                   keypair, security_group)
+        instance = self._boot_instance_from_volume(volume_id, keypair,
+                                                   security_group)
 
         # write content to volume on instance
         LOG.info("Setting timestamp in instance %s", instance['id'])
@@ -118,9 +122,11 @@
                                           server=instance)
 
         # retype volume with migration from backend #1 to backend #2
-        LOG.info("Retyping Volume %s to new type %s", volume_origin['id'],
-                 dest_type)
-        self._volume_retype_with_migration(volume_origin['id'], dest_type)
+        LOG.info("Retyping Volume %s to new type %s", volume_id,
+                 dest_type['name'])
+        # This method calls for the retype of the volume before calling a
+        # waiter that asserts that the volume type has changed successfully.
+        self._volume_retype_with_migration(volume_id, dest_type['name'])
 
         # check the content of written file
         LOG.info("Getting timestamp in postmigrated instance %s",
@@ -129,3 +135,82 @@
                                         private_key=keypair['private_key'],
                                         server=instance)
         self.assertEqual(timestamp, timestamp2)
+
+        # Assert that the volume is on the new host, is still in-use and has a
+        # migration_status of success
+        volume = self.admin_volumes_client.show_volume(volume_id)['volume']
+        # dest_type is host@backend, os-vol-host-attr:host is host@backend#type
+        self.assertIn(dest_type['host'], volume['os-vol-host-attr:host'])
+        self.assertEqual('in-use', volume['status'])
+        self.assertEqual('success', volume['migration_status'])
+
+        # Assert that the same volume id is attached to the instance, ensuring
+        # the os-migrate_volume_completion Cinder API has been called.
+        attached_volumes = self.servers_client.list_volume_attachments(
+            instance['id'])['volumeAttachments']
+        self.assertEqual(volume_id, attached_volumes[0]['id'])
+
+    @decorators.attr(type='slow')
+    @decorators.idempotent_id('fe47b1ed-640e-4e3b-a090-200e25607362')
+    @utils.services('compute', 'volume')
+    def test_volume_migrate_attached(self):
+        LOG.info("Creating keypair and security group")
+        keypair = self.create_keypair()
+        security_group = self._create_security_group()
+
+        LOG.info("Creating volume")
+        # Create a unique volume type to avoid using the backend default
+        migratable_type = self.create_volume_type()['name']
+        volume_id = self.create_volume(imageRef=CONF.compute.image_ref,
+                                       volume_type=migratable_type)['id']
+        volume = self.admin_volumes_client.show_volume(volume_id)
+
+        LOG.info("Booting instance from volume")
+        instance = self._boot_instance_from_volume(volume_id, keypair,
+                                                   security_group)
+
+        # Identify the source and destination hosts for the migration
+        src_host = volume['volume']['os-vol-host-attr:host']
+
+        # Select the first c-vol host that isn't hosting the volume as the dest
+        # host['host_name'] should take the format of host@backend.
+        # src_host should take the format of host@backend#type
+        hosts = self.admin_volumes_client.list_hosts()['hosts']
+        for host in hosts:
+            if (host['service'] == 'cinder-volume' and
+                not src_host.startswith(host['host_name'])):
+                dest_host = host['host_name']
+                break
+
+        ip_instance = self.get_server_ip(instance)
+        timestamp = self.create_timestamp(ip_instance,
+                                          private_key=keypair['private_key'],
+                                          server=instance)
+
+        LOG.info("Migrating Volume %s from host %s to host %s",
+                 volume_id, src_host, dest_host)
+        self.admin_volumes_client.migrate_volume(volume_id, host=dest_host)
+
+        # This waiter asserts that the migration_status is success and that
+        # the volume has moved to the dest_host
+        waiters.wait_for_volume_migration(self.admin_volumes_client, volume_id,
+                                          dest_host)
+
+        # check the content of written file
+        LOG.info("Getting timestamp in postmigrated instance %s",
+                 instance['id'])
+        timestamp2 = self.get_timestamp(ip_instance,
+                                        private_key=keypair['private_key'],
+                                        server=instance)
+        self.assertEqual(timestamp, timestamp2)
+
+        # Assert that the volume is in-use
+        volume = self.admin_volumes_client.show_volume(volume_id)['volume']
+        self.assertEqual('in-use', volume['status'])
+
+        # Assert that the same volume id is attached to the instance, ensuring
+        # the os-migrate_volume_completion Cinder API has been called
+        attached_volumes = self.servers_client.list_volume_attachments(
+            instance['id'])['volumeAttachments']
+        attached_volume_id = attached_volumes[0]['id']
+        self.assertEqual(volume_id, attached_volume_id)
diff --git a/tempest/test.py b/tempest/test.py
index c3c58dc..85000b6 100644
--- a/tempest/test.py
+++ b/tempest/test.py
@@ -28,6 +28,7 @@
 from tempest.common import utils
 from tempest import config
 from tempest.lib.common import fixed_network
+from tempest.lib.common import profiler
 from tempest.lib.common import validation_resources as vr
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
@@ -231,6 +232,9 @@
         if CONF.pause_teardown:
             BaseTestCase.insert_pdb_breakpoint()
 
+        if CONF.profiler.key:
+            profiler.disable()
+
     @classmethod
     def insert_pdb_breakpoint(cls):
         """Add pdb breakpoint.
@@ -608,6 +612,8 @@
             self.useFixture(fixtures.LoggerFixture(nuke_handlers=False,
                                                    format=self.log_format,
                                                    level=None))
+        if CONF.profiler.key:
+            profiler.enable(CONF.profiler.key)
 
     @property
     def credentials_provider(self):
diff --git a/tempest/test_discover/plugins.py b/tempest/test_discover/plugins.py
index 9c18052..7a037eb 100644
--- a/tempest/test_discover/plugins.py
+++ b/tempest/test_discover/plugins.py
@@ -179,6 +179,7 @@
     This class is used to manage the lifecycle of external tempest test
     plugins. It provides functions for getting set
     """
+
     def __init__(self):
         self.ext_plugins = stevedore.ExtensionManager(
             'tempest.test_plugins', invoke_on_load=True,
diff --git a/tempest/test_discover/test_discover.py b/tempest/test_discover/test_discover.py
index 330f370..143c6e1 100644
--- a/tempest/test_discover/test_discover.py
+++ b/tempest/test_discover/test_discover.py
@@ -37,7 +37,7 @@
                                            top_level_dir=base_path))
         else:
             suite.addTests(loader.discover(full_test_dir, pattern=pattern,
-                           top_level_dir=base_path))
+                                           top_level_dir=base_path))
 
     plugin_load_tests = ext_plugins.get_plugin_load_tests_tuple()
     if not plugin_load_tests:
diff --git a/tempest/tests/cmd/test_run.py b/tempest/tests/cmd/test_run.py
index 00f8bc5..0e00d94 100644
--- a/tempest/tests/cmd/test_run.py
+++ b/tempest/tests/cmd/test_run.py
@@ -29,6 +29,10 @@
 from tempest.lib.common.utils import data_utils
 from tempest.tests import base
 
+if six.PY2:
+    # Python 2 has not FileNotFoundError exception
+    FileNotFoundError = IOError
+
 DEVNULL = open(os.devnull, 'wb')
 atexit.register(DEVNULL.close)
 
@@ -244,14 +248,22 @@
         # getting set in os environment when some data has passed to
         # set the environment.
 
-        self.run_cmd._set_env("/fakedir/fakefile")
-        self.assertEqual("/fakedir/fakefile", CONF._path)
-        self.assertIn('TEMPEST_CONFIG_DIR', os.environ)
-        self.assertEqual("/fakedir/fakefile",
-                         os.path.join(os.environ['TEMPEST_CONFIG_DIR'],
-                                      os.environ['TEMPEST_CONFIG']))
+        _, path = tempfile.mkstemp()
+        self.addCleanup(os.remove, path)
 
-    def test_tempest_run_set_config_no_path(self):
+        self.run_cmd._set_env(path)
+        self.assertEqual(path, CONF._path)
+        self.assertIn('TEMPEST_CONFIG_DIR', os.environ)
+        self.assertEqual(path, os.path.join(os.environ['TEMPEST_CONFIG_DIR'],
+                                            os.environ['TEMPEST_CONFIG']))
+
+    def test_tempest_run_set_config_no_exist_path(self):
+        path = "fake/path"
+        self.assertRaisesRegex(FileNotFoundError,
+                               'Config file: .* doesn\'t exist',
+                               self.run_cmd._set_env, path)
+
+    def test_tempest_run_no_config_path(self):
         # Note: (mbindlish) This test is created for the bug id: 1783751
         # Checking TEMPEST_CONFIG_DIR and TEMPEST_CONFIG should have no value
         # in os environment when no data has passed to set the environment.
@@ -313,13 +325,15 @@
 
     def test_config_file_specified(self):
         self._setup_test_dirs()
+        _, path = tempfile.mkstemp()
+        self.addCleanup(os.remove, path)
         tempest_run = run.TempestRun(app=mock.Mock(), app_args=mock.Mock())
         parsed_args = mock.Mock()
 
         parsed_args.workspace = None
         parsed_args.state = None
         parsed_args.list_tests = False
-        parsed_args.config_file = '.stestr.conf'
+        parsed_args.config_file = path
 
         with mock.patch('stestr.commands.run_command') as m:
             m.return_value = 0
@@ -341,13 +355,15 @@
 
     def test_config_file_workspace_registered(self):
         self._setup_test_dirs()
+        _, path = tempfile.mkstemp()
+        self.addCleanup(os.remove, path)
         tempest_run = run.TempestRun(app=mock.Mock(), app_args=mock.Mock())
         parsed_args = mock.Mock()
         parsed_args.workspace = self.name
         parsed_args.workspace_path = self.store_file
         parsed_args.state = None
         parsed_args.list_tests = False
-        parsed_args.config_file = '.stestr.conf'
+        parsed_args.config_file = path
 
         with mock.patch('stestr.commands.run_command') as m:
             m.return_value = 0
@@ -406,13 +422,15 @@
     @mock.patch('tempest.cmd.run.TempestRun._init_state')
     def test_no_workspace_config_file_state_true(self, mock_init_state):
         self._setup_test_dirs()
+        _, path = tempfile.mkstemp()
+        self.addCleanup(os.remove, path)
         tempest_run = run.TempestRun(app=mock.Mock(), app_args=mock.Mock())
         parsed_args = mock.Mock()
         parsed_args.workspace = None
         parsed_args.workspace_path = self.store_file
         parsed_args.state = True
         parsed_args.list_tests = False
-        parsed_args.config_file = '.stestr.conf'
+        parsed_args.config_file = path
 
         with mock.patch('stestr.commands.run_command') as m:
             m.return_value = 0
diff --git a/tempest/tests/cmd/test_workspace.py b/tempest/tests/cmd/test_workspace.py
index b4f6c5f..7a6b576 100644
--- a/tempest/tests/cmd/test_workspace.py
+++ b/tempest/tests/cmd/test_workspace.py
@@ -48,7 +48,7 @@
         stdout, stderr = process.communicate()
         return_code = process.returncode
         msg = ("%s failed with:\nstdout: %s\nstderr: %s" % (' '.join(cmd),
-               stdout, stderr))
+                                                            stdout, stderr))
         self.assertEqual(return_code, expected, msg)
 
     def test_run_workspace_list(self):
diff --git a/tempest/tests/common/test_waiters.py b/tempest/tests/common/test_waiters.py
old mode 100644
new mode 100755
index 938d226..02e1c99
--- a/tempest/tests/common/test_waiters.py
+++ b/tempest/tests/common/test_waiters.py
@@ -73,6 +73,25 @@
                                     mock.call(volume_id)])
         mock_sleep.assert_called_once_with(1)
 
+    @mock.patch.object(time, 'sleep')
+    def test_wait_for_volume_status_error_extending(self, mock_sleep):
+        # Tests that the wait method raises VolumeExtendErrorException if
+        # the volume status is 'error_extending'.
+        client = mock.Mock(spec=volumes_client.VolumesClient,
+                           resource_type="volume",
+                           build_interval=1)
+        volume1 = {'volume': {'status': 'extending'}}
+        volume2 = {'volume': {'status': 'error_extending'}}
+        mock_show = mock.Mock(side_effect=(volume1, volume2))
+        client.show_volume = mock_show
+        volume_id = '7532b91e-aa0a-4e06-b3e5-20c0c5ee1caa'
+        self.assertRaises(exceptions.VolumeExtendErrorException,
+                          waiters.wait_for_volume_resource_status,
+                          client, volume_id, 'available')
+        mock_show.assert_has_calls([mock.call(volume_id),
+                                    mock.call(volume_id)])
+        mock_sleep.assert_called_once_with(1)
+
 
 class TestInterfaceWaiters(base.TestCase):
 
@@ -148,3 +167,68 @@
         list_interfaces.assert_has_calls([mock.call('server_id'),
                                           mock.call('server_id')])
         sleep.assert_called_once_with(client.build_interval)
+
+
+class TestVolumeWaiters(base.TestCase):
+    vol_migrating_src_host = {
+        'volume': {'migration_status': 'migrating',
+                   'os-vol-host-attr:host': 'src_host@backend#type'}}
+    vol_migrating_dst_host = {
+        'volume': {'migration_status': 'migrating',
+                   'os-vol-host-attr:host': 'dst_host@backend#type'}}
+    vol_migration_success = {
+        'volume': {'migration_status': 'success',
+                   'os-vol-host-attr:host': 'dst_host@backend#type'}}
+    vol_migration_error = {
+        'volume': {'migration_status': 'error',
+                   'os-vol-host-attr:host': 'src_host@backend#type'}}
+
+    def test_wait_for_volume_migration_timeout(self):
+        show_volume = mock.MagicMock(return_value=self.vol_migrating_src_host)
+        client = mock.Mock(spec=volumes_client.VolumesClient,
+                           resource_type="volume",
+                           build_interval=1,
+                           build_timeout=1,
+                           show_volume=show_volume)
+        self.patch('time.time', side_effect=[0., client.build_timeout + 1.])
+        self.patch('time.sleep')
+        self.assertRaises(lib_exc.TimeoutException,
+                          waiters.wait_for_volume_migration,
+                          client, mock.sentinel.volume_id, 'dst_host')
+
+    def test_wait_for_volume_migration_error(self):
+        show_volume = mock.MagicMock(side_effect=[
+            self.vol_migrating_src_host,
+            self.vol_migrating_src_host,
+            self.vol_migration_error])
+        client = mock.Mock(spec=volumes_client.VolumesClient,
+                           resource_type="volume",
+                           build_interval=1,
+                           build_timeout=1,
+                           show_volume=show_volume)
+        self.patch('time.time', return_value=0.)
+        self.patch('time.sleep')
+        self.assertRaises(lib_exc.TempestException,
+                          waiters.wait_for_volume_migration,
+                          client, mock.sentinel.volume_id, 'dst_host')
+
+    def test_wait_for_volume_migration_success_and_dst(self):
+        show_volume = mock.MagicMock(side_effect=[
+            self.vol_migrating_src_host,
+            self.vol_migrating_dst_host,
+            self.vol_migration_success])
+        client = mock.Mock(spec=volumes_client.VolumesClient,
+                           resource_type="volume",
+                           build_interval=1,
+                           build_timeout=1,
+                           show_volume=show_volume)
+        self.patch('time.time', return_value=0.)
+        self.patch('time.sleep')
+        waiters.wait_for_volume_migration(
+            client, mock.sentinel.volume_id, 'dst_host')
+
+        # Assert that we wait until migration_status is success and dst_host is
+        # part of the returned os-vol-host-attr:host.
+        show_volume.assert_has_calls([mock.call(mock.sentinel.volume_id),
+                                      mock.call(mock.sentinel.volume_id),
+                                      mock.call(mock.sentinel.volume_id)])
diff --git a/tempest/tests/lib/common/test_dynamic_creds.py b/tempest/tests/lib/common/test_dynamic_creds.py
index ebcf5d1..4723458 100644
--- a/tempest/tests/lib/common/test_dynamic_creds.py
+++ b/tempest/tests/lib/common/test_dynamic_creds.py
@@ -109,8 +109,8 @@
             return_value=(rest_client.ResponseBody
                           (200,
                            {'roles': [{'id': id, 'name': name},
-                            {'id': '1', 'name': 'FakeRole'},
-                            {'id': '2', 'name': 'Member'}]}))))
+                                      {'id': '1', 'name': 'FakeRole'},
+                                      {'id': '2', 'name': 'Member'}]}))))
         return roles_fix
 
     def _mock_list_2_roles(self):
@@ -120,8 +120,8 @@
             return_value=(rest_client.ResponseBody
                           (200,
                            {'roles': [{'id': '1234', 'name': 'role1'},
-                            {'id': '1', 'name': 'FakeRole'},
-                            {'id': '12345', 'name': 'role2'}]}))))
+                                      {'id': '1', 'name': 'FakeRole'},
+                                      {'id': '12345', 'name': 'role2'}]}))))
         return roles_fix
 
     def _mock_assign_user_role(self):
diff --git a/tempest/tests/lib/common/test_profiler.py b/tempest/tests/lib/common/test_profiler.py
new file mode 100644
index 0000000..59fa0364
--- /dev/null
+++ b/tempest/tests/lib/common/test_profiler.py
@@ -0,0 +1,63 @@
+#    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
+import testtools
+
+from tempest.lib.common import profiler
+
+
+class TestProfiler(testtools.TestCase):
+
+    def test_serialize(self):
+        key = 'SECRET_KEY'
+        pm = {'key': key, 'uuid': 'ID'}
+
+        with mock.patch('tempest.lib.common.profiler._profiler', pm):
+            with mock.patch('json.dumps') as jdm:
+                jdm.return_value = '{"base_id": "ID", "parent_id": "ID"}'
+
+                expected = {
+                    'X-Trace-HMAC':
+                        '887292df9f13b8b5ecd6bbbd2e16bfaaa4d914b0',
+                    'X-Trace-Info':
+                        b'eyJiYXNlX2lkIjogIklEIiwgInBhcmVudF9pZCI6ICJJRCJ9'
+                }
+
+                self.assertEqual(expected,
+                                 profiler.serialize_as_http_headers())
+
+    def test_profiler_lifecycle(self):
+        key = 'SECRET_KEY'
+        uuid = 'ID'
+
+        self.assertEqual({}, profiler._profiler)
+
+        profiler.enable(key, uuid)
+        self.assertEqual({'key': key, 'uuid': uuid}, profiler._profiler)
+
+        profiler.disable()
+        self.assertEqual({}, profiler._profiler)
+
+    @mock.patch('oslo_utils.uuidutils.generate_uuid')
+    def test_profiler_lifecycle_generate_trace_id(self, generate_uuid_mock):
+        key = 'SECRET_KEY'
+        uuid = 'ID'
+        generate_uuid_mock.return_value = uuid
+
+        self.assertEqual({}, profiler._profiler)
+
+        profiler.enable(key)
+        self.assertEqual({'key': key, 'uuid': uuid}, profiler._profiler)
+
+        profiler.disable()
+        self.assertEqual({}, profiler._profiler)
diff --git a/tempest/tests/lib/services/compute/test_images_client.py b/tempest/tests/lib/services/compute/test_images_client.py
index c2c3b76..d1500e5 100644
--- a/tempest/tests/lib/services/compute/test_images_client.py
+++ b/tempest/tests/lib/services/compute/test_images_client.py
@@ -186,15 +186,19 @@
     def _test_resource_deleted(self, bytes_body=False):
         params = {"id": self.FAKE_IMAGE_ID}
         expected_op = self.FAKE_IMAGE_DATA['show']
-        self.useFixture(fixtures.MockPatch('tempest.lib.services.compute'
-                        '.images_client.ImagesClient.show_image',
-                                           side_effect=lib_exc.NotFound))
+        self.useFixture(
+            fixtures.MockPatch(
+                'tempest.lib.services.compute'
+                '.images_client.ImagesClient.show_image',
+                side_effect=lib_exc.NotFound))
         self.assertEqual(True, self.client.is_resource_deleted(**params))
         tempdata = copy.deepcopy(self.FAKE_IMAGE_DATA['show'])
         tempdata['image']['id'] = None
-        self.useFixture(fixtures.MockPatch('tempest.lib.services.compute'
-                        '.images_client.ImagesClient.show_image',
-                                           return_value=expected_op))
+        self.useFixture(
+            fixtures.MockPatch(
+                'tempest.lib.services.compute'
+                '.images_client.ImagesClient.show_image',
+                return_value=expected_op))
         self.assertEqual(False, self.client.is_resource_deleted(**params))
 
     def test_list_images_with_str_body(self):
diff --git a/tempest/tests/lib/services/network/test_qos_client.py b/tempest/tests/lib/services/network/test_qos_client.py
new file mode 100644
index 0000000..b04b847
--- /dev/null
+++ b/tempest/tests/lib/services/network/test_qos_client.py
@@ -0,0 +1,139 @@
+# Copyright (c) 2019 Ericsson
+#
+#    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.services.network import qos_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestQosClient(base.BaseServiceTest):
+
+    FAKE_QOS_POLICY_ID = "f1011b08-1297-11e9-a1e7-c7e6825a2616"
+
+    FAKE_QOS_POLICY_REQUEST = {
+        'name': 'foo',
+        'shared': True
+    }
+
+    FAKE_QOS_POLICY_RESPONSE = {
+        'policy': {
+            "name": "10Mbit",
+            "description": "This policy limits the ports to 10Mbit max.",
+            "rules": [],
+            "id": FAKE_QOS_POLICY_ID,
+            "is_default": False,
+            "project_id": "8d4c70a21fed4aeba121a1a429ba0d04",
+            "revision_number": 1,
+            "tenant_id": "8d4c70a21fed4aeba121a1a429ba0d04",
+            "created_at": "2018-04-03T21:26:39Z",
+            "updated_at": "2018-04-03T21:26:39Z",
+            "shared": False,
+            "tags": ["tag1,tag2"]
+        }
+    }
+
+    FAKE_QOS_POLICIES = {
+        'policies': [
+            FAKE_QOS_POLICY_RESPONSE['policy']
+        ]
+    }
+
+    def setUp(self):
+        super(TestQosClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.qos_client = qos_client.QosClient(
+            fake_auth, "network", "regionOne")
+
+    def _test_create_qos_policy(self, bytes_body=False):
+        self.check_service_client_function(
+            self.qos_client.create_qos_policy,
+            "tempest.lib.common.rest_client.RestClient.post",
+            self.FAKE_QOS_POLICY_RESPONSE,
+            bytes_body,
+            201,
+            **self.FAKE_QOS_POLICY_REQUEST)
+
+    def _test_list_qos_policies(self, bytes_body=False):
+        self.check_service_client_function(
+            self.qos_client.list_qos_policies,
+            "tempest.lib.common.rest_client.RestClient.get",
+            self.FAKE_QOS_POLICIES,
+            bytes_body,
+            200)
+
+    def _test_show_qos_policy(self, bytes_body=False):
+        self.check_service_client_function(
+            self.qos_client.show_qos_policy,
+            "tempest.lib.common.rest_client.RestClient.get",
+            self.FAKE_QOS_POLICY_RESPONSE,
+            bytes_body,
+            200,
+            qos_policy_id=self.FAKE_QOS_POLICY_ID)
+
+    def _test_update_qos_polcy(self, bytes_body=False):
+        update_kwargs = {
+            "name": "100Mbit",
+            "description": "This policy limits the ports to 100Mbit max.",
+            "shared": True
+        }
+
+        resp_body = {
+            "policy": copy.deepcopy(
+                self.FAKE_QOS_POLICY_RESPONSE['policy']
+            )
+        }
+        resp_body["policy"].update(update_kwargs)
+
+        self.check_service_client_function(
+            self.qos_client.update_qos_policy,
+            "tempest.lib.common.rest_client.RestClient.put",
+            resp_body,
+            bytes_body,
+            200,
+            qos_policy_id=self.FAKE_QOS_POLICY_ID,
+            **update_kwargs)
+
+    def test_create_qos_policy_with_str_body(self):
+        self._test_create_qos_policy()
+
+    def test_create_qos_policy_with_bytes_body(self):
+        self._test_create_qos_policy(bytes_body=True)
+
+    def test_update_qos_policy_with_str_body(self):
+        self._test_update_qos_polcy()
+
+    def test_update_qos_policy_with_bytes_body(self):
+        self._test_update_qos_polcy(bytes_body=True)
+
+    def test_show_qos_policy_with_str_body(self):
+        self._test_show_qos_policy()
+
+    def test_show_qos_policy_with_bytes_body(self):
+        self._test_show_qos_policy(bytes_body=True)
+
+    def test_delete_qos_policy(self):
+        self.check_service_client_function(
+            self.qos_client.delete_qos_policy,
+            "tempest.lib.common.rest_client.RestClient.delete",
+            {},
+            status=204,
+            qos_policy_id=self.FAKE_QOS_POLICY_ID)
+
+    def test_list_qos_policies_with_str_body(self):
+        self._test_list_qos_policies()
+
+    def test_list_qos_policies_with_bytes_body(self):
+        self._test_list_qos_policies(bytes_body=True)
diff --git a/tempest/tests/lib/services/network/test_qos_minimum_bandwidth_rules_client.py b/tempest/tests/lib/services/network/test_qos_minimum_bandwidth_rules_client.py
new file mode 100644
index 0000000..8234dda
--- /dev/null
+++ b/tempest/tests/lib/services/network/test_qos_minimum_bandwidth_rules_client.py
@@ -0,0 +1,137 @@
+# Copyright (c) 2019 Ericsson
+#
+#    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.services.network import qos_minimum_bandwidth_rules_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestQosMinimumBandwidthRulesClient(base.BaseServiceTest):
+
+    FAKE_QOS_POLICY_ID = "f1011b08-1297-11e9-a1e7-c7e6825a2616"
+    FAKE_MIN_BW_RULE_ID = "e758c89e-1297-11e9-a6cf-cf46a71e6699"
+
+    FAKE_MIN_BW_RULE_REQUEST = {
+        'qos_policy_id': FAKE_QOS_POLICY_ID,
+        'min_kbps': 1000,
+        'direction': 'ingress'
+    }
+
+    FAKE_MIN_BW_RULE_RESPONSE = {
+        'minimum_bandwidth_rule': {
+            'id': FAKE_MIN_BW_RULE_ID,
+            'min_kbps': 10000,
+            'direction': 'egress'
+        }
+    }
+
+    FAKE_MIN_BW_RULES = {
+        'bandwidth_limit_rules': [
+            FAKE_MIN_BW_RULE_RESPONSE['minimum_bandwidth_rule']
+        ]
+    }
+
+    def setUp(self):
+        super(TestQosMinimumBandwidthRulesClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.qos_min_bw_client = qos_minimum_bandwidth_rules_client.\
+            QosMinimumBandwidthRulesClient(fake_auth, "network", "regionOne")
+
+    def _test_create_minimum_bandwidth_rule(self, bytes_body=False):
+        self.check_service_client_function(
+            self.qos_min_bw_client.create_minimum_bandwidth_rule,
+            "tempest.lib.common.rest_client.RestClient.post",
+            self.FAKE_MIN_BW_RULE_RESPONSE,
+            bytes_body,
+            201,
+            **self.FAKE_MIN_BW_RULE_REQUEST
+        )
+
+    def _test_list_minimum_bandwidth_rules(self, bytes_body=False):
+        self.check_service_client_function(
+            self.qos_min_bw_client.list_minimum_bandwidth_rules,
+            "tempest.lib.common.rest_client.RestClient.get",
+            self.FAKE_MIN_BW_RULES,
+            bytes_body,
+            200,
+            qos_policy_id=self.FAKE_QOS_POLICY_ID
+        )
+
+    def _test_show_minimum_bandwidth_rule(self, bytes_body=False):
+        self.check_service_client_function(
+            self.qos_min_bw_client.show_minimum_bandwidth_rule,
+            "tempest.lib.common.rest_client.RestClient.get",
+            self.FAKE_MIN_BW_RULE_RESPONSE,
+            bytes_body,
+            200,
+            qos_policy_id=self.FAKE_QOS_POLICY_ID,
+            rule_id=self.FAKE_MIN_BW_RULE_ID
+        )
+
+    def _test_update_qos_polcy(self, bytes_body=False):
+        update_kwargs = {
+            "min_kbps": "20000"
+        }
+
+        resp_body = {
+            "minimum_bandwidth_rule": copy.deepcopy(
+                self.FAKE_MIN_BW_RULE_RESPONSE['minimum_bandwidth_rule']
+            )
+        }
+        resp_body["minimum_bandwidth_rule"].update(update_kwargs)
+
+        self.check_service_client_function(
+            self.qos_min_bw_client.update_minimum_bandwidth_rule,
+            "tempest.lib.common.rest_client.RestClient.put",
+            resp_body,
+            bytes_body,
+            200,
+            qos_policy_id=self.FAKE_QOS_POLICY_ID,
+            rule_id=self.FAKE_MIN_BW_RULE_ID,
+            **update_kwargs)
+
+    def test_create_minimum_bandwidth_rule_with_str_body(self):
+        self._test_create_minimum_bandwidth_rule()
+
+    def test_create_minimum_bandwidth_rule_with_bytes_body(self):
+        self._test_create_minimum_bandwidth_rule(bytes_body=True)
+
+    def test_update_minimum_bandwidth_rule_with_str_body(self):
+        self._test_update_qos_polcy()
+
+    def test_update_minimum_bandwidth_rule_with_bytes_body(self):
+        self._test_update_qos_polcy(bytes_body=True)
+
+    def test_show_minimum_bandwidth_rule_with_str_body(self):
+        self._test_show_minimum_bandwidth_rule()
+
+    def test_show_minimum_bandwidth_rule_with_bytes_body(self):
+        self._test_show_minimum_bandwidth_rule(bytes_body=True)
+
+    def test_delete_minimum_bandwidth_rule(self):
+        self.check_service_client_function(
+            self.qos_min_bw_client.delete_minimum_bandwidth_rule,
+            "tempest.lib.common.rest_client.RestClient.delete",
+            {},
+            status=204,
+            qos_policy_id=self.FAKE_QOS_POLICY_ID,
+            rule_id=self.FAKE_MIN_BW_RULE_ID)
+
+    def test_list_minimum_bandwidth_rule_with_str_body(self):
+        self._test_list_minimum_bandwidth_rules()
+
+    def test_list_minimum_bandwidth_rule_with_bytes_body(self):
+        self._test_list_minimum_bandwidth_rules(bytes_body=True)
diff --git a/tempest/tests/lib/services/registry_fixture.py b/tempest/tests/lib/services/registry_fixture.py
index 1da2112..07af68a 100644
--- a/tempest/tests/lib/services/registry_fixture.py
+++ b/tempest/tests/lib/services/registry_fixture.py
@@ -37,8 +37,9 @@
     def __init__(self):
         """Initialise the registry fixture"""
         self.services = set(['compute', 'identity.v2', 'identity.v3',
-                             'image.v1', 'image.v2', 'network', 'volume.v1',
-                             'volume.v2', 'volume.v3', 'object-storage'])
+                             'image.v1', 'image.v2', 'network', 'placement',
+                             'volume.v1', 'volume.v2', 'volume.v3',
+                             'object-storage'])
 
     def _setUp(self):
         # Cleanup the registry
diff --git a/tempest/tests/lib/services/volume/v3/test_scheduler_stats_client.py b/tempest/tests/lib/services/volume/v3/test_scheduler_stats_client.py
index e0f5566..84c7589 100644
--- a/tempest/tests/lib/services/volume/v3/test_scheduler_stats_client.py
+++ b/tempest/tests/lib/services/volume/v3/test_scheduler_stats_client.py
@@ -62,7 +62,7 @@
             resp_body = self.FAKE_POOLS_LIST
         else:
             resp_body = {'pools': [{'name': pool['name']}
-                         for pool in self.FAKE_POOLS_LIST['pools']]}
+                                   for pool in self.FAKE_POOLS_LIST['pools']]}
         self.check_service_client_function(
             self.client.list_pools,
             'tempest.lib.common.rest_client.RestClient.get',
diff --git a/tempest/tests/lib/test_api_microversion_fixture.py b/tempest/tests/lib/test_api_microversion_fixture.py
new file mode 100644
index 0000000..ad98ed0
--- /dev/null
+++ b/tempest/tests/lib/test_api_microversion_fixture.py
@@ -0,0 +1,58 @@
+# Copyright 2019 NEC 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.common import api_microversion_fixture
+from tempest.lib.services.compute import base_compute_client
+from tempest.lib.services.placement import base_placement_client
+from tempest.lib.services.volume import base_client
+from tempest.tests import base
+
+
+class TestAPIMicroversionFixture(base.TestCase):
+    def setUp(self):
+        super(TestAPIMicroversionFixture, self).setUp()
+        # Verify that all the microversion are reset back to None
+        # by Fixture.
+        self.assertIsNone(base_compute_client.COMPUTE_MICROVERSION)
+        self.assertIsNone(base_client.VOLUME_MICROVERSION)
+        self.assertIsNone(base_placement_client.PLACEMENT_MICROVERSION)
+
+    def test_compute_microversion(self):
+        self.useFixture(api_microversion_fixture.APIMicroversionFixture(
+            compute_microversion='2.10'))
+        self.assertEqual('2.10', base_compute_client.COMPUTE_MICROVERSION)
+        self.assertIsNone(base_client.VOLUME_MICROVERSION)
+        self.assertIsNone(base_placement_client.PLACEMENT_MICROVERSION)
+
+    def test_volume_microversion(self):
+        self.useFixture(api_microversion_fixture.APIMicroversionFixture(
+            volume_microversion='3.10'))
+        self.assertIsNone(base_compute_client.COMPUTE_MICROVERSION)
+        self.assertEqual('3.10', base_client.VOLUME_MICROVERSION)
+        self.assertIsNone(base_placement_client.PLACEMENT_MICROVERSION)
+
+    def test_placement_microversion(self):
+        self.useFixture(api_microversion_fixture.APIMicroversionFixture(
+            placement_microversion='1.10'))
+        self.assertIsNone(base_compute_client.COMPUTE_MICROVERSION)
+        self.assertIsNone(base_client.VOLUME_MICROVERSION)
+        self.assertEqual('1.10', base_placement_client.PLACEMENT_MICROVERSION)
+
+    def test_multiple_service_microversion(self):
+        self.useFixture(api_microversion_fixture.APIMicroversionFixture(
+            compute_microversion='2.10', volume_microversion='3.10',
+            placement_microversion='1.10'))
+        self.assertEqual('2.10', base_compute_client.COMPUTE_MICROVERSION)
+        self.assertEqual('3.10', base_client.VOLUME_MICROVERSION)
+        self.assertEqual('1.10', base_placement_client.PLACEMENT_MICROVERSION)
diff --git a/tempest/tests/lib/test_decorators.py b/tempest/tests/lib/test_decorators.py
index 3e6160e..9c6cac7 100644
--- a/tempest/tests/lib/test_decorators.py
+++ b/tempest/tests/lib/test_decorators.py
@@ -13,7 +13,10 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import abc
+
 import mock
+import six
 import testtools
 
 from tempest.lib import base as test
@@ -66,9 +69,36 @@
                                condition=True)
 
 
-class TestSkipBecauseDecorator(base.TestCase):
-    def _test_skip_because_helper(self, expected_to_skip=True,
-                                  **decorator_args):
+@six.add_metaclass(abc.ABCMeta)
+class BaseSkipDecoratorTests(object):
+
+    @abc.abstractmethod
+    def _test_skip_helper(self, raise_exception=True, expected_to_skip=True,
+                          **decorator_args):
+        return
+
+    def test_skip_launchpad_bug(self):
+        self._test_skip_helper(bug='12345')
+
+    def test_skip_storyboard_bug(self):
+        self._test_skip_helper(bug='1992', bug_type='storyboard')
+
+    def test_skip_bug_without_bug_never_skips(self):
+        """Never skip without a bug parameter."""
+        self._test_skip_helper(
+            raise_exception=False, expected_to_skip=False, condition=True)
+        self._test_skip_helper(
+            raise_exception=False, expected_to_skip=False)
+
+    def test_skip_invalid_bug_number(self):
+        """Raise InvalidParam if with an invalid bug number"""
+        self.assertRaises(lib_exc.InvalidParam, self._test_skip_helper,
+                          bug='critical_bug')
+
+
+class TestSkipBecauseDecorator(base.TestCase, BaseSkipDecoratorTests):
+    def _test_skip_helper(self, raise_exception=True, expected_to_skip=True,
+                          **decorator_args):
         class TestFoo(test.BaseTestCase):
             _interface = 'json'
 
@@ -90,38 +120,56 @@
             # assert that test_bar returned 0
             self.assertEqual(TestFoo('test_bar').test_bar(), 0)
 
-    def test_skip_because_launchpad_bug(self):
-        self._test_skip_because_helper(bug='12345')
-
     def test_skip_because_launchpad_bug_and_condition_true(self):
-        self._test_skip_because_helper(bug='12348', condition=True)
+        self._test_skip_helper(bug='12348', condition=True)
 
     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)
+        self._test_skip_helper(expected_to_skip=False,
+                               bug='12349', condition=False)
 
     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)
+        self._test_skip_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,
-                                       condition=True)
-        self._test_skip_because_helper(expected_to_skip=False)
+    def test_skip_because_storyboard_bug_and_condition_true(self):
+        self._test_skip_helper(bug='1992', bug_type='storyboard',
+                               condition=True)
 
-    def test_skip_because_invalid_bug_number(self):
-        """Raise InvalidParam if with an invalid bug number"""
-        self.assertRaises(lib_exc.InvalidParam, self._test_skip_because_helper,
-                          bug='critical_bug')
+
+class TestUnstableTestDecorator(base.TestCase, BaseSkipDecoratorTests):
+
+    def _test_skip_helper(self, raise_exception=True, expected_to_skip=True,
+                          **decorator_args):
+        fail_test_reason = "test_bar failed"
+
+        class TestFoo(test.BaseTestCase):
+
+            @decorators.unstable_test(**decorator_args)
+            def test_bar(self):
+                if raise_exception:
+                    raise Exception(fail_test_reason)
+                else:
+                    return 0
+
+        t = TestFoo('test_bar')
+        if expected_to_skip:
+            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'Marked as unstable and skipped because of bug\: %s.*, '
+                'failure was: %s' % (decorators._get_bug_url(bug, bug_type),
+                                     fail_test_reason)
+            )
+        else:
+            # assert that test_bar returned 0
+            self.assertEqual(TestFoo('test_bar').test_bar(), 0)
+
+    def test_skip_bug_given_exception_not_raised(self):
+        self._test_skip_helper(raise_exception=False, expected_to_skip=False,
+                               bug='1234')
 
 
 class TestIdempotentIdDecorator(base.TestCase):
diff --git a/tempest/tests/test_hacking.py b/tempest/tests/test_hacking.py
index 9534ce8..83c1abb 100644
--- a/tempest/tests/test_hacking.py
+++ b/tempest/tests/test_hacking.py
@@ -48,6 +48,7 @@
     just assertTrue if the check is expected to fail and assertFalse if it
     should pass.
     """
+
     def test_no_setup_teardown_class_for_tests(self):
         self.assertTrue(checks.no_setup_teardown_class_for_tests(
             "  def setUpClass(cls):", './tempest/tests/fake_test.py'))
diff --git a/tools/format.sh b/tools/format.sh
new file mode 100755
index 0000000..dec8f1c
--- /dev/null
+++ b/tools/format.sh
@@ -0,0 +1,29 @@
+#!/bin/bash
+
+cd $(dirname "$(readlink -f "$0")")
+
+AUTOPEP8=`which autopep8 2>/dev/null`
+
+if [[ -z "$AUTOPEP8" ]]; then
+    AUTOPEP8=`which autopep8-3`
+fi
+
+if [[ -z "$AUTOPEP8" ]]; then
+    echo "Unable to locate autopep8" >&2
+    exit 2
+fi
+
+# isort is not compatible with the default flake8 (H306), maybe flake8-isort
+# isort -rc -sl -fss ../tempest ../setup.py
+$AUTOPEP8 --exit-code --max-line-length=79 --experimental --in-place -r ../tempest ../setup.py
+ERROR=$?
+
+if [[ $ERROR -eq 0 ]]; then
+    echo "Formatting was not needed." >&2
+    exit 0
+elif [[ $ERROR -eq 1 ]]; then
+    echo "Formatting failed.." >&2
+    exit 1
+else
+    echo "done" >&2
+fi
diff --git a/tools/generate-tempest-plugins-list.py b/tools/generate-tempest-plugins-list.py
index 3772774..746cb34 100644
--- a/tools/generate-tempest-plugins-list.py
+++ b/tools/generate-tempest-plugins-list.py
@@ -19,9 +19,9 @@
 #
 # In order to function correctly, the environment in which the
 # script runs must have
-#   * network access to the review.openstack.org Gerrit API
+#   * network access to the review.opendev.org Gerrit API
 #     working directory
-#   * network access to https://git.openstack.org/cgit
+#   * network access to https://opendev.org/openstack
 
 import json
 import re
@@ -36,7 +36,7 @@
     from urllib2 import HTTPError
 
 
-url = 'https://review.openstack.org/projects/'
+url = 'https://review.opendev.org/projects/'
 
 # This is what a project looks like
 '''
@@ -59,7 +59,8 @@
 def has_tempest_plugin(proj):
     try:
         r = urllib.urlopen(
-            "https://git.openstack.org/cgit/%s/plain/setup.cfg" % proj)
+            "https://opendev.org/%s/raw/branch/"
+            "master/setup.cfg" % proj)
     except HTTPError as err:
         if err.code == 404:
             return False
diff --git a/tools/generate-tempest-plugins-list.sh b/tools/generate-tempest-plugins-list.sh
index 111c9ce..b4e5430 100755
--- a/tools/generate-tempest-plugins-list.sh
+++ b/tools/generate-tempest-plugins-list.sh
@@ -28,9 +28,9 @@
 #   * the environment variable git_dir pointing to the location
 #   * of said git repositories
 #   ) OR (
-#   * network access to the review.openstack.org Gerrit API
+#   * network access to the review.opendev.org Gerrit API
 #     working directory
-#   * network access to https://git.openstack.org/cgit
+#   * network access to https://opendev.org/openstack
 #   ))
 #
 # If a file named doc/source/data/tempest-plugins-registry.header or
@@ -69,8 +69,8 @@
 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}"
+    giturl="https://opendev.org/openstack/${plugin}"
+    gitlink="https://opendev.org/openstack/${plugin}"
     printf "%-3s %-${name_col_len}s %s\n" "$i" "${plugin}" "\`${giturl} <${gitlink}>\`__"
 done
 
diff --git a/tools/tempest-plugin-sanity.sh b/tools/tempest-plugin-sanity.sh
index 16e7b8c..47a9ac9 100644
--- a/tools/tempest-plugin-sanity.sh
+++ b/tools/tempest-plugin-sanity.sh
@@ -46,23 +46,23 @@
 # List of projects having tempest plugin stale or unmaintained for a long time
 # (6 months or more)
 # TODO(masayukig): Some of these can be removed from BLACKLIST in the future.
-# airship-tempest-plugin: https://review.openstack.org/#/c/634387/
-# barbican-tempest-plugin: https://review.openstack.org/#/c/634631/
-# intel-nfv-ci-tests: https://review.openstack.org/#/c/634640/
-# networking-ansible: https://review.openstack.org/#/c/634647/
-# networking-generic-switch: https://review.openstack.org/#/c/634846/
-# networking-l2gw-tempest-plugin: https://review.openstack.org/#/c/635093/
-# networking-midonet: https://review.openstack.org/#/c/635096/
-# networking-plumgrid: https://review.openstack.org/#/c/635096/
-# networking-spp: https://review.openstack.org/#/c/635098/
-# neutron-dynamic-routing: https://review.openstack.org/#/c/637718/
-# neutron-vpnaas: https://review.openstack.org/#/c/637719/
-# nova-lxd: https://review.openstack.org/#/c/638334/
-# valet: https://review.openstack.org/#/c/638339/
-# vitrage-tempest-plugin: https://review.openstack.org/#/c/639003/
+# barbican-tempest-plugin: https://review.opendev.org/#/c/634631/
+# cyborg-tempest-plugin: https://review.opendev.org/659687
+# intel-nfv-ci-tests: https://review.opendev.org/#/c/634640/
+# networking-ansible: https://review.opendev.org/#/c/634647/
+# networking-generic-switch: https://review.opendev.org/#/c/634846/
+# networking-l2gw-tempest-plugin: https://review.opendev.org/#/c/635093/
+# networking-midonet: https://review.opendev.org/#/c/635096/
+# networking-plumgrid: https://review.opendev.org/#/c/635096/
+# networking-spp: https://review.opendev.org/#/c/635098/
+# neutron-dynamic-routing: https://review.opendev.org/#/c/637718/
+# neutron-vpnaas: https://review.opendev.org/#/c/637719/
+# nova-lxd: https://review.opendev.org/#/c/638334/
+# valet: https://review.opendev.org/#/c/638339/
+
 BLACKLIST="
-airship-tempest-plugin
 barbican-tempest-plugin
+cyborg-tempest-plugin
 intel-nfv-ci-tests
 networking-ansible
 networking-generic-switch
@@ -74,18 +74,17 @@
 neutron-vpnaas
 nova-lxd
 valet
-vitrage-tempest-plugin
 "
 
 # Function to clone project using zuul-cloner or from git
 function clone_project() {
     if [ -e /usr/zuul-env/bin/zuul-cloner ]; then
         /usr/zuul-env/bin/zuul-cloner --cache-dir /opt/git \
-        git://git.openstack.org \
+        https://opendev.org \
         openstack/"$1"
 
     elif [ -e /usr/bin/git ]; then
-        /usr/bin/git clone git://git.openstack.org/openstack/"$1" \
+        /usr/bin/git clone https://opendev.org/openstack/"$1" \
         openstack/"$1"
 
     fi
@@ -94,7 +93,7 @@
 # function to create virtualenv to perform sanity operation
 function prepare_workspace() {
     SANITY_DIR=$(pwd)
-    virtualenv --clear "$SANITY_DIR"/.venv
+    virtualenv -p python3 --clear "$SANITY_DIR"/.venv
     export TVENV="$SANITY_DIR/tools/with_venv.sh"
     cd "$SANITY_DIR"
 
@@ -152,8 +151,10 @@
     fi
 done
 
+echo "Passed Plugins: $passed_plugin"
+echo "Failed Plugins: $failed_plugin"
+
 # Check for failed status
 if [[ -n $failed_plugin ]]; then
-    echo "Failed Plugins: $failed_plugin"
     exit 1
 fi
diff --git a/tox.ini b/tox.ini
index b565507..291d899 100644
--- a/tox.ini
+++ b/tox.ini
@@ -9,7 +9,7 @@
     VIRTUAL_ENV={envdir}
     OS_TEST_PATH=./tempest/test_discover
 deps =
-    -c{env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt}
+    -c{env:UPPER_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master}
     -r{toxinidir}/requirements.txt
 
 [testenv]
@@ -25,7 +25,7 @@
 install_command = pip install {opts} {packages}
 whitelist_externals = *
 deps =
-    -c{env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt}
+    -c{env:UPPER_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master}
     -r{toxinidir}/requirements.txt
     -r{toxinidir}/test-requirements.txt
 commands =
@@ -62,7 +62,7 @@
 deps = {[tempestenv]deps}
 commands =
     find . -type f -name "*.pyc" -delete
-    tempest run --regex {posargs}
+    tempest run --regex {posargs:''}
 
 [testenv:all-plugin]
 # DEPRECATED
@@ -82,7 +82,7 @@
     echo "WARNING: The all-plugin env is deprecated and will be removed"
     echo "WARNING  Please use the 'all' environment for Tempest plugins."
     find . -type f -name "*.pyc" -delete
-    tempest run --regex {posargs}
+    tempest run --regex {posargs:''}
 
 [testenv:all-site-packages]
 sitepackages = True
@@ -93,7 +93,7 @@
 deps = {[tempestenv]deps}
 commands =
     find . -type f -name "*.pyc" -delete
-    tempest run --regex {posargs}
+    tempest run --regex {posargs:''}
 
 [testenv:full]
 envdir = .tox/tempest
@@ -173,7 +173,7 @@
 
 [testenv:venv]
 deps =
-  -c{env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt}
+  -c{env:UPPER_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master}
   -r{toxinidir}/requirements.txt
   -r{toxinidir}/doc/requirements.txt
 commands = {posargs}
@@ -188,7 +188,7 @@
 [testenv:docs]
 basepython = python3
 deps =
-  -c{env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt}
+  -c{env:UPPER_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master}
   -r{toxinidir}/requirements.txt
   -r{toxinidir}/doc/requirements.txt
 commands =
@@ -197,11 +197,21 @@
 whitelist_externals = rm
 
 [testenv:pep8]
+deps =
+    -r{toxinidir}/test-requirements.txt
+    autopep8
 basepython = python3
 commands =
+    autopep8 --exit-code --max-line-length=79 --experimental --diff -r tempest setup.py
     flake8 {posargs}
     check-uuid
 
+[testenv:autopep8]
+deps = autopep8
+basepython = python3
+commands =
+    {toxinidir}/tools/format.sh
+
 [testenv:uuidgen]
 commands =
     check-uuid --fix
@@ -211,7 +221,7 @@
 import_exceptions = tempest.services
 
 [flake8]
-# 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/
+# E125 is a won't fix until https://github.com/jcrocholl/pep8/issues/126 is resolved.  For further detail see https://review.opendev.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
 # W504 skipped because it is overeager and unnecessary
@@ -224,7 +234,7 @@
 [testenv:releasenotes]
 basepython = python3
 deps =
-  -c{env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt}
+  -c{env:UPPER_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master}
   -r{toxinidir}/requirements.txt
   -r{toxinidir}/doc/requirements.txt
 commands =
@@ -255,6 +265,7 @@
 
 [testenv:plugin-sanity-check]
 # perform tempest plugin sanity
+basepython = python3
 whitelist_externals = bash
 commands =
   bash tools/tempest-plugin-sanity.sh