Merge "remove unused local variable"
diff --git a/neutron_tempest_plugin/api/base.py b/neutron_tempest_plugin/api/base.py
index 1b02211..7cf8dd4 100644
--- a/neutron_tempest_plugin/api/base.py
+++ b/neutron_tempest_plugin/api/base.py
@@ -478,7 +478,7 @@
         """
 
         if not cls.try_reserve_subnet_cidr(addr, **ipnetwork_kwargs):
-            raise ValueError('Subnet CIDR already reserved: %r'.format(
+            raise ValueError('Subnet CIDR already reserved: {0!r}'.format(
                 addr))
 
     @classmethod
@@ -558,6 +558,8 @@
         """Wrapper utility that returns a test port."""
         if CONF.network.port_vnic_type and 'binding:vnic_type' not in kwargs:
             kwargs['binding:vnic_type'] = CONF.network.port_vnic_type
+        if CONF.network.port_profile and 'binding:profile' not in kwargs:
+            kwargs['binding:profile'] = CONF.network.port_profile
         body = cls.client.create_port(network_id=network['id'],
                                       **kwargs)
         port = body['port']
diff --git a/neutron_tempest_plugin/bgpvpn/scenario/manager.py b/neutron_tempest_plugin/bgpvpn/scenario/manager.py
index 8a5f9f2..4ff1c0d 100644
--- a/neutron_tempest_plugin/bgpvpn/scenario/manager.py
+++ b/neutron_tempest_plugin/bgpvpn/scenario/manager.py
@@ -462,8 +462,8 @@
         port_map = [(p["id"], fxip["ip_address"])
                     for p in ports
                     for fxip in p["fixed_ips"]
-                    if netutils.is_valid_ipv4(fxip["ip_address"])
-                    and p['status'] in p_status]
+                    if netutils.is_valid_ipv4(fxip["ip_address"]) and
+                    p['status'] in p_status]
         inactive = [p for p in ports if p['status'] != 'ACTIVE']
         if inactive:
             LOG.warning("Instance has ports that are not ACTIVE: %s", inactive)
diff --git a/neutron_tempest_plugin/common/ip.py b/neutron_tempest_plugin/common/ip.py
index a286d6b..83cd3d9 100644
--- a/neutron_tempest_plugin/common/ip.py
+++ b/neutron_tempest_plugin/common/ip.py
@@ -295,7 +295,7 @@
     for address in list_ip_addresses(addresses=addresses, port=port):
         return address.device.name
 
-    msg = "Port %r fixed IPs not found on server.".format(port['id'])
+    msg = "Port {0!r} fixed IPs not found on server.".format(port['id'])
     raise ValueError(msg)
 
 
diff --git a/neutron_tempest_plugin/config.py b/neutron_tempest_plugin/config.py
index 2290d0f..c0e21c1 100644
--- a/neutron_tempest_plugin/config.py
+++ b/neutron_tempest_plugin/config.py
@@ -120,6 +120,18 @@
                     'This is required if advanced image has to be used in '
                     'tests.'),
 
+    # Enable/disable metadata over IPv6 tests. This feature naturally
+    # does not have an API extension, but at the time of first implementation
+    # it works only on victoria+ deployments with dhcp- and/or l3-agents
+    # (which in the gate is the same as non-ovn jobs).
+    cfg.BoolOpt('ipv6_metadata',
+                default=True,
+                help='Enable metadata over IPv6 tests where the feature is '
+                     'implemented, disable where it is not. Use this instead '
+                     'of network-feature-enabled.api_extensions, since API '
+                     'extensions do not make sense for a feature not '
+                     'exposed on the API.'),
+
     # Option for creating QoS policies configures as "shared".
     # The default is false in order to prevent undesired usage
     # while testing in parallel.
diff --git a/neutron_tempest_plugin/scenario/test_floatingip.py b/neutron_tempest_plugin/scenario/test_floatingip.py
index 7c59d3a..6d4d830 100644
--- a/neutron_tempest_plugin/scenario/test_floatingip.py
+++ b/neutron_tempest_plugin/scenario/test_floatingip.py
@@ -434,8 +434,7 @@
 
         # The FIP is now associated with the port of the second server.
         try:
-            common_utils.wait_until_true(_wait_for_fip_associated,
-                                         timeout=15, sleep=3)
+            common_utils.wait_until_true(_wait_for_fip_associated, sleep=3)
         except common_utils.WaitTimeout:
             self._log_console_output(servers[-1:])
             self.fail(
diff --git a/neutron_tempest_plugin/scenario/test_metadata.py b/neutron_tempest_plugin/scenario/test_metadata.py
new file mode 100644
index 0000000..c78ff69
--- /dev/null
+++ b/neutron_tempest_plugin/scenario/test_metadata.py
@@ -0,0 +1,142 @@
+# Copyright 2020 Ericsson Software Technology
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import collections
+
+from neutron_lib import constants as nlib_const
+from oslo_log import log as logging
+from tempest.lib.common.utils import data_utils
+from tempest.lib import decorators
+import testtools
+
+from neutron_tempest_plugin.common import ssh
+from neutron_tempest_plugin import config
+from neutron_tempest_plugin.scenario import base
+
+LOG = logging.getLogger(__name__)
+CONF = config.CONF
+
+Server = collections.namedtuple(
+    'Server', ['floating_ip', 'server', 'ssh_client'])
+
+
+class MetadataTest(base.BaseTempestTestCase):
+
+    """Test metadata access over IPv6 tenant subnet.
+
+    Please note that there is metadata over IPv4 test coverage in tempest:
+
+    tempest.scenario.test_server_basic_ops\
+        .TestServerBasicOps.test_server_basic_ops
+    """
+
+    credentials = ['primary', 'admin']
+    force_tenant_isolation = False
+
+    @classmethod
+    def resource_setup(cls):
+        super(MetadataTest, cls).resource_setup()
+        cls.rand_name = data_utils.rand_name(
+            cls.__name__.rsplit('.', 1)[-1])
+        cls.network = cls.create_network(name=cls.rand_name)
+        cls.subnet_v4 = cls.create_subnet(
+            network=cls.network, name=cls.rand_name)
+        cls.subnet_v6 = cls.create_subnet(
+            network=cls.network, name=cls.rand_name, ip_version=6)
+        cls.router = cls.create_router_by_client()
+        cls.create_router_interface(cls.router['id'], cls.subnet_v4['id'])
+        cls.create_router_interface(cls.router['id'], cls.subnet_v6['id'])
+        cls.keypair = cls.create_keypair(name=cls.rand_name)
+        cls.security_group = cls.create_security_group(name=cls.rand_name)
+        cls.create_loginable_secgroup_rule(cls.security_group['id'])
+
+    def _create_server_with_network(self, network, use_advanced_image=False):
+        port = self._create_server_port(network=network)
+        floating_ip = self.create_floatingip(port=port)
+        ssh_client = self._create_ssh_client(
+            floating_ip=floating_ip, use_advanced_image=use_advanced_image)
+        server = self._create_server(port=port,
+                                     use_advanced_image=use_advanced_image)
+        return Server(
+            floating_ip=floating_ip, server=server, ssh_client=ssh_client)
+
+    def _create_server_port(self, network=None, **params):
+        network = network or self.network
+        return self.create_port(network=network, name=self.rand_name,
+                                security_groups=[self.security_group['id']],
+                                **params)
+
+    def _create_server(self, port, use_advanced_image=False, **params):
+        if use_advanced_image:
+            flavor_ref = CONF.neutron_plugin_options.advanced_image_flavor_ref
+            image_ref = CONF.neutron_plugin_options.advanced_image_ref
+        else:
+            flavor_ref = CONF.compute.flavor_ref
+            image_ref = CONF.compute.image_ref
+        return self.create_server(flavor_ref=flavor_ref,
+                                  image_ref=image_ref,
+                                  key_name=self.keypair['name'],
+                                  networks=[{'port': port['id']}],
+                                  **params)['server']
+
+    def _create_ssh_client(self, floating_ip, use_advanced_image=False):
+        if use_advanced_image:
+            username = CONF.neutron_plugin_options.advanced_image_ssh_user
+        else:
+            username = CONF.validation.image_ssh_user
+        return ssh.Client(host=floating_ip['floating_ip_address'],
+                          username=username,
+                          pkey=self.keypair['private_key'])
+
+    def _assert_has_ssh_connectivity(self, ssh_client):
+        ssh_client.exec_command('true')
+
+    def _get_primary_interface(self, ssh_client):
+        out = ssh_client.exec_command(
+            "ip -6 -br address show scope link up | head -1 | cut -d ' ' -f1")
+        interface = out.strip()
+        if not interface:
+            self.fail(
+                'Could not find a single interface '
+                'with an IPv6 link-local address.')
+        return interface
+
+    @testtools.skipUnless(
+        (CONF.neutron_plugin_options.ipv6_metadata and
+         (CONF.neutron_plugin_options.advanced_image_ref or
+          CONF.neutron_plugin_options.default_image_is_advanced)),
+        'Advanced image and neutron_plugin_options.ipv6_metadata=True '
+        'is required to run this test.')
+    @decorators.idempotent_id('e680949a-f1cc-11ea-b49a-cba39bbbe5ad')
+    def test_metadata_routed(self):
+        use_advanced_image = (
+            not CONF.neutron_plugin_options.default_image_is_advanced)
+
+        vm = self._create_server_with_network(
+            self.network, use_advanced_image=use_advanced_image)
+        self.wait_for_server_active(server=vm.server)
+        self._assert_has_ssh_connectivity(vm.ssh_client)
+        interface = self._get_primary_interface(vm.ssh_client)
+
+        out = vm.ssh_client.exec_command(
+            'curl http://[%(address)s%%25%(interface)s]/' % {
+                'address': nlib_const.METADATA_V6_IP,
+                'interface': interface})
+        self.assertIn('latest', out)
+
+        out = vm.ssh_client.exec_command(
+            'curl http://[%(address)s%%25%(interface)s]/openstack/' % {
+                'address': nlib_const.METADATA_V6_IP,
+                'interface': interface})
+        self.assertIn('latest', out)
diff --git a/neutron_tempest_plugin/scenario/test_port_forwardings.py b/neutron_tempest_plugin/scenario/test_port_forwardings.py
index da1db1b..3158ea0 100644
--- a/neutron_tempest_plugin/scenario/test_port_forwardings.py
+++ b/neutron_tempest_plugin/scenario/test_port_forwardings.py
@@ -14,6 +14,7 @@
 #    under the License.
 
 from neutron_lib import constants
+from neutron_lib.utils import test
 from oslo_log import log
 from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
@@ -108,6 +109,7 @@
                     "Timed out waiting for message from server {!r} ".format(
                         server['id'])))
 
+    @test.unstable_test("bug 1896735")
     @decorators.idempotent_id('ab40fc48-ca8d-41a0-b2a3-f6679c847bfe')
     def test_port_forwarding_to_2_servers(self):
         udp_sg_rule = {'protocol': constants.PROTO_NAME_UDP,
@@ -123,6 +125,7 @@
         # And now test UDP port forwarding using nc
         self._test_udp_port_forwarding(servers)
 
+    @test.unstable_test("bug 1896735")
     @decorators.idempotent_id('aa19d46c-a4a6-11ea-bb37-0242ac130002')
     def test_port_forwarding_editing_and_deleting_tcp_rule(self):
         server = self._prepare_resources(
diff --git a/neutron_tempest_plugin/sfc/tests/scenario/test_sfc.py b/neutron_tempest_plugin/sfc/tests/scenario/test_sfc.py
index 995868a..2f091e0 100644
--- a/neutron_tempest_plugin/sfc/tests/scenario/test_sfc.py
+++ b/neutron_tempest_plugin/sfc/tests/scenario/test_sfc.py
@@ -14,6 +14,7 @@
 
 import time
 
+from neutron_lib.utils import test
 from oslo_log import log
 from tempest.common import utils
 from tempest import config
@@ -953,6 +954,7 @@
             username=self.ssh_user,
             private_key=self.keypair['private_key'])
 
+    @test.unstable_test("bug 1897753")
     @decorators.idempotent_id('f970f6b3-6541-47ac-a9ea-f769be1e21ba')
     @utils.services('compute', 'network')
     def test_update_port_pair_group_remove_port_pairs(self):
diff --git a/playbooks/linuxbridge-scenario-pre-run.yaml b/playbooks/linuxbridge-scenario-pre-run.yaml
new file mode 100644
index 0000000..26586f6
--- /dev/null
+++ b/playbooks/linuxbridge-scenario-pre-run.yaml
@@ -0,0 +1,7 @@
+- hosts: all
+  tasks:
+    # TODO(slaweq): remove it when nftables will support syntax for src and
+    # destination IP addresses in arp tables:
+    - include_role:
+        name: legacy_ebtables
+      when: ansible_distribution_release | lower == 'focal'
diff --git a/test-requirements.txt b/test-requirements.txt
index 6cbe947..bf1c626 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -2,7 +2,7 @@
 # of appearance. Changing the order has an impact on the overall integration
 # process, which may cause wedges in the gate later.
 
-hacking<0.13,>=0.12.0 # Apache-2.0
+hacking>=3.2.0,<3.3.0 # Apache-2.0
 
 coverage!=4.4,>=4.0 # Apache-2.0
 flake8-import-order==0.12 # LGPLv3
diff --git a/tox.ini b/tox.ini
index 760cc47..eecd16e 100644
--- a/tox.ini
+++ b/tox.ini
@@ -53,7 +53,8 @@
 # E128 continuation line under-indented for visual indent
 # E129 visually indented line with same indent as next logical line
 # N530 direct neutron imports not allowed
-ignore = E126,E128,E129,N530
+# W504 line break after binary operator
+ignore = E126,E128,E129,N530,W504
 # H106: Don't put vim configuration in source files
 # H203: Use assertIs(Not)None to check for None
 # H204: Use assert(Not)Equal to check for equality
diff --git a/zuul.d/base.yaml b/zuul.d/base.yaml
index db59ac0..77947fa 100644
--- a/zuul.d/base.yaml
+++ b/zuul.d/base.yaml
@@ -17,6 +17,8 @@
       devstack_localrc:
         USE_PYTHON3: true
         NETWORK_API_EXTENSIONS: "{{ (network_api_extensions_common + network_api_extensions_tempest) | join(',') }}"
+        CIRROS_VERSION: 0.5.1
+        BUILD_TIMEOUT: 784
       devstack_plugins:
         neutron: https://opendev.org/openstack/neutron.git
         neutron-tempest-plugin: https://opendev.org/openstack/neutron-tempest-plugin.git
@@ -100,8 +102,9 @@
       tempest_test_regex: ^neutron_tempest_plugin\.scenario
       devstack_localrc:
         PHYSICAL_NETWORK: default
-        IMAGE_URLS: https://cloud-images.ubuntu.com/releases/xenial/release/ubuntu-16.04-server-cloudimg-amd64-disk1.img
-        ADVANCED_IMAGE_NAME: ubuntu-16.04-server-cloudimg-amd64-disk1
+        CIRROS_VERSION: 0.5.1
+        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
         BUILD_TIMEOUT: 784
diff --git a/zuul.d/master_jobs.yaml b/zuul.d/master_jobs.yaml
index 45862c8..71dfcb2 100644
--- a/zuul.d/master_jobs.yaml
+++ b/zuul.d/master_jobs.yaml
@@ -158,6 +158,9 @@
     name: neutron-tempest-plugin-scenario-linuxbridge
     parent: neutron-tempest-plugin-scenario
     timeout: 10000
+    roles:
+      - zuul: openstack/neutron
+    pre-run: playbooks/linuxbridge-scenario-pre-run.yaml
     vars:
       network_api_extensions: *api_extensions
       devstack_localrc:
@@ -202,10 +205,6 @@
         ENABLE_CHASSIS_AS_GW: true
         OVN_L3_CREATE_PUBLIC_NETWORK: true
         OVN_DBS_LOG_LEVEL: dbg
-        # TODO(mjozefcz): Stop compiling OVS modules when meter action in kernel
-        # will be released in Ubuntu Bionic.
-        # More info: https://mail.openvswitch.org/pipermail/ovs-discuss/2018-December/048009.html
-        OVN_BUILD_MODULES: True
         ENABLE_TLS: True
         OVN_IGMP_SNOOPING_ENABLE: True
       devstack_services:
@@ -240,6 +239,7 @@
           $TEMPEST_CONFIG:
             neutron_plugin_options:
               available_type_drivers: local,flat,vlan,geneve
+              ipv6_metadata: False
               is_igmp_snooping_enabled: True
 
 - job:
@@ -271,8 +271,9 @@
         USE_PYTHON3: true
         NETWORK_API_EXTENSIONS: "{{ (network_api_extensions_common + network_api_extensions_dvr) | join(',') }}"
         PHYSICAL_NETWORK: default
-        IMAGE_URLS: https://cloud-images.ubuntu.com/releases/xenial/release/ubuntu-16.04-server-cloudimg-amd64-disk1.img
-        ADVANCED_IMAGE_NAME: ubuntu-16.04-server-cloudimg-amd64-disk1
+        CIRROS_VERSION: 0.5.1
+        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
         BUILD_TIMEOUT: 784
diff --git a/zuul.d/project.yaml b/zuul.d/project.yaml
index db4d6e1..afab1d3 100644
--- a/zuul.d/project.yaml
+++ b/zuul.d/project.yaml
@@ -3,7 +3,11 @@
     check:
       jobs:
         - neutron-tempest-plugin-api
-        - neutron-tempest-plugin-designate-scenario
+        - neutron-tempest-plugin-designate-scenario:
+            # TODO(slaweq): switch it to be voting when bug
+            # https://bugs.launchpad.net/neutron/+bug/1891309
+            # will be fixed
+            voting: false
         - neutron-tempest-plugin-scenario-linuxbridge
         - neutron-tempest-plugin-scenario-openvswitch
         - neutron-tempest-plugin-scenario-openvswitch-iptables_hybrid
@@ -44,7 +48,11 @@
     check:
       jobs:
         - neutron-tempest-plugin-api-rocky
-        - neutron-tempest-plugin-designate-scenario-rocky
+        - neutron-tempest-plugin-designate-scenario-rocky:
+            # TODO(slaweq): switch it to be voting when bug
+            # https://bugs.launchpad.net/neutron/+bug/1891309
+            # will be fixed
+            voting: false
         - neutron-tempest-plugin-scenario-linuxbridge-rocky
         - neutron-tempest-plugin-scenario-openvswitch-rocky
         - neutron-tempest-plugin-scenario-openvswitch-iptables_hybrid-rocky
@@ -63,7 +71,11 @@
     check:
       jobs:
         - neutron-tempest-plugin-api-stein
-        - neutron-tempest-plugin-designate-scenario-stein
+        - neutron-tempest-plugin-designate-scenario-stein:
+            # TODO(slaweq): switch it to be voting when bug
+            # https://bugs.launchpad.net/neutron/+bug/1891309
+            # will be fixed
+            voting: false
         - neutron-tempest-plugin-scenario-linuxbridge-stein
         - neutron-tempest-plugin-scenario-openvswitch-stein
         - neutron-tempest-plugin-scenario-openvswitch-iptables_hybrid-stein
@@ -82,7 +94,11 @@
     check:
       jobs:
         - neutron-tempest-plugin-api-train
-        - neutron-tempest-plugin-designate-scenario-train
+        - neutron-tempest-plugin-designate-scenario-train:
+            # TODO(slaweq): switch it to be voting when bug
+            # https://bugs.launchpad.net/neutron/+bug/1891309
+            # will be fixed
+            voting: false
         - neutron-tempest-plugin-scenario-linuxbridge-train
         - neutron-tempest-plugin-scenario-openvswitch-train
         - neutron-tempest-plugin-scenario-openvswitch-iptables_hybrid-train
@@ -101,7 +117,11 @@
     check:
       jobs:
         - neutron-tempest-plugin-api-ussuri
-        - neutron-tempest-plugin-designate-scenario-ussuri
+        - neutron-tempest-plugin-designate-scenario-ussuri:
+            # TODO(slaweq): switch it to be voting when bug
+            # https://bugs.launchpad.net/neutron/+bug/1891309
+            # will be fixed
+            voting: false
         - neutron-tempest-plugin-scenario-linuxbridge-ussuri
         - neutron-tempest-plugin-scenario-openvswitch-ussuri
         - neutron-tempest-plugin-scenario-openvswitch-iptables_hybrid-ussuri
@@ -131,9 +151,17 @@
         - neutron-tempest-plugin-sfc
         - neutron-tempest-plugin-sfc-train
         - neutron-tempest-plugin-sfc-ussuri
-        - neutron-tempest-plugin-bgpvpn-bagpipe
+        - neutron-tempest-plugin-bgpvpn-bagpipe:
+            # TODO(slaweq): switch it to be voting when bug
+            # https://bugs.launchpad.net/networking-bagpipe/+bug/1897408
+            # will be fixed
+            voting: false
         - neutron-tempest-plugin-bgpvpn-bagpipe-train
-        - neutron-tempest-plugin-bgpvpn-bagpipe-ussuri
+        - neutron-tempest-plugin-bgpvpn-bagpipe-ussuri:
+            # TODO(slaweq): switch it to be voting when bug
+            # https://bugs.launchpad.net/networking-bagpipe/+bug/1897408
+            # will be fixed
+            voting: false
         - neutron-tempest-plugin-fwaas-train:
             # TODO(slaweq): switch it to be voting when bug
             # https://bugs.launchpad.net/neutron/+bug/1858645 will be fixed
@@ -150,5 +178,8 @@
     gate:
       jobs:
         - neutron-tempest-plugin-sfc
-        - neutron-tempest-plugin-bgpvpn-bagpipe
+        # TODO(slaweq): make bgpvpn-bagpipe job gating again when
+        # https://bugs.launchpad.net/networking-bagpipe/+bug/1897408
+        # will be fixed
+        #- neutron-tempest-plugin-bgpvpn-bagpipe
         - neutron-tempest-plugin-dynamic-routing
diff --git a/zuul.d/stein_jobs.yaml b/zuul.d/stein_jobs.yaml
index 1c9e299..ff6ed38 100644
--- a/zuul.d/stein_jobs.yaml
+++ b/zuul.d/stein_jobs.yaml
@@ -1,6 +1,7 @@
 - job:
     name: neutron-tempest-plugin-api-stein
     parent: neutron-tempest-plugin-api
+    nodeset: openstack-single-node-bionic
     override-checkout: stable/stein
     vars:
       branch_override: stable/stein
@@ -78,36 +79,55 @@
 - job:
     name: neutron-tempest-plugin-scenario-openvswitch-stein
     parent: neutron-tempest-plugin-scenario-openvswitch
+    nodeset: openstack-single-node-bionic
     override-checkout: stable/stein
     vars:
       branch_override: stable/stein
       network_api_extensions: *api_extensions
       devstack_localrc:
         NETWORK_API_EXTENSIONS: "{{ network_api_extensions | join(',') }}"
+      devstack_local_conf:
+        test-config:
+          $TEMPEST_CONFIG:
+            neutron_plugin_options:
+              ipv6_metadata: False
 
 - job:
     name: neutron-tempest-plugin-scenario-openvswitch-iptables_hybrid-stein
     parent: neutron-tempest-plugin-scenario-openvswitch-iptables_hybrid
+    nodeset: openstack-single-node-bionic
     override-checkout: stable/stein
     vars:
       branch_override: stable/stein
       network_api_extensions: *api_extensions
       devstack_localrc:
         NETWORK_API_EXTENSIONS: "{{ network_api_extensions | join(',') }}"
+      devstack_local_conf:
+        test-config:
+          $TEMPEST_CONFIG:
+            neutron_plugin_options:
+              ipv6_metadata: False
 
 - job:
     name: neutron-tempest-plugin-scenario-linuxbridge-stein
     parent: neutron-tempest-plugin-scenario-linuxbridge
+    nodeset: openstack-single-node-bionic
     override-checkout: stable/stein
     vars:
       branch_override: stable/stein
       network_api_extensions: *api_extensions
       devstack_localrc:
         NETWORK_API_EXTENSIONS: "{{ network_api_extensions | join(',') }}"
+      devstack_local_conf:
+        test-config:
+          $TEMPEST_CONFIG:
+            neutron_plugin_options:
+              ipv6_metadata: False
 
 - job:
     name: neutron-tempest-plugin-dvr-multinode-scenario-stein
     parent: neutron-tempest-plugin-dvr-multinode-scenario
+    nodeset: openstack-two-node-bionic
     override-checkout: stable/stein
     vars:
       network_api_extensions_common: *api_extensions
@@ -116,6 +136,7 @@
 - job:
     name: neutron-tempest-plugin-designate-scenario-stein
     parent: neutron-tempest-plugin-designate-scenario
+    nodeset: openstack-single-node-bionic
     override-checkout: stable/stein
     required-projects:
       - openstack/devstack-gate
diff --git a/zuul.d/train_jobs.yaml b/zuul.d/train_jobs.yaml
index 132873b..a9cc5be 100644
--- a/zuul.d/train_jobs.yaml
+++ b/zuul.d/train_jobs.yaml
@@ -1,6 +1,7 @@
 - job:
     name: neutron-tempest-plugin-api-train
     parent: neutron-tempest-plugin-api
+    nodeset: openstack-single-node-bionic
     override-checkout: stable/train
     vars:
       branch_override: stable/train
@@ -83,36 +84,55 @@
 - job:
     name: neutron-tempest-plugin-scenario-openvswitch-train
     parent: neutron-tempest-plugin-scenario-openvswitch
+    nodeset: openstack-single-node-bionic
     override-checkout: stable/train
     vars:
       branch_override: stable/train
       network_api_extensions: *api_extensions
       devstack_localrc:
         NETWORK_API_EXTENSIONS: "{{ network_api_extensions | join(',') }}"
+      devstack_local_conf:
+        test-config:
+          $TEMPEST_CONFIG:
+            neutron_plugin_options:
+              ipv6_metadata: False
 
 - job:
     name: neutron-tempest-plugin-scenario-openvswitch-iptables_hybrid-train
     parent: neutron-tempest-plugin-scenario-openvswitch-iptables_hybrid
+    nodeset: openstack-single-node-bionic
     override-checkout: stable/train
     vars:
       branch_override: stable/train
       network_api_extensions: *api_extensions
       devstack_localrc:
         NETWORK_API_EXTENSIONS: "{{ network_api_extensions | join(',') }}"
+      devstack_local_conf:
+        test-config:
+          $TEMPEST_CONFIG:
+            neutron_plugin_options:
+              ipv6_metadata: False
 
 - job:
     name: neutron-tempest-plugin-scenario-linuxbridge-train
     parent: neutron-tempest-plugin-scenario-linuxbridge
+    nodeset: openstack-single-node-bionic
     override-checkout: stable/train
     vars:
       branch_override: stable/train
       network_api_extensions: *api_extensions
       devstack_localrc:
         NETWORK_API_EXTENSIONS: "{{ network_api_extensions | join(',') }}"
+      devstack_local_conf:
+        test-config:
+          $TEMPEST_CONFIG:
+            neutron_plugin_options:
+              ipv6_metadata: False
 
 - job:
     name: neutron-tempest-plugin-dvr-multinode-scenario-train
     parent: neutron-tempest-plugin-dvr-multinode-scenario
+    nodeset: openstack-two-node-bionic
     override-checkout: stable/train
     vars:
       network_api_extensions_common: *api_extensions
@@ -121,6 +141,7 @@
 - job:
     name: neutron-tempest-plugin-designate-scenario-train
     parent: neutron-tempest-plugin-designate-scenario
+    nodeset: openstack-single-node-bionic
     override-checkout: stable/train
     vars:
       branch_override: stable/train
@@ -129,6 +150,7 @@
 - job:
     name: neutron-tempest-plugin-sfc-train
     parent: neutron-tempest-plugin-sfc
+    nodeset: openstack-single-node-bionic
     override-checkout: stable/train
     vars:
       branch_override: stable/train
@@ -137,6 +159,7 @@
 - job:
     name: neutron-tempest-plugin-bgpvpn-bagpipe-train
     parent: neutron-tempest-plugin-bgpvpn-bagpipe
+    nodeset: openstack-single-node-bionic
     override-checkout: stable/train
     vars:
       branch_override: stable/train
@@ -145,6 +168,7 @@
 - job:
     name: neutron-tempest-plugin-fwaas-train
     parent: neutron-tempest-plugin-fwaas-ussuri
+    nodeset: openstack-single-node-bionic
     override-checkout: stable/train
     vars:
       branch_override: stable/train
diff --git a/zuul.d/ussuri_jobs.yaml b/zuul.d/ussuri_jobs.yaml
index a9e578e..135d9f5 100644
--- a/zuul.d/ussuri_jobs.yaml
+++ b/zuul.d/ussuri_jobs.yaml
@@ -1,6 +1,7 @@
 - job:
     name: neutron-tempest-plugin-api-ussuri
     parent: neutron-tempest-plugin-api
+    nodeset: openstack-single-node-bionic
     override-checkout: stable/ussuri
     vars:
       branch_override: stable/ussuri
@@ -87,46 +88,72 @@
 - job:
     name: neutron-tempest-plugin-scenario-openvswitch-ussuri
     parent: neutron-tempest-plugin-scenario-openvswitch
+    nodeset: openstack-single-node-bionic
     override-checkout: stable/ussuri
     vars:
       branch_override: stable/ussuri
       network_api_extensions: *api_extensions
       devstack_localrc:
         NETWORK_API_EXTENSIONS: "{{ network_api_extensions | join(',') }}"
+      devstack_local_conf:
+        test-config:
+          $TEMPEST_CONFIG:
+            neutron_plugin_options:
+              ipv6_metadata: False
 
 - job:
     name: neutron-tempest-plugin-scenario-openvswitch-iptables_hybrid-ussuri
     parent: neutron-tempest-plugin-scenario-openvswitch-iptables_hybrid
+    nodeset: openstack-single-node-bionic
     override-checkout: stable/ussuri
     vars:
       branch_override: stable/ussuri
       network_api_extensions: *api_extensions
       devstack_localrc:
         NETWORK_API_EXTENSIONS: "{{ network_api_extensions | join(',') }}"
+      devstack_local_conf:
+        test-config:
+          $TEMPEST_CONFIG:
+            neutron_plugin_options:
+              ipv6_metadata: False
 
 - job:
     name: neutron-tempest-plugin-scenario-linuxbridge-ussuri
     parent: neutron-tempest-plugin-scenario-linuxbridge
+    nodeset: openstack-single-node-bionic
     override-checkout: stable/ussuri
     vars:
       branch_override: stable/ussuri
       network_api_extensions: *api_extensions
       devstack_localrc:
         NETWORK_API_EXTENSIONS: "{{ network_api_extensions | join(',') }}"
+      devstack_local_conf:
+        test-config:
+          $TEMPEST_CONFIG:
+            neutron_plugin_options:
+              ipv6_metadata: False
 
 - job:
     name: neutron-tempest-plugin-scenario-ovn-ussuri
     parent: neutron-tempest-plugin-scenario-ovn
+    nodeset: openstack-single-node-bionic
     override-checkout: stable/ussuri
     vars:
       branch_override: stable/ussuri
       network_api_extensions: *api_extensions
       devstack_localrc:
         NETWORK_API_EXTENSIONS: "{{ network_api_extensions | join(',') }}"
+        # TODO(mjozefcz): Stop compiling OVS modules when meter action in kernel
+        # will be released in Ubuntu Bionic.
+        # More info: https://mail.openvswitch.org/pipermail/ovs-discuss/2018-December/048009.html
+        OVN_BUILD_MODULES: True
+        # TODO(skaplons): v2.13.1 is incompatible with kernel 4.15.0-118, sticking to commit hash until new v2.13 tag is created
+        OVS_BRANCH: 0047ca3a0290f1ef954f2c76b31477cf4b9755f5
 
 - job:
     name: neutron-tempest-plugin-dvr-multinode-scenario-ussuri
     parent: neutron-tempest-plugin-dvr-multinode-scenario
+    nodeset: openstack-two-node-bionic
     override-checkout: stable/ussuri
     vars:
       network_api_extensions_common: *api_extensions
@@ -135,6 +162,7 @@
 - job:
     name: neutron-tempest-plugin-designate-scenario-ussuri
     parent: neutron-tempest-plugin-designate-scenario
+    nodeset: openstack-single-node-bionic
     override-checkout: stable/ussuri
     vars:
       branch_override: stable/ussuri
@@ -143,6 +171,7 @@
 - job:
     name: neutron-tempest-plugin-sfc-ussuri
     parent: neutron-tempest-plugin-sfc
+    nodeset: openstack-single-node-bionic
     override-checkout: stable/ussuri
     vars:
       branch_override: stable/ussuri
@@ -151,6 +180,7 @@
 - job:
     name: neutron-tempest-plugin-bgpvpn-bagpipe-ussuri
     parent: neutron-tempest-plugin-bgpvpn-bagpipe
+    nodeset: openstack-single-node-bionic
     override-checkout: stable/ussuri
     vars:
       branch_override: stable/ussuri
@@ -159,6 +189,7 @@
 - job:
     name: neutron-tempest-plugin-fwaas-ussuri
     parent: neutron-tempest-plugin-base
+    nodeset: openstack-single-node-bionic
     timeout: 10800
     override-checkout: stable/ussuri
     required-projects:
@@ -182,6 +213,7 @@
 - job:
     name: neutron-tempest-plugin-dynamic-routing-ussuri
     parent: neutron-tempest-plugin-dynamic-routing
+    nodeset: openstack-single-node-bionic
     override-checkout: stable/ussuri
     vars:
       branch_override: stable/ussuri
@@ -190,6 +222,7 @@
 - job:
     name: neutron-tempest-plugin-vpnaas-ussuri
     parent: neutron-tempest-plugin-vpnaas
+    nodeset: openstack-single-node-bionic
     override-checkout: stable/ussuri
     vars:
       branch_override: stable/ussuri