Merge "Add tap-as-a-service scenario tests"
diff --git a/neutron_tempest_plugin/api/admin/test_ports.py b/neutron_tempest_plugin/api/admin/test_ports.py
index a374b81..e9a1bdb 100644
--- a/neutron_tempest_plugin/api/admin/test_ports.py
+++ b/neutron_tempest_plugin/api/admin/test_ports.py
@@ -74,6 +74,7 @@
EGRESS_KBPS = 1000
INGRESS_KBPS = 2000
+ ANY_KPPS = 500
@classmethod
def skip_checks(cls):
@@ -101,9 +102,11 @@
cls.os_admin.qos_minimum_bandwidth_rules_client
cls.qos_bw_limit_rule_client = \
cls.os_admin.qos_limit_bandwidth_rules_client
+ cls.qos_minimum_packet_rate_rules_client = \
+ cls.os_admin.qos_minimum_packet_rate_rules_client
def _create_qos_policy_and_port(self, network, vnic_type,
- network_policy=False):
+ network_policy=False, min_kpps=False):
qos_policy = self.create_qos_policy(
name=data_utils.rand_name('test_policy'), shared=True)
self.qos_minimum_bandwidth_rules_client.create_minimum_bandwidth_rule(
@@ -116,6 +119,13 @@
**{'direction': const.INGRESS_DIRECTION,
'min_kbps': self.INGRESS_KBPS})
+ if min_kpps:
+ self.qos_minimum_packet_rate_rules_client.\
+ create_minimum_packet_rate_rule(
+ qos_policy_id=qos_policy['id'],
+ **{'direction': const.ANY_DIRECTION,
+ 'min_kpps': min_kpps})
+
port_policy_id = qos_policy['id'] if not network_policy else None
port_kwargs = {
'qos_policy_id': port_policy_id,
@@ -129,13 +139,14 @@
port_id = self.create_port(network, **port_kwargs)['id']
return self.admin_client.show_port(port_id)['port']
- def _assert_resource_request(self, port, vnic_type):
+ def _assert_resource_request(self, port, vnic_type, min_kpps=None):
self.assertIn('resource_request', port)
vnic_trait = 'CUSTOM_VNIC_TYPE_%s' % vnic_type.upper()
physnet_trait = 'CUSTOM_PHYSNET_%s' % self.physnet_name.upper()
if utils.is_extension_enabled('port-resource-request-groups',
'network'):
min_bw_group_found = False
+ min_pps_group_found = False if min_kpps else True
for rg in port['resource_request']['request_groups']:
self.assertIn(rg['id'],
port['resource_request']['same_subtree'])
@@ -151,11 +162,21 @@
rg['resources']
)
min_bw_group_found = True
+ elif (('NET_PACKET_RATE_KILOPACKET_PER_SEC' in
+ rg['resources'] and min_kpps) and
+ not min_pps_group_found):
+ self.assertCountEqual([vnic_trait], rg['required'])
+
+ self.assertEqual(
+ {'NET_PACKET_RATE_KILOPACKET_PER_SEC': min_kpps},
+ rg['resources']
+ )
+ min_pps_group_found = True
else:
self.fail('"resource_request" contains unexpected request '
'group: %s', rg)
- if not min_bw_group_found:
+ if not min_bw_group_found or not min_pps_group_found:
self.fail('Did not find expected request groups in '
'"resource_request": %s',
port['resource_request']['request_groups'])
@@ -186,6 +207,27 @@
port = self.admin_client.show_port(port_id)['port']
self.assertIsNone(port['resource_request'])
+ @decorators.idempotent_id('5ae93aa0-408a-11ec-bbca-17b1a60f3438')
+ @utils.requires_ext(service='network',
+ extension='port-resource-request-groups')
+ def test_port_resource_request_min_bw_and_min_pps(self):
+ port = self._create_qos_policy_and_port(
+ network=self.prov_network, vnic_type=self.vnic_type,
+ network_policy=False, min_kpps=self.ANY_KPPS)
+ port_id = port['id']
+
+ self._assert_resource_request(port, self.vnic_type,
+ min_kpps=self.ANY_KPPS)
+
+ # Note(lajoskatona): port-resource-request is an admin only feature,
+ # so test if non-admin user can't see the new field.
+ port = self.client.show_port(port_id)['port']
+ self.assertNotIn('resource_request', port)
+
+ self.update_port(port, **{'qos_policy_id': None})
+ port = self.admin_client.show_port(port_id)['port']
+ self.assertIsNone(port['resource_request'])
+
@decorators.idempotent_id('7261391f-64cc-45a6-a1e3-435694c54bf5')
def test_port_resource_request_no_provider_net_conflict(self):
conflict = self.assertRaises(
@@ -220,8 +262,12 @@
@decorators.idempotent_id('b6c34ae4-44c8-47f0-86de-7ef9866fa000')
def test_port_resource_request_inherited_policy(self):
+ base_segm = CONF.neutron_plugin_options.provider_net_base_segm_id
+ prov_network = self.create_provider_network(
+ physnet_name=self.physnet_name,
+ start_segmentation_id=base_segm)
port = self._create_qos_policy_and_port(
- network=self.prov_network, vnic_type=self.vnic_type,
+ network=prov_network, vnic_type=self.vnic_type,
network_policy=True)
self._assert_resource_request(port, self.vnic_type)
diff --git a/neutron_tempest_plugin/api/clients.py b/neutron_tempest_plugin/api/clients.py
index 2855a7a..053e5ea 100644
--- a/neutron_tempest_plugin/api/clients.py
+++ b/neutron_tempest_plugin/api/clients.py
@@ -89,7 +89,8 @@
self.interfaces_client = interfaces_client.InterfacesClient(
self.auth_provider, **params)
self.keypairs_client = keypairs_client.KeyPairsClient(
- self.auth_provider, **params)
+ self.auth_provider, ssh_key_type=CONF.validation.ssh_key_type,
+ **params)
self.hv_client = hypervisor_client.HypervisorClient(
self.auth_provider, **params)
self.az_client = availability_zone_client.AvailabilityZoneClient(
diff --git a/neutron_tempest_plugin/api/test_qos.py b/neutron_tempest_plugin/api/test_qos.py
index 59a0eb6..2929542 100644
--- a/neutron_tempest_plugin/api/test_qos.py
+++ b/neutron_tempest_plugin/api/test_qos.py
@@ -1399,8 +1399,6 @@
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
@@ -1419,6 +1417,8 @@
def setUp(self):
super(QosMinimumPpsRuleTestJSON, self).setUp()
self.policy_name = data_utils.rand_name(name='test', prefix='policy')
+ self.RULE_NAME = qos_consts.RULE_TYPE_MINIMUM_PACKET_RATE + "_rule"
+ self.RULES_NAME = self.RULE_NAME + "s"
def _create_qos_min_pps_rule(self, policy_id, rule_data):
rule = self.min_pps_client.create_minimum_packet_rate_rule(
diff --git a/neutron_tempest_plugin/common/ssh.py b/neutron_tempest_plugin/common/ssh.py
index 8334521..4cb1474 100644
--- a/neutron_tempest_plugin/common/ssh.py
+++ b/neutron_tempest_plugin/common/ssh.py
@@ -62,7 +62,8 @@
host=host, username=username, password=password, timeout=timeout,
pkey=pkey, channel_timeout=channel_timeout,
look_for_keys=look_for_keys, key_filename=key_filename, port=port,
- proxy_client=proxy_client)
+ proxy_client=proxy_client,
+ ssh_key_type=CONF.validation.ssh_key_type)
@classmethod
def create_proxy_client(cls, look_for_keys=True, **kwargs):
diff --git a/neutron_tempest_plugin/common/utils.py b/neutron_tempest_plugin/common/utils.py
index 898e4b9..4ccec72 100644
--- a/neutron_tempest_plugin/common/utils.py
+++ b/neutron_tempest_plugin/common/utils.py
@@ -29,11 +29,14 @@
from tempest.lib import exceptions
+from neutron_tempest_plugin import config
+
SCHEMA_PORT_MAPPING = {
"http": 80,
"https": 443,
}
+CONF = config.CONF
class classproperty(object):
@@ -161,13 +164,20 @@
return 'attempt_{}'.format(str(self.test_attempt).zfill(3))
def _start_connection(self):
+ if CONF.neutron_plugin_options.default_image_is_advanced:
+ server_exec_method = self.server_ssh.execute_script
+ client_exec_method = self.client_ssh.execute_script
+ else:
+ server_exec_method = self.server_ssh.exec_command
+ client_exec_method = self.client_ssh.exec_command
+
self.server_ssh.exec_command(
'echo "{}" > input.txt'.format(self.test_str))
- self.server_ssh.exec_command('tail -f input.txt | nc -lp '
+ server_exec_method('tail -f input.txt | nc -lp '
'{} &> output.txt &'.format(self.port))
self.client_ssh.exec_command(
'echo "{}" > input.txt'.format(self.test_str))
- self.client_ssh.exec_command('tail -f input.txt | nc {} {} &>'
+ client_exec_method('tail -f input.txt | nc {} {} &>'
'output.txt &'.format(self.ip, self.port))
def _test_connection(self):
@@ -203,9 +213,11 @@
self._test_connection, timeout=timeout, sleep=sleep_timer)
def __exit__(self, type, value, traceback):
- self.server_ssh.exec_command('sudo killall nc || killall nc')
+ self.server_ssh.exec_command('sudo killall nc || killall nc || '
+ 'echo "True"')
self.server_ssh.exec_command(
'sudo killall tail || killall tail || echo "True"')
- self.client_ssh.exec_command('sudo killall nc || killall nc')
+ self.client_ssh.exec_command('sudo killall nc || killall nc || '
+ 'echo "True"')
self.client_ssh.exec_command(
'sudo killall tail || killall tail || echo "True"')
diff --git a/neutron_tempest_plugin/fwaas/scenario/fwaas_v2_base.py b/neutron_tempest_plugin/fwaas/scenario/fwaas_v2_base.py
index 00cdf2c..f8eb44c 100644
--- a/neutron_tempest_plugin/fwaas/scenario/fwaas_v2_base.py
+++ b/neutron_tempest_plugin/fwaas/scenario/fwaas_v2_base.py
@@ -46,6 +46,7 @@
try:
client = ssh.Client(ip_address, username, pkey=private_key,
channel_timeout=connect_timeout,
+ ssh_key_type=CONF.validation.ssh_key_type,
**kwargs)
client.test_connection_auth()
self.assertTrue(should_connect, "Unexpectedly reachable")
diff --git a/neutron_tempest_plugin/neutron_dynamic_routing/scenario/test_simple_bgp.py b/neutron_tempest_plugin/neutron_dynamic_routing/scenario/test_simple_bgp.py
index 85cc810..3ec231e 100644
--- a/neutron_tempest_plugin/neutron_dynamic_routing/scenario/test_simple_bgp.py
+++ b/neutron_tempest_plugin/neutron_dynamic_routing/scenario/test_simple_bgp.py
@@ -214,7 +214,8 @@
left_server = self._create_server()
ssh_client = ssh.Client(left_server['fip']['floating_ip_address'],
CONF.validation.image_ssh_user,
- pkey=self.keypair['private_key'])
+ pkey=self.keypair['private_key'],
+ ssh_key_type=CONF.validation.ssh_key_type)
# check LEFT -> RIGHT connectivity via BGP advertised routes
self.check_remote_connectivity(
diff --git a/neutron_tempest_plugin/scenario/test_dhcp.py b/neutron_tempest_plugin/scenario/test_dhcp.py
index b95eaa2..d0545e2 100644
--- a/neutron_tempest_plugin/scenario/test_dhcp.py
+++ b/neutron_tempest_plugin/scenario/test_dhcp.py
@@ -11,14 +11,18 @@
# 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 netaddr
+from neutron_lib import constants
from oslo_log import log
from paramiko import ssh_exception as ssh_exc
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
+import testtools
from neutron_tempest_plugin.common import ssh
+from neutron_tempest_plugin.common import utils as neutron_utils
from neutron_tempest_plugin import config
from neutron_tempest_plugin.scenario import base
@@ -92,3 +96,96 @@
self._log_console_output([server])
self._log_local_network_status()
raise
+
+
+class DHCPPortUpdateTest(base.BaseTempestTestCase):
+
+ credentials = ['primary', 'admin']
+
+ @classmethod
+ def resource_setup(cls):
+ super(DHCPPortUpdateTest, 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.router = cls.create_router_by_client()
+ 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'])
+ cls.create_pingable_secgroup_rule(cls.security_group['id'])
+
+ @testtools.skipUnless(
+ CONF.neutron_plugin_options.firewall_driver == 'ovn',
+ "OVN driver is required to run this test - "
+ "LP#1942794 solution only applied to OVN")
+ @decorators.idempotent_id('8171cc68-9dbb-46ca-b065-17b5b2e26094')
+ def test_modify_dhcp_port_ip_address(self):
+ """Test Scenario
+
+ 1) Create a network and a subnet with DHCP enabled
+ 2) Modify the default IP address from the subnet DHCP port
+ 3) Create a server in this network and check ssh connectivity
+
+ For the step 3), the server needs to obtain ssh keys from the metadata
+
+ Related bug: LP#1942794
+ """
+ # create subnet (dhcp is enabled by default)
+ subnet = self.create_subnet(network=self.network, name=self.rand_name)
+
+ def _get_dhcp_ports():
+ # in some cases, like ML2/OVS, the subnet port associated to DHCP
+ # is created with device_owner='network:dhcp'
+ dhcp_ports = self.client.list_ports(
+ network_id=self.network['id'],
+ device_owner=constants.DEVICE_OWNER_DHCP)['ports']
+ # in other cases, like ML2/OVN, the subnet port used for metadata
+ # is created with device_owner='network:distributed'
+ distributed_ports = self.client.list_ports(
+ network_id=self.network['id'],
+ device_owner=constants.DEVICE_OWNER_DISTRIBUTED)['ports']
+ self.dhcp_ports = dhcp_ports + distributed_ports
+ self.assertLessEqual(
+ len(self.dhcp_ports), 1, msg='Only one port was expected')
+ return len(self.dhcp_ports) == 1
+
+ # obtain the dhcp port
+ # in some cases this port is not created together with the subnet, but
+ # immediately after it, so some delay may be needed and that is the
+ # reason why a waiter function is used here
+ self.dhcp_ports = []
+ neutron_utils.wait_until_true(
+ lambda: _get_dhcp_ports(),
+ timeout=10)
+ dhcp_port = self.dhcp_ports[0]
+
+ # modify DHCP port IP address
+ old_dhcp_port_ip = netaddr.IPAddress(
+ dhcp_port['fixed_ips'][0]['ip_address'])
+ if str(old_dhcp_port_ip) != subnet['allocation_pools'][0]['end']:
+ new_dhcp_port_ip = str(old_dhcp_port_ip + 1)
+ else:
+ new_dhcp_port_ip = str(old_dhcp_port_ip - 1)
+ self.update_port(port=dhcp_port,
+ fixed_ips=[{'subnet_id': subnet['id'],
+ 'ip_address': new_dhcp_port_ip}])
+
+ # create server
+ server = self.create_server(
+ flavor_ref=CONF.compute.flavor_ref,
+ image_ref=CONF.compute.image_ref,
+ key_name=self.keypair['name'],
+ security_groups=[{'name': self.security_group['name']}],
+ networks=[{'uuid': self.network['id']}])
+
+ # attach fip to the server
+ self.create_router_interface(self.router['id'], subnet['id'])
+ server_port = self.client.list_ports(
+ network_id=self.network['id'],
+ device_id=server['server']['id'])['ports'][0]
+ fip = self.create_floatingip(port_id=server_port['id'])
+
+ # check connectivity
+ self.check_connectivity(fip['floating_ip_address'],
+ CONF.validation.image_ssh_user,
+ self.keypair['private_key'])
diff --git a/neutron_tempest_plugin/scenario/test_ipv6.py b/neutron_tempest_plugin/scenario/test_ipv6.py
index d9d1a22..41ac2e6 100644
--- a/neutron_tempest_plugin/scenario/test_ipv6.py
+++ b/neutron_tempest_plugin/scenario/test_ipv6.py
@@ -33,17 +33,47 @@
LOG = log.getLogger(__name__)
-def turn_nic6_on(ssh, ipv6_port):
+def turn_nic6_on(ssh, ipv6_port, config_nic=True):
"""Turns the IPv6 vNIC on
Required because guest images usually set only the first vNIC on boot.
Searches for the IPv6 vNIC's MAC and brings it up.
+ # NOTE(slaweq): on RHEL based OS ifcfg file for new interface is
+ # needed to make IPv6 working on it, so if
+ # /etc/sysconfig/network-scripts directory exists ifcfg-%(nic)s file
+ # should be added in it
@param ssh: RemoteClient ssh instance to server
@param ipv6_port: port from IPv6 network attached to the server
"""
ip_command = ip.IPCommand(ssh)
nic = ip_command.get_nic_name_by_mac(ipv6_port['mac_address'])
+
+ if config_nic:
+ try:
+ if sysconfig_network_scripts_dir_exists(ssh):
+ ssh.execute_script(
+ 'echo -e "DEVICE=%(nic)s\\nNAME=%(nic)s\\nIPV6INIT=yes" | '
+ 'tee /etc/sysconfig/network-scripts/ifcfg-%(nic)s; '
+ % {'nic': nic}, become_root=True)
+ if nmcli_command_exists(ssh):
+ ssh.execute_script('nmcli connection reload %s' % nic,
+ become_root=True)
+ ssh.execute_script('nmcli con mod %s ipv6.addr-gen-mode eui64'
+ % nic, become_root=True)
+ ssh.execute_script('nmcli connection up %s' % nic,
+ become_root=True)
+
+ except lib_exc.SSHExecCommandFailed as e:
+ # NOTE(slaweq): Sometimes it can happen that this SSH command
+ # will fail because of some error from network manager in
+ # guest os.
+ # But even then doing ip link set up below is fine and
+ # IP address should be configured properly.
+ LOG.debug("Error creating NetworkManager profile. "
+ "Error message: %(error)s",
+ {'error': e})
+
ip_command.set_link(nic, "up")
@@ -76,6 +106,11 @@
{'error': e})
+def sysconfig_network_scripts_dir_exists(ssh):
+ return "False" not in ssh.execute_script(
+ 'test -d /etc/sysconfig/network-scripts/ || echo "False"')
+
+
def nmcli_command_exists(ssh):
return "False" not in ssh.execute_script(
'if ! type nmcli > /dev/null ; then echo "False"; fi')
@@ -122,24 +157,45 @@
if expected_address in ip_address:
return True
return False
-
+ # Set NIC with IPv6 to be UP and wait until IPv6 address
+ # will be configured on this NIC
+ turn_nic6_on(ssh_client, ipv6_port, False)
+ # And check if IPv6 address will be properly configured
+ # on this NIC
try:
- # Set NIC with IPv6 to be UP and wait until IPv6 address will be
- # configured on this NIC
- turn_nic6_on(ssh_client, ipv6_port)
- # And check if IPv6 address will be properly configured on this NIC
utils.wait_until_true(
lambda: guest_has_address(ipv6_address),
- timeout=120,
- exception=RuntimeError(
- "Timed out waiting for IP address {!r} to be configured "
- "in the VM {!r}.".format(ipv6_address, vm['id'])))
- except (lib_exc.SSHTimeout, ssh_exc.AuthenticationException) as ssh_e:
+ timeout=60)
+ except utils.WaitTimeout:
+ LOG.debug('Timeout without NM configuration')
+ except (lib_exc.SSHTimeout,
+ ssh_exc.AuthenticationException) as ssh_e:
LOG.debug(ssh_e)
self._log_console_output([vm])
self._log_local_network_status()
raise
+ if not guest_has_address(ipv6_address):
+ try:
+ # Set NIC with IPv6 to be UP and wait until IPv6 address
+ # will be configured on this NIC
+ turn_nic6_on(ssh_client, ipv6_port)
+ # And check if IPv6 address will be properly configured
+ # on this NIC
+ utils.wait_until_true(
+ lambda: guest_has_address(ipv6_address),
+ timeout=90,
+ exception=RuntimeError(
+ "Timed out waiting for IP address {!r} to be "
+ "configured in the VM {!r}.".format(ipv6_address,
+ vm['id'])))
+ except (lib_exc.SSHTimeout,
+ ssh_exc.AuthenticationException) as ssh_e:
+ LOG.debug(ssh_e)
+ self._log_console_output([vm])
+ self._log_local_network_status()
+ raise
+
def _test_ipv6_hotplug(self, ra_mode, address_mode):
ipv6_networks = [self.create_network() for _ in range(2)]
for net in ipv6_networks:
diff --git a/neutron_tempest_plugin/vpnaas/scenario/test_vpnaas.py b/neutron_tempest_plugin/vpnaas/scenario/test_vpnaas.py
index 1a51198..92eed9e 100644
--- a/neutron_tempest_plugin/vpnaas/scenario/test_vpnaas.py
+++ b/neutron_tempest_plugin/vpnaas/scenario/test_vpnaas.py
@@ -233,7 +233,8 @@
left_server = self._create_server()
ssh_client = ssh.Client(left_server['fip']['floating_ip_address'],
CONF.validation.image_ssh_user,
- pkey=self.keypair['private_key'])
+ pkey=self.keypair['private_key'],
+ ssh_key_type=CONF.validation.ssh_key_type)
# check LEFT -> RIGHT connectivity via VPN
self.check_remote_connectivity(ssh_client, right_ip,
diff --git a/zuul.d/master_jobs.yaml b/zuul.d/master_jobs.yaml
index 26bf860..5085afd 100644
--- a/zuul.d/master_jobs.yaml
+++ b/zuul.d/master_jobs.yaml
@@ -459,13 +459,9 @@
network_api_extensions: *api_extensions
network_api_extensions_ovn:
- vlan-transparent
- # TODO(slaweq): Remove test_trunk_subport_lifecycle test from the
- # blacklist when bug https://bugs.launchpad.net/neutron/+bug/1885900 will
- # be fixed
# TODO(jlibosva): Remove the NetworkWritableMtuTest test from the list
# once east/west fragmentation is supported in core OVN
tempest_exclude_regex: "\
- (^neutron_tempest_plugin.scenario.test_trunk.TrunkTest.test_trunk_subport_lifecycle)|\
(^neutron_tempest_plugin.scenario.test_mtu.NetworkWritableMtuTest)"
devstack_localrc:
Q_AGENT: ovn
@@ -1011,6 +1007,43 @@
- ^zuul.d/(?!(project)).*\.yaml
- job:
+ name: neutron-tempest-plugin-fwaas
+ parent: neutron-tempest-plugin-base
+ timeout: 10800
+ required-projects:
+ - openstack/devstack-gate
+ - openstack/neutron-fwaas
+ - openstack/neutron
+ - openstack/neutron-tempest-plugin
+ - openstack/tempest
+ vars:
+ tempest_test_regex: ^neutron_tempest_plugin\.fwaas
+ devstack_plugins:
+ neutron-fwaas: https://opendev.org/openstack/neutron-fwaas.git
+ neutron-tempest-plugin: https://opendev.org/openstack/neutron-tempest-plugin.git
+ network_api_extensions_common: *api_extensions
+ network_api_extensions_fwaas:
+ - fwaas_v2
+ devstack_localrc:
+ NETWORK_API_EXTENSIONS: "{{ (network_api_extensions_common + network_api_extensions_fwaas) | join(',') }}"
+ Q_AGENT: openvswitch
+ Q_ML2_TENANT_NETWORK_TYPE: vxlan
+ Q_ML2_PLUGIN_MECHANISM_DRIVERS: openvswitch
+ devstack_services:
+ # Disable OVN services
+ br-ex-tcpdump: false
+ br-int-flows: false
+ ovn-controller: false
+ ovn-northd: false
+ q-ovn-metadata-agent: false
+ # Neutron services
+ q-agt: true
+ q-dhcp: true
+ q-meta: true
+ q-metering: true
+ q-l3: true
+
+- job:
name: neutron-tempest-plugin-vpnaas
parent: neutron-tempest-plugin-base
timeout: 3900
diff --git a/zuul.d/project.yaml b/zuul.d/project.yaml
index caf83da..3463d84 100644
--- a/zuul.d/project.yaml
+++ b/zuul.d/project.yaml
@@ -202,6 +202,7 @@
- neutron-tempest-plugin-dynamic-routing-victoria
- neutron-tempest-plugin-dynamic-routing-wallaby
- neutron-tempest-plugin-dynamic-routing-xena
+ - neutron-tempest-plugin-fwaas
- neutron-tempest-plugin-vpnaas
- neutron-tempest-plugin-vpnaas-victoria
- neutron-tempest-plugin-vpnaas-wallaby
@@ -214,3 +215,4 @@
- neutron-tempest-plugin-sfc
- neutron-tempest-plugin-bgpvpn-bagpipe
- neutron-tempest-plugin-dynamic-routing
+ - neutron-tempest-plugin-fwaas