Merge "Add wait for location import task"
diff --git a/releasenotes/notes/2024.2-intermediate-release-2a9f305375fcb462.yaml b/releasenotes/notes/2024.2-intermediate-release-2a9f305375fcb462.yaml
new file mode 100644
index 0000000..11d3a4f
--- /dev/null
+++ b/releasenotes/notes/2024.2-intermediate-release-2a9f305375fcb462.yaml
@@ -0,0 +1,5 @@
+---
+prelude: >
+    This is an intermediate release during the 2024.2 Dalmatian development
+    cycle to make new functionality available to plugins and other consumers.
+
diff --git a/releasenotes/notes/add-manager-creds-49acd9192110c3e3.yaml b/releasenotes/notes/add-manager-creds-49acd9192110c3e3.yaml
new file mode 100644
index 0000000..a5d7984
--- /dev/null
+++ b/releasenotes/notes/add-manager-creds-49acd9192110c3e3.yaml
@@ -0,0 +1,7 @@
+---
+features:
+  - |
+    Add support for project manager and domain manager personas by adding
+    ``get_project_manager_creds`` and ``get_domain_manager_creds`` to
+    the ``DynamicCredentialProvider`` and ``PreProvisionedCredentialProvider``
+    classes of the common library.
diff --git a/releasenotes/source/index.rst b/releasenotes/source/index.rst
index f36c837..475a99c 100644
--- a/releasenotes/source/index.rst
+++ b/releasenotes/source/index.rst
@@ -6,6 +6,7 @@
    :maxdepth: 1
 
    unreleased
+   v40.0.0
    v39.0.0
    v38.0.0
    v37.0.0
diff --git a/releasenotes/source/v40.0.0.rst b/releasenotes/source/v40.0.0.rst
new file mode 100644
index 0000000..995767b
--- /dev/null
+++ b/releasenotes/source/v40.0.0.rst
@@ -0,0 +1,6 @@
+=====================
+v40.0.0 Release Notes
+=====================
+
+.. release-notes:: 40.0.0 Release Notes
+   :version: 40.0.0
diff --git a/tempest/api/compute/base.py b/tempest/api/compute/base.py
index 1069e0f..b974b52 100644
--- a/tempest/api/compute/base.py
+++ b/tempest/api/compute/base.py
@@ -547,7 +547,7 @@
         def get_and_verify_metadata():
             try:
                 ssh_client.exec_command('curl -V')
-            except exceptions.SSHExecCommandFailed:
+            except lib_exc.SSHExecCommandFailed:
                 if not CONF.compute_feature_enabled.config_drive:
                     raise self.skipException('curl not found in guest '
                                              'and config drive is '
diff --git a/tempest/api/image/base.py b/tempest/api/image/base.py
index 0cc088a..c2f067c 100644
--- a/tempest/api/image/base.py
+++ b/tempest/api/image/base.py
@@ -177,8 +177,8 @@
         # If we added the location directly, the image goes straight
         # to active and no hashing is done
         self.assertEqual('active', image['status'])
-        self.assertIsNone(None, image['os_hash_algo'])
-        self.assertIsNone(None, image['os_hash_value'])
+        self.assertIsNone(image['os_hash_algo'])
+        self.assertIsNone(image['os_hash_value'])
 
         return image
 
@@ -201,8 +201,8 @@
 
         # The image should still be active and still have no hashes
         self.assertEqual('active', image['status'])
-        self.assertIsNone(None, image['os_hash_algo'])
-        self.assertIsNone(None, image['os_hash_value'])
+        self.assertIsNone(image['os_hash_algo'])
+        self.assertIsNone(image['os_hash_value'])
 
         # The direct_url should still match the first location
         if 'direct_url' in image:
diff --git a/tempest/api/image/v2/test_images_formats.py b/tempest/api/image/v2/test_images_formats.py
index 7ebc685..48f1325 100644
--- a/tempest/api/image/v2/test_images_formats.py
+++ b/tempest/api/image/v2/test_images_formats.py
@@ -110,22 +110,25 @@
         if not CONF.image_feature_enabled.image_conversion:
             self.skipTest('Import image_conversion not enabled')
 
-        glance_noconvert = [
-            # Glance does not support vmdk-sparse-with-footer with the
-            # in-tree format_inspector
-            'vmdk-sparse-with-footer',
-            ]
-        # Any images glance does not support in *conversion* for some
-        # reason will fail, even though the manifest marks them as usable.
-        expect_fail = any(x in self.imgdef['name']
-                          for x in glance_noconvert)
+        # VMDK with footer was not supported by earlier service versions,
+        # so we need to tolerate it passing and failing (skip for the latter).
+        # See this for more info:
+        # https://bugs.launchpad.net/glance/+bug/2073262
+        is_broken = 'footer' in self.imgdef['name']
 
         if (self.imgdef['format'] in CONF.image.disk_formats and
-                self.imgdef['usable'] and not expect_fail):
+                self.imgdef['usable']):
             # Usable images should end up in active state
             image = self._test_image(self.imgdef, asimport=True)
-            waiters.wait_for_image_status(self.client, image['id'],
-                                          'active')
+            try:
+                waiters.wait_for_image_status(self.client, image['id'],
+                                              'active')
+            except lib_exc.TimeoutException:
+                if is_broken:
+                    self.skipTest(
+                        'Older glance did not support vmdk-with-footer')
+                else:
+                    raise
         else:
             # FIXME(danms): Make this better, but gpt will fail before
             # the import even starts until glance has it in its API
@@ -164,7 +167,9 @@
             self.skipTest(
                 'Format %s not allowed by config' % self.imgdef['format'])
 
-        # VMDK with footer is not supported by anyone yet until fixed:
+        # VMDK with footer was not supported by earlier service versions,
+        # so we need to tolerate it passing and failing (skip for the latter).
+        # See this for more info:
         # https://bugs.launchpad.net/glance/+bug/2073262
         is_broken = 'footer' in self.imgdef['name']
 
diff --git a/tempest/common/waiters.py b/tempest/common/waiters.py
index e249f35..9e97f47 100644
--- a/tempest/common/waiters.py
+++ b/tempest/common/waiters.py
@@ -672,6 +672,28 @@
     raise lib_exc.TimeoutException
 
 
+def wait_for_server_ports_active(client, server_id, is_active, **kwargs):
+    """Wait for all server ports to reach active status
+    :param client: The network client to use when querying the port's status
+    :param server_id: The uuid of the server's ports we need to verify.
+    :param is_active: A function to call to the check port active status.
+    :param kwargs: Additional arguments, if any, to pass to list_ports()
+    """
+    start_time = time.time()
+    while (time.time() - start_time <= client.build_timeout):
+        ports = client.list_ports(device_id=server_id, **kwargs)['ports']
+        if all(is_active(port) for port in ports):
+            LOG.debug("Server ID %s ports are all ACTIVE %s: ",
+                      server_id, ports)
+            return ports
+        LOG.warning("Server ID %s has ports that are not ACTIVE, waiting "
+                    "for state to change on all: %s", server_id, ports)
+        time.sleep(client.build_interval)
+    LOG.error("Server ID %s ports have failed to transition to ACTIVE, "
+              "timing out: %s", server_id, ports)
+    raise lib_exc.TimeoutException
+
+
 def wait_for_ssh(ssh_client, timeout=30):
     """Waits for SSH connection to become usable"""
     start_time = int(time.time())
diff --git a/tempest/lib/common/cred_provider.py b/tempest/lib/common/cred_provider.py
index 2da206f..93b9586 100644
--- a/tempest/lib/common/cred_provider.py
+++ b/tempest/lib/common/cred_provider.py
@@ -76,6 +76,10 @@
         return
 
     @abc.abstractmethod
+    def get_domain_manager_creds(self):
+        return
+
+    @abc.abstractmethod
     def get_domain_member_creds(self):
         return
 
@@ -92,6 +96,10 @@
         return
 
     @abc.abstractmethod
+    def get_project_manager_creds(self):
+        return
+
+    @abc.abstractmethod
     def get_project_member_creds(self):
         return
 
diff --git a/tempest/lib/common/dynamic_creds.py b/tempest/lib/common/dynamic_creds.py
index 6814373..6c90938 100644
--- a/tempest/lib/common/dynamic_creds.py
+++ b/tempest/lib/common/dynamic_creds.py
@@ -432,7 +432,7 @@
                         cred_type = [cred_type]
                     credentials = self._create_creds(
                         roles=cred_type, scope=scope, project_id=project_id)
-                elif credential_type in [['member'], ['reader']]:
+                elif credential_type in [['manager'], ['member'], ['reader']]:
                     credentials = self._create_creds(
                         roles=credential_type, scope=scope,
                         project_id=project_id)
@@ -492,6 +492,9 @@
     def get_domain_admin_creds(self):
         return self.get_credentials(['admin'], scope='domain')
 
+    def get_domain_manager_creds(self):
+        return self.get_credentials(['manager'], scope='domain')
+
     def get_domain_member_creds(self):
         return self.get_credentials(['member'], scope='domain')
 
@@ -504,6 +507,9 @@
     def get_project_alt_admin_creds(self):
         return self.get_credentials(['alt_admin'], scope='project')
 
+    def get_project_manager_creds(self):
+        return self.get_credentials(['manager'], scope='project')
+
     def get_project_member_creds(self):
         return self.get_credentials(['member'], scope='project')
 
diff --git a/tempest/lib/common/preprov_creds.py b/tempest/lib/common/preprov_creds.py
index 6d948cf..3ba7db1 100644
--- a/tempest/lib/common/preprov_creds.py
+++ b/tempest/lib/common/preprov_creds.py
@@ -353,6 +353,13 @@
         self._creds['domain_admin'] = domain_admin
         return domain_admin
 
+    def get_domain_manager_creds(self):
+        if self._creds.get('domain_manager'):
+            return self._creds.get('domain_manager')
+        domain_manager = self._get_creds(['manager'], scope='domain')
+        self._creds['domain_manager'] = domain_manager
+        return domain_manager
+
     def get_domain_member_creds(self):
         if self._creds.get('domain_member'):
             return self._creds.get('domain_member')
@@ -378,6 +385,13 @@
         # TODO(gmann): Implement alt admin hash.
         return
 
+    def get_project_manager_creds(self):
+        if self._creds.get('project_manager'):
+            return self._creds.get('project_manager')
+        project_manager = self._get_creds(['manager'], scope='project')
+        self._creds['project_manager'] = project_manager
+        return project_manager
+
     def get_project_member_creds(self):
         if self._creds.get('project_member'):
             return self._creds.get('project_member')
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index 714a7c7..2b2ee99 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -1098,8 +1098,6 @@
 
         if ip_addr and not kwargs.get('fixed_ips'):
             kwargs['fixed_ips'] = 'ip_address=%s' % ip_addr
-        ports = self.os_admin.ports_client.list_ports(
-            device_id=server['id'], **kwargs)['ports']
 
         # A port can have more than one IP address in some cases.
         # If the network is dual-stack (IPv4 + IPv6), this port is associated
@@ -1114,6 +1112,18 @@
             return (port['status'] == 'ACTIVE' or
                     port.get('binding:vnic_type') == 'baremetal')
 
+        # Wait for all compute ports to be ACTIVE.
+        # This will raise a TimeoutException if that does not happen.
+        client = self.os_admin.ports_client
+        try:
+            ports = waiters.wait_for_server_ports_active(
+                client=client, server_id=server['id'],
+                is_active=_is_active, **kwargs)
+        except lib_exc.TimeoutException:
+            LOG.error("Server ports failed transitioning to ACTIVE for "
+                      "server: %s", server)
+            raise
+
         port_map = [(p["id"], fxip["ip_address"])
                     for p in ports
                     for fxip in p["fixed_ips"]
@@ -1121,7 +1131,8 @@
                         _is_active(p))]
         inactive = [p for p in ports if p['status'] != 'ACTIVE']
         if inactive:
-            LOG.warning("Instance has ports that are not ACTIVE: %s", inactive)
+            # This should just be Ironic ports, see _is_active() above
+            LOG.debug("Instance has ports that are not ACTIVE: %s", inactive)
 
         self.assertNotEmpty(port_map,
                             "No IPv4 addresses found in: %s" % ports)
diff --git a/tempest/tests/common/test_waiters.py b/tempest/tests/common/test_waiters.py
index f194173..f7f2dc7 100755
--- a/tempest/tests/common/test_waiters.py
+++ b/tempest/tests/common/test_waiters.py
@@ -884,6 +884,58 @@
                           waiters.wait_for_port_status, mock_client,
                           fake_port_id, fake_status)
 
+    def test_wait_for_server_ports_active(self):
+        """Test that the waiter replies with the ports before the timeout"""
+
+        def is_active(port):
+            return port['status'] == 'ACTIVE'
+
+        def client_response(device_id):
+            """Mock client response, replies with partial status after one
+            call and final status after 2 calls
+            """
+            if mock_client.call_count >= 2:
+                return mock_ports_active
+            else:
+                mock_client.call_count += 1
+                if mock_client.call_count > 1:
+                    return mock_ports_half_active
+                return mock_ports
+
+        mock_ports = {'ports': [{'id': '1234', 'status': 'DOWN'},
+                                {'id': '5678', 'status': 'DOWN'}]}
+        mock_ports_half_active = {'ports': [{'id': '1234', 'status': 'ACTIVE'},
+                                            {'id': '5678', 'status': 'DOWN'}]}
+        mock_ports_active = {'ports': [{'id': '1234', 'status': 'ACTIVE'},
+                                       {'id': '5678', 'status': 'ACTIVE'}]}
+        mock_client = mock.Mock(
+            spec=ports_client.PortsClient,
+            build_timeout=30, build_interval=1,
+            list_ports=client_response)
+        fake_server_id = "9876"
+        self.assertEqual(mock_ports_active['ports'],
+                         waiters.wait_for_server_ports_active(
+                             mock_client, fake_server_id, is_active))
+
+    def test_wait_for_server_ports_active_timeout(self):
+        """Negative test - checking that a timeout
+        presented by a small 'fake_timeout' and a static status of
+        'DOWN' in the mock will raise a timeout exception
+        """
+
+        def is_active(port):
+            return port['status'] == 'ACTIVE'
+
+        mock_ports = {'ports': [{'id': '1234', 'status': "DOWN"}]}
+        mock_client = mock.Mock(
+            spec=ports_client.PortsClient,
+            build_timeout=2, build_interval=1,
+            list_ports=lambda device_id: mock_ports)
+        fake_server_id = "9876"
+        self.assertRaises(lib_exc.TimeoutException,
+                          waiters.wait_for_server_ports_active,
+                          mock_client, fake_server_id, is_active)
+
 
 class TestServerFloatingIPWaiters(base.TestCase):
 
diff --git a/tempest/tests/lib/common/test_dynamic_creds.py b/tempest/tests/lib/common/test_dynamic_creds.py
index d3d01c0..4c2ea30 100644
--- a/tempest/tests/lib/common/test_dynamic_creds.py
+++ b/tempest/tests/lib/common/test_dynamic_creds.py
@@ -104,6 +104,14 @@
                           (200, {'tenant': {'id': id, 'name': name}}))))
         return tenant_fix
 
+    def _mock_domain_create(self, id, name):
+        domain_fix = self.useFixture(fixtures.MockPatchObject(
+            self.domains_client.DomainsClient,
+            'create_domain',
+            return_value=(rest_client.ResponseBody
+                          (200, {'domain': {'id': id, 'name': name}}))))
+        return domain_fix
+
     def _mock_list_roles(self, id, name):
         roles_fix = self.useFixture(fixtures.MockPatchObject(
             self.roles_client.RolesClient,
@@ -143,7 +151,8 @@
                               {'id': '1', 'name': 'FakeRole'},
                               {'id': '2', 'name': 'member'},
                               {'id': '3', 'name': 'reader'},
-                              {'id': '4', 'name': 'admin'}]}))))
+                              {'id': '4', 'name': 'manager'},
+                              {'id': '5', 'name': 'admin'}]}))))
         return roles_fix
 
     def _mock_list_ec2_credentials(self, user_id, tenant_id):
@@ -999,6 +1008,7 @@
     roles_client = v3_roles_client
     tenants_client = v3_projects_client
     users_client = v3_users_client
+    domains_client = domains_client
     token_client_class = token_client.V3TokenClient
     fake_response = fake_identity._fake_v3_response
     tenants_client_class = tenants_client.ProjectsClient
@@ -1263,3 +1273,47 @@
                 "member role already exists, ignoring conflict.")
         creds.creds_client.assign_user_role.assert_called_once_with(
             mock.ANY, mock.ANY, 'member')
+
+    @mock.patch('tempest.lib.common.rest_client.RestClient')
+    def test_project_manager_creds(self, MockRestClient):
+        creds = dynamic_creds.DynamicCredentialProvider(**self.fixed_params)
+        self._mock_list_roles('1234', 'manager')
+        self._mock_user_create('1234', 'fake_manager_user')
+        self._mock_tenant_create('1234', 'fake_manager_tenant')
+
+        user_mock = mock.patch.object(self.roles_client.RolesClient,
+                                      'create_user_role_on_project')
+        user_mock.start()
+        self.addCleanup(user_mock.stop)
+        with mock.patch.object(self.roles_client.RolesClient,
+                               'create_user_role_on_project') as user_mock:
+            manager_creds = creds.get_project_manager_creds()
+        user_mock.assert_has_calls([
+            mock.call('1234', '1234', '1234')])
+        self.assertEqual(manager_creds.username, 'fake_manager_user')
+        self.assertEqual(manager_creds.tenant_name, 'fake_manager_tenant')
+        # Verify IDs
+        self.assertEqual(manager_creds.tenant_id, '1234')
+        self.assertEqual(manager_creds.user_id, '1234')
+
+    @mock.patch('tempest.lib.common.rest_client.RestClient')
+    def test_domain_manager_creds(self, MockRestClient):
+        creds = dynamic_creds.DynamicCredentialProvider(**self.fixed_params)
+        self._mock_list_roles('1234', 'manager')
+        self._mock_user_create('1234', 'fake_manager_user')
+        self._mock_domain_create('1234', 'fake_manager_domain')
+
+        user_mock = mock.patch.object(self.roles_client.RolesClient,
+                                      'create_user_role_on_domain')
+        user_mock.start()
+        self.addCleanup(user_mock.stop)
+        with mock.patch.object(self.roles_client.RolesClient,
+                               'create_user_role_on_domain') as user_mock:
+            manager_creds = creds.get_domain_manager_creds()
+        user_mock.assert_has_calls([
+            mock.call('1234', '1234', '1234')])
+        self.assertEqual(manager_creds.username, 'fake_manager_user')
+        self.assertEqual(manager_creds.domain_name, 'fake_manager_domain')
+        # Verify IDs
+        self.assertEqual(manager_creds.domain_id, '1234')
+        self.assertEqual(manager_creds.user_id, '1234')
diff --git a/tempest/tests/lib/common/test_preprov_creds.py b/tempest/tests/lib/common/test_preprov_creds.py
index f2131dc..5a36f71 100644
--- a/tempest/tests/lib/common/test_preprov_creds.py
+++ b/tempest/tests/lib/common/test_preprov_creds.py
@@ -77,7 +77,13 @@
             {'username': 'test_admin2', 'project_name': 'test_tenant12',
              'password': 'p', 'roles': [admin_role]},
             {'username': 'test_admin3', 'project_name': 'test_tenant13',
-             'password': 'p', 'types': ['admin']}]
+             'password': 'p', 'types': ['admin']},
+            {'username': 'test_project_manager1',
+             'project_name': 'test_tenant14', 'password': 'p',
+             'roles': ['manager']},
+            {'username': 'test_project_manager2',
+             'tenant_name': 'test_tenant15', 'password': 'p',
+             'roles': ['manager']}]
 
     def setUp(self):
         super(TestPreProvisionedCredentials, self).setUp()
@@ -319,7 +325,7 @@
         calls = get_free_hash_mock.mock.mock_calls
         self.assertEqual(len(calls), 1)
         args = calls[0][1][0]
-        self.assertEqual(len(args), 10)
+        self.assertEqual(len(args), 12)
         for i in admin_hashes:
             self.assertNotIn(i, args)
 
@@ -431,6 +437,26 @@
             # Get one more
             test_accounts_class.get_admin_creds()
 
+    def test_get_project_manager_creds(self):
+        test_accounts_class = preprov_creds.PreProvisionedCredentialProvider(
+            **self.fixed_params)
+        p_manager_creds = test_accounts_class.get_project_manager_creds()
+        self.assertNotIn('test_admin', p_manager_creds.username)
+        self.assertNotIn('test_user', p_manager_creds.username)
+        self.assertIn('test_project_manager', p_manager_creds.username)
+
+    def test_get_project_manager_creds_none_available(self):
+        admin_accounts = [x for x in self.test_accounts if 'test_admin'
+                          in x['username']]
+        self.useFixture(fixtures.MockPatch(
+            'tempest.lib.common.preprov_creds.read_accounts_yaml',
+            return_value=admin_accounts))
+        test_accounts_class = preprov_creds.PreProvisionedCredentialProvider(
+            **self.fixed_params)
+        with testtools.ExpectedException(lib_exc.InvalidCredentials):
+            # Get one more
+            test_accounts_class.get_project_manager_creds()
+
 
 class TestPreProvisionedCredentialsV3(TestPreProvisionedCredentials):
 
@@ -480,4 +506,29 @@
             {'username': 'test_admin2', 'project_name': 'test_project12',
              'domain_name': 'domain', 'password': 'p', 'roles': [admin_role]},
             {'username': 'test_admin3', 'project_name': 'test_tenant13',
-             'domain_name': 'domain', 'password': 'p', 'types': ['admin']}]
+             'domain_name': 'domain', 'password': 'p', 'types': ['admin']},
+            {'username': 'test_project_manager1',
+             'project_name': 'test_project14', 'domain_name': 'domain',
+             'password': 'p', 'roles': ['manager']},
+            {'username': 'test_domain_manager1',
+             'domain_name': 'domain', 'password': 'p', 'roles': ['manager']}]
+
+    def test_get_domain_manager_creds(self):
+        test_accounts_class = preprov_creds.PreProvisionedCredentialProvider(
+            **self.fixed_params)
+        d_manager_creds = test_accounts_class.get_domain_manager_creds()
+        self.assertNotIn('test_admin', d_manager_creds.username)
+        self.assertNotIn('test_user', d_manager_creds.username)
+        self.assertIn('test_domain_manager', d_manager_creds.username)
+
+    def test_get_domain_manager_creds_none_available(self):
+        admin_accounts = [x for x in self.test_accounts if 'test_admin'
+                          in x['username']]
+        self.useFixture(fixtures.MockPatch(
+            'tempest.lib.common.preprov_creds.read_accounts_yaml',
+            return_value=admin_accounts))
+        test_accounts_class = preprov_creds.PreProvisionedCredentialProvider(
+            **self.fixed_params)
+        with testtools.ExpectedException(lib_exc.InvalidCredentials):
+            # Get one more
+            test_accounts_class.get_domain_manager_creds()
diff --git a/zuul.d/integrated-gate.yaml b/zuul.d/integrated-gate.yaml
index 1343a7c..8077308 100644
--- a/zuul.d/integrated-gate.yaml
+++ b/zuul.d/integrated-gate.yaml
@@ -358,18 +358,6 @@
         TEMPEST_VOLUME_TYPE: volumev2
 
 - job:
-    name: tempest-centos8-stream-fips
-    parent: devstack-tempest
-    description: |
-      Integration testing for a FIPS enabled Centos 8 system
-    nodeset: devstack-single-node-centos-8-stream
-    vars:
-      tox_envlist: full
-      configure_swap_size: 4096
-      nslookup_target: 'opendev.org'
-      enable_fips: True
-
-- job:
     name: tempest-centos9-stream-fips
     parent: devstack-tempest
     description: |
@@ -503,9 +491,6 @@
               - ^.*/2024.1
               - master
         - tempest-integrated-compute
-        # centos-8-stream is tested from wallaby -> yoga branches
-        - tempest-integrated-compute-centos-8-stream:
-            branches: ^.*/(wallaby|xena|yoga)$
         # Do not run it on ussuri until below issue is fixed
         # https://storyboard.openstack.org/#!/story/2010057
         # and job is broken up to wallaby branch due to the issue
diff --git a/zuul.d/stable-jobs.yaml b/zuul.d/stable-jobs.yaml
index 9d69715..cb1330f 100644
--- a/zuul.d/stable-jobs.yaml
+++ b/zuul.d/stable-jobs.yaml
@@ -149,41 +149,3 @@
       - ^.*/victoria
       - ^.*/wallaby
       - ^.*/xena
-
-- job:
-    name: tempest-integrated-compute-centos-8-stream
-    parent: tempest-integrated-compute
-    # TODO(gmann): Make this job non voting until bug#1957941 if fixed.
-    voting: false
-    nodeset: devstack-single-node-centos-8-stream
-    branches:
-      - ^.*/wallaby
-      - ^.*/xena
-      - ^.*/yoga
-    description: |
-      This job runs integration tests for compute. This is
-      subset of 'tempest-full-py3' job and run Nova, Neutron, Cinder (except backup tests)
-      and Glance related tests. This is meant to be run on Nova gate only.
-      This version of the job also uses CentOS 8 stream.
-    vars:
-      # Required until bug/1949606 is resolved when using libvirt and QEMU
-      # >=5.0.0 with a [libvirt]virt_type of qemu (TCG).
-      configure_swap_size: 4096
-
-- job:
-    name: tempest-full-py3-centos-8-stream
-    parent: tempest-full-py3
-    # TODO(gmann): Make this job non voting until bug#1957941 if fixed.
-    voting: false
-    branches:
-      - ^.*/wallaby
-      - ^.*/xena
-      - ^.*/yoga
-    nodeset: devstack-single-node-centos-8-stream
-    description: |
-      Base integration test with Neutron networking and py36 running
-      on CentOS 8 stream
-    vars:
-      # Required until bug/1949606 is resolved when using libvirt and QEMU
-      # >=5.0.0 with a [libvirt]virt_type of qemu (TCG).
-      configure_swap_size: 4096