Merge "Add tests for creating servers with (anti-)affinity"
diff --git a/doc/source/microversion_testing.rst b/doc/source/microversion_testing.rst
index 20ace9e..33e75ff 100644
--- a/doc/source/microversion_testing.rst
+++ b/doc/source/microversion_testing.rst
@@ -454,6 +454,10 @@
 
   .. _2.86: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id79
 
+  * `2.96`_
+
+  .. _2.96: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#maximum-in-2024-1-caracal-and-2024-2-dalmatian
+
 * Volume
 
   * `3.3`_
diff --git a/doc/source/supported_version.rst b/doc/source/supported_version.rst
index 162a111..0adfebd 100644
--- a/doc/source/supported_version.rst
+++ b/doc/source/supported_version.rst
@@ -9,9 +9,9 @@
 
 Tempest master supports the below OpenStack Releases:
 
+* 2024.2
 * 2024.1
 * 2023.2
-* 2023.1
 
 For older OpenStack Release:
 
@@ -32,7 +32,7 @@
 
 Tempest master supports the below python versions:
 
-* Python 3.8
 * Python 3.9
 * Python 3.10
 * Python 3.11
+* Python 3.12
diff --git a/etc/accounts.yaml.sample b/etc/accounts.yaml.sample
index 3dbed79..702cc6f 100644
--- a/etc/accounts.yaml.sample
+++ b/etc/accounts.yaml.sample
@@ -22,6 +22,12 @@
 #
 # The value of domain_[id|name] is used for project_domain_[id|name] if not
 # specified and user_domain_[id|name] if not specified.
+#
+# When specifying domain-scoped accounts, domain_[id|name] must be present
+# and project_[id|name] must be absent.
+#
+# When specifying system-scoped accounts, value of system can be anything
+# (for now), and neither project_[id|name] nor domain_[id|name] must be present
 
 - username: 'user_1'
   tenant_name: 'test_tenant_1'
@@ -40,6 +46,22 @@
     - 'not_an_admin'
     - 'an_admin'
 
+# To specify a domain-scoped admin
+- username: 'user_3'
+  user_domain_name: 'test_domain'
+  password: 'test_password'
+  domain_name: 'test_domain'
+  roles:
+  - 'admin'
+
+# To specify a system-scoped admin
+- username: 'user_4'
+  user_domain_name: 'test_domain'
+  password: 'test_password'
+  system: 'all'
+  roles:
+  - 'admin'
+
 # To specify a user has a role specified in the config file you can use the
 # type field to specify it, valid values are admin, operator, and reseller_admin
 - username: 'swift_pseudo_admin_user_1'
diff --git a/releasenotes/notes/2024.2-intermediate-release-2a9f305375fcb462.yaml b/releasenotes/notes/2024.2-intermediate-release-2a9f305375fcb462.yaml
new file mode 100644
index 0000000..11d3a4f
--- /dev/null
+++ b/releasenotes/notes/2024.2-intermediate-release-2a9f305375fcb462.yaml
@@ -0,0 +1,5 @@
+---
+prelude: >
+    This is an intermediate release during the 2024.2 Dalmatian development
+    cycle to make new functionality available to plugins and other consumers.
+
diff --git a/releasenotes/notes/Add-http_qcow2_image-config-option-a9dca410897c3044.yaml b/releasenotes/notes/Add-http_qcow2_image-config-option-a9dca410897c3044.yaml
new file mode 100644
index 0000000..c1b0033
--- /dev/null
+++ b/releasenotes/notes/Add-http_qcow2_image-config-option-a9dca410897c3044.yaml
@@ -0,0 +1,8 @@
+---
+features:
+  - |
+    Added a new config option in the `image` section, `http_qcow2_image`,
+    which will use `qcow2` format image to download from the external
+    source specified and use it for image conversion in glance tests. By
+    default it will download
+    `http://download.cirros-cloud.net/0.6.2/cirros-0.6.2-x86_64-disk.img`
diff --git a/releasenotes/notes/Add-scenario-config-opt-target-dir-5a969b64be1dc718.yaml b/releasenotes/notes/Add-scenario-config-opt-target-dir-5a969b64be1dc718.yaml
new file mode 100644
index 0000000..3adacfc
--- /dev/null
+++ b/releasenotes/notes/Add-scenario-config-opt-target-dir-5a969b64be1dc718.yaml
@@ -0,0 +1,7 @@
+---
+features:
+  - |
+    Adding a new config options `[scenario]/target_dir` which allows
+    users to specify the location where timestamps files will be
+    written to. The default value is /tmp that, however, cannot be
+    expected to persist across reboots of an instance.
diff --git a/releasenotes/notes/add-manager-creds-49acd9192110c3e3.yaml b/releasenotes/notes/add-manager-creds-49acd9192110c3e3.yaml
new file mode 100644
index 0000000..a5d7984
--- /dev/null
+++ b/releasenotes/notes/add-manager-creds-49acd9192110c3e3.yaml
@@ -0,0 +1,7 @@
+---
+features:
+  - |
+    Add support for project manager and domain manager personas by adding
+    ``get_project_manager_creds`` and ``get_domain_manager_creds`` to
+    the ``DynamicCredentialProvider`` and ``PreProvisionedCredentialProvider``
+    classes of the common library.
diff --git a/releasenotes/notes/add-target-host-filter-94803e93b701d052.yaml b/releasenotes/notes/add-target-host-filter-94803e93b701d052.yaml
new file mode 100644
index 0000000..83a3728
--- /dev/null
+++ b/releasenotes/notes/add-target-host-filter-94803e93b701d052.yaml
@@ -0,0 +1,6 @@
+---
+features:
+  - |
+    Add a new config option `[compute]/target_hosts_to_avoid` which will
+    filter out any hypervisor candidates with a hostname that matches the
+    provided pattern when determining target hosts for migration.
diff --git a/releasenotes/notes/change-volume-catalog_type-default-fbcb2be6ebc42818.yaml b/releasenotes/notes/change-volume-catalog_type-default-fbcb2be6ebc42818.yaml
new file mode 100644
index 0000000..a507bd7
--- /dev/null
+++ b/releasenotes/notes/change-volume-catalog_type-default-fbcb2be6ebc42818.yaml
@@ -0,0 +1,6 @@
+---
+upgrade:
+  - |
+    The default for ``[volume] catalog_type``, which is used to determine the
+    service type to use to identify the block storage service in the service
+    catalog, has changed from ``volumev3`` to ``block-storage``.
diff --git a/releasenotes/notes/drop-python38-support-c0a696af00110602.yaml b/releasenotes/notes/drop-python38-support-c0a696af00110602.yaml
new file mode 100644
index 0000000..035f628
--- /dev/null
+++ b/releasenotes/notes/drop-python38-support-c0a696af00110602.yaml
@@ -0,0 +1,8 @@
+---
+prelude: >
+    Tempest dropped the Python 3.8 support.
+upgrade:
+  - |
+    Python 3.8 support has been dropped. Last release of Tempest
+    to support python 3.8 is Temepst 41.0.0. The minimum version
+    of Python now supported by Tempest is Python 3.9.
diff --git a/releasenotes/notes/end-of-support-of-2023-1-ddec1dac59700063.yaml b/releasenotes/notes/end-of-support-of-2023-1-ddec1dac59700063.yaml
new file mode 100644
index 0000000..d52b54e
--- /dev/null
+++ b/releasenotes/notes/end-of-support-of-2023-1-ddec1dac59700063.yaml
@@ -0,0 +1,12 @@
+---
+prelude: >
+    This is an intermediate release during the 2025.1 development cycle to
+    mark the end of support for 2023.1 release in Tempest.
+    After this release, Tempest will support below OpenStack Releases:
+
+    * 2024.2
+    * 2024.1
+    * 2023.2
+
+    Current development of Tempest is for OpenStack 2025.1 development
+    cycle.
diff --git a/releasenotes/notes/image-config-http-image-default-value-change-476622e984e16ab5.yaml b/releasenotes/notes/image-config-http-image-default-value-change-476622e984e16ab5.yaml
new file mode 100644
index 0000000..96e9251
--- /dev/null
+++ b/releasenotes/notes/image-config-http-image-default-value-change-476622e984e16ab5.yaml
@@ -0,0 +1,7 @@
+---
+upgrade:
+  - |
+    Changed the default value of 'http_image' config option in the
+    'image' group to 'http://download.cirros-cloud.net/0.6.2/cirros-0.6.2-x86_64-uec.tar.gz'
+    from 'http://download.cirros-cloud.net/0.3.1/cirros-0.3.1-x86_64-uec.tar.gz' as former
+    image is very old and we should always use latest one.
\ No newline at end of file
diff --git a/releasenotes/notes/image-enforcement-config-0bc67791a40bac56.yaml b/releasenotes/notes/image-enforcement-config-0bc67791a40bac56.yaml
new file mode 100644
index 0000000..2bbc82b
--- /dev/null
+++ b/releasenotes/notes/image-enforcement-config-0bc67791a40bac56.yaml
@@ -0,0 +1,8 @@
+---
+features:
+  - |
+    Add a new config option
+    `[image_feature_enabled]/image_format_enforcement` which tells tempest
+    that glance will do image format inspection and enforcement on upload. This
+    will disable tests that require glance to accept a bad image in order to
+    test another service (i.e. nova).
diff --git a/releasenotes/notes/image-wait-multiple-79c55305b584b1ba.yaml b/releasenotes/notes/image-wait-multiple-79c55305b584b1ba.yaml
new file mode 100644
index 0000000..6f63ebd
--- /dev/null
+++ b/releasenotes/notes/image-wait-multiple-79c55305b584b1ba.yaml
@@ -0,0 +1,6 @@
+---
+features:
+  - |
+    The wait_for_image_status() waiter now allows a list of status values
+    instead of just a string, and returns the state the image was in when we
+    stopped waiting.
diff --git a/releasenotes/notes/remove-identity-v2-tests-369b3fa190f624da.yaml b/releasenotes/notes/remove-identity-v2-tests-369b3fa190f624da.yaml
new file mode 100644
index 0000000..d927a68
--- /dev/null
+++ b/releasenotes/notes/remove-identity-v2-tests-369b3fa190f624da.yaml
@@ -0,0 +1,23 @@
+---
+upgrade:
+  - |
+    Tests for identity v2 API have been removed.
+
+deprecations:
+  - |
+    The following options have been formally deprecated. These options were
+    used to test identity v2 API which was removed during Queens cycle.
+    The tests for identity v2 API were removed from tempest and these options
+    have no effect.
+
+    - ``[identity] uri``
+    - ``[identity] v2_admin_endpoint_type``
+    - ``[identity] v2_public_endpoint_type``
+    - ``[identity-feature-enabled] api_v2_admin``
+
+  - |
+    The following options have been deprecated because only identity v3 API
+    is used.
+
+    - ``[identity] auth_version``
+    - ``[identity-feature-enabled] api_v3``
diff --git a/releasenotes/notes/tempest-2024-2-release-78846595720db3cd.yaml b/releasenotes/notes/tempest-2024-2-release-78846595720db3cd.yaml
new file mode 100644
index 0000000..57367c8
--- /dev/null
+++ b/releasenotes/notes/tempest-2024-2-release-78846595720db3cd.yaml
@@ -0,0 +1,17 @@
+---
+prelude: >
+    This release is to tag Tempest for OpenStack 2024.2 release.
+    This release marks the start of 2024.2 release support in Tempest.
+    After this release, Tempest will support below OpenStack Releases:
+
+    * 2024.2
+    * 2024.1
+    * 2023.2
+    * 2023.1
+
+    Current development of Tempest is for OpenStack 2025.1 development
+    cycle. Every Tempest commit is also tested against master during
+    the 2025.1 cycle. However, this does not necessarily mean that using
+    Tempest as of this tag will work against a 2025.1 (or future release)
+    cloud.
+    To be on safe side, use this tag to test the OpenStack 2024.2 release.
diff --git a/releasenotes/source/index.rst b/releasenotes/source/index.rst
index f36c837..633d90e 100644
--- a/releasenotes/source/index.rst
+++ b/releasenotes/source/index.rst
@@ -6,6 +6,8 @@
    :maxdepth: 1
 
    unreleased
+   v41.0.0
+   v40.0.0
    v39.0.0
    v38.0.0
    v37.0.0
diff --git a/releasenotes/source/v40.0.0.rst b/releasenotes/source/v40.0.0.rst
new file mode 100644
index 0000000..995767b
--- /dev/null
+++ b/releasenotes/source/v40.0.0.rst
@@ -0,0 +1,6 @@
+=====================
+v40.0.0 Release Notes
+=====================
+
+.. release-notes:: 40.0.0 Release Notes
+   :version: 40.0.0
diff --git a/releasenotes/source/v41.0.0.rst b/releasenotes/source/v41.0.0.rst
new file mode 100644
index 0000000..6d79c4c
--- /dev/null
+++ b/releasenotes/source/v41.0.0.rst
@@ -0,0 +1,6 @@
+=====================
+v41.0.0 Release Notes
+=====================
+
+.. release-notes:: 41.0.0 Release Notes
+   :version: 41.0.0
diff --git a/requirements.txt b/requirements.txt
index 6e66046..a1eff53 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,6 +1,3 @@
-# The order of packages is significant, because pip processes them in the order
-# of appearance. Changing the order has an impact on the overall integration
-# process, which may cause wedges in the gate later.
 pbr!=2.1.0,>=2.0.0 # Apache-2.0
 cliff!=2.9.0,>=2.8.0 # Apache-2.0
 jsonschema>=3.2.0 # MIT
@@ -13,7 +10,7 @@
 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>=4.7.0 # Apache-2.0
+oslo.utils>=7.0.0 # Apache-2.0
 fixtures>=3.0.0 # Apache-2.0/BSD
 PyYAML>=3.12 # MIT
 python-subunit>=1.0.0 # Apache-2.0/BSD
@@ -23,3 +20,4 @@
 debtcollector>=1.2.0 # Apache-2.0
 defusedxml>=0.7.1 # PSFL
 fasteners>=0.16.0 # Apache-2.0
+testscenarios>=0.5.0
diff --git a/roles/run-tempest/tasks/main.yaml b/roles/run-tempest/tasks/main.yaml
index 29409c0..15b1743 100644
--- a/roles/run-tempest/tasks/main.yaml
+++ b/roles/run-tempest/tasks/main.yaml
@@ -25,11 +25,11 @@
     target_branch: "{{ zuul.override_checkout }}"
   when: zuul.override_checkout is defined
 
-- name: Use stable branch upper-constraints till Wallaby
+- name: Use stable branch upper-constraints till 2023.1
   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", "stable/stein", "stable/train", "stable/ussuri", "unmaintained/victoria", "unmaintained/wallaby"]
+  when: target_branch in ["stable/ocata", "stable/pike", "stable/queens", "stable/rocky", "stable/stein", "stable/train", "stable/ussuri", "stable/2023.1", "unmaintained/victoria", "unmaintained/wallaby", "unmaintained/xena", "unmaintained/yoga", "unmaintained/zed", "unmaintained/2023.1"]
 
 - name: Use Configured upper-constraints for non-master Tempest
   set_fact:
diff --git a/setup.cfg b/setup.cfg
index bb1ced5..67555f4 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -6,7 +6,6 @@
 author = OpenStack
 author_email = openstack-discuss@lists.openstack.org
 home_page = https://docs.openstack.org/tempest/latest/
-python_requires = >=3.8
 classifier =
     Intended Audience :: Information Technology
     Intended Audience :: System Administrators
@@ -15,10 +14,10 @@
     Operating System :: POSIX :: Linux
     Programming Language :: Python
     Programming Language :: Python :: 3
-    Programming Language :: Python :: 3.8
     Programming Language :: Python :: 3.9
     Programming Language :: Python :: 3.10
     Programming Language :: Python :: 3.11
+    Programming Language :: Python :: 3.12
     Programming Language :: Python :: 3 :: Only
     Programming Language :: Python :: Implementation :: CPython
 
diff --git a/tempest/api/compute/admin/test_hosts.py b/tempest/api/compute/admin/test_hosts.py
index 0d79570..849b535 100644
--- a/tempest/api/compute/admin/test_hosts.py
+++ b/tempest/api/compute/admin/test_hosts.py
@@ -14,8 +14,11 @@
 
 from tempest.api.compute import base
 from tempest.common import tempest_fixtures as fixtures
+from tempest import config
 from tempest.lib import decorators
 
+CONF = config.CONF
+
 
 class HostsAdminTestJSON(base.BaseV2ComputeAdminTest):
     """Tests nova hosts API using admin privileges."""
@@ -70,7 +73,7 @@
 
         hosts = [host for host in hosts if (
             host['service'] == 'compute' and
-            not host['host_name'].endswith('-ironic'))]
+            CONF.compute.target_hosts_to_avoid not in host['host_name'])]
         self.assertNotEmpty(hosts)
 
         for host in hosts:
diff --git a/tempest/api/compute/admin/test_servers_on_multinodes.py b/tempest/api/compute/admin/test_servers_on_multinodes.py
index 22ff13c..e0290e4 100644
--- a/tempest/api/compute/admin/test_servers_on_multinodes.py
+++ b/tempest/api/compute/admin/test_servers_on_multinodes.py
@@ -154,6 +154,15 @@
         compute.shelve_server(self.servers_client, server['id'],
                               force_shelve_offload=True)
 
+        # Work around https://bugs.launchpad.net/nova/+bug/2045785
+        # This can be removed when ^ is fixed.
+        def _check_server_host_is_none():
+            server_details = self.os_admin.servers_client.show_server(
+                server['id'])
+            self.assertIsNone(server_details['server']['OS-EXT-SRV-ATTR:host'])
+
+        self.wait_for(_check_server_host_is_none)
+
         self.os_admin.servers_client.unshelve_server(
             server['id'],
             body={'unshelve': {'host': host}}
diff --git a/tempest/api/compute/base.py b/tempest/api/compute/base.py
index ed94af0..b974b52 100644
--- a/tempest/api/compute/base.py
+++ b/tempest/api/compute/base.py
@@ -538,6 +538,37 @@
                                                     volume['id'], 'available')
         return volume
 
+    @classmethod
+    def verify_metadata_from_api(self, server, ssh_client, verify_method):
+        md_url = 'http://169.254.169.254/openstack/latest/meta_data.json'
+        LOG.info('Attempting to verify tagged devices in server %s via '
+                 'the metadata service: %s', server['id'], md_url)
+
+        def get_and_verify_metadata():
+            try:
+                ssh_client.exec_command('curl -V')
+            except lib_exc.SSHExecCommandFailed:
+                if not CONF.compute_feature_enabled.config_drive:
+                    raise self.skipException('curl not found in guest '
+                                             'and config drive is '
+                                             'disabled')
+                LOG.warning('curl was not found in the guest, device '
+                            'tagging metadata was not checked in the '
+                            'metadata API')
+                return True
+            cmd = 'curl %s' % md_url
+            md_json = ssh_client.exec_command(cmd)
+            return verify_method(md_json)
+        # NOTE(gmann) Keep refreshing the metadata info until the metadata
+        # cache is refreshed. For safer side, we will go with wait loop of
+        # build_interval till build_timeout. verify_method() above will return
+        # True if all metadata verification is done as expected.
+        if not test_utils.call_until_true(get_and_verify_metadata,
+                                          CONF.compute.build_timeout,
+                                          CONF.compute.build_interval):
+            raise lib_exc.TimeoutException('Timeout while verifying '
+                                           'metadata on server.')
+
     def _detach_volume(self, server, volume):
         """Helper method to detach a volume.
 
@@ -689,7 +720,7 @@
             binary='nova-compute')['services']
         hosts = []
         for svc in svcs:
-            if svc['host'].endswith('-ironic'):
+            if CONF.compute.target_hosts_to_avoid in svc['host']:
                 continue
             if svc['state'] == 'up' and svc['status'] == 'enabled':
                 if CONF.compute.compute_volume_common_az:
diff --git a/tempest/api/compute/flavors/test_flavors_negative.py b/tempest/api/compute/flavors/test_flavors_negative.py
index 09f54b5..efd9cdd 100644
--- a/tempest/api/compute/flavors/test_flavors_negative.py
+++ b/tempest/api/compute/flavors/test_flavors_negative.py
@@ -47,7 +47,7 @@
             'name': data_utils.rand_name(
                 prefix=CONF.resource_name_prefix, name='image'),
             'container_format': CONF.image.container_formats[0],
-            'disk_format': CONF.image.disk_formats[0],
+            'disk_format': 'raw',
             'min_ram': min_img_ram,
             'visibility': 'private'
         }
diff --git a/tempest/api/compute/images/test_images.py b/tempest/api/compute/images/test_images.py
index d728853..a90d500 100644
--- a/tempest/api/compute/images/test_images.py
+++ b/tempest/api/compute/images/test_images.py
@@ -90,6 +90,14 @@
                                               name=snapshot_name,
                                               wait_until='ACTIVE',
                                               wait_for_server=False)
+        # This is required due to ceph issue:
+        # https://bugs.launchpad.net/glance/+bug/2045769.
+        # New location APIs are async so we need to wait for the location
+        # import task to complete.
+        # This should work with old location API since we don't fail if there
+        # are no tasks for the image
+        waiters.wait_for_image_tasks_status(self.images_client,
+                                            image['id'], 'success')
         self.addCleanup(self.client.delete_image, image['id'])
         self.assertEqual(snapshot_name, image['name'])
 
@@ -110,6 +118,14 @@
                                               name=snapshot_name,
                                               wait_until='ACTIVE',
                                               wait_for_server=False)
+        # This is required due to ceph issue:
+        # https://bugs.launchpad.net/glance/+bug/2045769.
+        # New location APIs are async so we need to wait for the location
+        # import task to complete.
+        # This should work with old location API since we don't fail if there
+        # are no tasks for the image
+        waiters.wait_for_image_tasks_status(self.images_client,
+                                            image['id'], 'success')
         self.addCleanup(self.client.delete_image, image['id'])
         self.assertEqual(snapshot_name, image['name'])
 
@@ -130,6 +146,14 @@
                                               name=snapshot_name,
                                               wait_until='ACTIVE',
                                               wait_for_server=False)
+        # This is required due to ceph issue:
+        # https://bugs.launchpad.net/glance/+bug/2045769.
+        # New location APIs are async so we need to wait for the location
+        # import task to complete.
+        # This should work with old location API since we don't fail if there
+        # are no tasks for the image
+        waiters.wait_for_image_tasks_status(self.images_client,
+                                            image['id'], 'success')
         self.addCleanup(self.client.delete_image, image['id'])
         self.assertEqual(snapshot_name, image['name'])
 
diff --git a/tempest/api/compute/servers/test_attach_interfaces.py b/tempest/api/compute/servers/test_attach_interfaces.py
index 8984d1d..eddfd73 100644
--- a/tempest/api/compute/servers/test_attach_interfaces.py
+++ b/tempest/api/compute/servers/test_attach_interfaces.py
@@ -316,6 +316,7 @@
             _, servers = compute.create_test_server(
                 self.os_primary, tenant_network=network,
                 validatable=True,
+                wait_until='ACTIVE',
                 validation_resources=validation_resources)
             return servers[0]
 
diff --git a/tempest/api/compute/servers/test_create_server.py b/tempest/api/compute/servers/test_create_server.py
index b7db200..0b39b8a 100644
--- a/tempest/api/compute/servers/test_create_server.py
+++ b/tempest/api/compute/servers/test_create_server.py
@@ -16,6 +16,8 @@
 import netaddr
 import testtools
 
+from oslo_serialization import jsonutils as json
+
 from tempest.api.compute import base
 from tempest.common import utils
 from tempest.common.utils.linux import remote_client
@@ -235,3 +237,76 @@
             servers_client=self.client)
         hostname = linux_client.exec_command("hostname").rstrip()
         self.assertEqual('guest-instance-1-domain-com', hostname)
+
+
+class ServersV294TestFqdnHostnames(base.BaseV2ComputeTest):
+    """Test creating server with FQDN hostname and verifying attributes
+
+    Starting Antelope release, Nova allows to set hostname as an FQDN
+    type and allows free form characters in hostname using --hostname
+    parameter with API above 2.94 .
+
+    This is to create server with --hostname having FQDN type value having
+    more than 64 characters
+    """
+
+    min_microversion = '2.94'
+
+    @classmethod
+    def setup_credentials(cls):
+        cls.prepare_instance_network()
+        super(ServersV294TestFqdnHostnames, cls).setup_credentials()
+
+    @classmethod
+    def setup_clients(cls):
+        super(ServersV294TestFqdnHostnames, cls).setup_clients()
+        cls.client = cls.servers_client
+
+    @classmethod
+    def resource_setup(cls):
+        super(ServersV294TestFqdnHostnames, cls).resource_setup()
+        cls.validation_resources = cls.get_class_validation_resources(
+            cls.os_primary)
+        cls.accessIPv4 = '1.1.1.1'
+        cls.name = 'guest-instance-1'
+        cls.password = data_utils.rand_password()
+        cls.hostname = 'x' * 52 + '-guest-test.domaintest.com'
+        cls.test_server = cls.create_test_server(
+            validatable=True,
+            validation_resources=cls.validation_resources,
+            wait_until='ACTIVE',
+            name=cls.name,
+            accessIPv4=cls.accessIPv4,
+            adminPass=cls.password,
+            hostname=cls.hostname)
+        cls.server = cls.client.show_server(cls.test_server['id'])['server']
+
+    def verify_metadata_hostname(self, md_json):
+        md_dict = json.loads(md_json)
+        dhcp_domain = CONF.compute_feature_enabled.dhcp_domain
+        if md_dict['hostname'] == f"{self.hostname}{dhcp_domain}":
+            return True
+        else:
+            return False
+
+    @decorators.idempotent_id('e7b05488-f9d5-4fce-91b3-e82216c52017')
+    @testtools.skipUnless(CONF.validation.run_validation,
+                          'Instance validation tests are disabled.')
+    def test_verify_hostname_allows_fqdn(self):
+        """Test to verify --hostname allows FQDN type name scheme
+
+        Verify the hostname has FQDN value and Freeform characters
+        in the hostname are allowed
+        """
+        self.assertEqual(
+            self.hostname, self.server['OS-EXT-SRV-ATTR:hostname'])
+        # Verify that metadata API has correct hostname inside guest
+        linux_client = remote_client.RemoteClient(
+            self.get_server_ip(self.test_server, self.validation_resources),
+            self.ssh_user,
+            self.password,
+            self.validation_resources['keypair']['private_key'],
+            server=self.test_server,
+            servers_client=self.client)
+        self.verify_metadata_from_api(
+            self.test_server, linux_client, self.verify_metadata_hostname)
diff --git a/tempest/api/compute/servers/test_device_tagging.py b/tempest/api/compute/servers/test_device_tagging.py
index 2640311..d2fdd52 100644
--- a/tempest/api/compute/servers/test_device_tagging.py
+++ b/tempest/api/compute/servers/test_device_tagging.py
@@ -23,9 +23,7 @@
 from tempest.common import waiters
 from tempest import config
 from tempest.lib.common.utils import data_utils
-from tempest.lib.common.utils import test_utils
 from tempest.lib import decorators
-from tempest.lib import exceptions
 
 
 CONF = config.CONF
@@ -64,36 +62,6 @@
                                   dhcp=True)
         super(DeviceTaggingBase, cls).setup_credentials()
 
-    def verify_metadata_from_api(self, server, ssh_client, verify_method):
-        md_url = 'http://169.254.169.254/openstack/latest/meta_data.json'
-        LOG.info('Attempting to verify tagged devices in server %s via '
-                 'the metadata service: %s', server['id'], md_url)
-
-        def get_and_verify_metadata():
-            try:
-                ssh_client.exec_command('curl -V')
-            except exceptions.SSHExecCommandFailed:
-                if not CONF.compute_feature_enabled.config_drive:
-                    raise self.skipException('curl not found in guest '
-                                             'and config drive is '
-                                             'disabled')
-                LOG.warning('curl was not found in the guest, device '
-                            'tagging metadata was not checked in the '
-                            'metadata API')
-                return True
-            cmd = 'curl %s' % md_url
-            md_json = ssh_client.exec_command(cmd)
-            return verify_method(md_json)
-        # NOTE(gmann) Keep refreshing the metadata info until the metadata
-        # cache is refreshed. For safer side, we will go with wait loop of
-        # build_interval till build_timeout. verify_method() above will return
-        # True if all metadata verification is done as expected.
-        if not test_utils.call_until_true(get_and_verify_metadata,
-                                          CONF.compute.build_timeout,
-                                          CONF.compute.build_interval):
-            raise exceptions.TimeoutException('Timeout while verifying '
-                                              'metadata on server.')
-
     def verify_metadata_on_config_drive(self, server, ssh_client,
                                         verify_method):
         LOG.info('Attempting to verify tagged devices in server %s via '
diff --git a/tempest/api/compute/servers/test_server_actions.py b/tempest/api/compute/servers/test_server_actions.py
index 21ed0cd..10153bb 100644
--- a/tempest/api/compute/servers/test_server_actions.py
+++ b/tempest/api/compute/servers/test_server_actions.py
@@ -45,15 +45,6 @@
         try:
             self.validation_resources = self.get_class_validation_resources(
                 self.os_primary)
-            # _test_rebuild_server test compares ip address attached to the
-            # server before and after the rebuild, in order to avoid
-            # a situation when a newly created server doesn't have a floating
-            # ip attached at the beginning of the test_rebuild_server let's
-            # make sure right here the floating ip is attached
-            waiters.wait_for_server_floating_ip(
-                self.client,
-                self.client.show_server(self.server_id)['server'],
-                self.validation_resources['floating_ip'])
             waiters.wait_for_server_status(self.client,
                                            self.server_id, 'ACTIVE')
         except lib_exc.NotFound:
@@ -127,7 +118,7 @@
             self.assertGreater(new_boot_time, boot_time,
                                '%s > %s' % (new_boot_time, boot_time))
 
-    def _test_rebuild_server(self, server_id):
+    def _test_rebuild_server(self, server_id, **kwargs):
         # Get the IPs the server has before rebuilding it
         original_addresses = (self.client.show_server(server_id)['server']
                               ['addresses'])
@@ -166,11 +157,17 @@
             # 3.Any "id_rsa", "id_dsa" or "id_ecdsa" key discoverable in
             #   ~/.ssh/ (if allowed).
             # 4.Plain username/password auth, if a password was given.
+
+            if 'validation_resources' in kwargs:
+                validation_resources = kwargs['validation_resources']
+            else:
+                validation_resources = self.validation_resources
+
             linux_client = remote_client.RemoteClient(
-                self.get_server_ip(rebuilt_server, self.validation_resources),
+                self.get_server_ip(rebuilt_server, validation_resources),
                 self.ssh_alt_user,
                 password,
-                self.validation_resources['keypair']['private_key'],
+                validation_resources['keypair']['private_key'],
                 server=rebuilt_server,
                 servers_client=self.client)
             linux_client.validate_authentication()
@@ -267,17 +264,32 @@
         The server should be rebuilt using the provided image and data.
         """
         tenant_network = self.get_tenant_network()
+        validation_resources = self.get_test_validation_resources(
+            self.os_primary)
         _, servers = compute.create_test_server(
             self.os_primary,
-            wait_until='ACTIVE',
+            wait_until='SSHABLE',
+            validatable=True,
+            validation_resources=validation_resources,
             tenant_network=tenant_network)
         server = servers[0]
+        # _test_rebuild_server test compares ip address attached to the
+        # server before and after the rebuild, in order to avoid
+        # a situation when a newly created server doesn't have a floating
+        # ip attached at the beginning of the test_rebuild_server let's
+        # make sure right here the floating ip is attached
+        waiters.wait_for_server_floating_ip(
+            self.client,
+            server,
+            validation_resources['floating_ip'])
 
         self.addCleanup(waiters.wait_for_server_termination,
                         self.client, server['id'])
         self.addCleanup(self.client.delete_server, server['id'])
 
-        self._test_rebuild_server(server_id=server['id'])
+        self._test_rebuild_server(
+            server_id=server['id'],
+            validation_resources=validation_resources)
 
     @decorators.idempotent_id('1499262a-9328-4eda-9068-db1ac57498d2')
     @testtools.skipUnless(CONF.compute_feature_enabled.resize,
@@ -465,7 +477,9 @@
         self.attach_volume(server, volume)
 
         # run general rebuild test
-        self._test_rebuild_server(server_id=server['id'])
+        self._test_rebuild_server(
+            server_id=server['id'],
+            validation_resources=validation_resources)
 
         # make sure the volume is attached to the instance after rebuild
         vol_after_rebuild = self.volumes_client.show_volume(volume['id'])
@@ -605,6 +619,14 @@
         self.addCleanup(_clean_oldest_backup, image1_id)
         waiters.wait_for_image_status(glance_client,
                                       image1_id, 'active')
+        # This is required due to ceph issue:
+        # https://bugs.launchpad.net/glance/+bug/2045769.
+        # New location APIs are async so we need to wait for the location
+        # import task to complete.
+        # This should work with old location API since we don't fail if there
+        # are no tasks for the image
+        waiters.wait_for_image_tasks_status(self.images_client,
+                                            image1_id, 'success')
 
         backup2 = data_utils.rand_name(
             prefix=CONF.resource_name_prefix, name='backup-2')
@@ -621,6 +643,8 @@
         self.addCleanup(glance_client.delete_image, image2_id)
         waiters.wait_for_image_status(glance_client,
                                       image2_id, 'active')
+        waiters.wait_for_image_tasks_status(self.images_client,
+                                            image2_id, 'success')
 
         # verify they have been created
         properties = {
@@ -655,6 +679,8 @@
             image3_id = resp['image_id']
         else:
             image3_id = data_utils.parse_image_id(resp.response['location'])
+        waiters.wait_for_image_tasks_status(self.images_client,
+                                            image3_id, 'success')
         self.addCleanup(glance_client.delete_image, image3_id)
         # the first back up should be deleted
         waiters.wait_for_server_status(self.client, self.server_id, 'ACTIVE')
diff --git a/tempest/api/compute/servers/test_servers.py b/tempest/api/compute/servers/test_servers.py
index c72b74e..e7e84d6 100644
--- a/tempest/api/compute/servers/test_servers.py
+++ b/tempest/api/compute/servers/test_servers.py
@@ -263,3 +263,22 @@
         servers = self.servers_client.list_servers(
             detail=True, **params)['servers']
         self.assertNotEmpty(servers)
+
+
+class ServersListShow296Test(base.BaseV2ComputeTest):
+    """Test compute server with microversion >= than 2.96
+
+    This test tests the Server APIs response schema for 2.96 microversion.
+    No specific assert or behaviour verification is needed.
+    """
+
+    min_microversion = '2.96'
+    max_microversion = 'latest'
+
+    @decorators.idempotent_id('4eee1ffe-9e00-4c99-a431-0d3e0f323a8f')
+    def test_list_show_server_296(self):
+        server = self.create_test_server()
+        # Checking list API response schema.
+        self.servers_client.list_servers(detail=True)
+        # Checking show API response schema
+        self.servers_client.show_server(server['id'])
diff --git a/tempest/api/identity/admin/v2/test_endpoints.py b/tempest/api/identity/admin/v2/test_endpoints.py
deleted file mode 100644
index 20d023b..0000000
--- a/tempest/api/identity/admin/v2/test_endpoints.py
+++ /dev/null
@@ -1,97 +0,0 @@
-# Copyright 2013 OpenStack Foundation
-# All Rights Reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
-#    not use this file except in compliance with the License. You may obtain
-#    a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-#    License for the specific language governing permissions and limitations
-#    under the License.
-
-from tempest.api.identity import base
-from tempest import config
-from tempest.lib.common.utils import data_utils
-from tempest.lib import decorators
-
-CONF = config.CONF
-
-
-class EndPointsTestJSON(base.BaseIdentityV2AdminTest):
-    """Test keystone v2 endpoints"""
-
-    @classmethod
-    def resource_setup(cls):
-        super(EndPointsTestJSON, cls).resource_setup()
-        s_name = data_utils.rand_name(
-            name='service', prefix=CONF.resource_name_prefix)
-        s_type = data_utils.rand_name(
-            name='type', prefix=CONF.resource_name_prefix)
-        s_description = data_utils.rand_name(
-            name='description', prefix=CONF.resource_name_prefix)
-        service_data = cls.services_client.create_service(
-            name=s_name, type=s_type,
-            description=s_description)['OS-KSADM:service']
-        cls.addClassResourceCleanup(cls.services_client.delete_service,
-                                    service_data['id'])
-        cls.service_id = service_data['id']
-        # Create endpoints so as to use for LIST and GET test cases
-        cls.setup_endpoints = list()
-        for _ in range(2):
-            region = data_utils.rand_name(
-                name='region', prefix=CONF.resource_name_prefix)
-            url = data_utils.rand_url()
-            endpoint = cls.endpoints_client.create_endpoint(
-                service_id=cls.service_id,
-                region=region,
-                publicurl=url,
-                adminurl=url,
-                internalurl=url)['endpoint']
-            cls.addClassResourceCleanup(cls.endpoints_client.delete_endpoint,
-                                        endpoint['id'])
-            # list_endpoints() will return 'enabled' field
-            endpoint['enabled'] = True
-            cls.setup_endpoints.append(endpoint)
-
-    @decorators.idempotent_id('11f590eb-59d8-4067-8b2b-980c7f387f51')
-    def test_list_endpoints(self):
-        """Test listing keystone endpoints"""
-        # Get a list of endpoints
-        fetched_endpoints = self.endpoints_client.list_endpoints()['endpoints']
-        # Asserting LIST endpoints
-        missing_endpoints =\
-            [e for e in self.setup_endpoints if e not in fetched_endpoints]
-        self.assertEmpty(missing_endpoints,
-                         "Failed to find endpoint %s in fetched list" %
-                         ', '.join(str(e) for e in missing_endpoints))
-
-    @decorators.idempotent_id('9974530a-aa28-4362-8403-f06db02b26c1')
-    def test_create_list_delete_endpoint(self):
-        """Test creating, listing and deleting a keystone endpoint"""
-        region = data_utils.rand_name(
-            name='region', prefix=CONF.resource_name_prefix)
-        url = data_utils.rand_url()
-        endpoint = self.endpoints_client.create_endpoint(
-            service_id=self.service_id,
-            region=region,
-            publicurl=url,
-            adminurl=url,
-            internalurl=url)['endpoint']
-        # Asserting Create Endpoint response body
-        self.assertIn('id', endpoint)
-        self.assertEqual(region, endpoint['region'])
-        self.assertEqual(url, endpoint['publicurl'])
-        # Checking if created endpoint is present in the list of endpoints
-        fetched_endpoints = self.endpoints_client.list_endpoints()['endpoints']
-        fetched_endpoints_id = [e['id'] for e in fetched_endpoints]
-        self.assertIn(endpoint['id'], fetched_endpoints_id)
-        # Deleting the endpoint created in this method
-        self.endpoints_client.delete_endpoint(endpoint['id'])
-        # Checking whether endpoint is deleted successfully
-        fetched_endpoints = self.endpoints_client.list_endpoints()['endpoints']
-        fetched_endpoints_id = [e['id'] for e in fetched_endpoints]
-        self.assertNotIn(endpoint['id'], fetched_endpoints_id)
diff --git a/tempest/api/identity/admin/v2/test_roles.py b/tempest/api/identity/admin/v2/test_roles.py
deleted file mode 100644
index 6d384ab..0000000
--- a/tempest/api/identity/admin/v2/test_roles.py
+++ /dev/null
@@ -1,121 +0,0 @@
-# Copyright 2012 OpenStack Foundation
-# All Rights Reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
-#    not use this file except in compliance with the License. You may obtain
-#    a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-#    License for the specific language governing permissions and limitations
-#    under the License.
-
-from tempest.api.identity import base
-from tempest import config
-from tempest.lib.common.utils import data_utils
-from tempest.lib.common.utils import test_utils
-from tempest.lib import decorators
-
-CONF = config.CONF
-
-
-class RolesTestJSON(base.BaseIdentityV2AdminTest):
-
-    @classmethod
-    def resource_setup(cls):
-        super(RolesTestJSON, cls).resource_setup()
-        cls.roles = list()
-        for _ in range(5):
-            role_name = data_utils.rand_name(
-                name='role', prefix=CONF.resource_name_prefix)
-            role = cls.roles_client.create_role(name=role_name)['role']
-            cls.addClassResourceCleanup(
-                test_utils.call_and_ignore_notfound_exc,
-                cls.roles_client.delete_role, role['id'])
-            cls.roles.append(role)
-
-    def _get_role_params(self):
-        user = self.setup_test_user()
-        tenant = self.tenants_client.show_tenant(user['tenantId'])['tenant']
-        role = self.setup_test_role()
-        return (user, tenant, role)
-
-    def assert_role_in_role_list(self, role, roles):
-        found = False
-        for user_role in roles:
-            if user_role['id'] == role['id']:
-                found = True
-        self.assertTrue(found, "assigned role was not in list")
-
-    @decorators.idempotent_id('75d9593f-50b7-4fcf-bd64-e3fb4a278e23')
-    def test_list_roles(self):
-        """Return a list of all roles."""
-        body = self.roles_client.list_roles()['roles']
-        found = [role for role in body if role in self.roles]
-        self.assertNotEmpty(found)
-        self.assertEqual(len(found), len(self.roles))
-
-    @decorators.idempotent_id('c62d909d-6c21-48c0-ae40-0a0760e6db5e')
-    def test_role_create_delete(self):
-        """Role should be created, verified, and deleted."""
-        role_name = data_utils.rand_name(
-            name='role-test', prefix=CONF.resource_name_prefix)
-        body = self.roles_client.create_role(name=role_name)['role']
-        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
-                        self.roles_client.delete_role, body['id'])
-        self.assertEqual(role_name, body['name'])
-
-        body = self.roles_client.list_roles()['roles']
-        found = [role for role in body if role['name'] == role_name]
-        self.assertNotEmpty(found)
-
-        body = self.roles_client.delete_role(found[0]['id'])
-
-        body = self.roles_client.list_roles()['roles']
-        found = [role for role in body if role['name'] == role_name]
-        self.assertEmpty(found)
-
-    @decorators.idempotent_id('db6870bd-a6ed-43be-a9b1-2f10a5c9994f')
-    def test_get_role_by_id(self):
-        """Get a role by its id."""
-        role = self.setup_test_role()
-        role_id = role['id']
-        role_name = role['name']
-        body = self.roles_client.show_role(role_id)['role']
-        self.assertEqual(role_id, body['id'])
-        self.assertEqual(role_name, body['name'])
-
-    @decorators.idempotent_id('0146f675-ffbd-4208-b3a4-60eb628dbc5e')
-    def test_assign_user_role(self):
-        """Assign a role to a user on a tenant."""
-        (user, tenant, role) = self._get_role_params()
-        self.roles_client.create_user_role_on_project(tenant['id'],
-                                                      user['id'],
-                                                      role['id'])
-        roles = self.roles_client.list_user_roles_on_project(
-            tenant['id'], user['id'])['roles']
-        self.assert_role_in_role_list(role, roles)
-
-    @decorators.idempotent_id('f0b9292c-d3ba-4082-aa6c-440489beef69')
-    def test_remove_user_role(self):
-        """Remove a role assigned to a user on a tenant."""
-        (user, tenant, role) = self._get_role_params()
-        user_role = self.roles_client.create_user_role_on_project(
-            tenant['id'], user['id'], role['id'])['role']
-        self.roles_client.delete_role_from_user_on_project(tenant['id'],
-                                                           user['id'],
-                                                           user_role['id'])
-
-    @decorators.idempotent_id('262e1e3e-ed71-4edd-a0e5-d64e83d66d05')
-    def test_list_user_roles(self):
-        """List roles assigned to a user on tenant."""
-        (user, tenant, role) = self._get_role_params()
-        self.roles_client.create_user_role_on_project(tenant['id'],
-                                                      user['id'],
-                                                      role['id'])
-        roles = self.roles_client.list_user_roles_on_project(
-            tenant['id'], user['id'])['roles']
-        self.assert_role_in_role_list(role, roles)
diff --git a/tempest/api/identity/admin/v2/test_roles_negative.py b/tempest/api/identity/admin/v2/test_roles_negative.py
deleted file mode 100644
index 0f0466e..0000000
--- a/tempest/api/identity/admin/v2/test_roles_negative.py
+++ /dev/null
@@ -1,296 +0,0 @@
-# Copyright 2013 Huawei Technologies Co.,LTD.
-# All Rights Reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
-#    not use this file except in compliance with the License. You may obtain
-#    a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-#    License for the specific language governing permissions and limitations
-#    under the License.
-
-from tempest.api.identity import base
-from tempest import config
-from tempest.lib.common.utils import data_utils
-from tempest.lib import decorators
-from tempest.lib import exceptions as lib_exc
-
-CONF = config.CONF
-
-
-class RolesNegativeTestJSON(base.BaseIdentityV2AdminTest):
-    """Negative tests of keystone roles via v2 API"""
-
-    def _get_role_params(self):
-        user = self.setup_test_user()
-        tenant = self.tenants_client.show_tenant(user['tenantId'])['tenant']
-        role = self.setup_test_role()
-        return (user, tenant, role)
-
-    @decorators.attr(type=['negative'])
-    @decorators.idempotent_id('d5d5f1df-f8ca-4de0-b2ef-259c1cc67025')
-    def test_list_roles_by_unauthorized_user(self):
-        """Test Non-admin user should not be able to list roles via v2 API"""
-        self.assertRaises(lib_exc.Forbidden,
-                          self.non_admin_roles_client.list_roles)
-
-    @decorators.attr(type=['negative'])
-    @decorators.idempotent_id('11a3c7da-df6c-40c2-abc2-badd682edf9f')
-    def test_list_roles_request_without_token(self):
-        """Test listing roles without a valid token via v2 API should fail"""
-        token = self.client.auth_provider.get_token()
-        self.client.delete_token(token)
-        self.assertRaises(lib_exc.Unauthorized, self.roles_client.list_roles)
-        self.client.auth_provider.clear_auth()
-
-    @decorators.attr(type=['negative'])
-    @decorators.idempotent_id('c0b89e56-accc-4c73-85f8-9c0f866104c1')
-    def test_role_create_blank_name(self):
-        """Test creating a role with a blank name via v2 API is not allowed"""
-        self.assertRaises(lib_exc.BadRequest, self.roles_client.create_role,
-                          name='')
-
-    @decorators.attr(type=['negative'])
-    @decorators.idempotent_id('585c8998-a8a4-4641-a5dd-abef7a8ced00')
-    def test_create_role_by_unauthorized_user(self):
-        """Test non-admin user should not be able to create role via v2 API"""
-        role_name = data_utils.rand_name(
-            name='role', prefix=CONF.resource_name_prefix)
-        self.assertRaises(lib_exc.Forbidden,
-                          self.non_admin_roles_client.create_role,
-                          name=role_name)
-
-    @decorators.attr(type=['negative'])
-    @decorators.idempotent_id('a7edd17a-e34a-4aab-8bb7-fa6f498645b8')
-    def test_create_role_request_without_token(self):
-        """Test creating role without a valid token via v2 API should fail"""
-        token = self.client.auth_provider.get_token()
-        self.client.delete_token(token)
-        role_name = data_utils.rand_name(
-            name='role', prefix=CONF.resource_name_prefix)
-        self.assertRaises(lib_exc.Unauthorized,
-                          self.roles_client.create_role, name=role_name)
-        self.client.auth_provider.clear_auth()
-
-    @decorators.attr(type=['negative'])
-    @decorators.idempotent_id('c0cde2c8-81c1-4bb0-8fe2-cf615a3547a8')
-    def test_role_create_duplicate(self):
-        """Test role names should be unique via v2 API"""
-        role_name = data_utils.rand_name(
-            name='role-dup', prefix=CONF.resource_name_prefix)
-        body = self.roles_client.create_role(name=role_name)['role']
-        role1_id = body.get('id')
-        self.addCleanup(self.roles_client.delete_role, role1_id)
-        self.assertRaises(lib_exc.Conflict, self.roles_client.create_role,
-                          name=role_name)
-
-    @decorators.attr(type=['negative'])
-    @decorators.idempotent_id('15347635-b5b1-4a87-a280-deb2bd6d865e')
-    def test_delete_role_by_unauthorized_user(self):
-        """Test non-admin user should not be able to delete role via v2 API"""
-        role_name = data_utils.rand_name(
-            name='role', prefix=CONF.resource_name_prefix)
-        body = self.roles_client.create_role(name=role_name)['role']
-        self.addCleanup(self.roles_client.delete_role, body['id'])
-        role_id = body.get('id')
-        self.assertRaises(lib_exc.Forbidden,
-                          self.non_admin_roles_client.delete_role, role_id)
-
-    @decorators.attr(type=['negative'])
-    @decorators.idempotent_id('44b60b20-70de-4dac-beaf-a3fc2650a16b')
-    def test_delete_role_request_without_token(self):
-        """Test deleting role without a valid token via v2 API should fail"""
-        role_name = data_utils.rand_name(
-            name='role', prefix=CONF.resource_name_prefix)
-        body = self.roles_client.create_role(name=role_name)['role']
-        self.addCleanup(self.roles_client.delete_role, body['id'])
-        role_id = body.get('id')
-        token = self.client.auth_provider.get_token()
-        self.client.delete_token(token)
-        self.assertRaises(lib_exc.Unauthorized,
-                          self.roles_client.delete_role,
-                          role_id)
-        self.client.auth_provider.clear_auth()
-
-    @decorators.attr(type=['negative'])
-    @decorators.idempotent_id('38373691-8551-453a-b074-4260ad8298ef')
-    def test_delete_role_non_existent(self):
-        """Test deleting a non existent role via v2 API should fail"""
-        non_existent_role = data_utils.rand_uuid_hex()
-        self.assertRaises(lib_exc.NotFound, self.roles_client.delete_role,
-                          non_existent_role)
-
-    @decorators.attr(type=['negative'])
-    @decorators.idempotent_id('391df5cf-3ec3-46c9-bbe5-5cb58dd4dc41')
-    def test_assign_user_role_by_unauthorized_user(self):
-        """Test non-admin user assigning a role to user via v2 API
-
-        Non-admin user should not be authorized to assign a role to user via
-        v2 API.
-        """
-        (user, tenant, role) = self._get_role_params()
-        self.assertRaises(
-            lib_exc.Forbidden,
-            self.non_admin_roles_client.create_user_role_on_project,
-            tenant['id'], user['id'], role['id'])
-
-    @decorators.attr(type=['negative'])
-    @decorators.idempotent_id('f0d2683c-5603-4aee-95d7-21420e87cfd8')
-    def test_assign_user_role_request_without_token(self):
-        """Test assigning a role to a user without a valid token via v2 API
-
-        Assigning a role to a user without a valid token via v2 API should
-        fail.
-        """
-        (user, tenant, role) = self._get_role_params()
-        token = self.client.auth_provider.get_token()
-        self.client.delete_token(token)
-        self.assertRaises(
-            lib_exc.Unauthorized,
-            self.roles_client.create_user_role_on_project, tenant['id'],
-            user['id'], role['id'])
-        self.client.auth_provider.clear_auth()
-
-    @decorators.attr(type=['negative'])
-    @decorators.idempotent_id('99b297f6-2b5d-47c7-97a9-8b6bb4f91042')
-    def test_assign_user_role_for_non_existent_role(self):
-        """Test assigning a non existent role to user via v2 API
-
-        Assigning a non existent role to user via v2 API should fail.
-        """
-        (user, tenant, _) = self._get_role_params()
-        non_existent_role = data_utils.rand_uuid_hex()
-        self.assertRaises(lib_exc.NotFound,
-                          self.roles_client.create_user_role_on_project,
-                          tenant['id'], user['id'], non_existent_role)
-
-    @decorators.attr(type=['negative'])
-    @decorators.idempotent_id('b2285aaa-9e76-4704-93a9-7a8acd0a6c8f')
-    def test_assign_user_role_for_non_existent_tenant(self):
-        """Test assigning a role on a non existent tenant via v2 API
-
-        Assigning a role on a non existent tenant via v2 API should fail.
-        """
-        (user, _, role) = self._get_role_params()
-        non_existent_tenant = data_utils.rand_uuid_hex()
-        self.assertRaises(lib_exc.NotFound,
-                          self.roles_client.create_user_role_on_project,
-                          non_existent_tenant, user['id'], role['id'])
-
-    @decorators.attr(type=['negative'])
-    @decorators.idempotent_id('5c3132cd-c4c8-4402-b5ea-71eb44e97793')
-    def test_assign_duplicate_user_role(self):
-        """Test duplicate user role should not get assigned via v2 API"""
-        (user, tenant, role) = self._get_role_params()
-        self.roles_client.create_user_role_on_project(tenant['id'],
-                                                      user['id'],
-                                                      role['id'])
-        self.assertRaises(lib_exc.Conflict,
-                          self.roles_client.create_user_role_on_project,
-                          tenant['id'], user['id'], role['id'])
-
-    @decorators.attr(type=['negative'])
-    @decorators.idempotent_id('d0537987-0977-448f-a435-904c15de7298')
-    def test_remove_user_role_by_unauthorized_user(self):
-        """Test non-admin user removing a user's role via v2 API
-
-        Non-admin user should not be authorized to remove a user's role via
-        v2 API
-        """
-        (user, tenant, role) = self._get_role_params()
-        self.roles_client.create_user_role_on_project(tenant['id'],
-                                                      user['id'],
-                                                      role['id'])
-        self.assertRaises(
-            lib_exc.Forbidden,
-            self.non_admin_roles_client.delete_role_from_user_on_project,
-            tenant['id'], user['id'], role['id'])
-
-    @decorators.attr(type=['negative'])
-    @decorators.idempotent_id('cac81cf4-c1d2-47dc-90d3-f2b7eb572286')
-    def test_remove_user_role_request_without_token(self):
-        """Test removing a user's role without a valid token via v2 API
-
-        Removing a user's role without a valid token via v2 API should fail.
-        """
-        (user, tenant, role) = self._get_role_params()
-        self.roles_client.create_user_role_on_project(tenant['id'],
-                                                      user['id'],
-                                                      role['id'])
-        token = self.client.auth_provider.get_token()
-        self.client.delete_token(token)
-        self.assertRaises(lib_exc.Unauthorized,
-                          self.roles_client.delete_role_from_user_on_project,
-                          tenant['id'], user['id'], role['id'])
-        self.client.auth_provider.clear_auth()
-
-    @decorators.attr(type=['negative'])
-    @decorators.idempotent_id('ab32d759-cd16-41f1-a86e-44405fa9f6d2')
-    def test_remove_user_role_non_existent_role(self):
-        """Test deleting a non existent role from a user via v2 API
-
-        Deleting a non existent role from a user via v2 API should fail.
-        """
-        (user, tenant, role) = self._get_role_params()
-        self.roles_client.create_user_role_on_project(tenant['id'],
-                                                      user['id'],
-                                                      role['id'])
-        non_existent_role = data_utils.rand_uuid_hex()
-        self.assertRaises(lib_exc.NotFound,
-                          self.roles_client.delete_role_from_user_on_project,
-                          tenant['id'], user['id'], non_existent_role)
-
-    @decorators.attr(type=['negative'])
-    @decorators.idempotent_id('67a679ec-03dd-4551-bbfc-d1c93284f023')
-    def test_remove_user_role_non_existent_tenant(self):
-        """Test removing a role from a non existent tenant via v2 API
-
-        Removing a role from a non existent tenant via v2 API should fail.
-        """
-        (user, tenant, role) = self._get_role_params()
-        self.roles_client.create_user_role_on_project(tenant['id'],
-                                                      user['id'],
-                                                      role['id'])
-        non_existent_tenant = data_utils.rand_uuid_hex()
-        self.assertRaises(lib_exc.NotFound,
-                          self.roles_client.delete_role_from_user_on_project,
-                          non_existent_tenant, user['id'], role['id'])
-
-    @decorators.attr(type=['negative'])
-    @decorators.idempotent_id('7391ab4c-06f3-477a-a64a-c8e55ce89837')
-    def test_list_user_roles_by_unauthorized_user(self):
-        """Test non-admin user listing a user's roles via v2 API
-
-        Non-admin user should not be authorized to list a user's roles via v2
-        API.
-        """
-        (user, tenant, role) = self._get_role_params()
-        self.roles_client.create_user_role_on_project(tenant['id'],
-                                                      user['id'],
-                                                      role['id'])
-        self.assertRaises(
-            lib_exc.Forbidden,
-            self.non_admin_roles_client.list_user_roles_on_project,
-            tenant['id'], user['id'])
-
-    @decorators.attr(type=['negative'])
-    @decorators.idempotent_id('682adfb2-fd5f-4b0a-a9ca-322e9bebb907')
-    def test_list_user_roles_request_without_token(self):
-        """Test listing user's roles without a valid token via v2 API
-
-        Listing user's roles without a valid token via v2 API should fail
-        """
-        (user, tenant, _) = self._get_role_params()
-        token = self.client.auth_provider.get_token()
-        self.client.delete_token(token)
-        try:
-            self.assertRaises(lib_exc.Unauthorized,
-                              self.roles_client.list_user_roles_on_project,
-                              tenant['id'],
-                              user['id'])
-        finally:
-            self.client.auth_provider.clear_auth()
diff --git a/tempest/api/identity/admin/v2/test_services.py b/tempest/api/identity/admin/v2/test_services.py
deleted file mode 100644
index 0e5d378..0000000
--- a/tempest/api/identity/admin/v2/test_services.py
+++ /dev/null
@@ -1,115 +0,0 @@
-# Copyright 2012 OpenStack Foundation
-# All Rights Reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
-#    not use this file except in compliance with the License. You may obtain
-#    a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-#    License for the specific language governing permissions and limitations
-#    under the License.
-
-from tempest.api.identity import base
-from tempest import config
-from tempest.lib.common.utils import data_utils
-from tempest.lib import decorators
-from tempest.lib import exceptions as lib_exc
-
-CONF = config.CONF
-
-
-class ServicesTestJSON(base.BaseIdentityV2AdminTest):
-    """Test identity services via v2 API"""
-
-    def _del_service(self, service_id):
-        # Deleting the service created in this method
-        self.services_client.delete_service(service_id)
-        # Checking whether service is deleted successfully
-        self.assertRaises(lib_exc.NotFound, self.services_client.show_service,
-                          service_id)
-
-    @decorators.idempotent_id('84521085-c6e6-491c-9a08-ec9f70f90110')
-    def test_create_get_delete_service(self):
-        """Test verifies the identity service create/get/delete via v2 API"""
-        # GET Service
-        # Creating a Service
-        name = data_utils.rand_name(
-            name='service', prefix=CONF.resource_name_prefix)
-        s_type = data_utils.rand_name(
-            name='type', prefix=CONF.resource_name_prefix)
-        description = data_utils.rand_name(
-            name='description', prefix=CONF.resource_name_prefix)
-        service_data = self.services_client.create_service(
-            name=name, type=s_type,
-            description=description)['OS-KSADM:service']
-        self.assertIsNotNone(service_data['id'])
-        self.addCleanup(self._del_service, service_data['id'])
-        # Verifying response body of create service
-        self.assertIn('name', service_data)
-        self.assertEqual(name, service_data['name'])
-        self.assertIn('type', service_data)
-        self.assertEqual(s_type, service_data['type'])
-        self.assertIn('description', service_data)
-        self.assertEqual(description, service_data['description'])
-        # Get service
-        fetched_service = (
-            self.services_client.show_service(service_data['id'])
-            ['OS-KSADM:service'])
-        # verifying the existence of service created
-        self.assertIn('id', fetched_service)
-        self.assertEqual(fetched_service['id'], service_data['id'])
-        self.assertIn('name', fetched_service)
-        self.assertEqual(fetched_service['name'], service_data['name'])
-        self.assertIn('type', fetched_service)
-        self.assertEqual(fetched_service['type'], service_data['type'])
-        self.assertIn('description', fetched_service)
-        self.assertEqual(fetched_service['description'],
-                         service_data['description'])
-
-    @decorators.idempotent_id('5d3252c8-e555-494b-a6c8-e11d7335da42')
-    def test_create_service_without_description(self):
-        """Test creating identity service without description via v2 API
-
-        Create a service only with name and type.
-        """
-        name = data_utils.rand_name(
-            name='service', prefix=CONF.resource_name_prefix)
-        s_type = data_utils.rand_name(
-            name='type', prefix=CONF.resource_name_prefix)
-        service = self.services_client.create_service(
-            name=name, type=s_type)['OS-KSADM:service']
-        self.assertIn('id', service)
-        self.addCleanup(self._del_service, service['id'])
-        self.assertIn('name', service)
-        self.assertEqual(name, service['name'])
-        self.assertIn('type', service)
-        self.assertEqual(s_type, service['type'])
-
-    @decorators.attr(type='smoke')
-    @decorators.idempotent_id('34ea6489-012d-4a86-9038-1287cadd5eca')
-    def test_list_services(self):
-        """Test Create/List/Verify/Delete of identity service via v2 API"""
-        services = []
-        for _ in range(3):
-            name = data_utils.rand_name(
-                name='service', prefix=CONF.resource_name_prefix)
-            s_type = data_utils.rand_name(
-                name='type', prefix=CONF.resource_name_prefix)
-            description = data_utils.rand_name(
-                name='description', prefix=CONF.resource_name_prefix)
-
-            service = self.services_client.create_service(
-                name=name, type=s_type,
-                description=description)['OS-KSADM:service']
-            self.addCleanup(self.services_client.delete_service, service['id'])
-            services.append(service)
-        service_ids = [svc['id'] for svc in services]
-
-        # List and Verify Services
-        body = self.services_client.list_services()['OS-KSADM:services']
-        found = [serv for serv in body if serv['id'] in service_ids]
-        self.assertEqual(len(found), len(services), 'Services not found')
diff --git a/tempest/api/identity/admin/v2/test_tenant_negative.py b/tempest/api/identity/admin/v2/test_tenant_negative.py
deleted file mode 100644
index 4c7c44c..0000000
--- a/tempest/api/identity/admin/v2/test_tenant_negative.py
+++ /dev/null
@@ -1,164 +0,0 @@
-# Copyright 2013 Huawei Technologies Co.,LTD.
-# All Rights Reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
-#    not use this file except in compliance with the License. You may obtain
-#    a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-#    License for the specific language governing permissions and limitations
-#    under the License.
-
-from tempest.api.identity import base
-from tempest import config
-from tempest.lib.common.utils import data_utils
-from tempest.lib import decorators
-from tempest.lib import exceptions as lib_exc
-
-CONF = config.CONF
-
-
-class TenantsNegativeTestJSON(base.BaseIdentityV2AdminTest):
-    """Negative tests of keystone tenants via v2 API"""
-
-    @decorators.attr(type=['negative'])
-    @decorators.idempotent_id('ca9bb202-63dd-4240-8a07-8ef9c19c04bb')
-    def test_list_tenants_by_unauthorized_user(self):
-        """Test Non-admin should not be able to list tenants via v2 API"""
-        self.assertRaises(lib_exc.Forbidden,
-                          self.non_admin_tenants_client.list_tenants)
-
-    @decorators.attr(type=['negative'])
-    @decorators.idempotent_id('df33926c-1c96-4d8d-a762-79cc6b0c3cf4')
-    def test_list_tenant_request_without_token(self):
-        """Test listing tenants without a valid token via v2 API
-
-        Listing tenants without a valid token via v2 API should fail.
-        """
-        token = self.client.auth_provider.get_token()
-        self.client.delete_token(token)
-        self.assertRaises(lib_exc.Unauthorized,
-                          self.tenants_client.list_tenants)
-        self.client.auth_provider.clear_auth()
-
-    @decorators.attr(type=['negative'])
-    @decorators.idempotent_id('162ba316-f18b-4987-8c0c-fd9140cd63ed')
-    def test_tenant_delete_by_unauthorized_user(self):
-        """Test non-admin should not be able to delete a tenant via v2 API"""
-        tenant = self.setup_test_tenant()
-        self.assertRaises(lib_exc.Forbidden,
-                          self.non_admin_tenants_client.delete_tenant,
-                          tenant['id'])
-
-    @decorators.attr(type=['negative'])
-    @decorators.idempotent_id('e450db62-2e9d-418f-893a-54772d6386b1')
-    def test_tenant_delete_request_without_token(self):
-        """Test deleting a tenant without a valid token via v2 API
-
-        Deleting a tenant without a valid token via v2 API should fail.
-        """
-        tenant = self.setup_test_tenant()
-        token = self.client.auth_provider.get_token()
-        self.client.delete_token(token)
-        self.assertRaises(lib_exc.Unauthorized,
-                          self.tenants_client.delete_tenant,
-                          tenant['id'])
-        self.client.auth_provider.clear_auth()
-
-    @decorators.attr(type=['negative'])
-    @decorators.idempotent_id('9c9a2aed-6e3c-467a-8f5c-89da9d1b516b')
-    def test_delete_non_existent_tenant(self):
-        """Test deleting a non existent tenant via v2 API should fail"""
-        self.assertRaises(lib_exc.NotFound, self.tenants_client.delete_tenant,
-                          data_utils.rand_uuid_hex())
-
-    @decorators.attr(type=['negative'])
-    @decorators.idempotent_id('af16f44b-a849-46cb-9f13-a751c388f739')
-    def test_tenant_create_duplicate(self):
-        """Test tenant names should be unique via v2 API"""
-        tenant_name = data_utils.rand_name(
-            name='tenant', prefix=CONF.resource_name_prefix)
-        self.setup_test_tenant(name=tenant_name)
-        self.assertRaises(lib_exc.Conflict, self.tenants_client.create_tenant,
-                          name=tenant_name)
-
-    @decorators.attr(type=['negative'])
-    @decorators.idempotent_id('d26b278a-6389-4702-8d6e-5980d80137e0')
-    def test_create_tenant_by_unauthorized_user(self):
-        """Test non-admin user creating a tenant via v2 API
-
-        Non-admin user should not be authorized to create a tenant via v2 API.
-        """
-        tenant_name = data_utils.rand_name(
-            name='tenant', prefix=CONF.resource_name_prefix)
-        self.assertRaises(lib_exc.Forbidden,
-                          self.non_admin_tenants_client.create_tenant,
-                          name=tenant_name)
-
-    @decorators.attr(type=['negative'])
-    @decorators.idempotent_id('a3ee9d7e-6920-4dd5-9321-d4b2b7f0a638')
-    def test_create_tenant_request_without_token(self):
-        """Test creating tenant without a token via v2 API is not allowed"""
-        tenant_name = data_utils.rand_name(
-            name='tenant', prefix=CONF.resource_name_prefix)
-        token = self.client.auth_provider.get_token()
-        self.client.delete_token(token)
-        self.assertRaises(lib_exc.Unauthorized,
-                          self.tenants_client.create_tenant,
-                          name=tenant_name)
-        self.client.auth_provider.clear_auth()
-
-    @decorators.attr(type=['negative'])
-    @decorators.idempotent_id('5a2e4ca9-b0c0-486c-9c48-64a94fba2395')
-    def test_create_tenant_with_empty_name(self):
-        """Test tenant name should not be empty via v2 API"""
-        self.assertRaises(lib_exc.BadRequest,
-                          self.tenants_client.create_tenant,
-                          name='')
-
-    @decorators.attr(type=['negative'])
-    @decorators.idempotent_id('2ff18d1e-dfe3-4359-9dc3-abf582c196b9')
-    def test_create_tenants_name_length_over_64(self):
-        """Test tenant name length should not exceed 64 via v2 API"""
-        tenant_name = 'a' * 65
-        self.assertRaises(lib_exc.BadRequest,
-                          self.tenants_client.create_tenant,
-                          name=tenant_name)
-
-    @decorators.attr(type=['negative'])
-    @decorators.idempotent_id('bd20dc2a-9557-4db7-b755-f48d952ad706')
-    def test_update_non_existent_tenant(self):
-        """Test updating a non existent tenant via v2 API should fail"""
-        self.assertRaises(lib_exc.NotFound, self.tenants_client.update_tenant,
-                          data_utils.rand_uuid_hex())
-
-    @decorators.attr(type=['negative'])
-    @decorators.idempotent_id('41704dc5-c5f7-4f79-abfa-76e6fedc570b')
-    def test_tenant_update_by_unauthorized_user(self):
-        """Test non-admin user updating a tenant via v2 API
-
-        Non-admin user should not be able to update a tenant via v2 API
-        """
-        tenant = self.setup_test_tenant()
-        self.assertRaises(lib_exc.Forbidden,
-                          self.non_admin_tenants_client.update_tenant,
-                          tenant['id'])
-
-    @decorators.attr(type=['negative'])
-    @decorators.idempotent_id('7a421573-72c7-4c22-a98e-ce539219c657')
-    def test_tenant_update_request_without_token(self):
-        """Test updating a tenant without a valid token via v2 API
-
-        Updating a tenant without a valid token via v2 API should fail
-        """
-        tenant = self.setup_test_tenant()
-        token = self.client.auth_provider.get_token()
-        self.client.delete_token(token)
-        self.assertRaises(lib_exc.Unauthorized,
-                          self.tenants_client.update_tenant,
-                          tenant['id'])
-        self.client.auth_provider.clear_auth()
diff --git a/tempest/api/identity/admin/v2/test_tenants.py b/tempest/api/identity/admin/v2/test_tenants.py
deleted file mode 100644
index 4f674a8..0000000
--- a/tempest/api/identity/admin/v2/test_tenants.py
+++ /dev/null
@@ -1,157 +0,0 @@
-# Copyright 2012 OpenStack Foundation
-# All Rights Reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
-#    not use this file except in compliance with the License. You may obtain
-#    a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-#    License for the specific language governing permissions and limitations
-#    under the License.
-
-from tempest.api.identity import base
-from tempest import config
-from tempest.lib.common.utils import data_utils
-from tempest.lib import decorators
-
-CONF = config.CONF
-
-
-class TenantsTestJSON(base.BaseIdentityV2AdminTest):
-    """Test identity tenants via v2 API"""
-
-    @decorators.idempotent_id('16c6e05c-6112-4b0e-b83f-5e43f221b6b0')
-    def test_tenant_list_delete(self):
-        """Test listing and deleting tenants via v2 API
-
-        Create several tenants and delete them
-        """
-        tenants = []
-        for _ in range(3):
-            tenant = self.setup_test_tenant()
-            tenants.append(tenant)
-        tenant_ids = [tn['id'] for tn in tenants]
-        body = self.tenants_client.list_tenants()['tenants']
-        found = [t for t in body if t['id'] in tenant_ids]
-        self.assertEqual(len(found), len(tenants), 'Tenants not created')
-
-        for tenant in tenants:
-            self.tenants_client.delete_tenant(tenant['id'])
-
-        body = self.tenants_client.list_tenants()['tenants']
-        found = [tenant for tenant in body if tenant['id'] in tenant_ids]
-        self.assertEmpty(found, 'Tenants failed to delete')
-
-    @decorators.idempotent_id('d25e9f24-1310-4d29-b61b-d91299c21d6d')
-    def test_tenant_create_with_description(self):
-        """Test creating tenant with a description via v2 API"""
-        tenant_desc = data_utils.rand_name(
-            name='desc', prefix=CONF.resource_name_prefix)
-        tenant = self.setup_test_tenant(description=tenant_desc)
-        tenant_id = tenant['id']
-        desc1 = tenant['description']
-        self.assertEqual(desc1, tenant_desc, 'Description should have '
-                         'been sent in response for create')
-        body = self.tenants_client.show_tenant(tenant_id)['tenant']
-        desc2 = body['description']
-        self.assertEqual(desc2, tenant_desc, 'Description does not appear '
-                         'to be set')
-        self.tenants_client.delete_tenant(tenant_id)
-
-    @decorators.idempotent_id('670bdddc-1cd7-41c7-b8e2-751cfb67df50')
-    def test_tenant_create_enabled(self):
-        """Test creating a tenant that is enabled via v2 API"""
-        tenant = self.setup_test_tenant(enabled=True)
-        tenant_id = tenant['id']
-        self.assertTrue(tenant['enabled'], 'Enable should be True in response')
-        body = self.tenants_client.show_tenant(tenant_id)['tenant']
-        self.assertTrue(body['enabled'], 'Enable should be True in lookup')
-        self.tenants_client.delete_tenant(tenant_id)
-
-    @decorators.idempotent_id('3be22093-b30f-499d-b772-38340e5e16fb')
-    def test_tenant_create_not_enabled(self):
-        """Test creating a tenant that is not enabled via v2 API"""
-        tenant = self.setup_test_tenant(enabled=False)
-        tenant_id = tenant['id']
-        self.assertFalse(tenant['enabled'],
-                         'Enable should be False in response')
-        body = self.tenants_client.show_tenant(tenant_id)['tenant']
-        self.assertFalse(body['enabled'],
-                         'Enable should be False in lookup')
-        self.tenants_client.delete_tenant(tenant_id)
-
-    @decorators.idempotent_id('781f2266-d128-47f3-8bdb-f70970add238')
-    def test_tenant_update_name(self):
-        """Test updating name attribute of a tenant via v2 API"""
-        t_name1 = data_utils.rand_name(
-            name='tenant', prefix=CONF.resource_name_prefix)
-        tenant = self.setup_test_tenant(name=t_name1)
-        t_id = tenant['id']
-        resp1_name = tenant['name']
-
-        t_name2 = data_utils.rand_name(
-            name='tenant2', prefix=CONF.resource_name_prefix)
-        body = self.tenants_client.update_tenant(t_id, name=t_name2)['tenant']
-        resp2_name = body['name']
-        self.assertNotEqual(resp1_name, resp2_name)
-
-        body = self.tenants_client.show_tenant(t_id)['tenant']
-        resp3_name = body['name']
-
-        self.assertNotEqual(resp1_name, resp3_name)
-        self.assertEqual(t_name1, resp1_name)
-        self.assertEqual(resp2_name, resp3_name)
-
-        self.tenants_client.delete_tenant(t_id)
-
-    @decorators.idempotent_id('859fcfe1-3a03-41ef-86f9-b19a47d1cd87')
-    def test_tenant_update_desc(self):
-        """Test updating description attribute of a tenant via v2 API"""
-        t_desc = data_utils.rand_name(
-            name='desc', prefix=CONF.resource_name_prefix)
-        tenant = self.setup_test_tenant(description=t_desc)
-        t_id = tenant['id']
-        resp1_desc = tenant['description']
-
-        t_desc2 = data_utils.rand_name(
-            name='desc2', prefix=CONF.resource_name_prefix)
-        body = self.tenants_client.update_tenant(t_id, description=t_desc2)
-        updated_tenant = body['tenant']
-        resp2_desc = updated_tenant['description']
-        self.assertNotEqual(resp1_desc, resp2_desc)
-
-        body = self.tenants_client.show_tenant(t_id)['tenant']
-        resp3_desc = body['description']
-
-        self.assertNotEqual(resp1_desc, resp3_desc)
-        self.assertEqual(t_desc, resp1_desc)
-        self.assertEqual(resp2_desc, resp3_desc)
-
-        self.tenants_client.delete_tenant(t_id)
-
-    @decorators.idempotent_id('8fc8981f-f12d-4c66-9972-2bdcf2bc2e1a')
-    def test_tenant_update_enable(self):
-        """Test updating the enabled attribute of a tenant via v2 API"""
-        t_en = False
-        tenant = self.setup_test_tenant(enabled=t_en)
-        t_id = tenant['id']
-        resp1_en = tenant['enabled']
-
-        t_en2 = True
-        body = self.tenants_client.update_tenant(t_id, enabled=t_en2)
-        updated_tenant = body['tenant']
-        resp2_en = updated_tenant['enabled']
-        self.assertNotEqual(resp1_en, resp2_en)
-
-        body = self.tenants_client.show_tenant(t_id)['tenant']
-        resp3_en = body['enabled']
-
-        self.assertNotEqual(resp1_en, resp3_en)
-        self.assertFalse(tenant['enabled'])
-        self.assertEqual(resp2_en, resp3_en)
-
-        self.tenants_client.delete_tenant(t_id)
diff --git a/tempest/api/identity/admin/v2/test_tokens.py b/tempest/api/identity/admin/v2/test_tokens.py
deleted file mode 100644
index 78a2aad..0000000
--- a/tempest/api/identity/admin/v2/test_tokens.py
+++ /dev/null
@@ -1,143 +0,0 @@
-# Copyright 2013 Huawei Technologies Co.,LTD.
-# All Rights Reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
-#    not use this file except in compliance with the License. You may obtain
-#    a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-#    License for the specific language governing permissions and limitations
-#    under the License.
-
-from tempest.api.identity import base
-from tempest import config
-from tempest.lib.common.utils import data_utils
-from tempest.lib import decorators
-from tempest.lib import exceptions as lib_exc
-
-CONF = config.CONF
-
-
-class TokensTestJSON(base.BaseIdentityV2AdminTest):
-    """Test keystone tokens via v2 API"""
-
-    @decorators.idempotent_id('453ad4d5-e486-4b2f-be72-cffc8149e586')
-    def test_create_check_get_delete_token(self):
-        """Test getting create/check/get/delete token for user via v2 API"""
-        # get a token by username and password
-        user_name = data_utils.rand_name(
-            name='user', prefix=CONF.resource_name_prefix)
-        user_password = data_utils.rand_password()
-        # first:create a tenant
-        tenant = self.setup_test_tenant()
-        # second:create a user
-        user = self.create_test_user(name=user_name,
-                                     password=user_password,
-                                     tenantId=tenant['id'],
-                                     email='')
-        # then get a token for the user
-        body = self.token_client.auth(user_name,
-                                      user_password,
-                                      tenant['name'])
-        self.assertEqual(body['token']['tenant']['name'],
-                         tenant['name'])
-        # Perform GET Token
-        token_id = body['token']['id']
-        self.client.check_token_existence(token_id)
-        token_details = self.client.show_token(token_id)['access']
-        self.assertEqual(token_id, token_details['token']['id'])
-        self.assertEqual(user['id'], token_details['user']['id'])
-        self.assertEqual(user_name, token_details['user']['name'])
-        self.assertEqual(tenant['name'],
-                         token_details['token']['tenant']['name'])
-        # then delete the token
-        self.client.delete_token(token_id)
-        self.assertRaises(lib_exc.NotFound,
-                          self.client.check_token_existence,
-                          token_id)
-
-    @decorators.idempotent_id('25ba82ee-8a32-4ceb-8f50-8b8c71e8765e')
-    def test_rescope_token(self):
-        """Test an unscoped token can be requested via v2 API
-
-        That token can be used to request a scoped token.
-        """
-
-        # Create a user.
-        user_name = data_utils.rand_name(
-            name='user', prefix=CONF.resource_name_prefix)
-        user_password = data_utils.rand_password()
-        tenant_id = None  # No default tenant so will get unscoped token.
-        user = self.create_test_user(name=user_name,
-                                     password=user_password,
-                                     tenantId=tenant_id,
-                                     email='')
-
-        # Create a couple tenants.
-        tenant1_name = data_utils.rand_name(
-            name='tenant', prefix=CONF.resource_name_prefix)
-        tenant1 = self.setup_test_tenant(name=tenant1_name)
-
-        tenant2_name = data_utils.rand_name(
-            name='tenant', prefix=CONF.resource_name_prefix)
-        tenant2 = self.setup_test_tenant(name=tenant2_name)
-
-        # Create a role
-        role = self.setup_test_role()
-
-        # Grant the user the role on the tenants.
-        self.roles_client.create_user_role_on_project(tenant1['id'],
-                                                      user['id'],
-                                                      role['id'])
-
-        self.roles_client.create_user_role_on_project(tenant2['id'],
-                                                      user['id'],
-                                                      role['id'])
-
-        # Get an unscoped token.
-        body = self.token_client.auth(user_name, user_password)
-
-        token_id = body['token']['id']
-
-        # Use the unscoped token to get a token scoped to tenant1
-        body = self.token_client.auth_token(token_id,
-                                            tenant=tenant1_name)
-
-        scoped_token_id = body['token']['id']
-
-        # Revoke the scoped token
-        self.client.delete_token(scoped_token_id)
-
-        # Use the unscoped token to get a token scoped to tenant2
-        body = self.token_client.auth_token(token_id,
-                                            tenant=tenant2_name)
-
-    @decorators.idempotent_id('ca3ea6f7-ed08-4a61-adbd-96906456ad31')
-    def test_list_endpoints_for_token(self):
-        """Test listing endpoints for token via v2 API"""
-        tempest_services = ['keystone', 'nova', 'neutron', 'swift', 'cinder',
-                            'neutron']
-        # get a token for the user
-        creds = self.os_primary.credentials
-        username = creds.username
-        password = creds.password
-        tenant_name = creds.tenant_name
-        token = self.token_client.auth(username,
-                                       password,
-                                       tenant_name)['token']
-        endpoints = self.client.list_endpoints_for_token(
-            token['id'])['endpoints']
-        self.assertIsInstance(endpoints, list)
-        # Store list of service names
-        service_names = [e['name'] for e in endpoints]
-        # Get the list of available services. Keystone is always available.
-        available_services = [s[0] for s in list(
-            CONF.service_available.items()) if s[1] is True] + ['keystone']
-        # Verify that all available services are present.
-        for service in tempest_services:
-            if service in available_services:
-                self.assertIn(service, service_names)
diff --git a/tempest/api/identity/admin/v2/test_tokens_negative.py b/tempest/api/identity/admin/v2/test_tokens_negative.py
deleted file mode 100644
index f2e41ff..0000000
--- a/tempest/api/identity/admin/v2/test_tokens_negative.py
+++ /dev/null
@@ -1,43 +0,0 @@
-# Copyright 2017 AT&T Corporation
-# All Rights Reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
-#    not use this file except in compliance with the License. You may obtain
-#    a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-#    License for the specific language governing permissions and limitations
-#    under the License.
-
-from tempest.api.identity import base
-from tempest.lib import decorators
-from tempest.lib import exceptions as lib_exc
-
-
-class TokensAdminTestNegative(base.BaseIdentityV2AdminTest):
-    """Negative tests of keystone tokens via v2 API"""
-
-    credentials = ['primary', 'admin', 'alt']
-
-    @decorators.attr(type=['negative'])
-    @decorators.idempotent_id('a0a0a600-4292-4364-99c5-922c834fdf05')
-    def test_check_token_existence_negative(self):
-        """Test checking other tenant's token existence via v2 API
-
-        Checking other tenant's token existence via v2 API should fail.
-        """
-        creds = self.os_primary.credentials
-        creds_alt = self.os_alt.credentials
-        username = creds.username
-        password = creds.password
-        tenant_name = creds.tenant_name
-        alt_tenant_name = creds_alt.tenant_name
-        body = self.token_client.auth(username, password, tenant_name)
-        self.assertRaises(lib_exc.Unauthorized,
-                          self.client.check_token_existence,
-                          body['token']['id'],
-                          belongsTo=alt_tenant_name)
diff --git a/tempest/api/identity/admin/v2/test_users.py b/tempest/api/identity/admin/v2/test_users.py
deleted file mode 100644
index 011419e..0000000
--- a/tempest/api/identity/admin/v2/test_users.py
+++ /dev/null
@@ -1,204 +0,0 @@
-# Copyright 2012 OpenStack Foundation
-# All Rights Reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
-#    not use this file except in compliance with the License. You may obtain
-#    a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-#    License for the specific language governing permissions and limitations
-#    under the License.
-
-import time
-
-from testtools import matchers
-
-from tempest.api.identity import base
-from tempest import config
-from tempest.lib.common.utils import data_utils
-from tempest.lib import decorators
-
-CONF = config.CONF
-
-
-class UsersTestJSON(base.BaseIdentityV2AdminTest):
-    """Test keystone users via v2 API"""
-
-    @classmethod
-    def resource_setup(cls):
-        super(UsersTestJSON, cls).resource_setup()
-        cls.alt_user = data_utils.rand_name(
-            name='test_user', prefix=CONF.resource_name_prefix)
-        cls.alt_email = cls.alt_user + '@testmail.tm'
-
-    @decorators.attr(type='smoke')
-    @decorators.idempotent_id('2d55a71e-da1d-4b43-9c03-d269fd93d905')
-    def test_create_user(self):
-        """Test creating a user via v2 API"""
-        tenant = self.setup_test_tenant()
-        user = self.create_test_user(name=self.alt_user, tenantId=tenant['id'])
-        self.assertEqual(self.alt_user, user['name'])
-
-    @decorators.idempotent_id('89d9fdb8-15c2-4304-a429-48715d0af33d')
-    def test_create_user_with_enabled(self):
-        """Test creating a user with enabled : False via v2 API"""
-        tenant = self.setup_test_tenant()
-        name = data_utils.rand_name(
-            name='test_user', prefix=CONF.resource_name_prefix)
-        user = self.create_test_user(name=name,
-                                     tenantId=tenant['id'],
-                                     email=self.alt_email,
-                                     enabled=False)
-        self.assertEqual(name, user['name'])
-        self.assertEqual(False, user['enabled'])
-        self.assertEqual(self.alt_email, user['email'])
-
-    @decorators.idempotent_id('39d05857-e8a5-4ed4-ba83-0b52d3ab97ee')
-    def test_update_user(self):
-        """Test updating user attributes via v2 API"""
-        tenant = self.setup_test_tenant()
-        user = self.create_test_user(tenantId=tenant['id'])
-
-        # Updating user details with new values
-        u_name2 = data_utils.rand_name(
-            name='user2', prefix=CONF.resource_name_prefix)
-        u_email2 = u_name2 + '@testmail.tm'
-        update_user = self.users_client.update_user(user['id'], name=u_name2,
-                                                    email=u_email2,
-                                                    enabled=False)['user']
-        self.assertEqual(u_name2, update_user['name'])
-        self.assertEqual(u_email2, update_user['email'])
-        self.assertEqual(False, update_user['enabled'])
-        # GET by id after updating
-        updated_user = self.users_client.show_user(user['id'])['user']
-        # Assert response body of GET after updating
-        self.assertEqual(u_name2, updated_user['name'])
-        self.assertEqual(u_email2, updated_user['email'])
-        self.assertEqual(False, update_user['enabled'])
-
-    @decorators.idempotent_id('29ed26f4-a74e-4425-9a85-fdb49fa269d2')
-    def test_delete_user(self):
-        """Test deleting a user via v2 API"""
-        tenant = self.setup_test_tenant()
-        user = self.create_test_user(tenantId=tenant['id'])
-        self.users_client.delete_user(user['id'])
-
-    @decorators.idempotent_id('aca696c3-d645-4f45-b728-63646045beb1')
-    def test_user_authentication(self):
-        """Test that valid user's token is authenticated via v2 API"""
-        password = data_utils.rand_password()
-        user = self.setup_test_user(password)
-        tenant = self.tenants_client.show_tenant(user['tenantId'])['tenant']
-        # Get a token
-        self.token_client.auth(user['name'],
-                               password,
-                               tenant['name'])
-        # Re-auth
-        self.token_client.auth(user['name'],
-                               password,
-                               tenant['name'])
-
-    @decorators.idempotent_id('5d1fa498-4c2d-4732-a8fe-2b054598cfdd')
-    def test_authentication_request_without_token(self):
-        """Test authentication request without token via v2 API"""
-        # Request for token authentication with a valid token in header
-        password = data_utils.rand_password()
-        user = self.setup_test_user(password)
-        tenant = self.tenants_client.show_tenant(user['tenantId'])['tenant']
-        self.token_client.auth(user['name'],
-                               password,
-                               tenant['name'])
-        # Get the token of the current client
-        token = self.client.auth_provider.get_token()
-        # Delete the token from database
-        self.client.delete_token(token)
-        # Re-auth
-        self.token_client.auth(user['name'],
-                               password,
-                               tenant['name'])
-        self.client.auth_provider.clear_auth()
-
-    @decorators.idempotent_id('a149c02e-e5e0-4b89-809e-7e8faf33ccda')
-    def test_get_users(self):
-        """Test getting users via v2 API
-
-        Get a list of users and find the test user
-        """
-        user = self.setup_test_user()
-        users = self.users_client.list_users()['users']
-        self.assertThat([u['name'] for u in users],
-                        matchers.Contains(user['name']),
-                        "Could not find %s" % user['name'])
-
-    @decorators.idempotent_id('6e317209-383a-4bed-9f10-075b7c82c79a')
-    def test_list_users_for_tenant(self):
-        """Test returning a list of all users for a tenant via v2 API"""
-        tenant = self.setup_test_tenant()
-        user_ids = list()
-        fetched_user_ids = list()
-        user1 = self.create_test_user(tenantId=tenant['id'])
-        user_ids.append(user1['id'])
-        user2 = self.create_test_user(tenantId=tenant['id'])
-        user_ids.append(user2['id'])
-        # List of users for the respective tenant ID
-        body = (self.tenants_client.list_tenant_users(tenant['id'])
-                ['users'])
-        for i in body:
-            fetched_user_ids.append(i['id'])
-        # verifying the user Id in the list
-        missing_users =\
-            [user for user in user_ids if user not in fetched_user_ids]
-        self.assertEmpty(missing_users,
-                         "Failed to find user %s in fetched list" %
-                         ', '.join(m_user for m_user in missing_users))
-
-    @decorators.idempotent_id('a8b54974-40e1-41c0-b812-50fc90827971')
-    def test_list_users_with_roles_for_tenant(self):
-        """Test listing users on tenant with roles assigned via v2 API"""
-        user = self.setup_test_user()
-        tenant = self.tenants_client.show_tenant(user['tenantId'])['tenant']
-        role = self.setup_test_role()
-        # Assigning roles to two users
-        user_ids = list()
-        fetched_user_ids = list()
-        user_ids.append(user['id'])
-        role = self.roles_client.create_user_role_on_project(
-            tenant['id'], user['id'], role['id'])['role']
-
-        second_user = self.create_test_user(tenantId=tenant['id'])
-        user_ids.append(second_user['id'])
-        role = self.roles_client.create_user_role_on_project(
-            tenant['id'], second_user['id'], role['id'])['role']
-        # List of users with roles for the respective tenant ID
-        body = (self.tenants_client.list_tenant_users(tenant['id'])['users'])
-        for i in body:
-            fetched_user_ids.append(i['id'])
-        # verifying the user Id in the list
-        missing_users = [missing_user for missing_user in user_ids
-                         if missing_user not in fetched_user_ids]
-        self.assertEmpty(missing_users,
-                         "Failed to find user %s in fetched list" %
-                         ', '.join(m_user for m_user in missing_users))
-
-    @decorators.idempotent_id('1aeb25ac-6ec5-4d8b-97cb-7ac3567a989f')
-    def test_update_user_password(self):
-        """Test updating of user password via v2 API"""
-        user = self.setup_test_user()
-        tenant = self.tenants_client.show_tenant(user['tenantId'])['tenant']
-        # Updating the user with new password
-        new_pass = data_utils.rand_password()
-        update_user = self.users_client.update_user_password(
-            user['id'], password=new_pass)['user']
-        self.assertEqual(update_user['id'], user['id'])
-        # NOTE(morganfainberg): Fernet tokens are not subsecond aware and
-        # Keystone should only be precise to the second. Sleep to ensure
-        # we are passing the second boundary.
-        time.sleep(1)
-        # Validate the updated password through getting a token.
-        body = self.token_client.auth(user['name'], new_pass,
-                                      tenant['name'])
-        self.assertIn('id', body['token'])
diff --git a/tempest/api/identity/admin/v2/test_users_negative.py b/tempest/api/identity/admin/v2/test_users_negative.py
deleted file mode 100644
index 7ccd75c..0000000
--- a/tempest/api/identity/admin/v2/test_users_negative.py
+++ /dev/null
@@ -1,286 +0,0 @@
-# Copyright 2012 OpenStack Foundation
-# All Rights Reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
-#    not use this file except in compliance with the License. You may obtain
-#    a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-#    License for the specific language governing permissions and limitations
-#    under the License.
-
-from tempest.api.identity import base
-from tempest import config
-from tempest.lib.common.utils import data_utils
-from tempest.lib import decorators
-from tempest.lib import exceptions as lib_exc
-
-CONF = config.CONF
-
-
-class UsersNegativeTestJSON(base.BaseIdentityV2AdminTest):
-    """Negative tests of identity users via v2 API"""
-
-    @classmethod
-    def resource_setup(cls):
-        super(UsersNegativeTestJSON, cls).resource_setup()
-        cls.alt_user = data_utils.rand_name(
-            'test_user', prefix=CONF.resource_name_prefix)
-        cls.alt_password = data_utils.rand_password()
-        cls.alt_email = cls.alt_user + '@testmail.tm'
-
-    @decorators.attr(type=['negative'])
-    @decorators.idempotent_id('60a1f5fa-5744-4cdf-82bf-60b7de2d29a4')
-    def test_create_user_by_unauthorized_user(self):
-        """Non-admin should not be authorized to create a user via v2 API"""
-        tenant = self.setup_test_tenant()
-        self.assertRaises(lib_exc.Forbidden,
-                          self.non_admin_users_client.create_user,
-                          name=self.alt_user, password=self.alt_password,
-                          tenantId=tenant['id'],
-                          email=self.alt_email)
-
-    @decorators.attr(type=['negative'])
-    @decorators.idempotent_id('d80d0c2f-4514-4d1e-806d-0930dfc5a187')
-    def test_create_user_with_empty_name(self):
-        """User with an empty name should not be created via v2 API"""
-        tenant = self.setup_test_tenant()
-        self.assertRaises(lib_exc.BadRequest, self.users_client.create_user,
-                          name='', password=self.alt_password,
-                          tenantId=tenant['id'],
-                          email=self.alt_email)
-
-    @decorators.attr(type=['negative'])
-    @decorators.idempotent_id('7704b4f3-3b75-4b82-87cc-931d41c8f780')
-    def test_create_user_with_name_length_over_255(self):
-        """Length of user name should not exceed 255 via v2 API"""
-        tenant = self.setup_test_tenant()
-        self.assertRaises(lib_exc.BadRequest, self.users_client.create_user,
-                          name='a' * 256, password=self.alt_password,
-                          tenantId=tenant['id'],
-                          email=self.alt_email)
-
-    @decorators.attr(type=['negative'])
-    @decorators.idempotent_id('57ae8558-120c-4723-9308-3751474e7ecf')
-    def test_create_user_with_duplicate_name(self):
-        """Duplicate user should not be created via v2 API"""
-        password = data_utils.rand_password()
-        user = self.setup_test_user(password)
-        tenant = self.tenants_client.show_tenant(user['tenantId'])['tenant']
-        self.assertRaises(lib_exc.Conflict, self.users_client.create_user,
-                          name=user['name'],
-                          password=password,
-                          tenantId=tenant['id'],
-                          email=user['email'])
-
-    @decorators.attr(type=['negative'])
-    @decorators.idempotent_id('0132cc22-7c4f-42e1-9e50-ac6aad31d59a')
-    def test_create_user_for_non_existent_tenant(self):
-        """Creating a user in a non-existent tenant via v2 API should fail"""
-        self.assertRaises(lib_exc.NotFound, self.users_client.create_user,
-                          name=self.alt_user,
-                          password=self.alt_password,
-                          tenantId='49ffgg99999',
-                          email=self.alt_email)
-
-    @decorators.attr(type=['negative'])
-    @decorators.idempotent_id('55bbb103-d1ae-437b-989b-bcdf8175c1f4')
-    def test_create_user_request_without_a_token(self):
-        """Creating a user without a valid token via v2 API should fail"""
-        tenant = self.setup_test_tenant()
-        # Get the token of the current client
-        token = self.client.auth_provider.get_token()
-        # Delete the token from database
-        self.client.delete_token(token)
-
-        # Unset the token to allow further tests to generate a new token
-        self.addCleanup(self.client.auth_provider.clear_auth)
-
-        self.assertRaises(lib_exc.Unauthorized, self.users_client.create_user,
-                          name=self.alt_user, password=self.alt_password,
-                          tenantId=tenant['id'],
-                          email=self.alt_email)
-
-    @decorators.attr(type=['negative'])
-    @decorators.idempotent_id('23a2f3da-4a1a-41da-abdd-632328a861ad')
-    def test_create_user_with_enabled_non_bool(self):
-        """Creating a user with invalid enabled para via v2 API should fail"""
-        tenant = self.setup_test_tenant()
-        name = data_utils.rand_name(
-            'test_user', prefix=CONF.resource_name_prefix)
-        self.assertRaises(lib_exc.BadRequest, self.users_client.create_user,
-                          name=name, password=self.alt_password,
-                          tenantId=tenant['id'],
-                          email=self.alt_email, enabled=3)
-
-    @decorators.attr(type=['negative'])
-    @decorators.idempotent_id('3d07e294-27a0-4144-b780-a2a1bf6fee19')
-    def test_update_user_for_non_existent_user(self):
-        """Updating a non-existent user via v2 API should fail"""
-        user_name = data_utils.rand_name(
-            'user', prefix=CONF.resource_name_prefix)
-        non_existent_id = data_utils.rand_uuid()
-        self.assertRaises(lib_exc.NotFound, self.users_client.update_user,
-                          non_existent_id, name=user_name)
-
-    @decorators.attr(type=['negative'])
-    @decorators.idempotent_id('3cc2a64b-83aa-4b02-88f0-d6ab737c4466')
-    def test_update_user_request_without_a_token(self):
-        """Updating a user without a valid token via v2 API should fail"""
-
-        # Get the token of the current client
-        token = self.client.auth_provider.get_token()
-        # Delete the token from database
-        self.client.delete_token(token)
-
-        # Unset the token to allow further tests to generate a new token
-        self.addCleanup(self.client.auth_provider.clear_auth)
-
-        self.assertRaises(lib_exc.Unauthorized, self.users_client.update_user,
-                          self.alt_user)
-
-    @decorators.attr(type=['negative'])
-    @decorators.idempotent_id('424868d5-18a7-43e1-8903-a64f95ee3aac')
-    def test_update_user_by_unauthorized_user(self):
-        """Non-admin should not be authorized to update user via v2 API"""
-        self.assertRaises(lib_exc.Forbidden,
-                          self.non_admin_users_client.update_user,
-                          self.alt_user)
-
-    @decorators.attr(type=['negative'])
-    @decorators.idempotent_id('d45195d5-33ed-41b9-a452-7d0d6a00f6e9')
-    def test_delete_users_by_unauthorized_user(self):
-        """Non-admin should not be authorized to delete a user via v2 API"""
-        user = self.setup_test_user()
-        self.assertRaises(lib_exc.Forbidden,
-                          self.non_admin_users_client.delete_user,
-                          user['id'])
-
-    @decorators.attr(type=['negative'])
-    @decorators.idempotent_id('7cc82f7e-9998-4f89-abae-23df36495867')
-    def test_delete_non_existent_user(self):
-        """Attempt to delete a non-existent user via v2 API should fail"""
-        self.assertRaises(lib_exc.NotFound, self.users_client.delete_user,
-                          'junk12345123')
-
-    @decorators.attr(type=['negative'])
-    @decorators.idempotent_id('57fe1df8-0aa7-46c0-ae9f-c2e785c7504a')
-    def test_delete_user_request_without_a_token(self):
-        """Deleting a user without a valid token via v2 API should fail"""
-
-        # Get the token of the current client
-        token = self.client.auth_provider.get_token()
-        # Delete the token from database
-        self.client.delete_token(token)
-
-        # Unset the token to allow further tests to generate a new token
-        self.addCleanup(self.client.auth_provider.clear_auth)
-
-        self.assertRaises(lib_exc.Unauthorized, self.users_client.delete_user,
-                          self.alt_user)
-
-    @decorators.attr(type=['negative'])
-    @decorators.idempotent_id('593a4981-f6d4-460a-99a1-57a78bf20829')
-    def test_authentication_for_disabled_user(self):
-        """Disabled user's token should not get authenticated via v2 API"""
-        password = data_utils.rand_password()
-        user = self.setup_test_user(password)
-        tenant = self.tenants_client.show_tenant(user['tenantId'])['tenant']
-        self.disable_user(user['name'])
-        self.assertRaises(lib_exc.Unauthorized, self.token_client.auth,
-                          user['name'],
-                          password,
-                          tenant['name'])
-
-    @decorators.attr(type=['negative'])
-    @decorators.idempotent_id('440a7a8d-9328-4b7b-83e0-d717010495e4')
-    def test_authentication_when_tenant_is_disabled(self):
-        """Test User's token for a disabled tenant via v2 API
-
-        User's token for a disabled tenant should not be authenticated via
-        v2 API.
-        """
-        password = data_utils.rand_password()
-        user = self.setup_test_user(password)
-        tenant = self.tenants_client.show_tenant(user['tenantId'])['tenant']
-        self.disable_tenant(tenant['name'])
-        self.assertRaises(lib_exc.Unauthorized, self.token_client.auth,
-                          user['name'],
-                          password,
-                          tenant['name'])
-
-    @decorators.attr(type=['negative'])
-    @decorators.idempotent_id('921f1ad6-7907-40b8-853f-637e7ee52178')
-    def test_authentication_with_invalid_tenant(self):
-        """Test User's token for an invalid tenant via v2 API
-
-        User's token for an invalid tenant should not be authenticated via V2
-        API.
-        """
-        password = data_utils.rand_password()
-        user = self.setup_test_user(password)
-        self.assertRaises(lib_exc.Unauthorized, self.token_client.auth,
-                          user['name'],
-                          password,
-                          'junktenant1234')
-
-    @decorators.attr(type=['negative'])
-    @decorators.idempotent_id('bde9aecd-3b1c-4079-858f-beb5deaa5b5e')
-    def test_authentication_with_invalid_username(self):
-        """Non-existent user's token should not get authorized via v2 API"""
-        password = data_utils.rand_password()
-        user = self.setup_test_user(password)
-        tenant = self.tenants_client.show_tenant(user['tenantId'])['tenant']
-        self.assertRaises(lib_exc.Unauthorized, self.token_client.auth,
-                          'junkuser123', password, tenant['name'])
-
-    @decorators.attr(type=['negative'])
-    @decorators.idempotent_id('d5308b33-3574-43c3-8d87-1c090c5e1eca')
-    def test_authentication_with_invalid_password(self):
-        """Test User's token with invalid password via v2 API
-
-        User's token with invalid password should not be authenticated via V2
-        API.
-        """
-        user = self.setup_test_user()
-        tenant = self.tenants_client.show_tenant(user['tenantId'])['tenant']
-        self.assertRaises(lib_exc.Unauthorized, self.token_client.auth,
-                          user['name'], 'junkpass1234', tenant['name'])
-
-    @decorators.attr(type=['negative'])
-    @decorators.idempotent_id('284192ce-fb7c-4909-a63b-9a502e0ddd11')
-    def test_get_users_by_unauthorized_user(self):
-        """Non-admin should not be authorized to get user list via v2 API"""
-        self.assertRaises(lib_exc.Forbidden,
-                          self.non_admin_users_client.list_users)
-
-    @decorators.attr(type=['negative'])
-    @decorators.idempotent_id('a73591ec-1903-4ffe-be42-282b39fefc9d')
-    def test_get_users_request_without_token(self):
-        """Listing users without a valid token via v2 API should fail"""
-        token = self.client.auth_provider.get_token()
-        self.client.delete_token(token)
-
-        # Unset the token to allow further tests to generate a new token
-        self.addCleanup(self.client.auth_provider.clear_auth)
-
-        self.assertRaises(lib_exc.Unauthorized, self.users_client.list_users)
-
-    @decorators.attr(type=['negative'])
-    @decorators.idempotent_id('f5d39046-fc5f-425c-b29e-bac2632da28e')
-    def test_list_users_with_invalid_tenant(self):
-        """Listing users for a non-existent tenant via v2 API should fail"""
-        # Assign invalid tenant ids
-        invalid_id = list()
-        invalid_id.append(data_utils.rand_name('999'))
-        invalid_id.append('alpha')
-        invalid_id.append(data_utils.rand_name("dddd@#%%^$"))
-        invalid_id.append('!@#()$%^&*?<>{}[]')
-        # List the users with invalid tenant id
-        for invalid in invalid_id:
-            self.assertRaises(lib_exc.NotFound,
-                              self.tenants_client.list_tenant_users, invalid)
diff --git a/tempest/api/identity/admin/v3/test_groups.py b/tempest/api/identity/admin/v3/test_groups.py
index b5b3c5d..96218bb 100644
--- a/tempest/api/identity/admin/v3/test_groups.py
+++ b/tempest/api/identity/admin/v3/test_groups.py
@@ -128,7 +128,7 @@
         for g in user_groups:
             if 'membership_expires_at' in g:
                 self.assertIsNone(g['membership_expires_at'])
-                del(g['membership_expires_at'])
+                del g['membership_expires_at']
         self.assertEqual(sorted(groups, key=lambda k: k['name']),
                          sorted(user_groups, key=lambda k: k['name']))
         self.assertEqual(2, len(user_groups))
diff --git a/tempest/api/identity/base.py b/tempest/api/identity/base.py
index 9cdd917..811dff4 100644
--- a/tempest/api/identity/base.py
+++ b/tempest/api/identity/base.py
@@ -98,85 +98,6 @@
         return role
 
 
-class BaseIdentityV2Test(BaseIdentityTest):
-
-    credentials = ['primary']
-
-    # identity v2 tests should obtain tokens and create accounts via v2
-    # regardless of the configured CONF.identity.auth_version
-    identity_version = 'v2'
-
-    @classmethod
-    def setup_clients(cls):
-        super(BaseIdentityV2Test, cls).setup_clients()
-        cls.non_admin_client = cls.os_primary.identity_public_client
-        cls.non_admin_token_client = cls.os_primary.token_client
-        cls.non_admin_tenants_client = cls.os_primary.tenants_public_client
-        cls.non_admin_users_client = cls.os_primary.users_public_client
-
-
-class BaseIdentityV2AdminTest(BaseIdentityV2Test):
-
-    credentials = ['primary', 'admin']
-
-    # NOTE(andreaf) Identity tests work with credentials, so it is safer
-    # for them to always use disposable credentials. Forcing dynamic creds
-    # on regular identity tests would be however to restrictive, since it
-    # would prevent any identity test from being executed against clouds where
-    # admin credentials are not available.
-    # Since All admin tests require admin credentials to be
-    # executed, so this will not impact the ability to execute tests.
-    force_tenant_isolation = True
-
-    @classmethod
-    def skip_checks(cls):
-        super(BaseIdentityV2AdminTest, cls).skip_checks()
-        if not CONF.identity_feature_enabled.api_v2_admin:
-            raise cls.skipException('Identity v2 admin not available')
-
-    @classmethod
-    def setup_clients(cls):
-        super(BaseIdentityV2AdminTest, cls).setup_clients()
-        cls.client = cls.os_admin.identity_client
-        cls.non_admin_client = cls.os_primary.identity_client
-        cls.token_client = cls.os_admin.token_client
-        cls.tenants_client = cls.os_admin.tenants_client
-        cls.non_admin_tenants_client = cls.os_primary.tenants_client
-        cls.roles_client = cls.os_admin.roles_client
-        cls.non_admin_roles_client = cls.os_primary.roles_client
-        cls.users_client = cls.os_admin.users_client
-        cls.non_admin_users_client = cls.os_primary.users_client
-        cls.services_client = cls.os_admin.identity_services_client
-        cls.endpoints_client = cls.os_admin.endpoints_client
-
-    @classmethod
-    def resource_setup(cls):
-        super(BaseIdentityV2AdminTest, cls).resource_setup()
-        cls.projects_client = cls.tenants_client
-
-    def setup_test_user(self, password=None):
-        """Set up a test user."""
-        tenant = self.setup_test_tenant()
-        user = self.create_test_user(tenantId=tenant['id'], password=password)
-        return user
-
-    def setup_test_tenant(self, **kwargs):
-        """Set up a test tenant."""
-        if 'name' not in kwargs:
-            kwargs['name'] = data_utils.rand_name(
-                name='test_tenant',
-                prefix=CONF.resource_name_prefix)
-        if 'description' not in kwargs:
-            kwargs['description'] = data_utils.rand_name(
-                name='desc', prefix=CONF.resource_name_prefix)
-        tenant = self.projects_client.create_tenant(**kwargs)['tenant']
-        # Delete the tenant at the end of the test
-        self.addCleanup(
-            test_utils.call_and_ignore_notfound_exc,
-            self.tenants_client.delete_tenant, tenant['id'])
-        return tenant
-
-
 class BaseIdentityV3Test(BaseIdentityTest):
 
     credentials = ['primary']
diff --git a/tempest/api/identity/v2/__init__.py b/tempest/api/identity/v2/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tempest/api/identity/v2/__init__.py
+++ /dev/null
diff --git a/tempest/api/identity/v2/test_api_discovery.py b/tempest/api/identity/v2/test_api_discovery.py
deleted file mode 100644
index afda104..0000000
--- a/tempest/api/identity/v2/test_api_discovery.py
+++ /dev/null
@@ -1,60 +0,0 @@
-# Copyright 2015 OpenStack Foundation.
-# Copyright 2015, Red Hat, Inc.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
-#    not use this file except in compliance with the License. You may obtain
-#    a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-#    License for the specific language governing permissions and limitations
-#    under the License.
-
-from tempest.api.identity import base
-from tempest.lib import decorators
-
-
-class TestApiDiscovery(base.BaseIdentityV2Test):
-    """Tests for identity v2 API discovery features."""
-
-    @decorators.attr(type='smoke')
-    @decorators.idempotent_id('ea889a68-a15f-4166-bfb1-c12456eae853')
-    def test_api_version_resources(self):
-        """Test showing identity v2 api version resources"""
-        descr = self.non_admin_client.show_api_description()['version']
-        expected_resources = ('id', 'links', 'media-types', 'status',
-                              'updated')
-
-        keys = descr.keys()
-        for res in expected_resources:
-            self.assertIn(res, keys)
-
-    @decorators.attr(type='smoke')
-    @decorators.idempotent_id('007a0be0-78fe-4fdb-bbee-e9216cc17bb2')
-    def test_api_media_types(self):
-        """Test showing identity v2 api version media type"""
-        descr = self.non_admin_client.show_api_description()['version']
-        # Get MIME type bases and descriptions
-        media_types = [(media_type['base'], media_type['type']) for
-                       media_type in descr['media-types']]
-        # These are supported for API version 2
-        supported_types = [('application/json',
-                            'application/vnd.openstack.identity-v2.0+json')]
-
-        # Check if supported types exist in response body
-        for s_type in supported_types:
-            self.assertIn(s_type, media_types)
-
-    @decorators.attr(type='smoke')
-    @decorators.idempotent_id('77fd6be0-8801-48e6-b9bf-38cdd2f253ec')
-    def test_api_version_statuses(self):
-        """Test showing identity v2 api version status"""
-        descr = self.non_admin_client.show_api_description()['version']
-        status = descr['status'].lower()
-        supported_statuses = ['current', 'stable', 'experimental',
-                              'supported', 'deprecated']
-
-        self.assertIn(status, supported_statuses)
diff --git a/tempest/api/identity/v2/test_ec2_credentials.py b/tempest/api/identity/v2/test_ec2_credentials.py
deleted file mode 100644
index 9981ef8..0000000
--- a/tempest/api/identity/v2/test_ec2_credentials.py
+++ /dev/null
@@ -1,113 +0,0 @@
-# Copyright 2015 OpenStack Foundation
-# All Rights Reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
-#    not use this file except in compliance with the License. You may obtain
-#    a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-#    License for the specific language governing permissions and limitations
-#    under the License.
-
-from tempest.api.identity import base
-from tempest.common import utils
-from tempest.lib import decorators
-from tempest.lib import exceptions as lib_exc
-
-
-class EC2CredentialsTest(base.BaseIdentityV2Test):
-
-    @classmethod
-    def skip_checks(cls):
-        super(EC2CredentialsTest, cls).skip_checks()
-        if not utils.is_extension_enabled('OS-EC2', 'identity'):
-            msg = "OS-EC2 identity extension not enabled."
-            raise cls.skipException(msg)
-
-    @classmethod
-    def resource_setup(cls):
-        super(EC2CredentialsTest, cls).resource_setup()
-        cls.creds = cls.os_primary.credentials
-
-    @decorators.idempotent_id('b580fab9-7ae9-46e8-8138-417260cb6f9f')
-    def test_create_ec2_credential(self):
-        """Create user ec2 credential."""
-        resp = self.non_admin_users_client.create_user_ec2_credential(
-            self.creds.user_id,
-            tenant_id=self.creds.tenant_id)["credential"]
-        access = resp['access']
-        self.addCleanup(
-            self.non_admin_users_client.delete_user_ec2_credential,
-            self.creds.user_id, access)
-        self.assertNotEmpty(resp['access'])
-        self.assertNotEmpty(resp['secret'])
-        self.assertEqual(self.creds.user_id, resp['user_id'])
-        self.assertEqual(self.creds.tenant_id, resp['tenant_id'])
-
-    @decorators.idempotent_id('9e2ea42f-0a4f-468c-a768-51859ce492e0')
-    def test_list_ec2_credentials(self):
-        """Get the list of user ec2 credentials."""
-        created_creds = []
-        # create first ec2 credentials
-        creds1 = self.non_admin_users_client.create_user_ec2_credential(
-            self.creds.user_id,
-            tenant_id=self.creds.tenant_id)["credential"]
-        created_creds.append(creds1['access'])
-        self.addCleanup(
-            self.non_admin_users_client.delete_user_ec2_credential,
-            self.creds.user_id, creds1['access'])
-
-        # create second ec2 credentials
-        creds2 = self.non_admin_users_client.create_user_ec2_credential(
-            self.creds.user_id,
-            tenant_id=self.creds.tenant_id)["credential"]
-        created_creds.append(creds2['access'])
-        self.addCleanup(
-            self.non_admin_users_client.delete_user_ec2_credential,
-            self.creds.user_id, creds2['access'])
-
-        # get the list of user ec2 credentials
-        resp = self.non_admin_users_client.list_user_ec2_credentials(
-            self.creds.user_id)["credentials"]
-        fetched_creds = [cred['access'] for cred in resp]
-        # created credentials should be in a fetched list
-        missing = [cred for cred in created_creds
-                   if cred not in fetched_creds]
-        self.assertEmpty(missing,
-                         "Failed to find ec2_credentials %s in fetched list" %
-                         ', '.join(cred for cred in missing))
-
-    @decorators.idempotent_id('cb284075-b613-440d-83ca-fe0b33b3c2b8')
-    def test_show_ec2_credential(self):
-        """Get the definite user ec2 credential."""
-        resp = self.non_admin_users_client.create_user_ec2_credential(
-            self.creds.user_id,
-            tenant_id=self.creds.tenant_id)["credential"]
-        self.addCleanup(
-            self.non_admin_users_client.delete_user_ec2_credential,
-            self.creds.user_id, resp['access'])
-
-        ec2_creds = self.non_admin_users_client.show_user_ec2_credential(
-            self.creds.user_id, resp['access']
-        )["credential"]
-        for key in ['access', 'secret', 'user_id', 'tenant_id']:
-            self.assertEqual(ec2_creds[key], resp[key])
-
-    @decorators.idempotent_id('6aba0d4c-b76b-4e46-aa42-add79bc1551d')
-    def test_delete_ec2_credential(self):
-        """Delete user ec2 credential."""
-        resp = self.non_admin_users_client.create_user_ec2_credential(
-            self.creds.user_id,
-            tenant_id=self.creds.tenant_id)["credential"]
-        access = resp['access']
-        self.non_admin_users_client.delete_user_ec2_credential(
-            self.creds.user_id, access)
-        self.assertRaises(
-            lib_exc.NotFound,
-            self.non_admin_users_client.show_user_ec2_credential,
-            self.creds.user_id,
-            access)
diff --git a/tempest/api/identity/v2/test_extension.py b/tempest/api/identity/v2/test_extension.py
deleted file mode 100644
index 13555bd..0000000
--- a/tempest/api/identity/v2/test_extension.py
+++ /dev/null
@@ -1,32 +0,0 @@
-# Copyright 2014 NEC Corporation
-# All Rights Reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
-#    not use this file except in compliance with the License. You may obtain
-#    a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-#    License for the specific language governing permissions and limitations
-#    under the License.
-
-from tempest.api.identity import base
-from tempest.lib import decorators
-
-
-class ExtensionTestJSON(base.BaseIdentityV2Test):
-    """Test extensions in identity v2 API"""
-
-    @decorators.idempotent_id('85f3f661-f54c-4d48-b563-72ae952b9383')
-    def test_list_extensions(self):
-        """List all the identity extensions via v2 API"""
-        body = self.non_admin_client.list_extensions()['extensions']['values']
-        self.assertNotEmpty(body)
-        keys = ['name', 'updated', 'alias', 'links',
-                'namespace', 'description']
-        for value in body:
-            for key in keys:
-                self.assertIn(key, value)
diff --git a/tempest/api/identity/v2/test_tenants.py b/tempest/api/identity/v2/test_tenants.py
deleted file mode 100644
index 1752b65..0000000
--- a/tempest/api/identity/v2/test_tenants.py
+++ /dev/null
@@ -1,52 +0,0 @@
-# Copyright 2015 OpenStack Foundation
-# All Rights Reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
-#    not use this file except in compliance with the License. You may obtain
-#    a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-#    License for the specific language governing permissions and limitations
-#    under the License.
-
-from tempest.api.identity import base
-from tempest.lib import decorators
-from tempest.lib import exceptions as lib_exc
-
-
-class IdentityTenantsTest(base.BaseIdentityV2Test):
-    """Test listing tenants in identity v2 API"""
-
-    credentials = ['primary', 'alt']
-
-    @decorators.idempotent_id('ecae2459-243d-4ba1-ad02-65f15dc82b78')
-    def test_list_tenants_returns_only_authorized_tenants(self):
-        """Test listing tenants only returns authorized tenants via v2 API"""
-        alt_tenant_name = self.os_alt.credentials.tenant_name
-        resp = self.non_admin_tenants_client.list_tenants()
-
-        # check that user can see only that tenants that he presents in so user
-        # can successfully authenticate using his credentials and tenant name
-        # from received tenants list
-        for tenant in resp['tenants']:
-            body = self.non_admin_token_client.auth(
-                self.os_primary.credentials.username,
-                self.os_primary.credentials.password,
-                tenant['name'])
-            self.assertNotEmpty(body['token']['id'])
-            self.assertEqual(body['token']['tenant']['id'], tenant['id'])
-            self.assertEqual(body['token']['tenant']['name'], tenant['name'])
-            self.assertEqual(
-                body['user']['id'], self.os_primary.credentials.user_id)
-
-        # check that user cannot log in to alt user's tenant
-        self.assertRaises(
-            lib_exc.Unauthorized,
-            self.non_admin_token_client.auth,
-            self.os_primary.credentials.username,
-            self.os_primary.credentials.password,
-            alt_tenant_name)
diff --git a/tempest/api/identity/v2/test_tokens.py b/tempest/api/identity/v2/test_tokens.py
deleted file mode 100644
index d3776b8..0000000
--- a/tempest/api/identity/v2/test_tokens.py
+++ /dev/null
@@ -1,50 +0,0 @@
-# Copyright 2015 OpenStack Foundation
-# All Rights Reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
-#    not use this file except in compliance with the License. You may obtain
-#    a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-#    License for the specific language governing permissions and limitations
-#    under the License.
-
-from oslo_utils import timeutils
-from tempest.api.identity import base
-from tempest.lib import decorators
-
-
-class TokensTest(base.BaseIdentityV2Test):
-    """Test tokens in identity v2 API"""
-
-    @decorators.idempotent_id('65ae3b78-91ff-467b-a705-f6678863b8ec')
-    def test_create_token(self):
-        """Test creating token for user via v2 API"""
-        token_client = self.non_admin_token_client
-
-        # get a token for the user
-        creds = self.os_primary.credentials
-        username = creds.username
-        password = creds.password
-        tenant_name = creds.tenant_name
-
-        body = token_client.auth(username, password, tenant_name)
-
-        self.assertNotEmpty(body['token']['id'])
-        self.assertIsInstance(body['token']['id'], str)
-
-        now = timeutils.utcnow()
-        expires_at = timeutils.normalize_time(
-            timeutils.parse_isotime(body['token']['expires']))
-        self.assertGreater(expires_at, now)
-
-        self.assertEqual(body['token']['tenant']['id'],
-                         creds.tenant_id)
-        self.assertEqual(body['token']['tenant']['name'],
-                         tenant_name)
-
-        self.assertEqual(body['user']['id'], creds.user_id)
diff --git a/tempest/api/identity/v2/test_users.py b/tempest/api/identity/v2/test_users.py
deleted file mode 100644
index a63b45c..0000000
--- a/tempest/api/identity/v2/test_users.py
+++ /dev/null
@@ -1,112 +0,0 @@
-# Copyright 2015 OpenStack Foundation
-# All Rights Reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
-#    not use this file except in compliance with the License. You may obtain
-#    a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-#    License for the specific language governing permissions and limitations
-#    under the License.
-
-import time
-
-import testtools
-
-from tempest.api.identity import base
-from tempest import config
-from tempest.lib.common.utils import data_utils
-from tempest.lib import decorators
-from tempest.lib import exceptions
-
-
-CONF = config.CONF
-
-
-class IdentityUsersTest(base.BaseIdentityV2Test):
-    """Test user password in identity v2 API"""
-
-    @classmethod
-    def resource_setup(cls):
-        super(IdentityUsersTest, cls).resource_setup()
-        cls.creds = cls.os_primary.credentials
-        cls.username = cls.creds.username
-        cls.password = cls.creds.password
-        cls.tenant_name = cls.creds.tenant_name
-
-    def _update_password(self, user_id, original_password, password):
-        self.non_admin_users_client.update_user_own_password(
-            user_id, password=password, original_password=original_password)
-
-        # NOTE(morganfainberg): Fernet tokens are not subsecond aware and
-        # Keystone should only be precise to the second. Sleep to ensure
-        # we are passing the second boundary.
-        time.sleep(1)
-
-        # check authorization with new password
-        self.non_admin_token_client.auth(self.username,
-                                         password,
-                                         self.tenant_name)
-
-        # Reset auth to get a new token with the new password
-        self.non_admin_users_client.auth_provider.clear_auth()
-        self.non_admin_users_client.auth_provider.credentials.password = (
-            password)
-
-    def _restore_password(self, user_id, old_pass, new_pass):
-        if CONF.identity_feature_enabled.security_compliance:
-            # First we need to clear the password history
-            unique_count = CONF.identity.user_unique_last_password_count
-            for _ in range(unique_count):
-                random_pass = data_utils.rand_password()
-                self._update_password(
-                    user_id, original_password=new_pass, password=random_pass)
-                new_pass = random_pass
-
-        self._update_password(
-            user_id, original_password=new_pass, password=old_pass)
-        # Reset auth again to verify the password restore does work.
-        # Clear auth restores the original credentials and deletes
-        # cached auth data
-        self.non_admin_users_client.auth_provider.clear_auth()
-        # NOTE(lbragstad): Fernet tokens are not subsecond aware and
-        # Keystone should only be precise to the second. Sleep to ensure we
-        # are passing the second boundary before attempting to
-        # authenticate.
-        time.sleep(1)
-        self.non_admin_users_client.auth_provider.set_auth()
-
-    @decorators.idempotent_id('165859c9-277f-4124-9479-a7d1627b0ca7')
-    @testtools.skipIf(CONF.identity_feature_enabled.immutable_user_source,
-                      'Skipped because environment has an '
-                      'immutable user source and solely '
-                      'provides read-only access to users.')
-    def test_user_update_own_password(self):
-        """test updating user's own password via v2 API"""
-        old_pass = self.creds.password
-        old_token = self.non_admin_users_client.token
-        new_pass = data_utils.rand_password()
-        user_id = self.creds.user_id
-
-        # to change password back. important for use_dynamic_credentials=false
-        self.addCleanup(self._restore_password, user_id, old_pass, new_pass)
-
-        # user updates own password
-        self._update_password(
-            user_id, original_password=old_pass, password=new_pass)
-
-        # authorize with old token should lead to Unauthorized
-        self.assertRaises(exceptions.Unauthorized,
-                          self.non_admin_token_client.auth_token,
-                          old_token)
-
-        # authorize with old password should lead to Unauthorized
-        self.assertRaises(exceptions.Unauthorized,
-                          self.non_admin_token_client.auth,
-                          self.username,
-                          old_pass,
-                          self.tenant_name)
diff --git a/tempest/api/image/base.py b/tempest/api/image/base.py
index 89d5f91..c2f067c 100644
--- a/tempest/api/image/base.py
+++ b/tempest/api/image/base.py
@@ -100,10 +100,9 @@
         """Create Image & stage image file for glance-direct import method."""
         image_name = data_utils.rand_name('test-image')
         container_format = CONF.image.container_formats[0]
-        disk_format = CONF.image.disk_formats[0]
         image = self.create_image(name=image_name,
                                   container_format=container_format,
-                                  disk_format=disk_format,
+                                  disk_format='raw',
                                   visibility='private')
         self.assertEqual('queued', image['status'])
 
@@ -178,8 +177,8 @@
         # If we added the location directly, the image goes straight
         # to active and no hashing is done
         self.assertEqual('active', image['status'])
-        self.assertIsNone(None, image['os_hash_algo'])
-        self.assertIsNone(None, image['os_hash_value'])
+        self.assertIsNone(image['os_hash_algo'])
+        self.assertIsNone(image['os_hash_value'])
 
         return image
 
@@ -202,8 +201,8 @@
 
         # The image should still be active and still have no hashes
         self.assertEqual('active', image['status'])
-        self.assertIsNone(None, image['os_hash_algo'])
-        self.assertIsNone(None, image['os_hash_value'])
+        self.assertIsNone(image['os_hash_algo'])
+        self.assertIsNone(image['os_hash_value'])
 
         # The direct_url should still match the first location
         if 'direct_url' in image:
diff --git a/tempest/api/image/v2/admin/test_images.py b/tempest/api/image/v2/admin/test_images.py
index 2b1c4fb..2c2e9a8 100644
--- a/tempest/api/image/v2/admin/test_images.py
+++ b/tempest/api/image/v2/admin/test_images.py
@@ -112,7 +112,7 @@
         image_name = data_utils.rand_name(
             prefix=CONF.resource_name_prefix, name='copy-image')
         container_format = CONF.image.container_formats[0]
-        disk_format = CONF.image.disk_formats[0]
+        disk_format = 'raw'
         image = self.create_image(name=image_name,
                                   container_format=container_format,
                                   disk_format=disk_format,
diff --git a/tempest/api/image/v2/test_images.py b/tempest/api/image/v2/test_images.py
index e468e32..9309c76 100644
--- a/tempest/api/image/v2/test_images.py
+++ b/tempest/api/image/v2/test_images.py
@@ -56,7 +56,14 @@
         image_name = data_utils.rand_name(
             prefix=CONF.resource_name_prefix, name='image')
         container_format = container_format or CONF.image.container_formats[0]
-        disk_format = disk_format or CONF.image.disk_formats[0]
+        disk_format = disk_format or 'raw'
+        if disk_format not in CONF.image.disk_formats:
+            # If the test asked for some disk format that is not available,
+            # consider that a programming error. Tests with specific
+            # requirements should be checking to see if it is available and
+            # skipping themselves instead of this helper doing it.
+            raise RuntimeError('Test requires unavailable disk_format %s, '
+                               'but did not skip' % disk_format)
         image = self.create_image(name=image_name,
                                   container_format=container_format,
                                   disk_format=disk_format,
@@ -76,7 +83,7 @@
                                      '%s import method' % method)
 
     def _stage_and_check(self):
-        image = self._create_image()
+        image = self._create_image(disk_format='raw')
         # Stage image data
         file_content = data_utils.random_bytes()
         image_file = io.BytesIO(file_content)
@@ -125,13 +132,13 @@
         """
         self._require_import_method('web-download')
 
-        image = self._create_image()
+        image = self._create_image(disk_format='qcow2')
         # Now try to get image details
         body = self.client.show_image(image['id'])
         self.assertEqual(image['id'], body['id'])
         self.assertEqual('queued', body['status'])
         # import image from web to backend
-        image_uri = CONF.image.http_image
+        image_uri = CONF.image.http_qcow2_image
         self.client.image_import(image['id'], method='web-download',
                                  import_params={'uri': image_uri})
         waiters.wait_for_image_imported_to_stores(self.client, image['id'])
@@ -390,7 +397,7 @@
         image_name = data_utils.rand_name(
             prefix=CONF.resource_name_prefix, name='image')
         container_format = CONF.image.container_formats[0]
-        disk_format = CONF.image.disk_formats[0]
+        disk_format = 'raw'
         image = self.create_image(name=image_name,
                                   container_format=container_format,
                                   disk_format=disk_format,
@@ -529,6 +536,15 @@
                      for container_fmt in container_fmts
                      for disk_fmt in disk_fmts]
 
+        # NOTE(danms): This tests depends on being able to lie about image
+        # content. We can probably improve this in some way, but without a
+        # valid sample of each image format in each container format, there is
+        # no easy solution.
+        if CONF.image_feature_enabled.image_format_enforcement:
+            raise cls.skipException(
+                'Image format enforcement prevents testing with '
+                'bogus image data')
+
         for (container_fmt, disk_fmt) in all_pairs[:6]:
             LOG.debug("Creating an image "
                       "(Container format: %s, Disk format: %s).",
@@ -754,7 +770,7 @@
         # Create an image to be shared using default visibility
         image_file = io.BytesIO(data_utils.random_bytes(2048))
         container_format = CONF.image.container_formats[0]
-        disk_format = CONF.image.disk_formats[0]
+        disk_format = 'raw'
         image = self.create_image(container_format=container_format,
                                   disk_format=disk_format)
         self.client.store_image_file(image['id'], data=image_file)
diff --git a/tempest/api/image/v2/test_images_dependency.py b/tempest/api/image/v2/test_images_dependency.py
index 326045b..41611bb 100644
--- a/tempest/api/image/v2/test_images_dependency.py
+++ b/tempest/api/image/v2/test_images_dependency.py
@@ -56,9 +56,10 @@
                 "not enabled" % (cls.__name__))
             raise cls.skipException(skip_msg)
 
-    def _create_instance_snapshot(self):
+    def _create_instance_snapshot(self, bfv=False):
         """Create instance from image and then snapshot the instance."""
         # Create image and store data to image
+        source = 'volume' if bfv else 'image'
         image_name = data_utils.rand_name(
             prefix=CONF.resource_name_prefix,
             name='image-dependency-test')
@@ -71,12 +72,20 @@
         self.client.store_image_file(image['id'], image_file)
         waiters.wait_for_image_status(
             self.client, image['id'], 'active')
-        # Create instance
-        instance = self.create_test_server(
-            name='instance-depend-image',
-            image_id=image['id'],
-            wait_until='ACTIVE')
-        LOG.info("Instance from image is created %s", instance)
+        if bfv:
+            # Create instance
+            instance = self.create_test_server(
+                name='instance-depend-image',
+                image_id=image['id'],
+                volume_backed=True,
+                wait_until='ACTIVE')
+        else:
+            # Create instance
+            instance = self.create_test_server(
+                name='instance-depend-image',
+                image_id=image['id'],
+                wait_until='ACTIVE')
+        LOG.info("Instance from %s is created %s", source, instance)
         instance_observed = \
             self.servers_client.show_server(instance['id'])['server']
         # Create instance snapshot
@@ -101,3 +110,26 @@
         fetched_images_id = [img['id'] for img in images_list]
         self.assertNotIn(base_image_id, fetched_images_id)
         self.assertIn(snapshot_image_id, fetched_images_id)
+
+    @utils.services('compute', 'volume')
+    @decorators.idempotent_id('f0c8a35d-8f8f-443c-8bcb-85a9c0f87d19')
+    def test_image_volume_server_snapshot_dependency(self):
+        """Test with image > volume > instance > snapshot dependency.
+
+        We are going to perform the following steps in the test:
+        * Create image
+        * Create a bootable volume from Image
+        * Launch an instance from the bootable volume
+        * Take snapshot of the instance -- which creates the volume snapshot
+        * Delete the image.
+
+        This will test the dependency chain of image -> volume -> snapshot.
+        """
+        base_image_id, snapshot_image_id = self._create_instance_snapshot(
+            bfv=True)
+        self.client.delete_image(base_image_id)
+        self.client.wait_for_resource_deletion(base_image_id)
+        images_list = self.client.list_images()['images']
+        fetched_images_id = [img['id'] for img in images_list]
+        self.assertNotIn(base_image_id, fetched_images_id)
+        self.assertIn(snapshot_image_id, fetched_images_id)
diff --git a/tempest/api/image/v2/test_images_formats.py b/tempest/api/image/v2/test_images_formats.py
new file mode 100644
index 0000000..520a215
--- /dev/null
+++ b/tempest/api/image/v2/test_images_formats.py
@@ -0,0 +1,224 @@
+# Copyright 2024 Red Hat, Inc.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+import os
+
+import testscenarios
+import yaml
+
+from tempest.api.compute import base as compute_base
+from tempest.api.image import base
+from tempest.common import waiters
+from tempest import config
+from tempest import exceptions
+from tempest.lib.common.utils import data_utils
+from tempest.lib import decorators
+from tempest.lib import exceptions as lib_exc
+
+CONF = config.CONF
+
+
+def load_tests(loader, suite, pattern):
+    """Generate scenarios from the image manifest."""
+    if CONF.image.images_manifest_file is None:
+        return suite
+    ImagesFormatTest.scenarios = []
+    with open(CONF.image.images_manifest_file) as f:
+        ImagesFormatTest._manifest = yaml.load(f, Loader=yaml.SafeLoader)
+        for imgdef in ImagesFormatTest._manifest['images']:
+            ImagesFormatTest.scenarios.append((imgdef['name'],
+                                               {'imgdef': imgdef}))
+    result = loader.suiteClass()
+    result.addTests(testscenarios.generate_scenarios(suite))
+    return result
+
+
+class ImagesFormatTest(base.BaseV2ImageTest,
+                       compute_base.BaseV2ComputeTest):
+    def setUp(self):
+        super().setUp()
+        if CONF.image.images_manifest_file is None:
+            self.skipTest('Image format testing is not configured')
+        self._image_base = os.path.dirname(os.path.abspath(
+            CONF.image.images_manifest_file))
+
+        self.images = []
+
+    def tearDown(self):
+        for img in self.images:
+            try:
+                self.client.delete_image(img['id'])
+            except lib_exc.NotFound:
+                pass
+        return super().tearDown()
+
+    @classmethod
+    def resource_setup(cls):
+        super().resource_setup()
+        cls.available_import_methods = cls.client.info_import()[
+            'import-methods']['value']
+
+    def _test_image(self, image_def, override_format=None, asimport=False):
+        image_name = data_utils.rand_name(
+            prefix=CONF.resource_name_prefix,
+            name=image_def['name'])
+        image = self.client.create_image(
+            name=image_name,
+            container_format='bare',
+            disk_format=override_format or image_def['format'])
+        self.images.append(image)
+        image_fn = os.path.join(self._image_base, image_def['filename'])
+        with open(image_fn, 'rb') as f:
+            if asimport:
+                self.client.stage_image_file(image['id'], f)
+                self.client.image_import(image['id'], method='glance-direct')
+            else:
+                self.client.store_image_file(image['id'], f)
+        return image
+
+    @decorators.idempotent_id('a245fcbe-63ce-4dc1-a1d0-c16d76d9e6df')
+    def test_accept_usable_formats(self):
+        if self.imgdef['usable']:
+            try:
+                self._test_image(self.imgdef)
+            except lib_exc.BadRequest:
+                format = self.imgdef['format']
+                if format == 'gpt' and format not in CONF.image.disk_formats:
+                    # If we don't have gpt defined, we don't expect this to
+                    # work because glance has not been updated for GPT
+                    # FIXME(danms): Remove this once glance support for GPT is
+                    # landed on master
+                    self.skipTest('GPT not configured and glance is too '
+                                  'old to support it')
+                raise
+        else:
+            self.skipTest(
+                'Glance does not currently reject unusable images on upload')
+
+    @decorators.idempotent_id('7c7c2f16-2e97-4dce-8cb4-bc10be031c85')
+    def test_accept_reject_formats_import(self):
+        """Make sure glance rejects invalid images during conversion."""
+        if 'glance-direct' not in self.available_import_methods:
+            self.skipTest('Import via glance-direct is not available')
+        if not CONF.image_feature_enabled.image_conversion:
+            self.skipTest('Import image_conversion not enabled')
+
+        # VMDK with footer was not supported by earlier service versions,
+        # so we need to tolerate it passing and failing (skip for the latter).
+        # See this for more info:
+        # https://bugs.launchpad.net/glance/+bug/2073262
+        is_broken = 'footer' in self.imgdef['name']
+
+        if (self.imgdef['format'] in CONF.image.disk_formats and
+                self.imgdef['usable']):
+            # Usable images should end up in active state
+            image = self._test_image(self.imgdef, asimport=True)
+            try:
+                waiters.wait_for_image_status(self.client, image['id'],
+                                              'active')
+            except lib_exc.TimeoutException:
+                if is_broken:
+                    self.skipTest(
+                        'Older glance did not support vmdk-with-footer')
+                else:
+                    raise
+        else:
+            # FIXME(danms): Make this better, but gpt will fail before
+            # the import even starts until glance has it in its API
+            # schema as a valid value. Other formats expected to fail
+            # do so during import and return to queued state.
+            try:
+                image = self._test_image(self.imgdef, asimport=True)
+                waiters.wait_for_image_status(self.client, image['id'],
+                                              ['queued', 'active'])
+                self.client.delete_image(image['id'])
+            except lib_exc.BadRequest:
+                format = self.imgdef['format']
+                if format == 'gpt' and format not in CONF.image.disk_formats:
+                    # If we don't have gpt defined, we don't expect this to
+                    # work because glance has not been updated for GPT
+                    # FIXME(danms): Remove this once glance support for GPT is
+                    # landed on master
+                    self.skipTest('GPT not configured and glance is too '
+                                  'old to support it')
+                elif format in CONF.image.disk_formats:
+                    # This is in our config so it's supposed to work, fail
+                    raise
+
+        if self.imgdef['format'] == 'iso':
+            # NOTE(danms): Glance has a special case to not convert ISO images
+            # because they are special and must remain as ISOs in order to be
+            # properly used for CD-based rescue and boot.
+            self.assertEqual('iso', image['disk_format'])
+
+    def _create_server_with_image_def(self, image_def, **overrides):
+        image_def = dict(image_def, **overrides)
+        image = self._test_image(image_def)
+        server = self.create_test_server(name='server-%s' % image['name'],
+                                         image_id=image['id'],
+                                         wait_until='ACTIVE')
+        return server
+
+    @decorators.idempotent_id('f77394bc-81f4-4d54-9f5b-e48f3d6b5376')
+    def test_compute_rejects_invalid(self):
+        """Make sure compute rejects invalid/insecure images."""
+        if self.imgdef['format'] not in CONF.image.disk_formats:
+            # if this format is not allowed by glance, we can not create
+            # a properly-formatted image for it, so skip it.
+            self.skipTest(
+                'Format %s not allowed by config' % self.imgdef['format'])
+        if CONF.image_feature_enabled.image_format_enforcement:
+            # If glance rejects bad images during upload, we cannot get them
+            # registered so that we can test nova.
+            self.skipTest(
+                'Unable to test compute image formats if glance does not '
+                'allow them to be uploaded')
+
+        # VMDK with footer was not supported by earlier service versions,
+        # so we need to tolerate it passing and failing (skip for the latter).
+        # See this for more info:
+        # https://bugs.launchpad.net/glance/+bug/2073262
+        is_broken = 'footer' in self.imgdef['name']
+
+        if self.imgdef['usable']:
+            try:
+                server = self._create_server_with_image_def(self.imgdef)
+            except exceptions.BuildErrorException:
+                if is_broken:
+                    self.skip('Tolerating failed build with known-broken '
+                              'image format')
+                else:
+                    raise
+            self.delete_server(server['id'])
+        else:
+            self.assertRaises(exceptions.BuildErrorException,
+                              self._create_server_with_image_def,
+                              self.imgdef)
+
+    @decorators.idempotent_id('ffe21610-e801-4992-9b81-e2d646e2e2e9')
+    def test_compute_rejects_format_mismatch(self):
+        """Make sure compute rejects any image with a format mismatch."""
+        if CONF.image_feature_enabled.image_format_enforcement:
+            # If glance rejects bad images during upload, we cannot get them
+            # registered so that we can test nova.
+            self.skipTest(
+                'Unable to test compute image formats if glance does not '
+                'allow them to be uploaded')
+        # Lying about the disk_format should always fail
+        override_fmt = (
+            self.imgdef['format'] in ('raw', 'gpt') and 'qcow2' or 'raw')
+        self.assertRaises(exceptions.BuildErrorException,
+                          self._create_server_with_image_def,
+                          self.imgdef,
+                          format=override_fmt)
diff --git a/tempest/api/network/admin/test_dhcp_agent_scheduler.py b/tempest/api/network/admin/test_dhcp_agent_scheduler.py
index b4bfc61..8b4766c 100644
--- a/tempest/api/network/admin/test_dhcp_agent_scheduler.py
+++ b/tempest/api/network/admin/test_dhcp_agent_scheduler.py
@@ -48,12 +48,13 @@
 
     @decorators.idempotent_id('f164801e-1dd8-4b8b-b5d3-cc3ac77cfaa5')
     def test_dhcp_port_status_active(self):
-        ports = self.admin_ports_client.list_ports(
+        dhcp_ports = self.admin_ports_client.list_ports(
+            device_owner='network:dhcp',
             network_id=self.network['id'])['ports']
-        for port in ports:
+        for dhcp_port in dhcp_ports:
             waiters.wait_for_port_status(
                 client=self.admin_ports_client,
-                port_id=port['id'],
+                port_id=dhcp_port['id'],
                 status='ACTIVE')
 
     @decorators.idempotent_id('5032b1fe-eb42-4a64-8f3b-6e189d8b5c7d')
diff --git a/tempest/api/network/base.py b/tempest/api/network/base.py
index 99742cc..5bbd50e 100644
--- a/tempest/api/network/base.py
+++ b/tempest/api/network/base.py
@@ -15,6 +15,8 @@
 
 import netaddr
 
+from tempest.common import utils as common_utils
+from tempest.common import waiters
 from tempest import config
 from tempest import exceptions
 from tempest.lib.common.utils import data_utils
@@ -226,6 +228,18 @@
                 subnet_id=i['fixed_ips'][0]['subnet_id'])
         cls.routers_client.delete_router(router['id'])
 
+    def remove_router_interface(self, router_id, port_id, subnet_id=None):
+        # NOTE: with DVR and without a VM port, it is not possible to know
+        # what agent will host the router interface thus won't be bound.
+        if not common_utils.is_extension_enabled('dvr', 'network'):
+            waiters.wait_for_port_status(client=self.ports_client,
+                                         port_id=port_id, status='ACTIVE')
+        if subnet_id:
+            kwargs = {'subnet_id': subnet_id}
+        else:
+            kwargs = {'port_id': port_id}
+        self.routers_client.remove_router_interface(router_id, **kwargs)
+
 
 class BaseAdminNetworkTest(BaseNetworkTest):
 
diff --git a/tempest/api/network/test_networks.py b/tempest/api/network/test_networks.py
index fd93779..b1fba2d 100644
--- a/tempest/api/network/test_networks.py
+++ b/tempest/api/network/test_networks.py
@@ -389,17 +389,20 @@
         # belong to other tests and their state may have changed during this
         # test
         body = self.subnets_client.list_subnets(network_id=public_network_id)
+        extensions = [
+            ext['alias'] for ext in
+            self.network_extensions_client.list_extensions()['extensions']]
+        is_sen_ext = 'subnet-external-network' in extensions
 
         # check subnet visibility of external_network
-        if external_network['shared']:
-            self.assertNotEmpty(body['subnets'], "Subnets should be visible "
-                                                 "for shared public network %s"
-                                % public_network_id)
+        if external_network['shared'] or is_sen_ext:
+            self.assertNotEmpty(body['subnets'],
+                                'Subnets should be visible for shared or '
+                                'external networks %s' % public_network_id)
         else:
-            self.assertEmpty(body['subnets'], "Subnets should not be visible "
-                                              "for non-shared public "
-                                              "network %s"
-                             % public_network_id)
+            self.assertEmpty(body['subnets'],
+                             'Subnets should not be visible for non-shared or'
+                             'non-external networks %s' % public_network_id)
 
     @decorators.idempotent_id('c72c1c0c-2193-4aca-ccc4-b1442640bbbb')
     @utils.requires_ext(extension="standard-attr-description",
diff --git a/tempest/api/network/test_routers.py b/tempest/api/network/test_routers.py
index 0dd7c70..fedf2f4 100644
--- a/tempest/api/network/test_routers.py
+++ b/tempest/api/network/test_routers.py
@@ -32,16 +32,11 @@
     def _add_router_interface_with_subnet_id(self, router_id, subnet_id):
         interface = self.routers_client.add_router_interface(
             router_id, subnet_id=subnet_id)
-        self.addCleanup(self._remove_router_interface_with_subnet_id,
-                        router_id, subnet_id)
+        self.addCleanup(self.remove_router_interface,
+                        router_id, interface['port_id'], subnet_id=subnet_id)
         self.assertEqual(subnet_id, interface['subnet_id'])
         return interface
 
-    def _remove_router_interface_with_subnet_id(self, router_id, subnet_id):
-        body = self.routers_client.remove_router_interface(router_id,
-                                                           subnet_id=subnet_id)
-        self.assertEqual(subnet_id, body['subnet_id'])
-
     @classmethod
     def skip_checks(cls):
         super(RoutersTest, cls).skip_checks()
@@ -106,8 +101,9 @@
         # Add router interface with subnet id
         interface = self.routers_client.add_router_interface(
             router['id'], subnet_id=subnet['id'])
-        self.addCleanup(self._remove_router_interface_with_subnet_id,
-                        router['id'], subnet['id'])
+        self.addCleanup(self.remove_router_interface,
+                        router['id'], interface['port_id'],
+                        subnet_id=subnet['id'])
         self.assertIn('subnet_id', interface.keys())
         self.assertIn('port_id', interface.keys())
         # Verify router id is equal to device id in port details
@@ -183,9 +179,11 @@
             next_cidr = next_cidr.next()
 
             # Add router interface with subnet id
-            self.create_router_interface(router['id'], subnet['id'])
-            self.addCleanup(self._remove_router_interface_with_subnet_id,
-                            router['id'], subnet['id'])
+            interface = self.create_router_interface(router['id'],
+                                                     subnet['id'])
+            self.addCleanup(self.remove_router_interface,
+                            router['id'], interface['port_id'],
+                            subnet_id=subnet['id'])
             cidr = netaddr.IPNetwork(subnet['cidr'])
             next_hop = str(cidr[2])
             destination = str(subnet['cidr'])
diff --git a/tempest/api/network/test_routers_negative.py b/tempest/api/network/test_routers_negative.py
index 50ba977..299e0e9 100644
--- a/tempest/api/network/test_routers_negative.py
+++ b/tempest/api/network/test_routers_negative.py
@@ -77,8 +77,9 @@
         subnet02 = self.create_subnet(network02)
         interface = self.routers_client.add_router_interface(
             self.router['id'], subnet_id=subnet01['id'])
-        self.addCleanup(self.routers_client.remove_router_interface,
-                        self.router['id'], subnet_id=subnet01['id'])
+        self.addCleanup(self.remove_router_interface,
+                        self.router['id'], interface['port_id'],
+                        subnet_id=subnet01['id'])
         self.assertEqual(subnet01['id'], interface['subnet_id'])
         self.assertRaises(lib_exc.BadRequest,
                           self.routers_client.add_router_interface,
@@ -89,10 +90,11 @@
     @decorators.idempotent_id('04df80f9-224d-47f5-837a-bf23e33d1c20')
     def test_router_remove_interface_in_use_returns_409(self):
         """Test removing in-use interface from router"""
-        self.routers_client.add_router_interface(self.router['id'],
-                                                 subnet_id=self.subnet['id'])
-        self.addCleanup(self.routers_client.remove_router_interface,
-                        self.router['id'], subnet_id=self.subnet['id'])
+        interface = self.routers_client.add_router_interface(
+            self.router['id'], subnet_id=self.subnet['id'])
+        self.addCleanup(self.remove_router_interface,
+                        self.router['id'], interface['port_id'],
+                        subnet_id=self.subnet['id'])
         self.assertRaises(lib_exc.Conflict,
                           self.routers_client.delete_router,
                           self.router['id'])
diff --git a/tempest/api/volume/admin/test_volume_types.py b/tempest/api/volume/admin/test_volume_types.py
index 5b17afb..0f032c6 100644
--- a/tempest/api/volume/admin/test_volume_types.py
+++ b/tempest/api/volume/admin/test_volume_types.py
@@ -143,6 +143,7 @@
 
         # Get encryption type
         encrypt_type_id = encryption_type['volume_type_id']
+        encryption_id = encryption_type['encryption_id']
         fetched_encryption_type = (
             self.admin_encryption_types_client.show_encryption_type(
                 encrypt_type_id))
@@ -157,7 +158,7 @@
                          'cipher': 'aes-xts-plain64',
                          'control_location': 'back-end'}
         self.admin_encryption_types_client.update_encryption_type(
-            encrypt_type_id, **update_kwargs)
+            encrypt_type_id, encryption_id, **update_kwargs)
         updated_encryption_type = (
             self.admin_encryption_types_client.show_encryption_type(
                 encrypt_type_id))
@@ -174,7 +175,7 @@
 
         # Delete encryption type
         self.admin_encryption_types_client.delete_encryption_type(
-            encrypt_type_id)
+            encrypt_type_id, encryption_id)
         self.admin_encryption_types_client.wait_for_resource_deletion(
             encrypt_type_id)
         deleted_encryption_type = (
diff --git a/tempest/api/volume/test_volume_delete_cascade.py b/tempest/api/volume/test_volume_delete_cascade.py
index 53f1bca..1a50eb5 100644
--- a/tempest/api/volume/test_volume_delete_cascade.py
+++ b/tempest/api/volume/test_volume_delete_cascade.py
@@ -78,8 +78,9 @@
         self._assert_cascade_delete(volume['id'])
 
     @decorators.idempotent_id('59a77ede-609b-4ee8-9f68-fc3c6ffe97b5')
-    @testtools.skipIf(CONF.volume.storage_protocol == 'ceph',
-                      'Skip because of Bug#1677525')
+    @testtools.skipUnless(
+        CONF.volume_feature_enabled.enable_volume_image_dep_tests,
+        'Volume dependency tests disabled')
     def test_volume_from_snapshot_cascade_delete(self):
         """Test deleting a volume with associated volume-associated snapshot
 
diff --git a/tempest/api/volume/test_volumes_negative.py b/tempest/api/volume/test_volumes_negative.py
index d8480df..754b676 100644
--- a/tempest/api/volume/test_volumes_negative.py
+++ b/tempest/api/volume/test_volumes_negative.py
@@ -45,7 +45,7 @@
         image = self.images_client.create_image(
             name=image_name,
             container_format=CONF.image.container_formats[0],
-            disk_format=CONF.image.disk_formats[0],
+            disk_format='raw',
             visibility='private',
             min_disk=CONF.volume.volume_size + CONF.volume.volume_size_extend)
         self.addCleanup(test_utils.call_and_ignore_notfound_exc,
diff --git a/tempest/clients.py b/tempest/clients.py
index e432120..6707429 100644
--- a/tempest/clients.py
+++ b/tempest/clients.py
@@ -183,30 +183,6 @@
             self.placement.ResourceProvidersClient()
 
     def _set_identity_clients(self):
-        # Clients below use the admin endpoint type of Keystone API v2
-        params_v2_admin = {
-            'endpoint_type': CONF.identity.v2_admin_endpoint_type}
-        self.endpoints_client = self.identity_v2.EndpointsClient(
-            **params_v2_admin)
-        self.identity_client = self.identity_v2.IdentityClient(
-            **params_v2_admin)
-        self.tenants_client = self.identity_v2.TenantsClient(
-            **params_v2_admin)
-        self.roles_client = self.identity_v2.RolesClient(**params_v2_admin)
-        self.users_client = self.identity_v2.UsersClient(**params_v2_admin)
-        self.identity_services_client = self.identity_v2.ServicesClient(
-            **params_v2_admin)
-
-        # Clients below use the public endpoint type of Keystone API v2
-        params_v2_public = {
-            'endpoint_type': CONF.identity.v2_public_endpoint_type}
-        self.identity_public_client = self.identity_v2.IdentityClient(
-            **params_v2_public)
-        self.tenants_public_client = self.identity_v2.TenantsClient(
-            **params_v2_public)
-        self.users_public_client = self.identity_v2.UsersClient(
-            **params_v2_public)
-
         # Clients below use the endpoint type of Keystone API v3, which is set
         # in endpoint_type
         params_v3 = {'endpoint_type': CONF.identity.v3_endpoint_type}
@@ -251,16 +227,6 @@
         self.identity_limits_client = \
             self.identity_v3.LimitsClient(**params_v3)
 
-        # Token clients do not use the catalog. They only need default_params.
-        # They read auth_url, so they should only be set if the corresponding
-        # API version is marked as enabled
-        if CONF.identity_feature_enabled.api_v2:
-            if CONF.identity.uri:
-                self.token_client = self.identity_v2.TokenClient(
-                    auth_url=CONF.identity.uri)
-            else:
-                msg = 'Identity v2 API enabled, but no identity.uri set'
-                raise lib_exc.InvalidConfiguration(msg)
         if CONF.identity_feature_enabled.api_v3:
             if CONF.identity.uri_v3:
                 self.token_v3_client = self.identity_v3.V3TokenClient(
diff --git a/tempest/cmd/cleanup_service.py b/tempest/cmd/cleanup_service.py
index b202940..db4407d 100644
--- a/tempest/cmd/cleanup_service.py
+++ b/tempest/cmd/cleanup_service.py
@@ -115,21 +115,32 @@
         return [item for item in item_list
                 if item['tenant_id'] == self.tenant_id]
 
-    def _filter_by_prefix(self, item_list):
-        items = [item for item in item_list
-                 if item['name'].startswith(self.prefix)]
+    def _filter_by_prefix(self, item_list, top_key=None):
+        items = []
+        for item in item_list:
+            name = item[top_key]['name'] if top_key else item['name']
+            if name.startswith(self.prefix):
+                items.append(item)
         return items
 
     def _filter_by_resource_list(self, item_list, attr):
         if attr not in self.resource_list_json:
             return []
-        items = [item for item in item_list if item['id']
-                 in self.resource_list_json[attr].keys()]
+        items = []
+        for item in item_list:
+            item_id = (item['keypair']['name'] if attr == 'keypairs'
+                       else item['id'])
+            if item_id in self.resource_list_json[attr].keys():
+                items.append(item)
         return items
 
     def _filter_out_ids_from_saved(self, item_list, attr):
-        items = [item for item in item_list if item['id']
-                 not in self.saved_state_json[attr].keys()]
+        items = []
+        for item in item_list:
+            item_id = (item['keypair']['name'] if attr == 'keypairs'
+                       else item['id'])
+            if item_id not in self.saved_state_json[attr].keys():
+                items.append(item)
         return items
 
     def list(self):
@@ -294,16 +305,11 @@
         keypairs = client.list_keypairs()['keypairs']
 
         if self.prefix:
-            keypairs = self._filter_by_prefix(keypairs)
+            keypairs = self._filter_by_prefix(keypairs, 'keypair')
         elif self.is_resource_list:
-            keypairs = [keypair for keypair in keypairs
-                        if keypair['keypair']['name']
-                        in self.resource_list_json['keypairs'].keys()]
+            keypairs = self._filter_by_resource_list(keypairs, 'keypairs')
         elif not self.is_save_state:
-            # recreate list removing saved keypairs
-            keypairs = [keypair for keypair in keypairs
-                        if keypair['keypair']['name']
-                        not in self.saved_state_json['keypairs'].keys()]
+            keypairs = self._filter_out_ids_from_saved(keypairs, 'keypairs')
         LOG.debug("List count, %s Keypairs", len(keypairs))
         return keypairs
 
diff --git a/tempest/common/utils/linux/remote_client.py b/tempest/common/utils/linux/remote_client.py
index dd18190..79cc09c 100644
--- a/tempest/common/utils/linux/remote_client.py
+++ b/tempest/common/utils/linux/remote_client.py
@@ -59,14 +59,14 @@
         output = self.exec_command(command)
         selected = []
         pos = None
-        for l in output.splitlines():
-            if pos is None and l.find("TYPE") > 0:
-                pos = l.find("TYPE")
+        for line in output.splitlines():
+            if pos is None and line.find("TYPE") > 0:
+                pos = line.find("TYPE")
                 # Show header line too
-                selected.append(l)
+                selected.append(line)
             # lsblk lists disk type in a column right-aligned with TYPE
-            elif pos is not None and pos > 0 and l[pos:pos + 4] == "disk":
-                selected.append(l)
+            elif pos is not None and pos > 0 and line[pos:pos + 4] == "disk":
+                selected.append(line)
 
         if selected:
             return "\n".join(selected)
@@ -121,9 +121,9 @@
     def _get_dns_servers(self):
         cmd = 'cat /etc/resolv.conf'
         resolve_file = self.exec_command(cmd).strip().split('\n')
-        entries = (l.split() for l in resolve_file)
-        dns_servers = [l[1] for l in entries
-                       if len(l) and l[0] == 'nameserver']
+        entries = (line.split() for line in resolve_file)
+        dns_servers = [line[1] for line in entries
+                       if len(line) and line[0] == 'nameserver']
         return dns_servers
 
     def get_dns_servers(self, timeout=5):
diff --git a/tempest/common/utils/net_downtime.py b/tempest/common/utils/net_downtime.py
index 9675ec8..ec1a4c8 100644
--- a/tempest/common/utils/net_downtime.py
+++ b/tempest/common/utils/net_downtime.py
@@ -22,12 +22,38 @@
 
 LOG = log.getLogger(__name__)
 
+PASSED = 'PASSED'
+FAILED = 'FAILED'
+METADATA_SCRIPT_PATH = '/tmp/metadata_meter_script.sh'
+METADATA_RESULTS_PATH = '/tmp/metadata_meter.log'
+METADATA_PID_PATH = '/tmp/metadata_meter.pid'
+# /proc/uptime is used because it include two decimals in cirros, while
+# `date +%s.%N` does not work in cirros (min granularity is seconds)
+METADATA_SCRIPT = """#!/bin/sh
+echo $$ > %(metadata_meter_pidfile)s
+old_time=$(cut -d" " -f1 /proc/uptime)
+while true; do
+    curl http://169.254.169.254/latest/meta-data/hostname 2>/dev/null | \
+grep -q `hostname`
+    result=$?
+    new_time=$(cut -d" " -f1 /proc/uptime)
+    runtime=$(awk -v new=$new_time -v old=$old_time "BEGIN {print new-old}")
+    old_time=$new_time
+    if [ $result -eq 0 ]; then
+        echo "PASSED $runtime"
+    else
+        echo "FAILED $runtime"
+    fi
+    sleep %(interval)s
+done
+"""
+
 
 class NetDowntimeMeter(fixtures.Fixture):
-    def __init__(self, dest_ip, interval='0.2'):
+    def __init__(self, dest_ip, interval=0.2):
         self.dest_ip = dest_ip
         # Note: for intervals lower than 0.2 ping requires root privileges
-        self.interval = interval
+        self.interval = float(interval)
         self.ping_process = None
 
     def _setUp(self):
@@ -35,18 +61,18 @@
 
     def start_background_pinger(self):
         cmd = ['ping', '-q', '-s1']
-        cmd.append('-i{}'.format(self.interval))
+        cmd.append('-i%g' % self.interval)
         cmd.append(self.dest_ip)
-        LOG.debug("Starting background pinger to '{}' with interval {}".format(
-            self.dest_ip, self.interval))
+        LOG.debug("Starting background pinger to '%s' with interval %g",
+                  self.dest_ip, self.interval)
         self.ping_process = subprocess.Popen(
             cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
         self.addCleanup(self.cleanup)
 
     def cleanup(self):
         if self.ping_process and self.ping_process.poll() is None:
-            LOG.debug('Terminating background pinger with pid {}'.format(
-                self.ping_process.pid))
+            LOG.debug('Terminating background pinger with pid %d',
+                      self.ping_process.pid)
             self.ping_process.terminate()
         self.ping_process = None
 
@@ -57,7 +83,68 @@
         output = self.ping_process.stderr.readline().strip().decode('utf-8')
         if output and len(output.split()[0].split('/')) == 2:
             succ, total = output.split()[0].split('/')
-            return (int(total) - int(succ)) * float(self.interval)
+            return (int(total) - int(succ)) * self.interval
         else:
             LOG.warning('Unexpected output obtained from the pinger: %s',
                         output)
+
+
+class MetadataDowntimeMeter(fixtures.Fixture):
+    def __init__(self, ssh_client,
+                 interval='0.2', script_path=METADATA_SCRIPT_PATH,
+                 output_path=METADATA_RESULTS_PATH,
+                 pidfile_path=METADATA_PID_PATH):
+        self.ssh_client = ssh_client
+        self.interval = interval
+        self.script_path = script_path
+        self.output_path = output_path
+        self.pidfile_path = pidfile_path
+        self.pid = None
+
+    def _setUp(self):
+        self.addCleanup(self.cleanup)
+        self.upload_metadata_script()
+        self.run_metadata_script()
+
+    def upload_metadata_script(self):
+        metadata_script = METADATA_SCRIPT % {
+            'metadata_meter_pidfile': self.pidfile_path,
+            'interval': self.interval}
+        echo_cmd = "echo '{}' > {}".format(
+            metadata_script, self.script_path)
+        chmod_cmd = 'chmod +x {}'.format(self.script_path)
+        self.ssh_client.exec_command(';'.join((echo_cmd, chmod_cmd)))
+        LOG.debug('script created: %s', self.script_path)
+        output = self.ssh_client.exec_command(
+            'cat {}'.format(self.script_path))
+        LOG.debug('script content: %s', output)
+
+    def run_metadata_script(self):
+        self.ssh_client.exec_command('{} > {} &'.format(self.script_path,
+                                                        self.output_path))
+        self.pid = self.ssh_client.exec_command(
+            'cat {}'.format(self.pidfile_path)).strip()
+        LOG.debug('running metadata downtime meter script in background with '
+                  'PID = %s', self.pid)
+
+    def get_results(self):
+        output = self.ssh_client.exec_command(
+            'cat {}'.format(self.output_path))
+        results = {}
+        results['successes'] = output.count(PASSED)
+        results['failures'] = output.count(FAILED)
+        downtime = {PASSED: 0.0, FAILED: 0.0}
+        for line in output.splitlines():
+            key, value = line.strip().split()
+            downtime[key] += float(value)
+
+        results['downtime'] = downtime
+        LOG.debug('metadata downtime meter results: %r', results)
+        return results
+
+    def cleanup(self):
+        if self.pid:
+            self.ssh_client.exec_command('kill {}'.format(self.pid))
+            LOG.debug('killed metadata downtime script with PID %s', self.pid)
+        else:
+            LOG.debug('No metadata downtime script found')
diff --git a/tempest/common/waiters.py b/tempest/common/waiters.py
index e249f35..b4312b7 100644
--- a/tempest/common/waiters.py
+++ b/tempest/common/waiters.py
@@ -154,13 +154,21 @@
 
 
 def wait_for_image_status(client, image_id, status):
-    """Waits for an image to reach a given status.
+    """Waits for an image to reach a given status (or list of them).
 
     The client should have a show_image(image_id) method to get the image.
     The client should also have build_interval and build_timeout attributes.
+
+    status can be either a string or a list of strings that constitute a
+    terminal state that we will return.
     """
     show_image = client.show_image
 
+    if isinstance(status, str):
+        terminal_status = [status]
+    else:
+        terminal_status = status
+
     current_status = 'An unknown status'
     start = int(time.time())
     while int(time.time()) - start < client.build_timeout:
@@ -171,8 +179,8 @@
             image = image['image']
 
         current_status = image['status']
-        if current_status == status:
-            return
+        if current_status in terminal_status:
+            return current_status
         if current_status.lower() == 'killed':
             raise exceptions.ImageKilledException(image_id=image_id,
                                                   status=status)
@@ -184,7 +192,7 @@
     message = ('Image %(image_id)s failed to reach %(status)s state '
                '(current state %(current_status)s) within the required '
                'time (%(timeout)s s).' % {'image_id': image_id,
-                                          'status': status,
+                                          'status': ','.join(terminal_status),
                                           'current_status': current_status,
                                           'timeout': client.build_timeout})
     caller = test_utils.find_test_caller()
@@ -327,8 +335,7 @@
     # Check if image have last store location
     if len(available_stores) == 1:
         exc_cls = lib_exc.OtherRestClientException
-        message = ('Delete from last store location not allowed'
-                   % (image, image_store_deleted))
+        message = 'Delete from last store location not allowed'
         raise exc_cls(message)
     start = int(time.time())
     while int(time.time()) - start < client.build_timeout:
@@ -548,7 +555,7 @@
     interface_status = body['port_state']
     start = int(time.time())
 
-    while(interface_status != status):
+    while interface_status != status:
         time.sleep(client.build_interval)
         body = (client.show_interface(server_id, port_id)
                 ['interfaceAttachment'])
@@ -672,6 +679,28 @@
     raise lib_exc.TimeoutException
 
 
+def wait_for_server_ports_active(client, server_id, is_active, **kwargs):
+    """Wait for all server ports to reach active status
+    :param client: The network client to use when querying the port's status
+    :param server_id: The uuid of the server's ports we need to verify.
+    :param is_active: A function to call to the check port active status.
+    :param kwargs: Additional arguments, if any, to pass to list_ports()
+    """
+    start_time = time.time()
+    while (time.time() - start_time <= client.build_timeout):
+        ports = client.list_ports(device_id=server_id, **kwargs)['ports']
+        if all(is_active(port) for port in ports):
+            LOG.debug("Server ID %s ports are all ACTIVE %s: ",
+                      server_id, ports)
+            return ports
+        LOG.warning("Server ID %s has ports that are not ACTIVE, waiting "
+                    "for state to change on all: %s", server_id, ports)
+        time.sleep(client.build_interval)
+    LOG.error("Server ID %s ports have failed to transition to ACTIVE, "
+              "timing out: %s", server_id, ports)
+    raise lib_exc.TimeoutException
+
+
 def wait_for_ssh(ssh_client, timeout=30):
     """Waits for SSH connection to become usable"""
     start_time = int(time.time())
diff --git a/tempest/config.py b/tempest/config.py
index 8ddb5a4..7719720 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -128,12 +128,18 @@
                     'TLS (https) server certificate.'),
     cfg.URIOpt('uri',
                schemes=['http', 'https'],
+               deprecated_for_removal=True,
+               deprecated_reason='The identity v2 API tests were removed '
+                                 'and this option has no effect',
                help="Full URI of the OpenStack Identity API (Keystone), v2"),
     cfg.URIOpt('uri_v3',
                schemes=['http', 'https'],
                help='Full URI of the OpenStack Identity API (Keystone), v3'),
     cfg.StrOpt('auth_version',
                default='v3',
+               deprecated_for_removal=True,
+               deprecated_reason='Identity v2 API was removed and v3 is '
+                                 'the only available identity API version now',
                help="Identity API version to be used for authentication "
                     "for API tests."),
     cfg.StrOpt('region',
@@ -146,12 +152,16 @@
                default='adminURL',
                choices=['public', 'admin', 'internal',
                         'publicURL', 'adminURL', 'internalURL'],
+               deprecated_for_removal=True,
+               deprecated_reason='This option has no effect',
                help="The admin endpoint type to use for OpenStack Identity "
                     "(Keystone) API v2"),
     cfg.StrOpt('v2_public_endpoint_type',
                default='publicURL',
                choices=['public', 'admin', 'internal',
                         'publicURL', 'adminURL', 'internalURL'],
+               deprecated_for_removal=True,
+               deprecated_reason='This option has no effect',
                help="The public endpoint type to use for OpenStack Identity "
                     "(Keystone) API v2"),
     cfg.StrOpt('v3_endpoint_type',
@@ -234,19 +244,22 @@
                      'impersonation enabled'),
     cfg.BoolOpt('api_v2',
                 default=False,
-                help='Is the v2 identity API enabled',
                 deprecated_for_removal=True,
-                deprecated_reason='The identity v2.0 API was removed in the '
-                                  'Queens release. Tests that exercise the '
-                                  'v2.0 API will be removed from tempest in '
-                                  'the v22.0.0 release. They are kept only to '
-                                  'test stable branches.'),
+                deprecated_reason='The identity v2 API tests were removed '
+                                  'and this option has no effect',
+                help='Is the v2 identity API enabled'),
     cfg.BoolOpt('api_v2_admin',
                 default=True,
-                help="Is the v2 identity admin API available? This setting "
-                     "only applies if api_v2 is set to True."),
+                deprecated_for_removal=True,
+                deprecated_reason='The identity v2 API tests were removed '
+                                  'and this option has no effect',
+                help="Is the v2 identity admin API available?"),
     cfg.BoolOpt('api_v3',
                 default=True,
+                deprecated_for_removal=True,
+                deprecated_reason='Identity v2 API was removed and v3 is '
+                                  'the only available identity API version '
+                                  'now',
                 help='Is the v3 identity API enabled'),
     cfg.ListOpt('api_extensions',
                 default=['all'],
@@ -405,7 +418,11 @@
                help="Specify destination host for live-migration and cold"
                     " migration. If option is not set tests will use host"
                     " automatically."),
-
+    cfg.StrOpt('target_hosts_to_avoid',
+               default='-ironic',
+               help="When aggregating available hypervisors for testing,"
+               " avoid migrating to and booting any test VM on hosts with"
+               " a name that matches the provided pattern"),
 ]
 
 placement_group = cfg.OptGroup(name='placement',
@@ -456,6 +473,15 @@
                      "the '.' with '-' to comply with fqdn hostname. Nova "
                      "changed that in Wallaby cycle, if your cloud is older "
                      "than wallaby then you can keep/make it False."),
+    cfg.StrOpt('dhcp_domain',
+               default='.novalocal',
+               help="Configure a fully-qualified domain name for instance "
+                    "hostnames. The value is suffixed to instance hostname "
+                    "from the database to construct the hostname that "
+                    "appears in the metadata API. To disable this behavior "
+                    "(for example in order to correctly support "
+                    "microversion's 2.94 FQDN hostnames), set this to the "
+                    "empty string."),
     cfg.BoolOpt('change_password',
                 default=False,
                 help="Does the test environment support changing the admin "
@@ -638,9 +664,14 @@
                       "service, operator should set this parameter to True "
                       "if 'image_cache_dir' is set in glance-api.conf")),
     cfg.StrOpt('http_image',
-               default='http://download.cirros-cloud.net/0.3.1/'
-               'cirros-0.3.1-x86_64-uec.tar.gz',
+               default='http://download.cirros-cloud.net/0.6.2/'
+               'cirros-0.6.2-x86_64-uec.tar.gz',
                help='http accessible image'),
+    cfg.StrOpt('http_qcow2_image',
+               default='http://download.cirros-cloud.net/0.6.2/'
+               'cirros-0.6.2-x86_64-disk.img',
+               help='http qcow2 accessible image which will be used '
+                    'for image conversion if enabled.'),
     cfg.IntOpt('build_timeout',
                default=300,
                help="Timeout in seconds to wait for an image to "
@@ -650,14 +681,18 @@
                help="Time in seconds between image operation status "
                     "checks."),
     cfg.ListOpt('container_formats',
-                default=['ami', 'ari', 'aki', 'bare', 'ovf', 'ova'],
+                default=['bare', 'ami', 'ari', 'aki', 'ovf', 'ova'],
                 help="A list of image's container formats "
                      "users can specify."),
     cfg.ListOpt('disk_formats',
-                default=['ami', 'ari', 'aki', 'vhd', 'vmdk', 'raw', 'qcow2',
+                default=['qcow2', 'raw', 'ami', 'ari', 'aki', 'vhd', 'vmdk',
                          'vdi', 'iso', 'vhdx'],
                 help="A list of image's disk formats "
-                     "users can specify.")
+                     "users can specify."),
+    cfg.StrOpt('images_manifest_file',
+               default=None,
+               help="A path to a manifest.yml generated using the "
+                    "os-test-images project"),
 ]
 
 image_feature_group = cfg.OptGroup(name='image-feature-enabled',
@@ -690,6 +725,14 @@
                 help=('Is show_multiple_locations enabled in glance. '
                       'Note that at least one http store must be enabled as '
                       'well, because we use that location scheme to test.')),
+    cfg.BoolOpt('image_conversion',
+                default=False,
+                help=('Is image_conversion enabled in glance.')),
+    cfg.BoolOpt('image_format_enforcement',
+                default=True,
+                help=('Indicates that image format is enforced by glance, '
+                      'such that we should not expect to be able to upload '
+                      'bad images for testing other services.')),
 ]
 
 network_group = cfg.OptGroup(name='network',
@@ -918,14 +961,20 @@
                     "connect_method=floating."),
     cfg.StrOpt('ssh_key_type',
                default='ecdsa',
-               help='Type of key to use for ssh connections. '
-                    'Valid types are rsa, ecdsa'),
+               choices=['ecdsa', 'rsa'],
+               help='Type of key to use for ssh connections.'),
     cfg.FloatOpt('allowed_network_downtime',
                  default=5.0,
                  help="Allowed VM network connection downtime during live "
                       "migration, in seconds. "
                       "When the measured downtime exceeds this value, an "
                       "exception is raised."),
+    cfg.FloatOpt('allowed_metadata_downtime',
+                 default=6.0,
+                 help="Allowed VM metadata connection downtime during live "
+                      "migration, in seconds. "
+                      "When the measured downtime exceeds this value, an "
+                      "exception is raised."),
 ]
 
 volume_group = cfg.OptGroup(name='volume',
@@ -940,7 +989,7 @@
                help='Timeout in seconds to wait for a volume to become '
                     'available.'),
     cfg.StrOpt('catalog_type',
-               default='volumev3',
+               default='block-storage',
                help="Catalog type of the Volume Service"),
     cfg.StrOpt('region',
                default='',
@@ -1072,8 +1121,9 @@
                help='Volume types used for data volumes. Multiple volume '
                     'types can be assigned.'),
     cfg.BoolOpt('enable_volume_image_dep_tests',
+                deprecated_name='volume_image_dep_tests',
                 default=True,
-                help='Run tests for dependencies between images, volumes'
+                help='Run tests for dependencies between images, volumes '
                 'and instance snapshots')
 ]
 
@@ -1179,7 +1229,10 @@
                default='icmp',
                choices=('icmp', 'tcp', 'udp'),
                help='The protocol used in security groups tests to check '
-                    'connectivity.')
+                    'connectivity.'),
+    cfg.StrOpt('target_dir',
+               default='/tmp',
+               help='Directory in which to write the timestamp file.'),
 ]
 
 
@@ -1302,9 +1355,9 @@
 
 The best use case is investigating used resources of one test.
 A test can be run as follows:
- $ stestr run --pdb TEST_ID
+$ stestr run --pdb TEST_ID
 or
- $ python -m testtools.run TEST_ID"""),
+$ python -m testtools.run TEST_ID"""),
     cfg.StrOpt('resource_name_prefix',
                default='tempest',
                help="Define the prefix name for the resources created by "
diff --git a/tempest/hacking/checks.py b/tempest/hacking/checks.py
index 1c9c55b..c81ec03 100644
--- a/tempest/hacking/checks.py
+++ b/tempest/hacking/checks.py
@@ -16,7 +16,6 @@
 import re
 
 from hacking import core
-import pycodestyle
 
 
 PYTHON_CLIENTS = ['cinder', 'glance', 'keystone', 'nova', 'swift', 'neutron',
@@ -40,22 +39,22 @@
 
 
 @core.flake8ext
-def import_no_clients_in_api_and_scenario_tests(physical_line, filename):
+def import_no_clients_in_api_and_scenario_tests(logical_line, filename):
     """Check for client imports from tempest/api & tempest/scenario tests
 
     T102: Cannot import OpenStack python clients
     """
 
     if "tempest/api" in filename or "tempest/scenario" in filename:
-        res = PYTHON_CLIENT_RE.match(physical_line)
+        res = PYTHON_CLIENT_RE.match(logical_line)
         if res:
-            return (physical_line.find(res.group(1)),
+            return (logical_line.find(res.group(1)),
                     ("T102: python clients import not allowed"
                      " in tempest/api/* or tempest/scenario/* tests"))
 
 
 @core.flake8ext
-def scenario_tests_need_service_tags(physical_line, filename,
+def scenario_tests_need_service_tags(logical_line, filename,
                                      previous_logical):
     """Check that scenario tests have service tags
 
@@ -63,28 +62,28 @@
     """
 
     if 'tempest/scenario/' in filename and '/test_' in filename:
-        if TEST_DEFINITION.match(physical_line):
+        if TEST_DEFINITION.match(logical_line):
             if not SCENARIO_DECORATOR.match(previous_logical):
-                return (physical_line.find('def'),
+                return (logical_line.find('def'),
                         "T104: Scenario tests require a service decorator")
 
 
 @core.flake8ext
-def no_setup_teardown_class_for_tests(physical_line, filename):
+def no_setup_teardown_class_for_tests(logical_line, filename, noqa):
 
-    if pycodestyle.noqa(physical_line):
+    if noqa:
         return
 
     if 'tempest/test.py' in filename or 'tempest/lib/' in filename:
         return
 
-    if SETUP_TEARDOWN_CLASS_DEFINITION.match(physical_line):
-        return (physical_line.find('def'),
+    if SETUP_TEARDOWN_CLASS_DEFINITION.match(logical_line):
+        return (logical_line.find('def'),
                 "T105: (setUp|tearDown)Class can not be used in tests")
 
 
 @core.flake8ext
-def service_tags_not_in_module_path(physical_line, filename):
+def service_tags_not_in_module_path(logical_line, filename):
     """Check that a service tag isn't in the module path
 
     A service tag should only be added if the service name isn't already in
@@ -96,14 +95,14 @@
     # created for services like heat which would cause false negatives for
     # those tests, so just exclude the scenario tests.
     if 'tempest/scenario' not in filename:
-        matches = SCENARIO_DECORATOR.match(physical_line)
+        matches = SCENARIO_DECORATOR.match(logical_line)
         if matches:
             services = matches.group(1).split(',')
             for service in services:
                 service_name = service.strip().strip("'")
                 modulepath = os.path.split(filename)[0]
                 if service_name in modulepath:
-                    return (physical_line.find(service_name),
+                    return (logical_line.find(service_name),
                             "T107: service tag should not be in path")
 
 
@@ -140,28 +139,27 @@
                "decorators.skip_because from tempest.lib")
 
 
-def _common_service_clients_check(logical_line, physical_line, filename):
+def _common_service_clients_check(logical_line, filename, noqa):
+    if noqa:
+        return False
+
     if not re.match('tempest/(lib/)?services/.*', filename):
         return False
 
-    if not METHOD.match(physical_line):
-        return False
-
-    if pycodestyle.noqa(physical_line):
+    if not METHOD.match(logical_line):
         return False
 
     return True
 
 
 @core.flake8ext
-def get_resources_on_service_clients(physical_line, logical_line, filename,
-                                     line_number, lines):
+def get_resources_on_service_clients(logical_line, filename,
+                                     line_number, lines, noqa):
     """Check that service client names of GET should be consistent
 
     T110
     """
-    if not _common_service_clients_check(logical_line, physical_line,
-                                         filename):
+    if not _common_service_clients_check(logical_line, filename, noqa):
         return
 
     for line in lines[line_number:]:
@@ -182,14 +180,13 @@
 
 
 @core.flake8ext
-def delete_resources_on_service_clients(physical_line, logical_line, filename,
-                                        line_number, lines):
+def delete_resources_on_service_clients(logical_line, filename,
+                                        line_number, lines, noqa):
     """Check that service client names of DELETE should be consistent
 
     T111
     """
-    if not _common_service_clients_check(logical_line, physical_line,
-                                         filename):
+    if not _common_service_clients_check(logical_line, filename, noqa):
         return
 
     for line in lines[line_number:]:
@@ -262,7 +259,7 @@
             'oslo_config' in logical_line):
         msg = ('T114: tempest.lib can not have any dependency on tempest '
                'config.')
-        yield(0, msg)
+        yield (0, msg)
 
 
 @core.flake8ext
@@ -281,7 +278,7 @@
 
     if not re.match(r'.\/tempest\/api\/.*\/admin\/.*', filename):
         msg = 'T115: All admin tests should exist under admin path.'
-        yield(0, msg)
+        yield (0, msg)
 
 
 @core.flake8ext
@@ -293,11 +290,11 @@
     result = EX_ATTRIBUTE.search(logical_line)
     msg = ("[T116] Unsupported 'message' Exception attribute in PY3")
     if result:
-        yield(0, msg)
+        yield (0, msg)
 
 
 @core.flake8ext
-def negative_test_attribute_always_applied_to_negative_tests(physical_line,
+def negative_test_attribute_always_applied_to_negative_tests(logical_line,
                                                              filename):
     """Check ``@decorators.attr(type=['negative'])`` applied to negative tests.
 
@@ -307,13 +304,13 @@
 
     if re.match(r'.\/tempest\/api\/.*_negative.*', filename):
 
-        if NEGATIVE_TEST_DECORATOR.match(physical_line):
+        if NEGATIVE_TEST_DECORATOR.match(logical_line):
             _HAVE_NEGATIVE_DECORATOR = True
             return
 
-        if TEST_DEFINITION.match(physical_line):
+        if TEST_DEFINITION.match(logical_line):
             if not _HAVE_NEGATIVE_DECORATOR:
-                return (
+                yield (
                     0, "T117: Must apply `@decorators.attr(type=['negative'])`"
                        " to all negative API tests"
                 )
diff --git a/tempest/api/identity/admin/v2/__init__.py b/tempest/lib/api_schema/response/compute/v2_96/__init__.py
similarity index 100%
rename from tempest/api/identity/admin/v2/__init__.py
rename to tempest/lib/api_schema/response/compute/v2_96/__init__.py
diff --git a/tempest/lib/api_schema/response/compute/v2_96/servers.py b/tempest/lib/api_schema/response/compute/v2_96/servers.py
new file mode 100644
index 0000000..7036a11
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_96/servers.py
@@ -0,0 +1,62 @@
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+import copy
+
+from tempest.lib.api_schema.response.compute.v2_89 import servers as servers289
+
+
+###########################################################################
+#
+# 2.96:
+#
+# The attachment_id and bdm_uuid parameter is now returned in the response body
+# of the following calls:
+# The pinned_availability_zone parameter is now returned in the response body
+# of the following calls:
+#
+# - GET /servers/detail
+# - GET /servers/{server_id}
+###########################################################################
+
+get_server = copy.deepcopy(servers289.get_server)
+get_server['response_body']['properties']['server'][
+    'properties'].update(
+        {'pinned_availability_zone': {'type': ['string', 'null']}})
+
+list_servers_detail = copy.deepcopy(servers289.list_servers_detail)
+list_servers_detail['response_body']['properties']['servers']['items'][
+    'properties'].update(
+        {'pinned_availability_zone': {'type': ['string', 'null']}})
+
+# NOTE(zhufl): Below are the unchanged schema in this microversion. We
+# need to keep this schema in this file to have the generic way to select the
+# right schema based on self.schema_versions_info mapping in service client.
+# ****** Schemas unchanged since microversion 2.89***
+attach_volume = copy.deepcopy(servers289.attach_volume)
+show_volume_attachment = copy.deepcopy(servers289.show_volume_attachment)
+list_volume_attachments = copy.deepcopy(servers289.list_volume_attachments)
+rebuild_server = copy.deepcopy(servers289.rebuild_server)
+rebuild_server_with_admin_pass = copy.deepcopy(
+    servers289.rebuild_server_with_admin_pass)
+update_server = copy.deepcopy(servers289.update_server)
+list_servers = copy.deepcopy(servers289.list_servers)
+show_server_diagnostics = copy.deepcopy(servers289.show_server_diagnostics)
+get_remote_consoles = copy.deepcopy(servers289.get_remote_consoles)
+list_tags = copy.deepcopy(servers289.list_tags)
+update_all_tags = copy.deepcopy(servers289.update_all_tags)
+delete_all_tags = copy.deepcopy(servers289.delete_all_tags)
+check_tag_existence = copy.deepcopy(servers289.check_tag_existence)
+update_tag = copy.deepcopy(servers289.update_tag)
+delete_tag = copy.deepcopy(servers289.delete_tag)
+show_instance_action = copy.deepcopy(servers289.show_instance_action)
+create_backup = copy.deepcopy(servers289.create_backup)
diff --git a/tempest/lib/auth.py b/tempest/lib/auth.py
index 8bdf98e..12fffdb 100644
--- a/tempest/lib/auth.py
+++ b/tempest/lib/auth.py
@@ -21,6 +21,7 @@
 from urllib import parse as urlparse
 
 from oslo_log import log as logging
+from oslo_utils import timeutils
 
 from tempest.lib import exceptions
 from tempest.lib.services.identity.v2 import token_client as json_v2id
@@ -419,8 +420,7 @@
     def is_expired(self, auth_data):
         _, access = auth_data
         expiry = self._parse_expiry_time(access['token']['expires'])
-        return (expiry - self.token_expiry_threshold <=
-                datetime.datetime.utcnow())
+        return (expiry - self.token_expiry_threshold <= timeutils.utcnow())
 
 
 class KeystoneV3AuthProvider(KeystoneAuthProvider):
@@ -595,8 +595,7 @@
     def is_expired(self, auth_data):
         _, access = auth_data
         expiry = self._parse_expiry_time(access['expires_at'])
-        return (expiry - self.token_expiry_threshold <=
-                datetime.datetime.utcnow())
+        return (expiry - self.token_expiry_threshold <= timeutils.utcnow())
 
 
 def is_identity_version_supported(identity_version):
diff --git a/tempest/lib/common/cred_provider.py b/tempest/lib/common/cred_provider.py
index 2da206f..93b9586 100644
--- a/tempest/lib/common/cred_provider.py
+++ b/tempest/lib/common/cred_provider.py
@@ -76,6 +76,10 @@
         return
 
     @abc.abstractmethod
+    def get_domain_manager_creds(self):
+        return
+
+    @abc.abstractmethod
     def get_domain_member_creds(self):
         return
 
@@ -92,6 +96,10 @@
         return
 
     @abc.abstractmethod
+    def get_project_manager_creds(self):
+        return
+
+    @abc.abstractmethod
     def get_project_member_creds(self):
         return
 
diff --git a/tempest/lib/common/dynamic_creds.py b/tempest/lib/common/dynamic_creds.py
index 6814373..6c90938 100644
--- a/tempest/lib/common/dynamic_creds.py
+++ b/tempest/lib/common/dynamic_creds.py
@@ -432,7 +432,7 @@
                         cred_type = [cred_type]
                     credentials = self._create_creds(
                         roles=cred_type, scope=scope, project_id=project_id)
-                elif credential_type in [['member'], ['reader']]:
+                elif credential_type in [['manager'], ['member'], ['reader']]:
                     credentials = self._create_creds(
                         roles=credential_type, scope=scope,
                         project_id=project_id)
@@ -492,6 +492,9 @@
     def get_domain_admin_creds(self):
         return self.get_credentials(['admin'], scope='domain')
 
+    def get_domain_manager_creds(self):
+        return self.get_credentials(['manager'], scope='domain')
+
     def get_domain_member_creds(self):
         return self.get_credentials(['member'], scope='domain')
 
@@ -504,6 +507,9 @@
     def get_project_alt_admin_creds(self):
         return self.get_credentials(['alt_admin'], scope='project')
 
+    def get_project_manager_creds(self):
+        return self.get_credentials(['manager'], scope='project')
+
     def get_project_member_creds(self):
         return self.get_credentials(['member'], scope='project')
 
diff --git a/tempest/lib/common/http.py b/tempest/lib/common/http.py
index d163968..5bdcecd 100644
--- a/tempest/lib/common/http.py
+++ b/tempest/lib/common/http.py
@@ -60,6 +60,14 @@
             retry = urllib3.util.Retry(redirect=False)
         r = super(ClosingProxyHttp, self).request(method, url, retries=retry,
                                                   *args, **new_kwargs)
+
+        # Clearing the pool is necessary to free memory that holds certificates
+        # loaded by the HTTPConnection class in urllib3. This line can be
+        # removed once we require a newer version of urllib3 (e.g., 2.2.3) that
+        # does not retain certificates in memory for each HTTPConnection
+        # managed by the PoolManager.
+        self.clear()
+
         if not kwargs.get('preload_content', True):
             # This means we asked urllib3 for streaming content, so we
             # need to return the raw response and not read any data yet
@@ -114,6 +122,14 @@
             retry = urllib3.util.Retry(redirect=False)
         r = super(ClosingHttp, self).request(method, url, retries=retry,
                                              *args, **new_kwargs)
+
+        # Clearing the pool is necessary to free memory that holds certificates
+        # loaded by the HTTPConnection class in urllib3. This line can be
+        # removed once we require a newer version of urllib3 (e.g., 2.2.3) that
+        # does not retain certificates in memory for each HTTPConnection
+        # managed by the PoolManager.
+        self.clear()
+
         if not kwargs.get('preload_content', True):
             # This means we asked urllib3 for streaming content, so we
             # need to return the raw response and not read any data yet
diff --git a/tempest/lib/common/preprov_creds.py b/tempest/lib/common/preprov_creds.py
index 6d948cf..3ba7db1 100644
--- a/tempest/lib/common/preprov_creds.py
+++ b/tempest/lib/common/preprov_creds.py
@@ -353,6 +353,13 @@
         self._creds['domain_admin'] = domain_admin
         return domain_admin
 
+    def get_domain_manager_creds(self):
+        if self._creds.get('domain_manager'):
+            return self._creds.get('domain_manager')
+        domain_manager = self._get_creds(['manager'], scope='domain')
+        self._creds['domain_manager'] = domain_manager
+        return domain_manager
+
     def get_domain_member_creds(self):
         if self._creds.get('domain_member'):
             return self._creds.get('domain_member')
@@ -378,6 +385,13 @@
         # TODO(gmann): Implement alt admin hash.
         return
 
+    def get_project_manager_creds(self):
+        if self._creds.get('project_manager'):
+            return self._creds.get('project_manager')
+        project_manager = self._get_creds(['manager'], scope='project')
+        self._creds['project_manager'] = project_manager
+        return project_manager
+
     def get_project_member_creds(self):
         if self._creds.get('project_member'):
             return self._creds.get('project_member')
diff --git a/tempest/lib/common/rest_client.py b/tempest/lib/common/rest_client.py
index 4f9e9ba..b360569 100644
--- a/tempest/lib/common/rest_client.py
+++ b/tempest/lib/common/rest_client.py
@@ -46,6 +46,8 @@
 JSONSCHEMA_VALIDATOR = jsonschema_validator.JSONSCHEMA_VALIDATOR
 FORMAT_CHECKER = jsonschema_validator.FORMAT_CHECKER
 
+RESOURCE_LIST_JSON = "resource_list.json"
+
 
 class RestClient(object):
     """Unified OpenStack RestClient class
@@ -879,6 +881,11 @@
                 resp_body = self._parse_resp(resp_body)
             raise exceptions.Gone(resp_body, resp=resp)
 
+        if resp.status == 406:
+            if parse_resp:
+                resp_body = self._parse_resp(resp_body)
+            raise exceptions.NotAcceptable(resp_body, resp=resp)
+
         if resp.status == 409:
             if parse_resp:
                 resp_body = self._parse_resp(resp_body)
@@ -1045,7 +1052,7 @@
             data[res_type].update({res_dict.get('name'): ""})
 
         self.rec_rw_lock.acquire_write_lock()
-        with open("resource_list.json", 'w+') as f:
+        with open(RESOURCE_LIST_JSON, 'w+') as f:
             f.write(json.dumps(data, indent=2, separators=(',', ': ')))
         self.rec_rw_lock.release_write_lock()
 
@@ -1059,7 +1066,7 @@
 
         self.rec_rw_lock.acquire_read_lock()
         try:
-            with open('resource_list.json', 'rb') as f:
+            with open(RESOURCE_LIST_JSON, 'rb') as f:
                 data = json.load(f)
         except IOError:
             data = {}
diff --git a/tempest/lib/exceptions.py b/tempest/lib/exceptions.py
index dd7885e..0242de2 100644
--- a/tempest/lib/exceptions.py
+++ b/tempest/lib/exceptions.py
@@ -94,6 +94,11 @@
     message = "Object not found"
 
 
+class NotAcceptable(ClientRestClientException):
+    status_code = 406
+    message = "Not Acceptable"
+
+
 class Conflict(ClientRestClientException):
     status_code = 409
     message = "Conflict with state of target resource"
diff --git a/tempest/lib/services/compute/servers_client.py b/tempest/lib/services/compute/servers_client.py
index 1b93f91..e91c87a 100644
--- a/tempest/lib/services/compute/servers_client.py
+++ b/tempest/lib/services/compute/servers_client.py
@@ -45,6 +45,7 @@
 from tempest.lib.api_schema.response.compute.v2_8 import servers as schemav28
 from tempest.lib.api_schema.response.compute.v2_89 import servers as schemav289
 from tempest.lib.api_schema.response.compute.v2_9 import servers as schemav29
+from tempest.lib.api_schema.response.compute.v2_96 import servers as schemav296
 from tempest.lib.common import rest_client
 from tempest.lib.services.compute import base_compute_client
 
@@ -75,7 +76,8 @@
         {'min': '2.73', 'max': '2.74', 'schema': schemav273},
         {'min': '2.75', 'max': '2.78', 'schema': schemav275},
         {'min': '2.79', 'max': '2.88', 'schema': schemav279},
-        {'min': '2.89', 'max': None, 'schema': schemav289}]
+        {'min': '2.89', 'max': '2.95', 'schema': schemav289},
+        {'min': '2.96', 'max': None, 'schema': schemav296}]
 
     def __init__(self, auth_provider, service, region,
                  enable_instance_password=True, **kwargs):
diff --git a/tempest/lib/services/object_storage/account_client.py b/tempest/lib/services/object_storage/account_client.py
index d7ce526..7bf0dcd 100644
--- a/tempest/lib/services/object_storage/account_client.py
+++ b/tempest/lib/services/object_storage/account_client.py
@@ -39,11 +39,13 @@
         headers = {}
         if create_update_metadata:
             for key in create_update_metadata:
-                metadata_header_name = create_update_metadata_prefix + key
+                metadata_header_name = create_update_metadata_prefix + \
+                    key.replace('_', '-')
                 headers[metadata_header_name] = create_update_metadata[key]
         if delete_metadata:
             for key in delete_metadata:
-                headers[delete_metadata_prefix + key] = delete_metadata[key]
+                headers[delete_metadata_prefix + key.replace(
+                    '_', '-')] = delete_metadata[key]
 
         resp, body = self.post('', headers=headers, body=None)
         self.expected_success([200, 204], resp.status)
diff --git a/tempest/lib/services/object_storage/container_client.py b/tempest/lib/services/object_storage/container_client.py
index 47edf70..641c084 100644
--- a/tempest/lib/services/object_storage/container_client.py
+++ b/tempest/lib/services/object_storage/container_client.py
@@ -41,7 +41,12 @@
         """
         url = str(container_name)
 
-        resp, body = self.put(url, body=None, headers=headers)
+        new_headers = {}
+        for key in headers:
+            new_key = key.replace('_', '-')
+            new_headers[new_key] = headers[key]
+
+        resp, body = self.put(url, body=None, headers=new_headers)
         self.expected_success([201, 202, 204], resp.status)
         return resp, body
 
@@ -74,11 +79,13 @@
         headers = {}
         if create_update_metadata:
             for key in create_update_metadata:
-                metadata_header_name = create_update_metadata_prefix + key
+                metadata_header_name = create_update_metadata_prefix + \
+                    key.replace('_', '-')
                 headers[metadata_header_name] = create_update_metadata[key]
         if delete_metadata:
             for key in delete_metadata:
-                headers[delete_metadata_prefix + key] = delete_metadata[key]
+                headers[delete_metadata_prefix + key.replace(
+                    '_', '-')] = delete_metadata[key]
 
         resp, body = self.post(url, headers=headers, body=None)
         self.expected_success(204, resp.status)
diff --git a/tempest/lib/services/volume/v3/encryption_types_client.py b/tempest/lib/services/volume/v3/encryption_types_client.py
index 7cced57..f6f2fd5 100644
--- a/tempest/lib/services/volume/v3/encryption_types_client.py
+++ b/tempest/lib/services/volume/v3/encryption_types_client.py
@@ -69,21 +69,21 @@
         self.validate_response(schema.create_encryption_type, resp, body)
         return rest_client.ResponseBody(resp, body)
 
-    def delete_encryption_type(self, volume_type_id):
+    def delete_encryption_type(self, volume_type_id, encryption_id):
         """Delete the encryption type for the specified volume-type."""
         resp, body = self.delete(
-            "/types/%s/encryption/provider" % volume_type_id)
+            "/types/%s/encryption/%s" % (volume_type_id, encryption_id))
         self.validate_response(schema.delete_encryption_type, resp, body)
         return rest_client.ResponseBody(resp, body)
 
-    def update_encryption_type(self, volume_type_id, **kwargs):
+    def update_encryption_type(self, volume_type_id, encryption_id, **kwargs):
         """Update an encryption type for an existing volume type.
 
         For a full list of available parameters, please refer to the official
         API reference:
         https://docs.openstack.org/api-ref/block-storage/v3/index.html#update-an-encryption-type
         """
-        url = "/types/%s/encryption/provider" % volume_type_id
+        url = "/types/%s/encryption/%s" % (volume_type_id, encryption_id)
         put_body = json.dumps({'encryption': kwargs})
         resp, body = self.put(url, put_body)
         body = json.loads(body)
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index 714a7c7..57bc2e9 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -851,7 +851,7 @@
                     kernel_img_path = os.path.join(extract_dir, fname)
                 elif re.search(r'(.*-initrd.*|ari-.*/image$)', fname):
                     ramdisk_img_path = os.path.join(extract_dir, fname)
-                elif re.search(f'(.*\\.img$|ami-.*/image$)', fname):
+                elif re.search(r'(.*\\.img$|ami-.*/image$)', fname):
                     img_path = os.path.join(extract_dir, fname)
             # Create the kernel image.
             kparams = {
@@ -923,6 +923,19 @@
         if not isinstance(exc, lib_exc.SSHTimeout):
             LOG.debug('Network information on a devstack host')
 
+    def get_snapshot_id(self, bdms):
+        if isinstance(bdms, str):
+            bdms = json.loads(bdms)
+        snapshot_id = None
+        for bdm in bdms:
+            # Look for the block device mapping that actually has a
+            # snapshot. If the server has ephemeral or swap disk, their
+            # block device mappings will be present with snapshot_id = None
+            if 'snapshot_id' in bdm and bdm['snapshot_id'] is not None:
+                snapshot_id = bdm['snapshot_id']
+                break
+        return snapshot_id
+
     def create_server_snapshot(self, server, name=None, **kwargs):
         """Creates server snapshot"""
         # Glance client
@@ -949,20 +962,19 @@
         snapshot_image = _image_client.show_image(image_id)
         image_props = snapshot_image
 
-        bdm = image_props.get('block_device_mapping')
-        if bdm:
-            bdm = json.loads(bdm)
-            if bdm and 'snapshot_id' in bdm[0]:
-                snapshot_id = bdm[0]['snapshot_id']
-                self.addCleanup(
-                    self.snapshots_client.wait_for_resource_deletion,
-                    snapshot_id)
-                self.addCleanup(test_utils.call_and_ignore_notfound_exc,
-                                self.snapshots_client.delete_snapshot,
-                                snapshot_id)
-                waiters.wait_for_volume_resource_status(self.snapshots_client,
-                                                        snapshot_id,
-                                                        'available')
+        bdms = image_props.get('block_device_mapping')
+        if bdms:
+            snapshot_id = self.get_snapshot_id(bdms)
+            self.assertIsNotNone(snapshot_id)
+            self.addCleanup(
+                self.snapshots_client.wait_for_resource_deletion,
+                snapshot_id)
+            self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                            self.snapshots_client.delete_snapshot,
+                            snapshot_id)
+            waiters.wait_for_volume_resource_status(
+                self.snapshots_client, snapshot_id, 'available')
+
         image_name = snapshot_image['name']
         self.assertEqual(name, image_name)
         LOG.debug("Created snapshot image %s for server %s",
@@ -1098,8 +1110,6 @@
 
         if ip_addr and not kwargs.get('fixed_ips'):
             kwargs['fixed_ips'] = 'ip_address=%s' % ip_addr
-        ports = self.os_admin.ports_client.list_ports(
-            device_id=server['id'], **kwargs)['ports']
 
         # A port can have more than one IP address in some cases.
         # If the network is dual-stack (IPv4 + IPv6), this port is associated
@@ -1114,6 +1124,18 @@
             return (port['status'] == 'ACTIVE' or
                     port.get('binding:vnic_type') == 'baremetal')
 
+        # Wait for all compute ports to be ACTIVE.
+        # This will raise a TimeoutException if that does not happen.
+        client = self.os_admin.ports_client
+        try:
+            ports = waiters.wait_for_server_ports_active(
+                client=client, server_id=server['id'],
+                is_active=_is_active, **kwargs)
+        except lib_exc.TimeoutException:
+            LOG.error("Server ports failed transitioning to ACTIVE for "
+                      "server: %s", server)
+            raise
+
         port_map = [(p["id"], fxip["ip_address"])
                     for p in ports
                     for fxip in p["fixed_ips"]
@@ -1121,7 +1143,8 @@
                         _is_active(p))]
         inactive = [p for p in ports if p['status'] != 'ACTIVE']
         if inactive:
-            LOG.warning("Instance has ports that are not ACTIVE: %s", inactive)
+            # This should just be Ironic ports, see _is_active() above
+            LOG.debug("Instance has ports that are not ACTIVE: %s", inactive)
 
         self.assertNotEmpty(port_map,
                             "No IPv4 addresses found in: %s" % ports)
@@ -1176,14 +1199,15 @@
         except (KeyError, IndexError):
             return None
 
-    def associate_floating_ip(self, floating_ip, server):
+    def associate_floating_ip(self, floating_ip, server, ip_addr=None,
+                              **kwargs):
         """Associate floating ip to server
 
         This wrapper utility attaches the floating_ip for
         the respective port_id of server
         """
-        port_id, _ = self.get_server_port_id_and_ip4(server)
-        kwargs = dict(port_id=port_id)
+        port_id, _ = self.get_server_port_id_and_ip4(server, ip_addr=ip_addr)
+        kwargs.update({"port_id": port_id})
         floating_ip = self.floating_ips_client.update_floatingip(
             floating_ip['id'], **kwargs)['floatingip']
         self.assertEqual(port_id, floating_ip['port_id'])
@@ -1227,7 +1251,7 @@
         # Default the directory in which to write the timestamp file to /tmp
         # and only use the mount_path as the target directory if we mounted
         # dev_name to mount_path.
-        target_dir = '/tmp'
+        target_dir = CONF.scenario.target_dir
         if dev_name is not None:
             mount_path = os.path.join(mount_path, dev_name)
             ssh_client.make_fs(dev_name, fs=fs)
@@ -1266,7 +1290,7 @@
         # Default the directory from which to read the timestamp file to /tmp
         # and only use the mount_path as the target directory if we mounted
         # dev_name to mount_path.
-        target_dir = '/tmp'
+        target_dir = CONF.scenario.target_dir
         if dev_name is not None:
             mount_path = os.path.join(mount_path, dev_name)
             ssh_client.mkdir(mount_path)
@@ -1550,8 +1574,8 @@
             floating_ip = (self.floating_ips_client.
                            show_floatingip(floatingip_id)['floatingip'])
             if status == floating_ip['status']:
-                LOG.info("FloatingIP: {fp} is at status: {st}"
-                         .format(fp=floating_ip, st=status))
+                LOG.info("FloatingIP: %s is at status: %s",
+                         floating_ip, status)
             return status == floating_ip['status']
 
         if not test_utils.call_until_true(refresh,
diff --git a/tempest/scenario/test_instances_with_cinder_volumes.py b/tempest/scenario/test_instances_with_cinder_volumes.py
index b9ac2c8..0ddbec1 100644
--- a/tempest/scenario/test_instances_with_cinder_volumes.py
+++ b/tempest/scenario/test_instances_with_cinder_volumes.py
@@ -80,7 +80,7 @@
                 for host in zone['hosts']:
                     if 'nova-compute' in zone['hosts'][host] and \
                         zone['hosts'][host]['nova-compute']['available'] and \
-                        not host.endswith('-ironic'):
+                        CONF.compute.target_hosts_to_avoid not in host:
                         hosts.append({'zone': zone['zoneName'],
                                       'host_name': host})
 
diff --git a/tempest/scenario/test_network_advanced_server_ops.py b/tempest/scenario/test_network_advanced_server_ops.py
index 911ff42..f4ee98d 100644
--- a/tempest/scenario/test_network_advanced_server_ops.py
+++ b/tempest/scenario/test_network_advanced_server_ops.py
@@ -17,6 +17,7 @@
 
 from oslo_log import log
 from tempest.common import utils
+from tempest.common.utils.linux import remote_client
 from tempest.common.utils import net_downtime
 from tempest.common import waiters
 from tempest import config
@@ -189,6 +190,12 @@
             floating_ip['floating_ip_address'])
         self.useFixture(downtime_meter)
 
+        metadata_downtime_meter = net_downtime.MetadataDowntimeMeter(
+            remote_client.RemoteClient(floating_ip['floating_ip_address'],
+                                       CONF.validation.image_ssh_user,
+                                       pkey=keypair['private_key']))
+        self.useFixture(metadata_downtime_meter)
+
         migration_kwargs = {'host': None, 'block_migration': block_migration}
 
         # check if microversion is less than 2.25 because of
@@ -230,6 +237,18 @@
             "Downtime of {} seconds is higher than expected '{}'".format(
                 downtime, allowed_downtime))
 
+        metadata_downtime_results = metadata_downtime_meter.get_results()
+        self.assertGreater(metadata_downtime_results['successes'], 0)
+        LOG.debug("Metadata Downtime seconds measured = %r",
+                  metadata_downtime_results['downtime'])
+        allowed_metadata_downtime = CONF.validation.allowed_metadata_downtime
+        metadata_downtime_failed = \
+            metadata_downtime_results['downtime']['FAILED']
+        self.assertLessEqual(
+            metadata_downtime_failed, allowed_metadata_downtime,
+            "Metadata downtime: {} seconds is higher than expected: {}".format(
+                metadata_downtime_failed, allowed_metadata_downtime))
+
     def _test_server_connectivity_cold_migration_revert(self, source_host=None,
                                                         dest_host=None):
         keypair = self.create_keypair(client=self.keypairs_client)
diff --git a/tempest/scenario/test_server_multinode.py b/tempest/scenario/test_server_multinode.py
index fe85234..556b925 100644
--- a/tempest/scenario/test_server_multinode.py
+++ b/tempest/scenario/test_server_multinode.py
@@ -48,7 +48,7 @@
                 for host in zone['hosts']:
                     if 'nova-compute' in zone['hosts'][host] and \
                         zone['hosts'][host]['nova-compute']['available'] and \
-                        not host.endswith('-ironic'):
+                        CONF.compute.target_hosts_to_avoid not in host:
                         hosts.append({'zone': zone['zoneName'],
                                       'host_name': host})
 
diff --git a/tempest/scenario/test_unified_limits.py b/tempest/scenario/test_unified_limits.py
index 6e194f9..7e8f8b2 100644
--- a/tempest/scenario/test_unified_limits.py
+++ b/tempest/scenario/test_unified_limits.py
@@ -32,6 +32,13 @@
     credentials = ['primary', 'system_admin']
 
     @classmethod
+    def skip_checks(cls):
+        super(ImageQuotaTest, cls).skip_checks()
+        if not CONF.service_available.glance:
+            skip_msg = ("%s skipped as glance is not available" % cls.__name__)
+            raise cls.skipException(skip_msg)
+
+    @classmethod
     def resource_setup(cls):
         super(ImageQuotaTest, cls).resource_setup()
 
diff --git a/tempest/scenario/test_volume_boot_pattern.py b/tempest/scenario/test_volume_boot_pattern.py
index 5e28ecd..febc2f6 100644
--- a/tempest/scenario/test_volume_boot_pattern.py
+++ b/tempest/scenario/test_volume_boot_pattern.py
@@ -11,7 +11,6 @@
 #    under the License.
 
 from oslo_log import log as logging
-from oslo_serialization import jsonutils as json
 import testtools
 
 from tempest.common import utils
@@ -245,8 +244,7 @@
         bdms = image.get('block_device_mapping')
         if not bdms:
             bdms = image['properties']['block_device_mapping']
-        bdms = json.loads(bdms)
-        snapshot_id = bdms[0]['snapshot_id']
+        snapshot_id = self.get_snapshot_id(bdms)
         self._delete_snapshot(snapshot_id)
 
         # Now, delete the first server which will also delete the first
diff --git a/tempest/tests/cmd/test_cleanup_services.py b/tempest/tests/cmd/test_cleanup_services.py
index 2557145..7f8db9f 100644
--- a/tempest/tests/cmd/test_cleanup_services.py
+++ b/tempest/tests/cmd/test_cleanup_services.py
@@ -610,21 +610,14 @@
         self._test_prefix_opt_precedence(delete_mock)
 
     def test_resource_list_opt_precedence(self):
-        delete_mock = [(self.filter_prefix, [], None),
+        delete_mock = [(self.filter_saved_state, [], None),
+                       (self.filter_resource_list, [], None),
+                       (self.filter_prefix, [], None),
                        (self.get_method, self.response, 200),
                        (self.validate_response, 'validate', None),
                        (self.delete_method, 'error', None),
                        (self.log_method, 'exception', None)]
-        serv = self._create_cmd_service(
-            self.service_class, is_resource_list=True)
-
-        _, fixtures = self.run_function_with_mocks(
-            serv.delete,
-            delete_mock
-        )
-
-        # Check that prefix was not used for filtering
-        fixtures[0].mock.assert_not_called()
+        self._test_resource_list_opt_precedence(delete_mock)
 
 
 class TestVolumeService(BaseCmdServiceTests):
diff --git a/tempest/tests/cmd/test_verify_tempest_config.py b/tempest/tests/cmd/test_verify_tempest_config.py
index fa43e58..3df9f19 100644
--- a/tempest/tests/cmd/test_verify_tempest_config.py
+++ b/tempest/tests/cmd/test_verify_tempest_config.py
@@ -498,7 +498,7 @@
                 return ('token',
                         {'serviceCatalog': [{'type': 'compute'},
                                             {'type': 'image'},
-                                            {'type': 'volumev3'},
+                                            {'type': 'block-storage'},
                                             {'type': 'network'},
                                             {'type': 'object-store'}]})
 
diff --git a/tempest/tests/common/test_waiters.py b/tempest/tests/common/test_waiters.py
index f194173..f7f2dc7 100755
--- a/tempest/tests/common/test_waiters.py
+++ b/tempest/tests/common/test_waiters.py
@@ -884,6 +884,58 @@
                           waiters.wait_for_port_status, mock_client,
                           fake_port_id, fake_status)
 
+    def test_wait_for_server_ports_active(self):
+        """Test that the waiter replies with the ports before the timeout"""
+
+        def is_active(port):
+            return port['status'] == 'ACTIVE'
+
+        def client_response(device_id):
+            """Mock client response, replies with partial status after one
+            call and final status after 2 calls
+            """
+            if mock_client.call_count >= 2:
+                return mock_ports_active
+            else:
+                mock_client.call_count += 1
+                if mock_client.call_count > 1:
+                    return mock_ports_half_active
+                return mock_ports
+
+        mock_ports = {'ports': [{'id': '1234', 'status': 'DOWN'},
+                                {'id': '5678', 'status': 'DOWN'}]}
+        mock_ports_half_active = {'ports': [{'id': '1234', 'status': 'ACTIVE'},
+                                            {'id': '5678', 'status': 'DOWN'}]}
+        mock_ports_active = {'ports': [{'id': '1234', 'status': 'ACTIVE'},
+                                       {'id': '5678', 'status': 'ACTIVE'}]}
+        mock_client = mock.Mock(
+            spec=ports_client.PortsClient,
+            build_timeout=30, build_interval=1,
+            list_ports=client_response)
+        fake_server_id = "9876"
+        self.assertEqual(mock_ports_active['ports'],
+                         waiters.wait_for_server_ports_active(
+                             mock_client, fake_server_id, is_active))
+
+    def test_wait_for_server_ports_active_timeout(self):
+        """Negative test - checking that a timeout
+        presented by a small 'fake_timeout' and a static status of
+        'DOWN' in the mock will raise a timeout exception
+        """
+
+        def is_active(port):
+            return port['status'] == 'ACTIVE'
+
+        mock_ports = {'ports': [{'id': '1234', 'status': "DOWN"}]}
+        mock_client = mock.Mock(
+            spec=ports_client.PortsClient,
+            build_timeout=2, build_interval=1,
+            list_ports=lambda device_id: mock_ports)
+        fake_server_id = "9876"
+        self.assertRaises(lib_exc.TimeoutException,
+                          waiters.wait_for_server_ports_active,
+                          mock_client, fake_server_id, is_active)
+
 
 class TestServerFloatingIPWaiters(base.TestCase):
 
diff --git a/tempest/tests/lib/common/test_dynamic_creds.py b/tempest/tests/lib/common/test_dynamic_creds.py
index d3d01c0..4c2ea30 100644
--- a/tempest/tests/lib/common/test_dynamic_creds.py
+++ b/tempest/tests/lib/common/test_dynamic_creds.py
@@ -104,6 +104,14 @@
                           (200, {'tenant': {'id': id, 'name': name}}))))
         return tenant_fix
 
+    def _mock_domain_create(self, id, name):
+        domain_fix = self.useFixture(fixtures.MockPatchObject(
+            self.domains_client.DomainsClient,
+            'create_domain',
+            return_value=(rest_client.ResponseBody
+                          (200, {'domain': {'id': id, 'name': name}}))))
+        return domain_fix
+
     def _mock_list_roles(self, id, name):
         roles_fix = self.useFixture(fixtures.MockPatchObject(
             self.roles_client.RolesClient,
@@ -143,7 +151,8 @@
                               {'id': '1', 'name': 'FakeRole'},
                               {'id': '2', 'name': 'member'},
                               {'id': '3', 'name': 'reader'},
-                              {'id': '4', 'name': 'admin'}]}))))
+                              {'id': '4', 'name': 'manager'},
+                              {'id': '5', 'name': 'admin'}]}))))
         return roles_fix
 
     def _mock_list_ec2_credentials(self, user_id, tenant_id):
@@ -999,6 +1008,7 @@
     roles_client = v3_roles_client
     tenants_client = v3_projects_client
     users_client = v3_users_client
+    domains_client = domains_client
     token_client_class = token_client.V3TokenClient
     fake_response = fake_identity._fake_v3_response
     tenants_client_class = tenants_client.ProjectsClient
@@ -1263,3 +1273,47 @@
                 "member role already exists, ignoring conflict.")
         creds.creds_client.assign_user_role.assert_called_once_with(
             mock.ANY, mock.ANY, 'member')
+
+    @mock.patch('tempest.lib.common.rest_client.RestClient')
+    def test_project_manager_creds(self, MockRestClient):
+        creds = dynamic_creds.DynamicCredentialProvider(**self.fixed_params)
+        self._mock_list_roles('1234', 'manager')
+        self._mock_user_create('1234', 'fake_manager_user')
+        self._mock_tenant_create('1234', 'fake_manager_tenant')
+
+        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:
+            manager_creds = creds.get_project_manager_creds()
+        user_mock.assert_has_calls([
+            mock.call('1234', '1234', '1234')])
+        self.assertEqual(manager_creds.username, 'fake_manager_user')
+        self.assertEqual(manager_creds.tenant_name, 'fake_manager_tenant')
+        # Verify IDs
+        self.assertEqual(manager_creds.tenant_id, '1234')
+        self.assertEqual(manager_creds.user_id, '1234')
+
+    @mock.patch('tempest.lib.common.rest_client.RestClient')
+    def test_domain_manager_creds(self, MockRestClient):
+        creds = dynamic_creds.DynamicCredentialProvider(**self.fixed_params)
+        self._mock_list_roles('1234', 'manager')
+        self._mock_user_create('1234', 'fake_manager_user')
+        self._mock_domain_create('1234', 'fake_manager_domain')
+
+        user_mock = mock.patch.object(self.roles_client.RolesClient,
+                                      'create_user_role_on_domain')
+        user_mock.start()
+        self.addCleanup(user_mock.stop)
+        with mock.patch.object(self.roles_client.RolesClient,
+                               'create_user_role_on_domain') as user_mock:
+            manager_creds = creds.get_domain_manager_creds()
+        user_mock.assert_has_calls([
+            mock.call('1234', '1234', '1234')])
+        self.assertEqual(manager_creds.username, 'fake_manager_user')
+        self.assertEqual(manager_creds.domain_name, 'fake_manager_domain')
+        # Verify IDs
+        self.assertEqual(manager_creds.domain_id, '1234')
+        self.assertEqual(manager_creds.user_id, '1234')
diff --git a/tempest/tests/lib/common/test_preprov_creds.py b/tempest/tests/lib/common/test_preprov_creds.py
index f2131dc..5a36f71 100644
--- a/tempest/tests/lib/common/test_preprov_creds.py
+++ b/tempest/tests/lib/common/test_preprov_creds.py
@@ -77,7 +77,13 @@
             {'username': 'test_admin2', 'project_name': 'test_tenant12',
              'password': 'p', 'roles': [admin_role]},
             {'username': 'test_admin3', 'project_name': 'test_tenant13',
-             'password': 'p', 'types': ['admin']}]
+             'password': 'p', 'types': ['admin']},
+            {'username': 'test_project_manager1',
+             'project_name': 'test_tenant14', 'password': 'p',
+             'roles': ['manager']},
+            {'username': 'test_project_manager2',
+             'tenant_name': 'test_tenant15', 'password': 'p',
+             'roles': ['manager']}]
 
     def setUp(self):
         super(TestPreProvisionedCredentials, self).setUp()
@@ -319,7 +325,7 @@
         calls = get_free_hash_mock.mock.mock_calls
         self.assertEqual(len(calls), 1)
         args = calls[0][1][0]
-        self.assertEqual(len(args), 10)
+        self.assertEqual(len(args), 12)
         for i in admin_hashes:
             self.assertNotIn(i, args)
 
@@ -431,6 +437,26 @@
             # Get one more
             test_accounts_class.get_admin_creds()
 
+    def test_get_project_manager_creds(self):
+        test_accounts_class = preprov_creds.PreProvisionedCredentialProvider(
+            **self.fixed_params)
+        p_manager_creds = test_accounts_class.get_project_manager_creds()
+        self.assertNotIn('test_admin', p_manager_creds.username)
+        self.assertNotIn('test_user', p_manager_creds.username)
+        self.assertIn('test_project_manager', p_manager_creds.username)
+
+    def test_get_project_manager_creds_none_available(self):
+        admin_accounts = [x for x in self.test_accounts if 'test_admin'
+                          in x['username']]
+        self.useFixture(fixtures.MockPatch(
+            'tempest.lib.common.preprov_creds.read_accounts_yaml',
+            return_value=admin_accounts))
+        test_accounts_class = preprov_creds.PreProvisionedCredentialProvider(
+            **self.fixed_params)
+        with testtools.ExpectedException(lib_exc.InvalidCredentials):
+            # Get one more
+            test_accounts_class.get_project_manager_creds()
+
 
 class TestPreProvisionedCredentialsV3(TestPreProvisionedCredentials):
 
@@ -480,4 +506,29 @@
             {'username': 'test_admin2', 'project_name': 'test_project12',
              'domain_name': 'domain', 'password': 'p', 'roles': [admin_role]},
             {'username': 'test_admin3', 'project_name': 'test_tenant13',
-             'domain_name': 'domain', 'password': 'p', 'types': ['admin']}]
+             'domain_name': 'domain', 'password': 'p', 'types': ['admin']},
+            {'username': 'test_project_manager1',
+             'project_name': 'test_project14', 'domain_name': 'domain',
+             'password': 'p', 'roles': ['manager']},
+            {'username': 'test_domain_manager1',
+             'domain_name': 'domain', 'password': 'p', 'roles': ['manager']}]
+
+    def test_get_domain_manager_creds(self):
+        test_accounts_class = preprov_creds.PreProvisionedCredentialProvider(
+            **self.fixed_params)
+        d_manager_creds = test_accounts_class.get_domain_manager_creds()
+        self.assertNotIn('test_admin', d_manager_creds.username)
+        self.assertNotIn('test_user', d_manager_creds.username)
+        self.assertIn('test_domain_manager', d_manager_creds.username)
+
+    def test_get_domain_manager_creds_none_available(self):
+        admin_accounts = [x for x in self.test_accounts if 'test_admin'
+                          in x['username']]
+        self.useFixture(fixtures.MockPatch(
+            'tempest.lib.common.preprov_creds.read_accounts_yaml',
+            return_value=admin_accounts))
+        test_accounts_class = preprov_creds.PreProvisionedCredentialProvider(
+            **self.fixed_params)
+        with testtools.ExpectedException(lib_exc.InvalidCredentials):
+            # Get one more
+            test_accounts_class.get_domain_manager_creds()
diff --git a/tempest/tests/lib/common/test_rest_client.py b/tempest/tests/lib/common/test_rest_client.py
index 9bc6f60..0d1660c 100644
--- a/tempest/tests/lib/common/test_rest_client.py
+++ b/tempest/tests/lib/common/test_rest_client.py
@@ -14,6 +14,7 @@
 
 import copy
 from unittest import mock
+from unittest.mock import patch
 
 import fixtures
 import jsonschema
@@ -755,103 +756,124 @@
     def setUp(self):
         self.fake_http = fake_http.fake_httplib2()
         super(TestRecordResources, self).setUp()
-
-    def _cleanup_test_resource_record(self):
-        # clear resource_list.json file
-        with open('resource_list.json', 'w') as f:
-            f.write('{}')
+        self.rest_client.rec_rw_lock = mock.MagicMock()
 
     def test_post_record_resources(self):
-        self.rest_client.record_resources = True
-        __, return_dict = self.rest_client.post(self.url, {}, {})
-        self.assertEqual({}, return_dict['headers'])
-        self.assertEqual({}, return_dict['body'])
-
-    def test_resource_record_no_top_key(self):
-        test_body_no_key = b'{}'
-        self.rest_client.resource_record(test_body_no_key)
+        with patch('builtins.open', mock.mock_open(read_data=b'{}')):
+            self.rest_client.record_resources = True
+            __, return_dict = self.rest_client.post(self.url, {}, {})
+            self.assertEqual({}, return_dict['headers'])
+            self.assertEqual({}, return_dict['body'])
 
     def test_resource_record_dict(self):
-        test_dict_body = b'{"project": {"id": "test-id", "name": ""}}\n'
-        self.rest_client.resource_record(test_dict_body)
+        mock_resource_list_1 = mock.mock_open(read_data=b'{}')
+        mock_resource_list_2 = mock.mock_open(
+            read_data=b'{"projects": {"test-id": ""}}')
 
-        with open('resource_list.json', 'r') as f:
-            content = f.read()
-            resource_list_content = json.loads(content)
+        with patch('builtins.open') as mock_open_func:
+            mock_open_func.side_effect = [
+                mock_resource_list_1.return_value,
+                mock_resource_list_2.return_value
+            ]
 
-        test_resource_list = {
-            "projects": {"test-id": ""}
-        }
-        self.assertEqual(resource_list_content, test_resource_list)
+            test_dict_body = b'{"project": {"id": "test-id", "name": ""}}\n'
+            self.rest_client.resource_record(test_dict_body)
 
-        # cleanup
-        self._cleanup_test_resource_record()
+            content = mock_resource_list_2().read()
+            resource_list_2 = json.loads(content)
+            test_resource_list_2 = {
+                "projects": {"test-id": ""}
+            }
+            self.assertEqual(resource_list_2, test_resource_list_2)
 
     def test_resource_record_list(self):
-        test_list_body = '''{
-            "user": [
-                {
-                    "id": "test-uuid",
-                    "name": "test-name"
-                },
-                {
-                    "id": "test-uuid2",
-                    "name": "test-name2"
-                }
+        mock_content_2 = b'{"users": {"test-uuid": "test-name"}}'
+        mock_content_3 = (
+            b'{"users": {"test-uuid": "test-name",'
+            b'"test-uuid2": "test-name2"}}'
+        )
+
+        mock_resource_list_1 = mock.mock_open(read_data=b'{}')
+        mock_resource_list_2 = mock.mock_open(read_data=mock_content_2)
+        mock_resource_list_3 = mock.mock_open(read_data=mock_content_3)
+
+        with patch('builtins.open') as mock_open_func:
+            mock_open_func.side_effect = [
+                mock_resource_list_1.return_value,
+                mock_resource_list_2.return_value,
+                mock_resource_list_3.return_value
             ]
-        }'''
-        test_list_body = test_list_body.encode('utf-8')
-        self.rest_client.resource_record(test_list_body)
 
-        with open('resource_list.json', 'r') as f:
-            content = f.read()
-            resource_list_content = json.loads(content)
+            test_list_body = '''{
+                "user": [
+                    {
+                        "id": "test-uuid",
+                        "name": "test-name"
+                    },
+                    {
+                        "id": "test-uuid2",
+                        "name": "test-name2"
+                    }
+                ]
+            }'''
+            test_list_body = test_list_body.encode('utf-8')
+            self.rest_client.resource_record(test_list_body)
 
-        test_resource_list = {
-            "users": {
-                "test-uuid": "test-name",
-                "test-uuid2": "test-name2"
+            content_2 = mock_resource_list_2().read()
+            resource_list_2 = json.loads(content_2)
+
+            test_resource_list_2 = {
+                "users": {
+                    "test-uuid": "test-name"
+                }
             }
-        }
-        self.assertEqual(resource_list_content, test_resource_list)
+            self.assertEqual(resource_list_2, test_resource_list_2)
 
-        # cleanup
-        self._cleanup_test_resource_record()
+            content_3 = mock_resource_list_3().read()
+            resource_list_3 = json.loads(content_3)
+
+            test_resource_list_3 = {
+                "users": {
+                    "test-uuid": "test-name",
+                    "test-uuid2": "test-name2"
+                }
+            }
+            self.assertEqual(resource_list_3, test_resource_list_3)
 
     def test_resource_update_id(self):
         data = {}
         res_dict = {'id': 'test-uuid', 'name': 'test-name'}
 
-        self.rest_client.rec_rw_lock = mock.MagicMock()
-        self.rest_client.resource_update(data, 'user', res_dict)
-        result = {'users': {'test-uuid': 'test-name'}}
-        self.assertEqual(data, result)
+        with patch('builtins.open', mock.mock_open(read_data=b'{}')):
+            self.rest_client.resource_update(data, 'user', res_dict)
+            result = {'users': {'test-uuid': 'test-name'}}
+            self.assertEqual(data, result)
 
     def test_resource_update_name(self):
         data = {'keypairs': {}}
         res_dict = {'name': 'test-keypair'}
 
-        self.rest_client.rec_rw_lock = mock.MagicMock()
-        self.rest_client.resource_update(data, 'keypair', res_dict)
-        result = {'keypairs': {'test-keypair': ""}}
-        self.assertEqual(data, result)
+        with patch('builtins.open', mock.mock_open(read_data=b'{}')):
+            self.rest_client.resource_update(data, 'keypair', res_dict)
+            result = {'keypairs': {'test-keypair': ""}}
+            self.assertEqual(data, result)
 
     def test_resource_update_no_id(self):
         data = {}
         res_dict = {'type': 'test', 'description': 'example'}
 
-        self.rest_client.rec_rw_lock = mock.MagicMock()
-        self.rest_client.resource_update(data, 'projects', res_dict)
-        result = {'projects': {}}
-        self.assertEqual(data, result)
+        with patch('builtins.open', mock.mock_open(read_data=b'{}')):
+            self.rest_client.resource_update(data, 'projects', res_dict)
+            result = {'projects': {}}
+            self.assertEqual(data, result)
 
     def test_resource_update_not_dict(self):
         data = {}
         res_dict = 'test-string'
 
-        self.rest_client.rec_rw_lock = mock.MagicMock()
-        self.rest_client.resource_update(data, 'user', res_dict)
-        self.assertEqual(data, {})
+        with patch('builtins.open', mock.mock_open(read_data=b'{}')):
+            self.rest_client.resource_update(data, 'user', res_dict)
+            self.assertEqual(data, {})
 
 
 class TestResponseBody(base.TestCase):
diff --git a/tempest/tests/lib/services/volume/v3/test_encryption_types_client.py b/tempest/tests/lib/services/volume/v3/test_encryption_types_client.py
index 7218224..8164ea6 100644
--- a/tempest/tests/lib/services/volume/v3/test_encryption_types_client.py
+++ b/tempest/tests/lib/services/volume/v3/test_encryption_types_client.py
@@ -106,6 +106,7 @@
             'tempest.lib.common.rest_client.RestClient.delete',
             {},
             volume_type_id="cbc36478b0bd8e67e89",
+            encryption_id="test_id",
             status=202)
 
     def test_update_encryption_type_with_str_body(self):
@@ -119,4 +120,5 @@
             self.client.update_encryption_type,
             'tempest.lib.common.rest_client.RestClient.put',
             self.FAKE_UPDATE_ENCRYPTION_TYPE,
-            bytes_body, volume_type_id="cbc36478b0bd8e67e89")
+            bytes_body, volume_type_id="cbc36478b0bd8e67e89",
+            encryption_id="test_id")
diff --git a/tempest/tests/lib/test_auth.py b/tempest/tests/lib/test_auth.py
index 3edb122..4e5ec48 100644
--- a/tempest/tests/lib/test_auth.py
+++ b/tempest/tests/lib/test_auth.py
@@ -17,6 +17,7 @@
 import datetime
 
 import fixtures
+from oslo_utils import timeutils
 import testtools
 
 from tempest.lib import auth
@@ -509,15 +510,15 @@
         self._test_base_url_helper(expected, filters, ('token', auth_data))
 
     def test_token_not_expired(self):
-        expiry_data = datetime.datetime.utcnow() + datetime.timedelta(days=1)
+        expiry_data = timeutils.utcnow() + datetime.timedelta(days=1)
         self._verify_expiry(expiry_data=expiry_data, should_be_expired=False)
 
     def test_token_expired(self):
-        expiry_data = datetime.datetime.utcnow() - datetime.timedelta(hours=1)
+        expiry_data = timeutils.utcnow() - datetime.timedelta(hours=1)
         self._verify_expiry(expiry_data=expiry_data, should_be_expired=True)
 
     def test_token_not_expired_to_be_renewed(self):
-        expiry_data = (datetime.datetime.utcnow() +
+        expiry_data = (timeutils.utcnow() +
                        self.auth_provider.token_expiry_threshold / 2)
         self._verify_expiry(expiry_data=expiry_data, should_be_expired=True)
 
diff --git a/tempest/tests/test_hacking.py b/tempest/tests/test_hacking.py
index 464e66a..3f603e8 100644
--- a/tempest/tests/test_hacking.py
+++ b/tempest/tests/test_hacking.py
@@ -51,25 +51,34 @@
 
     def test_no_setup_teardown_class_for_tests(self):
         self.assertTrue(checks.no_setup_teardown_class_for_tests(
-            "  def setUpClass(cls):", './tempest/tests/fake_test.py'))
+            "  def setUpClass(cls):", './tempest/tests/fake_test.py', False))
         self.assertIsNone(checks.no_setup_teardown_class_for_tests(
-            "  def setUpClass(cls): # noqa", './tempest/tests/fake_test.py'))
+            "  def setUpClass(cls):", './tempest/tests/fake_test.py',
+            True))
         self.assertTrue(checks.no_setup_teardown_class_for_tests(
-            "  def setUpClass(cls):", './tempest/api/fake_test.py'))
+            "  def setUpClass(cls):", './tempest/api/fake_test.py',
+            False))
         self.assertTrue(checks.no_setup_teardown_class_for_tests(
-            "  def setUpClass(cls):", './tempest/scenario/fake_test.py'))
+            "  def setUpClass(cls):", './tempest/scenario/fake_test.py',
+            False))
         self.assertFalse(checks.no_setup_teardown_class_for_tests(
-            "  def setUpClass(cls):", './tempest/test.py'))
+            "  def setUpClass(cls):", './tempest/test.py',
+            False))
         self.assertTrue(checks.no_setup_teardown_class_for_tests(
-            "  def tearDownClass(cls):", './tempest/tests/fake_test.py'))
+            "  def tearDownClass(cls):", './tempest/tests/fake_test.py',
+            False))
         self.assertIsNone(checks.no_setup_teardown_class_for_tests(
-            "  def tearDownClass(cls): # noqa", './tempest/tests/fake_test.py'))
+            "  def tearDownClass(cls):", './tempest/tests/fake_test.py',
+            True))
         self.assertTrue(checks.no_setup_teardown_class_for_tests(
-            "  def tearDownClass(cls):", './tempest/api/fake_test.py'))
+            "  def tearDownClass(cls):", './tempest/api/fake_test.py',
+            False))
         self.assertTrue(checks.no_setup_teardown_class_for_tests(
-            "  def tearDownClass(cls):", './tempest/scenario/fake_test.py'))
+            "  def tearDownClass(cls):", './tempest/scenario/fake_test.py',
+            False))
         self.assertFalse(checks.no_setup_teardown_class_for_tests(
-            "  def tearDownClass(cls):", './tempest/test.py'))
+            "  def tearDownClass(cls):", './tempest/test.py',
+            False))
 
     def test_import_no_clients_in_api_and_scenario_tests(self):
         for client in checks.PYTHON_CLIENTS:
@@ -198,22 +207,26 @@
             # arbitrarily many decorators. These insert decorators above the
             # @decorators.attr(type=['negative']) decorator.
             for decorator in other_decorators:
-                self.assertIsNone(check(" %s" % decorator, filename))
+                self.assertFalse(
+                    list(check(" %s" % decorator, filename)))
         if with_negative_decorator:
-            self.assertIsNone(
-                check("@decorators.attr(type=['negative'])", filename))
+            self.assertFalse(
+                list(check("@decorators.attr(type=['negative'])", filename)))
         if with_other_decorators:
             # Include multiple decorators to verify that this check works with
             # arbitrarily many decorators. These insert decorators between
             # the test and the @decorators.attr(type=['negative']) decorator.
             for decorator in other_decorators:
-                self.assertIsNone(check(" %s" % decorator, filename))
-        final_result = check(" def test_some_negative_case", filename)
+                self.assertFalse(
+                    list(check(" %s" % decorator, filename)))
+        final_result = list(check(" def test_some_negative_case", filename))
         if expected_success:
-            self.assertIsNone(final_result)
+            self.assertFalse(final_result)
         else:
-            self.assertIsInstance(final_result, tuple)
-            self.assertFalse(final_result[0])
+            self.assertEqual(1, len(final_result))
+            self.assertIsInstance(final_result[0], tuple)
+            self.assertEqual(0, final_result[0][0])
+            self.assertTrue(final_result[0][1])
 
     def test_no_negatve_test_attribute_applied_to_negative_test(self):
         # Check negative filename, negative decorator passes
diff --git a/tempest/tests/test_test.py b/tempest/tests/test_test.py
index 80825a4..7fb9bb3 100644
--- a/tempest/tests/test_test.py
+++ b/tempest/tests/test_test.py
@@ -303,7 +303,7 @@
         # [0]: test, err, details [1] -> exc_info
         # Type, Exception, traceback [1] -> MultipleException
         found_exc = log[0][1][1]
-        self.assertTrue(isinstance(found_exc, testtools.MultipleExceptions))
+        self.assertIsInstance(found_exc, testtools.MultipleExceptions)
         self.assertEqual(2, len(found_exc.args))
         # Each arg is exc_info - match messages and order
         self.assertIn('mock3 resource', str(found_exc.args[0][1]))
@@ -332,7 +332,7 @@
         # [0]: test, err, details [1] -> exc_info
         # Type, Exception, traceback [1] -> RuntimeError
         found_exc = log[0][1][1]
-        self.assertTrue(isinstance(found_exc, RuntimeError))
+        self.assertIsInstance(found_exc, RuntimeError)
         self.assertIn(BadResourceCleanup.__name__, str(found_exc))
 
     def test_super_skip_checks_not_invoked(self):
diff --git a/test-requirements.txt b/test-requirements.txt
index 17fa9f1..b925921 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -1,8 +1,4 @@
-# The order of packages is significant, because pip processes them in the order
-# of appearance. Changing the order has an impact on the overall integration
-# process, which may cause wedges in the gate later.
-hacking>=3.0.1,<3.1.0;python_version>='3.5' # Apache-2.0
+hacking>=7.0.0,<7.1.0
 coverage!=4.4,>=4.0 # Apache-2.0
 oslotest>=3.2.0 # Apache-2.0
-pycodestyle>=2.0.0,<2.6.0 # MIT
-flake8-import-order==0.11 # LGPLv3
+flake8-import-order>=0.18.0,<0.19.0 # LGPLv3
diff --git a/tools/tempest-extra-tests-list.txt b/tools/tempest-extra-tests-list.txt
index 9c88109..03cf7e9 100644
--- a/tools/tempest-extra-tests-list.txt
+++ b/tools/tempest-extra-tests-list.txt
@@ -16,5 +16,10 @@
 tempest.api.image.admin
 tempest.api.network.admin
 
+# This also run cinder-tempest-plugin tests so that we can avoid any
+# breaking change to plugins (cinder-tempest-plugins uses most of the
+# Tempest interface) but we can add more plugins tests here if needed.
+cinder_tempest_plugin
+
 # All negative tests
 negative
diff --git a/tox.ini b/tox.ini
index e3c8fcf..0fbc252 100644
--- a/tox.ini
+++ b/tox.ini
@@ -387,14 +387,14 @@
 [testenv:pep8]
 deps =
     {[testenv]deps}
-    autopep8
+    autopep8>=2.1.0
 commands =
     autopep8 --exit-code --max-line-length=79 --experimental --diff -r tempest setup.py
     flake8 {posargs}
     check-uuid
 
 [testenv:autopep8]
-deps = autopep8
+deps = autopep8>=2.1.0
 commands =
     {toxinidir}/tools/format.sh
 
@@ -411,7 +411,8 @@
 # E129 skipped because it is too limiting when combined with other rules
 # W504 skipped because it is overeager and unnecessary
 # H405 skipped because it arbitrarily forces doctring "title" lines
-ignore = E125,E123,E129,W504,H405
+# I201 and I202 skipped because the rule does not allow new line between 3rd party modules and own modules
+ignore = E125,E123,E129,W504,H405,I201,I202,T117
 show-source = True
 exclude = .git,.venv,.tox,dist,doc,*egg,build
 enable-extensions = H106,H203,H904
diff --git a/zuul.d/base.yaml b/zuul.d/base.yaml
index 633f501..4de4111 100644
--- a/zuul.d/base.yaml
+++ b/zuul.d/base.yaml
@@ -12,6 +12,10 @@
     timeout: 7200
     roles: &base_roles
       - zuul: opendev.org/openstack/devstack
+    failure-output:
+      # This matches stestr/tempest output when a test fails
+      # {1} tempest.api.test_blah [5.743446s] ... FAILED
+      - '\{\d+\} (.*?) \[[\d\.]+s\] \.\.\. FAILED'
     vars: &base_vars
       devstack_localrc:
         IMAGE_URLS: http://download.cirros-cloud.net/0.6.2/cirros-0.6.2-x86_64-disk.img, http://download.cirros-cloud.net/0.6.1/cirros-0.6.1-x86_64-disk.img
@@ -60,6 +64,10 @@
     required-projects: *base_required-projects
     timeout: 7200
     roles: *base_roles
+    failure-output:
+      # This matches stestr/tempest output when a test fails
+      # {1} tempest.api.test_blah [5.743446s] ... FAILED
+      - '\{\d+\} (.*?) \[[\d\.]+s\] \.\.\. FAILED'
     vars: *base_vars
     run: playbooks/devstack-tempest-ipv6.yaml
     post-run: playbooks/post-tempest.yaml
diff --git a/zuul.d/integrated-gate.yaml b/zuul.d/integrated-gate.yaml
index 2fd6e36..47b7812 100644
--- a/zuul.d/integrated-gate.yaml
+++ b/zuul.d/integrated-gate.yaml
@@ -42,11 +42,18 @@
     description: |
       This job runs the extra tests mentioned in
       tools/tempest-extra-tests-list.txt.
+    # NOTE(gmann): We need c-t-p as this job run c-t-p tests also.
+    required-projects:
+      - opendev.org/openstack/cinder-tempest-plugin
     vars:
       tox_envlist: extra-tests
+      tempest_plugins:
+        - cinder-tempest-plugin
       run_tempest_cleanup: true
       run_tempest_cleanup_resource_list: true
       run_tempest_dry_cleanup: true
+      devstack_localrc:
+        CINDER_ENFORCE_SCOPE: true
       devstack_local_conf:
         test-config:
           $TEMPEST_CONFIG:
@@ -65,6 +72,10 @@
     branches:
       regex: ^.*/(victoria|wallaby)$
       negate: true
+    # NOTE(sean-k-mooney): this job and its descendants frequently times out
+    # run on rax-* providers with a timeout of 2 hours. temporary increase
+    # the timeout to 2.5 hours.
+    timeout: 9000
     description: |
       Base integration test with Neutron networking, horizon, swift enable,
       and py3.
@@ -78,6 +89,8 @@
       # end up 6 in upstream CI. Higher concurrency means high parallel
       # requests to services and can cause more oom issues. To avoid the
       # oom issue, setting the concurrency to 4 in this job.
+      # NOTE(sean-k-mooney): now that we use zswap we should be able to
+      # increase the concurrency to 6.
       tempest_concurrency: 4
       tox_envlist: integrated-full
       devstack_localrc:
@@ -133,11 +146,18 @@
       This job runs integration tests for compute. This is
       subset of 'tempest-full-py3' job and run Nova, Neutron, Cinder (except backup tests)
       and Glance related tests. This is meant to be run on Nova gate only.
+    # NOTE(sean-k-mooney): this job and its descendants frequently times out
+    # when run on rax-* providers, recent optimizations have reduced the
+    # runtime of the job but it still times out. temporary increase the
+    # timeout to 2.5 hours.
+    timeout: 9000
     vars:
       # NOTE(gmann): Default concurrency is higher (number of cpu -2) which
       # end up 6 in upstream CI. Higher concurrency means high parallel
       # requests to services and can cause more oom issues. To avoid the
       # oom issue, setting the concurrency to 4 in this job.
+      # NOTE(sean-k-mooney): now that we use zswap we should be able to
+      # increase the concurrency to 6.
       tempest_concurrency: 4
       tox_envlist: integrated-compute
       tempest_exclude_regex: ""
@@ -242,10 +262,10 @@
 - job:
     name: tempest-multinode-full-py3
     parent: tempest-multinode-full-base
-    nodeset: openstack-two-node-jammy
-    # This job runs on ubuntu Jammy and after unmaintained/zed.
+    nodeset: openstack-two-node-noble
+    # This job runs on ubuntu Noble from 2025.1 onwards.
     branches:
-      regex: ^.*/(victoria|wallaby|xena|yoga|zed)$
+      regex: ^.*/(victoria|wallaby|xena|yoga|zed|2023.1|2023.2|2024.1|2024.2)$
       negate: true
     vars:
       # NOTE(gmann): Default concurrency is higher (number of cpu -2) which
@@ -254,8 +274,6 @@
       # oom issue, setting the concurrency to 4 in this job.
       tempest_concurrency: 4
       tempest_set_src_dest_host: true
-      devstack_localrc:
-        USE_PYTHON3: true
       devstack_plugins:
         neutron: https://opendev.org/openstack/neutron
       devstack_services:
@@ -264,8 +282,6 @@
         br-int-flows: true
     group-vars:
       subnode:
-        devstack_localrc:
-          USE_PYTHON3: true
         devstack_services:
           br-ex-tcpdump: true
           br-int-flows: true
@@ -317,6 +333,7 @@
       devstack_localrc:
         CINDER_ENABLED_BACKENDS: lvm:lvmdriver-1,lvm:lvmdriver-2
         ENABLE_VOLUME_MULTIATTACH: true
+        GLANCE_ENFORCE_IMAGE_FORMAT: false
       devstack_plugins:
         neutron: https://opendev.org/openstack/neutron
       devstack_services:
@@ -330,6 +347,27 @@
         devstack_localrc:
           ENABLE_VOLUME_MULTIATTACH: true
 
+# TODO(gmann): As per the 2025.1 testing runtime, we need to run at least
+# one job set on Jammy. These jammy job can be removed in the next
+# cycle(2025.2).
+- job:
+    name: tempest-full-ubuntu-jammy
+    description: This is tempest-full python3 job on Ubuntu Jammy(22.04)
+    parent: tempest-full-py3
+    nodeset: openstack-single-node-jammy
+
+- job:
+    name: tempest-multinode-full-ubuntu-jammy
+    description: This is tempest-multinode-full-py3 python3 job on Ubuntu Jammy(22.04)
+    parent: tempest-multinode-full-py3
+    nodeset: openstack-two-node-jammy
+
+- job:
+    name: tempest-extra-tests-ubuntu-jammy
+    description: This is tempest-extra-tests python3 job on Ubuntu Jammy(22.04)
+    parent: tempest-extra-tests
+    nodeset: openstack-single-node-jammy
+
 - job:
     name: tempest-cinder-v2-api
     parent: devstack-tempest
@@ -345,18 +383,6 @@
         TEMPEST_VOLUME_TYPE: volumev2
 
 - job:
-    name: tempest-centos8-stream-fips
-    parent: devstack-tempest
-    description: |
-      Integration testing for a FIPS enabled Centos 8 system
-    nodeset: devstack-single-node-centos-8-stream
-    vars:
-      tox_envlist: full
-      configure_swap_size: 4096
-      nslookup_target: 'opendev.org'
-      enable_fips: True
-
-- job:
     name: tempest-centos9-stream-fips
     parent: devstack-tempest
     description: |
@@ -429,6 +455,11 @@
         - grenade-skip-level:
             branches:
               - ^.*/2024.1
+        # on current master 2025.1(SLURP) grenade-skip-level-always is voting
+        # which test stable/2024.1 to 2025.1 upgrade.
+        - grenade-skip-level-always:
+            branches:
+              - master
         - tempest-integrated-networking
         # Do not run it on ussuri until below issue is fixed
         # https://storyboard.openstack.org/#!/story/2010057
@@ -448,6 +479,11 @@
         - grenade-skip-level:
             branches:
               - ^.*/2024.1
+        # on current master 2025.1(SLURP) grenade-skip-level-always is voting
+        # which test stable/2024.1 to 2025.1 upgrade.
+        - grenade-skip-level-always:
+            branches:
+              - master
         # Do not run it on ussuri until below issue is fixed
         # https://storyboard.openstack.org/#!/story/2010057
         # and job is broken up to wallaby branch due to the issue
@@ -488,11 +524,9 @@
             branches:
               - ^.*/2023.2
               - ^.*/2024.1
+              - ^.*/2024.2
               - master
         - tempest-integrated-compute
-        # centos-8-stream is tested from wallaby -> yoga branches
-        - tempest-integrated-compute-centos-8-stream:
-            branches: ^.*/(wallaby|xena|yoga)$
         # Do not run it on ussuri until below issue is fixed
         # https://storyboard.openstack.org/#!/story/2010057
         # and job is broken up to wallaby branch due to the issue
@@ -507,6 +541,7 @@
             branches:
               - ^.*/2023.2
               - ^.*/2024.1
+              - ^.*/2024.2
               - master
         - tempest-integrated-compute
         - openstacksdk-functional-devstack:
@@ -544,6 +579,11 @@
         - grenade-skip-level:
             branches:
               - ^.*/2024.1
+        # on current master 2025.1(SLURP) grenade-skip-level-always is voting
+        # which test stable/2024.1 to 2025.1 upgrade.
+        - grenade-skip-level-always:
+            branches:
+              - master
         - tempest-integrated-placement
         # Do not run it on ussuri until below issue is fixed
         # https://storyboard.openstack.org/#!/story/2010057
@@ -563,6 +603,11 @@
         - grenade-skip-level:
             branches:
               - ^.*/2024.1
+        # on current master 2025.1(SLURP) grenade-skip-level-always is voting
+        # which test stable/2024.1 to 2025.1 upgrade.
+        - grenade-skip-level-always:
+            branches:
+              - master
         # Do not run it on ussuri until below issue is fixed
         # https://storyboard.openstack.org/#!/story/2010057
         # and job is broken up to wallaby branch due to the issue
@@ -595,6 +640,11 @@
         - grenade-skip-level:
             branches:
               - ^.*/2024.1
+        # on current master 2025.1(SLURP) grenade-skip-level-always is voting
+        # which test stable/2024.1 to 2025.1 upgrade.
+        - grenade-skip-level-always:
+            branches:
+              - master
         - tempest-integrated-storage
         # Do not run it on ussuri until below issue is fixed
         # https://storyboard.openstack.org/#!/story/2010057
@@ -613,6 +663,11 @@
         - grenade-skip-level:
             branches:
               - ^.*/2024.1
+        # on current master 2025.1(SLURP) grenade-skip-level-always is voting
+        # which test stable/2024.1 to 2025.1 upgrade.
+        - grenade-skip-level-always:
+            branches:
+              - master
         - tempest-integrated-storage
         # Do not run it on ussuri until below issue is fixed
         # https://storyboard.openstack.org/#!/story/2010057
@@ -639,6 +694,11 @@
         - grenade-skip-level:
             branches:
               - ^.*/2024.1
+        # on current master 2025.1(SLURP) grenade-skip-level-always is voting
+        # which test stable/2024.1 to 2025.1 upgrade.
+        - grenade-skip-level-always:
+            branches:
+              - master
         - tempest-integrated-object-storage
         # Do not run it on ussuri until below issue is fixed
         # https://storyboard.openstack.org/#!/story/2010057
@@ -657,6 +717,11 @@
         - grenade-skip-level:
             branches:
               - ^.*/2024.1
+        # on current master 2025.1(SLURP) grenade-skip-level-always is voting
+        # which test stable/2024.1 to 2025.1 upgrade.
+        - grenade-skip-level-always:
+            branches:
+              - master
         - tempest-integrated-object-storage
         # Do not run it on ussuri until below issue is fixed
         # https://storyboard.openstack.org/#!/story/2010057
diff --git a/zuul.d/project.yaml b/zuul.d/project.yaml
index c652fe0..2f21c2d 100644
--- a/zuul.d/project.yaml
+++ b/zuul.d/project.yaml
@@ -8,10 +8,10 @@
     check:
       jobs:
         - openstack-tox-pep8
-        - openstack-tox-py38
         - openstack-tox-py39
         - openstack-tox-py310
         - openstack-tox-py311
+        - openstack-tox-py312
         - tempest-full-py3:
             # Define list of irrelevant files to use everywhere else
             irrelevant-files: &tempest-irrelevant-files
@@ -27,6 +27,14 @@
               - ^.gitignore$
               - ^.gitreview$
               - ^.mailmap$
+        # NOTE(gmann): Running jobs on Jammy as per the additional testing
+        # for 2025.1 cycle and these can be removed in 2025.2 cycle.
+        - tempest-full-ubuntu-jammy:
+            irrelevant-files: *tempest-irrelevant-files
+        - tempest-multinode-full-ubuntu-jammy:
+            irrelevant-files: *tempest-irrelevant-files
+        - tempest-extra-tests-ubuntu-jammy:
+            irrelevant-files: *tempest-irrelevant-files
         - tempest-extra-tests:
             irrelevant-files: *tempest-irrelevant-files
         - glance-multistore-cinder-import:
@@ -37,9 +45,9 @@
         # if things are working in latest and oldest it will work in between
         # stable branches also. If anything is breaking we will be catching
         # those in respective stable branch gate.
-        - tempest-full-2024-1:
+        - tempest-full-2024-2:
             irrelevant-files: *tempest-irrelevant-files
-        - tempest-full-2023-1:
+        - tempest-full-2023-2:
             irrelevant-files: *tempest-irrelevant-files
         - tempest-multinode-full-py3:
             irrelevant-files: *tempest-irrelevant-files
@@ -103,9 +111,7 @@
         - tempest-full-enforce-scope-new-defaults:
             irrelevant-files: *tempest-irrelevant-files
         - devstack-plugin-ceph-tempest-py3:
-            # TODO(kopecmartin): make it voting once the below bug is fixed
-            # https://bugs.launchpad.net/devstack-plugin-ceph/+bug/1975648
-            voting: false
+            timeout: 9000
             irrelevant-files: *tempest-irrelevant-files
         - neutron-ovs-grenade-multinode:
             irrelevant-files: *tempest-irrelevant-files
@@ -128,10 +134,10 @@
     gate:
       jobs:
         - openstack-tox-pep8
-        - openstack-tox-py38
         - openstack-tox-py39
         - openstack-tox-py310
         - openstack-tox-py311
+        - openstack-tox-py312
         - tempest-slow-py3:
             irrelevant-files: *tempest-irrelevant-files
         - neutron-ovs-grenade-multinode:
@@ -156,6 +162,14 @@
             irrelevant-files: *tempest-irrelevant-files
         - ironic-tempest-bios-ipmi-direct-tinyipa:
             irrelevant-files: *tempest-irrelevant-files
+        # NOTE(gmann): Running jobs on Jammy as per the additional testing
+        # for 2025.1 cycle and these can be removed in 2025.2 cycle.
+        - tempest-full-ubuntu-jammy:
+            irrelevant-files: *tempest-irrelevant-files
+        - tempest-multinode-full-ubuntu-jammy:
+            irrelevant-files: *tempest-irrelevant-files
+        - tempest-extra-tests-ubuntu-jammy:
+            irrelevant-files: *tempest-irrelevant-files
     experimental:
       jobs:
         - nova-multi-cell
@@ -187,15 +201,15 @@
             irrelevant-files: *tempest-irrelevant-files
     periodic-stable:
       jobs:
+        - tempest-full-2024-2
         - tempest-full-2024-1
         - tempest-full-2023-2
-        - tempest-full-2023-1
+        - tempest-slow-2024-2
         - tempest-slow-2024-1
         - tempest-slow-2023-2
-        - tempest-slow-2023-1
+        - tempest-full-2024-2-extra-tests
         - tempest-full-2024-1-extra-tests
         - tempest-full-2023-2-extra-tests
-        - tempest-full-2023-1-extra-tests
     periodic:
       jobs:
         - tempest-all
diff --git a/zuul.d/stable-jobs.yaml b/zuul.d/stable-jobs.yaml
index 9d69715..5785ec6 100644
--- a/zuul.d/stable-jobs.yaml
+++ b/zuul.d/stable-jobs.yaml
@@ -1,5 +1,11 @@
 # NOTE(gmann): This file includes all stable release jobs definition.
 - job:
+    name: tempest-full-2024-2
+    parent: tempest-full-py3
+    nodeset: openstack-single-node-jammy
+    override-checkout: stable/2024.2
+
+- job:
     name: tempest-full-2024-1
     parent: tempest-full-py3
     nodeset: openstack-single-node-jammy
@@ -12,10 +18,10 @@
     override-checkout: stable/2023.2
 
 - job:
-    name: tempest-full-2023-1
-    parent: tempest-full-py3
+    name: tempest-full-2024-2-extra-tests
+    parent: tempest-extra-tests
     nodeset: openstack-single-node-jammy
-    override-checkout: stable/2023.1
+    override-checkout: stable/2024.2
 
 - job:
     name: tempest-full-2024-1-extra-tests
@@ -30,10 +36,10 @@
     override-checkout: stable/2023.2
 
 - job:
-    name: tempest-full-2023-1-extra-tests
-    parent: tempest-extra-tests
-    nodeset: openstack-single-node-jammy
-    override-checkout: stable/2023.1
+    name: tempest-slow-2024-2
+    parent: tempest-slow-py3
+    nodeset: openstack-two-node-jammy
+    override-checkout: stable/2024.2
 
 - job:
     name: tempest-slow-2024-1
@@ -48,19 +54,14 @@
     override-checkout: stable/2023.2
 
 - job:
-    name: tempest-slow-2023-1
-    parent: tempest-slow-py3
-    nodeset: openstack-two-node-jammy
-    override-checkout: stable/2023.1
-
-- job:
     name: tempest-full-py3
     parent: devstack-tempest
     # This job version is to use the 'full' tox env which
-    # is available for stable/ussuri to stable/wallaby also.
+    # is available for unmaintained/victoria to unmaintained/xena also.
     branches:
       - ^.*/victoria
       - ^.*/wallaby
+      - ^.*/xena
     description: |
       Base integration test with Neutron networking, horizon, swift enable,
       and py3.
@@ -71,6 +72,10 @@
       - openstack/horizon
     vars:
       tox_envlist: full
+      tempest_exclude_regex: "\
+          (DHCPAgentSchedulersTestJSON)|\
+          (AttachVolumeMultiAttachTest)|\
+          (UpdateMultiattachVolumeNegativeTest)"
       devstack_localrc:
         USE_PYTHON3: true
         FORCE_CONFIG_DRIVE: true
@@ -85,6 +90,26 @@
 - job:
     name: tempest-multinode-full-py3
     parent: tempest-multinode-full
+    nodeset: openstack-two-node-jammy
+    # This job runs on Jammy and supposed to run until 2024.2.
+    branches:
+      - ^.*/2023.1
+      - ^.*/2023.2
+      - ^.*/2024.1
+      - ^.*/2024.2
+    vars:
+      devstack_plugins:
+        neutron: https://opendev.org/openstack/neutron
+      devstack_services:
+        neutron-trunk: true
+    group-vars:
+      subnode:
+        devstack_localrc:
+          USE_PYTHON3: true
+
+- job:
+    name: tempest-multinode-full-py3
+    parent: tempest-multinode-full
     nodeset: openstack-two-node-focal
     # This job runs on Focal and supposed to run until unmaintained/zed.
     branches:
@@ -109,11 +134,30 @@
     name: tempest-multinode-full
     parent: tempest-multinode-full-base
     nodeset: openstack-two-node-focal
-    # This job runs on Focal and on python2. This is for unmaintained/victoria to unmaintained/zed.
+    # This job runs on Focal and on python2. This is for unmaintained/victoria to unmaintained/xena.
     branches:
       - ^.*/victoria
       - ^.*/wallaby
       - ^.*/xena
+    vars:
+      tox_envlist: full
+      tempest_exclude_regex: "\
+          (DHCPAgentSchedulersTestJSON)|\
+          (AttachVolumeMultiAttachTest)|\
+          (UpdateMultiattachVolumeNegativeTest)"
+      devstack_localrc:
+        USE_PYTHON3: False
+    group-vars:
+      subnode:
+        devstack_localrc:
+          USE_PYTHON3: False
+
+- job:
+    name: tempest-multinode-full
+    parent: tempest-multinode-full-base
+    nodeset: openstack-two-node-focal
+    # This job runs on Focal and on python2. This is for unmaintained/yoga to unmaintained/zed.
+    branches:
       - ^.*/yoga
       - ^.*/zed
     vars:
@@ -149,41 +193,3 @@
       - ^.*/victoria
       - ^.*/wallaby
       - ^.*/xena
-
-- job:
-    name: tempest-integrated-compute-centos-8-stream
-    parent: tempest-integrated-compute
-    # TODO(gmann): Make this job non voting until bug#1957941 if fixed.
-    voting: false
-    nodeset: devstack-single-node-centos-8-stream
-    branches:
-      - ^.*/wallaby
-      - ^.*/xena
-      - ^.*/yoga
-    description: |
-      This job runs integration tests for compute. This is
-      subset of 'tempest-full-py3' job and run Nova, Neutron, Cinder (except backup tests)
-      and Glance related tests. This is meant to be run on Nova gate only.
-      This version of the job also uses CentOS 8 stream.
-    vars:
-      # Required until bug/1949606 is resolved when using libvirt and QEMU
-      # >=5.0.0 with a [libvirt]virt_type of qemu (TCG).
-      configure_swap_size: 4096
-
-- job:
-    name: tempest-full-py3-centos-8-stream
-    parent: tempest-full-py3
-    # TODO(gmann): Make this job non voting until bug#1957941 if fixed.
-    voting: false
-    branches:
-      - ^.*/wallaby
-      - ^.*/xena
-      - ^.*/yoga
-    nodeset: devstack-single-node-centos-8-stream
-    description: |
-      Base integration test with Neutron networking and py36 running
-      on CentOS 8 stream
-    vars:
-      # Required until bug/1949606 is resolved when using libvirt and QEMU
-      # >=5.0.0 with a [libvirt]virt_type of qemu (TCG).
-      configure_swap_size: 4096
diff --git a/zuul.d/tempest-specific.yaml b/zuul.d/tempest-specific.yaml
index 296682e..deb4157 100644
--- a/zuul.d/tempest-specific.yaml
+++ b/zuul.d/tempest-specific.yaml
@@ -76,7 +76,7 @@
     parent: tox
     description: |
       Run tempest plugin sanity check script using tox.
-    nodeset: ubuntu-jammy
+    nodeset: ubuntu-noble
     vars:
       tox_envlist: plugin-sanity-check
     timeout: 5000