Merge "Test fips job on centos-9"
diff --git a/bindep.txt b/bindep.txt
index efd3a10..7d34939 100644
--- a/bindep.txt
+++ b/bindep.txt
@@ -5,7 +5,6 @@
 libffi-devel [platform:rpm]
 gcc [platform:rpm]
 gcc [platform:dpkg]
-python-dev [platform:dpkg]
 python-devel [platform:rpm]
 python3-dev [platform:dpkg]
 python3-devel [platform:rpm]
diff --git a/doc/source/supported_version.rst b/doc/source/supported_version.rst
index 4ca7f0d..f630578 100644
--- a/doc/source/supported_version.rst
+++ b/doc/source/supported_version.rst
@@ -9,9 +9,10 @@
 
 Tempest master supports the below OpenStack Releases:
 
+* Yoga
+* Xena
+* Wallaby
 * Victoria
-* Ussuri
-* Train
 
 For older OpenStack Release:
 
@@ -32,6 +33,5 @@
 
 Tempest master supports the below python versions:
 
-* Python 3.6
-* Python 3.7
 * Python 3.8
+* Python 3.9
diff --git a/releasenotes/notes/add-image-cache-apis-as-tempest-clients-fbcd186927a85e2f.yaml b/releasenotes/notes/add-image-cache-apis-as-tempest-clients-fbcd186927a85e2f.yaml
new file mode 100644
index 0000000..38cc9ac
--- /dev/null
+++ b/releasenotes/notes/add-image-cache-apis-as-tempest-clients-fbcd186927a85e2f.yaml
@@ -0,0 +1,6 @@
+---
+features:
+  - |
+    The following ``image_cache`` tempest client for glance v2 image
+    caching API is implemented in this release.
+
diff --git a/releasenotes/notes/drop-py-3-6-and-3-7-a34f2294f5341539.yaml b/releasenotes/notes/drop-py-3-6-and-3-7-a34f2294f5341539.yaml
new file mode 100644
index 0000000..ec4e2f2
--- /dev/null
+++ b/releasenotes/notes/drop-py-3-6-and-3-7-a34f2294f5341539.yaml
@@ -0,0 +1,6 @@
+---
+upgrade:
+  - |
+    Python 3.6 and 3.7 support has been dropped. Last release of Tempest
+    to support python 3.6 and 3.7 is Temepst 30.0.0. The minimum version
+    of Python now supported by Tempest is Python 3.8.
diff --git a/releasenotes/notes/end-of-support-of-victoria-9c33f2b089b14cb5.yaml b/releasenotes/notes/end-of-support-of-victoria-9c33f2b089b14cb5.yaml
new file mode 100644
index 0000000..c644e3a
--- /dev/null
+++ b/releasenotes/notes/end-of-support-of-victoria-9c33f2b089b14cb5.yaml
@@ -0,0 +1,12 @@
+---
+prelude: |
+    This is an intermediate release during the Zed development cycle to
+    mark the end of support for EM Victoria release in Tempest.
+    After this release, Tempest will support below OpenStack Releases:
+
+    * Yoga
+    * Xena
+    * Wallaby
+
+    Current development of Tempest is for OpenStack Zed development
+    cycle.
diff --git a/releasenotes/notes/measure-downtime-during-live-migration-5e8305be270de680.yaml b/releasenotes/notes/measure-downtime-during-live-migration-5e8305be270de680.yaml
new file mode 100644
index 0000000..9f4abd1
--- /dev/null
+++ b/releasenotes/notes/measure-downtime-during-live-migration-5e8305be270de680.yaml
@@ -0,0 +1,9 @@
+---
+features:
+  - |
+    Added new module net_downtime including the fixture NetDowntimeMeter that
+    can be used to measure how long the connectivity with an IP is lost
+    during certain operations like a server live migration.
+    The configuration option allowed_network_downtime has been added with a
+    default value of 5.0 seconds, which would be the maximum time that
+    the connectivity downtime is expected to last.
diff --git a/releasenotes/notes/temp_url_tests_digest_config-3d8c9bb271961ddd.yaml b/releasenotes/notes/temp_url_tests_digest_config-3d8c9bb271961ddd.yaml
new file mode 100644
index 0000000..f96c030
--- /dev/null
+++ b/releasenotes/notes/temp_url_tests_digest_config-3d8c9bb271961ddd.yaml
@@ -0,0 +1,11 @@
+---
+features:
+  - |
+    Add configuration parameter `tempurl_digest_hashlib` into
+    `object-storage-feature-enabled` which configures the hashing algorithm to
+    use for the temp_url tests; defaults to 'sha256'.
+security:
+  - |
+    Swift used to support only 'sha1' for temp_url hashing but from many
+    years now 'sha256' and 'sha512' are also available. These are stronger
+    than 'sha1' and tempest now allows configuring which one to use.
diff --git a/releasenotes/source/index.rst b/releasenotes/source/index.rst
index 122f7c7..9b5aad3 100644
--- a/releasenotes/source/index.rst
+++ b/releasenotes/source/index.rst
@@ -6,6 +6,8 @@
    :maxdepth: 1
 
    unreleased
+   v31.1.0
+   v31.0.0
    v30.0.0
    v29.2.0
    v29.1.0
diff --git a/releasenotes/source/v31.0.0.rst b/releasenotes/source/v31.0.0.rst
new file mode 100644
index 0000000..8fb797c
--- /dev/null
+++ b/releasenotes/source/v31.0.0.rst
@@ -0,0 +1,5 @@
+=====================
+v31.0.0 Release Notes
+=====================
+.. release-notes:: 31.0.0 Release Notes
+   :version: 31.0.0
diff --git a/releasenotes/source/v31.1.0.rst b/releasenotes/source/v31.1.0.rst
new file mode 100644
index 0000000..ecd7c36
--- /dev/null
+++ b/releasenotes/source/v31.1.0.rst
@@ -0,0 +1,5 @@
+=====================
+v31.1.0 Release Notes
+=====================
+.. release-notes:: 31.1.0 Release Notes
+   :version: 31.1.0
diff --git a/roles/run-tempest/README.rst b/roles/run-tempest/README.rst
index 1919393..d9f855a 100644
--- a/roles/run-tempest/README.rst
+++ b/roles/run-tempest/README.rst
@@ -81,7 +81,7 @@
 .. zuul:rolevar:: stable_constraints_file
    :default: ''
 
-   Upper constraints file to be used for stable branch till stable/train.
+   Upper constraints file to be used for stable branch till stable/victoria.
 
 .. zuul:rolevar:: tempest_tox_environment
    :default: ''
diff --git a/roles/run-tempest/tasks/main.yaml b/roles/run-tempest/tasks/main.yaml
index 397de1e..f302fa5 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 stable/train
+- name: Use stable branch upper-constraints till stable/victoria
   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"]
+  when: target_branch in ["stable/ocata", "stable/pike", "stable/queens", "stable/rocky", "stable/stein", "stable/train", "stable/ussuri", "stable/victoria"]
 
 - name: Use Configured upper-constraints for non-master Tempest
   set_fact:
@@ -78,16 +78,16 @@
         exclude_list_option: "--exclude-list={{ tempest_test_exclude_list|quote }}"
       when: exclude_list_stat.stat.exists
 
-- name: stable/train workaround to fallback exclude-list to blacklist
-  # NOTE(gmann): stable/train use Tempest 26.1.0 and with stestr 2.5.1
-  # (beacause of upper constraints of stestr 2.5.1 in stable/train) which
-  # does not have new args exclude-list so let's fallback to old arg
-  # if new arg is passed.
+- name: Tempest 26.1.0 workaround to fallback exclude-list to blacklist
+  # NOTE(gmann): stable/train|ussuri|victoria use Tempest 26.1.0 and with
+  # stestr 2.5.1/3.0.1 (beacause of upper constraints of stestr 2.5.1/3.0.1
+  # in stable/train|ussuri|victoria) which does not have new args exclude-list
+  # so let's fallback to old arg if new arg is passed.
   set_fact:
     exclude_list_option: "--blacklist-file={{ tempest_test_exclude_list|quote }}"
   when:
     - tempest_test_exclude_list is defined
-    - target_branch == "stable/train"
+    - target_branch in ["stable/train", "stable/ussuri", "stable/victoria"]
 
 # TODO(kopecmartin) remove this after all consumers of the role have switched
 # to tempest_exclude_regex option, until then it's kept here for the backward
@@ -105,19 +105,19 @@
   when:
     - tempest_black_regex is not defined
     - tempest_exclude_regex is defined
-    - target_branch != "stable/train"
+    - target_branch not in ["stable/train", "stable/ussuri", "stable/victoria"]
 
-- name: stable/train workaround to fallback exclude-regex to black-regex
-  # NOTE(gmann): stable/train use Tempest 26.1.0 and with stestr 2.5.1
-  # (beacause of upper constraints of stestr 2.5.1 in stable/train) which
-  # does not have new args exclude-regex so let's fallback to old arg
-  # if new arg is passed.
+- name: Tempest 26.1.0 workaround to fallback exclude-regex to black-regex
+  # NOTE(gmann): stable/train|ussuri|victoria use Tempest 26.1.0 and with stestr
+  # 2.5.1/3.0.1 (beacause of upper constraints of stestr 2.5.1/3.0.1 in
+  # stable/train|ussuri|victoria) which does not have new args exclude-list so
+  # let's fallback to old arg if new arg is passed.
   set_fact:
     tempest_test_exclude_regex: "--black-regex={{tempest_exclude_regex|quote}}"
   when:
     - tempest_black_regex is not defined
     - tempest_exclude_regex is defined
-    - target_branch == "stable/train"
+    - target_branch in ["stable/train", "stable/ussuri", "stable/victoria"]
 
 - name: Run Tempest
   command: tox -e {{tox_envlist}} {{tox_extra_args}} -- {{tempest_test_regex|quote}} \
diff --git a/setup.cfg b/setup.cfg
index a41eccf..a531eb4 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -6,7 +6,7 @@
 author = OpenStack
 author_email = openstack-discuss@lists.openstack.org
 home_page = https://docs.openstack.org/tempest/latest/
-python_requires = >=3.6
+python_requires = >=3.8
 classifier =
     Intended Audience :: Information Technology
     Intended Audience :: System Administrators
@@ -15,8 +15,6 @@
     Operating System :: POSIX :: Linux
     Programming Language :: Python
     Programming Language :: Python :: 3
-    Programming Language :: Python :: 3.6
-    Programming Language :: Python :: 3.7
     Programming Language :: Python :: 3.8
     Programming Language :: Python :: 3.9
     Programming Language :: Python :: 3 :: Only
diff --git a/tempest/api/compute/admin/test_live_migration.py b/tempest/api/compute/admin/test_live_migration.py
index c91b557..2826f56 100644
--- a/tempest/api/compute/admin/test_live_migration.py
+++ b/tempest/api/compute/admin/test_live_migration.py
@@ -34,11 +34,6 @@
 class LiveMigrationTestBase(base.BaseV2ComputeAdminTest):
     """Test live migration operations supported by admin user"""
 
-    # These tests don't attempt any SSH validation nor do they use
-    # floating IPs on the instance, so all we need is a network and
-    # a subnet so the instance being migrated has a single port, but
-    # we need that to make sure we are properly updating the port
-    # host bindings during the live migration.
     create_default_network = True
 
     @classmethod
@@ -104,6 +99,11 @@
     max_microversion = '2.24'
     block_migration = None
 
+    @classmethod
+    def setup_credentials(cls):
+        cls.prepare_instance_network()
+        super(LiveMigrationTest, cls).setup_credentials()
+
     def _test_live_migration(self, state='ACTIVE', volume_backed=False):
         """Tests live migration between two hosts.
 
@@ -182,7 +182,12 @@
         attach volume. This differs from test_volume_backed_live_migration
         above that tests live-migration with only an attached volume.
         """
-        server = self.create_test_server(wait_until="ACTIVE")
+        validation_resources = self.get_class_validation_resources(
+            self.os_primary)
+        server = self.create_test_server(
+            validatable=True,
+            validation_resources=validation_resources,
+            wait_until="SSHABLE")
         server_id = server['id']
         if not CONF.compute_feature_enabled.can_migrate_between_any_hosts:
             # not to specify a host so that the scheduler will pick one
diff --git a/tempest/api/compute/admin/test_volume_swap.py b/tempest/api/compute/admin/test_volume_swap.py
index c1236a7..7da87c7 100644
--- a/tempest/api/compute/admin/test_volume_swap.py
+++ b/tempest/api/compute/admin/test_volume_swap.py
@@ -26,6 +26,11 @@
     create_default_network = True
 
     @classmethod
+    def setup_credentials(cls):
+        cls.prepare_instance_network()
+        super(TestVolumeSwapBase, cls).setup_credentials()
+
+    @classmethod
     def skip_checks(cls):
         super(TestVolumeSwapBase, cls).skip_checks()
         if not CONF.compute_feature_enabled.swap_volume:
@@ -100,7 +105,16 @@
         volume1 = self.create_volume()
         volume2 = self.create_volume()
         # Boot server
-        server = self.create_test_server(wait_until='ACTIVE')
+        validation_resources = self.get_class_validation_resources(
+            self.os_primary)
+        # NOTE(gibi): We need to wait for the guest to fully boot as the test
+        # will attach a volume to the server and therefore cleanup will try to
+        # detach it. See bug 1960346 for details.
+        server = self.create_test_server(
+            validatable=True,
+            validation_resources=validation_resources,
+            wait_until='SSHABLE'
+        )
         # Attach "volume1" to server
         self.attach_volume(server, volume1)
         # Swap volume from "volume1" to "volume2"
@@ -200,9 +214,18 @@
         volume2 = self.create_volume(multiattach=True)
 
         # Create two servers and wait for them to be ACTIVE.
+        validation_resources = self.get_class_validation_resources(
+            self.os_primary)
+        # NOTE(gibi): We need to wait for the guests to fully boot as the test
+        # will attach volumes to the servers and therefore cleanup will try to
+        # detach them. See bug 1960346 for details.
         reservation_id = self.create_test_server(
-            wait_until='ACTIVE', min_count=2,
-            return_reservation_id=True)['reservation_id']
+            validatable=True,
+            validation_resources=validation_resources,
+            wait_until='SSHABLE',
+            min_count=2,
+            return_reservation_id=True,
+        )['reservation_id']
         # Get the servers using the reservation_id.
         servers = self.servers_client.list_servers(
             reservation_id=reservation_id)['servers']
diff --git a/tempest/api/compute/admin/test_volumes_negative.py b/tempest/api/compute/admin/test_volumes_negative.py
index 10d522b..91ab09e 100644
--- a/tempest/api/compute/admin/test_volumes_negative.py
+++ b/tempest/api/compute/admin/test_volumes_negative.py
@@ -28,21 +28,22 @@
     create_default_network = True
 
     @classmethod
+    def setup_credentials(cls):
+        cls.prepare_instance_network()
+        super(VolumesAdminNegativeTest, cls).setup_credentials()
+
+    @classmethod
     def skip_checks(cls):
         super(VolumesAdminNegativeTest, cls).skip_checks()
         if not CONF.service_available.cinder:
             skip_msg = ("%s skipped as Cinder is not available" % cls.__name__)
             raise cls.skipException(skip_msg)
 
-    @classmethod
-    def resource_setup(cls):
-        super(VolumesAdminNegativeTest, cls).resource_setup()
-        cls.server = cls.create_test_server(wait_until='ACTIVE')
-
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('309b5ecd-0585-4a7e-a36f-d2b2bf55259d')
     def test_update_attached_volume_with_nonexistent_volume_in_uri(self):
         """Test swapping non existent volume should fail"""
+        self.server = self.create_test_server(wait_until="ACTIVE")
         volume = self.create_volume()
         nonexistent_volume = data_utils.rand_uuid()
         self.assertRaises(lib_exc.NotFound,
@@ -55,6 +56,17 @@
     @decorators.idempotent_id('7dcac15a-b107-46d3-a5f6-cb863f4e454a')
     def test_update_attached_volume_with_nonexistent_volume_in_body(self):
         """Test swapping volume to a non existence volume should fail"""
+        validation_resources = self.get_class_validation_resources(
+            self.os_primary)
+        # NOTE(gibi): We need to wait for the guest to fully boot as
+        # test_update_attached_volume_with_nonexistent_volume_in_body case
+        # will attach a volume to it and therefore cleanup will try to detach
+        # it. See bug 1960346 for details.
+        self.server = self.create_test_server(
+            validatable=True,
+            validation_resources=validation_resources,
+            wait_until="SSHABLE")
+
         volume = self.create_volume()
         self.attach_volume(self.server, volume)
 
@@ -76,6 +88,13 @@
     min_microversion = '2.60'
     volume_min_microversion = '3.27'
 
+    create_default_network = True
+
+    @classmethod
+    def setup_credentials(cls):
+        cls.prepare_instance_network()
+        super(UpdateMultiattachVolumeNegativeTest, cls).setup_credentials()
+
     @classmethod
     def skip_checks(cls):
         super(UpdateMultiattachVolumeNegativeTest, cls).skip_checks()
@@ -101,8 +120,21 @@
         vol2 = self.create_volume(multiattach=True)
 
         # Create two instances.
-        server1 = self.create_test_server(wait_until='ACTIVE')
-        server2 = self.create_test_server(wait_until='ACTIVE')
+        validation_resources = self.get_class_validation_resources(
+            self.os_primary)
+        # NOTE(gibi): We need to wait for the guests to fully boot as the test
+        # will attach volumes to the servers and therefore cleanup will try to
+        # detach them. See bug 1960346 for details.
+        server1 = self.create_test_server(
+            validatable=True,
+            validation_resources=validation_resources,
+            wait_until='SSHABLE'
+        )
+        server2 = self.create_test_server(
+            validatable=True,
+            validation_resources=validation_resources,
+            wait_until='SSHABLE'
+        )
 
         # Attach vol1 to both of these instances.
         vol1_attachment1 = self.attach_volume(server1, vol1)
diff --git a/tempest/api/compute/base.py b/tempest/api/compute/base.py
index 41e73ec..75df5ae 100644
--- a/tempest/api/compute/base.py
+++ b/tempest/api/compute/base.py
@@ -66,7 +66,9 @@
         # Setting network=True, subnet=True creates a default network
         cls.set_network_resources(
             network=cls.create_default_network,
-            subnet=cls.create_default_network)
+            subnet=cls.create_default_network,
+            router=cls.create_default_network,
+            dhcp=cls.create_default_network)
         super(BaseV2ComputeTest, cls).setup_credentials()
 
     @classmethod
@@ -456,15 +458,31 @@
         except Exception:
             LOG.exception('Failed to delete server %s', server_id)
 
-    def resize_server(self, server_id, new_flavor_id, **kwargs):
+    def resize_server(
+        self, server_id, new_flavor_id, wait_until='ACTIVE', **kwargs
+    ):
         """resize and confirm_resize an server, waits for it to be ACTIVE."""
         self.servers_client.resize_server(server_id, new_flavor_id, **kwargs)
         waiters.wait_for_server_status(self.servers_client, server_id,
                                        'VERIFY_RESIZE')
         self.servers_client.confirm_resize_server(server_id)
+
         waiters.wait_for_server_status(
             self.servers_client, server_id, 'ACTIVE')
         server = self.servers_client.show_server(server_id)['server']
+
+        validation_resources = self.get_class_validation_resources(
+            self.os_primary)
+        if (
+            validation_resources and
+            wait_until in ("SSHABLE", "PINGABLE") and
+            CONF.validation.run_validation
+        ):
+            tenant_network = self.get_tenant_network()
+            compute.wait_for_ssh_or_ping(
+                server, self.os_primary, tenant_network,
+                True, validation_resources, wait_until, True)
+
         self.assert_flavor_equal(new_flavor_id, server['flavor'])
 
     def reboot_server(self, server_id, type):
diff --git a/tempest/api/compute/images/test_images_negative.py b/tempest/api/compute/images/test_images_negative.py
index 5ff2a6a..124651e 100644
--- a/tempest/api/compute/images/test_images_negative.py
+++ b/tempest/api/compute/images/test_images_negative.py
@@ -43,6 +43,7 @@
 
 class ImagesNegativeTestJSON(ImagesNegativeTestBase):
     """Negative tests of server image"""
+    create_default_network = True
 
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('6cd5a89d-5b47-46a7-93bc-3916f0d84973')
diff --git a/tempest/api/compute/servers/test_server_actions.py b/tempest/api/compute/servers/test_server_actions.py
index bd4e0e8..0ed73a8 100644
--- a/tempest/api/compute/servers/test_server_actions.py
+++ b/tempest/api/compute/servers/test_server_actions.py
@@ -92,7 +92,8 @@
     @classmethod
     def resource_setup(cls):
         super(ServerActionsTestJSON, cls).resource_setup()
-        cls.server_id = cls.recreate_server(None, validatable=True)
+        cls.server_id = cls.recreate_server(None, validatable=True,
+                                            wait_until='SSHABLE')
 
     @decorators.idempotent_id('6158df09-4b82-4ab3-af6d-29cf36af858d')
     @testtools.skipUnless(CONF.compute_feature_enabled.change_password,
diff --git a/tempest/api/compute/volumes/test_attach_volume.py b/tempest/api/compute/volumes/test_attach_volume.py
index e4ec209..5380c67 100644
--- a/tempest/api/compute/volumes/test_attach_volume.py
+++ b/tempest/api/compute/volumes/test_attach_volume.py
@@ -378,10 +378,19 @@
                   the created volume, and dict of server ID to volumeAttachment
                   dict entries
         """
+        validation_resources = self.get_class_validation_resources(
+            self.os_primary)
+
         servers = []
         for x in range(2):
             name = 'multiattach-server-%i' % x
-            servers.append(self.create_test_server(name=name))
+            servers.append(
+                self.create_test_server(
+                    name=name,
+                    validatable=True,
+                    validation_resources=validation_resources
+                )
+            )
 
         # Now wait for the servers to be ACTIVE.
         for server in servers:
@@ -492,7 +501,10 @@
         servers, volume, _ = self._create_and_multiattach()
 
         for server in servers:
-            self.resize_server(server['id'], self.flavor_ref_alt)
+            # We need to wait until the guest OS fully boots up as we are going
+            # to detach volumes after the resize. See bug #1960346.
+            self.resize_server(
+                server['id'], self.flavor_ref_alt, wait_until='SSHABLE')
 
         for server in servers:
             self._detach_multiattach_volume(volume['id'], server['id'])
diff --git a/tempest/api/object_storage/test_container_sync.py b/tempest/api/object_storage/test_container_sync.py
index 6b1f849..b31ff76 100644
--- a/tempest/api/object_storage/test_container_sync.py
+++ b/tempest/api/object_storage/test_container_sync.py
@@ -126,7 +126,7 @@
                 self.assertEqual(object_content, obj_name[::-1].encode())
 
     @decorators.attr(type='slow')
-    @decorators.unstable_test(bug='1317133')
+    @decorators.skip_because(bug='1317133')
     @decorators.idempotent_id('be008325-1bba-4925-b7dd-93b58f22ce9b')
     @testtools.skipIf(
         not CONF.object_storage_feature_enabled.container_sync,
diff --git a/tempest/api/object_storage/test_object_services.py b/tempest/api/object_storage/test_object_services.py
index 2823185..a11bed8 100644
--- a/tempest/api/object_storage/test_object_services.py
+++ b/tempest/api/object_storage/test_object_services.py
@@ -182,7 +182,6 @@
         self.assertEqual(data, body)
 
     @decorators.idempotent_id('4f84422a-e2f2-4403-b601-726a4220b54e')
-    @decorators.unstable_test(bug='1905432')
     def test_create_object_with_transfer_encoding(self):
         """Test creating object with transfer_encoding"""
         object_name = data_utils.rand_name(name='TestObject')
diff --git a/tempest/api/object_storage/test_object_temp_url.py b/tempest/api/object_storage/test_object_temp_url.py
index 4ca7412..8f218e2 100644
--- a/tempest/api/object_storage/test_object_temp_url.py
+++ b/tempest/api/object_storage/test_object_temp_url.py
@@ -19,9 +19,12 @@
 
 from tempest.api.object_storage import base
 from tempest.common import utils
+from tempest import config
 from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 
+CONF = config.CONF
+
 
 class ObjectTempUrlTest(base.BaseObjectTest):
     """Test object temp url"""
@@ -77,8 +80,11 @@
             container, object_name)
 
         hmac_body = '%s\n%s\n%s' % (method, expires, path)
+        hlib = getattr(
+            hashlib,
+            CONF.object_storage_feature_enabled.tempurl_digest_hashlib)
         sig = hmac.new(
-            key.encode(), hmac_body.encode(), hashlib.sha256
+            key.encode(), hmac_body.encode(), hlib
         ).hexdigest()
 
         url = "%s/%s?temp_url_sig=%s&temp_url_expires=%s" % (container,
diff --git a/tempest/api/object_storage/test_object_temp_url_negative.py b/tempest/api/object_storage/test_object_temp_url_negative.py
index e5f4cf2..712697e 100644
--- a/tempest/api/object_storage/test_object_temp_url_negative.py
+++ b/tempest/api/object_storage/test_object_temp_url_negative.py
@@ -19,10 +19,13 @@
 
 from tempest.api.object_storage import base
 from tempest.common import utils
+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 ObjectTempUrlNegativeTest(base.BaseObjectTest):
     """Negative tests of object temp url"""
@@ -82,8 +85,11 @@
             container, object_name)
 
         hmac_body = '%s\n%s\n%s' % (method, expires, path)
+        hlib = getattr(
+            hashlib,
+            CONF.object_storage_feature_enabled.tempurl_digest_hashlib)
         sig = hmac.new(
-            key.encode(), hmac_body.encode(), hashlib.sha256
+            key.encode(), hmac_body.encode(), hlib
         ).hexdigest()
 
         url = "%s/%s?temp_url_sig=%s&temp_url_expires=%s" % (container,
diff --git a/tempest/api/volume/admin/test_backends_capabilities.py b/tempest/api/volume/admin/test_backends_capabilities.py
index 3c76eca..e3a8156 100644
--- a/tempest/api/volume/admin/test_backends_capabilities.py
+++ b/tempest/api/volume/admin/test_backends_capabilities.py
@@ -37,6 +37,33 @@
         # Check response schema
         self.admin_capabilities_client.show_backend_capabilities(self.hosts[0])
 
+    @staticmethod
+    def _change_capabilities_storage_protocol(capabilities):
+        """Convert storage_protocol to its canonical version"""
+        # List of storage protocols variants defined in cinder.common.constants
+        # The canonical name for storage protocol comes first in the list
+        VARIANTS = [['iSCSI', 'iscsi'], ['FC', 'fibre_channel', 'fc'],
+                    ['NFS', 'nfs'], ['NVMe-oF', 'NVMeOF', 'nvmeof']]
+
+        capabilities = sorted(list(capabilities))
+
+        # Cinder Bug #1966103: Some drivers were reporting different strings
+        # to represent the same storage protocol. For backward compatibility,
+        # the scheduler can handle the variants, but to standardize this for
+        # operators (who may need to refer to the protocol in volume-type
+        # extra-specs), the get-pools and get-capabilities response was changed
+        # to only report the canonical name for a storage protocol, but these
+        # 2 REST API call swere not changed simultaneously, so we may or may
+        # not get canonical names, so just convert canonical names.
+        for item in range(len(capabilities)):
+            for variants in VARIANTS:
+                if capabilities[item][2] in variants:
+                    capabilities[item] = (capabilities[item][0],
+                                          capabilities[item][1],
+                                          variants[0])
+
+        return capabilities
+
     @decorators.idempotent_id('a9035743-d46a-47c5-9cb7-3c80ea16dea0')
     def test_compare_volume_stats_values(self):
         """Test comparing volume stats values
@@ -60,8 +87,9 @@
         ]
 
         # Returns a tuple of VOLUME_STATS values
-        expected_list = sorted(list(map(operator.itemgetter(*VOLUME_STATS),
-                                        cinder_pools)))
-        observed_list = sorted(list(map(operator.itemgetter(*VOLUME_STATS),
-                                        capabilities)))
+        expected_list = self._change_capabilities_storage_protocol(
+            map(operator.itemgetter(*VOLUME_STATS), cinder_pools))
+        observed_list = self._change_capabilities_storage_protocol(
+            map(operator.itemgetter(*VOLUME_STATS), capabilities))
+
         self.assertEqual(expected_list, observed_list)
diff --git a/tempest/api/volume/admin/test_encrypted_volumes_extend.py b/tempest/api/volume/admin/test_encrypted_volumes_extend.py
index 7339179..e85a00d 100644
--- a/tempest/api/volume/admin/test_encrypted_volumes_extend.py
+++ b/tempest/api/volume/admin/test_encrypted_volumes_extend.py
@@ -31,5 +31,18 @@
         "Attached encrypted volume extend is disabled.")
     @utils.services('compute')
     def test_extend_attached_encrypted_volume_luksv1(self):
+        """LUKs v1 decrypts and extends through libvirt."""
         volume = self.create_encrypted_volume(encryption_provider="luks")
         self._test_extend_attached_volume(volume)
+
+    @decorators.idempotent_id('381a2a3a-b2f4-4631-a910-720881f2cc2f')
+    @testtools.skipUnless(
+        CONF.volume_feature_enabled.extend_attached_encrypted_volume,
+        "Attached encrypted volume extend is disabled.")
+    @testtools.skipIf(CONF.volume.storage_protocol == 'ceph',
+                      'Ceph only supports LUKSv2 if doing host attach.')
+    @utils.services('compute')
+    def test_extend_attached_encrypted_volume_luksv2(self):
+        """LUKs v2 decrypts and extends through os-brick."""
+        volume = self.create_encrypted_volume(encryption_provider="luks2")
+        self._test_extend_attached_volume(volume)
diff --git a/tempest/clients.py b/tempest/clients.py
index 4c3d875..b7fa54a 100644
--- a/tempest/clients.py
+++ b/tempest/clients.py
@@ -87,6 +87,7 @@
             self.image_member_client = self.image_v1.ImageMembersClient()
             self.image_client_v2 = self.image_v2.ImagesClient()
             self.image_member_client_v2 = self.image_v2.ImageMembersClient()
+            self.image_cache_client = self.image_v2.ImageCacheClient()
             self.namespaces_client = self.image_v2.NamespacesClient()
             self.resource_types_client = self.image_v2.ResourceTypesClient()
             self.namespace_objects_client = \
diff --git a/tempest/common/utils/net_downtime.py b/tempest/common/utils/net_downtime.py
new file mode 100644
index 0000000..9675ec8
--- /dev/null
+++ b/tempest/common/utils/net_downtime.py
@@ -0,0 +1,63 @@
+# Copyright 2022 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 signal
+import subprocess
+
+import fixtures
+
+from oslo_log import log
+
+
+LOG = log.getLogger(__name__)
+
+
+class NetDowntimeMeter(fixtures.Fixture):
+    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.ping_process = None
+
+    def _setUp(self):
+        self.start_background_pinger()
+
+    def start_background_pinger(self):
+        cmd = ['ping', '-q', '-s1']
+        cmd.append('-i{}'.format(self.interval))
+        cmd.append(self.dest_ip)
+        LOG.debug("Starting background pinger to '{}' with interval {}".format(
+            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))
+            self.ping_process.terminate()
+        self.ping_process = None
+
+    def get_downtime(self):
+        self.ping_process.send_signal(signal.SIGQUIT)
+        # Example of the expected output:
+        # 264/274 packets, 3% loss
+        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)
+        else:
+            LOG.warning('Unexpected output obtained from the pinger: %s',
+                        output)
diff --git a/tempest/config.py b/tempest/config.py
index ebde421..f986ddb 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -965,6 +965,12 @@
                default='ecdsa',
                help='Type of key to use for ssh connections. '
                     'Valid types are rsa, ecdsa'),
+    cfg.IntOpt('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."),
 ]
 
 volume_group = cfg.OptGroup(name='volume',
@@ -1158,6 +1164,11 @@
     cfg.BoolOpt('discoverability',
                 default=True,
                 help="Execute discoverability tests"),
+    cfg.StrOpt('tempurl_digest_hashlib',
+               default='sha256',
+               help="Hashing algorithm to use for the temp_url tests. "
+                    "Needs to be supported both by Swift and the "
+                    "hashlib module, for example sha1 or sha256"),
 ]
 
 
diff --git a/tempest/services/__init__.py b/tempest/lib/api_schema/response/volume/v3_61/__init__.py
similarity index 100%
rename from tempest/services/__init__.py
rename to tempest/lib/api_schema/response/volume/v3_61/__init__.py
diff --git a/tempest/lib/api_schema/response/volume/v3_61/volumes.py b/tempest/lib/api_schema/response/volume/v3_61/volumes.py
new file mode 100644
index 0000000..2e28b7e
--- /dev/null
+++ b/tempest/lib/api_schema/response/volume/v3_61/volumes.py
@@ -0,0 +1,69 @@
+# Copyright 2022 Red Hat, Inc.
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+import copy
+
+from tempest.lib.api_schema.response.compute.v2_1 import parameter_types
+from tempest.lib.api_schema.response.volume import volumes
+
+# Volume micro version 3.61:
+# 1. Add cluster_name attribute to response body of volume details
+# for admin in Active/Active HA mode.
+# https://docs.openstack.org/cinder/latest/contributor/
+# api_microversion_history.html
+
+common_show_volume = copy.deepcopy(volumes.common_show_volume)
+common_show_volume['properties'].update(
+    {'cluster_name': parameter_types.uuid_or_null})
+
+create_volume = copy.deepcopy(volumes.create_volume)
+create_volume['response_body']['properties']['volume']['properties'].update(
+    {'cluster_name': parameter_types.uuid_or_null})
+
+# copy unchanged volumes schema
+attachments = copy.deepcopy(volumes.attachments)
+list_volumes_no_detail = copy.deepcopy(volumes.list_volumes_no_detail)
+# show_volume refers to common_show_volume
+show_volume = copy.deepcopy(volumes.show_volume)
+show_volume['response_body']['properties']['volume'] = common_show_volume
+# list copy refers to latest common_show_volume
+list_volumes_detail = copy.deepcopy(common_show_volume)
+list_volumes_with_detail = copy.deepcopy(volumes.list_volumes_with_detail)
+list_volumes_with_detail['response_body']['properties']['volumes']['items'] \
+    = list_volumes_detail
+update_volume = copy.deepcopy(volumes.update_volume)
+delete_volume = copy.deepcopy(volumes.delete_volume)
+show_volume_summary = copy.deepcopy(volumes.show_volume_summary)
+attach_volume = copy.deepcopy(volumes.attach_volume)
+set_bootable_volume = copy.deepcopy(volumes.set_bootable_volume)
+detach_volume = copy.deepcopy(volumes.detach_volume)
+reserve_volume = copy.deepcopy(volumes.reserve_volume)
+unreserve_volume = copy.deepcopy(volumes.unreserve_volume)
+extend_volume = copy.deepcopy(volumes.extend_volume)
+reset_volume_status = copy.deepcopy(volumes.reset_volume_status)
+update_volume_readonly = copy.deepcopy(volumes.update_volume_readonly)
+force_delete_volume = copy.deepcopy(volumes.force_delete_volume)
+retype_volume = copy.deepcopy(volumes.retype_volume)
+force_detach_volume = copy.deepcopy(volumes.force_detach_volume)
+create_volume_metadata = copy.deepcopy(volumes.create_volume_metadata)
+show_volume_metadata = copy.deepcopy(volumes.show_volume_metadata)
+update_volume_metadata = copy.deepcopy(volumes.update_volume_metadata)
+update_volume_metadata_item = copy.deepcopy(
+    volumes.update_volume_metadata_item)
+update_volume_image_metadata = copy.deepcopy(
+    volumes.update_volume_image_metadata)
+delete_volume_image_metadata = copy.deepcopy(
+    volumes.delete_volume_image_metadata)
+unmanage_volume = copy.deepcopy(volumes.unmanage_volume)
diff --git a/tempest/services/__init__.py b/tempest/lib/api_schema/response/volume/v3_63/__init__.py
similarity index 100%
copy from tempest/services/__init__.py
copy to tempest/lib/api_schema/response/volume/v3_63/__init__.py
diff --git a/tempest/lib/api_schema/response/volume/v3_63/volumes.py b/tempest/lib/api_schema/response/volume/v3_63/volumes.py
new file mode 100644
index 0000000..218db90
--- /dev/null
+++ b/tempest/lib/api_schema/response/volume/v3_63/volumes.py
@@ -0,0 +1,69 @@
+# Copyright 2022 Red Hat, Inc.
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+import copy
+
+from tempest.lib.api_schema.response.compute.v2_1 import parameter_types
+from tempest.lib.api_schema.response.volume.v3_61 import volumes
+
+# Volume micro version 3.63:
+# 1. Includes volume type ID in the volume-show and volume-detail-list.
+# for admin in Active/Active HA mode.
+# https://docs.openstack.org/cinder/latest/contributor/
+# api_microversion_history.html
+
+common_show_volume = copy.deepcopy(volumes.common_show_volume)
+common_show_volume['properties'].update(
+    {'volume_type_id': parameter_types.uuid_or_null})
+
+create_volume = copy.deepcopy(volumes.create_volume)
+create_volume['response_body']['properties']['volume']['properties'].update(
+    {'volume_type_id': parameter_types.uuid_or_null})
+
+# copy unchanged volumes schema
+attachments = copy.deepcopy(volumes.attachments)
+list_volumes_no_detail = copy.deepcopy(volumes.list_volumes_no_detail)
+# show_volume refers to common_show_volume
+show_volume = copy.deepcopy(volumes.show_volume)
+show_volume['response_body']['properties']['volume'] = common_show_volume
+# list copy refers to latest common_show_volume
+list_volumes_detail = copy.deepcopy(common_show_volume)
+list_volumes_with_detail = copy.deepcopy(volumes.list_volumes_with_detail)
+list_volumes_with_detail['response_body']['properties']['volumes']['items'] \
+    = list_volumes_detail
+update_volume = copy.deepcopy(volumes.update_volume)
+delete_volume = copy.deepcopy(volumes.delete_volume)
+show_volume_summary = copy.deepcopy(volumes.show_volume_summary)
+attach_volume = copy.deepcopy(volumes.attach_volume)
+set_bootable_volume = copy.deepcopy(volumes.set_bootable_volume)
+detach_volume = copy.deepcopy(volumes.detach_volume)
+reserve_volume = copy.deepcopy(volumes.reserve_volume)
+unreserve_volume = copy.deepcopy(volumes.unreserve_volume)
+extend_volume = copy.deepcopy(volumes.extend_volume)
+reset_volume_status = copy.deepcopy(volumes.reset_volume_status)
+update_volume_readonly = copy.deepcopy(volumes.update_volume_readonly)
+force_delete_volume = copy.deepcopy(volumes.force_delete_volume)
+retype_volume = copy.deepcopy(volumes.retype_volume)
+force_detach_volume = copy.deepcopy(volumes.force_detach_volume)
+create_volume_metadata = copy.deepcopy(volumes.create_volume_metadata)
+show_volume_metadata = copy.deepcopy(volumes.show_volume_metadata)
+update_volume_metadata = copy.deepcopy(volumes.update_volume_metadata)
+update_volume_metadata_item = copy.deepcopy(
+    volumes.update_volume_metadata_item)
+update_volume_image_metadata = copy.deepcopy(
+    volumes.update_volume_image_metadata)
+delete_volume_image_metadata = copy.deepcopy(
+    volumes.delete_volume_image_metadata)
+unmanage_volume = copy.deepcopy(volumes.unmanage_volume)
diff --git a/tempest/services/__init__.py b/tempest/lib/api_schema/response/volume/v3_64/__init__.py
similarity index 100%
copy from tempest/services/__init__.py
copy to tempest/lib/api_schema/response/volume/v3_64/__init__.py
diff --git a/tempest/lib/api_schema/response/volume/v3_64/backups.py b/tempest/lib/api_schema/response/volume/v3_64/backups.py
new file mode 100644
index 0000000..01b93bc
--- /dev/null
+++ b/tempest/lib/api_schema/response/volume/v3_64/backups.py
@@ -0,0 +1,48 @@
+# Copyright 2022 Red Hat, Inc.
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+import copy
+
+from tempest.lib.api_schema.response.compute.v2_1 import parameter_types
+from tempest.lib.api_schema.response.volume import backups
+
+# Volume micro version 3.64:
+# 1. Include the encryption_key_id in volume and backup
+# details when the associated volume is encrypted.
+# https://docs.openstack.org/cinder/latest/contributor/
+# api_microversion_history.html
+
+common_show_backup = copy.deepcopy(backups.common_show_backup)
+common_show_backup['properties'].update(
+    {'encryption_key_id': parameter_types.uuid_or_null})
+
+create_backup = copy.deepcopy(backups.create_backup)
+update_backup = copy.deepcopy(backups.update_backup)
+restore_backup = copy.deepcopy(backups.restore_backup)
+delete_backup = copy.deepcopy(backups.delete_backup)
+# show backup refers to common_show_backup
+show_backup = copy.deepcopy(backups.show_backup)
+show_backup['response_body']['properties']['backup'] = common_show_backup
+list_backups_no_detail = copy.deepcopy(backups.list_backups_no_detail)
+# list_backups_detail refers to latest common_show_backup
+list_backups_detail = copy.deepcopy(common_show_backup)
+list_backups_detail['properties'].update({'count': {'type': 'integer'}})
+list_backups_with_detail = copy.deepcopy(backups.list_backups_with_detail)
+# list_backups_with_detail refers to latest list_backups_detail
+list_backups_with_detail['response_body']['properties']['backups']['items'] =\
+    list_backups_detail
+export_backup = copy.deepcopy(backups.export_backup)
+import_backup = copy.deepcopy(backups.import_backup)
+reset_backup_status = copy.deepcopy(backups.reset_backup_status)
diff --git a/tempest/lib/api_schema/response/volume/v3_64/volumes.py b/tempest/lib/api_schema/response/volume/v3_64/volumes.py
new file mode 100644
index 0000000..0fbbb3f
--- /dev/null
+++ b/tempest/lib/api_schema/response/volume/v3_64/volumes.py
@@ -0,0 +1,69 @@
+# Copyright 2022 Red Hat, Inc.
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+import copy
+
+from tempest.lib.api_schema.response.compute.v2_1 import parameter_types
+from tempest.lib.api_schema.response.volume.v3_63 import volumes
+
+# Volume micro version 3.64:
+# 1. Include the encryption_key_id in volume and backup
+# details when the associated volume is encrypted.
+# https://docs.openstack.org/cinder/latest/contributor/
+# api_microversion_history.html
+
+common_show_volume = copy.deepcopy(volumes.common_show_volume)
+common_show_volume['properties'].update(
+    {'encryption_key_id': parameter_types.uuid_or_null})
+
+create_volume = copy.deepcopy(volumes.create_volume)
+create_volume['response_body']['properties']['volume']['properties'].update(
+    {'encryption_key_id': parameter_types.uuid_or_null})
+
+# copy unchanged volumes schema
+attachments = copy.deepcopy(volumes.attachments)
+list_volumes_no_detail = copy.deepcopy(volumes.list_volumes_no_detail)
+# show_volume refers to common_show_volume
+show_volume = copy.deepcopy(volumes.show_volume)
+show_volume['response_body']['properties']['volume'] = common_show_volume
+# list_volumes_detail refers to latest common_show_volume
+list_volumes_detail = copy.deepcopy(common_show_volume)
+list_volumes_with_detail = copy.deepcopy(volumes.list_volumes_with_detail)
+list_volumes_with_detail['response_body']['properties']['volumes']['items'] \
+    = list_volumes_detail
+update_volume = copy.deepcopy(volumes.update_volume)
+delete_volume = copy.deepcopy(volumes.delete_volume)
+show_volume_summary = copy.deepcopy(volumes.show_volume_summary)
+attach_volume = copy.deepcopy(volumes.attach_volume)
+set_bootable_volume = copy.deepcopy(volumes.set_bootable_volume)
+detach_volume = copy.deepcopy(volumes.detach_volume)
+reserve_volume = copy.deepcopy(volumes.reserve_volume)
+unreserve_volume = copy.deepcopy(volumes.unreserve_volume)
+extend_volume = copy.deepcopy(volumes.extend_volume)
+reset_volume_status = copy.deepcopy(volumes.reset_volume_status)
+update_volume_readonly = copy.deepcopy(volumes.update_volume_readonly)
+force_delete_volume = copy.deepcopy(volumes.force_delete_volume)
+retype_volume = copy.deepcopy(volumes.retype_volume)
+force_detach_volume = copy.deepcopy(volumes.force_detach_volume)
+create_volume_metadata = copy.deepcopy(volumes.create_volume_metadata)
+show_volume_metadata = copy.deepcopy(volumes.show_volume_metadata)
+update_volume_metadata = copy.deepcopy(volumes.update_volume_metadata)
+update_volume_metadata_item = copy.deepcopy(
+    volumes.update_volume_metadata_item)
+update_volume_image_metadata = copy.deepcopy(
+    volumes.update_volume_image_metadata)
+delete_volume_image_metadata = copy.deepcopy(
+    volumes.delete_volume_image_metadata)
+unmanage_volume = copy.deepcopy(volumes.unmanage_volume)
diff --git a/tempest/lib/common/rest_client.py b/tempest/lib/common/rest_client.py
index ef14dfc..a11b7c1 100644
--- a/tempest/lib/common/rest_client.py
+++ b/tempest/lib/common/rest_client.py
@@ -414,6 +414,11 @@
                 return resp[i]
         return ""
 
+    def _get_global_request_id(self, resp):
+        if 'x-openstack-request-id' in resp:
+            return resp['x-openstack-request-id']
+        return ''
+
     def _safe_body(self, body, maxlen=4096):
         # convert a structure into a string safely
         try:
@@ -461,7 +466,10 @@
         if req_headers is None:
             req_headers = {}
         # if we have the request id, put it in the right part of the log
-        extra = dict(request_id=self._get_request_id(resp))
+        extra = {
+            'request_id': self._get_request_id(resp),
+            'global_request_id': self._get_global_request_id(resp),
+        }
         # NOTE(sdague): while we still have 6 callers to this function
         # we're going to just provide work around on who is actually
         # providing timings by gracefully adding no content if they don't.
@@ -484,7 +492,7 @@
             self._log_request_full(resp, req_headers, req_body,
                                    resp_body, extra)
 
-    def _parse_resp(self, body):
+    def _parse_resp(self, body, top_key_to_verify=None):
         try:
             body = json.loads(body)
         except ValueError:
@@ -508,8 +516,17 @@
             if not hasattr(body, "keys") or len(body.keys()) != 1:
                 return body
             # Just return the "wrapped" element
-            _, first_item = tuple(body.items())[0]
+            first_key, first_item = tuple(body.items())[0]
             if isinstance(first_item, (dict, list)):
+                if top_key_to_verify is not None:
+                    msg_args = {
+                        'top_key': top_key_to_verify,
+                        'actual_key': first_key,
+                    }
+                    assert_msg = ("The expected top level key is "
+                                  "'%(top_key)s' but we found "
+                                  "'%(actual_key)s'." % msg_args)
+                    assert top_key_to_verify == first_key, assert_msg
                 return first_item
         except (ValueError, IndexError):
             pass
diff --git a/tempest/lib/services/image/v2/__init__.py b/tempest/lib/services/image/v2/__init__.py
index 99a5321..a2f5bdc 100644
--- a/tempest/lib/services/image/v2/__init__.py
+++ b/tempest/lib/services/image/v2/__init__.py
@@ -12,6 +12,8 @@
 # License for the specific language governing permissions and limitations under
 # the License.
 
+from tempest.lib.services.image.v2.image_cache_client import \
+    ImageCacheClient
 from tempest.lib.services.image.v2.image_members_client import \
     ImageMembersClient
 from tempest.lib.services.image.v2.images_client import ImagesClient
@@ -27,7 +29,7 @@
 from tempest.lib.services.image.v2.schemas_client import SchemasClient
 from tempest.lib.services.image.v2.versions_client import VersionsClient
 
-__all__ = ['ImageMembersClient', 'ImagesClient', 'NamespaceObjectsClient',
-           'NamespacePropertiesClient', 'NamespaceTagsClient',
-           'NamespacesClient', 'ResourceTypesClient', 'SchemasClient',
-           'VersionsClient']
+__all__ = ['ImageMembersClient', 'ImagesClient', 'ImageCacheClient',
+           'NamespaceObjectsClient', 'NamespacePropertiesClient',
+           'NamespaceTagsClient', 'NamespacesClient', 'ResourceTypesClient',
+           'SchemasClient', 'VersionsClient']
diff --git a/tempest/lib/services/image/v2/image_cache_client.py b/tempest/lib/services/image/v2/image_cache_client.py
new file mode 100644
index 0000000..90ff776
--- /dev/null
+++ b/tempest/lib/services/image/v2/image_cache_client.py
@@ -0,0 +1,74 @@
+# Copyright 2022 Red Hat, Inc.
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from oslo_serialization import jsonutils as json
+
+from tempest.lib.common import rest_client
+
+
+class ImageCacheClient(rest_client.RestClient):
+    api_version = "v2"
+
+    def list_cache(self):
+        """Lists all images in cache or queue. (Since Image API v2.14)
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://docs.openstack.org/api-ref/image/v2/?expanded=query-cache-status-detail#cache-manage
+        """
+        url = 'cache'
+        resp, body = self.get(url)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def cache_queue(self, image_id):
+        """Queues image for caching. (Since Image API v2.14)
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://docs.openstack.org/api-ref/image/v2/?expanded=queue-image-detail#queue-image
+        """
+        url = 'cache/%s' % image_id
+        resp, body = self.put(url, body=None)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body=body)
+
+    def cache_delete(self, image_id):
+        """Deletes a image from cache. (Since Image API v2.14)
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://docs.openstack.org/api-ref/image/v2/?expanded=delete-image-from-cache-detail#delete-image-from-cache
+        """
+        url = 'cache/%s' % image_id
+        resp, _ = self.delete(url)
+        self.expected_success(204, resp.status)
+        return rest_client.ResponseBody(resp)
+
+    def cache_clear(self, target=None):
+        """Clears the cache and its queue. (Since Image API v2.14)
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://docs.openstack.org/api-ref/image/v2/?expanded=clear-images-from-cache-detail#delete-image-from-cache
+        """
+        url = 'cache'
+        headers = {}
+        if target:
+            headers['x-image-cache-clear-target'] = target
+        resp, _ = self.delete(url, headers=headers)
+        self.expected_success(204, resp.status)
+        return rest_client.ResponseBody(resp)
diff --git a/tempest/lib/services/volume/v3/backups_client.py b/tempest/lib/services/volume/v3/backups_client.py
index 4bf7ffb..0c32c52 100644
--- a/tempest/lib/services/volume/v3/backups_client.py
+++ b/tempest/lib/services/volume/v3/backups_client.py
@@ -18,6 +18,7 @@
 from oslo_serialization import jsonutils as json
 
 from tempest.lib.api_schema.response.volume import backups as schema
+from tempest.lib.api_schema.response.volume.v3_64 import backups as schemav364
 from tempest.lib.common import rest_client
 from tempest.lib import exceptions as lib_exc
 from tempest.lib.services.volume import base_client
@@ -26,6 +27,11 @@
 class BackupsClient(base_client.BaseClient):
     """Volume V3 Backups client"""
 
+    schema_versions_info = [
+        {'min': None, 'max': '3.63', 'schema': schema},
+        {'min': '3.64', 'max': None, 'schema': schemav364}
+        ]
+
     def create_backup(self, **kwargs):
         """Creates a backup of volume.
 
@@ -76,6 +82,7 @@
         url = "backups/%s" % backup_id
         resp, body = self.get(url)
         body = json.loads(body)
+        schema = self.get_schema(self.schema_versions_info)
         self.validate_response(schema.show_backup, resp, body)
         return rest_client.ResponseBody(resp, body)
 
@@ -88,6 +95,7 @@
         https://docs.openstack.org/api-ref/block-storage/v3/index.html#list-backups-with-detail
         """
         url = "backups"
+        schema = self.get_schema(self.schema_versions_info)
         list_backups_schema = schema.list_backups_no_detail
         if detail:
             url += "/detail"
diff --git a/tempest/lib/services/volume/v3/volumes_client.py b/tempest/lib/services/volume/v3/volumes_client.py
index 9c6fe68..9934e47 100644
--- a/tempest/lib/services/volume/v3/volumes_client.py
+++ b/tempest/lib/services/volume/v3/volumes_client.py
@@ -17,6 +17,9 @@
 
 from oslo_serialization import jsonutils as json
 
+from tempest.lib.api_schema.response.volume.v3_61 import volumes as schemav361
+from tempest.lib.api_schema.response.volume.v3_63 import volumes as schemav363
+from tempest.lib.api_schema.response.volume.v3_64 import volumes as schemav364
 from tempest.lib.api_schema.response.volume import volumes as schema
 from tempest.lib.common import rest_client
 from tempest.lib import exceptions as lib_exc
@@ -26,6 +29,13 @@
 class VolumesClient(base_client.BaseClient):
     """Client class to send CRUD Volume V3 API requests"""
 
+    schema_versions_info = [
+        {'min': None, 'max': '3.60', 'schema': schema},
+        {'min': '3.61', 'max': '3.62', 'schema': schemav361},
+        {'min': '3.63', 'max': '3.63', 'schema': schemav363},
+        {'min': '3.64', 'max': None, 'schema': schemav364}
+        ]
+
     def _prepare_params(self, params):
         """Prepares params for use in get or _ext_get methods.
 
@@ -56,6 +66,7 @@
         https://docs.openstack.org/api-ref/block-storage/v3/index.html#list-accessible-volumes
         """
         url = 'volumes'
+        schema = self.get_schema(self.schema_versions_info)
         list_schema = schema.list_volumes_no_detail
         if detail:
             list_schema = schema.list_volumes_with_detail
@@ -86,6 +97,7 @@
         url = "volumes/%s" % volume_id
         resp, body = self.get(url)
         body = json.loads(body)
+        schema = self.get_schema(self.schema_versions_info)
         self.validate_response(schema.show_volume, resp, body)
         return rest_client.ResponseBody(resp, body)
 
@@ -99,6 +111,7 @@
         post_body = json.dumps({'volume': kwargs})
         resp, body = self.post('volumes', post_body)
         body = json.loads(body)
+        schema = self.get_schema(self.schema_versions_info)
         self.validate_response(schema.create_volume, resp, body)
         return rest_client.ResponseBody(resp, body)
 
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index db28487..2843498 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -419,8 +419,12 @@
 
         body = self.backups_client.restore_backup(backup_id, **kwargs)
         restore = body['restore']
-        self.addCleanup(self.volumes_client.delete_volume,
-                        restore['volume_id'])
+
+        using_pre_existing_volume = kwargs.get('volume_id', False)
+        if not using_pre_existing_volume:
+            self.addCleanup(self.volumes_client.delete_volume,
+                            restore['volume_id'])
+
         waiters.wait_for_volume_resource_status(self.backups_client,
                                                 backup_id, 'available')
         waiters.wait_for_volume_resource_status(self.volumes_client,
@@ -473,7 +477,8 @@
 
         self.addCleanup(self.snapshots_client.wait_for_resource_deletion,
                         snapshot['id'])
-        self.addCleanup(self.snapshots_client.delete_snapshot, 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')
         snapshot = self.snapshots_client.show_snapshot(
@@ -810,7 +815,9 @@
             name = data_utils.rand_name(self.__class__.__name__ + 'snapshot')
         LOG.debug("Creating a snapshot image for server: %s", server['name'])
         image = _images_client.create_image(server['id'], name=name, **kwargs)
-        image_id = image.response['location'].split('images/')[1]
+        # microversion 2.45 and above returns image_id
+        image_id = image.get('image_id') or image.response['location'].split(
+            'images/')[1]
         waiters.wait_for_image_status(_image_client, image_id, 'active')
 
         self.addCleanup(_image_client.wait_for_resource_deletion,
diff --git a/tempest/scenario/test_encrypted_cinder_volumes.py b/tempest/scenario/test_encrypted_cinder_volumes.py
index 6ee9f28..9788e19 100644
--- a/tempest/scenario/test_encrypted_cinder_volumes.py
+++ b/tempest/scenario/test_encrypted_cinder_volumes.py
@@ -13,6 +13,8 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import testtools
+
 from tempest.common import utils
 from tempest import config
 from tempest.lib import decorators
@@ -27,7 +29,7 @@
 
     This test is for verifying the functionality of encrypted cinder volumes.
 
-    For both LUKS and cryptsetup encryption types, this test performs
+    For both LUKS (v1 & v2) and cryptsetup encryption types, this test performs
     the following:
 
     * Boots an instance from an image (CONF.compute.image_ref)
@@ -55,11 +57,24 @@
     @decorators.attr(type='slow')
     @utils.services('compute', 'volume', 'image')
     def test_encrypted_cinder_volumes_luks(self):
+        """LUKs v1 decrypts volume through libvirt."""
         server = self.launch_instance()
         volume = self.create_encrypted_volume('luks',
                                               volume_type='luks')
         self.attach_detach_volume(server, volume)
 
+    @decorators.idempotent_id('7abec0a3-61a0-42a5-9e36-ad3138fb38b4')
+    @testtools.skipIf(CONF.volume.storage_protocol == 'ceph',
+                      'Ceph only supports LUKSv2 if doing host attach.')
+    @decorators.attr(type='slow')
+    @utils.services('compute', 'volume', 'image')
+    def test_encrypted_cinder_volumes_luksv2(self):
+        """LUKs v2 decrypts volume through os-brick."""
+        server = self.launch_instance()
+        volume = self.create_encrypted_volume('luks2',
+                                              volume_type='luksv2')
+        self.attach_detach_volume(server, volume)
+
     @decorators.idempotent_id('cbc752ed-b716-4717-910f-956cce965722')
     @decorators.attr(type='slow')
     @utils.services('compute', 'volume', 'image')
diff --git a/tempest/scenario/test_minimum_basic.py b/tempest/scenario/test_minimum_basic.py
index 5aac19c..5fcaa10 100644
--- a/tempest/scenario/test_minimum_basic.py
+++ b/tempest/scenario/test_minimum_basic.py
@@ -13,6 +13,8 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import testtools
+
 from tempest.common import custom_matchers
 from tempest.common import utils
 from tempest.common import waiters
@@ -29,24 +31,11 @@
 
     """This is a basic minimum scenario test.
 
-    This test below:
+    These tests below:
     * across the multiple components
     * as a regular user
     * with and without optional parameters
     * check command outputs
-
-    Steps:
-    1. Create image
-    2. Create keypair
-    3. Boot instance with keypair and get list of instances
-    4. Create volume and show list of volumes
-    5. Attach volume to instance and getlist of volumes
-    6. Add IP to instance
-    7. Create and add security group to instance
-    8. Check SSH connection to instance
-    9. Reboot instance
-    10. Check SSH connection to instance after reboot
-
     """
 
     def nova_show(self, server):
@@ -67,8 +56,9 @@
             volume, custom_matchers.MatchesDictExceptForKeys(
                 got_volume, excluded_keys=excluded_keys))
 
-    def nova_reboot(self, server):
-        self.servers_client.reboot_server(server['id'], type='SOFT')
+    def nova_reboot(self, server, hard=False):
+        self.servers_client.reboot_server(server['id'],
+                                          type="HARD" if hard else "SOFT")
         waiters.wait_for_server_status(self.servers_client,
                                        server['id'], 'ACTIVE')
 
@@ -96,9 +86,37 @@
                    '%s' % (secgroup['id'], server['id']))
             raise exceptions.TimeoutException(msg)
 
+    def _get_floating_ip_in_server_addresses(self, floating_ip, server):
+        for addresses in server['addresses'].values():
+            for address in addresses:
+                if (address['OS-EXT-IPS:type'] == 'floating' and
+                        address['addr'] == floating_ip['floating_ip_address']):
+                    return address
+
+    def _is_floating_ip_detached_from_server(self, server, floating_ip):
+        server_info = self.servers_client.show_server(
+            server['id'])['server']
+        address = self._get_floating_ip_in_server_addresses(
+            floating_ip, server_info)
+        return (not address)
+
     @decorators.idempotent_id('bdbb5441-9204-419d-a225-b4fdbfb1a1a8')
     @utils.services('compute', 'volume', 'image', 'network')
     def test_minimum_basic_scenario(self):
+        """This is a basic minimum scenario with multiple components
+
+        Steps:
+        1. Create image
+        2. Create keypair
+        3. Boot instance with keypair and get list of instances
+        4. Create volume and show list of volumes
+        5. Attach volume to instance and getlist of volumes
+        6. Add IP to instance
+        7. Create and add security group to instance
+        8. Check SSH connection to instance
+        9. Reboot instance
+        10. Check SSH connection to instance after reboot
+        """
         image = self.image_create()
         keypair = self.create_keypair()
 
@@ -121,7 +139,7 @@
         floating_ip = None
         server = self.servers_client.show_server(server['id'])['server']
         if (CONF.network_feature_enabled.floating_ips and
-            CONF.network.floating_network_name):
+                CONF.network.floating_network_name):
             fip = self.create_floating_ip(server)
             floating_ip = self.associate_floating_ip(
                 fip, server)
@@ -154,3 +172,118 @@
             waiters.wait_for_server_floating_ip(
                 self.servers_client, server, floating_ip,
                 wait_for_disassociate=True)
+
+            if not test_utils.call_until_true(
+                    self._is_floating_ip_detached_from_server,
+                    CONF.compute.build_timeout,
+                    CONF.compute.build_interval, server, floating_ip):
+                msg = ("Floating IP '%s' should not be in server addresses: %s"
+                       % (floating_ip['floating_ip_address'],
+                          server['addresses']))
+                raise exceptions.TimeoutException(msg)
+
+    @decorators.idempotent_id('a8fd48ec-1d01-4895-b932-02321661ec1e')
+    @testtools.skipUnless(CONF.volume_feature_enabled.snapshot,
+                          "Cinder volume snapshots are disabled")
+    @utils.services('compute', 'volume', 'image', 'network')
+    def test_minimum_basic_instance_hard_reboot_after_vol_snap_deletion(self):
+        """Test compute hard reboot after volume snapshot deleted
+
+        Steps:
+        1. Create image
+        2. Create keypair
+        3. Boot instance with keypair and get list of instances
+        4. Create volume and show list of volumes
+        5. Attach volume to instance and getlist of volumes
+        6. Create a snapshot from volume
+        7. Add IP to instance
+        8. Create and add security group to instance
+        9. Check SSH connection to instance
+        10. Write data timestamp to the attached volume
+        11. Delete volume snapshot before reboot instance
+        12. Reboot instance (HARD)
+        13. Check SSH connection to instance after reboot
+        14. Verify attached disk data timestamp post instance reboot
+        """
+        image = self.image_create()
+        keypair = self.create_keypair()
+
+        server = self.create_server(image_id=image, key_name=keypair['name'])
+        servers = self.servers_client.list_servers()['servers']
+        self.assertIn(server['id'], [x['id'] for x in servers])
+
+        self.nova_show(server)
+
+        volume = self.create_volume()
+        volumes = self.volumes_client.list_volumes()['volumes']
+        self.assertIn(volume['id'], [x['id'] for x in volumes])
+
+        self.cinder_show(volume)
+
+        volume = self.nova_volume_attach(server, volume)
+        self.addCleanup(self.nova_volume_detach, server, volume)
+        snapshot = self.create_volume_snapshot(volume['id'], force=True)
+        self.cinder_show(volume)
+
+        floating_ip = None
+        server = self.servers_client.show_server(server['id'])['server']
+        if (CONF.network_feature_enabled.floating_ips and
+                CONF.network.floating_network_name):
+            fip = self.create_floating_ip(server)
+            floating_ip = self.associate_floating_ip(
+                fip, server)
+            # fetch the server again to make sure the addresses were refreshed
+            # after associating the floating IP
+            waiters.wait_for_server_floating_ip(self.servers_client, server,
+                                                floating_ip)
+            server = self.servers_client.show_server(server['id'])['server']
+            address = self._get_floating_ip_in_server_addresses(
+                floating_ip, server)
+            self.assertIsNotNone(
+                address,
+                "Failed to find floating IP '%s' in server addresses: %s" %
+                (floating_ip['floating_ip_address'], server['addresses']))
+            ssh_ip = floating_ip['floating_ip_address']
+        else:
+            ssh_ip = self.get_server_ip(server)
+
+        self.create_and_add_security_group_to_server(server)
+
+        # check that we can SSH to the server before reboot
+        self.linux_client = self.get_remote_client(
+            ssh_ip, private_key=keypair['private_key'],
+            server=server)
+
+        # write data to the volume before reboot instance
+        timestamp_before = self.create_timestamp(
+            ssh_ip, private_key=keypair['private_key'], server=server)
+        # delete the snapshot before rebooting the instance
+        self.snapshots_client.delete_snapshot(snapshot['id'])
+        self.snapshots_client.wait_for_resource_deletion(snapshot['id'])
+        self.nova_reboot(server, hard=True)
+
+        # check that we can SSH to the server after reboot
+        # (both connections are part of the scenario)
+        self.linux_client = self.get_remote_client(
+            ssh_ip, private_key=keypair['private_key'],
+            server=server)
+
+        self.check_disks()
+        timestamp_after = self.get_timestamp(
+            ssh_ip, private_key=keypair['private_key'], server=server)
+        self.assertEqual(timestamp_before, timestamp_after)
+        if floating_ip:
+            # delete the floating IP, this should refresh the server addresses
+            self.disassociate_floating_ip(floating_ip)
+            waiters.wait_for_server_floating_ip(
+                self.servers_client, server, floating_ip,
+                wait_for_disassociate=True)
+
+            if not test_utils.call_until_true(
+                self._is_floating_ip_detached_from_server,
+                    CONF.compute.build_timeout, CONF.compute.build_interval,
+                    server, floating_ip):
+                msg = ("Floating IP '%s' should not be in server addresses: %s"
+                       % (floating_ip['floating_ip_address'],
+                          server['addresses']))
+                raise exceptions.TimeoutException(msg)
diff --git a/tempest/scenario/test_network_advanced_server_ops.py b/tempest/scenario/test_network_advanced_server_ops.py
index b48ac3c..e630e29 100644
--- a/tempest/scenario/test_network_advanced_server_ops.py
+++ b/tempest/scenario/test_network_advanced_server_ops.py
@@ -15,7 +15,9 @@
 
 import testtools
 
+from oslo_log import log
 from tempest.common import utils
+from tempest.common.utils import net_downtime
 from tempest.common import waiters
 from tempest import config
 from tempest.lib import decorators
@@ -23,6 +25,8 @@
 
 CONF = config.CONF
 
+LOG = log.getLogger(__name__)
+
 
 class TestNetworkAdvancedServerOps(manager.NetworkScenarioTest):
     """Check VM connectivity after some advanced instance operations executed:
@@ -252,6 +256,11 @@
         block_migration = (CONF.compute_feature_enabled.
                            block_migration_for_live_migration)
         old_host = self.get_host_for_server(server['id'])
+
+        downtime_meter = net_downtime.NetDowntimeMeter(
+            floating_ip['floating_ip_address'])
+        self.useFixture(downtime_meter)
+
         self.admin_servers_client.live_migrate_server(
             server['id'], host=None, block_migration=block_migration,
             disk_over_commit=False)
@@ -261,10 +270,19 @@
         new_host = self.get_host_for_server(server['id'])
         self.assertNotEqual(old_host, new_host, 'Server did not migrate')
 
+        downtime = downtime_meter.get_downtime()
+        self.assertIsNotNone(downtime)
+        LOG.debug("Downtime seconds measured with downtime_meter = %r",
+                  downtime)
+        allowed_downtime = CONF.validation.allowed_network_downtime
+        self.assertLess(
+            downtime, allowed_downtime,
+            "Downtime of {} seconds is higher than expected '{}'".format(
+                downtime, allowed_downtime))
+
         self._wait_server_status_and_check_network_connectivity(
             server, keypair, floating_ip)
 
-    @decorators.unstable_test(bug='1836595')
     @decorators.idempotent_id('25b188d7-0183-4b1e-a11d-15840c8e2fd6')
     @testtools.skipUnless(CONF.compute_feature_enabled.cold_migration,
                           'Cold migration is not available.')
diff --git a/tempest/scenario/test_volume_boot_pattern.py b/tempest/scenario/test_volume_boot_pattern.py
index 5a5cc27..2e87c15 100644
--- a/tempest/scenario/test_volume_boot_pattern.py
+++ b/tempest/scenario/test_volume_boot_pattern.py
@@ -246,14 +246,10 @@
         # Assert that the underlying volume is gone.
         self.volumes_client.wait_for_resource_deletion(volume_origin['id'])
 
-    @decorators.idempotent_id('cb78919a-e553-4bab-b73b-10cf4d2eb125')
-    @testtools.skipUnless(CONF.compute_feature_enabled.attach_encrypted_volume,
-                          'Encrypted volume attach is not supported')
-    @utils.services('compute', 'volume')
-    def test_boot_server_from_encrypted_volume_luks(self):
+    def _do_test_boot_server_from_encrypted_volume_luks(self, provider):
         # Create an encrypted volume
-        volume = self.create_encrypted_volume('luks',
-                                              volume_type='luks')
+        volume = self.create_encrypted_volume(provider,
+                                              volume_type=provider)
 
         self.volumes_client.set_bootable_volume(volume['id'], bootable=True)
 
@@ -266,3 +262,21 @@
         server_info = self.servers_client.show_server(server['id'])['server']
         created_volume = server_info['os-extended-volumes:volumes_attached']
         self.assertEqual(volume['id'], created_volume[0]['id'])
+
+    @decorators.idempotent_id('cb78919a-e553-4bab-b73b-10cf4d2eb125')
+    @testtools.skipUnless(CONF.compute_feature_enabled.attach_encrypted_volume,
+                          'Encrypted volume attach is not supported')
+    @utils.services('compute', 'volume')
+    def test_boot_server_from_encrypted_volume_luks(self):
+        """LUKs v1 decrypts volume through libvirt."""
+        self._do_test_boot_server_from_encrypted_volume_luks('luks')
+
+    @decorators.idempotent_id('5ab6100f-1b31-4dd0-a774-68cfd837ef77')
+    @testtools.skipIf(CONF.volume.storage_protocol == 'ceph',
+                      'Ceph only supports LUKSv2 if doing host attach.')
+    @testtools.skipUnless(CONF.compute_feature_enabled.attach_encrypted_volume,
+                          'Encrypted volume attach is not supported')
+    @utils.services('compute', 'volume')
+    def test_boot_server_from_encrypted_volume_luksv2(self):
+        """LUKs v2 decrypts volume through os-brick."""
+        self._do_test_boot_server_from_encrypted_volume_luks('luks2')
diff --git a/tempest/services/orchestration/__init__.py b/tempest/services/orchestration/__init__.py
deleted file mode 100644
index 5a1ffcc..0000000
--- a/tempest/services/orchestration/__init__.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Copyright (c) 2016 Hewlett-Packard Enterprise Development Company, L.P.
-#
-# 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.services.orchestration.json.orchestration_client import \
-    OrchestrationClient
-
-__all__ = ['OrchestrationClient']
diff --git a/tempest/services/orchestration/json/__init__.py b/tempest/services/orchestration/json/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tempest/services/orchestration/json/__init__.py
+++ /dev/null
diff --git a/tempest/services/orchestration/json/orchestration_client.py b/tempest/services/orchestration/json/orchestration_client.py
deleted file mode 100644
index 0d7720e..0000000
--- a/tempest/services/orchestration/json/orchestration_client.py
+++ /dev/null
@@ -1,413 +0,0 @@
-# Copyright 2013 IBM Corp.
-# All Rights Reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
-#    not use this file except in compliance with the License. You may obtain
-#    a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-#    License for the specific language governing permissions and limitations
-#    under the License.
-
-import re
-import time
-from urllib import parse as urllib
-
-from oslo_serialization import jsonutils as json
-
-from tempest import exceptions
-from tempest.lib.common import rest_client
-from tempest.lib import exceptions as lib_exc
-
-
-class OrchestrationClient(rest_client.RestClient):
-
-    def list_stacks(self, params=None):
-        """Lists all stacks for a user."""
-
-        uri = 'stacks'
-        if params:
-            uri += '?%s' % urllib.urlencode(params)
-
-        resp, body = self.get(uri)
-        self.expected_success(200, resp.status)
-        body = json.loads(body)
-        return rest_client.ResponseBody(resp, body)
-
-    def create_stack(self, name, disable_rollback=True, parameters=None,
-                     timeout_mins=60, template=None, template_url=None,
-                     environment=None, files=None):
-        if parameters is None:
-            parameters = {}
-        headers, body = self._prepare_update_create(
-            name,
-            disable_rollback,
-            parameters,
-            timeout_mins,
-            template,
-            template_url,
-            environment,
-            files)
-        uri = 'stacks'
-        resp, body = self.post(uri, headers=headers, body=body)
-        self.expected_success(201, resp.status)
-        body = json.loads(body)
-        return rest_client.ResponseBody(resp, body)
-
-    def update_stack(self, stack_identifier, name, disable_rollback=True,
-                     parameters=None, timeout_mins=60, template=None,
-                     template_url=None, environment=None, files=None):
-        if parameters is None:
-            parameters = {}
-        headers, body = self._prepare_update_create(
-            name,
-            disable_rollback,
-            parameters,
-            timeout_mins,
-            template,
-            template_url,
-            environment)
-
-        uri = "stacks/%s" % stack_identifier
-        resp, body = self.put(uri, headers=headers, body=body)
-        self.expected_success(202, resp.status)
-        return rest_client.ResponseBody(resp, body)
-
-    def _prepare_update_create(self, name, disable_rollback=True,
-                               parameters=None, timeout_mins=60,
-                               template=None, template_url=None,
-                               environment=None, files=None):
-        if parameters is None:
-            parameters = {}
-        post_body = {
-            "stack_name": name,
-            "disable_rollback": disable_rollback,
-            "parameters": parameters,
-            "timeout_mins": timeout_mins,
-            "template": "HeatTemplateFormatVersion: '2012-12-12'\n",
-            "environment": environment,
-            "files": files
-        }
-        if template:
-            post_body['template'] = template
-        if template_url:
-            post_body['template_url'] = template_url
-        body = json.dumps(post_body)
-
-        # Password must be provided on stack create so that heat
-        # can perform future operations on behalf of the user
-        headers = self.get_headers()
-        headers['X-Auth-Key'] = self.password
-        headers['X-Auth-User'] = self.user
-        return headers, body
-
-    def show_stack(self, stack_identifier):
-        """Returns the details of a single stack."""
-        url = "stacks/%s" % stack_identifier
-        resp, body = self.get(url)
-        self.expected_success(200, resp.status)
-        body = json.loads(body)
-        return rest_client.ResponseBody(resp, body)
-
-    def suspend_stack(self, stack_identifier):
-        """Suspend a stack."""
-        url = 'stacks/%s/actions' % stack_identifier
-        body = {'suspend': None}
-        resp, body = self.post(url, json.dumps(body))
-        self.expected_success(200, resp.status)
-        return rest_client.ResponseBody(resp)
-
-    def resume_stack(self, stack_identifier):
-        """Resume a stack."""
-        url = 'stacks/%s/actions' % stack_identifier
-        body = {'resume': None}
-        resp, body = self.post(url, json.dumps(body))
-        self.expected_success(200, resp.status)
-        return rest_client.ResponseBody(resp)
-
-    def list_resources(self, stack_identifier):
-        """Returns the details of a single resource."""
-        url = "stacks/%s/resources" % stack_identifier
-        resp, body = self.get(url)
-        self.expected_success(200, resp.status)
-        body = json.loads(body)
-        return rest_client.ResponseBody(resp, body)
-
-    def show_resource(self, stack_identifier, resource_name):
-        """Returns the details of a single resource."""
-        url = "stacks/%s/resources/%s" % (stack_identifier, resource_name)
-        resp, body = self.get(url)
-        self.expected_success(200, resp.status)
-        body = json.loads(body)
-        return rest_client.ResponseBody(resp, body)
-
-    def delete_stack(self, stack_identifier):
-        """Deletes the specified Stack."""
-        resp, _ = self.delete("stacks/%s" % str(stack_identifier))
-        self.expected_success(204, resp.status)
-        return rest_client.ResponseBody(resp)
-
-    def wait_for_stack_status(self, stack_identifier, status,
-                              failure_pattern='^.*_FAILED$'):
-        """Waits for a Stack to reach a given status."""
-        start = int(time.time())
-        fail_regexp = re.compile(failure_pattern)
-
-        while True:
-            try:
-                body = self.show_stack(stack_identifier)['stack']
-            except lib_exc.NotFound:
-                if status == 'DELETE_COMPLETE':
-                    return
-            stack_name = body['stack_name']
-            stack_status = body['stack_status']
-            if stack_status == status:
-                return body
-            if fail_regexp.search(stack_status):
-                raise exceptions.StackBuildErrorException(
-                    stack_identifier=stack_identifier,
-                    stack_status=stack_status,
-                    stack_status_reason=body['stack_status_reason'])
-
-            if int(time.time()) - start >= self.build_timeout:
-                message = ('Stack %s failed to reach %s status (current: %s) '
-                           'within the required time (%s s).' %
-                           (stack_name, status, stack_status,
-                            self.build_timeout))
-                raise lib_exc.TimeoutException(message)
-            time.sleep(self.build_interval)
-
-    def show_resource_metadata(self, stack_identifier, resource_name):
-        """Returns the resource's metadata."""
-        url = ('stacks/{stack_identifier}/resources/{resource_name}'
-               '/metadata'.format(**locals()))
-        resp, body = self.get(url)
-        self.expected_success(200, resp.status)
-        body = json.loads(body)
-        return rest_client.ResponseBody(resp, body)
-
-    def list_events(self, stack_identifier):
-        """Returns list of all events for a stack."""
-        url = 'stacks/{stack_identifier}/events'.format(**locals())
-        resp, body = self.get(url)
-        self.expected_success(200, resp.status)
-        body = json.loads(body)
-        return rest_client.ResponseBody(resp, body)
-
-    def list_resource_events(self, stack_identifier, resource_name):
-        """Returns list of all events for a resource from stack."""
-        url = ('stacks/{stack_identifier}/resources/{resource_name}'
-               '/events'.format(**locals()))
-        resp, body = self.get(url)
-        self.expected_success(200, resp.status)
-        body = json.loads(body)
-        return rest_client.ResponseBody(resp, body)
-
-    def show_event(self, stack_identifier, resource_name, event_id):
-        """Returns the details of a single stack's event."""
-        url = ('stacks/{stack_identifier}/resources/{resource_name}/events'
-               '/{event_id}'.format(**locals()))
-        resp, body = self.get(url)
-        self.expected_success(200, resp.status)
-        body = json.loads(body)
-        return rest_client.ResponseBody(resp, body)
-
-    def show_template(self, stack_identifier):
-        """Returns the template for the stack."""
-        url = ('stacks/{stack_identifier}/template'.format(**locals()))
-        resp, body = self.get(url)
-        self.expected_success(200, resp.status)
-        body = json.loads(body)
-        return rest_client.ResponseBody(resp, body)
-
-    def _validate_template(self, post_body):
-        """Returns the validation request result."""
-        post_body = json.dumps(post_body)
-        resp, body = self.post('validate', post_body)
-        self.expected_success(200, resp.status)
-        body = json.loads(body)
-        return rest_client.ResponseBody(resp, body)
-
-    def validate_template(self, template, parameters=None):
-        """Returns the validation result for a template with parameters."""
-        if parameters is None:
-            parameters = {}
-        post_body = {
-            'template': template,
-            'parameters': parameters,
-        }
-        return self._validate_template(post_body)
-
-    def validate_template_url(self, template_url, parameters=None):
-        """Returns the validation result for a template with parameters."""
-        if parameters is None:
-            parameters = {}
-        post_body = {
-            'template_url': template_url,
-            'parameters': parameters,
-        }
-        return self._validate_template(post_body)
-
-    def list_resource_types(self):
-        """List resource types."""
-        resp, body = self.get('resource_types')
-        self.expected_success(200, resp.status)
-        body = json.loads(body)
-        return rest_client.ResponseBody(resp, body)
-
-    def show_resource_type(self, resource_type_name):
-        """Return the schema of a resource type."""
-        url = 'resource_types/%s' % resource_type_name
-        resp, body = self.get(url)
-        self.expected_success(200, resp.status)
-        return rest_client.ResponseBody(resp, json.loads(body))
-
-    def show_resource_type_template(self, resource_type_name):
-        """Return the template of a resource type."""
-        url = 'resource_types/%s/template' % resource_type_name
-        resp, body = self.get(url)
-        self.expected_success(200, resp.status)
-        return rest_client.ResponseBody(resp, json.loads(body))
-
-    def create_software_config(self, name=None, config=None, group=None,
-                               inputs=None, outputs=None, options=None):
-        headers, body = self._prep_software_config_create(
-            name, config, group, inputs, outputs, options)
-
-        url = 'software_configs'
-        resp, body = self.post(url, headers=headers, body=body)
-        self.expected_success(200, resp.status)
-        body = json.loads(body)
-        return rest_client.ResponseBody(resp, body)
-
-    def show_software_config(self, conf_id):
-        """Returns a software configuration resource."""
-        url = 'software_configs/%s' % str(conf_id)
-        resp, body = self.get(url)
-        self.expected_success(200, resp.status)
-        body = json.loads(body)
-        return rest_client.ResponseBody(resp, body)
-
-    def delete_software_config(self, conf_id):
-        """Deletes a specific software configuration."""
-        url = 'software_configs/%s' % str(conf_id)
-        resp, _ = self.delete(url)
-        self.expected_success(204, resp.status)
-        return rest_client.ResponseBody(resp)
-
-    def create_software_deploy(self, server_id=None, config_id=None,
-                               action=None, status=None,
-                               input_values=None, output_values=None,
-                               status_reason=None, signal_transport=None):
-        """Creates or updates a software deployment."""
-        headers, body = self._prep_software_deploy_update(
-            None, server_id, config_id, action, status, input_values,
-            output_values, status_reason, signal_transport)
-
-        url = 'software_deployments'
-        resp, body = self.post(url, headers=headers, body=body)
-        self.expected_success(200, resp.status)
-        body = json.loads(body)
-        return rest_client.ResponseBody(resp, body)
-
-    def update_software_deploy(self, deploy_id=None, server_id=None,
-                               config_id=None, action=None, status=None,
-                               input_values=None, output_values=None,
-                               status_reason=None, signal_transport=None):
-        """Creates or updates a software deployment."""
-        headers, body = self._prep_software_deploy_update(
-            deploy_id, server_id, config_id, action, status, input_values,
-            output_values, status_reason, signal_transport)
-
-        url = 'software_deployments/%s' % str(deploy_id)
-        resp, body = self.put(url, headers=headers, body=body)
-        self.expected_success(200, resp.status)
-        body = json.loads(body)
-        return rest_client.ResponseBody(resp, body)
-
-    def list_software_deployments(self):
-        """Returns a list of all deployments."""
-        url = 'software_deployments'
-        resp, body = self.get(url)
-        self.expected_success(200, resp.status)
-        body = json.loads(body)
-        return rest_client.ResponseBody(resp, body)
-
-    def show_software_deployment(self, deploy_id):
-        """Returns a specific software deployment."""
-        url = 'software_deployments/%s' % str(deploy_id)
-        resp, body = self.get(url)
-        self.expected_success(200, resp.status)
-        body = json.loads(body)
-        return rest_client.ResponseBody(resp, body)
-
-    def show_software_deployment_metadata(self, server_id):
-        """Return a config metadata for a specific server."""
-        url = 'software_deployments/metadata/%s' % server_id
-        resp, body = self.get(url)
-        self.expected_success(200, resp.status)
-        body = json.loads(body)
-        return rest_client.ResponseBody(resp, body)
-
-    def delete_software_deploy(self, deploy_id):
-        """Deletes a specific software deployment."""
-        url = 'software_deployments/%s' % str(deploy_id)
-        resp, _ = self.delete(url)
-        self.expected_success(204, resp.status)
-        return rest_client.ResponseBody(resp)
-
-    def _prep_software_config_create(self, name=None, conf=None, group=None,
-                                     inputs=None, outputs=None, options=None):
-        """Prepares a software configuration body."""
-        post_body = {}
-        if name is not None:
-            post_body["name"] = name
-        if conf is not None:
-            post_body["config"] = conf
-        if group is not None:
-            post_body["group"] = group
-        if inputs is not None:
-            post_body["inputs"] = inputs
-        if outputs is not None:
-            post_body["outputs"] = outputs
-        if options is not None:
-            post_body["options"] = options
-        body = json.dumps(post_body)
-
-        headers = self.get_headers()
-        return headers, body
-
-    def _prep_software_deploy_update(self, deploy_id=None, server_id=None,
-                                     config_id=None, action=None, status=None,
-                                     input_values=None, output_values=None,
-                                     status_reason=None,
-                                     signal_transport=None):
-        """Prepares a deployment create or update (if an id was given)."""
-        post_body = {}
-
-        if deploy_id is not None:
-            post_body["id"] = deploy_id
-        if server_id is not None:
-            post_body["server_id"] = server_id
-        if config_id is not None:
-            post_body["config_id"] = config_id
-        if action is not None:
-            post_body["action"] = action
-        if status is not None:
-            post_body["status"] = status
-        if input_values is not None:
-            post_body["input_values"] = input_values
-        if output_values is not None:
-            post_body["output_values"] = output_values
-        if status_reason is not None:
-            post_body["status_reason"] = status_reason
-        if signal_transport is not None:
-            post_body["signal_transport"] = signal_transport
-        body = json.dumps(post_body)
-
-        headers = self.get_headers()
-        return headers, body
diff --git a/tempest/tests/lib/common/test_rest_client.py b/tempest/tests/lib/common/test_rest_client.py
index 1dea5f5..910756f 100644
--- a/tempest/tests/lib/common/test_rest_client.py
+++ b/tempest/tests/lib/common/test_rest_client.py
@@ -280,6 +280,26 @@
         body = self.rest_client._parse_resp(json.dumps(empty_list))
         self.assertEqual(empty_list, body)
 
+    def test_parse_top_key_match(self):
+        body = self.rest_client._parse_resp(json.dumps(self.dict_expected),
+                                            top_key_to_verify="body_dict")
+        self.assertEqual(self.dict_expected["body_dict"], body)
+
+
+class TestRestClientParseErrorRespJSON(BaseRestClientTestClass):
+
+    dict_expected = {"body_dict": {"fake_key": "fake_value"}}
+
+    def setUp(self):
+        self.fake_http = fake_http.fake_httplib2()
+        super(TestRestClientParseErrorRespJSON, self).setUp()
+
+    def test_parse_top_key_no_match(self):
+        self.assertRaises(AssertionError,
+                          self.rest_client._parse_resp,
+                          json.dumps(self.dict_expected),
+                          top_key_to_verify="body_key")
+
 
 class TestRestClientErrorCheckerJSON(base.TestCase):
     c_type = "application/json"
diff --git a/tempest/tests/lib/services/image/v2/test_image_cache_client.py b/tempest/tests/lib/services/image/v2/test_image_cache_client.py
new file mode 100644
index 0000000..1a99115
--- /dev/null
+++ b/tempest/tests/lib/services/image/v2/test_image_cache_client.py
@@ -0,0 +1,64 @@
+# Copyright 2022 Red Hat, Inc.  All rights reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from tempest.lib.services.image.v2 import image_cache_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestImageCacheClient(base.BaseServiceTest):
+    def setUp(self):
+        super(TestImageCacheClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = image_cache_client.ImageCacheClient(
+            fake_auth, 'image', 'regionOne')
+
+    def test_list_cache(self):
+        fake_result = {
+            "cached_images": [{
+                "image_id": "8f332e84-ea60-4501-8e11-5efcddb81f30",
+                "hits": 3,
+                "last_accessed": 1639578364.65118,
+                "last_modified": 1639389612.596718,
+                "size": 16300544
+            }],
+            "queued_images": ['1bea47ed-f6a9-463b-b423-14b9cca9ad27']}
+        self.check_service_client_function(
+            self.client.list_cache,
+            'tempest.lib.common.rest_client.RestClient.get',
+            fake_result,
+            mock_args=['cache'])
+
+    def test_cache_queue(self):
+        self.check_service_client_function(
+            self.client.cache_queue,
+            'tempest.lib.common.rest_client.RestClient.put',
+            {},
+            status=202,
+            image_id="e485aab9-0907-4973-921c-bb6da8a8fcf8")
+
+    def test_cache_delete(self):
+        fake_result = {}
+        self.check_service_client_function(
+            self.client.cache_delete,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            fake_result, image_id="e485aab9-0907-4973-921c-bb6da8a8fcf8",
+            status=204)
+
+    def test_cache_clear_without_target(self):
+        fake_result = {}
+        self.check_service_client_function(
+            self.client.cache_clear,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            fake_result, status=204)
diff --git a/tools/tempest-integrated-gate-networking-exclude-list.txt b/tools/tempest-integrated-gate-networking-exclude-list.txt
index 263b2e4..9d79a35 100644
--- a/tools/tempest-integrated-gate-networking-exclude-list.txt
+++ b/tools/tempest-integrated-gate-networking-exclude-list.txt
@@ -11,9 +11,11 @@
 
 # Skip Cinder, Glance and Swift only scenario tests.
 tempest.scenario.test_encrypted_cinder_volumes.TestEncryptedCinderVolumes.test_encrypted_cinder_volumes_luks
+tempest.scenario.test_encrypted_cinder_volumes.TestEncryptedCinderVolumes.test_encrypted_cinder_volumes_luks2
 tempest.scenario.test_encrypted_cinder_volumes.TestEncryptedCinderVolumes.test_encrypted_cinder_volumes_cryptsetup
 tempest.scenario.test_object_storage_basic_ops.TestObjectStorageBasicOps.test_swift_basic_ops
 tempest.scenario.test_object_storage_basic_ops.TestObjectStorageBasicOps.test_swift_acl_anonymous_download
 tempest.scenario.test_volume_boot_pattern.TestVolumeBootPattern.test_boot_server_from_encrypted_volume_luks
+tempest.scenario.test_volume_boot_pattern.TestVolumeBootPattern.test_boot_server_from_encrypted_volume_luks2
 tempest.scenario.test_volume_boot_pattern.TestVolumeBootPattern.test_image_defined_boot_from_volume
 tempest.scenario.test_volume_boot_pattern.TestVolumeBootPattern.test_create_server_from_volume_snapshot
diff --git a/tools/tempest-integrated-gate-placement-exclude-list.txt b/tools/tempest-integrated-gate-placement-exclude-list.txt
index efba796..eb68b32 100644
--- a/tools/tempest-integrated-gate-placement-exclude-list.txt
+++ b/tools/tempest-integrated-gate-placement-exclude-list.txt
@@ -11,9 +11,11 @@
 
 # Skip Cinder, Glance and Swift only scenario tests.
 tempest.scenario.test_encrypted_cinder_volumes.TestEncryptedCinderVolumes.test_encrypted_cinder_volumes_luks
+tempest.scenario.test_encrypted_cinder_volumes.TestEncryptedCinderVolumes.test_encrypted_cinder_volumes_luks2
 tempest.scenario.test_encrypted_cinder_volumes.TestEncryptedCinderVolumes.test_encrypted_cinder_volumes_cryptsetup
 tempest.scenario.test_object_storage_basic_ops.TestObjectStorageBasicOps.test_swift_basic_ops
 tempest.scenario.test_object_storage_basic_ops.TestObjectStorageBasicOps.test_swift_acl_anonymous_download
 tempest.scenario.test_volume_boot_pattern.TestVolumeBootPattern.test_boot_server_from_encrypted_volume_luks
+tempest.scenario.test_volume_boot_pattern.TestVolumeBootPattern.test_boot_server_from_encrypted_volume_luks2
 tempest.scenario.test_volume_boot_pattern.TestVolumeBootPattern.test_image_defined_boot_from_volume
 tempest.scenario.test_volume_boot_pattern.TestVolumeBootPattern.test_create_server_from_volume_snapshot
diff --git a/tox.ini b/tox.ini
index b07fdaf..94eb4d9 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,5 +1,5 @@
 [tox]
-envlist = pep8,py36,py39,bashate,pip-check-reqs
+envlist = pep8,py39,bashate,pip-check-reqs
 minversion = 3.18.0
 skipsdist = True
 ignore_basepython_conflict = True
diff --git a/zuul.d/integrated-gate.yaml b/zuul.d/integrated-gate.yaml
index 3305aeb..4c08ad9 100644
--- a/zuul.d/integrated-gate.yaml
+++ b/zuul.d/integrated-gate.yaml
@@ -358,12 +358,18 @@
         - grenade-skip-level:
             voting: false
         - tempest-integrated-networking
-        - openstacksdk-functional-devstack
+        # Do not run it on ussuri until below issue is fixed
+        # https://storyboard.openstack.org/#!/story/2010057
+        - openstacksdk-functional-devstack:
+            branches: ^(?!stable/ussuri).*$
     gate:
       jobs:
         - grenade
         - tempest-integrated-networking
-        - openstacksdk-functional-devstack
+        # Do not run it on ussuri until below issue is fixed
+        # https://storyboard.openstack.org/#!/story/2010057
+        - openstacksdk-functional-devstack:
+            branches: ^(?!stable/ussuri).*$
 
 - project-template:
     name: integrated-gate-compute
@@ -385,15 +391,20 @@
         # centos-8-stream is tested from wallaby -> yoga branches
         - tempest-integrated-compute-centos-8-stream:
             branches: ^stable/(wallaby|xena|yoga).*$
-        # centos-9-stream is tested from zed release onwards
-        - tempest-integrated-compute-centos-9-stream:
-            branches: ^(?!stable/(pike|queens|rocky|stein|train|ussuri|victoria|wallaby|xena|yoga)).*$
-        - openstacksdk-functional-devstack
+        # Do not run it on ussuri until below issue is fixed
+        # https://storyboard.openstack.org/#!/story/2010057
+        - openstacksdk-functional-devstack:
+            branches: ^(?!stable/ussuri).*$
     gate:
       jobs:
         - tempest-integrated-compute
-        - tempest-integrated-compute-centos-9-stream
-        - openstacksdk-functional-devstack
+        - openstacksdk-functional-devstack:
+            branches: ^(?!stable/ussuri).*$
+    periodic-weekly:
+      jobs:
+        # centos-9-stream is tested from zed release onwards
+        - tempest-integrated-compute-centos-9-stream:
+            branches: ^(?!stable/(pike|queens|rocky|stein|train|ussuri|victoria|wallaby|xena|yoga)).*$
 
 - project-template:
     name: integrated-gate-placement
@@ -408,12 +419,18 @@
         - grenade-skip-level:
             voting: false
         - tempest-integrated-placement
-        - openstacksdk-functional-devstack
+        # Do not run it on ussuri until below issue is fixed
+        # https://storyboard.openstack.org/#!/story/2010057
+        - openstacksdk-functional-devstack:
+            branches: ^(?!stable/ussuri).*$
     gate:
       jobs:
         - grenade
         - tempest-integrated-placement
-        - openstacksdk-functional-devstack
+        # Do not run it on ussuri until below issue is fixed
+        # https://storyboard.openstack.org/#!/story/2010057
+        - openstacksdk-functional-devstack:
+            branches: ^(?!stable/ussuri).*$
 
 - project-template:
     name: integrated-gate-storage
@@ -428,12 +445,18 @@
         - grenade-skip-level:
             voting: false
         - tempest-integrated-storage
-        - openstacksdk-functional-devstack
+        # Do not run it on ussuri until below issue is fixed
+        # https://storyboard.openstack.org/#!/story/2010057
+        - openstacksdk-functional-devstack:
+            branches: ^(?!stable/ussuri).*$
     gate:
       jobs:
         - grenade
         - tempest-integrated-storage
-        - openstacksdk-functional-devstack
+        # Do not run it on ussuri until below issue is fixed
+        # https://storyboard.openstack.org/#!/story/2010057
+        - openstacksdk-functional-devstack:
+            branches: ^(?!stable/ussuri).*$
 
 - project-template:
     name: integrated-gate-object-storage
@@ -446,9 +469,15 @@
       jobs:
         - grenade
         - tempest-integrated-object-storage
-        - openstacksdk-functional-devstack
+        # Do not run it on ussuri until below issue is fixed
+        # https://storyboard.openstack.org/#!/story/2010057
+        - openstacksdk-functional-devstack:
+            branches: ^(?!stable/ussuri).*$
     gate:
       jobs:
         - grenade
         - tempest-integrated-object-storage
-        - openstacksdk-functional-devstack
+        # Do not run it on ussuri until below issue is fixed
+        # https://storyboard.openstack.org/#!/story/2010057
+        - openstacksdk-functional-devstack:
+            branches: ^(?!stable/ussuri).*$
diff --git a/zuul.d/project.yaml b/zuul.d/project.yaml
index 1a54fe4..3cc3fda 100644
--- a/zuul.d/project.yaml
+++ b/zuul.d/project.yaml
@@ -8,10 +8,9 @@
     check:
       jobs:
         - openstack-tox-pep8
-        - openstack-tox-py36
-        - openstack-tox-py37
         - openstack-tox-py38
         - openstack-tox-py39
+        - openstack-tox-py310
         - tempest-full-parallel:
             # Define list of irrelevant files to use everywhere else
             irrelevant-files: &tempest-irrelevant-files
@@ -41,8 +40,6 @@
             irrelevant-files: *tempest-irrelevant-files
         - tempest-full-wallaby-py3:
             irrelevant-files: *tempest-irrelevant-files
-        - tempest-full-victoria-py3:
-            irrelevant-files: *tempest-irrelevant-files
         - tempest-slow-wallaby:
             irrelevant-files: *tempest-irrelevant-files
         - tempest-multinode-full-py3:
@@ -101,9 +98,11 @@
         - tempest-slow-py3:
             irrelevant-files: *tempest-irrelevant-files
         - nova-live-migration:
-            voting: false
             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
             irrelevant-files: *tempest-irrelevant-files
         - neutron-ovs-grenade-multinode:
             irrelevant-files: *tempest-irrelevant-files
@@ -123,14 +122,16 @@
         - openstack-tox-bashate:
             irrelevant-files: *tempest-irrelevant-files-2
         - tempest-full-centos-9-stream:
+            # TODO(gmann): make it voting once below fix is merged
+            # https://review.opendev.org/c/openstack/tempest/+/842140
+            voting: false
             irrelevant-files: *tempest-irrelevant-files
     gate:
       jobs:
         - openstack-tox-pep8
-        - openstack-tox-py36
-        - openstack-tox-py37
         - openstack-tox-py38
         - openstack-tox-py39
+        - openstack-tox-py310
         - tempest-slow-py3:
             irrelevant-files: *tempest-irrelevant-files
         - neutron-ovs-grenade-multinode:
@@ -141,9 +142,11 @@
             irrelevant-files: *tempest-irrelevant-files
         - tempest-ipv6-only:
             irrelevant-files: *tempest-irrelevant-files-3
-        - devstack-plugin-ceph-tempest-py3:
-            irrelevant-files: *tempest-irrelevant-files
-        - tempest-full-centos-9-stream:
+        #- devstack-plugin-ceph-tempest-py3:
+        #    irrelevant-files: *tempest-irrelevant-files
+        #- tempest-full-centos-9-stream:
+        #    irrelevant-files: *tempest-irrelevant-files
+        - nova-live-migration:
             irrelevant-files: *tempest-irrelevant-files
     experimental:
       jobs:
@@ -168,7 +171,6 @@
         - tempest-full-yoga
         - tempest-full-xena
         - tempest-full-wallaby-py3
-        - tempest-full-victoria-py3
         - tempest-slow-yoga
         - tempest-slow-xena
         - tempest-slow-wallaby
diff --git a/zuul.d/stable-jobs.yaml b/zuul.d/stable-jobs.yaml
index 00b40f5..d1445c0 100644
--- a/zuul.d/stable-jobs.yaml
+++ b/zuul.d/stable-jobs.yaml
@@ -15,11 +15,6 @@
     override-checkout: stable/wallaby
 
 - job:
-    name: tempest-full-victoria-py3
-    parent: tempest-full-py3
-    override-checkout: stable/victoria
-
-- job:
     name: tempest-slow-yoga
     parent: tempest-slow-py3
     override-checkout: stable/yoga