Page MenuHomeVyOS Platform

No OneTemporary

Size
506 KB
Referenced Files
None
Subscribers
None
This file is larger than 256 KB, so syntax highlighting was skipped.
diff --git a/smoketest/scripts/cli/base_interfaces_test.py b/smoketest/scripts/cli/base_interfaces_test.py
index 20f108b03..45b300857 100644
--- a/smoketest/scripts/cli/base_interfaces_test.py
+++ b/smoketest/scripts/cli/base_interfaces_test.py
@@ -1,1392 +1,1388 @@
# Copyright VyOS maintainers and contributors <maintainers@vyos.io>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import re
from json import loads
from netifaces import ifaddresses # pylint: disable = no-name-in-module
from socket import AF_INET
from socket import AF_INET6
from systemd import journal
from base_vyostest_shim import VyOSUnitTestSHIM
-from base_vyostest_shim import CSTORE_GUARD_TIME
from vyos.configsession import ConfigSessionError
from vyos.defaults import directories
from vyos.ifconfig import Interface
from vyos.ifconfig import Section
from vyos.pki import CERT_BEGIN
from vyos.utils.file import read_file
from vyos.utils.dict import dict_search
from vyos.utils.process import cmd
from vyos.utils.process import process_named_running
from vyos.utils.network import get_interface_config
from vyos.utils.network import get_interface_vrf
from vyos.utils.network import get_vrf_tableid
from vyos.utils.network import interface_exists
from vyos.utils.network import is_intf_addr_assigned
from vyos.utils.network import is_ipv6_link_local
from vyos.utils.network import get_nft_vrf_zone_mapping
from vyos.xml_ref import cli_defined
from vyos.xml_ref import default_value
dhclient_base_dir = directories['isc_dhclient_dir']
dhclient_process_name = 'dhclient'
dhcp6c_base_dir = directories['dhcp6_client_dir']
dhcp6c_process_name = 'dhcp6c'
MSG_TESTCASE_UNSUPPORTED = 'unsupported on interface family'
server_ca_root_cert_data = """
MIIBcTCCARagAwIBAgIUDcAf1oIQV+6WRaW7NPcSnECQ/lUwCgYIKoZIzj0EAwIw
HjEcMBoGA1UEAwwTVnlPUyBzZXJ2ZXIgcm9vdCBDQTAeFw0yMjAyMTcxOTQxMjBa
Fw0zMjAyMTUxOTQxMjBaMB4xHDAaBgNVBAMME1Z5T1Mgc2VydmVyIHJvb3QgQ0Ew
WTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQ0y24GzKQf4aM2Ir12tI9yITOIzAUj
ZXyJeCmYI6uAnyAMqc4Q4NKyfq3nBi4XP87cs1jlC1P2BZ8MsjL5MdGWozIwMDAP
BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRwC/YaieMEnjhYa7K3Flw/o0SFuzAK
BggqhkjOPQQDAgNJADBGAiEAh3qEj8vScsjAdBy5shXzXDVVOKWCPTdGrPKnu8UW
a2cCIQDlDgkzWmn5ujc5ATKz1fj+Se/aeqwh4QyoWCVTFLIxhQ==
"""
server_ca_intermediate_cert_data = """
MIIBmTCCAT+gAwIBAgIUNzrtHzLmi3QpPK57tUgCnJZhXXQwCgYIKoZIzj0EAwIw
HjEcMBoGA1UEAwwTVnlPUyBzZXJ2ZXIgcm9vdCBDQTAeFw0yMjAyMTcxOTQxMjFa
Fw0zMjAyMTUxOTQxMjFaMCYxJDAiBgNVBAMMG1Z5T1Mgc2VydmVyIGludGVybWVk
aWF0ZSBDQTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABEl2nJ1CzoqPV6hWII2m
eGN/uieU6wDMECTk/LgG8CCCSYb488dibUiFN/1UFsmoLIdIhkx/6MUCYh62m8U2
WNujUzBRMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFMV3YwH88I5gFsFUibbQ
kMR0ECPsMB8GA1UdIwQYMBaAFHAL9hqJ4wSeOFhrsrcWXD+jRIW7MAoGCCqGSM49
BAMCA0gAMEUCIQC/ahujD9dp5pMMCd3SZddqGC9cXtOwMN0JR3e5CxP13AIgIMQm
jMYrinFoInxmX64HfshYqnUY8608nK9D2BNPOHo=
"""
client_ca_root_cert_data = """
MIIBcDCCARagAwIBAgIUZmoW2xVdwkZSvglnkCq0AHKa6zIwCgYIKoZIzj0EAwIw
HjEcMBoGA1UEAwwTVnlPUyBjbGllbnQgcm9vdCBDQTAeFw0yMjAyMTcxOTQxMjFa
Fw0zMjAyMTUxOTQxMjFaMB4xHDAaBgNVBAMME1Z5T1MgY2xpZW50IHJvb3QgQ0Ew
WTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATUpKXzQk2NOVKDN4VULk2yw4mOKPvn
mg947+VY7lbpfOfAUD0QRg95qZWCw899eKnXp/U4TkAVrmEKhUb6OJTFozIwMDAP
BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTXu6xGWUl25X3sBtrhm3BJSICIATAK
BggqhkjOPQQDAgNIADBFAiEAnTzEwuTI9bz2Oae3LZbjP6f/f50KFJtjLZFDbQz7
DpYCIDNRHV8zBUibC+zg5PqMpQBKd/oPfNU76nEv6xkp/ijO
"""
client_ca_intermediate_cert_data = """
MIIBmDCCAT+gAwIBAgIUJEMdotgqA7wU4XXJvEzDulUAGqgwCgYIKoZIzj0EAwIw
HjEcMBoGA1UEAwwTVnlPUyBjbGllbnQgcm9vdCBDQTAeFw0yMjAyMTcxOTQxMjJa
Fw0zMjAyMTUxOTQxMjJaMCYxJDAiBgNVBAMMG1Z5T1MgY2xpZW50IGludGVybWVk
aWF0ZSBDQTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABGyIVIi217s9j3O+WQ2b
6R65/Z0ZjQpELxPjBRc0CA0GFCo+pI5EvwI+jNFArvTAJ5+ZdEWUJ1DQhBKDDQdI
avCjUzBRMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFOUS8oNJjChB1Rb9Blcl
ETvziHJ9MB8GA1UdIwQYMBaAFNe7rEZZSXblfewG2uGbcElIgIgBMAoGCCqGSM49
BAMCA0cAMEQCIArhaxWgRsAUbEeNHD/ULtstLHxw/P97qPUSROLQld53AiBjgiiz
9pDfISmpekZYz6bIDWRIR0cXUToZEMFNzNMrQg==
"""
client_cert_data = """
MIIBmTCCAUCgAwIBAgIUV5T77XdE/tV82Tk4Vzhp5BIFFm0wCgYIKoZIzj0EAwIw
JjEkMCIGA1UEAwwbVnlPUyBjbGllbnQgaW50ZXJtZWRpYXRlIENBMB4XDTIyMDIx
NzE5NDEyMloXDTMyMDIxNTE5NDEyMlowIjEgMB4GA1UEAwwXVnlPUyBjbGllbnQg
Y2VydGlmaWNhdGUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAARuyynqfc/qJj5e
KJ03oOH8X4Z8spDeAPO9WYckMM0ldPj+9kU607szFzPwjaPWzPdgyIWz3hcN8yAh
CIhytmJao1AwTjAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBTIFKrxZ+PqOhYSUqnl
TGCUmM7wTjAfBgNVHSMEGDAWgBTlEvKDSYwoQdUW/QZXJRE784hyfTAKBggqhkjO
PQQDAgNHADBEAiAvO8/jvz05xqmP3OXD53XhfxDLMIxzN4KPoCkFqvjlhQIgIHq2
/geVx3rAOtSps56q/jiDouN/aw01TdpmGKVAa9U=
"""
client_key_data = """
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgxaxAQsJwjoOCByQE
+qSYKtKtJzbdbOnTsKNSrfgkFH6hRANCAARuyynqfc/qJj5eKJ03oOH8X4Z8spDe
APO9WYckMM0ldPj+9kU607szFzPwjaPWzPdgyIWz3hcN8yAhCIhytmJa
"""
def get_wpa_supplicant_value(interface, key):
tmp = read_file(f'/run/wpa_supplicant/{interface}.conf')
tmp = re.findall(r'\n?{}=(.*)'.format(key), tmp)
return tmp[0]
def get_certificate_count(interface, cert_type):
tmp = read_file(f'/run/wpa_supplicant/{interface}_{cert_type}.pem')
return tmp.count(CERT_BEGIN)
def is_mirrored_to(interface, mirror_if, qdisc):
"""
Ask TC if we are mirroring traffic to a discrete interface.
interface: source interface
mirror_if: destination where we mirror our data to
qdisc: must be ffff or 1 for ingress/egress
"""
if qdisc not in ['ffff', '1']:
raise ValueError()
ret_val = False
tmp = cmd(f'tc -s -p filter ls dev {interface} parent {qdisc}: | grep mirred')
tmp = tmp.lower()
if mirror_if in tmp:
ret_val = True
return ret_val
class BasicInterfaceTest:
class TestCase(VyOSUnitTestSHIM.TestCase):
_test_dhcp = False
_test_eapol = False
_test_ip = False
_test_mtu = False
_test_vlan = False
_test_qinq = False
_test_ipv6 = False
_test_ipv6_pd = False
_test_ipv6_dhcpc6 = False
_test_mirror = False
_test_vrf = False
_base_path = []
_options = {}
_interfaces = []
_qinq_range = ['10', '20', '30']
_vlan_range = ['100', '200', '300', '2000']
_test_addr = ['192.0.2.1/26', '192.0.2.255/31', '192.0.2.64/32',
'2001:db8:1::ffff/64', '2001:db8:101::1/112']
_mirror_interfaces = []
# choose IPv6 minimum MTU value for tests - this must always work
_mtu = '1280'
@classmethod
def setUpClass(cls):
super(BasicInterfaceTest.TestCase, cls).setUpClass()
# XXX the case of test_vif_8021q_mtu_limits, below, shows that
# we should extend cli_defined to support more complex queries
cls._test_vlan = cli_defined(cls._base_path, 'vif')
cls._test_qinq = cli_defined(cls._base_path, 'vif-s')
cls._test_dhcp = cli_defined(cls._base_path, 'dhcp-options')
cls._test_eapol = cli_defined(cls._base_path, 'eapol')
cls._test_ip = cli_defined(cls._base_path, 'ip')
cls._test_ipv6 = cli_defined(cls._base_path, 'ipv6')
cls._test_ipv6_dhcpc6 = cli_defined(cls._base_path, 'dhcpv6-options')
cls._test_ipv6_pd = cli_defined(cls._base_path + ['dhcpv6-options'], 'pd')
cls._test_mtu = cli_defined(cls._base_path, 'mtu')
cls._test_vrf = cli_defined(cls._base_path, 'vrf')
# Setup mirror interfaces for SPAN (Switch Port Analyzer)
for span in cls._mirror_interfaces:
section = Section.section(span)
cls.cli_set(cls, ['interfaces', section, span])
- # Enable CSTORE guard time required by FRR related tests
- cls._commit_guard_time = CSTORE_GUARD_TIME
-
@classmethod
def tearDownClass(cls):
# Tear down mirror interfaces for SPAN (Switch Port Analyzer)
for span in cls._mirror_interfaces:
section = Section.section(span)
cls.cli_delete(cls, ['interfaces', section, span])
super(BasicInterfaceTest.TestCase, cls).tearDownClass()
def tearDown(self):
self.cli_delete(self._base_path)
self.cli_commit()
# Verify that no previously interface remained on the system
ct_map = get_nft_vrf_zone_mapping()
for intf in self._interfaces:
self.assertFalse(interface_exists(intf))
for map_entry in ct_map:
self.assertNotEqual(intf, map_entry['interface'])
# No daemon started during tests should remain running
for daemon in ['dhcp6c', 'dhclient']:
# if _interface list is populated do a more fine grained search
# by also checking the cmd arguments passed to the daemon
if self._interfaces:
for tmp in self._interfaces:
self.assertFalse(process_named_running(daemon, tmp))
else:
self.assertFalse(process_named_running(daemon))
def test_dhcp_disable_interface(self):
if not self._test_dhcp:
self.skipTest(MSG_TESTCASE_UNSUPPORTED)
# When interface is configured as admin down, it must be admin down
# even when dhcpc starts on the given interface
for interface in self._interfaces:
self.cli_set(self._base_path + [interface, 'disable'])
for option in self._options.get(interface, []):
self.cli_set(self._base_path + [interface] + option.split())
# Also enable DHCP (ISC DHCP always places interface in admin up
# state so we check that we do not start DHCP client.
# https://vyos.dev/T2767
self.cli_set(self._base_path + [interface, 'address', 'dhcp'])
self.cli_commit()
# Validate interface state
for interface in self._interfaces:
flags = read_file(f'/sys/class/net/{interface}/flags')
self.assertEqual(int(flags, 16) & 1, 0)
def test_dhcp_client_options(self):
if not self._test_dhcp or not self._test_vrf:
self.skipTest(MSG_TESTCASE_UNSUPPORTED)
client_id = 'VyOS-router'
distance = '100'
hostname = 'vyos'
vendor_class_id = 'vyos-vendor'
user_class = 'vyos'
for interface in self._interfaces:
for option in self._options.get(interface, []):
self.cli_set(self._base_path + [interface] + option.split())
self.cli_set(self._base_path + [interface, 'address', 'dhcp'])
self.cli_set(self._base_path + [interface, 'dhcp-options', 'client-id', client_id])
self.cli_set(self._base_path + [interface, 'dhcp-options', 'default-route-distance', distance])
self.cli_set(self._base_path + [interface, 'dhcp-options', 'host-name', hostname])
self.cli_set(self._base_path + [interface, 'dhcp-options', 'vendor-class-id', vendor_class_id])
self.cli_set(self._base_path + [interface, 'dhcp-options', 'user-class', user_class])
self.cli_commit()
for interface in self._interfaces:
# Check if dhclient process runs
dhclient_pid = process_named_running(dhclient_process_name, cmdline=interface, timeout=10)
self.assertTrue(dhclient_pid)
dhclient_config = read_file(f'{dhclient_base_dir}/dhclient_{interface}.conf')
self.assertIn(f'request subnet-mask, broadcast-address, routers, domain-name-servers', dhclient_config)
self.assertIn(f'require subnet-mask;', dhclient_config)
self.assertIn(f'send host-name "{hostname}";', dhclient_config)
self.assertIn(f'send dhcp-client-identifier "{client_id}";', dhclient_config)
self.assertIn(f'send vendor-class-identifier "{vendor_class_id}";', dhclient_config)
self.assertIn(f'send user-class "{user_class}";', dhclient_config)
# and the commandline has the appropriate options
cmdline = read_file(f'/proc/{dhclient_pid}/cmdline')
self.assertIn(f'-e\x00IF_METRIC={distance}', cmdline)
def test_dhcp_vrf(self):
if not self._test_dhcp or not self._test_vrf:
self.skipTest(MSG_TESTCASE_UNSUPPORTED)
cli_default_metric = default_value(self._base_path + [self._interfaces[0],
'dhcp-options', 'default-route-distance'])
vrf_name = 'purple4'
self.cli_set(['vrf', 'name', vrf_name, 'table', '65000'])
for interface in self._interfaces:
for option in self._options.get(interface, []):
self.cli_set(self._base_path + [interface] + option.split())
self.cli_set(self._base_path + [interface, 'address', 'dhcp'])
self.cli_set(self._base_path + [interface, 'vrf', vrf_name])
self.cli_commit()
# Validate interface state
for interface in self._interfaces:
tmp = get_interface_vrf(interface)
self.assertEqual(tmp, vrf_name)
# Check if dhclient process runs
dhclient_pid = process_named_running(dhclient_process_name, cmdline=interface, timeout=10)
self.assertTrue(dhclient_pid)
# .. inside the appropriate VRF instance
vrf_pids = cmd(f'ip vrf pids {vrf_name}')
self.assertIn(str(dhclient_pid), vrf_pids)
# and the commandline has the appropriate options
cmdline = read_file(f'/proc/{dhclient_pid}/cmdline')
self.assertIn(f'-e\x00IF_METRIC={cli_default_metric}', cmdline)
# T5103: remove interface from VRF instance and move DHCP client
# back to default VRF. This must restart the DHCP client process
for interface in self._interfaces:
self.cli_delete(self._base_path + [interface, 'vrf'])
self.cli_commit()
# Validate interface state
for interface in self._interfaces:
tmp = get_interface_vrf(interface)
self.assertEqual(tmp, 'default')
# Check if dhclient process runs
dhclient_pid = process_named_running(dhclient_process_name, cmdline=interface, timeout=10)
self.assertTrue(dhclient_pid)
# .. inside the appropriate VRF instance
vrf_pids = cmd(f'ip vrf pids {vrf_name}')
self.assertNotIn(str(dhclient_pid), vrf_pids)
# and the commandline has the appropriate options
cmdline = read_file(f'/proc/{dhclient_pid}/cmdline')
self.assertIn(f'-e\x00IF_METRIC={cli_default_metric}', cmdline)
self.cli_delete(['vrf', 'name', vrf_name])
def test_dhcpv6_vrf(self):
if not self._test_ipv6_dhcpc6 or not self._test_vrf:
self.skipTest(MSG_TESTCASE_UNSUPPORTED)
vrf_name = 'purple6'
self.cli_set(['vrf', 'name', vrf_name, 'table', '65001'])
# When interface is configured as admin down, it must be admin down
# even when dhcpc starts on the given interface
for interface in self._interfaces:
for option in self._options.get(interface, []):
self.cli_set(self._base_path + [interface] + option.split())
self.cli_set(self._base_path + [interface, 'address', 'dhcpv6'])
self.cli_set(self._base_path + [interface, 'vrf', vrf_name])
self.cli_commit()
# Validate interface state
for interface in self._interfaces:
tmp = get_interface_vrf(interface)
self.assertEqual(tmp, vrf_name)
# Check if dhclient process runs
tmp = process_named_running(dhcp6c_process_name, cmdline=interface, timeout=10)
self.assertTrue(tmp)
# .. inside the appropriate VRF instance
vrf_pids = cmd(f'ip vrf pids {vrf_name}')
self.assertIn(str(tmp), vrf_pids)
# T7135: remove interface from VRF instance and move DHCP client
# back to default VRF. This must restart the DHCP client process
for interface in self._interfaces:
self.cli_delete(self._base_path + [interface, 'vrf'])
self.cli_commit()
# Validate interface state
for interface in self._interfaces:
tmp = get_interface_vrf(interface)
self.assertEqual(tmp, 'default')
# Check if dhclient process runs
tmp = process_named_running(dhcp6c_process_name, cmdline=interface, timeout=10)
self.assertTrue(tmp)
# .. inside the appropriate VRF instance
vrf_pids = cmd(f'ip vrf pids {vrf_name}')
self.assertNotIn(str(tmp), vrf_pids)
self.cli_delete(['vrf', 'name', vrf_name])
def test_move_interface_between_vrf_instances(self):
if not self._test_vrf:
self.skipTest(MSG_TESTCASE_UNSUPPORTED)
vrf1_name = 'smoketest_mgmt1'
vrf1_table = '5424'
vrf2_name = 'smoketest_mgmt2'
vrf2_table = '7412'
self.cli_set(['vrf', 'name', vrf1_name, 'table', vrf1_table])
self.cli_set(['vrf', 'name', vrf2_name, 'table', vrf2_table])
# move interface into first VRF
for interface in self._interfaces:
for option in self._options.get(interface, []):
self.cli_set(self._base_path + [interface] + option.split())
self.cli_set(self._base_path + [interface, 'vrf', vrf1_name])
self.cli_commit()
# check that interface belongs to proper VRF
for interface in self._interfaces:
tmp = get_interface_vrf(interface)
self.assertEqual(tmp, vrf1_name)
tmp = get_interface_config(vrf1_name)
self.assertEqual(int(vrf1_table), get_vrf_tableid(interface))
# move interface into second VRF
for interface in self._interfaces:
self.cli_set(self._base_path + [interface, 'vrf', vrf2_name])
self.cli_commit()
# check that interface belongs to proper VRF
for interface in self._interfaces:
tmp = get_interface_vrf(interface)
self.assertEqual(tmp, vrf2_name)
tmp = get_interface_config(vrf2_name)
self.assertEqual(int(vrf2_table), get_vrf_tableid(interface))
self.cli_delete(['vrf', 'name', vrf1_name])
self.cli_delete(['vrf', 'name', vrf2_name])
def test_add_to_invalid_vrf(self):
if not self._test_vrf:
self.skipTest(MSG_TESTCASE_UNSUPPORTED)
# move interface into first VRF
for interface in self._interfaces:
for option in self._options.get(interface, []):
self.cli_set(self._base_path + [interface] + option.split())
self.cli_set(self._base_path + [interface, 'vrf', 'invalid'])
# check validate() - can not use a non-existing VRF
with self.assertRaises(ConfigSessionError):
self.cli_commit()
for interface in self._interfaces:
self.cli_delete(self._base_path + [interface, 'vrf', 'invalid'])
self.cli_set(self._base_path + [interface, 'description', 'test_add_to_invalid_vrf'])
def test_span_mirror(self):
if not self._mirror_interfaces:
self.skipTest(MSG_TESTCASE_UNSUPPORTED)
# Check the two-way mirror rules of ingress and egress
for mirror in self._mirror_interfaces:
for interface in self._interfaces:
self.cli_set(self._base_path + [interface, 'mirror', 'ingress', mirror])
self.cli_set(self._base_path + [interface, 'mirror', 'egress', mirror])
self.cli_commit()
# Verify config
for mirror in self._mirror_interfaces:
for interface in self._interfaces:
self.assertTrue(is_mirrored_to(interface, mirror, 'ffff'))
self.assertTrue(is_mirrored_to(interface, mirror, '1'))
def test_interface_disable(self):
# Check if description can be added to interface and
# can be read back
for intf in self._interfaces:
self.cli_set(self._base_path + [intf, 'disable'])
for option in self._options.get(intf, []):
self.cli_set(self._base_path + [intf] + option.split())
self.cli_commit()
# Validate interface description
for intf in self._interfaces:
self.assertEqual(Interface(intf).get_admin_state(), 'down')
def test_interface_description(self):
# Check if description can be added to interface and
# can be read back
for intf in self._interfaces:
test_string=f'Description-Test-{intf}'
self.cli_set(self._base_path + [intf, 'description', test_string])
for option in self._options.get(intf, []):
self.cli_set(self._base_path + [intf] + option.split())
self.cli_commit()
# Validate interface description
for intf in self._interfaces:
test_string=f'Description-Test-{intf}'
tmp = read_file(f'/sys/class/net/{intf}/ifalias')
self.assertEqual(tmp, test_string)
self.assertEqual(Interface(intf).get_alias(), test_string)
self.cli_delete(self._base_path + [intf, 'description'])
self.cli_commit()
# Validate remove interface description "empty"
for intf in self._interfaces:
tmp = read_file(f'/sys/class/net/{intf}/ifalias')
self.assertEqual(tmp, str())
self.assertEqual(Interface(intf).get_alias(), str())
# Test maximum interface description lengt (255 characters)
test_string='abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789___'
for intf in self._interfaces:
self.cli_set(self._base_path + [intf, 'description', test_string])
for option in self._options.get(intf, []):
self.cli_set(self._base_path + [intf] + option.split())
self.cli_commit()
# Validate interface description
for intf in self._interfaces:
tmp = read_file(f'/sys/class/net/{intf}/ifalias')
self.assertEqual(tmp, test_string)
self.assertEqual(Interface(intf).get_alias(), test_string)
def test_add_single_ip_address(self):
addr = '192.0.2.0/31'
for intf in self._interfaces:
self.cli_set(self._base_path + [intf, 'address', addr])
for option in self._options.get(intf, []):
self.cli_set(self._base_path + [intf] + option.split())
self.cli_commit()
for intf in self._interfaces:
self.assertTrue(is_intf_addr_assigned(intf, addr))
self.assertEqual(Interface(intf).get_admin_state(), 'up')
def test_add_multiple_ip_addresses(self):
# Add address
for intf in self._interfaces:
for option in self._options.get(intf, []):
self.cli_set(self._base_path + [intf] + option.split())
for addr in self._test_addr:
self.cli_set(self._base_path + [intf, 'address', addr])
self.cli_commit()
# Validate address
for intf in self._interfaces:
for af in AF_INET, AF_INET6:
for addr in ifaddresses(intf)[af]:
# checking link local addresses makes no sense
if is_ipv6_link_local(addr['addr']):
continue
self.assertTrue(is_intf_addr_assigned(intf, addr['addr']))
def test_ipv6_link_local_address(self):
# Common function for IPv6 link-local address assignemnts
if not self._test_ipv6:
self.skipTest(MSG_TESTCASE_UNSUPPORTED)
for interface in self._interfaces:
base = self._base_path + [interface]
# just set the interface base without any option - some interfaces
# (VTI) do not require any option to be brought up
self.cli_set(base)
for option in self._options.get(interface, []):
self.cli_set(base + option.split())
# after commit we must have an IPv6 link-local address
self.cli_commit()
for interface in self._interfaces:
self.assertIn(AF_INET6, ifaddresses(interface))
for addr in ifaddresses(interface)[AF_INET6]:
self.assertTrue(is_ipv6_link_local(addr['addr']))
# disable IPv6 link-local address assignment
for interface in self._interfaces:
base = self._base_path + [interface]
self.cli_set(base + ['ipv6', 'address', 'no-default-link-local'])
# after commit we must have no IPv6 link-local address
self.cli_commit()
for interface in self._interfaces:
self.assertNotIn(AF_INET6, ifaddresses(interface))
def test_interface_mtu(self):
if not self._test_mtu:
self.skipTest(MSG_TESTCASE_UNSUPPORTED)
for intf in self._interfaces:
base = self._base_path + [intf]
self.cli_set(base + ['mtu', self._mtu])
for option in self._options.get(intf, []):
self.cli_set(base + option.split())
# commit interface changes
self.cli_commit()
# verify changed MTU
for intf in self._interfaces:
tmp = get_interface_config(intf)
self.assertEqual(tmp['mtu'], int(self._mtu))
def test_mtu_1200_no_ipv6_interface(self):
# Testcase if MTU can be changed to 1200 on non IPv6
# enabled interfaces
if not self._test_mtu or not self._test_ipv6:
self.skipTest(MSG_TESTCASE_UNSUPPORTED)
old_mtu = self._mtu
self._mtu = '1200'
for intf in self._interfaces:
base = self._base_path + [intf]
for option in self._options.get(intf, []):
self.cli_set(base + option.split())
self.cli_set(base + ['mtu', self._mtu])
# check validate() - can not set low MTU if 'no-default-link-local'
# is not set on CLI
with self.assertRaises(ConfigSessionError):
self.cli_commit()
for intf in self._interfaces:
base = self._base_path + [intf]
self.cli_set(base + ['ipv6', 'address', 'no-default-link-local'])
# commit interface changes
self.cli_commit()
# verify changed MTU
for intf in self._interfaces:
tmp = get_interface_config(intf)
self.assertEqual(tmp['mtu'], int(self._mtu))
self._mtu = old_mtu
def test_vif_8021q_interfaces(self):
# XXX: This testcase is not allowed to run as first testcase, reason
# is the Wireless test will first load the wifi kernel hwsim module
# which creates a wlan0 and wlan1 interface which will fail the
# tearDown() test in the end that no interface is allowed to survive!
if not self._test_vlan:
self.skipTest(MSG_TESTCASE_UNSUPPORTED)
for interface in self._interfaces:
base = self._base_path + [interface]
for option in self._options.get(interface, []):
self.cli_set(base + option.split())
for vlan in self._vlan_range:
base = self._base_path + [interface, 'vif', vlan]
for address in self._test_addr:
self.cli_set(base + ['address', address])
self.cli_commit()
for intf in self._interfaces:
for vlan in self._vlan_range:
vif = f'{intf}.{vlan}'
for address in self._test_addr:
self.assertTrue(is_intf_addr_assigned(vif, address))
self.assertEqual(Interface(vif).get_admin_state(), 'up')
# T4064: Delete interface addresses, keep VLAN interface
for interface in self._interfaces:
base = self._base_path + [interface]
for vlan in self._vlan_range:
base = self._base_path + [interface, 'vif', vlan]
self.cli_delete(base + ['address'])
self.cli_commit()
# Verify no IP address is assigned
for interface in self._interfaces:
for vlan in self._vlan_range:
vif = f'{intf}.{vlan}'
for address in self._test_addr:
self.assertFalse(is_intf_addr_assigned(vif, address))
def test_vif_8021q_mtu_limits(self):
# XXX: This testcase is not allowed to run as first testcase, reason
# is the Wireless test will first load the wifi kernel hwsim module
# which creates a wlan0 and wlan1 interface which will fail the
# tearDown() test in the end that no interface is allowed to survive!
if not self._test_vlan or not self._test_mtu:
self.skipTest(MSG_TESTCASE_UNSUPPORTED)
mtu_1500 = '1500'
mtu_9000 = '9000'
for interface in self._interfaces:
base = self._base_path + [interface]
self.cli_set(base + ['mtu', mtu_1500])
for option in self._options.get(interface, []):
self.cli_set(base + option.split())
if 'source-interface' in option:
iface = option.split()[-1]
iface_type = Section.section(iface)
self.cli_set(['interfaces', iface_type, iface, 'mtu', mtu_9000])
for vlan in self._vlan_range:
base = self._base_path + [interface, 'vif', vlan]
self.cli_set(base + ['mtu', mtu_9000])
# check validate() - Interface MTU "9000" too high, parent interface MTU is "1500"!
with self.assertRaises(ConfigSessionError):
self.cli_commit()
# Change MTU on base interface to be the same as on the VIF interface
for interface in self._interfaces:
base = self._base_path + [interface]
self.cli_set(base + ['mtu', mtu_9000])
self.cli_commit()
# Verify MTU on base and VIF interfaces
for interface in self._interfaces:
tmp = get_interface_config(interface)
self.assertEqual(tmp['mtu'], int(mtu_9000))
for vlan in self._vlan_range:
tmp = get_interface_config(f'{interface}.{vlan}')
self.assertEqual(tmp['mtu'], int(mtu_9000))
def test_vif_8021q_qos_change(self):
# XXX: This testcase is not allowed to run as first testcase, reason
# is the Wireless test will first load the wifi kernel hwsim module
# which creates a wlan0 and wlan1 interface which will fail the
# tearDown() test in the end that no interface is allowed to survive!
if not self._test_vlan:
self.skipTest(MSG_TESTCASE_UNSUPPORTED)
for interface in self._interfaces:
base = self._base_path + [interface]
for option in self._options.get(interface, []):
self.cli_set(base + option.split())
for vlan in self._vlan_range:
base = self._base_path + [interface, 'vif', vlan]
self.cli_set(base + ['ingress-qos', '0:1'])
self.cli_set(base + ['egress-qos', '1:6'])
self.cli_commit()
for intf in self._interfaces:
for vlan in self._vlan_range:
vif = f'{intf}.{vlan}'
tmp = get_interface_config(f'{vif}')
tmp2 = dict_search('linkinfo.info_data.ingress_qos', tmp)
for item in tmp2 if tmp2 else []:
from_key = item['from']
to_key = item['to']
self.assertEqual(from_key, 0)
self.assertEqual(to_key, 1)
tmp2 = dict_search('linkinfo.info_data.egress_qos', tmp)
for item in tmp2 if tmp2 else []:
from_key = item['from']
to_key = item['to']
self.assertEqual(from_key, 1)
self.assertEqual(to_key, 6)
self.assertEqual(Interface(vif).get_admin_state(), 'up')
new_ingress_qos_from = 1
new_ingress_qos_to = 6
new_egress_qos_from = 2
new_egress_qos_to = 7
for interface in self._interfaces:
base = self._base_path + [interface]
for vlan in self._vlan_range:
base = self._base_path + [interface, 'vif', vlan]
self.cli_set(base + ['ingress-qos', f'{new_ingress_qos_from}:{new_ingress_qos_to}'])
self.cli_set(base + ['egress-qos', f'{new_egress_qos_from}:{new_egress_qos_to}'])
self.cli_commit()
for intf in self._interfaces:
for vlan in self._vlan_range:
vif = f'{intf}.{vlan}'
tmp = get_interface_config(f'{vif}')
tmp2 = dict_search('linkinfo.info_data.ingress_qos', tmp)
if tmp2:
from_key = tmp2[0]['from']
to_key = tmp2[0]['to']
self.assertEqual(from_key, new_ingress_qos_from)
self.assertEqual(to_key, new_ingress_qos_to)
tmp2 = dict_search('linkinfo.info_data.egress_qos', tmp)
if tmp2:
from_key = tmp2[0]['from']
to_key = tmp2[0]['to']
self.assertEqual(from_key, new_egress_qos_from)
self.assertEqual(to_key, new_egress_qos_to)
def test_vif_8021q_lower_up_down(self):
# Testcase for https://vyos.dev/T3349
if not self._test_vlan:
self.skipTest(MSG_TESTCASE_UNSUPPORTED)
for interface in self._interfaces:
base = self._base_path + [interface]
for option in self._options.get(interface, []):
self.cli_set(base + option.split())
# disable the lower interface
self.cli_set(base + ['disable'])
for vlan in self._vlan_range:
vlan_base = self._base_path + [interface, 'vif', vlan]
# disable the vlan interface
self.cli_set(vlan_base + ['disable'])
self.cli_commit()
# re-enable all lower interfaces
for interface in self._interfaces:
base = self._base_path + [interface]
self.cli_delete(base + ['disable'])
self.cli_commit()
# verify that the lower interfaces are admin up and the vlan
# interfaces are all admin down
for interface in self._interfaces:
self.assertEqual(Interface(interface).get_admin_state(), 'up')
for vlan in self._vlan_range:
ifname = f'{interface}.{vlan}'
self.assertEqual(Interface(ifname).get_admin_state(), 'down')
def test_vif_s_8021ad_vlan_interfaces(self):
# XXX: This testcase is not allowed to run as first testcase, reason
# is the Wireless test will first load the wifi kernel hwsim module
# which creates a wlan0 and wlan1 interface which will fail the
# tearDown() test in the end that no interface is allowed to survive!
if not self._test_qinq:
self.skipTest(MSG_TESTCASE_UNSUPPORTED)
for interface in self._interfaces:
base = self._base_path + [interface]
for option in self._options.get(interface, []):
self.cli_set(base + option.split())
for vif_s in self._qinq_range:
for vif_c in self._vlan_range:
base = self._base_path + [interface, 'vif-s', vif_s, 'vif-c', vif_c]
self.cli_set(base + ['mtu', self._mtu])
for address in self._test_addr:
self.cli_set(base + ['address', address])
self.cli_commit()
for interface in self._interfaces:
for vif_s in self._qinq_range:
tmp = get_interface_config(f'{interface}.{vif_s}')
self.assertEqual(dict_search('linkinfo.info_data.protocol', tmp), '802.1ad')
for vif_c in self._vlan_range:
vif = f'{interface}.{vif_s}.{vif_c}'
# For an unknown reason this regularely fails on the QEMU builds,
# thus the test for reading back IP addresses is temporary
# disabled. There is no big deal here, as this uses the same
# methods on 802.1q and here it works and is verified.
# for address in self._test_addr:
# self.assertTrue(is_intf_addr_assigned(vif, address))
tmp = get_interface_config(vif)
self.assertEqual(tmp['mtu'], int(self._mtu))
# T4064: Delete interface addresses, keep VLAN interface
for interface in self._interfaces:
base = self._base_path + [interface]
for vif_s in self._qinq_range:
for vif_c in self._vlan_range:
self.cli_delete(self._base_path + [interface, 'vif-s', vif_s, 'vif-c', vif_c, 'address'])
self.cli_commit()
# Verify no IP address is assigned
for interface in self._interfaces:
base = self._base_path + [interface]
for vif_s in self._qinq_range:
for vif_c in self._vlan_range:
vif = f'{interface}.{vif_s}.{vif_c}'
for address in self._test_addr:
self.assertFalse(is_intf_addr_assigned(vif, address))
# T3972: remove vif-c interfaces from vif-s
for interface in self._interfaces:
base = self._base_path + [interface]
for vif_s in self._qinq_range:
base = self._base_path + [interface, 'vif-s', vif_s, 'vif-c']
self.cli_delete(base)
self.cli_commit()
def test_vif_s_protocol_change(self):
# XXX: This testcase is not allowed to run as first testcase, reason
# is the Wireless test will first load the wifi kernel hwsim module
# which creates a wlan0 and wlan1 interface which will fail the
# tearDown() test in the end that no interface is allowed to survive!
if not self._test_qinq:
self.skipTest(MSG_TESTCASE_UNSUPPORTED)
for interface in self._interfaces:
base = self._base_path + [interface]
for option in self._options.get(interface, []):
self.cli_set(base + option.split())
for vif_s in self._qinq_range:
for vif_c in self._vlan_range:
base = self._base_path + [interface, 'vif-s', vif_s, 'vif-c', vif_c]
for address in self._test_addr:
self.cli_set(base + ['address', address])
self.cli_commit()
for interface in self._interfaces:
for vif_s in self._qinq_range:
tmp = get_interface_config(f'{interface}.{vif_s}')
# check for the default value
self.assertEqual(tmp['linkinfo']['info_data']['protocol'], '802.1ad')
# T3532: now change ethertype
new_protocol = '802.1q'
for interface in self._interfaces:
for vif_s in self._qinq_range:
base = self._base_path + [interface, 'vif-s', vif_s]
self.cli_set(base + ['protocol', new_protocol])
self.cli_commit()
# Verify new ethertype configuration
for interface in self._interfaces:
for vif_s in self._qinq_range:
tmp = get_interface_config(f'{interface}.{vif_s}')
self.assertEqual(tmp['linkinfo']['info_data']['protocol'], new_protocol.upper())
def test_interface_ip_options(self):
if not self._test_ip:
self.skipTest(MSG_TESTCASE_UNSUPPORTED)
arp_tmo = '300'
mss = '1420'
for interface in self._interfaces:
path = self._base_path + [interface]
for option in self._options.get(interface, []):
self.cli_set(path + option.split())
# Options
if cli_defined(self._base_path + ['ip'], 'adjust-mss'):
self.cli_set(path + ['ip', 'adjust-mss', mss])
if cli_defined(self._base_path + ['ip'], 'arp-cache-timeout'):
self.cli_set(path + ['ip', 'arp-cache-timeout', arp_tmo])
if cli_defined(self._base_path + ['ip'], 'disable-arp-filter'):
self.cli_set(path + ['ip', 'disable-arp-filter'])
if cli_defined(self._base_path + ['ip'], 'disable-forwarding'):
self.cli_set(path + ['ip', 'disable-forwarding'])
if cli_defined(self._base_path + ['ip'], 'enable-directed-broadcast'):
self.cli_set(path + ['ip', 'enable-directed-broadcast'])
if cli_defined(self._base_path + ['ip'], 'enable-arp-accept'):
self.cli_set(path + ['ip', 'enable-arp-accept'])
if cli_defined(self._base_path + ['ip'], 'enable-arp-announce'):
self.cli_set(path + ['ip', 'enable-arp-announce'])
if cli_defined(self._base_path + ['ip'], 'enable-arp-ignore'):
self.cli_set(path + ['ip', 'enable-arp-ignore'])
if cli_defined(self._base_path + ['ip'], 'enable-proxy-arp'):
self.cli_set(path + ['ip', 'enable-proxy-arp'])
if cli_defined(self._base_path + ['ip'], 'proxy-arp-pvlan'):
self.cli_set(path + ['ip', 'proxy-arp-pvlan'])
if cli_defined(self._base_path + ['ip'], 'source-validation'):
self.cli_set(path + ['ip', 'source-validation', 'loose'])
self.cli_commit()
for interface in self._interfaces:
if cli_defined(self._base_path + ['ip'], 'adjust-mss'):
base_options = f'oifname "{interface}"'
out = cmd('sudo nft list chain raw VYOS_TCP_MSS')
for line in out.splitlines():
if line.startswith(base_options):
self.assertIn(f'tcp option maxseg size set {mss}', line)
if cli_defined(self._base_path + ['ip'], 'arp-cache-timeout'):
tmp = read_file(f'/proc/sys/net/ipv4/neigh/{interface}/base_reachable_time_ms')
self.assertEqual(tmp, str((int(arp_tmo) * 1000))) # tmo value is in milli seconds
proc_base = f'/proc/sys/net/ipv4/conf/{interface}'
if cli_defined(self._base_path + ['ip'], 'disable-arp-filter'):
tmp = read_file(f'{proc_base}/arp_filter')
self.assertEqual('0', tmp)
if cli_defined(self._base_path + ['ip'], 'enable-arp-accept'):
tmp = read_file(f'{proc_base}/arp_accept')
self.assertEqual('1', tmp)
if cli_defined(self._base_path + ['ip'], 'enable-arp-announce'):
tmp = read_file(f'{proc_base}/arp_announce')
self.assertEqual('1', tmp)
if cli_defined(self._base_path + ['ip'], 'enable-arp-ignore'):
tmp = read_file(f'{proc_base}/arp_ignore')
self.assertEqual('1', tmp)
if cli_defined(self._base_path + ['ip'], 'disable-forwarding'):
tmp = read_file(f'{proc_base}/forwarding')
self.assertEqual('0', tmp)
if cli_defined(self._base_path + ['ip'], 'enable-directed-broadcast'):
tmp = read_file(f'{proc_base}/bc_forwarding')
self.assertEqual('1', tmp)
if cli_defined(self._base_path + ['ip'], 'enable-proxy-arp'):
tmp = read_file(f'{proc_base}/proxy_arp')
self.assertEqual('1', tmp)
if cli_defined(self._base_path + ['ip'], 'proxy-arp-pvlan'):
tmp = read_file(f'{proc_base}/proxy_arp_pvlan')
self.assertEqual('1', tmp)
if cli_defined(self._base_path + ['ip'], 'source-validation'):
base_options = f'iifname "{interface}"'
out = cmd('sudo nft list chain ip raw vyos_rpfilter')
for line in out.splitlines():
if line.startswith(base_options):
self.assertIn('fib saddr oif 0', line)
self.assertIn('drop', line)
def test_interface_ipv6_options(self):
if not self._test_ipv6:
self.skipTest(MSG_TESTCASE_UNSUPPORTED)
mss = '1400'
dad_transmits = '10'
accept_dad = '0'
source_validation = 'strict'
interface_identifier = '::fffe'
for interface in self._interfaces:
path = self._base_path + [interface]
for option in self._options.get(interface, []):
self.cli_set(path + option.split())
# Options
if cli_defined(self._base_path + ['ipv6'], 'adjust-mss'):
self.cli_set(path + ['ipv6', 'adjust-mss', mss])
if cli_defined(self._base_path + ['ipv6'], 'accept-dad'):
self.cli_set(path + ['ipv6', 'accept-dad', accept_dad])
if cli_defined(self._base_path + ['ipv6'], 'dup-addr-detect-transmits'):
self.cli_set(path + ['ipv6', 'dup-addr-detect-transmits', dad_transmits])
if cli_defined(self._base_path + ['ipv6'], 'disable-forwarding'):
self.cli_set(path + ['ipv6', 'disable-forwarding'])
if cli_defined(self._base_path + ['ipv6'], 'source-validation'):
self.cli_set(path + ['ipv6', 'source-validation', source_validation])
if cli_defined(self._base_path + ['ipv6', 'address'], 'interface-identifier'):
self.cli_set(path + ['ipv6', 'address', 'interface-identifier', interface_identifier])
self.cli_commit()
for interface in self._interfaces:
proc_base = f'/proc/sys/net/ipv6/conf/{interface}'
if cli_defined(self._base_path + ['ipv6'], 'adjust-mss'):
base_options = f'oifname "{interface}"'
out = cmd('sudo nft list chain ip6 raw VYOS_TCP_MSS')
for line in out.splitlines():
if line.startswith(base_options):
self.assertIn(f'tcp option maxseg size set {mss}', line)
if cli_defined(self._base_path + ['ipv6'], 'accept-dad'):
tmp = read_file(f'{proc_base}/accept_dad')
self.assertEqual(accept_dad, tmp)
if cli_defined(self._base_path + ['ipv6'], 'dup-addr-detect-transmits'):
tmp = read_file(f'{proc_base}/dad_transmits')
self.assertEqual(dad_transmits, tmp)
if cli_defined(self._base_path + ['ipv6'], 'disable-forwarding'):
tmp = read_file(f'{proc_base}/forwarding')
self.assertEqual('0', tmp)
if cli_defined(self._base_path + ['ipv6'], 'source-validation'):
base_options = f'iifname "{interface}"'
out = cmd('sudo nft list chain ip6 raw vyos_rpfilter')
for line in out.splitlines():
if line.startswith(base_options):
self.assertIn('fib saddr . iif oif 0', line)
self.assertIn('drop', line)
if cli_defined(self._base_path + ['ipv6', 'address'], 'interface-identifier'):
tmp = cmd(f'ip -j token show dev {interface}')
tmp = loads(tmp)[0]
self.assertEqual(tmp['token'], interface_identifier)
self.assertEqual(tmp['ifname'], interface)
def test_dhcpv6_client_options(self):
if not self._test_ipv6_dhcpc6:
self.skipTest(MSG_TESTCASE_UNSUPPORTED)
duid_base = 10
for interface in self._interfaces:
duid = f'00:01:00:01:27:71:db:f0:00:50:00:00:00:{duid_base}'
path = self._base_path + [interface]
for option in self._options.get(interface, []):
self.cli_set(path + option.split())
# Enable DHCPv6 client
self.cli_set(path + ['address', 'dhcpv6'])
self.cli_set(path + ['dhcpv6-options', 'no-release'])
self.cli_set(path + ['dhcpv6-options', 'rapid-commit'])
self.cli_set(path + ['dhcpv6-options', 'parameters-only'])
self.cli_set(path + ['dhcpv6-options', 'duid', duid])
duid_base += 1
self.cli_commit()
duid_base = 10
for interface in self._interfaces:
duid = f'00:01:00:01:27:71:db:f0:00:50:00:00:00:{duid_base}'
dhcpc6_config = read_file(f'{dhcp6c_base_dir}/dhcp6c.{interface}.conf')
self.assertIn(f'interface {interface} ' + '{', dhcpc6_config)
self.assertIn(f' request domain-name-servers;', dhcpc6_config)
self.assertIn(f' request domain-name;', dhcpc6_config)
self.assertIn(f' information-only;', dhcpc6_config)
self.assertIn(f' send ia-na 0;', dhcpc6_config)
self.assertIn(f' send rapid-commit;', dhcpc6_config)
self.assertIn(f' send client-id {duid};', dhcpc6_config)
self.assertIn('};', dhcpc6_config)
duid_base += 1
# T7058: verify daemon has no problems understanding the custom DUID option
j = journal.Reader()
j.this_boot()
j.add_match(_SYSTEMD_UNIT=f'dhcp6c@{interface}.service')
for entry in j:
self.assertNotIn('yyerror0', entry.get('MESSAGE', ''))
self.assertNotIn('syntax error', entry.get('MESSAGE', ''))
# Better ask the process about it's commandline in the future
pid = process_named_running(dhcp6c_process_name, cmdline=interface, timeout=10)
self.assertTrue(pid)
# DHCPv6 option "no-release" requires "-n" daemon startup option
dhcp6c_options = read_file(f'/proc/{pid}/cmdline')
self.assertIn('-n', dhcp6c_options)
def test_dhcpv6pd_auto_sla_id(self):
if not self._test_ipv6_pd:
self.skipTest(MSG_TESTCASE_UNSUPPORTED)
prefix_len = '56'
sla_len = str(64 - int(prefix_len))
# Create delegatee interfaces first to avoid any confusion by dhcpc6
# this is mainly an "issue" with virtual-ethernet interfaces
delegatees = ['dum2340', 'dum2341', 'dum2342', 'dum2343', 'dum2344']
for delegatee in delegatees:
section = Section.section(delegatee)
self.cli_set(['interfaces', section, delegatee])
self.cli_commit()
for interface in self._interfaces:
path = self._base_path + [interface]
for option in self._options.get(interface, []):
self.cli_set(path + option.split())
address = '1'
# prefix delegation stuff
pd_base = path + ['dhcpv6-options', 'pd', '0']
self.cli_set(pd_base + ['length', prefix_len])
for delegatee in delegatees:
self.cli_set(pd_base + ['interface', delegatee, 'address', address])
# increment interface address
address = str(int(address) + 1)
self.cli_commit()
for interface in self._interfaces:
dhcpc6_config = read_file(f'{dhcp6c_base_dir}/dhcp6c.{interface}.conf')
# verify DHCPv6 prefix delegation
self.assertIn(f'prefix ::/{prefix_len} infinity;', dhcpc6_config)
address = '1'
sla_id = '0'
for delegatee in delegatees:
self.assertIn(f'prefix-interface {delegatee}' + r' {', dhcpc6_config)
self.assertIn(f'ifid {address};', dhcpc6_config)
self.assertIn(f'sla-id {sla_id};', dhcpc6_config)
# increment sla-id
sla_id = str(int(sla_id) + 1)
# increment interface address
address = str(int(address) + 1)
# Check for running process
self.assertTrue(process_named_running(dhcp6c_process_name, cmdline=interface, timeout=10))
for delegatee in delegatees:
# we can already cleanup the test delegatee interface here
# as until commit() is called, nothing happens
section = Section.section(delegatee)
self.cli_delete(['interfaces', section, delegatee])
def test_dhcpv6pd_manual_sla_id(self):
if not self._test_ipv6_pd:
self.skipTest(MSG_TESTCASE_UNSUPPORTED)
prefix_len = '56'
sla_len = str(64 - int(prefix_len))
# Create delegatee interfaces first to avoid any confusion by dhcpc6
# this is mainly an "issue" with virtual-ethernet interfaces
delegatees = ['dum3340', 'dum3341', 'dum3342', 'dum3343', 'dum3344']
for delegatee in delegatees:
section = Section.section(delegatee)
self.cli_set(['interfaces', section, delegatee])
self.cli_commit()
for interface in self._interfaces:
path = self._base_path + [interface]
for option in self._options.get(interface, []):
self.cli_set(path + option.split())
# prefix delegation stuff
address = '1'
sla_id = '1'
pd_base = path + ['dhcpv6-options', 'pd', '0']
self.cli_set(pd_base + ['length', prefix_len])
for delegatee in delegatees:
self.cli_set(pd_base + ['interface', delegatee, 'address', address])
self.cli_set(pd_base + ['interface', delegatee, 'sla-id', sla_id])
# increment interface address
address = str(int(address) + 1)
sla_id = str(int(sla_id) + 1)
self.cli_commit()
# Verify dhcpc6 client configuration
for interface in self._interfaces:
address = '1'
sla_id = '1'
dhcpc6_config = read_file(f'{dhcp6c_base_dir}/dhcp6c.{interface}.conf')
# verify DHCPv6 prefix delegation
self.assertIn(f'prefix ::/{prefix_len} infinity;', dhcpc6_config)
for delegatee in delegatees:
self.assertIn(f'prefix-interface {delegatee}' + r' {', dhcpc6_config)
self.assertIn(f'ifid {address};', dhcpc6_config)
self.assertIn(f'sla-id {sla_id};', dhcpc6_config)
# increment sla-id
sla_id = str(int(sla_id) + 1)
# increment interface address
address = str(int(address) + 1)
# Check for running process
self.assertTrue(process_named_running(dhcp6c_process_name, cmdline=interface, timeout=10))
for delegatee in delegatees:
# we can already cleanup the test delegatee interface here
# as until commit() is called, nothing happens
section = Section.section(delegatee)
self.cli_delete(['interfaces', section, delegatee])
def test_eapol(self):
if not self._test_eapol:
self.skipTest(MSG_TESTCASE_UNSUPPORTED)
cfg_dir = '/run/wpa_supplicant'
ca_certs = {
'eapol-server-ca-root': server_ca_root_cert_data,
'eapol-server-ca-intermediate': server_ca_intermediate_cert_data,
'eapol-client-ca-root': client_ca_root_cert_data,
'eapol-client-ca-intermediate': client_ca_intermediate_cert_data,
}
cert_name = 'eapol-client'
for name, data in ca_certs.items():
self.cli_set(['pki', 'ca', name, 'certificate', data.replace('\n','')])
self.cli_set(['pki', 'certificate', cert_name, 'certificate', client_cert_data.replace('\n','')])
self.cli_set(['pki', 'certificate', cert_name, 'private', 'key', client_key_data.replace('\n','')])
for interface in self._interfaces:
path = self._base_path + [interface]
for option in self._options.get(interface, []):
self.cli_set(path + option.split())
# Enable EAPoL
self.cli_set(self._base_path + [interface, 'eapol', 'ca-certificate', 'eapol-server-ca-intermediate'])
self.cli_set(self._base_path + [interface, 'eapol', 'ca-certificate', 'eapol-client-ca-intermediate'])
self.cli_set(self._base_path + [interface, 'eapol', 'certificate', cert_name])
self.cli_commit()
# Test multiple CA chains
self.assertEqual(get_certificate_count(interface, 'ca'), 4)
for interface in self._interfaces:
self.cli_delete(self._base_path + [interface, 'eapol', 'ca-certificate', 'eapol-client-ca-intermediate'])
self.cli_commit()
# Validate interface config
for interface in self._interfaces:
tmp = get_wpa_supplicant_value(interface, 'key_mgmt')
self.assertEqual('IEEE8021X', tmp)
tmp = get_wpa_supplicant_value(interface, 'eap')
self.assertEqual('TLS', tmp)
tmp = get_wpa_supplicant_value(interface, 'eapol_flags')
self.assertEqual('0', tmp)
tmp = get_wpa_supplicant_value(interface, 'ca_cert')
self.assertEqual(f'"{cfg_dir}/{interface}_ca.pem"', tmp)
tmp = get_wpa_supplicant_value(interface, 'client_cert')
self.assertEqual(f'"{cfg_dir}/{interface}_cert.pem"', tmp)
tmp = get_wpa_supplicant_value(interface, 'private_key')
self.assertEqual(f'"{cfg_dir}/{interface}_cert.key"', tmp)
mac = read_file(f'/sys/class/net/{interface}/address')
tmp = get_wpa_supplicant_value(interface, 'identity')
self.assertEqual(f'"{mac}"', tmp)
# Check certificate files have the full chain
self.assertEqual(get_certificate_count(interface, 'ca'), 2)
self.assertEqual(get_certificate_count(interface, 'cert'), 3)
# Check for running process
self.assertTrue(process_named_running('wpa_supplicant', cmdline=f'-i{interface}'))
# Remove EAPoL configuration
for interface in self._interfaces:
self.cli_delete(self._base_path + [interface, 'eapol'])
# Commit and check that process is no longer running
self.cli_commit()
self.assertFalse(process_named_running('wpa_supplicant'))
for name in ca_certs:
self.cli_delete(['pki', 'ca', name])
self.cli_delete(['pki', 'certificate', cert_name])
diff --git a/smoketest/scripts/cli/base_vyostest_shim.py b/smoketest/scripts/cli/base_vyostest_shim.py
index 9f4656dfc..fd14f529f 100644
--- a/smoketest/scripts/cli/base_vyostest_shim.py
+++ b/smoketest/scripts/cli/base_vyostest_shim.py
@@ -1,245 +1,231 @@
# Copyright VyOS maintainers and contributors <maintainers@vyos.io>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
import unittest
import paramiko
import pprint
from time import sleep
from typing import Type
from vyos.configsession import ConfigSession
from vyos.configsession import ConfigSessionError
from vyos import ConfigError
from vyos.defaults import commit_lock
from vyos.utils.process import cmd
from vyos.utils.process import run
save_config = '/tmp/vyos-smoketest-save'
-# The commit process is not finished until all pending files from
-# VYATTA_CHANGES_ONLY_DIR are copied to VYATTA_ACTIVE_CONFIGURATION_DIR. This
-# is done inside libvyatta-cfg1 and the FUSE UnionFS part. On large non-
-# interactive commits FUSE UnionFS might not replicate the real state in time,
-# leading to errors when querying the working and effective configuration.
-# TO BE DELETED AFTER SWITCH TO IN MEMORY CONFIG
-CSTORE_GUARD_TIME = 4
-
# This class acts as shim between individual Smoketests developed for VyOS and
# the Python UnitTest framework. Before every test is loaded, we dump the current
# system configuration and reload it after the test - despite the test results.
#
# Using this approach we can not render a live system useless while running any
# kind of smoketest. In addition it adds debug capabilities like printing the
# command used to execute the test.
class VyOSUnitTestSHIM:
class TestCase(unittest.TestCase):
# if enabled in derived class, print out each and every set/del command
# on the CLI. This is usefull to grap all the commands required to
# trigger the certain failure condition.
# Use "self.debug = True" in derived classes setUp() method
debug = False
- # Time to wait after a commit to ensure the CStore is up to date
- # only required for testcases using FRR
- _commit_guard_time = 0
@staticmethod
def debug_on():
return os.path.exists('/tmp/vyos.smoketest.debug')
@classmethod
def setUpClass(cls):
cls._session = ConfigSession(os.getpid())
cls._session.save_config(save_config)
cls.debug = cls.debug_on()
pass
@classmethod
def tearDownClass(cls):
# discard any pending changes which might caused a messed up config
cls._session.discard()
# ... and restore the initial state
cls._session.migrate_and_load_config(save_config)
try:
cls._session.commit()
except (ConfigError, ConfigSessionError):
cls._session.discard()
cls.fail(cls)
def cli_set(self, path, value=None):
if self.debug:
str = f'set {" ".join(path)} {value}' if value else f'set {" ".join(path)}'
print(str)
self._session.set(path, value)
def cli_delete(self, config):
if self.debug:
print('del ' + ' '.join(config))
self._session.delete(config)
def cli_discard(self):
if self.debug:
print('DISCARD')
self._session.discard()
def cli_commit(self):
if self.debug:
print('commit')
# During a commit there is a process opening commit_lock, and run()
# returns 0
while run(f'sudo lsof -nP {commit_lock}') == 0:
sleep(0.250)
# Return the output of commit
# Necessary for testing Warning cases
out = self._session.commit()
- # Wait for CStore completion for fast non-interactive commits
- sleep(self._commit_guard_time)
-
return out
def cli_save(self, file):
if self.debug:
print('save')
self._session.save_config(file)
def op_mode(self, path : list) -> None:
"""
Execute OP-mode command and return stdout
"""
if self.debug:
print('commit')
path = ' '.join(path)
out = cmd(f'/opt/vyatta/bin/vyatta-op-cmd-wrapper {path}')
if self.debug:
print(f'\n\ncommand "{path}" returned:\n')
pprint.pprint(out)
return out
def getFRRconfig(self, string=None, end='$', endsection='^!',
substring=None, endsubsection=None, empty_retry=0):
"""
Retrieve current "running configuration" from FRR
string: search for a specific start string in the configuration
end: end of the section to search for (line ending)
endsection: end of the configuration
substring: search section under the result found by string
endsubsection: end of the subsection (usually something with "exit")
"""
command = f'vtysh -c "show run no-header"'
if string:
command += f' | sed -n "/^{string}{end}/,/{endsection}/p"'
if substring and endsubsection:
command += f' | sed -n "/^{substring}/,/{endsubsection}/p"'
out = cmd(command)
if self.debug:
print(f'\n\ncommand "{command}" returned:\n')
pprint.pprint(out)
if empty_retry > 0:
retry_count = 0
while not out and retry_count < empty_retry:
if self.debug and retry_count % 10 == 0:
print(f"Attempt {retry_count}: FRR config is still empty. Retrying...")
retry_count += 1
sleep(1)
out = cmd(command)
if not out:
print(f'FRR configuration still empty after {empty_retry} retires!')
return out
def getFRRopmode(self, command : str, json : bool=False):
from json import loads
if json: command += f' json'
out = cmd(f'vtysh -c "{command}"')
if json:
out = loads(out)
if self.debug:
print(f'\n\ncommand "{command}" returned:\n')
pprint.pprint(out)
return out
@staticmethod
def ssh_send_cmd(command, username, password, key_filename=None,
hostname='localhost'):
""" SSH command execution helper """
# Try to login via SSH
ssh_client = paramiko.SSHClient()
ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh_client.connect(hostname=hostname, username=username,
password=password, key_filename=key_filename)
_, stdout, stderr = ssh_client.exec_command(command)
output = stdout.read().decode().strip()
error = stderr.read().decode().strip()
ssh_client.close()
return output, error
# Verify nftables output
def verify_nftables(self, nftables_search, table, inverse=False, args=''):
nftables_output = cmd(f'sudo nft {args} list table {table}')
for search in nftables_search:
matched = False
for line in nftables_output.split("\n"):
if all(item in line for item in search):
matched = True
break
self.assertTrue(not matched if inverse else matched, msg=search)
def verify_nftables_chain(self, nftables_search, table, chain, inverse=False, args=''):
nftables_output = cmd(f'sudo nft {args} list chain {table} {chain}')
for search in nftables_search:
matched = False
for line in nftables_output.split("\n"):
if all(item in line for item in search):
matched = True
break
self.assertTrue(not matched if inverse else matched, msg=search)
def verify_nftables_chain_exists(self, table, chain, inverse=False):
try:
cmd(f'sudo nft list chain {table} {chain}')
if inverse:
self.fail(f'Chain exists: {table} {chain}')
except OSError:
if not inverse:
self.fail(f'Chain does not exist: {table} {chain}')
# Verify ip rule output
def verify_rules(self, rules_search, inverse=False, addr_family='inet'):
rule_output = cmd(f'ip -family {addr_family} rule show')
for search in rules_search:
matched = False
for line in rule_output.split("\n"):
if all(item in line for item in search):
matched = True
break
self.assertTrue(not matched if inverse else matched, msg=search)
# standard construction; typing suggestion: https://stackoverflow.com/a/70292317
def ignore_warning(warning: Type[Warning]):
import warnings
from functools import wraps
def inner(f):
@wraps(f)
def wrapped(*args, **kwargs):
with warnings.catch_warnings():
warnings.simplefilter("ignore", category=warning)
return f(*args, **kwargs)
return wrapped
return inner
diff --git a/smoketest/scripts/cli/test_policy.py b/smoketest/scripts/cli/test_policy.py
index 69e768a4b..6e61c395c 100755
--- a/smoketest/scripts/cli/test_policy.py
+++ b/smoketest/scripts/cli/test_policy.py
@@ -1,2054 +1,2051 @@
#!/usr/bin/env python3
#
# Copyright VyOS maintainers and contributors <maintainers@vyos.io>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
-from base_vyostest_shim import CSTORE_GUARD_TIME
from vyos.configsession import ConfigSessionError
from vyos.utils.process import cmd
base_path = ['policy']
class TestPolicy(VyOSUnitTestSHIM.TestCase):
@classmethod
def setUpClass(cls):
super(TestPolicy, cls).setUpClass()
# ensure we can also run this test on a live system - so lets clean
# out the current configuration :)
cls.cli_delete(cls, base_path)
cls.cli_delete(cls, ['vrf'])
- # Enable CSTORE guard time required by FRR related tests
- cls._commit_guard_time = CSTORE_GUARD_TIME
def tearDown(self):
self.cli_delete(base_path)
self.cli_commit()
def test_access_list(self):
acls = {
'50' : {
'rule' : {
'5' : {
'action' : 'permit',
'source' : { 'any' : '' },
},
'10' : {
'action' : 'deny',
'source' : { 'host' : '1.2.3.4' },
},
},
},
'150' : {
'rule' : {
'5' : {
'action' : 'permit',
'source' : { 'any' : '' },
'destination' : { 'host' : '2.2.2.2' },
},
'10' : {
'action' : 'deny',
'source' : { 'any' : '' },
'destination' : { 'any' : '' },
},
},
},
'2000' : {
'rule' : {
'5' : {
'action' : 'permit',
'destination' : { 'any' : '' },
'source' : { 'network' : '10.0.0.0', 'inverse-mask' : '0.255.255.255' },
},
'10' : {
'action' : 'permit',
'destination' : { 'any' : '' },
'source' : { 'network' : '172.16.0.0', 'inverse-mask' : '0.15.255.255' },
},
'15' : {
'action' : 'permit',
'destination' : { 'any' : '' },
'source' : { 'network' : '192.168.0.0', 'inverse-mask' : '0.0.255.255' },
},
'20' : {
'action' : 'permit',
'destination' : { 'network' : '172.16.0.0', 'inverse-mask' : '0.15.255.255' },
'source' : { 'network' : '10.0.0.0', 'inverse-mask' : '0.255.255.255' },
},
'25' : {
'action' : 'deny',
'destination' : { 'network' : '192.168.0.0', 'inverse-mask' : '0.0.255.255' },
'source' : { 'network' : '172.16.0.0', 'inverse-mask' : '0.15.255.255' },
},
'30' : {
'action' : 'deny',
'destination' : { 'any' : '' },
'source' : { 'any' : '' },
},
},
},
}
for acl, acl_config in acls.items():
path = base_path + ['access-list', acl]
self.cli_set(path + ['description', f'VyOS-ACL-{acl}'])
if 'rule' not in acl_config:
continue
for rule, rule_config in acl_config['rule'].items():
self.cli_set(path + ['rule', rule, 'action', rule_config['action']])
for direction in ['source', 'destination']:
if direction in rule_config:
if 'any' in rule_config[direction]:
self.cli_set(path + ['rule', rule, direction, 'any'])
if 'host' in rule_config[direction]:
self.cli_set(path + ['rule', rule, direction, 'host', rule_config[direction]['host']])
if 'network' in rule_config[direction]:
self.cli_set(path + ['rule', rule, direction, 'network', rule_config[direction]['network']])
self.cli_set(path + ['rule', rule, direction, 'inverse-mask', rule_config[direction]['inverse-mask']])
self.cli_commit()
config = self.getFRRconfig('access-list', end='')
for acl, acl_config in acls.items():
for rule, rule_config in acl_config['rule'].items():
tmp = f'access-list {acl} seq {rule}'
if rule_config['action'] == 'permit':
tmp += ' permit'
else:
tmp += ' deny'
if {'source', 'destination'} <= set(rule_config):
tmp += ' ip'
for direction in ['source', 'destination']:
if direction in rule_config:
if 'any' in rule_config[direction]:
tmp += ' any'
if 'host' in rule_config[direction]:
# XXX: Some weird side rule from the old vyatta days
# possible to clean this up after the vyos-1x migration
if int(acl) in range(100, 200) or int(acl) in range(2000, 2700):
tmp += ' host'
tmp += ' ' + rule_config[direction]['host']
if 'network' in rule_config[direction]:
tmp += ' ' + rule_config[direction]['network'] + ' ' + rule_config[direction]['inverse-mask']
self.assertIn(tmp, config)
def test_access_list6(self):
acls = {
'50' : {
'rule' : {
'5' : {
'action' : 'permit',
'source' : { 'any' : '' },
},
'10' : {
'action' : 'deny',
'source' : { 'network' : '2001:db8:10::/48', 'exact-match' : '' },
},
'15' : {
'action' : 'deny',
'source' : { 'network' : '2001:db8:20::/48' },
},
},
},
'100' : {
'rule' : {
'5' : {
'action' : 'deny',
'source' : { 'network' : '2001:db8:10::/64', 'exact-match' : '' },
},
'10' : {
'action' : 'deny',
'source' : { 'network' : '2001:db8:20::/64', },
},
'15' : {
'action' : 'deny',
'source' : { 'network' : '2001:db8:30::/64', 'exact-match' : '' },
},
'20' : {
'action' : 'deny',
'source' : { 'network' : '2001:db8:40::/64', 'exact-match' : '' },
},
'25' : {
'action' : 'deny',
'source' : { 'any' : '' },
},
},
},
}
for acl, acl_config in acls.items():
path = base_path + ['access-list6', acl]
self.cli_set(path + ['description', f'VyOS-ACL-{acl}'])
if 'rule' not in acl_config:
continue
for rule, rule_config in acl_config['rule'].items():
self.cli_set(path + ['rule', rule, 'action', rule_config['action']])
for direction in ['source', 'destination']:
if direction in rule_config:
if 'any' in rule_config[direction]:
self.cli_set(path + ['rule', rule, direction, 'any'])
if 'network' in rule_config[direction]:
self.cli_set(path + ['rule', rule, direction, 'network', rule_config[direction]['network']])
if 'exact-match' in rule_config[direction]:
self.cli_set(path + ['rule', rule, direction, 'exact-match'])
self.cli_commit()
config = self.getFRRconfig('ipv6 access-list', end='')
for acl, acl_config in acls.items():
for rule, rule_config in acl_config['rule'].items():
tmp = f'ipv6 access-list {acl} seq {rule}'
if rule_config['action'] == 'permit':
tmp += ' permit'
else:
tmp += ' deny'
if {'source', 'destination'} <= set(rule_config):
tmp += ' ip'
for direction in ['source', 'destination']:
if direction in rule_config:
if 'any' in rule_config[direction]:
tmp += ' any'
if 'network' in rule_config[direction]:
tmp += ' ' + rule_config[direction]['network']
if 'exact-match' in rule_config[direction]:
tmp += ' exact-match'
self.assertIn(tmp, config)
def test_as_path_list(self):
test_data = {
'VyOS' : {
'rule' : {
'5' : {
'action' : 'permit',
'regex' : '^44501 64502$',
},
'10' : {
'action' : 'permit',
'regex' : '44501|44502|44503',
},
'15' : {
'action' : 'permit',
'regex' : '^44501_([0-9]+_)+',
},
},
},
'Customers' : {
'rule' : {
'5' : {
'action' : 'permit',
'regex' : '_10_',
},
'10' : {
'action' : 'permit',
'regex' : '_20_',
},
'15' : {
'action' : 'permit',
'regex' : '_30_',
},
'20' : {
'action' : 'deny',
'regex' : '_40_',
},
},
},
'bogons' : {
'rule' : {
'5' : {
'action' : 'permit',
'regex' : '_0_',
},
'10' : {
'action' : 'permit',
'regex' : '_23456_',
},
'15' : {
'action' : 'permit',
'regex' : '_6449[6-9]_|_65[0-4][0-9][0-9]_|_655[0-4][0-9]_|_6555[0-1]_',
},
'20' : {
'action' : 'permit',
'regex' : '_6555[2-9]_|_655[6-9][0-9]_|_65[6-9][0-9][0-9]_|_6[6-9][0-9][0-9][0-]_|_[7-9][0-9][0-9][0-9][0-9]_|_1[0-2][0-9][0-9][0-9][0-9]_|_130[0-9][0-9][0-9]_|_1310[0-6][0-9]_|_13107[01]_',
},
},
},
}
for as_path, as_path_config in test_data.items():
path = base_path + ['as-path-list', as_path]
self.cli_set(path + ['description', f'VyOS-ASPATH-{as_path}'])
if 'rule' not in as_path_config:
continue
for rule, rule_config in as_path_config['rule'].items():
if 'action' in rule_config:
self.cli_set(path + ['rule', rule, 'action', rule_config['action']])
if 'regex' in rule_config:
self.cli_set(path + ['rule', rule, 'regex', rule_config['regex']])
self.cli_commit()
config = self.getFRRconfig('bgp as-path access-list', end='')
for as_path, as_path_config in test_data.items():
if 'rule' not in as_path_config:
continue
for rule, rule_config in as_path_config['rule'].items():
tmp = f'bgp as-path access-list {as_path} seq {rule}'
if rule_config['action'] == 'permit':
tmp += ' permit'
else:
tmp += ' deny'
tmp += ' ' + rule_config['regex']
self.assertIn(tmp, config)
def test_community_list(self):
test_data = {
'100' : {
'rule' : {
'5' : {
'action' : 'permit',
'regex' : '.*',
},
},
},
'200' : {
'rule' : {
'5' : {
'action' : 'deny',
'regex' : '^1:201$',
},
'10' : {
'action' : 'deny',
'regex' : '1:101$',
},
'15' : {
'action' : 'deny',
'regex' : '^1:100$',
},
},
},
}
for comm_list, comm_list_config in test_data.items():
path = base_path + ['community-list', comm_list]
self.cli_set(path + ['description', f'VyOS-COMM-{comm_list}'])
if 'rule' not in comm_list_config:
continue
for rule, rule_config in comm_list_config['rule'].items():
if 'action' in rule_config:
self.cli_set(path + ['rule', rule, 'action', rule_config['action']])
if 'regex' in rule_config:
self.cli_set(path + ['rule', rule, 'regex', rule_config['regex']])
self.cli_commit()
config = self.getFRRconfig('bgp community-list', end='')
for comm_list, comm_list_config in test_data.items():
if 'rule' not in comm_list_config:
continue
for rule, rule_config in comm_list_config['rule'].items():
tmp = f'bgp community-list {comm_list} seq {rule}'
if rule_config['action'] == 'permit':
tmp += ' permit'
else:
tmp += ' deny'
tmp += ' ' + rule_config['regex']
self.assertIn(tmp, config)
def test_extended_community_list(self):
test_data = {
'foo' : {
'rule' : {
'5' : {
'action' : 'permit',
'regex' : '.*',
},
},
},
'200' : {
'rule' : {
'5' : {
'action' : 'deny',
'regex' : '^1:201$',
},
'10' : {
'action' : 'deny',
'regex' : '1:101$',
},
'15' : {
'action' : 'deny',
'regex' : '^1:100$',
},
},
},
}
for comm_list, comm_list_config in test_data.items():
path = base_path + ['extcommunity-list', comm_list]
self.cli_set(path + ['description', f'VyOS-EXTCOMM-{comm_list}'])
if 'rule' not in comm_list_config:
continue
for rule, rule_config in comm_list_config['rule'].items():
if 'action' in rule_config:
self.cli_set(path + ['rule', rule, 'action', rule_config['action']])
if 'regex' in rule_config:
self.cli_set(path + ['rule', rule, 'regex', rule_config['regex']])
self.cli_commit()
config = self.getFRRconfig('bgp extcommunity-list', end='')
for comm_list, comm_list_config in test_data.items():
if 'rule' not in comm_list_config:
continue
for rule, rule_config in comm_list_config['rule'].items():
# if the community is not a number but a name, the expanded
# keyword is used
expanded = ''
if not comm_list.isnumeric():
expanded = ' expanded'
tmp = f'bgp extcommunity-list{expanded} {comm_list} seq {rule}'
if rule_config['action'] == 'permit':
tmp += ' permit'
else:
tmp += ' deny'
tmp += ' ' + rule_config['regex']
self.assertIn(tmp, config)
def test_large_community_list(self):
test_data = {
'foo' : {
'rule' : {
'5' : {
'action' : 'permit',
'regex' : '667:123:100',
},
},
},
'bar' : {
'rule' : {
'5' : {
'action' : 'permit',
'regex' : '65000:120:10',
},
'10' : {
'action' : 'permit',
'regex' : '65000:120:20',
},
'15' : {
'action' : 'permit',
'regex' : '65000:120:30',
},
},
},
}
for comm_list, comm_list_config in test_data.items():
path = base_path + ['large-community-list', comm_list]
self.cli_set(path + ['description', f'VyOS-LARGECOMM-{comm_list}'])
if 'rule' not in comm_list_config:
continue
for rule, rule_config in comm_list_config['rule'].items():
if 'action' in rule_config:
self.cli_set(path + ['rule', rule, 'action', rule_config['action']])
if 'regex' in rule_config:
self.cli_set(path + ['rule', rule, 'regex', rule_config['regex']])
self.cli_commit()
config = self.getFRRconfig('bgp large-community-list', end='')
for comm_list, comm_list_config in test_data.items():
if 'rule' not in comm_list_config:
continue
for rule, rule_config in comm_list_config['rule'].items():
tmp = f'bgp large-community-list expanded {comm_list} seq {rule}'
if rule_config['action'] == 'permit':
tmp += ' permit'
else:
tmp += ' deny'
tmp += ' ' + rule_config['regex']
self.assertIn(tmp, config)
def test_prefix_list(self):
test_data = {
'foo' : {
'rule' : {
'5' : {
'action' : 'permit',
'prefix' : '10.0.0.0/8',
'ge' : '16',
'le' : '24',
},
'10' : {
'action' : 'deny',
'prefix' : '172.16.0.0/12',
'ge' : '16',
},
'15' : {
'action' : 'permit',
'prefix' : '192.168.0.0/16',
},
},
},
'bar' : {
'rule' : {
'5' : {
'action' : 'permit',
'prefix' : '10.0.10.0/24',
'ge' : '25',
'le' : '26',
},
'10' : {
'action' : 'deny',
'prefix' : '10.0.20.0/24',
'le' : '25',
},
'15' : {
'action' : 'permit',
'prefix' : '10.0.25.0/24',
},
},
},
}
for prefix_list, prefix_list_config in test_data.items():
path = base_path + ['prefix-list', prefix_list]
self.cli_set(path + ['description', f'VyOS-PFX-LIST-{prefix_list}'])
if 'rule' not in prefix_list_config:
continue
for rule, rule_config in prefix_list_config['rule'].items():
if 'action' in rule_config:
self.cli_set(path + ['rule', rule, 'action', rule_config['action']])
if 'prefix' in rule_config:
self.cli_set(path + ['rule', rule, 'prefix', rule_config['prefix']])
if 'ge' in rule_config:
self.cli_set(path + ['rule', rule, 'ge', rule_config['ge']])
if 'le' in rule_config:
self.cli_set(path + ['rule', rule, 'le', rule_config['le']])
self.cli_commit()
config = self.getFRRconfig('ip prefix-list', end='')
for prefix_list, prefix_list_config in test_data.items():
if 'rule' not in prefix_list_config:
continue
for rule, rule_config in prefix_list_config['rule'].items():
tmp = f'ip prefix-list {prefix_list} seq {rule}'
if rule_config['action'] == 'permit':
tmp += ' permit'
else:
tmp += ' deny'
tmp += ' ' + rule_config['prefix']
if 'ge' in rule_config:
tmp += ' ge ' + rule_config['ge']
if 'le' in rule_config:
tmp += ' le ' + rule_config['le']
self.assertIn(tmp, config)
def test_prefix_list6(self):
test_data = {
'foo' : {
'rule' : {
'5' : {
'action' : 'permit',
'prefix' : '2001:db8::/32',
'ge' : '40',
'le' : '48',
},
'10' : {
'action' : 'deny',
'prefix' : '2001:db8::/32',
'ge' : '48',
},
'15' : {
'action' : 'permit',
'prefix' : '2001:db8:1000::/64',
},
},
},
'bar' : {
'rule' : {
'5' : {
'action' : 'permit',
'prefix' : '2001:db8:100::/40',
'ge' : '48',
},
'10' : {
'action' : 'permit',
'prefix' : '2001:db8:200::/40',
'ge' : '48',
},
'15' : {
'action' : 'deny',
'prefix' : '2001:db8:300::/40',
'le' : '64',
},
},
},
}
for prefix_list, prefix_list_config in test_data.items():
path = base_path + ['prefix-list6', prefix_list]
self.cli_set(path + ['description', f'VyOS-PFX-LIST-{prefix_list}'])
if 'rule' not in prefix_list_config:
continue
for rule, rule_config in prefix_list_config['rule'].items():
if 'action' in rule_config:
self.cli_set(path + ['rule', rule, 'action', rule_config['action']])
if 'prefix' in rule_config:
self.cli_set(path + ['rule', rule, 'prefix', rule_config['prefix']])
if 'ge' in rule_config:
self.cli_set(path + ['rule', rule, 'ge', rule_config['ge']])
if 'le' in rule_config:
self.cli_set(path + ['rule', rule, 'le', rule_config['le']])
self.cli_commit()
config = self.getFRRconfig('ipv6 prefix-list', end='')
for prefix_list, prefix_list_config in test_data.items():
if 'rule' not in prefix_list_config:
continue
for rule, rule_config in prefix_list_config['rule'].items():
tmp = f'ipv6 prefix-list {prefix_list} seq {rule}'
if rule_config['action'] == 'permit':
tmp += ' permit'
else:
tmp += ' deny'
tmp += ' ' + rule_config['prefix']
if 'ge' in rule_config:
tmp += ' ge ' + rule_config['ge']
if 'le' in rule_config:
tmp += ' le ' + rule_config['le']
self.assertIn(tmp, config)
def test_prefix_list_duplicates(self):
# FRR does not allow to specify the same profix list rule multiple times
#
# vyos(config)# ip prefix-list foo seq 10 permit 192.0.2.0/24
# vyos(config)# ip prefix-list foo seq 20 permit 192.0.2.0/24
# % Configuration failed.
# Error type: validation
# Error description: duplicated prefix list value: 192.0.2.0/24
# There is also a VyOS verify() function to test this
prefix = '100.64.0.0/10'
prefix_list = 'duplicates'
test_range = range(20, 25)
path = base_path + ['prefix-list', prefix_list]
for rule in test_range:
self.cli_set(path + ['rule', str(rule), 'action', 'permit'])
self.cli_set(path + ['rule', str(rule), 'prefix', prefix])
# Duplicate prefixes
with self.assertRaises(ConfigSessionError):
self.cli_commit()
for rule in test_range:
self.cli_set(path + ['rule', str(rule), 'le', str(rule)])
self.cli_commit()
config = self.getFRRconfig('ip prefix-list', end='')
for rule in test_range:
tmp = f'ip prefix-list {prefix_list} seq {rule} permit {prefix} le {rule}'
self.assertIn(tmp, config)
def test_route_map_community_set(self):
test_data = {
"community-configuration": {
"rule": {
"10": {
"action": "permit",
"set": {
"community": {
"replace": [
"65000:10",
"65001:11"
]
},
"extcommunity": {
"bandwidth": "200",
"rt": [
"65000:10",
"192.168.0.1:11"
],
"soo": [
"192.168.0.1:11",
"65000:10"
]
},
"large-community": {
"replace": [
"65000:65000:10",
"65000:65000:11"
]
}
}
},
"20": {
"action": "permit",
"set": {
"community": {
"add": [
"65000:10",
"65001:11"
]
},
"extcommunity": {
"bandwidth": "200",
"bandwidth-non-transitive": {}
},
"large-community": {
"add": [
"65000:65000:10",
"65000:65000:11"
]
}
}
},
"30": {
"action": "permit",
"set": {
"community": {
"none": {}
},
"extcommunity": {
"none": {}
},
"large-community": {
"none": {}
}
}
}
}
}
}
for route_map, route_map_config in test_data.items():
path = base_path + ['route-map', route_map]
self.cli_set(path + ['description', f'VyOS ROUTE-MAP {route_map}'])
if 'rule' not in route_map_config:
continue
for rule, rule_config in route_map_config['rule'].items():
if 'action' in rule_config:
self.cli_set(path + ['rule', rule, 'action', rule_config['action']])
if 'set' in rule_config:
#Add community in configuration
if 'community' in rule_config['set']:
if 'none' in rule_config['set']['community']:
self.cli_set(path + ['rule', rule, 'set', 'community', 'none'])
else:
community_path = path + ['rule', rule, 'set', 'community']
if 'add' in rule_config['set']['community']:
for community_unit in rule_config['set']['community']['add']:
self.cli_set(community_path + ['add', community_unit])
if 'replace' in rule_config['set']['community']:
for community_unit in rule_config['set']['community']['replace']:
self.cli_set(community_path + ['replace', community_unit])
#Add large-community in configuration
if 'large-community' in rule_config['set']:
if 'none' in rule_config['set']['large-community']:
self.cli_set(path + ['rule', rule, 'set', 'large-community', 'none'])
else:
community_path = path + ['rule', rule, 'set', 'large-community']
if 'add' in rule_config['set']['large-community']:
for community_unit in rule_config['set']['large-community']['add']:
self.cli_set(community_path + ['add', community_unit])
if 'replace' in rule_config['set']['large-community']:
for community_unit in rule_config['set']['large-community']['replace']:
self.cli_set(community_path + ['replace', community_unit])
#Add extcommunity in configuration
if 'extcommunity' in rule_config['set']:
if 'none' in rule_config['set']['extcommunity']:
self.cli_set(path + ['rule', rule, 'set', 'extcommunity', 'none'])
else:
if 'bandwidth' in rule_config['set']['extcommunity']:
self.cli_set(path + ['rule', rule, 'set', 'extcommunity', 'bandwidth', rule_config['set']['extcommunity']['bandwidth']])
if 'bandwidth-non-transitive' in rule_config['set']['extcommunity']:
self.cli_set(path + ['rule', rule, 'set','extcommunity', 'bandwidth-non-transitive'])
if 'rt' in rule_config['set']['extcommunity']:
for community_unit in rule_config['set']['extcommunity']['rt']:
self.cli_set(path + ['rule', rule, 'set', 'extcommunity','rt',community_unit])
if 'soo' in rule_config['set']['extcommunity']:
for community_unit in rule_config['set']['extcommunity']['soo']:
self.cli_set(path + ['rule', rule, 'set', 'extcommunity','soo',community_unit])
self.cli_commit()
for route_map, route_map_config in test_data.items():
if 'rule' not in route_map_config:
continue
for rule, rule_config in route_map_config['rule'].items():
name = f'route-map {route_map} {rule_config["action"]} {rule}'
config = self.getFRRconfig(name)
self.assertIn(name, config)
if 'set' in rule_config:
#Check community
if 'community' in rule_config['set']:
if 'none' in rule_config['set']['community']:
tmp = f'set community none'
self.assertIn(tmp, config)
if 'replace' in rule_config['set']['community']:
values = ' '.join(rule_config['set']['community']['replace'])
tmp = f'set community {values}'
self.assertIn(tmp, config)
if 'add' in rule_config['set']['community']:
values = ' '.join(rule_config['set']['community']['add'])
tmp = f'set community {values} additive'
self.assertIn(tmp, config)
#Check large-community
if 'large-community' in rule_config['set']:
if 'none' in rule_config['set']['large-community']:
tmp = f'set large-community none'
self.assertIn(tmp, config)
if 'replace' in rule_config['set']['large-community']:
values = ' '.join(rule_config['set']['large-community']['replace'])
tmp = f'set large-community {values}'
self.assertIn(tmp, config)
if 'add' in rule_config['set']['large-community']:
values = ' '.join(rule_config['set']['large-community']['add'])
tmp = f'set large-community {values} additive'
self.assertIn(tmp, config)
#Check extcommunity
if 'extcommunity' in rule_config['set']:
if 'none' in rule_config['set']['extcommunity']:
tmp = 'set extcommunity none'
self.assertIn(tmp, config)
if 'bandwidth' in rule_config['set']['extcommunity']:
values = rule_config['set']['extcommunity']['bandwidth']
tmp = f'set extcommunity bandwidth {values}'
if 'bandwidth-non-transitive' in rule_config['set']['extcommunity']:
tmp = tmp + ' non-transitive'
self.assertIn(tmp, config)
if 'rt' in rule_config['set']['extcommunity']:
values = ' '.join(rule_config['set']['extcommunity']['rt'])
tmp = f'set extcommunity rt {values}'
self.assertIn(tmp, config)
if 'soo' in rule_config['set']['extcommunity']:
values = ' '.join(rule_config['set']['extcommunity']['soo'])
tmp = f'set extcommunity soo {values}'
self.assertIn(tmp, config)
def test_route_map(self):
access_list = '50'
as_path_list = '100'
test_interface = 'eth0'
community_list = 'BGP-comm-0815'
# ext community name only allows alphanumeric characters and no hyphen :/
# maybe change this if possible in vyos-1x rewrite
extcommunity_list = 'BGPextcomm123'
large_community_list = 'bgp-large-community-123456'
prefix_list = 'foo-pfx-list'
ipv6_nexthop_address = 'fe80::1'
local_pref = '300'
metric = '50'
peer = '2.3.4.5'
peerv6 = '2001:db8::1'
tag = '6542'
goto = '25'
ipv4_nexthop_address= '192.0.2.2'
ipv4_prefix_len= '18'
ipv6_prefix_len= '122'
ipv4_nexthop_type= 'blackhole'
ipv6_nexthop_type= 'blackhole'
test_data = {
'foo-map-bar' : {
'rule' : {
'5' : {
'action' : 'permit',
'continue' : '20',
},
'10' : {
'action' : 'permit',
'call' : 'complicated-configuration',
},
},
},
'a-matching-rule-0815': {
'rule' : {
'5' : {
'action' : 'deny',
'match' : {
'as-path' : as_path_list,
'rpki-invalid': '',
'tag': tag,
},
},
'7' : {
'action' : 'deny',
'match' : {
'rpki-comm-invalid': '',
},
},
'10' : {
'action' : 'permit',
'match' : {
'community' : community_list,
'interface' : test_interface,
'rpki-not-found': '',
},
},
'12' : {
'action' : 'permit',
'match' : {
'rpki-comm-not-found': '',
},
},
'15' : {
'action' : 'permit',
'match' : {
'extcommunity' : extcommunity_list,
'rpki-valid': '',
},
'on-match' : {
'next' : '',
},
},
'17' : {
'action' : 'permit',
'match' : {
'rpki-comm-valid': '',
},
},
'20' : {
'action' : 'permit',
'match' : {
'ip-address-acl': access_list,
'ip-nexthop-acl': access_list,
'ip-route-source-acl': access_list,
'ipv6-address-acl': access_list,
'origin-incomplete' : '',
},
'on-match' : {
'goto' : goto,
},
},
'25' : {
'action' : 'permit',
'match' : {
'ip-address-pfx': prefix_list,
'ip-nexthop-pfx': prefix_list,
'ip-route-source-pfx': prefix_list,
'ipv6-address-pfx': prefix_list,
'origin-igp': '',
},
},
'30' : {
'action' : 'permit',
'match' : {
'ipv6-nexthop-address' : ipv6_nexthop_address,
'ipv6-nexthop-access-list' : access_list,
'ipv6-nexthop-prefix-list' : prefix_list,
'ipv6-nexthop-type' : ipv6_nexthop_type,
'ipv6-address-pfx-len' : ipv6_prefix_len,
'large-community' : large_community_list,
'local-pref' : local_pref,
'metric': metric,
'origin-egp': '',
'peer' : peer,
},
},
'31' : {
'action' : 'permit',
'match' : {
'peer' : peerv6,
},
},
'40' : {
'action' : 'permit',
'match' : {
'ip-nexthop-addr' : ipv4_nexthop_address,
'ip-address-pfx-len' : ipv4_prefix_len,
},
},
'42' : {
'action' : 'deny',
'match' : {
'ip-nexthop-plen' : ipv4_prefix_len,
},
},
'44' : {
'action' : 'permit',
'match' : {
'ip-nexthop-type' : ipv4_nexthop_type,
},
},
},
},
'complicated-configuration' : {
'rule' : {
'10' : {
'action' : 'deny',
'set' : {
'aggregator-as' : '1234567890',
'aggregator-ip' : '10.255.255.0',
'as-path-exclude' : '1234',
'as-path-prepend' : '1234567890 987654321',
'as-path-prepend-last-as' : '5',
'atomic-aggregate' : '',
'distance' : '110',
'ipv6-next-hop-global' : '2001::1',
'ipv6-next-hop-local' : 'fe80::1',
'ip-next-hop' : '192.168.1.1',
'local-preference' : '500',
'metric' : '150',
'metric-type' : 'type-1',
'origin' : 'incomplete',
'l3vpn' : '',
'originator-id' : '172.16.10.1',
'src' : '100.0.0.1',
'tag' : '65530',
'weight' : '2',
},
},
},
},
'bandwidth-configuration' : {
'rule' : {
'10' : {
'action' : 'deny',
'set' : {
'as-path-prepend' : '100 100',
'distance' : '200',
'extcommunity-bw' : 'num-multipaths',
},
},
},
},
'evpn-configuration' : {
'rule' : {
'10' : {
'action' : 'permit',
'match' : {
'evpn-default-route' : '',
'evpn-rd' : '100:300',
'evpn-route-type' : 'prefix',
'evpn-vni' : '1234',
},
},
'20' : {
'action' : 'permit',
'set' : {
'as-path-exclude' : 'all',
'evpn-gateway-ipv4' : '192.0.2.99',
'evpn-gateway-ipv6' : '2001:db8:f00::1',
},
},
},
},
'match-protocol' : {
'rule' : {
'10' : {
'action' : 'permit',
'match' : {
'protocol' : 'static',
},
},
'20' : {
'action' : 'permit',
'match' : {
'protocol' : 'bgp',
},
},
},
},
'relative-metric' : {
'rule' : {
'10' : {
'action' : 'permit',
'match' : {
'ip-nexthop-addr' : ipv4_nexthop_address,
},
'set' : {
'metric' : '+10',
},
},
'20' : {
'action' : 'permit',
'match' : {
'ip-nexthop-addr' : ipv4_nexthop_address,
},
'set' : {
'metric' : '-20',
},
},
'30': {
'action': 'permit',
'match': {
'ip-nexthop-addr': ipv4_nexthop_address,
},
'set': {
'metric': 'rtt',
},
},
'40': {
'action': 'permit',
'match': {
'ip-nexthop-addr': ipv4_nexthop_address,
},
'set': {
'metric': '+rtt',
},
},
'50': {
'action': 'permit',
'match': {
'ip-nexthop-addr': ipv4_nexthop_address,
},
'set': {
'metric': '-rtt',
},
},
},
},
'vrf-match': {
'rule': {
'10': {
'action': 'permit',
'match': {
'source-vrf': 'TEST',
},
},
},
},
}
self.cli_set(['policy', 'access-list', access_list, 'rule', '10', 'action', 'permit'])
self.cli_set(['policy', 'access-list', access_list, 'rule', '10', 'source', 'host', '1.1.1.1'])
self.cli_set(['policy', 'access-list6', access_list, 'rule', '10', 'action', 'permit'])
self.cli_set(['policy', 'access-list6', access_list, 'rule', '10', 'source', 'network', '2001:db8::/32'])
self.cli_set(['policy', 'as-path-list', as_path_list, 'rule', '10', 'action', 'permit'])
self.cli_set(['policy', 'as-path-list', as_path_list, 'rule', '10', 'regex', '64501 64502'])
self.cli_set(['policy', 'community-list', community_list, 'rule', '10', 'action', 'deny'])
self.cli_set(['policy', 'community-list', community_list, 'rule', '10', 'regex', '65432'])
self.cli_set(['policy', 'extcommunity-list', extcommunity_list, 'rule', '10', 'action', 'deny'])
self.cli_set(['policy', 'extcommunity-list', extcommunity_list, 'rule', '10', 'regex', '65000'])
self.cli_set(['policy', 'large-community-list', large_community_list, 'rule', '10', 'action', 'permit'])
self.cli_set(['policy', 'large-community-list', large_community_list, 'rule', '10', 'regex', '100:200:300'])
self.cli_set(['policy', 'prefix-list', prefix_list, 'rule', '10', 'action', 'permit'])
self.cli_set(['policy', 'prefix-list', prefix_list, 'rule', '10', 'prefix', '192.0.2.0/24'])
self.cli_set(['policy', 'prefix-list6', prefix_list, 'rule', '10', 'action', 'permit'])
self.cli_set(['policy', 'prefix-list6', prefix_list, 'rule', '10', 'prefix', '2001:db8::/32'])
for route_map, route_map_config in test_data.items():
path = base_path + ['route-map', route_map]
self.cli_set(path + ['description', f'VyOS ROUTE-MAP {route_map}'])
if 'rule' not in route_map_config:
continue
for rule, rule_config in route_map_config['rule'].items():
if 'action' in rule_config:
self.cli_set(path + ['rule', rule, 'action', rule_config['action']])
if 'call' in rule_config:
self.cli_set(path + ['rule', rule, 'call', rule_config['call']])
if 'continue' in rule_config:
self.cli_set(path + ['rule', rule, 'continue', rule_config['continue']])
if 'match' in rule_config:
if 'as-path' in rule_config['match']:
self.cli_set(path + ['rule', rule, 'match', 'as-path', rule_config['match']['as-path']])
if 'community' in rule_config['match']:
self.cli_set(path + ['rule', rule, 'match', 'community', 'community-list', rule_config['match']['community']])
self.cli_set(path + ['rule', rule, 'match', 'community', 'exact-match'])
if 'evpn-default-route' in rule_config['match']:
self.cli_set(path + ['rule', rule, 'match', 'evpn', 'default-route'])
if 'evpn-rd' in rule_config['match']:
self.cli_set(path + ['rule', rule, 'match', 'evpn', 'rd', rule_config['match']['evpn-rd']])
if 'evpn-route-type' in rule_config['match']:
self.cli_set(path + ['rule', rule, 'match', 'evpn', 'route-type', rule_config['match']['evpn-route-type']])
if 'evpn-vni' in rule_config['match']:
self.cli_set(path + ['rule', rule, 'match', 'evpn', 'vni', rule_config['match']['evpn-vni']])
if 'extcommunity' in rule_config['match']:
self.cli_set(path + ['rule', rule, 'match', 'extcommunity', rule_config['match']['extcommunity']])
if 'interface' in rule_config['match']:
self.cli_set(path + ['rule', rule, 'match', 'interface', rule_config['match']['interface']])
if 'ip-address-acl' in rule_config['match']:
self.cli_set(path + ['rule', rule, 'match', 'ip', 'address', 'access-list', rule_config['match']['ip-address-acl']])
if 'ip-address-pfx' in rule_config['match']:
self.cli_set(path + ['rule', rule, 'match', 'ip', 'address', 'prefix-list', rule_config['match']['ip-address-pfx']])
if 'ip-address-pfx-len' in rule_config['match']:
self.cli_set(path + ['rule', rule, 'match', 'ip', 'address', 'prefix-len', rule_config['match']['ip-address-pfx-len']])
if 'ip-nexthop-acl' in rule_config['match']:
self.cli_set(path + ['rule', rule, 'match', 'ip', 'nexthop', 'access-list', rule_config['match']['ip-nexthop-acl']])
if 'ip-nexthop-pfx' in rule_config['match']:
self.cli_set(path + ['rule', rule, 'match', 'ip', 'nexthop', 'prefix-list', rule_config['match']['ip-nexthop-pfx']])
if 'ip-nexthop-addr' in rule_config['match']:
self.cli_set(path + ['rule', rule, 'match', 'ip', 'nexthop', 'address', rule_config['match']['ip-nexthop-addr']])
if 'ip-nexthop-plen' in rule_config['match']:
self.cli_set(path + ['rule', rule, 'match', 'ip', 'nexthop', 'prefix-len', rule_config['match']['ip-nexthop-plen']])
if 'ip-nexthop-type' in rule_config['match']:
self.cli_set(path + ['rule', rule, 'match', 'ip', 'nexthop', 'type', rule_config['match']['ip-nexthop-type']])
if 'ip-route-source-acl' in rule_config['match']:
self.cli_set(path + ['rule', rule, 'match', 'ip', 'route-source', 'access-list', rule_config['match']['ip-route-source-acl']])
if 'ip-route-source-pfx' in rule_config['match']:
self.cli_set(path + ['rule', rule, 'match', 'ip', 'route-source', 'prefix-list', rule_config['match']['ip-route-source-pfx']])
if 'ipv6-address-acl' in rule_config['match']:
self.cli_set(path + ['rule', rule, 'match', 'ipv6', 'address', 'access-list', rule_config['match']['ipv6-address-acl']])
if 'ipv6-address-pfx' in rule_config['match']:
self.cli_set(path + ['rule', rule, 'match', 'ipv6', 'address', 'prefix-list', rule_config['match']['ipv6-address-pfx']])
if 'ipv6-address-pfx-len' in rule_config['match']:
self.cli_set(path + ['rule', rule, 'match', 'ipv6', 'address', 'prefix-len', rule_config['match']['ipv6-address-pfx-len']])
if 'ipv6-nexthop-address' in rule_config['match']:
self.cli_set(path + ['rule', rule, 'match', 'ipv6', 'nexthop', 'address', rule_config['match']['ipv6-nexthop-address']])
if 'ipv6-nexthop-access-list' in rule_config['match']:
self.cli_set(path + ['rule', rule, 'match', 'ipv6', 'nexthop', 'access-list', rule_config['match']['ipv6-nexthop-access-list']])
if 'ipv6-nexthop-prefix-list' in rule_config['match']:
self.cli_set(path + ['rule', rule, 'match', 'ipv6', 'nexthop', 'prefix-list', rule_config['match']['ipv6-nexthop-prefix-list']])
if 'ipv6-nexthop-type' in rule_config['match']:
self.cli_set(path + ['rule', rule, 'match', 'ipv6', 'nexthop', 'type', rule_config['match']['ipv6-nexthop-type']])
if 'large-community' in rule_config['match']:
self.cli_set(path + ['rule', rule, 'match', 'large-community', 'large-community-list', rule_config['match']['large-community']])
if 'local-pref' in rule_config['match']:
self.cli_set(path + ['rule', rule, 'match', 'local-preference', rule_config['match']['local-pref']])
if 'metric' in rule_config['match']:
self.cli_set(path + ['rule', rule, 'match', 'metric', rule_config['match']['metric']])
if 'origin-igp' in rule_config['match']:
self.cli_set(path + ['rule', rule, 'match', 'origin', 'igp'])
if 'origin-egp' in rule_config['match']:
self.cli_set(path + ['rule', rule, 'match', 'origin', 'egp'])
if 'origin-incomplete' in rule_config['match']:
self.cli_set(path + ['rule', rule, 'match', 'origin', 'incomplete'])
if 'peer' in rule_config['match']:
self.cli_set(path + ['rule', rule, 'match', 'peer', rule_config['match']['peer']])
if 'rpki-invalid' in rule_config['match']:
self.cli_set(path + ['rule', rule, 'match', 'rpki', 'invalid'])
if 'rpki-not-found' in rule_config['match']:
self.cli_set(path + ['rule', rule, 'match', 'rpki', 'notfound'])
if 'rpki-valid' in rule_config['match']:
self.cli_set(path + ['rule', rule, 'match', 'rpki', 'valid'])
if 'rpki-comm-invalid' in rule_config['match']:
self.cli_set(path + ['rule', rule, 'match', 'rpki-extcommunity', 'invalid'])
if 'rpki-comm-not-found' in rule_config['match']:
self.cli_set(path + ['rule', rule, 'match', 'rpki-extcommunity', 'notfound'])
if 'rpki-comm-valid' in rule_config['match']:
self.cli_set(path + ['rule', rule, 'match', 'rpki-extcommunity', 'valid'])
if 'protocol' in rule_config['match']:
self.cli_set(path + ['rule', rule, 'match', 'protocol', rule_config['match']['protocol']])
if 'source-vrf' in rule_config['match']:
self.cli_set(path + ['rule', rule, 'match', 'source-vrf', rule_config['match']['source-vrf']])
if 'tag' in rule_config['match']:
self.cli_set(path + ['rule', rule, 'match', 'tag', rule_config['match']['tag']])
if 'on-match' in rule_config:
if 'goto' in rule_config['on-match']:
self.cli_set(path + ['rule', rule, 'on-match', 'goto', rule_config['on-match']['goto']])
if 'next' in rule_config['on-match']:
self.cli_set(path + ['rule', rule, 'on-match', 'next'])
if 'set' in rule_config:
if 'aggregator-as' in rule_config['set']:
self.cli_set(path + ['rule', rule, 'set', 'aggregator', 'as', rule_config['set']['aggregator-as']])
if 'aggregator-ip' in rule_config['set']:
self.cli_set(path + ['rule', rule, 'set', 'aggregator', 'ip', rule_config['set']['aggregator-ip']])
if 'as-path-exclude' in rule_config['set']:
self.cli_set(path + ['rule', rule, 'set', 'as-path', 'exclude', rule_config['set']['as-path-exclude']])
if 'as-path-prepend' in rule_config['set']:
self.cli_set(path + ['rule', rule, 'set', 'as-path', 'prepend', rule_config['set']['as-path-prepend']])
if 'atomic-aggregate' in rule_config['set']:
self.cli_set(path + ['rule', rule, 'set', 'atomic-aggregate'])
if 'distance' in rule_config['set']:
self.cli_set(path + ['rule', rule, 'set', 'distance', rule_config['set']['distance']])
if 'ipv6-next-hop-global' in rule_config['set']:
self.cli_set(path + ['rule', rule, 'set', 'ipv6-next-hop', 'global', rule_config['set']['ipv6-next-hop-global']])
if 'ipv6-next-hop-local' in rule_config['set']:
self.cli_set(path + ['rule', rule, 'set', 'ipv6-next-hop', 'local', rule_config['set']['ipv6-next-hop-local']])
if 'ip-next-hop' in rule_config['set']:
self.cli_set(path + ['rule', rule, 'set', 'ip-next-hop', rule_config['set']['ip-next-hop']])
if 'l3vpn' in rule_config['set']:
self.cli_set(path + ['rule', rule, 'set', 'l3vpn-nexthop', 'encapsulation', 'gre'])
if 'local-preference' in rule_config['set']:
self.cli_set(path + ['rule', rule, 'set', 'local-preference', rule_config['set']['local-preference']])
if 'metric' in rule_config['set']:
self.cli_set(path + ['rule', rule, 'set', 'metric', rule_config['set']['metric']])
if 'metric-type' in rule_config['set']:
self.cli_set(path + ['rule', rule, 'set', 'metric-type', rule_config['set']['metric-type']])
if 'origin' in rule_config['set']:
self.cli_set(path + ['rule', rule, 'set', 'origin', rule_config['set']['origin']])
if 'originator-id' in rule_config['set']:
self.cli_set(path + ['rule', rule, 'set', 'originator-id', rule_config['set']['originator-id']])
if 'src' in rule_config['set']:
self.cli_set(path + ['rule', rule, 'set', 'src', rule_config['set']['src']])
if 'tag' in rule_config['set']:
self.cli_set(path + ['rule', rule, 'set', 'tag', rule_config['set']['tag']])
if 'weight' in rule_config['set']:
self.cli_set(path + ['rule', rule, 'set', 'weight', rule_config['set']['weight']])
if 'evpn-gateway-ipv4' in rule_config['set']:
self.cli_set(path + ['rule', rule, 'set', 'evpn', 'gateway', 'ipv4', rule_config['set']['evpn-gateway-ipv4']])
if 'evpn-gateway-ipv6' in rule_config['set']:
self.cli_set(path + ['rule', rule, 'set', 'evpn', 'gateway', 'ipv6', rule_config['set']['evpn-gateway-ipv6']])
self.cli_commit()
for route_map, route_map_config in test_data.items():
if 'rule' not in route_map_config:
continue
for rule, rule_config in route_map_config['rule'].items():
name = f'route-map {route_map} {rule_config["action"]} {rule}'
config = self.getFRRconfig(name)
self.assertIn(name, config)
if 'call' in rule_config:
tmp = 'call ' + rule_config['call']
self.assertIn(tmp, config)
if 'continue' in rule_config:
tmp = 'on-match goto ' + rule_config['continue']
self.assertIn(tmp, config)
if 'match' in rule_config:
if 'as-path' in rule_config['match']:
tmp = 'match as-path ' + rule_config['match']['as-path']
self.assertIn(tmp, config)
if 'community' in rule_config['match']:
tmp = f'match community {rule_config["match"]["community"]} exact-match'
self.assertIn(tmp, config)
if 'evpn-default-route' in rule_config['match']:
tmp = f'match evpn default-route'
self.assertIn(tmp, config)
if 'evpn-rd' in rule_config['match']:
tmp = f'match evpn rd {rule_config["match"]["evpn-rd"]}'
self.assertIn(tmp, config)
if 'evpn-route-type' in rule_config['match']:
tmp = f'match evpn route-type {rule_config["match"]["evpn-route-type"]}'
self.assertIn(tmp, config)
if 'evpn-vni' in rule_config['match']:
tmp = f'match evpn vni {rule_config["match"]["evpn-vni"]}'
self.assertIn(tmp, config)
if 'extcommunity' in rule_config['match']:
tmp = f'match extcommunity {rule_config["match"]["extcommunity"]}'
self.assertIn(tmp, config)
if 'interface' in rule_config['match']:
tmp = f'match interface {rule_config["match"]["interface"]}'
self.assertIn(tmp, config)
if 'ip-address-acl' in rule_config['match']:
tmp = f'match ip address {rule_config["match"]["ip-address-acl"]}'
self.assertIn(tmp, config)
if 'ip-address-pfx' in rule_config['match']:
tmp = f'match ip address prefix-list {rule_config["match"]["ip-address-pfx"]}'
self.assertIn(tmp, config)
if 'ip-address-pfx-len' in rule_config['match']:
tmp = f'match ip address prefix-len {rule_config["match"]["ip-address-pfx-len"]}'
self.assertIn(tmp, config)
if 'ip-nexthop-acl' in rule_config['match']:
tmp = f'match ip next-hop {rule_config["match"]["ip-nexthop-acl"]}'
self.assertIn(tmp, config)
if 'ip-nexthop-pfx' in rule_config['match']:
tmp = f'match ip next-hop prefix-list {rule_config["match"]["ip-nexthop-pfx"]}'
self.assertIn(tmp, config)
if 'ip-nexthop-addr' in rule_config['match']:
tmp = f'match ip next-hop address {rule_config["match"]["ip-nexthop-addr"]}'
self.assertIn(tmp, config)
if 'ip-nexthop-plen' in rule_config['match']:
tmp = f'match ip next-hop prefix-len {rule_config["match"]["ip-nexthop-plen"]}'
self.assertIn(tmp, config)
if 'ip-nexthop-type' in rule_config['match']:
tmp = f'match ip next-hop type {rule_config["match"]["ip-nexthop-type"]}'
self.assertIn(tmp, config)
if 'ip-route-source-acl' in rule_config['match']:
tmp = f'match ip route-source {rule_config["match"]["ip-route-source-acl"]}'
self.assertIn(tmp, config)
if 'ip-route-source-pfx' in rule_config['match']:
tmp = f'match ip route-source prefix-list {rule_config["match"]["ip-route-source-pfx"]}'
self.assertIn(tmp, config)
if 'ipv6-address-acl' in rule_config['match']:
tmp = f'match ipv6 address {rule_config["match"]["ipv6-address-acl"]}'
self.assertIn(tmp, config)
if 'ipv6-address-pfx' in rule_config['match']:
tmp = f'match ipv6 address prefix-list {rule_config["match"]["ipv6-address-pfx"]}'
self.assertIn(tmp, config)
if 'ipv6-address-pfx-len' in rule_config['match']:
tmp = f'match ipv6 address prefix-len {rule_config["match"]["ipv6-address-pfx-len"]}'
self.assertIn(tmp, config)
if 'ipv6-nexthop-address' in rule_config['match']:
tmp = f'match ipv6 next-hop address {rule_config["match"]["ipv6-nexthop-address"]}'
self.assertIn(tmp, config)
if 'ipv6-nexthop-access-list' in rule_config['match']:
tmp = f'match ipv6 next-hop {rule_config["match"]["ipv6-nexthop-access-list"]}'
self.assertIn(tmp, config)
if 'ipv6-nexthop-prefix-list' in rule_config['match']:
tmp = f'match ipv6 next-hop prefix-list {rule_config["match"]["ipv6-nexthop-prefix-list"]}'
self.assertIn(tmp, config)
if 'ipv6-nexthop-type' in rule_config['match']:
tmp = f'match ipv6 next-hop type {rule_config["match"]["ipv6-nexthop-type"]}'
self.assertIn(tmp, config)
if 'large-community' in rule_config['match']:
tmp = f'match large-community {rule_config["match"]["large-community"]}'
self.assertIn(tmp, config)
if 'local-pref' in rule_config['match']:
tmp = f'match local-preference {rule_config["match"]["local-pref"]}'
self.assertIn(tmp, config)
if 'metric' in rule_config['match']:
tmp = f'match metric {rule_config["match"]["metric"]}'
self.assertIn(tmp, config)
if 'origin-igp' in rule_config['match']:
tmp = f'match origin igp'
self.assertIn(tmp, config)
if 'origin-egp' in rule_config['match']:
tmp = f'match origin egp'
self.assertIn(tmp, config)
if 'origin-incomplete' in rule_config['match']:
tmp = f'match origin incomplete'
self.assertIn(tmp, config)
if 'peer' in rule_config['match']:
tmp = f'match peer {rule_config["match"]["peer"]}'
self.assertIn(tmp, config)
if 'protocol' in rule_config['match']:
tmp = f'match source-protocol {rule_config["match"]["protocol"]}'
self.assertIn(tmp, config)
if 'rpki-invalid' in rule_config['match']:
tmp = f'match rpki invalid'
self.assertIn(tmp, config)
if 'rpki-not-found' in rule_config['match']:
tmp = f'match rpki notfound'
self.assertIn(tmp, config)
if 'rpki-valid' in rule_config['match']:
tmp = f'match rpki valid'
self.assertIn(tmp, config)
if 'rpki-comm-invalid' in rule_config['match']:
tmp = f'match rpki-extcommunity invalid'
self.assertIn(tmp, config)
if 'rpki-comm-not-found' in rule_config['match']:
tmp = f'match rpki-extcommunity notfound'
self.assertIn(tmp, config)
if 'rpki-comm-valid' in rule_config['match']:
tmp = f'match rpki-extcommunity valid'
self.assertIn(tmp, config)
if 'source-vrf' in rule_config['match']:
tmp = f'match source-vrf {rule_config["match"]["source-vrf"]}'
self.assertIn(tmp, config)
if 'tag' in rule_config['match']:
tmp = f'match tag {rule_config["match"]["tag"]}'
self.assertIn(tmp, config)
if 'on-match' in rule_config:
if 'goto' in rule_config['on-match']:
tmp = f'on-match goto {rule_config["on-match"]["goto"]}'
self.assertIn(tmp, config)
if 'next' in rule_config['on-match']:
tmp = f'on-match next'
self.assertIn(tmp, config)
if 'set' in rule_config:
tmp = ' set '
if 'aggregator-as' in rule_config['set']:
tmp += 'aggregator as ' + rule_config['set']['aggregator-as']
elif 'aggregator-ip' in rule_config['set']:
tmp += ' ' + rule_config['set']['aggregator-ip']
elif 'as-path-exclude' in rule_config['set']:
tmp += 'as-path exclude ' + rule_config['set']['as-path-exclude']
elif 'as-path-prepend' in rule_config['set']:
tmp += 'as-path prepend ' + rule_config['set']['as-path-prepend']
elif 'as-path-prepend-last-as' in rule_config['set']:
tmp += 'as-path prepend last-as' + rule_config['set']['as-path-prepend-last-as']
elif 'atomic-aggregate' in rule_config['set']:
tmp += 'atomic-aggregate'
elif 'distance' in rule_config['set']:
tmp += 'distance ' + rule_config['set']['distance']
elif 'ip-next-hop' in rule_config['set']:
tmp += 'ip next-hop ' + rule_config['set']['ip-next-hop']
elif 'ipv6-next-hop-global' in rule_config['set']:
tmp += 'ipv6 next-hop global ' + rule_config['set']['ipv6-next-hop-global']
elif 'ipv6-next-hop-local' in rule_config['set']:
tmp += 'ipv6 next-hop local ' + rule_config['set']['ipv6-next-hop-local']
elif 'l3vpn' in rule_config['set']:
tmp += 'l3vpn next-hop encapsulation gre'
elif 'local-preference' in rule_config['set']:
tmp += 'local-preference ' + rule_config['set']['local-preference']
elif 'metric' in rule_config['set']:
tmp += 'metric ' + rule_config['set']['metric']
elif 'metric-type' in rule_config['set']:
tmp += 'metric-type ' + rule_config['set']['metric-type']
elif 'origin' in rule_config['set']:
tmp += 'origin ' + rule_config['set']['origin']
elif 'originator-id' in rule_config['set']:
tmp += 'originator-id ' + rule_config['set']['originator-id']
elif 'src' in rule_config['set']:
tmp += 'src ' + rule_config['set']['src']
elif 'tag' in rule_config['set']:
tmp += 'tag ' + rule_config['set']['tag']
elif 'weight' in rule_config['set']:
tmp += 'weight ' + rule_config['set']['weight']
elif 'vpn-gateway-ipv4' in rule_config['set']:
tmp += 'evpn gateway ipv4 ' + rule_config['set']['vpn-gateway-ipv4']
elif 'vpn-gateway-ipv6' in rule_config['set']:
tmp += 'evpn gateway ipv6 ' + rule_config['set']['vpn-gateway-ipv6']
self.assertIn(tmp, config)
# Test set table for some sources
def test_table_id(self):
path = base_path + ['local-route']
sources = ['203.0.113.1', '203.0.113.2']
rule = '50'
table = '23'
for src in sources:
self.cli_set(path + ['rule', rule, 'set', 'table', table])
self.cli_set(path + ['rule', rule, 'source', 'address', src])
self.cli_commit()
original = """
50: from 203.0.113.1 lookup 23
50: from 203.0.113.2 lookup 23
"""
tmp = cmd('ip rule show prio 50')
self.assertEqual(sort_ip(tmp), sort_ip(original))
# Test set table for fwmark
def test_fwmark_table_id(self):
path = base_path + ['local-route']
fwmk = '24'
rule = '101'
table = '154'
self.cli_set(path + ['rule', rule, 'set', 'table', table])
self.cli_set(path + ['rule', rule, 'fwmark', fwmk])
self.cli_commit()
original = """
101: from all fwmark 0x18 lookup 154
"""
tmp = cmd('ip rule show prio 101')
self.assertEqual(sort_ip(tmp), sort_ip(original))
# Test set table for destination
def test_destination_table_id(self):
path = base_path + ['local-route']
dst = '203.0.113.1'
rule = '102'
table = '154'
self.cli_set(path + ['rule', rule, 'set', 'table', table])
self.cli_set(path + ['rule', rule, 'destination', 'address', dst])
self.cli_commit()
original = """
102: from all to 203.0.113.1 lookup 154
"""
tmp = cmd('ip rule show prio 102')
self.assertEqual(sort_ip(tmp), sort_ip(original))
# Test set table for destination and protocol
def test_protocol_destination_table_id(self):
path = base_path + ['local-route']
dst = '203.0.113.12'
rule = '85'
table = '104'
proto = 'tcp'
self.cli_set(path + ['rule', rule, 'set', 'table', table])
self.cli_set(path + ['rule', rule, 'destination', 'address', dst])
self.cli_set(path + ['rule', rule, 'protocol', proto])
self.cli_commit()
original = """
85: from all to 203.0.113.12 ipproto tcp lookup 104
"""
tmp = cmd('ip rule show prio 85')
self.assertEqual(sort_ip(tmp), sort_ip(original))
# Test set table for destination, source, protocol, fwmark and port
def test_protocol_port_address_fwmark_table_id(self):
path = base_path + ['local-route']
dst = '203.0.113.5'
src_list = ['203.0.113.1', '203.0.113.2']
rule = '23'
fwmark = '123456'
table = '123'
new_table = '111'
proto = 'udp'
new_proto = 'tcp'
src_port = '5555'
dst_port = '8888'
self.cli_set(path + ['rule', rule, 'set', 'table', table])
self.cli_set(path + ['rule', rule, 'destination', 'address', dst])
self.cli_set(path + ['rule', rule, 'source', 'port', src_port])
self.cli_set(path + ['rule', rule, 'protocol', proto])
self.cli_set(path + ['rule', rule, 'fwmark', fwmark])
self.cli_set(path + ['rule', rule, 'destination', 'port', dst_port])
for src in src_list:
self.cli_set(path + ['rule', rule, 'source', 'address', src])
self.cli_commit()
original = """
23: from 203.0.113.1 to 203.0.113.5 fwmark 0x1e240 ipproto udp sport 5555 dport 8888 lookup 123
23: from 203.0.113.2 to 203.0.113.5 fwmark 0x1e240 ipproto udp sport 5555 dport 8888 lookup 123
"""
tmp = cmd(f'ip rule show prio {rule}')
self.assertEqual(sort_ip(tmp), sort_ip(original))
# Change table and protocol, delete fwmark and source port
self.cli_delete(path + ['rule', rule, 'fwmark'])
self.cli_delete(path + ['rule', rule, 'source', 'port'])
self.cli_set(path + ['rule', rule, 'set', 'table', new_table])
self.cli_set(path + ['rule', rule, 'protocol', new_proto])
self.cli_commit()
original = """
23: from 203.0.113.1 to 203.0.113.5 ipproto tcp dport 8888 lookup 111
23: from 203.0.113.2 to 203.0.113.5 ipproto tcp dport 8888 lookup 111
"""
tmp = cmd(f'ip rule show prio {rule}')
self.assertEqual(sort_ip(tmp), sort_ip(original))
# Test set table for sources with fwmark
def test_fwmark_sources_table_id(self):
path = base_path + ['local-route']
sources = ['203.0.113.11', '203.0.113.12']
fwmk = '23'
rule = '100'
table = '150'
for src in sources:
self.cli_set(path + ['rule', rule, 'set', 'table', table])
self.cli_set(path + ['rule', rule, 'source', 'address', src])
self.cli_set(path + ['rule', rule, 'fwmark', fwmk])
self.cli_commit()
original = """
100: from 203.0.113.11 fwmark 0x17 lookup 150
100: from 203.0.113.12 fwmark 0x17 lookup 150
"""
tmp = cmd('ip rule show prio 100')
self.assertEqual(sort_ip(tmp), sort_ip(original))
# Test set table for sources with iif
def test_iif_sources_table_id(self):
path = base_path + ['local-route']
sources = ['203.0.113.11', '203.0.113.12']
iif = 'lo'
rule = '100'
table = '150'
self.cli_set(path + ['rule', rule, 'set', 'table', table])
self.cli_set(path + ['rule', rule, 'inbound-interface', iif])
for src in sources:
self.cli_set(path + ['rule', rule, 'source', 'address', src])
self.cli_commit()
# Check generated configuration
# Expected values
original = """
100: from 203.0.113.11 iif lo lookup 150
100: from 203.0.113.12 iif lo lookup 150
"""
tmp = cmd('ip rule show prio 100')
self.assertEqual(sort_ip(tmp), sort_ip(original))
# Test set table for sources and destinations with fwmark
def test_fwmark_sources_destination_table_id(self):
path = base_path + ['local-route']
sources = ['203.0.113.11', '203.0.113.12']
destinations = ['203.0.113.13', '203.0.113.15']
fwmk = '23'
rule = '103'
table = '150'
for src in sources:
for dst in destinations:
self.cli_set(path + ['rule', rule, 'set', 'table', table])
self.cli_set(path + ['rule', rule, 'source', 'address', src])
self.cli_set(path + ['rule', rule, 'destination', 'address', dst])
self.cli_set(path + ['rule', rule, 'fwmark', fwmk])
self.cli_commit()
original = """
103: from 203.0.113.11 to 203.0.113.13 fwmark 0x17 lookup 150
103: from 203.0.113.11 to 203.0.113.15 fwmark 0x17 lookup 150
103: from 203.0.113.12 to 203.0.113.13 fwmark 0x17 lookup 150
103: from 203.0.113.12 to 203.0.113.15 fwmark 0x17 lookup 150
"""
tmp = cmd('ip rule show prio 103')
self.assertEqual(sort_ip(tmp), sort_ip(original))
# Test set table ipv6 for some sources ipv6
def test_ipv6_table_id(self):
path = base_path + ['local-route6']
sources = ['2001:db8:123::/48', '2001:db8:126::/48']
rule = '50'
table = '23'
for src in sources:
self.cli_set(path + ['rule', rule, 'set', 'table', table])
self.cli_set(path + ['rule', rule, 'source', 'address', src])
self.cli_commit()
original = """
50: from 2001:db8:123::/48 lookup 23
50: from 2001:db8:126::/48 lookup 23
"""
tmp = cmd('ip -6 rule show prio 50')
self.assertEqual(sort_ip(tmp), sort_ip(original))
# Test set table for fwmark ipv6
def test_fwmark_ipv6_table_id(self):
path = base_path + ['local-route6']
fwmk = '24'
rule = '100'
table = '154'
self.cli_set(path + ['rule', rule, 'set', 'table', table])
self.cli_set(path + ['rule', rule, 'fwmark', fwmk])
self.cli_commit()
original = """
100: from all fwmark 0x18 lookup 154
"""
tmp = cmd('ip -6 rule show prio 100')
self.assertEqual(sort_ip(tmp), sort_ip(original))
# Test set table for destination ipv6
def test_destination_ipv6_table_id(self):
path = base_path + ['local-route6']
dst = '2001:db8:1337::/126'
rule = '101'
table = '154'
self.cli_set(path + ['rule', rule, 'set', 'table', table])
self.cli_set(path + ['rule', rule, 'destination', 'address', dst])
self.cli_commit()
original = """
101: from all to 2001:db8:1337::/126 lookup 154
"""
tmp = cmd('ip -6 rule show prio 101')
self.assertEqual(sort_ip(tmp), sort_ip(original))
# Test set table for sources with fwmark ipv6
def test_fwmark_sources_ipv6_table_id(self):
path = base_path + ['local-route6']
sources = ['2001:db8:1338::/126', '2001:db8:1339::/126']
fwmk = '23'
rule = '102'
table = '150'
for src in sources:
self.cli_set(path + ['rule', rule, 'set', 'table', table])
self.cli_set(path + ['rule', rule, 'source', 'address', src])
self.cli_set(path + ['rule', rule, 'fwmark', fwmk])
self.cli_commit()
original = """
102: from 2001:db8:1338::/126 fwmark 0x17 lookup 150
102: from 2001:db8:1339::/126 fwmark 0x17 lookup 150
"""
tmp = cmd('ip -6 rule show prio 102')
self.assertEqual(sort_ip(tmp), sort_ip(original))
# Test set table for sources with iif ipv6
def test_iif_sources_ipv6_table_id(self):
path = base_path + ['local-route6']
sources = ['2001:db8:1338::/126', '2001:db8:1339::/126']
iif = 'lo'
rule = '102'
table = '150'
for src in sources:
self.cli_set(path + ['rule', rule, 'set', 'table', table])
self.cli_set(path + ['rule', rule, 'source', 'address', src])
self.cli_set(path + ['rule', rule, 'inbound-interface', iif])
self.cli_commit()
# Check generated configuration
# Expected values
original = """
102: from 2001:db8:1338::/126 iif lo lookup 150
102: from 2001:db8:1339::/126 iif lo lookup 150
"""
tmp = cmd('ip -6 rule show prio 102')
self.assertEqual(sort_ip(tmp), sort_ip(original))
# Test set table for sources and destinations with fwmark ipv6
def test_fwmark_sources_destination_ipv6_table_id(self):
path = base_path + ['local-route6']
sources = ['2001:db8:1338::/126', '2001:db8:1339::/56']
destinations = ['2001:db8:13::/48', '2001:db8:16::/48']
fwmk = '23'
rule = '103'
table = '150'
for src in sources:
for dst in destinations:
self.cli_set(path + ['rule', rule, 'set', 'table', table])
self.cli_set(path + ['rule', rule, 'source', 'address', src])
self.cli_set(path + ['rule', rule, 'destination', 'address', dst])
self.cli_set(path + ['rule', rule, 'fwmark', fwmk])
self.cli_commit()
original = """
103: from 2001:db8:1338::/126 to 2001:db8:13::/48 fwmark 0x17 lookup 150
103: from 2001:db8:1338::/126 to 2001:db8:16::/48 fwmark 0x17 lookup 150
103: from 2001:db8:1339::/56 to 2001:db8:13::/48 fwmark 0x17 lookup 150
103: from 2001:db8:1339::/56 to 2001:db8:16::/48 fwmark 0x17 lookup 150
"""
tmp = cmd('ip -6 rule show prio 103')
self.assertEqual(sort_ip(tmp), sort_ip(original))
# Test delete table for sources and destination with fwmark ipv4/ipv6
def test_delete_ipv4_ipv6_table_id(self):
path = base_path + ['local-route']
path_v6 = base_path + ['local-route6']
sources = ['203.0.113.0/24', '203.0.114.5']
destinations = ['203.0.112.0/24', '203.0.116.5']
sources_v6 = ['2001:db8:1338::/126', '2001:db8:1339::/56']
destinations_v6 = ['2001:db8:13::/48', '2001:db8:16::/48']
fwmk = '23'
rule = '103'
table = '150'
for src in sources:
for dst in destinations:
self.cli_set(path + ['rule', rule, 'set', 'table', table])
self.cli_set(path + ['rule', rule, 'source', 'address', src])
self.cli_set(path + ['rule', rule, 'destination', 'address', dst])
self.cli_set(path + ['rule', rule, 'fwmark', fwmk])
for src in sources_v6:
for dst in destinations_v6:
self.cli_set(path_v6 + ['rule', rule, 'set', 'table', table])
self.cli_set(path_v6 + ['rule', rule, 'source', 'address', src])
self.cli_set(path_v6 + ['rule', rule, 'destination', 'address', dst])
self.cli_set(path_v6 + ['rule', rule, 'fwmark', fwmk])
self.cli_commit()
original = """
103: from 203.0.113.0/24 to 203.0.116.5 fwmark 0x17 lookup 150
103: from 203.0.114.5 to 203.0.112.0/24 fwmark 0x17 lookup 150
103: from 203.0.114.5 to 203.0.116.5 fwmark 0x17 lookup 150
103: from 203.0.113.0/24 to 203.0.112.0/24 fwmark 0x17 lookup 150
"""
original_v6 = """
103: from 2001:db8:1338::/126 to 2001:db8:16::/48 fwmark 0x17 lookup 150
103: from 2001:db8:1339::/56 to 2001:db8:13::/48 fwmark 0x17 lookup 150
103: from 2001:db8:1339::/56 to 2001:db8:16::/48 fwmark 0x17 lookup 150
103: from 2001:db8:1338::/126 to 2001:db8:13::/48 fwmark 0x17 lookup 150
"""
tmp = cmd('ip rule show prio 103')
tmp_v6 = cmd('ip -6 rule show prio 103')
self.assertEqual(sort_ip(tmp), sort_ip(original))
self.assertEqual(sort_ip(tmp_v6), sort_ip(original_v6))
self.cli_delete(path)
self.cli_delete(path_v6)
self.cli_commit()
tmp = cmd('ip rule show prio 103')
tmp_v6 = cmd('ip -6 rule show prio 103')
self.assertEqual(sort_ip(tmp), [])
self.assertEqual(sort_ip(tmp_v6), [])
# Test multiple commits ipv4
def test_multiple_commit_ipv4_table_id(self):
path = base_path + ['local-route']
sources = ['192.0.2.1', '192.0.2.2']
destination = '203.0.113.25'
rule = '105'
table = '151'
self.cli_set(path + ['rule', rule, 'set', 'table', table])
for src in sources:
self.cli_set(path + ['rule', rule, 'source', 'address', src])
self.cli_commit()
original_first = """
105: from 192.0.2.1 lookup 151
105: from 192.0.2.2 lookup 151
"""
tmp = cmd('ip rule show prio 105')
self.assertEqual(sort_ip(tmp), sort_ip(original_first))
# Create second commit with added destination
self.cli_set(path + ['rule', rule, 'destination', 'address', destination])
self.cli_commit()
original_second = """
105: from 192.0.2.1 to 203.0.113.25 lookup 151
105: from 192.0.2.2 to 203.0.113.25 lookup 151
"""
tmp = cmd('ip rule show prio 105')
self.assertEqual(sort_ip(tmp), sort_ip(original_second))
def test_frr_individual_remove_T6283_T6250(self):
path = base_path + ['route-map']
route_maps = ['RMAP-1', 'RMAP_2']
seq = '10'
base_local_preference = 300
base_table = 50
# T6250
local_preference = base_local_preference
table = base_table
for route_map in route_maps:
self.cli_set(path + [route_map, 'rule', seq, 'action', 'permit'])
self.cli_set(path + [route_map, 'rule', seq, 'set', 'table', str(table)])
self.cli_set(path + [route_map, 'rule', seq, 'set', 'local-preference', str(local_preference)])
local_preference += 20
table += 5
self.cli_commit()
local_preference = base_local_preference
table = base_table
for route_map in route_maps:
config = self.getFRRconfig(f'route-map {route_map} permit {seq}', end='', endsection='^exit')
self.assertIn(f' set local-preference {local_preference}', config)
self.assertIn(f' set table {table}', config)
local_preference += 20
table += 5
for route_map in route_maps:
self.cli_delete(path + [route_map, 'rule', '10', 'set', 'table'])
# we explicitly commit multiple times to be as vandal as possible to the system
self.cli_commit()
local_preference = base_local_preference
for route_map in route_maps:
config = self.getFRRconfig(f'route-map {route_map} permit {seq}', end='', endsection='^exit')
self.assertIn(f' set local-preference {local_preference}', config)
local_preference += 20
# T6283
seq = '20'
prepend = '100 100 100'
for route_map in route_maps:
self.cli_set(path + [route_map, 'rule', seq, 'action', 'permit'])
self.cli_set(path + [route_map, 'rule', seq, 'set', 'as-path', 'prepend', prepend])
self.cli_commit()
for route_map in route_maps:
config = self.getFRRconfig(f'route-map {route_map} permit {seq}', end='', endsection='^exit')
self.assertIn(f' set as-path prepend {prepend}', config)
for route_map in route_maps:
self.cli_delete(path + [route_map, 'rule', seq, 'set'])
# we explicitly commit multiple times to be as vandal as possible to the system
self.cli_commit()
for route_map in route_maps:
config = self.getFRRconfig(f'route-map {route_map} permit {seq}', end='', endsection='^exit')
self.assertNotIn(f' set', config)
def sort_ip(output):
o = '\n'.join([' '.join(line.strip().split()) for line in output.strip().splitlines()])
o = o.splitlines()
o.sort()
return o
if __name__ == '__main__':
unittest.main(verbosity=2, failfast=VyOSUnitTestSHIM.TestCase.debug_on())
diff --git a/smoketest/scripts/cli/test_policy_local-route.py b/smoketest/scripts/cli/test_policy_local-route.py
index afb3ee43b..51c400619 100644
--- a/smoketest/scripts/cli/test_policy_local-route.py
+++ b/smoketest/scripts/cli/test_policy_local-route.py
@@ -1,174 +1,171 @@
#!/usr/bin/env python3
#
# Copyright VyOS maintainers and contributors <maintainers@vyos.io>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
-from base_vyostest_shim import CSTORE_GUARD_TIME
interface = 'eth0'
mark = '100'
table_id = '101'
extra_table_id = '102'
vrf_name = 'LPBRVRF'
vrf_rt_id = '202'
class TestPolicyLocalRoute(VyOSUnitTestSHIM.TestCase):
@classmethod
def setUpClass(cls):
super(TestPolicyLocalRoute, cls).setUpClass()
# Clear out current configuration to allow running this test on a live system
cls.cli_delete(cls, ['policy', 'local-route'])
cls.cli_delete(cls, ['policy', 'local-route6'])
- # Enable CSTORE guard time required by FRR related tests
- cls._commit_guard_time = CSTORE_GUARD_TIME
cls.cli_set(cls, ['vrf', 'name', vrf_name, 'table', vrf_rt_id])
@classmethod
def tearDownClass(cls):
cls.cli_delete(cls, ['vrf', 'name', vrf_name])
super(TestPolicyLocalRoute, cls).tearDownClass()
def tearDown(self):
self.cli_delete(['policy', 'local-route'])
self.cli_delete(['policy', 'local-route6'])
self.cli_commit()
ip_rule_search = [
[f'lookup {table_id}']
]
self.verify_rules(ip_rule_search, inverse=True)
self.verify_rules(ip_rule_search, inverse=True, addr_family='inet6')
def test_local_pbr_matching_criteria(self):
self.cli_set(['policy', 'local-route', 'rule', '4', 'inbound-interface', interface])
self.cli_set(['policy', 'local-route', 'rule', '4', 'protocol', 'udp'])
self.cli_set(['policy', 'local-route', 'rule', '4', 'fwmark', mark])
self.cli_set(['policy', 'local-route', 'rule', '4', 'destination', 'address', '198.51.100.0/24'])
self.cli_set(['policy', 'local-route', 'rule', '4', 'destination', 'port', '111'])
self.cli_set(['policy', 'local-route', 'rule', '4', 'source', 'address', '198.51.100.1'])
self.cli_set(['policy', 'local-route', 'rule', '4', 'source', 'port', '443'])
self.cli_set(['policy', 'local-route', 'rule', '4', 'set', 'table', table_id])
self.cli_set(['policy', 'local-route6', 'rule', '6', 'inbound-interface', interface])
self.cli_set(['policy', 'local-route6', 'rule', '6', 'protocol', 'tcp'])
self.cli_set(['policy', 'local-route6', 'rule', '6', 'fwmark', mark])
self.cli_set(['policy', 'local-route6', 'rule', '6', 'destination', 'address', '2001:db8::/64'])
self.cli_set(['policy', 'local-route6', 'rule', '6', 'destination', 'port', '123'])
self.cli_set(['policy', 'local-route6', 'rule', '6', 'source', 'address', '2001:db8::1'])
self.cli_set(['policy', 'local-route6', 'rule', '6', 'source', 'port', '80'])
self.cli_set(['policy', 'local-route6', 'rule', '6', 'set', 'table', table_id])
self.cli_commit()
rule_lookup = f'lookup {table_id}'
rule_fwmark = 'fwmark ' + hex(int(mark))
rule_interface = f'iif {interface}'
ip4_rule_search = [
['from 198.51.100.1', 'to 198.51.100.0/24', rule_fwmark, rule_interface, 'ipproto udp', 'sport 443', 'dport 111', rule_lookup]
]
self.verify_rules(ip4_rule_search)
ip6_rule_search = [
['from 2001:db8::1', 'to 2001:db8::/64', rule_fwmark, rule_interface, 'ipproto tcp', 'sport 80', 'dport 123', rule_lookup]
]
self.verify_rules(ip6_rule_search, addr_family='inet6')
def test_local_pbr_rule_removal(self):
self.cli_set(['policy', 'local-route', 'rule', '1', 'destination', 'address', '198.51.100.1'])
self.cli_set(['policy', 'local-route', 'rule', '1', 'set', 'table', table_id])
self.cli_set(['policy', 'local-route', 'rule', '2', 'destination', 'address', '198.51.100.2'])
self.cli_set(['policy', 'local-route', 'rule', '2', 'set', 'table', table_id])
self.cli_set(['policy', 'local-route', 'rule', '3', 'destination', 'address', '198.51.100.3'])
self.cli_set(['policy', 'local-route', 'rule', '3', 'set', 'table', table_id])
self.cli_commit()
rule_lookup = f'lookup {table_id}'
ip_rule_search = [
['to 198.51.100.1', rule_lookup],
['to 198.51.100.2', rule_lookup],
['to 198.51.100.3', rule_lookup],
]
self.verify_rules(ip_rule_search)
self.cli_delete(['policy', 'local-route', 'rule', '2'])
self.cli_commit()
ip_rule_missing = [
['to 198.51.100.2', rule_lookup],
]
self.verify_rules(ip_rule_missing, inverse=True)
def test_local_pbr_rule_changes(self):
self.cli_set(['policy', 'local-route', 'rule', '1', 'destination', 'address', '198.51.100.0/24'])
self.cli_set(['policy', 'local-route', 'rule', '1', 'set', 'table', table_id])
self.cli_commit()
self.cli_set(['policy', 'local-route', 'rule', '1', 'set', 'table', extra_table_id])
self.cli_commit()
ip_rule_search_extra = [
['to 198.51.100.0/24', f'lookup {extra_table_id}']
]
self.verify_rules(ip_rule_search_extra)
ip_rule_search_orig = [
['to 198.51.100.0/24', f'lookup {table_id}']
]
self.verify_rules(ip_rule_search_orig, inverse=True)
self.cli_delete(['policy', 'local-route', 'rule', '1', 'set', 'table'])
self.cli_set(['policy', 'local-route', 'rule', '1', 'set', 'vrf', vrf_name])
self.cli_commit()
ip_rule_search_vrf = [
['to 198.51.100.0/24', f'lookup {vrf_name}']
]
self.verify_rules(ip_rule_search_extra, inverse=True)
self.verify_rules(ip_rule_search_vrf)
def test_local_pbr_target_vrf(self):
self.cli_set(['policy', 'local-route', 'rule', '1', 'destination', 'address', '198.51.100.0/24'])
self.cli_set(['policy', 'local-route', 'rule', '1', 'set', 'vrf', vrf_name])
self.cli_commit()
ip_rule_search = [
['to 198.51.100.0/24', f'lookup {vrf_name}']
]
self.verify_rules(ip_rule_search)
if __name__ == '__main__':
unittest.main(verbosity=2, failfast=VyOSUnitTestSHIM.TestCase.debug_on())
diff --git a/smoketest/scripts/cli/test_policy_route.py b/smoketest/scripts/cli/test_policy_route.py
index 6eb403979..8caa7b342 100755
--- a/smoketest/scripts/cli/test_policy_route.py
+++ b/smoketest/scripts/cli/test_policy_route.py
@@ -1,345 +1,342 @@
#!/usr/bin/env python3
#
# Copyright VyOS maintainers and contributors <maintainers@vyos.io>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
-from base_vyostest_shim import CSTORE_GUARD_TIME
mark = '100'
conn_mark = '555'
conn_mark_set = '111'
table_mark_offset = 0x7fffffff
table_id = '101'
vrf = 'PBRVRF'
vrf_table_id = '102'
interface = 'eth0'
interface_wc = 'ppp*'
interface_ip = '172.16.10.1/24'
class TestPolicyRoute(VyOSUnitTestSHIM.TestCase):
@classmethod
def setUpClass(cls):
super(TestPolicyRoute, cls).setUpClass()
# Clear out current configuration to allow running this test on a live system
cls.cli_delete(cls, ['policy', 'route'])
cls.cli_delete(cls, ['policy', 'route6'])
- # Enable CSTORE guard time required by FRR related tests
- cls._commit_guard_time = CSTORE_GUARD_TIME
cls.cli_set(cls, ['interfaces', 'ethernet', interface, 'address', interface_ip])
cls.cli_set(cls, ['protocols', 'static', 'table', table_id, 'route', '0.0.0.0/0', 'interface', interface])
cls.cli_set(cls, ['vrf', 'name', vrf, 'table', vrf_table_id])
@classmethod
def tearDownClass(cls):
cls.cli_delete(cls, ['interfaces', 'ethernet', interface, 'address', interface_ip])
cls.cli_delete(cls, ['protocols', 'static', 'table', table_id])
cls.cli_delete(cls, ['vrf', 'name', vrf])
super(TestPolicyRoute, cls).tearDownClass()
def tearDown(self):
self.cli_delete(['policy', 'route'])
self.cli_delete(['policy', 'route6'])
self.cli_commit()
# Verify nftables cleanup
nftables_search = [
['set N_smoketest_network'],
['set N_smoketest_network1'],
['chain VYOS_PBR_smoketest']
]
self.verify_nftables(nftables_search, 'ip vyos_mangle', inverse=True)
# Verify ip rule cleanup
ip_rule_search = [
['fwmark ' + hex(table_mark_offset - int(table_id)), 'lookup ' + table_id]
]
self.verify_rules(ip_rule_search, inverse=True)
def test_pbr_group(self):
self.cli_set(['firewall', 'group', 'network-group', 'smoketest_network', 'network', '172.16.99.0/24'])
self.cli_set(['firewall', 'group', 'network-group', 'smoketest_network1', 'network', '172.16.101.0/24'])
self.cli_set(['firewall', 'group', 'network-group', 'smoketest_network1', 'include', 'smoketest_network'])
self.cli_set(['policy', 'route', 'smoketest', 'rule', '1', 'source', 'group', 'network-group', 'smoketest_network'])
self.cli_set(['policy', 'route', 'smoketest', 'rule', '1', 'destination', 'group', 'network-group', 'smoketest_network1'])
self.cli_set(['policy', 'route', 'smoketest', 'rule', '1', 'set', 'mark', mark])
self.cli_set(['policy', 'route', 'smoketest', 'interface', interface])
self.cli_commit()
nftables_search = [
[f'iifname "{interface}"','jump VYOS_PBR_UD_smoketest'],
['ip daddr @N_smoketest_network1', 'ip saddr @N_smoketest_network'],
]
self.verify_nftables(nftables_search, 'ip vyos_mangle')
self.cli_delete(['firewall'])
def test_pbr_mark(self):
self.cli_set(['policy', 'route', 'smoketest', 'rule', '1', 'source', 'address', '172.16.20.10'])
self.cli_set(['policy', 'route', 'smoketest', 'rule', '1', 'destination', 'address', '172.16.10.10'])
self.cli_set(['policy', 'route', 'smoketest', 'rule', '1', 'set', 'mark', mark])
self.cli_set(['policy', 'route', 'smoketest', 'interface', interface])
self.cli_commit()
mark_hex = "{0:#010x}".format(int(mark))
nftables_search = [
[f'iifname "{interface}"','jump VYOS_PBR_UD_smoketest'],
['ip daddr 172.16.10.10', 'ip saddr 172.16.20.10', 'meta mark set ' + mark_hex],
]
self.verify_nftables(nftables_search, 'ip vyos_mangle')
def test_pbr_mark_connection(self):
self.cli_set(['policy', 'route', 'smoketest', 'rule', '1', 'source', 'address', '172.16.20.10'])
self.cli_set(['policy', 'route', 'smoketest', 'rule', '1', 'destination', 'address', '172.16.10.10'])
self.cli_set(['policy', 'route', 'smoketest', 'rule', '1', 'connection-mark', conn_mark])
self.cli_set(['policy', 'route', 'smoketest', 'rule', '1', 'set', 'connection-mark', conn_mark_set])
self.cli_set(['policy', 'route', 'smoketest', 'interface', interface])
self.cli_commit()
mark_hex = "{0:#010x}".format(int(conn_mark))
mark_hex_set = "{0:#010x}".format(int(conn_mark_set))
nftables_search = [
[f'iifname "{interface}"','jump VYOS_PBR_UD_smoketest'],
['ip daddr 172.16.10.10', 'ip saddr 172.16.20.10', 'ct mark ' + mark_hex, 'ct mark set ' + mark_hex_set],
]
self.verify_nftables(nftables_search, 'ip vyos_mangle')
def test_pbr_table(self):
self.cli_set(['policy', 'route', 'smoketest', 'rule', '1', 'protocol', 'tcp'])
self.cli_set(['policy', 'route', 'smoketest', 'rule', '1', 'destination', 'port', '8888'])
self.cli_set(['policy', 'route', 'smoketest', 'rule', '1', 'tcp', 'flags', 'syn'])
self.cli_set(['policy', 'route', 'smoketest', 'rule', '1', 'tcp', 'flags', 'not', 'ack'])
self.cli_set(['policy', 'route', 'smoketest', 'rule', '1', 'set', 'table', table_id])
self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '1', 'protocol', 'tcp_udp'])
self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '1', 'destination', 'port', '8888'])
self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '1', 'set', 'table', table_id])
self.cli_set(['policy', 'route', 'smoketest', 'interface', interface])
self.cli_set(['policy', 'route6', 'smoketest6', 'interface', interface])
self.cli_commit()
mark_hex = "{0:#010x}".format(table_mark_offset - int(table_id))
# IPv4
nftables_search = [
[f'iifname "{interface}"', 'jump VYOS_PBR_UD_smoketest'],
['tcp flags syn / syn,ack', 'tcp dport 8888', 'meta mark set ' + mark_hex]
]
self.verify_nftables(nftables_search, 'ip vyos_mangle')
# IPv6
nftables6_search = [
[f'iifname "{interface}"', 'jump VYOS_PBR6_UD_smoketest'],
['meta l4proto { tcp, udp }', 'th dport 8888', 'meta mark set ' + mark_hex]
]
self.verify_nftables(nftables6_search, 'ip6 vyos_mangle')
# IP rule fwmark -> table
ip_rule_search = [
['fwmark ' + hex(table_mark_offset - int(table_id)), 'lookup ' + table_id]
]
self.verify_rules(ip_rule_search)
def test_pbr_vrf(self):
self.cli_set(['policy', 'route', 'smoketest', 'rule', '1', 'protocol', 'tcp'])
self.cli_set(['policy', 'route', 'smoketest', 'rule', '1', 'destination', 'port', '8888'])
self.cli_set(['policy', 'route', 'smoketest', 'rule', '1', 'tcp', 'flags', 'syn'])
self.cli_set(['policy', 'route', 'smoketest', 'rule', '1', 'tcp', 'flags', 'not', 'ack'])
self.cli_set(['policy', 'route', 'smoketest', 'rule', '1', 'set', 'vrf', vrf])
self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '1', 'protocol', 'tcp_udp'])
self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '1', 'destination', 'port', '8888'])
self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '1', 'set', 'vrf', vrf])
self.cli_set(['policy', 'route', 'smoketest', 'interface', interface])
self.cli_set(['policy', 'route6', 'smoketest6', 'interface', interface])
self.cli_commit()
mark_hex = "{0:#010x}".format(table_mark_offset - int(vrf_table_id))
# IPv4
nftables_search = [
[f'iifname "{interface}"', 'jump VYOS_PBR_UD_smoketest'],
['tcp flags syn / syn,ack', 'tcp dport 8888', 'meta mark set ' + mark_hex]
]
self.verify_nftables(nftables_search, 'ip vyos_mangle')
# IPv6
nftables6_search = [
[f'iifname "{interface}"', 'jump VYOS_PBR6_UD_smoketest'],
['meta l4proto { tcp, udp }', 'th dport 8888', 'meta mark set ' + mark_hex]
]
self.verify_nftables(nftables6_search, 'ip6 vyos_mangle')
# IP rule fwmark -> table
ip_rule_search = [
['fwmark ' + hex(table_mark_offset - int(vrf_table_id)), 'lookup ' + vrf]
]
self.verify_rules(ip_rule_search)
def test_pbr_matching_criteria(self):
self.cli_set(['policy', 'route', 'smoketest', 'default-log'])
self.cli_set(['policy', 'route', 'smoketest', 'rule', '1', 'protocol', 'udp'])
self.cli_set(['policy', 'route', 'smoketest', 'rule', '1', 'action', 'drop'])
self.cli_set(['policy', 'route', 'smoketest', 'rule', '1', 'mark', '2020'])
self.cli_set(['policy', 'route', 'smoketest', 'rule', '2', 'protocol', 'tcp'])
self.cli_set(['policy', 'route', 'smoketest', 'rule', '2', 'tcp', 'flags', 'syn'])
self.cli_set(['policy', 'route', 'smoketest', 'rule', '2', 'tcp', 'flags', 'not', 'ack'])
self.cli_set(['policy', 'route', 'smoketest', 'rule', '2', 'mark', '2-3000'])
self.cli_set(['policy', 'route', 'smoketest', 'rule', '2', 'set', 'table', table_id])
self.cli_set(['policy', 'route', 'smoketest', 'rule', '3', 'source', 'address', '198.51.100.0/24'])
self.cli_set(['policy', 'route', 'smoketest', 'rule', '3', 'protocol', 'tcp'])
self.cli_set(['policy', 'route', 'smoketest', 'rule', '3', 'destination', 'port', '22'])
self.cli_set(['policy', 'route', 'smoketest', 'rule', '3', 'state', 'new'])
self.cli_set(['policy', 'route', 'smoketest', 'rule', '3', 'ttl', 'gt', '2'])
self.cli_set(['policy', 'route', 'smoketest', 'rule', '3', 'mark', '!456'])
self.cli_set(['policy', 'route', 'smoketest', 'rule', '3', 'set', 'table', table_id])
self.cli_set(['policy', 'route', 'smoketest', 'rule', '4', 'protocol', 'icmp'])
self.cli_set(['policy', 'route', 'smoketest', 'rule', '4', 'icmp', 'type-name', 'echo-request'])
self.cli_set(['policy', 'route', 'smoketest', 'rule', '4', 'packet-length', '128'])
self.cli_set(['policy', 'route', 'smoketest', 'rule', '4', 'packet-length', '1024-2048'])
self.cli_set(['policy', 'route', 'smoketest', 'rule', '4', 'packet-type', 'other'])
self.cli_set(['policy', 'route', 'smoketest', 'rule', '4', 'log'])
self.cli_set(['policy', 'route', 'smoketest', 'rule', '4', 'set', 'table', table_id])
self.cli_set(['policy', 'route', 'smoketest', 'rule', '5', 'dscp', '41'])
self.cli_set(['policy', 'route', 'smoketest', 'rule', '5', 'dscp', '57-59'])
self.cli_set(['policy', 'route', 'smoketest', 'rule', '5', 'mark', '!456-500'])
self.cli_set(['policy', 'route', 'smoketest', 'rule', '5', 'set', 'table', table_id])
self.cli_set(['policy', 'route6', 'smoketest6', 'default-log'])
self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '1', 'protocol', 'udp'])
self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '1', 'action', 'drop'])
self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '2', 'protocol', 'tcp'])
self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '2', 'tcp', 'flags', 'syn'])
self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '2', 'tcp', 'flags', 'not', 'ack'])
self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '2', 'set', 'table', table_id])
self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '3', 'source', 'address', '2001:db8::0/64'])
self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '3', 'protocol', 'tcp'])
self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '3', 'destination', 'port', '22'])
self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '3', 'state', 'new'])
self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '3', 'hop-limit', 'gt', '2'])
self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '3', 'set', 'table', table_id])
self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '4', 'protocol', 'icmpv6'])
self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '4', 'icmpv6', 'type', 'echo-request'])
self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '4', 'packet-length-exclude', '128'])
self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '4', 'packet-length-exclude', '1024-2048'])
self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '4', 'packet-type', 'multicast'])
self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '4', 'log'])
self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '4', 'set', 'table', table_id])
self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '5', 'dscp-exclude', '61'])
self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '5', 'dscp-exclude', '14-19'])
self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '5', 'set', 'table', table_id])
self.cli_set(['policy', 'route', 'smoketest', 'interface', interface])
self.cli_set(['policy', 'route', 'smoketest', 'interface', interface_wc])
self.cli_set(['policy', 'route6', 'smoketest6', 'interface', interface_wc])
self.cli_commit()
mark_hex = "{0:#010x}".format(table_mark_offset - int(table_id))
# IPv4
nftables_search = [
['iifname { "' + interface + '", "' + interface_wc + '" }', 'jump VYOS_PBR_UD_smoketest'],
['meta l4proto udp', 'meta mark 0x000007e4', 'drop'],
['tcp flags syn / syn,ack', 'meta mark 0x00000002-0x00000bb8', 'meta mark set ' + mark_hex],
['ct state new', 'tcp dport 22', 'ip saddr 198.51.100.0/24', 'ip ttl > 2', 'meta mark != 0x000001c8', 'meta mark set ' + mark_hex],
['log prefix "[ipv4-route-smoketest-4-A]"', 'icmp type echo-request', 'ip length { 128, 1024-2048 }', 'meta pkttype other', 'meta mark set ' + mark_hex],
['ip dscp { 0x29, 0x39-0x3b }', 'meta mark != 0x000001c8-0x000001f4', 'meta mark set ' + mark_hex],
['log prefix "[ipv4-smoketest-default]"']
]
self.verify_nftables(nftables_search, 'ip vyos_mangle')
# IPv6
nftables6_search = [
[f'iifname "{interface_wc}"', 'jump VYOS_PBR6_UD_smoketest'],
['meta l4proto udp', 'drop'],
['tcp flags syn / syn,ack', 'meta mark set ' + mark_hex],
['ct state new', 'tcp dport 22', 'ip6 saddr 2001:db8::/64', 'ip6 hoplimit > 2', 'meta mark set ' + mark_hex],
['log prefix "[ipv6-route6-smoketest6-4-A]"', 'icmpv6 type echo-request', 'ip6 length != { 128, 1024-2048 }', 'meta pkttype multicast', 'meta mark set ' + mark_hex],
['ip6 dscp != { 0x0e-0x13, 0x3d }', 'meta mark set ' + mark_hex],
['log prefix "[ipv6-smoketest6-default]"']
]
self.verify_nftables(nftables6_search, 'ip6 vyos_mangle')
def test_geoip(self):
self.cli_set(['policy', 'route', 'smoketest', 'rule', '1', 'action', 'drop'])
self.cli_set(['policy', 'route', 'smoketest', 'rule', '1', 'source', 'geoip', 'country-code', 'se'])
self.cli_set(['policy', 'route', 'smoketest', 'rule', '1', 'source', 'geoip', 'country-code', 'gb'])
self.cli_set(['policy', 'route', 'smoketest', 'rule', '2', 'action', 'accept'])
self.cli_set(['policy', 'route', 'smoketest', 'rule', '2', 'source', 'geoip', 'country-code', 'de'])
self.cli_set(['policy', 'route', 'smoketest', 'rule', '2', 'source', 'geoip', 'country-code', 'fr'])
self.cli_set(['policy', 'route', 'smoketest', 'rule', '2', 'source', 'geoip', 'inverse-match'])
self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '1', 'action', 'drop'])
self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '1', 'source', 'geoip', 'country-code', 'se'])
self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '1', 'source', 'geoip', 'country-code', 'gb'])
self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '2', 'action', 'accept'])
self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '2', 'source', 'geoip', 'country-code', 'de'])
self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '2', 'source', 'geoip', 'country-code', 'fr'])
self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '2', 'source', 'geoip', 'inverse-match'])
self.cli_commit()
nftables_search = [
['ip saddr @GEOIP_CC_route_smoketest_1', 'drop'],
['ip saddr != @GEOIP_CC_route_smoketest_2', 'accept'],
]
# -t prevents 1000+ GeoIP elements being returned
self.verify_nftables(nftables_search, 'ip vyos_mangle', args='-t')
nftables_search = [
['ip6 saddr @GEOIP_CC6_route6_smoketest6_1', 'drop'],
['ip6 saddr != @GEOIP_CC6_route6_smoketest6_2', 'accept'],
]
self.verify_nftables(nftables_search, 'ip6 vyos_mangle', args='-t')
if __name__ == '__main__':
unittest.main(verbosity=2, failfast=VyOSUnitTestSHIM.TestCase.debug_on())
diff --git a/smoketest/scripts/cli/test_protocols_babel.py b/smoketest/scripts/cli/test_protocols_babel.py
index 51fce28e4..a047d789a 100755
--- a/smoketest/scripts/cli/test_protocols_babel.py
+++ b/smoketest/scripts/cli/test_protocols_babel.py
@@ -1,222 +1,219 @@
#!/usr/bin/env python3
#
# Copyright VyOS maintainers and contributors <maintainers@vyos.io>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
-from base_vyostest_shim import CSTORE_GUARD_TIME
from vyos.ifconfig import Section
from vyos.frrender import babel_daemon
from vyos.utils.process import process_named_running
from vyos.xml_ref import default_value
base_path = ['protocols', 'babel']
class TestProtocolsBABEL(VyOSUnitTestSHIM.TestCase):
@classmethod
def setUpClass(cls):
cls._interfaces = Section.interfaces('ethernet', vlan=False)
# call base-classes classmethod
super(TestProtocolsBABEL, cls).setUpClass()
# Retrieve FRR daemon PID - it is not allowed to crash, thus PID must remain the same
cls.daemon_pid = process_named_running(babel_daemon)
# ensure we can also run this test on a live system - so lets clean
# out the current configuration :)
cls.cli_delete(cls, base_path)
cls.cli_delete(cls, ['policy', 'prefix-list'])
cls.cli_delete(cls, ['policy', 'prefix-list6'])
- # Enable CSTORE guard time required by FRR related tests
- cls._commit_guard_time = CSTORE_GUARD_TIME
def tearDown(self):
# always destroy the entire babel configuration to make the processes
# life as hard as possible
self.cli_delete(base_path)
self.cli_delete(['policy', 'prefix-list'])
self.cli_delete(['policy', 'prefix-list6'])
self.cli_commit()
# check process health and continuity
self.assertEqual(self.daemon_pid, process_named_running(babel_daemon))
def test_01_basic(self):
diversity_factor = '64'
resend_delay = '100'
smoothing_half_life = '400'
self.cli_set(base_path + ['parameters', 'diversity'])
self.cli_set(base_path + ['parameters', 'diversity-factor', diversity_factor])
self.cli_set(base_path + ['parameters', 'resend-delay', resend_delay])
self.cli_set(base_path + ['parameters', 'smoothing-half-life', smoothing_half_life])
self.cli_commit()
frrconfig = self.getFRRconfig('router babel', endsection='^exit')
self.assertIn(f' babel diversity', frrconfig)
self.assertIn(f' babel diversity-factor {diversity_factor}', frrconfig)
self.assertIn(f' babel resend-delay {resend_delay}', frrconfig)
self.assertIn(f' babel smoothing-half-life {smoothing_half_life}', frrconfig)
def test_02_redistribute(self):
ipv4_protos = ['bgp', 'connected', 'isis', 'kernel', 'nhrp', 'ospf', 'rip', 'static']
ipv6_protos = ['bgp', 'connected', 'isis', 'kernel', 'ospfv3', 'ripng', 'static']
self.cli_set(base_path + ['interface', self._interfaces[0], 'enable-timestamps'])
for protocol in ipv4_protos:
self.cli_set(base_path + ['redistribute', 'ipv4', protocol])
for protocol in ipv6_protos:
self.cli_set(base_path + ['redistribute', 'ipv6', protocol])
self.cli_commit()
frrconfig = self.getFRRconfig('router babel', endsection='^exit', empty_retry=5)
for protocol in ipv4_protos:
self.assertIn(f' redistribute ipv4 {protocol}', frrconfig)
for protocol in ipv6_protos:
if protocol == 'ospfv3':
protocol = 'ospf6'
self.assertIn(f' redistribute ipv6 {protocol}', frrconfig)
def test_03_distribute_list(self):
access_list_in4 = '40'
access_list_out4 = '50'
access_list_in4_iface = '44'
access_list_out4_iface = '55'
access_list_in6 = 'AL-foo-in6'
access_list_out6 = 'AL-foo-out6'
prefix_list_in4 = 'PL-foo-in4'
prefix_list_out4 = 'PL-foo-out4'
prefix_list_in6 = 'PL-foo-in6'
prefix_list_out6 = 'PL-foo-out6'
self.cli_set(['policy', 'access-list', access_list_in4])
self.cli_set(['policy', 'access-list', access_list_out4])
self.cli_set(['policy', 'access-list6', access_list_in6])
self.cli_set(['policy', 'access-list6', access_list_out6])
self.cli_set(['policy', 'access-list', f'{access_list_in4_iface}'])
self.cli_set(['policy', 'access-list', f'{access_list_out4_iface}'])
self.cli_set(['policy', 'prefix-list', prefix_list_in4])
self.cli_set(['policy', 'prefix-list', prefix_list_out4])
self.cli_set(['policy', 'prefix-list6', prefix_list_in6])
self.cli_set(['policy', 'prefix-list6', prefix_list_out6])
self.cli_set(base_path + ['distribute-list', 'ipv4', 'access-list', 'in', access_list_in4])
self.cli_set(base_path + ['distribute-list', 'ipv4', 'access-list', 'out', access_list_out4])
self.cli_set(base_path + ['distribute-list', 'ipv6', 'access-list', 'in', access_list_in6])
self.cli_set(base_path + ['distribute-list', 'ipv6', 'access-list', 'out', access_list_out6])
self.cli_set(base_path + ['distribute-list', 'ipv4', 'prefix-list', 'in', prefix_list_in4])
self.cli_set(base_path + ['distribute-list', 'ipv4', 'prefix-list', 'out', prefix_list_out4])
self.cli_set(base_path + ['distribute-list', 'ipv6', 'prefix-list', 'in', prefix_list_in6])
self.cli_set(base_path + ['distribute-list', 'ipv6', 'prefix-list', 'out', prefix_list_out6])
for interface in self._interfaces:
self.cli_set(base_path + ['interface', interface])
self.cli_set(['policy', 'access-list6', f'{access_list_in6}-{interface}'])
self.cli_set(['policy', 'access-list6', f'{access_list_out6}-{interface}'])
self.cli_set(['policy', 'prefix-list', f'{prefix_list_in4}-{interface}'])
self.cli_set(['policy', 'prefix-list', f'{prefix_list_out4}-{interface}'])
self.cli_set(['policy', 'prefix-list6', f'{prefix_list_in6}-{interface}'])
self.cli_set(['policy', 'prefix-list6', f'{prefix_list_out6}-{interface}'])
tmp_path = base_path + ['distribute-list', 'ipv4', 'interface', interface]
self.cli_set(tmp_path + ['access-list', 'in', f'{access_list_in4_iface}'])
self.cli_set(tmp_path + ['access-list', 'out', f'{access_list_out4_iface}'])
self.cli_set(tmp_path + ['prefix-list', 'in', f'{prefix_list_in4}-{interface}'])
self.cli_set(tmp_path + ['prefix-list', 'out', f'{prefix_list_out4}-{interface}'])
tmp_path = base_path + ['distribute-list', 'ipv6', 'interface', interface]
self.cli_set(tmp_path + ['access-list', 'in', f'{access_list_in6}-{interface}'])
self.cli_set(tmp_path + ['access-list', 'out', f'{access_list_out6}-{interface}'])
self.cli_set(tmp_path + ['prefix-list', 'in', f'{prefix_list_in6}-{interface}'])
self.cli_set(tmp_path + ['prefix-list', 'out', f'{prefix_list_out6}-{interface}'])
self.cli_commit()
frrconfig = self.getFRRconfig('router babel', endsection='^exit')
self.assertIn(f' distribute-list {access_list_in4} in', frrconfig)
self.assertIn(f' distribute-list {access_list_out4} out', frrconfig)
self.assertIn(f' ipv6 distribute-list {access_list_in6} in', frrconfig)
self.assertIn(f' ipv6 distribute-list {access_list_out6} out', frrconfig)
self.assertIn(f' distribute-list prefix {prefix_list_in4} in', frrconfig)
self.assertIn(f' distribute-list prefix {prefix_list_out4} out', frrconfig)
self.assertIn(f' ipv6 distribute-list prefix {prefix_list_in6} in', frrconfig)
self.assertIn(f' ipv6 distribute-list prefix {prefix_list_out6} out', frrconfig)
for interface in self._interfaces:
self.assertIn(f' distribute-list {access_list_in4_iface} in {interface}', frrconfig)
self.assertIn(f' distribute-list {access_list_out4_iface} out {interface}', frrconfig)
self.assertIn(f' ipv6 distribute-list {access_list_in6}-{interface} in {interface}', frrconfig)
self.assertIn(f' ipv6 distribute-list {access_list_out6}-{interface} out {interface}', frrconfig)
self.assertIn(f' distribute-list prefix {prefix_list_in4}-{interface} in {interface}', frrconfig)
self.assertIn(f' distribute-list prefix {prefix_list_out4}-{interface} out {interface}', frrconfig)
self.assertIn(f' ipv6 distribute-list prefix {prefix_list_in6}-{interface} in {interface}', frrconfig)
self.assertIn(f' ipv6 distribute-list prefix {prefix_list_out6}-{interface} out {interface}', frrconfig)
def test_04_interfaces(self):
def_update_interval = default_value(base_path + ['interface', 'eth0', 'update-interval'])
channel = '20'
hello_interval = '1000'
max_rtt_penalty = '100'
rtt_decay = '23'
rtt_max = '119'
rtt_min = '11'
rxcost = '40000'
type = 'wired'
for interface in self._interfaces:
self.cli_set(base_path + ['interface', interface])
self.cli_set(base_path + ['interface', interface, 'channel', channel])
self.cli_set(base_path + ['interface', interface, 'enable-timestamps'])
self.cli_set(base_path + ['interface', interface, 'hello-interval', hello_interval])
self.cli_set(base_path + ['interface', interface, 'max-rtt-penalty', max_rtt_penalty])
self.cli_set(base_path + ['interface', interface, 'rtt-decay', rtt_decay])
self.cli_set(base_path + ['interface', interface, 'rtt-max', rtt_max])
self.cli_set(base_path + ['interface', interface, 'rtt-min', rtt_min])
self.cli_set(base_path + ['interface', interface, 'rxcost', rxcost])
self.cli_set(base_path + ['interface', interface, 'split-horizon', 'disable'])
self.cli_set(base_path + ['interface', interface, 'type', type])
self.cli_commit()
frrconfig = self.getFRRconfig('router babel', endsection='^exit')
for interface in self._interfaces:
self.assertIn(f' network {interface}', frrconfig)
iface_config = self.getFRRconfig(f'interface {interface}', endsection='^exit')
self.assertIn(f' babel channel {channel}', iface_config)
self.assertIn(f' babel enable-timestamps', iface_config)
self.assertIn(f' babel update-interval {def_update_interval}', iface_config)
self.assertIn(f' babel hello-interval {hello_interval}', iface_config)
self.assertIn(f' babel rtt-decay {rtt_decay}', iface_config)
self.assertIn(f' babel rtt-max {rtt_max}', iface_config)
self.assertIn(f' babel rtt-min {rtt_min}', iface_config)
self.assertIn(f' babel rxcost {rxcost}', iface_config)
self.assertIn(f' babel max-rtt-penalty {max_rtt_penalty}', iface_config)
self.assertIn(f' no babel split-horizon', iface_config)
self.assertIn(f' babel {type}', iface_config)
if __name__ == '__main__':
unittest.main(verbosity=2, failfast=VyOSUnitTestSHIM.TestCase.debug_on())
diff --git a/smoketest/scripts/cli/test_protocols_bfd.py b/smoketest/scripts/cli/test_protocols_bfd.py
index 6923593f3..611c6b605 100755
--- a/smoketest/scripts/cli/test_protocols_bfd.py
+++ b/smoketest/scripts/cli/test_protocols_bfd.py
@@ -1,243 +1,239 @@
#!/usr/bin/env python3
#
# Copyright VyOS maintainers and contributors <maintainers@vyos.io>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
-from base_vyostest_shim import CSTORE_GUARD_TIME
from vyos.configsession import ConfigSessionError
from vyos.frrender import bfd_daemon
from vyos.utils.process import process_named_running
base_path = ['protocols', 'bfd']
dum_if = 'dum1001'
vrf_name = 'red'
peers = {
'192.0.2.10' : {
'intv_rx' : '500',
'intv_tx' : '600',
'multihop' : '',
'source_addr': '192.0.2.254',
'profile' : 'foo-bar-baz',
'minimum_ttl': '20',
},
'192.0.2.20' : {
'echo_mode' : '',
'intv_echo' : '100',
'intv_mult' : '100',
'intv_rx' : '222',
'intv_tx' : '333',
'passive' : '',
'shutdown' : '',
'profile' : 'foo',
'source_intf': dum_if,
},
'2001:db8::1000:1' : {
'source_addr': '2001:db8::1',
'vrf' : vrf_name,
},
'2001:db8::2000:1' : {
'source_addr': '2001:db8::1',
'multihop' : '',
'profile' : 'baz_foo',
},
}
profiles = {
'foo' : {
'echo_mode' : '',
'intv_echo' : '100',
'intv_mult' : '101',
'intv_rx' : '222',
'intv_tx' : '333',
'shutdown' : '',
'minimum_ttl': '40',
},
'foo-bar-baz' : {
'intv_mult' : '4',
'intv_rx' : '400',
'intv_tx' : '400',
},
'baz_foo' : {
'intv_mult' : '102',
'intv_rx' : '444',
'passive' : '',
},
}
class TestProtocolsBFD(VyOSUnitTestSHIM.TestCase):
@classmethod
def setUpClass(cls):
super(TestProtocolsBFD, cls).setUpClass()
# Retrieve FRR daemon PID - it is not allowed to crash, thus PID must remain the same
cls.daemon_pid = process_named_running(bfd_daemon)
- # Enable CSTORE guard time required by FRR related tests
- cls._commit_guard_time = CSTORE_GUARD_TIME
-
# ensure we can also run this test on a live system - so lets clean
# out the current configuration :)
cls.cli_delete(cls, base_path)
def tearDown(self):
self.cli_delete(base_path)
self.cli_commit()
# check process health and continuity
self.assertEqual(self.daemon_pid, process_named_running(bfd_daemon))
def test_bfd_peer(self):
self.cli_set(['vrf', 'name', vrf_name, 'table', '1000'])
for peer, peer_config in peers.items():
if 'echo_mode' in peer_config:
self.cli_set(base_path + ['peer', peer, 'echo-mode'])
if 'intv_echo' in peer_config:
self.cli_set(base_path + ['peer', peer, 'interval', 'echo-interval', peer_config["intv_echo"]])
if 'intv_mult' in peer_config:
self.cli_set(base_path + ['peer', peer, 'interval', 'multiplier', peer_config["intv_mult"]])
if 'intv_rx' in peer_config:
self.cli_set(base_path + ['peer', peer, 'interval', 'receive', peer_config["intv_rx"]])
if 'intv_tx' in peer_config:
self.cli_set(base_path + ['peer', peer, 'interval', 'transmit', peer_config["intv_tx"]])
if 'minimum_ttl' in peer_config:
self.cli_set(base_path + ['peer', peer, 'minimum-ttl', peer_config["minimum_ttl"]])
if 'multihop' in peer_config:
self.cli_set(base_path + ['peer', peer, 'multihop'])
if 'passive' in peer_config:
self.cli_set(base_path + ['peer', peer, 'passive'])
if 'shutdown' in peer_config:
self.cli_set(base_path + ['peer', peer, 'shutdown'])
if 'source_addr' in peer_config:
self.cli_set(base_path + ['peer', peer, 'source', 'address', peer_config["source_addr"]])
if 'source_intf' in peer_config:
self.cli_set(base_path + ['peer', peer, 'source', 'interface', peer_config["source_intf"]])
if 'vrf' in peer_config:
self.cli_set(base_path + ['peer', peer, 'vrf', peer_config["vrf"]])
# commit changes
self.cli_commit()
# Verify FRR bgpd configuration
frrconfig = self.getFRRconfig('bfd', endsection='^exit')
for peer, peer_config in peers.items():
tmp = f'peer {peer}'
if 'multihop' in peer_config:
tmp += f' multihop'
if 'source_addr' in peer_config:
tmp += f' local-address {peer_config["source_addr"]}'
if 'source_intf' in peer_config:
tmp += f' interface {peer_config["source_intf"]}'
if 'vrf' in peer_config:
tmp += f' vrf {peer_config["vrf"]}'
self.assertIn(tmp, frrconfig)
peerconfig = self.getFRRconfig('bfd', endsection='^exit', substring=f' peer {peer}',
endsubsection='^ exit')
if 'echo_mode' in peer_config:
self.assertIn(f'echo-mode', peerconfig)
if 'intv_echo' in peer_config:
self.assertIn(f'echo receive-interval {peer_config["intv_echo"]}', peerconfig)
self.assertIn(f'echo transmit-interval {peer_config["intv_echo"]}', peerconfig)
if 'intv_mult' in peer_config:
self.assertIn(f'detect-multiplier {peer_config["intv_mult"]}', peerconfig)
if 'intv_rx' in peer_config:
self.assertIn(f'receive-interval {peer_config["intv_rx"]}', peerconfig)
if 'intv_tx' in peer_config:
self.assertIn(f'transmit-interval {peer_config["intv_tx"]}', peerconfig)
if 'minimum_ttl' in peer_config:
self.assertIn(f'minimum-ttl {peer_config["minimum_ttl"]}', peerconfig)
if 'passive' in peer_config:
self.assertIn(f'passive-mode', peerconfig)
if 'shutdown' in peer_config:
self.assertIn(f'shutdown', peerconfig)
else:
self.assertNotIn(f'shutdown', peerconfig)
self.cli_delete(['vrf', 'name', vrf_name])
def test_bfd_profile(self):
for profile, profile_config in profiles.items():
if 'echo_mode' in profile_config:
self.cli_set(base_path + ['profile', profile, 'echo-mode'])
if 'intv_echo' in profile_config:
self.cli_set(base_path + ['profile', profile, 'interval', 'echo-interval', profile_config["intv_echo"]])
if 'intv_mult' in profile_config:
self.cli_set(base_path + ['profile', profile, 'interval', 'multiplier', profile_config["intv_mult"]])
if 'intv_rx' in profile_config:
self.cli_set(base_path + ['profile', profile, 'interval', 'receive', profile_config["intv_rx"]])
if 'intv_tx' in profile_config:
self.cli_set(base_path + ['profile', profile, 'interval', 'transmit', profile_config["intv_tx"]])
if 'minimum_ttl' in profile_config:
self.cli_set(base_path + ['profile', profile, 'minimum-ttl', profile_config["minimum_ttl"]])
if 'passive' in profile_config:
self.cli_set(base_path + ['profile', profile, 'passive'])
if 'shutdown' in profile_config:
self.cli_set(base_path + ['profile', profile, 'shutdown'])
for peer, peer_config in peers.items():
if 'profile' in peer_config:
self.cli_set(base_path + ['peer', peer, 'profile', peer_config["profile"] + 'wrong'])
if 'source_addr' in peer_config:
self.cli_set(base_path + ['peer', peer, 'source', 'address', peer_config["source_addr"]])
if 'source_intf' in peer_config:
self.cli_set(base_path + ['peer', peer, 'source', 'interface', peer_config["source_intf"]])
# BFD profile does not exist!
with self.assertRaises(ConfigSessionError):
self.cli_commit()
for peer, peer_config in peers.items():
if 'profile' in peer_config:
self.cli_set(base_path + ['peer', peer, 'profile', peer_config["profile"]])
# commit changes
self.cli_commit()
# Verify FRR bgpd configuration
for profile, profile_config in profiles.items():
config = self.getFRRconfig('bfd', endsection='^exit',
substring=f' profile {profile}', endsubsection='^ exit',)
if 'echo_mode' in profile_config:
self.assertIn(f' echo-mode', config)
if 'intv_echo' in profile_config:
self.assertIn(f' echo receive-interval {profile_config["intv_echo"]}', config)
self.assertIn(f' echo transmit-interval {profile_config["intv_echo"]}', config)
if 'intv_mult' in profile_config:
self.assertIn(f' detect-multiplier {profile_config["intv_mult"]}', config)
if 'intv_rx' in profile_config:
self.assertIn(f' receive-interval {profile_config["intv_rx"]}', config)
if 'intv_tx' in profile_config:
self.assertIn(f' transmit-interval {profile_config["intv_tx"]}', config)
if 'minimum_ttl' in profile_config:
self.assertIn(f' minimum-ttl {profile_config["minimum_ttl"]}', config)
if 'passive' in profile_config:
self.assertIn(f' passive-mode', config)
if 'shutdown' in profile_config:
self.assertIn(f' shutdown', config)
else:
self.assertNotIn(f'shutdown', config)
for peer, peer_config in peers.items():
peerconfig = self.getFRRconfig('bfd', endsection='^exit',
substring=f' peer {peer}', endsubsection='^ exit')
if 'profile' in peer_config:
self.assertIn(f' profile {peer_config["profile"]}', peerconfig)
if __name__ == '__main__':
unittest.main(verbosity=2, failfast=VyOSUnitTestSHIM.TestCase.debug_on())
diff --git a/smoketest/scripts/cli/test_protocols_bgp.py b/smoketest/scripts/cli/test_protocols_bgp.py
index 3a4cba386..711bbb815 100755
--- a/smoketest/scripts/cli/test_protocols_bgp.py
+++ b/smoketest/scripts/cli/test_protocols_bgp.py
@@ -1,1688 +1,1684 @@
#!/usr/bin/env python3
#
# Copyright VyOS maintainers and contributors <maintainers@vyos.io>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import unittest
from time import sleep
from base_vyostest_shim import VyOSUnitTestSHIM
-from base_vyostest_shim import CSTORE_GUARD_TIME
from vyos.ifconfig import Section
from vyos.configsession import ConfigSessionError
from vyos.template import is_ipv6
from vyos.utils.process import process_named_running
from vyos.utils.process import cmd
from vyos.frrender import bgp_daemon
ASN = '64512'
base_path = ['protocols', 'bgp']
route_map_in = 'foo-map-in'
route_map_out = 'foo-map-out'
prefix_list_in = 'pfx-foo-in'
prefix_list_out = 'pfx-foo-out'
prefix_list_in6 = 'pfx-foo-in6'
prefix_list_out6 = 'pfx-foo-out6'
bfd_profile = 'foo-bar-baz'
import_afi = 'ipv4-unicast'
import_vrf = 'red'
import_rd = ASN + ':100'
import_vrf_base = ['vrf', 'name']
neighbor_config = {
'192.0.2.1' : {
'bfd' : '',
'cap_dynamic' : '',
'cap_ext_next' : '',
'cap_ext_sver' : '',
'remote_as' : '100',
'adv_interv' : '400',
'passive' : '',
'password' : 'VyOS-Secure123',
'shutdown' : '',
'cap_over' : '',
'ttl_security' : '5',
'system_as' : '300',
'route_map_in' : route_map_in,
'route_map_out' : route_map_out,
'no_send_comm_ext' : '',
'addpath_all' : '',
'p_attr_discard' : ['10', '20', '30', '40', '50'],
},
'192.0.2.2' : {
'bfd_profile' : bfd_profile,
'remote_as' : '200',
'shutdown' : '',
'no_cap_nego' : '',
'port' : '667',
'cap_strict' : '',
'advertise_map' : route_map_in,
'non_exist_map' : route_map_out,
'pfx_list_in' : prefix_list_in,
'pfx_list_out' : prefix_list_out,
'no_send_comm_std' : '',
'local_role' : 'rs-client',
'p_attr_taw' : '200',
},
'192.0.2.3' : {
'advertise_map' : route_map_in,
'description' : 'foo bar baz',
'remote_as' : '200',
'passive' : '',
'multi_hop' : '5',
'update_src' : 'lo',
'peer_group' : 'foo',
'graceful_rst' : '',
},
'2001:db8::1' : {
'advertise_map' : route_map_in,
'exist_map' : route_map_out,
'cap_dynamic' : '',
'cap_ext_next' : '',
'cap_ext_sver' : '',
'remote_as' : '123',
'adv_interv' : '400',
'passive' : '',
'password' : 'VyOS-Secure123',
'shutdown' : '',
'cap_over' : '',
'ttl_security' : '5',
'system_as' : '300',
'solo' : '',
'route_map_in' : route_map_in,
'route_map_out' : route_map_out,
'no_send_comm_std' : '',
'addpath_per_as' : '',
'peer_group' : 'foo-bar',
'local_role' : 'customer',
'local_role_strict': '',
},
'2001:db8::2' : {
'remote_as' : '456',
'shutdown' : '',
'no_cap_nego' : '',
'port' : '667',
'cap_strict' : '',
'pfx_list_in' : prefix_list_in6,
'pfx_list_out' : prefix_list_out6,
'no_send_comm_ext' : '',
'peer_group' : 'foo-bar_baz',
'graceful_rst_hlp' : '',
'disable_conn_chk' : '',
},
}
peer_group_config = {
'foo' : {
'advertise_map' : route_map_in,
'exist_map' : route_map_out,
'bfd' : '',
'remote_as' : '100',
'passive' : '',
'password' : 'VyOS-Secure123',
'shutdown' : '',
'solo' : '',
'cap_over' : '',
'ttl_security' : '5',
'disable_conn_chk' : '',
'p_attr_discard' : ['100', '150', '200'],
},
'bar' : {
'remote_as' : '111',
'graceful_rst_no' : '',
'port' : '667',
'p_attr_taw' : '126',
},
'foo-bar' : {
'advertise_map' : route_map_in,
'description' : 'foo peer bar group',
'remote_as' : '200',
'shutdown' : '',
'no_cap_nego' : '',
'system_as' : '300',
'pfx_list_in' : prefix_list_in,
'pfx_list_out' : prefix_list_out,
'no_send_comm_ext' : '',
},
'foo-bar_baz' : {
'advertise_map' : route_map_in,
'non_exist_map' : route_map_out,
'bfd_profile' : bfd_profile,
'cap_dynamic' : '',
'cap_ext_next' : '',
'remote_as' : '200',
'passive' : '',
'multi_hop' : '5',
'update_src' : 'lo',
'route_map_in' : route_map_in,
'route_map_out' : route_map_out,
'local_role' : 'peer',
'local_role_strict': '',
},
}
class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
@classmethod
def setUpClass(cls):
super(TestProtocolsBGP, cls).setUpClass()
# Retrieve FRR daemon PID - it is not allowed to crash, thus PID must remain the same
cls.daemon_pid = process_named_running(bgp_daemon)
# ensure we can also run this test on a live system - so lets clean
# out the current configuration :)
cls.cli_delete(cls, base_path)
cls.cli_delete(cls, ['policy', 'route-map'])
cls.cli_delete(cls, ['policy', 'prefix-list'])
cls.cli_delete(cls, ['policy', 'prefix-list6'])
cls.cli_delete(cls, ['vrf'])
cls.cli_set(cls, ['policy', 'route-map', route_map_in, 'rule', '10', 'action', 'permit'])
cls.cli_set(cls, ['policy', 'route-map', route_map_out, 'rule', '10', 'action', 'permit'])
cls.cli_set(cls, ['policy', 'prefix-list', prefix_list_in, 'rule', '10', 'action', 'permit'])
cls.cli_set(cls, ['policy', 'prefix-list', prefix_list_in, 'rule', '10', 'prefix', '192.0.2.0/25'])
cls.cli_set(cls, ['policy', 'prefix-list', prefix_list_out, 'rule', '10', 'action', 'permit'])
cls.cli_set(cls, ['policy', 'prefix-list', prefix_list_out, 'rule', '10', 'prefix', '192.0.2.128/25'])
cls.cli_set(cls, ['policy', 'prefix-list6', prefix_list_in6, 'rule', '10', 'action', 'permit'])
cls.cli_set(cls, ['policy', 'prefix-list6', prefix_list_in6, 'rule', '10', 'prefix', '2001:db8:1000::/64'])
cls.cli_set(cls, ['policy', 'prefix-list6', prefix_list_out6, 'rule', '10', 'action', 'deny'])
cls.cli_set(cls, ['policy', 'prefix-list6', prefix_list_out6, 'rule', '10', 'prefix', '2001:db8:2000::/64'])
- # Enable CSTORE guard time required by FRR related tests
- cls._commit_guard_time = CSTORE_GUARD_TIME
-
@classmethod
def tearDownClass(cls):
cls.cli_delete(cls, ['policy', 'route-map'])
cls.cli_delete(cls, ['policy', 'prefix-list'])
cls.cli_delete(cls, ['policy', 'prefix-list6'])
def setUp(self):
self.cli_set(base_path + ['system-as', ASN])
def tearDown(self):
# cleanup any possible VRF mess
self.cli_delete(['vrf'])
# always destrox the entire bgpd configuration to make the processes
# life as hard as possible
self.cli_delete(base_path)
self.cli_commit()
frrconfig = self.getFRRconfig('router bgp', endsection='^exit')
self.assertNotIn(f'router bgp', frrconfig)
# check process health and continuity
self.assertEqual(self.daemon_pid, process_named_running(bgp_daemon))
def create_bgp_instances_for_import_test(self):
table = '1000'
self.cli_set(import_vrf_base + [import_vrf, 'table', table])
self.cli_set(import_vrf_base + [import_vrf, 'protocols', 'bgp', 'system-as', ASN])
def verify_frr_config(self, peer, peer_config, frrconfig):
# recurring patterns to verify for both a simple neighbor and a peer-group
if 'bfd' in peer_config:
self.assertIn(f' neighbor {peer} bfd', frrconfig)
if 'bfd_profile' in peer_config:
self.assertIn(f' neighbor {peer} bfd profile {peer_config["bfd_profile"]}', frrconfig)
self.assertIn(f' neighbor {peer} bfd check-control-plane-failure', frrconfig)
if 'cap_dynamic' in peer_config:
self.assertIn(f' neighbor {peer} capability dynamic', frrconfig)
if 'cap_ext_next' in peer_config:
self.assertIn(f' neighbor {peer} capability extended-nexthop', frrconfig)
if 'cap_ext_sver' in peer_config:
self.assertIn(f' neighbor {peer} capability software-version', frrconfig)
if 'description' in peer_config:
self.assertIn(f' neighbor {peer} description {peer_config["description"]}', frrconfig)
if 'no_cap_nego' in peer_config:
self.assertIn(f' neighbor {peer} dont-capability-negotiate', frrconfig)
if 'multi_hop' in peer_config:
self.assertIn(f' neighbor {peer} ebgp-multihop {peer_config["multi_hop"]}', frrconfig)
if 'local_as' in peer_config:
self.assertIn(f' neighbor {peer} local-as {peer_config["local_as"]} no-prepend replace-as', frrconfig)
if 'local_role' in peer_config:
tmp = f' neighbor {peer} local-role {peer_config["local_role"]}'
if 'local_role_strict' in peer_config:
tmp += ' strict'
self.assertIn(tmp, frrconfig)
if 'cap_over' in peer_config:
self.assertIn(f' neighbor {peer} override-capability', frrconfig)
if 'passive' in peer_config:
self.assertIn(f' neighbor {peer} passive', frrconfig)
if 'password' in peer_config:
self.assertIn(f' neighbor {peer} password {peer_config["password"]}', frrconfig)
if 'port' in peer_config:
self.assertIn(f' neighbor {peer} port {peer_config["port"]}', frrconfig)
if 'remote_as' in peer_config:
self.assertIn(f' neighbor {peer} remote-as {peer_config["remote_as"]}', frrconfig)
if 'solo' in peer_config:
self.assertIn(f' neighbor {peer} solo', frrconfig)
if 'shutdown' in peer_config:
self.assertIn(f' neighbor {peer} shutdown', frrconfig)
if 'ttl_security' in peer_config:
self.assertIn(f' neighbor {peer} ttl-security hops {peer_config["ttl_security"]}', frrconfig)
if 'update_src' in peer_config:
self.assertIn(f' neighbor {peer} update-source {peer_config["update_src"]}', frrconfig)
if 'route_map_in' in peer_config:
self.assertIn(f' neighbor {peer} route-map {peer_config["route_map_in"]} in', frrconfig)
if 'route_map_out' in peer_config:
self.assertIn(f' neighbor {peer} route-map {peer_config["route_map_out"]} out', frrconfig)
if 'pfx_list_in' in peer_config:
self.assertIn(f' neighbor {peer} prefix-list {peer_config["pfx_list_in"]} in', frrconfig)
if 'pfx_list_out' in peer_config:
self.assertIn(f' neighbor {peer} prefix-list {peer_config["pfx_list_out"]} out', frrconfig)
if 'no_send_comm_std' in peer_config:
self.assertIn(f' no neighbor {peer} send-community', frrconfig)
if 'no_send_comm_ext' in peer_config:
self.assertIn(f' no neighbor {peer} send-community extended', frrconfig)
if 'addpath_all' in peer_config:
self.assertIn(f' neighbor {peer} addpath-tx-all-paths', frrconfig)
if 'p_attr_discard' in peer_config:
tmp = ' '.join(peer_config["p_attr_discard"])
self.assertIn(f' neighbor {peer} path-attribute discard {tmp}', frrconfig)
if 'p_attr_taw' in peer_config:
self.assertIn(f' neighbor {peer} path-attribute treat-as-withdraw {peer_config["p_attr_taw"]}', frrconfig)
if 'addpath_per_as' in peer_config:
self.assertIn(f' neighbor {peer} addpath-tx-bestpath-per-AS', frrconfig)
if 'advertise_map' in peer_config:
base = f' neighbor {peer} advertise-map {peer_config["advertise_map"]}'
if 'exist_map' in peer_config:
base = f'{base} exist-map {peer_config["exist_map"]}'
if 'non_exist_map' in peer_config:
base = f'{base} non-exist-map {peer_config["non_exist_map"]}'
self.assertIn(base, frrconfig)
if 'graceful_rst' in peer_config:
self.assertIn(f' neighbor {peer} graceful-restart', frrconfig)
if 'graceful_rst_no' in peer_config:
self.assertIn(f' neighbor {peer} graceful-restart-disable', frrconfig)
if 'graceful_rst_hlp' in peer_config:
self.assertIn(f' neighbor {peer} graceful-restart-helper', frrconfig)
if 'disable_conn_chk' in peer_config:
self.assertIn(f' neighbor {peer} disable-connected-check', frrconfig)
def test_bgp_01_simple(self):
router_id = '127.0.0.1'
local_pref = '500'
stalepath_time = '60'
max_path_v4 = '2'
max_path_v4ibgp = '4'
max_path_v6 = '8'
max_path_v6ibgp = '16'
cond_adv_timer = '30'
min_hold_time = '2'
tcp_keepalive_idle = '66'
tcp_keepalive_interval = '77'
tcp_keepalive_probes = '22'
self.cli_set(base_path + ['parameters', 'allow-martian-nexthop'])
self.cli_set(base_path + ['parameters', 'disable-ebgp-connected-route-check'])
self.cli_set(base_path + ['parameters', 'no-hard-administrative-reset'])
self.cli_set(base_path + ['parameters', 'log-neighbor-changes'])
self.cli_set(base_path + ['parameters', 'labeled-unicast', 'explicit-null'])
self.cli_set(base_path + ['parameters', 'router-id', router_id])
# System AS number MUST be defined - as this is set in setUp() we remove
# this once for testing of the proper error
self.cli_delete(base_path + ['system-as'])
with self.assertRaises(ConfigSessionError):
self.cli_commit()
self.cli_set(base_path + ['system-as', ASN])
# Default local preference (higher = more preferred, default value is 100)
self.cli_set(base_path + ['parameters', 'default', 'local-pref', local_pref])
self.cli_set(base_path + ['parameters', 'graceful-restart', 'stalepath-time', stalepath_time])
self.cli_set(base_path + ['parameters', 'graceful-shutdown'])
self.cli_set(base_path + ['parameters', 'ebgp-requires-policy'])
self.cli_set(base_path + ['parameters', 'bestpath', 'as-path', 'multipath-relax'])
self.cli_set(base_path + ['parameters', 'bestpath', 'bandwidth', 'default-weight-for-missing'])
self.cli_set(base_path + ['parameters', 'bestpath', 'compare-routerid'])
self.cli_set(base_path + ['parameters', 'bestpath', 'peer-type', 'multipath-relax'])
self.cli_set(base_path + ['parameters', 'conditional-advertisement', 'timer', cond_adv_timer])
self.cli_set(base_path + ['parameters', 'fast-convergence'])
self.cli_set(base_path + ['parameters', 'minimum-holdtime', min_hold_time])
self.cli_set(base_path + ['parameters', 'no-suppress-duplicates'])
self.cli_set(base_path + ['parameters', 'reject-as-sets'])
self.cli_set(base_path + ['parameters', 'route-reflector-allow-outbound-policy'])
self.cli_set(base_path + ['parameters', 'no-ipv6-auto-ra'])
self.cli_set(base_path + ['parameters', 'shutdown'])
self.cli_set(base_path + ['parameters', 'suppress-fib-pending'])
self.cli_set(base_path + ['parameters', 'tcp-keepalive', 'idle', tcp_keepalive_idle])
self.cli_set(base_path + ['parameters', 'tcp-keepalive', 'interval', tcp_keepalive_interval])
self.cli_set(base_path + ['parameters', 'tcp-keepalive', 'probes', tcp_keepalive_probes])
# AFI maximum path support
self.cli_set(base_path + ['address-family', 'ipv4-unicast', 'maximum-paths', 'ebgp', max_path_v4])
self.cli_set(base_path + ['address-family', 'ipv4-unicast', 'maximum-paths', 'ibgp', max_path_v4ibgp])
self.cli_set(base_path + ['address-family', 'ipv4-labeled-unicast', 'maximum-paths', 'ebgp', max_path_v4])
self.cli_set(base_path + ['address-family', 'ipv4-labeled-unicast', 'maximum-paths', 'ibgp', max_path_v4ibgp])
self.cli_set(base_path + ['address-family', 'ipv6-unicast', 'maximum-paths', 'ebgp', max_path_v6])
self.cli_set(base_path + ['address-family', 'ipv6-unicast', 'maximum-paths', 'ibgp', max_path_v6ibgp])
# commit changes
self.cli_commit()
# Verify FRR bgpd configuration
frrconfig = self.getFRRconfig(f'router bgp {ASN}', endsection='^exit')
self.assertIn(f'router bgp {ASN}', frrconfig)
self.assertIn(f' bgp router-id {router_id}', frrconfig)
self.assertIn(f' bgp allow-martian-nexthop', frrconfig)
self.assertIn(f' bgp disable-ebgp-connected-route-check', frrconfig)
self.assertIn(f' bgp log-neighbor-changes', frrconfig)
self.assertIn(f' bgp default local-preference {local_pref}', frrconfig)
self.assertIn(f' bgp conditional-advertisement timer {cond_adv_timer}', frrconfig)
self.assertIn(f' bgp fast-convergence', frrconfig)
self.assertIn(f' bgp graceful-restart stalepath-time {stalepath_time}', frrconfig)
self.assertIn(f' bgp graceful-shutdown', frrconfig)
self.assertIn(f' no bgp hard-administrative-reset', frrconfig)
self.assertIn(f' bgp labeled-unicast explicit-null', frrconfig)
self.assertIn(f' bgp bestpath as-path multipath-relax', frrconfig)
self.assertIn(f' bgp bestpath bandwidth default-weight-for-missing', frrconfig)
self.assertIn(f' bgp bestpath compare-routerid', frrconfig)
self.assertIn(f' bgp bestpath peer-type multipath-relax', frrconfig)
self.assertIn(f' bgp minimum-holdtime {min_hold_time}', frrconfig)
self.assertIn(f' bgp reject-as-sets', frrconfig)
self.assertIn(f' bgp route-reflector allow-outbound-policy', frrconfig)
self.assertIn(f' no bgp ipv6-auto-ra', frrconfig)
self.assertIn(f' bgp shutdown', frrconfig)
self.assertIn(f' bgp suppress-fib-pending', frrconfig)
self.assertIn(f' bgp tcp-keepalive {tcp_keepalive_idle} {tcp_keepalive_interval} {tcp_keepalive_probes}', frrconfig)
self.assertNotIn(f'bgp ebgp-requires-policy', frrconfig)
self.assertIn(f' no bgp suppress-duplicates', frrconfig)
afiv4_config = self.getFRRconfig(f'router bgp {ASN}', endsection='^exit',
substring=' address-family ipv4 unicast',
endsubsection='^ exit-address-family')
self.assertIn(f' maximum-paths {max_path_v4}', afiv4_config)
self.assertIn(f' maximum-paths ibgp {max_path_v4ibgp}', afiv4_config)
afiv4_config = self.getFRRconfig(f'router bgp {ASN}', endsection='^exit',
substring=' address-family ipv4 labeled-unicast',
endsubsection='^ exit-address-family')
self.assertIn(f' maximum-paths {max_path_v4}', afiv4_config)
self.assertIn(f' maximum-paths ibgp {max_path_v4ibgp}', afiv4_config)
afiv6_config = self.getFRRconfig(f'router bgp {ASN}', endsection='^exit',
substring=' address-family ipv6 unicast',
endsubsection='^ exit-address-family')
self.assertIn(f' maximum-paths {max_path_v6}', afiv6_config)
self.assertIn(f' maximum-paths ibgp {max_path_v6ibgp}', afiv6_config)
def test_bgp_02_neighbors(self):
# Test out individual neighbor configuration items, not all of them are
# also available to a peer-group!
self.cli_set(base_path + ['parameters', 'deterministic-med'])
for peer, peer_config in neighbor_config.items():
afi = 'ipv4-unicast'
if is_ipv6(peer):
afi = 'ipv6-unicast'
if 'adv_interv' in peer_config:
self.cli_set(base_path + ['neighbor', peer, 'advertisement-interval', peer_config["adv_interv"]])
if 'bfd' in peer_config:
self.cli_set(base_path + ['neighbor', peer, 'bfd'])
if 'bfd_profile' in peer_config:
self.cli_set(base_path + ['neighbor', peer, 'bfd', 'profile', peer_config["bfd_profile"]])
self.cli_set(base_path + ['neighbor', peer, 'bfd', 'check-control-plane-failure'])
if 'cap_dynamic' in peer_config:
self.cli_set(base_path + ['neighbor', peer, 'capability', 'dynamic'])
if 'cap_ext_next' in peer_config:
self.cli_set(base_path + ['neighbor', peer, 'capability', 'extended-nexthop'])
if 'cap_ext_sver' in peer_config:
self.cli_set(base_path + ['neighbor', peer, 'capability', 'software-version'])
if 'description' in peer_config:
self.cli_set(base_path + ['neighbor', peer, 'description', peer_config["description"]])
if 'no_cap_nego' in peer_config:
self.cli_set(base_path + ['neighbor', peer, 'disable-capability-negotiation'])
if 'multi_hop' in peer_config:
self.cli_set(base_path + ['neighbor', peer, 'ebgp-multihop', peer_config["multi_hop"]])
if 'local_as' in peer_config:
self.cli_set(base_path + ['neighbor', peer, 'local-as', peer_config["local_as"], 'no-prepend', 'replace-as'])
if 'local_role' in peer_config:
self.cli_set(base_path + ['neighbor', peer, 'local-role', peer_config["local_role"]])
if 'local_role_strict' in peer_config:
self.cli_set(base_path + ['neighbor', peer, 'local-role', peer_config["local_role"], 'strict'])
if 'cap_over' in peer_config:
self.cli_set(base_path + ['neighbor', peer, 'override-capability'])
if 'passive' in peer_config:
self.cli_set(base_path + ['neighbor', peer, 'passive'])
if 'password' in peer_config:
self.cli_set(base_path + ['neighbor', peer, 'password', peer_config["password"]])
if 'port' in peer_config:
self.cli_set(base_path + ['neighbor', peer, 'port', peer_config["port"]])
if 'remote_as' in peer_config:
self.cli_set(base_path + ['neighbor', peer, 'remote-as', peer_config["remote_as"]])
if 'cap_strict' in peer_config:
self.cli_set(base_path + ['neighbor', peer, 'strict-capability-match'])
if 'shutdown' in peer_config:
self.cli_set(base_path + ['neighbor', peer, 'shutdown'])
if 'solo' in peer_config:
self.cli_set(base_path + ['neighbor', peer, 'solo'])
if 'ttl_security' in peer_config:
self.cli_set(base_path + ['neighbor', peer, 'ttl-security', 'hops', peer_config["ttl_security"]])
if 'update_src' in peer_config:
self.cli_set(base_path + ['neighbor', peer, 'update-source', peer_config["update_src"]])
if 'p_attr_discard' in peer_config:
for attribute in peer_config['p_attr_discard']:
self.cli_set(base_path + ['neighbor', peer, 'path-attribute', 'discard', attribute])
if 'p_attr_taw' in peer_config:
self.cli_set(base_path + ['neighbor', peer, 'path-attribute', 'treat-as-withdraw', peer_config["p_attr_taw"]])
if 'route_map_in' in peer_config:
self.cli_set(base_path + ['neighbor', peer, 'address-family', afi, 'route-map', 'import', peer_config["route_map_in"]])
if 'route_map_out' in peer_config:
self.cli_set(base_path + ['neighbor', peer, 'address-family', afi, 'route-map', 'export', peer_config["route_map_out"]])
if 'pfx_list_in' in peer_config:
self.cli_set(base_path + ['neighbor', peer, 'address-family', afi, 'prefix-list', 'import', peer_config["pfx_list_in"]])
if 'pfx_list_out' in peer_config:
self.cli_set(base_path + ['neighbor', peer, 'address-family', afi, 'prefix-list', 'export', peer_config["pfx_list_out"]])
if 'no_send_comm_std' in peer_config:
self.cli_set(base_path + ['neighbor', peer, 'address-family', afi, 'disable-send-community', 'standard'])
if 'no_send_comm_ext' in peer_config:
self.cli_set(base_path + ['neighbor', peer, 'address-family', afi, 'disable-send-community', 'extended'])
if 'addpath_all' in peer_config:
self.cli_set(base_path + ['neighbor', peer, 'address-family', afi, 'addpath-tx-all'])
if 'addpath_per_as' in peer_config:
self.cli_set(base_path + ['neighbor', peer, 'address-family', afi, 'addpath-tx-per-as'])
if 'graceful_rst' in peer_config:
self.cli_set(base_path + ['neighbor', peer, 'graceful-restart', 'enable'])
if 'graceful_rst_no' in peer_config:
self.cli_set(base_path + ['neighbor', peer, 'graceful-restart', 'disable'])
if 'graceful_rst_hlp' in peer_config:
self.cli_set(base_path + ['neighbor', peer, 'graceful-restart', 'restart-helper'])
if 'disable_conn_chk' in peer_config:
self.cli_set(base_path + ['neighbor', peer, 'disable-connected-check'])
# Conditional advertisement
if 'advertise_map' in peer_config:
self.cli_set(base_path + ['neighbor', peer, 'address-family', afi, 'conditionally-advertise', 'advertise-map', peer_config["advertise_map"]])
# Either exist-map or non-exist-map needs to be specified
if 'exist_map' not in peer_config and 'non_exist_map' not in peer_config:
with self.assertRaises(ConfigSessionError):
self.cli_commit()
self.cli_set(base_path + ['neighbor', peer, 'address-family', afi, 'conditionally-advertise', 'exist-map', route_map_in])
if 'exist_map' in peer_config:
self.cli_set(base_path + ['neighbor', peer, 'address-family', afi, 'conditionally-advertise', 'exist-map', peer_config["exist_map"]])
if 'non_exist_map' in peer_config:
self.cli_set(base_path + ['neighbor', peer, 'address-family', afi, 'conditionally-advertise', 'non-exist-map', peer_config["non_exist_map"]])
# commit changes
self.cli_commit()
# Verify FRR bgpd configuration
frrconfig = self.getFRRconfig(f'router bgp {ASN}', endsection='^exit')
self.assertIn(f'router bgp {ASN}', frrconfig)
for peer, peer_config in neighbor_config.items():
if 'adv_interv' in peer_config:
self.assertIn(f' neighbor {peer} advertisement-interval {peer_config["adv_interv"]}', frrconfig)
if 'cap_strict' in peer_config:
self.assertIn(f' neighbor {peer} strict-capability-match', frrconfig)
self.verify_frr_config(peer, peer_config, frrconfig)
def test_bgp_03_peer_groups(self):
# Test out individual peer-group configuration items
for peer_group, config in peer_group_config.items():
if 'bfd' in config:
self.cli_set(base_path + ['peer-group', peer_group, 'bfd'])
if 'bfd_profile' in config:
self.cli_set(base_path + ['peer-group', peer_group, 'bfd', 'profile', config["bfd_profile"]])
self.cli_set(base_path + ['peer-group', peer_group, 'bfd', 'check-control-plane-failure'])
if 'cap_dynamic' in config:
self.cli_set(base_path + ['peer-group', peer_group, 'capability', 'dynamic'])
if 'cap_ext_next' in config:
self.cli_set(base_path + ['peer-group', peer_group, 'capability', 'extended-nexthop'])
if 'cap_ext_sver' in config:
self.cli_set(base_path + ['peer-group', peer_group, 'capability', 'software-version'])
if 'description' in config:
self.cli_set(base_path + ['peer-group', peer_group, 'description', config["description"]])
if 'no_cap_nego' in config:
self.cli_set(base_path + ['peer-group', peer_group, 'disable-capability-negotiation'])
if 'multi_hop' in config:
self.cli_set(base_path + ['peer-group', peer_group, 'ebgp-multihop', config["multi_hop"]])
if 'local_as' in config:
self.cli_set(base_path + ['peer-group', peer_group, 'local-as', config["local_as"], 'no-prepend', 'replace-as'])
if 'local_role' in config:
self.cli_set(base_path + ['peer-group', peer_group, 'local-role', config["local_role"]])
if 'local_role_strict' in config:
self.cli_set(base_path + ['peer-group', peer_group, 'local-role', config["local_role"], 'strict'])
if 'cap_over' in config:
self.cli_set(base_path + ['peer-group', peer_group, 'override-capability'])
if 'passive' in config:
self.cli_set(base_path + ['peer-group', peer_group, 'passive'])
if 'password' in config:
self.cli_set(base_path + ['peer-group', peer_group, 'password', config["password"]])
if 'port' in config:
self.cli_set(base_path + ['peer-group', peer_group, 'port', config["port"]])
if 'remote_as' in config:
self.cli_set(base_path + ['peer-group', peer_group, 'remote-as', config["remote_as"]])
if 'solo' in config:
self.cli_set(base_path + ['peer-group', peer_group, 'solo'])
if 'shutdown' in config:
self.cli_set(base_path + ['peer-group', peer_group, 'shutdown'])
if 'ttl_security' in config:
self.cli_set(base_path + ['peer-group', peer_group, 'ttl-security', 'hops', config["ttl_security"]])
if 'update_src' in config:
self.cli_set(base_path + ['peer-group', peer_group, 'update-source', config["update_src"]])
if 'route_map_in' in config:
self.cli_set(base_path + ['peer-group', peer_group, 'address-family', 'ipv4-unicast', 'route-map', 'import', config["route_map_in"]])
if 'route_map_out' in config:
self.cli_set(base_path + ['peer-group', peer_group, 'address-family', 'ipv4-unicast', 'route-map', 'export', config["route_map_out"]])
if 'pfx_list_in' in config:
self.cli_set(base_path + ['peer-group', peer_group, 'address-family', 'ipv4-unicast', 'prefix-list', 'import', config["pfx_list_in"]])
if 'pfx_list_out' in config:
self.cli_set(base_path + ['peer-group', peer_group, 'address-family', 'ipv4-unicast', 'prefix-list', 'export', config["pfx_list_out"]])
if 'no_send_comm_std' in config:
self.cli_set(base_path + ['peer-group', peer_group, 'address-family', 'ipv4-unicast', 'disable-send-community', 'standard'])
if 'no_send_comm_ext' in config:
self.cli_set(base_path + ['peer-group', peer_group, 'address-family', 'ipv4-unicast', 'disable-send-community', 'extended'])
if 'addpath_all' in config:
self.cli_set(base_path + ['peer-group', peer_group, 'address-family', 'ipv4-unicast', 'addpath-tx-all'])
if 'addpath_per_as' in config:
self.cli_set(base_path + ['peer-group', peer_group, 'address-family', 'ipv4-unicast', 'addpath-tx-per-as'])
if 'graceful_rst' in config:
self.cli_set(base_path + ['peer-group', peer_group, 'graceful-restart', 'enable'])
if 'graceful_rst_no' in config:
self.cli_set(base_path + ['peer-group', peer_group, 'graceful-restart', 'disable'])
if 'graceful_rst_hlp' in config:
self.cli_set(base_path + ['peer-group', peer_group, 'graceful-restart', 'restart-helper'])
if 'disable_conn_chk' in config:
self.cli_set(base_path + ['peer-group', peer_group, 'disable-connected-check'])
if 'p_attr_discard' in config:
for attribute in config['p_attr_discard']:
self.cli_set(base_path + ['peer-group', peer_group, 'path-attribute', 'discard', attribute])
if 'p_attr_taw' in config:
self.cli_set(base_path + ['peer-group', peer_group, 'path-attribute', 'treat-as-withdraw', config["p_attr_taw"]])
# Conditional advertisement
if 'advertise_map' in config:
self.cli_set(base_path + ['peer-group', peer_group, 'address-family', 'ipv4-unicast', 'conditionally-advertise', 'advertise-map', config["advertise_map"]])
# Either exist-map or non-exist-map needs to be specified
if 'exist_map' not in config and 'non_exist_map' not in config:
with self.assertRaises(ConfigSessionError):
self.cli_commit()
self.cli_set(base_path + ['peer-group', peer_group, 'address-family', 'ipv4-unicast', 'conditionally-advertise', 'exist-map', route_map_in])
if 'exist_map' in config:
self.cli_set(base_path + ['peer-group', peer_group, 'address-family', 'ipv4-unicast', 'conditionally-advertise', 'exist-map', config["exist_map"]])
if 'non_exist_map' in config:
self.cli_set(base_path + ['peer-group', peer_group, 'address-family', 'ipv4-unicast', 'conditionally-advertise', 'non-exist-map', config["non_exist_map"]])
for peer, peer_config in neighbor_config.items():
if 'peer_group' in peer_config:
self.cli_set(base_path + ['neighbor', peer, 'peer-group', peer_config['peer_group']])
# commit changes
self.cli_commit()
# Verify FRR bgpd configuration
frrconfig = self.getFRRconfig(f'router bgp {ASN}', endsection='^exit')
self.assertIn(f'router bgp {ASN}', frrconfig)
for peer, peer_config in peer_group_config.items():
self.assertIn(f' neighbor {peer_group} peer-group', frrconfig)
self.verify_frr_config(peer, peer_config, frrconfig)
for peer, peer_config in neighbor_config.items():
if 'peer_group' in peer_config:
self.assertIn(f' neighbor {peer} peer-group {peer_config["peer_group"]}', frrconfig)
def test_bgp_04_afi_ipv4(self):
networks = {
'10.0.0.0/8' : {
'as_set' : '',
'summary_only' : '',
'route_map' : route_map_in,
},
'100.64.0.0/10' : {
'as_set' : '',
},
'192.168.0.0/16' : {
'summary_only' : '',
},
}
# We want to redistribute ...
redistributes = {
'babel' : {
'metric' : '100',
'route_map' : 'redistr-ipv4-babel',
},
'connected' : {
'metric' : '200',
'route_map' : 'redistr-ipv4-connected',
},
'isis' : {
'metric' : '300',
'route_map' : 'redistr-ipv4-isis',
},
'kernel' : {
'metric' : '400',
'route_map' : 'redistr-ipv4-kernel',
},
'nhrp': {
'metric': '400',
'route_map': 'redistr-ipv4-nhrp',
},
'ospf' : {
'metric' : '500',
'route_map' : 'redistr-ipv4-ospf',
},
'rip' : {
'metric' : '600',
'route_map' : 'redistr-ipv4-rip',
},
'static' : {
'metric' : '700',
'route_map' : 'redistr-ipv4-static',
},
'table' : {
'10' : {
'metric' : '810',
'route_map' : 'redistr-ipv4-table-10',
},
'20' : {
'metric' : '820',
'route_map' : 'redistr-ipv4-table-20',
},
'30' : {
'metric' : '830',
'route_map' : 'redistr-ipv4-table-30',
},
},
}
for proto, proto_config in redistributes.items():
proto_path = base_path + ['address-family', 'ipv4-unicast', 'redistribute', proto]
if proto == 'table':
for table, table_config in proto_config.items():
self.cli_set(proto_path + [table])
if 'metric' in table_config:
self.cli_set(proto_path + [table, 'metric'], value=table_config.get('metric'))
if 'route_map' in table_config:
self.cli_set(['policy', 'route-map', table_config.get('route_map'), 'rule', '10', 'action'], value='permit')
self.cli_set(proto_path + [table, 'route-map'], value=table_config.get('route_map'))
else:
self.cli_set(proto_path)
if 'metric' in proto_config:
self.cli_set(proto_path + ['metric', proto_config['metric']])
if 'route_map' in proto_config:
self.cli_set(['policy', 'route-map', proto_config['route_map'], 'rule', '10', 'action', 'permit'])
self.cli_set(proto_path + ['route-map', proto_config['route_map']])
for network, network_config in networks.items():
self.cli_set(base_path + ['address-family', 'ipv4-unicast',
'network', network])
if 'as_set' in network_config:
self.cli_set(base_path + ['address-family', 'ipv4-unicast',
'aggregate-address', network, 'as-set'])
if 'summary_only' in network_config:
self.cli_set(base_path + ['address-family', 'ipv4-unicast',
'aggregate-address', network, 'summary-only'])
if 'route_map' in network_config:
self.cli_set(base_path + ['address-family', 'ipv4-unicast',
'aggregate-address', network, 'route-map', network_config['route_map']])
# commit changes
self.cli_commit()
# Verify FRR bgpd configuration
frrconfig = self.getFRRconfig(f'router bgp {ASN}', endsection='^exit')
self.assertIn(f'router bgp {ASN}', frrconfig)
self.assertIn(' address-family ipv4 unicast', frrconfig)
for proto, proto_config in redistributes.items():
if proto == 'table':
for table, table_config in proto_config.items():
tmp = f' redistribute table-direct {table}'
if 'metric' in proto_config:
metric = proto_config['metric']
tmp += f' metric {metric}'
if 'route_map' in proto_config:
route_map = proto_config['route_map']
tmp += f' route-map {route_map}'
self.assertIn(tmp, frrconfig)
else:
tmp = f' redistribute {proto}'
if 'metric' in proto_config:
metric = proto_config['metric']
tmp += f' metric {metric}'
if 'route_map' in proto_config:
route_map = proto_config['route_map']
tmp += f' route-map {route_map}'
self.assertIn(tmp, frrconfig)
for network, network_config in networks.items():
self.assertIn(f' network {network}', frrconfig)
command = f'aggregate-address {network}'
if 'as_set' in network_config:
command = f'{command} as-set'
if 'summary_only' in network_config:
command = f'{command} summary-only'
if 'route_map' in network_config:
command = f'{command} route-map {network_config["route_map"]}'
self.assertIn(command, frrconfig)
for proto, proto_config in redistributes.items():
if 'route_map' in proto_config:
self.cli_delete(['policy', 'route-map', proto_config['route_map']])
def test_bgp_05_afi_ipv6(self):
networks = {
'2001:db8:100::/48' : {
},
'2001:db8:200::/48' : {
},
'2001:db8:300::/48' : {
'summary_only' : '',
},
}
# We want to redistribute ...
redistributes = {
'babel' : {
'metric' : '100',
'route_map' : 'redistr-ipv6-babel',
},
'connected' : {
'metric' : '200',
'route_map' : 'redistr-ipv6-connected',
},
'isis' : {
'metric' : '300',
'route_map' : 'redistr-ipv6-isis',
},
'kernel' : {
'metric' : '400',
'route_map' : 'redistr-ipv6-kernel',
},
'ospfv3' : {
'metric' : '500',
'route_map' : 'redistr-ipv6-ospfv3',
},
'ripng' : {
'metric' : '600',
'route_map' : 'redistr-ipv6-ripng',
},
'static' : {
'metric' : '700',
'route_map' : 'redistr-ipv6-static',
},
'table' : {
'110' : {
'metric' : '811',
'route_map' : 'redistr-ipv6-table-110',
},
'120' : {
'metric' : '821',
'route_map' : 'redistr-ipv6-table-120',
},
'130' : {
'metric' : '831',
'route_map' : 'redistr-ipv6-table-130',
},
},
}
for proto, proto_config in redistributes.items():
proto_path = base_path + ['address-family', 'ipv6-unicast', 'redistribute', proto]
if proto == 'table':
for table, table_config in proto_config.items():
self.cli_set(proto_path + [table])
if 'metric' in table_config:
self.cli_set(proto_path + [table, 'metric'], value=table_config.get('metric'))
if 'route_map' in table_config:
self.cli_set(['policy', 'route-map', table_config.get('route_map'), 'rule', '10', 'action'], value='permit')
self.cli_set(proto_path + [table, 'route-map'], value=table_config.get('route_map'))
else:
self.cli_set(proto_path)
if 'metric' in proto_config:
self.cli_set(proto_path + ['metric', proto_config['metric']])
if 'route_map' in proto_config:
self.cli_set(['policy', 'route-map', proto_config['route_map'], 'rule', '20', 'action', 'permit'])
self.cli_set(proto_path + ['route-map', proto_config['route_map']])
for network, network_config in networks.items():
self.cli_set(base_path + ['address-family', 'ipv6-unicast',
'network', network])
if 'summary_only' in network_config:
self.cli_set(base_path + ['address-family', 'ipv6-unicast',
'aggregate-address', network, 'summary-only'])
# commit changes
self.cli_commit()
# Verify FRR bgpd configuration
frrconfig = self.getFRRconfig(f'router bgp {ASN}', endsection='^exit')
self.assertIn(f'router bgp {ASN}', frrconfig)
self.assertIn(' address-family ipv6 unicast', frrconfig)
# T2100: By default ebgp-requires-policy is disabled to keep VyOS
# 1.3 and 1.2 backwards compatibility
self.assertIn(' no bgp ebgp-requires-policy', frrconfig)
for proto, proto_config in redistributes.items():
if proto == 'table':
for table, table_config in proto_config.items():
tmp = f' redistribute table-direct {table}'
if 'metric' in proto_config:
metric = proto_config['metric']
tmp += f' metric {metric}'
if 'route_map' in proto_config:
route_map = proto_config['route_map']
tmp += f' route-map {route_map}'
self.assertIn(tmp, frrconfig)
else:
# FRR calls this OSPF6
if proto == 'ospfv3':
proto = 'ospf6'
tmp = f' redistribute {proto}'
if 'metric' in proto_config:
metric = proto_config['metric']
tmp += f' metric {metric}'
if 'route_map' in proto_config:
route_map = proto_config['route_map']
tmp += f' route-map {route_map}'
self.assertIn(tmp, frrconfig)
for network, network_config in networks.items():
self.assertIn(f' network {network}', frrconfig)
if 'as_set' in network_config:
self.assertIn(f' aggregate-address {network} summary-only', frrconfig)
for proto, proto_config in redistributes.items():
if 'route_map' in proto_config:
self.cli_delete(['policy', 'route-map', proto_config['route_map']])
def test_bgp_06_listen_range(self):
# Implemented via T1875
limit = '64'
listen_ranges = ['192.0.2.0/25', '192.0.2.128/25']
peer_group = 'listenfoobar'
self.cli_set(base_path + ['listen', 'limit', limit])
for prefix in listen_ranges:
self.cli_set(base_path + ['listen', 'range', prefix])
# check validate() - peer-group must be defined for range/prefix
with self.assertRaises(ConfigSessionError):
self.cli_commit()
self.cli_set(base_path + ['listen', 'range', prefix, 'peer-group', peer_group])
# check validate() - peer-group does yet not exist!
with self.assertRaises(ConfigSessionError):
self.cli_commit()
self.cli_set(base_path + ['peer-group', peer_group, 'remote-as', ASN])
# commit changes
self.cli_commit()
# Verify FRR bgpd configuration
frrconfig = self.getFRRconfig(f'router bgp {ASN}', endsection='^exit')
self.assertIn(f'router bgp {ASN}', frrconfig)
self.assertIn(f' neighbor {peer_group} peer-group', frrconfig)
self.assertIn(f' neighbor {peer_group} remote-as {ASN}', frrconfig)
self.assertIn(f' bgp listen limit {limit}', frrconfig)
for prefix in listen_ranges:
self.assertIn(f' bgp listen range {prefix} peer-group {peer_group}', frrconfig)
def test_bgp_07_l2vpn_evpn(self):
vnis = ['10010', '10020', '10030']
soo = '1.2.3.4:10000'
evi_limit = '1000'
route_targets = ['1.1.1.1:100', '1.1.1.1:200', '1.1.1.1:300']
self.cli_set(base_path + ['address-family', 'l2vpn-evpn', 'advertise-all-vni'])
self.cli_set(base_path + ['address-family', 'l2vpn-evpn', 'advertise-default-gw'])
self.cli_set(base_path + ['address-family', 'l2vpn-evpn', 'advertise-svi-ip'])
self.cli_set(base_path + ['address-family', 'l2vpn-evpn', 'flooding', 'disable'])
self.cli_set(base_path + ['address-family', 'l2vpn-evpn', 'default-originate', 'ipv4'])
self.cli_set(base_path + ['address-family', 'l2vpn-evpn', 'default-originate', 'ipv6'])
self.cli_set(base_path + ['address-family', 'l2vpn-evpn', 'disable-ead-evi-rx'])
self.cli_set(base_path + ['address-family', 'l2vpn-evpn', 'disable-ead-evi-tx'])
self.cli_set(base_path + ['address-family', 'l2vpn-evpn', 'mac-vrf', 'soo', soo])
for vni in vnis:
self.cli_set(base_path + ['address-family', 'l2vpn-evpn', 'vni', vni, 'advertise-default-gw'])
self.cli_set(base_path + ['address-family', 'l2vpn-evpn', 'vni', vni, 'advertise-svi-ip'])
self.cli_set(base_path + ['address-family', 'l2vpn-evpn', 'ead-es-frag', 'evi-limit', evi_limit])
for route_target in route_targets:
self.cli_set(base_path + ['address-family', 'l2vpn-evpn', 'ead-es-route-target', 'export', route_target])
# commit changes
self.cli_commit()
# Verify FRR bgpd configuration
frrconfig = self.getFRRconfig(f'router bgp {ASN}', endsection='^exit')
self.assertIn(f'router bgp {ASN}', frrconfig)
self.assertIn(f' address-family l2vpn evpn', frrconfig)
self.assertIn(f' advertise-all-vni', frrconfig)
self.assertIn(f' advertise-default-gw', frrconfig)
self.assertIn(f' advertise-svi-ip', frrconfig)
self.assertIn(f' default-originate ipv4', frrconfig)
self.assertIn(f' default-originate ipv6', frrconfig)
self.assertIn(f' disable-ead-evi-rx', frrconfig)
self.assertIn(f' disable-ead-evi-tx', frrconfig)
self.assertIn(f' flooding disable', frrconfig)
self.assertIn(f' mac-vrf soo {soo}', frrconfig)
for vni in vnis:
vniconfig = self.getFRRconfig(f' vni {vni}', endsection='^ exit-vni')
self.assertIn(f'vni {vni}', vniconfig)
self.assertIn(f' advertise-default-gw', vniconfig)
self.assertIn(f' advertise-svi-ip', vniconfig)
self.assertIn(f' ead-es-frag evi-limit {evi_limit}', frrconfig)
for route_target in route_targets:
self.assertIn(f' ead-es-route-target export {route_target}', frrconfig)
def test_bgp_09_distance_and_flowspec(self):
distance_external = '25'
distance_internal = '30'
distance_local = '35'
distance_v4_prefix = '169.254.0.0/32'
distance_v6_prefix = '2001::/128'
distance_prefix_value = '110'
distance_families = ['ipv4-unicast', 'ipv6-unicast','ipv4-multicast', 'ipv6-multicast']
verify_families = ['ipv4 unicast', 'ipv6 unicast','ipv4 multicast', 'ipv6 multicast']
flowspec_families = ['address-family ipv4 flowspec', 'address-family ipv6 flowspec']
flowspec_int = 'lo'
# Per family distance support
for family in distance_families:
self.cli_set(base_path + ['address-family', family, 'distance', 'external', distance_external])
self.cli_set(base_path + ['address-family', family, 'distance', 'internal', distance_internal])
self.cli_set(base_path + ['address-family', family, 'distance', 'local', distance_local])
if 'ipv4' in family:
self.cli_set(base_path + ['address-family', family, 'distance',
'prefix', distance_v4_prefix, 'distance', distance_prefix_value])
if 'ipv6' in family:
self.cli_set(base_path + ['address-family', family, 'distance',
'prefix', distance_v6_prefix, 'distance', distance_prefix_value])
# IPv4 flowspec interface check
self.cli_set(base_path + ['address-family', 'ipv4-flowspec', 'local-install', 'interface', flowspec_int])
# IPv6 flowspec interface check
self.cli_set(base_path + ['address-family', 'ipv6-flowspec', 'local-install', 'interface', flowspec_int])
# Commit changes
self.cli_commit()
# Verify FRR distances configuration
frrconfig = self.getFRRconfig(f'router bgp {ASN}', endsection='^exit')
self.assertIn(f'router bgp {ASN}', frrconfig)
for family in verify_families:
self.assertIn(f'address-family {family}', frrconfig)
self.assertIn(f'distance bgp {distance_external} {distance_internal} {distance_local}', frrconfig)
if 'ipv4' in family:
self.assertIn(f'distance {distance_prefix_value} {distance_v4_prefix}', frrconfig)
if 'ipv6' in family:
self.assertIn(f'distance {distance_prefix_value} {distance_v6_prefix}', frrconfig)
# Verify FRR flowspec configuration
for family in flowspec_families:
self.assertIn(f'{family}', frrconfig)
self.assertIn(f'local-install {flowspec_int}', frrconfig)
def test_bgp_10_vrf_simple(self):
router_id = '127.0.0.3'
vrfs = ['red', 'green', 'blue']
# It is safe to assume that when the basic VRF test works, all
# other BGP related features work, as we entirely inherit the CLI
# templates and Jinja2 FRR template.
table = '1000'
# testing only one AFI is sufficient as it's generic code
for vrf in vrfs:
vrf_base = ['vrf', 'name', vrf]
self.cli_set(vrf_base + ['table', table])
self.cli_set(vrf_base + ['protocols', 'bgp', 'system-as', ASN])
self.cli_set(vrf_base + ['protocols', 'bgp', 'parameters', 'router-id', router_id])
table = str(int(table) + 1000)
# import VRF routes do main RIB
self.cli_set(base_path + ['address-family', 'ipv6-unicast', 'import', 'vrf', vrf])
self.cli_commit()
# Verify FRR bgpd configuration
frrconfig = self.getFRRconfig(f'router bgp {ASN}', endsection='^exit')
self.assertIn(f'router bgp {ASN}', frrconfig)
self.assertIn(f' address-family ipv6 unicast', frrconfig)
for vrf in vrfs:
self.assertIn(f' import vrf {vrf}', frrconfig)
# Verify FRR bgpd configuration
frr_vrf_config = self.getFRRconfig(f'router bgp {ASN} vrf {vrf}', endsection='^exit')
self.assertIn(f'router bgp {ASN} vrf {vrf}', frr_vrf_config)
self.assertIn(f' bgp router-id {router_id}', frr_vrf_config)
def test_bgp_11_confederation(self):
router_id = '127.10.10.2'
confed_id = str(int(ASN) + 1)
confed_asns = '10 20 30 40'
self.cli_set(base_path + ['parameters', 'router-id', router_id])
self.cli_set(base_path + ['parameters', 'confederation', 'identifier', confed_id])
for asn in confed_asns.split():
self.cli_set(base_path + ['parameters', 'confederation', 'peers', asn])
# commit changes
self.cli_commit()
# Verify FRR bgpd configuration
frrconfig = self.getFRRconfig(f'router bgp {ASN}', endsection='^exit')
self.assertIn(f'router bgp {ASN}', frrconfig)
self.assertIn(f' bgp router-id {router_id}', frrconfig)
self.assertIn(f' bgp confederation identifier {confed_id}', frrconfig)
self.assertIn(f' bgp confederation peers {confed_asns}', frrconfig)
def test_bgp_12_v6_link_local(self):
remote_asn = str(int(ASN) + 10)
interface = 'eth0'
self.cli_set(base_path + ['neighbor', interface, 'address-family', 'ipv6-unicast'])
self.cli_set(base_path + ['neighbor', interface, 'interface', 'v6only', 'remote-as', remote_asn])
# commit changes
self.cli_commit()
# Verify FRR bgpd configuration
frrconfig = self.getFRRconfig(f'router bgp {ASN}', endsection='^exit')
self.assertIn(f'router bgp {ASN}', frrconfig)
self.assertIn(f' neighbor {interface} interface v6only remote-as {remote_asn}', frrconfig)
self.assertIn(f' address-family ipv6 unicast', frrconfig)
self.assertIn(f' neighbor {interface} activate', frrconfig)
self.assertIn(f' exit-address-family', frrconfig)
def test_bgp_13_vpn(self):
remote_asn = str(int(ASN) + 150)
neighbor = '192.0.2.55'
vrf_name = 'red'
label = 'auto'
rd = f'{neighbor}:{ASN}'
rt_export = f'{neighbor}:1002 1.2.3.4:567'
rt_import = f'{neighbor}:1003 500:100'
# testing only one AFI is sufficient as it's generic code
for afi in ['ipv4-unicast', 'ipv6-unicast']:
self.cli_set(base_path + ['address-family', afi, 'export', 'vpn'])
self.cli_set(base_path + ['address-family', afi, 'import', 'vpn'])
self.cli_set(base_path + ['address-family', afi, 'label', 'vpn', 'export', label])
self.cli_set(base_path + ['address-family', afi, 'label', 'vpn', 'allocation-mode', 'per-nexthop'])
self.cli_set(base_path + ['address-family', afi, 'rd', 'vpn', 'export', rd])
self.cli_set(base_path + ['address-family', afi, 'route-map', 'vpn', 'export', route_map_out])
self.cli_set(base_path + ['address-family', afi, 'route-map', 'vpn', 'import', route_map_in])
self.cli_set(base_path + ['address-family', afi, 'route-target', 'vpn', 'export', rt_export])
self.cli_set(base_path + ['address-family', afi, 'route-target', 'vpn', 'import', rt_import])
# commit changes
self.cli_commit()
# Verify FRR bgpd configuration
frrconfig = self.getFRRconfig(f'router bgp {ASN}', endsection='^exit')
self.assertIn(f'router bgp {ASN}', frrconfig)
for afi in ['ipv4', 'ipv6']:
afi_config = self.getFRRconfig(f'router bgp {ASN}', endsection='^exit',
substring=f' address-family {afi} unicast',
endsubsection='^ exit-address-family')
self.assertIn(f'address-family {afi} unicast', afi_config)
self.assertIn(f' export vpn', afi_config)
self.assertIn(f' import vpn', afi_config)
self.assertIn(f' label vpn export {label}', afi_config)
self.assertIn(f' label vpn export allocation-mode per-nexthop', afi_config)
self.assertIn(f' rd vpn export {rd}', afi_config)
self.assertIn(f' route-map vpn export {route_map_out}', afi_config)
self.assertIn(f' route-map vpn import {route_map_in}', afi_config)
self.assertIn(f' rt vpn export {rt_export}', afi_config)
self.assertIn(f' rt vpn import {rt_import}', afi_config)
self.assertIn(f' exit-address-family', afi_config)
def test_bgp_14_remote_as_peer_group_override(self):
# Peer-group member cannot override remote-as of peer-group
remote_asn = str(int(ASN) + 150)
neighbor = '192.0.2.1'
peer_group = 'bar'
interface = 'eth0'
self.cli_set(base_path + ['neighbor', neighbor, 'remote-as', remote_asn])
self.cli_set(base_path + ['neighbor', neighbor, 'peer-group', peer_group])
self.cli_set(base_path + ['peer-group', peer_group, 'remote-as', remote_asn])
# Peer-group member cannot override remote-as of peer-group
with self.assertRaises(ConfigSessionError):
self.cli_commit()
self.cli_delete(base_path + ['neighbor', neighbor, 'remote-as'])
# re-test with interface based peer-group
self.cli_set(base_path + ['neighbor', interface, 'interface', 'peer-group', peer_group])
self.cli_set(base_path + ['neighbor', interface, 'interface', 'remote-as', 'external'])
with self.assertRaises(ConfigSessionError):
self.cli_commit()
self.cli_delete(base_path + ['neighbor', interface, 'interface', 'remote-as'])
# re-test with interface based v6only peer-group
self.cli_set(base_path + ['neighbor', interface, 'interface', 'v6only', 'peer-group', peer_group])
self.cli_set(base_path + ['neighbor', interface, 'interface', 'v6only', 'remote-as', 'external'])
with self.assertRaises(ConfigSessionError):
self.cli_commit()
self.cli_delete(base_path + ['neighbor', interface, 'interface', 'v6only', 'remote-as'])
self.cli_commit()
frrconfig = self.getFRRconfig(f'router bgp {ASN}', endsection='^exit')
self.assertIn(f'router bgp {ASN}', frrconfig)
self.assertIn(f' neighbor {neighbor} peer-group {peer_group}', frrconfig)
self.assertIn(f' neighbor {peer_group} peer-group', frrconfig)
self.assertIn(f' neighbor {peer_group} remote-as {remote_asn}', frrconfig)
def test_bgp_15_local_as_ebgp(self):
# https://vyos.dev/T4560
# local-as allowed only for ebgp peers
neighbor = '192.0.2.99'
remote_asn = '500'
local_asn = '400'
self.cli_set(base_path + ['neighbor', neighbor, 'remote-as', ASN])
self.cli_set(base_path + ['neighbor', neighbor, 'local-as', local_asn])
# check validate() - local-as allowed only for ebgp peers
with self.assertRaises(ConfigSessionError):
self.cli_commit()
self.cli_set(base_path + ['neighbor', neighbor, 'remote-as', remote_asn])
self.cli_commit()
frrconfig = self.getFRRconfig(f'router bgp {ASN}', endsection='^exit')
self.assertIn(f'router bgp {ASN}', frrconfig)
self.assertIn(f' neighbor {neighbor} remote-as {remote_asn}', frrconfig)
self.assertIn(f' neighbor {neighbor} local-as {local_asn}', frrconfig)
def test_bgp_16_import_rd_rt_compatibility(self):
# Verify if import vrf and rd vpn export
# exist in the same address family
self.create_bgp_instances_for_import_test()
self.cli_set(
base_path + ['address-family', import_afi, 'import', 'vrf',
import_vrf])
self.cli_set(
base_path + ['address-family', import_afi, 'rd', 'vpn', 'export',
import_rd])
with self.assertRaises(ConfigSessionError):
self.cli_commit()
def test_bgp_17_import_rd_rt_compatibility(self):
# Verify if vrf that is in import vrf list contains rd vpn export
self.create_bgp_instances_for_import_test()
self.cli_set(
base_path + ['address-family', import_afi, 'import', 'vrf',
import_vrf])
self.cli_commit()
frrconfig = self.getFRRconfig(f'router bgp {ASN}', endsection='^exit')
frrconfig_vrf = self.getFRRconfig(f'router bgp {ASN} vrf {import_vrf}', endsection='^exit')
self.assertIn(f'router bgp {ASN}', frrconfig)
self.assertIn(f'address-family ipv4 unicast', frrconfig)
self.assertIn(f' import vrf {import_vrf}', frrconfig)
self.assertIn(f'router bgp {ASN} vrf {import_vrf}', frrconfig_vrf)
self.cli_set(
import_vrf_base + [import_vrf] + base_path + ['address-family',
import_afi, 'rd',
'vpn', 'export',
import_rd])
with self.assertRaises(ConfigSessionError):
self.cli_commit()
def test_bgp_18_deleting_import_vrf(self):
# Verify deleting vrf that is in import vrf list
self.create_bgp_instances_for_import_test()
self.cli_set(
base_path + ['address-family', import_afi, 'import', 'vrf',
import_vrf])
self.cli_commit()
frrconfig = self.getFRRconfig(f'router bgp {ASN}', endsection='^exit')
frrconfig_vrf = self.getFRRconfig(f'router bgp {ASN} vrf {import_vrf}', endsection='^exit')
self.assertIn(f'router bgp {ASN}', frrconfig)
self.assertIn(f'address-family ipv4 unicast', frrconfig)
self.assertIn(f' import vrf {import_vrf}', frrconfig)
self.assertIn(f'router bgp {ASN} vrf {import_vrf}', frrconfig_vrf)
self.cli_delete(import_vrf_base + [import_vrf])
with self.assertRaises(ConfigSessionError):
self.cli_commit()
def test_bgp_19_deleting_default_vrf(self):
# Verify deleting existent vrf default if other vrfs were created
self.create_bgp_instances_for_import_test()
self.cli_commit()
frrconfig = self.getFRRconfig(f'router bgp {ASN}', endsection='^exit')
frrconfig_vrf = self.getFRRconfig(f'router bgp {ASN} vrf {import_vrf}', endsection='^exit')
self.assertIn(f'router bgp {ASN}', frrconfig)
self.assertIn(f'router bgp {ASN} vrf {import_vrf}', frrconfig_vrf)
self.cli_delete(base_path)
with self.assertRaises(ConfigSessionError):
self.cli_commit()
def test_bgp_20_import_rd_rt_compatibility(self):
# Verify if vrf that has rd vpn export is in import vrf of other vrfs
self.create_bgp_instances_for_import_test()
self.cli_set(
import_vrf_base + [import_vrf] + base_path + ['address-family',
import_afi, 'rd',
'vpn', 'export',
import_rd])
self.cli_commit()
frrconfig = self.getFRRconfig(f'router bgp {ASN}', endsection='^exit')
frrconfig_vrf = self.getFRRconfig(f'router bgp {ASN} vrf {import_vrf}', endsection='^exit')
self.assertIn(f'router bgp {ASN}', frrconfig)
self.assertIn(f'router bgp {ASN} vrf {import_vrf}', frrconfig_vrf)
self.assertIn(f'address-family ipv4 unicast', frrconfig_vrf)
self.assertIn(f' rd vpn export {import_rd}', frrconfig_vrf)
self.cli_set(
base_path + ['address-family', import_afi, 'import', 'vrf',
import_vrf])
with self.assertRaises(ConfigSessionError):
self.cli_commit()
def test_bgp_21_import_unspecified_vrf(self):
# Verify if vrf that is in import is unspecified
self.create_bgp_instances_for_import_test()
self.cli_set(
base_path + ['address-family', import_afi, 'import', 'vrf',
'test'])
with self.assertRaises(ConfigSessionError):
self.cli_commit()
def test_bgp_22_interface_mpls_forwarding(self):
interfaces = Section.interfaces('ethernet', vlan=False)
for interface in interfaces:
self.cli_set(base_path + ['interface', interface, 'mpls', 'forwarding'])
self.cli_commit()
for interface in interfaces:
frrconfig = self.getFRRconfig(f'interface {interface}', endsection='^exit')
self.assertIn(f'interface {interface}', frrconfig)
self.assertIn(f' mpls bgp forwarding', frrconfig)
def test_bgp_23_vrf_interface_mpls_forwarding(self):
self.create_bgp_instances_for_import_test()
interfaces = Section.interfaces('ethernet', vlan=False)
for interface in interfaces:
self.cli_set(['interfaces', 'ethernet', interface, 'vrf', import_vrf])
self.cli_set(import_vrf_base + [import_vrf] + base_path + ['interface', interface, 'mpls', 'forwarding'])
self.cli_commit()
for interface in interfaces:
frrconfig = self.getFRRconfig(f'interface {interface}', endsection='^exit')
self.assertIn(f'interface {interface}', frrconfig)
self.assertIn(f' mpls bgp forwarding', frrconfig)
self.cli_delete(['interfaces', 'ethernet', interface, 'vrf'])
def test_bgp_24_srv6_sid(self):
locator_name = 'VyOS_foo'
sid = 'auto'
nexthop_ipv4 = '192.0.0.1'
nexthop_ipv6 = '2001:db8:100:200::2'
self.cli_set(base_path + ['srv6', 'locator', locator_name])
self.cli_set(base_path + ['sid', 'vpn', 'per-vrf', 'export', sid])
self.cli_set(base_path + ['address-family', 'ipv4-unicast', 'sid', 'vpn', 'export', sid])
# verify() - SID per VRF and SID per address-family are mutually exclusive!
with self.assertRaises(ConfigSessionError):
self.cli_commit()
self.cli_delete(base_path + ['address-family', 'ipv4-unicast', 'sid'])
self.cli_commit()
frrconfig = self.getFRRconfig(f'router bgp {ASN}', endsection='^exit')
self.assertIn(f'router bgp {ASN}', frrconfig)
self.assertIn(f' segment-routing srv6', frrconfig)
self.assertIn(f' locator {locator_name}', frrconfig)
self.assertIn(f' sid vpn per-vrf export {sid}', frrconfig)
# Now test AFI SID
self.cli_delete(base_path + ['sid'])
self.cli_set(base_path + ['address-family', 'ipv4-unicast', 'sid', 'vpn', 'export', sid])
self.cli_set(base_path + ['address-family', 'ipv4-unicast', 'nexthop', 'vpn', 'export', nexthop_ipv4])
self.cli_set(base_path + ['address-family', 'ipv6-unicast', 'sid', 'vpn', 'export', sid])
self.cli_set(base_path + ['address-family', 'ipv6-unicast', 'nexthop', 'vpn', 'export', nexthop_ipv6])
self.cli_commit()
frrconfig = self.getFRRconfig(f'router bgp {ASN}', endsection='^exit')
self.assertIn(f'router bgp {ASN}', frrconfig)
self.assertIn(f' segment-routing srv6', frrconfig)
self.assertIn(f' locator {locator_name}', frrconfig)
afiv4_config = self.getFRRconfig(f'router bgp {ASN}', endsection='^exit',
substring=' address-family ipv4 unicast',
endsubsection='^ exit-address-family')
self.assertIn(f' sid vpn export {sid}', afiv4_config)
self.assertIn(f' nexthop vpn export {nexthop_ipv4}', afiv4_config)
afiv6_config = self.getFRRconfig(f'router bgp {ASN}', endsection='^exit',
substring=' address-family ipv6 unicast',
endsubsection='^ exit-address-family')
self.assertIn(f' sid vpn export {sid}', afiv6_config)
self.assertIn(f' nexthop vpn export {nexthop_ipv6}', afiv6_config)
def test_bgp_25_ipv4_labeled_unicast_peer_group(self):
pg_ipv4 = 'foo4'
ipv4_max_prefix = '20'
ipv4_prefix = '192.0.2.0/24'
self.cli_set(base_path + ['listen', 'range', ipv4_prefix, 'peer-group', pg_ipv4])
self.cli_set(base_path + ['parameters', 'labeled-unicast', 'ipv4-explicit-null'])
self.cli_set(base_path + ['peer-group', pg_ipv4, 'address-family', 'ipv4-labeled-unicast', 'maximum-prefix', ipv4_max_prefix])
self.cli_set(base_path + ['peer-group', pg_ipv4, 'remote-as', 'external'])
self.cli_commit()
frrconfig = self.getFRRconfig(f'router bgp {ASN}', endsection='^exit')
self.assertIn(f'router bgp {ASN}', frrconfig)
self.assertIn(f' neighbor {pg_ipv4} peer-group', frrconfig)
self.assertIn(f' neighbor {pg_ipv4} remote-as external', frrconfig)
self.assertIn(f' bgp listen range {ipv4_prefix} peer-group {pg_ipv4}', frrconfig)
self.assertIn(f' bgp labeled-unicast ipv4-explicit-null', frrconfig)
afiv4_config = self.getFRRconfig(f'router bgp {ASN}', endsection='^exit',
substring=' address-family ipv4 labeled-unicast',
endsubsection='^ exit-address-family')
self.assertIn(f' neighbor {pg_ipv4} activate', afiv4_config)
self.assertIn(f' neighbor {pg_ipv4} maximum-prefix {ipv4_max_prefix}', afiv4_config)
def test_bgp_26_ipv6_labeled_unicast_peer_group(self):
pg_ipv6 = 'foo6'
ipv6_max_prefix = '200'
ipv6_prefix = '2001:db8:1000::/64'
self.cli_set(base_path + ['listen', 'range', ipv6_prefix, 'peer-group', pg_ipv6])
self.cli_set(base_path + ['parameters', 'labeled-unicast', 'ipv6-explicit-null'])
self.cli_set(base_path + ['peer-group', pg_ipv6, 'address-family', 'ipv6-labeled-unicast', 'maximum-prefix', ipv6_max_prefix])
self.cli_set(base_path + ['peer-group', pg_ipv6, 'remote-as', 'external'])
self.cli_commit()
frrconfig = self.getFRRconfig(f'router bgp {ASN}', endsection='^exit')
self.assertIn(f'router bgp {ASN}', frrconfig)
self.assertIn(f' neighbor {pg_ipv6} peer-group', frrconfig)
self.assertIn(f' neighbor {pg_ipv6} remote-as external', frrconfig)
self.assertIn(f' bgp listen range {ipv6_prefix} peer-group {pg_ipv6}', frrconfig)
self.assertIn(f' bgp labeled-unicast ipv6-explicit-null', frrconfig)
afiv6_config = self.getFRRconfig(f'router bgp {ASN}', endsection='^exit',
substring=' address-family ipv6 labeled-unicast',
endsubsection='^ exit-address-family')
self.assertIn(f' neighbor {pg_ipv6} activate', afiv6_config)
self.assertIn(f' neighbor {pg_ipv6} maximum-prefix {ipv6_max_prefix}', afiv6_config)
def test_bgp_27_route_reflector_client(self):
int_neighbors = ['192.0.2.2', '192.0.2.3', '192.0.2.4', '192.0.2.5']
int_interfaces = ['dum0', 'dum1', 'dum2', 'dum3']
int_pg_names = ['SMOKETESTINT0', 'SMOKETESTINT1', 'SMOKETESTINT2']
remote_as_types = ['external', 'internal']
for int_interface in int_interfaces:
self.cli_set(['interfaces', 'dummy', int_interface])
self.cli_commit()
def _set_neighbor_0(neighbor, remote_as_type):
# set route-reflector-client in neighbor and set remote-as in peer_group
interface_cmd = ['interface'] if neighbor.startswith('dum') else []
self.cli_set(base_path + ['peer-group', int_pg_names[0], 'remote-as', remote_as_type])
self.cli_set(base_path + ['neighbor', neighbor, 'address-family', 'ipv4-unicast', 'route-reflector-client'])
self.cli_set(base_path + ['neighbor', neighbor] + interface_cmd + ['peer-group', int_pg_names[0]])
def _set_neighbor_1(neighbor, remote_as_type):
# set route-reflector-client in peer_group and set remote-as in neighbor
interface_cmd = ['interface'] if neighbor.startswith('dum') else []
self.cli_set(base_path + ['peer-group', int_pg_names[1], 'address-family', 'ipv4-unicast', 'route-reflector-client'])
self.cli_set(base_path + ['neighbor', neighbor] + interface_cmd + ['remote-as', remote_as_type])
self.cli_set(base_path + ['neighbor', neighbor] + interface_cmd + ['peer-group', int_pg_names[1]])
def _set_neighbor_2(neighbor, remote_as_type):
# set route-reflector-client and remote-as in peer_group
interface_cmd = ['interface'] if neighbor.startswith('dum') else []
self.cli_set(base_path + ['peer-group', int_pg_names[2], 'remote-as', remote_as_type])
self.cli_set(base_path + ['peer-group', int_pg_names[2], 'address-family', 'ipv4-unicast', 'route-reflector-client'])
self.cli_set(base_path + ['neighbor', neighbor] + interface_cmd + ['peer-group', int_pg_names[2]])
def _set_neighbor_3(neighbor, remote_as_type):
# set route-reflector-client and remote-as in neighbor
interface_cmd = ['interface'] if neighbor.startswith('dum') else []
self.cli_set(base_path + ['neighbor', neighbor, 'address-family', 'ipv4-unicast', 'route-reflector-client'])
self.cli_set(base_path + ['neighbor', neighbor] + interface_cmd + ['remote-as', remote_as_type])
set_neighbor_funcs = [_set_neighbor_0, _set_neighbor_1, _set_neighbor_2, _set_neighbor_3]
for remote_as_type in remote_as_types:
for func_count, set_neighbor_func in enumerate(set_neighbor_funcs):
for neighbors in [int_neighbors, int_interfaces]:
set_neighbor_func(neighbors[func_count], remote_as_type)
if remote_as_type == 'external':
with self.assertRaises(ConfigSessionError) as e:
self.cli_commit()
self.cli_discard()
else:
self.cli_commit()
frrconfig = self.getFRRconfig(f'router bgp {ASN}', endsection='^exit', substring=' address-family ipv4 unicast', endsubsection='^ exit-address-family')
neighbor_has_rr_client = [
int_neighbors[0], int_neighbors[3],
int_interfaces[0], int_interfaces[3],
int_pg_names[1], int_pg_names[2],
]
[self.assertIn(f'neighbor {neighbor} route-reflector-client', frrconfig) for neighbor in neighbor_has_rr_client]
# tearDown dummy interfaces
self.cli_delete(['interfaces', 'dummy'])
self.cli_commit()
def test_bgp_28_peer_group_member_all_internal_or_external(self):
def _common_config_check(conf, include_ras=True):
if include_ras:
self.assertIn(f'neighbor {int_neighbors[0]} remote-as {ASN}', conf)
self.assertIn(f'neighbor {int_neighbors[1]} remote-as {ASN}', conf)
self.assertIn(f'neighbor {ext_neighbors[0]} remote-as {int(ASN) + 1}',conf)
self.assertIn(f'neighbor {int_neighbors[0]} peer-group {int_pg_name}', conf)
self.assertIn(f'neighbor {int_neighbors[1]} peer-group {int_pg_name}', conf)
self.assertIn(f'neighbor {ext_neighbors[0]} peer-group {ext_pg_name}', conf)
int_neighbors = ['192.0.2.2', '192.0.2.3']
ext_neighbors = ['192.122.2.2', '192.122.2.3']
int_pg_name, ext_pg_name = 'SMOKETESTINT', 'SMOKETESTEXT'
self.cli_set(base_path + ['neighbor', int_neighbors[0], 'peer-group', int_pg_name])
self.cli_set(base_path + ['neighbor', int_neighbors[0], 'remote-as', ASN])
self.cli_set(base_path + ['peer-group', int_pg_name, 'address-family', 'ipv4-unicast'])
self.cli_set(base_path + ['neighbor', ext_neighbors[0], 'peer-group', ext_pg_name])
self.cli_set(base_path + ['neighbor', ext_neighbors[0], 'remote-as', f'{int(ASN) + 1}'])
self.cli_set(base_path + ['peer-group', ext_pg_name, 'address-family', 'ipv4-unicast'])
self.cli_commit()
# test add external remote-as to internal group
self.cli_set(base_path + ['neighbor', int_neighbors[1], 'peer-group', int_pg_name])
self.cli_set(base_path + ['neighbor', int_neighbors[1], 'remote-as', f'{int(ASN) + 1}'])
with self.assertRaises(ConfigSessionError) as e:
self.cli_commit()
# self.assertIn('\nPeer-group members must be all internal or all external\n', str(e.exception))
# test add internal remote-as to internal group
self.cli_set(base_path + ['neighbor', int_neighbors[1], 'remote-as', ASN])
self.cli_commit()
conf = self.getFRRconfig(f'router bgp {ASN}', endsection='^exit')
_common_config_check(conf)
# test add internal remote-as to external group
self.cli_set(base_path + ['neighbor', ext_neighbors[1], 'peer-group', ext_pg_name])
self.cli_set(base_path + ['neighbor', ext_neighbors[1], 'remote-as', ASN])
with self.assertRaises(ConfigSessionError) as e:
self.cli_commit()
# self.assertIn('\nPeer-group members must be all internal or all external\n', str(e.exception))
# test add external remote-as to external group
self.cli_set(base_path + ['neighbor', ext_neighbors[1], 'remote-as', f'{int(ASN) + 2}'])
self.cli_commit()
conf = self.getFRRconfig(f'router bgp {ASN}', endsection='^exit')
_common_config_check(conf)
self.assertIn(f'neighbor {ext_neighbors[1]} remote-as {int(ASN) + 2}', conf)
self.assertIn(f'neighbor {ext_neighbors[1]} peer-group {ext_pg_name}', conf)
# test named remote-as
self.cli_set(base_path + ['neighbor', int_neighbors[0], 'remote-as', 'internal'])
self.cli_set(base_path + ['neighbor', int_neighbors[1], 'remote-as', 'internal'])
self.cli_set(base_path + ['neighbor', ext_neighbors[0], 'remote-as', 'external'])
self.cli_set(base_path + ['neighbor', ext_neighbors[1], 'remote-as', 'external'])
self.cli_commit()
conf = self.getFRRconfig(f'router bgp {ASN}', endsection='^exit')
_common_config_check(conf, include_ras=False)
self.assertIn(f'neighbor {int_neighbors[0]} remote-as internal', conf)
self.assertIn(f'neighbor {int_neighbors[1]} remote-as internal', conf)
self.assertIn(f'neighbor {ext_neighbors[0]} remote-as external', conf)
self.assertIn(f'neighbor {ext_neighbors[1]} remote-as external', conf)
self.assertIn(f'neighbor {ext_neighbors[1]} peer-group {ext_pg_name}', conf)
def test_bgp_29_peer_group_remote_as_equal_local_as(self):
self.cli_set(base_path + ['system-as', ASN])
self.cli_set(base_path + ['peer-group', 'OVERLAY', 'local-as', f'{int(ASN) + 1}'])
self.cli_set(base_path + ['peer-group', 'OVERLAY', 'remote-as', f'{int(ASN) + 1}'])
self.cli_set(base_path + ['peer-group', 'OVERLAY', 'address-family', 'l2vpn-evpn'])
self.cli_set(base_path + ['peer-group', 'UNDERLAY', 'address-family', 'ipv4-unicast'])
self.cli_set(base_path + ['neighbor', '10.177.70.62', 'peer-group', 'UNDERLAY'])
self.cli_set(base_path + ['neighbor', '10.177.70.62', 'remote-as', 'external'])
self.cli_set(base_path + ['neighbor', '10.177.75.1', 'peer-group', 'OVERLAY'])
self.cli_set(base_path + ['neighbor', '10.177.75.2', 'peer-group', 'OVERLAY'])
self.cli_commit()
conf = self.getFRRconfig(f'router bgp {ASN}', endsection='^exit')
self.assertIn(f'neighbor OVERLAY remote-as {int(ASN) + 1}', conf)
self.assertIn(f'neighbor OVERLAY local-as {int(ASN) + 1}', conf)
def test_bgp_30_import_vrf_routemap(self):
router_id = '127.0.0.3'
table = '1000'
vrf = 'red'
vrf_base = ['vrf', 'name', vrf]
self.cli_set(vrf_base + ['table', table])
self.cli_set(vrf_base + ['protocols', 'bgp', 'system-as', ASN])
self.cli_set(
vrf_base + ['protocols', 'bgp', 'parameters', 'router-id',
router_id])
self.cli_set(
base_path + ['address-family', 'ipv4-unicast', 'import',
'vrf', vrf])
self.cli_set(
base_path + ['address-family', 'ipv4-unicast', 'route-map',
'vrf', 'import', route_map_in])
self.cli_commit()
# Verify FRR bgpd configuration
frrconfig = self.getFRRconfig(f'router bgp {ASN}',
endsection='^exit')
self.assertIn(f'router bgp {ASN}', frrconfig)
self.assertIn(f' address-family ipv4 unicast', frrconfig)
self.assertIn(f' import vrf {vrf}', frrconfig)
self.assertIn(f' import vrf route-map {route_map_in}', frrconfig)
# Verify FRR bgpd configuration
frr_vrf_config = self.getFRRconfig(
f'router bgp {ASN} vrf {vrf}', endsection='^exit')
self.assertIn(f'router bgp {ASN} vrf {vrf}', frr_vrf_config)
self.assertIn(f' bgp router-id {router_id}', frr_vrf_config)
def test_bgp_99_bmp(self):
target_name = 'instance-bmp'
target_address = '127.0.0.1'
target_port = '5000'
min_retry = '1024'
max_retry = '2048'
monitor_ipv4 = 'pre-policy'
monitor_ipv6 = 'pre-policy'
mirror_buffer = '32000000'
bmp_path = base_path + ['bmp']
target_path = bmp_path + ['target', target_name]
# by default the 'bmp' module not loaded for the bgpd expect Error
self.cli_set(bmp_path)
if not process_named_running('bgpd', 'bmp'):
with self.assertRaises(ConfigSessionError):
self.cli_commit()
# add required 'bmp' module to bgpd and restart bgpd
self.cli_delete(bmp_path)
self.cli_set(['system', 'frr', 'bmp'])
self.cli_commit()
# restart bgpd to apply "-M bmp" and update PID
cmd(f'sudo kill -9 {self.daemon_pid}')
# let the bgpd process recover
sleep(10)
# update daemon PID - this was a planned daemon restart
self.daemon_pid = process_named_running(bgp_daemon)
# set bmp config but not set address
self.cli_set(target_path + ['port', target_port])
# address is not set, expect Error
with self.assertRaises(ConfigSessionError):
self.cli_commit()
# config other bmp options
self.cli_set(target_path + ['address', target_address])
self.cli_set(bmp_path + ['mirror-buffer-limit', mirror_buffer])
self.cli_set(target_path + ['port', target_port])
self.cli_set(target_path + ['min-retry', min_retry])
self.cli_set(target_path + ['max-retry', max_retry])
self.cli_set(target_path + ['mirror'])
self.cli_set(target_path + ['monitor', 'ipv4-unicast', monitor_ipv4])
self.cli_set(target_path + ['monitor', 'ipv6-unicast', monitor_ipv6])
self.cli_commit()
# Verify bgpd bmp configuration
frrconfig = self.getFRRconfig(f'router bgp {ASN}', endsection='^exit')
self.assertIn(f'bmp mirror buffer-limit {mirror_buffer}', frrconfig)
self.assertIn(f'bmp targets {target_name}', frrconfig)
self.assertIn(f'bmp mirror', frrconfig)
self.assertIn(f'bmp monitor ipv4 unicast {monitor_ipv4}', frrconfig)
self.assertIn(f'bmp monitor ipv6 unicast {monitor_ipv6}', frrconfig)
self.assertIn(f'bmp connect {target_address} port {target_port} min-retry {min_retry} max-retry {max_retry}', frrconfig)
if __name__ == '__main__':
unittest.main(verbosity=2, failfast=VyOSUnitTestSHIM.TestCase.debug_on())
diff --git a/smoketest/scripts/cli/test_protocols_isis.py b/smoketest/scripts/cli/test_protocols_isis.py
index b755b8b0f..7359b5cc0 100755
--- a/smoketest/scripts/cli/test_protocols_isis.py
+++ b/smoketest/scripts/cli/test_protocols_isis.py
@@ -1,481 +1,478 @@
#!/usr/bin/env python3
#
# Copyright VyOS maintainers and contributors <maintainers@vyos.io>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
-from base_vyostest_shim import CSTORE_GUARD_TIME
from vyos.configsession import ConfigSessionError
from vyos.ifconfig import Section
from vyos.utils.process import process_named_running
from vyos.frrender import isis_daemon
base_path = ['protocols', 'isis']
domain = 'VyOS'
net = '49.0001.1921.6800.1002.00'
class TestProtocolsISIS(VyOSUnitTestSHIM.TestCase):
@classmethod
def setUpClass(cls):
cls._interfaces = Section.interfaces('ethernet')
# call base-classes classmethod
super(TestProtocolsISIS, cls).setUpClass()
# Retrieve FRR daemon PID - it is not allowed to crash, thus PID must remain the same
cls.daemon_pid = process_named_running(isis_daemon)
# ensure we can also run this test on a live system - so lets clean
# out the current configuration :)
cls.cli_delete(cls, base_path)
cls.cli_delete(cls, ['vrf'])
- # Enable CSTORE guard time required by FRR related tests
- cls._commit_guard_time = CSTORE_GUARD_TIME
def tearDown(self):
# cleanup any possible VRF mess
self.cli_delete(['vrf'])
# always destrox the entire isisd configuration to make the processes
# life as hard as possible
self.cli_delete(base_path)
self.cli_commit()
# check process health and continuity
self.assertEqual(self.daemon_pid, process_named_running(isis_daemon))
def test_isis_01_redistribute(self):
prefix_list = 'EXPORT-ISIS'
route_map = 'EXPORT-ISIS'
rule = '10'
metric_style = 'transition'
redistribute = ['babel', 'bgp', 'connected', 'kernel', 'nhrp', 'ospf', 'rip', 'static']
self.cli_set(['policy', 'prefix-list', prefix_list, 'rule', rule, 'action', 'permit'])
self.cli_set(['policy', 'prefix-list', prefix_list, 'rule', rule, 'prefix', '203.0.113.0/24'])
self.cli_set(['policy', 'route-map', route_map, 'rule', rule, 'action', 'permit'])
self.cli_set(['policy', 'route-map', route_map, 'rule', rule, 'match', 'ip', 'address', 'prefix-list', prefix_list])
self.cli_set(base_path)
# verify() - net id and interface are mandatory
with self.assertRaises(ConfigSessionError):
self.cli_commit()
self.cli_set(base_path + ['net', net])
for interface in self._interfaces:
self.cli_set(base_path + ['interface', interface])
self.cli_set(base_path + ['redistribute', 'ipv4', 'connected'])
# verify() - Redistribute level-1 or level-2 should be specified
with self.assertRaises(ConfigSessionError):
self.cli_commit()
for proto in redistribute:
self.cli_set(base_path + ['redistribute', 'ipv4', proto, 'level-2', 'route-map', route_map])
self.cli_set(base_path + ['metric-style', metric_style])
self.cli_set(base_path + ['log-adjacency-changes'])
# Commit all changes
self.cli_commit()
# Verify all changes
tmp = self.getFRRconfig(f'router isis {domain}', endsection='^exit')
self.assertIn(f' net {net}', tmp)
self.assertIn(f' metric-style {metric_style}', tmp)
self.assertIn(f' log-adjacency-changes', tmp)
for proto in redistribute:
self.assertIn(f' redistribute ipv4 {proto} level-2 route-map {route_map}', tmp)
for interface in self._interfaces:
tmp = self.getFRRconfig(f'interface {interface}', endsection='^exit')
self.assertIn(f' ip router isis {domain}', tmp)
self.assertIn(f' ipv6 router isis {domain}', tmp)
self.cli_delete(['policy', 'route-map', route_map])
self.cli_delete(['policy', 'prefix-list', prefix_list])
def test_isis_02_vrfs(self):
vrfs = ['red', 'green', 'blue']
# It is safe to assume that when the basic VRF test works, all other
# IS-IS related features work, as we entirely inherit the CLI templates
# and Jinja2 FRR template.
table = '1000'
vrf = 'red'
vrf_base = ['vrf', 'name', vrf]
vrf_iface = 'eth1'
self.cli_set(vrf_base + ['table', table])
self.cli_set(vrf_base + ['protocols', 'isis', 'net', net])
self.cli_set(vrf_base + ['protocols', 'isis', 'interface', vrf_iface])
self.cli_set(vrf_base + ['protocols', 'isis', 'advertise-high-metrics'])
self.cli_set(vrf_base + ['protocols', 'isis', 'advertise-passive-only'])
self.cli_set(['interfaces', 'ethernet', vrf_iface, 'vrf', vrf])
# Also set a default VRF IS-IS config
self.cli_set(base_path + ['net', net])
self.cli_set(base_path + ['interface', 'eth0'])
self.cli_commit()
# Verify FRR isisd configuration
tmp = self.getFRRconfig(f'router isis {domain}', endsection='^exit')
self.assertIn(f'router isis {domain}', tmp)
self.assertIn(f' net {net}', tmp)
tmp = self.getFRRconfig(f'router isis {domain} vrf {vrf}', endsection='^exit')
self.assertIn(f'router isis {domain} vrf {vrf}', tmp)
self.assertIn(f' net {net}', tmp)
self.assertIn(f' advertise-high-metrics', tmp)
self.assertIn(f' advertise-passive-only', tmp)
self.cli_delete(['vrf', 'name', vrf])
self.cli_delete(['interfaces', 'ethernet', vrf_iface, 'vrf'])
def test_isis_04_default_information(self):
metric = '50'
route_map = 'default-foo-'
self.cli_set(base_path + ['net', net])
for interface in self._interfaces:
self.cli_set(base_path + ['interface', interface])
for afi in ['ipv4', 'ipv6']:
for level in ['level-1', 'level-2']:
self.cli_set(base_path + ['default-information', 'originate', afi, level, 'always'])
self.cli_set(base_path + ['default-information', 'originate', afi, level, 'metric', metric])
self.cli_set(base_path + ['default-information', 'originate', afi, level, 'route-map', route_map + level + afi])
# Commit all changes
self.cli_commit()
# Verify all changes
tmp = self.getFRRconfig(f'router isis {domain}', endsection='^exit')
self.assertIn(f' net {net}', tmp)
for afi in ['ipv4', 'ipv6']:
for level in ['level-1', 'level-2']:
route_map_name = route_map + level + afi
self.assertIn(f' default-information originate {afi} {level} always route-map {route_map_name} metric {metric}', tmp)
def test_isis_05_password(self):
password = 'foo'
self.cli_set(base_path + ['net', net])
for interface in self._interfaces:
self.cli_set(base_path + ['interface', interface, 'password', 'plaintext-password', f'{password}-{interface}'])
self.cli_set(base_path + ['area-password', 'plaintext-password', password])
self.cli_set(base_path + ['area-password', 'md5', password])
self.cli_set(base_path + ['domain-password', 'plaintext-password', password])
self.cli_set(base_path + ['domain-password', 'md5', password])
# verify() - can not use both md5 and plaintext-password for area-password
with self.assertRaises(ConfigSessionError):
self.cli_commit()
self.cli_delete(base_path + ['area-password', 'md5', password])
# verify() - can not use both md5 and plaintext-password for domain-password
with self.assertRaises(ConfigSessionError):
self.cli_commit()
self.cli_delete(base_path + ['domain-password', 'md5', password])
# Commit all changes
self.cli_commit()
# Verify all changes
tmp = self.getFRRconfig(f'router isis {domain}', endsection='exit')
self.assertIn(f' net {net}', tmp)
self.assertIn(f' domain-password clear {password}', tmp)
self.assertIn(f' area-password clear {password}', tmp)
for interface in self._interfaces:
tmp = self.getFRRconfig(f'interface {interface}', endsection='^exit')
self.assertIn(f' isis password clear {password}-{interface}', tmp)
def test_isis_06_spf_delay_bfd(self):
network = 'point-to-point'
holddown = '10'
init_delay = '50'
long_delay = '200'
short_delay = '100'
time_to_learn = '75'
bfd_profile = 'isis-bfd'
self.cli_set(base_path + ['net', net])
for interface in self._interfaces:
self.cli_set(base_path + ['interface', interface, 'network', network])
self.cli_set(base_path + ['interface', interface, 'bfd', 'profile', bfd_profile])
self.cli_set(base_path + ['spf-delay-ietf', 'holddown', holddown])
# verify() - All types of spf-delay must be configured
with self.assertRaises(ConfigSessionError):
self.cli_commit()
self.cli_set(base_path + ['spf-delay-ietf', 'init-delay', init_delay])
# verify() - All types of spf-delay must be configured
with self.assertRaises(ConfigSessionError):
self.cli_commit()
self.cli_set(base_path + ['spf-delay-ietf', 'long-delay', long_delay])
# verify() - All types of spf-delay must be configured
with self.assertRaises(ConfigSessionError):
self.cli_commit()
self.cli_set(base_path + ['spf-delay-ietf', 'short-delay', short_delay])
# verify() - All types of spf-delay must be configured
with self.assertRaises(ConfigSessionError):
self.cli_commit()
self.cli_set(base_path + ['spf-delay-ietf', 'time-to-learn', time_to_learn])
# Commit all changes
self.cli_commit()
# Verify all changes
tmp = self.getFRRconfig(f'router isis {domain}', endsection='^exit')
self.assertIn(f' net {net}', tmp)
self.assertIn(f' spf-delay-ietf init-delay {init_delay} short-delay {short_delay} long-delay {long_delay} holddown {holddown} time-to-learn {time_to_learn}', tmp)
for interface in self._interfaces:
tmp = self.getFRRconfig(f'interface {interface}', endsection='^exit')
self.assertIn(f' ip router isis {domain}', tmp)
self.assertIn(f' ipv6 router isis {domain}', tmp)
self.assertIn(f' isis network {network}', tmp)
self.assertIn(f' isis bfd', tmp)
self.assertIn(f' isis bfd profile {bfd_profile}', tmp)
def test_isis_07_segment_routing_configuration(self):
global_block_low = "300"
global_block_high = "399"
local_block_low = "400"
local_block_high = "499"
maximum_stack_size = '5'
prefix_one = '192.168.0.1/32'
prefix_two = '192.168.0.2/32'
prefix_three = '192.168.0.3/32'
prefix_four = '192.168.0.4/32'
prefix_one_value = '1'
prefix_two_value = '2'
prefix_three_value = '60000'
prefix_four_value = '65000'
self.cli_set(base_path + ['net', net])
for interface in self._interfaces:
self.cli_set(base_path + ['interface', interface])
self.cli_set(base_path + ['segment-routing', 'maximum-label-depth', maximum_stack_size])
self.cli_set(base_path + ['segment-routing', 'global-block', 'low-label-value', global_block_low])
self.cli_set(base_path + ['segment-routing', 'global-block', 'high-label-value', global_block_high])
self.cli_set(base_path + ['segment-routing', 'local-block', 'low-label-value', local_block_low])
self.cli_set(base_path + ['segment-routing', 'local-block', 'high-label-value', local_block_high])
self.cli_set(base_path + ['segment-routing', 'prefix', prefix_one, 'index', 'value', prefix_one_value])
self.cli_set(base_path + ['segment-routing', 'prefix', prefix_one, 'index', 'explicit-null'])
self.cli_set(base_path + ['segment-routing', 'prefix', prefix_two, 'index', 'value', prefix_two_value])
self.cli_set(base_path + ['segment-routing', 'prefix', prefix_two, 'index', 'no-php-flag'])
self.cli_set(base_path + ['segment-routing', 'prefix', prefix_three, 'absolute', 'value', prefix_three_value])
self.cli_set(base_path + ['segment-routing', 'prefix', prefix_three, 'absolute', 'explicit-null'])
self.cli_set(base_path + ['segment-routing', 'prefix', prefix_four, 'absolute', 'value', prefix_four_value])
self.cli_set(base_path + ['segment-routing', 'prefix', prefix_four, 'absolute', 'no-php-flag'])
# Commit all changes
self.cli_commit()
# Verify all changes
tmp = self.getFRRconfig(f'router isis {domain}', endsection='^exit')
self.assertIn(f' net {net}', tmp)
self.assertIn(f' segment-routing on', tmp)
self.assertIn(f' segment-routing global-block {global_block_low} {global_block_high} local-block {local_block_low} {local_block_high}', tmp)
self.assertIn(f' segment-routing node-msd {maximum_stack_size}', tmp)
self.assertIn(f' segment-routing prefix {prefix_one} index {prefix_one_value} explicit-null', tmp)
self.assertIn(f' segment-routing prefix {prefix_two} index {prefix_two_value} no-php-flag', tmp)
self.assertIn(f' segment-routing prefix {prefix_three} absolute {prefix_three_value} explicit-null', tmp)
self.assertIn(f' segment-routing prefix {prefix_four} absolute {prefix_four_value} no-php-flag', tmp)
def test_isis_08_ldp_sync(self):
holddown = "500"
interface = 'lo'
self.cli_set(base_path + ['net', net])
self.cli_set(base_path + ['interface', interface])
self.cli_set(base_path + ['ldp-sync', 'holddown', holddown])
# Commit main ISIS changes
self.cli_commit()
# Verify main ISIS changes
tmp = self.getFRRconfig(f'router isis {domain}', endsection='^exit')
self.assertIn(f' net {net}', tmp)
self.assertIn(f' mpls ldp-sync', tmp)
self.assertIn(f' mpls ldp-sync holddown {holddown}', tmp)
for interface in self._interfaces:
self.cli_set(base_path + ['interface', interface, 'ldp-sync', 'holddown', holddown])
# Commit interface changes for holddown
self.cli_commit()
for interface in self._interfaces:
# Verify interface changes for holddown
tmp = self.getFRRconfig(f'interface {interface}', endsection='^exit')
self.assertIn(f'interface {interface}', tmp)
self.assertIn(f' ip router isis {domain}', tmp)
self.assertIn(f' ipv6 router isis {domain}', tmp)
self.assertIn(f' isis mpls ldp-sync holddown {holddown}', tmp)
for interface in self._interfaces:
self.cli_set(base_path + ['interface', interface, 'ldp-sync', 'disable'])
# Commit interface changes for disable
self.cli_commit()
for interface in self._interfaces:
# Verify interface changes for disable
tmp = self.getFRRconfig(f'interface {interface}', endsection='^exit')
self.assertIn(f'interface {interface}', tmp)
self.assertIn(f' ip router isis {domain}', tmp)
self.assertIn(f' ipv6 router isis {domain}', tmp)
self.assertIn(f' no isis mpls ldp-sync', tmp)
def test_isis_09_lfa(self):
prefix_list = 'lfa-prefix-list-test-1'
prefix_list_address = '192.168.255.255/32'
interface = 'lo'
self.cli_set(base_path + ['net', net])
self.cli_set(base_path + ['interface', interface])
self.cli_set(['policy', 'prefix-list', prefix_list, 'rule', '1', 'action', 'permit'])
self.cli_set(['policy', 'prefix-list', prefix_list, 'rule', '1', 'prefix', prefix_list_address])
# Commit main ISIS changes
self.cli_commit()
# Add remote portion of LFA with prefix list with validation
for level in ['level-1', 'level-2']:
self.cli_set(base_path + ['fast-reroute', 'lfa', 'remote', 'prefix-list', prefix_list, level])
self.cli_commit()
tmp = self.getFRRconfig(f'router isis {domain}', endsection='^exit')
self.assertIn(f' net {net}', tmp)
self.assertIn(f' fast-reroute remote-lfa prefix-list {prefix_list} {level}', tmp)
self.cli_delete(base_path + ['fast-reroute'])
self.cli_commit()
# Add local portion of LFA load-sharing portion with validation
for level in ['level-1', 'level-2']:
self.cli_set(base_path + ['fast-reroute', 'lfa', 'local', 'load-sharing', 'disable', level])
self.cli_commit()
tmp = self.getFRRconfig(f'router isis {domain}', endsection='^exit')
self.assertIn(f' net {net}', tmp)
self.assertIn(f' fast-reroute load-sharing disable {level}', tmp)
self.cli_delete(base_path + ['fast-reroute'])
self.cli_commit()
# Add local portion of LFA priority-limit portion with validation
for priority in ['critical', 'high', 'medium']:
for level in ['level-1', 'level-2']:
self.cli_set(base_path + ['fast-reroute', 'lfa', 'local', 'priority-limit', priority, level])
self.cli_commit()
tmp = self.getFRRconfig(f'router isis {domain}', endsection='^exit')
self.assertIn(f' net {net}', tmp)
self.assertIn(f' fast-reroute priority-limit {priority} {level}', tmp)
self.cli_delete(base_path + ['fast-reroute'])
self.cli_commit()
# Add local portion of LFA tiebreaker portion with validation
index = '100'
for tiebreaker in ['downstream','lowest-backup-metric','node-protecting']:
for level in ['level-1', 'level-2']:
self.cli_set(base_path + ['fast-reroute', 'lfa', 'local', 'tiebreaker', tiebreaker, 'index', index, level])
self.cli_commit()
tmp = self.getFRRconfig(f'router isis {domain}', endsection='^exit')
self.assertIn(f' net {net}', tmp)
self.assertIn(f' fast-reroute lfa tiebreaker {tiebreaker} index {index} {level}', tmp)
self.cli_delete(base_path + ['fast-reroute'])
self.cli_commit()
# Clean up and remove prefix list
self.cli_delete(['policy', 'prefix-list', prefix_list])
self.cli_commit()
def test_isis_10_topology(self):
topologies = ['ipv4-multicast', 'ipv4-mgmt', 'ipv6-unicast', 'ipv6-multicast', 'ipv6-mgmt']
interface = 'lo'
# Set a basic IS-IS config
self.cli_set(base_path + ['net', net])
self.cli_set(base_path + ['interface', interface])
for topology in topologies:
self.cli_set(base_path + ['topology', topology])
self.cli_commit()
tmp = self.getFRRconfig(f'router isis {domain}', endsection='^exit')
self.assertIn(f' net {net}', tmp)
self.assertIn(f' topology {topology}', tmp)
def test_isis_11_srv6(self):
locator = "TEST"
interface = 'lo'
self.cli_set(base_path + ['net', net])
self.cli_set(base_path + ['interface', interface])
self.cli_set(base_path + ['segment-routing', 'srv6', 'locator', locator])
# Commit main ISIS changes
self.cli_commit()
# Verify main ISIS changes
tmp = self.getFRRconfig(f'router isis {domain}', endsection='^exit')
self.assertIn(f' net {net}', tmp)
self.assertIn(f' segment-routing srv6', tmp)
self.assertIn(f' locator {locator}', tmp)
# Commit for isis
self.cli_commit()
def test_isis_12_frr_interface_lfa_remotelfa(self):
interface = 'eth0'
rla_metric = '10'
frr_interface_base_path = base_path + ['interface', interface, 'fast-reroute']
self.cli_set(base_path + ['net', net])
self.cli_set(base_path + ['interface', interface])
self.cli_set(frr_interface_base_path + ['lfa', 'level-1', 'enable'])
self.cli_set(frr_interface_base_path + ['lfa', 'level-1', 'exclude',
'interface', interface])
self.cli_set(frr_interface_base_path + ['remote-lfa', 'level-1',
'maximum-metric', rla_metric])
self.cli_set(frr_interface_base_path + ['remote-lfa', 'level-1',
'tunnel', 'mpls-ldp'])
# Commit main ISIS changes
self.cli_commit()
# Verify interface ISIS changes
tmp = self.getFRRconfig(f'interface {interface}', endsection='^exit')
self.assertIn(f' isis fast-reroute lfa level-1', tmp)
self.assertIn(f' isis fast-reroute lfa level-1 exclude interface {interface}', tmp)
self.assertIn(f' isis fast-reroute remote-lfa maximum-metric {rla_metric} level-1', tmp)
self.assertIn(f' isis fast-reroute remote-lfa tunnel mpls-ldp level-1', tmp)
def test_isis_13_frr_interface_tilfa(self):
interface = 'eth0'
frr_interface_base_path = base_path + ['interface', interface, 'fast-reroute']
self.cli_set(base_path + ['net', net])
self.cli_set(base_path + ['interface', interface])
self.cli_set(frr_interface_base_path + ['ti-lfa', 'level-1', 'node-protection',
'link-fallback'])
# Commit main ISIS changes
self.cli_commit()
# Verify interface ISIS changes
tmp = self.getFRRconfig(f'interface {interface}', endsection='^exit')
self.assertIn(f' isis fast-reroute ti-lfa level-1 node-protection link-fallback', tmp)
if __name__ == '__main__':
unittest.main(verbosity=2, failfast=VyOSUnitTestSHIM.TestCase.debug_on())
diff --git a/smoketest/scripts/cli/test_protocols_mpls.py b/smoketest/scripts/cli/test_protocols_mpls.py
index 1818b6fa6..c97e542cb 100755
--- a/smoketest/scripts/cli/test_protocols_mpls.py
+++ b/smoketest/scripts/cli/test_protocols_mpls.py
@@ -1,194 +1,191 @@
#!/usr/bin/env python3
#
# Copyright VyOS maintainers and contributors <maintainers@vyos.io>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
-from base_vyostest_shim import CSTORE_GUARD_TIME
from vyos.configsession import ConfigSessionError
from vyos.ifconfig import Section
from vyos.frrender import ldpd_daemon
from vyos.utils.process import process_named_running
base_path = ['protocols', 'mpls', 'ldp']
peers = {
'192.0.2.10' : {
'intv_rx' : '500',
'intv_tx' : '600',
'multihop' : '',
'source_addr': '192.0.2.254',
},
'192.0.2.20' : {
'echo_mode' : '',
'intv_echo' : '100',
'intv_mult' : '100',
'intv_rx' : '222',
'intv_tx' : '333',
'passive' : '',
'shutdown' : '',
},
'2001:db8::a' : {
'source_addr': '2001:db8::1',
},
'2001:db8::b' : {
'source_addr': '2001:db8::1',
'multihop' : '',
},
}
profiles = {
'foo' : {
'echo_mode' : '',
'intv_echo' : '100',
'intv_mult' : '101',
'intv_rx' : '222',
'intv_tx' : '333',
'shutdown' : '',
},
'bar' : {
'intv_mult' : '102',
'intv_rx' : '444',
'passive' : '',
},
}
class TestProtocolsMPLS(VyOSUnitTestSHIM.TestCase):
@classmethod
def setUpClass(cls):
super(TestProtocolsMPLS, cls).setUpClass()
# Retrieve FRR daemon PID - it is not allowed to crash, thus PID must remain the same
cls.daemon_pid = process_named_running(ldpd_daemon)
# ensure we can also run this test on a live system - so lets clean
# out the current configuration :)
cls.cli_delete(cls, base_path)
- # Enable CSTORE guard time required by FRR related tests
- cls._commit_guard_time = CSTORE_GUARD_TIME
def tearDown(self):
self.cli_delete(base_path)
self.cli_commit()
# check process health and continuity
self.assertEqual(self.daemon_pid, process_named_running(ldpd_daemon))
def test_mpls_basic(self):
router_id = '1.2.3.4'
transport_ipv4_addr = '5.6.7.8'
interfaces = Section.interfaces('ethernet')
self.cli_set(base_path + ['router-id', router_id])
# At least one LDP interface must be configured
with self.assertRaises(ConfigSessionError):
self.cli_commit()
for interface in interfaces:
self.cli_set(base_path + ['interface', interface])
# LDP transport address missing
with self.assertRaises(ConfigSessionError):
self.cli_commit()
self.cli_set(base_path + ['discovery', 'transport-ipv4-address', transport_ipv4_addr])
# Commit changes
self.cli_commit()
# Validate configuration
frrconfig = self.getFRRconfig('mpls ldp', endsection='^exit')
self.assertIn(f'mpls ldp', frrconfig)
self.assertIn(f' router-id {router_id}', frrconfig)
# Validate AFI IPv4
afiv4_config = self.getFRRconfig('mpls ldp', endsection='^exit',
substring=' address-family ipv4',
endsubsection='^ exit-address-family')
self.assertIn(f' discovery transport-address {transport_ipv4_addr}', afiv4_config)
for interface in interfaces:
self.assertIn(f' interface {interface}', afiv4_config)
def test_02_mpls_disable_establish_hello(self):
router_id = '1.2.3.4'
transport_ipv4_addr = '5.6.7.8'
transport_ipv6_addr = '2001:db8:1111::1111'
interfaces = Section.interfaces('ethernet')
self.cli_set(base_path + ['router-id', router_id])
# At least one LDP interface must be configured
with self.assertRaises(ConfigSessionError):
self.cli_commit()
for interface in interfaces:
self.cli_set(base_path + ['interface', interface, 'disable-establish-hello'])
# LDP transport address missing
with self.assertRaises(ConfigSessionError):
self.cli_commit()
self.cli_set(base_path + ['discovery', 'transport-ipv4-address', transport_ipv4_addr])
self.cli_set(base_path + ['discovery', 'transport-ipv6-address', transport_ipv6_addr])
# Commit changes
self.cli_commit()
# Validate configuration
frrconfig = self.getFRRconfig('mpls ldp', endsection='^exit')
self.assertIn(f'mpls ldp', frrconfig)
self.assertIn(f' router-id {router_id}', frrconfig)
# Validate AFI IPv4
afiv4_config = self.getFRRconfig('mpls ldp', endsection='^exit',
substring=' address-family ipv4',
endsubsection='^ exit-address-family')
self.assertIn(f' discovery transport-address {transport_ipv4_addr}', afiv4_config)
for interface in interfaces:
self.assertIn(f' interface {interface}', afiv4_config)
self.assertIn(f' disable-establish-hello', afiv4_config)
# Validate AFI IPv6
afiv6_config = self.getFRRconfig('mpls ldp', endsection='^exit',
substring=' address-family ipv6',
endsubsection='^ exit-address-family')
self.assertIn(f' discovery transport-address {transport_ipv6_addr}', afiv6_config)
for interface in interfaces:
self.assertIn(f' interface {interface}', afiv6_config)
self.assertIn(f' disable-establish-hello', afiv6_config)
# Delete disable-establish-hello
for interface in interfaces:
self.cli_delete(base_path + ['interface', interface, 'disable-establish-hello'])
# Commit changes
self.cli_commit()
# Validate AFI IPv4
afiv4_config = self.getFRRconfig('mpls ldp', endsection='^exit',
substring=' address-family ipv4',
endsubsection='^ exit-address-family')
# Validate AFI IPv6
afiv6_config = self.getFRRconfig('mpls ldp', endsection='^exit',
substring=' address-family ipv6',
endsubsection='^ exit-address-family')
# Check deleted 'disable-establish-hello' option per interface
for interface in interfaces:
self.assertIn(f' interface {interface}', afiv4_config)
self.assertNotIn(f' disable-establish-hello', afiv4_config)
self.assertIn(f' interface {interface}', afiv6_config)
self.assertNotIn(f' disable-establish-hello', afiv6_config)
if __name__ == '__main__':
unittest.main(verbosity=2, failfast=VyOSUnitTestSHIM.TestCase.debug_on())
diff --git a/smoketest/scripts/cli/test_protocols_openfabric.py b/smoketest/scripts/cli/test_protocols_openfabric.py
index 8cb131e1b..4ff6a42e1 100644
--- a/smoketest/scripts/cli/test_protocols_openfabric.py
+++ b/smoketest/scripts/cli/test_protocols_openfabric.py
@@ -1,189 +1,186 @@
#!/usr/bin/env python3
#
# Copyright VyOS maintainers and contributors <maintainers@vyos.io>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
-from base_vyostest_shim import CSTORE_GUARD_TIME
from vyos.configsession import ConfigSessionError
from vyos.utils.process import process_named_running
from vyos.frrender import openfabric_daemon
base_path = ['protocols', 'openfabric']
domain = 'VyOS'
net = '49.0001.1111.1111.1111.00'
dummy_if = 'dum1234'
address_families = ['ipv4', 'ipv6']
path = base_path + ['domain', domain]
class TestProtocolsOpenFabric(VyOSUnitTestSHIM.TestCase):
@classmethod
def setUpClass(cls):
# call base-classes classmethod
super(TestProtocolsOpenFabric, cls).setUpClass()
# Retrieve FRR daemon PID - it is not allowed to crash, thus PID must remain the same
cls.daemon_pid = process_named_running(openfabric_daemon)
# ensure we can also run this test on a live system - so lets clean
# out the current configuration :)
cls.cli_delete(cls, base_path)
- # Enable CSTORE guard time required by FRR related tests
- cls._commit_guard_time = CSTORE_GUARD_TIME
def tearDown(self):
self.cli_delete(base_path)
self.cli_commit()
# check process health and continuity
self.assertEqual(self.daemon_pid, process_named_running(openfabric_daemon))
def openfabric_base_config(self):
self.cli_set(['interfaces', 'dummy', dummy_if])
self.cli_set(base_path + ['net', net])
for family in address_families:
self.cli_set(path + ['interface', dummy_if, 'address-family', family])
def test_openfabric_01_router_params(self):
fabric_tier = '5'
lsp_gen_interval = '20'
self.cli_set(base_path)
# verify() - net id and domain name are mandatory
with self.assertRaises(ConfigSessionError):
self.cli_commit()
self.openfabric_base_config()
self.cli_set(path + ['log-adjacency-changes'])
self.cli_set(path + ['set-overload-bit'])
self.cli_set(path + ['fabric-tier', fabric_tier])
self.cli_set(path + ['lsp-gen-interval', lsp_gen_interval])
# Commit all changes
self.cli_commit()
# Verify all changes
tmp = self.getFRRconfig(f'router openfabric {domain}', endsection='^exit')
self.assertIn(f' net {net}', tmp)
self.assertIn(f' log-adjacency-changes', tmp)
self.assertIn(f' set-overload-bit', tmp)
self.assertIn(f' fabric-tier {fabric_tier}', tmp)
self.assertIn(f' lsp-gen-interval {lsp_gen_interval}', tmp)
tmp = self.getFRRconfig(f'interface {dummy_if}', endsection='^exit')
self.assertIn(f' ip router openfabric {domain}', tmp)
self.assertIn(f' ipv6 router openfabric {domain}', tmp)
def test_openfabric_02_loopback_interface(self):
interface = 'lo'
hello_interval = '100'
metric = '24478'
self.openfabric_base_config()
self.cli_set(path + ['interface', interface, 'address-family', 'ipv4'])
self.cli_set(path + ['interface', interface, 'hello-interval', hello_interval])
self.cli_set(path + ['interface', interface, 'metric', metric])
# Commit all changes
self.cli_commit()
# Verify FRR openfabric configuration
tmp = self.getFRRconfig(f'router openfabric {domain}', endsection='^exit')
self.assertIn(f'router openfabric {domain}', tmp)
self.assertIn(f' net {net}', tmp)
# Verify interface configuration
tmp = self.getFRRconfig(f'interface {interface}', endsection='^exit')
self.assertIn(f' ip router openfabric {domain}', tmp)
# for lo interface 'openfabric passive' is implied
self.assertIn(f' openfabric passive', tmp)
self.assertIn(f' openfabric metric {metric}', tmp)
def test_openfabric_03_password(self):
password = 'foo'
self.openfabric_base_config()
self.cli_set(path + ['interface', dummy_if, 'password', 'plaintext-password', f'{password}-{dummy_if}'])
self.cli_set(path + ['interface', dummy_if, 'password', 'md5', f'{password}-{dummy_if}'])
# verify() - can not use both md5 and plaintext-password for password for the interface
with self.assertRaises(ConfigSessionError):
self.cli_commit()
self.cli_delete(path + ['interface', dummy_if, 'password', 'md5'])
self.cli_set(path + ['domain-password', 'plaintext-password', password])
self.cli_set(path + ['domain-password', 'md5', password])
# verify() - can not use both md5 and plaintext-password for domain-password
with self.assertRaises(ConfigSessionError):
self.cli_commit()
self.cli_delete(path + ['domain-password', 'md5'])
# Commit all changes
self.cli_commit()
# Verify all changes
tmp = self.getFRRconfig(f'router openfabric {domain}', endsection='^exit')
self.assertIn(f' net {net}', tmp)
self.assertIn(f' domain-password clear {password}', tmp)
tmp = self.getFRRconfig(f'interface {dummy_if}', endsection='^exit')
self.assertIn(f' openfabric password clear {password}-{dummy_if}', tmp)
def test_openfabric_multiple_domains(self):
domain_2 = 'VyOS_2'
interface = 'dum5678'
new_path = base_path + ['domain', domain_2]
self.openfabric_base_config()
# set same interface for 2 OpenFabric domains
self.cli_set(['interfaces', 'dummy', interface])
self.cli_set(new_path + ['interface', interface, 'address-family', 'ipv4'])
self.cli_set(path + ['interface', interface, 'address-family', 'ipv4'])
# verify() - same interface can be used only for one OpenFabric instance
with self.assertRaises(ConfigSessionError):
self.cli_commit()
self.cli_delete(path + ['interface', interface])
# Commit all changes
self.cli_commit()
# Verify FRR openfabric configuration
tmp = self.getFRRconfig(f'router openfabric {domain}', endsection='^exit')
self.assertIn(f'router openfabric {domain}', tmp)
self.assertIn(f' net {net}', tmp)
tmp = self.getFRRconfig(f'router openfabric {domain_2}', endsection='^exit')
self.assertIn(f'router openfabric {domain_2}', tmp)
self.assertIn(f' net {net}', tmp)
# Verify interface configuration
tmp = self.getFRRconfig(f'interface {dummy_if}', endsection='^exit')
self.assertIn(f' ip router openfabric {domain}', tmp)
self.assertIn(f' ipv6 router openfabric {domain}', tmp)
tmp = self.getFRRconfig(f'interface {interface}', endsection='^exit')
self.assertIn(f' ip router openfabric {domain_2}', tmp)
if __name__ == '__main__':
unittest.main(verbosity=2, failfast=VyOSUnitTestSHIM.TestCase.debug_on())
diff --git a/smoketest/scripts/cli/test_protocols_ospf.py b/smoketest/scripts/cli/test_protocols_ospf.py
index c5ada032f..4ca98692e 100755
--- a/smoketest/scripts/cli/test_protocols_ospf.py
+++ b/smoketest/scripts/cli/test_protocols_ospf.py
@@ -1,617 +1,614 @@
#!/usr/bin/env python3
#
# Copyright VyOS maintainers and contributors <maintainers@vyos.io>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import unittest
from time import sleep
from base_vyostest_shim import VyOSUnitTestSHIM
-from base_vyostest_shim import CSTORE_GUARD_TIME
from vyos.configsession import ConfigSessionError
from vyos.ifconfig import Section
from vyos.frrender import ospf_daemon
from vyos.utils.process import process_named_running
from vyos.xml_ref import default_value
base_path = ['protocols', 'ospf']
route_map = 'foo-bar-baz10'
dummy_if = 'dum3562'
class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase):
@classmethod
def setUpClass(cls):
super(TestProtocolsOSPF, cls).setUpClass()
# Retrieve FRR daemon PID - it is not allowed to crash, thus PID must remain the same
cls.daemon_pid = process_named_running(ospf_daemon)
cls.cli_set(cls, ['policy', 'route-map', route_map, 'rule', '10', 'action', 'permit'])
cls.cli_set(cls, ['policy', 'route-map', route_map, 'rule', '20', 'action', 'permit'])
cls.cli_set(cls, ['interfaces', 'dummy', dummy_if])
# ensure we can also run this test on a live system - so lets clean
# out the current configuration :)
cls.cli_delete(cls, base_path)
- # Enable CSTORE guard time required by FRR related tests
- cls._commit_guard_time = CSTORE_GUARD_TIME
@classmethod
def tearDownClass(cls):
cls.cli_delete(cls, ['policy', 'route-map', route_map])
cls.cli_delete(cls, ['interfaces', 'dummy', dummy_if])
super(TestProtocolsOSPF, cls).tearDownClass()
def tearDown(self):
self.cli_delete(base_path)
self.cli_commit()
frrconfig = self.getFRRconfig('router ospf', endsection='^exit')
self.assertNotIn(f'router ospf', frrconfig)
# check process health and continuity
self.assertEqual(self.daemon_pid, process_named_running(ospf_daemon))
def test_ospf_01_defaults(self):
# commit changes
self.cli_set(base_path)
self.cli_commit()
# Verify FRR ospfd configuration
frrconfig = self.getFRRconfig('router ospf', endsection='^exit')
self.assertIn(f'router ospf', frrconfig)
self.assertIn(f' auto-cost reference-bandwidth 100', frrconfig)
self.assertIn(f' timers throttle spf 200 1000 10000', frrconfig) # defaults
def test_ospf_02_simple(self):
router_id = '127.0.0.1'
abr_type = 'ibm'
bandwidth = '1000'
metric = '123'
self.cli_set(base_path + ['auto-cost', 'reference-bandwidth', bandwidth])
self.cli_set(base_path + ['parameters', 'router-id', router_id])
self.cli_set(base_path + ['parameters', 'abr-type', abr_type])
self.cli_set(base_path + ['parameters', 'opaque-lsa'])
self.cli_set(base_path + ['parameters', 'rfc1583-compatibility'])
self.cli_set(base_path + ['log-adjacency-changes', 'detail'])
self.cli_set(base_path + ['default-metric', metric])
self.cli_set(base_path + ['passive-interface', 'default'])
self.cli_set(base_path + ['area', '10', 'area-type', 'stub'])
self.cli_set(base_path + ['area', '10', 'network', '10.0.0.0/16'])
self.cli_set(base_path + ['area', '10', 'range', '10.0.1.0/24'])
self.cli_set(base_path + ['area', '10', 'range', '10.0.2.0/24', 'not-advertise'])
# commit changes
self.cli_commit()
# Verify FRR ospfd configuration
frrconfig = self.getFRRconfig('router ospf', endsection='^exit')
self.assertIn(f'router ospf', frrconfig)
self.assertIn(f' compatible rfc1583', frrconfig)
self.assertIn(f' auto-cost reference-bandwidth {bandwidth}', frrconfig)
self.assertIn(f' ospf router-id {router_id}', frrconfig)
self.assertIn(f' ospf abr-type {abr_type}', frrconfig)
self.assertIn(f' timers throttle spf 200 1000 10000', frrconfig) # defaults
self.assertIn(f' capability opaque', frrconfig)
self.assertIn(f' default-metric {metric}', frrconfig)
self.assertIn(f' passive-interface default', frrconfig)
self.assertIn(f' area 10 stub', frrconfig)
self.assertIn(f' network 10.0.0.0/16 area 10', frrconfig)
self.assertIn(f' area 10 range 10.0.1.0/24', frrconfig)
self.assertNotIn(f' area 10 range 10.0.1.0/24 not-advertise', frrconfig)
self.assertIn(f' area 10 range 10.0.2.0/24 not-advertise', frrconfig)
def test_ospf_03_access_list(self):
acl = '100'
seq = '10'
protocols = ['bgp', 'connected', 'isis', 'kernel', 'rip', 'static']
self.cli_set(['policy', 'access-list', acl, 'rule', seq, 'action', 'permit'])
self.cli_set(['policy', 'access-list', acl, 'rule', seq, 'source', 'any'])
self.cli_set(['policy', 'access-list', acl, 'rule', seq, 'destination', 'any'])
for ptotocol in protocols:
self.cli_set(base_path + ['access-list', acl, 'export', ptotocol])
# commit changes
self.cli_commit()
# Verify FRR ospfd configuration
frrconfig = self.getFRRconfig('router ospf', endsection='^exit')
self.assertIn(f'router ospf', frrconfig)
self.assertIn(f' timers throttle spf 200 1000 10000', frrconfig) # defaults
for ptotocol in protocols:
self.assertIn(f' distribute-list {acl} out {ptotocol}', frrconfig) # defaults
self.cli_delete(['policy', 'access-list', acl])
def test_ospf_04_default_originate(self):
seq = '100'
metric = '50'
metric_type = '1'
self.cli_set(base_path + ['default-information', 'originate', 'metric', metric])
self.cli_set(base_path + ['default-information', 'originate', 'metric-type', metric_type])
self.cli_set(base_path + ['default-information', 'originate', 'route-map', route_map])
# commit changes
self.cli_commit()
# Verify FRR ospfd configuration
frrconfig = self.getFRRconfig('router ospf', endsection='^exit')
self.assertIn(f'router ospf', frrconfig)
self.assertIn(f' timers throttle spf 200 1000 10000', frrconfig) # defaults
self.assertIn(f' default-information originate metric {metric} metric-type {metric_type} route-map {route_map}', frrconfig)
# Now set 'always'
self.cli_set(base_path + ['default-information', 'originate', 'always'])
self.cli_commit()
# Verify FRR ospfd configuration
frrconfig = self.getFRRconfig('router ospf', endsection='^exit')
self.assertIn(f' default-information originate always metric {metric} metric-type {metric_type} route-map {route_map}', frrconfig)
def test_ospf_05_options(self):
global_distance = '128'
intra_area = '100'
inter_area = '110'
external = '120'
on_startup = '30'
on_shutdown = '60'
refresh = '50'
aggregation_timer = '100'
summary_nets = {
'10.0.1.0/24' : {},
'10.0.2.0/24' : {'tag' : '50'},
'10.0.3.0/24' : {'no_advertise' : {}},
}
self.cli_set(base_path + ['distance', 'global', global_distance])
self.cli_set(base_path + ['distance', 'ospf', 'external', external])
self.cli_set(base_path + ['distance', 'ospf', 'intra-area', intra_area])
self.cli_set(base_path + ['max-metric', 'router-lsa', 'on-startup', on_startup])
self.cli_set(base_path + ['max-metric', 'router-lsa', 'on-shutdown', on_shutdown])
self.cli_set(base_path + ['mpls-te', 'enable'])
self.cli_set(base_path + ['refresh', 'timers', refresh])
self.cli_set(base_path + ['aggregation', 'timer', aggregation_timer])
for summary, summary_options in summary_nets.items():
self.cli_set(base_path + ['summary-address', summary])
if 'tag' in summary_options:
self.cli_set(base_path + ['summary-address', summary, 'tag', summary_options['tag']])
if 'no_advertise' in summary_options:
self.cli_set(base_path + ['summary-address', summary, 'no-advertise'])
# commit changes
self.cli_commit()
# Verify FRR ospfd configuration
frrconfig = self.getFRRconfig('router ospf', endsection='^exit')
self.assertIn(f'router ospf', frrconfig)
self.assertIn(f' mpls-te on', frrconfig)
self.assertIn(f' mpls-te router-address 0.0.0.0', frrconfig) # default
self.assertIn(f' distance {global_distance}', frrconfig)
self.assertIn(f' distance ospf intra-area {intra_area} external {external}', frrconfig)
self.assertIn(f' max-metric router-lsa on-startup {on_startup}', frrconfig)
self.assertIn(f' max-metric router-lsa on-shutdown {on_shutdown}', frrconfig)
self.assertIn(f' refresh timer {refresh}', frrconfig)
self.assertIn(f' aggregation timer {aggregation_timer}', frrconfig)
for summary, summary_options in summary_nets.items():
self.assertIn(f' summary-address {summary}', frrconfig)
if 'tag' in summary_options:
tag = summary_options['tag']
self.assertIn(f' summary-address {summary} tag {tag}', frrconfig)
if 'no_advertise' in summary_options:
self.assertIn(f' summary-address {summary} no-advertise', frrconfig)
# enable inter-area
self.cli_set(base_path + ['distance', 'ospf', 'inter-area', inter_area])
self.cli_commit()
frrconfig = self.getFRRconfig('router ospf', endsection='^exit')
self.assertIn(f' distance ospf intra-area {intra_area} inter-area {inter_area} external {external}', frrconfig)
# https://github.com/FRRouting/frr/issues/17011
# We need to wait on_shutdown time, until the OSPF process is removed from the CLI
# otherwise the test in tearDown() will fail
self.cli_delete(base_path)
self.cli_commit()
sleep(int(on_shutdown) + 5) # additional grace period of 5 seconds
def test_ospf_06_neighbor(self):
priority = '10'
poll_interval = '20'
neighbors = ['1.1.1.1', '2.2.2.2', '3.3.3.3']
for neighbor in neighbors:
self.cli_set(base_path + ['neighbor', neighbor, 'priority', priority])
self.cli_set(base_path + ['neighbor', neighbor, 'poll-interval', poll_interval])
# commit changes
self.cli_commit()
# Verify FRR ospfd configuration
frrconfig = self.getFRRconfig('router ospf', endsection='^exit')
self.assertIn(f'router ospf', frrconfig)
for neighbor in neighbors:
self.assertIn(f' neighbor {neighbor} priority {priority} poll-interval {poll_interval}', frrconfig) # default
def test_ospf_07_redistribute(self):
metric = '15'
metric_type = '1'
table_id = '21'
redistribute = [
'babel',
'bgp',
'connected',
'isis',
'kernel',
'nhrp',
'rip',
'static',
'table',
]
for protocol in redistribute:
redistr_base = base_path + ['redistribute', protocol]
if protocol == 'table':
redistr_base += [table_id]
self.cli_set(redistr_base + ['metric', metric])
self.cli_set(redistr_base + ['route-map', route_map])
self.cli_set(redistr_base + ['metric-type', metric_type])
# commit changes
self.cli_commit()
# Verify FRR ospfd configuration
frrconfig = self.getFRRconfig('router ospf', endsection='^exit')
self.assertIn(f'router ospf', frrconfig)
for protocol in redistribute:
if protocol == 'table':
protocolstr = f'table-direct {table_id}'
else:
protocolstr = protocol
self.assertIn(
f' redistribute {protocolstr} metric {metric} metric-type {metric_type} route-map {route_map}',
frrconfig,
)
def test_ospf_08_virtual_link(self):
networks = ['10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16']
area = '10'
shortcut = 'enable'
virtual_link = '192.0.2.1'
hello = '6'
retransmit = '5'
transmit = '5'
dead = '40'
window_default = default_value(base_path + ['area', area, 'virtual-link', virtual_link, 'retransmit-window'])
self.cli_set(base_path + ['area', area, 'shortcut', shortcut])
self.cli_set(base_path + ['area', area, 'virtual-link', virtual_link, 'hello-interval', hello])
self.cli_set(base_path + ['area', area, 'virtual-link', virtual_link, 'retransmit-interval', retransmit])
self.cli_set(base_path + ['area', area, 'virtual-link', virtual_link, 'transmit-delay', transmit])
self.cli_set(base_path + ['area', area, 'virtual-link', virtual_link, 'dead-interval', dead])
for network in networks:
self.cli_set(base_path + ['area', area, 'network', network])
# commit changes
self.cli_commit()
# Verify FRR ospfd configuration
frrconfig = self.getFRRconfig('router ospf', endsection='^exit')
self.assertIn(f'router ospf', frrconfig)
self.assertIn(f' area {area} shortcut {shortcut}', frrconfig)
self.assertIn(f' area {area} virtual-link {virtual_link} hello-interval {hello} retransmit-interval {retransmit} retransmit-window {window_default} transmit-delay {transmit} dead-interval {dead}', frrconfig)
for network in networks:
self.assertIn(f' network {network} area {area}', frrconfig)
def test_ospf_09_interface_configuration(self):
interfaces = Section.interfaces('ethernet')
password = 'vyos1234'
bandwidth = '10000'
cost = '150'
network = 'point-to-point'
priority = '200'
bfd_profile = 'vyos-test'
self.cli_set(base_path + ['passive-interface', 'default'])
for interface in interfaces:
base_interface = base_path + ['interface', interface]
self.cli_set(base_interface + ['authentication', 'plaintext-password', password])
self.cli_set(base_interface + ['bandwidth', bandwidth])
self.cli_set(base_interface + ['bfd', 'profile', bfd_profile])
self.cli_set(base_interface + ['cost', cost])
self.cli_set(base_interface + ['mtu-ignore'])
self.cli_set(base_interface + ['network', network])
self.cli_set(base_interface + ['priority', priority])
self.cli_set(base_interface + ['passive', 'disable'])
# commit changes
self.cli_commit()
frrconfig = self.getFRRconfig('router ospf', endsection='^exit')
self.assertIn(f'router ospf', frrconfig)
self.assertIn(f' passive-interface default', frrconfig)
for interface in interfaces:
# Can not use daemon for getFRRconfig() as bandwidth parameter belongs to zebra process
config = self.getFRRconfig(f'interface {interface}', endsection='^exit')
self.assertIn(f'interface {interface}', config)
self.assertIn(f' ip ospf authentication-key {password}', config)
self.assertIn(f' ip ospf bfd', config)
self.assertIn(f' ip ospf bfd profile {bfd_profile}', config)
self.assertIn(f' ip ospf cost {cost}', config)
self.assertIn(f' ip ospf mtu-ignore', config)
self.assertIn(f' ip ospf network {network}', config)
self.assertIn(f' ip ospf priority {priority}', config)
self.assertIn(f' no ip ospf passive', config)
self.assertIn(f' bandwidth {bandwidth}', config)
# T5467: Remove interface from OSPF process and VRF
self.cli_delete(base_path + ['interface'])
self.cli_commit()
for interface in interfaces:
# T5467: It must also be removed from FRR config
frrconfig = self.getFRRconfig(f'interface {interface}', endsection='^exit')
self.assertNotIn(f'interface {interface}', frrconfig)
# There should be no OSPF related command at all under the interface
self.assertNotIn(f' ip ospf', frrconfig)
def test_ospf_11_interface_area(self):
area = '0'
interfaces = Section.interfaces('ethernet')
self.cli_set(base_path + ['area', area, 'network', '10.0.0.0/8'])
for interface in interfaces:
self.cli_set(base_path + ['interface', interface, 'area', area])
# we can not have bot area network and interface area set
with self.assertRaises(ConfigSessionError):
self.cli_commit()
self.cli_delete(base_path + ['area', area, 'network'])
self.cli_commit()
# Verify FRR ospfd configuration
frrconfig = self.getFRRconfig('router ospf', endsection='^exit')
self.assertIn(f'router ospf', frrconfig)
for interface in interfaces:
config = self.getFRRconfig(f'interface {interface}', endsection='^exit')
self.assertIn(f'interface {interface}', config)
self.assertIn(f' ip ospf area {area}', config)
def test_ospf_12_vrfs(self):
# It is safe to assume that when the basic VRF test works, all
# other OSPF related features work, as we entirely inherit the CLI
# templates and Jinja2 FRR template.
table = '1000'
vrf = 'blue'
vrf_base = ['vrf', 'name', vrf]
vrf_iface = 'eth1'
area = '1'
self.cli_set(vrf_base + ['table', table])
self.cli_set(vrf_base + ['protocols', 'ospf', 'interface', vrf_iface, 'area', area])
self.cli_set(['interfaces', 'ethernet', vrf_iface, 'vrf', vrf])
# Also set a default VRF OSPF config
self.cli_set(base_path)
self.cli_commit()
# Verify FRR ospfd configuration
frrconfig = self.getFRRconfig('router ospf', endsection='^exit')
self.assertIn(f'router ospf', frrconfig)
self.assertIn(f' auto-cost reference-bandwidth 100', frrconfig)
self.assertIn(f' timers throttle spf 200 1000 10000', frrconfig) # defaults
frrconfig = self.getFRRconfig(f'router ospf vrf {vrf}', endsection='^exit')
self.assertIn(f'router ospf vrf {vrf}', frrconfig)
self.assertIn(f' auto-cost reference-bandwidth 100', frrconfig)
self.assertIn(f' timers throttle spf 200 1000 10000', frrconfig) # defaults
frrconfig = self.getFRRconfig(f'interface {vrf_iface}', endsection='^exit')
self.assertIn(f'interface {vrf_iface}', frrconfig)
self.assertIn(f' ip ospf area {area}', frrconfig)
# T5467: Remove interface from OSPF process and VRF
self.cli_delete(vrf_base + ['protocols', 'ospf', 'interface'])
self.cli_delete(['interfaces', 'ethernet', vrf_iface, 'vrf'])
self.cli_commit()
# T5467: It must also be removed from FRR config
frrconfig = self.getFRRconfig(f'interface {vrf_iface}', endsection='^exit')
self.assertNotIn(f'interface {vrf_iface}', frrconfig)
# There should be no OSPF related command at all under the interface
self.assertNotIn(f' ip ospf', frrconfig)
# cleanup
self.cli_delete(['vrf', 'name', vrf])
self.cli_delete(['interfaces', 'ethernet', vrf_iface, 'vrf'])
def test_ospf_13_export_list(self):
# Verify explort-list works on ospf-area
acl = '100'
seq = '10'
area = '0.0.0.10'
network = '10.0.0.0/8'
self.cli_set(['policy', 'access-list', acl, 'rule', seq, 'action', 'permit'])
self.cli_set(['policy', 'access-list', acl, 'rule', seq, 'source', 'any'])
self.cli_set(['policy', 'access-list', acl, 'rule', seq, 'destination', 'any'])
self.cli_set(base_path + ['area', area, 'network', network])
self.cli_set(base_path + ['area', area, 'export-list', acl])
# commit changes
self.cli_commit()
# Verify FRR ospfd configuration
frrconfig = self.getFRRconfig('router ospf', endsection='^exit')
self.assertIn(f'router ospf', frrconfig)
self.assertIn(f' timers throttle spf 200 1000 10000', frrconfig) # default
self.assertIn(f' network {network} area {area}', frrconfig)
self.assertIn(f' area {area} export-list {acl}', frrconfig)
def test_ospf_14_segment_routing_configuration(self):
global_block_low = "300"
global_block_high = "399"
local_block_low = "400"
local_block_high = "499"
maximum_stack_size = '5'
prefix_one = '192.168.0.1/32'
prefix_two = '192.168.0.2/32'
prefix_one_value = '1'
prefix_two_value = '2'
self.cli_set(base_path + ['interface', dummy_if])
self.cli_set(base_path + ['segment-routing', 'maximum-label-depth', maximum_stack_size])
self.cli_set(base_path + ['segment-routing', 'global-block', 'low-label-value', global_block_low])
self.cli_set(base_path + ['segment-routing', 'global-block', 'high-label-value', global_block_high])
self.cli_set(base_path + ['segment-routing', 'local-block', 'low-label-value', local_block_low])
self.cli_set(base_path + ['segment-routing', 'local-block', 'high-label-value', local_block_high])
self.cli_set(base_path + ['segment-routing', 'prefix', prefix_one, 'index', 'value', prefix_one_value])
self.cli_set(base_path + ['segment-routing', 'prefix', prefix_one, 'index', 'explicit-null'])
self.cli_set(base_path + ['segment-routing', 'prefix', prefix_two, 'index', 'value', prefix_two_value])
self.cli_set(base_path + ['segment-routing', 'prefix', prefix_two, 'index', 'no-php-flag'])
# Commit all changes
self.cli_commit()
# Verify all changes
frrconfig = self.getFRRconfig('router ospf', endsection='^exit')
self.assertIn(f' segment-routing on', frrconfig)
self.assertIn(f' segment-routing global-block {global_block_low} {global_block_high} local-block {local_block_low} {local_block_high}', frrconfig)
self.assertIn(f' segment-routing node-msd {maximum_stack_size}', frrconfig)
self.assertIn(f' segment-routing prefix {prefix_one} index {prefix_one_value} explicit-null', frrconfig)
self.assertIn(f' segment-routing prefix {prefix_two} index {prefix_two_value} no-php-flag', frrconfig)
def test_ospf_15_ldp_sync(self):
holddown = "500"
interfaces = Section.interfaces('ethernet')
self.cli_set(base_path + ['interface', dummy_if])
self.cli_set(base_path + ['ldp-sync', 'holddown', holddown])
# Commit main OSPF changes
self.cli_commit()
# Verify main OSPF changes
frrconfig = self.getFRRconfig('router ospf', endsection='^exit')
self.assertIn(f'router ospf', frrconfig)
self.assertIn(f' timers throttle spf 200 1000 10000', frrconfig)
self.assertIn(f' mpls ldp-sync holddown {holddown}', frrconfig)
for interface in interfaces:
self.cli_set(base_path + ['interface', interface, 'ldp-sync', 'holddown', holddown])
# Commit interface changes for holddown
self.cli_commit()
for interface in interfaces:
# Verify interface changes for holddown
config = self.getFRRconfig(f'interface {interface}', endsection='^exit')
self.assertIn(f'interface {interface}', config)
self.assertIn(f' ip ospf dead-interval 40', config)
self.assertIn(f' ip ospf mpls ldp-sync', config)
self.assertIn(f' ip ospf mpls ldp-sync holddown {holddown}', config)
for interface in interfaces:
self.cli_set(base_path + ['interface', interface, 'ldp-sync', 'disable'])
# Commit interface changes for disable
self.cli_commit()
for interface in interfaces:
# Verify interface changes for disable
config = self.getFRRconfig(f'interface {interface}', endsection='^exit')
self.assertIn(f'interface {interface}', config)
self.assertIn(f' ip ospf dead-interval 40', config)
self.assertNotIn(f' ip ospf mpls ldp-sync', config)
def test_ospf_16_graceful_restart(self):
period = '300'
supported_grace_time = '400'
router_ids = ['192.0.2.1', '192.0.2.2']
self.cli_set(base_path + ['capability', 'opaque'])
self.cli_set(base_path + ['graceful-restart', 'grace-period', period])
self.cli_set(base_path + ['graceful-restart', 'helper', 'planned-only'])
self.cli_set(base_path + ['graceful-restart', 'helper', 'no-strict-lsa-checking'])
self.cli_set(base_path + ['graceful-restart', 'helper', 'supported-grace-time', supported_grace_time])
for router_id in router_ids:
self.cli_set(base_path + ['graceful-restart', 'helper', 'enable', 'router-id', router_id])
# commit changes
self.cli_commit()
# Verify FRR ospfd configuration
frrconfig = self.getFRRconfig('router ospf', endsection='^exit')
self.assertIn(f'router ospf', frrconfig)
self.assertIn(f' capability opaque', frrconfig)
self.assertIn(f' graceful-restart grace-period {period}', frrconfig)
self.assertIn(f' graceful-restart helper planned-only', frrconfig)
self.assertIn(f' no graceful-restart helper strict-lsa-checking', frrconfig)
self.assertIn(f' graceful-restart helper supported-grace-time {supported_grace_time}', frrconfig)
for router_id in router_ids:
self.assertIn(f' graceful-restart helper enable {router_id}', frrconfig)
def test_ospf_17_duplicate_area_network(self):
area0 = '0'
area1 = '1'
network = '10.0.0.0/8'
self.cli_set(base_path + ['area', area0, 'network', network])
# we can not have the same network defined on two areas
self.cli_set(base_path + ['area', area1, 'network', network])
with self.assertRaises(ConfigSessionError):
self.cli_commit()
self.cli_delete(base_path + ['area', area0])
self.cli_commit()
# Verify FRR ospfd configuration
frrconfig = self.getFRRconfig('router ospf', endsection='^exit', empty_retry=60)
self.assertIn(f'router ospf', frrconfig)
self.assertIn(f' network {network} area {area1}', frrconfig)
def test_ospf_18_area_translate_no_summary(self):
area = '11'
area_type = 'nssa'
network = '100.64.0.0/10'
self.cli_set(base_path + ['area', area, 'area-type', area_type, 'no-summary'])
self.cli_set(base_path + ['area', area, 'area-type', area_type, 'translate', 'never'])
self.cli_set(base_path + ['area', area, 'network', network])
# commit changes
self.cli_commit()
# Verify FRR ospfd configuration
frrconfig = self.getFRRconfig('router ospf', endsection='^exit')
self.assertIn(f'router ospf', frrconfig)
self.assertIn(f' area {area} {area_type} translate-never no-summary', frrconfig)
self.assertIn(f' network {network} area {area}', frrconfig)
if __name__ == '__main__':
unittest.main(verbosity=2, failfast=VyOSUnitTestSHIM.TestCase.debug_on())
diff --git a/smoketest/scripts/cli/test_protocols_ospfv3.py b/smoketest/scripts/cli/test_protocols_ospfv3.py
index 1041ad52c..44d016b7e 100755
--- a/smoketest/scripts/cli/test_protocols_ospfv3.py
+++ b/smoketest/scripts/cli/test_protocols_ospfv3.py
@@ -1,345 +1,342 @@
#!/usr/bin/env python3
#
# Copyright VyOS maintainers and contributors <maintainers@vyos.io>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
-from base_vyostest_shim import CSTORE_GUARD_TIME
from vyos.configsession import ConfigSessionError
from vyos.ifconfig import Section
from vyos.frrender import ospf6_daemon
from vyos.utils.process import process_named_running
base_path = ['protocols', 'ospfv3']
route_map = 'foo-bar-baz-0815'
router_id = '192.0.2.1'
default_area = '0'
class TestProtocolsOSPFv3(VyOSUnitTestSHIM.TestCase):
@classmethod
def setUpClass(cls):
super(TestProtocolsOSPFv3, cls).setUpClass()
# Retrieve FRR daemon PID - it is not allowed to crash, thus PID must remain the same
cls.daemon_pid = process_named_running(ospf6_daemon)
cls.cli_set(cls, ['policy', 'route-map', route_map, 'rule', '10', 'action', 'permit'])
cls.cli_set(cls, ['policy', 'route-map', route_map, 'rule', '20', 'action', 'permit'])
# ensure we can also run this test on a live system - so lets clean
# out the current configuration :)
cls.cli_delete(cls, base_path)
- # Enable CSTORE guard time required by FRR related tests
- cls._commit_guard_time = CSTORE_GUARD_TIME
@classmethod
def tearDownClass(cls):
cls.cli_delete(cls, ['policy', 'route-map', route_map])
super(TestProtocolsOSPFv3, cls).tearDownClass()
def tearDown(self):
self.cli_delete(base_path)
self.cli_commit()
frrconfig = self.getFRRconfig('router ospf6', endsection='^exit')
self.assertNotIn(f'router ospf6', frrconfig)
# check process health and continuity
self.assertEqual(self.daemon_pid, process_named_running(ospf6_daemon))
def test_ospfv3_01_basic(self):
seq = '10'
prefix = '2001:db8::/32'
acl_name = 'foo-acl-100'
self.cli_set(['policy', 'access-list6', acl_name, 'rule', seq, 'action', 'permit'])
self.cli_set(['policy', 'access-list6', acl_name, 'rule', seq, 'source', 'any'])
self.cli_set(base_path + ['parameters', 'router-id', router_id])
self.cli_set(base_path + ['area', default_area, 'range', prefix, 'advertise'])
self.cli_set(base_path + ['area', default_area, 'export-list', acl_name])
self.cli_set(base_path + ['area', default_area, 'import-list', acl_name])
interfaces = Section.interfaces('ethernet')
for interface in interfaces:
self.cli_set(base_path + ['interface', interface, 'area', default_area])
# commit changes
self.cli_commit()
# Verify FRR ospfd configuration
frrconfig = self.getFRRconfig('router ospf6', endsection='^exit')
self.assertIn(f'router ospf6', frrconfig)
self.assertIn(f' area {default_area} range {prefix}', frrconfig)
self.assertIn(f' ospf6 router-id {router_id}', frrconfig)
self.assertIn(f' area {default_area} import-list {acl_name}', frrconfig)
self.assertIn(f' area {default_area} export-list {acl_name}', frrconfig)
for interface in interfaces:
if_config = self.getFRRconfig(f'interface {interface}', endsection='^exit')
self.assertIn(f'ipv6 ospf6 area {default_area}', if_config)
self.cli_delete(['policy', 'access-list6', acl_name])
def test_ospfv3_02_distance(self):
dist_global = '200'
dist_external = '110'
dist_inter_area = '120'
dist_intra_area = '130'
self.cli_set(base_path + ['distance', 'global', dist_global])
self.cli_set(base_path + ['distance', 'ospfv3', 'external', dist_external])
self.cli_set(base_path + ['distance', 'ospfv3', 'inter-area', dist_inter_area])
self.cli_set(base_path + ['distance', 'ospfv3', 'intra-area', dist_intra_area])
# commit changes
self.cli_commit()
# Verify FRR ospfd configuration
frrconfig = self.getFRRconfig('router ospf6', endsection='^exit')
self.assertIn(f'router ospf6', frrconfig)
self.assertIn(f' distance {dist_global}', frrconfig)
self.assertIn(f' distance ospf6 intra-area {dist_intra_area} inter-area {dist_inter_area} external {dist_external}', frrconfig)
def test_ospfv3_03_redistribute(self):
metric = '15'
metric_type = '1'
route_map = 'foo-bar'
route_map_seq = '10'
redistribute = ['babel', 'bgp', 'connected', 'isis', 'kernel', 'ripng', 'static']
self.cli_set(['policy', 'route-map', route_map, 'rule', route_map_seq, 'action', 'permit'])
for protocol in redistribute:
self.cli_set(base_path + ['redistribute', protocol, 'metric', metric])
self.cli_set(base_path + ['redistribute', protocol, 'route-map', route_map])
self.cli_set(base_path + ['redistribute', protocol, 'metric-type', metric_type])
# commit changes
self.cli_commit()
# Verify FRR ospfd configuration
frrconfig = self.getFRRconfig('router ospf6', endsection='^exit')
self.assertIn(f'router ospf6', frrconfig)
for protocol in redistribute:
self.assertIn(f' redistribute {protocol} metric {metric} metric-type {metric_type} route-map {route_map}', frrconfig)
def test_ospfv3_04_interfaces(self):
bfd_profile = 'vyos-ipv6'
self.cli_set(base_path + ['parameters', 'router-id', router_id])
self.cli_set(base_path + ['area', default_area])
cost = '100'
priority = '10'
interfaces = Section.interfaces('ethernet')
for interface in interfaces:
if_base = base_path + ['interface', interface]
self.cli_set(if_base + ['bfd', 'profile', bfd_profile])
self.cli_set(if_base + ['cost', cost])
self.cli_set(if_base + ['instance-id', '0'])
self.cli_set(if_base + ['mtu-ignore'])
self.cli_set(if_base + ['network', 'point-to-point'])
self.cli_set(if_base + ['passive'])
self.cli_set(if_base + ['priority', priority])
cost = str(int(cost) + 10)
priority = str(int(priority) + 5)
# commit changes
self.cli_commit()
# Verify FRR ospfd configuration
frrconfig = self.getFRRconfig('router ospf6', endsection='^exit')
self.assertIn(f'router ospf6', frrconfig)
cost = '100'
priority = '10'
for interface in interfaces:
if_config = self.getFRRconfig(f'interface {interface}', endsection='^exit')
self.assertIn(f'interface {interface}', if_config)
self.assertIn(f' ipv6 ospf6 bfd', if_config)
self.assertIn(f' ipv6 ospf6 bfd profile {bfd_profile}', if_config)
self.assertIn(f' ipv6 ospf6 cost {cost}', if_config)
self.assertIn(f' ipv6 ospf6 mtu-ignore', if_config)
self.assertIn(f' ipv6 ospf6 network point-to-point', if_config)
self.assertIn(f' ipv6 ospf6 passive', if_config)
self.assertIn(f' ipv6 ospf6 priority {priority}', if_config)
cost = str(int(cost) + 10)
priority = str(int(priority) + 5)
# Cleanup interfaces
self.cli_delete(base_path + ['interface'])
self.cli_commit()
for interface in interfaces:
if_config = self.getFRRconfig(f'interface {interface}', endsection='^exit')
# There should be no OSPF6 configuration at all after interface removal
self.assertNotIn(f' ipv6 ospf6', if_config)
def test_ospfv3_05_area_stub(self):
area_stub = '23'
area_stub_nosum = '26'
self.cli_set(base_path + ['area', area_stub, 'area-type', 'stub'])
self.cli_set(base_path + ['area', area_stub_nosum, 'area-type', 'stub', 'no-summary'])
# commit changes
self.cli_commit()
# Verify FRR ospfd configuration
frrconfig = self.getFRRconfig('router ospf6', endsection='^exit')
self.assertIn(f'router ospf6', frrconfig)
self.assertIn(f' area {area_stub} stub', frrconfig)
self.assertIn(f' area {area_stub_nosum} stub no-summary', frrconfig)
def test_ospfv3_06_area_nssa(self):
area_nssa = '1.1.1.1'
area_nssa_nosum = '2.2.2.2'
area_nssa_default = '3.3.3.3'
self.cli_set(base_path + ['area', area_nssa, 'area-type', 'nssa'])
self.cli_set(base_path + ['area', area_nssa, 'area-type', 'stub'])
# can only set one area-type per OSPFv3 area
with self.assertRaises(ConfigSessionError):
self.cli_commit()
self.cli_delete(base_path + ['area', area_nssa, 'area-type', 'stub'])
self.cli_set(base_path + ['area', area_nssa_nosum, 'area-type', 'nssa', 'no-summary'])
self.cli_set(base_path + ['area', area_nssa_nosum, 'area-type', 'nssa', 'default-information-originate'])
self.cli_set(base_path + ['area', area_nssa_default, 'area-type', 'nssa', 'default-information-originate'])
# commit changes
self.cli_commit()
# Verify FRR ospfd configuration
frrconfig = self.getFRRconfig('router ospf6', endsection='^exit')
self.assertIn(f'router ospf6', frrconfig)
self.assertIn(f' area {area_nssa} nssa', frrconfig)
self.assertIn(f' area {area_nssa_nosum} nssa default-information-originate no-summary', frrconfig)
self.assertIn(f' area {area_nssa_default} nssa default-information-originate', frrconfig)
def test_ospfv3_07_default_originate(self):
seq = '100'
metric = '50'
metric_type = '1'
self.cli_set(base_path + ['default-information', 'originate', 'metric', metric])
self.cli_set(base_path + ['default-information', 'originate', 'metric-type', metric_type])
self.cli_set(base_path + ['default-information', 'originate', 'route-map', route_map])
# commit changes
self.cli_commit()
# Verify FRR ospfd configuration
frrconfig = self.getFRRconfig('router ospf6', endsection='^exit')
self.assertIn(f'router ospf6', frrconfig)
self.assertIn(f' default-information originate metric {metric} metric-type {metric_type} route-map {route_map}', frrconfig)
# Now set 'always'
self.cli_set(base_path + ['default-information', 'originate', 'always'])
self.cli_commit()
# Verify FRR ospfd configuration
frrconfig = self.getFRRconfig('router ospf6', endsection='^exit')
self.assertIn(f' default-information originate always metric {metric} metric-type {metric_type} route-map {route_map}', frrconfig)
def test_ospfv3_08_vrfs(self):
# It is safe to assume that when the basic VRF test works, all
# other OSPF related features work, as we entirely inherit the CLI
# templates and Jinja2 FRR template.
table = '1000'
vrf = 'blue'
vrf_base = ['vrf', 'name', vrf]
vrf_iface = 'eth1'
router_id = '1.2.3.4'
router_id_vrf = '1.2.3.5'
self.cli_set(vrf_base + ['table', table])
self.cli_set(vrf_base + ['protocols', 'ospfv3', 'interface', vrf_iface, 'bfd'])
self.cli_set(vrf_base + ['protocols', 'ospfv3', 'parameters', 'router-id', router_id_vrf])
self.cli_set(['interfaces', 'ethernet', vrf_iface, 'vrf', vrf])
# Also set a default VRF OSPF config
self.cli_set(base_path + ['parameters', 'router-id', router_id])
self.cli_commit()
# Verify FRR ospfd configuration
frrconfig = self.getFRRconfig('router ospf6', endsection='^exit')
self.assertIn(f'router ospf6', frrconfig)
self.assertIn(f' ospf6 router-id {router_id}', frrconfig)
frrconfig = self.getFRRconfig(f'interface {vrf_iface}', endsection='^exit')
self.assertIn(f'interface {vrf_iface}', frrconfig)
self.assertIn(f' ipv6 ospf6 bfd', frrconfig)
frrconfig = self.getFRRconfig(f'router ospf6 vrf {vrf}', endsection='^exit')
self.assertIn(f'router ospf6 vrf {vrf}', frrconfig)
self.assertIn(f' ospf6 router-id {router_id_vrf}', frrconfig)
# T5467: Remove interface from OSPF process and VRF
self.cli_delete(vrf_base + ['protocols', 'ospfv3', 'interface'])
self.cli_delete(['interfaces', 'ethernet', vrf_iface, 'vrf'])
self.cli_commit()
# T5467: It must also be removed from FRR config
frrconfig = self.getFRRconfig(f'interface {vrf_iface}', endsection='^exit')
self.assertNotIn(f'interface {vrf_iface}', frrconfig)
# There should be no OSPF related command at all under the interface
self.assertNotIn(f' ipv6 ospf6', frrconfig)
# cleanup
self.cli_delete(['vrf', 'name', vrf])
self.cli_delete(['interfaces', 'ethernet', vrf_iface, 'vrf'])
def test_ospfv3_09_graceful_restart(self):
period = '300'
supported_grace_time = '400'
router_ids = ['192.0.2.1', '192.0.2.2']
self.cli_set(base_path + ['graceful-restart', 'grace-period', period])
self.cli_set(base_path + ['graceful-restart', 'helper', 'planned-only'])
self.cli_set(base_path + ['graceful-restart', 'helper', 'lsa-check-disable'])
self.cli_set(base_path + ['graceful-restart', 'helper', 'supported-grace-time', supported_grace_time])
for router_id in router_ids:
self.cli_set(base_path + ['graceful-restart', 'helper', 'enable', 'router-id', router_id])
# commit changes
self.cli_commit()
# Verify FRR ospfd configuration
frrconfig = self.getFRRconfig('router ospf6', endsection='^exit')
self.assertIn(f'router ospf6', frrconfig)
self.assertIn(f' graceful-restart grace-period {period}', frrconfig)
self.assertIn(f' graceful-restart helper planned-only', frrconfig)
self.assertIn(f' graceful-restart helper lsa-check-disable', frrconfig)
self.assertIn(f' graceful-restart helper supported-grace-time {supported_grace_time}', frrconfig)
for router_id in router_ids:
self.assertIn(f' graceful-restart helper enable {router_id}', frrconfig)
if __name__ == '__main__':
unittest.main(verbosity=2, failfast=VyOSUnitTestSHIM.TestCase.debug_on())
diff --git a/smoketest/scripts/cli/test_protocols_pim.py b/smoketest/scripts/cli/test_protocols_pim.py
index 7702c5de4..2560254bc 100755
--- a/smoketest/scripts/cli/test_protocols_pim.py
+++ b/smoketest/scripts/cli/test_protocols_pim.py
@@ -1,203 +1,200 @@
#!/usr/bin/env python3
#
# Copyright VyOS maintainers and contributors <maintainers@vyos.io>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
-from base_vyostest_shim import CSTORE_GUARD_TIME
from vyos.configsession import ConfigSessionError
from vyos.frrender import pim_daemon
from vyos.ifconfig import Section
from vyos.utils.process import process_named_running
base_path = ['protocols', 'pim']
class TestProtocolsPIM(VyOSUnitTestSHIM.TestCase):
@classmethod
def setUpClass(cls):
# call base-classes classmethod
super(TestProtocolsPIM, cls).setUpClass()
# ensure we can also run this test on a live system - so lets clean
# out the current configuration :)
cls.cli_delete(cls, base_path)
- # Enable CSTORE guard time required by FRR related tests
- cls._commit_guard_time = CSTORE_GUARD_TIME
def tearDown(self):
# pimd process must be running
self.assertTrue(process_named_running(pim_daemon))
self.cli_delete(base_path)
self.cli_commit()
# pimd process must be stopped by now
self.assertFalse(process_named_running(pim_daemon))
def test_01_pim_basic(self):
rp = '127.0.0.1'
group = '224.0.0.0/4'
hello = '100'
dr_priority = '64'
self.cli_set(base_path + ['rp', 'address', rp, 'group', group])
interfaces = Section.interfaces('ethernet')
for interface in interfaces:
self.cli_set(base_path + ['interface', interface , 'bfd'])
self.cli_set(base_path + ['interface', interface , 'dr-priority', dr_priority])
self.cli_set(base_path + ['interface', interface , 'hello', hello])
self.cli_set(base_path + ['interface', interface , 'no-bsm'])
self.cli_set(base_path + ['interface', interface , 'no-unicast-bsm'])
self.cli_set(base_path + ['interface', interface , 'passive'])
# commit changes
self.cli_commit()
# Verify FRR pimd configuration
frrconfig = self.getFRRconfig('router pim', endsection='^exit')
self.assertIn(f' rp {rp} {group}', frrconfig)
for interface in interfaces:
frrconfig = self.getFRRconfig(f'interface {interface}', endsection='^exit')
self.assertIn(f'interface {interface}', frrconfig)
self.assertIn(f' ip pim', frrconfig)
self.assertIn(f' ip pim bfd', frrconfig)
self.assertIn(f' ip pim drpriority {dr_priority}', frrconfig)
self.assertIn(f' ip pim hello {hello}', frrconfig)
self.assertIn(f' no ip pim bsm', frrconfig)
self.assertIn(f' no ip pim unicast-bsm', frrconfig)
self.assertIn(f' ip pim passive', frrconfig)
self.cli_commit()
def test_02_pim_advanced(self):
rp = '127.0.0.2'
group = '224.0.0.0/4'
join_prune_interval = '123'
rp_keep_alive_timer = '190'
keep_alive_timer = '180'
packets = '10'
prefix_list = 'pim-test'
register_suppress_time = '300'
self.cli_set(base_path + ['rp', 'address', rp, 'group', group])
self.cli_set(base_path + ['rp', 'keep-alive-timer', rp_keep_alive_timer])
self.cli_set(base_path + ['ecmp', 'rebalance'])
self.cli_set(base_path + ['join-prune-interval', join_prune_interval])
self.cli_set(base_path + ['keep-alive-timer', keep_alive_timer])
self.cli_set(base_path + ['packets', packets])
self.cli_set(base_path + ['register-accept-list', 'prefix-list', prefix_list])
self.cli_set(base_path + ['register-suppress-time', register_suppress_time])
self.cli_set(base_path + ['no-v6-secondary'])
self.cli_set(base_path + ['spt-switchover', 'infinity-and-beyond', 'prefix-list', prefix_list])
self.cli_set(base_path + ['ssm', 'prefix-list', prefix_list])
# check validate() - PIM require defined interfaces!
with self.assertRaises(ConfigSessionError):
self.cli_commit()
interfaces = Section.interfaces('ethernet')
for interface in interfaces:
self.cli_set(base_path + ['interface', interface])
# commit changes
self.cli_commit()
# Verify FRR pimd configuration
frrconfig = self.getFRRconfig('router pim', endsection='^exit')
self.assertIn(f' no send-v6-secondary', frrconfig)
self.assertIn(f' rp {rp} {group}', frrconfig)
self.assertIn(f' register-suppress-time {register_suppress_time}', frrconfig)
self.assertIn(f' join-prune-interval {join_prune_interval}', frrconfig)
self.assertIn(f' packets {packets}', frrconfig)
self.assertIn(f' keep-alive-timer {keep_alive_timer}', frrconfig)
self.assertIn(f' rp keep-alive-timer {rp_keep_alive_timer}', frrconfig)
self.assertIn(f' ssm prefix-list {prefix_list}', frrconfig)
self.assertIn(f' register-accept-list {prefix_list}', frrconfig)
self.assertIn(f' spt-switchover infinity-and-beyond prefix-list {prefix_list}', frrconfig)
self.assertIn(f' ecmp rebalance', frrconfig)
def test_03_pim_igmp_proxy(self):
igmp_proxy = ['protocols', 'igmp-proxy']
rp = '127.0.0.1'
group = '224.0.0.0/4'
self.cli_set(base_path)
self.cli_set(igmp_proxy)
# check validate() - can not set both IGMP proxy and PIM
with self.assertRaises(ConfigSessionError):
self.cli_commit()
self.cli_delete(igmp_proxy)
self.cli_set(base_path + ['rp', 'address', rp, 'group', group])
interfaces = Section.interfaces('ethernet')
for interface in interfaces:
self.cli_set(base_path + ['interface', interface , 'bfd'])
# commit changes
self.cli_commit()
def test_04_igmp(self):
watermark_warning = '2000'
query_interval = '1000'
query_max_response_time = '200'
version = '2'
igmp_join = {
'224.1.1.1' : { 'source' : ['1.1.1.1', '2.2.2.2', '3.3.3.3'] },
'224.1.2.2' : { 'source' : [] },
'224.1.3.3' : {},
}
self.cli_set(base_path + ['igmp', 'watermark-warning', watermark_warning])
interfaces = Section.interfaces('ethernet')
for interface in interfaces:
self.cli_set(base_path + ['interface', interface , 'igmp', 'version', version])
self.cli_set(base_path + ['interface', interface , 'igmp', 'query-interval', query_interval])
self.cli_set(base_path + ['interface', interface , 'igmp', 'query-max-response-time', query_max_response_time])
for join, join_config in igmp_join.items():
self.cli_set(base_path + ['interface', interface , 'igmp', 'join', join])
if 'source' in join_config:
for source in join_config['source']:
self.cli_set(base_path + ['interface', interface , 'igmp', 'join', join, 'source-address', source])
self.cli_commit()
frrconfig = self.getFRRconfig()
self.assertIn(f'ip igmp watermark-warn {watermark_warning}', frrconfig)
for interface in interfaces:
frrconfig = self.getFRRconfig(f'interface {interface}', endsection='^exit')
self.assertIn(f'interface {interface}', frrconfig)
self.assertIn(f' ip igmp', frrconfig)
self.assertIn(f' ip igmp version {version}', frrconfig)
self.assertIn(f' ip igmp query-interval {query_interval}', frrconfig)
self.assertIn(f' ip igmp query-max-response-time {query_max_response_time}', frrconfig)
for join, join_config in igmp_join.items():
if 'source' in join_config:
for source in join_config['source']:
self.assertIn(f' ip igmp join-group {join} {source}', frrconfig)
else:
self.assertIn(f' ip igmp join-group {join}', frrconfig)
if __name__ == '__main__':
unittest.main(verbosity=2, failfast=VyOSUnitTestSHIM.TestCase.debug_on())
diff --git a/smoketest/scripts/cli/test_protocols_pim6.py b/smoketest/scripts/cli/test_protocols_pim6.py
index 2e1ac6d6e..36a81c306 100755
--- a/smoketest/scripts/cli/test_protocols_pim6.py
+++ b/smoketest/scripts/cli/test_protocols_pim6.py
@@ -1,150 +1,147 @@
#!/usr/bin/env python3
#
# Copyright VyOS maintainers and contributors <maintainers@vyos.io>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
-from base_vyostest_shim import CSTORE_GUARD_TIME
-
from vyos.configsession import ConfigSessionError
+
from vyos.ifconfig import Section
from vyos.frrender import pim6_daemon
from vyos.utils.process import process_named_running
base_path = ['protocols', 'pim6']
class TestProtocolsPIMv6(VyOSUnitTestSHIM.TestCase):
@classmethod
def setUpClass(cls):
# call base-classes classmethod
super(TestProtocolsPIMv6, cls).setUpClass()
# Retrieve FRR daemon PID - it is not allowed to crash, thus PID must remain the same
cls.daemon_pid = process_named_running(pim6_daemon)
# ensure we can also run this test on a live system - so lets clean
# out the current configuration :)
cls.cli_delete(cls, base_path)
- # Enable CSTORE guard time required by FRR related tests
- cls._commit_guard_time = CSTORE_GUARD_TIME
def tearDown(self):
self.cli_delete(base_path)
self.cli_commit()
# check process health and continuity
self.assertEqual(self.daemon_pid, process_named_running(pim6_daemon))
def test_pim6_01_mld_simple(self):
# commit changes
interfaces = Section.interfaces('ethernet')
for interface in interfaces:
self.cli_set(base_path + ['interface', interface])
self.cli_commit()
# Verify FRR pim6d configuration
for interface in interfaces:
config = self.getFRRconfig(f'interface {interface}', endsection='^exit')
self.assertIn(f'interface {interface}', config)
self.assertIn(f' ipv6 mld', config)
self.assertNotIn(f' ipv6 mld version 1', config)
# Change to MLD version 1
for interface in interfaces:
self.cli_set(base_path + ['interface', interface, 'mld', 'version', '1'])
self.cli_commit()
# Verify FRR pim6d configuration
for interface in interfaces:
config = self.getFRRconfig(f'interface {interface}', endsection='^exit')
self.assertIn(f'interface {interface}', config)
self.assertIn(f' ipv6 mld', config)
self.assertIn(f' ipv6 mld version 1', config)
def test_pim6_02_mld_join(self):
interfaces = Section.interfaces('ethernet')
# Use an invalid multicast group address
for interface in interfaces:
self.cli_set(base_path + ['interface', interface, 'mld', 'join', 'fd00::1234'])
with self.assertRaises(ConfigSessionError):
self.cli_commit()
self.cli_delete(base_path + ['interface'])
# Use a valid multicast group address
for interface in interfaces:
self.cli_set(base_path + ['interface', interface, 'mld', 'join', 'ff18::1234'])
self.cli_commit()
# Verify FRR pim6d configuration
for interface in interfaces:
config = self.getFRRconfig(f'interface {interface}', endsection='^exit')
self.assertIn(f'interface {interface}', config)
self.assertIn(f' ipv6 mld join-group ff18::1234', config)
# Join a source-specific multicast group
for interface in interfaces:
self.cli_set(base_path + ['interface', interface, 'mld', 'join', 'ff38::5678', 'source', '2001:db8::5678'])
self.cli_commit()
# Verify FRR pim6d configuration
for interface in interfaces:
config = self.getFRRconfig(f'interface {interface}', endsection='^exit')
self.assertIn(f'interface {interface}', config)
self.assertIn(f' ipv6 mld join-group ff38::5678 2001:db8::5678', config)
def test_pim6_03_basic(self):
interfaces = Section.interfaces('ethernet')
join_prune_interval = '123'
keep_alive_timer = '77'
packets = '5'
register_suppress_time = '99'
dr_priority = '100'
hello = '50'
self.cli_set(base_path + ['join-prune-interval', join_prune_interval])
self.cli_set(base_path + ['keep-alive-timer', keep_alive_timer])
self.cli_set(base_path + ['packets', packets])
self.cli_set(base_path + ['register-suppress-time', register_suppress_time])
for interface in interfaces:
self.cli_set(base_path + ['interface', interface, 'dr-priority', dr_priority])
self.cli_set(base_path + ['interface', interface, 'hello', hello])
self.cli_set(base_path + ['interface', interface, 'no-bsm'])
self.cli_set(base_path + ['interface', interface, 'no-unicast-bsm'])
self.cli_set(base_path + ['interface', interface, 'passive'])
self.cli_commit()
# Verify FRR pim6d configuration
config = self.getFRRconfig('router pim6', endsection='^exit')
self.assertIn(f' join-prune-interval {join_prune_interval}', config)
self.assertIn(f' keep-alive-timer {keep_alive_timer}', config)
self.assertIn(f' packets {packets}', config)
self.assertIn(f' register-suppress-time {register_suppress_time}', config)
for interface in interfaces:
config = self.getFRRconfig(f'interface {interface}', endsection='^exit')
self.assertIn(f' ipv6 pim drpriority {dr_priority}', config)
self.assertIn(f' ipv6 pim hello {hello}', config)
self.assertIn(f' no ipv6 pim bsm', config)
self.assertIn(f' no ipv6 pim unicast-bsm', config)
self.assertIn(f' ipv6 pim passive', config)
if __name__ == '__main__':
unittest.main(verbosity=2, failfast=VyOSUnitTestSHIM.TestCase.debug_on())
diff --git a/smoketest/scripts/cli/test_protocols_rip.py b/smoketest/scripts/cli/test_protocols_rip.py
index f405702e5..6d1922017 100755
--- a/smoketest/scripts/cli/test_protocols_rip.py
+++ b/smoketest/scripts/cli/test_protocols_rip.py
@@ -1,189 +1,186 @@
#!/usr/bin/env python3
#
# Copyright VyOS maintainers and contributors <maintainers@vyos.io>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
-from base_vyostest_shim import CSTORE_GUARD_TIME
from vyos.ifconfig import Section
from vyos.frrender import rip_daemon
from vyos.utils.process import process_named_running
acl_in = '198'
acl_out = '199'
prefix_list_in = 'foo-prefix'
prefix_list_out = 'bar-prefix'
route_map = 'FooBar123'
base_path = ['protocols', 'rip']
class TestProtocolsRIP(VyOSUnitTestSHIM.TestCase):
@classmethod
def setUpClass(cls):
super(TestProtocolsRIP, cls).setUpClass()
# Retrieve FRR daemon PID - it is not allowed to crash, thus PID must remain the same
cls.daemon_pid = process_named_running(rip_daemon)
# ensure we can also run this test on a live system - so lets clean
# out the current configuration :)
cls.cli_delete(cls, base_path)
- # Enable CSTORE guard time required by FRR related tests
- cls._commit_guard_time = CSTORE_GUARD_TIME
cls.cli_set(cls, ['policy', 'access-list', acl_in, 'rule', '10', 'action', 'permit'])
cls.cli_set(cls, ['policy', 'access-list', acl_in, 'rule', '10', 'source', 'any'])
cls.cli_set(cls, ['policy', 'access-list', acl_in, 'rule', '10', 'destination', 'any'])
cls.cli_set(cls, ['policy', 'access-list', acl_out, 'rule', '20', 'action', 'deny'])
cls.cli_set(cls, ['policy', 'access-list', acl_out, 'rule', '20', 'source', 'any'])
cls.cli_set(cls, ['policy', 'access-list', acl_out, 'rule', '20', 'destination', 'any'])
cls.cli_set(cls, ['policy', 'prefix-list', prefix_list_in, 'rule', '100', 'action', 'permit'])
cls.cli_set(cls, ['policy', 'prefix-list', prefix_list_in, 'rule', '100', 'prefix', '192.0.2.0/24'])
cls.cli_set(cls, ['policy', 'prefix-list', prefix_list_out, 'rule', '200', 'action', 'deny'])
cls.cli_set(cls, ['policy', 'prefix-list', prefix_list_out, 'rule', '200', 'prefix', '192.0.2.0/24'])
cls.cli_set(cls, ['policy', 'route-map', route_map, 'rule', '10', 'action', 'permit'])
@classmethod
def tearDownClass(cls):
cls.cli_delete(cls, ['policy', 'access-list', acl_in])
cls.cli_delete(cls, ['policy', 'access-list', acl_out])
cls.cli_delete(cls, ['policy', 'prefix-list', prefix_list_in])
cls.cli_delete(cls, ['policy', 'prefix-list', prefix_list_out])
cls.cli_delete(cls, ['policy', 'route-map', route_map])
super(TestProtocolsRIP, cls).tearDownClass()
def tearDown(self):
self.cli_delete(base_path)
self.cli_commit()
frrconfig = self.getFRRconfig('router rip', endsection='^exit')
self.assertNotIn(f'router rip', frrconfig)
# check process health and continuity
self.assertEqual(self.daemon_pid, process_named_running(rip_daemon))
def test_rip_01_parameters(self):
distance = '40'
network_distance = '66'
metric = '8'
interfaces = Section.interfaces('ethernet')
neighbors = ['1.2.3.4', '1.2.3.5', '1.2.3.6']
networks = ['10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16']
redistribute = ['bgp', 'connected', 'isis', 'kernel', 'nhrp', 'ospf', 'static']
timer_garbage = '888'
timer_timeout = '1000'
timer_update = '90'
self.cli_set(base_path + ['default-distance', distance])
self.cli_set(base_path + ['default-information', 'originate'])
self.cli_set(base_path + ['default-metric', metric])
self.cli_set(base_path + ['distribute-list', 'access-list', 'in', acl_in])
self.cli_set(base_path + ['distribute-list', 'access-list', 'out', acl_out])
self.cli_set(base_path + ['distribute-list', 'prefix-list', 'in', prefix_list_in])
self.cli_set(base_path + ['distribute-list', 'prefix-list', 'out', prefix_list_out])
self.cli_set(base_path + ['passive-interface', 'default'])
self.cli_set(base_path + ['timers', 'garbage-collection', timer_garbage])
self.cli_set(base_path + ['timers', 'timeout', timer_timeout])
self.cli_set(base_path + ['timers', 'update', timer_update])
for interface in interfaces:
self.cli_set(base_path + ['interface', interface])
self.cli_set(base_path + ['distribute-list', 'interface', interface, 'access-list', 'in', acl_in])
self.cli_set(base_path + ['distribute-list', 'interface', interface, 'access-list', 'out', acl_out])
self.cli_set(base_path + ['distribute-list', 'interface', interface, 'prefix-list', 'in', prefix_list_in])
self.cli_set(base_path + ['distribute-list', 'interface', interface, 'prefix-list', 'out', prefix_list_out])
for neighbor in neighbors:
self.cli_set(base_path + ['neighbor', neighbor])
for network in networks:
self.cli_set(base_path + ['network', network])
self.cli_set(base_path + ['network-distance', network, 'distance', network_distance])
self.cli_set(base_path + ['route', network])
for proto in redistribute:
self.cli_set(base_path + ['redistribute', proto, 'metric', metric])
self.cli_set(base_path + ['redistribute', proto, 'route-map', route_map])
# commit changes
self.cli_commit()
# Verify FRR ripd configuration
frrconfig = self.getFRRconfig('router rip', endsection='^exit')
self.assertIn(f'router rip', frrconfig)
self.assertIn(f' distance {distance}', frrconfig)
self.assertIn(f' default-information originate', frrconfig)
self.assertIn(f' default-metric {metric}', frrconfig)
self.assertIn(f' distribute-list {acl_in} in', frrconfig)
self.assertIn(f' distribute-list {acl_out} out', frrconfig)
self.assertIn(f' distribute-list prefix {prefix_list_in} in', frrconfig)
self.assertIn(f' distribute-list prefix {prefix_list_out} out', frrconfig)
self.assertIn(f' passive-interface default', frrconfig)
self.assertIn(f' timers basic {timer_update} {timer_timeout} {timer_garbage}', frrconfig)
for interface in interfaces:
self.assertIn(f' network {interface}', frrconfig)
self.assertIn(f' distribute-list {acl_in} in {interface}', frrconfig)
self.assertIn(f' distribute-list {acl_out} out {interface}', frrconfig)
self.assertIn(f' distribute-list prefix {prefix_list_in} in {interface}', frrconfig)
self.assertIn(f' distribute-list prefix {prefix_list_out} out {interface}', frrconfig)
for neighbor in neighbors:
self.assertIn(f' neighbor {neighbor}', frrconfig)
for network in networks:
self.assertIn(f' network {network}', frrconfig)
self.assertIn(f' distance {network_distance} {network}', frrconfig)
self.assertIn(f' route {network}', frrconfig)
for proto in redistribute:
self.assertIn(f' redistribute {proto} metric {metric} route-map {route_map}', frrconfig)
def test_rip_02_zebra_route_map(self):
# Implemented because of T3328
self.cli_set(base_path + ['route-map', route_map])
# commit changes
self.cli_commit()
# Verify FRR configuration
zebra_route_map = f'ip protocol rip route-map {route_map}'
frrconfig = self.getFRRconfig(zebra_route_map)
self.assertIn(zebra_route_map, frrconfig)
# Remove the route-map again
self.cli_delete(base_path + ['route-map'])
# commit changes
self.cli_commit()
# Verify FRR configuration
frrconfig = self.getFRRconfig(zebra_route_map)
self.assertNotIn(zebra_route_map, frrconfig)
def test_rip_03_version(self):
rx_version = '1'
tx_version = '2'
interface = 'eth0'
self.cli_set(base_path + ['version', tx_version])
self.cli_set(base_path + ['interface', interface, 'send', 'version', tx_version])
self.cli_set(base_path + ['interface', interface, 'receive', 'version', rx_version])
# commit changes
self.cli_commit()
# Verify FRR configuration
frrconfig = self.getFRRconfig('router rip', endsection='^exit')
self.assertIn(f'version {tx_version}', frrconfig)
frrconfig = self.getFRRconfig(f'interface {interface}', endsection='^exit')
self.assertIn(f' ip rip receive version {rx_version}', frrconfig)
self.assertIn(f' ip rip send version {tx_version}', frrconfig)
if __name__ == '__main__':
unittest.main(verbosity=2, failfast=VyOSUnitTestSHIM.TestCase.debug_on())
diff --git a/smoketest/scripts/cli/test_protocols_ripng.py b/smoketest/scripts/cli/test_protocols_ripng.py
index 305f31db5..07546ad08 100755
--- a/smoketest/scripts/cli/test_protocols_ripng.py
+++ b/smoketest/scripts/cli/test_protocols_ripng.py
@@ -1,166 +1,163 @@
#!/usr/bin/env python3
#
# Copyright VyOS maintainers and contributors <maintainers@vyos.io>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
-from base_vyostest_shim import CSTORE_GUARD_TIME
from vyos.ifconfig import Section
from vyos.frrender import ripng_daemon
from vyos.utils.process import process_named_running
acl_in = '198'
acl_out = '199'
prefix_list_in = 'foo-prefix'
prefix_list_out = 'bar-prefix'
route_map = 'FooBar123'
base_path = ['protocols', 'ripng']
class TestProtocolsRIPng(VyOSUnitTestSHIM.TestCase):
@classmethod
def setUpClass(cls):
# call base-classes classmethod
super(TestProtocolsRIPng, cls).setUpClass()
# Retrieve FRR daemon PID - it is not allowed to crash, thus PID must remain the same
cls.daemon_pid = process_named_running(ripng_daemon)
# ensure we can also run this test on a live system - so lets clean
# out the current configuration :)
cls.cli_delete(cls, base_path)
- # Enable CSTORE guard time required by FRR related tests
- cls._commit_guard_time = CSTORE_GUARD_TIME
cls.cli_set(cls, ['policy', 'access-list6', acl_in, 'rule', '10', 'action', 'permit'])
cls.cli_set(cls, ['policy', 'access-list6', acl_in, 'rule', '10', 'source', 'any'])
cls.cli_set(cls, ['policy', 'access-list6', acl_out, 'rule', '20', 'action', 'deny'])
cls.cli_set(cls, ['policy', 'access-list6', acl_out, 'rule', '20', 'source', 'any'])
cls.cli_set(cls, ['policy', 'prefix-list6', prefix_list_in, 'rule', '100', 'action', 'permit'])
cls.cli_set(cls, ['policy', 'prefix-list6', prefix_list_in, 'rule', '100', 'prefix', '2001:db8::/32'])
cls.cli_set(cls, ['policy', 'prefix-list6', prefix_list_out, 'rule', '200', 'action', 'deny'])
cls.cli_set(cls, ['policy', 'prefix-list6', prefix_list_out, 'rule', '200', 'prefix', '2001:db8::/32'])
cls.cli_set(cls, ['policy', 'route-map', route_map, 'rule', '10', 'action', 'permit'])
@classmethod
def tearDownClass(cls):
# call base-classes classmethod
super(TestProtocolsRIPng, cls).tearDownClass()
cls.cli_delete(cls, ['policy', 'access-list6', acl_in])
cls.cli_delete(cls, ['policy', 'access-list6', acl_out])
cls.cli_delete(cls, ['policy', 'prefix-list6', prefix_list_in])
cls.cli_delete(cls, ['policy', 'prefix-list6', prefix_list_out])
cls.cli_delete(cls, ['policy', 'route-map', route_map])
def tearDown(self):
self.cli_delete(base_path)
self.cli_commit()
frrconfig = self.getFRRconfig('router ripng', endsection='^exit')
self.assertNotIn(f'router ripng', frrconfig)
# check process health and continuity
self.assertEqual(self.daemon_pid, process_named_running(ripng_daemon))
def test_ripng_01_parameters(self):
metric = '8'
interfaces = Section.interfaces('ethernet')
aggregates = ['2001:db8:1000::/48', '2001:db8:2000::/48', '2001:db8:3000::/48']
networks = ['2001:db8:1000::/64', '2001:db8:1001::/64', '2001:db8:2000::/64', '2001:db8:2001::/64']
redistribute = ['bgp', 'connected', 'kernel', 'ospfv3', 'static']
timer_garbage = '888'
timer_timeout = '1000'
timer_update = '90'
self.cli_set(base_path + ['default-information', 'originate'])
self.cli_set(base_path + ['default-metric', metric])
self.cli_set(base_path + ['distribute-list', 'access-list', 'in', acl_in])
self.cli_set(base_path + ['distribute-list', 'access-list', 'out', acl_out])
self.cli_set(base_path + ['distribute-list', 'prefix-list', 'in', prefix_list_in])
self.cli_set(base_path + ['distribute-list', 'prefix-list', 'out', prefix_list_out])
self.cli_set(base_path + ['passive-interface', 'default'])
self.cli_set(base_path + ['timers', 'garbage-collection', timer_garbage])
self.cli_set(base_path + ['timers', 'timeout', timer_timeout])
self.cli_set(base_path + ['timers', 'update', timer_update])
for aggregate in aggregates:
self.cli_set(base_path + ['aggregate-address', aggregate])
for interface in interfaces:
self.cli_set(base_path + ['interface', interface])
self.cli_set(base_path + ['distribute-list', 'interface', interface, 'access-list', 'in', acl_in])
self.cli_set(base_path + ['distribute-list', 'interface', interface, 'access-list', 'out', acl_out])
self.cli_set(base_path + ['distribute-list', 'interface', interface, 'prefix-list', 'in', prefix_list_in])
self.cli_set(base_path + ['distribute-list', 'interface', interface, 'prefix-list', 'out', prefix_list_out])
for network in networks:
self.cli_set(base_path + ['network', network])
self.cli_set(base_path + ['route', network])
for proto in redistribute:
self.cli_set(base_path + ['redistribute', proto, 'metric', metric])
self.cli_set(base_path + ['redistribute', proto, 'route-map', route_map])
# commit changes
self.cli_commit()
# Verify FRR ospfd configuration
frrconfig = self.getFRRconfig('router ripng', endsection='^exit')
self.assertIn(f'router ripng', frrconfig)
self.assertIn(f' default-information originate', frrconfig)
self.assertIn(f' default-metric {metric}', frrconfig)
self.assertIn(f' ipv6 distribute-list {acl_in} in', frrconfig)
self.assertIn(f' ipv6 distribute-list {acl_out} out', frrconfig)
self.assertIn(f' ipv6 distribute-list prefix {prefix_list_in} in', frrconfig)
self.assertIn(f' ipv6 distribute-list prefix {prefix_list_out} out', frrconfig)
self.assertIn(f' passive-interface default', frrconfig)
self.assertIn(f' timers basic {timer_update} {timer_timeout} {timer_garbage}', frrconfig)
for aggregate in aggregates:
self.assertIn(f' aggregate-address {aggregate}', frrconfig)
for interface in interfaces:
self.assertIn(f' network {interface}', frrconfig)
self.assertIn(f' ipv6 distribute-list {acl_in} in {interface}', frrconfig)
self.assertIn(f' ipv6 distribute-list {acl_out} out {interface}', frrconfig)
self.assertIn(f' ipv6 distribute-list prefix {prefix_list_in} in {interface}', frrconfig)
self.assertIn(f' ipv6 distribute-list prefix {prefix_list_out} out {interface}', frrconfig)
for network in networks:
self.assertIn(f' network {network}', frrconfig)
self.assertIn(f' route {network}', frrconfig)
for proto in redistribute:
if proto == 'ospfv3':
proto = 'ospf6'
self.assertIn(f' redistribute {proto} metric {metric} route-map {route_map}', frrconfig)
def test_ripng_02_zebra_route_map(self):
# Implemented because of T3328
self.cli_set(base_path + ['route-map', route_map])
# commit changes
self.cli_commit()
# Verify FRR configuration
zebra_route_map = f'ipv6 protocol ripng route-map {route_map}'
frrconfig = self.getFRRconfig(zebra_route_map)
self.assertIn(zebra_route_map, frrconfig)
# Remove the route-map again
self.cli_delete(base_path + ['route-map'])
# commit changes
self.cli_commit()
# Verify FRR configuration
frrconfig = self.getFRRconfig(zebra_route_map)
self.assertNotIn(zebra_route_map, frrconfig)
if __name__ == '__main__':
unittest.main(verbosity=2, failfast=VyOSUnitTestSHIM.TestCase.debug_on())
diff --git a/smoketest/scripts/cli/test_protocols_rpki.py b/smoketest/scripts/cli/test_protocols_rpki.py
index 5a049dde1..4e1a18c74 100755
--- a/smoketest/scripts/cli/test_protocols_rpki.py
+++ b/smoketest/scripts/cli/test_protocols_rpki.py
@@ -1,327 +1,324 @@
#!/usr/bin/env python3
#
# Copyright VyOS maintainers and contributors <maintainers@vyos.io>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
-from base_vyostest_shim import CSTORE_GUARD_TIME
from vyos.configsession import ConfigSessionError
from vyos.frrender import bgp_daemon
from vyos.utils.file import read_file
from vyos.utils.process import process_named_running
base_path = ['protocols', 'rpki']
base_frr_config_args = {'string': 'rpki', 'endsection': '^exit'}
vrf = 'blue'
vrf_path = ['vrf', 'name', vrf]
vrf_frr_config_args = {'string': f'vrf {vrf}', 'endsection':'^exit-vrf',
'substring': ' rpki', 'endsubsection': '^ exit'}
rpki_key_name = 'rpki-smoketest'
rpki_key_type = 'ssh-rsa'
rpki_ssh_key = """
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABFwAAAAdzc2gtcn
NhAAAAAwEAAQAAAQEAweDyflDFR4qyEwETbJkZ2ZZc+sJNiDTvYpwGsWIkju49lJSxHe1x
Kf8FhwfyMu40Snt1yDlRmmmz4CsbLgbuZGMPvXG11e34+C0pSVUvpF6aqRTeLl1pDRK7Rn
jgm3su+I8SRLQR4qbLG6VXWOFuVpwiqbExLaU0hFYTPNP+dArNpsWEEKsohk6pTXdhg3Vz
Wp3vCMjl2JTshDa3lD7p2xISSAReEY0fnfEAmQzH4Z6DIwwGdFuMWoQIg+oFBM9ARrO2/F
IjRsz6AecR/WeU72JEw4aJic1/cAJQA6PiQBHwkuo3Wll1tbpxeRZoB2NQG22ETyJLvhfT
aooNLT9HpQAAA8joU5dM6FOXTAAAAAdzc2gtcnNhAAABAQDB4PJ+UMVHirITARNsmRnZll
z6wk2INO9inAaxYiSO7j2UlLEd7XEp/wWHB/Iy7jRKe3XIOVGaabPgKxsuBu5kYw+9cbXV
7fj4LSlJVS+kXpqpFN4uXWkNErtGeOCbey74jxJEtBHipssbpVdY4W5WnCKpsTEtpTSEVh
M80/50Cs2mxYQQqyiGTqlNd2GDdXNane8IyOXYlOyENreUPunbEhJIBF4RjR+d8QCZDMfh
noMjDAZ0W4xahAiD6gUEz0BGs7b8UiNGzPoB5xH9Z5TvYkTDhomJzX9wAlADo+JAEfCS6j
daWXW1unF5FmgHY1AbbYRPIku+F9Nqig0tP0elAAAAAwEAAQAAAQACkDlUjzfUhtJs6uY5
WNrdJB5NmHUS+HQzzxFNlhkapK6+wKqI1UNaRUtq6iF7J+gcFf7MK2nXS098BsXguWm8fQ
zPuemoDvHsQhiaJhyvpSqRUrvPTB/f8t/0AhQiKiJIWgfpTaIw53inAGwjujNNxNm2eafH
TThhCYxOkRT7rsT6bnSio6yeqPy5QHg7IKFztp5FXDUyiOS3aX3SvzQcDUkMXALdvzX50t
1XIk+X48Rgkq72dL4VpV2oMNDu3hM6FqBUplf9Mv3s51FNSma/cibCQoVufrIfoqYjkNTj
IpYFUcq4zZ0/KvgXgzSsy9VN/4TtbalrOuu7X/SHJbvhAAAAgGPFsXgONYQvXxCnK1dIue
ozgaZg1I/n522E2ZCOXBW4dYJVyNpppwRreDzuFzTDEe061MpNHfScjVBJCCulivFYWscL
6oaGsryDbFxO3QmB4I98UBqrds2yan9/JGc6EYe299yvaHy7Y64+NC0+fN8H2RAZ61T4w1
0JrCaJRyvzAAAAgQDvBfuV1U7o9k/fbU+U7W2UYnWblpOZAMfi1XQP6IJJeyWs90PdTdXh
+l0eIQrCawIiRJytNfxMmbD4huwTf77fWiyCcPznmALQ7ex/yJ+W5Z0V4dPGF3h7o1uiS2
36JhQ7mfcliCkhp/1PIklBIMPcCp0zl+s9wMv2hX7w1Pah9QAAAIEAz6YgU9Xute+J+dBw
oWxEQ+igR6KE55Um7O9AvSrqnCm9r7lSFsXC2ErYOxoDSJ3yIBEV0b4XAGn6tbbVIs3jS8
BnLHxclAHQecOx1PGn7PKbnPW0oJRq/X9QCIEelKYvlykpayn7uZooTXqcDaPZxfPpmPdy
e8chVJvdygi7kPEAAAAMY3BvQExSMS53dWUzAQIDBAUGBw==
"""
rpki_ssh_pub = """
AAAAB3NzaC1yc2EAAAADAQABAAABAQDB4PJ+UMVHirITARNsmRnZllz6wk2INO9inAaxYi
SO7j2UlLEd7XEp/wWHB/Iy7jRKe3XIOVGaabPgKxsuBu5kYw+9cbXV7fj4LSlJVS+kXpqp
FN4uXWkNErtGeOCbey74jxJEtBHipssbpVdY4W5WnCKpsTEtpTSEVhM80/50Cs2mxYQQqy
iGTqlNd2GDdXNane8IyOXYlOyENreUPunbEhJIBF4RjR+d8QCZDMfhnoMjDAZ0W4xahAiD
6gUEz0BGs7b8UiNGzPoB5xH9Z5TvYkTDhomJzX9wAlADo+JAEfCS6jdaWXW1unF5FmgHY1
AbbYRPIku+F9Nqig0tP0el
"""
rpki_ssh_key_replacement = """
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABFwAAAAdzc2gtcn
NhAAAAAwEAAQAAAQEAtLPMwiGR3o6puPDbus9Yqoah9/7rv7i6ykykPmcEZ6ERnA0N6bl7
LkQxnCuX270ukTTZOhROvQnvQYIZohCMz27Q16z7r+I755QXL0x8x4Gqhg/hQUY7UtX6ts
db8+pO7G1PL4r9zT6/KJAF/wv86DezJ3I6TMaA7MCikXfQWJisBvhgAXF1+7V9CWaroGgV
/hHzQJu1yd4cfsYoHyeDaZ+lwFw4egNItIy63fIGDxrnXaonJ1ODGQh7zWlpl/cwQR/KyJ
P8vvOZ9olQ6syZV+DAcAo4Fe59wW2Zj4bl8bdGcdiDn0grkafxwTcg9ynr9kwQ8b66oXY4
hwB4vlPFPwAAA8jkGyX45Bsl+AAAAAdzc2gtcnNhAAABAQC0s8zCIZHejqm48Nu6z1iqhq
H3/uu/uLrKTKQ+ZwRnoRGcDQ3puXsuRDGcK5fbvS6RNNk6FE69Ce9BghmiEIzPbtDXrPuv
4jvnlBcvTHzHgaqGD+FBRjtS1fq2x1vz6k7sbU8viv3NPr8okAX/C/zoN7MncjpMxoDswK
KRd9BYmKwG+GABcXX7tX0JZqugaBX+EfNAm7XJ3hx+xigfJ4Npn6XAXDh6A0i0jLrd8gYP
GuddqicnU4MZCHvNaWmX9zBBH8rIk/y+85n2iVDqzJlX4MBwCjgV7n3BbZmPhuXxt0Zx2I
OfSCuRp/HBNyD3Kev2TBDxvrqhdjiHAHi+U8U/AAAAAwEAAQAAAQA99gkX5/rknXaE+9Hc
VIzKrC+NodOkgetKwszuuNRB1HD9WVyT8A3U5307V5dSuaPmFoEF8UCugWGQzNONRq+B0T
W7Po1u2dxAo/7vMQL4RfX60icjAroExWqakfFtycIWP8UPQFGWtxVFC12C/tFRrwe3Vuu2
t7otdEBKMRM3zU0Hj88/5FIk/MDhththDCKTMe4+iwNKo30dyqSCckpTd2k5de9JYz8Aom
87jtQcyDdynaELSo9CsA8KRPlozZ4VSWTVLH+Cv2TZWPL7hy79YvvIfuF/Sd6PGkNwG1Vj
TAbq2Wx4uq+HmpNiz7W0LnbZtQJ7dzLA3FZlvQMC8fVBAAAAgQDWvImVZCyVWpoG+LnKY3
joegjKRYKdgKRPCqGoIHiYsqCRxqSRW3jsuQCCvk4YO3/ZmqORiGktK+5r8R1QEtwg5qbi
N7GZD34m7USNuqG2G/4puEly8syMmR6VRRvEURFQrpv2wniXNSefvsDc+WDqTfXGUxr+FT
478wkzjwc/fAAAAIEA9uP0Ym3OC3cZ5FOvmu51lxo5lqPlUeE78axg2I4u/9Il8nOvSVuq
B9X5wAUyGAGcUjT3EZmRAtL2sQxc5T0Vw3bnxCjzukEbFM+DRtYy1hXSOoGTTwKoMWBpho
R3X5uRLUQL/22C4rd7tSJpjqnZXIH0B5z2fFh4vzu8/SrgCrUAAACBALtep4BcGJfjfhfF
ODzQe7Rk7tsaX8pfNv6bQu0sR5C9pDURFRf0fRC0oqgeTuzq/vHPyNLsUUgTCpKWiLFmvU
G9pelLT3XPPgzA+g0gycM0unuX8kkP3T5VQAM/7u0+h1CaJ8A6cCkzvDJxYdfio3WR60OP
ulHg7HCcyomFLaSjAAAADGNwb0BMUjEud3VlMwECAwQFBg==
"""
rpki_ssh_pub_replacement = """
AAAAB3NzaC1yc2EAAAADAQABAAABAQC0s8zCIZHejqm48Nu6z1iqhqH3/uu/uLrKTKQ+Zw
RnoRGcDQ3puXsuRDGcK5fbvS6RNNk6FE69Ce9BghmiEIzPbtDXrPuv4jvnlBcvTHzHgaqG
D+FBRjtS1fq2x1vz6k7sbU8viv3NPr8okAX/C/zoN7MncjpMxoDswKKRd9BYmKwG+GABcX
X7tX0JZqugaBX+EfNAm7XJ3hx+xigfJ4Npn6XAXDh6A0i0jLrd8gYPGuddqicnU4MZCHvN
aWmX9zBBH8rIk/y+85n2iVDqzJlX4MBwCjgV7n3BbZmPhuXxt0Zx2IOfSCuRp/HBNyD3Ke
v2TBDxvrqhdjiHAHi+U8U/
"""
class TestProtocolsRPKI(VyOSUnitTestSHIM.TestCase):
@classmethod
def setUpClass(cls):
# call base-classes classmethod
super(TestProtocolsRPKI, cls).setUpClass()
# Retrieve FRR daemon PID - it is not allowed to crash, thus PID must remain the same
cls.daemon_pid = process_named_running(bgp_daemon)
# ensure we can also run this test on a live system - so lets clean
# out the current configuration :)
cls.cli_delete(cls, base_path)
cls.cli_delete(cls, vrf_path)
- # Enable CSTORE guard time required by FRR related tests
- cls._commit_guard_time = CSTORE_GUARD_TIME
def tearDown(self):
self.cli_delete(base_path)
self.cli_delete(vrf_path)
self.cli_commit()
frrconfig = self.getFRRconfig(**base_frr_config_args)
self.assertNotIn(f'rpki', frrconfig)
frrconfig = self.getFRRconfig(**vrf_frr_config_args)
self.assertNotIn(f'rpki', frrconfig)
# check process health and continuity
self.assertEqual(self.daemon_pid, process_named_running(bgp_daemon))
def test_rpki(self):
expire_interval = '3600'
polling_period = '600'
retry_interval = '300'
cache = {
'192.0.2.1' : {
'port' : '8080',
'preference' : '10'
},
'2001:db8::1' : {
'port' : '1234',
'preference' : '30'
},
'rpki.vyos.net' : {
'port' : '5678',
'preference' : '40'
},
}
for test_set in [ {'path': base_path, 'frrargs': base_frr_config_args},
{'path': vrf_path + base_path, 'frrargs': vrf_frr_config_args} ]:
if 'vrf' in test_set['path']:
self.cli_set(vrf_path + ['table', '1000'])
self.cli_set(test_set['path'] + ['expire-interval', expire_interval])
self.cli_set(test_set['path'] + ['polling-period', polling_period])
self.cli_set(test_set['path'] + ['retry-interval', retry_interval])
for peer, peer_config in cache.items():
self.cli_set(test_set['path'] + ['cache', peer, 'port', peer_config['port']])
self.cli_set(test_set['path'] + ['cache', peer, 'preference', peer_config['preference']])
# commit changes
self.cli_commit()
# Verify FRR configuration
frrconfig = self.getFRRconfig(**test_set['frrargs'])
self.assertIn(f'rpki expire_interval {expire_interval}', frrconfig)
self.assertIn(f'rpki polling_period {polling_period}', frrconfig)
self.assertIn(f'rpki retry_interval {retry_interval}', frrconfig)
for peer, peer_config in cache.items():
port = peer_config['port']
preference = peer_config['preference']
self.assertIn(f'rpki cache tcp {peer} {port} preference {preference}', frrconfig)
def test_rpki_ssh(self):
polling = '7200'
cache = {
'192.0.2.3' : {
'port' : '1234',
'username' : 'foo',
'preference' : '10'
},
'192.0.2.4' : {
'port' : '5678',
'username' : 'bar',
'preference' : '20'
},
}
self.cli_set(['pki', 'openssh', rpki_key_name, 'private', 'key', rpki_ssh_key.replace('\n','')])
self.cli_set(['pki', 'openssh', rpki_key_name, 'public', 'key', rpki_ssh_pub.replace('\n','')])
self.cli_set(['pki', 'openssh', rpki_key_name, 'public', 'type', rpki_key_type])
for test_set in [ {'path': base_path, 'frrargs': base_frr_config_args},
{'path': vrf_path + base_path, 'frrargs': vrf_frr_config_args} ]:
if 'vrf' in test_set['path']:
self.cli_set(vrf_path + ['table', '1000'])
for cache_name, cache_config in cache.items():
self.cli_set(test_set['path'] + ['cache', cache_name, 'port', cache_config['port']])
self.cli_set(test_set['path'] + ['cache', cache_name, 'preference', cache_config['preference']])
self.cli_set(test_set['path'] + ['cache', cache_name, 'ssh', 'username', cache_config['username']])
self.cli_set(test_set['path'] + ['cache', cache_name, 'ssh', 'key', rpki_key_name])
# commit changes
self.cli_commit()
# Verify FRR configuration
frrconfig = self.getFRRconfig(**test_set['frrargs'])
for cache_name, cache_config in cache.items():
port = cache_config['port']
preference = cache_config['preference']
username = cache_config['username']
self.assertIn(f'rpki cache ssh {cache_name} {port} {username} /run/frr/id_rpki_{cache_name} /run/frr/id_rpki_{cache_name}.pub preference {preference}', frrconfig)
# Verify content of SSH keys
tmp = read_file(f'/run/frr/id_rpki_{cache_name}')
self.assertIn(rpki_ssh_key.replace('\n',''), tmp)
tmp = read_file(f'/run/frr/id_rpki_{cache_name}.pub')
self.assertIn(rpki_ssh_pub.replace('\n',''), tmp)
# Change OpenSSH key and verify it was properly written to filesystem
self.cli_set(['pki', 'openssh', rpki_key_name, 'private', 'key', rpki_ssh_key_replacement.replace('\n','')])
self.cli_set(['pki', 'openssh', rpki_key_name, 'public', 'key', rpki_ssh_pub_replacement.replace('\n','')])
# commit changes
self.cli_commit()
for test_set in [ {'path': base_path, 'frrargs': base_frr_config_args},
{'path': vrf_path + base_path, 'frrargs': vrf_frr_config_args} ]:
frrconfig = self.getFRRconfig(**test_set['frrargs'])
for cache_name, cache_config in cache.items():
port = cache_config['port']
preference = cache_config['preference']
username = cache_config['username']
self.assertIn(f'rpki cache ssh {cache_name} {port} {username} /run/frr/id_rpki_{cache_name} /run/frr/id_rpki_{cache_name}.pub preference {preference}', frrconfig)
# Verify content of SSH keys
tmp = read_file(f'/run/frr/id_rpki_{cache_name}')
self.assertIn(rpki_ssh_key_replacement.replace('\n',''), tmp)
tmp = read_file(f'/run/frr/id_rpki_{cache_name}.pub')
self.assertIn(rpki_ssh_pub_replacement.replace('\n',''), tmp)
self.cli_delete(['pki', 'openssh'])
def test_rpki_verify_preference(self):
cache = {
'192.0.2.1' : {
'port' : '8080',
'preference' : '1'
},
'192.0.2.2' : {
'port' : '9090',
'preference' : '1'
},
}
for test_set in [ {'path': base_path, 'frrargs': base_frr_config_args},
{'path': vrf_path + base_path, 'frrargs': vrf_frr_config_args} ]:
if 'vrf' in test_set['path']:
self.cli_set(vrf_path + ['table', '1000'])
for peer, peer_config in cache.items():
self.cli_set(test_set['path'] + ['cache', peer, 'port', peer_config['port']])
self.cli_set(test_set['path'] + ['cache', peer, 'preference', peer_config['preference']])
# check validate() - preferences must be unique
with self.assertRaises(ConfigSessionError):
self.cli_commit()
def test_rpki_source_address(self):
peer = '192.0.2.1'
port = '8080'
preference = '1'
username = 'foo'
source_address = '100.10.10.1'
self.cli_set(['interfaces', 'ethernet', 'eth0', 'address', f'{source_address}/24'])
for test_set in [ {'path': base_path, 'frrargs': base_frr_config_args},
{'path': vrf_path + base_path, 'frrargs': vrf_frr_config_args} ]:
if 'vrf' in test_set['path']:
self.cli_set(vrf_path + ['table', '1000'])
# Configure a TCP cache server
self.cli_set(test_set['path'] + ['cache', peer, 'port', port])
self.cli_set(test_set['path'] + ['cache', peer, 'preference', preference])
self.cli_set(test_set['path'] + ['cache', peer, 'source-address', source_address])
self.cli_commit()
# Verify FRR configuration
frrconfig = self.getFRRconfig(**test_set['frrargs'])
self.assertIn(f'rpki cache tcp {peer} {port} source {source_address} preference {preference}', frrconfig)
self.cli_set(['pki', 'openssh', rpki_key_name, 'private', 'key', rpki_ssh_key.replace('\n', '')])
self.cli_set(['pki', 'openssh', rpki_key_name, 'public', 'key', rpki_ssh_pub.replace('\n', '')])
self.cli_set(['pki', 'openssh', rpki_key_name, 'public', 'type', rpki_key_type])
# Configure a SSH cache server
self.cli_set(test_set['path'] + ['cache', peer, 'ssh', 'username', username])
self.cli_set(test_set['path'] + ['cache', peer, 'ssh', 'key', rpki_key_name])
self.cli_commit()
# Verify FRR configuration
frrconfig = self.getFRRconfig(**test_set['frrargs'])
self.assertIn(
f'rpki cache ssh {peer} {port} {username} /run/frr/id_rpki_{peer} /run/frr/id_rpki_{peer}.pub source {source_address} preference {preference}',
frrconfig,
)
if __name__ == '__main__':
unittest.main(verbosity=2, failfast=VyOSUnitTestSHIM.TestCase.debug_on())
diff --git a/smoketest/scripts/cli/test_protocols_segment-routing.py b/smoketest/scripts/cli/test_protocols_segment-routing.py
index 258e23e75..1e935b150 100755
--- a/smoketest/scripts/cli/test_protocols_segment-routing.py
+++ b/smoketest/scripts/cli/test_protocols_segment-routing.py
@@ -1,190 +1,187 @@
#!/usr/bin/env python3
#
# Copyright VyOS maintainers and contributors <maintainers@vyos.io>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
-from base_vyostest_shim import CSTORE_GUARD_TIME
from vyos.configsession import ConfigSessionError
from vyos.ifconfig import Section
from vyos.frrender import zebra_daemon
from vyos.utils.process import process_named_running
from vyos.utils.system import sysctl_read
base_path = ['protocols', 'segment-routing']
class TestProtocolsSegmentRouting(VyOSUnitTestSHIM.TestCase):
@classmethod
def setUpClass(cls):
# call base-classes classmethod
super(TestProtocolsSegmentRouting, cls).setUpClass()
# Retrieve FRR daemon PID - it is not allowed to crash, thus PID must remain the same
cls.daemon_pid = process_named_running(zebra_daemon)
# ensure we can also run this test on a live system - so lets clean
# out the current configuration :)
cls.cli_delete(cls, base_path)
- # Enable CSTORE guard time required by FRR related tests
- cls._commit_guard_time = CSTORE_GUARD_TIME
def tearDown(self):
self.cli_delete(base_path)
self.cli_commit()
# check process health and continuity
self.assertEqual(self.daemon_pid, process_named_running(zebra_daemon))
def test_srv6(self):
interfaces = Section.interfaces('ethernet', vlan=False)
locators = {
'foo1': {'prefix': '2001:a::/64'},
'foo2': {'prefix': '2001:b::/64', 'usid': {}},
'foo3': {'prefix': '2001:c::/64', 'format': 'uncompressed-f4024'},
'foo4': {
'prefix': '2001:d::/48',
'block-len': '32',
'node-len': '16',
'func-bits': '16',
'usid': {},
'format': 'usid-f3216',
},
}
for locator, locator_config in locators.items():
self.cli_set(
base_path
+ ['srv6', 'locator', locator, 'prefix', locator_config['prefix']]
)
if 'block-len' in locator_config:
self.cli_set(
base_path
+ [
'srv6',
'locator',
locator,
'block-len',
locator_config['block-len'],
]
)
if 'node-len' in locator_config:
self.cli_set(
base_path
+ [
'srv6',
'locator',
locator,
'node-len',
locator_config['node-len'],
]
)
if 'func-bits' in locator_config:
self.cli_set(
base_path
+ [
'srv6',
'locator',
locator,
'func-bits',
locator_config['func-bits'],
]
)
if 'usid' in locator_config:
self.cli_set(base_path + ['srv6', 'locator', locator, 'behavior-usid'])
if 'format' in locator_config:
self.cli_set(
base_path
+ ['srv6', 'locator', locator, 'format', locator_config['format']]
)
# verify() - SRv6 should be enabled on at least one interface!
with self.assertRaises(ConfigSessionError):
self.cli_commit()
for interface in interfaces:
self.cli_set(base_path + ['interface', interface, 'srv6'])
self.cli_commit()
for interface in interfaces:
self.assertEqual(
sysctl_read(f'net.ipv6.conf.{interface}.seg6_enabled'), '1'
)
self.assertEqual(
sysctl_read(f'net.ipv6.conf.{interface}.seg6_require_hmac'), '0'
) # default
frrconfig = self.getFRRconfig('segment-routing', endsection='^exit')
self.assertIn('segment-routing', frrconfig)
self.assertIn(' srv6', frrconfig)
self.assertIn(' locators', frrconfig)
for locator, locator_config in locators.items():
prefix = locator_config['prefix']
block_len = locator_config.get('block-len', '40')
node_len = locator_config.get('node-len', '24')
func_bits = locator_config.get('func-bits', '16')
self.assertIn(f' locator {locator}', frrconfig)
self.assertIn(
f' prefix {prefix} block-len {block_len} node-len {node_len} func-bits {func_bits}',
frrconfig,
)
if 'format' in locator_config:
self.assertIn(f' format {locator_config["format"]}', frrconfig)
if 'usid' in locator_config:
self.assertIn(' behavior usid', frrconfig)
def test_srv6_sysctl(self):
interfaces = Section.interfaces('ethernet', vlan=False)
# HMAC accept
for interface in interfaces:
self.cli_set(base_path + ['interface', interface, 'srv6'])
self.cli_set(base_path + ['interface', interface, 'srv6', 'hmac', 'ignore'])
self.cli_commit()
for interface in interfaces:
self.assertEqual(
sysctl_read(f'net.ipv6.conf.{interface}.seg6_enabled'), '1'
)
self.assertEqual(
sysctl_read(f'net.ipv6.conf.{interface}.seg6_require_hmac'), '-1'
) # ignore
# HMAC drop
for interface in interfaces:
self.cli_set(base_path + ['interface', interface, 'srv6'])
self.cli_set(base_path + ['interface', interface, 'srv6', 'hmac', 'drop'])
self.cli_commit()
for interface in interfaces:
self.assertEqual(
sysctl_read(f'net.ipv6.conf.{interface}.seg6_enabled'), '1'
)
self.assertEqual(
sysctl_read(f'net.ipv6.conf.{interface}.seg6_require_hmac'), '1'
) # drop
# Disable SRv6 on first interface
first_if = interfaces[-1]
self.cli_delete(base_path + ['interface', first_if])
self.cli_commit()
self.assertEqual(sysctl_read(f'net.ipv6.conf.{first_if}.seg6_enabled'), '0')
if __name__ == '__main__':
unittest.main(verbosity=2, failfast=VyOSUnitTestSHIM.TestCase.debug_on())
diff --git a/smoketest/scripts/cli/test_protocols_static.py b/smoketest/scripts/cli/test_protocols_static.py
index 8f01cd101..54eefedb3 100755
--- a/smoketest/scripts/cli/test_protocols_static.py
+++ b/smoketest/scripts/cli/test_protocols_static.py
@@ -1,655 +1,652 @@
#!/usr/bin/env python3
#
# Copyright VyOS maintainers and contributors <maintainers@vyos.io>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
import unittest
from time import sleep
from base_vyostest_shim import VyOSUnitTestSHIM
-from base_vyostest_shim import CSTORE_GUARD_TIME
from vyos.configsession import ConfigSessionError
from vyos.template import is_ipv6
from vyos.template import get_dhcp_router
from vyos.utils.network import get_interface_config
from vyos.utils.network import get_vrf_tableid
from vyos.utils.process import process_named_running
from vyos.xml_ref import default_value
base_path = ['protocols', 'static']
vrf_path = ['protocols', 'vrf']
routes = {
'10.0.0.0/8' : {
'next_hop' : {
'192.0.2.100' : { 'distance' : '100' },
'192.0.2.110' : { 'distance' : '110', 'interface' : 'eth0' },
'192.0.2.120' : { 'distance' : '120', 'disable' : '' },
'192.0.2.130' : { 'bfd' : '' },
'192.0.2.131' : { 'bfd' : '',
'bfd_profile' : 'vyos1' },
'192.0.2.140' : { 'bfd' : '',
'bfd_source' : '192.0.2.10',
'bfd_profile' : 'vyos2' },
},
'interface' : {
'eth0' : { 'distance' : '130' },
'eth1' : { 'distance' : '140' },
},
'blackhole' : { 'distance' : '250', 'tag' : '500' },
},
'172.16.0.0/12' : {
'interface' : {
'eth0' : { 'distance' : '50', 'vrf' : 'black' },
'eth1' : { 'distance' : '60', 'vrf' : 'black' },
},
'blackhole' : { 'distance' : '90' },
},
'192.0.2.0/24' : {
'interface' : {
'eth0' : { 'distance' : '50', 'vrf' : 'black' },
'eth1' : { 'disable' : '' },
},
'blackhole' : { 'distance' : '90' },
},
'100.64.0.0/16' : {
'blackhole' : {},
},
'100.65.0.0/16' : {
'reject' : { 'distance' : '10', 'tag' : '200' },
},
'100.66.0.0/16' : {
'blackhole' : {},
'reject' : { 'distance' : '10', 'tag' : '200' },
},
'2001:db8:100::/40' : {
'next_hop' : {
'2001:db8::1' : { 'distance' : '10' },
'2001:db8::2' : { 'distance' : '20', 'interface' : 'eth0' },
'2001:db8::3' : { 'distance' : '30', 'disable' : '' },
'2001:db8::4' : { 'bfd' : '' },
'2001:db8::5' : { 'bfd_source' : '2001:db8::ffff' },
},
'interface' : {
'eth0' : { 'distance' : '40', 'vrf' : 'black' },
'eth1' : { 'distance' : '50', 'disable' : '' },
},
'blackhole' : { 'distance' : '250', 'tag' : '500' },
},
'2001:db8:200::/40' : {
'interface' : {
'eth0' : { 'distance' : '40' },
'eth1' : { 'distance' : '50', 'disable' : '' },
},
'blackhole' : { 'distance' : '250', 'tag' : '500' },
},
'2001:db8:300::/40' : {
'reject' : { 'distance' : '250', 'tag' : '500' },
},
'2001:db8:400::/40' : {
'next_hop' : {
'2001:db8::400' : { 'segments' : '2001:db8:aaaa::400/2002::400/2003::400/2004::400' },
},
},
'2001:db8:500::/40' : {
'next_hop' : {
'2001:db8::500' : { 'segments' : '2001:db8:aaaa::500/2002::500/2003::500/2004::500' },
},
},
'2001:db8:600::/40' : {
'interface' : {
'eth0' : { 'segments' : '2001:db8:aaaa::600/2002::600' },
},
},
'2001:db8:700::/40' : {
'interface' : {
'eth1' : { 'segments' : '2001:db8:aaaa::700' },
},
},
'2001:db8::/32' : {
'blackhole' : { 'distance' : '200', 'tag' : '600' }
},
}
multicast_routes = {
'224.0.0.0/24' : {
'next_hop' : {
'224.203.0.1' : { },
'224.203.0.2' : { 'distance' : '110'},
},
},
'224.1.0.0/24' : {
'next_hop' : {
'224.205.0.1' : { 'disable' : {} },
'224.205.0.2' : { 'distance' : '110'},
},
},
'224.2.0.0/24' : {
'next_hop' : {
'1.2.3.0' : { },
'1.2.3.1' : { 'distance' : '110'},
},
},
'224.10.0.0/24' : {
'interface' : {
'eth1' : { 'disable' : {} },
'eth2' : { 'distance' : '110'},
},
},
'224.11.0.0/24' : {
'interface' : {
'eth0' : { },
'eth1' : { 'distance' : '10'},
},
},
'224.12.0.0/24' : {
'interface' : {
'eth0' : { },
'eth1' : { 'distance' : '200'},
},
},
}
tables = ['80', '81', '82']
class TestProtocolsStatic(VyOSUnitTestSHIM.TestCase):
@classmethod
def setUpClass(cls):
super(TestProtocolsStatic, cls).setUpClass()
cls.cli_delete(cls, base_path)
cls.cli_delete(cls, ['vrf'])
- # Enable CSTORE guard time required by FRR related tests
- cls._commit_guard_time = CSTORE_GUARD_TIME
@classmethod
def tearDownClass(cls):
cls.cli_delete(cls, base_path)
cls.cli_delete(cls, ['vrf'])
super(TestProtocolsStatic, cls).tearDownClass()
def tearDown(self):
self.cli_delete(base_path)
self.cli_delete(['vrf'])
self.cli_commit()
v4route = self.getFRRconfig('ip route', end='')
self.assertFalse(v4route)
v6route = self.getFRRconfig('ipv6 route', end='')
self.assertFalse(v6route)
def test_01_static(self):
self.cli_set(['vrf', 'name', 'black', 'table', '43210'])
for route, route_config in routes.items():
route_type = 'route'
if is_ipv6(route):
route_type = 'route6'
base = base_path + [route_type, route]
if 'next_hop' in route_config:
for next_hop, next_hop_config in route_config['next_hop'].items():
self.cli_set(base + ['next-hop', next_hop])
if 'disable' in next_hop_config:
self.cli_set(base + ['next-hop', next_hop, 'disable'])
if 'distance' in next_hop_config:
self.cli_set(base + ['next-hop', next_hop, 'distance', next_hop_config['distance']])
if 'interface' in next_hop_config:
self.cli_set(base + ['next-hop', next_hop, 'interface', next_hop_config['interface']])
if 'vrf' in next_hop_config:
self.cli_set(base + ['next-hop', next_hop, 'vrf', next_hop_config['vrf']])
if 'bfd' in next_hop_config:
self.cli_set(base + ['next-hop', next_hop, 'bfd'])
if 'bfd_profile' in next_hop_config:
self.cli_set(base + ['next-hop', next_hop, 'bfd', 'profile', next_hop_config['bfd_profile']])
if 'bfd_source' in next_hop_config:
self.cli_set(base + ['next-hop', next_hop, 'bfd', 'multi-hop', 'source-address', next_hop_config['bfd_source']])
if 'segments' in next_hop_config:
self.cli_set(base + ['next-hop', next_hop, 'segments', next_hop_config['segments']])
if 'interface' in route_config:
for interface, interface_config in route_config['interface'].items():
self.cli_set(base + ['interface', interface])
if 'disable' in interface_config:
self.cli_set(base + ['interface', interface, 'disable'])
if 'distance' in interface_config:
self.cli_set(base + ['interface', interface, 'distance', interface_config['distance']])
if 'vrf' in interface_config:
self.cli_set(base + ['interface', interface, 'vrf', interface_config['vrf']])
if 'segments' in interface_config:
self.cli_set(base + ['interface', interface, 'segments', interface_config['segments']])
if 'blackhole' in route_config:
self.cli_set(base + ['blackhole'])
if 'distance' in route_config['blackhole']:
self.cli_set(base + ['blackhole', 'distance', route_config['blackhole']['distance']])
if 'tag' in route_config['blackhole']:
self.cli_set(base + ['blackhole', 'tag', route_config['blackhole']['tag']])
if 'reject' in route_config:
self.cli_set(base + ['reject'])
if 'distance' in route_config['reject']:
self.cli_set(base + ['reject', 'distance', route_config['reject']['distance']])
if 'tag' in route_config['reject']:
self.cli_set(base + ['reject', 'tag', route_config['reject']['tag']])
if {'blackhole', 'reject'} <= set(route_config):
# Can not use blackhole and reject at the same time
with self.assertRaises(ConfigSessionError):
self.cli_commit()
self.cli_delete(base + ['blackhole'])
self.cli_delete(base + ['reject'])
# commit changes
self.cli_commit()
# Verify FRR bgpd configuration
frrconfig = self.getFRRconfig('ip route', end='')
# Verify routes
for route, route_config in routes.items():
ip_ipv6 = 'ip'
if is_ipv6(route):
ip_ipv6 = 'ipv6'
if 'next_hop' in route_config:
for next_hop, next_hop_config in route_config['next_hop'].items():
tmp = f'{ip_ipv6} route {route} {next_hop}'
if 'interface' in next_hop_config:
tmp += ' ' + next_hop_config['interface']
if 'distance' in next_hop_config:
tmp += ' ' + next_hop_config['distance']
if 'vrf' in next_hop_config:
tmp += ' nexthop-vrf ' + next_hop_config['vrf']
if 'bfd' in next_hop_config:
tmp += ' bfd'
if 'bfd_source' in next_hop_config:
tmp += ' multi-hop source ' + next_hop_config['bfd_source']
if 'bfd_profile' in next_hop_config:
tmp += ' profile ' + next_hop_config['bfd_profile']
if 'segments' in next_hop_config:
tmp += ' segments ' + next_hop_config['segments']
if 'disable' in next_hop_config:
self.assertNotIn(tmp, frrconfig)
else:
self.assertIn(tmp, frrconfig)
if 'interface' in route_config:
for interface, interface_config in route_config['interface'].items():
tmp = f'{ip_ipv6} route {route} {interface}'
if 'interface' in interface_config:
tmp += ' ' + interface_config['interface']
if 'distance' in interface_config:
tmp += ' ' + interface_config['distance']
if 'vrf' in interface_config:
tmp += ' nexthop-vrf ' + interface_config['vrf']
if 'segments' in interface_config:
tmp += ' segments ' + interface_config['segments']
if 'disable' in interface_config:
self.assertNotIn(tmp, frrconfig)
else:
self.assertIn(tmp, frrconfig)
if {'blackhole', 'reject'} <= set(route_config):
# Can not use blackhole and reject at the same time
# Config error validated above - skip this route
continue
if 'blackhole' in route_config:
tmp = f'{ip_ipv6} route {route} blackhole'
if 'tag' in route_config['blackhole']:
tmp += ' tag ' + route_config['blackhole']['tag']
if 'distance' in route_config['blackhole']:
tmp += ' ' + route_config['blackhole']['distance']
self.assertIn(tmp, frrconfig)
if 'reject' in route_config:
tmp = f'{ip_ipv6} route {route} reject'
if 'tag' in route_config['reject']:
tmp += ' tag ' + route_config['reject']['tag']
if 'distance' in route_config['reject']:
tmp += ' ' + route_config['reject']['distance']
self.assertIn(tmp, frrconfig)
def test_02_static_table(self):
self.cli_set(['vrf', 'name', 'black', 'table', '43210'])
for table in tables:
for route, route_config in routes.items():
route_type = 'route'
if is_ipv6(route):
route_type = 'route6'
base = base_path + ['table', table, route_type, route]
if 'next_hop' in route_config:
for next_hop, next_hop_config in route_config['next_hop'].items():
self.cli_set(base + ['next-hop', next_hop])
if 'disable' in next_hop_config:
self.cli_set(base + ['next-hop', next_hop, 'disable'])
if 'distance' in next_hop_config:
self.cli_set(base + ['next-hop', next_hop, 'distance', next_hop_config['distance']])
if 'interface' in next_hop_config:
self.cli_set(base + ['next-hop', next_hop, 'interface', next_hop_config['interface']])
if 'vrf' in next_hop_config:
self.cli_set(base + ['next-hop', next_hop, 'vrf', next_hop_config['vrf']])
if 'interface' in route_config:
for interface, interface_config in route_config['interface'].items():
self.cli_set(base + ['interface', interface])
if 'disable' in interface_config:
self.cli_set(base + ['interface', interface, 'disable'])
if 'distance' in interface_config:
self.cli_set(base + ['interface', interface, 'distance', interface_config['distance']])
if 'vrf' in interface_config:
self.cli_set(base + ['interface', interface, 'vrf', interface_config['vrf']])
if 'blackhole' in route_config:
self.cli_set(base + ['blackhole'])
if 'distance' in route_config['blackhole']:
self.cli_set(base + ['blackhole', 'distance', route_config['blackhole']['distance']])
if 'tag' in route_config['blackhole']:
self.cli_set(base + ['blackhole', 'tag', route_config['blackhole']['tag']])
# commit changes
self.cli_commit()
# Verify FRR bgpd configuration
frrconfig = self.getFRRconfig('ip route', end='')
for table in tables:
# Verify routes
for route, route_config in routes.items():
ip_ipv6 = 'ip'
if is_ipv6(route):
ip_ipv6 = 'ipv6'
if 'next_hop' in route_config:
for next_hop, next_hop_config in route_config['next_hop'].items():
tmp = f'{ip_ipv6} route {route} {next_hop}'
if 'interface' in next_hop_config:
tmp += ' ' + next_hop_config['interface']
if 'distance' in next_hop_config:
tmp += ' ' + next_hop_config['distance']
if 'vrf' in next_hop_config:
tmp += ' nexthop-vrf ' + next_hop_config['vrf']
tmp += ' table ' + table
if 'disable' in next_hop_config:
self.assertNotIn(tmp, frrconfig)
else:
self.assertIn(tmp, frrconfig)
if 'interface' in route_config:
for interface, interface_config in route_config['interface'].items():
tmp = f'{ip_ipv6} route {route} {interface}'
if 'interface' in interface_config:
tmp += ' ' + interface_config['interface']
if 'distance' in interface_config:
tmp += ' ' + interface_config['distance']
if 'vrf' in interface_config:
tmp += ' nexthop-vrf ' + interface_config['vrf']
tmp += ' table ' + table
if 'disable' in interface_config:
self.assertNotIn(tmp, frrconfig)
else:
self.assertIn(tmp, frrconfig)
if 'blackhole' in route_config:
tmp = f'{ip_ipv6} route {route} blackhole'
if 'tag' in route_config['blackhole']:
tmp += ' tag ' + route_config['blackhole']['tag']
if 'distance' in route_config['blackhole']:
tmp += ' ' + route_config['blackhole']['distance']
tmp += ' table ' + table
self.assertIn(tmp, frrconfig)
def test_03_static_vrf(self):
self.cli_set(['vrf', 'name', 'black', 'table', '43210'])
# Create VRF instances and apply the static routes from above to FRR.
# Re-read the configured routes and match them if they are programmed
# properly. This also includes VRF leaking
vrfs = {
'red' : { 'table' : '1000' },
'green' : { 'table' : '2000' },
'blue' : { 'table' : '3000' },
}
for vrf, vrf_config in vrfs.items():
vrf_base_path = ['vrf', 'name', vrf]
self.cli_set(vrf_base_path + ['table', vrf_config['table']])
for route, route_config in routes.items():
route_type = 'route'
if is_ipv6(route):
route_type = 'route6'
route_base_path = vrf_base_path + ['protocols', 'static', route_type, route]
if 'next_hop' in route_config:
for next_hop, next_hop_config in route_config['next_hop'].items():
self.cli_set(route_base_path + ['next-hop', next_hop])
if 'disable' in next_hop_config:
self.cli_set(route_base_path + ['next-hop', next_hop, 'disable'])
if 'distance' in next_hop_config:
self.cli_set(route_base_path + ['next-hop', next_hop, 'distance', next_hop_config['distance']])
if 'interface' in next_hop_config:
self.cli_set(route_base_path + ['next-hop', next_hop, 'interface', next_hop_config['interface']])
if 'vrf' in next_hop_config:
self.cli_set(route_base_path + ['next-hop', next_hop, 'vrf', next_hop_config['vrf']])
if 'segments' in next_hop_config:
self.cli_set(route_base_path + ['next-hop', next_hop, 'segments', next_hop_config['segments']])
if 'interface' in route_config:
for interface, interface_config in route_config['interface'].items():
self.cli_set(route_base_path + ['interface', interface])
if 'disable' in interface_config:
self.cli_set(route_base_path + ['interface', interface, 'disable'])
if 'distance' in interface_config:
self.cli_set(route_base_path + ['interface', interface, 'distance', interface_config['distance']])
if 'vrf' in interface_config:
self.cli_set(route_base_path + ['interface', interface, 'vrf', interface_config['vrf']])
if 'segments' in interface_config:
self.cli_set(route_base_path + ['interface', interface, 'segments', interface_config['segments']])
if 'blackhole' in route_config:
self.cli_set(route_base_path + ['blackhole'])
if 'distance' in route_config['blackhole']:
self.cli_set(route_base_path + ['blackhole', 'distance', route_config['blackhole']['distance']])
if 'tag' in route_config['blackhole']:
self.cli_set(route_base_path + ['blackhole', 'tag', route_config['blackhole']['tag']])
# commit changes
self.cli_commit()
for vrf, vrf_config in vrfs.items():
tmp = get_interface_config(vrf)
# Compare VRF table ID
self.assertEqual(get_vrf_tableid(vrf), int(vrf_config['table']))
self.assertEqual(tmp['linkinfo']['info_kind'], 'vrf')
# Verify FRR bgpd configuration
frrconfig = self.getFRRconfig(f'vrf {vrf}', endsection='^exit-vrf')
self.assertIn(f'vrf {vrf}', frrconfig)
# Verify routes
for route, route_config in routes.items():
ip_ipv6 = 'ip'
if is_ipv6(route):
ip_ipv6 = 'ipv6'
if 'next_hop' in route_config:
for next_hop, next_hop_config in route_config['next_hop'].items():
tmp = f'{ip_ipv6} route {route} {next_hop}'
if 'interface' in next_hop_config:
tmp += ' ' + next_hop_config['interface']
if 'distance' in next_hop_config:
tmp += ' ' + next_hop_config['distance']
if 'vrf' in next_hop_config:
tmp += ' nexthop-vrf ' + next_hop_config['vrf']
if 'segments' in next_hop_config:
tmp += ' segments ' + next_hop_config['segments']
if 'disable' in next_hop_config:
self.assertNotIn(tmp, frrconfig)
else:
self.assertIn(tmp, frrconfig)
if 'interface' in route_config:
for interface, interface_config in route_config['interface'].items():
tmp = f'{ip_ipv6} route {route} {interface}'
if 'interface' in interface_config:
tmp += ' ' + interface_config['interface']
if 'distance' in interface_config:
tmp += ' ' + interface_config['distance']
if 'vrf' in interface_config:
tmp += ' nexthop-vrf ' + interface_config['vrf']
if 'segments' in interface_config:
tmp += ' segments ' + interface_config['segments']
if 'disable' in interface_config:
self.assertNotIn(tmp, frrconfig)
else:
self.assertIn(tmp, frrconfig)
if 'blackhole' in route_config:
tmp = f'{ip_ipv6} route {route} blackhole'
if 'tag' in route_config['blackhole']:
tmp += ' tag ' + route_config['blackhole']['tag']
if 'distance' in route_config['blackhole']:
tmp += ' ' + route_config['blackhole']['distance']
self.assertIn(tmp, frrconfig)
def test_04_static_multicast(self):
for route, route_config in multicast_routes.items():
if 'next_hop' in route_config:
base = base_path + ['mroute', route]
for next_hop, next_hop_config in route_config['next_hop'].items():
self.cli_set(base + ['next-hop', next_hop])
if 'distance' in next_hop_config:
self.cli_set(base + ['next-hop', next_hop, 'distance', next_hop_config['distance']])
if 'disable' in next_hop_config:
self.cli_set(base + ['next-hop', next_hop, 'disable'])
if 'interface' in route_config:
base = base_path + ['mroute', route]
for next_hop, next_hop_config in route_config['interface'].items():
self.cli_set(base + ['interface', next_hop])
if 'distance' in next_hop_config:
self.cli_set(base + ['interface', next_hop, 'distance', next_hop_config['distance']])
self.cli_commit()
# Verify FRR configuration
frrconfig = self.getFRRconfig('ip mroute', end='')
for route, route_config in multicast_routes.items():
if 'next_hop' in route_config:
for next_hop, next_hop_config in route_config['next_hop'].items():
tmp = f'ip mroute {route} {next_hop}'
if 'distance' in next_hop_config:
tmp += ' ' + next_hop_config['distance']
if 'disable' in next_hop_config:
self.assertNotIn(tmp, frrconfig)
else:
self.assertIn(tmp, frrconfig)
if 'next_hop_interface' in route_config:
for next_hop, next_hop_config in route_config['next_hop_interface'].items():
tmp = f'ip mroute {route} {next_hop}'
if 'distance' in next_hop_config:
tmp += ' ' + next_hop_config['distance']
if 'disable' in next_hop_config:
self.assertNotIn(tmp, frrconfig)
else:
self.assertIn(tmp, frrconfig)
def test_05_dhcp_default_route(self):
# When running via vyos-build under the QEMU environment a local DHCP
# server is available. This test verifies that the default route is set.
# When not running under the VyOS QEMU environment, this test is skipped.
if not os.path.exists('/tmp/vyos.smoketests.hint'):
self.skipTest('Not running under VyOS CI/CD QEMU environment!')
interface = 'eth0'
interface_path = ['interfaces', 'ethernet', interface]
default_distance = default_value(interface_path + ['dhcp-options', 'default-route-distance'])
self.cli_set(interface_path + ['address', 'dhcp'])
self.cli_commit()
# Wait for dhclient to receive IP address and default gateway
sleep(5)
router = get_dhcp_router(interface)
frrconfig = self.getFRRconfig('')
self.assertIn(rf'ip route 0.0.0.0/0 {router} {interface} tag 210 {default_distance}', frrconfig)
# T6991: Default route is missing when there is no "protocols static"
# CLI node entry
self.cli_delete(base_path)
# We can trigger a FRR reconfiguration and config re-rendering when
# we simply disable IPv6 forwarding
self.cli_set(['system', 'ipv6', 'disable-forwarding'])
self.cli_commit()
# Re-check FRR configuration that default route is still present
frrconfig = self.getFRRconfig('')
self.assertIn(rf'ip route 0.0.0.0/0 {router} {interface} tag 210 {default_distance}', frrconfig)
self.cli_delete(interface_path + ['address'])
self.cli_commit()
# Wait for dhclient to stop
while process_named_running('dhclient', cmdline=interface, timeout=10):
sleep(0.250)
def test_06_dhcp_default_route_for_vrf(self):
# When running via vyos-build under the QEMU environment a local DHCP
# server is available. This test verifies that the default route is set.
# When not running under the VyOS QEMU environment, this test is skipped.
if not os.path.exists('/tmp/vyos.smoketests.hint'):
self.skipTest('Not running under VyOS CI/CD QEMU environment!')
interface = 'eth0'
vrf = 'red'
vrf_path = ['vrf', 'name', vrf]
interface_path = ['interfaces', 'ethernet', interface]
self.cli_set(vrf_path + ['table', '1000'])
default_distance = default_value(interface_path + ['dhcp-options', 'default-route-distance'])
self.cli_set(interface_path + ['address', 'dhcp'])
self.cli_set(interface_path + ['vrf', vrf])
self.cli_commit()
# Wait for dhclient to receive IP address and default gateway
sleep(5)
router = get_dhcp_router(interface)
frrconfig = self.getFRRconfig(f'vrf {vrf}', endsection='^exit-vrf')
self.assertIn(rf'ip route 0.0.0.0/0 {router} {interface} tag 210 {default_distance}', frrconfig)
self.cli_delete(interface_path + ['address'])
self.cli_delete(interface_path + ['vrf'])
self.cli_commit()
# Wait for dhclient to stop
while process_named_running('dhclient', cmdline=interface, timeout=10):
sleep(0.250)
if __name__ == '__main__':
unittest.main(verbosity=2, failfast=VyOSUnitTestSHIM.TestCase.debug_on())
diff --git a/smoketest/scripts/cli/test_vrf.py b/smoketest/scripts/cli/test_vrf.py
index 82d5f3ddd..7ba518a4b 100755
--- a/smoketest/scripts/cli/test_vrf.py
+++ b/smoketest/scripts/cli/test_vrf.py
@@ -1,998 +1,994 @@
#!/usr/bin/env python3
#
# Copyright VyOS maintainers and contributors <maintainers@vyos.io>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import re
import os
import unittest
from json import loads
from jmespath import search
from base_vyostest_shim import VyOSUnitTestSHIM
-from base_vyostest_shim import CSTORE_GUARD_TIME
from vyos.configsession import ConfigSessionError
from vyos.ifconfig import Interface
from vyos.ifconfig import Section
from vyos.utils.file import read_file
from vyos.utils.network import get_interface_config
from vyos.utils.network import get_vrf_tableid
from vyos.utils.network import is_intf_addr_assigned
from vyos.utils.network import interface_exists
from vyos.utils.process import cmd
from vyos.utils.system import sysctl_read
from vyos.template import inc_ip
from vyos.utils.process import process_named_running
base_path = ['vrf']
vrfs = ['red', 'green', 'blue', 'foo-bar', 'baz_foo']
v4_protocols = ['any', 'babel', 'bgp', 'connected', 'eigrp', 'isis', 'kernel', 'ospf', 'rip', 'static', 'table']
v6_protocols = ['any', 'babel', 'bgp', 'connected', 'isis', 'kernel', 'ospfv3', 'ripng', 'static', 'table']
class VRFTest(VyOSUnitTestSHIM.TestCase):
_interfaces = []
@classmethod
def setUpClass(cls):
# we need to filter out VLAN interfaces identified by a dot (.)
# in their name - just in case!
if 'TEST_ETH' in os.environ:
tmp = os.environ['TEST_ETH'].split()
cls._interfaces = tmp
else:
for tmp in Section.interfaces('ethernet', vlan=False):
cls._interfaces.append(tmp)
- # Enable CSTORE guard time required by FRR related tests
- cls._commit_guard_time = CSTORE_GUARD_TIME
-
# call base-classes classmethod
super(VRFTest, cls).setUpClass()
def setUp(self):
# VRF strict_most ist always enabled
tmp = read_file('/proc/sys/net/vrf/strict_mode')
self.assertEqual(tmp, '1')
def tearDown(self):
# delete all VRFs
self.cli_delete(base_path)
self.cli_commit()
for vrf in vrfs:
self.assertFalse(interface_exists(vrf))
def walk_path(self, obj, path):
current = obj
for i, key in enumerate(path):
if isinstance(key, str):
self.assertTrue(isinstance(current, dict), msg=f'Failed path: {path}')
self.assertTrue(key in current, msg=f'Failed path: {path}')
elif isinstance(key, int):
self.assertTrue(isinstance(current, list), msg=f'Failed path: {path}')
self.assertTrue(0 <= key < len(current), msg=f'Failed path: {path}')
else:
assert False, 'Invalid type'
current = current[key]
return current
def verify_config_object(self, obj, path, value):
base_obj = self.walk_path(obj, path)
self.assertTrue(isinstance(base_obj, list))
self.assertTrue(any(True for v in base_obj if v == value))
def verify_config_value(self, obj, path, key, value):
base_obj = self.walk_path(obj, path)
if isinstance(base_obj, list):
self.assertTrue(any(True for v in base_obj if key in v and v[key] == value))
elif isinstance(base_obj, dict):
self.assertTrue(key in base_obj)
self.assertEqual(base_obj[key], value)
def verify_kea_service_running(self, process_name):
tmp = cmd('tail -n 100 /var/log/messages')
self.assertTrue(
process_named_running(process_name), msg=f'Service not running, log: {tmp}'
)
def test_vrf_vni_and_table_id(self):
base_table = '1000'
table = base_table
for vrf in vrfs:
base = base_path + ['name', vrf]
description = f'VyOS-VRF-{vrf}'
self.cli_set(base + ['description', description])
# check validate() - a table ID is mandatory
with self.assertRaises(ConfigSessionError):
self.cli_commit()
self.cli_set(base + ['table', table])
self.cli_set(base + ['vni', table])
if vrf == 'green':
self.cli_set(base + ['disable'])
table = str(int(table) + 1)
# commit changes
self.cli_commit()
# Verify VRF configuration
table = base_table
iproute2_config = read_file('/etc/iproute2/rt_tables.d/vyos-vrf.conf')
for vrf in vrfs:
description = f'VyOS-VRF-{vrf}'
self.assertTrue(interface_exists(vrf))
vrf_if = Interface(vrf)
# validate proper interface description
self.assertEqual(vrf_if.get_alias(), description)
# validate admin up/down state of VRF
state = 'up'
if vrf == 'green':
state = 'down'
self.assertEqual(vrf_if.get_admin_state(), state)
# Test the iproute2 lookup file, syntax is as follows:
#
# # id vrf name comment
# 1000 red # VyOS-VRF-red
# 1001 green # VyOS-VRF-green
# ...
regex = f'{table}\s+{vrf}\s+#\s+{description}'
self.assertTrue(re.findall(regex, iproute2_config))
frrconfig = self.getFRRconfig(f'vrf {vrf}', endsection='^exit-vrf')
self.assertIn(f' vni {table}', frrconfig)
self.assertEqual(int(table), get_vrf_tableid(vrf))
# Increment table ID for the next run
table = str(int(table) + 1)
def test_vrf_loopbacks_ips(self):
table = '2000'
for vrf in vrfs:
base = base_path + ['name', vrf]
self.cli_set(base + ['table', str(table)])
table = str(int(table) + 1)
# commit changes
self.cli_commit()
# Verify VRF configuration
loopbacks = ['127.0.0.1', '::1']
for vrf in vrfs:
# Ensure VRF was created
self.assertTrue(interface_exists(vrf))
# Verify IP forwarding is 1 (enabled)
self.assertEqual(sysctl_read(f'net.ipv4.conf.{vrf}.forwarding'), '1')
self.assertEqual(sysctl_read(f'net.ipv6.conf.{vrf}.forwarding'), '1')
# Test for proper loopback IP assignment
for addr in loopbacks:
self.assertTrue(is_intf_addr_assigned(vrf, addr))
def test_vrf_bind_all(self):
table = '2000'
for vrf in vrfs:
base = base_path + ['name', vrf]
self.cli_set(base + ['table', str(table)])
table = str(int(table) + 1)
self.cli_set(base_path + ['bind-to-all'])
# commit changes
self.cli_commit()
# Verify VRF configuration
self.assertEqual(sysctl_read('net.ipv4.tcp_l3mdev_accept'), '1')
self.assertEqual(sysctl_read('net.ipv4.udp_l3mdev_accept'), '1')
# If there is any VRF defined, strict_mode should be on
self.assertEqual(sysctl_read('net.vrf.strict_mode'), '1')
def test_vrf_table_id_is_unalterable(self):
# Linux Kernel prohibits the change of a VRF table on the fly.
# VRF must be deleted and recreated!
table = '1000'
vrf = vrfs[0]
base = base_path + ['name', vrf]
self.cli_set(base + ['table', table])
# commit changes
self.cli_commit()
# Check if VRF has been created
self.assertTrue(interface_exists(vrf))
table = str(int(table) + 1)
self.cli_set(base + ['table', table])
# check validate() - table ID can not be altered!
with self.assertRaises(ConfigSessionError):
self.cli_commit()
def test_vrf_assign_interface(self):
vrf = vrfs[0]
table = '5000'
self.cli_set(['vrf', 'name', vrf, 'table', table])
for interface in self._interfaces:
section = Section.section(interface)
self.cli_set(['interfaces', section, interface, 'vrf', vrf])
# commit changes
self.cli_commit()
# Verify VRF assignmant
for interface in self._interfaces:
tmp = get_interface_config(interface)
self.assertEqual(vrf, tmp['master'])
# cleanup
section = Section.section(interface)
self.cli_delete(['interfaces', section, interface, 'vrf'])
def test_vrf_static_route(self):
base_table = '100'
table = base_table
for vrf in vrfs:
next_hop = f'192.0.{table}.1'
prefix = f'10.0.{table}.0/24'
base = base_path + ['name', vrf]
self.cli_set(base + ['vni', table])
# check validate() - a table ID is mandatory
with self.assertRaises(ConfigSessionError):
self.cli_commit()
self.cli_set(base + ['table', table])
self.cli_set(base + ['protocols', 'static', 'route', prefix, 'next-hop', next_hop])
table = str(int(table) + 1)
# commit changes
self.cli_commit()
# Verify VRF configuration
table = base_table
for vrf in vrfs:
next_hop = f'192.0.{table}.1'
prefix = f'10.0.{table}.0/24'
self.assertTrue(interface_exists(vrf))
frrconfig = self.getFRRconfig(f'vrf {vrf}', endsection='^exit-vrf')
self.assertIn(f' vni {table}', frrconfig)
self.assertIn(f' ip route {prefix} {next_hop}', frrconfig)
# Increment table ID for the next run
table = str(int(table) + 1)
def test_vrf_link_local_ip_addresses(self):
# Testcase for issue T4331
table = '100'
vrf = 'orange'
interface = 'dum9998'
addresses = ['192.0.2.1/26', '2001:db8:9998::1/64', 'fe80::1/64']
for address in addresses:
self.cli_set(['interfaces', 'dummy', interface, 'address', address])
# Create dummy interfaces
self.cli_commit()
# ... and verify IP addresses got assigned
for address in addresses:
self.assertTrue(is_intf_addr_assigned(interface, address))
# Move interface to VRF
self.cli_set(base_path + ['name', vrf, 'table', table])
self.cli_set(['interfaces', 'dummy', interface, 'vrf', vrf])
# Apply VRF config
self.cli_commit()
# Ensure VRF got created
self.assertTrue(interface_exists(vrf))
# ... and IP addresses are still assigned
for address in addresses:
self.assertTrue(is_intf_addr_assigned(interface, address))
# Verify VRF table ID
self.assertEqual(int(table), get_vrf_tableid(vrf))
# Verify interface is assigned to VRF
tmp = get_interface_config(interface)
self.assertEqual(vrf, tmp['master'])
# Delete Interface
self.cli_delete(['interfaces', 'dummy', interface])
self.cli_commit()
def test_vrf_disable_forwarding(self):
table = '2000'
for vrf in vrfs:
base = base_path + ['name', vrf]
self.cli_set(base + ['table', table])
self.cli_set(base + ['ip', 'disable-forwarding'])
self.cli_set(base + ['ipv6', 'disable-forwarding'])
table = str(int(table) + 1)
# commit changes
self.cli_commit()
# Verify VRF configuration
loopbacks = ['127.0.0.1', '::1']
for vrf in vrfs:
# Ensure VRF was created
self.assertTrue(interface_exists(vrf))
# Verify IP forwarding is 0 (disabled)
self.assertEqual(sysctl_read(f'net.ipv4.conf.{vrf}.forwarding'), '0')
self.assertEqual(sysctl_read(f'net.ipv6.conf.{vrf}.forwarding'), '0')
def test_vrf_ip_protocol_route_map(self):
table = '6000'
for vrf in vrfs:
base = base_path + ['name', vrf]
self.cli_set(base + ['table', table])
for protocol in v4_protocols:
self.cli_set(['policy', 'route-map', f'route-map-{vrf}-{protocol}', 'rule', '10', 'action', 'permit'])
self.cli_set(base + ['ip', 'protocol', protocol, 'route-map', f'route-map-{vrf}-{protocol}'])
table = str(int(table) + 1)
self.cli_commit()
# Verify route-map properly applied to FRR
for vrf in vrfs:
frrconfig = self.getFRRconfig(f'vrf {vrf}', endsection='^exit-vrf')
self.assertIn(f'vrf {vrf}', frrconfig)
for protocol in v4_protocols:
self.assertIn(f' ip protocol {protocol} route-map route-map-{vrf}-{protocol}', frrconfig)
# Delete route-maps
for vrf in vrfs:
base = base_path + ['name', vrf]
self.cli_delete(['policy', 'route-map', f'route-map-{vrf}-{protocol}'])
self.cli_delete(base + ['ip', 'protocol'])
self.cli_commit()
# Verify route-map properly is removed from FRR
for vrf in vrfs:
frrconfig = self.getFRRconfig(f'vrf {vrf}', endsection='^exit-vrf')
self.assertNotIn(f' ip protocol', frrconfig)
def test_vrf_ip_ipv6_protocol_non_existing_route_map(self):
table = '6100'
non_existing = 'non-existing'
for vrf in vrfs:
base = base_path + ['name', vrf]
self.cli_set(base + ['table', table])
for protocol in v4_protocols:
self.cli_set(base + ['ip', 'protocol', protocol, 'route-map', f'v4-{non_existing}'])
for protocol in v6_protocols:
self.cli_set(base + ['ipv6', 'protocol', protocol, 'route-map', f'v6-{non_existing}'])
table = str(int(table) + 1)
# Both v4 and v6 route-maps do not exist yet
with self.assertRaises(ConfigSessionError):
self.cli_commit()
self.cli_set(['policy', 'route-map', f'v4-{non_existing}', 'rule', '10', 'action', 'deny'])
# v6 route-map does not exist yet
with self.assertRaises(ConfigSessionError):
self.cli_commit()
self.cli_set(['policy', 'route-map', f'v6-{non_existing}', 'rule', '10', 'action', 'deny'])
# Commit again
self.cli_commit()
def test_vrf_ipv6_protocol_route_map(self):
table = '6200'
for vrf in vrfs:
base = base_path + ['name', vrf]
self.cli_set(base + ['table', table])
for protocol in v6_protocols:
route_map = f'route-map-{vrf}-{protocol.replace("ospfv3", "ospf6")}'
self.cli_set(['policy', 'route-map', route_map, 'rule', '10', 'action', 'permit'])
self.cli_set(base + ['ipv6', 'protocol', protocol, 'route-map', route_map])
table = str(int(table) + 1)
self.cli_commit()
# Verify route-map properly applied to FRR
for vrf in vrfs:
frrconfig = self.getFRRconfig(f'vrf {vrf}', endsection='^exit-vrf')
self.assertIn(f'vrf {vrf}', frrconfig)
for protocol in v6_protocols:
# VyOS and FRR use a different name for OSPFv3 (IPv6)
if protocol == 'ospfv3':
protocol = 'ospf6'
route_map = f'route-map-{vrf}-{protocol}'
self.assertIn(f' ipv6 protocol {protocol} route-map {route_map}', frrconfig)
# Delete route-maps
for vrf in vrfs:
base = base_path + ['name', vrf]
self.cli_delete(['policy', 'route-map', f'route-map-{vrf}-{protocol}'])
self.cli_delete(base + ['ipv6', 'protocol'])
self.cli_commit()
# Verify route-map properly is removed from FRR
for vrf in vrfs:
frrconfig = self.getFRRconfig(f'vrf {vrf}', endsection='^exit-vrf')
self.assertNotIn(f' ipv6 protocol', frrconfig)
def test_vrf_vni_duplicates(self):
base_table = '6300'
table = base_table
for vrf in vrfs:
base = base_path + ['name', vrf]
self.cli_set(base + ['table', str(table)])
self.cli_set(base + ['vni', '100'])
table = str(int(table) + 1)
# L3VNIs can only be used once
with self.assertRaises(ConfigSessionError):
self.cli_commit()
table = base_table
for vrf in vrfs:
base = base_path + ['name', vrf]
self.cli_set(base + ['vni', str(table)])
table = str(int(table) + 1)
# commit changes
self.cli_commit()
# Verify VRF configuration
table = base_table
for vrf in vrfs:
self.assertTrue(interface_exists(vrf))
frrconfig = self.getFRRconfig(f'vrf {vrf}', endsection='^exit-vrf')
self.assertIn(f' vni {table}', frrconfig)
# Increment table ID for the next run
table = str(int(table) + 1)
def test_vrf_vni_add_change_remove(self):
base_table = '6300'
table = base_table
for vrf in vrfs:
base = base_path + ['name', vrf]
self.cli_set(base + ['table', str(table)])
self.cli_set(base + ['vni', str(table)])
table = str(int(table) + 1)
# commit changes
self.cli_commit()
# Verify VRF configuration
table = base_table
for vrf in vrfs:
self.assertTrue(interface_exists(vrf))
frrconfig = self.getFRRconfig(f'vrf {vrf}', endsection='^exit-vrf')
self.assertIn(f' vni {table}', frrconfig)
# Increment table ID for the next run
table = str(int(table) + 1)
# Now change all L3VNIs (increment 2)
# We must also change the base_table number as we probably could get
# duplicate VNI's during the test as VNIs are applied 1:1 to FRR
base_table = '5000'
table = base_table
for vrf in vrfs:
base = base_path + ['name', vrf]
self.cli_set(base + ['vni', str(table)])
table = str(int(table) + 2)
# commit changes
self.cli_commit()
# Verify VRF configuration
table = base_table
for vrf in vrfs:
self.assertTrue(interface_exists(vrf))
frrconfig = self.getFRRconfig(f'vrf {vrf}', endsection='^exit-vrf')
self.assertIn(f' vni {table}', frrconfig)
# Increment table ID for the next run
table = str(int(table) + 2)
# add a new VRF with VNI - this must not delete any existing VRF/VNI
purple = 'purple'
table = str(int(table) + 10)
self.cli_set(base_path + ['name', purple, 'table', table])
self.cli_set(base_path + ['name', purple, 'vni', table])
# commit changes
self.cli_commit()
# Verify VRF configuration
table = base_table
for vrf in vrfs:
self.assertTrue(interface_exists(vrf))
frrconfig = self.getFRRconfig(f'vrf {vrf}', endsection='^exit-vrf')
self.assertIn(f' vni {table}', frrconfig)
# Increment table ID for the next run
table = str(int(table) + 2)
# Verify purple VRF/VNI
self.assertTrue(interface_exists(purple))
table = str(int(table) + 10)
frrconfig = self.getFRRconfig(f'vrf {purple}', endsection='^exit-vrf')
self.assertIn(f' vni {table}', frrconfig)
# Now delete all the VNIs
for vrf in vrfs:
base = base_path + ['name', vrf]
self.cli_delete(base + ['vni'])
# commit changes
self.cli_commit()
# Verify no VNI is defined
for vrf in vrfs:
self.assertTrue(interface_exists(vrf))
frrconfig = self.getFRRconfig(f'vrf {vrf}', endsection='^exit-vrf')
self.assertNotIn('vni', frrconfig)
# Verify purple VNI remains
self.assertTrue(interface_exists(purple))
frrconfig = self.getFRRconfig(f'vrf {purple}', endsection='^exit-vrf')
self.assertIn(f' vni {table}', frrconfig)
def test_vrf_ip_ipv6_nht(self):
table = '6910'
for vrf in vrfs:
base = base_path + ['name', vrf]
self.cli_set(base + ['table', table])
self.cli_set(base + ['ip', 'nht', 'no-resolve-via-default'])
self.cli_set(base + ['ipv6', 'nht', 'no-resolve-via-default'])
table = str(int(table) + 1)
self.cli_commit()
# Verify route-map properly applied to FRR
for vrf in vrfs:
frrconfig = self.getFRRconfig(f'vrf {vrf}', endsection='^exit-vrf')
self.assertIn(f'vrf {vrf}', frrconfig)
self.assertIn(f' no ip nht resolve-via-default', frrconfig)
self.assertIn(f' no ipv6 nht resolve-via-default', frrconfig)
# Delete route-maps
for vrf in vrfs:
base = base_path + ['name', vrf]
self.cli_delete(base + ['ip'])
self.cli_delete(base + ['ipv6'])
self.cli_commit()
# Verify route-map properly is removed from FRR
for vrf in vrfs:
frrconfig = self.getFRRconfig(f'vrf {vrf}', endsection='^exit-vrf')
self.assertNotIn(f' no ip nht resolve-via-default', frrconfig)
self.assertNotIn(f' no ipv6 nht resolve-via-default', frrconfig)
def test_vrf_conntrack(self):
table = '8710'
nftables_rules = {
'vrf_zones_ct_in': ['ct original zone set iifname map @ct_iface_map'],
'vrf_zones_ct_out': ['ct original zone set oifname map @ct_iface_map']
}
self.cli_set(base_path + ['name', 'randomVRF', 'table', '1000'])
self.cli_commit()
# Conntrack rules should not be present
for chain, rule in nftables_rules.items():
self.verify_nftables_chain(rule, 'inet vrf_zones', chain, inverse=True)
# conntrack is only enabled once NAT, NAT66 or firewalling is enabled
self.cli_set(['nat'])
for vrf in vrfs:
base = base_path + ['name', vrf]
self.cli_set(base + ['table', table])
table = str(int(table) + 1)
# We need the commit inside the loop to trigger the bug in T6603
self.cli_commit()
# Conntrack rules should now be present
for chain, rule in nftables_rules.items():
self.verify_nftables_chain(rule, 'inet vrf_zones', chain, inverse=False)
# T6603: there should be only ONE entry for the iifname/oifname in the chains
tmp = loads(cmd('sudo nft -j list table inet vrf_zones'))
num_rules = len(search("nftables[].rule[].chain", tmp))
# ['vrf_zones_ct_in', 'vrf_zones_ct_out']
self.assertEqual(num_rules, 2)
self.cli_delete(['nat'])
def test_dhcp_single_pool(self):
# Prepare the vrf and options
table = '100'
vrf = 'dhcp_smoke'
interface = 'dum8888'
subnet = '192.0.2.0/25'
router = inc_ip(subnet, 1)
dns_1 = inc_ip(subnet, 2)
dns_2 = inc_ip(subnet, 3)
domain_name = 'vyos.net'
# declare fiels
process_name = 'kea-dhcp4'
kea4_conf = f'/var/run/kea/kea-{vrf}-dhcp4.conf'
# create interface
cidr_mask = subnet.split('/')[-1]
self.cli_set(
['interfaces', 'dummy', interface, 'address', f'{router}/{cidr_mask}']
)
self.cli_set(['interfaces', 'dummy', interface, 'vrf', f'{vrf}'])
# create the vrf with table
base = base_path + ['name', vrf]
self.cli_set(base + ['table', table])
# set the dhcp scope
base = base_path + ['name', vrf, 'service', 'dhcp-server']
shared_net_name = 'SMOKE-1'
range_0_start = inc_ip(subnet, 10)
range_0_stop = inc_ip(subnet, 20)
range_1_start = inc_ip(subnet, 40)
range_1_stop = inc_ip(subnet, 50)
self.cli_set(base + ['listen-interface', interface])
self.cli_set(base + ['shared-network-name', shared_net_name, 'ping-check'])
pool = base + ['shared-network-name', shared_net_name, 'subnet', subnet]
self.cli_set(pool + ['subnet-id', '1'])
self.cli_set(pool + ['ignore-client-id'])
self.cli_set(pool + ['ping-check'])
# we use the first subnet IP address as default gateway
self.cli_set(pool + ['option', 'default-router', router])
self.cli_set(pool + ['option', 'name-server', dns_1])
self.cli_set(pool + ['option', 'name-server', dns_2])
self.cli_set(pool + ['option', 'domain-name', domain_name])
# check validate() - No DHCP address range or active static-mapping set
with self.assertRaises(ConfigSessionError):
self.cli_commit()
self.cli_set(pool + ['range', '0', 'start', range_0_start])
self.cli_set(pool + ['range', '0', 'stop', range_0_stop])
self.cli_set(pool + ['range', '1', 'start', range_1_start])
self.cli_set(pool + ['range', '1', 'stop', range_1_stop])
# commit changes
self.cli_commit()
config = read_file(kea4_conf)
obj = loads(config)
self.verify_config_value(
obj, ['Dhcp4', 'interfaces-config'], 'interfaces', [interface]
)
self.verify_config_value(
obj, ['Dhcp4', 'shared-networks'], 'name', shared_net_name
)
self.verify_config_value(
obj, ['Dhcp4', 'shared-networks', 0, 'subnet4'], 'subnet', subnet
)
self.verify_config_value(
obj, ['Dhcp4', 'shared-networks', 0, 'subnet4'], 'id', 1
)
self.verify_config_value(
obj, ['Dhcp4', 'shared-networks', 0, 'subnet4'], 'match-client-id', False
)
self.verify_config_value(
obj, ['Dhcp4', 'shared-networks', 0, 'subnet4'], 'valid-lifetime', 86400
)
self.verify_config_value(
obj, ['Dhcp4', 'shared-networks', 0, 'subnet4'], 'max-valid-lifetime', 86400
)
# Verify ping-check
self.verify_config_value(
obj,
['Dhcp4', 'shared-networks', 0, 'user-context'],
'enable-ping-check',
True,
)
self.verify_config_value(
obj,
['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'user-context'],
'enable-ping-check',
True,
)
# Verify options
self.verify_config_object(
obj,
['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'],
{'name': 'domain-name', 'data': domain_name},
)
self.verify_config_object(
obj,
['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'],
{'name': 'domain-name-servers', 'data': f'{dns_1}, {dns_2}'},
)
self.verify_config_object(
obj,
['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'],
{'name': 'routers', 'data': router},
)
# Verify pools
self.verify_config_object(
obj,
['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'pools'],
{'pool': f'{range_0_start} - {range_0_stop}'},
)
self.verify_config_object(
obj,
['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'pools'],
{'pool': f'{range_1_start} - {range_1_stop}'},
)
# Check for running process
self.verify_kea_service_running(process_name)
# perform cleanup
self.cli_delete(['interfaces', 'dummy', interface, 'address'])
self.cli_delete(['interfaces', 'dummy', interface, 'vrf'])
self.cli_delete(base)
self.cli_commit()
def test_dhcpv6_single_pool(self):
# Prepare the vrf and other options
table = '100'
vrf = 'dhcp_smoke'
subnet = '2001:db8:f00::/64'
dns_1 = '2001:db8::1'
dns_2 = '2001:db8::2'
domain = 'vyos.net'
nis_servers = ['2001:db8:ffff::1', '2001:db8:ffff::2']
interface = 'dum8888'
interface_addr = inc_ip(subnet, 1) + '/64'
# declare fiels
process_name = 'kea-dhcp6'
kea6_conf = f'/var/run/kea/kea-{vrf}-dhcp6.conf'
# create interface
self.cli_set(['interfaces', 'dummy', interface, 'address', f'{interface_addr}'])
self.cli_set(['interfaces', 'dummy', interface, 'vrf', f'{vrf}'])
# create the vrf with table
base = base_path + ['name', vrf]
self.cli_set(base + ['table', table])
# set the dhcp scope
base = base_path + ['name', vrf, 'service', 'dhcpv6-server']
shared_net_name = 'SMOKE-1'
search_domains = ['foo.vyos.net', 'bar.vyos.net']
lease_time = '1200'
max_lease_time = '72000'
min_lease_time = '600'
preference = '10'
sip_server = 'sip.vyos.net'
sntp_server = inc_ip(subnet, 100)
range_start = inc_ip(subnet, 256) # ::100
range_stop = inc_ip(subnet, 65535) # ::ffff
pool = base + ['shared-network-name', shared_net_name, 'subnet', subnet]
self.cli_set(base + ['preference', preference])
self.cli_set(pool + ['interface', interface])
self.cli_set(pool + ['subnet-id', '1'])
# we use the first subnet IP address as default gateway
self.cli_set(pool + ['lease-time', 'default', lease_time])
self.cli_set(pool + ['lease-time', 'maximum', max_lease_time])
self.cli_set(pool + ['lease-time', 'minimum', min_lease_time])
self.cli_set(pool + ['option', 'capwap-controller', dns_1])
self.cli_set(pool + ['option', 'name-server', dns_1])
self.cli_set(pool + ['option', 'name-server', dns_2])
self.cli_set(pool + ['option', 'name-server', dns_2])
self.cli_set(pool + ['option', 'nis-domain', domain])
self.cli_set(pool + ['option', 'nisplus-domain', domain])
self.cli_set(pool + ['option', 'sip-server', sip_server])
self.cli_set(pool + ['option', 'sntp-server', sntp_server])
self.cli_set(pool + ['range', '1', 'start', range_start])
self.cli_set(pool + ['range', '1', 'stop', range_stop])
for server in nis_servers:
self.cli_set(pool + ['option', 'nis-server', server])
self.cli_set(pool + ['option', 'nisplus-server', server])
for search_domain in search_domains:
self.cli_set(pool + ['option', 'domain-search', search_domain])
client_base = 1
for client in ['client1', 'client2', 'client3']:
duid = f'00:01:00:01:12:34:56:78:aa:bb:cc:dd:ee:{client_base:02}'
self.cli_set(pool + ['static-mapping', client, 'duid', duid])
self.cli_set(
pool
+ [
'static-mapping',
client,
'ipv6-address',
inc_ip(subnet, client_base),
]
)
self.cli_set(
pool
+ [
'static-mapping',
client,
'ipv6-prefix',
inc_ip(subnet, client_base << 64) + '/64',
]
)
client_base += 1
# cannot have both mac-address and duid set
with self.assertRaises(ConfigSessionError):
self.cli_set(
pool + ['static-mapping', 'client1', 'mac', '00:50:00:00:00:11']
)
self.cli_commit()
self.cli_delete(pool + ['static-mapping', 'client1', 'mac'])
# commit changes
self.cli_commit()
config = read_file(kea6_conf)
obj = loads(config)
self.verify_config_value(
obj, ['Dhcp6', 'shared-networks'], 'name', shared_net_name
)
self.verify_config_value(
obj, ['Dhcp6', 'shared-networks', 0, 'subnet6'], 'subnet', subnet
)
self.verify_config_value(
obj, ['Dhcp6', 'shared-networks', 0, 'subnet6'], 'interface', interface
)
self.verify_config_value(
obj, ['Dhcp6', 'shared-networks', 0, 'subnet6'], 'id', 1
)
self.verify_config_value(
obj,
['Dhcp6', 'shared-networks', 0, 'subnet6'],
'valid-lifetime',
int(lease_time),
)
self.verify_config_value(
obj,
['Dhcp6', 'shared-networks', 0, 'subnet6'],
'min-valid-lifetime',
int(min_lease_time),
)
self.verify_config_value(
obj,
['Dhcp6', 'shared-networks', 0, 'subnet6'],
'max-valid-lifetime',
int(max_lease_time),
)
# Verify options
self.verify_config_object(
obj,
['Dhcp6', 'shared-networks', 0, 'subnet6', 0, 'option-data'],
{'name': 'capwap-ac-v6', 'data': dns_1},
)
self.verify_config_object(
obj,
['Dhcp6', 'shared-networks', 0, 'subnet6', 0, 'option-data'],
{'name': 'dns-servers', 'data': f'{dns_1}, {dns_2}'},
)
self.verify_config_object(
obj,
['Dhcp6', 'shared-networks', 0, 'subnet6', 0, 'option-data'],
{'name': 'domain-search', 'data': ', '.join(search_domains)},
)
self.verify_config_object(
obj,
['Dhcp6', 'shared-networks', 0, 'subnet6', 0, 'option-data'],
{'name': 'nis-domain-name', 'data': domain},
)
self.verify_config_object(
obj,
['Dhcp6', 'shared-networks', 0, 'subnet6', 0, 'option-data'],
{'name': 'nis-servers', 'data': ', '.join(nis_servers)},
)
self.verify_config_object(
obj,
['Dhcp6', 'shared-networks', 0, 'subnet6', 0, 'option-data'],
{'name': 'nisp-domain-name', 'data': domain},
)
self.verify_config_object(
obj,
['Dhcp6', 'shared-networks', 0, 'subnet6', 0, 'option-data'],
{'name': 'nisp-servers', 'data': ', '.join(nis_servers)},
)
self.verify_config_object(
obj,
['Dhcp6', 'shared-networks', 0, 'subnet6', 0, 'option-data'],
{'name': 'sntp-servers', 'data': sntp_server},
)
self.verify_config_object(
obj,
['Dhcp6', 'shared-networks', 0, 'subnet6', 0, 'option-data'],
{'name': 'sip-server-dns', 'data': sip_server},
)
# Verify pools
self.verify_config_object(
obj,
['Dhcp6', 'shared-networks', 0, 'subnet6', 0, 'pools'],
{'pool': f'{range_start} - {range_stop}'},
)
client_base = 1
for client in ['client1', 'client2', 'client3']:
duid = f'00:01:00:01:12:34:56:78:aa:bb:cc:dd:ee:{client_base:02}'
ip = inc_ip(subnet, client_base)
prefix = inc_ip(subnet, client_base << 64) + '/64'
self.verify_config_object(
obj,
['Dhcp6', 'shared-networks', 0, 'subnet6', 0, 'reservations'],
{
'hostname': client,
'duid': duid,
'ip-addresses': [ip],
'prefixes': [prefix],
},
)
client_base += 1
# Check for running process
self.verify_kea_service_running(process_name)
# perform cleanup
self.cli_delete(['interfaces', 'dummy', interface, 'address'])
self.cli_delete(['interfaces', 'dummy', interface, 'vrf'])
self.cli_delete(base)
self.cli_commit()
if __name__ == '__main__':
unittest.main(verbosity=2, failfast=VyOSUnitTestSHIM.TestCase.debug_on())

File Metadata

Mime Type
text/x-diff
Expires
Sat, Oct 17, 12:11 AM (1 d, 14 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
4416447
Default Alt Text
(506 KB)

Event Timeline