Merge "Adding 2 traffic based test cases."
diff --git a/octavia_tempest_plugin/common/cert_utils.py b/octavia_tempest_plugin/common/cert_utils.py
index f99ce88..753da6b 100644
--- a/octavia_tempest_plugin/common/cert_utils.py
+++ b/octavia_tempest_plugin/common/cert_utils.py
@@ -17,6 +17,8 @@
 from cryptography.hazmat.backends import default_backend
 from cryptography.hazmat.primitives.asymmetric import rsa
 from cryptography.hazmat.primitives import hashes
+from cryptography.hazmat.primitives.serialization import NoEncryption
+from cryptography.hazmat.primitives.serialization import pkcs12
 from cryptography import x509
 from cryptography.x509.oid import NameOID
 import OpenSSL
@@ -184,12 +186,29 @@
     :param server_key: A cryptography key (x509) object.
     :returns: A pkcs12 bundle.
     """
-    # TODO(johnsom) Replace with cryptography once it supports creating pkcs12
-    pkcs12 = OpenSSL.crypto.PKCS12()
-    pkcs12.set_privatekey(
-        OpenSSL.crypto.PKey.from_cryptography_key(server_key))
-    pkcs12.set_certificate(OpenSSL.crypto.X509.from_cryptography(server_cert))
-    return pkcs12.export()
+    # Use the PKCS12 serialization function from cryptography if it exists
+    # (>=3.0), otherwise use the pyOpenSSL module.
+    #
+    # The PKCS12 class of the pyOpenSSL module is not compliant with FIPS.
+    # It uses the SHA1 function [0] which is not allowed when generating
+    # digital signatures [1]
+    #
+    # [0] https://github.com/pyca/pyopenssl/blob/
+    #       65ca53a7a06a7c78c1749200a6b3a007e47d3214/src/OpenSSL/
+    #       crypto.py#L2748-L2749
+    # [1] https://nvlpubs.nist.gov/nistpubs/SpecialPublications/
+    #       NIST.SP.800-131Ar1.pdf
+    if hasattr(pkcs12, 'serialize_key_and_certificates'):
+        p12 = pkcs12.serialize_key_and_certificates(
+            b'', server_key, server_cert,
+            cas=None, encryption_algorithm=NoEncryption())
+    else:
+        p12 = OpenSSL.crypto.PKCS12()
+        p12.set_privatekey(
+            OpenSSL.crypto.PKey.from_cryptography_key(server_key))
+        p12.set_certificate(OpenSSL.crypto.X509.from_cryptography(server_cert))
+        p12 = p12.export()
+    return p12
 
 
 def generate_certificate_revocation_list(ca_cert, ca_key, cert_to_revoke):
diff --git a/octavia_tempest_plugin/common/constants.py b/octavia_tempest_plugin/common/constants.py
index 2910d40..4d854f1 100644
--- a/octavia_tempest_plugin/common/constants.py
+++ b/octavia_tempest_plugin/common/constants.py
@@ -45,6 +45,7 @@
 PROVIDER_NAME = 'provider_name'
 PROVISIONING_STATUS = 'provisioning_status'
 REQUEST_ERRORS = 'request_errors'
+TLS_CONTAINER_REF = 'tls_container_ref'
 TOTAL_CONNECTIONS = 'total_connections'
 UPDATED_AT = 'updated_at'
 VIP_ADDRESS = 'vip_address'
diff --git a/octavia_tempest_plugin/services/load_balancer/v2/pool_client.py b/octavia_tempest_plugin/services/load_balancer/v2/pool_client.py
index c19d97c..e93f33c 100644
--- a/octavia_tempest_plugin/services/load_balancer/v2/pool_client.py
+++ b/octavia_tempest_plugin/services/load_balancer/v2/pool_client.py
@@ -34,7 +34,7 @@
                     admin_state_up=Unset, session_persistence=Unset,
                     ca_tls_container_ref=Unset, crl_container_ref=Unset,
                     tls_enabled=Unset, tls_container_ref=Unset,
-                    return_object_only=True):
+                    alpn_protocols=Unset, return_object_only=True):
         """Create a pool.
 
         :param protocol: The protocol for the resource.
@@ -61,6 +61,7 @@
                                   a PKCS12 bundle with the client
                                   authentication certificate and key used
                                   when connecting to pool members over TLS.
+        :param alpn_protocols: A list of ALPN protocols for TLS enabled pools.
         :param return_object_only: If True, the response returns the object
                                    inside the root tag. False returns the full
                                    response from the API.
@@ -180,7 +181,8 @@
                     description=Unset, tags=Unset, admin_state_up=Unset,
                     session_persistence=Unset, ca_tls_container_ref=Unset,
                     crl_container_ref=Unset, tls_enabled=Unset,
-                    tls_container_ref=Unset, return_object_only=True):
+                    tls_container_ref=Unset, alpn_protocols=Unset,
+                    return_object_only=True):
         """Update a pool.
 
         :param pool_id: The pool ID to update.
@@ -205,6 +207,7 @@
                                   a PKCS12 bundle with the client
                                   authentication certificate and key used
                                   when connecting to pool members over TLS.
+        :param alpn_protocols: A list of ALPN protocols for TLS enabled pools.
         :param return_object_only: If True, the response returns the object
                                    inside the root tag. False returns the full
                                    response from the API.
diff --git a/octavia_tempest_plugin/tests/act_stdby_scenario/v2/test_active_standby_iptables.py b/octavia_tempest_plugin/tests/act_stdby_scenario/v2/test_active_standby_iptables.py
index 83f2d50..e7dccb6 100644
--- a/octavia_tempest_plugin/tests/act_stdby_scenario/v2/test_active_standby_iptables.py
+++ b/octavia_tempest_plugin/tests/act_stdby_scenario/v2/test_active_standby_iptables.py
@@ -185,7 +185,8 @@
         ssh_key = cls._get_amphora_ssh_key()
         linux_client = remote_client.RemoteClient(
             amp['lb_network_ip'], CONF.load_balancer.amphora_ssh_user,
-            pkey=ssh_key)
+            pkey=ssh_key,
+            **cls.remote_client_args())
         linux_client.validate_authentication()
 
         # Allow logging from non-init namespaces
@@ -202,7 +203,8 @@
     def _has_vip_traffic(cls, ip_address, log_prefix):
         ssh_key = cls._get_amphora_ssh_key()
         linux_client = remote_client.RemoteClient(
-            ip_address, CONF.load_balancer.amphora_ssh_user, pkey=ssh_key)
+            ip_address, CONF.load_balancer.amphora_ssh_user, pkey=ssh_key,
+            **cls.remote_client_args())
         linux_client.validate_authentication()
 
         try:
diff --git a/octavia_tempest_plugin/tests/api/v2/test_listener.py b/octavia_tempest_plugin/tests/api/v2/test_listener.py
index 152f6ff..1c2fa75 100644
--- a/octavia_tempest_plugin/tests/api/v2/test_listener.py
+++ b/octavia_tempest_plugin/tests/api/v2/test_listener.py
@@ -540,6 +540,11 @@
                 const.OPERATING_STATUS, const.ONLINE,
                 CONF.load_balancer.build_interval,
                 CONF.load_balancer.build_timeout)
+            listener3 = waiters.wait_for_status(
+                self.mem_listener_client.show_listener, listener3[const.ID],
+                const.OPERATING_STATUS, const.OFFLINE,
+                CONF.load_balancer.build_interval,
+                CONF.load_balancer.build_timeout)
 
         # Test that a different users cannot see the lb_member listeners.
         expected_allowed = []
@@ -999,35 +1004,24 @@
                 self.api_version, '2.12'):
             self.assertEqual(self.allowed_cidrs, listener[const.ALLOWED_CIDRS])
 
-        # Test that a user, without the load balancer member role, cannot
-        # use this command
+        # Test that a user without the loadbalancer role cannot
+        # update a listener.
+        expected_allowed = []
+        if CONF.load_balancer.RBAC_test_type == const.OWNERADMIN:
+            expected_allowed = ['os_admin', 'os_roles_lb_admin',
+                                'os_roles_lb_member']
+        if CONF.load_balancer.RBAC_test_type == const.KEYSTONE_DEFAULT_ROLES:
+            expected_allowed = ['os_system_admin', 'os_roles_lb_member']
         if CONF.load_balancer.RBAC_test_type == const.ADVANCED:
-            self.assertRaises(
-                exceptions.Forbidden,
-                self.listener_client.update_listener,
-                listener[const.ID], admin_state_up=True)
-
-        # Assert we didn't go into PENDING_*
-        listener_check = self.mem_listener_client.show_listener(
-            listener[const.ID])
-        self.assertEqual(const.ACTIVE,
-                         listener_check[const.PROVISIONING_STATUS])
-        self.assertFalse(listener_check[const.ADMIN_STATE_UP])
-
-        # Test that a user, without the load balancer member role, cannot
-        # update this listener
-        if not CONF.load_balancer.RBAC_test_type == const.NONE:
-            member2_client = self.member2_listener_client
-            self.assertRaises(exceptions.Forbidden,
-                              member2_client.update_listener,
-                              listener[const.ID], admin_state_up=True)
-
-        # Assert we didn't go into PENDING_*
-        listener_check = self.mem_listener_client.show_listener(
-            listener[const.ID])
-        self.assertEqual(const.ACTIVE,
-                         listener_check[const.PROVISIONING_STATUS])
-        self.assertFalse(listener_check[const.ADMIN_STATE_UP])
+            expected_allowed = ['os_system_admin', 'os_roles_lb_admin',
+                                'os_roles_lb_member']
+        if expected_allowed:
+            self.check_update_RBAC_enforcement(
+                'ListenerClient', 'update_listener',
+                expected_allowed,
+                status_method=self.mem_listener_client.show_listener,
+                obj_id=listener[const.ID], listener_id=listener[const.ID],
+                admin_state_up=True)
 
         new_name = data_utils.rand_name("lb_member_listener1-UPDATED")
         new_description = data_utils.arbitrary_string(size=255,
@@ -1183,21 +1177,23 @@
             CONF.load_balancer.build_interval,
             CONF.load_balancer.build_timeout)
 
-        # Test that a user without the load balancer role cannot
-        # delete this listener
+        # Test that a user without the loadbalancer role cannot
+        # delete a listener.
+        expected_allowed = []
+        if CONF.load_balancer.RBAC_test_type == const.OWNERADMIN:
+            expected_allowed = ['os_admin', 'os_roles_lb_admin',
+                                'os_roles_lb_member']
+        if CONF.load_balancer.RBAC_test_type == const.KEYSTONE_DEFAULT_ROLES:
+            expected_allowed = ['os_system_admin', 'os_roles_lb_member']
         if CONF.load_balancer.RBAC_test_type == const.ADVANCED:
-            self.assertRaises(
-                exceptions.Forbidden,
-                self.listener_client.delete_listener,
-                listener[const.ID])
-
-        # Test that a different user, with the load balancer member role
-        # cannot delete this listener
-        if not CONF.load_balancer.RBAC_test_type == const.NONE:
-            member2_client = self.member2_listener_client
-            self.assertRaises(exceptions.Forbidden,
-                              member2_client.delete_listener,
-                              listener[const.ID])
+            expected_allowed = ['os_system_admin', 'os_roles_lb_admin',
+                                'os_roles_lb_member']
+        if expected_allowed:
+            self.check_update_RBAC_enforcement(
+                'ListenerClient', 'delete_listener',
+                expected_allowed,
+                status_method=self.mem_listener_client.show_listener,
+                obj_id=listener[const.ID], listener_id=listener[const.ID])
 
         self.mem_listener_client.delete_listener(listener[const.ID])
 
diff --git a/octavia_tempest_plugin/tests/api/v2/test_member.py b/octavia_tempest_plugin/tests/api/v2/test_member.py
index aa7cf25..fee2893 100644
--- a/octavia_tempest_plugin/tests/api/v2/test_member.py
+++ b/octavia_tempest_plugin/tests/api/v2/test_member.py
@@ -141,6 +141,8 @@
         cls.listener_pool_cache[listener_pool_key] = pool[const.ID]
         return pool[const.ID]
 
+
+class MemberAPITest1(MemberAPITest):
     @decorators.idempotent_id('0684575a-0970-4fa8-8006-10c2b39c5f2b')
     def test_ipv4_HTTP_LC_alt_monitor_member_create(self):
         pool_id = self._listener_pool_create(
@@ -1375,6 +1377,8 @@
             self.assertTrue(not any(["" in member[const.TAGS]
                                      for member in list_of_members]))
 
+
+class MemberAPITest2(MemberAPITest):
     @decorators.idempotent_id('2674b363-7922-494a-b121-cf415dbbb716')
     def test_HTTP_LC_alt_monitor_member_show(self):
         pool_id = self._listener_pool_create(
diff --git a/octavia_tempest_plugin/tests/barbican_scenario/v2/test_tls_barbican.py b/octavia_tempest_plugin/tests/barbican_scenario/v2/test_tls_barbican.py
index 3f41892..3fdada4 100644
--- a/octavia_tempest_plugin/tests/barbican_scenario/v2/test_tls_barbican.py
+++ b/octavia_tempest_plugin/tests/barbican_scenario/v2/test_tls_barbican.py
@@ -337,7 +337,10 @@
                 return False
             return True
 
-        context = SSL.Context(SSL.SSLv23_METHOD)
+        try:
+            context = SSL.Context(SSL.TLS_METHOD)
+        except AttributeError:
+            context = SSL.Context(SSL.SSLv23_METHOD)
         context.set_verify(SSL.VERIFY_PEER | SSL.VERIFY_FAIL_IF_NO_PEER_CERT,
                            _verify_cb)
         ca_store = context.get_cert_store()
@@ -473,7 +476,10 @@
             return True
 
         # Test that the default certificate is used with no SNI host request
-        context = SSL.Context(SSL.SSLv23_METHOD)
+        try:
+            context = SSL.Context(SSL.TLS_METHOD)
+        except AttributeError:
+            context = SSL.Context(SSL.SSLv23_METHOD)
         context.set_verify(SSL.VERIFY_PEER | SSL.VERIFY_FAIL_IF_NO_PEER_CERT,
                            _verify_server_cb)
         ca_store = context.get_cert_store()
@@ -485,7 +491,10 @@
         sock.do_handshake()
 
         # Test that the default certificate is used with bogus SNI host request
-        context = SSL.Context(SSL.TLSv1_2_METHOD)
+        try:
+            context = SSL.Context(SSL.TLS_METHOD)
+        except AttributeError:
+            context = SSL.Context(SSL.TLSv1_2_METHOD)
         context.set_verify(SSL.VERIFY_PEER | SSL.VERIFY_FAIL_IF_NO_PEER_CERT,
                            _verify_server_cb)
         ca_store = context.get_cert_store()
@@ -498,7 +507,10 @@
         sock.do_handshake()
 
         # Test that the SNI1 certificate is used when SNI1 host is specified
-        context = SSL.Context(SSL.TLSv1_2_METHOD)
+        try:
+            context = SSL.Context(SSL.TLS_METHOD)
+        except AttributeError:
+            context = SSL.Context(SSL.TLSv1_2_METHOD)
         context.set_verify(SSL.VERIFY_PEER | SSL.VERIFY_FAIL_IF_NO_PEER_CERT,
                            _verify_SNI1_cb)
         ca_store = context.get_cert_store()
@@ -512,7 +524,10 @@
         sock.do_handshake()
 
         # Test that the SNI2 certificate is used when SNI2 host is specified
-        context = SSL.Context(SSL.SSLv23_METHOD)
+        try:
+            context = SSL.Context(SSL.TLS_METHOD)
+        except AttributeError:
+            context = SSL.Context(SSL.SSLv23_METHOD)
         context.set_verify(SSL.VERIFY_PEER | SSL.VERIFY_FAIL_IF_NO_PEER_CERT,
                            _verify_SNI2_cb)
         ca_store = context.get_cert_store()
@@ -634,7 +649,10 @@
             return True
 
         # Test that the default certificate is used with no SNI host request
-        context = SSL.Context(SSL.SSLv23_METHOD)
+        try:
+            context = SSL.Context(SSL.TLS_METHOD)
+        except AttributeError:
+            context = SSL.Context(SSL.SSLv23_METHOD)
         context.set_verify(SSL.VERIFY_PEER | SSL.VERIFY_FAIL_IF_NO_PEER_CERT,
                            _verify_server_cb)
         ca_store = context.get_cert_store()
@@ -646,7 +664,10 @@
         sock.do_handshake()
 
         # Test that the SNI1 certificate is used when SNI1 host is specified
-        context = SSL.Context(SSL.TLSv1_2_METHOD)
+        try:
+            context = SSL.Context(SSL.TLS_METHOD)
+        except AttributeError:
+            context = SSL.Context(SSL.TLSv1_2_METHOD)
         context.set_verify(SSL.VERIFY_PEER | SSL.VERIFY_FAIL_IF_NO_PEER_CERT,
                            _verify_SNI1_cb)
         ca_store = context.get_cert_store()
@@ -660,7 +681,10 @@
         sock.do_handshake()
 
         # Test that the default certificate is used when SNI2 host is specified
-        context = SSL.Context(SSL.SSLv23_METHOD)
+        try:
+            context = SSL.Context(SSL.TLS_METHOD)
+        except AttributeError:
+            context = SSL.Context(SSL.SSLv23_METHOD)
         context.set_verify(SSL.VERIFY_PEER | SSL.VERIFY_FAIL_IF_NO_PEER_CERT,
                            _verify_server_cb)
         ca_store = context.get_cert_store()
@@ -675,7 +699,10 @@
 
         # Test that the SNI2 certificate is used with no SNI host request
         # on listener 2, SNI2 is the default cert for listener 2
-        context = SSL.Context(SSL.SSLv23_METHOD)
+        try:
+            context = SSL.Context(SSL.TLS_METHOD)
+        except AttributeError:
+            context = SSL.Context(SSL.SSLv23_METHOD)
         context.set_verify(SSL.VERIFY_PEER | SSL.VERIFY_FAIL_IF_NO_PEER_CERT,
                            _verify_SNI2_cb)
         ca_store = context.get_cert_store()
@@ -688,7 +715,10 @@
 
         # Test that the SNI2 certificate is used with listener 1 host request
         # on listener 2, SNI2 is the default cert for listener 2
-        context = SSL.Context(SSL.SSLv23_METHOD)
+        try:
+            context = SSL.Context(SSL.TLS_METHOD)
+        except AttributeError:
+            context = SSL.Context(SSL.SSLv23_METHOD)
         context.set_verify(SSL.VERIFY_PEER | SSL.VERIFY_FAIL_IF_NO_PEER_CERT,
                            _verify_SNI2_cb)
         ca_store = context.get_cert_store()
@@ -703,7 +733,10 @@
 
         # Test that the SNI2 certificate is used with SNI1 host request
         # on listener 2, SNI2 is the default cert for listener 2
-        context = SSL.Context(SSL.SSLv23_METHOD)
+        try:
+            context = SSL.Context(SSL.TLS_METHOD)
+        except AttributeError:
+            context = SSL.Context(SSL.SSLv23_METHOD)
         context.set_verify(SSL.VERIFY_PEER | SSL.VERIFY_FAIL_IF_NO_PEER_CERT,
                            _verify_SNI2_cb)
         ca_store = context.get_cert_store()
@@ -1362,3 +1395,147 @@
 
         self.check_members_balanced(self.lb_vip_address, protocol=const.HTTP,
                                     protocol_port=84, traffic_member_count=1)
+
+    @decorators.idempotent_id('11b67c96-a553-4b47-9fc6-4c3d7a2a10ce')
+    def test_pool_reencryption_client_authentication(self):
+        if not self.mem_listener_client.is_version_supported(
+                self.api_version, '2.8'):
+            raise self.skipException('Pool re-encryption is only available on '
+                                     'Octavia API version 2.8 or newer.')
+        pool_name = data_utils.rand_name("lb_member_pool1-tls-client-auth")
+        pool_kwargs = {
+            const.NAME: pool_name,
+            const.PROTOCOL: const.HTTP,
+            const.LB_ALGORITHM: self.lb_algorithm,
+            const.LOADBALANCER_ID: self.lb_id,
+            const.TLS_ENABLED: True
+        }
+        # Specify an http/1.x alpn to work around HTTP healthchecks
+        # on older haproxy versions when alpn includes h2
+        if self.mem_listener_client.is_version_supported(
+                self.api_version, '2.24'):
+            pool_kwargs[const.ALPN_PROTOCOLS] = ['http/1.0', 'http/1.1']
+
+        pool = self.mem_pool_client.create_pool(**pool_kwargs)
+        pool_id = pool[const.ID]
+
+        waiters.wait_for_status(self.mem_lb_client.show_loadbalancer,
+                                self.lb_id, const.PROVISIONING_STATUS,
+                                const.ACTIVE,
+                                CONF.load_balancer.build_interval,
+                                CONF.load_balancer.build_timeout)
+
+        hm_name = data_utils.rand_name("lb_member_hm1-tls-client-auth")
+        hm_kwargs = {
+            const.POOL_ID: pool_id,
+            const.NAME: hm_name,
+            const.TYPE: const.HEALTH_MONITOR_HTTPS,
+            const.HTTP_METHOD: const.GET,
+            const.URL_PATH: '/',
+            const.EXPECTED_CODES: '200',
+            const.DELAY: 1,
+            const.TIMEOUT: 1,
+            const.MAX_RETRIES: 1,
+            const.MAX_RETRIES_DOWN: 1,
+            const.ADMIN_STATE_UP: True,
+        }
+        hm = self.mem_healthmonitor_client.create_healthmonitor(**hm_kwargs)
+        self.addCleanup(
+            self.mem_healthmonitor_client.cleanup_healthmonitor,
+            hm[const.ID],
+            lb_client=self.mem_lb_client, lb_id=self.lb_id)
+
+        waiters.wait_for_status(
+            self.mem_lb_client.show_loadbalancer, self.lb_id,
+            const.PROVISIONING_STATUS, const.ACTIVE,
+            CONF.load_balancer.build_interval,
+            CONF.load_balancer.build_timeout)
+        hm = waiters.wait_for_status(
+            self.mem_healthmonitor_client.show_healthmonitor,
+            hm[const.ID], const.PROVISIONING_STATUS,
+            const.ACTIVE,
+            CONF.load_balancer.build_interval,
+            CONF.load_balancer.build_timeout)
+
+        # Set up Member 1 for Webserver 1
+        member1_name = data_utils.rand_name(
+            "lb_member_member1-tls-client-auth")
+        member1_kwargs = {
+            const.POOL_ID: pool_id,
+            const.NAME: member1_name,
+            const.ADMIN_STATE_UP: True,
+            const.ADDRESS: self.webserver1_ip,
+            const.PROTOCOL_PORT: 9443,
+        }
+        if self.lb_member_1_subnet:
+            member1_kwargs[const.SUBNET_ID] = self.lb_member_1_subnet[const.ID]
+
+        self.mem_member_client.create_member(**member1_kwargs)
+        waiters.wait_for_status(
+            self.mem_lb_client.show_loadbalancer, self.lb_id,
+            const.PROVISIONING_STATUS, const.ACTIVE,
+            CONF.load_balancer.check_interval,
+            CONF.load_balancer.check_timeout)
+
+        # Set up Member 2 for Webserver 2
+        member2_name = data_utils.rand_name(
+            "lb_member_member2-tls-client-auth")
+        member2_kwargs = {
+            const.POOL_ID: pool_id,
+            const.NAME: member2_name,
+            const.ADMIN_STATE_UP: True,
+            const.ADDRESS: self.webserver2_ip,
+            const.PROTOCOL_PORT: 9443,
+        }
+        if self.lb_member_2_subnet:
+            member2_kwargs[const.SUBNET_ID] = self.lb_member_2_subnet[const.ID]
+
+        self.mem_member_client.create_member(**member2_kwargs)
+        waiters.wait_for_status(
+            self.mem_lb_client.show_loadbalancer, self.lb_id,
+            const.PROVISIONING_STATUS, const.ACTIVE,
+            CONF.load_balancer.check_interval,
+            CONF.load_balancer.check_timeout)
+
+        listener_name = data_utils.rand_name(
+            "lb_member_listener1-tls-client-auth")
+        listener_kwargs = {
+            const.NAME: listener_name,
+            const.PROTOCOL: const.HTTP,
+            const.PROTOCOL_PORT: '85',
+            const.LOADBALANCER_ID: self.lb_id,
+            const.DEFAULT_POOL_ID: pool_id,
+        }
+        listener = self.mem_listener_client.create_listener(**listener_kwargs)
+        self.listener_id = listener[const.ID]
+
+        waiters.wait_for_status(self.mem_lb_client.show_loadbalancer,
+                                self.lb_id, const.PROVISIONING_STATUS,
+                                const.ACTIVE,
+                                CONF.load_balancer.build_interval,
+                                CONF.load_balancer.build_timeout)
+
+        # Test that there are no members without a client certificate
+        url = 'http://{0}:85'.format(self.lb_vip_address)
+        self.validate_URL_response(url, expected_status_code=503)
+
+        # Test with client certificates
+        pool_update_kwargs = {
+            const.TLS_CONTAINER_REF: self.pool_client_ref
+        }
+
+        self.mem_pool_client.update_pool(pool_id, **pool_update_kwargs)
+
+        waiters.wait_for_status(
+            self.mem_lb_client.show_loadbalancer, self.lb_id,
+            const.PROVISIONING_STATUS, const.ACTIVE,
+            CONF.load_balancer.check_interval,
+            CONF.load_balancer.check_timeout)
+        waiters.wait_for_status(
+            self.mem_pool_client.show_pool, pool_id,
+            const.PROVISIONING_STATUS, const.ACTIVE,
+            CONF.load_balancer.check_interval,
+            CONF.load_balancer.check_timeout)
+
+        self.check_members_balanced(self.lb_vip_address, protocol=const.HTTP,
+                                    protocol_port=85)
diff --git a/octavia_tempest_plugin/tests/scenario/v2/test_ipv6_traffic_ops.py b/octavia_tempest_plugin/tests/scenario/v2/test_ipv6_traffic_ops.py
index 4efa241..69c1f2b 100644
--- a/octavia_tempest_plugin/tests/scenario/v2/test_ipv6_traffic_ops.py
+++ b/octavia_tempest_plugin/tests/scenario/v2/test_ipv6_traffic_ops.py
@@ -431,8 +431,9 @@
             const.ADDRESS: self.webserver1_ipv6,
             const.PROTOCOL_PORT: 80,
         }
-        if self.lb_member_1_subnet:
-            member1_kwargs[const.SUBNET_ID] = self.lb_member_1_subnet[const.ID]
+        if self.lb_member_1_ipv6_subnet:
+            member1_kwargs[const.SUBNET_ID] = (
+                self.lb_member_1_ipv6_subnet[const.ID])
 
         member1 = self.mem_member_client.create_member(
             **member1_kwargs)
@@ -455,8 +456,9 @@
             const.ADDRESS: self.webserver2_ipv6,
             const.PROTOCOL_PORT: 80,
         }
-        if self.lb_member_2_subnet:
-            member2_kwargs[const.SUBNET_ID] = self.lb_member_2_subnet[const.ID]
+        if self.lb_member_2_ipv6_subnet:
+            member2_kwargs[const.SUBNET_ID] = (
+                self.lb_member_2_ipv6_subnet[const.ID])
 
         member2 = self.mem_member_client.create_member(**member2_kwargs)
         self.addCleanup(
diff --git a/octavia_tempest_plugin/tests/test_base.py b/octavia_tempest_plugin/tests/test_base.py
index e7a344d..5592571 100644
--- a/octavia_tempest_plugin/tests/test_base.py
+++ b/octavia_tempest_plugin/tests/test_base.py
@@ -15,12 +15,14 @@
 import ipaddress
 import os
 import random
+import re
 import shlex
 import string
 import subprocess
 import tempfile
 
 from cryptography.hazmat.primitives import serialization
+from oslo_config import cfg
 from oslo_log import log as logging
 from oslo_utils import uuidutils
 from tempest import config
@@ -582,6 +584,18 @@
 
 class LoadBalancerBaseTestWithCompute(LoadBalancerBaseTest):
     @classmethod
+    def remote_client_args(cls):
+        # In case we're using octavia-tempest-plugin with old tempest releases
+        # (for instance on stable/train) that don't support ssh_key_type, catch
+        # the exception and don't pass any argument
+        args = {}
+        try:
+            args['ssh_key_type'] = CONF.validation.ssh_key_type
+        except cfg.NoSuchOptError:
+            pass
+        return args
+
+    @classmethod
     def resource_setup(cls):
         super(LoadBalancerBaseTestWithCompute, cls).resource_setup()
         # If validation is disabled in this cloud, we won't be able to
@@ -653,6 +667,20 @@
                 cls.lb_mem_SGr_client.delete_security_group_rule,
                 cls.lb_mem_SGr_client.show_security_group_rule,
                 SGr['id'])
+            # Create a security group rule to allow 9443 (test webservers)
+            # Used in the pool backend encryption client authentication tests
+            SGr = cls.lb_mem_SGr_client.create_security_group_rule(
+                direction='ingress',
+                security_group_id=cls.lb_member_sec_group['id'],
+                protocol='tcp',
+                ethertype='IPv4',
+                port_range_min=9443,
+                port_range_max=9443)['security_group_rule']
+            cls.addClassResourceCleanup(
+                waiters.wait_for_not_found,
+                cls.lb_mem_SGr_client.delete_security_group_rule,
+                cls.lb_mem_SGr_client.show_security_group_rule,
+                SGr['id'])
             # Create a security group rule to allow UDP 9999 (test webservers)
             # Port 9999 is used to illustrate health monitor ERRORs on closed
             # ports.
@@ -722,6 +750,20 @@
                     cls.lb_mem_SGr_client.delete_security_group_rule,
                     cls.lb_mem_SGr_client.show_security_group_rule,
                     SGr['id'])
+                # Create a security group rule to allow 9443 (test webservers)
+                # Used in the pool encryption client authentication tests
+                SGr = cls.lb_mem_SGr_client.create_security_group_rule(
+                    direction='ingress',
+                    security_group_id=cls.lb_member_sec_group['id'],
+                    protocol='tcp',
+                    ethertype='IPv6',
+                    port_range_min=9443,
+                    port_range_max=9443)['security_group_rule']
+                cls.addClassResourceCleanup(
+                    waiters.wait_for_not_found,
+                    cls.lb_mem_SGr_client.delete_security_group_rule,
+                    cls.lb_mem_SGr_client.show_security_group_rule,
+                    SGr['id'])
                 # Create a security group rule to allow 22 (ssh)
                 SGr = cls.lb_mem_SGr_client.create_security_group_rule(
                     direction='ingress',
@@ -976,25 +1018,57 @@
         return webserver_details
 
     @classmethod
+    def _get_openssh_version(cls):
+        p = subprocess.Popen(["ssh", "-V"],
+                             stdout=subprocess.PIPE,
+                             stderr=subprocess.PIPE)
+        output = p.communicate()[1]
+
+        try:
+            m = re.match(r"OpenSSH_(\d+)\.(\d+)", output.decode('utf-8'))
+            version_maj = int(m.group(1))
+            version_min = int(m.group(2))
+            return version_maj, version_min
+        except Exception:
+            return None, None
+
+    @classmethod
+    def _need_scp_protocol(cls):
+        # When using scp >= 8.7, force the use of the SCP protocol,
+        # the new default (SFTP protocol) doesn't work with
+        # cirros VMs.
+        ssh_version = cls._get_openssh_version()
+        LOG.debug("ssh_version = {}".format(ssh_version))
+        return (ssh_version[0] > 8 or
+                (ssh_version[0] == 8 and ssh_version[1] >= 7))
+
+    @classmethod
     def _install_start_webserver(cls, ip_address, ssh_key, start_id,
                                  revoke_cert=False):
         local_file = CONF.load_balancer.test_server_path
 
         linux_client = remote_client.RemoteClient(
-            ip_address, CONF.validation.image_ssh_user, pkey=ssh_key)
+            ip_address, CONF.validation.image_ssh_user, pkey=ssh_key,
+            **cls.remote_client_args())
         linux_client.validate_authentication()
 
         with tempfile.NamedTemporaryFile() as key:
             key.write(ssh_key.encode('utf-8'))
             key.flush()
+            ssh_extra_args = (
+                "-o PubkeyAcceptedKeyTypes=+ssh-rsa")
+            if cls._need_scp_protocol():
+                ssh_extra_args += " -O"
             cmd = ("scp -v -o UserKnownHostsFile=/dev/null "
+                   "{7} "
                    "-o StrictHostKeyChecking=no "
                    "-o ConnectTimeout={0} -o ConnectionAttempts={1} "
                    "-i {2} {3} {4}@{5}:{6}").format(
                 CONF.load_balancer.scp_connection_timeout,
                 CONF.load_balancer.scp_connection_attempts,
                 key.name, local_file, CONF.validation.image_ssh_user,
-                ip_address, const.TEST_SERVER_BINARY)
+                ip_address, const.TEST_SERVER_BINARY,
+                ssh_extra_args)
             args = shlex.split(cmd)
             subprocess_args = {'stdout': subprocess.PIPE,
                                'stderr': subprocess.STDOUT,
@@ -1036,7 +1110,8 @@
     def _enable_ipv6_nic_webserver(cls, ip_address, ssh_key,
                                    ipv6_address, ipv6_prefix):
         linux_client = remote_client.RemoteClient(
-            ip_address, CONF.validation.image_ssh_user, pkey=ssh_key)
+            ip_address, CONF.validation.image_ssh_user, pkey=ssh_key,
+            **cls.remote_client_args())
         linux_client.validate_authentication()
 
         linux_client.exec_command('sudo ip address add {0}/{1} dev '
@@ -1151,14 +1226,20 @@
             subprocess_args = {'stdout': subprocess.PIPE,
                                'stderr': subprocess.STDOUT,
                                'cwd': None}
+            ssh_extra_args = (
+                "-o PubkeyAcceptedKeyTypes=+ssh-rsa")
+            if cls._need_scp_protocol():
+                ssh_extra_args += " -O"
             cmd = ("scp -v -o UserKnownHostsFile=/dev/null "
+                   "{9} "
                    "-o StrictHostKeyChecking=no "
                    "-o ConnectTimeout={0} -o ConnectionAttempts={1} "
                    "-i {2} {3} {4} {5} {6}@{7}:{8}").format(
                 CONF.load_balancer.scp_connection_timeout,
                 CONF.load_balancer.scp_connection_attempts,
                 ssh_key.name, cert_filename, key_filename, client_ca_filename,
-                CONF.validation.image_ssh_user, ip_address, const.DEV_SHM_PATH)
+                CONF.validation.image_ssh_user, ip_address, const.DEV_SHM_PATH,
+                ssh_extra_args)
             args = shlex.split(cmd)
             proc = subprocess.Popen(args, **subprocess_args)
             stdout, stderr = proc.communicate()
diff --git a/releasenotes/notes/add-pool-client-auth-scenario-02abca554e60d3da.yaml b/releasenotes/notes/add-pool-client-auth-scenario-02abca554e60d3da.yaml
new file mode 100644
index 0000000..b14517f
--- /dev/null
+++ b/releasenotes/notes/add-pool-client-auth-scenario-02abca554e60d3da.yaml
@@ -0,0 +1,4 @@
+---
+features:
+  - |
+    Added scenario test coverage for pool client authentication.
diff --git a/releasenotes/notes/pools-service-client-alpn-support-7de3bcd3c901ff1a.yaml b/releasenotes/notes/pools-service-client-alpn-support-7de3bcd3c901ff1a.yaml
new file mode 100644
index 0000000..e7554e2
--- /dev/null
+++ b/releasenotes/notes/pools-service-client-alpn-support-7de3bcd3c901ff1a.yaml
@@ -0,0 +1,4 @@
+---
+features:
+  - |
+    Added support for ALPN selection in the pools service client.
diff --git a/setup.cfg b/setup.cfg
index d7d3196..954fe98 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,12 +1,12 @@
 [metadata]
 name = octavia-tempest-plugin
 summary = Tempest plugin for Octavia
-description-file =
+description_file =
     README.rst
 author = OpenStack
-author-email = openstack-discuss@lists.openstack.org
-home-page = https://docs.openstack.org/octavia-tempest-plugin/latest/
-python-requires = >=3.6
+author_email = openstack-discuss@lists.openstack.org
+home_page = https://docs.openstack.org/octavia-tempest-plugin/latest/
+python_requires = >=3.6
 classifier =
     Environment :: OpenStack
     Intended Audience :: Information Technology
@@ -22,7 +22,7 @@
     Programming Language :: Python :: 3.8
 
 [global]
-setup-hooks =
+setup_hooks =
     pbr.hooks.setup_hook
 
 [files]
diff --git a/tox.ini b/tox.ini
index 86d81b1..ed7a3cd 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,5 +1,5 @@
 [tox]
-minversion = 3.1
+minversion = 3.18.0
 envlist = pep8
 skipsdist = True
 ignore_basepython_conflict = True
@@ -32,7 +32,7 @@
 setenv =
   {[testenv]setenv}
   PYTHON=coverage run --source octavia_tempest_plugin --parallel-mode
-whitelist_externals =
+allowlist_externals =
   find
 commands =
   find octavia_tempest_plugin -type f -name "*.pyc" -delete
@@ -45,17 +45,16 @@
 [testenv:docs]
 deps =
     -c{env:UPPER_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master}
-    -r{toxinidir}/requirements.txt
     -r{toxinidir}/test-requirements.txt
     -r{toxinidir}/doc/requirements.txt
-whitelist_externals = rm
+allowlist_externals = rm
 commands =
   rm -rf doc/build
   sphinx-build -W -b html doc/source doc/build/html
 
 [testenv:pdf-docs]
 deps = {[testenv:docs]deps}
-whitelist_externals =
+allowlist_externals =
   make
   rm
 commands =
@@ -105,7 +104,7 @@
   ./octavia_tempest_plugin/hacking
 
 [testenv:genconfig]
-whitelist_externals = mkdir
+allowlist_externals = mkdir
 commands =
          mkdir -p etc
          oslo-config-generator --output-file etc/octavia.tempest.conf.sample \
@@ -114,6 +113,6 @@
 [testenv:requirements]
 deps =
   -egit+https://opendev.org/openstack/requirements#egg=openstack-requirements
-whitelist_externals = sh
+allowlist_externals = sh
 commands =
     sh -c '{envdir}/src/openstack-requirements/playbooks/files/project-requirements-change.py --req {envdir}/src/openstack-requirements --local {toxinidir} master'
diff --git a/zuul.d/jobs.yaml b/zuul.d/jobs.yaml
index eb5a593..f4dd9db 100644
--- a/zuul.d/jobs.yaml
+++ b/zuul.d/jobs.yaml
@@ -39,6 +39,26 @@
           - controller
 
 - nodeset:
+    name: octavia-single-node-centos-8-stream
+    nodes:
+      - name: controller
+        label: nested-virt-centos-8-stream
+    groups:
+      - name: tempest
+        nodes:
+          - controller
+
+- nodeset:
+    name: octavia-single-node-centos-9-stream
+    nodes:
+      - name: controller
+        label: nested-virt-centos-9-stream
+    groups:
+      - name: tempest
+        nodes:
+          - controller
+
+- nodeset:
     name: octavia-two-node
     nodes:
       - name: controller
@@ -510,28 +530,21 @@
         USE_PYTHON3: False
 
 - job:
+    name: octavia-v2-dsvm-noop-api-stable-yoga
+    parent: octavia-v2-dsvm-noop-api
+    override-checkout: stable/yoga
+
+- job:
+    name: octavia-v2-dsvm-noop-api-stable-xena
+    parent: octavia-v2-dsvm-noop-api
+    override-checkout: stable/xena
+
+- job:
     name: octavia-v2-dsvm-noop-api-stable-wallaby
     parent: octavia-v2-dsvm-noop-api
     override-checkout: stable/wallaby
 
 - job:
-    name: octavia-v2-dsvm-noop-api-stable-victoria
-    parent: octavia-v2-dsvm-noop-api
-    override-checkout: stable/victoria
-
-- job:
-    name: octavia-v2-dsvm-noop-api-stable-ussuri
-    parent: octavia-v2-dsvm-noop-api
-    nodeset: octavia-single-node-ubuntu-bionic
-    override-checkout: stable/ussuri
-
-- job:
-    name: octavia-v2-dsvm-noop-api-stable-train
-    parent: octavia-v2-dsvm-noop-api
-    nodeset: octavia-single-node-ubuntu-bionic
-    override-checkout: stable/train
-
-- job:
     name: octavia-v2-dsvm-scenario
     parent: octavia-dsvm-live-base
     vars:
@@ -586,25 +599,19 @@
         override-checkout: 2.30.0
 
 - job:
+    name: octavia-v2-dsvm-scenario-stable-yoga
+    parent: octavia-v2-dsvm-scenario
+    override-checkout: stable/yoga
+
+- job:
+    name: octavia-v2-dsvm-scenario-stable-xena
+    parent: octavia-v2-dsvm-scenario
+    override-checkout: stable/xena
+
+- job:
     name: octavia-v2-dsvm-scenario-stable-wallaby
     parent: octavia-v2-dsvm-scenario
     override-checkout: stable/wallaby
-- job:
-    name: octavia-v2-dsvm-scenario-stable-victoria
-    parent: octavia-v2-dsvm-scenario
-    override-checkout: stable/victoria
-
-- job:
-    name: octavia-v2-dsvm-scenario-stable-ussuri
-    parent: octavia-v2-dsvm-scenario
-    nodeset: octavia-single-node-ubuntu-bionic
-    override-checkout: stable/ussuri
-
-- job:
-    name: octavia-v2-dsvm-scenario-stable-train
-    parent: octavia-v2-dsvm-scenario
-    nodeset: octavia-single-node-ubuntu-bionic
-    override-checkout: stable/train
 
 # Legacy jobs for the transition to the act-stdby two node jobs
 - job:
@@ -689,6 +696,33 @@
         OCTAVIA_AMP_IMAGE_SIZE: 3
 
 - job:
+    name: octavia-v2-dsvm-scenario-centos-8-stream
+    parent: octavia-v2-dsvm-scenario
+    nodeset: octavia-single-node-centos-8-stream
+    vars:
+      devstack_localrc:
+        OCTAVIA_AMP_BASE_OS: centos
+        OCTAVIA_AMP_DISTRIBUTION_RELEASE_ID: 8-stream
+        OCTAVIA_AMP_IMAGE_SIZE: 3
+
+- job:
+    name: octavia-v2-dsvm-scenario-centos-9-stream
+    parent: octavia-v2-dsvm-scenario
+    nodeset: octavia-single-node-centos-9-stream
+    vars:
+      devstack_localrc:
+        OCTAVIA_AMP_BASE_OS: centos
+        OCTAVIA_AMP_DISTRIBUTION_RELEASE_ID: 9-stream
+        OCTAVIA_AMP_IMAGE_SIZE: 3
+        OCTAVIA_SSH_KEY_TYPE: ecdsa
+        OCTAVIA_SSH_KEY_BITS: 256
+      devstack_local_conf:
+        test-config:
+          "$TEMPEST_CONFIG":
+            validation:
+              ssh_key_type: ecdsa
+
+- job:
     name: octavia-v2-dsvm-scenario-ubuntu-focal
     parent: octavia-v2-dsvm-scenario
     vars:
@@ -722,6 +756,16 @@
       - ^octavia_tempest_plugin/tests/(?!barbican_scenario/|\w+\.py).*
 
 - job:
+    name: octavia-v2-dsvm-tls-barbican-stable-yoga
+    parent: octavia-v2-dsvm-tls-barbican
+    override-checkout: stable/yoga
+
+- job:
+    name: octavia-v2-dsvm-tls-barbican-stable-xena
+    parent: octavia-v2-dsvm-tls-barbican
+    override-checkout: stable/xena
+
+- job:
     name: octavia-v2-dsvm-tls-barbican-stable-wallaby
     parent: octavia-v2-dsvm-tls-barbican
     override-checkout: stable/wallaby
@@ -744,18 +788,6 @@
     override-checkout: stable/train
 
 - job:
-    name: octavia-v2-dsvm-tls-barbican-stable-rocky
-    parent: octavia-v2-dsvm-tls-barbican
-    nodeset: openstack-single-node-xenial
-    override-checkout: stable/rocky
-
-- job:
-    name: octavia-v2-dsvm-tls-barbican-stable-queens
-    parent: octavia-v2-dsvm-tls-barbican
-    nodeset: openstack-single-node-xenial
-    override-checkout: stable/queens
-
-- job:
     name: octavia-v2-dsvm-spare-pool
     parent: octavia-v2-dsvm-scenario
     vars:
@@ -786,23 +818,6 @@
     override-checkout: stable/wallaby
 
 - job:
-    name: octavia-v2-dsvm-spare-pool-stable-victoria
-    parent: octavia-v2-dsvm-spare-pool
-    override-checkout: stable/victoria
-
-- job:
-    name: octavia-v2-dsvm-spare-pool-stable-ussuri
-    parent: octavia-v2-dsvm-spare-pool
-    nodeset: octavia-single-node-ubuntu-bionic
-    override-checkout: stable/ussuri
-
-- job:
-    name: octavia-v2-dsvm-spare-pool-stable-train
-    parent: octavia-v2-dsvm-spare-pool
-    nodeset: octavia-single-node-ubuntu-bionic
-    override-checkout: stable/train
-
-- job:
     name: octavia-v2-dsvm-cinder-amphora
     parent: octavia-v2-dsvm-scenario
     required-projects:
@@ -932,27 +947,20 @@
       tox_envlist: all
 
 - job:
+    name: octavia-v2-act-stdby-dsvm-scenario-stable-yoga
+    parent: octavia-v2-act-stdby-dsvm-scenario
+    override-checkout: stable/yoga
+
+- job:
+    name: octavia-v2-act-stdby-dsvm-scenario-stable-xena
+    parent: octavia-v2-act-stdby-dsvm-scenario
+    override-checkout: stable/xena
+
+- job:
     name: octavia-v2-act-stdby-dsvm-scenario-stable-wallaby
     parent: octavia-v2-act-stdby-dsvm-scenario
     override-checkout: stable/wallaby
 
-- job:
-    name: octavia-v2-act-stdby-dsvm-scenario-stable-victoria
-    parent: octavia-v2-act-stdby-dsvm-scenario
-    override-checkout: stable/victoria
-
-- job:
-    name: octavia-v2-act-stdby-dsvm-scenario-stable-ussuri
-    parent: octavia-v2-act-stdby-dsvm-scenario
-    nodeset: octavia-single-node-ubuntu-bionic
-    override-checkout: stable/ussuri
-
-- job:
-    name: octavia-v2-act-stdby-dsvm-scenario-stable-train
-    parent: octavia-v2-act-stdby-dsvm-scenario
-    nodeset: octavia-single-node-ubuntu-bionic
-    override-checkout: stable/train
-
 ######### Third party jobs ##########
 
 - job:
diff --git a/zuul.d/projects.yaml b/zuul.d/projects.yaml
index 02d3ac7..116da49 100644
--- a/zuul.d/projects.yaml
+++ b/zuul.d/projects.yaml
@@ -9,47 +9,36 @@
     check:
       jobs:
         - octavia-v2-dsvm-noop-api
+        - octavia-v2-dsvm-noop-api-stable-yoga
+        - octavia-v2-dsvm-noop-api-stable-xena
         - octavia-v2-dsvm-noop-api-stable-wallaby
-        - octavia-v2-dsvm-noop-api-stable-victoria
-        - octavia-v2-dsvm-noop-api-stable-ussuri
-        - octavia-v2-dsvm-noop-api-stable-train
         - octavia-v2-dsvm-noop-api-scoped-tokens
         - octavia-v2-dsvm-scenario
+        - octavia-v2-dsvm-scenario-stable-yoga
+        - octavia-v2-dsvm-scenario-stable-xena
         - octavia-v2-dsvm-scenario-stable-wallaby
-        - octavia-v2-dsvm-scenario-stable-victoria
-        - octavia-v2-dsvm-scenario-stable-ussuri
-        - octavia-v2-dsvm-scenario-stable-train
         - octavia-v2-dsvm-tls-barbican
+        - octavia-v2-dsvm-tls-barbican-stable-yoga
+        - octavia-v2-dsvm-tls-barbican-stable-xena
         - octavia-v2-dsvm-tls-barbican-stable-wallaby
-        - octavia-v2-dsvm-tls-barbican-stable-victoria
-        - octavia-v2-dsvm-tls-barbican-stable-ussuri
-        - octavia-v2-dsvm-tls-barbican-stable-train
         - octavia-v2-dsvm-scenario-ipv6-only:
             voting: false
-        - octavia-v2-dsvm-scenario-centos-8:
+        - octavia-v2-dsvm-scenario-centos-8-stream:
+            voting: false
+        - octavia-v2-dsvm-scenario-centos-9-stream:
             voting: false
         - octavia-v2-act-stdby-dsvm-scenario-two-node:
             voting: false
         - octavia-v2-act-stdby-dsvm-scenario:
             voting: false
+        - octavia-v2-act-stdby-dsvm-scenario-stable-yoga:
+            voting: false
+        - octavia-v2-act-stdby-dsvm-scenario-stable-xena:
+            voting: false
         - octavia-v2-act-stdby-dsvm-scenario-stable-wallaby:
             voting: false
-        - octavia-v2-act-stdby-dsvm-scenario-stable-victoria:
-            voting: false
-        - octavia-v2-act-stdby-dsvm-scenario-stable-ussuri:
-            voting: false
-        - octavia-v2-act-stdby-dsvm-scenario-stable-train:
-            voting: false
-        - octavia-v2-dsvm-spare-pool:
-            voting: false
         - octavia-v2-dsvm-spare-pool-stable-wallaby:
             voting: false
-        - octavia-v2-dsvm-spare-pool-stable-victoria:
-            voting: false
-        - octavia-v2-dsvm-spare-pool-stable-ussuri:
-            voting: false
-        - octavia-v2-dsvm-spare-pool-stable-train:
-            voting: false
         - octavia-v2-dsvm-cinder-amphora:
             voting: false
         # Third party provider jobs
@@ -57,23 +46,20 @@
             voting: false
         - neutron-ovn-provider-v2-scenario:
             voting: false
+    queue: octavia
     gate:
       fail-fast: true
-      queue: octavia
       jobs:
         - octavia-v2-dsvm-noop-api
+        - octavia-v2-dsvm-noop-api-stable-yoga
+        - octavia-v2-dsvm-noop-api-stable-xena
         - octavia-v2-dsvm-noop-api-stable-wallaby
-        - octavia-v2-dsvm-noop-api-stable-victoria
-        - octavia-v2-dsvm-noop-api-stable-ussuri
-        - octavia-v2-dsvm-noop-api-stable-train
         - octavia-v2-dsvm-noop-api-scoped-tokens
         - octavia-v2-dsvm-scenario
+        - octavia-v2-dsvm-scenario-stable-yoga
+        - octavia-v2-dsvm-scenario-stable-xena
         - octavia-v2-dsvm-scenario-stable-wallaby
-        - octavia-v2-dsvm-scenario-stable-victoria
-        - octavia-v2-dsvm-scenario-stable-ussuri
-        - octavia-v2-dsvm-scenario-stable-train
         - octavia-v2-dsvm-tls-barbican
+        - octavia-v2-dsvm-tls-barbican-stable-yoga
+        - octavia-v2-dsvm-tls-barbican-stable-xena
         - octavia-v2-dsvm-tls-barbican-stable-wallaby
-        - octavia-v2-dsvm-tls-barbican-stable-victoria
-        - octavia-v2-dsvm-tls-barbican-stable-ussuri
-        - octavia-v2-dsvm-tls-barbican-stable-train