Merge "Remove deprecated --experimental option of autopep8"
diff --git a/doc/source/supported_version.rst b/doc/source/supported_version.rst
index 3ffa68a..2cca456 100644
--- a/doc/source/supported_version.rst
+++ b/doc/source/supported_version.rst
@@ -9,10 +9,10 @@
 
 Tempest master supports the below OpenStack Releases:
 
+* 2025.2
 * 2025.1
 * 2024.2
 * 2024.1
-* 2023.2
 
 For older OpenStack Release:
 
@@ -33,7 +33,7 @@
 
 Tempest master supports the below python versions:
 
-* Python 3.9
 * Python 3.10
 * Python 3.11
 * Python 3.12
+* Python 3.13
diff --git a/releasenotes/notes/add-config-nova-policy-roles-37fc4ef511f97f50.yaml b/releasenotes/notes/add-config-nova-policy-roles-37fc4ef511f97f50.yaml
new file mode 100644
index 0000000..f357b00
--- /dev/null
+++ b/releasenotes/notes/add-config-nova-policy-roles-37fc4ef511f97f50.yaml
@@ -0,0 +1,7 @@
+---
+features:
+  - |
+    A new config option ``nova_policy_roles`` is added in the
+    ``compute-feature-enabled`` section. This can be used to
+    configure the available roles that are used as default in
+    nova policy rules.
diff --git a/releasenotes/notes/add-location-api-5a57ab29dc6d6cd7.yaml b/releasenotes/notes/add-location-api-5a57ab29dc6d6cd7.yaml
new file mode 100644
index 0000000..f9166a2
--- /dev/null
+++ b/releasenotes/notes/add-location-api-5a57ab29dc6d6cd7.yaml
@@ -0,0 +1,4 @@
+---
+features:
+  - |
+    Add new location API support to image V2 client
diff --git a/releasenotes/notes/add-server-migrations-clients-ffbf5cbdf7818305.yaml b/releasenotes/notes/add-server-migrations-clients-ffbf5cbdf7818305.yaml
new file mode 100644
index 0000000..8e88513
--- /dev/null
+++ b/releasenotes/notes/add-server-migrations-clients-ffbf5cbdf7818305.yaml
@@ -0,0 +1,5 @@
+---
+features:
+  - |
+    Add the server live migration list and force complete service
+    clients.
diff --git a/releasenotes/notes/drop-python-3-9-b8a25c06e4bc0787.yaml b/releasenotes/notes/drop-python-3-9-b8a25c06e4bc0787.yaml
new file mode 100644
index 0000000..f9488d7
--- /dev/null
+++ b/releasenotes/notes/drop-python-3-9-b8a25c06e4bc0787.yaml
@@ -0,0 +1,8 @@
+---
+prelude: >
+    Tempest dropped the support of python 3.9.
+upgrade:
+  - |
+    Python 3.9 support has been dropped. The last release of Tempest
+    to support python 3.9 is Temepst 45.0.0. The minimum version
+    of Python supported by Tempest is python 3.10.
diff --git a/releasenotes/notes/end-of-support-of-2023-2-3631d8d0f3507518.yaml b/releasenotes/notes/end-of-support-of-2023-2-3631d8d0f3507518.yaml
new file mode 100644
index 0000000..74323ea
--- /dev/null
+++ b/releasenotes/notes/end-of-support-of-2023-2-3631d8d0f3507518.yaml
@@ -0,0 +1,12 @@
+---
+prelude: >
+    This is an intermediate release during the 2025.2 development cycle to
+    mark the end of support for 2023.2 release in Tempest.
+    After this release, Tempest will support below OpenStack Releases:
+
+    * 2025.1
+    * 2024.2
+    * 2024.1
+
+    Current development of Tempest is for OpenStack 2025.2 development
+    cycle.
diff --git a/releasenotes/notes/tempest-2025-2-release-085c56b9b4cf2c84.yaml b/releasenotes/notes/tempest-2025-2-release-085c56b9b4cf2c84.yaml
new file mode 100644
index 0000000..cfc6b19
--- /dev/null
+++ b/releasenotes/notes/tempest-2025-2-release-085c56b9b4cf2c84.yaml
@@ -0,0 +1,17 @@
+---
+prelude: >
+    This release is to tag Tempest for OpenStack 2025.2 release.
+    This release marks the start of 2025.2 release support in Tempest.
+    After this release, Tempest will support below OpenStack Releases:
+
+    * 2025.2
+    * 2025.1
+    * 2024.2
+    * 2024.1
+
+    Current development of Tempest is for OpenStack 2026.1 development
+    cycle. Every Tempest commit is also tested against master during
+    the 2026.1 cycle. However, this does not necessarily mean that using
+    Tempest as of this tag will work against a 2026.1 (or future release)
+    cloud.
+    To be on safe side, use this tag to test the OpenStack 2025.2 release.
diff --git a/releasenotes/source/index.rst b/releasenotes/source/index.rst
index 058f65f..33c141d 100644
--- a/releasenotes/source/index.rst
+++ b/releasenotes/source/index.rst
@@ -6,6 +6,8 @@
    :maxdepth: 1
 
    unreleased
+   v45.0.0
+   v44.0.0
    v43.0.0
    v42.0.0
    v41.0.0
diff --git a/releasenotes/source/v44.0.0.rst b/releasenotes/source/v44.0.0.rst
new file mode 100644
index 0000000..bdb3b69
--- /dev/null
+++ b/releasenotes/source/v44.0.0.rst
@@ -0,0 +1,6 @@
+=====================
+v44.0.0 Release Notes
+=====================
+
+.. release-notes:: 44.0.0 Release Notes
+   :version: 44.0.0
diff --git a/releasenotes/source/v45.0.0.rst b/releasenotes/source/v45.0.0.rst
new file mode 100644
index 0000000..4183a63
--- /dev/null
+++ b/releasenotes/source/v45.0.0.rst
@@ -0,0 +1,6 @@
+=====================
+v45.0.0 Release Notes
+=====================
+
+.. release-notes:: 45.0.0 Release Notes
+   :version: 45.0.0
diff --git a/roles/run-tempest/tasks/main.yaml b/roles/run-tempest/tasks/main.yaml
index 15b1743..1a1b5f4 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 2023.1
+- name: Use stable branch upper-constraints till 2025.1
   set_fact:
     # TOX_CONSTRAINTS_FILE is new name, UPPER_CONSTRAINTS_FILE is old one, best to set both
     tempest_tox_environment: "{{ tempest_tox_environment | combine({'UPPER_CONSTRAINTS_FILE': stable_constraints_file}) | combine({'TOX_CONSTRAINTS_FILE': stable_constraints_file}) }}"
-  when: target_branch in ["stable/ocata", "stable/pike", "stable/queens", "stable/rocky", "stable/stein", "stable/train", "stable/ussuri", "stable/2023.1", "unmaintained/victoria", "unmaintained/wallaby", "unmaintained/xena", "unmaintained/yoga", "unmaintained/zed", "unmaintained/2023.1"]
+  when: target_branch in ["stable/ocata", "stable/pike", "stable/queens", "stable/rocky", "stable/stein", "stable/train", "stable/ussuri", "stable/2023.1", "unmaintained/victoria", "unmaintained/wallaby", "unmaintained/xena", "unmaintained/yoga", "unmaintained/zed", "unmaintained/2023.1", "unmaintained/2024.1", "stable/2024.2", "stable/2025.1"]
 
 - name: Use Configured upper-constraints for non-master Tempest
   set_fact:
diff --git a/setup.cfg b/setup.cfg
index 67555f4..fa17801 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -14,10 +14,10 @@
     Operating System :: POSIX :: Linux
     Programming Language :: Python
     Programming Language :: Python :: 3
-    Programming Language :: Python :: 3.9
     Programming Language :: Python :: 3.10
     Programming Language :: Python :: 3.11
     Programming Language :: Python :: 3.12
+    Programming Language :: Python :: 3.13
     Programming Language :: Python :: 3 :: Only
     Programming Language :: Python :: Implementation :: CPython
 
diff --git a/tempest/api/compute/admin/test_assisted_volume_snapshots.py b/tempest/api/compute/admin/test_assisted_volume_snapshots.py
index b7be796..034efc9 100644
--- a/tempest/api/compute/admin/test_assisted_volume_snapshots.py
+++ b/tempest/api/compute/admin/test_assisted_volume_snapshots.py
@@ -26,11 +26,6 @@
 
     create_default_network = True
 
-    # TODO(gmann): Remove the admin access to service user
-    # once nova change the default of this API to service
-    # role. To merge the nova changing the policy default
-    # we need to use token with admin as well as service
-    # role and later we can use only service token.
     credentials = ['primary', 'admin', ['service_user', 'admin', 'service']]
 
     @classmethod
@@ -39,6 +34,13 @@
         if not CONF.service_available.cinder:
             skip_msg = ("%s skipped as Cinder is not available" % cls.__name__)
             raise cls.skipException(skip_msg)
+            # NOTE(gmaan): If new policy is enforced and and service role
+            # is present in nova then use service user (no admin role) for
+            # assisted volume snapshots APIs.
+            if (CONF.enforce_scope.nova and 'service' in
+                CONF.compute_feature_enabled.nova_policy_roles):
+                cls.credentials = [
+                    'primary', 'admin', ['service_user', 'service']]
 
     @classmethod
     def setup_clients(cls):
diff --git a/tempest/api/compute/admin/test_live_migration.py b/tempest/api/compute/admin/test_live_migration.py
index f6a1ae9..bd2e7bf 100644
--- a/tempest/api/compute/admin/test_live_migration.py
+++ b/tempest/api/compute/admin/test_live_migration.py
@@ -26,6 +26,7 @@
 from tempest.lib.common.utils import data_utils
 from tempest.lib.common.utils import test_utils
 from tempest.lib import decorators
+from tempest.lib import exceptions as lib_exceptions
 
 CONF = config.CONF
 LOG = logging.getLogger(__name__)
@@ -34,6 +35,7 @@
 class LiveMigrationTestBase(base.BaseV2ComputeAdminTest):
     """Test live migration operations supported by admin user"""
 
+    credentials = ['primary', 'admin', 'project_manager']
     create_default_network = True
 
     @classmethod
@@ -51,13 +53,15 @@
     @classmethod
     def setup_clients(cls):
         super(LiveMigrationTestBase, cls).setup_clients()
-        cls.admin_migration_client = cls.os_admin.migrations_client
+        cls.migration_client = cls.os_admin.migrations_client
         cls.networks_client = cls.os_primary.networks_client
         cls.subnets_client = cls.os_primary.subnets_client
         cls.ports_client = cls.os_primary.ports_client
         cls.trunks_client = cls.os_primary.trunks_client
+        cls.mgr_server_client = cls.admin_servers_client
 
-    def _migrate_server_to(self, server_id, dest_host, volume_backed=False):
+    def _migrate_server_to(self, server_id, dest_host, volume_backed=False,
+                           use_manager_client=False):
         kwargs = dict()
         block_migration = getattr(self, 'block_migration', None)
         if self.block_migration is None:
@@ -66,7 +70,13 @@
             block_migration = (CONF.compute_feature_enabled.
                                block_migration_for_live_migration and
                                not volume_backed)
-        self.admin_servers_client.live_migrate_server(
+        if use_manager_client:
+            self.mgr_server_client = self.os_project_manager.servers_client
+            LOG.info("Using project manager for live migrating server: %s, "
+                     "project manager user id: %s",
+                     server_id, self.mgr_server_client.user_id)
+
+        self.mgr_server_client.live_migrate_server(
             server_id, host=dest_host, block_migration=block_migration,
             **kwargs)
 
@@ -74,11 +84,20 @@
                       volume_backed=False):
         # If target_host is None, check whether source host is different with
         # the new host after migration.
+        use_manager_client = False
         if target_host is None:
             source_host = self.get_host_for_server(server_id)
-        self._migrate_server_to(server_id, target_host, volume_backed)
+            # NOTE(gmaan): If new policy is enforced and and manager role
+            # is present in nova then use manager user to live migrate.
+            if (CONF.enforce_scope.nova and 'manager' in
+                CONF.compute_feature_enabled.nova_policy_roles):
+                use_manager_client = True
+
+        self._migrate_server_to(server_id, target_host, volume_backed,
+                                use_manager_client)
         waiters.wait_for_server_status(self.servers_client, server_id, state)
-        migration_list = (self.admin_migration_client.list_migrations()
+
+        migration_list = (self.os_admin.migrations_client.list_migrations()
                           ['migrations'])
 
         msg = ("Live Migration failed. Migrations list for Instance "
@@ -98,6 +117,9 @@
 class LiveMigrationTest(LiveMigrationTestBase):
     max_microversion = '2.24'
     block_migration = None
+    # If test case want to request the destination host to Nova
+    # otherwise Nova scheduler will pick one.
+    request_host = True
 
     @classmethod
     def setup_credentials(cls):
@@ -119,11 +141,12 @@
         server_id = self.create_test_server(wait_until="ACTIVE",
                                             volume_backed=volume_backed)['id']
         source_host = self.get_host_for_server(server_id)
-        if not CONF.compute_feature_enabled.can_migrate_between_any_hosts:
+        if (self.request_host and
+            CONF.compute_feature_enabled.can_migrate_between_any_hosts):
+            destination_host = self.get_host_other_than(server_id)
+        else:
             # not to specify a host so that the scheduler will pick one
             destination_host = None
-        else:
-            destination_host = self.get_host_other_than(server_id)
 
         if state == 'PAUSED':
             self.admin_servers_client.pause_server(server_id)
@@ -381,3 +404,153 @@
     min_microversion = '2.25'
     max_microversion = 'latest'
     block_migration = 'auto'
+
+
+class LiveMigrationWithoutHostTest(LiveMigrationTest):
+    # Test live migrations without host and let Nova scheduler will pick one.
+    request_host = False
+
+    @classmethod
+    def skip_checks(cls):
+        super(LiveMigrationWithoutHostTest, cls).skip_checks()
+        if not CONF.compute_feature_enabled.can_migrate_between_any_hosts:
+            skip_msg = ("Existing live migration tests are configured to live "
+                        "migrate without requesting host.")
+            raise cls.skipException(skip_msg)
+
+
+class LiveMigrationManagerWorkflowTest(LiveMigrationTestBase):
+    # This test shows live migrations workflow for the project manager.
+    # NOTE(gmaan): microversion 2.80 adds project_id query param in list
+    # migration API which is what needed by project manager to request
+    # their project migration list.
+    min_microversion = '2.80'
+    block_migration = None
+    request_host = False
+
+    @classmethod
+    def skip_checks(cls):
+        super(LiveMigrationManagerWorkflowTest, cls).skip_checks()
+        # If RBAC new defaults (manager defaults) are not present then we have
+        # existing test cases tests the live migration scenarios.
+        if (not CONF.enforce_scope.nova or 'manager' not in
+            CONF.compute_feature_enabled.nova_policy_roles):
+            skip_msg = ("Nova RBAC new defaults are not enabled or manager "
+                        "role is not present so skipping the project manager "
+                        "specific test.")
+            raise cls.skipException(skip_msg)
+
+    @classmethod
+    def setup_clients(cls):
+        super(LiveMigrationManagerWorkflowTest, cls).setup_clients()
+        cls.mgr_server_client = cls.os_project_manager.servers_client
+        LOG.info("Using project manager for live migrating servers, "
+                 "project manager user id: %s",
+                 cls.mgr_server_client.user_id)
+
+    def _initiate_live_migration(self):
+        # Project member create the server.
+        server_id = self.create_test_server(wait_until="ACTIVE")['id']
+        source_host = self.get_host_for_server(server_id)
+        # Project manager does not know about host info so they will not
+        # specify a host to nova and let nova scheduler to pick one.
+        dest_host = None
+
+        LOG.info("Live migrate from source %s", source_host)
+        # Live migrate an instance to another host
+        self._migrate_server_to(
+            server_id, dest_host, use_manager_client=True)
+        # Try to list the in-progress live migation. This list can be empty
+        # if migration is already completed.
+        in_progress_migrations = (
+            self.mgr_server_client.list_in_progress_live_migration(
+                server_id)['migrations'])
+        LOG.info("in-progress live migrations: %s", in_progress_migrations)
+
+        in_progress_migration_uuid = None
+        for migration in in_progress_migrations:
+            if (migration['server_uuid'] == server_id):
+                in_progress_migration_uuid = migration['uuid']
+            # Project manager should not get any host related field.
+            self.assertIsNone(migration['dest_compute'])
+            self.assertIsNone(migration['dest_host'])
+            self.assertIsNone(migration['dest_node'])
+            self.assertIsNone(migration['source_compute'])
+            self.assertIsNone(migration['source_node'])
+
+        return server_id, source_host, in_progress_migration_uuid
+
+    @decorators.attr(type='multinode')
+    @decorators.idempotent_id('cc4e2431-4476-49b0-9a80-d7a2f638f091')
+    def test_live_migration_by_project_manager(self):
+        """Tests live migrations workflow for the project manager.
+
+        - Create server by project member.
+        - Project manager perform the below steps:
+
+          * Initiate the live migration.
+          * List the in-progress live migration. If migration is completed
+            then list might be empty but test does not fail for empty list.
+          * Assuming migration is in progress, force complete the live
+            migration. Test do not fail if migration is already completed
+            and force complete request raise error.
+          * Wait for server to be active.
+          * List migrations with project_id filter, means request only
+            their project migrations. This should return the migration
+            initiated by project manager.
+          * Check if server is migrated to the differnet host than source
+            host.
+        """
+        server_id, source_host, in_progress_migration_uuid = (
+            self._initiate_live_migration())
+        try:
+            # If we know that migration is in progress then try to force
+            # complete it but there is chance that migration might be
+            # completed before test send request to nova. In that case,
+            # test will not fail. It will skip the force complete and
+            # resume to the next step.
+            if in_progress_migration_uuid:
+                LOG.info("Starting force complete live migrations: %s",
+                         in_progress_migration_uuid)
+                self.mgr_server_client.force_complete_live_migration(
+                    server_id, in_progress_migration_uuid)
+                LOG.info("Finish force complete live migrations: %s",
+                         in_progress_migration_uuid)
+        except lib_exceptions.BadRequest:
+            # If migration is already completed then nova will raise
+            # HTTPBadRequest. In that case, log the info and execute the
+            # rest of the steps.
+            LOG.info("Server %s live migration %s is already completed. Due "
+                     "to that force completed live migration is not "
+                     "performed.", server_id, in_progress_migration_uuid)
+
+        waiters.wait_for_server_status(self.mgr_server_client,
+                                       server_id, 'ACTIVE')
+        # List migration with project_id as filter so that manager can
+        # get its own project migrations.
+        mgr_migration_client = self.os_project_manager.migrations_client
+        project_id = mgr_migration_client.project_id
+        migrations = (mgr_migration_client.list_migrations(
+            migration_type='live-migration',
+            project_id=project_id)['migrations'])
+        migration_uuid = None
+        LOG.info("Project %s migrations list: %s", project_id, migrations)
+        for migration in migrations:
+            if (migration['instance_uuid'] == server_id):
+                migration_uuid = migration['uuid']
+            # Check if project manager get other project migrations
+            self.assertEqual(project_id, migration['project_id'])
+            # Project manager should not get any host related field.
+            self.assertIsNone(migration['dest_compute'])
+            self.assertIsNone(migration['dest_host'])
+            self.assertIsNone(migration['dest_node'])
+            self.assertIsNone(migration['source_compute'])
+            self.assertIsNone(migration['source_node'])
+        self.assertIsNotNone(migration_uuid)
+        if in_progress_migration_uuid:
+            self.assertEqual(in_progress_migration_uuid, migration_uuid)
+        msg = ("Server %s Live Migration %s failed.",
+               server_id, migration_uuid)
+        # Check if server is migrated to the differnet host than source host.
+        self.assertNotEqual(source_host,
+                            self.get_host_for_server(server_id), msg)
diff --git a/tempest/api/compute/admin/test_migrations.py b/tempest/api/compute/admin/test_migrations.py
index fa8a737..88847e6 100644
--- a/tempest/api/compute/admin/test_migrations.py
+++ b/tempest/api/compute/admin/test_migrations.py
@@ -12,6 +12,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from oslo_log import log as logging
 import testtools
 
 from tempest.api.compute import base
@@ -22,15 +23,27 @@
 from tempest.lib import exceptions
 
 CONF = config.CONF
+LOG = logging.getLogger(__name__)
 
 
 class MigrationsAdminTest(base.BaseV2ComputeAdminTest):
     """Test migration operations supported by admin user"""
 
+    credentials = ['primary', 'admin', 'project_manager']
+
     @classmethod
     def setup_clients(cls):
         super(MigrationsAdminTest, cls).setup_clients()
         cls.client = cls.os_admin.migrations_client
+        cls.mgr_server_client = cls.admin_servers_client
+        # NOTE(gmaan): If new policy is enforced and and manager role
+        # is present in nova then use manager user to live migrate.
+        if (CONF.enforce_scope.nova and 'manager' in
+            CONF.compute_feature_enabled.nova_policy_roles):
+            cls.mgr_server_client = cls.os_project_manager.servers_client
+            LOG.info("Using project manager for migrating servers, "
+                     "project manager user id: %s",
+                     cls.mgr_server_client.user_id)
 
     @decorators.idempotent_id('75c0b83d-72a0-4cf8-a153-631e83e7d53f')
     def test_list_migrations(self):
@@ -143,7 +156,7 @@
         server = self.create_test_server(wait_until="ACTIVE")
         src_host = self.get_host_for_server(server['id'])
 
-        self.admin_servers_client.migrate_server(server['id'])
+        self.mgr_server_client.migrate_server(server['id'])
 
         waiters.wait_for_server_status(self.servers_client,
                                        server['id'], 'VERIFY_RESIZE')
diff --git a/tempest/api/compute/admin/test_server_external_events.py b/tempest/api/compute/admin/test_server_external_events.py
index d867a39..6b26c68 100644
--- a/tempest/api/compute/admin/test_server_external_events.py
+++ b/tempest/api/compute/admin/test_server_external_events.py
@@ -19,11 +19,6 @@
 class ServerExternalEventsTest(base.BaseV2ComputeAdminTest):
     """Test server external events test"""
 
-    # TODO(gmann): Remove the admin access to service user
-    # once nova change the default of this API to service
-    # role. To merge the nova changing the policy default
-    # we need to use token with admin as well as service
-    # role and later we can use only service token.
     credentials = ['primary', 'admin', ['service_user', 'admin', 'service']]
 
     @decorators.idempotent_id('6bbf4723-61d2-4372-af55-7ba27f1c9ba6')
diff --git a/tempest/api/compute/admin/test_volume_swap.py b/tempest/api/compute/admin/test_volume_swap.py
index 9576b74..423407e 100644
--- a/tempest/api/compute/admin/test_volume_swap.py
+++ b/tempest/api/compute/admin/test_volume_swap.py
@@ -24,6 +24,8 @@
 class TestVolumeSwapBase(base.BaseV2ComputeAdminTest):
     create_default_network = True
 
+    credentials = ['primary', 'admin', ['service_user', 'admin', 'service']]
+
     @classmethod
     def setup_credentials(cls):
         cls.prepare_instance_network()
@@ -37,6 +39,11 @@
         if not CONF.compute_feature_enabled.swap_volume:
             raise cls.skipException("Swapping volumes is not supported.")
 
+    @classmethod
+    def setup_clients(cls):
+        super(TestVolumeSwapBase, cls).setup_clients()
+        cls.service_client = cls.os_service_user.servers_client
+
     def wait_for_server_volume_swap(self, server_id, old_volume_id,
                                     new_volume_id):
         """Waits for a server to swap the old volume to a new one."""
@@ -74,16 +81,17 @@
 
 
 class TestVolumeSwap(TestVolumeSwapBase):
-    """The test suite for swapping of volume with admin user"""
+    """The test suite for swapping of volume with service user"""
 
     # NOTE(mriedem): This is an uncommon scenario to call the compute API
     # to swap volumes directly; swap volume is primarily only for volume
     # live migration and retype callbacks from the volume service, and is slow
     # so it's marked as such.
+    @decorators.skip_because(bug='2112187')
     @decorators.attr(type='slow')
     @decorators.idempotent_id('1769f00d-a693-4d67-a631-6a3496773813')
     def test_volume_swap(self):
-        """Test swapping of volume attached to server with admin user
+        """Test swapping of volume attached to server with service user
 
         The following is the scenario outline:
 
@@ -91,11 +99,8 @@
         2. Create a volume "volume2" with non-admin.
         3. Boot an instance "instance1" with non-admin.
         4. Attach "volume1" to "instance1" with non-admin.
-        5. Swap volume from "volume1" to "volume2" as admin.
-        6. Check the swap volume is successful and "volume2"
-           is attached to "instance1" and "volume1" is in available state.
-        7. Swap volume from "volume2" to "volume1" as admin.
-        8. Check the swap volume is successful and "volume1"
+        5. Swap volume from "volume1" to "volume2" as service.
+        6. Check the swap volume is rejected and "volume1"
            is attached to "instance1" and "volume2" is in available state.
         """
         # Create two volumes.
@@ -118,34 +123,19 @@
         # Attach "volume1" to server
         self.attach_volume(server, volume1)
         # Swap volume from "volume1" to "volume2"
-        self.admin_servers_client.update_attached_volume(
+        self.assertRaises(
+            lib_exc.Conflict, self.service_client.update_attached_volume,
             server['id'], volume1['id'], volumeId=volume2['id'])
-        waiters.wait_for_volume_resource_status(self.volumes_client,
-                                                volume1['id'], 'available')
-        waiters.wait_for_volume_resource_status(self.volumes_client,
-                                                volume2['id'], 'in-use')
-        self.wait_for_server_volume_swap(server['id'], volume1['id'],
-                                         volume2['id'])
-        # Verify "volume2" is attached to the server
-        vol_attachments = self.servers_client.list_volume_attachments(
-            server['id'])['volumeAttachments']
-        self.assertEqual(1, len(vol_attachments))
-        self.assertIn(volume2['id'], vol_attachments[0]['volumeId'])
-
-        # Swap volume from "volume2" to "volume1"
-        self.admin_servers_client.update_attached_volume(
-            server['id'], volume2['id'], volumeId=volume1['id'])
-        waiters.wait_for_volume_resource_status(self.volumes_client,
-                                                volume2['id'], 'available')
-        waiters.wait_for_volume_resource_status(self.volumes_client,
-                                                volume1['id'], 'in-use')
-        self.wait_for_server_volume_swap(server['id'], volume2['id'],
-                                         volume1['id'])
         # Verify "volume1" is attached to the server
         vol_attachments = self.servers_client.list_volume_attachments(
             server['id'])['volumeAttachments']
         self.assertEqual(1, len(vol_attachments))
         self.assertIn(volume1['id'], vol_attachments[0]['volumeId'])
+        waiters.wait_for_volume_resource_status(
+            self.volumes_client, volume1['id'], 'in-use')
+        # verify "volume2" is still available
+        waiters.wait_for_volume_resource_status(
+            self.volumes_client, volume2['id'], 'available')
 
 
 class TestMultiAttachVolumeSwap(TestVolumeSwapBase):
@@ -194,9 +184,8 @@
         4. Attach "volume1" to "server1" with non-admin.
         5. Attach "volume1" to "server2" with non-admin.
         6. Swap "volume1" to "volume2" on "server1"
-        7. Check "volume1" is attached to "server2" and not attached to
-           "server1"
-        8. Check "volume2" is attached to "server1".
+        7. Check the swap volume is rejected and "volume1" is attached to
+           "server1".
         """
         multiattach_vol_type = CONF.volume.volume_type_multiattach
         # Create two volumes.
@@ -237,23 +226,22 @@
         server2 = servers[1]
         self.attach_volume(server2, volume1)
 
-        # Swap volume1 to volume2 on server1, volume1 should remain attached
-        # to server 2
-        self.admin_servers_client.update_attached_volume(
+        # Swap volume is rejected by nova
+        self.assertRaises(
+            lib_exc.Conflict, self.service_client.update_attached_volume,
             server1['id'], volume1['id'], volumeId=volume2['id'])
-        # volume1 will return to in-use after the swap
+
+        # volume1 remains in in-use and volume2 in available
         waiters.wait_for_volume_resource_status(self.volumes_client,
                                                 volume1['id'], 'in-use')
         waiters.wait_for_volume_resource_status(self.volumes_client,
-                                                volume2['id'], 'in-use')
-        self.wait_for_server_volume_swap(server1['id'], volume1['id'],
-                                         volume2['id'])
+                                                volume2['id'], 'available')
 
-        # Verify volume2 is attached to server1
+        # Verify volume1 is attached to server1
         vol_attachments = self.servers_client.list_volume_attachments(
             server1['id'])['volumeAttachments']
         self.assertEqual(1, len(vol_attachments))
-        self.assertIn(volume2['id'], vol_attachments[0]['volumeId'])
+        self.assertIn(volume1['id'], vol_attachments[0]['volumeId'])
 
         # Verify volume1 is still attached to server2
         vol_attachments = self.servers_client.list_volume_attachments(
diff --git a/tempest/api/compute/admin/test_volumes_negative.py b/tempest/api/compute/admin/test_volumes_negative.py
index 55c842f..3d42388 100644
--- a/tempest/api/compute/admin/test_volumes_negative.py
+++ b/tempest/api/compute/admin/test_volumes_negative.py
@@ -26,6 +26,7 @@
     """Negative tests of volume swapping"""
 
     create_default_network = True
+    credentials = ['primary', 'admin', ['service_user', 'admin', 'service']]
 
     @classmethod
     def setup_credentials(cls):
@@ -39,6 +40,11 @@
             skip_msg = ("%s skipped as Cinder is not available" % cls.__name__)
             raise cls.skipException(skip_msg)
 
+    @classmethod
+    def setup_clients(cls):
+        super(VolumesAdminNegativeTest, cls).setup_clients()
+        cls.service_client = cls.os_service_user.servers_client
+
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('309b5ecd-0585-4a7e-a36f-d2b2bf55259d')
     def test_update_attached_volume_with_nonexistent_volume_in_uri(self):
@@ -47,10 +53,11 @@
         volume = self.create_volume()
         nonexistent_volume = data_utils.rand_uuid()
         self.assertRaises(lib_exc.NotFound,
-                          self.admin_servers_client.update_attached_volume,
+                          self.service_client.update_attached_volume,
                           self.server['id'], nonexistent_volume,
                           volumeId=volume['id'])
 
+    @decorators.skip_because(bug='2112187')
     @decorators.related_bug('1629110', status_code=400)
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('7dcac15a-b107-46d3-a5f6-cb863f4e454a')
@@ -71,8 +78,8 @@
         self.attach_volume(self.server, volume)
 
         nonexistent_volume = data_utils.rand_uuid()
-        self.assertRaises(lib_exc.BadRequest,
-                          self.admin_servers_client.update_attached_volume,
+        self.assertRaises(lib_exc.Conflict,
+                          self.service_servers_client.update_attached_volume,
                           self.server['id'], volume['id'],
                           volumeId=nonexistent_volume)
 
@@ -89,6 +96,7 @@
     volume_min_microversion = '3.27'
 
     create_default_network = True
+    credentials = ['primary', 'admin', ['service_user', 'admin', 'service']]
 
     @classmethod
     def setup_credentials(cls):
@@ -101,9 +109,15 @@
         if not CONF.compute_feature_enabled.volume_multiattach:
             raise cls.skipException('Volume multi-attach is not available.')
 
+    @classmethod
+    def setup_clients(cls):
+        super(UpdateMultiattachVolumeNegativeTest, cls).setup_clients()
+        cls.service_client = cls.os_service_user.servers_client
+
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('7576d497-b7c6-44bd-9cc5-c5b4e50fec71')
     @utils.services('volume')
+    @decorators.skip_because(bug='2112187')
     def test_multiattach_rw_volume_update_failure(self):
         """Test swapping volume attached to multi-servers with read-write mode
 
@@ -158,10 +172,10 @@
         # Assert that a BadRequest is raised when we attempt to update volume1
         # to volume2 on server1 or server2.
         self.assertRaises(lib_exc.BadRequest,
-                          self.admin_servers_client.update_attached_volume,
+                          self.service_client.update_attached_volume,
                           server1['id'], vol1['id'], volumeId=vol2['id'])
         self.assertRaises(lib_exc.BadRequest,
-                          self.admin_servers_client.update_attached_volume,
+                          self.service_client.update_attached_volume,
                           server2['id'], vol1['id'], volumeId=vol2['id'])
 
         # Fetch the volume 1 to check the current attachments.
diff --git a/tempest/api/image/v2/test_images.py b/tempest/api/image/v2/test_images.py
index 9309c76..4375da5 100644
--- a/tempest/api/image/v2/test_images.py
+++ b/tempest/api/image/v2/test_images.py
@@ -19,6 +19,7 @@
 
 from oslo_log import log as logging
 from tempest.api.image import base
+from tempest.common import image as image_utils
 from tempest.common import waiters
 from tempest import config
 from tempest.lib.common.utils import data_utils
@@ -980,3 +981,87 @@
         self.assertEqual(orig_image['os_hash_value'], image['os_hash_value'])
         self.assertEqual(orig_image['os_hash_algo'], image['os_hash_algo'])
         self.assertNotIn('validation_data', image['locations'][0])
+
+
+class HashCalculationRemoteDeletionTest(base.BaseV2ImageTest):
+    """Test calculation of image hash with new location API when the image is
+    deleted from a remote Glance service.
+    """
+    @classmethod
+    def resource_setup(cls):
+        super(HashCalculationRemoteDeletionTest,
+              cls).resource_setup()
+        if not cls.versions_client.has_version('2.17'):
+            # API is not new enough to support add location API
+            skip_msg = (
+                '%s skipped as Glance does not support v2.17')
+            raise cls.skipException(skip_msg)
+
+    @classmethod
+    def skip_checks(cls):
+        super(HashCalculationRemoteDeletionTest,
+              cls).skip_checks()
+        if not CONF.image_feature_enabled.do_secure_hash:
+            skip_msg = (
+                "%s skipped as do_secure_hash is disabled" %
+                cls.__name__)
+            raise cls.skipException(skip_msg)
+
+        if not CONF.image_feature_enabled.http_store_enabled:
+            skip_msg = (
+                "%s skipped as http store is disabled" %
+                cls.__name__)
+            raise cls.skipException(skip_msg)
+
+    @decorators.idempotent_id('123e4567-e89b-12d3-a456-426614174000')
+    def test_hash_calculation_cancelled(self):
+        """Test that image hash calculation is cancelled when the image
+        is deleted from a remote Glance service.
+
+        This test creates an image using new location API, verifies that
+        the hash calculation is initiated, and then deletes the image from a
+        remote Glance service, and verifies that the hash calculation process
+        is properly cancelled and image deleted successfully.
+        """
+
+        # Create an image with a location
+        image_name = data_utils.rand_name('image')
+        container_format = CONF.image.container_formats[0]
+        disk_format = CONF.image.disk_formats[0]
+        image = self.create_image(name=image_name,
+                                  container_format=container_format,
+                                  disk_format=disk_format,
+                                  visibility='private')
+        self.assertEqual(image_name, image['name'])
+        self.assertEqual('queued', image['status'])
+
+        # Start http server at random port to simulate the image location
+        # and to provide random data for the image with slow transfer
+        server = image_utils.RandomDataServer()
+        server.start()
+        self.addCleanup(server.stop)
+
+        # Add a location to the image
+        location = 'http://localhost:%d' % server.port
+        self.client.add_image_location(image['id'], location)
+        waiters.wait_for_image_status(self.client, image['id'], 'active')
+
+        # Verify that the hash calculation is initiated
+        image_info = self.client.show_image(image['id'])
+        self.assertEqual(CONF.image.hashing_algorithm,
+                         image_info['os_hash_algo'])
+        self.assertEqual('active', image_info['status'])
+
+        if CONF.image.alternate_image_endpoint:
+            # If alternate image endpoint is configured, we will delete the
+            # image from the alternate worker
+            self.os_primary.image_client_remote.delete_image(image['id'])
+        else:
+            # delete image from backend
+            self.client.delete_image(image['id'])
+
+        # If image is deleted successfully, the hash calculation is cancelled
+        self.client.wait_for_resource_deletion(image['id'])
+
+        # Stop the server to release the port
+        server.stop()
diff --git a/tempest/api/network/test_allowed_address_pair.py b/tempest/api/network/test_allowed_address_pair.py
index 01dda06..58160e0 100644
--- a/tempest/api/network/test_allowed_address_pair.py
+++ b/tempest/api/network/test_allowed_address_pair.py
@@ -124,7 +124,12 @@
     @decorators.idempotent_id('4d6d178f-34f6-4bff-a01c-0a2f8fe909e4')
     def test_update_port_with_cidr_address_pair(self):
         """Update allowed address pair with cidr"""
-        self._update_port_with_address(str(self.cidr))
+        # NOTE(slaweq): We need to use the next IP subnet to the one which
+        # is configured in the tempest config as the self.cidr will include
+        # "distributed" port created by the ML2/OVN backend and adding this
+        # particular IP address to the allowed address pair is forbidden by
+        # the ML2/OVN backend.
+        self._update_port_with_address(str(self.cidr.next()))
 
     @decorators.idempotent_id('b3f20091-6cd5-472b-8487-3516137df933')
     def test_update_port_with_multiple_ip_mac_address_pair(self):
diff --git a/tempest/api/object_storage/test_account_quotas.py b/tempest/api/object_storage/test_account_quotas.py
index 0a40237..37783b8 100644
--- a/tempest/api/object_storage/test_account_quotas.py
+++ b/tempest/api/object_storage/test_account_quotas.py
@@ -17,6 +17,7 @@
 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
 
@@ -52,7 +53,8 @@
             auth_data=self.reselleradmin_auth_data
         )
         # Set a quota of 20 bytes on the user's account before each test
-        headers = {"X-Account-Meta-Quota-Bytes": "20"}
+        self.set_quota = 20
+        headers = {"X-Account-Meta-Quota-Bytes": self.set_quota}
 
         self.os_roles_operator.account_client.request(
             "POST", url="", headers=headers, body="")
@@ -89,6 +91,24 @@
 
         self.assertHeaders(resp, 'Object', 'PUT')
 
+    @decorators.attr(type="smoke")
+    @decorators.idempotent_id('93fd7776-ae41-4949-8d0c-21889804c1ca')
+    @utils.requires_ext(extension='account_quotas', service='object')
+    def test_overlimit_upload(self):
+        """Test uploading an oversized object raises an OverLimit exception"""
+        object_name = data_utils.rand_name(
+            prefix=CONF.resource_name_prefix, name="TestObject")
+        data = data_utils.arbitrary_string(self.set_quota + 1)
+
+        nbefore = self._get_bytes_used()
+
+        self.assertRaises(lib_exc.OverLimit,
+                          self.object_client.create_object,
+                          self.container_name, object_name, data)
+
+        nafter = self._get_bytes_used()
+        self.assertEqual(nbefore, nafter)
+
     @decorators.attr(type=["smoke"])
     @decorators.idempotent_id('63f51f9f-5f1d-4fc6-b5be-d454d70949d6')
     @utils.requires_ext(extension='account_quotas', service='object')
@@ -115,3 +135,11 @@
 
             self.assertEqual(resp["status"], "204")
             self.assertHeaders(resp, 'Account', 'POST')
+
+    def _get_account_metadata(self):
+        resp, _ = self.account_client.list_account_metadata()
+        return resp
+
+    def _get_bytes_used(self):
+        resp = self._get_account_metadata()
+        return int(resp["x-account-bytes-used"])
diff --git a/tempest/api/volume/admin/test_encrypted_volumes_extend.py b/tempest/api/volume/admin/test_encrypted_volumes_extend.py
index 4506389..9c4819d 100644
--- a/tempest/api/volume/admin/test_encrypted_volumes_extend.py
+++ b/tempest/api/volume/admin/test_encrypted_volumes_extend.py
@@ -34,6 +34,7 @@
             raise cls.skipException(
                 "Attached encrypted volume extend is disabled.")
 
+    @decorators.skip_because(bug="2116852")
     @decorators.idempotent_id('e93243ec-7c37-4b5b-a099-ebf052c13216')
     def test_extend_attached_encrypted_volume_luksv1(self):
         """LUKs v1 decrypts and extends through libvirt."""
diff --git a/tempest/api/volume/admin/test_volume_retype.py b/tempest/api/volume/admin/test_volume_retype.py
index 4a3f494..e19038e 100644
--- a/tempest/api/volume/admin/test_volume_retype.py
+++ b/tempest/api/volume/admin/test_volume_retype.py
@@ -204,3 +204,50 @@
 
         # Retype the volume from snapshot
         self._retype_volume(src_vol, migration_policy='never')
+
+
+class VolumeRetypeMultiattachTest(VolumeRetypeTest):
+    """Test volume retype with/without multiattach"""
+
+    volume_min_microversion = '3.50'
+    volume_max_microversion = 'latest'
+
+    @classmethod
+    def skip_checks(cls):
+        super(VolumeRetypeMultiattachTest, cls).skip_checks()
+        if not CONF.compute_feature_enabled.volume_multiattach:
+            raise cls.skipException('Volume multi-attach is not available.')
+
+    @classmethod
+    def resource_setup(cls):
+        super(VolumeRetypeMultiattachTest, cls).resource_setup()
+        extra_specs_src = {"multiattach": '<is> True'}
+        cls.src_vol_type = cls.create_volume_type()
+        cls.dst_vol_type = cls.create_volume_type(extra_specs=extra_specs_src)
+
+    def _verify_migration(self, source_vol, dest_vol):
+        self.assertEqual(dest_vol['status'], "available")
+        self.assertEqual(dest_vol['volume_type'], self.dst_vol_type['name'])
+        if "multiattach" in self.dst_vol_type['extra_specs'].keys():
+            self.assertEqual(dest_vol['multiattach'], True)
+        else:
+            self.assertEqual(dest_vol['multiattach'], False)
+
+    @decorators.idempotent_id('c0521465-ed82-4d03-961d-a68d673a5051')
+    def test_volume_retype_multiattach(self):
+        """Test volume retype with/without multiattach
+
+        1. Create dst_vol_type with "multiattach = '<is> True'"
+        2. Create src_vol_type without the "multiattach" property
+        3. Retype volume from src_vol_type (non-multiattach)
+           to dst_vol_type(multiattach) and vice versa
+        4. Verify successful retype.
+        """
+        # Retype from non-multiattach to multiattach
+        vol = self.create_volume(volume_type=self.src_vol_type['name'])
+        self._retype_volume(vol, migration_policy='never')
+
+        self.dst_vol_type = self.src_vol_type
+
+        # Retype from multiattach to non-multiattach
+        self._retype_volume(vol, migration_policy='never')
diff --git a/tempest/api/volume/test_volumes_actions.py b/tempest/api/volume/test_volumes_actions.py
index 8b2bc69..6261ddc 100644
--- a/tempest/api/volume/test_volumes_actions.py
+++ b/tempest/api/volume/test_volumes_actions.py
@@ -126,13 +126,6 @@
                             image_id)
             waiters.wait_for_image_status(self.images_client, image_id,
                                           'active')
-            # This is required for the optimized upload volume path.
-            # New location APIs are async so we need to wait for the location
-            # import task to complete.
-            # This should work with old location API since we don't fail if
-            # there are no tasks for the image
-            waiters.wait_for_image_tasks_status(self.images_client,
-                                                image_id, 'success')
             waiters.wait_for_volume_resource_status(self.volumes_client,
                                                     self.volume['id'],
                                                     'available')
diff --git a/tempest/common/compute.py b/tempest/common/compute.py
index 49fcaf2..b885b83 100644
--- a/tempest/common/compute.py
+++ b/tempest/common/compute.py
@@ -255,6 +255,9 @@
         if CONF.compute.compute_volume_common_az:
             params.setdefault('availability_zone',
                               CONF.compute.compute_volume_common_az)
+        if CONF.volume.volume_type:
+            params.setdefault('volume_type',
+                              CONF.volume.volume_type)
         volume = volumes_client.create_volume(**params)
         try:
             waiters.wait_for_volume_resource_status(volumes_client,
diff --git a/tempest/common/image.py b/tempest/common/image.py
index 3618f7e..b8f76fb 100644
--- a/tempest/common/image.py
+++ b/tempest/common/image.py
@@ -14,6 +14,10 @@
 #    under the License.
 
 import copy
+from http import server
+import random
+import threading
+import time
 
 
 def get_image_meta_from_headers(resp):
@@ -63,3 +67,57 @@
         headers['x-image-meta-%s' % key] = str(value)
 
     return headers
+
+
+class RandomDataHandler(server.BaseHTTPRequestHandler):
+    def do_GET(self):
+        self.send_response(200)
+        self.send_header('Content-Type', 'application/octet-stream')
+        self.end_headers()
+
+        start_time = time.time()
+        chunk_size = 64 * 1024  # 64 KiB per chunk
+        while time.time() - start_time < 60:
+            data = bytes(random.getrandbits(8) for _ in range(chunk_size))
+            try:
+                self.wfile.write(data)
+                self.wfile.flush()
+                # simulate slow transfer
+                time.sleep(0.2)
+            except BrokenPipeError:
+                # Client disconnected; stop sending data
+                break
+
+    def do_HEAD(self):
+        # same size as in do_GET (19,660,800 bytes (about 18.75 MiB)
+        size = 300 * 65536
+        self.send_response(200)
+        self.send_header('Content-Type', 'application/octet-stream')
+        self.send_header('Content-Length', str(size))
+        self.end_headers()
+
+
+class RandomDataServer(object):
+    def __init__(self, handler_class=RandomDataHandler):
+        self.handler_class = handler_class
+        self.server = None
+        self.thread = None
+        self.port = None
+
+    def start(self):
+        # Bind to port 0 for an unused port
+        self.server = server.HTTPServer(('localhost', 0), self.handler_class)
+        self.port = self.server.server_address[1]
+
+        # Run server in background thread
+        self.thread = threading.Thread(target=self.server.serve_forever)
+        self.thread.daemon = True
+        self.thread.start()
+
+    def stop(self):
+        if self.server:
+            self.server.shutdown()
+            self.server.server_close()
+            self.thread.join()
+            self.server = None
+            self.thread = None
diff --git a/tempest/config.py b/tempest/config.py
index 9c288ff..fec7692 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -627,6 +627,14 @@
     cfg.BoolOpt('unified_limits',
                 default=False,
                 help='Does the test environment support unified limits?'),
+    cfg.ListOpt('nova_policy_roles',
+                default=['admin', 'member', 'reader'],
+                help='Compute service API policies roles list. List all the '
+                     'roles which are used as default in Nova policy rules. '
+                     'This config option value is used to run the tests with '
+                     'the available roles in Nova. For example, if manager '
+                     'role is not present in the nova release then tempest '
+                     'will use old defaults role token to call nova APIs'),
 ]
 
 
@@ -688,6 +696,11 @@
                          'vdi', 'iso', 'vhdx'],
                 help="A list of image's disk formats "
                      "users can specify."),
+    cfg.StrOpt('hashing_algorithm',
+               default='sha512',
+               help=('Hashing algorithm used by glance to calculate image '
+                     'hashes. This configuration value should be same as '
+                     'glance-api.conf: hashing_algorithm config option.')),
     cfg.StrOpt('images_manifest_file',
                default=None,
                help="A path to a manifest.yml generated using the "
@@ -732,6 +745,17 @@
                 help=('Indicates that image format is enforced by glance, '
                       'such that we should not expect to be able to upload '
                       'bad images for testing other services.')),
+    cfg.BoolOpt('do_secure_hash',
+                default=True,
+                help=('Is do_secure_hash enabled in glance. '
+                      'This configuration value should be same as '
+                      'glance-api.conf: do_secure_hash config option.')),
+    cfg.BoolOpt('http_store_enabled',
+                default=False,
+                help=('Is http store is enabled in glance. '
+                      'http store needs to be mentioned either in '
+                      'glance-api.conf: stores or in enabled_backends '
+                      'configuration option.')),
 ]
 
 network_group = cfg.OptGroup(name='network',
diff --git a/tempest/lib/api_schema/response/compute/v2_1/servers.py b/tempest/lib/api_schema/response/compute/v2_1/servers.py
index 14e2d3b..d86ac6a 100644
--- a/tempest/lib/api_schema/response/compute/v2_1/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_1/servers.py
@@ -520,3 +520,49 @@
         'type': 'object'
     }
 }
+
+list_live_migrations = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'migrations': {
+                'type': 'array',
+                'items': {
+                    'type': 'object',
+                    'properties': {
+                        'id': {'type': 'integer'},
+                        'status': {'type': ['string', 'null']},
+                        'server_uuid': {'type': ['string', 'null']},
+                        'source_node': {'type': ['string', 'null']},
+                        'source_compute': {'type': ['string', 'null']},
+                        'dest_node': {'type': ['string', 'null']},
+                        'dest_compute': {'type': ['string', 'null']},
+                        'dest_host': {'type': ['string', 'null']},
+                        'disk_processed_bytes': {'type': ['integer', 'null']},
+                        'disk_remaining_bytes': {'type': ['integer', 'null']},
+                        'disk_total_bytes': {'type': ['integer', 'null']},
+                        'memory_processed_bytes': {
+                            'type': ['integer', 'null']},
+                        'memory_remaining_bytes': {
+                            'type': ['integer', 'null']},
+                        'memory_total_bytes': {'type': ['integer', 'null']},
+                        'created_at': parameter_types.date_time,
+                        'updated_at': parameter_types.date_time_or_null
+                    },
+                    'additionalProperties': False,
+                    'required': [
+                        'id', 'status', 'instance_uuid', 'source_node',
+                        'source_compute', 'dest_node', 'dest_compute',
+                        'dest_host', 'disk_processed_bytes',
+                        'disk_remaining_bytes', 'disk_total_bytes',
+                        'memory_processed_bytes', 'memory_remaining_bytes',
+                        'memory_total_bytes', 'created_at', 'updated_at'
+                    ]
+                }
+            }
+        },
+        'additionalProperties': False,
+        'required': ['migrations']
+    }
+}
diff --git a/tempest/lib/api_schema/response/compute/v2_100/servers.py b/tempest/lib/api_schema/response/compute/v2_100/servers.py
index 8721387..8a2c15d 100644
--- a/tempest/lib/api_schema/response/compute/v2_100/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_100/servers.py
@@ -127,3 +127,4 @@
 get_remote_consoles = copy.deepcopy(servers299.get_remote_consoles)
 show_instance_action = copy.deepcopy(servers299.show_instance_action)
 create_backup = copy.deepcopy(servers299.create_backup)
+list_live_migrations = copy.deepcopy(servers299.list_live_migrations)
diff --git a/tempest/lib/api_schema/response/compute/v2_16/servers.py b/tempest/lib/api_schema/response/compute/v2_16/servers.py
index 2b3ce38..f09ea7f 100644
--- a/tempest/lib/api_schema/response/compute/v2_16/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_16/servers.py
@@ -173,3 +173,4 @@
 list_volume_attachments = copy.deepcopy(servers.list_volume_attachments)
 show_instance_action = copy.deepcopy(servers.show_instance_action)
 create_backup = copy.deepcopy(servers.create_backup)
+list_live_migrations = copy.deepcopy(servers.list_live_migrations)
diff --git a/tempest/lib/api_schema/response/compute/v2_19/servers.py b/tempest/lib/api_schema/response/compute/v2_19/servers.py
index ba3d787..5cb5bf3 100644
--- a/tempest/lib/api_schema/response/compute/v2_19/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_19/servers.py
@@ -63,3 +63,4 @@
 list_volume_attachments = copy.deepcopy(serversv216.list_volume_attachments)
 show_instance_action = copy.deepcopy(serversv216.show_instance_action)
 create_backup = copy.deepcopy(serversv216.create_backup)
+list_live_migrations = copy.deepcopy(serversv216.list_live_migrations)
diff --git a/tempest/lib/api_schema/response/compute/v2_26/servers.py b/tempest/lib/api_schema/response/compute/v2_26/servers.py
index 123eb72..4ce7f90 100644
--- a/tempest/lib/api_schema/response/compute/v2_26/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_26/servers.py
@@ -106,3 +106,4 @@
 list_volume_attachments = copy.deepcopy(servers219.list_volume_attachments)
 show_instance_action = copy.deepcopy(servers219.show_instance_action)
 create_backup = copy.deepcopy(servers219.create_backup)
+list_live_migrations = copy.deepcopy(servers219.list_live_migrations)
diff --git a/tempest/lib/api_schema/response/compute/v2_3/servers.py b/tempest/lib/api_schema/response/compute/v2_3/servers.py
index d19f1ad..c7e0147 100644
--- a/tempest/lib/api_schema/response/compute/v2_3/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_3/servers.py
@@ -178,3 +178,4 @@
 list_volume_attachments = copy.deepcopy(servers.list_volume_attachments)
 show_instance_action = copy.deepcopy(servers.show_instance_action)
 create_backup = copy.deepcopy(servers.create_backup)
+list_live_migrations = copy.deepcopy(servers.list_live_migrations)
diff --git a/tempest/lib/api_schema/response/compute/v2_45/servers.py b/tempest/lib/api_schema/response/compute/v2_45/servers.py
index cb0fc13..0746465 100644
--- a/tempest/lib/api_schema/response/compute/v2_45/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_45/servers.py
@@ -47,3 +47,4 @@
 attach_volume = copy.deepcopy(servers226.attach_volume)
 show_volume_attachment = copy.deepcopy(servers226.show_volume_attachment)
 list_volume_attachments = copy.deepcopy(servers226.list_volume_attachments)
+list_live_migrations = copy.deepcopy(servers226.list_live_migrations)
diff --git a/tempest/lib/api_schema/response/compute/v2_47/servers.py b/tempest/lib/api_schema/response/compute/v2_47/servers.py
index 1399c2d..d24cc25 100644
--- a/tempest/lib/api_schema/response/compute/v2_47/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_47/servers.py
@@ -72,3 +72,4 @@
 list_volume_attachments = copy.deepcopy(servers245.list_volume_attachments)
 show_instance_action = copy.deepcopy(servers226.show_instance_action)
 create_backup = copy.deepcopy(servers245.create_backup)
+list_live_migrations = copy.deepcopy(servers245.list_live_migrations)
diff --git a/tempest/lib/api_schema/response/compute/v2_48/servers.py b/tempest/lib/api_schema/response/compute/v2_48/servers.py
index 5b53906..a500155 100644
--- a/tempest/lib/api_schema/response/compute/v2_48/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_48/servers.py
@@ -134,3 +134,4 @@
 list_volume_attachments = copy.deepcopy(servers247.list_volume_attachments)
 show_instance_action = copy.deepcopy(servers247.show_instance_action)
 create_backup = copy.deepcopy(servers247.create_backup)
+list_live_migrations = copy.deepcopy(servers247.list_live_migrations)
diff --git a/tempest/lib/api_schema/response/compute/v2_51/servers.py b/tempest/lib/api_schema/response/compute/v2_51/servers.py
index 50d6aaa..27e5f45 100644
--- a/tempest/lib/api_schema/response/compute/v2_51/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_51/servers.py
@@ -41,3 +41,4 @@
 show_volume_attachment = copy.deepcopy(servers248.show_volume_attachment)
 list_volume_attachments = copy.deepcopy(servers248.list_volume_attachments)
 create_backup = copy.deepcopy(servers248.create_backup)
+list_live_migrations = copy.deepcopy(servers248.list_live_migrations)
diff --git a/tempest/lib/api_schema/response/compute/v2_54/servers.py b/tempest/lib/api_schema/response/compute/v2_54/servers.py
index 9de3016..bef1e7f 100644
--- a/tempest/lib/api_schema/response/compute/v2_54/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_54/servers.py
@@ -60,3 +60,4 @@
 list_volume_attachments = copy.deepcopy(servers251.list_volume_attachments)
 show_instance_action = copy.deepcopy(servers251.show_instance_action)
 create_backup = copy.deepcopy(servers251.create_backup)
+list_live_migrations = copy.deepcopy(servers251.list_live_migrations)
diff --git a/tempest/lib/api_schema/response/compute/v2_57/servers.py b/tempest/lib/api_schema/response/compute/v2_57/servers.py
index ee91391..7bee542 100644
--- a/tempest/lib/api_schema/response/compute/v2_57/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_57/servers.py
@@ -64,3 +64,4 @@
 list_volume_attachments = copy.deepcopy(servers254.list_volume_attachments)
 show_instance_action = copy.deepcopy(servers254.show_instance_action)
 create_backup = copy.deepcopy(servers254.create_backup)
+list_live_migrations = copy.deepcopy(servers254.list_live_migrations)
diff --git a/tempest/lib/api_schema/response/compute/v2_58/servers.py b/tempest/lib/api_schema/response/compute/v2_58/servers.py
index 637b765..3e7be49 100644
--- a/tempest/lib/api_schema/response/compute/v2_58/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_58/servers.py
@@ -43,3 +43,4 @@
 show_volume_attachment = copy.deepcopy(servers257.show_volume_attachment)
 list_volume_attachments = copy.deepcopy(servers257.list_volume_attachments)
 create_backup = copy.deepcopy(servers257.create_backup)
+list_live_migrations = copy.deepcopy(servers257.list_live_migrations)
diff --git a/tempest/lib/api_schema/response/compute/v2_59/servers.py b/tempest/lib/api_schema/response/compute/v2_59/servers.py
new file mode 100644
index 0000000..a52c3f4
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_59/servers.py
@@ -0,0 +1,57 @@
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+import copy
+
+from tempest.lib.api_schema.response.compute.v2_58 import servers as servers258
+
+###########################################################################
+#
+# 2.59:
+#
+# The uuid value is now returned in the response body in addition to the
+# migration id for the following API responses:
+#
+# - GET /os-migrations
+# - GET /servers/{server_id}/migrations/{migration_id}
+# - GET /servers/{server_id}/migrations
+#
+###########################################################################
+
+list_live_migrations = copy.deepcopy(servers258.list_live_migrations)
+list_live_migrations['response_body']['properties']['migrations']['items'][
+    'properties'].update({'uuid': {'type': 'string', 'format': 'uuid'}})
+list_live_migrations['response_body']['properties']['migrations']['items'][
+    'required'].append('uuid')
+
+# Below are the unchanged schema in this microversion. We need
+# to keep this schema in this file to have the generic way to select the
+# right schema based on self.schema_versions_info mapping in service client.
+list_servers = copy.deepcopy(servers258.list_servers)
+show_server_diagnostics = copy.deepcopy(servers258.show_server_diagnostics)
+get_remote_consoles = copy.deepcopy(servers258.get_remote_consoles)
+list_tags = copy.deepcopy(servers258.list_tags)
+update_all_tags = copy.deepcopy(servers258.update_all_tags)
+delete_all_tags = copy.deepcopy(servers258.delete_all_tags)
+check_tag_existence = copy.deepcopy(servers258.check_tag_existence)
+update_tag = copy.deepcopy(servers258.update_tag)
+delete_tag = copy.deepcopy(servers258.delete_tag)
+get_server = copy.deepcopy(servers258.get_server)
+list_servers_detail = copy.deepcopy(servers258.list_servers_detail)
+update_server = copy.deepcopy(servers258.update_server)
+rebuild_server = copy.deepcopy(servers258.rebuild_server)
+rebuild_server_with_admin_pass = copy.deepcopy(
+    servers258.rebuild_server_with_admin_pass)
+attach_volume = copy.deepcopy(servers258.attach_volume)
+show_volume_attachment = copy.deepcopy(servers258.show_volume_attachment)
+list_volume_attachments = copy.deepcopy(servers258.list_volume_attachments)
+show_instance_action = copy.deepcopy(servers258.show_instance_action)
+create_backup = copy.deepcopy(servers258.create_backup)
diff --git a/tempest/lib/api_schema/response/compute/v2_6/servers.py b/tempest/lib/api_schema/response/compute/v2_6/servers.py
index 05ab616..d3fc884 100644
--- a/tempest/lib/api_schema/response/compute/v2_6/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_6/servers.py
@@ -33,6 +33,7 @@
 list_volume_attachments = copy.deepcopy(servers.list_volume_attachments)
 show_instance_action = copy.deepcopy(servers.show_instance_action)
 create_backup = copy.deepcopy(servers.create_backup)
+list_live_migrations = copy.deepcopy(servers.list_live_migrations)
 
 # NOTE: The consolidated remote console API got introduced with v2.6
 # with bp/consolidate-console-api. See Nova commit 578bafeda
diff --git a/tempest/lib/api_schema/response/compute/v2_62/servers.py b/tempest/lib/api_schema/response/compute/v2_62/servers.py
index d761fe9..829479f 100644
--- a/tempest/lib/api_schema/response/compute/v2_62/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_62/servers.py
@@ -11,11 +11,11 @@
 #    under the License.
 import copy
 
-from tempest.lib.api_schema.response.compute.v2_58 import servers as servers258
+from tempest.lib.api_schema.response.compute.v2_59 import servers as servers259
 
 # microversion 2.62 added hostId and host to the event, but only hostId is
 # mandatory
-show_instance_action = copy.deepcopy(servers258.show_instance_action)
+show_instance_action = copy.deepcopy(servers259.show_instance_action)
 show_instance_action['response_body']['properties']['instanceAction'][
     'properties']['events']['items'][
     'properties']['hostId'] = {'type': 'string'}
@@ -27,22 +27,23 @@
 # Below are the unchanged schema in this microversion. We need
 # to keep this schema in this file to have the generic way to select the
 # right schema based on self.schema_versions_info mapping in service client.
-list_servers = copy.deepcopy(servers258.list_servers)
-show_server_diagnostics = copy.deepcopy(servers258.show_server_diagnostics)
-get_remote_consoles = copy.deepcopy(servers258.get_remote_consoles)
-list_tags = copy.deepcopy(servers258.list_tags)
-update_all_tags = copy.deepcopy(servers258.update_all_tags)
-delete_all_tags = copy.deepcopy(servers258.delete_all_tags)
-check_tag_existence = copy.deepcopy(servers258.check_tag_existence)
-update_tag = copy.deepcopy(servers258.update_tag)
-delete_tag = copy.deepcopy(servers258.delete_tag)
-get_server = copy.deepcopy(servers258.get_server)
-list_servers_detail = copy.deepcopy(servers258.list_servers_detail)
-update_server = copy.deepcopy(servers258.update_server)
-rebuild_server = copy.deepcopy(servers258.rebuild_server)
+list_servers = copy.deepcopy(servers259.list_servers)
+show_server_diagnostics = copy.deepcopy(servers259.show_server_diagnostics)
+get_remote_consoles = copy.deepcopy(servers259.get_remote_consoles)
+list_tags = copy.deepcopy(servers259.list_tags)
+update_all_tags = copy.deepcopy(servers259.update_all_tags)
+delete_all_tags = copy.deepcopy(servers259.delete_all_tags)
+check_tag_existence = copy.deepcopy(servers259.check_tag_existence)
+update_tag = copy.deepcopy(servers259.update_tag)
+delete_tag = copy.deepcopy(servers259.delete_tag)
+get_server = copy.deepcopy(servers259.get_server)
+list_servers_detail = copy.deepcopy(servers259.list_servers_detail)
+update_server = copy.deepcopy(servers259.update_server)
+rebuild_server = copy.deepcopy(servers259.rebuild_server)
 rebuild_server_with_admin_pass = copy.deepcopy(
-    servers258.rebuild_server_with_admin_pass)
-attach_volume = copy.deepcopy(servers258.attach_volume)
-show_volume_attachment = copy.deepcopy(servers258.show_volume_attachment)
-list_volume_attachments = copy.deepcopy(servers258.list_volume_attachments)
-create_backup = copy.deepcopy(servers258.create_backup)
+    servers259.rebuild_server_with_admin_pass)
+attach_volume = copy.deepcopy(servers259.attach_volume)
+show_volume_attachment = copy.deepcopy(servers259.show_volume_attachment)
+list_volume_attachments = copy.deepcopy(servers259.list_volume_attachments)
+create_backup = copy.deepcopy(servers259.create_backup)
+list_live_migrations = copy.deepcopy(servers259.list_live_migrations)
diff --git a/tempest/lib/api_schema/response/compute/v2_63/servers.py b/tempest/lib/api_schema/response/compute/v2_63/servers.py
index 865b4fd..fe596f5 100644
--- a/tempest/lib/api_schema/response/compute/v2_63/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_63/servers.py
@@ -78,3 +78,4 @@
 list_volume_attachments = copy.deepcopy(servers262.list_volume_attachments)
 show_instance_action = copy.deepcopy(servers262.show_instance_action)
 create_backup = copy.deepcopy(servers262.create_backup)
+list_live_migrations = copy.deepcopy(servers262.list_live_migrations)
diff --git a/tempest/lib/api_schema/response/compute/v2_70/servers.py b/tempest/lib/api_schema/response/compute/v2_70/servers.py
index 6bb688a..bafc7cb 100644
--- a/tempest/lib/api_schema/response/compute/v2_70/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_70/servers.py
@@ -80,3 +80,4 @@
 delete_tag = copy.deepcopy(servers263.delete_tag)
 show_instance_action = copy.deepcopy(servers263.show_instance_action)
 create_backup = copy.deepcopy(servers263.create_backup)
+list_live_migrations = copy.deepcopy(servers263.list_live_migrations)
diff --git a/tempest/lib/api_schema/response/compute/v2_71/servers.py b/tempest/lib/api_schema/response/compute/v2_71/servers.py
index b1c202b..6444e7b 100644
--- a/tempest/lib/api_schema/response/compute/v2_71/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_71/servers.py
@@ -84,3 +84,4 @@
 list_volume_attachments = copy.deepcopy(servers270.list_volume_attachments)
 show_instance_action = copy.deepcopy(servers270.show_instance_action)
 create_backup = copy.deepcopy(servers270.create_backup)
+list_live_migrations = copy.deepcopy(servers270.list_live_migrations)
diff --git a/tempest/lib/api_schema/response/compute/v2_73/servers.py b/tempest/lib/api_schema/response/compute/v2_73/servers.py
index 89f100d..e6ca52e 100644
--- a/tempest/lib/api_schema/response/compute/v2_73/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_73/servers.py
@@ -81,3 +81,4 @@
 list_volume_attachments = copy.deepcopy(servers271.list_volume_attachments)
 show_instance_action = copy.deepcopy(servers271.show_instance_action)
 create_backup = copy.deepcopy(servers271.create_backup)
+list_live_migrations = copy.deepcopy(servers271.list_live_migrations)
diff --git a/tempest/lib/api_schema/response/compute/v2_75/servers.py b/tempest/lib/api_schema/response/compute/v2_75/servers.py
index 6b3e93d..a06355b 100644
--- a/tempest/lib/api_schema/response/compute/v2_75/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_75/servers.py
@@ -62,3 +62,4 @@
 list_volume_attachments = copy.deepcopy(servers273.list_volume_attachments)
 show_instance_action = copy.deepcopy(servers273.show_instance_action)
 create_backup = copy.deepcopy(servers273.create_backup)
+list_live_migrations = copy.deepcopy(servers273.list_live_migrations)
diff --git a/tempest/lib/api_schema/response/compute/v2_79/servers.py b/tempest/lib/api_schema/response/compute/v2_79/servers.py
index 77d9beb..f2d3103 100644
--- a/tempest/lib/api_schema/response/compute/v2_79/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_79/servers.py
@@ -67,3 +67,4 @@
 delete_tag = copy.deepcopy(servers275.delete_tag)
 show_instance_action = copy.deepcopy(servers275.show_instance_action)
 create_backup = copy.deepcopy(servers275.create_backup)
+list_live_migrations = copy.deepcopy(servers275.list_live_migrations)
diff --git a/tempest/lib/api_schema/response/compute/v2_8/servers.py b/tempest/lib/api_schema/response/compute/v2_8/servers.py
index 366fb1b..0d37155 100644
--- a/tempest/lib/api_schema/response/compute/v2_8/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_8/servers.py
@@ -40,3 +40,4 @@
 list_volume_attachments = copy.deepcopy(servers.list_volume_attachments)
 show_instance_action = copy.deepcopy(servers.show_instance_action)
 create_backup = copy.deepcopy(servers.create_backup)
+list_live_migrations = copy.deepcopy(servers.list_live_migrations)
diff --git a/tempest/lib/api_schema/response/compute/v2_80/servers.py b/tempest/lib/api_schema/response/compute/v2_80/servers.py
new file mode 100644
index 0000000..cde1612
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_80/servers.py
@@ -0,0 +1,60 @@
+#    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_79 import servers as servers279
+
+###########################################################################
+#
+# 2.80:
+#
+# The user_id and project_id value is now returned in the response body in
+# addition to the migration id for the following API responses:
+#
+# - GET /os-migrations
+#
+###########################################################################
+
+list_live_migrations = copy.deepcopy(servers279.list_live_migrations)
+list_live_migrations['response_body']['properties']['migrations']['items'][
+    'properties'].update({
+        'user_id': {'type': 'string'},
+        'project_id': {'type': 'string'}
+    })
+list_live_migrations['response_body']['properties']['migrations']['items'][
+    'required'].extend(['user_id', 'project_id'])
+
+# NOTE(zhufl): Below are the unchanged schema in this microversion. We
+# need to keep this schema in this file to have the generic way to select the
+# right schema based on self.schema_versions_info mapping in service client.
+# ****** Schemas unchanged since microversion 2.79 ***
+rebuild_server = copy.deepcopy(servers279.rebuild_server)
+rebuild_server_with_admin_pass = copy.deepcopy(
+    servers279.rebuild_server_with_admin_pass)
+update_server = copy.deepcopy(servers279.update_server)
+get_server = copy.deepcopy(servers279.get_server)
+list_servers_detail = copy.deepcopy(servers279.list_servers_detail)
+list_servers = copy.deepcopy(servers279.list_servers)
+show_server_diagnostics = copy.deepcopy(servers279.show_server_diagnostics)
+get_remote_consoles = copy.deepcopy(servers279.get_remote_consoles)
+list_tags = copy.deepcopy(servers279.list_tags)
+update_all_tags = copy.deepcopy(servers279.update_all_tags)
+delete_all_tags = copy.deepcopy(servers279.delete_all_tags)
+check_tag_existence = copy.deepcopy(servers279.check_tag_existence)
+update_tag = copy.deepcopy(servers279.update_tag)
+delete_tag = copy.deepcopy(servers279.delete_tag)
+show_instance_action = copy.deepcopy(servers279.show_instance_action)
+create_backup = copy.deepcopy(servers279.create_backup)
+attach_volume = copy.deepcopy(servers279.attach_volume)
+show_volume_attachment = copy.deepcopy(servers279.show_volume_attachment)
+list_volume_attachments = copy.deepcopy(servers279.list_volume_attachments)
diff --git a/tempest/lib/api_schema/response/compute/v2_89/servers.py b/tempest/lib/api_schema/response/compute/v2_89/servers.py
index debf0dc..f072eda 100644
--- a/tempest/lib/api_schema/response/compute/v2_89/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_89/servers.py
@@ -12,7 +12,7 @@
 
 import copy
 
-from tempest.lib.api_schema.response.compute.v2_79 import servers as servers279
+from tempest.lib.api_schema.response.compute.v2_80 import servers as servers280
 
 
 ###########################################################################
@@ -27,11 +27,11 @@
 # - POST /servers/{server_id}/os-volume_attachments
 ###########################################################################
 
-attach_volume = copy.deepcopy(servers279.attach_volume)
+attach_volume = copy.deepcopy(servers280.attach_volume)
 
-show_volume_attachment = copy.deepcopy(servers279.show_volume_attachment)
+show_volume_attachment = copy.deepcopy(servers280.show_volume_attachment)
 
-list_volume_attachments = copy.deepcopy(servers279.list_volume_attachments)
+list_volume_attachments = copy.deepcopy(servers280.list_volume_attachments)
 
 # Remove properties
 # 'id' is available unti v2.88
@@ -64,21 +64,22 @@
 # NOTE(zhufl): Below are the unchanged schema in this microversion. We
 # need to keep this schema in this file to have the generic way to select the
 # right schema based on self.schema_versions_info mapping in service client.
-# ****** Schemas unchanged since microversion 2.75 ***
-rebuild_server = copy.deepcopy(servers279.rebuild_server)
+# ****** Schemas unchanged since microversion 2.80 ***
+rebuild_server = copy.deepcopy(servers280.rebuild_server)
 rebuild_server_with_admin_pass = copy.deepcopy(
-    servers279.rebuild_server_with_admin_pass)
-update_server = copy.deepcopy(servers279.update_server)
-get_server = copy.deepcopy(servers279.get_server)
-list_servers_detail = copy.deepcopy(servers279.list_servers_detail)
-list_servers = copy.deepcopy(servers279.list_servers)
-show_server_diagnostics = copy.deepcopy(servers279.show_server_diagnostics)
-get_remote_consoles = copy.deepcopy(servers279.get_remote_consoles)
-list_tags = copy.deepcopy(servers279.list_tags)
-update_all_tags = copy.deepcopy(servers279.update_all_tags)
-delete_all_tags = copy.deepcopy(servers279.delete_all_tags)
-check_tag_existence = copy.deepcopy(servers279.check_tag_existence)
-update_tag = copy.deepcopy(servers279.update_tag)
-delete_tag = copy.deepcopy(servers279.delete_tag)
-show_instance_action = copy.deepcopy(servers279.show_instance_action)
-create_backup = copy.deepcopy(servers279.create_backup)
+    servers280.rebuild_server_with_admin_pass)
+update_server = copy.deepcopy(servers280.update_server)
+get_server = copy.deepcopy(servers280.get_server)
+list_servers_detail = copy.deepcopy(servers280.list_servers_detail)
+list_servers = copy.deepcopy(servers280.list_servers)
+show_server_diagnostics = copy.deepcopy(servers280.show_server_diagnostics)
+get_remote_consoles = copy.deepcopy(servers280.get_remote_consoles)
+list_tags = copy.deepcopy(servers280.list_tags)
+update_all_tags = copy.deepcopy(servers280.update_all_tags)
+delete_all_tags = copy.deepcopy(servers280.delete_all_tags)
+check_tag_existence = copy.deepcopy(servers280.check_tag_existence)
+update_tag = copy.deepcopy(servers280.update_tag)
+delete_tag = copy.deepcopy(servers280.delete_tag)
+show_instance_action = copy.deepcopy(servers280.show_instance_action)
+create_backup = copy.deepcopy(servers280.create_backup)
+list_live_migrations = copy.deepcopy(servers280.list_live_migrations)
diff --git a/tempest/lib/api_schema/response/compute/v2_9/servers.py b/tempest/lib/api_schema/response/compute/v2_9/servers.py
index b4c7865..ad39b14 100644
--- a/tempest/lib/api_schema/response/compute/v2_9/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_9/servers.py
@@ -59,3 +59,4 @@
 list_volume_attachments = copy.deepcopy(servers.list_volume_attachments)
 show_instance_action = copy.deepcopy(servers.show_instance_action)
 create_backup = copy.deepcopy(servers.create_backup)
+list_live_migrations = copy.deepcopy(servers.list_live_migrations)
diff --git a/tempest/lib/api_schema/response/compute/v2_96/servers.py b/tempest/lib/api_schema/response/compute/v2_96/servers.py
index 8a4ed9f..0c4be65 100644
--- a/tempest/lib/api_schema/response/compute/v2_96/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_96/servers.py
@@ -84,3 +84,4 @@
 delete_tag = copy.deepcopy(servers289.delete_tag)
 show_instance_action = copy.deepcopy(servers289.show_instance_action)
 create_backup = copy.deepcopy(servers289.create_backup)
+list_live_migrations = copy.deepcopy(servers289.list_live_migrations)
diff --git a/tempest/lib/api_schema/response/compute/v2_98/servers.py b/tempest/lib/api_schema/response/compute/v2_98/servers.py
index 2fca3eb..0296410 100644
--- a/tempest/lib/api_schema/response/compute/v2_98/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_98/servers.py
@@ -83,3 +83,4 @@
 delete_tag = copy.deepcopy(servers296.delete_tag)
 show_instance_action = copy.deepcopy(servers296.show_instance_action)
 create_backup = copy.deepcopy(servers296.create_backup)
+list_live_migrations = copy.deepcopy(servers296.list_live_migrations)
diff --git a/tempest/lib/api_schema/response/compute/v2_99/servers.py b/tempest/lib/api_schema/response/compute/v2_99/servers.py
index e667321..25b3150 100644
--- a/tempest/lib/api_schema/response/compute/v2_99/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_99/servers.py
@@ -31,6 +31,7 @@
 list_volume_attachments = copy.deepcopy(servers.list_volume_attachments)
 show_instance_action = copy.deepcopy(servers.show_instance_action)
 create_backup = copy.deepcopy(servers.create_backup)
+list_live_migrations = copy.deepcopy(servers.list_live_migrations)
 
 console_auth_tokens = {
     'status_code': [200],
diff --git a/tempest/lib/common/dynamic_creds.py b/tempest/lib/common/dynamic_creds.py
index 6c90938..1815dc6 100644
--- a/tempest/lib/common/dynamic_creds.py
+++ b/tempest/lib/common/dynamic_creds.py
@@ -384,8 +384,9 @@
                                                        subnet_id=subnet_id)
 
     def _get_project_id(self, credential_type, scope):
-        same_creds = [['admin'], ['member'], ['reader']]
-        same_alt_creds = [['alt_admin'], ['alt_member'], ['alt_reader']]
+        same_creds = [['admin'], ['manager'], ['member'], ['reader']]
+        same_alt_creds = [['alt_admin'], ['alt_manager'],
+                          ['alt_member'], ['alt_reader']]
         search_in = []
         if credential_type in same_creds:
             search_in = same_creds
diff --git a/tempest/lib/services/compute/servers_client.py b/tempest/lib/services/compute/servers_client.py
index 4a607a3..1778194 100644
--- a/tempest/lib/services/compute/servers_client.py
+++ b/tempest/lib/services/compute/servers_client.py
@@ -43,6 +43,7 @@
 from tempest.lib.api_schema.response.compute.v2_75 import servers as schemav275
 from tempest.lib.api_schema.response.compute.v2_79 import servers as schemav279
 from tempest.lib.api_schema.response.compute.v2_8 import servers as schemav28
+from tempest.lib.api_schema.response.compute.v2_80 import servers as schemav280
 from tempest.lib.api_schema.response.compute.v2_89 import servers as schemav289
 from tempest.lib.api_schema.response.compute.v2_9 import servers as schemav29
 from tempest.lib.api_schema.response.compute.v2_96 import servers as schemav296
@@ -79,7 +80,8 @@
         {'min': '2.71', 'max': '2.72', 'schema': schemav271},
         {'min': '2.73', 'max': '2.74', 'schema': schemav273},
         {'min': '2.75', 'max': '2.78', 'schema': schemav275},
-        {'min': '2.79', 'max': '2.88', 'schema': schemav279},
+        {'min': '2.79', 'max': '2.79', 'schema': schemav279},
+        {'min': '2.80', 'max': '2.88', 'schema': schemav280},
         {'min': '2.89', 'max': '2.95', 'schema': schemav289},
         {'min': '2.96', 'max': '2.97', 'schema': schemav296},
         {'min': '2.98', 'max': '2.98', 'schema': schemav298},
@@ -549,6 +551,35 @@
         """
         return self.action(server_id, 'os-migrateLive', **kwargs)
 
+    def list_in_progress_live_migration(self, server_id, **kwargs):
+        """This should be called with administrator privileges.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://docs.openstack.org/api-ref/compute/#id318
+        """
+        resp, body = self.get('servers/%s/migrations' % server_id)
+        body = json.loads(body)
+        schema = self.get_schema(self.schema_versions_info)
+        self.validate_response(schema.list_live_migrations, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def force_complete_live_migration(self, server_id, migration_id, **kwargs):
+        """Force complete a in-progress live migration.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://docs.openstack.org/api-ref/compute/#force-migration-complete-action-force-complete-action
+        """
+        post_body = json.dumps({"force_complete": 'null'})
+        resp, body = self.post('servers/%s/migrations/%s/action' %
+                               (server_id, migration_id),
+                               post_body)
+        body = json.loads(body)
+        schema = self.get_schema(self.schema_versions_info)
+        self.validate_response(schema.server_actions_common_schema, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
     def migrate_server(self, server_id, **kwargs):
         """Migrate a server to a new host.
 
diff --git a/tempest/lib/services/image/v2/images_client.py b/tempest/lib/services/image/v2/images_client.py
index a6a1623..c491d9b 100644
--- a/tempest/lib/services/image/v2/images_client.py
+++ b/tempest/lib/services/image/v2/images_client.py
@@ -304,3 +304,13 @@
         resp, _ = self.delete(url)
         self.expected_success(204, resp.status)
         return rest_client.ResponseBody(resp)
+
+    def add_image_location(self, image_id, url, validation_data=None):
+        """Add location for specific Image."""
+        if not validation_data:
+            validation_data = {}
+        data = json.dumps({'url': url, 'validation_data': validation_data})
+        resp, _ = self.post('images/%s/locations' % (image_id),
+                            data)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp)
diff --git a/tempest/scenario/test_network_advanced_server_ops.py b/tempest/scenario/test_network_advanced_server_ops.py
index f4ee98d..d8ffa54 100644
--- a/tempest/scenario/test_network_advanced_server_ops.py
+++ b/tempest/scenario/test_network_advanced_server_ops.py
@@ -33,6 +33,8 @@
 class BaseTestNetworkAdvancedServerOps(manager.NetworkScenarioTest):
     """Base class for defining methods used in tests."""
 
+    credentials = ['primary', 'admin', 'project_manager']
+
     @classmethod
     def skip_checks(cls):
         super(BaseTestNetworkAdvancedServerOps, cls).skip_checks()
@@ -47,7 +49,7 @@
     @classmethod
     def setup_clients(cls):
         super(BaseTestNetworkAdvancedServerOps, cls).setup_clients()
-        cls.admin_servers_client = cls.os_admin.servers_client
+        cls.mgr_server_client = cls.os_admin.servers_client
         cls.sec_group_rules_client = \
             cls.os_primary.security_group_rules_client
         cls.sec_groups_client = cls.os_primary.security_groups_client
@@ -159,7 +161,13 @@
         self._wait_server_status_and_check_network_connectivity(
             server, keypair, floating_ip)
 
-        self.admin_servers_client.migrate_server(
+        if (not dest_host and CONF.enforce_scope.nova and 'manager' in
+            CONF.compute_feature_enabled.nova_policy_roles):
+            self.mgr_server_client = self.os_project_manager.servers_client
+            LOG.info("Using project manager for migrating server: %s, "
+                     "project manager user id: %s",
+                     server['id'], self.mgr_server_client.user_id)
+        self.mgr_server_client.migrate_server(
             server['id'], host=dest_host)
         waiters.wait_for_server_status(self.servers_client, server['id'],
                                        'VERIFY_RESIZE')
@@ -210,8 +218,13 @@
 
         if dest_host:
             migration_kwargs['host'] = dest_host
-
-        self.admin_servers_client.live_migrate_server(
+        elif (CONF.enforce_scope.nova and 'manager' in
+              CONF.compute_feature_enabled.nova_policy_roles):
+            self.mgr_server_client = self.os_project_manager.servers_client
+            LOG.info("Using project manager for migrating server: %s, "
+                     "project manager user id: %s",
+                     server['id'], self.mgr_server_client.user_id)
+        self.mgr_server_client.live_migrate_server(
             server['id'], **migration_kwargs)
         waiters.wait_for_server_status(self.servers_client,
                                        server['id'], 'ACTIVE')
@@ -260,7 +273,13 @@
         self._wait_server_status_and_check_network_connectivity(
             server, keypair, floating_ip)
 
-        self.admin_servers_client.migrate_server(
+        if (not dest_host and CONF.enforce_scope.nova and 'manager' in
+            CONF.compute_feature_enabled.nova_policy_roles):
+            self.mgr_server_client = self.os_project_manager.servers_client
+            LOG.info("Using project manager for migrating server: %s, "
+                     "project manager user id: %s",
+                     server['id'], self.mgr_server_client.user_id)
+        self.mgr_server_client.migrate_server(
             server['id'], host=dest_host)
         waiters.wait_for_server_status(self.servers_client, server['id'],
                                        'VERIFY_RESIZE')
@@ -415,7 +434,7 @@
         - Cold Migration with revert
         - Live Migration
     """
-    credentials = ['primary', 'admin']
+    credentials = ['primary', 'admin', 'project_manager']
     compute_min_microversion = "2.74"
 
     @classmethod
@@ -441,7 +460,7 @@
         cls.keypairs_client = cls.os_admin.keypairs_client
         cls.floating_ips_client = cls.os_admin.floating_ips_client
         cls.servers_client = cls.os_admin.servers_client
-        cls.admin_servers_client = cls.os_admin.servers_client
+        cls.mgr_server_client = cls.os_admin.servers_client
 
     @decorators.idempotent_id('06e23934-79ae-11ee-b962-0242ac120002')
     @testtools.skipUnless(CONF.compute_feature_enabled.resize,
diff --git a/tempest/scenario/test_shelve_instance.py b/tempest/scenario/test_shelve_instance.py
index 204471e..d53e918 100644
--- a/tempest/scenario/test_shelve_instance.py
+++ b/tempest/scenario/test_shelve_instance.py
@@ -13,6 +13,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from oslo_log import log as logging
 import testtools
 
 from tempest.common import compute
@@ -23,6 +24,7 @@
 from tempest.scenario import manager
 
 CONF = config.CONF
+LOG = logging.getLogger(__name__)
 
 
 class TestShelveInstance(manager.ScenarioTest):
@@ -38,12 +40,18 @@
 
     """
 
-    credentials = ['primary', 'admin']
+    credentials = ['primary', 'admin', 'project_manager']
 
     @classmethod
     def setup_clients(cls):
         super(TestShelveInstance, cls).setup_clients()
-        cls.admin_servers_client = cls.os_admin.servers_client
+        cls.mgr_servers_client = cls.os_admin.servers_client
+        if (CONF.enforce_scope.nova and 'manager' in
+            CONF.compute_feature_enabled.nova_policy_roles):
+            cls.mgr_servers_client = cls.os_project_manager.servers_client
+            LOG.info("Using project manager for migrating server, "
+                     "project manager user id: %s",
+                     cls.mgr_servers_client.user_id)
 
     @classmethod
     def skip_checks(cls):
@@ -62,7 +70,7 @@
     def _cold_migrate_server(self, server):
         src_host = self.get_host_for_server(server['id'])
 
-        self.admin_servers_client.migrate_server(server['id'])
+        self.mgr_servers_client.migrate_server(server['id'])
         waiters.wait_for_server_status(self.servers_client,
                                        server['id'], 'VERIFY_RESIZE')
         self.servers_client.confirm_resize_server(server['id'])
diff --git a/tempest/scenario/test_volume_boot_pattern.py b/tempest/scenario/test_volume_boot_pattern.py
index aaa39c9..f378881 100644
--- a/tempest/scenario/test_volume_boot_pattern.py
+++ b/tempest/scenario/test_volume_boot_pattern.py
@@ -324,3 +324,72 @@
     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')
+
+
+class TestVolumeBootPatternV346(manager.EncryptionScenarioTest):
+
+    volume_min_microversion = '3.46'
+    volume_max_microversion = 'latest'
+
+    @decorators.idempotent_id('77889046-1a75-4f14-9b3a-fbbfdd8e5093')
+    @decorators.attr(type='slow')
+    @testtools.skipUnless(CONF.volume_feature_enabled.snapshot,
+                          'Cinder volume snapshots are disabled')
+    @utils.services('compute', 'volume', 'image')
+    def test_instance_boot_after_snapshot_deletion(self):
+        """Test instance bootability after deleting snapshots.
+
+        This test ensures volumes created from instance snapshots
+        are bootable with volume API microversion >= 3.46.
+
+        Steps:
+        1. Create a bootable volume1 from an image.
+        2. Launch an instance1 from the created volume.
+        3. Create image1 - a snapshot1 of the instance1.
+        4. Create a volume2 from the image1.
+        5. Boot an instance2 from the volume2 to verify it's bootable.
+        6. Delete image1 - the first instance1 snapshot1.
+        7. Create image2 - a snapshot2 of the instance1.
+        8. Create a volume3 from the image2.
+        9. Boot instance3 from the new volume3 to verify it's bootable.
+        """
+
+        # Step 1: Create a bootable volume1 from an image
+        volume1 = self.create_volume_from_image()
+
+        # Step 2: Launch instance1 from volume1
+        instance1 = self.boot_instance_from_resource(
+            source_id=volume1['id'],
+            source_type='volume',
+            wait_until='SSHABLE'
+        )
+
+        # Step 3: Create image1 – a snapshot of the instance1
+        image1 = self.create_server_snapshot(instance1)
+
+        # Step 4: Create volume2 from image1
+        volume2 = self.create_volume_from_image(image_id=image1['id'])
+
+        # Step 5: Launch instance2 from volume2
+        self.boot_instance_from_resource(
+            source_id=volume2['id'],
+            source_type='volume',
+            wait_until='SSHABLE'
+        )
+
+        # Step 6: Delete image1
+        self.image_client.delete_image(image1['id'])
+        self.image_client.wait_for_resource_deletion(image1['id'])
+
+        # Step 7: Create image2 – a snapshot of the instance1
+        image2 = self.create_server_snapshot(instance1)
+
+        # Step 8: Create volume3 from image2
+        volume3 = self.create_volume_from_image(image_id=image2['id'])
+
+        # Step 9: Launch instance from volume3
+        self.boot_instance_from_resource(
+            source_id=volume3['id'],
+            source_type='volume',
+            wait_until='SSHABLE'
+        )
diff --git a/tempest/tests/lib/common/test_dynamic_creds.py b/tempest/tests/lib/common/test_dynamic_creds.py
index 4c2ea30..4122db3 100644
--- a/tempest/tests/lib/common/test_dynamic_creds.py
+++ b/tempest/tests/lib/common/test_dynamic_creds.py
@@ -252,6 +252,7 @@
             reader_func = creds.get_project_alt_reader_creds
         else:
             admin_func = creds.get_project_admin_creds
+            manager_func = creds.get_project_manager_creds
             member_func = creds.get_project_member_creds
             reader_func = creds.get_project_reader_creds
         self._mock_assign_user_role()
@@ -286,6 +287,15 @@
         self._request_and_check_second_creds(
             creds, admin_func, member_creds, show_mock, sm_count=2)
 
+        # Now request for the project manager creds which should not create new
+        # project instead should use the project_id of member_creds already
+        # created project.
+        # TODO(gmaan): test test_alt_creds also once alt project
+        # manager is available.
+        if not test_alt_creds:
+            self._request_and_check_second_creds(
+                creds, manager_func, member_creds, show_mock, sm_count=3)
+
     def test_creds_within_same_project(self):
         self._creds_within_same_project()
 
diff --git a/tox.ini b/tox.ini
index b8ba32b..84dd978 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,5 +1,5 @@
 [tox]
-envlist = pep8,py39,bashate,pip-check-reqs
+envlist = pep8,py,bashate,pip-check-reqs
 minversion = 3.18.0
 
 [tempestenv]
diff --git a/zuul.d/integrated-gate.yaml b/zuul.d/integrated-gate.yaml
index 49f9ebc..7880bd8 100644
--- a/zuul.d/integrated-gate.yaml
+++ b/zuul.d/integrated-gate.yaml
@@ -94,7 +94,6 @@
       tempest_concurrency: 4
       tox_envlist: integrated-full
       devstack_localrc:
-        USE_PYTHON3: true
         FORCE_CONFIG_DRIVE: true
         ENABLE_VOLUME_MULTIATTACH: true
         GLANCE_USE_IMPORT_WORKFLOW: True
@@ -115,12 +114,28 @@
     description: |
       Base integration test on CentOS 9 stream
     vars:
+      devstack_localrc:
+        # TODO(ykarel) Remove this when moving to 10-stream
+        PYTHON3_VERSION: 3.11
       # Required until bug/1949606 is resolved when using libvirt and QEMU
       # >=5.0.0 with a [libvirt]virt_type of qemu (TCG).
       configure_swap_size: 4096
       tox_envlist: full
 
 - job:
+    name: tempest-full-rocky
+    parent: tempest-full-py3
+    nodeset: devstack-single-node-rockylinux-9
+    description: |
+      Tempest integration test on Rocky Linux
+    vars:
+      configure_swap_size: 4096
+      tox_envlist: full
+      devstack_localrc:
+        # TODO(ykarel) Remove this when moving to rocky10
+        PYTHON3_VERSION: 3.11
+
+- job:
     name: tempest-integrated-networking
     parent: devstack-tempest
     description: |
@@ -130,7 +145,6 @@
     vars:
       tox_envlist: integrated-network
       devstack_localrc:
-        USE_PYTHON3: true
         FORCE_CONFIG_DRIVE: true
       devstack_services:
         s-account: false
@@ -162,7 +176,6 @@
       tox_envlist: integrated-compute
       tempest_exclude_regex: ""
       devstack_localrc:
-        USE_PYTHON3: true
         FORCE_CONFIG_DRIVE: true
         ENABLE_VOLUME_MULTIATTACH: true
       devstack_services:
@@ -186,6 +199,9 @@
       and Glance related tests. This is meant to be run on Nova gate only.
       This version of the job also uses CentOS 9 stream.
     vars:
+      devstack_localrc:
+        # TODO(ykarel) Remove this when moving to 10-stream
+        PYTHON3_VERSION: 3.11
       # Required until bug/1949606 is resolved when using libvirt and QEMU
       # >=5.0.0 with a [libvirt]virt_type of qemu (TCG).
       configure_swap_size: 4096
@@ -205,7 +221,6 @@
       tempest_concurrency: 4
       tox_envlist: integrated-placement
       devstack_localrc:
-        USE_PYTHON3: true
         FORCE_CONFIG_DRIVE: true
         ENABLE_VOLUME_MULTIATTACH: true
       devstack_services:
@@ -225,7 +240,6 @@
     vars:
       tox_envlist: integrated-storage
       devstack_localrc:
-        USE_PYTHON3: true
         FORCE_CONFIG_DRIVE: true
         ENABLE_VOLUME_MULTIATTACH: true
         GLANCE_USE_IMPORT_WORKFLOW: True
@@ -239,11 +253,6 @@
       related tests. This is meant to be run on Swift gate only.
     vars:
       tox_envlist: integrated-object-storage
-      devstack_localrc:
-        # NOTE(gmann): swift is not ready on python3 yet and devstack
-        # install it on python2.7 only. But setting the USE_PYTHON3
-        # for future once swift is ready on py3.
-        USE_PYTHON3: true
 
 - job:
     name: tempest-with-latest-microversion
@@ -347,27 +356,6 @@
         devstack_localrc:
           ENABLE_VOLUME_MULTIATTACH: true
 
-# TODO(gmann): As per the 2025.1 testing runtime, we need to run at least
-# one job set on Jammy. These jammy job can be removed in the next
-# cycle(2025.2).
-- job:
-    name: tempest-full-ubuntu-jammy
-    description: This is tempest-full python3 job on Ubuntu Jammy(22.04)
-    parent: tempest-full-py3
-    nodeset: openstack-single-node-jammy
-
-- job:
-    name: tempest-multinode-full-ubuntu-jammy
-    description: This is tempest-multinode-full-py3 python3 job on Ubuntu Jammy(22.04)
-    parent: tempest-multinode-full-py3
-    nodeset: openstack-two-node-jammy
-
-- job:
-    name: tempest-extra-tests-ubuntu-jammy
-    description: This is tempest-extra-tests python3 job on Ubuntu Jammy(22.04)
-    parent: tempest-extra-tests
-    nodeset: openstack-single-node-jammy
-
 - job:
     name: tempest-cinder-v2-api
     parent: devstack-tempest
@@ -390,6 +378,9 @@
     timeout: 10800
     nodeset: devstack-single-node-centos-9-stream
     vars:
+      devstack_localrc:
+        # TODO(ykarel) Remove this when moving to 10-stream
+        PYTHON3_VERSION: 3.11
       tox_envlist: full
       configure_swap_size: 4096
       nslookup_target: 'opendev.org'
@@ -461,6 +452,7 @@
         # but if project feel that is not required to run for non SLURP releases then they can opt to make it non-voting or remove it.
         - grenade-skip-level-always:
             branches:
+              - ^.*/2025.2
               - ^.*/2025.1
               - master
         - tempest-integrated-networking
@@ -488,6 +480,7 @@
         # but if project feel that is not required to run for non SLURP releases then they can opt to make it non-voting or remove it.
         - grenade-skip-level-always:
             branches:
+              - ^.*/2025.2
               - ^.*/2025.1
               - master
         # Do not run it on ussuri until below issue is fixed
@@ -532,6 +525,7 @@
               - ^.*/2024.1
               - ^.*/2024.2
               - ^.*/2025.1
+              - ^.*/2025.2
               - master
         - tempest-integrated-compute
         # Do not run it on ussuri until below issue is fixed
@@ -550,6 +544,7 @@
               - ^.*/2024.1
               - ^.*/2024.2
               - ^.*/2025.1
+              - ^.*/2025.2
               - master
         - tempest-integrated-compute
         - openstacksdk-functional-devstack:
@@ -593,6 +588,7 @@
         # but if project feel that is not required to run for non SLURP releases then they can opt to make it non-voting or remove it.
         - grenade-skip-level-always:
             branches:
+              - ^.*/2025.2
               - ^.*/2025.1
               - master
         - tempest-integrated-placement
@@ -620,6 +616,7 @@
         # but if project feel that is not required to run for non SLURP releases then they can opt to make it non-voting or remove it.
         - grenade-skip-level-always:
             branches:
+              - ^.*/2025.2
               - ^.*/2025.1
               - master
         # Do not run it on ussuri until below issue is fixed
@@ -660,6 +657,7 @@
         # but if project feel that is not required to run for non SLURP releases then they can opt to make it non-voting or remove it.
         - grenade-skip-level-always:
             branches:
+              - ^.*/2025.2
               - ^.*/2025.1
               - master
         - tempest-integrated-storage
@@ -686,6 +684,7 @@
         # but if project feel that is not required to run for non SLURP releases then they can opt to make it non-voting or remove it.
         - grenade-skip-level-always:
             branches:
+              - ^.*/2025.2
               - ^.*/2025.1
               - master
         - tempest-integrated-storage
@@ -720,6 +719,7 @@
         # but if project feel that is not required to run for non SLURP releases then they can opt to make it non-voting or remove it.
         - grenade-skip-level-always:
             branches:
+              - ^.*/2025.2
               - ^.*/2025.1
               - master
         - tempest-integrated-object-storage
@@ -746,6 +746,7 @@
         # but if project feel that is not required to run for non SLURP releases then they can opt to make it non-voting or remove it.
         - grenade-skip-level-always:
             branches:
+              - ^.*/2025.2
               - ^.*/2025.1
               - master
         - tempest-integrated-object-storage
diff --git a/zuul.d/project.yaml b/zuul.d/project.yaml
index f044e79..78688c0 100644
--- a/zuul.d/project.yaml
+++ b/zuul.d/project.yaml
@@ -8,10 +8,10 @@
     check:
       jobs:
         - openstack-tox-pep8
-        - openstack-tox-py39
         - openstack-tox-py310
         - openstack-tox-py311
         - openstack-tox-py312
+        - openstack-tox-py313
         - tempest-full-py3:
             # Define list of irrelevant files to use everywhere else
             irrelevant-files: &tempest-irrelevant-files
@@ -27,14 +27,6 @@
               - ^.gitignore$
               - ^.gitreview$
               - ^.mailmap$
-        # NOTE(gmann): Running jobs on Jammy as per the additional testing
-        # for 2025.1 cycle and these can be removed in 2025.2 cycle.
-        - tempest-full-ubuntu-jammy:
-            irrelevant-files: *tempest-irrelevant-files
-        - tempest-multinode-full-ubuntu-jammy:
-            irrelevant-files: *tempest-irrelevant-files
-        - tempest-extra-tests-ubuntu-jammy:
-            irrelevant-files: *tempest-irrelevant-files
         - tempest-extra-tests:
             irrelevant-files: *tempest-irrelevant-files
         - glance-multistore-cinder-import:
@@ -45,9 +37,9 @@
         # if things are working in latest and oldest it will work in between
         # stable branches also. If anything is breaking we will be catching
         # those in respective stable branch gate.
-        - tempest-full-2025-1:
+        - tempest-full-2025-2:
             irrelevant-files: *tempest-irrelevant-files
-        - tempest-full-2023-2:
+        - tempest-full-2024-1:
             irrelevant-files: *tempest-irrelevant-files
         - tempest-multinode-full-py3:
             irrelevant-files: *tempest-irrelevant-files
@@ -121,8 +113,6 @@
         - neutron-ovs-tempest-dvr:
             voting: false
             irrelevant-files: *tempest-irrelevant-files
-        - interop-tempest-consistency:
-            irrelevant-files: *tempest-irrelevant-files
         - tempest-full-test-account-py3:
             voting: false
             irrelevant-files: *tempest-irrelevant-files
@@ -133,10 +123,10 @@
     gate:
       jobs:
         - openstack-tox-pep8
-        - openstack-tox-py39
         - openstack-tox-py310
         - openstack-tox-py311
         - openstack-tox-py312
+        - openstack-tox-py313
         - tempest-slow-py3:
             irrelevant-files: *tempest-irrelevant-files
         - neutron-ovs-grenade-multinode:
@@ -161,19 +151,13 @@
             irrelevant-files: *tempest-irrelevant-files
         - ironic-tempest-bios-ipmi-direct-tinyipa:
             irrelevant-files: *tempest-irrelevant-files
-        # NOTE(gmann): Running jobs on Jammy as per the additional testing
-        # for 2025.1 cycle and these can be removed in 2025.2 cycle.
-        - tempest-full-ubuntu-jammy:
-            irrelevant-files: *tempest-irrelevant-files
-        - tempest-multinode-full-ubuntu-jammy:
-            irrelevant-files: *tempest-irrelevant-files
-        - tempest-extra-tests-ubuntu-jammy:
-            irrelevant-files: *tempest-irrelevant-files
     experimental:
       jobs:
         - nova-multi-cell
         - tempest-with-latest-microversion
+        - tempest-full-oslo-master
         - tempest-stestr-master
+        - tempest-full-rocky
         - tempest-cinder-v2-api:
             irrelevant-files: *tempest-irrelevant-files
         - tempest-all:
@@ -196,20 +180,40 @@
             irrelevant-files: *tempest-irrelevant-files
         - tempest-full-test-account-no-admin-py3:
             irrelevant-files: *tempest-irrelevant-files
-    periodic-stable:
-      jobs:
+        # Run stable releases jobs except those are running in check
+        # pipeline already
         - tempest-full-2025-1
         - tempest-full-2024-2
-        - tempest-full-2024-1
-        - tempest-full-2023-2
+        - tempest-multinode-2025-2
+        - tempest-multinode-2025-1
+        - tempest-multinode-2024-2
+        - tempest-multinode-2024-1
+        - tempest-slow-2025-2
         - tempest-slow-2025-1
         - tempest-slow-2024-2
         - tempest-slow-2024-1
-        - tempest-slow-2023-2
+        - tempest-full-2025-2-extra-tests
         - tempest-full-2025-1-extra-tests
         - tempest-full-2024-2-extra-tests
         - tempest-full-2024-1-extra-tests
-        - tempest-full-2023-2-extra-tests
+    periodic-stable:
+      jobs:
+        - tempest-full-2025-2
+        - tempest-full-2025-1
+        - tempest-full-2024-2
+        - tempest-full-2024-1
+        - tempest-multinode-2025-2
+        - tempest-multinode-2025-1
+        - tempest-multinode-2024-2
+        - tempest-multinode-2024-1
+        - tempest-slow-2025-2
+        - tempest-slow-2025-1
+        - tempest-slow-2024-2
+        - tempest-slow-2024-1
+        - tempest-full-2025-2-extra-tests
+        - tempest-full-2025-1-extra-tests
+        - tempest-full-2024-2-extra-tests
+        - tempest-full-2024-1-extra-tests
     periodic:
       jobs:
         - tempest-all
@@ -220,4 +224,5 @@
         - tempest-full-py3-ipv6
         - tempest-centos9-stream-fips
         - tempest-full-centos-9-stream
+        - tempest-full-rocky
         - tempest-full-test-account-no-admin-py3
diff --git a/zuul.d/stable-jobs.yaml b/zuul.d/stable-jobs.yaml
index 6409ae3..e6fe124 100644
--- a/zuul.d/stable-jobs.yaml
+++ b/zuul.d/stable-jobs.yaml
@@ -1,4 +1,11 @@
 # NOTE(gmann): This file includes all stable release jobs definition.
+
+- job:
+    name: tempest-full-2025-2
+    parent: tempest-full-py3
+    nodeset: openstack-single-node-noble
+    override-checkout: stable/2025.2
+
 - job:
     name: tempest-full-2025-1
     parent: tempest-full-py3
@@ -18,10 +25,10 @@
     override-checkout: stable/2024.1
 
 - job:
-    name: tempest-full-2023-2
-    parent: tempest-full-py3
-    nodeset: openstack-single-node-jammy
-    override-checkout: stable/2023.2
+    name: tempest-full-2025-2-extra-tests
+    parent: tempest-extra-tests
+    nodeset: openstack-single-node-noble
+    override-checkout: stable/2025.2
 
 - job:
     name: tempest-full-2025-1-extra-tests
@@ -42,10 +49,34 @@
     override-checkout: stable/2024.1
 
 - job:
-    name: tempest-full-2023-2-extra-tests
-    parent: tempest-extra-tests
-    nodeset: openstack-single-node-jammy
-    override-checkout: stable/2023.2
+    name: tempest-multinode-2025-2
+    parent: tempest-multinode-full-py3
+    nodeset: openstack-two-node-noble
+    override-checkout: stable/2025.2
+
+- job:
+    name: tempest-multinode-2025-1
+    parent: tempest-multinode-full-py3
+    nodeset: openstack-two-node-noble
+    override-checkout: stable/2025.1
+
+- job:
+    name: tempest-multinode-2024-2
+    parent: tempest-multinode-full-py3
+    nodeset: openstack-two-node-jammy
+    override-checkout: stable/2024.2
+
+- job:
+    name: tempest-multinode-2024-1
+    parent: tempest-multinode-full-py3
+    nodeset: openstack-two-node-jammy
+    override-checkout: stable/2024.1
+
+- job:
+    name: tempest-slow-2025-2
+    parent: tempest-slow-py3
+    nodeset: openstack-two-node-noble
+    override-checkout: stable/2025.2
 
 - job:
     name: tempest-slow-2025-1
@@ -66,12 +97,6 @@
     override-checkout: stable/2024.1
 
 - job:
-    name: tempest-slow-2023-2
-    parent: tempest-slow-py3
-    nodeset: openstack-two-node-jammy
-    override-checkout: stable/2023.2
-
-- job:
     name: tempest-full-py3
     parent: devstack-tempest
     # This job version is to use the 'full' tox env which
@@ -95,7 +120,6 @@
           (AttachVolumeMultiAttachTest)|\
           (UpdateMultiattachVolumeNegativeTest)"
       devstack_localrc:
-        USE_PYTHON3: true
         FORCE_CONFIG_DRIVE: true
         ENABLE_VOLUME_MULTIATTACH: true
         GLANCE_USE_IMPORT_WORKFLOW: True
@@ -120,10 +144,6 @@
         neutron: https://opendev.org/openstack/neutron
       devstack_services:
         neutron-trunk: true
-    group-vars:
-      subnode:
-        devstack_localrc:
-          USE_PYTHON3: true
 
 - job:
     name: tempest-multinode-full-py3
@@ -137,16 +157,10 @@
       - ^.*/yoga
       - ^.*/zed
     vars:
-      devstack_localrc:
-        USE_PYTHON3: true
       devstack_plugins:
         neutron: https://opendev.org/openstack/neutron
       devstack_services:
         neutron-trunk: true
-    group-vars:
-      subnode:
-        devstack_localrc:
-          USE_PYTHON3: true
 
 - job:
     name: tempest-multinode-full
@@ -163,12 +177,6 @@
           (DHCPAgentSchedulersTestJSON)|\
           (AttachVolumeMultiAttachTest)|\
           (UpdateMultiattachVolumeNegativeTest)"
-      devstack_localrc:
-        USE_PYTHON3: False
-    group-vars:
-      subnode:
-        devstack_localrc:
-          USE_PYTHON3: False
 
 - job:
     name: tempest-multinode-full
@@ -178,13 +186,6 @@
     branches:
       - ^.*/yoga
       - ^.*/zed
-    vars:
-      devstack_localrc:
-        USE_PYTHON3: False
-    group-vars:
-      subnode:
-        devstack_localrc:
-          USE_PYTHON3: False
 
 - job:
     name: tempest-slow-py3
diff --git a/zuul.d/tempest-specific.yaml b/zuul.d/tempest-specific.yaml
index deb4157..11c755e 100644
--- a/zuul.d/tempest-specific.yaml
+++ b/zuul.d/tempest-specific.yaml
@@ -30,8 +30,15 @@
       - opendev.org/openstack/oslo.utils
       - opendev.org/openstack/oslo.versionedobjects
       - opendev.org/openstack/oslo.vmware
+      # this is a workaround for a packaging bug in ubuntu
+      # remove when https://bugs.launchpad.net/nova/+bug/2109592
+      # is resolved and oslo config is not a dep of the novnc deb
+      # via the defunct python3-novnc package.
+      - novnc/novnc
     vars:
       tox_envlist: full
+      devstack_localrc:
+         NOVNC_FROM_PACKAGE: false
 
 - job:
     name: tempest-full-parallel
@@ -61,7 +68,6 @@
       run_tempest_cleanup: true
       run_tempest_cleanup_prefix: true
       devstack_localrc:
-        USE_PYTHON3: true
         FORCE_CONFIG_DRIVE: true
       devstack_services:
         s-account: false