Merge "Switch to new rolevar for run-temepst role"
diff --git a/doc/source/configuration.rst b/doc/source/configuration.rst
index c43e420..c790c5f 100644
--- a/doc/source/configuration.rst
+++ b/doc/source/configuration.rst
@@ -153,6 +153,11 @@
 
 Pre-Provisioned Credentials are also known as accounts.yaml or accounts file.
 
+Keystone Scopes & Roles Support in Tempest
+""""""""""""""""""""""""""""""""""""""""""
+For details on scope and roles support in Tempest,
+please refer to :doc:`this document <keystone_scopes_and_roles_support>`
+
 Compute
 -------
 
diff --git a/doc/source/index.rst b/doc/source/index.rst
index 66e68ea..2f29cf2 100644
--- a/doc/source/index.rst
+++ b/doc/source/index.rst
@@ -105,6 +105,14 @@
 
    tempest_and_plugins_compatible_version_policy
 
+Keystone Scopes & Roles Support in Tempest
+------------------------------------------
+
+.. toctree::
+   :maxdepth: 2
+
+   keystone_scopes_and_roles_support
+
 Stable Branch Support Policy
 ----------------------------
 
diff --git a/doc/source/keystone_scopes_and_roles_support.rst b/doc/source/keystone_scopes_and_roles_support.rst
new file mode 100644
index 0000000..f446f8c
--- /dev/null
+++ b/doc/source/keystone_scopes_and_roles_support.rst
@@ -0,0 +1,286 @@
+Keystone Scopes & Roles Support in Tempest
+==========================================
+
+OpenStack Keystone supports different scopes in token, refer to the
+`Keystone doc <https://docs.openstack.org/keystone/latest/admin/tokens-overview.html#authorization-scopes>`_.
+Along with the scopes, keystone supports default roles, one of which
+is a reader role, for details refer to
+`this keystone document <https://docs.openstack.org/keystone/latest/admin/service-api-protection.html>`_.
+
+Tempest supports those scopes and roles credentials that can be used
+to test APIs under different scope and roles.
+
+Dynamic Credentials
+-------------------
+
+Dynamic credential supports all the below set of personas and allows
+you to generate credentials tailored to a specific persona that you
+can use in your test.
+
+Domain scoped personas:
+^^^^^^^^^^^^^^^^^^^^^^^^
+
+  #. Domain Admin: This is supported and can be requested and used from
+     the test as below:
+
+     .. code-block:: python
+
+         class TestDummy(base.DummyBaseTest):
+
+             credentials = ['domain_admin']
+
+             @classmethod
+             def setup_clients(cls):
+                 super(TestDummy, cls).setup_clients()
+                 cls.az_d_admin_client = (
+                     cls.os_domain_admin.availability_zone_client)
+
+  #. Domain Member: This is supported and can be requested and used from
+     the test as below:
+
+     .. code-block:: python
+
+         class TestDummy(base.DummyBaseTest):
+
+             credentials = ['domain_member']
+
+             @classmethod
+             def setup_clients(cls):
+                 super(TestDummy, cls).setup_clients()
+                 cls.az_d_member_client = (
+                     cls.os_domain_member.availability_zone_client)
+
+  #. Domain Reader: This is supported and can be requested and used from
+     the test as below:
+
+     .. code-block:: python
+
+         class TestDummy(base.DummyBaseTest):
+
+             credentials = ['domain_reader']
+
+             @classmethod
+             def setup_clients(cls):
+                 super(TestDummy, cls).setup_clients()
+                 cls.az_d_reader_client = (
+                     cls.os_domain_reader.availability_zone_client)
+
+  #. Domain other roles: This is supported and can be requested and used from
+     the test as below:
+
+     You need to use the ``domain`` as the prefix in credentials type, and
+     based on that, Tempest will create test users under 'domain' scope.
+
+     .. code-block:: python
+
+         class TestDummy(base.DummyBaseTest):
+
+             credentials = [['domain_my_role1', 'my_own_role1', 'admin']
+                            ['domain_my_role2', 'my_own_role2']]
+
+             @classmethod
+             def setup_clients(cls):
+                 super(TestDummy, cls).setup_clients()
+                 cls.az_d_role1_client = (
+                     cls.os_domain_my_role1.availability_zone_client)
+                 cls.az_d_role2_client = (
+                     cls.os_domain_my_role2.availability_zone_client)
+
+System scoped personas:
+^^^^^^^^^^^^^^^^^^^^^^^
+
+  #. System Admin: This is supported and can be requested and used from the
+     test as below:
+
+     .. code-block:: python
+
+         class TestDummy(base.DummyBaseTest):
+
+             credentials = ['system_admin']
+
+             @classmethod
+             def setup_clients(cls):
+                 super(TestDummy, cls).setup_clients()
+                 cls.az_s_admin_client = (
+                     cls.os_system_admin.availability_zone_client)
+
+  #. System Member: This is supported and can be requested and used from the
+     test as below:
+
+     .. code-block:: python
+
+         class TestDummy(base.DummyBaseTest):
+
+             credentials = ['system_member']
+
+             @classmethod
+             def setup_clients(cls):
+                 super(TestDummy, cls).setup_clients()
+                 cls.az_s_member_client = (
+                     cls.os_system_member.availability_zone_client)
+
+  #. System Reader: This is supported and can be requested and used from
+     the test as below:
+
+     .. code-block:: python
+
+         class TestDummy(base.DummyBaseTest):
+
+             credentials = ['system_reader']
+
+             @classmethod
+             def setup_clients(cls):
+                 super(TestDummy, cls).setup_clients()
+                 cls.az_s_reader_client = (
+                     cls.os_system_reader.availability_zone_client)
+
+  #. System other roles: This is supported and can be requested and used from
+     the test as below:
+
+     You need to use the ``system`` as the prefix in credentials type, and
+     based on that, Tempest will create test users under 'project' scope.
+
+     .. code-block:: python
+
+         class TestDummy(base.DummyBaseTest):
+
+             credentials = [['system_my_role1', 'my_own_role1', 'admin']
+                            ['system_my_role2', 'my_own_role2']]
+
+             @classmethod
+             def setup_clients(cls):
+                 super(TestDummy, cls).setup_clients()
+                 cls.az_s_role1_client = (
+                     cls.os_system_my_role1.availability_zone_client)
+                 cls.az_s_role2_client = (
+                     cls.os_system_my_role2.availability_zone_client)
+
+Project scoped personas:
+^^^^^^^^^^^^^^^^^^^^^^^^
+
+  #. Project Admin: This is supported and can be requested and used from
+     the test as below:
+
+     .. code-block:: python
+
+         class TestDummy(base.DummyBaseTest):
+
+             credentials = ['project_admin']
+
+             @classmethod
+             def setup_clients(cls):
+                 super(TestDummy, cls).setup_clients()
+                 cls.az_p_admin_client = (
+                     cls.os_project_admin.availability_zone_client)
+
+  #. Project Member: This is supported and can be requested and used from
+     the test as below:
+
+     .. code-block:: python
+
+         class TestDummy(base.DummyBaseTest):
+
+             credentials = ['project_member']
+
+             @classmethod
+             def setup_clients(cls):
+                 super(TestDummy, cls).setup_clients()
+                 cls.az_p_member_client = (
+                     cls.os_project_member.availability_zone_client)
+
+  #. Project Reader: This is supported and can be requested and used from
+     the test as below:
+
+     .. code-block:: python
+
+         class TestDummy(base.DummyBaseTest):
+
+             credentials = ['project_reader']
+
+             @classmethod
+             def setup_clients(cls):
+                 super(TestDummy, cls).setup_clients()
+                 cls.az_p_reader_client = (
+                     cls.os_project_reader.availability_zone_client)
+
+  #. Project alternate Admin: This is supported and can be requested and used from
+     the test as below:
+
+     .. code-block:: python
+
+         class TestDummy(base.DummyBaseTest):
+
+             credentials = ['project_alt_admin']
+
+             @classmethod
+             def setup_clients(cls):
+                 super(TestDummy, cls).setup_clients()
+                 cls.az_p_alt_admin_client = (
+                     cls.os_project_alt_admin.availability_zone_client)
+
+  #. Project alternate Member: This is supported and can be requested and used from
+     the test as below:
+
+     .. code-block:: python
+
+         class TestDummy(base.DummyBaseTest):
+
+             credentials = ['project_alt_member']
+
+             @classmethod
+             def setup_clients(cls):
+                 super(TestDummy, cls).setup_clients()
+                 cls.az_p_alt_member_client = (
+                     cls.os_project_alt_member.availability_zone_client)
+
+  #. Project alternate Reader: This is supported and can be requested and used from
+     the test as below:
+
+     .. code-block:: python
+
+         class TestDummy(base.DummyBaseTest):
+
+             credentials = ['project_alt_reader']
+
+             @classmethod
+             def setup_clients(cls):
+                 super(TestDummy, cls).setup_clients()
+                 cls.az_p_alt_reader_client = (
+                     cls.os_project_alt_reader.availability_zone_client)
+
+  #. Project other roles: This is supported and can be requested and used from
+     the test as below:
+
+     You need to use the ``project`` as the prefix in credentials type, and
+     based on that, Tempest will create test users under 'project' scope.
+
+     .. code-block:: python
+
+         class TestDummy(base.DummyBaseTest):
+
+             credentials = [['project_my_role1', 'my_own_role1', 'admin']
+                            ['project_my_role2', 'my_own_role2']]
+
+             @classmethod
+             def setup_clients(cls):
+                 super(TestDummy, cls).setup_clients()
+                 cls.az_role1_client = (
+                     cls.os_project_my_role1.availability_zone_client)
+                 cls.az_role2_client = (
+                     cls.os_project_my_role2.availability_zone_client)
+
+Pre-Provisioned Credentials
+---------------------------
+
+Pre-Provisioned credentials support the below set of personas and can be
+used in the test as shown above in the ``Dynamic Credentials`` Section.
+
+* Domain Admin
+* Domain Member
+* Domain Reader
+* System Admin
+* System Member
+* System Reader
+* Project Admin
+* Project Member
+* Project Reader
diff --git a/releasenotes/notes/Add-keystone-v3-OS_FEDERATION-APIs-as-tempest-clients-fe9e10a0fe5f09d4.yaml b/releasenotes/notes/Add-keystone-v3-OS_FEDERATION-APIs-as-tempest-clients-fe9e10a0fe5f09d4.yaml
new file mode 100644
index 0000000..33df7c4
--- /dev/null
+++ b/releasenotes/notes/Add-keystone-v3-OS_FEDERATION-APIs-as-tempest-clients-fe9e10a0fe5f09d4.yaml
@@ -0,0 +1,10 @@
+---
+features:
+  - |
+    The following tempest clients for keystone v3 OS_FEDERATION API were
+    implemented in this release
+
+    * identity_providers
+    * protocols
+    * mappings
+    * service_providers
diff --git a/releasenotes/notes/Remove-deprecated-scenario.img_dir-option-da626e6153181e16.yaml b/releasenotes/notes/Remove-deprecated-scenario.img_dir-option-da626e6153181e16.yaml
new file mode 100644
index 0000000..2514e48
--- /dev/null
+++ b/releasenotes/notes/Remove-deprecated-scenario.img_dir-option-da626e6153181e16.yaml
@@ -0,0 +1,8 @@
+---
+upgrade:
+  - |
+    img_dir scenario option has been deprecated and it's being removed.
+    Starting Tempest 25.0.0 release, CONF.scenario.img_file needs a full path
+    for the image. Until this release, old behavior was maintained and kept
+    working however a user needs to specify the full path in
+    CONF.scenario.img_file config option from now on.
diff --git a/releasenotes/notes/add-alt-project-dynamic-creds-1a3bc543e65d9433.yaml b/releasenotes/notes/add-alt-project-dynamic-creds-1a3bc543e65d9433.yaml
new file mode 100644
index 0000000..de81b2b
--- /dev/null
+++ b/releasenotes/notes/add-alt-project-dynamic-creds-1a3bc543e65d9433.yaml
@@ -0,0 +1,4 @@
+---
+features:
+  - |
+    Add project alternate admin, member and reader role for dynamic credentials.
diff --git a/releasenotes/notes/add-assisted-volume-snapshot-client-737f5cb35d58c1b6.yaml b/releasenotes/notes/add-assisted-volume-snapshot-client-737f5cb35d58c1b6.yaml
new file mode 100644
index 0000000..5498688
--- /dev/null
+++ b/releasenotes/notes/add-assisted-volume-snapshot-client-737f5cb35d58c1b6.yaml
@@ -0,0 +1,4 @@
+---
+features:
+  - |
+    Add a new client to handle requests to create and delete assisted volume snapshots.
diff --git a/releasenotes/notes/add-compute-feature-ide-bus-b63802502c378083.yaml b/releasenotes/notes/add-compute-feature-ide-bus-b63802502c378083.yaml
new file mode 100644
index 0000000..43a0e8c
--- /dev/null
+++ b/releasenotes/notes/add-compute-feature-ide-bus-b63802502c378083.yaml
@@ -0,0 +1,10 @@
+---
+other:
+  - |
+    A new ``[compute-feature-enabled]ide_bus`` config option has been
+    introduced to indicate if the environment supports attaching disks to an
+    instance using an ``IDE`` bus.
+
+    This currently defaults to ``True`` but should be set to ``False`` when
+    using the libvirt OpenStack Nova virt driver *and* the ``q35`` machine type
+    as support for this bus is no longer provided.
diff --git a/releasenotes/notes/image-client-add-versions-and-tasks-ac289dbfe1c899cc.yaml b/releasenotes/notes/image-client-add-versions-and-tasks-ac289dbfe1c899cc.yaml
new file mode 100644
index 0000000..fde6193
--- /dev/null
+++ b/releasenotes/notes/image-client-add-versions-and-tasks-ac289dbfe1c899cc.yaml
@@ -0,0 +1,6 @@
+---
+features:
+  - |
+    Adds a method to images_client to get tasks relevant to a given image. Also adds
+    has_version() method to image versions_client to probe for availability of a given
+    API version.
diff --git a/releasenotes/notes/intermediate-wallaby-release-55a0b31b1dee7b23.yaml b/releasenotes/notes/intermediate-wallaby-release-55a0b31b1dee7b23.yaml
new file mode 100644
index 0000000..cda3b89
--- /dev/null
+++ b/releasenotes/notes/intermediate-wallaby-release-55a0b31b1dee7b23.yaml
@@ -0,0 +1,4 @@
+---
+prelude: >
+    This is an intermediate release during the Wallaby development cycle to
+    make new functionality available to plugins and other consumers.
diff --git a/releasenotes/notes/make-create-user-domain-aware-for-v3-creds-client-5054f58e715adc0c.yaml b/releasenotes/notes/make-create-user-domain-aware-for-v3-creds-client-5054f58e715adc0c.yaml
new file mode 100644
index 0000000..8931f09
--- /dev/null
+++ b/releasenotes/notes/make-create-user-domain-aware-for-v3-creds-client-5054f58e715adc0c.yaml
@@ -0,0 +1,9 @@
+---
+fixes:
+  - |
+    [`bug 1613819 <https://bugs.launchpad.net/tempest/+bug/1613819>`_]
+    admin_domain_name and default_credentials_domain_name parameters
+    under [auth] now affect a domain used for creating test users just
+    as they affect it for projects. Previously a domain with an id set
+    to "default" had to be present in order for test user creation to
+    succeed with Keystone v3.
diff --git a/releasenotes/notes/remove-deprecated-volume-config-options-4b7ea93b88e5b982.yaml b/releasenotes/notes/remove-deprecated-volume-config-options-4b7ea93b88e5b982.yaml
new file mode 100644
index 0000000..f3002f9
--- /dev/null
+++ b/releasenotes/notes/remove-deprecated-volume-config-options-4b7ea93b88e5b982.yaml
@@ -0,0 +1,9 @@
+---
+upgrade:
+  - |
+    Deprecated config options to select the Volume API version have been
+    removed. Use ``CONF.volume.catalog_type`` to run volume tests under v3
+    or v2 APIs.
+
+    * ``CONF.volume-feature-enabled.api_v2``
+    * ``CONF.volume-feature-enabled.api_v3``
\ No newline at end of file
diff --git a/releasenotes/notes/remove-old-data-utils-e0966f882f7fe23a.yaml b/releasenotes/notes/remove-old-data-utils-e0966f882f7fe23a.yaml
new file mode 100644
index 0000000..ac20340
--- /dev/null
+++ b/releasenotes/notes/remove-old-data-utils-e0966f882f7fe23a.yaml
@@ -0,0 +1,6 @@
+---
+upgrade:
+  - |
+    The old deprecated ``data-ultis`` from ``tempest.common.utils`` has been
+    removed. If you are still using this, use the stable version
+    of ``data-utils`` from new location ``tempest.lib.common.utils``.
diff --git a/releasenotes/notes/support-for-rbac-new-scope-6ec8164ce1e7288c.yaml b/releasenotes/notes/support-for-rbac-new-scope-6ec8164ce1e7288c.yaml
new file mode 100644
index 0000000..af7df93
--- /dev/null
+++ b/releasenotes/notes/support-for-rbac-new-scope-6ec8164ce1e7288c.yaml
@@ -0,0 +1,13 @@
+---
+prelude: >
+    Support for RBAC new system scope is added in Tempest.
+features:
+  - |
+    Keystone provides the new scoped token called ``system`` which
+    can be used to query the system scoped API operation. Projects
+    are moving towards the policy with new scope types, Keystone, Nova
+    already provide the new policy for RBAC checks. Tempest has added
+    the support to query the system scoped token from keystone to test
+    the new policy.
+    As next step, we will be moving all the Tempest tests on the project's
+    new policy.
diff --git a/releasenotes/notes/support-scope-in-get-roles-dynamic-creds-90bfab163c1c289a.yaml b/releasenotes/notes/support-scope-in-get-roles-dynamic-creds-90bfab163c1c289a.yaml
new file mode 100644
index 0000000..26282f0
--- /dev/null
+++ b/releasenotes/notes/support-scope-in-get-roles-dynamic-creds-90bfab163c1c289a.yaml
@@ -0,0 +1,36 @@
+---
+features:
+  - |
+    Dynamic credentials now support the scope type for specific roles
+    too along with ``admin``, ``member``, ``reader`` role.
+    Test can specify the scope in the prefix of ``cls.credentials`` name.
+    If ``system`` is prefix in ``cls.credentials`` name then creds will
+    be created with scope as ``system``. If ``domain`` is prefix in
+    ``cls.credentials`` name then creds will be created with scope as
+    ``domain`` otherwise default ``project`` scope will be used.
+    For Example::
+
+        credentials = [['my_role', 'role1'], # this will be old style and project scoped
+                       ['project_my_role', 'role1'], # this will be project scoped
+                       ['domain_my_role', 'role1'], # this will be domain scoped
+                       ['system_my_role', 'role1']] # this will be system scoped
+
+    And below is how test can access the credential manager of respective
+    credentials type::
+
+        cls.os_my_role.any_client
+        cls.os_project_my_role.any_client
+        cls.os_domain_my_role.any_client
+        cls.os_system_my_role.any_client
+
+
+    For backward compatibility, we set the credentials manager class attribute
+    in old style form too which is prefix with ``os_roles_*``, example
+    ``cls.os_roles_my_role`` but we recommend to use the new style attribute
+    as shown above.
+issues:
+  - |
+    Scope support for specific role is not yet added for pre-provisioned credentials.
+fixes:
+  - |
+    Fixes the `bug# 1917168 <https://bugs.launchpad.net/tempest/+bug/1917168>`_
diff --git a/requirements.txt b/requirements.txt
index abc0bce..eae5427 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -12,13 +12,13 @@
 oslo.log>=3.36.0 # Apache-2.0
 stestr>=1.0.0 # Apache-2.0
 oslo.serialization!=2.19.1,>=2.18.0 # Apache-2.0
-oslo.utils>=3.33.0 # Apache-2.0
+oslo.utils>=4.7.0 # Apache-2.0
 six>=1.10.0 # MIT
 fixtures>=3.0.0 # Apache-2.0/BSD
 PyYAML>=3.12 # MIT
 python-subunit>=1.0.0 # Apache-2.0/BSD
 stevedore>=1.20.0 # Apache-2.0
-PrettyTable<0.8,>=0.7.1 # BSD
+PrettyTable>=0.7.1 # BSD
 urllib3>=1.21.1 # MIT
 debtcollector>=1.2.0 # Apache-2.0
 unittest2>=1.1.0 # BSD
diff --git a/roles/run-tempest/README.rst b/roles/run-tempest/README.rst
index f9fcf28..0c72b69 100644
--- a/roles/run-tempest/README.rst
+++ b/roles/run-tempest/README.rst
@@ -81,7 +81,7 @@
 .. zuul:rolevar:: stable_constraints_file
    :default: ''
 
-   Upper constraints file to be used for stable branch till stable/rocky.
+   Upper constraints file to be used for stable branch till stable/stein.
 
 .. zuul:rolevar:: tempest_tox_environment
    :default: ''
diff --git a/roles/run-tempest/defaults/main.yaml b/roles/run-tempest/defaults/main.yaml
index e7a1cc6..5f13883 100644
--- a/roles/run-tempest/defaults/main.yaml
+++ b/roles/run-tempest/defaults/main.yaml
@@ -1,12 +1,11 @@
 devstack_base_dir: /opt/stack
 tempest_test_regex: ''
 tox_envlist: smoke
-# TODO(kopecmartin) remove tempest_black_regex once all consumers of this
-# role have switched to the tempest_exclude_regex option.
-tempest_black_regex: ''
-tempest_exclude_regex: ''
 tox_extra_args: ''
 tempest_test_timeout: ''
 stable_constraints_file: "{{ devstack_base_dir }}/requirements/upper-constraints.txt"
 target_branch: "{{ zuul.branch }}"
 tempest_tox_environment: {}
+# NOTE(gmann): external_bridge_mtu shows as undefined for run-tempest role
+# defining default value here to avoid that error.
+external_bridge_mtu: 0
diff --git a/roles/run-tempest/tasks/main.yaml b/roles/run-tempest/tasks/main.yaml
index e9c8e92..37026e4 100644
--- a/roles/run-tempest/tasks/main.yaml
+++ b/roles/run-tempest/tasks/main.yaml
@@ -25,11 +25,22 @@
     target_branch: "{{ zuul.override_checkout }}"
   when: zuul.override_checkout is defined
 
-- name: Use stable branch upper-constraints till stable/rocky
+- name: Use stable branch upper-constraints till stable/stein
   set_fact:
     # TOX_CONSTRAINTS_FILE is new name, UPPER_CONSTRAINTS_FILE is old one, best to set both
     tempest_tox_environment: "{{ tempest_tox_environment | combine({'UPPER_CONSTRAINTS_FILE': stable_constraints_file}) | combine({'TOX_CONSTRAINTS_FILE': stable_constraints_file}) }}"
-  when: target_branch in ["stable/ocata", "stable/pike", "stable/queens", "stable/rocky"]
+  when: target_branch in ["stable/ocata", "stable/pike", "stable/queens", "stable/rocky", "stable/stein"]
+
+- name: Use Configured upper-constraints for non-master Tempest
+  set_fact:
+    # TOX_CONSTRAINTS_FILE is new name, UPPER_CONSTRAINTS_FILE is old one, best to set both
+    tempest_tox_environment: "{{ tempest_tox_environment | combine({'UPPER_CONSTRAINTS_FILE': devstack_localrc['TEMPEST_VENV_UPPER_CONSTRAINTS']}) | combine({'TOX_CONSTRAINTS_FILE': devstack_localrc['TEMPEST_VENV_UPPER_CONSTRAINTS']}) }}"
+  when:
+    - devstack_localrc is defined
+    - "'TEMPEST_BRANCH' in devstack_localrc"
+    - "'TEMPEST_VENV_UPPER_CONSTRAINTS' in devstack_localrc"
+    - devstack_localrc['TEMPEST_BRANCH'] != 'master'
+    - devstack_localrc['TEMPEST_VENV_UPPER_CONSTRAINTS'] != 'default'
 
 - name: Set OS_TEST_TIMEOUT if requested
   set_fact:
@@ -70,22 +81,16 @@
 # TODO(kopecmartin) remove this after all consumers of the role have switched
 # to tempest_exclude_regex option, until then it's kept here for the backward
 # compatibility
-- name: Set tempest_exclude_regex
+- name: Build exclude regex (old param)
   set_fact:
-    tempest_exclude_regex: "{{ tempest_black_regex }}"
+    tempest_test_exclude_regex: "--black-regex={{tempest_black_regex|quote}}"
   when:
     - tempest_black_regex is defined
     - tempest_exclude_regex is not defined
 
-- name: Build exclude regex (old param)
-  set_fact:
-    tempest_exclude_regex: "--black-regex={{tempest_black_regex|quote}}"
-  when:
-    - tempest_black_regex is defined
-
 - name: Build exclude regex (new param)
   set_fact:
-    tempest_exclude_regex: "--exclude-regex={{tempest_exclude_regex|quote}}"
+    tempest_test_exclude_regex: "--exclude-regex={{tempest_exclude_regex|quote}}"
   when:
     - tempest_black_regex is not defined
     - tempest_exclude_regex is defined
@@ -94,7 +99,7 @@
   command: tox -e {{tox_envlist}} {{tox_extra_args}} -- {{tempest_test_regex|quote}} \
            {{blacklist_option|default('')}}  {{exclude_list_option|default('')}} \
             --concurrency={{tempest_concurrency|default(default_concurrency)}} \
-           {{tempest_exclude_regex|default('')}}
+           {{tempest_test_exclude_regex|default('')}}
   args:
     chdir: "{{devstack_base_dir}}/tempest"
   register: tempest_run_result
diff --git a/setup.cfg b/setup.cfg
index 18427a2..d885db0 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,12 +1,12 @@
 [metadata]
 name = tempest
 summary = OpenStack Integration Testing
-description-file =
+description_file =
     README.rst
 author = OpenStack
-author-email = openstack-discuss@lists.openstack.org
-home-page = https://docs.openstack.org/tempest/latest/
-python-requires = >=3.6
+author_email = openstack-discuss@lists.openstack.org
+home_page = https://docs.openstack.org/tempest/latest/
+python_requires = >=3.6
 classifier =
     Intended Audience :: Information Technology
     Intended Audience :: System Administrators
@@ -48,4 +48,3 @@
     run = tempest.cmd.run:TempestRun
 oslo.config.opts =
     tempest.config = tempest.config:list_opts
-
diff --git a/tempest/api/compute/admin/test_instance_usage_audit_log.py b/tempest/api/compute/admin/test_instance_usage_audit_log.py
index 4dcbb3b..c4c0542 100644
--- a/tempest/api/compute/admin/test_instance_usage_audit_log.py
+++ b/tempest/api/compute/admin/test_instance_usage_audit_log.py
@@ -14,8 +14,7 @@
 #    under the License.
 
 import datetime
-
-from six.moves.urllib import parse as urllib
+from urllib import parse as urllib
 
 from tempest.api.compute import base
 from tempest.lib import decorators
diff --git a/tempest/api/compute/admin/test_instance_usage_audit_log_negative.py b/tempest/api/compute/admin/test_instance_usage_audit_log_negative.py
index 84d18c4..c115451 100644
--- a/tempest/api/compute/admin/test_instance_usage_audit_log_negative.py
+++ b/tempest/api/compute/admin/test_instance_usage_audit_log_negative.py
@@ -14,8 +14,7 @@
 #    under the License.
 
 import datetime
-
-from six.moves.urllib import parse as urllib
+from urllib import parse as urllib
 
 from tempest.api.compute import base
 from tempest.lib import decorators
diff --git a/tempest/api/compute/admin/test_live_migration.py b/tempest/api/compute/admin/test_live_migration.py
index 941315e..52ccea7 100644
--- a/tempest/api/compute/admin/test_live_migration.py
+++ b/tempest/api/compute/admin/test_live_migration.py
@@ -169,8 +169,13 @@
                       block_migrate_cinder_iscsi,
                       'Block Live migration not configured for iSCSI')
     @utils.services('volume')
-    def test_iscsi_volume(self):
-        """Test live migrating a server with volume attached"""
+    def test_live_block_migration_with_attached_volume(self):
+        """Test the live-migration of an instance with an attached volume.
+
+        This tests the live-migration of an instance with both a local disk and
+        attach volume. This differs from test_volume_backed_live_migration
+        above that tests live-migration with only an attached volume.
+        """
         server = self.create_test_server(wait_until="ACTIVE")
         server_id = server['id']
         if not CONF.compute_feature_enabled.can_migrate_between_any_hosts:
diff --git a/tempest/api/compute/admin/test_volume.py b/tempest/api/compute/admin/test_volume.py
index 342380e..cf8c560 100644
--- a/tempest/api/compute/admin/test_volume.py
+++ b/tempest/api/compute/admin/test_volume.py
@@ -13,7 +13,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import six
+import io
 
 from tempest.api.compute import base
 from tempest.common import waiters
@@ -51,7 +51,7 @@
         image = self.image_client.show_image(CONF.compute.image_ref)
         image_data = self.image_client.show_image_file(
             CONF.compute.image_ref).data
-        image_file = six.BytesIO(image_data)
+        image_file = io.BytesIO(image_data)
         create_dict = {
             'container_format': image['container_format'],
             'disk_format': image['disk_format'],
diff --git a/tempest/api/compute/base.py b/tempest/api/compute/base.py
index bb0f5ad..7900b77 100644
--- a/tempest/api/compute/base.py
+++ b/tempest/api/compute/base.py
@@ -638,6 +638,8 @@
         cls.admin_flavors_client = cls.os_admin.flavors_client
         cls.admin_servers_client = cls.os_admin.servers_client
         cls.image_client = cls.os_admin.image_client_v2
+        cls.admin_assisted_volume_snapshots_client = \
+            cls.os_admin.assisted_volume_snapshots_client
 
     def create_flavor(self, ram, vcpus, disk, name=None,
                       is_public='True', **kwargs):
diff --git a/tempest/api/compute/flavors/test_flavors_negative.py b/tempest/api/compute/flavors/test_flavors_negative.py
index 235049a..5d6a7d7 100644
--- a/tempest/api/compute/flavors/test_flavors_negative.py
+++ b/tempest/api/compute/flavors/test_flavors_negative.py
@@ -13,10 +13,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import io
 import random
 
-import six
-
 from tempest.api.compute import base
 from tempest.common import image as common_image
 from tempest.common import utils
@@ -44,7 +43,7 @@
             CONF.compute.flavor_ref)['flavor']
         min_img_ram = flavor['ram'] + 1
         size = random.randint(1024, 4096)
-        image_file = six.BytesIO(data_utils.random_bytes(size))
+        image_file = io.BytesIO(data_utils.random_bytes(size))
         params = {
             'name': data_utils.rand_name('image'),
             'container_format': CONF.image.container_formats[0],
diff --git a/tempest/api/compute/images/test_image_metadata.py b/tempest/api/compute/images/test_image_metadata.py
index 561265f..ece983d 100644
--- a/tempest/api/compute/images/test_image_metadata.py
+++ b/tempest/api/compute/images/test_image_metadata.py
@@ -13,7 +13,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import six
+import io
 
 from tempest.api.compute import base
 from tempest.common import image as common_image
@@ -77,7 +77,7 @@
         cls.addClassResourceCleanup(test_utils.call_and_ignore_notfound_exc,
                                     cls.glance_client.delete_image,
                                     cls.image_id)
-        image_file = six.BytesIO((b'*' * 1024))
+        image_file = io.BytesIO((b'*' * 1024))
         if CONF.image_feature_enabled.api_v1:
             cls.glance_client.update_image(cls.image_id, data=image_file)
         else:
diff --git a/tempest/api/compute/images/test_list_image_filters.py b/tempest/api/compute/images/test_list_image_filters.py
index 7930c67..15b8a00 100644
--- a/tempest/api/compute/images/test_list_image_filters.py
+++ b/tempest/api/compute/images/test_list_image_filters.py
@@ -15,7 +15,7 @@
 
 import time
 
-import six
+import io
 import testtools
 
 from tempest.api.compute import base
@@ -85,7 +85,7 @@
             # Wait 1 second between creation and upload to ensure a delta
             # between created_at and updated_at.
             time.sleep(1)
-            image_file = six.BytesIO((b'*' * 1024))
+            image_file = io.BytesIO((b'*' * 1024))
             if CONF.image_feature_enabled.api_v1:
                 cls.glance_client.update_image(image_id, data=image_file)
             else:
diff --git a/tempest/api/compute/servers/test_attach_interfaces.py b/tempest/api/compute/servers/test_attach_interfaces.py
index 102792e..ac18442 100644
--- a/tempest/api/compute/servers/test_attach_interfaces.py
+++ b/tempest/api/compute/servers/test_attach_interfaces.py
@@ -16,7 +16,6 @@
 import time
 
 from oslo_log import log
-import six
 
 from tempest.api.compute import base
 from tempest.common import compute
@@ -241,7 +240,7 @@
         except lib_exc.BadRequest as e:
             msg = ('Multiple possible networks found, use a Network ID to be '
                    'more specific.')
-            if not CONF.compute.fixed_network_name and six.text_type(e) == msg:
+            if not CONF.compute.fixed_network_name and str(e) == msg:
                 raise
         else:
             ifs.append(iface)
@@ -450,7 +449,7 @@
         except lib_exc.BadRequest as e:
             msg = ('Multiple possible networks found, use a Network ID to be '
                    'more specific.')
-            if not CONF.compute.fixed_network_name and six.text_type(e) == msg:
+            if not CONF.compute.fixed_network_name and str(e) == msg:
                 raise
         else:
             # just to check the response schema
diff --git a/tempest/api/compute/servers/test_novnc.py b/tempest/api/compute/servers/test_novnc.py
index 6ebdbdb..a9c0e56 100644
--- a/tempest/api/compute/servers/test_novnc.py
+++ b/tempest/api/compute/servers/test_novnc.py
@@ -14,9 +14,9 @@
 #    under the License.
 
 import struct
+import urllib.parse as urlparse
 
 import six
-import six.moves.urllib.parse as urlparse
 import urllib3
 
 from tempest.api.compute import base
@@ -69,7 +69,7 @@
         resp = urllib3.PoolManager().request('GET', vnc_url)
         # Make sure that the GET request was accepted by the novncproxy
         self.assertEqual(resp.status, 200, 'Got a Bad HTTP Response on the '
-                         'initial call: ' + six.text_type(resp.status))
+                         'initial call: ' + str(resp.status))
         # Do some basic validation to make sure it is an expected HTML document
         resp_data = resp.data.decode()
         # This is needed in the case of example: <html lang="en">
@@ -165,11 +165,11 @@
             self._websocket.response.startswith(b'HTTP/1.1 101 Switching '
                                                 b'Protocols'),
             'Incorrect HTTP return status code: {}'.format(
-                six.text_type(self._websocket.response)
+                str(self._websocket.response)
             )
         )
         _required_header = 'upgrade: websocket'
-        _response = six.text_type(self._websocket.response).lower()
+        _response = str(self._websocket.response).lower()
         self.assertIn(
             _required_header,
             _response,
diff --git a/tempest/api/compute/servers/test_server_actions.py b/tempest/api/compute/servers/test_server_actions.py
index 4527aa9..deb21c7 100644
--- a/tempest/api/compute/servers/test_server_actions.py
+++ b/tempest/api/compute/servers/test_server_actions.py
@@ -13,8 +13,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from urllib import parse as urlparse
+
 from oslo_log import log as logging
-from six.moves.urllib import parse as urlparse
 import testtools
 
 from tempest.api.compute import base
diff --git a/tempest/api/compute/servers/test_server_rescue.py b/tempest/api/compute/servers/test_server_rescue.py
index c222893..a9f8c09 100644
--- a/tempest/api/compute/servers/test_server_rescue.py
+++ b/tempest/api/compute/servers/test_server_rescue.py
@@ -158,8 +158,14 @@
             self.servers_client, server_id, 'ACTIVE')
 
 
-class ServerStableDeviceRescueTest(BaseServerStableDeviceRescueTest):
-    """Test rescuing server specifying type of device for the rescue disk"""
+class ServerStableDeviceRescueTestIDE(BaseServerStableDeviceRescueTest):
+    """Test rescuing server using an IDE device for the rescue disk"""
+
+    @classmethod
+    def skip_checks(cls):
+        super().skip_checks()
+        if not CONF.compute_feature_enabled.ide_bus:
+            raise cls.skipException("IDE bus not available.")
 
     @decorators.idempotent_id('947004c3-e8ef-47d9-9f00-97b74f9eaf96')
     def test_stable_device_rescue_cdrom_ide(self):
@@ -168,6 +174,10 @@
             hw_rescue_device='cdrom', hw_rescue_bus='ide')
         self._test_stable_device_rescue(server_id, rescue_image_id)
 
+
+class ServerStableDeviceRescueTest(BaseServerStableDeviceRescueTest):
+    """Test rescuing server specifying type of device for the rescue disk"""
+
     @decorators.idempotent_id('16865750-1417-4854-bcf7-496e6753c01e')
     def test_stable_device_rescue_disk_virtio(self):
         """Test rescuing server with disk and virtio as the rescue disk"""
diff --git a/tempest/api/identity/admin/v3/test_tokens.py b/tempest/api/identity/admin/v3/test_tokens.py
index f3a7471..5bbd65c 100644
--- a/tempest/api/identity/admin/v3/test_tokens.py
+++ b/tempest/api/identity/admin/v3/test_tokens.py
@@ -13,8 +13,6 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import six
-
 from tempest.api.identity import base
 from tempest import config
 from tempest.lib.common.utils import data_utils
@@ -70,8 +68,8 @@
         orig_expires_at = token_auth['token']['expires_at']
         orig_user = token_auth['token']['user']
 
-        self.assertIsInstance(token_auth['token']['expires_at'], six.text_type)
-        self.assertIsInstance(token_auth['token']['issued_at'], six.text_type)
+        self.assertIsInstance(token_auth['token']['expires_at'], str)
+        self.assertIsInstance(token_auth['token']['issued_at'], str)
         self.assertEqual(['password'], token_auth['token']['methods'])
         self.assertEqual(user['id'], token_auth['token']['user']['id'])
         self.assertEqual(user['name'], token_auth['token']['user']['name'])
@@ -91,7 +89,7 @@
 
         self.assertEqual(orig_expires_at, token_auth['token']['expires_at'],
                          'Expiration time should match original token')
-        self.assertIsInstance(token_auth['token']['issued_at'], six.text_type)
+        self.assertIsInstance(token_auth['token']['issued_at'], str)
         self.assertEqual(set(['password', 'token']),
                          set(token_auth['token']['methods']))
         self.assertEqual(orig_user, token_auth['token']['user'],
diff --git a/tempest/api/image/base.py b/tempest/api/image/base.py
index d3dc19a..23e7fd8 100644
--- a/tempest/api/image/base.py
+++ b/tempest/api/image/base.py
@@ -12,7 +12,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import six
+import io
 
 from tempest.common import image as common_image
 from tempest import config
@@ -113,7 +113,7 @@
         cls.alt_tenant_id = cls.alt_image_member_client.tenant_id
 
     def _create_image(self):
-        image_file = six.BytesIO(data_utils.random_bytes())
+        image_file = io.BytesIO(data_utils.random_bytes())
         image = self.create_image(container_format='bare',
                                   disk_format='raw',
                                   is_public=False,
@@ -134,12 +134,6 @@
     def setup_clients(cls):
         super(BaseV2ImageTest, cls).setup_clients()
         cls.client = cls.os_primary.image_client_v2
-        cls.namespaces_client = cls.os_primary.namespaces_client
-        cls.resource_types_client = cls.os_primary.resource_types_client
-        cls.namespace_properties_client =\
-            cls.os_primary.namespace_properties_client
-        cls.namespace_objects_client = cls.os_primary.namespace_objects_client
-        cls.namespace_tags_client = cls.os_primary.namespace_tags_client
         cls.schemas_client = cls.os_primary.schemas_client
         cls.versions_client = cls.os_primary.image_versions_client
 
@@ -204,3 +198,9 @@
     def setup_clients(cls):
         super(BaseV2ImageAdminTest, cls).setup_clients()
         cls.admin_client = cls.os_admin.image_client_v2
+        cls.namespaces_client = cls.os_admin.namespaces_client
+        cls.resource_types_client = cls.os_admin.resource_types_client
+        cls.namespace_properties_client =\
+            cls.os_admin.namespace_properties_client
+        cls.namespace_objects_client = cls.os_admin.namespace_objects_client
+        cls.namespace_tags_client = cls.os_admin.namespace_tags_client
diff --git a/tempest/api/image/v1/test_images.py b/tempest/api/image/v1/test_images.py
index 595717e..6fd6c4e 100644
--- a/tempest/api/image/v1/test_images.py
+++ b/tempest/api/image/v1/test_images.py
@@ -13,7 +13,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import six
+import io
 
 from tempest.api.image import base
 from tempest.common import image as common_image
@@ -72,7 +72,7 @@
             self.assertEqual(val, image.get('properties')[key])
 
         # Now try uploading an image file
-        image_file = six.BytesIO(data_utils.random_bytes())
+        image_file = io.BytesIO(data_utils.random_bytes())
         body = self.client.update_image(image['id'], data=image_file)['image']
         self.assertIn('size', body)
         self.assertEqual(1024, body.get('size'))
@@ -204,7 +204,7 @@
         Note that the size of the new image is a random number between
         1024 and 4096
         """
-        image_file = six.BytesIO(data_utils.random_bytes(size))
+        image_file = io.BytesIO(data_utils.random_bytes(size))
         name = 'New Standard Image %s' % name
         image = cls.create_image(name=name,
                                  container_format=container_format,
@@ -306,7 +306,7 @@
                                disk_format, size):
         """Create a new standard image and return newly-registered image-id"""
 
-        image_file = six.BytesIO(data_utils.random_bytes(size))
+        image_file = io.BytesIO(data_utils.random_bytes(size))
         name = 'New Standard Image %s' % name
         image = cls.create_image(name=name,
                                  container_format=container_format,
diff --git a/tempest/api/image/v2/admin/test_images.py b/tempest/api/image/v2/admin/test_images.py
index ad68d82..733c778 100644
--- a/tempest/api/image/v2/admin/test_images.py
+++ b/tempest/api/image/v2/admin/test_images.py
@@ -13,7 +13,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import six
+import io
 
 from tempest.api.image import base
 from tempest.common import waiters
@@ -99,7 +99,7 @@
         self.assertEqual('queued', image['status'])
 
         file_content = data_utils.random_bytes()
-        image_file = six.BytesIO(file_content)
+        image_file = io.BytesIO(file_content)
         self.client.store_image_file(image['id'], image_file)
 
         body = self.client.show_image(image['id'])
diff --git a/tempest/api/image/v2/test_images_metadefs_namespace_objects.py b/tempest/api/image/v2/admin/test_images_metadefs_namespace_objects.py
similarity index 98%
rename from tempest/api/image/v2/test_images_metadefs_namespace_objects.py
rename to tempest/api/image/v2/admin/test_images_metadefs_namespace_objects.py
index 32b81b1..9222920 100644
--- a/tempest/api/image/v2/test_images_metadefs_namespace_objects.py
+++ b/tempest/api/image/v2/admin/test_images_metadefs_namespace_objects.py
@@ -16,7 +16,7 @@
 from tempest.lib import decorators
 
 
-class MetadataNamespaceObjectsTest(base.BaseV2ImageTest):
+class MetadataNamespaceObjectsTest(base.BaseV2ImageAdminTest):
     """Test the Metadata definition namespace objects basic functionality"""
 
     def _create_namespace_object(self, namespace):
diff --git a/tempest/api/image/v2/test_images_metadefs_namespace_properties.py b/tempest/api/image/v2/admin/test_images_metadefs_namespace_properties.py
similarity index 97%
rename from tempest/api/image/v2/test_images_metadefs_namespace_properties.py
rename to tempest/api/image/v2/admin/test_images_metadefs_namespace_properties.py
index 1d4f0a6..10dfba1 100644
--- a/tempest/api/image/v2/test_images_metadefs_namespace_properties.py
+++ b/tempest/api/image/v2/admin/test_images_metadefs_namespace_properties.py
@@ -15,7 +15,7 @@
 from tempest.lib import decorators
 
 
-class MetadataNamespacePropertiesTest(base.BaseV2ImageTest):
+class MetadataNamespacePropertiesTest(base.BaseV2ImageAdminTest):
     """Test the Metadata definition namespace property basic functionality"""
 
     @decorators.idempotent_id('b1a3765e-3a5d-4f6d-a3a7-3ca3476ae768')
diff --git a/tempest/api/image/v2/test_images_metadefs_namespace_tags.py b/tempest/api/image/v2/admin/test_images_metadefs_namespace_tags.py
similarity index 98%
rename from tempest/api/image/v2/test_images_metadefs_namespace_tags.py
rename to tempest/api/image/v2/admin/test_images_metadefs_namespace_tags.py
index dc64185..9e88e03 100644
--- a/tempest/api/image/v2/test_images_metadefs_namespace_tags.py
+++ b/tempest/api/image/v2/admin/test_images_metadefs_namespace_tags.py
@@ -16,7 +16,7 @@
 from tempest.lib import decorators
 
 
-class MetadataNamespaceTagsTest(base.BaseV2ImageTest):
+class MetadataNamespaceTagsTest(base.BaseV2ImageAdminTest):
     """Test the Metadata definition namespace tags basic functionality"""
 
     tags = [
diff --git a/tempest/api/image/v2/test_images_metadefs_namespaces.py b/tempest/api/image/v2/admin/test_images_metadefs_namespaces.py
similarity index 98%
rename from tempest/api/image/v2/test_images_metadefs_namespaces.py
rename to tempest/api/image/v2/admin/test_images_metadefs_namespaces.py
index 502949f..0fe49f9 100644
--- a/tempest/api/image/v2/test_images_metadefs_namespaces.py
+++ b/tempest/api/image/v2/admin/test_images_metadefs_namespaces.py
@@ -20,7 +20,7 @@
 from tempest.lib import exceptions as lib_exc
 
 
-class MetadataNamespacesTest(base.BaseV2ImageTest):
+class MetadataNamespacesTest(base.BaseV2ImageAdminTest):
     """Test the Metadata definition Namespaces basic functionality"""
 
     @decorators.idempotent_id('319b765e-7f3d-4b3d-8b37-3ca3876ee768')
diff --git a/tempest/api/image/v2/test_images_metadefs_resource_types.py b/tempest/api/image/v2/admin/test_images_metadefs_resource_types.py
similarity index 97%
rename from tempest/api/image/v2/test_images_metadefs_resource_types.py
rename to tempest/api/image/v2/admin/test_images_metadefs_resource_types.py
index 6867f2d..e533c43 100644
--- a/tempest/api/image/v2/test_images_metadefs_resource_types.py
+++ b/tempest/api/image/v2/admin/test_images_metadefs_resource_types.py
@@ -17,7 +17,7 @@
 from tempest.lib import decorators
 
 
-class MetadataResourceTypesTest(base.BaseV2ImageTest):
+class MetadataResourceTypesTest(base.BaseV2ImageAdminTest):
     """Test the Metadata definition resource types basic functionality"""
 
     @decorators.idempotent_id('6f358a4e-5ef0-11e6-a795-080027d0d606')
diff --git a/tempest/api/image/v2/test_images.py b/tempest/api/image/v2/test_images.py
index ca72388..efa23bb 100644
--- a/tempest/api/image/v2/test_images.py
+++ b/tempest/api/image/v2/test_images.py
@@ -14,10 +14,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import io
 import random
 
-import six
-
 from oslo_log import log as logging
 from tempest.api.image import base
 from tempest.common import waiters
@@ -69,6 +68,23 @@
         self.assertEqual('queued', image['status'])
         return image
 
+    def _require_import_method(self, method):
+        if method not in self.available_import_methods:
+            raise self.skipException('Server does not support '
+                                     '%s import method' % method)
+
+    def _stage_and_check(self):
+        image = self._create_image()
+        # Stage image data
+        file_content = data_utils.random_bytes()
+        image_file = io.BytesIO(file_content)
+        self.client.stage_image_file(image['id'], image_file)
+        # Check image status is 'uploading'
+        body = self.client.show_image(image['id'])
+        self.assertEqual(image['id'], body['id'])
+        self.assertEqual('uploading', body['status'])
+        return image['id']
+
     @decorators.idempotent_id('32ca0c20-e16f-44ac-8590-07869c9b4cc2')
     def test_image_glance_direct_import(self):
         """Test 'glance-direct' import functionalities
@@ -76,21 +92,29 @@
         Create image, stage image data, import image and verify
         that import succeeded.
         """
-        if 'glance-direct' not in self.available_import_methods:
-            raise self.skipException('Server does not support '
-                                     'glance-direct import method')
-        image = self._create_image()
-        # Stage image data
-        file_content = data_utils.random_bytes()
-        image_file = six.BytesIO(file_content)
-        self.client.stage_image_file(image['id'], image_file)
-        # Check image status is 'uploading'
-        body = self.client.show_image(image['id'])
-        self.assertEqual(image['id'], body['id'])
-        self.assertEqual('uploading', body['status'])
+        self._require_import_method('glance-direct')
+
+        image_id = self._stage_and_check()
         # import image from staging to backend
-        self.client.image_import(image['id'], method='glance-direct')
-        waiters.wait_for_image_imported_to_stores(self.client, image['id'])
+        resp = self.client.image_import(image_id, method='glance-direct')
+        waiters.wait_for_image_imported_to_stores(self.client, image_id)
+
+        if not self.versions_client.has_version('2.12'):
+            # API is not new enough to support image/tasks API
+            LOG.info('Glance does not support v2.12, so I am unable to '
+                     'validate the image/tasks API.')
+            return
+
+        # Make sure we can access the task and that some of the key
+        # fields look legit.
+        tasks = self.client.show_image_tasks(image_id)
+        self.assertEqual(1, len(tasks['tasks']))
+        task = tasks['tasks'][0]
+        self.assertEqual('success', task['status'])
+        self.assertEqual(resp.response['x-openstack-request-id'],
+                         task['request_id'])
+        self.assertEqual('glance-direct',
+                         task['input']['import_req']['method']['name'])
 
     @decorators.idempotent_id('f6feb7a4-b04f-4706-a011-206129f83e62')
     def test_image_web_download_import(self):
@@ -99,9 +123,8 @@
         Create image, import image and verify that import
         succeeded.
         """
-        if 'web-download' not in self.available_import_methods:
-            raise self.skipException('Server does not support '
-                                     'web-download import method')
+        self._require_import_method('web-download')
+
         image = self._create_image()
         # Now try to get image details
         body = self.client.show_image(image['id'])
@@ -113,6 +136,47 @@
                                  image_uri=image_uri)
         waiters.wait_for_image_imported_to_stores(self.client, image['id'])
 
+    @decorators.idempotent_id('e04761a1-22af-42c2-b8bc-a34a3f12b585')
+    def test_remote_import(self):
+        """Test image import against a different worker than stage.
+
+        This creates and stages an image against the primary API worker,
+        but then calls import on a secondary worker (if available) to
+        test that distributed image import works (i.e. proxies the import
+        request to the proper worker).
+        """
+        self._require_import_method('glance-direct')
+
+        if not CONF.image.alternate_image_endpoint:
+            raise self.skipException('No image_remote service to test '
+                                     'against')
+
+        image_id = self._stage_and_check()
+        # import image from staging to backend, but on the alternate worker
+        self.os_primary.image_client_remote.image_import(
+            image_id, method='glance-direct')
+        waiters.wait_for_image_imported_to_stores(self.client, image_id)
+
+    @decorators.idempotent_id('44d60544-1524-42f7-8899-315301105dd8')
+    def test_remote_delete(self):
+        """Test image delete against a different worker than stage.
+
+        This creates and stages an image against the primary API worker,
+        but then calls delete on a secondary worker (if available) to
+        test that distributed image import works (i.e. proxies the delete
+        request to the proper worker).
+        """
+        self._require_import_method('glance-direct')
+
+        if not CONF.image.alternate_image_endpoint:
+            raise self.skipException('No image_remote service to test '
+                                     'against')
+
+        image_id = self._stage_and_check()
+        # delete image from staging to backend, but on the alternate worker
+        self.os_primary.image_client_remote.delete_image(image_id)
+        self.client.wait_for_resource_deletion(image_id)
+
 
 class MultiStoresImportImagesTest(base.BaseV2ImageTest):
     """Test importing image in multiple stores"""
@@ -158,7 +222,7 @@
 
         self.client.stage_image_file(
             image['id'],
-            six.BytesIO(data_utils.random_bytes()))
+            io.BytesIO(data_utils.random_bytes()))
         # Check image status is 'uploading'
         body = self.client.show_image(image['id'])
         self.assertEqual(image['id'], body['id'])
@@ -240,7 +304,7 @@
 
         # Now try uploading an image file
         file_content = data_utils.random_bytes()
-        image_file = six.BytesIO(file_content)
+        image_file = io.BytesIO(file_content)
         self.client.store_image_file(image['id'], image_file)
 
         # Now try to get image details
@@ -317,7 +381,7 @@
 
         # Upload an image file
         content = data_utils.random_bytes()
-        image_file = six.BytesIO(content)
+        image_file = io.BytesIO(content)
         self.client.store_image_file(image['id'], image_file)
 
         # Deactivate image
@@ -367,7 +431,7 @@
         1024 and 4096
         """
         size = random.randint(1024, 4096)
-        image_file = six.BytesIO(data_utils.random_bytes(size))
+        image_file = io.BytesIO(data_utils.random_bytes(size))
         tags = [data_utils.rand_name('tag'), data_utils.rand_name('tag')]
         image = cls.create_image(container_format=container_format,
                                  disk_format=disk_format,
@@ -550,7 +614,7 @@
     def test_list_images_param_member_status(self):
         """Test listing images by member_status and visibility"""
         # Create an image to be shared using default visibility
-        image_file = six.BytesIO(data_utils.random_bytes(2048))
+        image_file = io.BytesIO(data_utils.random_bytes(2048))
         container_format = CONF.image.container_formats[0]
         disk_format = CONF.image.disk_formats[0]
         image = self.create_image(container_format=container_format,
diff --git a/tempest/api/image/v2/test_images_negative.py b/tempest/api/image/v2/test_images_negative.py
index dc2bb96..a3802a9 100644
--- a/tempest/api/image/v2/test_images_negative.py
+++ b/tempest/api/image/v2/test_images_negative.py
@@ -14,11 +14,16 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import time
+
 from tempest.api.image import base
+from tempest import config
 from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
 
+CONF = config.CONF
+
 
 class ImagesNegativeTest(base.BaseV2ImageTest):
 
@@ -114,3 +119,102 @@
         self.assertRaises(lib_exc.Forbidden,
                           self.client.delete_image,
                           image['id'])
+
+    @decorators.attr(type=['negative'])
+    @decorators.idempotent_id('a0ae75d4-ce9c-4576-b823-aba04c8acabd')
+    def test_update_image_reserved_property(self):
+        """Attempt to add a reserved property to an image.
+
+        Glance bans some internal-use-only properties such that they will
+        cause a PATCH to fail. Since os_glance_* is banned, we can use a
+        key in that namespace here.
+        """
+        if not CONF.image_feature_enabled.os_glance_reserved:
+            raise self.skipException('os_glance_reserved is not enabled')
+
+        image = self.create_image(name='test',
+                                  container_format='bare',
+                                  disk_format='raw')
+        self.assertRaises(lib_exc.Forbidden,
+                          self.client.update_image,
+                          image['id'], [{'add': '/os_glance_foo',
+                                         'value': 'bar'}])
+
+    @decorators.attr(type=['negative'])
+    @decorators.idempotent_id('e3fb7df8-2742-4143-8976-f1b26870f0a0')
+    def test_create_image_reserved_property(self):
+        """Attempt to create an image with a reserved property.
+
+        Glance bans some internal-use-only properties such that they will
+        cause an image create to fail. Since os_glance_* is banned, we can
+        use a key in that namespace here.
+        """
+        if not CONF.image_feature_enabled.os_glance_reserved:
+            raise self.skipException('os_glance_reserved is not enabled')
+
+        self.assertRaises(lib_exc.Forbidden,
+                          self.create_image,
+                          name='test',
+                          container_format='bare',
+                          disk_format='raw',
+                          os_glance_foo='bar')
+
+
+class ImportImagesNegativeTest(base.BaseV2ImageTest):
+    """Here we test the import operations for image"""
+
+    @classmethod
+    def skip_checks(cls):
+        super(ImportImagesNegativeTest, cls).skip_checks()
+        if not CONF.image_feature_enabled.import_image:
+            skip_msg = (
+                "%s skipped as image import is not available" % cls.__name__)
+            raise cls.skipException(skip_msg)
+
+    @classmethod
+    def resource_setup(cls):
+        super(ImportImagesNegativeTest, cls).resource_setup()
+        cls.available_import_methods = cls.client.info_import()[
+            'import-methods']['value']
+        if not cls.available_import_methods:
+            raise cls.skipException('Server does not support '
+                                    'any import method')
+
+        cls.available_stores = cls.get_available_stores()
+        if not len(cls.available_stores) > 0:
+            raise cls.skipException(
+                'No stores configured %s' % cls.available_stores)
+
+    @decorators.attr(type=['negative'])
+    @decorators.idempotent_id('c52f6a77-f522-4411-8dbe-9d14f2b1ba6b')
+    def test_image_web_download_import_with_bad_url(self):
+        """Test 'web-download' import functionalities
+
+        Make sure that web-download with invalid URL fails properly.
+        """
+        if 'web-download' not in self.available_import_methods:
+            raise self.skipException('Server does not support '
+                                     'web-download import method')
+        image = self.client.create_image(name='test',
+                                         container_format='bare',
+                                         disk_format='raw')
+        # Now try to get image details
+        body = self.client.show_image(image['id'])
+        self.assertEqual(image['id'], body['id'])
+        self.assertEqual('queued', body['status'])
+        stores = self.get_available_stores()
+        # import image from web to backend
+        image_uri = 'http://does-not.exist/no/possible/way'
+        self.client.image_import(image['id'], method='web-download',
+                                 image_uri=image_uri,
+                                 stores=[stores[0]['id']])
+
+        start_time = int(time.time())
+        while int(time.time()) - start_time < self.client.build_timeout:
+            body = self.client.show_image(image['id'])
+            if body.get('os_glance_failed_import'):
+                # Store ended up in failed list, which is good
+                return
+            time.sleep(self.client.build_interval)
+
+        self.fail('Image never reported failed store')
diff --git a/tempest/api/network/test_floating_ips.py b/tempest/api/network/test_floating_ips.py
index eb31d24..64f6e80 100644
--- a/tempest/api/network/test_floating_ips.py
+++ b/tempest/api/network/test_floating_ips.py
@@ -15,9 +15,9 @@
 
 from tempest.api.network import base
 from tempest.common import utils
-from tempest.common.utils import data_utils
 from tempest.common.utils import net_utils
 from tempest import config
+from tempest.lib.common.utils import data_utils
 from tempest.lib.common.utils import test_utils
 from tempest.lib import decorators
 
diff --git a/tempest/api/network/test_ports.py b/tempest/api/network/test_ports.py
index 479578d..63078cd 100644
--- a/tempest/api/network/test_ports.py
+++ b/tempest/api/network/test_ports.py
@@ -16,7 +16,6 @@
 import ipaddress
 
 import netaddr
-import six
 import testtools
 
 from tempest.api.network import base_security_groups as sec_base
@@ -234,15 +233,15 @@
         # Get two IP addresses
         ip_address_1 = None
         ip_address_2 = None
-        ip_network = ipaddress.ip_network(six.text_type(subnet['cidr']))
+        ip_network = ipaddress.ip_network(str(subnet['cidr']))
         for ip in ip_network:
             if ip == ip_network.network_address:
                 continue
             if ip_address_1 is None:
-                ip_address_1 = six.text_type(ip)
+                ip_address_1 = str(ip)
             else:
                 ip_address_2 = ip_address_1
-                ip_address_1 = six.text_type(ip)
+                ip_address_1 = str(ip)
                 # Make sure these two IP addresses have different substring
                 if ip_address_1[:-1] != ip_address_2[:-1]:
                     break
diff --git a/tempest/api/object_storage/base.py b/tempest/api/object_storage/base.py
index e8f3f8b..478a834 100644
--- a/tempest/api/object_storage/base.py
+++ b/tempest/api/object_storage/base.py
@@ -13,12 +13,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import time
-
 from tempest.common import custom_matchers
 from tempest import config
 from tempest.lib.common.utils import data_utils
-from tempest.lib.common.utils import test_utils
 from tempest.lib import exceptions as lib_exc
 import tempest.test
 
@@ -50,12 +47,11 @@
             _, objlist = container_client.list_container_objects(cont, params)
             # delete every object in the container
             for obj in objlist:
-                test_utils.call_and_ignore_notfound_exc(
-                    object_client.delete_object, cont, obj['name'])
-            # sleep 2 seconds to sync the deletion of the objects
-            # in HA deployment
-            time.sleep(2)
+                object_client.delete_object(cont, obj['name'])
+                object_client.wait_for_resource_deletion(obj['name'], cont)
+            # Verify resource deletion
             container_client.delete_container(cont)
+            container_client.wait_for_resource_deletion(cont)
         except lib_exc.NotFound:
             pass
 
diff --git a/tempest/api/object_storage/test_account_services.py b/tempest/api/object_storage/test_account_services.py
index da8ad66..ffea6f6 100644
--- a/tempest/api/object_storage/test_account_services.py
+++ b/tempest/api/object_storage/test_account_services.py
@@ -62,7 +62,7 @@
         self.assertIsNotNone(container_list)
 
         for container_name in self.containers:
-            self.assertIn(six.text_type(container_name).encode('utf-8'),
+            self.assertIn(str(container_name).encode('utf-8'),
                           container_list)
 
     @decorators.idempotent_id('884ec421-fbad-4fcc-916b-0580f2699565')
diff --git a/tempest/api/object_storage/test_container_sync.py b/tempest/api/object_storage/test_container_sync.py
index eb2ef7f..276b826 100644
--- a/tempest/api/object_storage/test_container_sync.py
+++ b/tempest/api/object_storage/test_container_sync.py
@@ -14,8 +14,8 @@
 #    under the License.
 
 import time
+from urllib import parse as urlparse
 
-from six.moves.urllib import parse as urlparse
 import testtools
 
 from tempest.api.object_storage import base
diff --git a/tempest/api/object_storage/test_object_formpost.py b/tempest/api/object_storage/test_object_formpost.py
index d857d3b..39e895e 100644
--- a/tempest/api/object_storage/test_object_formpost.py
+++ b/tempest/api/object_storage/test_object_formpost.py
@@ -15,8 +15,7 @@
 import hashlib
 import hmac
 import time
-
-from six.moves.urllib import parse as urlparse
+from urllib import parse as urlparse
 
 from tempest.api.object_storage import base
 from tempest.common import utils
diff --git a/tempest/api/object_storage/test_object_formpost_negative.py b/tempest/api/object_storage/test_object_formpost_negative.py
index 0499eef..971a223 100644
--- a/tempest/api/object_storage/test_object_formpost_negative.py
+++ b/tempest/api/object_storage/test_object_formpost_negative.py
@@ -15,8 +15,7 @@
 import hashlib
 import hmac
 import time
-
-from six.moves.urllib import parse as urlparse
+from urllib import parse as urlparse
 
 from tempest.api.object_storage import base
 from tempest.common import utils
diff --git a/tempest/api/object_storage/test_object_services.py b/tempest/api/object_storage/test_object_services.py
index fc9b1a2..93f6fdb 100644
--- a/tempest/api/object_storage/test_object_services.py
+++ b/tempest/api/object_storage/test_object_services.py
@@ -13,12 +13,12 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import hashlib
 import random
 import re
 import time
 import zlib
 
+from oslo_utils.secretutils import md5
 from tempest.api.object_storage import base
 from tempest.common import custom_matchers
 from tempest import config
@@ -151,8 +151,8 @@
         """Test creating object with Etag"""
         object_name = data_utils.rand_name(name='TestObject')
         data = data_utils.random_bytes()
-        md5 = hashlib.md5(data).hexdigest()
-        metadata = {'Etag': md5}
+        create_md5 = md5(data, usedforsecurity=False).hexdigest()
+        metadata = {'Etag': create_md5}
         resp, _ = self.object_client.create_object(
             self.container_name,
             object_name,
@@ -641,7 +641,7 @@
         """Test getting object with if_match"""
         object_name = data_utils.rand_name(name='TestObject')
         data = data_utils.random_bytes(10)
-        create_md5 = hashlib.md5(data).hexdigest()
+        create_md5 = md5(data, usedforsecurity=False).hexdigest()
         create_metadata = {'Etag': create_md5}
         self.object_client.create_object(self.container_name,
                                          object_name,
@@ -681,7 +681,7 @@
         """Test getting object with if_none_match"""
         object_name = data_utils.rand_name(name='TestObject')
         data = data_utils.random_bytes()
-        create_md5 = hashlib.md5(data).hexdigest()
+        create_md5 = md5(data, usedforsecurity=False).hexdigest()
         create_metadata = {'Etag': create_md5}
         self.object_client.create_object(self.container_name,
                                          object_name,
@@ -689,7 +689,7 @@
                                          metadata=create_metadata)
 
         list_data = data_utils.random_bytes()
-        list_md5 = hashlib.md5(list_data).hexdigest()
+        list_md5 = md5(list_data, usedforsecurity=False).hexdigest()
         list_metadata = {'If-None-Match': list_md5}
         resp, body = self.object_client.get_object(
             self.container_name,
@@ -978,8 +978,8 @@
         """
         object_name, data = self.create_object(self.container_name)
         # local copy is identical, no download
-        md5 = hashlib.md5(data).hexdigest()
-        headers = {'If-None-Match': md5}
+        object_md5 = md5(data, usedforsecurity=False).hexdigest()
+        headers = {'If-None-Match': object_md5}
         url = "%s/%s" % (self.container_name, object_name)
         resp, _ = self.object_client.get(url, headers=headers)
         self.assertEqual(resp['status'], '304')
@@ -993,8 +993,8 @@
 
         # local copy is different, download
         local_data = "something different"
-        md5 = hashlib.md5(local_data.encode()).hexdigest()
-        headers = {'If-None-Match': md5}
+        other_md5 = md5(local_data.encode(), usedforsecurity=False).hexdigest()
+        headers = {'If-None-Match': other_md5}
         resp, _ = self.object_client.get(url, headers=headers)
         self.assertHeaders(resp, 'Object', 'GET')
 
diff --git a/tempest/api/object_storage/test_object_slo.py b/tempest/api/object_storage/test_object_slo.py
index 664bbc8..0c84357 100644
--- a/tempest/api/object_storage/test_object_slo.py
+++ b/tempest/api/object_storage/test_object_slo.py
@@ -12,10 +12,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import hashlib
-
 from oslo_serialization import jsonutils as json
 
+from oslo_utils.secretutils import md5
 from tempest.api.object_storage import base
 from tempest.common import utils
 from tempest.lib.common.utils import data_utils
@@ -70,10 +69,12 @@
         path_object_2 = '/%s/%s' % (self.container_name,
                                     object_name_base_2)
         data_manifest = [{'path': path_object_1,
-                          'etag': hashlib.md5(self.content).hexdigest(),
+                          'etag': md5(self.content,
+                                      usedforsecurity=False).hexdigest(),
                           'size_bytes': data_size},
                          {'path': path_object_2,
-                          'etag': hashlib.md5(self.content).hexdigest(),
+                          'etag': md5(self.content,
+                                      usedforsecurity=False).hexdigest(),
                           'size_bytes': data_size}]
 
         return json.dumps(data_manifest)
diff --git a/tempest/api/object_storage/test_object_temp_url.py b/tempest/api/object_storage/test_object_temp_url.py
index 29354b6..e75e22a 100644
--- a/tempest/api/object_storage/test_object_temp_url.py
+++ b/tempest/api/object_storage/test_object_temp_url.py
@@ -15,8 +15,7 @@
 import hashlib
 import hmac
 import time
-
-from six.moves.urllib import parse as urlparse
+from urllib import parse as urlparse
 
 from tempest.api.object_storage import base
 from tempest.common import utils
diff --git a/tempest/api/object_storage/test_object_temp_url_negative.py b/tempest/api/object_storage/test_object_temp_url_negative.py
index bbb4827..4ad8428 100644
--- a/tempest/api/object_storage/test_object_temp_url_negative.py
+++ b/tempest/api/object_storage/test_object_temp_url_negative.py
@@ -15,8 +15,7 @@
 import hashlib
 import hmac
 import time
-
-from six.moves.urllib import parse as urlparse
+from urllib import parse as urlparse
 
 from tempest.api.object_storage import base
 from tempest.common import utils
diff --git a/tempest/api/volume/admin/test_group_snapshots.py b/tempest/api/volume/admin/test_group_snapshots.py
index 0a8b56d..659e2c4 100644
--- a/tempest/api/volume/admin/test_group_snapshots.py
+++ b/tempest/api/volume/admin/test_group_snapshots.py
@@ -64,7 +64,6 @@
 class GroupSnapshotsTest(BaseGroupSnapshotsTest):
     """Test group snapshot"""
 
-    _api_version = 3
     min_microversion = '3.14'
     max_microversion = 'latest'
 
@@ -253,7 +252,6 @@
 class GroupSnapshotsV319Test(BaseGroupSnapshotsTest):
     """Test group snapshot with volume microversion greater than 3.18"""
 
-    _api_version = 3
     min_microversion = '3.19'
     max_microversion = 'latest'
 
diff --git a/tempest/api/volume/admin/test_group_type_specs.py b/tempest/api/volume/admin/test_group_type_specs.py
index 159c6fb..5c5913e 100644
--- a/tempest/api/volume/admin/test_group_type_specs.py
+++ b/tempest/api/volume/admin/test_group_type_specs.py
@@ -21,7 +21,6 @@
 class GroupTypeSpecsTest(base.BaseVolumeAdminTest):
     """Test group type specs"""
 
-    _api_version = 3
     min_microversion = '3.11'
     max_microversion = 'latest'
 
diff --git a/tempest/api/volume/admin/test_group_types.py b/tempest/api/volume/admin/test_group_types.py
index 3993020..a7a5d6f 100644
--- a/tempest/api/volume/admin/test_group_types.py
+++ b/tempest/api/volume/admin/test_group_types.py
@@ -21,7 +21,6 @@
 class GroupTypesTest(base.BaseVolumeAdminTest):
     """Test group types"""
 
-    _api_version = 3
     min_microversion = '3.11'
     max_microversion = 'latest'
 
diff --git a/tempest/api/volume/admin/test_groups.py b/tempest/api/volume/admin/test_groups.py
index e67b985..747a194 100644
--- a/tempest/api/volume/admin/test_groups.py
+++ b/tempest/api/volume/admin/test_groups.py
@@ -25,7 +25,6 @@
 class GroupsTest(base.BaseVolumeAdminTest):
     """Tests of volume groups with microversion greater than 3.12"""
 
-    _api_version = 3
     min_microversion = '3.13'
     max_microversion = 'latest'
 
@@ -156,7 +155,6 @@
 class GroupsV314Test(base.BaseVolumeAdminTest):
     """Tests of volume groups with microversion greater than 3.13"""
 
-    _api_version = 3
     min_microversion = '3.14'
     max_microversion = 'latest'
 
@@ -194,7 +192,6 @@
 class GroupsV320Test(base.BaseVolumeAdminTest):
     """Tests of volume groups with microversion greater than 3.19"""
 
-    _api_version = 3
     min_microversion = '3.20'
     max_microversion = 'latest'
 
diff --git a/tempest/api/volume/admin/test_user_messages.py b/tempest/api/volume/admin/test_user_messages.py
index 096709c..768c129 100644
--- a/tempest/api/volume/admin/test_user_messages.py
+++ b/tempest/api/volume/admin/test_user_messages.py
@@ -24,7 +24,6 @@
 class UserMessagesTest(base.BaseVolumeAdminTest):
     """Test volume messages with microversion greater than 3.2"""
 
-    _api_version = 3
     min_microversion = '3.3'
     max_microversion = 'latest'
 
diff --git a/tempest/api/volume/base.py b/tempest/api/volume/base.py
index c538e60..9f9fc3b 100644
--- a/tempest/api/volume/base.py
+++ b/tempest/api/volume/base.py
@@ -20,7 +20,6 @@
 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
 import tempest.test
 
 CONF = config.CONF
@@ -33,12 +32,6 @@
     # Set this to True in subclasses to create a default network. See
     # https://bugs.launchpad.net/tempest/+bug/1844568
     create_default_network = False
-    _api_version = 2
-    # if api_v2 is not enabled while api_v3 is enabled, the volume v2 classes
-    # should be transferred to volume v3 classes.
-    if (not CONF.volume_feature_enabled.api_v2 and
-        CONF.volume_feature_enabled.api_v3):
-        _api_version = 3
     credentials = ['primary']
 
     @classmethod
@@ -48,17 +41,6 @@
         if not CONF.service_available.cinder:
             skip_msg = ("%s skipped as Cinder is not available" % cls.__name__)
             raise cls.skipException(skip_msg)
-        if cls._api_version == 2:
-            if not CONF.volume_feature_enabled.api_v2:
-                msg = "Volume API v2 is disabled"
-                raise cls.skipException(msg)
-        elif cls._api_version == 3:
-            if not CONF.volume_feature_enabled.api_v3:
-                msg = "Volume API v3 is disabled"
-                raise cls.skipException(msg)
-        else:
-            msg = ("Invalid Cinder API version (%s)" % cls._api_version)
-            raise exceptions.InvalidConfiguration(msg)
 
         api_version_utils.check_skip_with_microversion(
             cls.min_microversion, cls.max_microversion,
@@ -129,6 +111,13 @@
             name = data_utils.rand_name(cls.__name__ + '-Volume')
             kwargs['name'] = name
 
+        if CONF.volume.volume_type and 'volume_type' not in kwargs:
+            # If volume_type is not provided in config then no need to
+            # add a volume type and
+            # if volume_type has already been added by child class then
+            # no need to override.
+            kwargs['volume_type'] = CONF.volume.volume_type
+
         if CONF.compute.compute_volume_common_az:
             kwargs.setdefault('availability_zone',
                               CONF.compute.compute_volume_common_az)
diff --git a/tempest/api/volume/test_versions.py b/tempest/api/volume/test_versions.py
index e065bdf..578be58 100644
--- a/tempest/api/volume/test_versions.py
+++ b/tempest/api/volume/test_versions.py
@@ -19,8 +19,6 @@
 class VersionsTest(base.BaseVolumeTest):
     """Test volume versions"""
 
-    _api_version = 3
-
     @decorators.idempotent_id('77838fc4-b49b-4c64-9533-166762517369')
     @decorators.attr(type='smoke')
     def test_list_versions(self):
diff --git a/tempest/api/volume/test_volumes_backup.py b/tempest/api/volume/test_volumes_backup.py
index 2e78114..fff6a44 100644
--- a/tempest/api/volume/test_volumes_backup.py
+++ b/tempest/api/volume/test_volumes_backup.py
@@ -164,7 +164,6 @@
 class VolumesBackupsV39Test(base.BaseVolumeTest):
     """Test volumes backup with volume microversion greater than 3.8"""
 
-    _api_version = 3
     min_microversion = '3.9'
     max_microversion = 'latest'
 
diff --git a/tempest/api/volume/test_volumes_extend.py b/tempest/api/volume/test_volumes_extend.py
index 7441f1d..d9790f3 100644
--- a/tempest/api/volume/test_volumes_extend.py
+++ b/tempest/api/volume/test_volumes_extend.py
@@ -77,7 +77,6 @@
     # details once that microversion is available in Nova.
     credentials = ['primary', 'admin']
 
-    _api_version = 3
     # NOTE(mriedem): The minimum required volume API version is 3.42 and the
     # minimum required compute API microversion is 2.51, but the compute call
     # is implicit - Cinder calls Nova at that microversion, Tempest does not.
diff --git a/tempest/api/volume/test_volumes_get.py b/tempest/api/volume/test_volumes_get.py
index 91728ab..28e41bf 100644
--- a/tempest/api/volume/test_volumes_get.py
+++ b/tempest/api/volume/test_volumes_get.py
@@ -143,7 +143,6 @@
 class VolumesSummaryTest(base.BaseVolumeTest):
     """Test volume summary"""
 
-    _api_version = 3
     min_microversion = '3.12'
     max_microversion = 'latest'
 
diff --git a/tempest/api/volume/test_volumes_list.py b/tempest/api/volume/test_volumes_list.py
index 60f85a4..1d1981c 100644
--- a/tempest/api/volume/test_volumes_list.py
+++ b/tempest/api/volume/test_volumes_list.py
@@ -16,8 +16,8 @@
 
 import operator
 import random
+from urllib.parse import urlparse
 
-from six.moves.urllib.parse import urlparse
 from testtools import matchers
 
 from tempest.api.volume import base
diff --git a/tempest/api/volume/test_volumes_negative.py b/tempest/api/volume/test_volumes_negative.py
index 389d3be..35dd0ca 100644
--- a/tempest/api/volume/test_volumes_negative.py
+++ b/tempest/api/volume/test_volumes_negative.py
@@ -13,7 +13,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import six
+import io
 
 from tempest.api.volume import base
 from tempest.common import utils
@@ -50,7 +50,7 @@
                         self.images_client.delete_image, image['id'])
 
         # Upload image with 1KB data
-        image_file = six.BytesIO(data_utils.random_bytes())
+        image_file = io.BytesIO(data_utils.random_bytes())
         self.images_client.store_image_file(image['id'], image_file)
         waiters.wait_for_image_status(self.images_client,
                                       image['id'], 'active')
diff --git a/tempest/clients.py b/tempest/clients.py
index 6d19a0c..c4e00fe 100644
--- a/tempest/clients.py
+++ b/tempest/clients.py
@@ -88,6 +88,14 @@
                 self.image_v2.NamespacePropertiesClient()
             self.namespace_tags_client = self.image_v2.NamespaceTagsClient()
             self.image_versions_client = self.image_v2.VersionsClient()
+            # NOTE(danms): If no alternate endpoint is configured,
+            # this client will work the same as the base self.images_client.
+            # If your test needs to know if these are different, check the
+            # config option to see if the alternate_image_endpoint is set.
+            self.image_client_remote = self.image_v2.ImagesClient(
+                service=CONF.image.alternate_image_endpoint,
+                endpoint_type=CONF.image.alternate_image_endpoint_type,
+                region=CONF.image.region)
 
     def _set_compute_clients(self):
         self.agents_client = self.compute.AgentsClient()
@@ -125,6 +133,8 @@
         self.instance_usages_audit_log_client = (
             self.compute.InstanceUsagesAuditLogClient())
         self.tenant_networks_client = self.compute.TenantNetworksClient()
+        self.assisted_volume_snapshots_client = (
+            self.compute.AssistedVolumeSnapshotsClient())
 
         # NOTE: The following client needs special timeout values because
         # the API is a proxy for the other component.
@@ -231,89 +241,85 @@
 
     def _set_volume_clients(self):
 
-        # if only api_v3 is enabled, all these clients should be available
-        if (CONF.volume_feature_enabled.api_v2 or
-            CONF.volume_feature_enabled.api_v3):
-            self.backups_client_latest = self.volume_v3.BackupsClient()
-            self.encryption_types_client_latest = \
-                self.volume_v3.EncryptionTypesClient()
-            self.snapshot_manage_client_latest = \
-                self.volume_v3.SnapshotManageClient()
-            self.snapshots_client_latest = self.volume_v3.SnapshotsClient()
-            self.volume_capabilities_client_latest = \
-                self.volume_v3.CapabilitiesClient()
-            self.volume_manage_client_latest = (
-                self.volume_v3.VolumeManageClient())
-            self.volume_qos_client_latest = self.volume_v3.QosSpecsClient()
-            self.volume_services_client_latest = (
-                self.volume_v3.ServicesClient())
-            self.volume_types_client_latest = self.volume_v3.TypesClient()
-            self.volume_hosts_client_latest = self.volume_v3.HostsClient()
-            self.volume_quotas_client_latest = self.volume_v3.QuotasClient()
-            self.volume_quota_classes_client_latest = \
-                self.volume_v3.QuotaClassesClient()
-            self.volume_scheduler_stats_client_latest = \
-                self.volume_v3.SchedulerStatsClient()
-            self.volume_transfers_client_latest = \
-                self.volume_v3.TransfersClient()
-            self.volume_availability_zone_client_latest = \
-                self.volume_v3.AvailabilityZoneClient()
-            self.volume_limits_client_latest = self.volume_v3.LimitsClient()
-            self.volumes_client_latest = self.volume_v3.VolumesClient()
-            self.volumes_extension_client_latest = \
-                self.volume_v3.ExtensionsClient()
-            self.group_types_client_latest = self.volume_v3.GroupTypesClient()
-            self.groups_client_latest = self.volume_v3.GroupsClient()
-            self.group_snapshots_client_latest = \
-                self.volume_v3.GroupSnapshotsClient()
-            self.volume_messages_client_latest = (
-                self.volume_v3.MessagesClient())
-            self.volume_versions_client_latest = (
-                self.volume_v3.VersionsClient())
-            self.attachments_client_latest = (
-                self.volume_v3.AttachmentsClient())
+        self.backups_client_latest = self.volume_v3.BackupsClient()
+        self.encryption_types_client_latest = \
+            self.volume_v3.EncryptionTypesClient()
+        self.snapshot_manage_client_latest = \
+            self.volume_v3.SnapshotManageClient()
+        self.snapshots_client_latest = self.volume_v3.SnapshotsClient()
+        self.volume_capabilities_client_latest = \
+            self.volume_v3.CapabilitiesClient()
+        self.volume_manage_client_latest = (
+            self.volume_v3.VolumeManageClient())
+        self.volume_qos_client_latest = self.volume_v3.QosSpecsClient()
+        self.volume_services_client_latest = (
+            self.volume_v3.ServicesClient())
+        self.volume_types_client_latest = self.volume_v3.TypesClient()
+        self.volume_hosts_client_latest = self.volume_v3.HostsClient()
+        self.volume_quotas_client_latest = self.volume_v3.QuotasClient()
+        self.volume_quota_classes_client_latest = \
+            self.volume_v3.QuotaClassesClient()
+        self.volume_scheduler_stats_client_latest = \
+            self.volume_v3.SchedulerStatsClient()
+        self.volume_transfers_client_latest = \
+            self.volume_v3.TransfersClient()
+        self.volume_availability_zone_client_latest = \
+            self.volume_v3.AvailabilityZoneClient()
+        self.volume_limits_client_latest = self.volume_v3.LimitsClient()
+        self.volumes_client_latest = self.volume_v3.VolumesClient()
+        self.volumes_extension_client_latest = \
+            self.volume_v3.ExtensionsClient()
+        self.group_types_client_latest = self.volume_v3.GroupTypesClient()
+        self.groups_client_latest = self.volume_v3.GroupsClient()
+        self.group_snapshots_client_latest = \
+            self.volume_v3.GroupSnapshotsClient()
+        self.volume_messages_client_latest = (
+            self.volume_v3.MessagesClient())
+        self.volume_versions_client_latest = (
+            self.volume_v3.VersionsClient())
+        self.attachments_client_latest = (
+            self.volume_v3.AttachmentsClient())
 
-            # TODO(gmann): Below alias for service clients have been
-            # deprecated and will be removed in future. Start using the alias
-            # defined above with suffix _latest.
-            # ****************Deprecated alias start from here***************
-            self.backups_v2_client = self.volume_v3.BackupsClient()
-            self.encryption_types_v2_client = \
-                self.volume_v3.EncryptionTypesClient()
-            self.snapshot_manage_v2_client = \
-                self.volume_v3.SnapshotManageClient()
-            self.snapshots_v2_client = self.volume_v3.SnapshotsClient()
-            self.volume_capabilities_v2_client = \
-                self.volume_v3.CapabilitiesClient()
-            self.volume_manage_v2_client = self.volume_v3.VolumeManageClient()
-            self.volume_qos_v2_client = self.volume_v3.QosSpecsClient()
-            self.volume_services_v2_client = self.volume_v3.ServicesClient()
-            self.volume_types_v2_client = self.volume_v3.TypesClient()
-            self.volume_hosts_v2_client = self.volume_v3.HostsClient()
-            self.volume_quotas_v2_client = self.volume_v3.QuotasClient()
-            self.volume_quota_classes_v2_client = \
-                self.volume_v3.QuotaClassesClient()
-            self.volume_scheduler_stats_v2_client = \
-                self.volume_v3.SchedulerStatsClient()
-            self.volume_transfers_v2_client = self.volume_v3.TransfersClient()
-            self.volume_v2_availability_zone_client = \
-                self.volume_v3.AvailabilityZoneClient()
-            self.volume_v2_limits_client = self.volume_v3.LimitsClient()
-            self.volumes_v2_client = self.volume_v3.VolumesClient()
-            self.volumes_v2_extension_client = \
-                self.volume_v3.ExtensionsClient()
+        # TODO(gmann): Below alias for service clients have been
+        # deprecated and will be removed in future. Start using the alias
+        # defined above with suffix _latest.
+        # ****************Deprecated alias start from here***************
+        self.backups_v2_client = self.volume_v3.BackupsClient()
+        self.encryption_types_v2_client = \
+            self.volume_v3.EncryptionTypesClient()
+        self.snapshot_manage_v2_client = \
+            self.volume_v3.SnapshotManageClient()
+        self.snapshots_v2_client = self.volume_v3.SnapshotsClient()
+        self.volume_capabilities_v2_client = \
+            self.volume_v3.CapabilitiesClient()
+        self.volume_manage_v2_client = self.volume_v3.VolumeManageClient()
+        self.volume_qos_v2_client = self.volume_v3.QosSpecsClient()
+        self.volume_services_v2_client = self.volume_v3.ServicesClient()
+        self.volume_types_v2_client = self.volume_v3.TypesClient()
+        self.volume_hosts_v2_client = self.volume_v3.HostsClient()
+        self.volume_quotas_v2_client = self.volume_v3.QuotasClient()
+        self.volume_quota_classes_v2_client = \
+            self.volume_v3.QuotaClassesClient()
+        self.volume_scheduler_stats_v2_client = \
+            self.volume_v3.SchedulerStatsClient()
+        self.volume_transfers_v2_client = self.volume_v3.TransfersClient()
+        self.volume_v2_availability_zone_client = \
+            self.volume_v3.AvailabilityZoneClient()
+        self.volume_v2_limits_client = self.volume_v3.LimitsClient()
+        self.volumes_v2_client = self.volume_v3.VolumesClient()
+        self.volumes_v2_extension_client = \
+            self.volume_v3.ExtensionsClient()
 
-        if CONF.volume_feature_enabled.api_v3:
-            self.backups_v3_client = self.volume_v3.BackupsClient()
-            self.group_types_v3_client = self.volume_v3.GroupTypesClient()
-            self.groups_v3_client = self.volume_v3.GroupsClient()
-            self.group_snapshots_v3_client = \
-                self.volume_v3.GroupSnapshotsClient()
-            self.snapshots_v3_client = self.volume_v3.SnapshotsClient()
-            self.volume_v3_messages_client = self.volume_v3.MessagesClient()
-            self.volume_v3_versions_client = self.volume_v3.VersionsClient()
-            self.volumes_v3_client = self.volume_v3.VolumesClient()
-            # ****************Deprecated alias end here***********************
+        self.backups_v3_client = self.volume_v3.BackupsClient()
+        self.group_types_v3_client = self.volume_v3.GroupTypesClient()
+        self.groups_v3_client = self.volume_v3.GroupsClient()
+        self.group_snapshots_v3_client = \
+            self.volume_v3.GroupSnapshotsClient()
+        self.snapshots_v3_client = self.volume_v3.SnapshotsClient()
+        self.volume_v3_messages_client = self.volume_v3.MessagesClient()
+        self.volume_v3_versions_client = self.volume_v3.VersionsClient()
+        self.volumes_v3_client = self.volume_v3.VolumesClient()
+        # ****************Deprecated alias end here***********************
 
     def _set_object_storage_clients(self):
         self.account_client = self.object_storage.AccountClient()
diff --git a/tempest/cmd/cleanup_service.py b/tempest/cmd/cleanup_service.py
index 84d2492..f2370f3 100644
--- a/tempest/cmd/cleanup_service.py
+++ b/tempest/cmd/cleanup_service.py
@@ -12,8 +12,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from urllib import parse as urllib
+
 from oslo_log import log as logging
-from six.moves.urllib import parse as urllib
 
 from tempest import clients
 from tempest.common import credentials_factory as credentials
diff --git a/tempest/cmd/init.py b/tempest/cmd/init.py
index 6e93d69..3eae552 100644
--- a/tempest/cmd/init.py
+++ b/tempest/cmd/init.py
@@ -12,6 +12,7 @@
 # License for the specific language governing permissions and limitations
 # under the License.
 
+import configparser
 import os
 import shutil
 import sys
@@ -19,7 +20,6 @@
 from cliff import command
 from oslo_config import generator
 from oslo_log import log as logging
-from six import moves
 from stestr import commands
 
 from tempest.cmd import workspace
@@ -92,7 +92,7 @@
             stestr_conf_file.write(stestr_conf)
 
     def get_configparser(self, conf_path):
-        config_parse = moves.configparser.ConfigParser()
+        config_parse = configparser.ConfigParser()
         config_parse.optionxform = str
         # get any existing values if a config file already exists
         if os.path.isfile(conf_path):
diff --git a/tempest/cmd/subunit_describe_calls.py b/tempest/cmd/subunit_describe_calls.py
index 172fbaa..6c36d82 100644
--- a/tempest/cmd/subunit_describe_calls.py
+++ b/tempest/cmd/subunit_describe_calls.py
@@ -40,12 +40,14 @@
 
 subunit-describe-calls will take in either stdin subunit v1 or v2 stream or a
 file path which contains either a subunit v1 or v2 stream passed via the
---subunit parameter. This is then parsed checking for details contained in the
-file_bytes of the --non-subunit-name parameter (the default is pythonlogging
-which is what Tempest uses to store logs). By default the OpenStack Kilo
-release port defaults (http://bit.ly/22jpF5P) are used unless a file is
-provided via the --ports option. The resulting output is dumped in JSON output
-to the path provided in the --output-file option.
+``--subunit`` parameter. This is then parsed checking for details contained in
+the file_bytes of the ``--non-subunit-name`` parameter (the default is
+pythonlogging which is what Tempest uses to store logs). By default `the
+OpenStack default ports
+<https://docs.openstack.org/install-guide/firewalls-default-ports.html>`_
+are used unless a file is provided via the ``--ports`` option. The resulting
+output is dumped in JSON output to the path provided in the ``--output-file``
+option.
 
 Ports file JSON structure
 ^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -110,9 +112,8 @@
     response_re = re.compile(r'.* Response - Headers: (?P<headers>.*)')
     body_re = re.compile(r'.*Body: (?P<body>.*)')
 
-    # Based on newton defaults:
-    # http://docs.openstack.org/newton/config-reference/
-    # firewalls-default-ports.html
+    # Based on OpenStack default ports:
+    # https://docs.openstack.org/install-guide/firewalls-default-ports.html
     services = {
         "8776": "Block Storage",
         "8774": "Nova",
@@ -131,7 +132,10 @@
         "3260": "iSCSI",
         "3306": "MySQL",
         "5672": "AMQP",
-        "8082": "murano"}
+        "8082": "murano",
+        "8778": "Clustering",
+        "8999": "Vitrage",
+        "8989": "Mistral"}
 
     def __init__(self, services=None):
         super(UrlParser, self).__init__()
diff --git a/tempest/cmd/verify_tempest_config.py b/tempest/cmd/verify_tempest_config.py
index 235d8e3..0db1ab1 100644
--- a/tempest/cmd/verify_tempest_config.py
+++ b/tempest/cmd/verify_tempest_config.py
@@ -202,22 +202,8 @@
                             not CONF.identity_feature_enabled.api_v3, update)
 
 
-def verify_cinder_api_versions(os, update):
-    # Check cinder api versions
-    versions = _get_api_versions(os, 'cinder')
-    if (CONF.volume_feature_enabled.api_v2 !=
-            contains_version('v2.', versions)):
-        print_and_or_update('api_v2', 'volume-feature-enabled',
-                            not CONF.volume_feature_enabled.api_v2, update)
-    if (CONF.volume_feature_enabled.api_v3 !=
-            contains_version('v3.', versions)):
-        print_and_or_update('api_v3', 'volume-feature-enabled',
-                            not CONF.volume_feature_enabled.api_v3, update)
-
-
 def verify_api_versions(os, service, update):
     verify = {
-        'cinder': verify_cinder_api_versions,
         'glance': verify_glance_api_versions,
         'keystone': verify_keystone_api_versions,
     }
diff --git a/tempest/common/compute.py b/tempest/common/compute.py
index 42f68f1..a062f6f 100644
--- a/tempest/common/compute.py
+++ b/tempest/common/compute.py
@@ -18,8 +18,7 @@
 import ssl
 import struct
 import textwrap
-
-from six.moves.urllib import parse as urlparse
+from urllib import parse as urlparse
 
 from oslo_log import log as logging
 from oslo_utils import excutils
diff --git a/tempest/common/utils/__init__.py b/tempest/common/utils/__init__.py
index 38881ee..88a16b7 100644
--- a/tempest/common/utils/__init__.py
+++ b/tempest/common/utils/__init__.py
@@ -13,37 +13,17 @@
 #    under the License.
 
 import functools
-from functools import partial
 
 import testtools
 
 from tempest import config
 from tempest.exceptions import InvalidServiceTag
-from tempest.lib.common.utils import data_utils as lib_data_utils
 from tempest.lib import decorators
 
 
 CONF = config.CONF
 
 
-class DataUtils(object):
-    def __getattr__(self, attr):
-
-        if attr == 'rand_name':
-            # NOTE(flwang): This is a proxy to generate a random name that
-            # includes a random number and a prefix 'tempest'
-            attr_obj = partial(lib_data_utils.rand_name,
-                               prefix='tempest')
-        else:
-            attr_obj = getattr(lib_data_utils, attr)
-
-        self.__dict__[attr] = attr_obj
-        return attr_obj
-
-
-data_utils = DataUtils()
-
-
 def get_service_list():
     service_list = {
         'compute': CONF.service_available.nova,
diff --git a/tempest/config.py b/tempest/config.py
index 31d9b1b..c409db6 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -636,6 +636,10 @@
                                   "APIs that only worked with that driver "
                                   "have been removed and there's nothing to "
                                   "test after Ussuri."),
+    cfg.BoolOpt('ide_bus',
+                default=True,
+                help='Does the test environment support attaching devices '
+                     'using an IDE bus to the instance?'),
 ]
 
 
@@ -657,6 +661,15 @@
                choices=['public', 'admin', 'internal',
                         'publicURL', 'adminURL', 'internalURL'],
                help="The endpoint type to use for the image service."),
+    cfg.StrOpt('alternate_image_endpoint',
+               default=None,
+               help="Alternate endpoint name for cross-worker testing"),
+    cfg.StrOpt('alternate_image_endpoint_type',
+               default='publicURL',
+               choices=['public', 'admin', 'internal',
+                        'publicURL', 'adminURL', 'internalURL'],
+               help=("The endpoint type to use for the alternate image "
+                     "service.")),
     cfg.StrOpt('http_image',
                default='http://download.cirros-cloud.net/0.3.1/'
                'cirros-0.3.1-x86_64-uec.tar.gz',
@@ -706,6 +719,13 @@
     cfg.BoolOpt('import_image',
                 default=False,
                 help="Is image import feature enabled"),
+    # NOTE(danms): Starting mid-Wallaby glance began enforcing the
+    # previously-informal requirement that os_glance_* properties are
+    # reserved for internal use. Thus, we can only run these checks
+    # if we know we are on a new enough glance.
+    cfg.BoolOpt('os_glance_reserved',
+                default=False,
+                help="Should we check that os_glance namespace is reserved"),
 ]
 
 network_group = cfg.OptGroup(name='network',
@@ -965,6 +985,9 @@
                 default=['BACKEND_1', 'BACKEND_2'],
                 help='A list of backend names separated by comma. '
                      'The backend name must be declared in cinder.conf'),
+    cfg.StrOpt('volume_type',
+               default='',
+               help='Volume type to be used while creating volume.'),
     cfg.StrOpt('storage_protocol',
                default='iSCSI',
                help='Backend protocol to target when creating volume types'),
@@ -1036,31 +1059,6 @@
                 help='A list of enabled volume extensions with a special '
                      'entry all which indicates every extension is enabled. '
                      'Empty list indicates all extensions are disabled'),
-    cfg.BoolOpt('api_v2',
-                default=True,
-                help="Is the v2 volume API enabled",
-                deprecated_for_removal=True,
-                deprecated_reason="The v2 volume API has been deprecated "
-                                  "since Pike release. Now Tempest run all "
-                                  "the volume tests against v2 or v3 API "
-                                  "based on CONF.volume.catalog_type which "
-                                  "makes this config option unusable. If "
-                                  "catalog_type is volumev2, then all the "
-                                  "volume tests will run against v2 API. "
-                                  "Use ``CONF.volume.catalog_type`` to run "
-                                  "the Tempest against volume v2 or v3 API"),
-    cfg.BoolOpt('api_v3',
-                default=True,
-                help="Is the v3 volume API enabled",
-                deprecated_for_removal=True,
-                deprecated_reason="Tempest run all the volume tests against "
-                                  "v2 or v3 API based on "
-                                  "CONF.volume.catalog_type which makes this "
-                                  "config option unusable. If catalog_type is "
-                                  "volumev3 which is default, then all the "
-                                  "volume tests will run against v3 API. "
-                                  "Use ``CONF.volume.catalog_type`` to run "
-                                  "the Tempest against volume v2 or v3 API"),
     cfg.BoolOpt('extend_attached_volume',
                 default=False,
                 help='Does the cloud support extending the size of a volume '
@@ -1150,12 +1148,6 @@
 scenario_group = cfg.OptGroup(name='scenario', title='Scenario Test Options')
 
 ScenarioGroup = [
-    cfg.StrOpt('img_dir',
-               default='/opt/stack/new/devstack/files/images/'
-               'cirros-0.3.1-x86_64-uec',
-               help='Directory containing image files, this has been '
-                    'deprecated - img_file option contains a full path now.',
-               deprecated_for_removal=True),
     cfg.StrOpt('img_file', deprecated_name='qcow2_img_file',
                default='/opt/stack/new/devstack/files/images'
                '/cirros-0.3.1-x86_64-disk.img',
diff --git a/tempest/lib/auth.py b/tempest/lib/auth.py
index 9f8c7c5..8bdf98e 100644
--- a/tempest/lib/auth.py
+++ b/tempest/lib/auth.py
@@ -18,10 +18,9 @@
 import copy
 import datetime
 import re
+from urllib import parse as urlparse
 
 from oslo_log import log as logging
-import six
-from six.moves.urllib import parse as urlparse
 
 from tempest.lib import exceptions
 from tempest.lib.services.identity.v2 import token_client as json_v2id
@@ -65,8 +64,7 @@
     return url
 
 
-@six.add_metaclass(abc.ABCMeta)
-class AuthProvider(object):
+class AuthProvider(object, metaclass=abc.ABCMeta):
     """Provide authentication"""
 
     SCOPES = set(['project'])
diff --git a/tempest/lib/cmd/check_uuid.py b/tempest/lib/cmd/check_uuid.py
index ff09671..0ae11ca 100755
--- a/tempest/lib/cmd/check_uuid.py
+++ b/tempest/lib/cmd/check_uuid.py
@@ -22,10 +22,10 @@
 import os
 import sys
 import unittest
+import urllib.parse as urlparse
 import uuid
 
 from oslo_utils import uuidutils
-import six.moves.urllib.parse as urlparse
 
 DECORATOR_MODULE = 'decorators'
 DECORATOR_NAME = 'idempotent_id'
diff --git a/tempest/lib/common/cred_client.py b/tempest/lib/common/cred_client.py
index e16a565..f13d6d0 100644
--- a/tempest/lib/common/cred_client.py
+++ b/tempest/lib/common/cred_client.py
@@ -13,7 +13,6 @@
 import abc
 
 from oslo_log import log as logging
-import six
 
 from tempest.lib import auth
 from tempest.lib import exceptions as lib_exc
@@ -22,8 +21,7 @@
 LOG = logging.getLogger(__name__)
 
 
-@six.add_metaclass(abc.ABCMeta)
-class CredsClient(object):
+class CredsClient(object, metaclass=abc.ABCMeta):
     """This class is a wrapper around the identity clients
 
      to provide a single interface for managing credentials in both v2 and v3
@@ -173,6 +171,22 @@
         self.domains_client.update_domain(domain_id, enabled=False)
         self.domains_client.delete_domain(domain_id)
 
+    def create_user(self, username, password, project=None, email=None,
+                    domain_id=None):
+        params = {'name': username,
+                  'password': password,
+                  'domain_id': domain_id or self.creds_domain['id']}
+        # with keystone v3, a default project is not required
+        if project:
+            params[self.project_id_param] = project['id']
+        # email is not a first-class attribute of a user
+        if email:
+            params['email'] = email
+        user = self.users_client.create_user(**params)
+        if 'user' in user:
+            user = user['user']
+        return user
+
     def get_credentials(
             self, user, project, password, domain=None, system=None):
         # User, project and domain already include both ID and name here,
diff --git a/tempest/lib/common/cred_provider.py b/tempest/lib/common/cred_provider.py
index 42ed41b..069172a 100644
--- a/tempest/lib/common/cred_provider.py
+++ b/tempest/lib/common/cred_provider.py
@@ -14,14 +14,12 @@
 
 import abc
 
-import six
 
 from tempest.lib import auth
 from tempest.lib import exceptions
 
 
-@six.add_metaclass(abc.ABCMeta)
-class CredentialProvider(object):
+class CredentialProvider(object, metaclass=abc.ABCMeta):
     def __init__(self, identity_version, name=None,
                  network_resources=None, credentials_domain=None,
                  admin_role=None, identity_uri=None):
@@ -60,6 +58,54 @@
         return
 
     @abc.abstractmethod
+    def get_system_admin_creds(self):
+        return
+
+    @abc.abstractmethod
+    def get_system_member_creds(self):
+        return
+
+    @abc.abstractmethod
+    def get_system_reader_creds(self):
+        return
+
+    @abc.abstractmethod
+    def get_domain_admin_creds(self):
+        return
+
+    @abc.abstractmethod
+    def get_domain_member_creds(self):
+        return
+
+    @abc.abstractmethod
+    def get_domain_reader_creds(self):
+        return
+
+    @abc.abstractmethod
+    def get_project_admin_creds(self):
+        return
+
+    @abc.abstractmethod
+    def get_project_alt_admin_creds(self):
+        return
+
+    @abc.abstractmethod
+    def get_project_member_creds(self):
+        return
+
+    @abc.abstractmethod
+    def get_project_alt_member_creds(self):
+        return
+
+    @abc.abstractmethod
+    def get_project_reader_creds(self):
+        return
+
+    @abc.abstractmethod
+    def get_project_alt_reader_creds(self):
+        return
+
+    @abc.abstractmethod
     def clear_creds(self):
         return
 
@@ -72,7 +118,7 @@
         return
 
     @abc.abstractmethod
-    def get_creds_by_roles(self, roles, force_new=False):
+    def get_creds_by_roles(self, roles, force_new=False, scope=None):
         return
 
     @abc.abstractmethod
diff --git a/tempest/lib/common/dynamic_creds.py b/tempest/lib/common/dynamic_creds.py
index ecbbe8f..f334c36 100644
--- a/tempest/lib/common/dynamic_creds.py
+++ b/tempest/lib/common/dynamic_creds.py
@@ -228,8 +228,9 @@
         roles_to_assign = [r for r in roles]
         if admin:
             roles_to_assign.append(self.admin_role)
-            self.creds_client.assign_user_role(
-                user, project, self.identity_admin_role)
+            if scope == 'project':
+                self.creds_client.assign_user_role(
+                    user, project, self.identity_admin_role)
             if (self.identity_version == 'v3' and
                     self.identity_admin_domain_scope):
                 self.creds_client.assign_user_role_on_domain(
@@ -254,6 +255,8 @@
                     user, role, domain)
             elif scope == 'system':
                 self.creds_client.assign_user_role_on_system(user, role)
+        LOG.info("Roles assigned to the user %s are: %s",
+                 user['id'], roles_to_assign)
 
         creds = self.creds_client.get_credentials(**cred_params)
         return cred_provider.TestResources(creds)
@@ -339,7 +342,7 @@
                             tenant_id=tenant_id,
                             enable_dhcp=self.network_resources['dhcp'],
                             ip_version=(ipaddress.ip_network(
-                                six.text_type(subnet_cidr)).version))
+                                str(subnet_cidr)).version))
                 else:
                     resp_body = self.subnets_admin_client.\
                         create_subnet(network_id=network_id,
@@ -347,7 +350,7 @@
                                       name=subnet_name,
                                       tenant_id=tenant_id,
                                       ip_version=(ipaddress.ip_network(
-                                          six.text_type(subnet_cidr)).version))
+                                          str(subnet_cidr)).version))
                 break
             except lib_exc.BadRequest as e:
                 if 'overlaps with another subnet' not in str(e):
@@ -373,13 +376,22 @@
     def get_credentials(self, credential_type, scope=None):
         if not scope and self._creds.get(str(credential_type)):
             credentials = self._creds[str(credential_type)]
-        elif scope and self._creds.get("%s_%s" % (scope, credential_type[0])):
-            credentials = self._creds["%s_%s" % (scope, credential_type[0])]
+        elif scope and (
+                self._creds.get("%s_%s" % (scope, str(credential_type)))):
+            credentials = self._creds["%s_%s" % (scope, str(credential_type))]
         else:
+            LOG.debug("Creating new dynamic creds for scope: %s and "
+                      "credential_type: %s", scope, credential_type)
             if scope:
-                if credential_type == 'admin':
+                if credential_type in [['admin'], ['alt_admin']]:
                     credentials = self._create_creds(
                         admin=True, scope=scope)
+                elif credential_type in [['alt_member'], ['alt_reader']]:
+                    cred_type = credential_type[0][4:]
+                    if isinstance(cred_type, str):
+                        cred_type = [cred_type]
+                    credentials = self._create_creds(
+                        roles=cred_type, scope=scope)
                 else:
                     credentials = self._create_creds(
                         roles=credential_type, scope=scope)
@@ -390,7 +402,7 @@
                 credentials = self._create_creds(roles=credential_type)
             if scope:
                 self._creds["%s_%s" %
-                            (scope, credential_type[0])] = credentials
+                            (scope, str(credential_type))] = credentials
             else:
                 self._creds[str(credential_type)] = credentials
             # Maintained until tests are ported
@@ -405,12 +417,18 @@
                          " credentials: %s", credentials)
         return credentials
 
+    # TODO(gmann): Remove this method in favor of get_project_member_creds()
+    # after the deprecation phase.
     def get_primary_creds(self):
         return self.get_credentials('primary')
 
+    # TODO(gmann): Remove this method in favor of get_project_admin_creds()
+    # after the deprecation phase.
     def get_admin_creds(self):
         return self.get_credentials('admin')
 
+    # TODO(gmann): Replace this method with more appropriate name.
+    # like get_project_alt_member_creds()
     def get_alt_creds(self):
         return self.get_credentials('alt')
 
@@ -435,25 +453,37 @@
     def get_project_admin_creds(self):
         return self.get_credentials(['admin'], scope='project')
 
+    def get_project_alt_admin_creds(self):
+        return self.get_credentials(['alt_admin'], scope='project')
+
     def get_project_member_creds(self):
         return self.get_credentials(['member'], scope='project')
 
+    def get_project_alt_member_creds(self):
+        return self.get_credentials(['alt_member'], scope='project')
+
     def get_project_reader_creds(self):
         return self.get_credentials(['reader'], scope='project')
 
-    def get_creds_by_roles(self, roles, force_new=False):
+    def get_project_alt_reader_creds(self):
+        return self.get_credentials(['alt_reader'], scope='project')
+
+    def get_creds_by_roles(self, roles, force_new=False, scope=None):
         roles = list(set(roles))
         # The roles list as a str will become the index as the dict key for
         # the created credentials set in the dynamic_creds dict.
-        exist_creds = self._creds.get(str(roles))
+        creds_name = str(roles)
+        if scope:
+            creds_name = "%s_%s" % (scope, str(roles))
+        exist_creds = self._creds.get(creds_name)
         # If force_new flag is True 2 cred sets with the same roles are needed
         # handle this by creating a separate index for old one to store it
         # separately for cleanup
         if exist_creds and force_new:
-            new_index = str(roles) + '-' + str(len(self._creds))
+            new_index = creds_name + '-' + str(len(self._creds))
             self._creds[new_index] = exist_creds
-            del self._creds[str(roles)]
-        return self.get_credentials(roles)
+            del self._creds[creds_name]
+        return self.get_credentials(roles, scope=scope)
 
     def _clear_isolated_router(self, router_id, router_name):
         client = self.routers_admin_client
diff --git a/tempest/lib/common/jsonschema_validator.py b/tempest/lib/common/jsonschema_validator.py
index bbf5e89..0ac757d 100644
--- a/tempest/lib/common/jsonschema_validator.py
+++ b/tempest/lib/common/jsonschema_validator.py
@@ -15,7 +15,6 @@
 import jsonschema
 from oslo_serialization import base64
 from oslo_utils import timeutils
-import six
 
 # JSON Schema validator and format checker used for JSON Schema validation
 JSONSCHEMA_VALIDATOR = jsonschema.Draft4Validator
@@ -43,7 +42,7 @@
 @jsonschema.FormatChecker.cls_checks('base64')
 def _validate_base64_format(instance):
     try:
-        if isinstance(instance, six.text_type):
+        if isinstance(instance, str):
             instance = instance.encode('utf-8')
         base64.decode_as_bytes(instance)
     except TypeError:
diff --git a/tempest/lib/common/preprov_creds.py b/tempest/lib/common/preprov_creds.py
index 8325f44..6d948cf 100644
--- a/tempest/lib/common/preprov_creds.py
+++ b/tempest/lib/common/preprov_creds.py
@@ -12,12 +12,11 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import hashlib
 import os
 
 from oslo_concurrency import lockutils
 from oslo_log import log as logging
-import six
+from oslo_utils.secretutils import md5
 import yaml
 
 from tempest.lib import auth
@@ -135,10 +134,10 @@
                 scope = 'domain'
             elif 'system' in account:
                 scope = 'system'
-            temp_hash = hashlib.md5()
+            temp_hash = md5(usedforsecurity=False)
             account_for_hash = dict((k, v) for (k, v) in account.items()
                                     if k in cls.HASH_CRED_FIELDS)
-            temp_hash.update(six.text_type(account_for_hash).encode('utf-8'))
+            temp_hash.update(str(account_for_hash).encode('utf-8'))
             temp_hash_key = temp_hash.hexdigest()
             hash_dict['creds'][temp_hash_key] = account
             for role in roles:
@@ -308,6 +307,8 @@
         self.remove_hash(_hash)
         LOG.info("%s returned allocated creds:\n%s", self.name, clean_creds)
 
+    # TODO(gmann): Remove this method in favor of get_project_member_creds()
+    # after the deprecation phase.
     def get_primary_creds(self):
         if self._creds.get('primary'):
             return self._creds.get('primary')
@@ -315,6 +316,8 @@
         self._creds['primary'] = net_creds
         return net_creds
 
+    # TODO(gmann): Replace this method with more appropriate name.
+    # like get_project_alt_member_creds()
     def get_alt_creds(self):
         if self._creds.get('alt'):
             return self._creds.get('alt')
@@ -371,6 +374,10 @@
         self._creds['project_admin'] = project_admin
         return project_admin
 
+    def get_project_alt_admin_creds(self):
+        # TODO(gmann): Implement alt admin hash.
+        return
+
     def get_project_member_creds(self):
         if self._creds.get('project_member'):
             return self._creds.get('project_member')
@@ -378,6 +385,10 @@
         self._creds['project_member'] = project_member
         return project_member
 
+    def get_project_alt_member_creds(self):
+        # TODO(gmann): Implement alt member hash.
+        return
+
     def get_project_reader_creds(self):
         if self._creds.get('project_reader'):
             return self._creds.get('project_reader')
@@ -385,9 +396,13 @@
         self._creds['project_reader'] = project_reader
         return project_reader
 
-    def get_creds_by_roles(self, roles, force_new=False):
+    def get_project_alt_reader_creds(self):
+        # TODO(gmann): Implement alt reader hash.
+        return
+
+    def get_creds_by_roles(self, roles, force_new=False, scope=None):
         roles = list(set(roles))
-        exist_creds = self._creds.get(six.text_type(roles).encode(
+        exist_creds = self._creds.get(str(roles).encode(
             'utf-8'), None)
         # The force kwarg is used to allocate an additional set of creds with
         # the same role list. The index used for the previously allocation
@@ -397,17 +412,19 @@
         elif exist_creds and force_new:
             # NOTE(andreaf) In py3.x encode returns bytes, and b'' is bytes
             # In py2.7 encode returns strings, and b'' is still string
-            new_index = six.text_type(roles).encode('utf-8') + b'-' + \
-                six.text_type(len(self._creds)).encode('utf-8')
+            new_index = str(roles).encode('utf-8') + b'-' + \
+                str(len(self._creds)).encode('utf-8')
             self._creds[new_index] = exist_creds
         net_creds = self._get_creds(roles=roles)
-        self._creds[six.text_type(roles).encode('utf-8')] = net_creds
+        self._creds[str(roles).encode('utf-8')] = net_creds
         return net_creds
 
     def clear_creds(self):
         for creds in self._creds.values():
             self.remove_credentials(creds)
 
+    # TODO(gmann): Remove this method in favor of get_project_admin_creds()
+    # after the deprecation phase.
     def get_admin_creds(self):
         return self.get_creds_by_roles([self.admin_role])
 
diff --git a/tempest/lib/common/rest_client.py b/tempest/lib/common/rest_client.py
index a987e03..00f2aeb 100644
--- a/tempest/lib/common/rest_client.py
+++ b/tempest/lib/common/rest_client.py
@@ -18,13 +18,13 @@
 import email.utils
 import re
 import time
+import urllib
 
 import jsonschema
 from oslo_log import log as logging
 from oslo_log import versionutils
 from oslo_serialization import jsonutils as json
 import six
-from six.moves import urllib
 
 from tempest.lib.common import http
 from tempest.lib.common import jsonschema_validator
@@ -418,7 +418,7 @@
     def _safe_body(self, body, maxlen=4096):
         # convert a structure into a string safely
         try:
-            text = six.text_type(body)
+            text = str(body)
         except UnicodeDecodeError:
             # if this isn't actually text, return marker that
             return "<BinaryData: removed>"
@@ -890,7 +890,7 @@
             return True
         return 'exceed' in resp_body.get('message', 'blabla')
 
-    def wait_for_resource_deletion(self, id):
+    def wait_for_resource_deletion(self, id, *args, **kwargs):
         """Waits for a resource to be deleted
 
         This method will loop over is_resource_deleted until either
@@ -903,7 +903,7 @@
         """
         start_time = int(time.time())
         while True:
-            if self.is_resource_deleted(id):
+            if self.is_resource_deleted(id, *args, **kwargs):
                 return
             if int(time.time()) - start_time >= self.build_timeout:
                 message = ('Failed to delete %(resource_type)s %(id)s within '
diff --git a/tempest/lib/common/ssh.py b/tempest/lib/common/ssh.py
index 3a05f27..60107d7 100644
--- a/tempest/lib/common/ssh.py
+++ b/tempest/lib/common/ssh.py
@@ -14,6 +14,7 @@
 #    under the License.
 
 
+import io
 import select
 import socket
 import time
@@ -67,7 +68,7 @@
         self.password = password
         if isinstance(pkey, six.string_types):
             pkey = paramiko.RSAKey.from_private_key(
-                six.StringIO(str(pkey)))
+                io.StringIO(str(pkey)))
         self.pkey = pkey
         self.look_for_keys = look_for_keys
         self.key_filename = key_filename
diff --git a/tempest/lib/services/compute/__init__.py b/tempest/lib/services/compute/__init__.py
index 91e896a..8d07a45 100644
--- a/tempest/lib/services/compute/__init__.py
+++ b/tempest/lib/services/compute/__init__.py
@@ -14,6 +14,8 @@
 
 from tempest.lib.services.compute.agents_client import AgentsClient
 from tempest.lib.services.compute.aggregates_client import AggregatesClient
+from tempest.lib.services.compute.assisted_volume_snapshots_client import \
+    AssistedVolumeSnapshotsClient
 from tempest.lib.services.compute.availability_zone_client import \
     AvailabilityZoneClient
 from tempest.lib.services.compute.baremetal_nodes_client import \
@@ -63,9 +65,10 @@
 from tempest.lib.services.compute.volumes_client import \
     VolumesClient
 
-__all__ = ['AgentsClient', 'AggregatesClient', 'AvailabilityZoneClient',
-           'BaremetalNodesClient', 'CertificatesClient', 'ExtensionsClient',
-           'FixedIPsClient', 'FlavorsClient', 'FloatingIPPoolsClient',
+__all__ = ['AgentsClient', 'AggregatesClient', 'AssistedVolumeSnapshotsClient',
+           'AvailabilityZoneClient', 'BaremetalNodesClient',
+           'CertificatesClient', 'ExtensionsClient', 'FixedIPsClient',
+           'FlavorsClient', 'FloatingIPPoolsClient',
            'FloatingIPsBulkClient', 'FloatingIPsClient', 'HostsClient',
            'HypervisorClient', 'ImagesClient', 'InstanceUsagesAuditLogClient',
            'InterfacesClient', 'KeyPairsClient', 'LimitsClient',
diff --git a/tempest/lib/services/compute/agents_client.py b/tempest/lib/services/compute/agents_client.py
index 12b3900..bd973dd 100644
--- a/tempest/lib/services/compute/agents_client.py
+++ b/tempest/lib/services/compute/agents_client.py
@@ -12,8 +12,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from urllib import parse as urllib
+
 from oslo_serialization import jsonutils as json
-from six.moves.urllib import parse as urllib
 
 from tempest.lib.api_schema.response.compute.v2_1 import agents as schema
 from tempest.lib.common import rest_client
diff --git a/tempest/lib/services/compute/assisted_volume_snapshots_client.py b/tempest/lib/services/compute/assisted_volume_snapshots_client.py
new file mode 100644
index 0000000..8b67491
--- /dev/null
+++ b/tempest/lib/services/compute/assisted_volume_snapshots_client.py
@@ -0,0 +1,63 @@
+# Copyright 2017 AT&T Corp
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from oslo_serialization import jsonutils as json
+from six.moves.urllib import parse as urllib
+
+from tempest.lib.common import rest_client
+from tempest.lib.services.compute import base_compute_client
+
+
+class AssistedVolumeSnapshotsClient(base_compute_client.BaseComputeClient):
+    """Service client for assisted volume snapshots"""
+
+    def delete_assisted_volume_snapshot(self, volume_id, snapshot_id):
+        """Delete snapshot for the given volume id.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://docs.openstack.org/api-ref/compute/#delete-assisted-volume-snapshot
+
+        :param volume_id: UUID of the volume
+        :param snapshot_id: The UUID of the snapshot
+        """
+        query_param = {'delete_info': json.dumps({'volume_id': volume_id})}
+        resp, body = self.delete("os-assisted-volume-snapshots/%s?%s"
+                                 % (snapshot_id,
+                                    urllib.urlencode(query_param)))
+        return rest_client.ResponseBody(resp, body)
+
+    def create_assisted_volume_snapshot(self, volume_id, snapshot_id,
+                                        **kwargs):
+        """Create a new assisted volume snapshot.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://docs.openstack.org/api-ref/compute/#create-assisted-volume-snapshots
+
+        :param volume_id: the source volume ID
+        :param snapshot_id: the UUID for a snapshot
+        :param type: Type of snapshot, such as qcow2
+        :param new_file: The name of image file that will be created
+        """
+        url = "os-assisted-volume-snapshots"
+        info = {"snapshot_id": snapshot_id}
+        if kwargs:
+            info.update(kwargs)
+        body = {"snapshot": {"volume_id": volume_id, "create_info": info}}
+        post_body = json.dumps(body)
+        resp, body = self.post(url, post_body)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/compute/baremetal_nodes_client.py b/tempest/lib/services/compute/baremetal_nodes_client.py
index 3efdbce..83af451 100644
--- a/tempest/lib/services/compute/baremetal_nodes_client.py
+++ b/tempest/lib/services/compute/baremetal_nodes_client.py
@@ -12,8 +12,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from urllib import parse as urllib
+
 from oslo_serialization import jsonutils as json
-from six.moves.urllib import parse as urllib
 
 from tempest.lib.api_schema.response.compute.v2_1 import baremetal_nodes \
     as schema
diff --git a/tempest/lib/services/compute/flavors_client.py b/tempest/lib/services/compute/flavors_client.py
index e22b5b2..5282405 100644
--- a/tempest/lib/services/compute/flavors_client.py
+++ b/tempest/lib/services/compute/flavors_client.py
@@ -13,8 +13,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from urllib import parse as urllib
+
 from oslo_serialization import jsonutils as json
-from six.moves.urllib import parse as urllib
 
 from tempest.lib.api_schema.response.compute.v2_1 import flavors as schema
 from tempest.lib.api_schema.response.compute.v2_1 import flavors_access \
diff --git a/tempest/lib/services/compute/floating_ip_pools_client.py b/tempest/lib/services/compute/floating_ip_pools_client.py
index d3af050..aa065b8 100644
--- a/tempest/lib/services/compute/floating_ip_pools_client.py
+++ b/tempest/lib/services/compute/floating_ip_pools_client.py
@@ -13,8 +13,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from urllib import parse as urllib
+
 from oslo_serialization import jsonutils as json
-from six.moves.urllib import parse as urllib
 
 from tempest.lib.api_schema.response.compute.v2_1 import floating_ips as schema
 from tempest.lib.common import rest_client
diff --git a/tempest/lib/services/compute/floating_ips_client.py b/tempest/lib/services/compute/floating_ips_client.py
index d7a1a9b..e6b6916 100644
--- a/tempest/lib/services/compute/floating_ips_client.py
+++ b/tempest/lib/services/compute/floating_ips_client.py
@@ -13,8 +13,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from urllib import parse as urllib
+
 from oslo_serialization import jsonutils as json
-from six.moves.urllib import parse as urllib
 
 from tempest.lib.api_schema.response.compute.v2_1 import floating_ips as schema
 from tempest.lib.common import rest_client
diff --git a/tempest/lib/services/compute/hosts_client.py b/tempest/lib/services/compute/hosts_client.py
index 743b4ec..bbecc3b 100644
--- a/tempest/lib/services/compute/hosts_client.py
+++ b/tempest/lib/services/compute/hosts_client.py
@@ -12,8 +12,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from urllib import parse as urllib
+
 from oslo_serialization import jsonutils as json
-from six.moves.urllib import parse as urllib
 
 from tempest.lib.api_schema.response.compute.v2_1 import hosts as schema
 from tempest.lib.common import rest_client
diff --git a/tempest/lib/services/compute/images_client.py b/tempest/lib/services/compute/images_client.py
index b252ee9..b6d8d30 100644
--- a/tempest/lib/services/compute/images_client.py
+++ b/tempest/lib/services/compute/images_client.py
@@ -13,8 +13,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from urllib import parse as urllib
+
 from oslo_serialization import jsonutils as json
-from six.moves.urllib import parse as urllib
 
 from tempest.lib.api_schema.response.compute.v2_1 import images as schema
 from tempest.lib.api_schema.response.compute.v2_45 import images as schemav245
diff --git a/tempest/lib/services/compute/keypairs_client.py b/tempest/lib/services/compute/keypairs_client.py
index 47cf2d0..9d7b7fc 100644
--- a/tempest/lib/services/compute/keypairs_client.py
+++ b/tempest/lib/services/compute/keypairs_client.py
@@ -13,8 +13,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from urllib import parse as urllib
+
 from oslo_serialization import jsonutils as json
-from six.moves.urllib import parse as urllib
 
 from tempest.lib.api_schema.response.compute.v2_1 import keypairs as schemav21
 from tempest.lib.api_schema.response.compute.v2_2 import keypairs as schemav22
diff --git a/tempest/lib/services/compute/migrations_client.py b/tempest/lib/services/compute/migrations_client.py
index 812dc96..8a6e62a 100644
--- a/tempest/lib/services/compute/migrations_client.py
+++ b/tempest/lib/services/compute/migrations_client.py
@@ -12,8 +12,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from urllib import parse as urllib
+
 from oslo_serialization import jsonutils as json
-from six.moves.urllib import parse as urllib
 
 from tempest.lib.api_schema.response.compute.v2_1 import migrations as schema
 from tempest.lib.api_schema.response.compute.v2_23 import migrations \
diff --git a/tempest/lib/services/compute/quotas_client.py b/tempest/lib/services/compute/quotas_client.py
index 12e865e..dd796aa 100644
--- a/tempest/lib/services/compute/quotas_client.py
+++ b/tempest/lib/services/compute/quotas_client.py
@@ -13,8 +13,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from urllib import parse as urllib
+
 from oslo_serialization import jsonutils as json
-from six.moves.urllib import parse as urllib
 
 from tempest.lib.api_schema.response.compute.v2_1 import quotas as schema
 from tempest.lib.api_schema.response.compute.v2_36 import quotas as schemav236
diff --git a/tempest/lib/services/compute/security_groups_client.py b/tempest/lib/services/compute/security_groups_client.py
index 9493144..0bba990 100644
--- a/tempest/lib/services/compute/security_groups_client.py
+++ b/tempest/lib/services/compute/security_groups_client.py
@@ -13,8 +13,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from urllib import parse as urllib
+
 from oslo_serialization import jsonutils as json
-from six.moves.urllib import parse as urllib
 
 from tempest.lib.api_schema.response.compute.v2_1 import \
     security_groups as schema
diff --git a/tempest/lib/services/compute/servers_client.py b/tempest/lib/services/compute/servers_client.py
index e82b58f..c36f80a 100644
--- a/tempest/lib/services/compute/servers_client.py
+++ b/tempest/lib/services/compute/servers_client.py
@@ -16,9 +16,9 @@
 #    under the License.
 
 import copy
+from urllib import parse as urllib
 
 from oslo_serialization import jsonutils as json
-from six.moves.urllib import parse as urllib
 
 from tempest.lib.api_schema.response.compute.v2_1 import \
     security_groups as security_groups_schema
diff --git a/tempest/lib/services/compute/services_client.py b/tempest/lib/services/compute/services_client.py
index 4e3383f..7d9f3e2 100644
--- a/tempest/lib/services/compute/services_client.py
+++ b/tempest/lib/services/compute/services_client.py
@@ -14,8 +14,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from urllib import parse as urllib
+
 from oslo_serialization import jsonutils as json
-from six.moves.urllib import parse as urllib
 
 from tempest.lib.api_schema.response.compute.v2_1 import services as schema
 from tempest.lib.api_schema.response.compute.v2_11 import services \
diff --git a/tempest/lib/services/compute/snapshots_client.py b/tempest/lib/services/compute/snapshots_client.py
index 225eb8d..2e6f7cf 100644
--- a/tempest/lib/services/compute/snapshots_client.py
+++ b/tempest/lib/services/compute/snapshots_client.py
@@ -13,8 +13,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from urllib import parse as urllib
+
 from oslo_serialization import jsonutils as json
-from six.moves.urllib import parse as urllib
 
 from tempest.lib.api_schema.response.compute.v2_1 import snapshots as schema
 from tempest.lib.common import rest_client
diff --git a/tempest/lib/services/compute/tenant_usages_client.py b/tempest/lib/services/compute/tenant_usages_client.py
index a34730c..b47d917 100644
--- a/tempest/lib/services/compute/tenant_usages_client.py
+++ b/tempest/lib/services/compute/tenant_usages_client.py
@@ -13,8 +13,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from urllib import parse as urllib
+
 from oslo_serialization import jsonutils as json
-from six.moves.urllib import parse as urllib
 
 from tempest.lib.api_schema.response.compute.v2_1 import tenant_usages
 from tempest.lib.common import rest_client
diff --git a/tempest/lib/services/compute/volumes_client.py b/tempest/lib/services/compute/volumes_client.py
index 11282ee..52172ed 100644
--- a/tempest/lib/services/compute/volumes_client.py
+++ b/tempest/lib/services/compute/volumes_client.py
@@ -13,8 +13,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from urllib import parse as urllib
+
 from oslo_serialization import jsonutils as json
-from six.moves.urllib import parse as urllib
 
 from tempest.lib.api_schema.response.compute.v2_1 import volumes as schema
 from tempest.lib.common import rest_client
diff --git a/tempest/lib/services/identity/v2/identity_client.py b/tempest/lib/services/identity/v2/identity_client.py
index d7526f3..6239ba6 100644
--- a/tempest/lib/services/identity/v2/identity_client.py
+++ b/tempest/lib/services/identity/v2/identity_client.py
@@ -10,8 +10,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from urllib import parse as urllib
+
 from oslo_serialization import jsonutils as json
-from six.moves.urllib import parse as urllib
 
 from tempest.lib.common import rest_client
 
diff --git a/tempest/lib/services/identity/v2/roles_client.py b/tempest/lib/services/identity/v2/roles_client.py
index a133fc3..1580c33 100644
--- a/tempest/lib/services/identity/v2/roles_client.py
+++ b/tempest/lib/services/identity/v2/roles_client.py
@@ -10,8 +10,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from urllib import parse as urllib
+
 from oslo_serialization import jsonutils as json
-from six.moves.urllib import parse as urllib
 
 from tempest.lib.common import rest_client
 
diff --git a/tempest/lib/services/identity/v2/services_client.py b/tempest/lib/services/identity/v2/services_client.py
index fc51cb4..2a0e5ca 100644
--- a/tempest/lib/services/identity/v2/services_client.py
+++ b/tempest/lib/services/identity/v2/services_client.py
@@ -12,8 +12,9 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+from urllib import parse as urllib
+
 from oslo_serialization import jsonutils as json
-from six.moves.urllib import parse as urllib
 
 from tempest.lib.common import rest_client
 
diff --git a/tempest/lib/services/identity/v2/tenants_client.py b/tempest/lib/services/identity/v2/tenants_client.py
index 09618ad..3435835 100644
--- a/tempest/lib/services/identity/v2/tenants_client.py
+++ b/tempest/lib/services/identity/v2/tenants_client.py
@@ -12,8 +12,9 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+from urllib import parse as urllib
+
 from oslo_serialization import jsonutils as json
-from six.moves.urllib import parse as urllib
 
 from tempest.lib.common import rest_client
 
diff --git a/tempest/lib/services/identity/v2/users_client.py b/tempest/lib/services/identity/v2/users_client.py
index 72f29be..c3217c9 100644
--- a/tempest/lib/services/identity/v2/users_client.py
+++ b/tempest/lib/services/identity/v2/users_client.py
@@ -10,8 +10,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from urllib import parse as urllib
+
 from oslo_serialization import jsonutils as json
-from six.moves.urllib import parse as urllib
 
 from tempest.lib.common import rest_client
 
diff --git a/tempest/lib/services/identity/v3/access_rules_client.py b/tempest/lib/services/identity/v3/access_rules_client.py
index 4f13e47..c3be5df 100644
--- a/tempest/lib/services/identity/v3/access_rules_client.py
+++ b/tempest/lib/services/identity/v3/access_rules_client.py
@@ -18,8 +18,9 @@
 https://docs.openstack.org/api-ref/identity/v3/index.html#application-credentials
 """
 
+from urllib import parse as urllib
+
 from oslo_serialization import jsonutils as json
-from six.moves.urllib import parse as urllib
 
 from tempest.lib.common import rest_client
 
diff --git a/tempest/lib/services/identity/v3/application_credentials_client.py b/tempest/lib/services/identity/v3/application_credentials_client.py
index be2e172..e7f3ac2 100644
--- a/tempest/lib/services/identity/v3/application_credentials_client.py
+++ b/tempest/lib/services/identity/v3/application_credentials_client.py
@@ -18,8 +18,9 @@
 https://docs.openstack.org/api-ref/identity/v3/index.html#application-credentials
 """
 
+from urllib import parse as urllib
+
 from oslo_serialization import jsonutils as json
-from six.moves.urllib import parse as urllib
 
 from tempest.lib.common import rest_client
 
diff --git a/tempest/lib/services/identity/v3/credentials_client.py b/tempest/lib/services/identity/v3/credentials_client.py
index 3f4b40e..27f6156 100644
--- a/tempest/lib/services/identity/v3/credentials_client.py
+++ b/tempest/lib/services/identity/v3/credentials_client.py
@@ -17,8 +17,9 @@
 https://docs.openstack.org/api-ref/identity/v3/index.html#credentials
 """
 
+from urllib import parse as urllib
+
 from oslo_serialization import jsonutils as json
-from six.moves.urllib import parse as urllib
 
 from tempest.lib.common import rest_client
 
diff --git a/tempest/lib/services/identity/v3/domains_client.py b/tempest/lib/services/identity/v3/domains_client.py
index bd32cfc..c1d1980 100644
--- a/tempest/lib/services/identity/v3/domains_client.py
+++ b/tempest/lib/services/identity/v3/domains_client.py
@@ -12,8 +12,9 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+from urllib import parse as urllib
+
 from oslo_serialization import jsonutils as json
-from six.moves.urllib import parse as urllib
 
 from tempest.lib.common import rest_client
 
diff --git a/tempest/lib/services/identity/v3/endpoints_client.py b/tempest/lib/services/identity/v3/endpoints_client.py
index 236b34c..de85388 100644
--- a/tempest/lib/services/identity/v3/endpoints_client.py
+++ b/tempest/lib/services/identity/v3/endpoints_client.py
@@ -17,8 +17,9 @@
 https://docs.openstack.org/api-ref/identity/v3/index.html#service-catalog-and-endpoints
 """
 
+from urllib import parse as urllib
+
 from oslo_serialization import jsonutils as json
-from six.moves.urllib import parse as urllib
 
 from tempest.lib.common import rest_client
 
diff --git a/tempest/lib/services/identity/v3/groups_client.py b/tempest/lib/services/identity/v3/groups_client.py
index 2cfb24a..6f82067 100644
--- a/tempest/lib/services/identity/v3/groups_client.py
+++ b/tempest/lib/services/identity/v3/groups_client.py
@@ -17,8 +17,9 @@
 https://docs.openstack.org/api-ref/identity/v3/index.html#groups
 """
 
+from urllib import parse as urllib
+
 from oslo_serialization import jsonutils as json
-from six.moves.urllib import parse as urllib
 
 from tempest.lib.common import rest_client
 
diff --git a/tempest/lib/services/identity/v3/identity_providers_client.py b/tempest/lib/services/identity/v3/identity_providers_client.py
new file mode 100644
index 0000000..af6a245
--- /dev/null
+++ b/tempest/lib/services/identity/v3/identity_providers_client.py
@@ -0,0 +1,92 @@
+# Copyright 2020 Samsung Electronics Co., Ltd
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+from oslo_serialization import jsonutils as json
+from six.moves.urllib import parse as urllib
+
+from tempest.lib.common import rest_client
+
+
+class IdentityProvidersClient(rest_client.RestClient):
+
+    def register_identity_provider(self, identity_provider_id, **kwargs):
+        """Register an identity provider.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://docs.openstack.org/api-ref/identity/v3-ext/index.html#register-an-identity-provider
+        """
+        post_body = json.dumps({'identity_provider': kwargs})
+        resp, body = self.put(
+            'OS-FEDERATION/identity_providers/%s' % identity_provider_id,
+            post_body)
+        self.expected_success(201, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def list_identity_providers(self, **params):
+        """List identity providers.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://docs.openstack.org/api-ref/identity/v3-ext/index.html#list-identity-providers
+        """
+        url = 'identity_providers'
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+        resp, body = self.get(url)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def get_identity_provider(self, identity_provider_id):
+        """Get identity provider.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://docs.openstack.org/api-ref/identity/v3-ext/index.html#get-identity-provider
+        """
+        resp, body = self.get(
+            'OS-FEDERATION/identity_providers/%s' % identity_provider_id)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_identity_provider(self, identity_provider_id):
+        """Delete identity provider.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://docs.openstack.org/api-ref/identity/v3-ext/index.html#delete-identity-provider
+        """
+        resp, body = self.delete(
+            'OS-FEDERATION/identity_providers/%s' % identity_provider_id)
+        self.expected_success(204, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_identity_provider(self, identity_provider_id, **kwargs):
+        """Update identity provider.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://docs.openstack.org/api-ref/identity/v3-ext/index.html#update-identity-provider
+        """
+        post_body = json.dumps({'identity_provider': kwargs})
+        resp, body = self.patch(
+            'OS-FEDERATION/identity_providers/%s' % identity_provider_id,
+            post_body)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/identity/v3/mappings_client.py b/tempest/lib/services/identity/v3/mappings_client.py
new file mode 100644
index 0000000..9ec5384
--- /dev/null
+++ b/tempest/lib/services/identity/v3/mappings_client.py
@@ -0,0 +1,90 @@
+# Copyright 2020 Samsung Electronics Co., Ltd
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+from oslo_serialization import jsonutils as json
+from six.moves.urllib import parse as urllib
+
+from tempest.lib.common import rest_client
+
+
+class MappingsClient(rest_client.RestClient):
+
+    def create_mapping(self, mapping_id, **kwargs):
+        """Create a mapping.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://docs.openstack.org/api-ref/identity/v3-ext/index.html#create-a-mapping
+        """
+        post_body = json.dumps({'mapping': kwargs})
+        resp, body = self.put(
+            'OS-FEDERATION/mappings/%s' % mapping_id, post_body)
+        self.expected_success(201, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def get_mapping(self, mapping_id):
+        """Get a mapping.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://docs.openstack.org/api-ref/identity/v3-ext/index.html#get-a-mapping
+        """
+        resp, body = self.get(
+            'OS-FEDERATION/mappings/%s' % mapping_id)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_mapping(self, mapping_id, **kwargs):
+        """Update a mapping.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://docs.openstack.org/api-ref/identity/v3-ext/index.html#update-a-mapping
+        """
+        post_body = json.dumps({'mapping': kwargs})
+        resp, body = self.patch(
+            'OS-FEDERATION/mappings/%s' % mapping_id, post_body)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def list_mappings(self, **kwargs):
+        """List mappings.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://docs.openstack.org/api-ref/identity/v3-ext/index.html#list-mappings
+        """
+        url = 'OS-FEDERATION/mappings'
+        if kwargs:
+            url += '?%s' % urllib.urlencode(kwargs)
+        resp, body = self.get(url)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_mapping(self, mapping_id):
+        """Delete a mapping.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://docs.openstack.org/api-ref/identity/v3-ext/index.html#delete-a-mapping
+        """
+        resp, body = self.delete(
+            'OS-FEDERATION/mappings/%s' % mapping_id)
+        self.expected_success(204, resp.status)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/identity/v3/oauth_token_client.py b/tempest/lib/services/identity/v3/oauth_token_client.py
index 722deca..236b224 100644
--- a/tempest/lib/services/identity/v3/oauth_token_client.py
+++ b/tempest/lib/services/identity/v3/oauth_token_client.py
@@ -18,9 +18,9 @@
 import hmac
 import random
 import time
+from urllib import parse as urlparse
 
 import six
-from six.moves.urllib import parse as urlparse
 
 from oslo_serialization import jsonutils as json
 
@@ -33,7 +33,7 @@
     def _escape(self, s):
         """Escape a unicode string in an OAuth-compatible fashion."""
         safe = b'~'
-        s = s.encode('utf-8') if isinstance(s, six.text_type) else s
+        s = s.encode('utf-8') if isinstance(s, str) else s
         s = urlparse.quote(s, safe)
         if isinstance(s, six.binary_type):
             s = s.decode('utf-8')
@@ -47,8 +47,8 @@
                                         verifier=None,
                                         http_method='GET'):
         """Generate OAUTH params along with signature."""
-        timestamp = six.text_type(int(time.time()))
-        nonce = six.text_type(random.getrandbits(64)) + timestamp
+        timestamp = str(int(time.time()))
+        nonce = str(random.getrandbits(64)) + timestamp
         oauth_params = [
             ('oauth_nonce', nonce),
             ('oauth_timestamp', timestamp),
diff --git a/tempest/lib/services/identity/v3/projects_client.py b/tempest/lib/services/identity/v3/projects_client.py
index b186fba..fffbe7a 100644
--- a/tempest/lib/services/identity/v3/projects_client.py
+++ b/tempest/lib/services/identity/v3/projects_client.py
@@ -13,8 +13,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from urllib import parse as urllib
+
 from oslo_serialization import jsonutils as json
-from six.moves.urllib import parse as urllib
 
 from tempest.lib.common import rest_client
 
diff --git a/tempest/lib/services/identity/v3/protocols_client.py b/tempest/lib/services/identity/v3/protocols_client.py
new file mode 100644
index 0000000..2e0221b
--- /dev/null
+++ b/tempest/lib/services/identity/v3/protocols_client.py
@@ -0,0 +1,96 @@
+# Copyright 2020 Samsung Electronics Co., Ltd
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+from oslo_serialization import jsonutils as json
+from six.moves.urllib import parse as urllib
+
+from tempest.lib.common import rest_client
+
+
+class ProtocolsClient(rest_client.RestClient):
+
+    def add_protocol_to_identity_provider(self, idp_id, protocol_id,
+                                          **kwargs):
+        """Add protocol to identity provider.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://docs.openstack.org/api-ref/identity/v3-ext/index.html#add-protocol-to-identity-provider
+        """
+        post_body = json.dumps({'protocol': kwargs})
+        resp, body = self.put(
+            'OS-FEDERATION/identity_providers/%s/protocols/%s'
+            % (idp_id, protocol_id), post_body)
+        self.expected_success(201, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def list_protocols_of_identity_provider(self, idp_id, **kwargs):
+        """List protocols of identity provider.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://docs.openstack.org/api-ref/identity/v3-ext/index.html#list-protocols-of-identity-provider
+        """
+        url = 'OS-FEDERATION/identity_providers/%s/protocols' % idp_id
+        if kwargs:
+            url += '?%s' % urllib.urlencode(kwargs)
+        resp, body = self.get(url)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def get_protocol_for_identity_provider(self, idp_id, protocol_id):
+        """Get protocol for identity provider.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://docs.openstack.org/api-ref/identity/v3-ext/index.html#get-protocol-for-identity-provider
+        """
+        resp, body = self.get(
+            'OS-FEDERATION/identity_providers/%s/protocols/%s'
+            % (idp_id, protocol_id))
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_mapping_for_identity_provider(self, idp_id, protocol_id,
+                                             **kwargs):
+        """Update attribute mapping for identity provider.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://docs.openstack.org/api-ref/identity/v3-ext/index.html#update-attribute-mapping-for-identity-provider
+        """
+        post_body = json.dumps({'protocol': kwargs})
+        resp, body = self.patch(
+            'OS-FEDERATION/identity_providers/%s/protocols/%s'
+            % (idp_id, protocol_id), post_body)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_protocol_from_identity_provider(self, idp_id, protocol_id):
+        """Delete a protocol from identity provider.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://docs.openstack.org/api-ref/identity/v3-ext/index.html#delete-a-protocol-from-identity-provider
+        """
+        resp, body = self.delete(
+            'OS-FEDERATION/identity_providers/%s/protocols/%s'
+            % (idp_id, protocol_id))
+        self.expected_success(204, resp.status)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/identity/v3/regions_client.py b/tempest/lib/services/identity/v3/regions_client.py
index a598c9c..3aed5b8 100644
--- a/tempest/lib/services/identity/v3/regions_client.py
+++ b/tempest/lib/services/identity/v3/regions_client.py
@@ -17,8 +17,9 @@
 https://docs.openstack.org/api-ref/identity/v3/index.html#regions
 """
 
+from urllib import parse as urllib
+
 from oslo_serialization import jsonutils as json
-from six.moves.urllib import parse as urllib
 
 from tempest.lib.common import rest_client
 
diff --git a/tempest/lib/services/identity/v3/role_assignments_client.py b/tempest/lib/services/identity/v3/role_assignments_client.py
index 51ee8f6..f615709 100644
--- a/tempest/lib/services/identity/v3/role_assignments_client.py
+++ b/tempest/lib/services/identity/v3/role_assignments_client.py
@@ -12,8 +12,9 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+from urllib import parse as urllib
+
 from oslo_serialization import jsonutils as json
-from six.moves.urllib import parse as urllib
 
 from tempest.lib.common import rest_client
 
diff --git a/tempest/lib/services/identity/v3/roles_client.py b/tempest/lib/services/identity/v3/roles_client.py
index e41dc28..4836784 100644
--- a/tempest/lib/services/identity/v3/roles_client.py
+++ b/tempest/lib/services/identity/v3/roles_client.py
@@ -12,8 +12,9 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+from urllib import parse as urllib
+
 from oslo_serialization import jsonutils as json
-from six.moves.urllib import parse as urllib
 
 from tempest.lib.common import rest_client
 
diff --git a/tempest/lib/services/identity/v3/service_providers_client.py b/tempest/lib/services/identity/v3/service_providers_client.py
new file mode 100644
index 0000000..b84cf43
--- /dev/null
+++ b/tempest/lib/services/identity/v3/service_providers_client.py
@@ -0,0 +1,92 @@
+# Copyright 2020 Samsung Electronics Co., Ltd
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+from oslo_serialization import jsonutils as json
+from six.moves.urllib import parse as urllib
+
+from tempest.lib.common import rest_client
+
+
+class ServiceProvidersClient(rest_client.RestClient):
+
+    def register_service_provider(self, service_provider_id, **kwargs):
+        """Register a service provider.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://docs.openstack.org/api-ref/identity/v3-ext/index.html#register-a-service-provider
+        """
+        post_body = json.dumps({'service_provider': kwargs})
+        resp, body = self.put(
+            'OS-FEDERATION/service_providers/%s' % service_provider_id,
+            post_body)
+        self.expected_success(201, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def list_service_providers(self, **kwargs):
+        """List service providers.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://docs.openstack.org/api-ref/identity/v3-ext/index.html#list-service-providers
+        """
+        url = 'OS-FEDERATION/service_providers'
+        if kwargs:
+            url += '?%s' % urllib.urlencode(kwargs)
+        resp, body = self.get(url)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def get_service_provider(self, service_provider_id):
+        """Get a service provider.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://docs.openstack.org/api-ref/identity/v3-ext/index.html#get-service-provider
+        """
+        resp, body = self.get(
+            'OS-FEDERATION/service_providers/%s' % service_provider_id)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_service_provider(self, service_provider_id):
+        """Delete a service provider.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://docs.openstack.org/api-ref/identity/v3-ext/index.html#delete-service-provider
+        """
+        resp, body = self.delete(
+            'OS-FEDERATION/service_providers/%s' % service_provider_id)
+        self.expected_success(204, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_service_provider(self, service_provider_id, **kwargs):
+        """Update a service provider.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://docs.openstack.org/api-ref/identity/v3-ext/index.html#update-service-provider
+        """
+        post_body = json.dumps({'service_provider': kwargs})
+        resp, body = self.patch(
+            'OS-FEDERATION/service_providers/%s' % service_provider_id,
+            post_body)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/identity/v3/services_client.py b/tempest/lib/services/identity/v3/services_client.py
index eb961a5..994df2f 100644
--- a/tempest/lib/services/identity/v3/services_client.py
+++ b/tempest/lib/services/identity/v3/services_client.py
@@ -17,8 +17,9 @@
 https://docs.openstack.org/api-ref/identity/v3/index.html#service-catalog-and-endpoints
 """
 
+from urllib import parse as urllib
+
 from oslo_serialization import jsonutils as json
-from six.moves.urllib import parse as urllib
 
 from tempest.lib.common import rest_client
 
diff --git a/tempest/lib/services/identity/v3/trusts_client.py b/tempest/lib/services/identity/v3/trusts_client.py
index f1cc806..48a7956 100644
--- a/tempest/lib/services/identity/v3/trusts_client.py
+++ b/tempest/lib/services/identity/v3/trusts_client.py
@@ -12,8 +12,9 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+from urllib import parse as urllib
+
 from oslo_serialization import jsonutils as json
-from six.moves.urllib import parse as urllib
 
 from tempest.lib.common import rest_client
 
diff --git a/tempest/lib/services/identity/v3/users_client.py b/tempest/lib/services/identity/v3/users_client.py
index bba02a4..771ffea 100644
--- a/tempest/lib/services/identity/v3/users_client.py
+++ b/tempest/lib/services/identity/v3/users_client.py
@@ -12,8 +12,9 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+from urllib import parse as urllib
+
 from oslo_serialization import jsonutils as json
-from six.moves.urllib import parse as urllib
 
 from tempest.lib.common import rest_client
 
diff --git a/tempest/lib/services/image/v1/images_client.py b/tempest/lib/services/image/v1/images_client.py
index 0e76a63..c9a4a94 100644
--- a/tempest/lib/services/image/v1/images_client.py
+++ b/tempest/lib/services/image/v1/images_client.py
@@ -14,9 +14,9 @@
 #    under the License.
 
 import functools
+from urllib import parse as urllib
 
 from oslo_serialization import jsonutils as json
-from six.moves.urllib import parse as urllib
 
 from tempest.lib.common import rest_client
 from tempest.lib import exceptions as lib_exc
diff --git a/tempest/lib/services/image/v2/images_client.py b/tempest/lib/services/image/v2/images_client.py
index 4713cce..abf427c 100644
--- a/tempest/lib/services/image/v2/images_client.py
+++ b/tempest/lib/services/image/v2/images_client.py
@@ -14,9 +14,9 @@
 #    under the License.
 
 import functools
+from urllib import parse as urllib
 
 from oslo_serialization import jsonutils as json
-from six.moves.urllib import parse as urllib
 
 from tempest.lib.common import rest_client
 from tempest.lib import exceptions as lib_exc
@@ -121,6 +121,14 @@
         body = json.loads(body)
         return rest_client.ResponseBody(resp, body)
 
+    def show_image_tasks(self, image_id):
+        """Show image tasks."""
+        url = 'images/%s/tasks' % image_id
+        resp, body = self.get(url)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
     def is_resource_deleted(self, id):
         try:
             self.show_image(id)
diff --git a/tempest/lib/services/image/v2/namespace_objects_client.py b/tempest/lib/services/image/v2/namespace_objects_client.py
index 0cae816..32f5a2c 100644
--- a/tempest/lib/services/image/v2/namespace_objects_client.py
+++ b/tempest/lib/services/image/v2/namespace_objects_client.py
@@ -13,8 +13,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from urllib import parse as urllib
+
 from oslo_serialization import jsonutils as json
-from six.moves.urllib import parse as urllib
 
 from tempest.lib.common import rest_client
 
diff --git a/tempest/lib/services/image/v2/namespace_tags_client.py b/tempest/lib/services/image/v2/namespace_tags_client.py
index 4315f16..5bca229 100644
--- a/tempest/lib/services/image/v2/namespace_tags_client.py
+++ b/tempest/lib/services/image/v2/namespace_tags_client.py
@@ -13,8 +13,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from urllib import parse as urllib
+
 from oslo_serialization import jsonutils as json
-from six.moves.urllib import parse as urllib
 
 from tempest.lib.common import rest_client
 
diff --git a/tempest/lib/services/image/v2/versions_client.py b/tempest/lib/services/image/v2/versions_client.py
index 1b7f806..98b4fb6 100644
--- a/tempest/lib/services/image/v2/versions_client.py
+++ b/tempest/lib/services/image/v2/versions_client.py
@@ -30,3 +30,13 @@
         self.expected_success(300, resp.status)
         body = json.loads(body)
         return rest_client.ResponseBody(resp, body)
+
+    def has_version(self, version):
+        """Return True if a version is supported."""
+        version = 'v%s' % version
+        supported = ['SUPPORTED', 'CURRENT']
+        versions = self.list_versions()
+        for version_struct in versions['versions']:
+            if version_struct['id'] == version:
+                return version_struct['status'] in supported
+        return False
diff --git a/tempest/lib/services/network/base.py b/tempest/lib/services/network/base.py
index fe8b244..ee87dd4 100644
--- a/tempest/lib/services/network/base.py
+++ b/tempest/lib/services/network/base.py
@@ -10,8 +10,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from urllib import parse as urllib
+
 from oslo_serialization import jsonutils as json
-from six.moves.urllib import parse as urllib
 
 from tempest.lib.common import rest_client
 
diff --git a/tempest/lib/services/object_storage/account_client.py b/tempest/lib/services/object_storage/account_client.py
index 8c15a88..52b2534 100644
--- a/tempest/lib/services/object_storage/account_client.py
+++ b/tempest/lib/services/object_storage/account_client.py
@@ -13,10 +13,10 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from urllib import parse as urllib
 from xml.etree import ElementTree as etree
 
 from oslo_serialization import jsonutils as json
-from six.moves.urllib import parse as urllib
 
 from tempest.lib.common import rest_client
 
diff --git a/tempest/lib/services/object_storage/container_client.py b/tempest/lib/services/object_storage/container_client.py
index 027fb1f..6d07ec1 100644
--- a/tempest/lib/services/object_storage/container_client.py
+++ b/tempest/lib/services/object_storage/container_client.py
@@ -13,17 +13,25 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from urllib import parse as urllib
 from xml.etree import ElementTree as etree
 
 import debtcollector.moves
 from oslo_serialization import jsonutils as json
-from six.moves.urllib import parse as urllib
 
 from tempest.lib.common import rest_client
+from tempest.lib import exceptions
 
 
 class ContainerClient(rest_client.RestClient):
 
+    def is_resource_deleted(self, container):
+        try:
+            self.list_container_metadata(container)
+        except exceptions.NotFound:
+            return True
+        return False
+
     def update_container(self, container_name, **headers):
         """Creates or Updates a container
 
diff --git a/tempest/lib/services/object_storage/object_client.py b/tempest/lib/services/object_storage/object_client.py
index 1d38153..bb82975 100644
--- a/tempest/lib/services/object_storage/object_client.py
+++ b/tempest/lib/services/object_storage/object_client.py
@@ -14,8 +14,8 @@
 #    under the License.
 import ssl
 
-from six.moves import http_client as httplib
-from six.moves.urllib import parse as urlparse
+from http import client as httplib
+from urllib import parse as urlparse
 
 from tempest.lib.common import rest_client
 from tempest.lib import exceptions
@@ -23,6 +23,13 @@
 
 class ObjectClient(rest_client.RestClient):
 
+    def is_resource_deleted(self, object_name, container):
+        try:
+            self.get_object(container, object_name)
+        except exceptions.NotFound:
+            return True
+        return False
+
     def create_object(self, container, object_name, data,
                       params=None, metadata=None, headers=None,
                       chunked=False):
diff --git a/tempest/lib/services/placement/placement_client.py b/tempest/lib/services/placement/placement_client.py
index b8e91b8..216ac08 100644
--- a/tempest/lib/services/placement/placement_client.py
+++ b/tempest/lib/services/placement/placement_client.py
@@ -12,8 +12,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from urllib import parse as urllib
+
 from oslo_serialization import jsonutils as json
-from six.moves.urllib import parse as urllib
 
 from tempest.lib.common import rest_client
 from tempest.lib.services.placement import base_placement_client
diff --git a/tempest/lib/services/placement/resource_providers_client.py b/tempest/lib/services/placement/resource_providers_client.py
index 56f6409..e6fbcb2 100644
--- a/tempest/lib/services/placement/resource_providers_client.py
+++ b/tempest/lib/services/placement/resource_providers_client.py
@@ -10,8 +10,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from urllib import parse as urllib
+
 from oslo_serialization import jsonutils as json
-from six.moves.urllib import parse as urllib
 
 from tempest.lib.common import rest_client
 from tempest.lib.services.placement import base_placement_client
@@ -80,3 +81,29 @@
         self.expected_success(200, resp.status)
         body = json.loads(body)
         return rest_client.ResponseBody(resp, body)
+
+    def update_resource_providers_inventories(self, rp_uuid, **kwargs):
+        """Update resource providers inventories.
+
+        For full list of available parameters, please refer to the official
+        API reference:
+        https://docs.openstack.org/api-ref/placement/#update-resource-provider-inventories
+        """
+        url = '/resource_providers/{}/inventories'.format(rp_uuid)
+        data = json.dumps(kwargs)
+        resp, body = self.put(url, data)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_resource_providers_inventories(self, rp_uuid):
+        """Delete resource providers inventories.
+
+        For full list of available parameters, please refer to the official
+        API reference:
+        https://docs.openstack.org/api-ref/placement/#delete-resource-provider-inventories
+        """
+        url = '/resource_providers/{}/inventories'.format(rp_uuid)
+        resp, body = self.delete(url)
+        self.expected_success(204, resp.status)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/volume/v1/hosts_client.py b/tempest/lib/services/volume/v1/hosts_client.py
index f344678..2e94274 100644
--- a/tempest/lib/services/volume/v1/hosts_client.py
+++ b/tempest/lib/services/volume/v1/hosts_client.py
@@ -13,8 +13,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from urllib import parse as urllib
+
 from oslo_serialization import jsonutils as json
-from six.moves.urllib import parse as urllib
 
 from tempest.lib.common import rest_client
 
diff --git a/tempest/lib/services/volume/v1/quotas_client.py b/tempest/lib/services/volume/v1/quotas_client.py
index 7f191ca..d7c9698 100644
--- a/tempest/lib/services/volume/v1/quotas_client.py
+++ b/tempest/lib/services/volume/v1/quotas_client.py
@@ -12,8 +12,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from urllib import parse as urllib
+
 from oslo_serialization import jsonutils
-from six.moves.urllib import parse as urllib
 
 from tempest.lib.common import rest_client
 
diff --git a/tempest/lib/services/volume/v1/services_client.py b/tempest/lib/services/volume/v1/services_client.py
index d438a34..957a0e6 100644
--- a/tempest/lib/services/volume/v1/services_client.py
+++ b/tempest/lib/services/volume/v1/services_client.py
@@ -13,8 +13,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from urllib import parse as urllib
+
 from oslo_serialization import jsonutils as json
-from six.moves.urllib import parse as urllib
 
 from tempest.lib.common import rest_client
 
diff --git a/tempest/lib/services/volume/v1/snapshots_client.py b/tempest/lib/services/volume/v1/snapshots_client.py
index 7dfdcf2..a478686 100644
--- a/tempest/lib/services/volume/v1/snapshots_client.py
+++ b/tempest/lib/services/volume/v1/snapshots_client.py
@@ -10,8 +10,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from urllib import parse as urllib
+
 from oslo_serialization import jsonutils as json
-from six.moves.urllib import parse as urllib
 
 from tempest.lib.common import rest_client
 from tempest.lib import exceptions as lib_exc
diff --git a/tempest/lib/services/volume/v1/types_client.py b/tempest/lib/services/volume/v1/types_client.py
index d434e65..6237fb4 100644
--- a/tempest/lib/services/volume/v1/types_client.py
+++ b/tempest/lib/services/volume/v1/types_client.py
@@ -13,8 +13,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from urllib import parse as urllib
+
 from oslo_serialization import jsonutils as json
-from six.moves.urllib import parse as urllib
 
 from tempest.lib.common import rest_client
 from tempest.lib import exceptions as lib_exc
diff --git a/tempest/lib/services/volume/v1/volumes_client.py b/tempest/lib/services/volume/v1/volumes_client.py
index 2efb0da..9fca800 100644
--- a/tempest/lib/services/volume/v1/volumes_client.py
+++ b/tempest/lib/services/volume/v1/volumes_client.py
@@ -13,9 +13,10 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from urllib import parse as urllib
+
 from oslo_serialization import jsonutils as json
 import six
-from six.moves.urllib import parse as urllib
 
 from tempest.lib.common import rest_client
 from tempest.lib import exceptions as lib_exc
diff --git a/tempest/lib/services/volume/v3/backups_client.py b/tempest/lib/services/volume/v3/backups_client.py
index 1df45fa..4bf7ffb 100644
--- a/tempest/lib/services/volume/v3/backups_client.py
+++ b/tempest/lib/services/volume/v3/backups_client.py
@@ -13,8 +13,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from urllib import parse as urllib
+
 from oslo_serialization import jsonutils as json
-from six.moves.urllib import parse as urllib
 
 from tempest.lib.api_schema.response.volume import backups as schema
 from tempest.lib.common import rest_client
diff --git a/tempest/lib/services/volume/v3/group_snapshots_client.py b/tempest/lib/services/volume/v3/group_snapshots_client.py
index 4051c06..0f36fc9 100644
--- a/tempest/lib/services/volume/v3/group_snapshots_client.py
+++ b/tempest/lib/services/volume/v3/group_snapshots_client.py
@@ -13,8 +13,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from urllib import parse as urllib
+
 from oslo_serialization import jsonutils as json
-from six.moves.urllib import parse as urllib
 
 from tempest.lib.api_schema.response.volume import group_snapshots as schema
 from tempest.lib.common import rest_client
diff --git a/tempest/lib/services/volume/v3/group_types_client.py b/tempest/lib/services/volume/v3/group_types_client.py
index 1dcd508..9de36f4 100644
--- a/tempest/lib/services/volume/v3/group_types_client.py
+++ b/tempest/lib/services/volume/v3/group_types_client.py
@@ -13,8 +13,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from urllib import parse as urllib
+
 from oslo_serialization import jsonutils as json
-from six.moves.urllib import parse as urllib
 
 from tempest.lib.api_schema.response.volume import group_types as schema
 from tempest.lib.common import rest_client
diff --git a/tempest/lib/services/volume/v3/groups_client.py b/tempest/lib/services/volume/v3/groups_client.py
index 3d8523d..d1500cf 100644
--- a/tempest/lib/services/volume/v3/groups_client.py
+++ b/tempest/lib/services/volume/v3/groups_client.py
@@ -13,8 +13,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from urllib import parse as urllib
+
 from oslo_serialization import jsonutils as json
-from six.moves.urllib import parse as urllib
 
 from tempest.lib.api_schema.response.volume import groups as schema
 from tempest.lib.common import rest_client
diff --git a/tempest/lib/services/volume/v3/hosts_client.py b/tempest/lib/services/volume/v3/hosts_client.py
index 019a852..9c64659 100644
--- a/tempest/lib/services/volume/v3/hosts_client.py
+++ b/tempest/lib/services/volume/v3/hosts_client.py
@@ -13,8 +13,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from urllib import parse as urllib
+
 from oslo_serialization import jsonutils as json
-from six.moves.urllib import parse as urllib
 
 from tempest.lib.api_schema.response.volume import hosts as schema
 from tempest.lib.common import rest_client
diff --git a/tempest/lib/services/volume/v3/quotas_client.py b/tempest/lib/services/volume/v3/quotas_client.py
index 5b1a52c..3f4c4e1 100644
--- a/tempest/lib/services/volume/v3/quotas_client.py
+++ b/tempest/lib/services/volume/v3/quotas_client.py
@@ -13,8 +13,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from urllib import parse as urllib
+
 from oslo_serialization import jsonutils
-from six.moves.urllib import parse as urllib
 
 from tempest.lib.api_schema.response.volume import quotas as schema
 from tempest.lib.common import rest_client
diff --git a/tempest/lib/services/volume/v3/services_client.py b/tempest/lib/services/volume/v3/services_client.py
index 8bc82c9..4672da8 100644
--- a/tempest/lib/services/volume/v3/services_client.py
+++ b/tempest/lib/services/volume/v3/services_client.py
@@ -13,8 +13,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from urllib import parse as urllib
+
 from oslo_serialization import jsonutils as json
-from six.moves.urllib import parse as urllib
 
 from tempest.lib.api_schema.response.volume import services as schema
 from tempest.lib.common import rest_client
diff --git a/tempest/lib/services/volume/v3/snapshots_client.py b/tempest/lib/services/volume/v3/snapshots_client.py
index 8ca2044..ae31ee9 100644
--- a/tempest/lib/services/volume/v3/snapshots_client.py
+++ b/tempest/lib/services/volume/v3/snapshots_client.py
@@ -13,8 +13,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from urllib import parse as urllib
+
 from oslo_serialization import jsonutils as json
-from six.moves.urllib import parse as urllib
 
 from tempest.lib.api_schema.response.volume import snapshots as schema
 from tempest.lib.common import rest_client
diff --git a/tempest/lib/services/volume/v3/transfers_client.py b/tempest/lib/services/volume/v3/transfers_client.py
index f572f95..36198c3 100644
--- a/tempest/lib/services/volume/v3/transfers_client.py
+++ b/tempest/lib/services/volume/v3/transfers_client.py
@@ -13,8 +13,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from urllib import parse as urllib
+
 from oslo_serialization import jsonutils as json
-from six.moves.urllib import parse as urllib
 
 from tempest.lib.api_schema.response.volume import transfers as schema
 from tempest.lib.common import rest_client
diff --git a/tempest/lib/services/volume/v3/types_client.py b/tempest/lib/services/volume/v3/types_client.py
index 1ebd447..9858d87 100644
--- a/tempest/lib/services/volume/v3/types_client.py
+++ b/tempest/lib/services/volume/v3/types_client.py
@@ -13,8 +13,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from urllib import parse as urllib
+
 from oslo_serialization import jsonutils as json
-from six.moves.urllib import parse as urllib
 
 from tempest.lib.api_schema.response.volume import volume_types as schema
 from tempest.lib.common import rest_client
diff --git a/tempest/lib/services/volume/v3/versions_client.py b/tempest/lib/services/volume/v3/versions_client.py
index 4ac4112..0bed827 100644
--- a/tempest/lib/services/volume/v3/versions_client.py
+++ b/tempest/lib/services/volume/v3/versions_client.py
@@ -12,7 +12,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from six.moves.urllib.parse import urljoin
+from urllib.parse import urljoin
 
 from oslo_serialization import jsonutils as json
 
diff --git a/tempest/lib/services/volume/v3/volumes_client.py b/tempest/lib/services/volume/v3/volumes_client.py
index b8535d8..147a79b 100644
--- a/tempest/lib/services/volume/v3/volumes_client.py
+++ b/tempest/lib/services/volume/v3/volumes_client.py
@@ -13,9 +13,10 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from urllib import parse as urllib
+
 from oslo_serialization import jsonutils as json
 import six
-from six.moves.urllib import parse as urllib
 
 from tempest.lib.api_schema.response.volume import volumes as schema
 from tempest.lib.common import rest_client
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index f725b3f..27d30f2 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -18,6 +18,7 @@
 import subprocess
 
 import netaddr
+
 from oslo_log import log
 from oslo_serialization import jsonutils as json
 from oslo_utils import netutils
@@ -451,11 +452,20 @@
                                            server_id, 'ACTIVE')
 
     def create_volume_snapshot(self, volume_id, name=None, description=None,
-                               metadata=None, force=False):
-        """Creates volume
+                               metadata=None, force=False, **kwargs):
+        """Creates volume's snapshot
 
-        This wrapper utility creates volume snapshot and waits for backup
-        to be in 'available' state.
+        This wrapper utility creates volume snapshot and waits for it until
+        it is in 'available' state.
+
+        :param volume_id: UUID of a volume to create snapshot of
+        :param name: name of the snapshot, '$classname-snapshot' by default
+        :param description: description of the snapshot
+        :param metadata: metadata key and value pairs for the snapshot
+        :param force: whether snapshot even when the volume is attached
+        :param **kwargs: additional parameters per the doc
+            https://docs.openstack.org/api-ref/block-storage/v3/
+            #create-a-snapshot
         """
 
         name = name or data_utils.rand_name(
@@ -465,7 +475,8 @@
             force=force,
             name=name,
             description=description,
-            metadata=metadata)['snapshot']
+            metadata=metadata,
+            **kwargs)['snapshot']
 
         self.addCleanup(self.snapshots_client.wait_for_resource_deletion,
                         snapshot['id'])
@@ -476,7 +487,7 @@
             snapshot['id'])['snapshot']
         return snapshot
 
-    def _cleanup_volume_type(self, volume_type):
+    def cleanup_volume_type(self, volume_type):
         """Clean up a given volume type.
 
         Ensuring all volumes associated to a type are first removed before
@@ -532,7 +543,7 @@
         volume_type = volume_type_resp['volume_type']
 
         self.assertIn('id', volume_type)
-        self.addCleanup(self._cleanup_volume_type, volume_type)
+        self.addCleanup(self.cleanup_volume_type, volume_type)
         return volume_type
 
     def create_loginable_secgroup_rule(self, secgroup_id=None, rulesets=None):
@@ -579,7 +590,7 @@
             rules.append(sg_rule)
         return rules
 
-    def _create_security_group(self, **kwargs):
+    def create_security_group(self, **kwargs):
         """Create security group and add rules to security group"""
         if not kwargs.get('name'):
             kwargs['name'] = data_utils.rand_name(self.__class__.__name__)
@@ -630,18 +641,13 @@
     def image_create(self, name='scenario-img', **kwargs):
         img_path = CONF.scenario.img_file
         if not os.path.exists(img_path):
-            # TODO(kopecmartin): replace LOG.warning for rasing
-            # InvalidConfiguration exception after tempest 25.0.0 is
-            # released - there will be one release which accepts both
-            # behaviors in order to avoid many failures across CIs and etc.
-            LOG.warning(
+            lib_exc.InvalidConfiguration(
                 'Starting Tempest 25.0.0 release, CONF.scenario.img_file need '
                 'a full path for the image. CONF.scenario.img_dir was '
                 'deprecated and will be removed in the next release. Till '
-                'Tempest 25.0.0, old behavior is maintained and keep working '
+                'Tempest 25.0.0, old behavior was maintained and kept working '
                 'but starting Tempest 26.0.0, you need to specify the full '
                 'path in CONF.scenario.img_file config option.')
-            img_path = os.path.join(CONF.scenario.img_dir, img_path)
         img_container_format = CONF.scenario.img_container_format
         img_disk_format = CONF.scenario.img_disk_format
         img_properties = CONF.scenario.img_properties
@@ -1050,10 +1056,10 @@
         if not CONF.service_available.neutron:
             raise cls.skipException('Neutron not available')
 
-    def _create_network(self, networks_client=None,
-                        project_id=None,
-                        namestart='network-smoke-',
-                        port_security_enabled=True, **net_dict):
+    def create_network(self, networks_client=None,
+                       project_id=None,
+                       namestart='network-smoke-',
+                       port_security_enabled=True, **net_dict):
         if not networks_client:
             networks_client = self.networks_client
         if not project_id:
@@ -1088,6 +1094,8 @@
         :Keyword Arguments:
 
             * *ip_version = ip version of the given network,
+            use_default_subnetpool = default subnetpool to
+                    manage IPv6 addresses range.
         """
 
         if not subnets_client:
@@ -1110,51 +1118,73 @@
                         network_id=ext_net['id'], cidr=cidr)['subnets'])
             return len(tenant_subnets + external_subnets) != 0
 
-        ip_version = kwargs.pop('ip_version', 4)
-
-        if ip_version == 6:
-            tenant_cidr = netaddr.IPNetwork(
-                CONF.network.project_network_v6_cidr)
-            num_bits = CONF.network.project_network_v6_mask_bits
-        else:
-            tenant_cidr = netaddr.IPNetwork(CONF.network.project_network_cidr)
-            num_bits = CONF.network.project_network_mask_bits
-
-        result = None
-        str_cidr = None
-        # Repeatedly attempt subnet creation with sequential cidr
-        # blocks until an unallocated block is found.
-        for subnet_cidr in tenant_cidr.subnet(num_bits):
-            str_cidr = str(subnet_cidr)
-            if cidr_in_use(str_cidr, project_id=network['project_id']):
-                continue
+        def _make_create_subnet_request(namestart, network,
+                                        ip_version, subnets_client, **kwargs):
 
             subnet = dict(
                 name=data_utils.rand_name(namestart),
                 network_id=network['id'],
                 project_id=network['project_id'],
-                cidr=str_cidr,
                 ip_version=ip_version,
                 **kwargs
             )
+
+            if ip_version == 6:
+                subnet['ipv6_address_mode'] = 'slaac'
+                subnet['ipv6_ra_mode'] = 'slaac'
+
             try:
-                result = subnets_client.create_subnet(**subnet)
-                break
+                return subnets_client.create_subnet(**subnet)
             except lib_exc.Conflict as e:
-                is_overlapping_cidr = 'overlaps with another subnet' in str(e)
-                if not is_overlapping_cidr:
+                if 'overlaps with another subnet' not in str(e):
                     raise
+
+        result = None
+        str_cidr = None
+
+        use_default_subnetpool = kwargs.get('use_default_subnetpool', False)
+        ip_version = kwargs.pop('ip_version', 4)
+
+        if not use_default_subnetpool:
+
+            if ip_version == 6:
+                tenant_cidr = netaddr.IPNetwork(
+                    CONF.network.project_network_v6_cidr)
+                num_bits = CONF.network.project_network_v6_mask_bits
+            else:
+                tenant_cidr = netaddr.IPNetwork(
+                    CONF.network.project_network_cidr)
+                num_bits = CONF.network.project_network_mask_bits
+
+        # Repeatedly attempt subnet creation with sequential cidr
+        # blocks until an unallocated block is found.
+            for subnet_cidr in tenant_cidr.subnet(num_bits):
+                str_cidr = str(subnet_cidr)
+                if cidr_in_use(str_cidr, project_id=network['project_id']):
+                    continue
+                result = _make_create_subnet_request(
+                    namestart, network, ip_version, subnets_client,
+                    cidr=str_cidr, **kwargs)
+
+                if result is not None:
+                    break
+
+        else:
+            result = _make_create_subnet_request(
+                namestart, network, ip_version, subnets_client,
+                **kwargs)
         self.assertIsNotNone(result, 'Unable to allocate tenant network')
 
         subnet = result['subnet']
-        self.assertEqual(subnet['cidr'], str_cidr)
+        if str_cidr is not None:
+            self.assertEqual(subnet['cidr'], str_cidr)
 
         self.addCleanup(test_utils.call_and_ignore_notfound_exc,
                         subnets_client.delete_subnet, subnet['id'])
 
         return subnet
 
-    def _get_server_port_id_and_ip4(self, server, ip_addr=None, **kwargs):
+    def get_server_port_id_and_ip4(self, server, ip_addr=None, **kwargs):
 
         if ip_addr and not kwargs.get('fixed_ips'):
             kwargs['fixed_ips'] = 'ip_address=%s' % ip_addr
@@ -1191,7 +1221,7 @@
                          % port_map)
         return port_map[0]
 
-    def _get_network_by_name(self, network_name):
+    def get_network_by_name(self, network_name):
         net = self.os_admin.networks_client.list_networks(
             name=network_name)['networks']
         self.assertNotEmpty(net,
@@ -1207,7 +1237,7 @@
         if not client:
             client = self.floating_ips_client
         if not port_id:
-            port_id, ip4 = self._get_server_port_id_and_ip4(server)
+            port_id, ip4 = self.get_server_port_id_and_ip4(server)
         else:
             ip4 = None
 
@@ -1235,7 +1265,7 @@
         This wrapper utility attaches the floating_ip for
         the respective port_id of server
         """
-        port_id, _ = self._get_server_port_id_and_ip4(server)
+        port_id, _ = self.get_server_port_id_and_ip4(server)
         kwargs = dict(port_id=port_id)
         floating_ip = self.floating_ips_client.update_floatingip(
             floating_ip['id'], **kwargs)['floatingip']
@@ -1351,17 +1381,17 @@
         self.log_console_output()
         self.fail(msg)
 
-    def _create_security_group(self, security_group_rules_client=None,
-                               project_id=None,
-                               namestart='secgroup-smoke',
-                               security_groups_client=None):
+    def create_security_group(self, security_group_rules_client=None,
+                              project_id=None,
+                              namestart='secgroup-smoke',
+                              security_groups_client=None):
         if security_group_rules_client is None:
             security_group_rules_client = self.security_group_rules_client
         if security_groups_client is None:
             security_groups_client = self.security_groups_client
         if project_id is None:
             project_id = security_groups_client.project_id
-        secgroup = self._create_empty_security_group(
+        secgroup = self.create_empty_security_group(
             namestart=namestart, client=security_groups_client,
             project_id=project_id)
 
@@ -1375,8 +1405,8 @@
             self.assertEqual(secgroup['id'], rule['security_group_id'])
         return secgroup
 
-    def _create_empty_security_group(self, client=None, project_id=None,
-                                     namestart='secgroup-smoke'):
+    def create_empty_security_group(self, client=None, project_id=None,
+                                    namestart='secgroup-smoke'):
         """Create a security group without rules.
 
         Default rules will be created:
@@ -1510,7 +1540,7 @@
 
         return rules
 
-    def _get_router(self, client=None, project_id=None, **kwargs):
+    def get_router(self, client=None, project_id=None, **kwargs):
         """Retrieve a router for the given tenant id.
 
         If a public router has been configured, it will be returned.
@@ -1579,18 +1609,18 @@
             if not CONF.compute.fixed_network_name:
                 m = 'fixed_network_name must be specified in config'
                 raise lib_exc.InvalidConfiguration(m)
-            network = self._get_network_by_name(
+            network = self.get_network_by_name(
                 CONF.compute.fixed_network_name)
             router = None
             subnet = None
         else:
-            network = self._create_network(
+            network = self.create_network(
                 networks_client=networks_client,
                 project_id=project_id,
                 port_security_enabled=port_security_enabled,
                 **net_dict)
-            router = self._get_router(client=routers_client,
-                                      project_id=project_id)
+            router = self.get_router(client=routers_client,
+                                     project_id=project_id)
             subnet_kwargs = dict(network=network,
                                  subnets_client=subnets_client)
             # use explicit check because empty list is a valid option
diff --git a/tempest/scenario/test_minimum_basic.py b/tempest/scenario/test_minimum_basic.py
index fe42583..5201315 100644
--- a/tempest/scenario/test_minimum_basic.py
+++ b/tempest/scenario/test_minimum_basic.py
@@ -78,7 +78,7 @@
         self.assertEqual(1, disks.count(CONF.compute.volume_device_name))
 
     def create_and_add_security_group_to_server(self, server):
-        secgroup = self._create_security_group()
+        secgroup = self.create_security_group()
         self.servers_client.add_security_group(server['id'],
                                                name=secgroup['name'])
         self.addCleanup(self.servers_client.remove_security_group,
diff --git a/tempest/scenario/test_network_advanced_server_ops.py b/tempest/scenario/test_network_advanced_server_ops.py
index dbab212..20d2e80 100644
--- a/tempest/scenario/test_network_advanced_server_ops.py
+++ b/tempest/scenario/test_network_advanced_server_ops.py
@@ -60,7 +60,7 @@
     def _setup_server(self, keypair):
         security_groups = []
         if utils.is_extension_enabled('security-group', 'network'):
-            security_group = self._create_security_group()
+            security_group = self.create_security_group()
             security_groups = [{'name': security_group['name']}]
         network, _, _ = self.create_networks()
         server = self.create_server(
diff --git a/tempest/scenario/test_network_basic_ops.py b/tempest/scenario/test_network_basic_ops.py
index 6c1b3fa..e359c71 100644
--- a/tempest/scenario/test_network_basic_ops.py
+++ b/tempest/scenario/test_network_basic_ops.py
@@ -159,7 +159,7 @@
         keypair = self.create_keypair()
         self.keypairs[keypair['name']] = keypair
         security_groups = [
-            {'name': self._create_security_group()['name']}
+            {'name': self.create_security_group()['name']}
         ]
         network = {'uuid': network['id']}
         if port_id is not None:
@@ -223,14 +223,14 @@
         floating_ip, server = self.floating_ip_tuple
         # create a new server for the floating ip
         server = self._create_server(self.network)
-        port_id, _ = self._get_server_port_id_and_ip4(server)
+        port_id, _ = self.get_server_port_id_and_ip4(server)
         floating_ip = self.floating_ips_client.update_floatingip(
             floating_ip['id'], port_id=port_id)['floatingip']
         self.assertEqual(port_id, floating_ip['port_id'])
         self.floating_ip_tuple = Floating_IP_tuple(floating_ip, server)
 
     def _create_new_network(self, create_gateway=False):
-        self.new_net = self._create_network()
+        self.new_net = self.create_network()
         if create_gateway:
             self.new_subnet = self.create_subnet(
                 network=self.new_net)
diff --git a/tempest/scenario/test_network_v6.py b/tempest/scenario/test_network_v6.py
index 9be28c4..4f5118b 100644
--- a/tempest/scenario/test_network_v6.py
+++ b/tempest/scenario/test_network_v6.py
@@ -66,7 +66,7 @@
     def setUp(self):
         super(TestGettingAddress, self).setUp()
         self.keypair = self.create_keypair()
-        self.sec_grp = self._create_security_group()
+        self.sec_grp = self.create_security_group()
 
     def prepare_network(self, address6_mode, n_subnets6=1, dualnet=False):
         """Prepare network
@@ -77,15 +77,15 @@
         if dualnet - create IPv6 subnets on a different network
         :return: list of created networks
         """
-        network = self._create_network()
+        network = self.create_network()
         if dualnet:
-            network_v6 = self._create_network()
+            network_v6 = self.create_network()
 
         sub4 = self.create_subnet(network=network,
                                   namestart='sub4',
                                   ip_version=4)
 
-        router = self._get_router()
+        router = self.get_router()
         self.routers_client.add_router_interface(router['id'],
                                                  subnet_id=sub4['id'])
 
diff --git a/tempest/scenario/test_security_groups_basic_ops.py b/tempest/scenario/test_security_groups_basic_ops.py
index 496a371..e078f23 100644
--- a/tempest/scenario/test_security_groups_basic_ops.py
+++ b/tempest/scenario/test_security_groups_basic_ops.py
@@ -197,14 +197,14 @@
         tenant.keypair = keypair
 
     def _create_tenant_security_groups(self, tenant):
-        access_sg = self._create_empty_security_group(
+        access_sg = self.create_empty_security_group(
             namestart='secgroup_access-',
             project_id=tenant.creds.project_id,
             client=tenant.manager.security_groups_client
         )
 
         # don't use default secgroup since it allows in-project traffic
-        def_sg = self._create_empty_security_group(
+        def_sg = self.create_empty_security_group(
             namestart='secgroup_general-',
             project_id=tenant.creds.project_id,
             client=tenant.manager.security_groups_client
@@ -534,7 +534,7 @@
         new_tenant = self.primary_tenant
 
         # Create empty security group and add icmp rule in it
-        new_sg = self._create_empty_security_group(
+        new_sg = self.create_empty_security_group(
             namestart='secgroup_new-',
             project_id=new_tenant.creds.project_id,
             client=new_tenant.manager.security_groups_client)
diff --git a/tempest/scenario/test_server_basic_ops.py b/tempest/scenario/test_server_basic_ops.py
index 60242d5..4c82d84 100644
--- a/tempest/scenario/test_server_basic_ops.py
+++ b/tempest/scenario/test_server_basic_ops.py
@@ -128,7 +128,7 @@
     @utils.services('compute', 'network')
     def test_server_basic_ops(self):
         keypair = self.create_keypair()
-        security_group = self._create_security_group()
+        security_group = self.create_security_group()
         self.md = {'meta1': 'data1', 'meta2': 'data2', 'metaN': 'dataN'}
         self.instance = self.create_server(
             key_name=keypair['name'],
diff --git a/tempest/scenario/test_shelve_instance.py b/tempest/scenario/test_shelve_instance.py
index ed06898..29612ec 100644
--- a/tempest/scenario/test_shelve_instance.py
+++ b/tempest/scenario/test_shelve_instance.py
@@ -76,7 +76,7 @@
                                                 cold_migrate=False):
         keypair = self.create_keypair()
 
-        security_group = self._create_security_group()
+        security_group = self.create_security_group()
         security_groups = [{'name': security_group['name']}]
 
         server = self.create_server(
diff --git a/tempest/scenario/test_snapshot_pattern.py b/tempest/scenario/test_snapshot_pattern.py
index a062d40..d04cb9a 100644
--- a/tempest/scenario/test_snapshot_pattern.py
+++ b/tempest/scenario/test_snapshot_pattern.py
@@ -50,7 +50,7 @@
     def test_snapshot_pattern(self):
         # prepare for booting an instance
         keypair = self.create_keypair()
-        security_group = self._create_security_group()
+        security_group = self.create_security_group()
 
         # boot an instance and create a timestamp file in it
         server = self.create_server(
diff --git a/tempest/scenario/test_stamp_pattern.py b/tempest/scenario/test_stamp_pattern.py
index a8e4c30..4b81b9e 100644
--- a/tempest/scenario/test_stamp_pattern.py
+++ b/tempest/scenario/test_stamp_pattern.py
@@ -81,7 +81,7 @@
     def test_stamp_pattern(self):
         # prepare for booting an instance
         keypair = self.create_keypair()
-        security_group = self._create_security_group()
+        security_group = self.create_security_group()
 
         # boot an instance and create a timestamp file in it
         volume = self.create_volume()
diff --git a/tempest/scenario/test_volume_backup_restore.py b/tempest/scenario/test_volume_backup_restore.py
index 8a8c54e..71e6b53 100644
--- a/tempest/scenario/test_volume_backup_restore.py
+++ b/tempest/scenario/test_volume_backup_restore.py
@@ -70,7 +70,7 @@
 
         # Create keypair and security group
         keypair = self.create_keypair()
-        security_group = self._create_security_group()
+        security_group = self.create_security_group()
 
         # Boot a server from the restored backup
         bd_map_v2 = [{
diff --git a/tempest/scenario/test_volume_boot_pattern.py b/tempest/scenario/test_volume_boot_pattern.py
index 3b4bbda..5a5cc27 100644
--- a/tempest/scenario/test_volume_boot_pattern.py
+++ b/tempest/scenario/test_volume_boot_pattern.py
@@ -64,7 +64,7 @@
 
         LOG.info("Creating keypair and security group")
         keypair = self.create_keypair()
-        security_group = self._create_security_group()
+        security_group = self.create_security_group()
 
         # create an instance from volume
         LOG.info("Booting instance 1 from volume")
diff --git a/tempest/scenario/test_volume_migrate_attached.py b/tempest/scenario/test_volume_migrate_attached.py
index 106500e..57d2a1a 100644
--- a/tempest/scenario/test_volume_migrate_attached.py
+++ b/tempest/scenario/test_volume_migrate_attached.py
@@ -100,7 +100,7 @@
     def test_volume_retype_attached(self):
         LOG.info("Creating keypair and security group")
         keypair = self.create_keypair()
-        security_group = self._create_security_group()
+        security_group = self.create_security_group()
 
         # create volume types
         LOG.info("Creating Volume types")
@@ -156,7 +156,7 @@
     def test_volume_migrate_attached(self):
         LOG.info("Creating keypair and security group")
         keypair = self.create_keypair()
-        security_group = self._create_security_group()
+        security_group = self.create_security_group()
 
         LOG.info("Creating volume")
         # Create a unique volume type to avoid using the backend default
diff --git a/tempest/services/orchestration/json/orchestration_client.py b/tempest/services/orchestration/json/orchestration_client.py
index 9fec548..0d7720e 100644
--- a/tempest/services/orchestration/json/orchestration_client.py
+++ b/tempest/services/orchestration/json/orchestration_client.py
@@ -15,9 +15,9 @@
 
 import re
 import time
+from urllib import parse as urllib
 
 from oslo_serialization import jsonutils as json
-from six.moves.urllib import parse as urllib
 
 from tempest import exceptions
 from tempest.lib.common import rest_client
diff --git a/tempest/test.py b/tempest/test.py
index 68602d6..655b9a4 100644
--- a/tempest/test.py
+++ b/tempest/test.py
@@ -296,6 +296,7 @@
         identity_version = cls.get_identity_version()
         # setting force_tenant_isolation to True also needs admin credentials.
         if ('admin' in cls.credentials or
+                'alt_admin' in cls.credentials or
                 getattr(cls, 'force_tenant_isolation', False)):
             if not credentials.is_admin_available(
                     identity_version=identity_version):
@@ -414,8 +415,18 @@
                             'alt_manager', 'os_alt', version='Pike',
                             removal_version='Queens')
             elif isinstance(credentials_type, list):
+                scope = 'project'
+                if credentials_type[0].startswith('system'):
+                    scope = 'system'
+                elif credentials_type[0].startswith('domain'):
+                    scope = 'domain'
                 manager = cls.get_client_manager(roles=credentials_type[1:],
-                                                 force_new=True)
+                                                 force_new=True,
+                                                 scope=scope)
+                setattr(cls, 'os_%s' % credentials_type[0], manager)
+                # TODO(gmann): Setting the old style attribute too for
+                # backward compatibility but at some point we should
+                # remove this.
                 setattr(cls, 'os_roles_%s' % credentials_type[0], manager)
 
     @classmethod
@@ -657,7 +668,7 @@
 
     @classmethod
     def get_client_manager(cls, credential_type=None, roles=None,
-                           force_new=None):
+                           force_new=None, scope=None):
         """Returns an OpenStack client manager
 
         Returns an OpenStack client manager based on either credential_type
@@ -665,6 +676,7 @@
         credential_type 'primary'
         :param credential_type: string - primary, alt or admin
         :param roles: list of roles
+        :param scope: scope for the test user
 
         :returns: the created client manager
         :raises skipException: if the requested credentials are not available
@@ -683,7 +695,7 @@
                         " is not able to provide credentials with the %s role "
                         "assigned." % (cls.__name__, role))
                     raise cls.skipException(skip_msg)
-            params = dict(roles=roles)
+            params = dict(roles=roles, scope=scope)
             if force_new is not None:
                 params.update(force_new=force_new)
             creds = cred_provider.get_creds_by_roles(**params)
@@ -850,7 +862,13 @@
         if isinstance(credentials_type, six.string_types):
             manager = cls.get_client_manager(credential_type=credentials_type)
         elif isinstance(credentials_type, list):
-            manager = cls.get_client_manager(roles=credentials_type[1:])
+            scope = 'project'
+            if credentials_type[0].startswith('system'):
+                scope = 'system'
+            elif credentials_type[0].startswith('domain'):
+                scope = 'domain'
+            manager = cls.get_client_manager(roles=credentials_type[1:],
+                                             scope=scope)
         else:
             manager = cls.get_client_manager()
 
diff --git a/tempest/test_discover/plugins.py b/tempest/test_discover/plugins.py
index b20b60e..1d69d9d 100644
--- a/tempest/test_discover/plugins.py
+++ b/tempest/test_discover/plugins.py
@@ -15,7 +15,6 @@
 import abc
 
 from oslo_log import log as logging
-import six
 import stevedore
 
 from tempest.lib.common.utils import misc
@@ -24,8 +23,7 @@
 LOG = logging.getLogger(__name__)
 
 
-@six.add_metaclass(abc.ABCMeta)
-class TempestPlugin(object):
+class TempestPlugin(object, metaclass=abc.ABCMeta):
     """Provide basic hooks for an external plugin
 
     To provide tempest the necessary information to run the plugin.
diff --git a/tempest/tests/api/compute/test_base.py b/tempest/tests/api/compute/test_base.py
index 74d2625..8a1873b 100644
--- a/tempest/tests/api/compute/test_base.py
+++ b/tempest/tests/api/compute/test_base.py
@@ -15,7 +15,6 @@
 from unittest import mock
 
 from oslo_utils import uuidutils
-import six
 
 from tempest.api.compute import base as compute_base
 from tempest.common import waiters
@@ -128,9 +127,9 @@
             mock.sentinel.server_id, wait_until='active')
         # make our assertions
         if fault:
-            self.assertIn(fault, six.text_type(ex))
+            self.assertIn(fault, str(ex))
         else:
-            self.assertNotIn(fault, six.text_type(ex))
+            self.assertNotIn(fault, str(ex))
         if compute_base.BaseV2ComputeTest.is_requested_microversion_compatible(
             '2.35'):
             status = 'ACTIVE'
diff --git a/tempest/tests/cmd/test_run.py b/tempest/tests/cmd/test_run.py
index ec7b760..3b5e901 100644
--- a/tempest/tests/cmd/test_run.py
+++ b/tempest/tests/cmd/test_run.py
@@ -21,7 +21,6 @@
 from unittest import mock
 
 import fixtures
-import six
 
 from tempest.cmd import run
 from tempest.cmd import workspace
@@ -153,12 +152,12 @@
         subprocess.call(['stestr', 'init'])
         out, err = self.assertRunExit(['tempest', 'run', '-l'], 0)
         tests = out.split()
-        tests = sorted([six.text_type(x.rstrip()) for x in tests if x])
+        tests = sorted([str(x.rstrip()) for x in tests if x])
         result = [
-            six.text_type('tests.test_failing.FakeTestClass.test_pass'),
-            six.text_type('tests.test_failing.FakeTestClass.test_pass_list'),
-            six.text_type('tests.test_passing.FakeTestClass.test_pass'),
-            six.text_type('tests.test_passing.FakeTestClass.test_pass_list'),
+            str('tests.test_failing.FakeTestClass.test_pass'),
+            str('tests.test_failing.FakeTestClass.test_pass_list'),
+            str('tests.test_passing.FakeTestClass.test_pass'),
+            str('tests.test_passing.FakeTestClass.test_pass_list'),
         ]
         # NOTE(mtreinish): on python 3 the subprocess prints b'' around
         # stdout.
diff --git a/tempest/tests/cmd/test_verify_tempest_config.py b/tempest/tests/cmd/test_verify_tempest_config.py
index 277e049..a8a4c0f 100644
--- a/tempest/tests/cmd/test_verify_tempest_config.py
+++ b/tempest/tests/cmd/test_verify_tempest_config.py
@@ -18,12 +18,9 @@
 import fixtures
 from oslo_serialization import jsonutils as json
 
-from tempest import clients
 from tempest.cmd import init
 from tempest.cmd import verify_tempest_config
-from tempest.common import credentials_factory
 from tempest import config
-from tempest.lib.common import rest_client
 from tempest.lib.common.utils import data_utils
 from tempest.lib import exceptions as lib_exc
 from tempest.tests import base
@@ -97,15 +94,15 @@
         self.useFixture(fixtures.MockPatchObject(
             verify_tempest_config, '_get_unversioned_endpoint',
             return_value='http://fake_endpoint:5000'))
-        fake_resp = {'versions': [{'id': 'v1.0'}, {'id': 'v2.0'}]}
+        fake_resp = {'versions': [{'id': 'v2.0'}, {'id': 'v3.0'}]}
         fake_resp = json.dumps(fake_resp)
         self.useFixture(fixtures.MockPatch(
             'tempest.lib.common.http.ClosingHttp.request',
             return_value=(None, fake_resp)))
         fake_os = mock.MagicMock()
         versions = verify_tempest_config._get_api_versions(fake_os, 'cinder')
-        self.assertIn('v1.0', versions)
         self.assertIn('v2.0', versions)
+        self.assertIn('v3.0', versions)
 
     def test_get_nova_versions(self):
         self.useFixture(fixtures.MockPatchObject(
@@ -145,7 +142,7 @@
         self.assertTrue(mock_log_error.called)
 
     def test_verify_api_versions(self):
-        api_services = ['cinder', 'glance', 'keystone']
+        api_services = ['glance', 'keystone']
         fake_os = mock.MagicMock()
         for svc in api_services:
             m = 'verify_%s_api_versions' % svc
@@ -154,7 +151,7 @@
                 verify_mock.assert_called_once_with(fake_os, True)
 
     def test_verify_api_versions_not_implemented(self):
-        api_services = ['cinder', 'glance', 'keystone']
+        api_services = ['glance', 'keystone']
         fake_os = mock.MagicMock()
         for svc in api_services:
             m = 'verify_%s_api_versions' % svc
@@ -178,52 +175,6 @@
                                            'identity-feature-enabled',
                                            False, True)
 
-    @mock.patch('tempest.lib.common.http.ClosingHttp.request')
-    def test_verify_cinder_api_versions_no_v3(self, mock_request):
-        self.useFixture(fixtures.MockPatchObject(
-            verify_tempest_config, '_get_unversioned_endpoint',
-            return_value='http://fake_endpoint:5000'))
-        fake_resp = {'versions': [{'id': 'v2.0'}]}
-        fake_resp = json.dumps(fake_resp)
-        mock_request.return_value = (None, fake_resp)
-        fake_os = mock.MagicMock()
-        with mock.patch.object(verify_tempest_config,
-                               'print_and_or_update') as print_mock:
-            verify_tempest_config.verify_cinder_api_versions(fake_os, True)
-        print_mock.assert_any_call('api_v3', 'volume-feature-enabled',
-                                   False, True)
-        self.assertEqual(1, print_mock.call_count)
-
-    @mock.patch('tempest.lib.common.http.ClosingHttp.request')
-    def test_verify_cinder_api_versions_no_v2(self, mock_request):
-        self.useFixture(fixtures.MockPatchObject(
-            verify_tempest_config, '_get_unversioned_endpoint',
-            return_value='http://fake_endpoint:5000'))
-        fake_resp = {'versions': [{'id': 'v3.0'}]}
-        fake_resp = json.dumps(fake_resp)
-        mock_request.return_value = (None, fake_resp)
-        fake_os = mock.MagicMock()
-        with mock.patch.object(verify_tempest_config,
-                               'print_and_or_update') as print_mock:
-            verify_tempest_config.verify_cinder_api_versions(fake_os, True)
-        print_mock.assert_any_call('api_v2', 'volume-feature-enabled',
-                                   False, True)
-        self.assertEqual(1, print_mock.call_count)
-
-    @mock.patch('tempest.lib.common.http.ClosingHttp.request')
-    def test_verify_cinder_api_versions_no_v1(self, mock_request):
-        self.useFixture(fixtures.MockPatchObject(
-            verify_tempest_config, '_get_unversioned_endpoint',
-            return_value='http://fake_endpoint:5000'))
-        fake_resp = {'versions': [{'id': 'v2.0'}, {'id': 'v3.0'}]}
-        fake_resp = json.dumps(fake_resp)
-        mock_request.return_value = (None, fake_resp)
-        fake_os = mock.MagicMock()
-        with mock.patch.object(verify_tempest_config,
-                               'print_and_or_update') as print_mock:
-            verify_tempest_config.verify_cinder_api_versions(fake_os, True)
-        print_mock.assert_not_called()
-
     def test_verify_glance_version_no_v2_with_v1_1(self):
         # This test verifies that wrong config api_v2 = True is detected
         class FakeClient(object):
@@ -560,23 +511,24 @@
         self.assertEqual([], results['swift']['extensions'])
 
     def test_get_extension_client(self):
-        creds = credentials_factory.get_credentials(
-            fill_in=False, username='fake_user', project_name='fake_project',
-            password='fake_password')
-        os = clients.Manager(creds)
-        for service in ['nova', 'neutron', 'swift', 'cinder']:
+        fake_os = mock.MagicMock()
+        services = {
+            'nova': fake_os.compute.ExtensionsClient(),
+            'neutron': fake_os.network.ExtensionsClient(),
+            'swift': fake_os.object_storage.CapabilitiesClient(),
+            'cinder': fake_os.volume_v2.ExtensionsClient(),
+        }
+        for service in services.keys():
             extensions_client = verify_tempest_config.get_extension_client(
-                os, service)
-            self.assertIsInstance(extensions_client, rest_client.RestClient)
+                fake_os, service)
+            self.assertIsInstance(extensions_client, mock.MagicMock)
+            self.assertEqual(extensions_client, services[service])
 
     def test_get_extension_client_sysexit(self):
-        creds = credentials_factory.get_credentials(
-            fill_in=False, username='fake_user', project_name='fake_project',
-            password='fake_password')
-        os = clients.Manager(creds)
+        fake_os = mock.MagicMock()
         self.assertRaises(SystemExit,
                           verify_tempest_config.get_extension_client,
-                          os, 'fakeservice')
+                          fake_os, 'fakeservice')
 
     def test_get_config_file(self):
         conf_dir = os.path.join(os.getcwd(), 'etc')
diff --git a/tempest/tests/cmd/test_workspace.py b/tempest/tests/cmd/test_workspace.py
index eae6202..f16d533 100644
--- a/tempest/tests/cmd/test_workspace.py
+++ b/tempest/tests/cmd/test_workspace.py
@@ -12,15 +12,12 @@
 # License for the specific language governing permissions and limitations
 # under the License.
 
+from io import StringIO
 import os
 import shutil
 import subprocess
 import tempfile
 from unittest.mock import patch
-try:
-    from StringIO import StringIO
-except ImportError:
-    from io import StringIO
 
 from tempest.cmd import workspace
 from tempest.lib.common.utils import data_utils
diff --git a/tempest/tests/common/test_compute.py b/tempest/tests/common/test_compute.py
index 45a439c..142bb08 100644
--- a/tempest/tests/common/test_compute.py
+++ b/tempest/tests/common/test_compute.py
@@ -15,7 +15,7 @@
 
 from unittest import mock
 
-from six.moves.urllib import parse as urlparse
+from urllib import parse as urlparse
 
 
 from tempest.common import compute
diff --git a/tempest/tests/lib/cmd/test_check_uuid.py b/tempest/tests/lib/cmd/test_check_uuid.py
index 428e047..5d63dec 100644
--- a/tempest/tests/lib/cmd/test_check_uuid.py
+++ b/tempest/tests/lib/cmd/test_check_uuid.py
@@ -13,13 +13,13 @@
 import ast
 import importlib
 import os
+import shutil
 import sys
 import tempfile
 from unittest import mock
 
-import fixtures
-
 from tempest.lib.cmd import check_uuid
+from tempest.lib import decorators
 from tempest.tests import base
 
 
@@ -40,23 +40,24 @@
         return tests_file
 
     def test_fix_argument_no(self):
-        temp_dir = self.useFixture(fixtures.TempDir(rootdir="."))
-        tests_file = self.create_tests_file(temp_dir.path)
-
+        temp_dir = tempfile.mkdtemp(prefix='check-uuid-no', dir=".")
+        self.addCleanup(shutil.rmtree, temp_dir, ignore_errors=True)
+        tests_file = self.create_tests_file(temp_dir)
         sys.argv = [sys.argv[0]] + ["--package",
-                                    os.path.relpath(temp_dir.path)]
+                                    os.path.relpath(temp_dir)]
 
         self.assertRaises(SystemExit, check_uuid.run)
         with open(tests_file, "r") as f:
             self.assertTrue(TestCLInterface.CODE == f.read())
 
+    @decorators.skip_because(bug='1918316')
     def test_fix_argument_yes(self):
-        temp_dir = self.useFixture(fixtures.TempDir(rootdir="."))
-        tests_file = self.create_tests_file(temp_dir.path)
+        temp_dir = tempfile.mkdtemp(prefix='check-uuid-yes', dir=".")
+        self.addCleanup(shutil.rmtree, temp_dir, ignore_errors=True)
+        tests_file = self.create_tests_file(temp_dir)
 
         sys.argv = [sys.argv[0]] + ["--fix", "--package",
-                                    os.path.relpath(temp_dir.path)]
-
+                                    os.path.relpath(temp_dir)]
         check_uuid.run()
         with open(tests_file, "r") as f:
             self.assertTrue(TestCLInterface.CODE != f.read())
diff --git a/tempest/tests/lib/common/test_api_version_utils.py b/tempest/tests/lib/common/test_api_version_utils.py
index b99e8d4..8d5de09 100644
--- a/tempest/tests/lib/common/test_api_version_utils.py
+++ b/tempest/tests/lib/common/test_api_version_utils.py
@@ -12,7 +12,6 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import six
 import testtools
 
 from tempest.lib.common import api_version_utils
@@ -31,7 +30,7 @@
                                                            cfg_max_version)
         except testtools.TestCase.skipException as e:
             if not expected_skip:
-                raise testtools.TestCase.failureException(six.text_type(e))
+                raise testtools.TestCase.failureException(str(e))
 
     def test_version_min_in_range(self):
         self._test_version('2.2', '2.10', '2.1', '2.7')
diff --git a/tempest/tests/lib/common/test_cred_client.py b/tempest/tests/lib/common/test_cred_client.py
index b99311c..7ea660b 100644
--- a/tempest/tests/lib/common/test_cred_client.py
+++ b/tempest/tests/lib/common/test_cred_client.py
@@ -111,3 +111,21 @@
         self.assertIsNone(ret.project_name)
         self.assertEqual(ret.system, {'system': 'all'})
         self.assertEqual(ret.domain_name, 'some_domain')
+
+    def test_create_user(self):
+        self.users_client.create_user.return_value = {
+            'user': 'a_user'
+        }
+        fake_project = {
+            'id': 'fake_project_id',
+        }
+        res = self.creds_client.create_user('fake_username',
+                                            'fake_password',
+                                            fake_project,
+                                            'fake_email')
+        self.assertEqual('a_user', res)
+        self.users_client.create_user.assert_called_once_with(
+            name='fake_username', password='fake_password',
+            project_id=fake_project['id'],
+            email='fake_email',
+            domain_id='fake_domain_id')
diff --git a/tempest/tests/lib/common/test_dynamic_creds.py b/tempest/tests/lib/common/test_dynamic_creds.py
index e9073cc..b4b1b91 100644
--- a/tempest/tests/lib/common/test_dynamic_creds.py
+++ b/tempest/tests/lib/common/test_dynamic_creds.py
@@ -214,6 +214,56 @@
         self.assertEqual(admin_creds.user_id, '1234')
 
     @mock.patch('tempest.lib.common.rest_client.RestClient')
+    def test_project_alt_admin_creds(self, MockRestClient):
+        creds = dynamic_creds.DynamicCredentialProvider(**self.fixed_params)
+        self._mock_list_roles('1234', 'admin')
+        self._mock_user_create('1234', 'fake_alt_admin_user')
+        self._mock_tenant_create('1234', 'fake_alt_admin')
+
+        user_mock = mock.patch.object(self.roles_client.RolesClient,
+                                      'create_user_role_on_project')
+        user_mock.start()
+        self.addCleanup(user_mock.stop)
+        with mock.patch.object(self.roles_client.RolesClient,
+                               'create_user_role_on_project') as user_mock:
+            alt_admin_creds = creds.get_project_alt_admin_creds()
+        user_mock.assert_has_calls([
+            mock.call('1234', '1234', '1234')])
+        self.assertEqual(alt_admin_creds.username, 'fake_alt_admin_user')
+        self.assertEqual(alt_admin_creds.project_name, 'fake_alt_admin')
+        # Verify IDs
+        self.assertEqual(alt_admin_creds.project_id, '1234')
+        self.assertEqual(alt_admin_creds.user_id, '1234')
+
+    @mock.patch('tempest.lib.common.rest_client.RestClient')
+    def test_project_alt_member_creds(self, MockRestClient):
+        creds = dynamic_creds.DynamicCredentialProvider(**self.fixed_params)
+        self._mock_assign_user_role()
+        self._mock_list_role()
+        self._mock_tenant_create('1234', 'fake_alt_member')
+        self._mock_user_create('1234', 'fake_alt_user')
+        alt_member_creds = creds.get_project_alt_member_creds()
+        self.assertEqual(alt_member_creds.username, 'fake_alt_user')
+        self.assertEqual(alt_member_creds.project_name, 'fake_alt_member')
+        # Verify IDs
+        self.assertEqual(alt_member_creds.project_id, '1234')
+        self.assertEqual(alt_member_creds.user_id, '1234')
+
+    @mock.patch('tempest.lib.common.rest_client.RestClient')
+    def test_project_alt_reader_creds(self, MockRestClient):
+        creds = dynamic_creds.DynamicCredentialProvider(**self.fixed_params)
+        self._mock_assign_user_role()
+        self._mock_list_roles('1234', 'reader')
+        self._mock_tenant_create('1234', 'fake_alt_reader')
+        self._mock_user_create('1234', 'fake_alt_user')
+        alt_reader_creds = creds.get_project_alt_reader_creds()
+        self.assertEqual(alt_reader_creds.username, 'fake_alt_user')
+        self.assertEqual(alt_reader_creds.project_name, 'fake_alt_reader')
+        # Verify IDs
+        self.assertEqual(alt_reader_creds.project_id, '1234')
+        self.assertEqual(alt_reader_creds.user_id, '1234')
+
+    @mock.patch('tempest.lib.common.rest_client.RestClient')
     def test_role_creds(self, MockRestClient):
         creds = dynamic_creds.DynamicCredentialProvider(**self.fixed_params)
         self._mock_list_2_roles()
@@ -242,6 +292,100 @@
         self.assertEqual(role_creds.user_id, '1234')
 
     @mock.patch('tempest.lib.common.rest_client.RestClient')
+    def test_role_creds_with_project_scope(self, MockRestClient):
+        creds = dynamic_creds.DynamicCredentialProvider(**self.fixed_params)
+        self._mock_list_2_roles()
+        self._mock_user_create('1234', 'fake_role_user')
+        self._mock_tenant_create('1234', 'fake_role_project')
+
+        user_mock = mock.patch.object(self.roles_client.RolesClient,
+                                      'create_user_role_on_project')
+        user_mock.start()
+        self.addCleanup(user_mock.stop)
+        with mock.patch.object(self.roles_client.RolesClient,
+                               'create_user_role_on_project') as user_mock:
+            role_creds = creds.get_creds_by_roles(
+                roles=['role1', 'role2'], scope='project')
+        calls = user_mock.mock_calls
+        # Assert that the role creation is called with the 2 specified roles
+        self.assertEqual(len(calls), 2)
+        args = map(lambda x: x[1], calls)
+        args = list(args)
+        self.assertIn(('1234', '1234', '1234'), args)
+        self.assertIn(('1234', '1234', '12345'), args)
+        self.assertEqual(role_creds.username, 'fake_role_user')
+        self.assertEqual(role_creds.project_name, 'fake_role_project')
+        # Verify IDs
+        self.assertEqual(role_creds.project_id, '1234')
+        self.assertEqual(role_creds.user_id, '1234')
+
+    @mock.patch('tempest.lib.common.rest_client.RestClient')
+    def _test_get_same_role_creds_with_project_scope(self, MockRestClient,
+                                                     scope=None):
+        creds = dynamic_creds.DynamicCredentialProvider(**self.fixed_params)
+        self._mock_list_2_roles()
+        self._mock_user_create('1234', 'fake_role_user')
+        self._mock_tenant_create('1234', 'fake_role_project')
+        with mock.patch.object(self.roles_client.RolesClient,
+                               'create_user_role_on_project') as user_mock:
+            role_creds = creds.get_creds_by_roles(
+                roles=['role1', 'role2'], scope=scope)
+        calls = user_mock.mock_calls
+        # Assert that the role creation is called with the 2 specified roles
+        self.assertEqual(len(calls), 2)
+
+        # Fetch the same creds again
+        with mock.patch.object(self.roles_client.RolesClient,
+                               'create_user_role_on_project') as user_mock1:
+            role_creds_new = creds.get_creds_by_roles(
+                roles=['role1', 'role2'], scope=scope)
+        calls = user_mock1.mock_calls
+        # Assert that previously created creds are return and no call to
+        # role creation.
+        self.assertEqual(len(calls), 0)
+        # Check if previously created creds are returned.
+        self.assertEqual(role_creds, role_creds_new)
+
+    def test_get_same_role_creds_with_project_scope(self):
+        self._test_get_same_role_creds_with_project_scope(scope='project')
+
+    def test_get_same_role_creds_with_default_scope(self):
+        self._test_get_same_role_creds_with_project_scope()
+
+    @mock.patch('tempest.lib.common.rest_client.RestClient')
+    def _test_get_different_role_creds_with_project_scope(
+            self, MockRestClient, scope=None):
+        creds = dynamic_creds.DynamicCredentialProvider(**self.fixed_params)
+        self._mock_list_2_roles()
+        self._mock_user_create('1234', 'fake_role_user')
+        self._mock_tenant_create('1234', 'fake_role_project')
+        with mock.patch.object(self.roles_client.RolesClient,
+                               'create_user_role_on_project') as user_mock:
+            role_creds = creds.get_creds_by_roles(
+                roles=['role1', 'role2'], scope=scope)
+        calls = user_mock.mock_calls
+        # Assert that the role creation is called with the 2 specified roles
+        self.assertEqual(len(calls), 2)
+        # Fetch the creds with one role different
+        with mock.patch.object(self.roles_client.RolesClient,
+                               'create_user_role_on_project') as user_mock1:
+            role_creds_new = creds.get_creds_by_roles(
+                roles=['role1'], scope=scope)
+        calls = user_mock1.mock_calls
+        # Because one role is different, assert that the role creation
+        # is called with the 1 specified roles
+        self.assertEqual(len(calls), 1)
+        # Check new creds is created for new roles.
+        self.assertNotEqual(role_creds, role_creds_new)
+
+    def test_get_different_role_creds_with_project_scope(self):
+        self._test_get_different_role_creds_with_project_scope(
+            scope='project')
+
+    def test_get_different_role_creds_with_default_scope(self):
+        self._test_get_different_role_creds_with_project_scope()
+
+    @mock.patch('tempest.lib.common.rest_client.RestClient')
     def test_all_cred_cleanup(self, MockRestClient):
         creds = dynamic_creds.DynamicCredentialProvider(**self.fixed_params)
         self._mock_assign_user_role()
@@ -658,6 +802,232 @@
         return project_fix
 
     @mock.patch('tempest.lib.common.rest_client.RestClient')
+    def test_role_creds_with_system_scope(self, MockRestClient):
+        creds = dynamic_creds.DynamicCredentialProvider(**self.fixed_params)
+        self._mock_list_2_roles()
+        self._mock_user_create('1234', 'fake_role_user')
+
+        with mock.patch.object(self.roles_client.RolesClient,
+                               'create_user_role_on_system') as user_mock:
+            role_creds = creds.get_creds_by_roles(
+                roles=['role1', 'role2'], scope='system')
+        calls = user_mock.mock_calls
+        # Assert that the role creation is called with the 2 specified roles
+        self.assertEqual(len(calls), 2)
+        args = map(lambda x: x[1], calls)
+        args = list(args)
+        self.assertIn(('1234', '1234'), args)
+        self.assertIn(('1234', '12345'), args)
+        self.assertEqual(role_creds.username, 'fake_role_user')
+        self.assertEqual(role_creds.user_id, '1234')
+        # Verify system scope
+        self.assertEqual(role_creds.system, 'all')
+        # Verify domain is default
+        self.assertEqual(role_creds.domain_id, 'default')
+        self.assertEqual(role_creds.domain_name, 'Default')
+
+    @mock.patch('tempest.lib.common.rest_client.RestClient')
+    def test_get_same_role_creds_with_system_scope(self, MockRestClient):
+        creds = dynamic_creds.DynamicCredentialProvider(**self.fixed_params)
+        self._mock_list_2_roles()
+        self._mock_user_create('1234', 'fake_role_user')
+        with mock.patch.object(self.roles_client.RolesClient,
+                               'create_user_role_on_system') as user_mock:
+            role_creds = creds.get_creds_by_roles(
+                roles=['role1', 'role2'], scope='system')
+        calls = user_mock.mock_calls
+        # Assert that the role creation is called with the 2 specified roles
+        self.assertEqual(len(calls), 2)
+
+        # Fetch the same creds again
+        with mock.patch.object(self.roles_client.RolesClient,
+                               'create_user_role_on_system') as user_mock1:
+            role_creds_new = creds.get_creds_by_roles(
+                roles=['role1', 'role2'], scope='system')
+        calls = user_mock1.mock_calls
+        # Assert that previously created creds are return and no call to
+        # role creation.
+        self.assertEqual(len(calls), 0)
+        # Verify system scope
+        self.assertEqual(role_creds_new.system, 'all')
+        # Check if previously created creds are returned.
+        self.assertEqual(role_creds, role_creds_new)
+
+    @mock.patch('tempest.lib.common.rest_client.RestClient')
+    def test_get_different_role_creds_with_system_scope(self, MockRestClient):
+        creds = dynamic_creds.DynamicCredentialProvider(**self.fixed_params)
+        self._mock_list_2_roles()
+        self._mock_user_create('1234', 'fake_role_user')
+
+        with mock.patch.object(self.roles_client.RolesClient,
+                               'create_user_role_on_system') as user_mock:
+            role_creds = creds.get_creds_by_roles(
+                roles=['role1', 'role2'], scope='system')
+        calls = user_mock.mock_calls
+        # Assert that the role creation is called with the 2 specified roles
+        self.assertEqual(len(calls), 2)
+        # Verify system scope
+        self.assertEqual(role_creds.system, 'all')
+        # Fetch the creds with one role different
+        with mock.patch.object(self.roles_client.RolesClient,
+                               'create_user_role_on_system') as user_mock1:
+            role_creds_new = creds.get_creds_by_roles(
+                roles=['role1'], scope='system')
+        calls = user_mock1.mock_calls
+        # Because one role is different, assert that the role creation
+        # is called with the 1 specified roles
+        self.assertEqual(len(calls), 1)
+        # Verify Scope
+        self.assertEqual(role_creds_new.system, 'all')
+        # Check new creds is created for new roles.
+        self.assertNotEqual(role_creds, role_creds_new)
+
+    @mock.patch('tempest.lib.common.rest_client.RestClient')
+    def test_role_creds_with_domain_scope(self, MockRestClient):
+        creds = dynamic_creds.DynamicCredentialProvider(**self.fixed_params)
+        self._mock_list_2_roles()
+        self._mock_user_create('1234', 'fake_role_user')
+
+        domain = {
+            "id": '12',
+            "enabled": True,
+            "name": "TestDomain"
+        }
+
+        self.useFixture(fixtures.MockPatch(
+            'tempest.lib.common.cred_client.V3CredsClient.create_domain',
+            return_value=domain))
+
+        with mock.patch.object(self.roles_client.RolesClient,
+                               'create_user_role_on_domain') as user_mock:
+            role_creds = creds.get_creds_by_roles(
+                roles=['role1', 'role2'], scope='domain')
+        calls = user_mock.mock_calls
+        # Assert that the role creation is called with the 2 specified roles
+        self.assertEqual(len(calls), 2)
+        args = map(lambda x: x[1], calls)
+        args = list(args)
+        self.assertIn((domain['id'], '1234', '1234'), args)
+        self.assertIn((domain['id'], '1234', '12345'), args)
+        self.assertEqual(role_creds.username, 'fake_role_user')
+        self.assertEqual(role_creds.user_id, '1234')
+        # Verify creds are under new created domain
+        self.assertEqual(role_creds.domain_id, domain['id'])
+        self.assertEqual(role_creds.domain_name, domain['name'])
+        # Verify that Scope is None
+        self.assertIsNone(role_creds.system)
+
+    @mock.patch('tempest.lib.common.rest_client.RestClient')
+    def test_get_same_role_creds_with_domain_scope(self, MockRestClient):
+        creds = dynamic_creds.DynamicCredentialProvider(**self.fixed_params)
+        self._mock_list_2_roles()
+        self._mock_user_create('1234', 'fake_role_user')
+
+        domain = {
+            "id": '12',
+            "enabled": True,
+            "name": "TestDomain"
+        }
+
+        self.useFixture(fixtures.MockPatch(
+            'tempest.lib.common.cred_client.V3CredsClient.create_domain',
+            return_value=domain))
+
+        with mock.patch.object(self.roles_client.RolesClient,
+                               'create_user_role_on_domain') as user_mock:
+            role_creds = creds.get_creds_by_roles(
+                roles=['role1', 'role2'], scope='domain')
+        calls = user_mock.mock_calls
+        # Assert that the role creation is called with the 2 specified roles
+        self.assertEqual(len(calls), 2)
+        self.assertEqual(role_creds.user_id, '1234')
+        # Verify Scope
+        self.assertIsNone(role_creds.system)
+        # Fetch the same creds again
+        with mock.patch.object(self.roles_client.RolesClient,
+                               'create_user_role_on_domain') as user_mock1:
+            role_creds_new = creds.get_creds_by_roles(
+                roles=['role1', 'role2'], scope='domain')
+        calls = user_mock1.mock_calls
+        # Assert that previously created creds are return and no call to
+        # role creation.
+        self.assertEqual(len(calls), 0)
+        # Verify Scope
+        self.assertIsNone(role_creds_new.system)
+        # Check if previously created creds are returned.
+        self.assertEqual(role_creds, role_creds_new)
+
+    @mock.patch('tempest.lib.common.rest_client.RestClient')
+    def test_get_different_role_creds_with_domain_scope(self, MockRestClient):
+        creds = dynamic_creds.DynamicCredentialProvider(**self.fixed_params)
+        self._mock_list_2_roles()
+        self._mock_user_create('1234', 'fake_role_user')
+
+        domain = {
+            "id": '12',
+            "enabled": True,
+            "name": "TestDomain"
+        }
+
+        self.useFixture(fixtures.MockPatch(
+            'tempest.lib.common.cred_client.V3CredsClient.create_domain',
+            return_value=domain))
+
+        with mock.patch.object(self.roles_client.RolesClient,
+                               'create_user_role_on_domain') as user_mock:
+            role_creds = creds.get_creds_by_roles(
+                roles=['role1', 'role2'], scope='domain')
+        calls = user_mock.mock_calls
+        # Assert that the role creation is called with the 2 specified roles
+        self.assertEqual(len(calls), 2)
+        self.assertEqual(role_creds.user_id, '1234')
+        # Verify Scope
+        self.assertIsNone(role_creds.system)
+        # Fetch the same creds again
+        with mock.patch.object(self.roles_client.RolesClient,
+                               'create_user_role_on_domain') as user_mock1:
+            role_creds_new = creds.get_creds_by_roles(
+                roles=['role1'], scope='domain')
+        calls = user_mock1.mock_calls
+        # Because one role is different, assert that the role creation
+        # is called with the 1 specified roles
+        self.assertEqual(len(calls), 1)
+        # Verify Scope
+        self.assertIsNone(role_creds_new.system)
+        # Check new creds is created for new roles.
+        self.assertNotEqual(role_creds, role_creds_new)
+
+    @mock.patch('tempest.lib.common.rest_client.RestClient')
+    def test_get_role_creds_with_different_scope(self, MockRestClient):
+        creds = dynamic_creds.DynamicCredentialProvider(**self.fixed_params)
+        self._mock_list_2_roles()
+        self._mock_user_create('1234', 'fake_role_user')
+        self._mock_tenant_create('1234', 'fake_role_project')
+        with mock.patch.object(self.roles_client.RolesClient,
+                               'create_user_role_on_system') as user_mock:
+            role_creds = creds.get_creds_by_roles(
+                roles=['role1', 'role2'], scope='system')
+        calls = user_mock.mock_calls
+        # Assert that the role creation is called with the 2 specified roles
+        self.assertEqual(len(calls), 2)
+        # Verify Scope
+        self.assertEqual(role_creds.system, 'all')
+
+        # Fetch the same role creds but with different scope
+        with mock.patch.object(self.roles_client.RolesClient,
+                               'create_user_role_on_project') as user_mock1:
+            role_creds_new = creds.get_creds_by_roles(
+                roles=['role1', 'role2'], scope='project')
+        calls = user_mock1.mock_calls
+        # Because scope is different, assert that the role creation
+        # is called with the 2 specified roles
+        self.assertEqual(len(calls), 2)
+        # Verify Scope
+        self.assertIsNone(role_creds_new.system)
+        # Check that created creds are different
+        self.assertNotEqual(role_creds, role_creds_new)
+
+    @mock.patch('tempest.lib.common.rest_client.RestClient')
     def test_member_role_creation_with_duplicate(self, rest_client_mock):
         creds = dynamic_creds.DynamicCredentialProvider(**self.fixed_params)
         creds.creds_client = mock.MagicMock()
diff --git a/tempest/tests/lib/common/test_preprov_creds.py b/tempest/tests/lib/common/test_preprov_creds.py
index 579363e..fe7fcd2 100644
--- a/tempest/tests/lib/common/test_preprov_creds.py
+++ b/tempest/tests/lib/common/test_preprov_creds.py
@@ -12,17 +12,16 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import hashlib
 import os
 import shutil
 from unittest import mock
 
-import six
 import testtools
 
 import fixtures
 from oslo_concurrency.fixture import lockutils as lockutils_fixtures
 from oslo_config import cfg
+from oslo_utils.secretutils import md5
 
 from tempest import config
 from tempest.lib import auth
@@ -106,10 +105,10 @@
         hash_fields = (
             preprov_creds.PreProvisionedCredentialProvider.HASH_CRED_FIELDS)
         for account in accounts_list:
-            hash = hashlib.md5()
+            hash = md5(usedforsecurity=False)
             account_for_hash = dict((k, v) for (k, v) in account.items()
                                     if k in hash_fields)
-            hash.update(six.text_type(account_for_hash).encode('utf-8'))
+            hash.update(str(account_for_hash).encode('utf-8'))
             temp_hash = hash.hexdigest()
             hash_list.append(temp_hash)
         return hash_list
diff --git a/tempest/tests/lib/services/compute/test_assisted_volume_snapshots_client.py b/tempest/tests/lib/services/compute/test_assisted_volume_snapshots_client.py
new file mode 100644
index 0000000..79855ea
--- /dev/null
+++ b/tempest/tests/lib/services/compute/test_assisted_volume_snapshots_client.py
@@ -0,0 +1,53 @@
+# Copyright 2017 AT&T.
+#
+#    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.compute import assisted_volume_snapshots_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestVolumesClient(base.BaseServiceTest):
+
+    FAKE_SNAPSHOT = {
+        "id": "bf7b810c-70df-4c64-88a7-8588f7a6739c",
+        "volumeId": "59f17c4f-66d4-4271-be40-f200523423a9"
+    }
+
+    def setUp(self):
+        super(TestVolumesClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = assisted_volume_snapshots_client.\
+            AssistedVolumeSnapshotsClient(fake_auth, 'compute', 'regionOne')
+
+    def _test_create_assisted_volume_snapshot(self, bytes_body=False):
+        kwargs = {"type": "qcow2", "new_file": "fake_name"}
+        self.check_service_client_function(
+            self.client.create_assisted_volume_snapshot,
+            'tempest.lib.common.rest_client.RestClient.post',
+            {"snapshot": self.FAKE_SNAPSHOT},
+            bytes_body, status=200, volume_id=self.FAKE_SNAPSHOT['volumeId'],
+            snapshot_id=self.FAKE_SNAPSHOT['id'], **kwargs)
+
+    def test_create_assisted_volume_snapshot_with_str_body(self):
+        self._test_create_assisted_volume_snapshot()
+
+    def test_create_assisted_volume_snapshot_with_byte_body(self):
+        self._test_create_assisted_volume_snapshot(bytes_body=True)
+
+    def test_delete_assisted_volume_snapshot(self):
+        self.check_service_client_function(
+            self.client.delete_assisted_volume_snapshot,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {}, status=204, volume_id=self.FAKE_SNAPSHOT['volumeId'],
+            snapshot_id=self.FAKE_SNAPSHOT['id'])
diff --git a/tempest/tests/lib/services/identity/v3/test_identity_providers_client.py b/tempest/tests/lib/services/identity/v3/test_identity_providers_client.py
new file mode 100644
index 0000000..964c51b
--- /dev/null
+++ b/tempest/tests/lib/services/identity/v3/test_identity_providers_client.py
@@ -0,0 +1,142 @@
+# Copyright 2020 Samsung Electronics Co., Ltd
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+from tempest.lib.services.identity.v3 import identity_providers_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestIdentityProvidersClient(base.BaseServiceTest):
+    FAKE_IDENTITY_PROVIDERS_INFO = {
+        "identity_providers": [
+            {
+                "domain_id": "FAKE_DOMAIN_ID",
+                "description": "FAKE IDENTITY PROVIDER",
+                "remote_ids": ["fake_id_1", "fake_id_2"],
+                "enabled": True,
+                "id": "FAKE_ID",
+                "links": {
+                    "protocols": "http://example.com/identity/v3/" +
+                                 "OS-FEDERATION/identity_providers/" +
+                                 "FAKE_ID/protocols",
+                    "self": "http://example.com/identity/v3/OS-FEDERATION/" +
+                            "identity_providers/FAKE_ID"
+                }
+            }
+        ],
+        "links": {
+            "next": None,
+            "previous": None,
+            "self": "http://example.com/identity/v3/OS-FEDERATION/" +
+                    "identity_providers"
+        }
+    }
+
+    FAKE_IDENTITY_PROVIDER_INFO = {
+        "identity_provider": {
+            "authorization_ttl": None,
+            "domain_id": "FAKE_DOMAIN_ID",
+            "description": "FAKE IDENTITY PROVIDER",
+            "remote_ids": ["fake_id_1", "fake_id_2"],
+            "enabled": True,
+            "id": "ACME",
+            "links": {
+                "protocols": "http://example.com/identity/v3/OS-FEDERATION/" +
+                             "identity_providers/FAKE_ID/protocols",
+                "self": "http://example.com/identity/v3/OS-FEDERATION/" +
+                        "identity_providers/FAKE_ID"
+            }
+        }
+    }
+
+    def setUp(self):
+        super(TestIdentityProvidersClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = identity_providers_client.IdentityProvidersClient(
+            fake_auth, 'identity', 'regionOne')
+
+    def _test_register_identity_provider(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.register_identity_provider,
+            'tempest.lib.common.rest_client.RestClient.put',
+            self.FAKE_IDENTITY_PROVIDER_INFO,
+            bytes_body,
+            identity_provider_id="FAKE_ID",
+            status=201)
+
+    def _test_list_identity_providers(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_identity_providers,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_IDENTITY_PROVIDERS_INFO,
+            bytes_body,
+            status=200)
+
+    def _test_get_identity_provider(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.get_identity_provider,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_IDENTITY_PROVIDER_INFO,
+            bytes_body,
+            identity_provider_id="FAKE_ID",
+            status=200)
+
+    def _test_delete_identity_provider(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.delete_identity_provider,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {},
+            bytes_body,
+            identity_provider_id="FAKE_ID",
+            status=204)
+
+    def _test_update_identity_provider(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.update_identity_provider,
+            'tempest.lib.common.rest_client.RestClient.patch',
+            self.FAKE_IDENTITY_PROVIDER_INFO,
+            bytes_body,
+            identity_provider_id="FAKE_ID",
+            status=200)
+
+    def test_register_identity_provider_with_str_body(self):
+        self._test_register_identity_provider()
+
+    def test_register_identity_provider_with_bytes_body(self):
+        self._test_register_identity_provider(bytes_body=True)
+
+    def test_list_identity_providers_with_str_body(self):
+        self._test_list_identity_providers()
+
+    def test_list_identity_providers_with_bytes_body(self):
+        self._test_list_identity_providers(bytes_body=True)
+
+    def test_get_identity_provider_with_str_body(self):
+        self._test_get_identity_provider()
+
+    def test_get_identity_provider_with_bytes_body(self):
+        self._test_get_identity_provider(bytes_body=True)
+
+    def test_delete_identity_provider_with_str_body(self):
+        self._test_delete_identity_provider()
+
+    def test_delete_identity_provider_with_bytes_body(self):
+        self._test_delete_identity_provider(bytes_body=True)
+
+    def test_update_identity_provider_with_str_body(self):
+        self._test_update_identity_provider()
+
+    def test_update_identity_provider_with_bytes_body(self):
+        self._test_update_identity_provider(bytes_body=True)
diff --git a/tempest/tests/lib/services/identity/v3/test_mappings_client.py b/tempest/tests/lib/services/identity/v3/test_mappings_client.py
new file mode 100644
index 0000000..845a3f9
--- /dev/null
+++ b/tempest/tests/lib/services/identity/v3/test_mappings_client.py
@@ -0,0 +1,183 @@
+# Copyright 2020 Samsung Electronics Co., Ltd
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+from tempest.lib.services.identity.v3 import mappings_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestMappingsClient(base.BaseServiceTest):
+    FAKE_MAPPING_INFO = {
+        "mapping": {
+            "id": "fake123",
+            "links": {
+                "self": "http://example.com/identity/v3/OS-FEDERATION/" +
+                        "mappings/fake123"
+            },
+            "rules": [
+                {
+                    "local": [
+                        {
+                            "user": {
+                                "name": "{0}"
+                            }
+                        },
+                        {
+                            "group": {
+                                "id": "0cd5e9"
+                            }
+                        }
+                    ],
+                    "remote": [
+                        {
+                            "type": "UserName"
+                        },
+                        {
+                            "type": "orgPersonType",
+                            "not_any_of": [
+                                "Contractor",
+                                "Guest"
+                            ]
+                        }
+                    ]
+                }
+            ]
+        }
+    }
+
+    FAKE_MAPPINGS_INFO = {
+        "links": {
+            "next": None,
+            "previous": None,
+            "self": "http://example.com/identity/v3/OS-FEDERATION/mappings"
+        },
+        "mappings": [
+            {
+                "id": "fake123",
+                "links": {
+                    "self": "http://example.com/identity/v3/OS-FEDERATION/" +
+                            "mappings/fake123"
+                },
+                "rules": [
+                    {
+                        "local": [
+                            {
+                                "user": {
+                                    "name": "{0}"
+                                }
+                            },
+                            {
+                                "group": {
+                                    "id": "0cd5e9"
+                                }
+                            }
+                        ],
+                        "remote": [
+                            {
+                                "type": "UserName"
+                            },
+                            {
+                                "type": "orgPersonType",
+                                "any_one_of": [
+                                    "Contractor",
+                                    "SubContractor"
+                                ]
+                            }
+                        ]
+                    }
+                ]
+            }
+        ]
+    }
+
+    def setUp(self):
+        super(TestMappingsClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = mappings_client.MappingsClient(
+            fake_auth, 'identity', 'regionOne')
+
+    def _test_create_mapping(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.create_mapping,
+            'tempest.lib.common.rest_client.RestClient.put',
+            self.FAKE_MAPPING_INFO,
+            bytes_body,
+            mapping_id="fake123",
+            status=201)
+
+    def _test_get_mapping(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.get_mapping,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_MAPPING_INFO,
+            bytes_body,
+            mapping_id="fake123",
+            status=200)
+
+    def _test_update_mapping(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.update_mapping,
+            'tempest.lib.common.rest_client.RestClient.patch',
+            self.FAKE_MAPPING_INFO,
+            bytes_body,
+            mapping_id="fake123",
+            status=200)
+
+    def _test_list_mappings(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_mappings,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_MAPPINGS_INFO,
+            bytes_body,
+            status=200)
+
+    def _test_delete_mapping(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.delete_mapping,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {},
+            bytes_body,
+            mapping_id="fake123",
+            status=204)
+
+    def test_create_mapping_with_str_body(self):
+        self._test_create_mapping()
+
+    def test_create_mapping_with_bytes_body(self):
+        self._test_create_mapping(bytes_body=True)
+
+    def test_get_mapping_with_str_body(self):
+        self._test_get_mapping()
+
+    def test_get_mapping_with_bytes_body(self):
+        self._test_get_mapping(bytes_body=True)
+
+    def test_update_mapping_with_str_body(self):
+        self._test_update_mapping()
+
+    def test_update_mapping_with_bytes_body(self):
+        self._test_update_mapping(bytes_body=True)
+
+    def test_list_mappings_with_str_body(self):
+        self._test_list_mappings()
+
+    def test_list_mappings_with_bytes_body(self):
+        self._test_list_mappings(bytes_body=True)
+
+    def test_delete_mapping_with_str_body(self):
+        self._test_delete_mapping()
+
+    def test_delete_mapping_with_bytes_body(self):
+        self._test_delete_mapping(bytes_body=True)
diff --git a/tempest/tests/lib/services/identity/v3/test_protocols_client.py b/tempest/tests/lib/services/identity/v3/test_protocols_client.py
new file mode 100644
index 0000000..c1d04f4
--- /dev/null
+++ b/tempest/tests/lib/services/identity/v3/test_protocols_client.py
@@ -0,0 +1,140 @@
+# Copyright 2020 Samsung Electronics Co., Ltd
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+from tempest.lib.services.identity.v3 import protocols_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestProtocolsClient(base.BaseServiceTest):
+    FAKE_PROTOCOLS_INFO = {
+        "links": {
+            "next": None,
+            "previous": None,
+            "self": "http://example.com/identity/v3/OS-FEDERATION/" +
+                    "identity_providers/FAKE_ID/protocols"
+        },
+        "protocols": [
+            {
+                "id": "fake_id1",
+                "links": {
+                    "identity_provider": "http://example.com/identity/v3/" +
+                                         "OS-FEDERATION/identity_providers/" +
+                                         "FAKE_ID",
+                    "self": "http://example.com/identity/v3/OS-FEDERATION/"
+                            "identity_providers/FAKE_ID/protocols/fake_id1"
+                },
+                "mapping_id": "fake123"
+            }
+        ]
+    }
+
+    FAKE_PROTOCOL_INFO = {
+        "protocol": {
+            "id": "fake_id1",
+            "links": {
+                "identity_provider": "http://example.com/identity/v3/OS-" +
+                                     "FEDERATION/identity_providers/FAKE_ID",
+                "self": "http://example.com/identity/v3/OS-FEDERATION/" +
+                        "identity_providers/FAKE_ID/protocols/fake_id1"
+            },
+            "mapping_id": "fake123"
+        }
+    }
+
+    def setUp(self):
+        super(TestProtocolsClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = protocols_client.ProtocolsClient(
+            fake_auth, 'identity', 'regionOne')
+
+    def _test_add_protocol_to_identity_provider(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.add_protocol_to_identity_provider,
+            'tempest.lib.common.rest_client.RestClient.put',
+            self.FAKE_PROTOCOL_INFO,
+            bytes_body,
+            idp_id="FAKE_ID",
+            protocol_id="fake_id1",
+            status=201)
+
+    def _test_list_protocols_of_identity_provider(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_protocols_of_identity_provider,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_PROTOCOLS_INFO,
+            bytes_body,
+            idp_id="FAKE_ID",
+            status=200)
+
+    def _test_get_protocol_for_identity_provider(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.get_protocol_for_identity_provider,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_PROTOCOL_INFO,
+            bytes_body,
+            idp_id="FAKE_ID",
+            protocol_id="fake_id1",
+            status=200)
+
+    def _test_update_mapping_for_identity_provider(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.update_mapping_for_identity_provider,
+            'tempest.lib.common.rest_client.RestClient.patch',
+            self.FAKE_PROTOCOL_INFO,
+            bytes_body,
+            idp_id="FAKE_ID",
+            protocol_id="fake_id1",
+            status=200)
+
+    def _test_delete_protocol_from_identity_provider(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.delete_protocol_from_identity_provider,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {},
+            bytes_body,
+            idp_id="FAKE_ID",
+            protocol_id="fake_id1",
+            status=204)
+
+    def test_add_protocol_to_identity_provider_with_str_body(self):
+        self._test_add_protocol_to_identity_provider()
+
+    def test_add_protocol_to_identity_provider_with_bytes_body(self):
+        self._test_add_protocol_to_identity_provider(bytes_body=True)
+
+    def test_list_protocols_of_identity_provider_with_str_body(self):
+        self._test_list_protocols_of_identity_provider()
+
+    def test_list_protocols_of_identity_provider_with_bytes_body(self):
+        self._test_list_protocols_of_identity_provider(bytes_body=True)
+
+    def test_get_protocol_for_identity_provider_with_str_body(self):
+        self._test_get_protocol_for_identity_provider()
+
+    def test_get_protocol_for_identity_provider_with_bytes_body(self):
+        self._test_get_protocol_for_identity_provider(bytes_body=True)
+
+    def test_update_mapping_for_identity_provider_with_str_body(self):
+        self._test_update_mapping_for_identity_provider()
+
+    def test_update_mapping_for_identity_provider_with_bytes_body(self):
+        self._test_update_mapping_for_identity_provider(bytes_body=True)
+
+    def test_delete_protocol_from_identity_provider_with_str_body(self):
+        self._test_delete_protocol_from_identity_provider()
+
+    def test_delete_protocol_from_identity_provider_with_bytes_body(self):
+        self._test_delete_protocol_from_identity_provider(bytes_body=False)
diff --git a/tempest/tests/lib/services/identity/v3/test_service_providers_client.py b/tempest/tests/lib/services/identity/v3/test_service_providers_client.py
new file mode 100644
index 0000000..ec908bc
--- /dev/null
+++ b/tempest/tests/lib/services/identity/v3/test_service_providers_client.py
@@ -0,0 +1,157 @@
+# Copyright 2020 Samsung Electronics Co., Ltd
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+from tempest.lib.services.identity.v3 import service_providers_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestServiceProvidersClient(base.BaseServiceTest):
+    FAKE_SERVICE_PROVIDER_INFO = {
+        "service_provider": {
+            "auth_url": "https://example.com/identity/v3/OS-FEDERATION/" +
+                        "identity_providers/FAKE_ID/protocols/fake_id1/auth",
+            "description": "Fake Service Provider",
+            "enabled": True,
+            "id": "FAKE_ID",
+            "links": {
+                "self": "https://example.com/identity/v3/OS-FEDERATION/" +
+                        "service_providers/FAKE_ID"
+            },
+            "relay_state_prefix": "ss:mem:",
+            "sp_url": "https://example.com/identity/Shibboleth.sso/" +
+                      "FAKE_ID1/ECP"
+        }
+    }
+
+    FAKE_SERVICE_PROVIDERS_INFO = {
+        "links": {
+            "next": None,
+            "previous": None,
+            "self": "http://example.com/identity/v3/OS-FEDERATION/" +
+                    "service_providers"
+        },
+        "service_providers": [
+            {
+                "auth_url": "https://example.com/identity/v3/OS-FEDERATION/" +
+                            "identity_providers/acme/protocols/saml2/auth",
+                "description": "Stores ACME identities",
+                "enabled": True,
+                "id": "ACME",
+                "links": {
+                    "self": "http://example.com/identity/v3/OS-FEDERATION/" +
+                            "service_providers/ACME"
+                },
+                "relay_state_prefix": "ss:mem:",
+                "sp_url": "https://example.com/identity/Shibboleth.sso/" +
+                          "SAML2/ECP"
+            },
+            {
+                "auth_url": "https://other.example.com/identity/v3/" +
+                            "OS-FEDERATION/identity_providers/acme/" +
+                            "protocols/saml2/auth",
+                "description": "Stores contractor identities",
+                "enabled": False,
+                "id": "ACME-contractors",
+                "links": {
+                    "self": "http://example.com/identity/v3/OS-FEDERATION/" +
+                            "service_providers/ACME-contractors"
+                },
+                "relay_state_prefix": "ss:mem:",
+                "sp_url": "https://other.example.com/identity/Shibboleth" +
+                          ".sso/SAML2/ECP"
+            }
+        ]
+    }
+
+    def setUp(self):
+        super(TestServiceProvidersClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = service_providers_client.ServiceProvidersClient(
+            fake_auth, 'identity', 'regionOne')
+
+    def _test_register_service_provider(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.register_service_provider,
+            'tempest.lib.common.rest_client.RestClient.put',
+            self.FAKE_SERVICE_PROVIDER_INFO,
+            bytes_body,
+            service_provider_id="FAKE_ID",
+            status=201)
+
+    def _test_list_service_providers(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_service_providers,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_SERVICE_PROVIDERS_INFO,
+            bytes_body,
+            status=200)
+
+    def _test_get_service_provider(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.get_service_provider,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_SERVICE_PROVIDER_INFO,
+            bytes_body,
+            service_provider_id="FAKE_ID",
+            status=200)
+
+    def _test_delete_service_provider(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.delete_service_provider,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {},
+            bytes_body,
+            service_provider_id="FAKE_ID",
+            status=204)
+
+    def _test_update_service_provider(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.update_service_provider,
+            'tempest.lib.common.rest_client.RestClient.patch',
+            self.FAKE_SERVICE_PROVIDER_INFO,
+            bytes_body,
+            service_provider_id="FAKE_ID",
+            status=200)
+
+    def test_register_service_provider_with_str_body(self):
+        self._test_register_service_provider()
+
+    def test_register_service_provider_with_bytes_body(self):
+        self._test_register_service_provider(bytes_body=True)
+
+    def test_list_service_providers_with_str_body(self):
+        self._test_list_service_providers()
+
+    def test_list_service_providers_with_bytes_body(self):
+        self._test_list_service_providers(bytes_body=True)
+
+    def test_get_service_provider_with_str_body(self):
+        self._test_get_service_provider()
+
+    def test_get_service_provider_with_bytes_body(self):
+        self._test_get_service_provider(bytes_body=True)
+
+    def test_delete_service_provider_with_str_body(self):
+        self._test_delete_service_provider()
+
+    def test_delete_service_provider_with_bytes_body(self):
+        self._test_delete_service_provider(bytes_body=True)
+
+    def test_update_service_provider_with_str_body(self):
+        self._test_update_service_provider()
+
+    def test_update_service_provider_with_bytes_body(self):
+        self._test_update_service_provider(bytes_body=True)
diff --git a/tempest/tests/lib/services/identity/v3/test_trusts_client.py b/tempest/tests/lib/services/identity/v3/test_trusts_client.py
index a1ca020..33dca7d 100644
--- a/tempest/tests/lib/services/identity/v3/test_trusts_client.py
+++ b/tempest/tests/lib/services/identity/v3/test_trusts_client.py
@@ -94,6 +94,35 @@
             }
         }
 
+    FAKE_LIST_TRUSTS_ROLES = {
+        "roles": [
+            {
+                "id": "c1648e",
+                "links": {
+                    "self": "http://example.com/identity/v3/roles/c1648e"
+                    },
+                "name": "manager"
+                },
+            {
+                "id": "ed7b78",
+                "links": {
+                    "self": "http://example.com/identity/v3/roles/ed7b78"
+                    },
+                "name": "member"
+                }
+            ]
+        }
+
+    FAKE_TRUST_ROLE = {
+        "role": {
+            "id": "c1648e",
+            "links": {
+                "self": "http://example.com/identity/v3/roles/c1648e"
+                },
+            "name": "manager"
+            }
+        }
+
     def setUp(self):
         super(TestTrustsClient, self).setUp()
         fake_auth = fake_auth_provider.FakeAuthProvider()
@@ -123,6 +152,43 @@
             self.FAKE_LIST_TRUSTS,
             bytes_body)
 
+    def _test_list_trust_roles(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_trust_roles,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_LIST_TRUSTS_ROLES,
+            bytes_body,
+            trust_id="1ff900")
+
+    def test_check_trust_role(self):
+        self.check_service_client_function(
+            self.client.check_trust_role,
+            'tempest.lib.common.rest_client.RestClient.head',
+            {},
+            trust_id="1ff900",
+            role_id="ed7b78")
+
+    def _check_show_trust_role(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.show_trust_role,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_TRUST_ROLE,
+            bytes_body,
+            trust_id="1ff900",
+            role_id="ed7b78")
+
+    def test_list_trust_roles_with_str_body(self):
+        self._test_list_trust_roles()
+
+    def test_list_trust_roles_with_bytes_body(self):
+        self._test_list_trust_roles(bytes_body=True)
+
+    def test_check_show_trust_role_with_str_body(self):
+        self._check_show_trust_role()
+
+    def test_check_show_trust_role_with_bytes_body(self):
+        self._check_show_trust_role(bytes_body=True)
+
     def test_create_trust_with_str_body(self):
         self._test_create_trust()
 
diff --git a/tempest/tests/lib/services/image/v2/test_images_client.py b/tempest/tests/lib/services/image/v2/test_images_client.py
index fe671bd..5b162f8 100644
--- a/tempest/tests/lib/services/image/v2/test_images_client.py
+++ b/tempest/tests/lib/services/image/v2/test_images_client.py
@@ -12,7 +12,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import six
+import io
 
 from tempest.lib.common.utils import data_utils
 from tempest.lib.services.image.v2 import images_client
@@ -105,6 +105,44 @@
         "first": "/v2/images"
     }
 
+    FAKE_SHOW_IMAGE_TASKS = {
+        "tasks": [
+            {
+                "id": "ee22890e-8948-4ea6-9668-831f973c84f5",
+                "image_id": "dddddddd-dddd-dddd-dddd-dddddddddddd",
+                "request-id": "rrrrrrr-rrrr-rrrr-rrrr-rrrrrrrrrrrr",
+                "user": "uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu",
+                "type": "api_image_import",
+                "status": "processing",
+                "owner": "64f0efc9955145aeb06f297a8a6fe402",
+                "expires_at": None,
+                "created_at": "2020-12-18T05:20:38.000000",
+                "updated_at": "2020-12-18T05:25:39.000000",
+                "deleted_at": None,
+                "deleted": False,
+                "input": {
+                    "image_id": "829c729b-ebc4-4cc7-a164-6f43f1149b17",
+                    "import_req": {
+                        "method": {
+                            "name": "copy-image",
+                        },
+                        "all_stores": True,
+                        "all_stores_must_succeed": False,
+                    },
+                    "backend": [
+                        "fast",
+                        "cheap",
+                        "slow",
+                        "reliable",
+                        "common",
+                    ]
+                },
+                "result": None,
+                "message": "Copied 15 MiB",
+            }
+        ]
+    }
+
     FAKE_TAG_NAME = "fake tag"
 
     def setUp(self):
@@ -178,7 +216,7 @@
             {}, image_id="e485aab9-0907-4973-921c-bb6da8a8fcf8", status=204)
 
     def test_store_image_file(self):
-        data = six.BytesIO(data_utils.random_bytes())
+        data = io.BytesIO(data_utils.random_bytes())
 
         self.check_service_client_function(
             self.client.store_image_file,
@@ -230,3 +268,11 @@
 
     def test_list_images_with_bytes_body(self):
         self._test_list_images(bytes_body=True)
+
+    def test_show_image_tasks(self):
+        self.check_service_client_function(
+            self.client.show_image_tasks,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_SHOW_IMAGE_TASKS,
+            True,
+            image_id="e485aab9-0907-4973-921c-bb6da8a8fcf8")
diff --git a/tempest/tests/lib/services/image/v2/test_versions_client.py b/tempest/tests/lib/services/image/v2/test_versions_client.py
index 6234b06..98c558a 100644
--- a/tempest/tests/lib/services/image/v2/test_versions_client.py
+++ b/tempest/tests/lib/services/image/v2/test_versions_client.py
@@ -12,6 +12,8 @@
 # License for the specific language governing permissions and limitations
 # under the License.
 
+import fixtures
+
 from tempest.lib.services.image.v2 import versions_client
 from tempest.tests.lib import fake_auth_provider
 from tempest.tests.lib.services import base
@@ -92,3 +94,13 @@
 
     def test_list_versions_with_bytes_body(self):
         self._test_list_versions(bytes_body=True)
+
+    def test_has_version(self):
+        mocked_r = self.create_response(self.FAKE_VERSIONS_INFO, False,
+                                        300, None)
+        self.useFixture(fixtures.MockPatch(
+            'tempest.lib.common.rest_client.RestClient.raw_request',
+            return_value=mocked_r))
+
+        self.assertTrue(self.client.has_version('2.1'))
+        self.assertFalse(self.client.has_version('9.9'))
diff --git a/tempest/tests/lib/services/network/test_ports_client.py b/tempest/tests/lib/services/network/test_ports_client.py
index 20ef3f1..9ca9ac6 100644
--- a/tempest/tests/lib/services/network/test_ports_client.py
+++ b/tempest/tests/lib/services/network/test_ports_client.py
@@ -22,53 +22,126 @@
 
 class TestPortsClient(base.BaseServiceTest):
 
+    FAKE_CREATE_PORTS = {
+        "port": {
+            "binding:host_id": "4df8d9ff-6f6f-438f-90a1-ef660d4586ad",
+            "binding:profile": {
+                "local_link_information": [
+                    {
+                        "port_id": "Ethernet3/1",
+                        "switch_id": "0a:1b:2c:3d:4e:5f",
+                        "switch_info": "switch1"
+                    }
+                ]
+            },
+            "binding:vnic_type": "baremetal",
+            "device_id": "d90a13da-be41-461f-9f99-1dbcf438fdf2",
+            "device_owner": "baremetal:none",
+            "dns_domain": "my-domain.org.",
+            "dns_name": "myport",
+            "qos_policy_id": "29d5e02e-d5ab-4929-bee4-4a9fc12e22ae",
+            "uplink_status_propagation": False
+        }
+    }
+
     FAKE_PORTS = {
         "ports": [
             {
                 "admin_state_up": True,
                 "allowed_address_pairs": [],
+                "created_at": "2016-03-08T20:19:41",
                 "data_plane_status": None,
                 "description": "",
                 "device_id": "9ae135f4-b6e0-4dad-9e91-3c223e385824",
                 "device_owner": "network:router_gateway",
-                "extra_dhcp_opts": [],
+                "dns_assignment": [
+                    {
+                        "hostname": "myport",
+                        "ip_address": "172.24.4.2",
+                        "fqdn": "myport.my-domain.org"
+                    }
+                ],
+                "dns_domain": "my-domain.org.",
+                "dns_name": "myport",
+                "extra_dhcp_opts": [
+                    {
+                        "opt_value": "pxelinux.0",
+                        "ip_version": 4,
+                        "opt_name": "bootfile-name"
+                    }
+                ],
                 "fixed_ips": [
                     {
                         "ip_address": "172.24.4.2",
-                        "subnet_id": "008ba151-0b8c-4a67-98b5-0d2b87666062"
+                        "subnet_id":
+                            "008ba151-0b8c-4a67-98b5-0d2b87666062"
                     }
                 ],
                 "id": "d80b1a3b-4fc1-49f3-952e-1e2ab7081d8b",
+                "ip_allocation": "immediate",
                 "mac_address": "fa:16:3e:58:42:ed",
                 "name": "",
                 "network_id": "70c1db1f-b701-45bd-96e0-a313ee3430b3",
                 "project_id": "",
+                "revision_number": 1,
                 "security_groups": [],
                 "status": "ACTIVE",
-                "tenant_id": ""
+                "tags": ["tag1,tag2"],
+                "tenant_id": "d6700c0c9ffa4f1cb322cd4a1f3906fa",
+                "updated_at": "2016-03-08T20:19:41",
+                "qos_network_policy_id":
+                    "174dd0c1-a4eb-49d4-a807-ae80246d82f4",
+                "qos_policy_id": "29d5e02e-d5ab-4929-bee4-4a9fc12e22ae",
+                "port_security_enabled": False,
+                "uplink_status_propagation": False
             },
             {
                 "admin_state_up": True,
                 "allowed_address_pairs": [],
+                "created_at": "2016-03-08T20:19:41",
                 "data_plane_status": None,
                 "description": "",
                 "device_id": "9ae135f4-b6e0-4dad-9e91-3c223e385824",
                 "device_owner": "network:router_interface",
-                "extra_dhcp_opts": [],
+                "dns_assignment": [
+                    {
+                        "hostname": "myport2",
+                        "ip_address": "10.0.0.1",
+                        "fqdn": "myport2.my-domain.org"
+                    }
+                ],
+                "dns_domain": "my-domain.org.",
+                "dns_name": "myport2",
+                "extra_dhcp_opts": [
+                    {
+                        "opt_value": "pxelinux.0",
+                        "ip_version": 4,
+                        "opt_name": "bootfile-name"
+                    }
+                ],
                 "fixed_ips": [
                     {
                         "ip_address": "10.0.0.1",
-                        "subnet_id": "288bf4a1-51ba-43b6-9d0a-520e9005db17"
+                        "subnet_id":
+                            "288bf4a1-51ba-43b6-9d0a-520e9005db17"
                     }
                 ],
                 "id": "f71a6703-d6de-4be1-a91a-a570ede1d159",
+                "ip_allocation": "immediate",
                 "mac_address": "fa:16:3e:bb:3c:e4",
                 "name": "",
                 "network_id": "f27aa545-cbdd-4907-b0c6-c9e8b039dcc2",
                 "project_id": "d397de8a63f341818f198abb0966f6f3",
+                "revision_number": 1,
                 "security_groups": [],
                 "status": "ACTIVE",
-                "tenant_id": "d397de8a63f341818f198abb0966f6f3"
+                "tags": ["tag1,tag2"],
+                "tenant_id": "d397de8a63f341818f198abb0966f6f3",
+                "updated_at": "2016-03-08T20:19:41",
+                "qos_network_policy_id": None,
+                "qos_policy_id": None,
+                "port_security_enabled": False,
+                "uplink_status_propagation": False
             }
         ]
     }
@@ -112,7 +185,7 @@
         self.check_service_client_function(
             self.ports_client.create_port,
             "tempest.lib.common.rest_client.RestClient.post",
-            {"port": self.FAKE_PORTS["ports"][0]},
+            self.FAKE_CREATE_PORTS,
             bytes_body,
             201,
             **self.FAKE_PORT1)
diff --git a/tempest/tests/lib/services/placement/test_resource_providers_client.py b/tempest/tests/lib/services/placement/test_resource_providers_client.py
index 11aeaf2..485f584 100644
--- a/tempest/tests/lib/services/placement/test_resource_providers_client.py
+++ b/tempest/tests/lib/services/placement/test_resource_providers_client.py
@@ -48,6 +48,43 @@
     FAKE_RESOURCE_PROVIDER_AGGREGATES = {
         'aggregates': [FAKE_AGGREGATE_UUID]
     }
+    FAKE_RESOURCE_UPDATE_INVENTORIES_RESPONSE = {
+        "inventories": {
+            "MEMORY_MB": {
+                "allocation_ratio": 2.0,
+                "max_unit": 16,
+                "min_unit": 1,
+                "reserved": 0,
+                "step_size": 4,
+                "total": 128
+            },
+            "VCPU": {
+                "allocation_ratio": 10.0,
+                "max_unit": 2147483647,
+                "min_unit": 1,
+                "reserved": 2,
+                "step_size": 1,
+                "total": 64
+            }
+        },
+        "resource_provider_generation": 2
+    }
+    FAKE_RESOURCE_UPDATE_INVENTORIES_REQUEST = {
+        "inventories": {
+            "MEMORY_MB": {
+                "allocation_ratio": 2.0,
+                "max_unit": 16,
+                "step_size": 4,
+                "total": 128
+            },
+            "VCPU": {
+                "allocation_ratio": 10.0,
+                "reserved": 2,
+                "total": 64
+            }
+        },
+        "resource_provider_generation": 1
+    }
 
     def setUp(self):
         super(TestResourceProvidersClient, self).setUp()
@@ -102,6 +139,32 @@
     def test_list_resource_provider_inventories_with_bytes_body(self):
         self._test_list_resource_provider_inventories(bytes_body=True)
 
+    def _test_update_resource_providers_inventories(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.update_resource_providers_inventories,
+            'tempest.lib.common.rest_client.RestClient.put',
+            self.FAKE_RESOURCE_UPDATE_INVENTORIES_RESPONSE,
+            to_utf=bytes_body,
+            status=200,
+            rp_uuid=self.FAKE_RESOURCE_PROVIDER_UUID,
+            **self.FAKE_RESOURCE_UPDATE_INVENTORIES_REQUEST
+        )
+
+    def test_update_resource_providers_inventories_with_str_body(self):
+        self._test_update_resource_providers_inventories()
+
+    def test_update_resource_providers_inventories_with_bytes_body(self):
+        self._test_update_resource_providers_inventories(bytes_body=True)
+
+    def test_delete_resource_providers_inventories(self):
+        self.check_service_client_function(
+            self.client.delete_resource_providers_inventories,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {},
+            status=204,
+            rp_uuid=self.FAKE_RESOURCE_PROVIDER_UUID,
+        )
+
     def _test_list_resource_provider_aggregates(self, bytes_body=False):
         self.check_service_client_function(
             self.client.list_resource_provider_aggregates,
diff --git a/tempest/tests/lib/test_decorators.py b/tempest/tests/lib/test_decorators.py
index e3c17e8..fc93f76 100644
--- a/tempest/tests/lib/test_decorators.py
+++ b/tempest/tests/lib/test_decorators.py
@@ -16,7 +16,6 @@
 import abc
 from unittest import mock
 
-import six
 import testtools
 
 from tempest.lib import base as test
@@ -69,8 +68,7 @@
                                condition=True)
 
 
-@six.add_metaclass(abc.ABCMeta)
-class BaseSkipDecoratorTests(object):
+class BaseSkipDecoratorTests(object, metaclass=abc.ABCMeta):
 
     @abc.abstractmethod
     def _test_skip_helper(self, raise_exception=True, expected_to_skip=True,
diff --git a/tempest/tests/lib/test_ssh.py b/tempest/tests/lib/test_ssh.py
index 85048fb..035bdb0 100644
--- a/tempest/tests/lib/test_ssh.py
+++ b/tempest/tests/lib/test_ssh.py
@@ -12,11 +12,11 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from io import StringIO
 import socket
 from unittest import mock
 
 import six
-from six import StringIO
 import testtools
 
 from tempest.lib.common import ssh
@@ -30,7 +30,7 @@
     SELECT_POLLIN = 1
 
     @mock.patch('paramiko.RSAKey.from_private_key')
-    @mock.patch('six.StringIO')
+    @mock.patch('io.StringIO')
     def test_pkey_calls_paramiko_RSAKey(self, cs_mock, rsa_mock):
         cs_mock.return_value = mock.sentinel.csio
         pkey = 'mykey'
@@ -274,7 +274,7 @@
         client = ssh.Client('localhost', 'root', timeout=2)
         exc = self.assertRaises(exceptions.SSHExecCommandFailed,
                                 client.exec_command, "test")
-        self.assertIn('R' + self._utf8_string, six.text_type(exc))
+        self.assertIn('R' + self._utf8_string, str(exc))
 
     def test_exec_command_no_select(self):
         gsc_mock = self.patch('tempest.lib.common.ssh.Client.'
diff --git a/tempest/tests/test_base_test.py b/tempest/tests/test_base_test.py
index b154cd5..88c28bf 100644
--- a/tempest/tests/test_base_test.py
+++ b/tempest/tests/test_base_test.py
@@ -109,7 +109,7 @@
 
         test.BaseTestCase.get_tenant_network(credentials_type=creds)
 
-        mock_gcm.assert_called_once_with(roles=['role1'])
+        mock_gcm.assert_called_once_with(roles=['role1'], scope='project')
         mock_gprov.assert_called_once_with()
         mock_gtn.assert_called_once_with(mock_prov, net_client,
                                          self.fixed_network_name)
diff --git a/tempest/tests/test_list_tests.py b/tempest/tests/test_list_tests.py
index 1cc9c9a..fe44ef6 100644
--- a/tempest/tests/test_list_tests.py
+++ b/tempest/tests/test_list_tests.py
@@ -16,8 +16,6 @@
 import re
 import subprocess
 
-import six
-
 from tempest.tests import base
 
 
@@ -32,7 +30,7 @@
         self.assertEqual(0, p.returncode,
                          "test discovery failed, one or more files cause an "
                          "error on import %s" % ids)
-        ids = six.text_type(ids).split('\n')
+        ids = str(ids).split('\n')
         for test_id in ids:
             if re.match(r'(\w+\.){3}\w+', test_id):
                 if not test_id.startswith('tempest.'):
diff --git a/tempest/tests/test_microversions.py b/tempest/tests/test_microversions.py
index ee6db71..835f51c 100644
--- a/tempest/tests/test_microversions.py
+++ b/tempest/tests/test_microversions.py
@@ -13,7 +13,6 @@
 #    under the License.
 
 from oslo_config import cfg
-import six
 import testtools
 
 from tempest.api.compute import base as compute_base
@@ -75,7 +74,7 @@
                 self.assertRaises(testtools.TestCase.skipException,
                                   test_class.skip_checks)
         except testtools.TestCase.skipException as e:
-            raise testtools.TestCase.failureException(six.text_type(e))
+            raise testtools.TestCase.failureException(str(e))
 
     def test_config_version_none_none(self):
         expected_pass_tests = [VersionTestNoneTolatest, VersionTestNoneTo2_2]
diff --git a/tempest/tests/test_test.py b/tempest/tests/test_test.py
index 72e8b6d..9aeedb3 100644
--- a/tempest/tests/test_test.py
+++ b/tempest/tests/test_test.py
@@ -453,6 +453,130 @@
             expected_creds[1][1:],
             mock_get_client_manager.mock_calls[1][2]['roles'])
 
+    def test_setup_credentials_with_role_and_system_scope(self):
+        expected_creds = [['system_my_role', 'role1', 'role2']]
+
+        class SystemRoleCredentials(self.parent_test):
+            credentials = expected_creds
+
+        expected_clients = 'clients'
+        with mock.patch.object(
+                SystemRoleCredentials,
+                'get_client_manager') as mock_get_client_manager:
+            mock_get_client_manager.return_value = expected_clients
+            sys_creds = SystemRoleCredentials()
+            sys_creds.setup_credentials()
+        self.assertTrue(hasattr(sys_creds, 'os_system_my_role'))
+        self.assertEqual(expected_clients, sys_creds.os_system_my_role)
+        self.assertTrue(hasattr(sys_creds, 'os_roles_system_my_role'))
+        self.assertEqual(expected_clients, sys_creds.os_roles_system_my_role)
+        self.assertEqual(1, mock_get_client_manager.call_count)
+        self.assertEqual(
+            expected_creds[0][1:],
+            mock_get_client_manager.mock_calls[0][2]['roles'])
+        self.assertEqual(
+            'system',
+            mock_get_client_manager.mock_calls[0][2]['scope'])
+
+    def test_setup_credentials_with_multiple_role_and_system_scope(self):
+        expected_creds = [['system_my_role', 'role1', 'role2'],
+                          ['system_my_role2', 'role1', 'role2'],
+                          ['system_my_role3', 'role3']]
+
+        class SystemRoleCredentials(self.parent_test):
+            credentials = expected_creds
+
+        expected_clients = 'clients'
+        with mock.patch.object(
+                SystemRoleCredentials,
+                'get_client_manager') as mock_get_client_manager:
+            mock_get_client_manager.return_value = expected_clients
+            sys_creds = SystemRoleCredentials()
+            sys_creds.setup_credentials()
+        self.assertTrue(hasattr(sys_creds, 'os_system_my_role'))
+        self.assertEqual(expected_clients, sys_creds.os_system_my_role)
+        self.assertTrue(hasattr(sys_creds, 'os_roles_system_my_role'))
+        self.assertEqual(expected_clients, sys_creds.os_roles_system_my_role)
+        self.assertTrue(hasattr(sys_creds, 'os_system_my_role2'))
+        self.assertEqual(expected_clients, sys_creds.os_system_my_role2)
+        self.assertTrue(hasattr(sys_creds, 'os_roles_system_my_role2'))
+        self.assertEqual(expected_clients, sys_creds.os_roles_system_my_role2)
+        self.assertTrue(hasattr(sys_creds, 'os_system_my_role3'))
+        self.assertEqual(expected_clients, sys_creds.os_system_my_role3)
+        self.assertTrue(hasattr(sys_creds, 'os_roles_system_my_role3'))
+        self.assertEqual(expected_clients, sys_creds.os_roles_system_my_role3)
+        self.assertEqual(3, mock_get_client_manager.call_count)
+        self.assertEqual(
+            expected_creds[0][1:],
+            mock_get_client_manager.mock_calls[0][2]['roles'])
+        self.assertEqual(
+            'system', mock_get_client_manager.mock_calls[0][2]['scope'])
+        self.assertEqual(
+            expected_creds[1][1:],
+            mock_get_client_manager.mock_calls[1][2]['roles'])
+        self.assertEqual(
+            'system', mock_get_client_manager.mock_calls[1][2]['scope'])
+        self.assertEqual(
+            expected_creds[2][1:],
+            mock_get_client_manager.mock_calls[2][2]['roles'])
+        self.assertEqual(
+            'system', mock_get_client_manager.mock_calls[2][2]['scope'])
+
+    def test_setup_credentials_with_role_and_multiple_scope(self):
+        expected_creds = [['my_role', 'role1', 'role2'],
+                          ['project_my_role', 'role1', 'role2'],
+                          ['domain_my_role', 'role1', 'role2'],
+                          ['system_my_role', 'role1', 'role2']]
+
+        class SystemRoleCredentials(self.parent_test):
+            credentials = expected_creds
+
+        expected_clients = 'clients'
+        with mock.patch.object(
+                SystemRoleCredentials,
+                'get_client_manager') as mock_get_client_manager:
+            mock_get_client_manager.return_value = expected_clients
+            sys_creds = SystemRoleCredentials()
+            sys_creds.setup_credentials()
+        self.assertTrue(hasattr(sys_creds, 'os_my_role'))
+        self.assertEqual(expected_clients, sys_creds.os_my_role)
+        self.assertTrue(hasattr(sys_creds, 'os_roles_my_role'))
+        self.assertEqual(expected_clients, sys_creds.os_roles_my_role)
+        self.assertTrue(hasattr(sys_creds, 'os_project_my_role'))
+        self.assertEqual(expected_clients, sys_creds.os_project_my_role)
+        self.assertTrue(hasattr(sys_creds, 'os_roles_project_my_role'))
+        self.assertEqual(expected_clients, sys_creds.os_roles_project_my_role)
+        self.assertTrue(hasattr(sys_creds, 'os_domain_my_role'))
+        self.assertEqual(expected_clients, sys_creds.os_domain_my_role)
+        self.assertTrue(hasattr(sys_creds, 'os_roles_domain_my_role'))
+        self.assertEqual(expected_clients, sys_creds.os_roles_domain_my_role)
+        self.assertTrue(hasattr(sys_creds, 'os_system_my_role'))
+        self.assertEqual(expected_clients, sys_creds.os_system_my_role)
+        self.assertTrue(hasattr(sys_creds, 'os_roles_system_my_role'))
+        self.assertEqual(expected_clients, sys_creds.os_roles_system_my_role)
+
+        self.assertEqual(4, mock_get_client_manager.call_count)
+        self.assertEqual(
+            expected_creds[0][1:],
+            mock_get_client_manager.mock_calls[0][2]['roles'])
+        self.assertEqual(
+            'project', mock_get_client_manager.mock_calls[0][2]['scope'])
+        self.assertEqual(
+            expected_creds[1][1:],
+            mock_get_client_manager.mock_calls[1][2]['roles'])
+        self.assertEqual(
+            'project', mock_get_client_manager.mock_calls[1][2]['scope'])
+        self.assertEqual(
+            expected_creds[2][1:],
+            mock_get_client_manager.mock_calls[2][2]['roles'])
+        self.assertEqual(
+            'domain', mock_get_client_manager.mock_calls[2][2]['scope'])
+        self.assertEqual(
+            expected_creds[3][1:],
+            mock_get_client_manager.mock_calls[3][2]['roles'])
+        self.assertEqual(
+            'system', mock_get_client_manager.mock_calls[3][2]['scope'])
+
     def test_setup_class_overwritten(self):
 
         class OverridesSetup(self.parent_test):
diff --git a/tools/check_logs.py b/tools/check_logs.py
index 7e191a0..cc74b17 100755
--- a/tools/check_logs.py
+++ b/tools/check_logs.py
@@ -17,12 +17,13 @@
 
 import argparse
 import gzip
+import io
 import os
 import re
 import sys
+import urllib.request as urlreq
 
 import six
-import six.moves.urllib.request as urlreq
 import yaml
 
 # DEVSTACK_GATE_GRENADE is either unset if grenade is not running
@@ -69,7 +70,7 @@
         req = urlreq.Request(url)
         req.add_header('Accept-Encoding', 'gzip')
         page = urlreq.urlopen(req)
-        buf = six.StringIO(page.read())
+        buf = io.StringIO(page.read())
         f = gzip.GzipFile(fileobj=buf)
         if scan_content(f.read().splitlines(), regexp, allow_list):
             logs_with_errors.append(name)
diff --git a/zuul.d/integrated-gate.yaml b/zuul.d/integrated-gate.yaml
index c0db9b1..5a14430 100644
--- a/zuul.d/integrated-gate.yaml
+++ b/zuul.d/integrated-gate.yaml
@@ -11,8 +11,10 @@
     vars:
       tox_envlist: all
       tempest_test_regex: tempest
-      devstack_localrc:
-        ENABLE_FILE_INJECTION: true
+      # TODO(gmann): Enable File injection tests once nova bug is fixed
+      # https://bugs.launchpad.net/nova/+bug/1882421
+      # devstack_localrc:
+      #   ENABLE_FILE_INJECTION: true
 
 - job:
     name: tempest-ipv6-only
@@ -60,12 +62,16 @@
 - job:
     name: tempest-full-py3
     parent: devstack-tempest
-    # This currently works from stable/pike on.
-    # Before stable/pike, legacy version of tempest-full
-    # 'legacy-tempest-dsvm-neutron-full' run.
-    branches: ^(?!stable/ocata).*$
+    # This job version is with swift disabled on py3
+    # as swift was not ready on py3 until stable/train.
+    branches:
+      - stable/pike
+      - stable/queens
+      - stable/rocky
+      - stable/stein
+      - stable/train
     description: |
-      Base integration test with Neutron networking and py3.
+      Base integration test with Neutron networking, swift disabled, and py3.
       Former names for this job where:
         * legacy-tempest-dsvm-py35
         * gate-tempest-dsvm-py35
@@ -109,6 +115,25 @@
         neutron-qos: true
 
 - job:
+    name: tempest-full-py3
+    parent: devstack-tempest
+    # This job version is with swift enabled on py3
+    # as swift is ready on py3 from stable/ussuri onwards.
+    branches: ^(?!stable/(ocata|pike|queens|rocky|stein|train)).*$
+    description: |
+      Base integration test with Neutron networking, swift enable, and py3.
+      Former names for this job where:
+        * legacy-tempest-dsvm-py35
+        * gate-tempest-dsvm-py35
+    vars:
+      tox_envlist: full
+      devstack_localrc:
+        USE_PYTHON3: true
+        FORCE_CONFIG_DRIVE: true
+        ENABLE_VOLUME_MULTIATTACH: true
+        GLANCE_USE_IMPORT_WORKFLOW: True
+
+- job:
     name: tempest-integrated-networking
     parent: devstack-tempest
     branches: ^(?!stable/ocata).*$
@@ -281,6 +306,12 @@
       subnode:
         devstack_localrc:
           USE_PYTHON3: true
+        devstack_local_conf:
+          post-config:
+            "/$NEUTRON_CORE_PLUGIN_CONF":
+              ovs:
+                bridge_mappings: public:br-ex
+                resource_provider_bandwidths: br-ex:1000000:1000000
 
 - job:
     name: tempest-slow
@@ -294,6 +325,8 @@
         * legacy-tempest-dsvm-neutron-scenario-multinode-lvm-multibackend
         * tempest-scenario-multinode-lvm-multibackend
     timeout: 10800
+    # This job runs on stable/stein onwards.
+    branches: ^(?!stable/(ocata|pike|queens|rocky)).*$
     vars:
       tox_envlist: slow-serial
       devstack_localrc:
@@ -314,6 +347,46 @@
           ENABLE_VOLUME_MULTIATTACH: true
 
 - job:
+    name: tempest-slow
+    parent: tempest-multinode-full
+    description: |
+      This multinode integration job will run all the tests tagged as slow.
+      It enables the lvm multibackend setup to cover few scenario tests.
+      This job will run only slow tests (API or Scenario) serially.
+
+      Former names for this job were:
+        * legacy-tempest-dsvm-neutron-scenario-multinode-lvm-multibackend
+        * tempest-scenario-multinode-lvm-multibackend
+    timeout: 10800
+    branches:
+      - stable/pike
+      - stable/queens
+      - stable/rocky
+    vars:
+      tox_envlist: slow-serial
+      devstack_localrc:
+        CINDER_ENABLED_BACKENDS: lvm:lvmdriver-1,lvm:lvmdriver-2
+        ENABLE_VOLUME_MULTIATTACH: true
+        # to avoid https://bugs.launchpad.net/neutron/+bug/1914037
+        # as we couldn't backport the fix to rocky and older releases
+        IPV6_PUBLIC_RANGE: 2001:db8:0:10::/64
+        IPV6_PUBLIC_NETWORK_GATEWAY: 2001:db8:0:10::2
+        IPV6_ROUTER_GW_IP: 2001:db8:0:10::1
+      devstack_plugins:
+        neutron: https://opendev.org/openstack/neutron
+      devstack_services:
+        neutron-placement: true
+        neutron-qos: true
+      tempest_concurrency: 2
+    group-vars:
+      # NOTE(mriedem): The ENABLE_VOLUME_MULTIATTACH variable is used on both
+      # the controller and subnode prior to Rocky so we have to make sure the
+      # variable is set in both locations.
+      subnode:
+        devstack_localrc:
+          ENABLE_VOLUME_MULTIATTACH: true
+
+- job:
     name: tempest-slow-py3
     parent: tempest-slow
     vars:
@@ -352,7 +425,9 @@
       Former name for this job was legacy-tempest-dsvm-neutron-pg-full.
     vars:
       devstack_localrc:
-        ENABLE_FILE_INJECTION: true
+        # TODO(gmann): Enable File injection tests once nova bug is fixed
+        # https://bugs.launchpad.net/nova/+bug/1882421
+        # ENABLE_FILE_INJECTION: true
         DATABASE_TYPE: postgresql
 
 - project-template:
@@ -379,11 +454,9 @@
       run on Nova gate only.
     check:
       jobs:
-        - grenade
         - tempest-integrated-compute
     gate:
       jobs:
-        - grenade
         - tempest-integrated-compute
 
 - project-template:
diff --git a/zuul.d/project.yaml b/zuul.d/project.yaml
index 5dcd27f..d5b2787 100644
--- a/zuul.d/project.yaml
+++ b/zuul.d/project.yaml
@@ -8,17 +8,6 @@
       - release-notes-jobs-python3
     check:
       jobs:
-        - devstack-tempest:
-            files:
-              - ^playbooks/
-              - ^roles/
-              - ^.zuul.yaml$
-        - devstack-tempest-ipv6:
-            voting: false
-            files:
-              - ^playbooks/
-              - ^roles/
-              - ^.zuul.yaml$
         - tempest-full-parallel:
             # Define list of irrelevant files to use everywhere else
             irrelevant-files: &tempest-irrelevant-files
diff --git a/zuul.d/tempest-specific.yaml b/zuul.d/tempest-specific.yaml
index 387a94b..fd348cc 100644
--- a/zuul.d/tempest-specific.yaml
+++ b/zuul.d/tempest-specific.yaml
@@ -46,6 +46,8 @@
       tox_envlist: full-parallel
       run_tempest_cleanup: true
       run_tempest_dry_cleanup: true
+      devstack_localrc:
+        DEVSTACK_PARALLEL: True
 
 - job:
     name: tempest-full-py3-ipv6