Merge "Add local ip scenario tests"
diff --git a/neutron_tempest_plugin/api/admin/test_agent_management.py b/neutron_tempest_plugin/api/admin/test_agent_management.py
index f63e81b..399e428 100644
--- a/neutron_tempest_plugin/api/admin/test_agent_management.py
+++ b/neutron_tempest_plugin/api/admin/test_agent_management.py
@@ -38,9 +38,13 @@
         # Heartbeats must be excluded from comparison
         self.agent.pop('heartbeat_timestamp', None)
         self.agent.pop('configurations', None)
+        # Exclude alive as it can happen that when testclass'
+        # resource_setup executed the selected agent is not up
+        self.agent.pop('alive', None)
         for agent in agents:
             agent.pop('heartbeat_timestamp', None)
             agent.pop('configurations', None)
+            agent.pop('alive', None)
         self.assertIn(self.agent, agents)
 
     @decorators.idempotent_id('e335be47-b9a1-46fd-be30-0874c0b751e6')
diff --git a/neutron_tempest_plugin/api/admin/test_logging.py b/neutron_tempest_plugin/api/admin/test_logging.py
index f4cbe29..b76377d 100644
--- a/neutron_tempest_plugin/api/admin/test_logging.py
+++ b/neutron_tempest_plugin/api/admin/test_logging.py
@@ -28,9 +28,11 @@
 
     @decorators.idempotent_id('8d2e1ba5-455b-4519-a88e-e587002faba6')
     def test_log_lifecycle(self):
+        security_group = self.create_security_group()
         name = data_utils.rand_name('test-log')
         description = data_utils.rand_name('test-log-desc')
         log = self.create_log(name=name, description=description,
+                              resource_id=security_group['id'],
                               resource_type='security_group', enabled=True)
 
         # Test 'show log'
@@ -72,3 +74,27 @@
         # Verify that only required fields present in logging types
         for log_type in actual_list_log_types:
             self.assertEqual(tuple(expected_log_keys), tuple(log_type.keys()))
+
+    @decorators.idempotent_id('1ab4eb2a-76f5-45b9-816b-1aa497a71eea')
+    def test_log_deleted_with_corresponding_security_group(self):
+        security_group = self.create_security_group()
+        name = data_utils.rand_name('test-log')
+        log = self.create_log(
+            name=name,
+            resource_type='security_group',
+            resource_id=security_group['id'],
+            enabled=True)
+
+        # Ensure log was created
+        retrieved_log = self.admin_client.show_log(log['id'])['log']
+        self.assertEqual(name, retrieved_log['name'])
+        self.assertEqual(security_group['id'], retrieved_log['resource_id'])
+        self.assertEqual('security_group', retrieved_log['resource_type'])
+        self.assertTrue(retrieved_log['enabled'])
+
+        # Delete SG
+        self.delete_security_group(security_group)
+
+        # Ensure log is also deleted
+        self.assertRaises(exceptions.NotFound,
+                          self.admin_client.show_log, log['id'])
diff --git a/neutron_tempest_plugin/api/admin/test_ports.py b/neutron_tempest_plugin/api/admin/test_ports.py
index b277fac..a374b81 100644
--- a/neutron_tempest_plugin/api/admin/test_ports.py
+++ b/neutron_tempest_plugin/api/admin/test_ports.py
@@ -133,14 +133,41 @@
         self.assertIn('resource_request', port)
         vnic_trait = 'CUSTOM_VNIC_TYPE_%s' % vnic_type.upper()
         physnet_trait = 'CUSTOM_PHYSNET_%s' % self.physnet_name.upper()
-        self.assertCountEqual([physnet_trait, vnic_trait],
-                              port['resource_request']['required'])
+        if utils.is_extension_enabled('port-resource-request-groups',
+                                      'network'):
+            min_bw_group_found = False
+            for rg in port['resource_request']['request_groups']:
+                self.assertIn(rg['id'],
+                              port['resource_request']['same_subtree'])
+                if (('NET_BW_EGR_KILOBIT_PER_SEC' in rg['resources'] or
+                        'NET_BW_IGR_KILOBIT_PER_SEC' in rg['resources']) and
+                        not min_bw_group_found):
+                    self.assertCountEqual([physnet_trait, vnic_trait],
+                                          rg['required'])
 
-        self.assertEqual(
-            {'NET_BW_EGR_KILOBIT_PER_SEC': self.EGRESS_KBPS,
-             'NET_BW_IGR_KILOBIT_PER_SEC': self.INGRESS_KBPS},
-            port['resource_request']['resources']
-        )
+                    self.assertEqual(
+                        {'NET_BW_EGR_KILOBIT_PER_SEC': self.EGRESS_KBPS,
+                        'NET_BW_IGR_KILOBIT_PER_SEC': self.INGRESS_KBPS},
+                        rg['resources']
+                    )
+                    min_bw_group_found = True
+                else:
+                    self.fail('"resource_request" contains unexpected request '
+                              'group: %s', rg)
+
+            if not min_bw_group_found:
+                self.fail('Did not find expected request groups in '
+                          '"resource_request": %s',
+                          port['resource_request']['request_groups'])
+        else:
+            self.assertCountEqual([physnet_trait, vnic_trait],
+                                  port['resource_request']['required'])
+
+            self.assertEqual(
+                {'NET_BW_EGR_KILOBIT_PER_SEC': self.EGRESS_KBPS,
+                'NET_BW_IGR_KILOBIT_PER_SEC': self.INGRESS_KBPS},
+                port['resource_request']['resources']
+            )
 
     @decorators.idempotent_id('ebb86dc4-716c-4558-8516-6dfc4a67601f')
     def test_port_resource_request(self):
diff --git a/neutron_tempest_plugin/api/base.py b/neutron_tempest_plugin/api/base.py
index 53d95ec..07fcb0b 100644
--- a/neutron_tempest_plugin/api/base.py
+++ b/neutron_tempest_plugin/api/base.py
@@ -1208,12 +1208,13 @@
                    target_id=None, event='ALL', enabled=True):
         """Wrapper utility that returns a test log object."""
         log_args = {'name': name,
-                    'description': description,
                     'resource_type': resource_type,
                     'resource_id': resource_id,
                     'target_id': target_id,
                     'event': event,
                     'enabled': enabled}
+        if description:
+            log_args['description'] = description
         body = cls.admin_client.create_log(**log_args)
         log_object = body['log']
         cls.log_objects.append(log_object)
diff --git a/neutron_tempest_plugin/api/clients.py b/neutron_tempest_plugin/api/clients.py
index 6565dcb..2855a7a 100644
--- a/neutron_tempest_plugin/api/clients.py
+++ b/neutron_tempest_plugin/api/clients.py
@@ -24,6 +24,7 @@
 from tempest.lib.services.identity.v3 import projects_client
 from tempest.lib.services.network import qos_limit_bandwidth_rules_client
 from tempest.lib.services.network import qos_minimum_bandwidth_rules_client
+from tempest.lib.services.network import qos_minimum_packet_rate_rules_client
 
 from neutron_tempest_plugin import config
 from neutron_tempest_plugin.services.network.json import network_client
@@ -114,6 +115,17 @@
                 build_timeout=CONF.network.build_timeout,
                 **self.default_params)
 
+        self.qos_minimum_packet_rate_rules_client = \
+            qos_minimum_packet_rate_rules_client.\
+            QosMinimumPacketRateRulesClient(
+                self.auth_provider,
+                CONF.network.catalog_type,
+                CONF.network.region or CONF.identity.region,
+                endpoint_type=CONF.network.endpoint_type,
+                build_interval=CONF.network.build_interval,
+                build_timeout=CONF.network.build_timeout,
+                **self.default_params)
+
     def _set_identity_clients(self):
         params = {
             'service': CONF.identity.catalog_type,
diff --git a/neutron_tempest_plugin/api/test_qos.py b/neutron_tempest_plugin/api/test_qos.py
index 5284688..ebce45b 100644
--- a/neutron_tempest_plugin/api/test_qos.py
+++ b/neutron_tempest_plugin/api/test_qos.py
@@ -1376,6 +1376,226 @@
         self.assertNotIn(rule2['id'], rules_ids)
 
 
+class QosMinimumPpsRuleTestJSON(base.BaseAdminNetworkTest):
+    RULE_NAME = qos_consts.RULE_TYPE_MINIMUM_PACKET_RATE + "_rule"
+    RULES_NAME = RULE_NAME + "s"
+    required_extensions = [qos_apidef.ALIAS]
+
+    @classmethod
+    @utils.requires_ext(service='network',
+                        extension='port-resource-request-groups')
+    def resource_setup(cls):
+        super(QosMinimumPpsRuleTestJSON, cls).resource_setup()
+
+    @classmethod
+    def setup_clients(cls):
+        super(QosMinimumPpsRuleTestJSON, cls).setup_clients()
+        cls.min_pps_client = cls.os_admin.qos_minimum_packet_rate_rules_client
+        cls.min_pps_client_primary = \
+            cls.os_primary.qos_minimum_packet_rate_rules_client
+
+    def _create_qos_min_pps_rule(self, policy_id, rule_data):
+        rule = self.min_pps_client.create_minimum_packet_rate_rule(
+            policy_id, **rule_data)['minimum_packet_rate_rule']
+        self.addCleanup(
+            test_utils.call_and_ignore_notfound_exc,
+            self.min_pps_client.delete_minimum_packet_rate_rule,
+            policy_id, rule['id'])
+        return rule
+
+    @decorators.idempotent_id('66a5b9b4-d4f9-4af8-b238-9e1881b78487')
+    def test_rule_create(self):
+        policy = self.create_qos_policy(name='test-policy',
+                                        description='test policy',
+                                        shared=False)
+        rule = self._create_qos_min_pps_rule(
+            policy['id'],
+            {qos_consts.DIRECTION: n_constants.EGRESS_DIRECTION,
+             qos_consts.MIN_KPPS: 1138})
+
+        # Test 'show rule'
+        retrieved_rule = self.min_pps_client.show_minimum_packet_rate_rule(
+            policy['id'], rule['id'])[self.RULE_NAME]
+        self.assertEqual(rule['id'], retrieved_rule['id'])
+        self.assertEqual(1138, retrieved_rule[qos_consts.MIN_KPPS])
+        self.assertEqual(n_constants.EGRESS_DIRECTION,
+                         retrieved_rule[qos_consts.DIRECTION])
+
+        # Test 'list rules'
+        rules = self.min_pps_client.list_minimum_packet_rate_rules(
+            policy['id'])
+        rules = rules[self.RULES_NAME]
+        rules_ids = [r['id'] for r in rules]
+        self.assertIn(rule['id'], rules_ids)
+
+        # Test 'show policy'
+        retrieved_policy = self.admin_client.show_qos_policy(policy['id'])
+        policy_rules = retrieved_policy['policy']['rules']
+        self.assertEqual(1, len(policy_rules))
+        self.assertEqual(rule['id'], policy_rules[0]['id'])
+        self.assertEqual('minimum_packet_rate',
+                         policy_rules[0]['type'])
+
+    @decorators.idempotent_id('6b656b57-d2bf-47f9-89a9-1baad1bd5418')
+    def test_rule_create_fail_for_missing_min_kpps(self):
+        policy = self.create_qos_policy(name='test-policy',
+                                        description='test policy',
+                                        shared=False)
+        self.assertRaises(exceptions.BadRequest,
+                          self._create_qos_min_pps_rule,
+                          policy['id'],
+                          {qos_consts.DIRECTION: n_constants.EGRESS_DIRECTION})
+
+    @decorators.idempotent_id('f41213e5-2ab8-4916-b106-38d2cac5e18c')
+    def test_rule_create_fail_for_the_same_type(self):
+        policy = self.create_qos_policy(name='test-policy',
+                                        description='test policy',
+                                        shared=False)
+        self._create_qos_min_pps_rule(policy['id'],
+            {qos_consts.DIRECTION: n_constants.EGRESS_DIRECTION,
+             qos_consts.MIN_KPPS: 200})
+
+        self.assertRaises(exceptions.Conflict,
+                          self._create_qos_min_pps_rule,
+                          policy['id'],
+                          {qos_consts.DIRECTION: n_constants.EGRESS_DIRECTION,
+                           qos_consts.MIN_KPPS: 201})
+
+    @decorators.idempotent_id('ceb8e41e-3d72-11ec-a446-d7faae6daec2')
+    def test_rule_create_any_direction_when_egress_direction_exists(self):
+        policy = self.create_qos_policy(name='test-policy',
+                                        description='test policy',
+                                        shared=False)
+        self._create_qos_min_pps_rule(policy['id'],
+            {qos_consts.DIRECTION: n_constants.EGRESS_DIRECTION,
+             qos_consts.MIN_KPPS: 200})
+
+        self.assertRaises(exceptions.Conflict,
+                          self._create_qos_min_pps_rule,
+                          policy['id'],
+                          {qos_consts.DIRECTION: n_constants.ANY_DIRECTION,
+                           qos_consts.MIN_KPPS: 201})
+
+    @decorators.idempotent_id('a147a71e-3d7b-11ec-8097-278b1afd5fa2')
+    def test_rule_create_egress_direction_when_any_direction_exists(self):
+        policy = self.create_qos_policy(name='test-policy',
+                                        description='test policy',
+                                        shared=False)
+        self._create_qos_min_pps_rule(policy['id'],
+            {qos_consts.DIRECTION: n_constants.ANY_DIRECTION,
+             qos_consts.MIN_KPPS: 200})
+
+        self.assertRaises(exceptions.Conflict,
+                          self._create_qos_min_pps_rule,
+                          policy['id'],
+                          {qos_consts.DIRECTION: n_constants.EGRESS_DIRECTION,
+                           qos_consts.MIN_KPPS: 201})
+
+    @decorators.idempotent_id('522ed09a-1d7f-4c1b-9195-61f19caf916f')
+    def test_rule_update(self):
+        policy = self.create_qos_policy(name='test-policy',
+                                        description='test policy',
+                                        shared=False)
+        rule = self._create_qos_min_pps_rule(
+            policy['id'],
+            {qos_consts.DIRECTION: n_constants.EGRESS_DIRECTION,
+             qos_consts.MIN_KPPS: 300})
+
+        self.min_pps_client.update_minimum_packet_rate_rule(
+            policy['id'], rule['id'],
+            **{qos_consts.MIN_KPPS: 350,
+               qos_consts.DIRECTION: n_constants.ANY_DIRECTION})
+
+        retrieved_rule = self.min_pps_client.show_minimum_packet_rate_rule(
+            policy['id'], rule['id'])[self.RULE_NAME]
+        self.assertEqual(350, retrieved_rule[qos_consts.MIN_KPPS])
+        self.assertEqual(n_constants.ANY_DIRECTION,
+                         retrieved_rule[qos_consts.DIRECTION])
+
+    @decorators.idempotent_id('a020e186-3d60-11ec-88ca-d7f5eec22764')
+    def test_rule_update_direction_conflict(self):
+        policy = self.create_qos_policy(name='test-policy',
+                                        description='test policy',
+                                        shared=False)
+        rule1 = self._create_qos_min_pps_rule(
+            policy['id'],
+            {qos_consts.DIRECTION: n_constants.EGRESS_DIRECTION,
+             qos_consts.MIN_KPPS: 300})
+
+        rule2 = self._create_qos_min_pps_rule(
+            policy['id'],
+            {qos_consts.DIRECTION: n_constants.INGRESS_DIRECTION,
+             qos_consts.MIN_KPPS: 300})
+
+        retrieved_rule1 = self.min_pps_client.show_minimum_packet_rate_rule(
+            policy['id'], rule1['id'])[self.RULE_NAME]
+        self.assertEqual(n_constants.EGRESS_DIRECTION,
+                         retrieved_rule1[qos_consts.DIRECTION])
+        retrieved_rule2 = self.min_pps_client.show_minimum_packet_rate_rule(
+            policy['id'], rule2['id'])[self.RULE_NAME]
+        self.assertEqual(n_constants.INGRESS_DIRECTION,
+                         retrieved_rule2[qos_consts.DIRECTION])
+
+        self.assertRaises(exceptions.Conflict,
+                          self.min_pps_client.update_minimum_packet_rate_rule,
+                          policy['id'], rule2['id'],
+                          **{qos_consts.DIRECTION: n_constants.ANY_DIRECTION})
+
+    @decorators.idempotent_id('c49018b6-d358-49a1-a94b-d53224165045')
+    def test_rule_delete(self):
+        policy = self.create_qos_policy(name='test-policy',
+                                        description='test policy',
+                                        shared=False)
+        rule = self._create_qos_min_pps_rule(
+            policy['id'],
+            {qos_consts.DIRECTION: n_constants.EGRESS_DIRECTION,
+             qos_consts.MIN_KPPS: 200})
+
+        retrieved_rule = self.min_pps_client.show_minimum_packet_rate_rule(
+            policy['id'], rule['id'])[self.RULE_NAME]
+        self.assertEqual(rule['id'], retrieved_rule['id'])
+
+        self.min_pps_client.delete_minimum_packet_rate_rule(policy['id'],
+                                                            rule['id'])
+        self.assertRaises(exceptions.NotFound,
+                          self.min_pps_client.show_minimum_packet_rate_rule,
+                          policy['id'], rule['id'])
+
+    @decorators.idempotent_id('1a6b6128-3d3e-11ec-bf49-57b326d417c0')
+    def test_rule_create_forbidden_for_regular_tenants(self):
+        self.assertRaises(
+            exceptions.Forbidden,
+            self.min_pps_client_primary.create_minimum_packet_rate_rule,
+            'policy', **{qos_consts.DIRECTION: n_constants.EGRESS_DIRECTION,
+                         qos_consts.MIN_KPPS: 300})
+
+    @decorators.idempotent_id('1b94f4e2-3d3e-11ec-bb21-6f98e4044b8b')
+    def test_get_rules_by_policy(self):
+        policy1 = self.create_qos_policy(name='test-policy1',
+                                         description='test policy1',
+                                         shared=False)
+        rule1 = self._create_qos_min_pps_rule(
+            policy1['id'],
+            {qos_consts.DIRECTION: n_constants.EGRESS_DIRECTION,
+             qos_consts.MIN_KPPS: 200})
+
+        policy2 = self.create_qos_policy(name='test-policy2',
+                                         description='test policy2',
+                                         shared=False)
+        rule2 = self._create_qos_min_pps_rule(
+            policy2['id'],
+            {qos_consts.DIRECTION: n_constants.EGRESS_DIRECTION,
+             qos_consts.MIN_KPPS: 5000})
+
+        # Test 'list rules'
+        rules = self.min_pps_client.list_minimum_packet_rate_rules(
+            policy1['id'])
+        rules = rules[self.RULES_NAME]
+        rules_ids = [r['id'] for r in rules]
+        self.assertIn(rule1['id'], rules_ids)
+        self.assertNotIn(rule2['id'], rules_ids)
+
+
 class QosSearchCriteriaTest(base.BaseSearchCriteriaTest,
                             base.BaseAdminNetworkTest):
 
diff --git a/neutron_tempest_plugin/api/test_qos_negative.py b/neutron_tempest_plugin/api/test_qos_negative.py
index 3e80129..505d1eb 100644
--- a/neutron_tempest_plugin/api/test_qos_negative.py
+++ b/neutron_tempest_plugin/api/test_qos_negative.py
@@ -11,7 +11,10 @@
 #    under the License.
 
 from neutron_lib.api.definitions import qos as qos_apidef
+from neutron_lib import constants as n_constants
 from neutron_lib.db import constants as db_const
+from neutron_lib.services.qos import constants as qos_consts
+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
@@ -103,6 +106,8 @@
         rule = self.rule_create_m(policy['id'], **create_params)
         if "minimum_bandwidth_rule" in rule.keys():
             rule_id = rule['minimum_bandwidth_rule']['id']
+        if "minimum_packet_rate_rule" in rule.keys():
+            rule_id = rule['minimum_packet_rate_rule']['id']
         if "bandwidth_limit_rule" in rule.keys():
             rule_id = rule['bandwidth_limit_rule']['id']
         if "dscp_mark" in rule.keys():
@@ -198,6 +203,41 @@
         self._test_rule_update_rule_nonexistent_rule(update_params)
 
 
+class QosMinimumPpsRuleNegativeTestJSON(QosRuleNegativeBaseTestJSON):
+
+    @classmethod
+    @utils.requires_ext(service='network',
+                        extension='port-resource-request-groups')
+    def resource_setup(cls):
+        cls.rule_create_m = cls.os_admin.qos_minimum_packet_rate_rules_client.\
+            create_minimum_packet_rate_rule
+        cls.rule_update_m = cls.os_admin.qos_minimum_packet_rate_rules_client.\
+            update_minimum_packet_rate_rule
+        super(QosMinimumPpsRuleNegativeTestJSON, cls).resource_setup()
+
+    @decorators.attr(type='negative')
+    @decorators.idempotent_id('ddd16824-3e10-11ec-928d-5b1ef3fb9f43')
+    def test_rule_update_rule_nonexistent_policy(self):
+        create_params = {qos_consts.DIRECTION: n_constants.EGRESS_DIRECTION,
+                         qos_consts.MIN_KPPS: 1}
+        update_params = {qos_consts.MIN_KPPS: 200}
+        self._test_rule_update_rule_nonexistent_policy(
+            create_params, update_params)
+
+    @decorators.attr(type='negative')
+    @decorators.idempotent_id('de4f5540-3e10-11ec-9700-4bf3629b843e')
+    def test_rule_create_rule_non_existent_policy(self):
+        create_params = {qos_consts.DIRECTION: n_constants.EGRESS_DIRECTION,
+                         qos_consts.MIN_KPPS: 200}
+        self._test_rule_create_rule_non_existent_policy(create_params)
+
+    @decorators.attr(type='negative')
+    @decorators.idempotent_id('deb914ee-3e10-11ec-b3dc-03e52f9269c9')
+    def test_rule_update_rule_nonexistent_rule(self):
+        update_params = {qos_consts.MIN_KPPS: 200}
+        self._test_rule_update_rule_nonexistent_rule(update_params)
+
+
 class QosDscpRuleNegativeTestJSON(QosRuleNegativeBaseTestJSON):
 
     @classmethod
diff --git a/neutron_tempest_plugin/api/test_security_groups.py b/neutron_tempest_plugin/api/test_security_groups.py
index 0970d83..d251f8c 100644
--- a/neutron_tempest_plugin/api/test_security_groups.py
+++ b/neutron_tempest_plugin/api/test_security_groups.py
@@ -143,6 +143,47 @@
             ' is: {}'.format(same_name_sg_number))
 
 
+class StatelessSecGroupTest(base.BaseAdminNetworkTest):
+
+    required_extensions = ['security-group', 'stateful-security-group']
+
+    @decorators.idempotent_id('0a6c1476-3d1a-11ec-b0ec-0800277ac3d9')
+    def test_stateless_security_group_update(self):
+        security_group = self.create_security_group(stateful=True)
+
+        # List security groups and verify if created group is there in response
+        security_groups = self.client.list_security_groups()['security_groups']
+        found = False
+        for sg in security_groups:
+            if sg['id'] == security_group['id']:
+                found = True
+                break
+        self.assertTrue(found)
+        self.assertTrue(sg['stateful'])
+
+        # Switch to stateless
+        updated_security_group = self.client.update_security_group(
+            security_group['id'], stateful=False)['security_group']
+
+        # Verify if security group is updated
+        self.assertFalse(updated_security_group['stateful'])
+
+        observed_security_group = self.client.show_security_group(
+            security_group['id'])['security_group']
+        self.assertFalse(observed_security_group['stateful'])
+
+        # Switch back to stateful
+        updated_security_group = self.client.update_security_group(
+            security_group['id'], stateful=True)['security_group']
+
+        # Verify if security group is stateful again
+        self.assertTrue(updated_security_group['stateful'])
+
+        observed_security_group = self.client.show_security_group(
+            security_group['id'])['security_group']
+        self.assertTrue(observed_security_group['stateful'])
+
+
 class BaseSecGroupQuota(base.BaseAdminNetworkTest):
 
     def _create_max_allowed_sg_amount(self):
diff --git a/neutron_tempest_plugin/scenario/admin/test_floatingip.py b/neutron_tempest_plugin/scenario/admin/test_floatingip.py
index a08acc3..d9abaf5 100644
--- a/neutron_tempest_plugin/scenario/admin/test_floatingip.py
+++ b/neutron_tempest_plugin/scenario/admin/test_floatingip.py
@@ -28,6 +28,14 @@
     credentials = ['primary', 'admin']
 
     @classmethod
+    def setup_clients(cls):
+        super(FloatingIpTestCasesAdmin, cls).setup_clients()
+        # admin_client set in BaseAdminNetworkTest but here we inherit from
+        # BaseNetworkTest
+        if not cls.admin_client:
+            cls.admin_client = cls.os_admin.network_client
+
+    @classmethod
     @utils.requires_ext(extension="router", service="network")
     def resource_setup(cls):
         super(FloatingIpTestCasesAdmin, cls).resource_setup()
@@ -75,7 +83,7 @@
             waiters.wait_for_server_status(
                 self.os_admin.servers_client, server['server']['id'],
                 const.SERVER_STATUS_ACTIVE)
-            port = self.client.list_ports(
+            port = self.admin_client.list_ports(
                 network_id=self.network['id'],
                 device_id=server['server']['id']
             )['ports'][0]
diff --git a/neutron_tempest_plugin/scenario/base.py b/neutron_tempest_plugin/scenario/base.py
index a1a054a..cbe5df6 100644
--- a/neutron_tempest_plugin/scenario/base.py
+++ b/neutron_tempest_plugin/scenario/base.py
@@ -539,7 +539,7 @@
             return False
 
         try:
-            utils.wait_until_true(system_booted, sleep=5)
+            utils.wait_until_true(system_booted, timeout=90, sleep=5)
         except utils.WaitTimeout:
             LOG.debug("No correct output in console of server %s found. "
                       "Guest operating system status can't be checked.",
diff --git a/neutron_tempest_plugin/scenario/test_floatingip.py b/neutron_tempest_plugin/scenario/test_floatingip.py
index a5afc73..a5f6486 100644
--- a/neutron_tempest_plugin/scenario/test_floatingip.py
+++ b/neutron_tempest_plugin/scenario/test_floatingip.py
@@ -18,6 +18,7 @@
 from neutron_lib import constants as lib_constants
 from neutron_lib.services.qos import constants as qos_consts
 from neutron_lib.utils import test
+from oslo_log import log
 from tempest.common import utils
 from tempest.common import waiters
 from tempest.lib.common.utils import data_utils
@@ -37,6 +38,7 @@
 
 
 CONF = config.CONF
+LOG = log.getLogger(__name__)
 
 
 load_tests = testscenarios.load_tests_apply_scenarios
@@ -244,11 +246,17 @@
             self._check_port_details(
                 fip, port, status=lib_constants.PORT_STATUS_ACTIVE,
                 device_id=server['server']['id'], device_owner='compute:nova')
+            LOG.debug('Port check for server %s and FIP %s finished, '
+                      'lets detach port %s from server!',
+                      server['server']['id'], fip['id'], port['id'])
 
             # detach the port from the server; this is a cast in the compute
             # API so we have to poll the port until the device_id is unset.
             self.delete_interface(server['server']['id'], port['id'])
             port = self._wait_for_port_detach(port['id'])
+            LOG.debug('Port %s has been detached from server %s, lets check '
+                      'the status of port in FIP %s details!',
+                      port['id'], server['server']['id'], fip['id'])
             fip = self._wait_for_fip_port_down(fip['id'])
             self._check_port_details(
                 fip, port, status=lib_constants.PORT_STATUS_DOWN,
@@ -323,6 +331,8 @@
                            (fip_id, status, timeout, port))
                 raise exceptions.TimeoutException(message)
 
+        LOG.debug('Port %s attached to FIP %s is down after %s!',
+                  fip.get("port_id"), fip_id, int(time.time()) - start)
         return fip
 
 
diff --git a/neutron_tempest_plugin/scenario/test_internal_dns.py b/neutron_tempest_plugin/scenario/test_internal_dns.py
index c0a2c04..692bb70 100644
--- a/neutron_tempest_plugin/scenario/test_internal_dns.py
+++ b/neutron_tempest_plugin/scenario/test_internal_dns.py
@@ -13,7 +13,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from oslo_log import log
 from tempest.common import utils
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 
 from neutron_tempest_plugin.common import ssh
@@ -21,9 +23,63 @@
 from neutron_tempest_plugin.scenario import base
 
 CONF = config.CONF
+LOG = log.getLogger(__name__)
 
 
-class InternalDNSTest(base.BaseTempestTestCase):
+class InternalDNSBase(base.BaseTempestTestCase):
+    """Base class of useful resources and functionalities for test class."""
+
+    port_error_msg = ('Openstack command returned incorrect '
+                      'hostname value in port.')
+    ssh_error_msg = ('Remote shell command returned incorrect hostname value '
+                     "(command: 'hostname' OR 'cat /etc/hostname').")
+
+    @staticmethod
+    def _rand_name(name):
+        """'data_utils.rand_name' wrapper, show name related to test suite."""
+        return data_utils.rand_name(f'internal-dns-test-{name}')
+
+    @classmethod
+    def resource_setup(cls):
+        super(InternalDNSBase, cls).resource_setup()
+        cls.router = cls.create_router_by_client()
+        cls.keypair = cls.create_keypair(
+            name=cls._rand_name('shared-keypair'))
+        cls.secgroup = cls.create_security_group(
+            name=cls._rand_name('shared-secgroup'))
+        cls.security_groups.append(cls.secgroup)
+        cls.create_loginable_secgroup_rule(
+            secgroup_id=cls.secgroup['id'])
+        cls.vm_kwargs = {
+            'flavor_ref': CONF.compute.flavor_ref,
+            'image_ref': CONF.compute.image_ref,
+            'key_name': cls.keypair['name'],
+            'security_groups': [{'name': cls.secgroup['name']}]
+        }
+
+    def _create_ssh_client(self, ip_addr):
+        return ssh.Client(ip_addr,
+                          CONF.validation.image_ssh_user,
+                          pkey=self.keypair['private_key'])
+
+    def _validate_port_dns_details(self, checked_hostname, checked_port):
+        """Validates reused objects for correct dns values in tests."""
+        dns_details = checked_port['dns_assignment'][0]
+        self.assertEqual(checked_hostname, checked_port['dns_name'],
+                         self.port_error_msg)
+        self.assertEqual(checked_hostname, dns_details['hostname'],
+                         self.port_error_msg)
+        self.assertIn(checked_hostname, dns_details['fqdn'],
+                      self.port_error_msg)
+
+    def _validate_ssh_dns_details(self, checked_hostname, ssh_client):
+        """Validates correct dns values returned from ssh command in tests."""
+        ssh_output = ssh_client.get_hostname()
+        self.assertIn(checked_hostname, ssh_output, self.ssh_error_msg)
+
+
+class InternalDNSTest(InternalDNSBase):
+    """Tests internal DNS capabilities."""
     credentials = ['primary', 'admin']
 
     @utils.requires_ext(extension="dns-integration", service="network")
@@ -52,8 +108,6 @@
             security_groups=[
                 {'name': self.security_groups[-1]['name']}],
             name='leia')
-        self.wait_for_server_active(leia['server'])
-        self.wait_for_guest_os_ready(leia['server'])
 
         ssh_client = ssh.Client(
             self.fip['floating_ip_address'],
@@ -82,3 +136,49 @@
                                        servers=[self.server, leia])
         self.check_remote_connectivity(ssh_client, 'leia.openstackgate.local',
                                        servers=[self.server, leia])
+
+    @utils.requires_ext(extension="dns-integration", service="network")
+    @decorators.idempotent_id('db5e612f-f17f-4974-b5f1-9fe89f4a6fc9')
+    def test_create_and_update_port_with_dns_name(self):
+        """Test creation of port with correct internal dns-name (hostname)."""
+
+        # 1) Create resources: network, subnet, etc.
+        # 2) Create a port with wrong dns-name (not as VM name).
+        # 3) Verify that wrong port initial dns-name.
+        #    was queried from openstack API.
+        # 4) Update the port with correct dns-name (as VM name).
+        # 5) Boot a VM with corrected predefined port.
+        # 6) Verify that correct port dns-name
+        #    was queried from openstack API.
+        # 7) Validate hostname configured on VM is same as VM's name.
+
+        # NOTE: VM's hostname has to be the same as VM's name
+        #       when a VM is created, it is a known limitation.
+        #       Therefore VM's dns-name/hostname is checked to be as VM's name.
+
+        vm_correct_name = self._rand_name('vm')
+        vm_wrong_name = self._rand_name('bazinga')
+        # create resources
+        network = self.create_network(name=self._rand_name('network'))
+        subnet = self.create_subnet(network, name=self._rand_name('subnet'))
+        self.create_router_interface(self.router['id'], subnet['id'])
+        # create port with wrong dns-name (not as VM name)
+        dns_port = self.create_port(network,
+                                    dns_name=vm_wrong_name,
+                                    security_groups=[self.secgroup['id']],
+                                    name=self._rand_name('port'))
+        # validate dns port with wrong initial hostname from API
+        self._validate_port_dns_details(vm_wrong_name, dns_port)
+        # update port with correct dns-name (as VM name)
+        dns_port = self.update_port(dns_port, dns_name=vm_correct_name)
+        # create VM with correct predefined dns-name on port
+        vm_1 = self.create_server(name=vm_correct_name,
+                                  networks=[{'port': dns_port['id']}],
+                                  **self.vm_kwargs)
+        # validate dns port with correct changed hostname using API
+        self._validate_port_dns_details(vm_correct_name, dns_port)
+        # validate hostname configured on VM is same as VM's name.
+        vm_1['fip'] = self.create_floatingip(port=dns_port)
+        vm_1['ssh_client'] = self._create_ssh_client(
+            vm_1['fip']['floating_ip_address'])
+        self._validate_ssh_dns_details(vm_correct_name, vm_1['ssh_client'])
diff --git a/neutron_tempest_plugin/scenario/test_mac_learning.py b/neutron_tempest_plugin/scenario/test_mac_learning.py
index 736d46c..6cd894f 100644
--- a/neutron_tempest_plugin/scenario/test_mac_learning.py
+++ b/neutron_tempest_plugin/scenario/test_mac_learning.py
@@ -14,8 +14,10 @@
 #    under the License.
 
 from oslo_log import log
+from paramiko import ssh_exception as ssh_exc
 from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
+from tempest.lib import exceptions as lib_exc
 
 from neutron_tempest_plugin.common import ssh
 from neutron_tempest_plugin.common import utils
@@ -116,17 +118,22 @@
                                           pkey=self.keypair['private_key'])
         return server
 
-    def _check_cmd_installed_on_server(self, ssh_client, server_id, cmd):
+    def _check_cmd_installed_on_server(self, ssh_client, server, cmd):
         try:
             ssh_client.execute_script('which %s' % cmd)
+        except (lib_exc.SSHTimeout, ssh_exc.AuthenticationException) as ssh_e:
+            LOG.debug(ssh_e)
+            self._log_console_output([server])
+            self._log_local_network_status()
+            raise
         except exceptions.SSHScriptFailed:
             raise self.skipException(
-                "%s is not available on server %s" % (cmd, server_id))
+                "%s is not available on server %s" % (cmd, server['id']))
 
     def _prepare_sender(self, server, address):
         check_script = get_sender_script(self.sender_output_file, address,
                                          self.completed_message)
-        self._check_cmd_installed_on_server(server['ssh_client'], server['id'],
+        self._check_cmd_installed_on_server(server['ssh_client'], server,
                                             'tcpdump')
         server['ssh_client'].execute_script(
             'echo "%s" > %s' % (check_script, self.sender_script_file))
@@ -135,7 +142,7 @@
         check_script = get_receiver_script(
             result_file=self.output_file,
             packets_expected=n_packets)
-        self._check_cmd_installed_on_server(server['ssh_client'], server['id'],
+        self._check_cmd_installed_on_server(server['ssh_client'], server,
                                             'tcpdump')
         server['ssh_client'].execute_script(
             'echo "%s" > %s' % (check_script, self.receiver_script_file))
diff --git a/neutron_tempest_plugin/scenario/test_multicast.py b/neutron_tempest_plugin/scenario/test_multicast.py
index 726d1e0..acfb75c 100644
--- a/neutron_tempest_plugin/scenario/test_multicast.py
+++ b/neutron_tempest_plugin/scenario/test_multicast.py
@@ -16,8 +16,10 @@
 import netaddr
 from neutron_lib import constants
 from oslo_log import log
+from paramiko import ssh_exception as ssh_exc
 from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
+from tempest.lib import exceptions as lib_exc
 
 from neutron_tempest_plugin.common import ip
 from neutron_tempest_plugin.common import ssh
@@ -210,15 +212,20 @@
                                           self.username,
                                           pkey=self.keypair['private_key'])
         self._check_cmd_installed_on_server(server['ssh_client'],
-                                            server['id'], PYTHON3_BIN)
+                                            server, PYTHON3_BIN)
         return server
 
-    def _check_cmd_installed_on_server(self, ssh_client, server_id, cmd):
+    def _check_cmd_installed_on_server(self, ssh_client, server, cmd):
         try:
             ssh_client.execute_script('which %s' % cmd)
+        except (lib_exc.SSHTimeout, ssh_exc.AuthenticationException) as ssh_e:
+            LOG.debug(ssh_e)
+            self._log_console_output([server])
+            self._log_local_network_status()
+            raise
         except exceptions.SSHScriptFailed:
             raise self.skipException(
-                "%s is not available on server %s" % (cmd, server_id))
+                "%s is not available on server %s" % (cmd, server['id']))
 
     def _prepare_sender(self, server, mcast_address):
         check_script = get_sender_script(
@@ -237,7 +244,7 @@
             server['fip']['floating_ip_address'],
             self.username,
             pkey=self.keypair['private_key'])
-        self._check_cmd_installed_on_server(ssh_client, server['id'],
+        self._check_cmd_installed_on_server(ssh_client, server,
                                             PYTHON3_BIN)
         server['ssh_client'].execute_script(
             'echo "%s" > /tmp/multicast_traffic_receiver.py' % check_script)
@@ -253,7 +260,7 @@
         check_script = get_unregistered_script(
             interface=port_iface, group=mcast_address,
             result_file=self.unregistered_output_file)
-        self._check_cmd_installed_on_server(ssh_client, server['id'],
+        self._check_cmd_installed_on_server(ssh_client, server,
                                             'tcpdump')
         server['ssh_client'].execute_script(
             'echo "%s" > /tmp/unregistered_traffic_receiver.sh' % check_script)
diff --git a/neutron_tempest_plugin/scenario/test_ports.py b/neutron_tempest_plugin/scenario/test_ports.py
index fb2d2b0..c661d39 100644
--- a/neutron_tempest_plugin/scenario/test_ports.py
+++ b/neutron_tempest_plugin/scenario/test_ports.py
@@ -107,8 +107,8 @@
                 if port is not None:
                     break
             except Exception as e:
-                LOG.warn('Failed to create Port, using Fixed_IP:{}, '
-                         'the Error was:{}'.format(ip, e))
+                LOG.warning('Failed to create Port, using Fixed_IP:{}, '
+                            'the Error was:{}'.format(ip, e))
         fip, server = self._create_instance_with_port(port)
         self.check_connectivity(fip[0]['floating_ip_address'],
                                 CONF.validation.image_ssh_user,
diff --git a/neutron_tempest_plugin/scenario/test_security_groups.py b/neutron_tempest_plugin/scenario/test_security_groups.py
index 40aa66a..f47ce44 100644
--- a/neutron_tempest_plugin/scenario/test_security_groups.py
+++ b/neutron_tempest_plugin/scenario/test_security_groups.py
@@ -71,10 +71,14 @@
     @classmethod
     def setup_credentials(cls):
         super(NetworkSecGroupTest, cls).setup_credentials()
-        cls.project_id = cls.os_primary.credentials.tenant_id
         cls.network_client = cls.os_admin.network_client
 
     @classmethod
+    def setup_clients(cls):
+        super(NetworkSecGroupTest, cls).setup_clients()
+        cls.project_id = cls.os_primary.credentials.tenant_id
+
+    @classmethod
     def resource_setup(cls):
         super(NetworkSecGroupTest, cls).resource_setup()
         # setup basic topology for servers we can log into it
diff --git a/neutron_tempest_plugin/scenario/test_trunk.py b/neutron_tempest_plugin/scenario/test_trunk.py
index b86c019..b994775 100644
--- a/neutron_tempest_plugin/scenario/test_trunk.py
+++ b/neutron_tempest_plugin/scenario/test_trunk.py
@@ -114,11 +114,6 @@
                 vlan_tag=segmentation_id,
                 vlan_subnet=vlan_subnet)
 
-        for server in server_list:
-            self.check_connectivity(
-                host=vm.floating_ip['floating_ip_address'],
-                ssh_client=vm.ssh_client)
-
         return server_list
 
     def _check_servers_remote_connectivity(self, vms=None,
@@ -197,6 +192,10 @@
         self._wait_for_trunk(trunk=vm.trunk)
         self._wait_for_port(port=vm.port)
         self._wait_for_port(port=vm.subport)
+        self.check_connectivity(
+            host=vm.floating_ip['floating_ip_address'],
+            ssh_client=vm.ssh_client,
+            servers=[vm.server])
 
         ip_command = ip.IPCommand(ssh_client=vm.ssh_client)
         for address in ip_command.list_addresses(port=vm.port):
diff --git a/requirements.txt b/requirements.txt
index 47dd923..21f14cc 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -11,7 +11,7 @@
 oslo.serialization!=2.19.1,>=2.18.0 # Apache-2.0
 oslo.utils>=3.33.0 # Apache-2.0
 paramiko>=2.0.0 # LGPLv2.1+
-tempest>=17.1.0 # Apache-2.0
+tempest>=29.2.0 # Apache-2.0
 tenacity>=3.2.1 # Apache-2.0
 ddt>=1.0.1 # MIT
 nose>=1.3.7 # LGPL
diff --git a/setup.cfg b/setup.cfg
index 136aa0f..3e352a0 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -18,6 +18,7 @@
     Programming Language :: Python :: 3.6
     Programming Language :: Python :: 3.7
     Programming Language :: Python :: 3.8
+    Programming Language :: Python :: 3.9
 
 [files]
 packages =
diff --git a/zuul.d/base-nested-switch.yaml b/zuul.d/base-nested-switch.yaml
new file mode 100644
index 0000000..69e841f
--- /dev/null
+++ b/zuul.d/base-nested-switch.yaml
@@ -0,0 +1,32 @@
+- nodeset:
+    name: neutron-nested-virt-ubuntu-focal
+    nodes:
+      - name: controller
+        label: nested-virt-ubuntu-focal
+    groups:
+      - name: tempest
+        nodes:
+          - controller
+
+# Base nested switch job for non EM releases
+- job:
+    name: neutron-tempest-plugin-scenario-nested-switch
+    parent: neutron-tempest-plugin-scenario
+    abstract: true
+    branches: ^(?!stable/(queens|rocky|stein|train|ussuri)).*$
+    # Comment nodeset and vars to switch back to non nested nodes
+    nodeset: neutron-nested-virt-ubuntu-focal
+    vars:
+      devstack_localrc:
+        LIBVIRT_TYPE: kvm
+        LIBVIRT_CPU_MODE: host-passthrough
+        CIRROS_VERSION: 0.5.1
+        DEFAULT_IMAGE_NAME: cirros-0.5.1-x86_64-disk
+        DEFAULT_IMAGE_FILE_NAME: cirros-0.5.1-x86_64-disk.img
+
+# Base nested switch job for EM releases
+- job:
+    name: neutron-tempest-plugin-scenario-nested-switch
+    parent: neutron-tempest-plugin-scenario
+    abstract: true
+    branches: ^(stable/(queens|rocky|stein|train|ussuri)).*$
diff --git a/zuul.d/base.yaml b/zuul.d/base.yaml
index 12408ea..bd2ae09 100644
--- a/zuul.d/base.yaml
+++ b/zuul.d/base.yaml
@@ -17,6 +17,8 @@
         USE_PYTHON3: true
         NETWORK_API_EXTENSIONS: "{{ (network_api_extensions_common + network_api_extensions_tempest) | join(',') }}"
         CIRROS_VERSION: 0.5.1
+        DEFAULT_IMAGE_NAME: cirros-0.5.1-x86_64-uec
+        DEFAULT_IMAGE_FILE_NAME: cirros-0.5.1-x86_64-uec.tar.gz
         BUILD_TIMEOUT: 784
       devstack_plugins:
         neutron: https://opendev.org/openstack/neutron.git
@@ -83,14 +85,22 @@
               provider_net_base_segm_id: 1
     irrelevant-files:
       - ^(test-|)requirements.txt$
+      - lower-constraints.txt
       - ^releasenotes/.*$
       - ^doc/.*$
+      - ^.*\.conf\.sample$
       - ^setup.cfg$
       - ^.*\.rst$
       - ^neutron/locale/.*$
       - ^neutron/tests/unit/.*$
+      - ^neutron/tests/fullstack/.*
+      - ^neutron/tests/functional/.*
       - ^tools/.*$
       - ^tox.ini$
+      - ^rally-jobs/.*$
+      - ^vagrant/.*$
+      - ^zuul.d/(queens|rocky|stein|train|ussuri)_jobs.yaml$
+      - ^zuul.d/base-nested-switch.yaml$
 
 - job:
     name: neutron-tempest-plugin-scenario
@@ -109,7 +119,6 @@
           (^tempest.api.compute.servers.test_multiple_create)"
       devstack_localrc:
         PHYSICAL_NETWORK: default
-        CIRROS_VERSION: 0.5.1
         IMAGE_URLS: https://cloud-images.ubuntu.com/minimal/releases/focal/release/ubuntu-20.04-minimal-cloudimg-amd64.img
         ADVANCED_IMAGE_NAME: ubuntu-20.04-minimal-cloudimg-amd64
         ADVANCED_INSTANCE_TYPE: ntp_image_256M
diff --git a/zuul.d/master_jobs.yaml b/zuul.d/master_jobs.yaml
index 6cddf0e..468128a 100644
--- a/zuul.d/master_jobs.yaml
+++ b/zuul.d/master_jobs.yaml
@@ -45,7 +45,9 @@
         - network_availability_zone
         - network-segment-range
         - pagination
+        - port-device-profile
         - port-resource-request
+        - port-resource-request-groups
         - port-mac-address-regenerate
         - port-security
         - port-security-groups-filtering
@@ -74,6 +76,7 @@
         - standard-attr-segment
         - standard-attr-tag
         - standard-attr-timestamp
+        - stateful-security-group
         - subnet_allocation
         - subnet-dns-publish-fixed-ip
         - subnet-service-types
@@ -98,6 +101,7 @@
         OVN_BUILD_FROM_SOURCE: True
         OVN_BRANCH: "v21.03.0"
         OVS_BRANCH: "8dc1733eaea866dce033b3c44853e1b09bf59fc7"
+        NETWORK_API_EXTENSIONS: "{{ network_api_extensions_common | join(',') }}"
       devstack_local_conf:
         post-config:
           # NOTE(slaweq): We can get rid of this hardcoded absolute path when
@@ -110,23 +114,30 @@
               local_output_log_base: /tmp/test_log.log
     irrelevant-files:
       - ^(test-|)requirements.txt$
+      - lower-constraints.txt
       - ^releasenotes/.*$
       - ^doc/.*$
       - ^setup.cfg$
       - ^.*\.rst$
+      - ^.*\.conf\.sample$
       - ^neutron/locale/.*$
       - ^neutron/tests/unit/.*$
+      - ^neutron/tests/fullstack/.*
+      - ^neutron/tests/functional/.*
       - ^tools/.*$
       - ^tox.ini$
       - ^neutron/agent/.*$
       - ^neutron/privileged/.*$
       - ^neutron_lib/tests/unit/.*$
       - ^neutron_tempest_plugin/scenario/.*$
-
+      - ^rally-jobs/.*$
+      - ^vagrant/.*$
+      - ^zuul.d/(queens|rocky|stein|train|ussuri)_jobs.yaml$
+      - ^zuul.d/base-nested-switch.yaml$
 
 - job:
     name: neutron-tempest-plugin-scenario-openvswitch
-    parent: neutron-tempest-plugin-scenario
+    parent: neutron-tempest-plugin-scenario-nested-switch
     timeout: 10000
     vars:
       devstack_services:
@@ -178,12 +189,16 @@
               firewall_driver: openvswitch
     irrelevant-files: &openvswitch-scenario-irrelevant-files
       - ^(test-|)requirements.txt$
+      - lower-constraints.txt
       - ^releasenotes/.*$
       - ^doc/.*$
       - ^setup.cfg$
       - ^.*\.rst$
+      - ^.*\.conf\.sample$
       - ^neutron/locale/.*$
       - ^neutron/tests/unit/.*$
+      - ^neutron/tests/fullstack/.*
+      - ^neutron/tests/functional/.*
       - ^tools/.*$
       - ^tox.ini$
       - ^neutron/agent/ovn/.*$
@@ -192,10 +207,26 @@
       - ^neutron/plugins/ml2/drivers/macvtap/.*$
       - ^neutron/plugins/ml2/drivers/mech_sriov/.*$
       - ^neutron/plugins/ml2/drivers/ovn/.*$
+      - ^neutron/services/ovn_l3/.*$
+      - ^neutron/services/logapi/drivers/ovn/.*$
+      - ^neutron/services/portforwarding/drivers/ovn/.*$
+      - ^neutron/services/qos/drivers/linuxbridge/.*$
+      - ^neutron/services/qos/drivers/ovn/.*$
+      - ^neutron/services/trunk/drivers/linuxbridge/.*$
+      - ^neutron/services/trunk/drivers/ovn/.*$
+      - ^neutron/cmd/ovn/.*$
+      - ^neutron/common/ovn/.*$
+      - ^neutron_tempest_plugin/api/test_.*$
+      - ^neutron_tempest_plugin/(bgpvpn|fwaas|neutron_dynamic_routing|sfc|tap_as_a_service|vpnaas).*$
+      - ^neutron_tempest_plugin/services/bgp/.*$
+      - ^rally-jobs/.*$
+      - ^vagrant/.*$
+      - ^zuul.d/(queens|rocky|stein|train|ussuri)_jobs.yaml$
+      - ^zuul.d/base-nested-switch.yaml$
 
 - job:
     name: neutron-tempest-plugin-scenario-openvswitch-iptables_hybrid
-    parent: neutron-tempest-plugin-scenario
+    parent: neutron-tempest-plugin-scenario-nested-switch
     timeout: 10000
     vars:
       devstack_services:
@@ -256,12 +287,16 @@
               firewall_driver: iptables_hybrid
     irrelevant-files:
       - ^(test-|)requirements.txt$
+      - lower-constraints.txt
       - ^releasenotes/.*$
       - ^doc/.*$
       - ^setup.cfg$
       - ^.*\.rst$
+      - ^.*\.conf\.sample$
       - ^neutron/locale/.*$
       - ^neutron/tests/unit/.*$
+      - ^neutron/tests/fullstack/.*
+      - ^neutron/tests/functional/.*
       - ^tools/.*$
       - ^tox.ini$
       - ^neutron/agent/linux/openvswitch_firewall/.*$
@@ -271,6 +306,22 @@
       - ^neutron/plugins/ml2/drivers/macvtap/.*$
       - ^neutron/plugins/ml2/drivers/mech_sriov/.*$
       - ^neutron/plugins/ml2/drivers/ovn/.*$
+      - ^neutron/services/ovn_l3/.*$
+      - ^neutron/services/logapi/drivers/ovn/.*$
+      - ^neutron/services/portforwarding/drivers/ovn/.*$
+      - ^neutron/services/qos/drivers/linuxbridge/.*$
+      - ^neutron/services/qos/drivers/ovn/.*$
+      - ^neutron/services/trunk/drivers/linuxbridge/.*$
+      - ^neutron/services/trunk/drivers/ovn/.*$
+      - ^neutron/cmd/ovn/.*$
+      - ^neutron/common/ovn/.*$
+      - ^neutron_tempest_plugin/api/test_.*$
+      - ^neutron_tempest_plugin/(bgpvpn|fwaas|neutron_dynamic_routing|sfc|tap_as_a_service|vpnaas).*$
+      - ^neutron_tempest_plugin/services/bgp/.*$
+      - ^rally-jobs/.*$
+      - ^vagrant/.*$
+      - ^zuul.d/(queens|rocky|stein|train|ussuri)_jobs.yaml$
+      - ^zuul.d/base-nested-switch.yaml$
 
 - job:
     name: neutron-tempest-plugin-scenario-openvswitch-distributed-dhcp
@@ -302,7 +353,7 @@
 
 - job:
     name: neutron-tempest-plugin-scenario-linuxbridge
-    parent: neutron-tempest-plugin-scenario
+    parent: neutron-tempest-plugin-scenario-nested-switch
     timeout: 10000
     roles:
       - zuul: openstack/neutron
@@ -367,12 +418,16 @@
               firewall_driver: iptables
     irrelevant-files:
       - ^(test-|)requirements.txt$
+      - lower-constraints.txt
       - ^releasenotes/.*$
       - ^doc/.*$
       - ^setup.cfg$
       - ^.*\.rst$
+      - ^.*\.conf\.sample$
       - ^neutron/locale/.*$
       - ^neutron/tests/unit/.*$
+      - ^neutron/tests/fullstack/.*
+      - ^neutron/tests/functional/.*
       - ^tools/.*$
       - ^tox.ini$
       - ^neutron/agent/linux/openvswitch_firewall/.*$
@@ -382,10 +437,27 @@
       - ^neutron/plugins/ml2/drivers/macvtap/.*$
       - ^neutron/plugins/ml2/drivers/mech_sriov/.*$
       - ^neutron/plugins/ml2/drivers/ovn/.*$
+      - ^neutron/services/ovn_l3/.*$
+      - ^neutron/services/logapi/drivers/openvswitch/.*$
+      - ^neutron/services/logapi/drivers/ovn/.*$
+      - ^neutron/services/portforwarding/drivers/ovn/.*$
+      - ^neutron/services/qos/drivers/openvswitch/.*$
+      - ^neutron/services/qos/drivers/ovn/.*$
+      - ^neutron/services/trunk/drivers/openvswitch/.*$
+      - ^neutron/services/trunk/drivers/ovn/.*$
+      - ^neutron/cmd/ovn/.*$
+      - ^neutron/common/ovn/.*$
+      - ^neutron_tempest_plugin/api/test_.*$
+      - ^neutron_tempest_plugin/(bgpvpn|fwaas|neutron_dynamic_routing|sfc|tap_as_a_service|vpnaas).*$
+      - ^neutron_tempest_plugin/services/bgp/.*$
+      - ^rally-jobs/.*$
+      - ^vagrant/.*$
+      - ^zuul.d/(queens|rocky|stein|train|ussuri)_jobs.yaml$
+      - ^zuul.d/base-nested-switch.yaml$
 
 - job:
     name: neutron-tempest-plugin-scenario-ovn
-    parent: neutron-tempest-plugin-scenario
+    parent: neutron-tempest-plugin-scenario-nested-switch
     timeout: 10800
     vars:
       network_api_extensions: *api_extensions
@@ -478,12 +550,16 @@
         '{{ devstack_log_dir }}/ovsdb-server-sb.log': 'logs'
     irrelevant-files:
       - ^(test-|)requirements.txt$
+      - lower-constraints.txt
       - ^releasenotes/.*$
       - ^doc/.*$
       - ^setup.cfg$
       - ^.*\.rst$
+      - ^.*\.conf\.sample$
       - ^neutron/locale/.*$
       - ^neutron/tests/unit/.*$
+      - ^neutron/tests/fullstack/.*
+      - ^neutron/tests/functional/.*
       - ^tools/.*$
       - ^tox.ini$
       - ^neutron/agent/dhcp/.*$
@@ -501,7 +577,18 @@
       - ^neutron/plugins/ml2/drivers/openvswitch/.*$
       - ^neutron/plugins/ml2/drivers/macvtap/.*$
       - ^neutron/plugins/ml2/drivers/mech_sriov/.*$
+      - ^neutron/services/qos/drivers/linuxbridge/.*$
+      - ^neutron/services/qos/drivers/openvswitch/.*$
+      - ^neutron/services/trunk/drivers/linuxbridge/.*$
+      - ^neutron/services/trunk/drivers/openvswitch/.*$
       - ^neutron/scheduler/.*$
+      - ^neutron_tempest_plugin/api/test_.*$
+      - ^neutron_tempest_plugin/(bgpvpn|fwaas|neutron_dynamic_routing|sfc|tap_as_a_service|vpnaas).*$
+      - ^neutron_tempest_plugin/services/bgp/.*$
+      - ^rally-jobs/.*$
+      - ^vagrant/.*$
+      - ^zuul.d/(queens|rocky|stein|train|ussuri)_jobs.yaml$
+      - ^zuul.d/base-nested-switch.yaml$
 
 - job:
     name: neutron-tempest-plugin-dvr-multinode-scenario
@@ -532,6 +619,8 @@
         NETWORK_API_EXTENSIONS: "{{ (network_api_extensions_common + network_api_extensions_dvr) | join(',') }}"
         PHYSICAL_NETWORK: default
         CIRROS_VERSION: 0.5.1
+        DEFAULT_IMAGE_NAME: cirros-0.5.1-x86_64-uec
+        DEFAULT_IMAGE_FILE_NAME: cirros-0.5.1-x86_64-uec.tar.gz
         IMAGE_URLS: https://cloud-images.ubuntu.com/minimal/releases/focal/release/ubuntu-20.04-minimal-cloudimg-amd64.img
         ADVANCED_IMAGE_NAME: ubuntu-20.04-minimal-cloudimg-amd64
         ADVANCED_INSTANCE_TYPE: ntp_image_256M
@@ -692,7 +781,7 @@
 
 - job:
     name: neutron-tempest-plugin-designate-scenario
-    parent: neutron-tempest-plugin-scenario
+    parent: neutron-tempest-plugin-scenario-nested-switch
     description: Neutron designate integration scenario
     required-projects:
       - openstack/designate
@@ -718,12 +807,16 @@
       tempest_test_regex: ^neutron_tempest_plugin\.scenario\.test_dns_integration
     irrelevant-files:
       - ^(test-|)requirements.txt$
+      - lower-constraints.txt
       - ^releasenotes/.*$
       - ^doc/.*$
       - ^setup.cfg$
       - ^.*\.rst$
+      - ^.*\.conf\.sample$
       - ^neutron/locale/.*$
       - ^neutron/tests/unit/.*$
+      - ^neutron/tests/fullstack/.*
+      - ^neutron/tests/functional/.*
       - ^tools/.*$
       - ^tox.ini$
       - ^neutron/agent/.*$
@@ -732,6 +825,13 @@
       - ^neutron/plugins/ml2/drivers/.*$
       - ^neutron/scheduler/.*$
       - ^neutron/services/(?!externaldns).*$
+      - ^neutron_tempest_plugin/api/test_.*$
+      - ^neutron_tempest_plugin/(bgpvpn|fwaas|neutron_dynamic_routing|sfc|tap_as_a_service|vpnaas).*$
+      - ^neutron_tempest_plugin/services/bgp/.*$
+      - ^rally-jobs/.*$
+      - ^vagrant/.*$
+      - ^zuul.d/(queens|rocky|stein|train|ussuri)_jobs.yaml$
+      - ^zuul.d/base-nested-switch.yaml$
 
 - job:
     name: neutron-tempest-plugin-sfc
@@ -775,6 +875,27 @@
       # https://bugs.launchpad.net/neutron/+bug/1851500
       # https://bugs.launchpad.net/networking-sfc/+bug/1660366
       tempest_concurrency: 1
+    irrelevant-files:
+      - ^(test-|)requirements.txt$
+      - lower-constraints.txt
+      - ^releasenotes/.*$
+      - ^doc/.*$
+      - ^.*\.conf\.sample$
+      - ^setup.cfg$
+      - ^.*\.rst$
+      - ^neutron/locale/.*$
+      - ^neutron/tests/unit/.*$
+      - ^neutron/tests/fullstack/.*
+      - ^neutron/tests/functional/.*
+      - ^neutron_tempest_plugin/api/test_.*$
+      - ^neutron_tempest_plugin/(bgpvpn|fwaas|neutron_dynamic_routing|tap_as_a_service|vpnaas).*$
+      - ^neutron_tempest_plugin/services/bgp/.*$
+      - ^tools/.*$
+      - ^tox.ini$
+      - ^rally-jobs/.*$
+      - ^vagrant/.*$
+      - ^zuul.d/(queens|rocky|stein|train|ussuri)_jobs.yaml$
+      - ^zuul.d/base-nested-switch.yaml$
 
 - job:
     name: neutron-tempest-plugin-bgpvpn-bagpipe
@@ -814,6 +935,27 @@
       devstack_plugins:
         networking-bgpvpn: https://git.openstack.org/openstack/networking-bgpvpn
         networking-bagpipe: https://git.openstack.org/openstack/networking-bagpipe
+    irrelevant-files:
+      - ^(test-|)requirements.txt$
+      - lower-constraints.txt
+      - ^releasenotes/.*$
+      - ^doc/.*$
+      - ^.*\.conf\.sample$
+      - ^setup.cfg$
+      - ^.*\.rst$
+      - ^neutron/locale/.*$
+      - ^neutron/tests/unit/.*$
+      - ^neutron/tests/fullstack/.*
+      - ^neutron/tests/functional/.*
+      - ^neutron_tempest_plugin/api/test_.*$
+      - ^neutron_tempest_plugin/(fwaas|neutron_dynamic_routing|sfc|tap_as_a_service|vpnaas).*$
+      - ^neutron_tempest_plugin/services/bgp/.*$
+      - ^tools/.*$
+      - ^tox.ini$
+      - ^rally-jobs/.*$
+      - ^vagrant/.*$
+      - ^zuul.d/(queens|rocky|stein|train|ussuri)_jobs.yaml$
+      - ^zuul.d/base-nested-switch.yaml$
 
 - job:
     name: neutron-tempest-plugin-dynamic-routing
@@ -859,6 +1001,26 @@
         neutron-dr-agent: true
       tempest_concurrency: 1
       tempest_test_regex: ^neutron_tempest_plugin\.neutron_dynamic_routing
+    irrelevant-files:
+      - ^(test-|)requirements.txt$
+      - lower-constraints.txt
+      - ^releasenotes/.*$
+      - ^doc/.*$
+      - ^.*\.conf\.sample$
+      - ^setup.cfg$
+      - ^.*\.rst$
+      - ^neutron/locale/.*$
+      - ^neutron/tests/unit/.*$
+      - ^neutron/tests/fullstack/.*
+      - ^neutron/tests/functional/.*
+      - ^neutron_tempest_plugin/api/test_.*$
+      - ^neutron_tempest_plugin/(bgpvpn|fwaas|sfc|tap_as_a_service|vpnaas).*$
+      - ^tools/.*$
+      - ^tox.ini$
+      - ^rally-jobs/.*$
+      - ^vagrant/.*$
+      - ^zuul.d/(queens|rocky|stein|train|ussuri)_jobs.yaml$
+      - ^zuul.d/base-nested-switch.yaml$
 
 - job:
     name: neutron-tempest-plugin-vpnaas
@@ -898,6 +1060,27 @@
         q-meta: true
         q-metering: true
         q-l3: true
+    irrelevant-files:
+      - ^(test-|)requirements.txt$
+      - lower-constraints.txt
+      - ^releasenotes/.*$
+      - ^doc/.*$
+      - ^.*\.conf\.sample$
+      - ^setup.cfg$
+      - ^.*\.rst$
+      - ^neutron/locale/.*$
+      - ^neutron/tests/unit/.*$
+      - ^neutron/tests/fullstack/.*
+      - ^neutron/tests/functional/.*
+      - ^neutron_tempest_plugin/api/test_.*$
+      - ^neutron_tempest_plugin/(bgpvpn|fwaas|neutron_dynamic_routing|sfc|tap_as_a_service).*$
+      - ^neutron_tempest_plugin/services/bgp/.*$
+      - ^tools/.*$
+      - ^tox.ini$
+      - ^rally-jobs/.*$
+      - ^vagrant/.*$
+      - ^zuul.d/(queens|rocky|stein|train|ussuri)_jobs.yaml$
+      - ^zuul.d/base-nested-switch.yaml$
 
 - job:
     name: neutron-tempest-plugin-tap-as-a-service
@@ -981,10 +1164,24 @@
         taas_openvswitch_agent: true
         tempest: true
         dstat: true
-    irrelevant-files: &tempest-irrelevant-files
+    irrelevant-files:
       - ^(test-|)requirements.txt$
+      - lower-constraints.txt
       - ^releasenotes/.*$
       - ^doc/.*$
+      - ^.*\.conf\.sample$
+      - ^setup.cfg$
       - ^.*\.rst$
+      - ^neutron/locale/.*$
+      - ^neutron/tests/unit/.*$
+      - ^neutron/tests/fullstack/.*
+      - ^neutron/tests/functional/.*
+      - ^neutron_tempest_plugin/api/test_.*$
+      - ^neutron_tempest_plugin/(bgpvpn|fwaas|neutron_dynamic_routing|sfc|vpnaas).*$
+      - ^neutron_tempest_plugin/services/bgp/.*$
       - ^tools/.*$
       - ^tox.ini$
+      - ^rally-jobs/.*$
+      - ^vagrant/.*$
+      - ^zuul.d/(queens|rocky|stein|train|ussuri)_jobs.yaml$
+      - ^zuul.d/base-nested-switch.yaml$
diff --git a/zuul.d/project.yaml b/zuul.d/project.yaml
index 031860f..caf83da 100644
--- a/zuul.d/project.yaml
+++ b/zuul.d/project.yaml
@@ -158,45 +158,59 @@
         - neutron-tempest-plugin-dvr-multinode-scenario-wallaby
 
 
+- project-template:
+    name: neutron-tempest-plugin-jobs-xena
+    check:
+      jobs:
+        - neutron-tempest-plugin-api-xena
+        - neutron-tempest-plugin-scenario-linuxbridge-xena
+        - neutron-tempest-plugin-scenario-openvswitch-xena
+        - neutron-tempest-plugin-scenario-openvswitch-iptables_hybrid-xena
+        - neutron-tempest-plugin-scenario-ovn-xena
+        - neutron-tempest-plugin-designate-scenario-xena
+    gate:
+      jobs:
+        - neutron-tempest-plugin-api-xena
+    #TODO(slaweq): Move neutron-tempest-plugin-dvr-multinode-scenario out of
+    #              the experimental queue when it will be more stable
+    experimental:
+      jobs:
+        - neutron-tempest-plugin-dvr-multinode-scenario-xena
+
+
 - project:
     templates:
       - build-openstack-docs-pti
       - neutron-tempest-plugin-jobs
-      - neutron-tempest-plugin-jobs-ussuri
       - neutron-tempest-plugin-jobs-victoria
       - neutron-tempest-plugin-jobs-wallaby
+      - neutron-tempest-plugin-jobs-xena
       - check-requirements
       - tempest-plugin-jobs
       - release-notes-jobs-python3
     check:
       jobs:
         - neutron-tempest-plugin-sfc
-        - neutron-tempest-plugin-sfc-ussuri
         - neutron-tempest-plugin-sfc-victoria
         - neutron-tempest-plugin-sfc-wallaby
+        - neutron-tempest-plugin-sfc-xena
         - neutron-tempest-plugin-bgpvpn-bagpipe
-        - neutron-tempest-plugin-bgpvpn-bagpipe-ussuri
         - neutron-tempest-plugin-bgpvpn-bagpipe-victoria
         - neutron-tempest-plugin-bgpvpn-bagpipe-wallaby
+        - neutron-tempest-plugin-bgpvpn-bagpipe-xena
         - neutron-tempest-plugin-dynamic-routing
-        - neutron-tempest-plugin-dynamic-routing-ussuri
         - neutron-tempest-plugin-dynamic-routing-victoria
         - neutron-tempest-plugin-dynamic-routing-wallaby
+        - neutron-tempest-plugin-dynamic-routing-xena
         - neutron-tempest-plugin-vpnaas
-        - neutron-tempest-plugin-vpnaas-ussuri
         - neutron-tempest-plugin-vpnaas-victoria
         - neutron-tempest-plugin-vpnaas-wallaby
+        - neutron-tempest-plugin-vpnaas-xena
         - neutron-tempest-plugin-tap-as-a-service
+        - neutron-tempest-plugin-tap-as-a-service-xena
 
     gate:
       jobs:
         - neutron-tempest-plugin-sfc
         - neutron-tempest-plugin-bgpvpn-bagpipe
         - neutron-tempest-plugin-dynamic-routing
-
-    experimental:
-      jobs:
-        - neutron-tempest-plugin-fwaas-ussuri:
-            # TODO(slaweq): switch it to be voting when bug
-            # https://bugs.launchpad.net/neutron/+bug/1858645 will be fixed
-            voting: false
diff --git a/zuul.d/queens_jobs.yaml b/zuul.d/queens_jobs.yaml
index 0b56b32..214df60 100644
--- a/zuul.d/queens_jobs.yaml
+++ b/zuul.d/queens_jobs.yaml
@@ -86,6 +86,8 @@
         NEUTRON_DEPLOY_MOD_WSGI: false
         USE_PYTHON3: false
         CIRROS_VERSION: 0.3.5
+        DEFAULT_IMAGE_NAME: cirros-0.3.5-x86_64-uec
+        DEFAULT_IMAGE_FILE_NAME: cirros-0.3.5-x86_64-uec.tar.gz
         NETWORK_API_EXTENSIONS: "{{ (network_api_extensions_common + network_api_extensions_tempest) | join(',') }}"
         TEMPEST_PLUGINS: /opt/stack/neutron-tempest-plugin
         Q_AGENT: openvswitch
@@ -139,9 +141,17 @@
       devstack_localrc:
         USE_PYTHON3: false
         CIRROS_VERSION: 0.3.5
+        DEFAULT_IMAGE_NAME: cirros-0.3.5-x86_64-uec
+        DEFAULT_IMAGE_FILE_NAME: cirros-0.3.5-x86_64-uec.tar.gz
         NETWORK_API_EXTENSIONS: "{{ network_api_extensions | join(',') }}"
         TEMPEST_PLUGINS: /opt/stack/neutron-tempest-plugin
+        # NOTE(slaweq) some tests are not running fine with ubuntu minimal on
+        # Queens
+        IMAGE_URLS: https://cloud-images.ubuntu.com/releases/bionic/release/ubuntu-18.04-server-cloudimg-amd64.img
+        ADVANCED_IMAGE_NAME: ubuntu-18.04-server-cloudimg-amd64
         ADVANCED_INSTANCE_TYPE: ds512M
+        ADVANCED_INSTANCE_USER: ubuntu
+        CUSTOMIZE_IMAGE: false
 
 - job:
     name: neutron-tempest-plugin-scenario-linuxbridge-queens
@@ -167,10 +177,18 @@
       devstack_localrc:
         USE_PYTHON3: false
         CIRROS_VERSION: 0.3.5
+        DEFAULT_IMAGE_NAME: cirros-0.3.5-x86_64-uec
+        DEFAULT_IMAGE_FILE_NAME: cirros-0.3.5-x86_64-uec.tar.gz
         Q_AGENT: linuxbridge
         NETWORK_API_EXTENSIONS: "{{ network_api_extensions | join(',') }}"
         TEMPEST_PLUGINS: /opt/stack/neutron-tempest-plugin
+        # NOTE(slaweq) some tests are not running fine with ubuntu minimal on
+        # Queens
+        IMAGE_URLS: https://cloud-images.ubuntu.com/releases/bionic/release/ubuntu-18.04-server-cloudimg-amd64.img
+        ADVANCED_IMAGE_NAME: ubuntu-18.04-server-cloudimg-amd64
         ADVANCED_INSTANCE_TYPE: ds512M
+        ADVANCED_INSTANCE_USER: ubuntu
+        CUSTOMIZE_IMAGE: false
       devstack_local_conf:
         post-config:
           $NEUTRON_CONF:
@@ -220,6 +238,8 @@
       devstack_localrc:
         USE_PYTHON3: false
         CIRROS_VERSION: 0.3.5
+        DEFAULT_IMAGE_NAME: cirros-0.3.5-x86_64-uec
+        DEFAULT_IMAGE_FILE_NAME: cirros-0.3.5-x86_64-uec.tar.gz
         TEMPEST_PLUGINS: /opt/stack/neutron-tempest-plugin
 
 - job:
@@ -244,5 +264,7 @@
       devstack_localrc:
         USE_PYTHON3: false
         CIRROS_VERSION: 0.3.5
+        DEFAULT_IMAGE_NAME: cirros-0.3.5-x86_64-uec
+        DEFAULT_IMAGE_FILE_NAME: cirros-0.3.5-x86_64-uec.tar.gz
         TEMPEST_PLUGINS: '"/opt/stack/designate-tempest-plugin /opt/stack/neutron-tempest-plugin"'
         ADVANCED_INSTANCE_TYPE: ds512M
diff --git a/zuul.d/rocky_jobs.yaml b/zuul.d/rocky_jobs.yaml
index 83b2f26..9915575 100644
--- a/zuul.d/rocky_jobs.yaml
+++ b/zuul.d/rocky_jobs.yaml
@@ -167,7 +167,13 @@
         Q_ML2_PLUGIN_MECHANISM_DRIVERS: openvswitch
         NETWORK_API_EXTENSIONS: "{{ network_api_extensions | join(',') }}"
         TEMPEST_PLUGINS: /opt/stack/neutron-tempest-plugin
+        # NOTE(slaweq) some tests are not running fine with ubuntu minimal on
+        # Rocky
+        IMAGE_URLS: https://cloud-images.ubuntu.com/releases/bionic/release/ubuntu-18.04-server-cloudimg-amd64.img
+        ADVANCED_IMAGE_NAME: ubuntu-18.04-server-cloudimg-amd64
         ADVANCED_INSTANCE_TYPE: ds512M
+        ADVANCED_INSTANCE_USER: ubuntu
+        CUSTOMIZE_IMAGE: false
       devstack_local_conf:
         post-config:
           $NEUTRON_CONF:
@@ -194,9 +200,21 @@
             neutron_plugin_options:
               available_type_drivers: flat,vlan,local,vxlan
               firewall_driver: openvswitch
-      tempest_black_regex: "\
+      # NOTE(bcafarel): filtering out unstable tests or tests with known
+      # issues in the used pinned version for this EM branch
+      tempest_black_regex: &rocky_tempest_exclude "\
+          (^neutron_tempest_plugin.scenario.admin.test_floatingip.FloatingIpTestCasesAdmin.test_two_vms_fips)|\
+          (^neutron_tempest_plugin.scenario.test_floatingip.FloatingIPQosTest.test_qos)|\
+          (^neutron_tempest_plugin.scenario.test_internal_dns.InternalDNSTest.test_dns_domain_and_name)|\
           (^neutron_tempest_plugin.scenario.test_port_forwardings.PortForwardingTestJSON.test_port_forwarding_to_2_servers)|\
-          (^neutron_tempest_plugin.scenario.test_security_groups.NetworkSecGroupTest.test_multiple_ports_portrange_remote)"
+          (^neutron_tempest_plugin.scenario.test_ports.PortsTest.test_previously_used_port)|\
+          (^neutron_tempest_plugin.scenario.test_security_groups.NetworkSecGroupTest.test_ip_prefix)|\
+          (^neutron_tempest_plugin.scenario.test_security_groups.NetworkSecGroupTest.test_multiple_ports_portrange_remote)|\
+          (^neutron_tempest_plugin.scenario.test_security_groups.NetworkSecGroupTest.test_multiple_ports_secgroup_inheritance)|\
+          (^neutron_tempest_plugin.scenario.test_security_groups.NetworkSecGroupTest.test_remote_group)|\
+          (^neutron_tempest_plugin.scenario.test_trunk.TrunkTest.test_subport_connectivity)|\
+          (^tempest.api.compute.servers.test_attach_interfaces.AttachInterfacesTestJSON.test_reassign_port_between_servers)|\
+          (^tempest.api.compute.servers.test_attach_interfaces.AttachInterfacesUnderV243Test.test_add_remove_fixed_ip)"
     branches:
       - stable/rocky
     irrelevant-files: &openvswitch-scenario-irrelevant-files
@@ -207,6 +225,8 @@
       - ^.*\.rst$
       - ^neutron/locale/.*$
       - ^neutron/tests/unit/.*$
+      - ^neutron/tests/fullstack/.*
+      - ^neutron/tests/functional/.*
       - ^tools/.*$
       - ^tox.ini$
       - ^neutron/agent/windows/.*$
@@ -265,7 +285,13 @@
         Q_ML2_TENANT_NETWORK_TYPE: vxlan
         Q_ML2_PLUGIN_MECHANISM_DRIVERS: openvswitch
         TEMPEST_PLUGINS: /opt/stack/neutron-tempest-plugin
+        # NOTE(slaweq) some tests are not running fine with ubuntu minimal on
+        # Rocky
+        IMAGE_URLS: https://cloud-images.ubuntu.com/releases/bionic/release/ubuntu-18.04-server-cloudimg-amd64.img
+        ADVANCED_IMAGE_NAME: ubuntu-18.04-server-cloudimg-amd64
         ADVANCED_INSTANCE_TYPE: ds512M
+        ADVANCED_INSTANCE_USER: ubuntu
+        CUSTOMIZE_IMAGE: false
       devstack_local_conf:
         post-config:
           $NEUTRON_CONF:
@@ -294,13 +320,7 @@
             neutron_plugin_options:
               available_type_drivers: flat,vlan,local,vxlan
               firewall_driver: iptables_hybrid
-      # TODO(bcafarel): remove trunks subport_connectivity test from blacklist
-      # when bug https://bugs.launchpad.net/neutron/+bug/1838760 will be fixed
-      # NOTE(bcafarel): other are newer tests, unstable on rocky branch
-      tempest_black_regex: "\
-          (^neutron_tempest_plugin.scenario.test_trunk.TrunkTest.test_subport_connectivity)|\
-          (^neutron_tempest_plugin.scenario.test_port_forwardings.PortForwardingTestJSON.test_port_forwarding_to_2_servers)|\
-          (^neutron_tempest_plugin.scenario.test_security_groups.NetworkSecGroupTest.test_multiple_ports_portrange_remote)"
+      tempest_black_regex: *rocky_tempest_exclude
     branches:
       - stable/rocky
     irrelevant-files: &iptables_hybrid_irrelevant_files
@@ -311,6 +331,8 @@
       - ^.*\.rst$
       - ^neutron/locale/.*$
       - ^neutron/tests/unit/.*$
+      - ^neutron/tests/fullstack/.*
+      - ^neutron/tests/functional/.*
       - ^tools/.*$
       - ^tox.ini$
       - ^neutron/agent/linux/openvswitch_firewall/.*$
@@ -356,7 +378,13 @@
         Q_AGENT: linuxbridge
         NETWORK_API_EXTENSIONS: "{{ network_api_extensions | join(',') }}"
         TEMPEST_PLUGINS: /opt/stack/neutron-tempest-plugin
+        # NOTE(slaweq) some tests are not running fine with ubuntu minimal on
+        # Rocky
+        IMAGE_URLS: https://cloud-images.ubuntu.com/releases/bionic/release/ubuntu-18.04-server-cloudimg-amd64.img
+        ADVANCED_IMAGE_NAME: ubuntu-18.04-server-cloudimg-amd64
         ADVANCED_INSTANCE_TYPE: ds512M
+        ADVANCED_INSTANCE_USER: ubuntu
+        CUSTOMIZE_IMAGE: false
       devstack_local_conf:
         post-config:
           $NEUTRON_CONF:
@@ -382,10 +410,7 @@
             neutron_plugin_options:
               available_type_drivers: flat,vlan,local,vxlan
               q_agent: None
-      # NOTE(bcafarel): newer tests, unstable on rocky branch
-      tempest_black_regex: "\
-          (^neutron_tempest_plugin.scenario.test_port_forwardings.PortForwardingTestJSON.test_port_forwarding_to_2_servers)|\
-          (^neutron_tempest_plugin.scenario.test_security_groups.NetworkSecGroupTest.test_multiple_ports_portrange_remote)"
+      tempest_black_regex: *rocky_tempest_exclude
     branches:
       - stable/rocky
 
@@ -439,6 +464,8 @@
         NETWORK_API_EXTENSIONS: "{{ (network_api_extensions_common + network_api_extensions_dvr) | join(',') }}"
         PHYSICAL_NETWORK: default
         CIRROS_VERSION: 0.5.1
+        DEFAULT_IMAGE_NAME: cirros-0.5.1-x86_64-uec
+        DEFAULT_IMAGE_FILE_NAME: cirros-0.5.1-x86_64-uec.tar.gz
         IMAGE_URLS: https://cloud-images.ubuntu.com/releases/bionic/release/ubuntu-18.04-server-cloudimg-amd64.img
         ADVANCED_IMAGE_NAME: ubuntu-18.04-server-cloudimg-amd64
         ADVANCED_INSTANCE_TYPE: ds512M
@@ -525,10 +552,7 @@
               l3_agent_mode: dvr_snat
               firewall_driver: openvswitch
       branch_override: stable/rocky
-      # NOTE(bcafarel): newer tests, unstable on rocky branch
-      tempest_black_regex: "\
-          (^neutron_tempest_plugin.scenario.test_port_forwardings.PortForwardingTestJSON.test_port_forwarding_to_2_servers)|\
-          (^neutron_tempest_plugin.scenario.test_security_groups.NetworkSecGroupTest.test_multiple_ports_portrange_remote)"
+      tempest_black_regex: *rocky_tempest_exclude
     branches:
       - stable/rocky
     group-vars: &multinode_scenario_group_vars_rocky
@@ -616,6 +640,9 @@
         USE_PYTHON3: false
         TEMPEST_PLUGINS: '"/opt/stack/designate-tempest-plugin /opt/stack/neutron-tempest-plugin"'
         ADVANCED_INSTANCE_TYPE: ds512M
+      # NOTE(bcafarel): filtering out unstable tests or tests with known
+      # issues in the used pinned version for this EM branch
+      tempest_black_regex: "(^neutron_tempest_plugin.scenario.test_dns_integration.DNSIntegrationAdminTests.test_port_on_special_network)"
     branches:
       - stable/rocky
 
diff --git a/zuul.d/stein_jobs.yaml b/zuul.d/stein_jobs.yaml
index f505a90..b229296 100644
--- a/zuul.d/stein_jobs.yaml
+++ b/zuul.d/stein_jobs.yaml
@@ -233,6 +233,8 @@
       - ^.*\.rst$
       - ^neutron/locale/.*$
       - ^neutron/tests/unit/.*$
+      - ^neutron/tests/fullstack/.*
+      - ^neutron/tests/functional/.*
       - ^tools/.*$
       - ^tox.ini$
       - ^neutron/agent/linux/openvswitch_firewall/.*$
@@ -323,6 +325,8 @@
       - ^.*\.rst$
       - ^neutron/locale/.*$
       - ^neutron/tests/unit/.*$
+      - ^neutron/tests/fullstack/.*
+      - ^neutron/tests/functional/.*
       - ^tools/.*$
       - ^tox.ini$
       - ^neutron/agent/linux/openvswitch_firewall/.*$
diff --git a/zuul.d/ussuri_jobs.yaml b/zuul.d/ussuri_jobs.yaml
index 5c5881e..a5a9c7d 100644
--- a/zuul.d/ussuri_jobs.yaml
+++ b/zuul.d/ussuri_jobs.yaml
@@ -3,6 +3,11 @@
     parent: neutron-tempest-plugin-api
     nodeset: openstack-single-node-bionic
     override-checkout: stable/ussuri
+    required-projects: &required-projects-ussuri
+      - openstack/neutron
+      - name: openstack/neutron-tempest-plugin
+        override-checkout: 1.8.0
+      - openstack/tempest
     vars:
       devstack_services:
         # Disable OVN services
@@ -128,6 +133,7 @@
     parent: neutron-tempest-plugin-scenario-openvswitch
     nodeset: openstack-single-node-bionic
     override-checkout: stable/ussuri
+    required-projects: *required-projects-ussuri
     vars:
       branch_override: stable/ussuri
       network_api_extensions: *api_extensions
@@ -153,6 +159,7 @@
     parent: neutron-tempest-plugin-scenario-openvswitch-iptables_hybrid
     nodeset: openstack-single-node-bionic
     override-checkout: stable/ussuri
+    required-projects: *required-projects-ussuri
     vars:
       branch_override: stable/ussuri
       network_api_extensions: *api_extensions
@@ -177,6 +184,7 @@
     parent: neutron-tempest-plugin-scenario-linuxbridge
     nodeset: openstack-single-node-bionic
     override-checkout: stable/ussuri
+    required-projects: *required-projects-ussuri
     vars:
       branch_override: stable/ussuri
       network_api_extensions: *api_extensions
@@ -201,6 +209,7 @@
     parent: neutron-tempest-plugin-scenario-ovn
     nodeset: openstack-single-node-bionic
     override-checkout: stable/ussuri
+    required-projects: *required-projects-ussuri
     vars:
       branch_override: stable/ussuri
       network_api_extensions: *api_extensions
@@ -226,6 +235,7 @@
     parent: neutron-tempest-plugin-dvr-multinode-scenario
     nodeset: openstack-two-node-bionic
     override-checkout: stable/ussuri
+    required-projects: *required-projects-ussuri
     vars:
       network_api_extensions_common: *api_extensions
       branch_override: stable/ussuri
@@ -235,6 +245,12 @@
     parent: neutron-tempest-plugin-designate-scenario
     nodeset: openstack-single-node-bionic
     override-checkout: stable/ussuri
+    required-projects:
+      - openstack/neutron
+      - name: openstack/neutron-tempest-plugin
+        override-checkout: 1.8.0
+      - openstack/tempest
+      - openstack/designate-tempest-plugin
     vars:
       branch_override: stable/ussuri
       network_api_extensions_common: *api_extensions
@@ -244,6 +260,7 @@
     parent: neutron-tempest-plugin-sfc
     nodeset: openstack-single-node-bionic
     override-checkout: stable/ussuri
+    required-projects: *required-projects-ussuri
     vars:
       branch_override: stable/ussuri
       network_api_extensions_common: *api_extensions
@@ -253,6 +270,7 @@
     parent: neutron-tempest-plugin-bgpvpn-bagpipe
     nodeset: openstack-single-node-bionic
     override-checkout: stable/ussuri
+    required-projects: *required-projects-ussuri
     vars:
       branch_override: stable/ussuri
       network_api_extensions: *api_extensions
@@ -266,7 +284,8 @@
     required-projects:
       - openstack/neutron-fwaas
       - openstack/neutron
-      - openstack/neutron-tempest-plugin
+      - name: openstack/neutron-tempest-plugin
+        override-checkout: 1.8.0
       - openstack/tempest
     vars:
       branch_override: stable/ussuri
@@ -285,6 +304,7 @@
     parent: neutron-tempest-plugin-dynamic-routing
     nodeset: openstack-single-node-bionic
     override-checkout: stable/ussuri
+    required-projects: *required-projects-ussuri
     vars:
       branch_override: stable/ussuri
       network_api_extensions_common: *api_extensions
@@ -294,6 +314,7 @@
     parent: neutron-tempest-plugin-vpnaas
     nodeset: openstack-single-node-bionic
     override-checkout: stable/ussuri
+    required-projects: *required-projects-ussuri
     vars:
       branch_override: stable/ussuri
       network_api_extensions_common: *api_extensions
diff --git a/zuul.d/xena_jobs.yaml b/zuul.d/xena_jobs.yaml
new file mode 100644
index 0000000..5ad63f0
--- /dev/null
+++ b/zuul.d/xena_jobs.yaml
@@ -0,0 +1,209 @@
+- job:
+    name: neutron-tempest-plugin-api-xena
+    parent: neutron-tempest-plugin-api
+    override-checkout: stable/xena
+    vars:
+      # TODO(slaweq): find a way to put this list of extensions in
+      # neutron repository and keep it different per branch,
+      # then it could be removed from here
+      network_api_extensions_common: &api_extensions
+        - address-group
+        - address-scope
+        - agent
+        - allowed-address-pairs
+        - auto-allocated-topology
+        - availability_zone
+        - binding
+        - default-subnetpools
+        - dhcp_agent_scheduler
+        - dns-domain-ports
+        - dns-integration
+        - dns-integration-domain-keywords
+        - empty-string-filtering
+        - expose-port-forwarding-in-fip
+        - expose-l3-conntrack-helper
+        - ext-gw-mode
+        - external-net
+        - extra_dhcp_opt
+        - extraroute
+        - extraroute-atomic
+        - filter-validation
+        - fip-port-details
+        - flavors
+        - floating-ip-port-forwarding
+        - floatingip-pools
+        - ip-substring-filtering
+        - l3-conntrack-helper
+        - l3-flavors
+        - l3-ha
+        - l3_agent_scheduler
+        - logging
+        - metering
+        - multi-provider
+        - net-mtu
+        - net-mtu-writable
+        - network-ip-availability
+        - network_availability_zone
+        - network-segment-range
+        - pagination
+        - port-device-profile
+        - port-resource-request
+        - port-mac-address-regenerate
+        - port-security
+        - port-security-groups-filtering
+        - project-id
+        - provider
+        - qos
+        - qos-bw-minimum-ingress
+        - qos-fip
+        - quotas
+        - quota_details
+        - rbac-address-group
+        - rbac-address-scope
+        - rbac-policies
+        - rbac-security-groups
+        - rbac-subnetpool
+        - router
+        - router-admin-state-down-before-update
+        - router_availability_zone
+        - security-group
+        - security-groups-remote-address-group
+        - segment
+        - service-type
+        - sorting
+        - standard-attr-description
+        - standard-attr-revisions
+        - standard-attr-segment
+        - standard-attr-tag
+        - standard-attr-timestamp
+        - subnet_allocation
+        - subnet-dns-publish-fixed-ip
+        - subnet-service-types
+        - subnetpool-prefix-ops
+        - tag-ports-during-bulk-creation
+        - trunk
+        - trunk-details
+        - uplink-status-propagation
+      network_api_extensions_tempest:
+        - dvr
+      network_available_features: &available_features
+        - ipv6_metadata
+
+- job:
+    name: neutron-tempest-plugin-scenario-openvswitch-xena
+    parent: neutron-tempest-plugin-scenario-openvswitch
+    override-checkout: stable/xena
+    vars:
+      branch_override: stable/xena
+      network_api_extensions: *api_extensions
+      network_available_features: *available_features
+      devstack_localrc:
+        NETWORK_API_EXTENSIONS: "{{ network_api_extensions | join(',') }}"
+      devstack_local_conf:
+        test-config:
+          $TEMPEST_CONFIG:
+            network-feature-enabled:
+              available_features: "{{ network_available_features | join(',') }}"
+
+- job:
+    name: neutron-tempest-plugin-scenario-openvswitch-iptables_hybrid-xena
+    parent: neutron-tempest-plugin-scenario-openvswitch-iptables_hybrid
+    override-checkout: stable-xena
+    vars:
+      branch_override: stable-xena
+      network_api_extensions: *api_extensions
+      network_available_features: *available_features
+      devstack_localrc:
+        NETWORK_API_EXTENSIONS: "{{ network_api_extensions | join(',') }}"
+      devstack_local_conf:
+        test-config:
+          $TEMPEST_CONFIG:
+            network-feature-enabled:
+              available_features: "{{ network_available_features | join(',') }}"
+
+- job:
+    name: neutron-tempest-plugin-scenario-linuxbridge-xena
+    parent: neutron-tempest-plugin-scenario-linuxbridge
+    override-checkout: stable/xena
+    vars:
+      branch_override: stable/xena
+      network_api_extensions: *api_extensions
+      network_available_features: *available_features
+      devstack_localrc:
+        NETWORK_API_EXTENSIONS: "{{ network_api_extensions | join(',') }}"
+      devstack_local_conf:
+        test-config:
+          $TEMPEST_CONFIG:
+            network-feature-enabled:
+              available_features: "{{ network_available_features | join(',') }}"
+
+- job:
+    name: neutron-tempest-plugin-scenario-ovn-xena
+    parent: neutron-tempest-plugin-scenario-ovn
+    override-checkout: stable/xena
+    vars:
+      branch_override: stable/xena
+      network_api_extensions: *api_extensions
+      devstack_localrc:
+        NETWORK_API_EXTENSIONS: "{{ network_api_extensions | join(',') }}"
+      devstack_local_conf:
+        test-config:
+          $TEMPEST_CONFIG:
+            network-feature-enabled:
+              available_features: ""
+
+- job:
+    name: neutron-tempest-plugin-dvr-multinode-scenario-xena
+    parent: neutron-tempest-plugin-dvr-multinode-scenario
+    override-checkout: stable/xena
+    vars:
+      network_api_extensions_common: *api_extensions
+      branch_override: stable/xena
+
+- job:
+    name: neutron-tempest-plugin-designate-scenario-xena
+    parent: neutron-tempest-plugin-designate-scenario
+    override-checkout: stable/xena
+    vars:
+      branch_override: stable/xena
+      network_api_extensions_common: *api_extensions
+
+- job:
+    name: neutron-tempest-plugin-sfc-xena
+    parent: neutron-tempest-plugin-sfc
+    override-checkout: stable/xena
+    vars:
+      branch_override: stable/xena
+      network_api_extensions_common: *api_extensions
+
+- job:
+    name: neutron-tempest-plugin-bgpvpn-bagpipe-xena
+    parent: neutron-tempest-plugin-bgpvpn-bagpipe
+    override-checkout: stable/xena
+    vars:
+      branch_override: stable/xena
+      network_api_extensions: *api_extensions
+
+- job:
+    name: neutron-tempest-plugin-dynamic-routing-xena
+    parent: neutron-tempest-plugin-dynamic-routing
+    override-checkout: stable/xena
+    vars:
+      branch_override: stable/xena
+      network_api_extensions_common: *api_extensions
+
+- job:
+    name: neutron-tempest-plugin-vpnaas-xena
+    parent: neutron-tempest-plugin-vpnaas
+    override-checkout: stable/xena
+    vars:
+      branch_override: stable/xena
+      network_api_extensions_common: *api_extensions
+
+- job:
+    name: neutron-tempest-plugin-tap-as-a-service-xena
+    parent: neutron-tempest-plugin-tap-as-a-service
+    override-checkout: stable/xena
+    vars:
+      branch_override: stable/xena
+      network_api_extensions_common: *api_extensions