Merge "Switch to ecdsa ssh key type by default"
diff --git a/doc/source/test_removal.rst b/doc/source/test_removal.rst
index a3bb645..bb223b1 100644
--- a/doc/source/test_removal.rst
+++ b/doc/source/test_removal.rst
@@ -50,19 +50,10 @@
 a Depends-On in the commit message for the commit which moved the test into
 another repo.
 
-For prong 2 you can use OpenStack-Health:
+For prong 2 you can use subunit2sql:
 
-Using OpenStack-Health
-""""""""""""""""""""""
-
-Go to: http://status.openstack.org/openstack-health and then navigate to a per
-test page for six months. You'll end up with a page that will graph the success
-and failure rates on the bottom graph. For example, something like `this URL`_.
-
-.. _this URL: http://status.openstack.org/openstack-health/#/test/tempest.scenario.test_volume_boot_pattern.TestVolumeBootPatternV2.test_volume_boot_pattern?groupKey=project&resolutionKey=day&duration=P6M
-
-The Old Way using subunit2sql directly
-""""""""""""""""""""""""""""""""""""""
+Using subunit2sql directly
+""""""""""""""""""""""""""
 
 ``SELECT * from tests where test_id like "%test_id%";``
 (where ``$test_id`` is the full test_id, but truncated to the class because of
diff --git a/releasenotes/notes/remove-compute-api-extensions-config-b8564f60f4fa5495.yaml b/releasenotes/notes/remove-compute-api-extensions-config-b8564f60f4fa5495.yaml
new file mode 100644
index 0000000..55df775
--- /dev/null
+++ b/releasenotes/notes/remove-compute-api-extensions-config-b8564f60f4fa5495.yaml
@@ -0,0 +1,5 @@
+---
+upgrade:
+  - |
+    Remove deprecated config option ``api_extensions`` from
+    ``compute_feature_enabled`` groups.
diff --git a/tempest/api/compute/admin/test_flavors.py b/tempest/api/compute/admin/test_flavors.py
index d6b6b7e..294b1ab 100644
--- a/tempest/api/compute/admin/test_flavors.py
+++ b/tempest/api/compute/admin/test_flavors.py
@@ -16,7 +16,6 @@
 import uuid
 
 from tempest.api.compute import base
-from tempest.common import utils
 from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
@@ -26,13 +25,6 @@
     """Tests Flavors API Create and Delete that require admin privileges"""
 
     @classmethod
-    def skip_checks(cls):
-        super(FlavorsAdminTestJSON, cls).skip_checks()
-        if not utils.is_extension_enabled('OS-FLV-EXT-DATA', 'compute'):
-            msg = "OS-FLV-EXT-DATA extension not enabled."
-            raise cls.skipException(msg)
-
-    @classmethod
     def resource_setup(cls):
         super(FlavorsAdminTestJSON, cls).resource_setup()
 
diff --git a/tempest/api/compute/admin/test_flavors_access.py b/tempest/api/compute/admin/test_flavors_access.py
index 87ab7c7..c86ff76 100644
--- a/tempest/api/compute/admin/test_flavors_access.py
+++ b/tempest/api/compute/admin/test_flavors_access.py
@@ -14,7 +14,6 @@
 #    under the License.
 
 from tempest.api.compute import base
-from tempest.common import utils
 from tempest.lib import decorators
 
 
@@ -25,13 +24,6 @@
     """
 
     @classmethod
-    def skip_checks(cls):
-        super(FlavorsAccessTestJSON, cls).skip_checks()
-        if not utils.is_extension_enabled('OS-FLV-EXT-DATA', 'compute'):
-            msg = "OS-FLV-EXT-DATA extension not enabled."
-            raise cls.skipException(msg)
-
-    @classmethod
     def resource_setup(cls):
         super(FlavorsAccessTestJSON, cls).resource_setup()
 
diff --git a/tempest/api/compute/admin/test_flavors_access_negative.py b/tempest/api/compute/admin/test_flavors_access_negative.py
index ac09cb0..3b38693 100644
--- a/tempest/api/compute/admin/test_flavors_access_negative.py
+++ b/tempest/api/compute/admin/test_flavors_access_negative.py
@@ -14,7 +14,6 @@
 #    under the License.
 
 from tempest.api.compute import base
-from tempest.common import utils
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
 
@@ -28,13 +27,6 @@
     credentials = ['primary', 'admin', 'alt']
 
     @classmethod
-    def skip_checks(cls):
-        super(FlavorsAccessNegativeTestJSON, cls).skip_checks()
-        if not utils.is_extension_enabled('OS-FLV-EXT-DATA', 'compute'):
-            msg = "OS-FLV-EXT-DATA extension not enabled."
-            raise cls.skipException(msg)
-
-    @classmethod
     def resource_setup(cls):
         super(FlavorsAccessNegativeTestJSON, cls).resource_setup()
 
diff --git a/tempest/api/compute/admin/test_flavors_extra_specs.py b/tempest/api/compute/admin/test_flavors_extra_specs.py
index 10018fe..da95660 100644
--- a/tempest/api/compute/admin/test_flavors_extra_specs.py
+++ b/tempest/api/compute/admin/test_flavors_extra_specs.py
@@ -14,7 +14,6 @@
 #    under the License.
 
 from tempest.api.compute import base
-from tempest.common import utils
 from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 
@@ -27,13 +26,6 @@
     """
 
     @classmethod
-    def skip_checks(cls):
-        super(FlavorsExtraSpecsTestJSON, cls).skip_checks()
-        if not utils.is_extension_enabled('OS-FLV-EXT-DATA', 'compute'):
-            msg = "OS-FLV-EXT-DATA extension not enabled."
-            raise cls.skipException(msg)
-
-    @classmethod
     def resource_setup(cls):
         super(FlavorsExtraSpecsTestJSON, cls).resource_setup()
         flavor_name = data_utils.rand_name('test_flavor')
diff --git a/tempest/api/compute/admin/test_flavors_extra_specs_negative.py b/tempest/api/compute/admin/test_flavors_extra_specs_negative.py
index 721acca..6822614 100644
--- a/tempest/api/compute/admin/test_flavors_extra_specs_negative.py
+++ b/tempest/api/compute/admin/test_flavors_extra_specs_negative.py
@@ -15,7 +15,6 @@
 #    under the License.
 
 from tempest.api.compute import base
-from tempest.common import utils
 from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
@@ -28,13 +27,6 @@
     """
 
     @classmethod
-    def skip_checks(cls):
-        super(FlavorsExtraSpecsNegativeTestJSON, cls).skip_checks()
-        if not utils.is_extension_enabled('OS-FLV-EXT-DATA', 'compute'):
-            msg = "OS-FLV-EXT-DATA extension not enabled."
-            raise cls.skipException(msg)
-
-    @classmethod
     def resource_setup(cls):
         super(FlavorsExtraSpecsNegativeTestJSON, cls).resource_setup()
 
diff --git a/tempest/api/compute/servers/test_server_group.py b/tempest/api/compute/servers/test_server_group.py
index 4811a7b..4b6d45a 100644
--- a/tempest/api/compute/servers/test_server_group.py
+++ b/tempest/api/compute/servers/test_server_group.py
@@ -17,7 +17,6 @@
 
 from tempest.api.compute import base
 from tempest.common import compute
-from tempest.common import utils
 from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 
@@ -32,13 +31,6 @@
     create_default_network = True
 
     @classmethod
-    def skip_checks(cls):
-        super(ServerGroupTestJSON, cls).skip_checks()
-        if not utils.is_extension_enabled('os-server-groups', 'compute'):
-            msg = "os-server-groups extension is not enabled."
-            raise cls.skipException(msg)
-
-    @classmethod
     def setup_clients(cls):
         super(ServerGroupTestJSON, cls).setup_clients()
         cls.client = cls.server_groups_client
diff --git a/tempest/api/compute/servers/test_server_tags.py b/tempest/api/compute/servers/test_server_tags.py
index c988788..cdeaae5 100644
--- a/tempest/api/compute/servers/test_server_tags.py
+++ b/tempest/api/compute/servers/test_server_tags.py
@@ -14,7 +14,6 @@
 #    under the License.
 
 from tempest.api.compute import base
-from tempest.common import utils
 from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 
@@ -28,13 +27,6 @@
     create_default_network = True
 
     @classmethod
-    def skip_checks(cls):
-        super(ServerTagsTestJSON, cls).skip_checks()
-        if not utils.is_extension_enabled('os-server-tags', 'compute'):
-            msg = "os-server-tags extension is not enabled."
-            raise cls.skipException(msg)
-
-    @classmethod
     def setup_clients(cls):
         super(ServerTagsTestJSON, cls).setup_clients()
         cls.client = cls.servers_client
diff --git a/tempest/api/compute/test_extensions.py b/tempest/api/compute/test_extensions.py
index 3318876..034cb9e 100644
--- a/tempest/api/compute/test_extensions.py
+++ b/tempest/api/compute/test_extensions.py
@@ -16,7 +16,6 @@
 from oslo_log import log as logging
 
 from tempest.api.compute import base
-from tempest.common import utils
 from tempest import config
 from tempest.lib import decorators
 
@@ -32,24 +31,14 @@
     @decorators.idempotent_id('3bb27738-b759-4e0d-a5fa-37d7a6df07d1')
     def test_list_extensions(self):
         """Test listing compute extensions"""
-        if not CONF.compute_feature_enabled.api_extensions:
-            raise self.skipException('There are not any extensions configured')
         extensions = self.extensions_client.list_extensions()['extensions']
-        ext = CONF.compute_feature_enabled.api_extensions[0]
-
         # Log extensions list
         extension_list = [x['alias'] for x in extensions]
         LOG.debug("Nova extensions: %s", ','.join(extension_list))
 
-        if ext == 'all':
-            self.assertIn('Hosts', map(lambda x: x['name'], extensions))
-        elif ext:
-            self.assertIn(ext, extension_list)
-        else:
-            raise self.skipException('There are not any extensions configured')
+        self.assertIn('Hosts', map(lambda x: x['name'], extensions))
 
     @decorators.idempotent_id('05762f39-bdfa-4cdb-9b46-b78f8e78e2fd')
-    @utils.requires_ext(extension='os-consoles', service='compute')
     def test_get_extension(self):
         """Test getting specified compute extension details"""
         extension = self.extensions_client.show_extension('os-consoles')
diff --git a/tempest/api/compute/test_quotas.py b/tempest/api/compute/test_quotas.py
index 5fe0e3b..38ca53b 100644
--- a/tempest/api/compute/test_quotas.py
+++ b/tempest/api/compute/test_quotas.py
@@ -15,20 +15,12 @@
 
 from tempest.api.compute import base
 from tempest.common import tempest_fixtures as fixtures
-from tempest.common import utils
 from tempest.lib import decorators
 
 
 class QuotasTestJSON(base.BaseV2ComputeTest):
     """Test compute quotas"""
 
-    @classmethod
-    def skip_checks(cls):
-        super(QuotasTestJSON, cls).skip_checks()
-        if not utils.is_extension_enabled('os-quota-sets', 'compute'):
-            msg = "quotas extension not enabled."
-            raise cls.skipException(msg)
-
     def setUp(self):
         # NOTE(mriedem): Avoid conflicts with os-quota-class-sets tests.
         self.useFixture(fixtures.LockFixture('compute_quotas'))
diff --git a/tempest/api/volume/base.py b/tempest/api/volume/base.py
index b90b5bb..172b6ed 100644
--- a/tempest/api/volume/base.py
+++ b/tempest/api/volume/base.py
@@ -98,7 +98,7 @@
     def create_volume(cls, wait_until='available', **kwargs):
         """Wrapper utility that returns a test volume.
 
-           :param wait_until: wait till volume status.
+           :param wait_until: wait till volume status, None means no wait.
         """
         if 'size' not in kwargs:
             kwargs['size'] = CONF.volume.volume_size
@@ -127,8 +127,9 @@
         cls.addClassResourceCleanup(test_utils.call_and_ignore_notfound_exc,
                                     cls.delete_volume, cls.volumes_client,
                                     volume['id'])
-        waiters.wait_for_volume_resource_status(cls.volumes_client,
-                                                volume['id'], wait_until)
+        if wait_until:
+            waiters.wait_for_volume_resource_status(cls.volumes_client,
+                                                    volume['id'], wait_until)
         return volume
 
     @classmethod
diff --git a/tempest/cmd/verify_tempest_config.py b/tempest/cmd/verify_tempest_config.py
index 421afd3..3d476b9 100644
--- a/tempest/cmd/verify_tempest_config.py
+++ b/tempest/cmd/verify_tempest_config.py
@@ -214,7 +214,6 @@
 
 def get_extension_client(os, service):
     extensions_client = {
-        'nova': os.compute.ExtensionsClient(),
         'neutron': os.network.ExtensionsClient(),
         'swift': os.object_storage.CapabilitiesClient(),
         # NOTE: Cinder v3 API is current and v2 and v1 are deprecated.
@@ -231,7 +230,6 @@
 
 def get_enabled_extensions(service):
     extensions_options = {
-        'nova': CONF.compute_feature_enabled.api_extensions,
         'cinder': CONF.volume_feature_enabled.api_extensions,
         'neutron': CONF.network_feature_enabled.api_extensions,
         'swift': CONF.object_storage_feature_enabled.discoverable_apis,
@@ -442,7 +440,7 @@
         os = clients.Manager(icreds.get_primary_creds().credentials)
         services = check_service_availability(os, update)
         results = {}
-        for service in ['nova', 'cinder', 'neutron', 'swift']:
+        for service in ['cinder', 'neutron', 'swift']:
             if service not in services:
                 continue
             results = verify_extensions(os, service, results)
diff --git a/tempest/common/utils/__init__.py b/tempest/common/utils/__init__.py
index 88a16b7..0fa5ce4 100644
--- a/tempest/common/utils/__init__.py
+++ b/tempest/common/utils/__init__.py
@@ -96,7 +96,6 @@
 
     """
     config_dict = {
-        'compute': CONF.compute_feature_enabled.api_extensions,
         'volume': CONF.volume_feature_enabled.api_extensions,
         'network': CONF.network_feature_enabled.api_extensions,
         'object': CONF.object_storage_feature_enabled.discoverable_apis,
diff --git a/tempest/config.py b/tempest/config.py
index 75c73c7..ebde421 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -450,18 +450,6 @@
                      "the '.' with '-' to comply with fqdn hostname. Nova "
                      "changed that in Wallaby cycle, if your cloud is older "
                      "than wallaby then you can keep/make it False."),
-    cfg.ListOpt('api_extensions',
-                default=['all'],
-                help='A list of enabled compute extensions with a special '
-                     'entry all which indicates every extension is enabled. '
-                     'Each extension should be specified with alias name. '
-                     'Empty list indicates all extensions are disabled',
-                     deprecated_for_removal=True,
-                     deprecated_reason='The Nova extensions API and mechanism '
-                                       'is deprecated. This option will be '
-                                       'removed when all releases supported '
-                                       'by tempest no longer contain the Nova '
-                                       'extensions API and mechanism.'),
     cfg.BoolOpt('change_password',
                 default=False,
                 help="Does the test environment support changing the admin "
diff --git a/tempest/lib/common/utils/linux/remote_client.py b/tempest/lib/common/utils/linux/remote_client.py
index 224f3bf..d0cdc25 100644
--- a/tempest/lib/common/utils/linux/remote_client.py
+++ b/tempest/lib/common/utils/linux/remote_client.py
@@ -75,7 +75,7 @@
         :param ip_address: IP address to ssh to
         :param username: Ssh username
         :param password: Ssh password
-        :param pkey: Ssh public key
+        :param pkey: Ssh private key
         :param server: Server dict, used for debugging purposes
         :param servers_client: Servers client, used for debugging purposes
         :param ssh_timeout: Timeout in seconds to wait for the ssh banner
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index 1d24bc1..db28487 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -321,11 +321,12 @@
         return server
 
     def create_volume(self, size=None, name=None, snapshot_id=None,
-                      imageRef=None, volume_type=None, **kwargs):
+                      imageRef=None, volume_type=None, wait_until='available',
+                      **kwargs):
         """Creates volume
 
         This wrapper utility creates volume and waits for volume to be
-        in 'available' state.
+        in 'available' state by default. If wait_until is None, means no wait.
         This method returns the volume's full representation by GET request.
         """
 
@@ -358,11 +359,12 @@
         self.addCleanup(test_utils.call_and_ignore_notfound_exc,
                         self.volumes_client.delete_volume, volume['id'])
         self.assertEqual(name, volume['name'])
-        waiters.wait_for_volume_resource_status(self.volumes_client,
-                                                volume['id'], 'available')
-        # The volume retrieved on creation has a non-up-to-date status.
-        # Retrieval after it becomes active ensures correct details.
-        volume = self.volumes_client.show_volume(volume['id'])['volume']
+        if wait_until:
+            waiters.wait_for_volume_resource_status(self.volumes_client,
+                                                    volume['id'], wait_until)
+            # The volume retrieved on creation has a non-up-to-date status.
+            # Retrieval after it becomes active ensures correct details.
+            volume = self.volumes_client.show_volume(volume['id'])['volume']
         return volume
 
     def create_backup(self, volume_id, name=None, description=None,
diff --git a/tempest/tests/cmd/test_verify_tempest_config.py b/tempest/tests/cmd/test_verify_tempest_config.py
index a8a4c0f..05ea84e 100644
--- a/tempest/tests/cmd/test_verify_tempest_config.py
+++ b/tempest/tests/cmd/test_verify_tempest_config.py
@@ -380,70 +380,6 @@
         self.assertIn('extensions', results['cinder'])
         self.assertEqual([], results['cinder']['extensions'])
 
-    def test_verify_extensions_nova(self):
-        def fake_list_extensions():
-            return ([{'alias': 'fake1'}, {'alias': 'fake2'},
-                     {'alias': 'not_fake'}])
-        fake_os = mock.MagicMock()
-        fake_client = mock.MagicMock()
-        fake_client.list_extensions = fake_list_extensions
-        self.useFixture(fixtures.MockPatchObject(
-            verify_tempest_config, 'get_extension_client',
-            return_value=fake_client))
-        self.useFixture(fixtures.MockPatchObject(
-            verify_tempest_config, 'get_enabled_extensions',
-            return_value=(['fake1', 'fake2', 'fake3'])))
-        results = verify_tempest_config.verify_extensions(fake_os,
-                                                          'nova', {})
-        self.assertIn('nova', results)
-        self.assertIn('fake1', results['nova'])
-        self.assertTrue(results['nova']['fake1'])
-        self.assertIn('fake2', results['nova'])
-        self.assertTrue(results['nova']['fake2'])
-        self.assertIn('fake3', results['nova'])
-        self.assertFalse(results['nova']['fake3'])
-        self.assertIn('not_fake', results['nova'])
-        self.assertFalse(results['nova']['not_fake'])
-
-    def test_verify_extensions_nova_all(self):
-        def fake_list_extensions():
-            return ({'extensions': [{'alias': 'fake1'},
-                                    {'alias': 'fake2'},
-                                    {'alias': 'not_fake'}]})
-        fake_os = mock.MagicMock()
-        fake_client = mock.MagicMock()
-        fake_client.list_extensions = fake_list_extensions
-        self.useFixture(fixtures.MockPatchObject(
-            verify_tempest_config, 'get_extension_client',
-            return_value=fake_client))
-        self.useFixture(fixtures.MockPatchObject(
-            verify_tempest_config, 'get_enabled_extensions',
-            return_value=(['all'])))
-        results = verify_tempest_config.verify_extensions(fake_os,
-                                                          'nova', {})
-        self.assertIn('nova', results)
-        self.assertIn('extensions', results['nova'])
-        self.assertEqual(sorted(['fake1', 'fake2', 'not_fake']),
-                         sorted(results['nova']['extensions']))
-
-    def test_verify_extensions_nova_none(self):
-        def fake_list_extensions():
-            return ({'extensions': []})
-        fake_os = mock.MagicMock()
-        fake_client = mock.MagicMock()
-        fake_client.list_extensions = fake_list_extensions
-        self.useFixture(fixtures.MockPatchObject(
-            verify_tempest_config, 'get_extension_client',
-            return_value=fake_client))
-        self.useFixture(fixtures.MockPatchObject(
-            verify_tempest_config, 'get_enabled_extensions',
-            return_value=(['all'])))
-        results = verify_tempest_config.verify_extensions(fake_os,
-                                                          'nova', {})
-        self.assertIn('nova', results)
-        self.assertIn('extensions', results['nova'])
-        self.assertEqual([], results['nova']['extensions'])
-
     def test_verify_extensions_swift(self):
         def fake_list_extensions():
             return {'fake1': 'metadata',
@@ -513,7 +449,6 @@
     def test_get_extension_client(self):
         fake_os = mock.MagicMock()
         services = {
-            'nova': fake_os.compute.ExtensionsClient(),
             'neutron': fake_os.network.ExtensionsClient(),
             'swift': fake_os.object_storage.CapabilitiesClient(),
             'cinder': fake_os.volume_v2.ExtensionsClient(),
diff --git a/tempest/tests/test_decorators.py b/tempest/tests/test_decorators.py
index 1889420..ede6d07 100644
--- a/tempest/tests/test_decorators.py
+++ b/tempest/tests/test_decorators.py
@@ -92,7 +92,7 @@
     def setUp(self):
         super(TestRequiresExtDecorator, self).setUp()
         cfg.CONF.set_default('api_extensions', ['enabled_ext', 'another_ext'],
-                             'compute-feature-enabled')
+                             'network-feature-enabled')
 
     def _test_requires_ext_helper(self, expected_to_skip=True,
                                   **decorator_args):
@@ -116,18 +116,18 @@
     def test_requires_ext_decorator(self):
         self._test_requires_ext_helper(expected_to_skip=False,
                                        extension='enabled_ext',
-                                       service='compute')
+                                       service='network')
 
     def test_requires_ext_decorator_disabled_ext(self):
         self._test_requires_ext_helper(extension='disabled_ext',
-                                       service='compute')
+                                       service='network')
 
     def test_requires_ext_decorator_with_all_ext_enabled(self):
         cfg.CONF.set_default('api_extensions', ['all'],
-                             group='compute-feature-enabled')
+                             group='network-feature-enabled')
         self._test_requires_ext_helper(expected_to_skip=False,
                                        extension='random_ext',
-                                       service='compute')
+                                       service='network')
 
     def test_requires_ext_decorator_bad_service(self):
         self.assertRaises(KeyError,
diff --git a/tools/check_logs.py b/tools/check_logs.py
index 8ab3af2..8ea94e8 100755
--- a/tools/check_logs.py
+++ b/tools/check_logs.py
@@ -136,7 +136,7 @@
     with open(ALLOW_LIST_FILE) as stream:
         loaded = yaml.safe_load(stream)
         if loaded:
-            for (name, l) in loaded.values():
+            for (name, l) in loaded.items():
                 for w in l:
                     assert 'module' in w, 'no module in %s' % name
                     assert 'message' in w, 'no message in %s' % name
diff --git a/zuul.d/integrated-gate.yaml b/zuul.d/integrated-gate.yaml
index d35e25d..3dd8c49 100644
--- a/zuul.d/integrated-gate.yaml
+++ b/zuul.d/integrated-gate.yaml
@@ -87,6 +87,19 @@
         horizon: true
 
 - job:
+    name: tempest-full-centos-9-stream
+    parent: tempest-full-py3
+    nodeset: devstack-single-node-centos-9-stream
+    # centos-9-stream is supported from yoga release onwards
+    branches: ^(?!stable/(pike|queens|rocky|stein|train|ussuri|victoria|wallaby|xena)).*$
+    description: |
+      Base integration test on CentOS 9 stream
+    vars:
+      # Required until bug/1949606 is resolved when using libvirt and QEMU
+      # >=5.0.0 with a [libvirt]virt_type of qemu (TCG).
+      configure_swap_size: 4096
+
+- job:
     name: tempest-integrated-networking
     parent: devstack-tempest
     branches: ^(?!stable/ocata).*$
@@ -129,17 +142,16 @@
         c-bak: false
 
 - job:
-    name: tempest-integrated-compute-centos-8-stream
+    name: tempest-integrated-compute-centos-9-stream
     parent: tempest-integrated-compute
-    # TODO(gmann): Make this job non voting until bug#1957941 if fixed.
-    voting: false
-    nodeset: devstack-single-node-centos-8-stream
-    branches: ^(?!stable/(ocata|pike|queens|rocky|stein|train|ussuri|victoria)).*$
+    nodeset: devstack-single-node-centos-9-stream
+    # centos-9-stream is supported from yoga release onwards
+    branches: ^(?!stable/(pike|queens|rocky|stein|train|ussuri|victoria|wallaby|xena)).*$
     description: |
       This job runs integration tests for compute. This is
       subset of 'tempest-full-py3' job and run Nova, Neutron, Cinder (except backup tests)
       and Glance related tests. This is meant to be run on Nova gate only.
-      This version of the job also uses CentOS 8 stream.
+      This version of the job also uses CentOS 9 stream.
     vars:
       # Required until bug/1949606 is resolved when using libvirt and QEMU
       # >=5.0.0 with a [libvirt]virt_type of qemu (TCG).
@@ -352,17 +364,27 @@
       (Nova, Neutron, Cinder and Glance related) in check and gate
       for the Nova integrated gate. This is meant to be
       run on Nova gate only.
+    # NOTE(gmann): This template is used for stable branches also so when we
+    # add/remove jobs here we need to make sure we should not change the
+    # behaviour for stable branches. For example, with branch variant we need
+    # to make sure old job keep running on stable branches and the new one run
+    # only from master(or the branch it was meant to run).
     check:
       jobs:
         - grenade-skip-level:
             voting: false
         - tempest-integrated-compute
-        - tempest-integrated-compute-centos-8-stream
+        # centos-8-stream is tested from wallaby -> yoga branches
+        - tempest-integrated-compute-centos-8-stream:
+            branches: ^stable/(wallaby|xena|yoga).*$
+        # centos-9-stream is tested from zed release onwards
+        - tempest-integrated-compute-centos-9-stream:
+            branches: ^(?!stable/(pike|queens|rocky|stein|train|ussuri|victoria|wallaby|xena|yoga)).*$
         - openstacksdk-functional-devstack
     gate:
       jobs:
         - tempest-integrated-compute
-        - tempest-integrated-compute-centos-8-stream
+        - tempest-integrated-compute-centos-9-stream
         - openstacksdk-functional-devstack
 
 - project-template:
diff --git a/zuul.d/project.yaml b/zuul.d/project.yaml
index eb8b793..6ab7eed 100644
--- a/zuul.d/project.yaml
+++ b/zuul.d/project.yaml
@@ -43,6 +43,8 @@
             irrelevant-files: *tempest-irrelevant-files
         - tempest-full-victoria-py3:
             irrelevant-files: *tempest-irrelevant-files
+        - tempest-slow-wallaby:
+            irrelevant-files: *tempest-irrelevant-files
         - tempest-multinode-full-py3:
             irrelevant-files: *tempest-irrelevant-files
         - tempest-tox-plugin-sanity-check:
@@ -120,8 +122,6 @@
             irrelevant-files: *tempest-irrelevant-files
         - openstack-tox-bashate:
             irrelevant-files: *tempest-irrelevant-files-2
-        - tempest-full-py3-centos-8-stream:
-            irrelevant-files: *tempest-irrelevant-files
         - tempest-full-centos-9-stream:
             irrelevant-files: *tempest-irrelevant-files
     gate:
@@ -137,8 +137,6 @@
             irrelevant-files: *tempest-irrelevant-files
         - tempest-full-py3:
             irrelevant-files: *tempest-irrelevant-files
-        - tempest-full-py3-centos-8-stream:
-            irrelevant-files: *tempest-irrelevant-files
         - grenade:
             irrelevant-files: *tempest-irrelevant-files
         - tempest-ipv6-only:
@@ -171,6 +169,9 @@
         - tempest-full-xena
         - tempest-full-wallaby-py3
         - tempest-full-victoria-py3
+        - tempest-slow-yoga
+        - tempest-slow-xena
+        - tempest-slow-wallaby
     periodic:
       jobs:
         - tempest-all
diff --git a/zuul.d/stable-jobs.yaml b/zuul.d/stable-jobs.yaml
index 8086458..00b40f5 100644
--- a/zuul.d/stable-jobs.yaml
+++ b/zuul.d/stable-jobs.yaml
@@ -20,6 +20,21 @@
     override-checkout: stable/victoria
 
 - job:
+    name: tempest-slow-yoga
+    parent: tempest-slow-py3
+    override-checkout: stable/yoga
+
+- job:
+    name: tempest-slow-xena
+    parent: tempest-slow-py3
+    override-checkout: stable/xena
+
+- job:
+    name: tempest-slow-wallaby
+    parent: tempest-slow-py3
+    override-checkout: stable/wallaby
+
+- job:
     name: tempest-full-py3
     parent: devstack-tempest
     # This job version is with swift disabled on py3
@@ -198,3 +213,41 @@
       - stable/victoria
       - stable/wallaby
       - stable/xena
+
+- job:
+    name: tempest-integrated-compute-centos-8-stream
+    parent: tempest-integrated-compute
+    # TODO(gmann): Make this job non voting until bug#1957941 if fixed.
+    voting: false
+    nodeset: devstack-single-node-centos-8-stream
+    branches:
+      - stable/wallaby
+      - stable/xena
+      - stable/yoga
+    description: |
+      This job runs integration tests for compute. This is
+      subset of 'tempest-full-py3' job and run Nova, Neutron, Cinder (except backup tests)
+      and Glance related tests. This is meant to be run on Nova gate only.
+      This version of the job also uses CentOS 8 stream.
+    vars:
+      # Required until bug/1949606 is resolved when using libvirt and QEMU
+      # >=5.0.0 with a [libvirt]virt_type of qemu (TCG).
+      configure_swap_size: 4096
+
+- job:
+    name: tempest-full-py3-centos-8-stream
+    parent: tempest-full-py3
+    # TODO(gmann): Make this job non voting until bug#1957941 if fixed.
+    voting: false
+    branches:
+      - stable/wallaby
+      - stable/xena
+      - stable/yoga
+    nodeset: devstack-single-node-centos-8-stream
+    description: |
+      Base integration test with Neutron networking and py36 running
+      on CentOS 8 stream
+    vars:
+      # Required until bug/1949606 is resolved when using libvirt and QEMU
+      # >=5.0.0 with a [libvirt]virt_type of qemu (TCG).
+      configure_swap_size: 4096
diff --git a/zuul.d/tempest-specific.yaml b/zuul.d/tempest-specific.yaml
index a4a4b67..822feaa 100644
--- a/zuul.d/tempest-specific.yaml
+++ b/zuul.d/tempest-specific.yaml
@@ -69,26 +69,6 @@
         c-bak: false
 
 - job:
-    name: tempest-full-py3-centos-8-stream
-    parent: tempest-full-py3
-    # TODO(gmann): Make this job non voting until bug#1957941 if fixed.
-    voting: false
-    nodeset: devstack-single-node-centos-8-stream
-    description: |
-      Base integration test with Neutron networking and py36 running
-      on CentOS 8 stream
-    vars:
-      # Required until bug/1949606 is resolved when using libvirt and QEMU
-      # >=5.0.0 with a [libvirt]virt_type of qemu (TCG).
-      configure_swap_size: 4096
-
-- job:
-    name: tempest-full-centos-9-stream
-    parent: tempest-full-py3-centos-8-stream
-    voting: true
-    nodeset: devstack-single-node-centos-9-stream
-
-- job:
     name: tempest-tox-plugin-sanity-check
     parent: tox
     description: |