summaryrefslogtreecommitdiff
path: root/smoketest/scripts/cli
diff options
context:
space:
mode:
Diffstat (limited to 'smoketest/scripts/cli')
-rw-r--r--smoketest/scripts/cli/base_interfaces_test.py128
-rw-r--r--smoketest/scripts/cli/base_vyostest_shim.py73
-rwxr-xr-xsmoketest/scripts/cli/test_container.py193
-rwxr-xr-xsmoketest/scripts/cli/test_firewall.py228
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_bonding.py18
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_bridge.py15
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_ethernet.py7
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_loopback.py5
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_virtual-ethernet.py5
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_vxlan.py26
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_wireguard.py108
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_wireless.py25
-rwxr-xr-xsmoketest/scripts/cli/test_load-balancing_haproxy.py197
-rwxr-xr-xsmoketest/scripts/cli/test_load-balancing_wan.py156
-rwxr-xr-xsmoketest/scripts/cli/test_nat.py2
-rwxr-xr-xsmoketest/scripts/cli/test_nat66.py29
-rwxr-xr-xsmoketest/scripts/cli/test_policy.py29
-rw-r--r--smoketest/scripts/cli/test_policy_local-route.py5
-rwxr-xr-xsmoketest/scripts/cli/test_policy_route.py39
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_babel.py17
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_bfd.py21
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_bgp.py359
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_isis.py74
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_mpls.py82
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_nhrp.py81
-rw-r--r--smoketest/scripts/cli/test_protocols_openfabric.py25
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_ospf.py64
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_ospfv3.py37
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_pim.py21
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_pim6.py16
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_rip.py15
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_ripng.py9
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_rpki.py45
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_segment-routing.py110
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_static.py56
-rwxr-xr-xsmoketest/scripts/cli/test_service_dhcp-server.py1104
-rwxr-xr-xsmoketest/scripts/cli/test_service_dhcpv6-server.py5
-rwxr-xr-xsmoketest/scripts/cli/test_service_dns_dynamic.py35
-rwxr-xr-xsmoketest/scripts/cli/test_service_https.py24
-rwxr-xr-xsmoketest/scripts/cli/test_service_ids_ddos-protection.py116
-rwxr-xr-xsmoketest/scripts/cli/test_service_ipoe-server.py26
-rwxr-xr-xsmoketest/scripts/cli/test_service_lldp.py49
-rw-r--r--smoketest/scripts/cli/test_service_monitoring_network_event.py65
-rwxr-xr-xsmoketest/scripts/cli/test_service_monitoring_prometheus.py81
-rwxr-xr-xsmoketest/scripts/cli/test_service_router-advert.py112
-rwxr-xr-xsmoketest/scripts/cli/test_service_webproxy.py18
-rwxr-xr-xsmoketest/scripts/cli/test_system_flow-accounting.py107
-rwxr-xr-xsmoketest/scripts/cli/test_system_ip.py14
-rwxr-xr-xsmoketest/scripts/cli/test_system_ipv6.py14
-rwxr-xr-xsmoketest/scripts/cli/test_system_login.py354
-rwxr-xr-xsmoketest/scripts/cli/test_system_option.py62
-rwxr-xr-xsmoketest/scripts/cli/test_system_sflow.py33
-rwxr-xr-xsmoketest/scripts/cli/test_system_syslog.py293
-rwxr-xr-xsmoketest/scripts/cli/test_vpn_ipsec.py134
-rwxr-xr-xsmoketest/scripts/cli/test_vrf.py41
55 files changed, 3831 insertions, 1176 deletions
diff --git a/smoketest/scripts/cli/base_interfaces_test.py b/smoketest/scripts/cli/base_interfaces_test.py
index 593b4b415..5348b0cc3 100644
--- a/smoketest/scripts/cli/base_interfaces_test.py
+++ b/smoketest/scripts/cli/base_interfaces_test.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2019-2024 VyOS maintainers and contributors
+# Copyright (C) 2019-2025 VyOS maintainers and contributors
#
# 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
@@ -14,11 +14,14 @@
import re
+from json import loads
from netifaces import AF_INET
from netifaces import AF_INET6
from netifaces import ifaddresses
+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
@@ -37,12 +40,15 @@ 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
@@ -133,6 +139,7 @@ def is_mirrored_to(interface, mirror_if, qdisc):
if mirror_if in tmp:
ret_val = True
return ret_val
+
class BasicInterfaceTest:
class TestCase(VyOSUnitTestSHIM.TestCase):
_test_dhcp = False
@@ -181,6 +188,9 @@ class BasicInterfaceTest:
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)
@@ -213,7 +223,7 @@ class BasicInterfaceTest:
def test_dhcp_disable_interface(self):
if not self._test_dhcp:
- self.skipTest('not supported')
+ 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
@@ -236,7 +246,7 @@ class BasicInterfaceTest:
def test_dhcp_client_options(self):
if not self._test_dhcp or not self._test_vrf:
- self.skipTest('not supported')
+ self.skipTest(MSG_TESTCASE_UNSUPPORTED)
client_id = 'VyOS-router'
distance = '100'
@@ -276,7 +286,10 @@ class BasicInterfaceTest:
def test_dhcp_vrf(self):
if not self._test_dhcp or not self._test_vrf:
- self.skipTest('not supported')
+ 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'])
@@ -303,13 +316,34 @@ class BasicInterfaceTest:
self.assertIn(str(dhclient_pid), vrf_pids)
# and the commandline has the appropriate options
cmdline = read_file(f'/proc/{dhclient_pid}/cmdline')
- self.assertIn('-e\x00IF_METRIC=210', cmdline) # 210 is the default value
+ 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('not supported')
+ self.skipTest(MSG_TESTCASE_UNSUPPORTED)
vrf_name = 'purple6'
self.cli_set(['vrf', 'name', vrf_name, 'table', '65001'])
@@ -337,11 +371,31 @@ class BasicInterfaceTest:
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('not supported')
+ self.skipTest(MSG_TESTCASE_UNSUPPORTED)
vrf1_name = 'smoketest_mgmt1'
vrf1_table = '5424'
@@ -386,7 +440,7 @@ class BasicInterfaceTest:
def test_add_to_invalid_vrf(self):
if not self._test_vrf:
- self.skipTest('not supported')
+ self.skipTest(MSG_TESTCASE_UNSUPPORTED)
# move interface into first VRF
for interface in self._interfaces:
@@ -404,7 +458,7 @@ class BasicInterfaceTest:
def test_span_mirror(self):
if not self._mirror_interfaces:
- self.skipTest('not supported')
+ self.skipTest(MSG_TESTCASE_UNSUPPORTED)
# Check the two-way mirror rules of ingress and egress
for mirror in self._mirror_interfaces:
@@ -513,7 +567,7 @@ class BasicInterfaceTest:
def test_ipv6_link_local_address(self):
# Common function for IPv6 link-local address assignemnts
if not self._test_ipv6:
- self.skipTest('not supported')
+ self.skipTest(MSG_TESTCASE_UNSUPPORTED)
for interface in self._interfaces:
base = self._base_path + [interface]
@@ -544,7 +598,7 @@ class BasicInterfaceTest:
def test_interface_mtu(self):
if not self._test_mtu:
- self.skipTest('not supported')
+ self.skipTest(MSG_TESTCASE_UNSUPPORTED)
for intf in self._interfaces:
base = self._base_path + [intf]
@@ -563,8 +617,8 @@ class BasicInterfaceTest:
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:
- self.skipTest('not supported')
+ if not self._test_mtu or not self._test_ipv6:
+ self.skipTest(MSG_TESTCASE_UNSUPPORTED)
old_mtu = self._mtu
self._mtu = '1200'
@@ -600,7 +654,7 @@ class BasicInterfaceTest:
# 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('not supported')
+ self.skipTest(MSG_TESTCASE_UNSUPPORTED)
for interface in self._interfaces:
base = self._base_path + [interface]
@@ -645,7 +699,7 @@ class BasicInterfaceTest:
# 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('not supported')
+ self.skipTest(MSG_TESTCASE_UNSUPPORTED)
mtu_1500 = '1500'
mtu_9000 = '9000'
@@ -691,7 +745,7 @@ class BasicInterfaceTest:
# 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('not supported')
+ self.skipTest(MSG_TESTCASE_UNSUPPORTED)
for interface in self._interfaces:
base = self._base_path + [interface]
@@ -761,7 +815,7 @@ class BasicInterfaceTest:
def test_vif_8021q_lower_up_down(self):
# Testcase for https://vyos.dev/T3349
if not self._test_vlan:
- self.skipTest('not supported')
+ self.skipTest(MSG_TESTCASE_UNSUPPORTED)
for interface in self._interfaces:
base = self._base_path + [interface]
@@ -801,7 +855,7 @@ class BasicInterfaceTest:
# 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('not supported')
+ self.skipTest(MSG_TESTCASE_UNSUPPORTED)
for interface in self._interfaces:
base = self._base_path + [interface]
@@ -868,7 +922,7 @@ class BasicInterfaceTest:
# 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('not supported')
+ self.skipTest(MSG_TESTCASE_UNSUPPORTED)
for interface in self._interfaces:
base = self._base_path + [interface]
@@ -906,7 +960,7 @@ class BasicInterfaceTest:
def test_interface_ip_options(self):
if not self._test_ip:
- self.skipTest('not supported')
+ self.skipTest(MSG_TESTCASE_UNSUPPORTED)
arp_tmo = '300'
mss = '1420'
@@ -1008,12 +1062,13 @@ class BasicInterfaceTest:
def test_interface_ipv6_options(self):
if not self._test_ipv6:
- self.skipTest('not supported')
+ 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]
@@ -1036,6 +1091,9 @@ class BasicInterfaceTest:
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:
@@ -1067,13 +1125,20 @@ class BasicInterfaceTest:
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('not supported')
+ self.skipTest(MSG_TESTCASE_UNSUPPORTED)
duid_base = 10
for interface in self._interfaces:
- duid = '00:01:00:01:27:71:db:f0:00:50:00:00:00:{}'.format(duid_base)
+ 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())
@@ -1090,7 +1155,7 @@ class BasicInterfaceTest:
duid_base = 10
for interface in self._interfaces:
- duid = '00:01:00:01:27:71:db:f0:00:50:00:00:00:{}'.format(duid_base)
+ 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)
@@ -1102,16 +1167,25 @@ class BasicInterfaceTest:
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('not supported')
+ self.skipTest(MSG_TESTCASE_UNSUPPORTED)
prefix_len = '56'
sla_len = str(64 - int(prefix_len))
@@ -1172,7 +1246,7 @@ class BasicInterfaceTest:
def test_dhcpv6pd_manual_sla_id(self):
if not self._test_ipv6_pd:
- self.skipTest('not supported')
+ self.skipTest(MSG_TESTCASE_UNSUPPORTED)
prefix_len = '56'
sla_len = str(64 - int(prefix_len))
@@ -1238,7 +1312,7 @@ class BasicInterfaceTest:
def test_eapol(self):
if not self._test_eapol:
- self.skipTest('not supported')
+ self.skipTest(MSG_TESTCASE_UNSUPPORTED)
cfg_dir = '/run/wpa_supplicant'
diff --git a/smoketest/scripts/cli/base_vyostest_shim.py b/smoketest/scripts/cli/base_vyostest_shim.py
index 9cf6a653a..f0674f187 100644
--- a/smoketest/scripts/cli/base_vyostest_shim.py
+++ b/smoketest/scripts/cli/base_vyostest_shim.py
@@ -18,7 +18,6 @@ import paramiko
import pprint
from time import sleep
-from time import time
from typing import Type
from vyos.configsession import ConfigSession
@@ -27,10 +26,17 @@ from vyos import ConfigError
from vyos.defaults import commit_lock
from vyos.utils.process import cmd
from vyos.utils.process import run
-from vyos.utils.process import process_named_running
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.
@@ -45,7 +51,9 @@ class VyOSUnitTestSHIM:
# trigger the certain failure condition.
# Use "self.debug = True" in derived classes setUp() method
debug = False
- commit_guard = time()
+ # Time to wait after a commit to ensure the CStore is up to date
+ # only required for testcases using FRR
+ _commit_guard_time = 0
@classmethod
def setUpClass(cls):
cls._session = ConfigSession(os.getpid())
@@ -67,10 +75,11 @@ class VyOSUnitTestSHIM:
cls._session.discard()
cls.fail(cls)
- def cli_set(self, config):
+ def cli_set(self, path, value=None):
if self.debug:
- print('set ' + ' '.join(config))
- self._session.set(config)
+ 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:
@@ -85,15 +94,17 @@ class VyOSUnitTestSHIM:
def cli_commit(self):
if self.debug:
print('commit')
- self._session.commit()
- # during a commit there is a process opening commit_lock, and run() returns 0
+ # 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)
- # wait for FRR reload to be complete
- while process_named_running('frr-reload.py'):
- sleep(0.250)
- # reset getFRRconfig() guard timer
- self.commit_guard = time()
+ # 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 op_mode(self, path : list) -> None:
"""
@@ -108,20 +119,27 @@ class VyOSUnitTestSHIM:
pprint.pprint(out)
return out
- def getFRRconfig(self, string=None, end='$', endsection='^!', daemon='', guard_time=10, empty_retry=0):
- """ Retrieve current "running configuration" from FRR """
- # Sometimes FRR needs some time after reloading the configuration to
- # appear in vtysh. This is a workaround addiung a 10 second guard timer
- # between the last cli_commit() and the first read of FRR config via vtysh
- while (time() - self.commit_guard) < guard_time:
- sleep(0.250) # wait 250 milliseconds
- command = f'vtysh -c "show run {daemon} no-header"'
- if string: command += f' | sed -n "/^{string}{end}/,/{endsection}/p"'
+ 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:
+ if empty_retry > 0:
retry_count = 0
while not out and retry_count < empty_retry:
if self.debug and retry_count % 10 == 0:
@@ -169,6 +187,15 @@ class VyOSUnitTestSHIM:
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')
diff --git a/smoketest/scripts/cli/test_container.py b/smoketest/scripts/cli/test_container.py
index 51559a7c6..daad3a909 100755
--- a/smoketest/scripts/cli/test_container.py
+++ b/smoketest/scripts/cli/test_container.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2021-2024 VyOS maintainers and contributors
+# Copyright (C) 2021-2025 VyOS maintainers and contributors
#
# 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
@@ -14,6 +14,7 @@
# 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 glob
import json
@@ -26,27 +27,28 @@ from vyos.utils.process import cmd
from vyos.utils.process import process_named_running
base_path = ['container']
-cont_image = 'busybox:stable' # busybox is included in vyos-build
PROCESS_NAME = 'conmon'
PROCESS_PIDFILE = '/run/vyos-container-{0}.service.pid'
+busybox_image = 'busybox:stable'
busybox_image_path = '/usr/share/vyos/busybox-stable.tar'
+
def cmd_to_json(command):
c = cmd(command + ' --format=json')
data = json.loads(c)[0]
return data
+
class TestContainer(VyOSUnitTestSHIM.TestCase):
@classmethod
def setUpClass(cls):
super(TestContainer, cls).setUpClass()
- # Load image for smoketest provided in vyos-build
- try:
- cmd(f'cat {busybox_image_path} | sudo podman load')
- except:
- cls.skipTest(cls, reason='busybox image not available')
+ # Load image for smoketest provided in vyos-1x-smoketest
+ if not os.path.exists(busybox_image_path):
+ cls.fail(cls, f'{busybox_image} image not available')
+ cmd(f'sudo podman load -i {busybox_image_path}')
# ensure we can also run this test on a live system - so lets clean
# out the current configuration :)
@@ -55,9 +57,8 @@ class TestContainer(VyOSUnitTestSHIM.TestCase):
@classmethod
def tearDownClass(cls):
super(TestContainer, cls).tearDownClass()
-
# Cleanup podman image
- cmd(f'sudo podman image rm -f {cont_image}')
+ cmd(f'sudo podman image rm -f {busybox_image}')
def tearDown(self):
self.cli_delete(base_path)
@@ -74,13 +75,26 @@ class TestContainer(VyOSUnitTestSHIM.TestCase):
cont_name = 'c1'
self.cli_set(['interfaces', 'ethernet', 'eth0', 'address', '10.0.2.15/24'])
- self.cli_set(['protocols', 'static', 'route', '0.0.0.0/0', 'next-hop', '10.0.2.2'])
+ self.cli_set(
+ ['protocols', 'static', 'route', '0.0.0.0/0', 'next-hop', '10.0.2.2']
+ )
self.cli_set(['system', 'name-server', '1.1.1.1'])
self.cli_set(['system', 'name-server', '8.8.8.8'])
- self.cli_set(base_path + ['name', cont_name, 'image', cont_image])
+ self.cli_set(base_path + ['name', cont_name, 'image', busybox_image])
self.cli_set(base_path + ['name', cont_name, 'allow-host-networks'])
- self.cli_set(base_path + ['name', cont_name, 'sysctl', 'parameter', 'kernel.msgmax', 'value', '4096'])
+ self.cli_set(
+ base_path
+ + [
+ 'name',
+ cont_name,
+ 'sysctl',
+ 'parameter',
+ 'kernel.msgmax',
+ 'value',
+ '4096',
+ ]
+ )
# commit changes
self.cli_commit()
@@ -96,6 +110,14 @@ class TestContainer(VyOSUnitTestSHIM.TestCase):
tmp = cmd(f'sudo podman exec -it {cont_name} sysctl kernel.msgmax')
self.assertEqual(tmp, 'kernel.msgmax = 4096')
+ def test_log_driver(self):
+ self.cli_set(base_path + ['log-driver', 'journald'])
+
+ self.cli_commit()
+
+ tmp = cmd('podman info --format "{{ .Host.LogDriver }}"')
+ self.assertEqual(tmp, 'journald')
+
def test_name_server(self):
cont_name = 'dns-test'
net_name = 'net-test'
@@ -104,9 +126,19 @@ class TestContainer(VyOSUnitTestSHIM.TestCase):
self.cli_set(base_path + ['network', net_name, 'prefix', prefix])
- self.cli_set(base_path + ['name', cont_name, 'image', cont_image])
+ self.cli_set(base_path + ['name', cont_name, 'image', busybox_image])
self.cli_set(base_path + ['name', cont_name, 'name-server', name_server])
- self.cli_set(base_path + ['name', cont_name, 'network', net_name, 'address', str(ip_interface(prefix).ip + 2)])
+ self.cli_set(
+ base_path
+ + [
+ 'name',
+ cont_name,
+ 'network',
+ net_name,
+ 'address',
+ str(ip_interface(prefix).ip + 2),
+ ]
+ )
# verify() - name server has no effect when container network has dns enabled
with self.assertRaises(ConfigSessionError):
@@ -125,7 +157,7 @@ class TestContainer(VyOSUnitTestSHIM.TestCase):
cont_name = 'c2'
self.cli_set(base_path + ['name', cont_name, 'allow-host-networks'])
- self.cli_set(base_path + ['name', cont_name, 'image', cont_image])
+ self.cli_set(base_path + ['name', cont_name, 'image', busybox_image])
self.cli_set(base_path + ['name', cont_name, 'cpu-quota', '1.25'])
self.cli_commit()
@@ -146,8 +178,18 @@ class TestContainer(VyOSUnitTestSHIM.TestCase):
for ii in range(1, 6):
name = f'{base_name}-{ii}'
- self.cli_set(base_path + ['name', name, 'image', cont_image])
- self.cli_set(base_path + ['name', name, 'network', net_name, 'address', str(ip_interface(prefix).ip + ii)])
+ self.cli_set(base_path + ['name', name, 'image', busybox_image])
+ self.cli_set(
+ base_path
+ + [
+ 'name',
+ name,
+ 'network',
+ net_name,
+ 'address',
+ str(ip_interface(prefix).ip + ii),
+ ]
+ )
# verify() - first IP address of a prefix can not be used by a container
with self.assertRaises(ConfigSessionError):
@@ -164,8 +206,14 @@ class TestContainer(VyOSUnitTestSHIM.TestCase):
for ii in range(2, 6):
name = f'{base_name}-{ii}'
c = cmd_to_json(f'sudo podman container inspect {name}')
- self.assertEqual(c['NetworkSettings']['Networks'][net_name]['Gateway'] , str(ip_interface(prefix).ip + 1))
- self.assertEqual(c['NetworkSettings']['Networks'][net_name]['IPAddress'], str(ip_interface(prefix).ip + ii))
+ self.assertEqual(
+ c['NetworkSettings']['Networks'][net_name]['Gateway'],
+ str(ip_interface(prefix).ip + 1),
+ )
+ self.assertEqual(
+ c['NetworkSettings']['Networks'][net_name]['IPAddress'],
+ str(ip_interface(prefix).ip + ii),
+ )
def test_ipv6_network(self):
prefix = '2001:db8::/64'
@@ -176,8 +224,18 @@ class TestContainer(VyOSUnitTestSHIM.TestCase):
for ii in range(1, 6):
name = f'{base_name}-{ii}'
- self.cli_set(base_path + ['name', name, 'image', cont_image])
- self.cli_set(base_path + ['name', name, 'network', net_name, 'address', str(ip_interface(prefix).ip + ii)])
+ self.cli_set(base_path + ['name', name, 'image', busybox_image])
+ self.cli_set(
+ base_path
+ + [
+ 'name',
+ name,
+ 'network',
+ net_name,
+ 'address',
+ str(ip_interface(prefix).ip + ii),
+ ]
+ )
# verify() - first IP address of a prefix can not be used by a container
with self.assertRaises(ConfigSessionError):
@@ -194,8 +252,14 @@ class TestContainer(VyOSUnitTestSHIM.TestCase):
for ii in range(2, 6):
name = f'{base_name}-{ii}'
c = cmd_to_json(f'sudo podman container inspect {name}')
- self.assertEqual(c['NetworkSettings']['Networks'][net_name]['IPv6Gateway'] , str(ip_interface(prefix).ip + 1))
- self.assertEqual(c['NetworkSettings']['Networks'][net_name]['GlobalIPv6Address'], str(ip_interface(prefix).ip + ii))
+ self.assertEqual(
+ c['NetworkSettings']['Networks'][net_name]['IPv6Gateway'],
+ str(ip_interface(prefix).ip + 1),
+ )
+ self.assertEqual(
+ c['NetworkSettings']['Networks'][net_name]['GlobalIPv6Address'],
+ str(ip_interface(prefix).ip + ii),
+ )
def test_dual_stack_network(self):
prefix4 = '192.0.2.0/24'
@@ -208,9 +272,29 @@ class TestContainer(VyOSUnitTestSHIM.TestCase):
for ii in range(1, 6):
name = f'{base_name}-{ii}'
- self.cli_set(base_path + ['name', name, 'image', cont_image])
- self.cli_set(base_path + ['name', name, 'network', net_name, 'address', str(ip_interface(prefix4).ip + ii)])
- self.cli_set(base_path + ['name', name, 'network', net_name, 'address', str(ip_interface(prefix6).ip + ii)])
+ self.cli_set(base_path + ['name', name, 'image', busybox_image])
+ self.cli_set(
+ base_path
+ + [
+ 'name',
+ name,
+ 'network',
+ net_name,
+ 'address',
+ str(ip_interface(prefix4).ip + ii),
+ ]
+ )
+ self.cli_set(
+ base_path
+ + [
+ 'name',
+ name,
+ 'network',
+ net_name,
+ 'address',
+ str(ip_interface(prefix6).ip + ii),
+ ]
+ )
# verify() - first IP address of a prefix can not be used by a container
with self.assertRaises(ConfigSessionError):
@@ -228,10 +312,22 @@ class TestContainer(VyOSUnitTestSHIM.TestCase):
for ii in range(2, 6):
name = f'{base_name}-{ii}'
c = cmd_to_json(f'sudo podman container inspect {name}')
- self.assertEqual(c['NetworkSettings']['Networks'][net_name]['IPv6Gateway'] , str(ip_interface(prefix6).ip + 1))
- self.assertEqual(c['NetworkSettings']['Networks'][net_name]['GlobalIPv6Address'], str(ip_interface(prefix6).ip + ii))
- self.assertEqual(c['NetworkSettings']['Networks'][net_name]['Gateway'] , str(ip_interface(prefix4).ip + 1))
- self.assertEqual(c['NetworkSettings']['Networks'][net_name]['IPAddress'] , str(ip_interface(prefix4).ip + ii))
+ self.assertEqual(
+ c['NetworkSettings']['Networks'][net_name]['IPv6Gateway'],
+ str(ip_interface(prefix6).ip + 1),
+ )
+ self.assertEqual(
+ c['NetworkSettings']['Networks'][net_name]['GlobalIPv6Address'],
+ str(ip_interface(prefix6).ip + ii),
+ )
+ self.assertEqual(
+ c['NetworkSettings']['Networks'][net_name]['Gateway'],
+ str(ip_interface(prefix4).ip + 1),
+ )
+ self.assertEqual(
+ c['NetworkSettings']['Networks'][net_name]['IPAddress'],
+ str(ip_interface(prefix4).ip + ii),
+ )
def test_no_name_server(self):
prefix = '192.0.2.0/24'
@@ -242,8 +338,18 @@ class TestContainer(VyOSUnitTestSHIM.TestCase):
self.cli_set(base_path + ['network', net_name, 'no-name-server'])
name = f'{base_name}-2'
- self.cli_set(base_path + ['name', name, 'image', cont_image])
- self.cli_set(base_path + ['name', name, 'network', net_name, 'address', str(ip_interface(prefix).ip + 2)])
+ self.cli_set(base_path + ['name', name, 'image', busybox_image])
+ self.cli_set(
+ base_path
+ + [
+ 'name',
+ name,
+ 'network',
+ net_name,
+ 'address',
+ str(ip_interface(prefix).ip + 2),
+ ]
+ )
self.cli_commit()
n = cmd_to_json(f'sudo podman network inspect {net_name}')
@@ -258,8 +364,18 @@ class TestContainer(VyOSUnitTestSHIM.TestCase):
self.cli_set(base_path + ['network', net_name, 'mtu', '1280'])
name = f'{base_name}-2'
- self.cli_set(base_path + ['name', name, 'image', cont_image])
- self.cli_set(base_path + ['name', name, 'network', net_name, 'address', str(ip_interface(prefix).ip + 2)])
+ self.cli_set(base_path + ['name', name, 'image', busybox_image])
+ self.cli_set(
+ base_path
+ + [
+ 'name',
+ name,
+ 'network',
+ net_name,
+ 'address',
+ str(ip_interface(prefix).ip + 2),
+ ]
+ )
self.cli_commit()
n = cmd_to_json(f'sudo podman network inspect {net_name}')
@@ -271,7 +387,7 @@ class TestContainer(VyOSUnitTestSHIM.TestCase):
uid = '1001'
self.cli_set(base_path + ['name', cont_name, 'allow-host-networks'])
- self.cli_set(base_path + ['name', cont_name, 'image', cont_image])
+ self.cli_set(base_path + ['name', cont_name, 'image', busybox_image])
self.cli_set(base_path + ['name', cont_name, 'gid', gid])
# verify() - GID can only be set if UID is set
@@ -293,17 +409,20 @@ class TestContainer(VyOSUnitTestSHIM.TestCase):
for ii in container_list:
name = f'{base_name}-{ii}'
- self.cli_set(base_path + ['name', name, 'image', cont_image])
+ self.cli_set(base_path + ['name', name, 'image', busybox_image])
self.cli_set(base_path + ['name', name, 'allow-host-networks'])
self.cli_commit()
# Query API about running containers
- tmp = cmd("sudo curl --unix-socket /run/podman/podman.sock -H 'content-type: application/json' -sf http://localhost/containers/json")
+ tmp = cmd(
+ "sudo curl --unix-socket /run/podman/podman.sock -H 'content-type: application/json' -sf http://localhost/containers/json"
+ )
tmp = json.loads(tmp)
# We expect the same amount of containers from the API that we started above
self.assertEqual(len(container_list), len(tmp))
+
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_firewall.py b/smoketest/scripts/cli/test_firewall.py
index 6420afa38..851a15f16 100755
--- a/smoketest/scripts/cli/test_firewall.py
+++ b/smoketest/scripts/cli/test_firewall.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2021-2023 VyOS maintainers and contributors
+# Copyright (C) 2021-2024 VyOS maintainers and contributors
#
# 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
@@ -119,6 +119,7 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):
self.cli_set(['firewall', 'group', 'domain-group', 'smoketest_domain', 'address', 'example.com'])
self.cli_set(['firewall', 'group', 'domain-group', 'smoketest_domain', 'address', 'example.org'])
self.cli_set(['firewall', 'group', 'interface-group', 'smoketest_interface', 'interface', 'eth0'])
+ self.cli_set(['firewall', 'group', 'interface-group', 'smoketest_interface', 'interface', 'pod-smoketest'])
self.cli_set(['firewall', 'group', 'interface-group', 'smoketest_interface', 'interface', 'vtun0'])
self.cli_set(['firewall', 'ipv4', 'forward', 'filter', 'rule', '1', 'action', 'accept'])
@@ -133,6 +134,9 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):
self.cli_set(['firewall', 'ipv4', 'forward', 'filter', 'rule', '4', 'action', 'accept'])
self.cli_set(['firewall', 'ipv4', 'forward', 'filter', 'rule', '4', 'outbound-interface', 'group', '!smoketest_interface'])
+ # Create container network so test won't fail
+ self.cli_set(['container', 'network', 'smoketest', 'prefix', '10.0.0.0/24'])
+
self.cli_commit()
self.wait_for_domain_resolver('ip vyos_filter', 'D_smoketest_domain', '192.0.2.5')
@@ -638,6 +642,10 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):
self.verify_nftables(nftables_search, 'ip6 vyos_filter')
def test_ipv4_global_state(self):
+ self.cli_set(['firewall', 'flowtable', 'smoketest', 'interface', 'eth0'])
+ self.cli_set(['firewall', 'flowtable', 'smoketest', 'offload', 'software'])
+
+ self.cli_set(['firewall', 'global-options', 'state-policy', 'offload', 'offload-target', 'smoketest'])
self.cli_set(['firewall', 'global-options', 'state-policy', 'established', 'action', 'accept'])
self.cli_set(['firewall', 'global-options', 'state-policy', 'related', 'action', 'accept'])
self.cli_set(['firewall', 'global-options', 'state-policy', 'invalid', 'action', 'drop'])
@@ -647,6 +655,9 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):
nftables_search = [
['jump VYOS_STATE_POLICY'],
['chain VYOS_STATE_POLICY'],
+ ['jump VYOS_STATE_POLICY_FORWARD'],
+ ['chain VYOS_STATE_POLICY_FORWARD'],
+ ['flow add @VYOS_FLOWTABLE_smoketest'],
['ct state established', 'accept'],
['ct state invalid', 'drop'],
['ct state related', 'accept']
@@ -654,6 +665,13 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):
self.verify_nftables(nftables_search, 'ip vyos_filter')
+ # T7148 - Ensure bridge rule reject -> drop
+ self.cli_set(['firewall', 'global-options', 'state-policy', 'invalid', 'action', 'reject'])
+ self.cli_commit()
+
+ self.verify_nftables([['ct state invalid', 'reject']], 'ip vyos_filter')
+ self.verify_nftables([['ct state invalid', 'drop']], 'bridge vyos_filter')
+
# Check conntrack is enabled from state-policy
self.verify_nftables_chain([['accept']], 'ip vyos_conntrack', 'FW_CONNTRACK')
self.verify_nftables_chain([['accept']], 'ip6 vyos_conntrack', 'FW_CONNTRACK')
@@ -906,7 +924,7 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):
def test_zone_basic(self):
self.cli_set(['firewall', 'ipv4', 'name', 'smoketest', 'default-action', 'drop'])
self.cli_set(['firewall', 'ipv6', 'name', 'smoketestv6', 'default-action', 'drop'])
- self.cli_set(['firewall', 'zone', 'smoketest-eth0', 'interface', 'eth0'])
+ self.cli_set(['firewall', 'zone', 'smoketest-eth0', 'member', 'interface', 'eth0'])
self.cli_set(['firewall', 'zone', 'smoketest-eth0', 'from', 'smoketest-local', 'firewall', 'name', 'smoketest'])
self.cli_set(['firewall', 'zone', 'smoketest-eth0', 'intra-zone-filtering', 'firewall', 'ipv6-name', 'smoketestv6'])
self.cli_set(['firewall', 'zone', 'smoketest-local', 'local-zone'])
@@ -964,6 +982,98 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):
self.verify_nftables(nftables_search, 'ip vyos_filter')
self.verify_nftables(nftables_search_v6, 'ip6 vyos_filter')
+ def test_zone_with_vrf(self):
+ self.cli_set(['firewall', 'ipv4', 'name', 'ZONE1-to-LOCAL', 'default-action', 'accept'])
+ self.cli_set(['firewall', 'ipv4', 'name', 'ZONE2_to_ZONE1', 'default-action', 'continue'])
+ self.cli_set(['firewall', 'ipv6', 'name', 'LOCAL_to_ZONE2_v6', 'default-action', 'drop'])
+ self.cli_set(['firewall', 'zone', 'LOCAL', 'from', 'ZONE1', 'firewall', 'name', 'ZONE1-to-LOCAL'])
+ self.cli_set(['firewall', 'zone', 'LOCAL', 'local-zone'])
+ self.cli_set(['firewall', 'zone', 'ZONE1', 'from', 'ZONE2', 'firewall', 'name', 'ZONE2_to_ZONE1'])
+ self.cli_set(['firewall', 'zone', 'ZONE1', 'member', 'interface', 'eth1'])
+ self.cli_set(['firewall', 'zone', 'ZONE1', 'member', 'interface', 'eth2'])
+ self.cli_set(['firewall', 'zone', 'ZONE1', 'member', 'vrf', 'VRF-1'])
+ self.cli_set(['firewall', 'zone', 'ZONE2', 'from', 'LOCAL', 'firewall', 'ipv6-name', 'LOCAL_to_ZONE2_v6'])
+ self.cli_set(['firewall', 'zone', 'ZONE2', 'member', 'interface', 'vtun66'])
+ self.cli_set(['firewall', 'zone', 'ZONE2', 'member', 'vrf', 'VRF-2'])
+
+ self.cli_set(['vrf', 'name', 'VRF-1', 'table', '101'])
+ self.cli_set(['vrf', 'name', 'VRF-2', 'table', '102'])
+ self.cli_set(['interfaces', 'ethernet', 'eth0', 'vrf', 'VRF-1'])
+ self.cli_set(['interfaces', 'vti', 'vti1', 'vrf', 'VRF-2'])
+
+ self.cli_commit()
+
+ nftables_search = [
+ ['chain NAME_ZONE1-to-LOCAL'],
+ ['counter', 'accept', 'comment "NAM-ZONE1-to-LOCAL default-action accept"'],
+ ['chain NAME_ZONE2_to_ZONE1'],
+ ['counter', 'continue', 'comment "NAM-ZONE2_to_ZONE1 default-action continue"'],
+ ['chain VYOS_ZONE_FORWARD'],
+ ['type filter hook forward priority filter + 1'],
+ ['oifname { "eth1", "eth2" }', 'counter packets', 'jump VZONE_ZONE1'],
+ ['oifname "eth0"', 'counter packets', 'jump VZONE_ZONE1'],
+ ['oifname "vtun66"', 'counter packets', 'jump VZONE_ZONE2'],
+ ['oifname "vti1"', 'counter packets', 'jump VZONE_ZONE2'],
+ ['chain VYOS_ZONE_LOCAL'],
+ ['type filter hook input priority filter + 1'],
+ ['counter packets', 'jump VZONE_LOCAL_IN'],
+ ['chain VYOS_ZONE_OUTPUT'],
+ ['type filter hook output priority filter + 1'],
+ ['counter packets', 'jump VZONE_LOCAL_OUT'],
+ ['chain VZONE_LOCAL_IN'],
+ ['iifname { "eth1", "eth2" }', 'counter packets', 'jump NAME_ZONE1-to-LOCAL'],
+ ['iifname "VRF-1"', 'counter packets', 'jump NAME_ZONE1-to-LOCAL'],
+ ['counter packets', 'drop', 'comment "zone_LOCAL default-action drop"'],
+ ['chain VZONE_LOCAL_OUT'],
+ ['counter packets', 'drop', 'comment "zone_LOCAL default-action drop"'],
+ ['chain VZONE_ZONE1'],
+ ['iifname { "eth1", "eth2" }', 'counter packets', 'return'],
+ ['iifname "VRF-1"', 'counter packets', 'return'],
+ ['iifname "vtun66"', 'counter packets', 'jump NAME_ZONE2_to_ZONE1'],
+ ['iifname "vtun66"', 'counter packets', 'return'],
+ ['iifname "VRF-2"', 'counter packets', 'jump NAME_ZONE2_to_ZONE1'],
+ ['iifname "VRF-2"', 'counter packets', 'return'],
+ ['counter packets', 'drop', 'comment "zone_ZONE1 default-action drop"'],
+ ['chain VZONE_ZONE2'],
+ ['iifname "vtun66"', 'counter packets', 'return'],
+ ['iifname "VRF-2"', 'counter packets', 'return'],
+ ['counter packets', 'drop', 'comment "zone_ZONE2 default-action drop"']
+ ]
+
+ nftables_search_v6 = [
+ ['chain NAME6_LOCAL_to_ZONE2_v6'],
+ ['counter', 'drop', 'comment "NAM-LOCAL_to_ZONE2_v6 default-action drop"'],
+ ['chain VYOS_ZONE_FORWARD'],
+ ['type filter hook forward priority filter + 1'],
+ ['oifname { "eth1", "eth2" }', 'counter packets', 'jump VZONE_ZONE1'],
+ ['oifname "eth0"', 'counter packets', 'jump VZONE_ZONE1'],
+ ['oifname "vtun66"', 'counter packets', 'jump VZONE_ZONE2'],
+ ['oifname "vti1"', 'counter packets', 'jump VZONE_ZONE2'],
+ ['chain VYOS_ZONE_LOCAL'],
+ ['type filter hook input priority filter + 1'],
+ ['counter packets', 'jump VZONE_LOCAL_IN'],
+ ['chain VYOS_ZONE_OUTPUT'],
+ ['type filter hook output priority filter + 1'],
+ ['counter packets', 'jump VZONE_LOCAL_OUT'],
+ ['chain VZONE_LOCAL_IN'],
+ ['counter packets', 'drop', 'comment "zone_LOCAL default-action drop"'],
+ ['chain VZONE_LOCAL_OUT'],
+ ['oifname "vtun66"', 'counter packets', 'jump NAME6_LOCAL_to_ZONE2_v6'],
+ ['oifname "vti1"', 'counter packets', 'jump NAME6_LOCAL_to_ZONE2_v6'],
+ ['counter packets', 'drop', 'comment "zone_LOCAL default-action drop"'],
+ ['chain VZONE_ZONE1'],
+ ['iifname { "eth1", "eth2" }', 'counter packets', 'return'],
+ ['iifname "VRF-1"', 'counter packets', 'return'],
+ ['counter packets', 'drop', 'comment "zone_ZONE1 default-action drop"'],
+ ['chain VZONE_ZONE2'],
+ ['iifname "vtun66"', 'counter packets', 'return'],
+ ['iifname "VRF-2"', 'counter packets', 'return'],
+ ['counter packets', 'drop', 'comment "zone_ZONE2 default-action drop"']
+ ]
+
+ self.verify_nftables(nftables_search, 'ip vyos_filter')
+ self.verify_nftables(nftables_search_v6, 'ip6 vyos_filter')
+
def test_flow_offload(self):
self.cli_set(['interfaces', 'ethernet', 'eth0', 'vif', '10'])
self.cli_set(['firewall', 'flowtable', 'smoketest', 'interface', 'eth0.10'])
@@ -1075,7 +1185,7 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):
self.cli_set(['firewall', 'ipv4', 'forward', 'filter', 'rule', '1', 'jump-target', 'smoketest-ipsec-in4'])
self.cli_set(['firewall', 'ipv4', 'prerouting', 'raw', 'rule', '1', 'action', 'jump'])
self.cli_set(['firewall', 'ipv4', 'prerouting', 'raw', 'rule', '1', 'jump-target', 'smoketest-ipsec-in4'])
-
+
self.cli_set(['firewall', 'ipv4', 'output', 'filter', 'rule', '1', 'action', 'jump'])
self.cli_set(['firewall', 'ipv4', 'output', 'filter', 'rule', '1', 'jump-target', 'smoketest-ipsec-out4'])
self.cli_set(['firewall', 'ipv4', 'forward', 'filter', 'rule', '1', 'action', 'jump'])
@@ -1110,8 +1220,8 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):
self.cli_set(['firewall', 'ipv4', 'name', 'smoketest-cycle-3', 'rule', '1', 'action', 'jump'])
self.cli_set(['firewall', 'ipv4', 'name', 'smoketest-cycle-3', 'rule', '1', 'jump-target', 'smoketest-cycle-1'])
- # nft will fail to load cyclic jumps in any form, whether the rule is reachable or not.
- # It should be caught by conf validation.
+ # nft will fail to load cyclic jumps in any form, whether the rule is reachable or not.
+ # It should be caught by conf validation.
with self.assertRaises(ConfigSessionError):
self.cli_commit()
@@ -1170,5 +1280,113 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):
with self.assertRaises(ConfigSessionError):
self.cli_commit()
+ def test_ipv4_remote_group(self):
+ # Setup base config for test
+ self.cli_set(['firewall', 'group', 'remote-group', 'group01', 'url', 'http://127.0.0.1:80/list.txt'])
+ self.cli_set(['firewall', 'group', 'remote-group', 'group01', 'description', 'Example Group 01'])
+ self.cli_set(['firewall', 'ipv4', 'input', 'filter', 'rule', '10', 'action', 'drop'])
+ self.cli_set(['firewall', 'ipv4', 'input', 'filter', 'rule', '10', 'protocol', 'tcp'])
+ self.cli_set(['firewall', 'ipv4', 'input', 'filter', 'rule', '10', 'destination', 'group', 'remote-group', 'group01'])
+
+ self.cli_commit()
+
+ # Test remote-group had been loaded correctly in nft
+ nftables_search = [
+ ['R_group01'],
+ ['type ipv4_addr'],
+ ['flags interval'],
+ ['meta l4proto', 'daddr @R_group01', 'ipv4-INP-filter-10']
+ ]
+ self.verify_nftables(nftables_search, 'ip vyos_filter')
+
+ # Test remote-group cannot be configured without a URL
+ self.cli_delete(['firewall', 'group', 'remote-group', 'group01', 'url'])
+
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+ self.cli_discard()
+
+ # Test remote-group cannot be set alongside address in rules
+ self.cli_set(['firewall', 'ipv4', 'input', 'filter', 'rule', '10', 'destination', 'address', '127.0.0.1'])
+
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+ self.cli_discard()
+
+
+ def test_ipv6_remote_group(self):
+ # Setup base config for test
+ self.cli_set(['firewall', 'group', 'remote-group', 'group01', 'url', 'http://127.0.0.1:80/list.txt'])
+ self.cli_set(['firewall', 'group', 'remote-group', 'group01', 'description', 'Example Group 01'])
+ self.cli_set(['firewall', 'ipv6', 'input', 'filter', 'rule', '10', 'action', 'drop'])
+ self.cli_set(['firewall', 'ipv6', 'input', 'filter', 'rule', '10', 'protocol', 'tcp'])
+ self.cli_set(['firewall', 'ipv6', 'input', 'filter', 'rule', '10', 'destination', 'group', 'remote-group', 'group01'])
+
+ self.cli_commit()
+
+ # Test remote-group had been loaded correctly in nft
+ nftables_search = [
+ ['R6_group01'],
+ ['type ipv6_addr'],
+ ['flags interval'],
+ ['meta l4proto', 'daddr @R6_group01', 'ipv6-INP-filter-10']
+ ]
+ self.verify_nftables(nftables_search, 'ip6 vyos_filter')
+
+ # Test remote-group cannot be configured without a URL
+ self.cli_delete(['firewall', 'group', 'remote-group', 'group01', 'url'])
+
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+ self.cli_discard()
+
+ # Test remote-group cannot be set alongside address in rules
+ self.cli_set(['firewall', 'ipv6', 'input', 'filter', 'rule', '10', 'destination', 'address', '2001:db8::1'])
+
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+ self.cli_discard()
+
+
+ def test_remote_group(self):
+ # Setup base config for test adding remote group to both ipv4 and ipv6 rules
+ self.cli_set(['firewall', 'group', 'remote-group', 'group01', 'url', 'http://127.0.0.1:80/list.txt'])
+ self.cli_set(['firewall', 'group', 'remote-group', 'group01', 'description', 'Example Group 01'])
+ self.cli_set(['firewall', 'ipv4', 'output', 'filter', 'rule', '10', 'action', 'drop'])
+ self.cli_set(['firewall', 'ipv4', 'output', 'filter', 'rule', '10', 'protocol', 'tcp'])
+ self.cli_set(['firewall', 'ipv4', 'output', 'filter', 'rule', '10', 'destination', 'group', 'remote-group', 'group01'])
+ self.cli_set(['firewall', 'ipv4', 'input', 'filter', 'rule', '10', 'action', 'drop'])
+ self.cli_set(['firewall', 'ipv4', 'input', 'filter', 'rule', '10', 'protocol', 'tcp'])
+ self.cli_set(['firewall', 'ipv4', 'input', 'filter', 'rule', '10', 'source', 'group', 'remote-group', 'group01'])
+ self.cli_set(['firewall', 'ipv6', 'output', 'filter', 'rule', '10', 'action', 'drop'])
+ self.cli_set(['firewall', 'ipv6', 'output', 'filter', 'rule', '10', 'protocol', 'tcp'])
+ self.cli_set(['firewall', 'ipv6', 'output', 'filter', 'rule', '10', 'destination', 'group', 'remote-group', 'group01'])
+ self.cli_set(['firewall', 'ipv6', 'input', 'filter', 'rule', '10', 'action', 'drop'])
+ self.cli_set(['firewall', 'ipv6', 'input', 'filter', 'rule', '10', 'protocol', 'tcp'])
+ self.cli_set(['firewall', 'ipv6', 'input', 'filter', 'rule', '10', 'source', 'group', 'remote-group', 'group01'])
+
+ self.cli_commit()
+
+ # Test remote-group had been loaded correctly in nft ip table
+ nftables_v4_search = [
+ ['R_group01'],
+ ['type ipv4_addr'],
+ ['flags interval'],
+ ['meta l4proto', 'daddr @R_group01', 'ipv4-OUT-filter-10'],
+ ['meta l4proto', 'saddr @R_group01', 'ipv4-INP-filter-10'],
+ ]
+ self.verify_nftables(nftables_v4_search, 'ip vyos_filter')
+
+ # Test remote-group had been loaded correctly in nft ip6 table
+ nftables_v6_search = [
+ ['R6_group01'],
+ ['type ipv6_addr'],
+ ['flags interval'],
+ ['meta l4proto', 'daddr @R6_group01', 'ipv6-OUT-filter-10'],
+ ['meta l4proto', 'saddr @R6_group01', 'ipv6-INP-filter-10'],
+ ]
+ self.verify_nftables(nftables_v6_search, 'ip6 vyos_filter')
+
+
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_interfaces_bonding.py b/smoketest/scripts/cli/test_interfaces_bonding.py
index 735e4f3c5..f99fd0363 100755
--- a/smoketest/scripts/cli/test_interfaces_bonding.py
+++ b/smoketest/scripts/cli/test_interfaces_bonding.py
@@ -24,7 +24,6 @@ from vyos.ifconfig.interface import Interface
from vyos.configsession import ConfigSessionError
from vyos.utils.network import get_interface_config
from vyos.utils.file import read_file
-from vyos.frrender import mgmt_daemon
class BondingInterfaceTest(BasicInterfaceTest.TestCase):
@classmethod
@@ -168,18 +167,25 @@ class BondingInterfaceTest(BasicInterfaceTest.TestCase):
def test_bonding_multi_use_member(self):
# Define available bonding hash policies
- for interface in ['bond10', 'bond20']:
+ bonds = ['bond10', 'bond20', 'bond30']
+ for interface in bonds:
for member in self._members:
self.cli_set(self._base_path + [interface, 'member', 'interface', member])
# check validate() - can not use the same member interfaces multiple times
with self.assertRaises(ConfigSessionError):
self.cli_commit()
-
- self.cli_delete(self._base_path + ['bond20'])
+ # only keep the first bond interface configuration
+ for interface in bonds[1:]:
+ self.cli_delete(self._base_path + [interface])
self.cli_commit()
+ bond = bonds[0]
+ member_ifaces = read_file(f'/sys/class/net/{bond}/bonding/slaves').split()
+ for member in self._members:
+ self.assertIn(member, member_ifaces)
+
def test_bonding_source_interface(self):
# Re-use member interface that is already a source-interface
bond = 'bond99'
@@ -287,7 +293,7 @@ class BondingInterfaceTest(BasicInterfaceTest.TestCase):
id = '5'
for interface in self._interfaces:
- frrconfig = self.getFRRconfig(f'interface {interface}', daemon=mgmt_daemon)
+ frrconfig = self.getFRRconfig(f'interface {interface}', endsection='^exit')
self.assertIn(f' evpn mh es-id {id}', frrconfig)
self.assertIn(f' evpn mh es-df-pref {id}', frrconfig)
@@ -304,7 +310,7 @@ class BondingInterfaceTest(BasicInterfaceTest.TestCase):
id = '5'
for interface in self._interfaces:
- frrconfig = self.getFRRconfig(f'interface {interface}', daemon=mgmt_daemon)
+ frrconfig = self.getFRRconfig(f'interface {interface}', endsection='^exit')
self.assertIn(f' evpn mh es-sys-mac 00:12:34:56:78:0{id}', frrconfig)
self.assertIn(f' evpn mh uplink', frrconfig)
diff --git a/smoketest/scripts/cli/test_interfaces_bridge.py b/smoketest/scripts/cli/test_interfaces_bridge.py
index 54c981adc..4041b3ef3 100755
--- a/smoketest/scripts/cli/test_interfaces_bridge.py
+++ b/smoketest/scripts/cli/test_interfaces_bridge.py
@@ -158,6 +158,21 @@ class BridgeInterfaceTest(BasicInterfaceTest.TestCase):
# verify member is assigned to the bridge
self.assertEqual(interface, tmp['master'])
+ def test_bridge_multi_use_member(self):
+ # Define available bonding hash policies
+ bridges = ['br10', 'br20', 'br30']
+ for interface in bridges:
+ for member in self._members:
+ self.cli_set(self._base_path + [interface, 'member', 'interface', member])
+
+ # check validate() - can not use the same member interfaces multiple times
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+ # only keep the first bond interface configuration
+ for interface in bridges[1:]:
+ self.cli_delete(self._base_path + [interface])
+
+ self.cli_commit()
def test_add_remove_bridge_member(self):
# Add member interfaces to bridge and set STP cost/priority
diff --git a/smoketest/scripts/cli/test_interfaces_ethernet.py b/smoketest/scripts/cli/test_interfaces_ethernet.py
index 183c10250..2b421e942 100755
--- a/smoketest/scripts/cli/test_interfaces_ethernet.py
+++ b/smoketest/scripts/cli/test_interfaces_ethernet.py
@@ -27,14 +27,12 @@ from netifaces import ifaddresses
from base_interfaces_test import BasicInterfaceTest
from vyos.configsession import ConfigSessionError
from vyos.ifconfig import Section
-from vyos.frrender import mgmt_daemon
from vyos.utils.file import read_file
from vyos.utils.network import is_intf_addr_assigned
from vyos.utils.network import is_ipv6_link_local
from vyos.utils.process import cmd
from vyos.utils.process import popen
-
class EthernetInterfaceTest(BasicInterfaceTest.TestCase):
@classmethod
def setUpClass(cls):
@@ -222,14 +220,14 @@ class EthernetInterfaceTest(BasicInterfaceTest.TestCase):
out = loads(out)
self.assertFalse(out[0]['autonegotiate'])
- def test_ethtool_evpn_uplink_tarcking(self):
+ def test_ethtool_evpn_uplink_tracking(self):
for interface in self._interfaces:
self.cli_set(self._base_path + [interface, 'evpn', 'uplink'])
self.cli_commit()
for interface in self._interfaces:
- frrconfig = self.getFRRconfig(f'interface {interface}', daemon=mgmt_daemon)
+ frrconfig = self.getFRRconfig(f'interface {interface}', endsection='^exit')
self.assertIn(' evpn mh uplink', frrconfig)
def test_switchdev(self):
@@ -241,6 +239,5 @@ class EthernetInterfaceTest(BasicInterfaceTest.TestCase):
self.cli_delete(self._base_path + [interface, 'switchdev'])
-
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_interfaces_loopback.py b/smoketest/scripts/cli/test_interfaces_loopback.py
index 0454dc658..f4b6038c5 100755
--- a/smoketest/scripts/cli/test_interfaces_loopback.py
+++ b/smoketest/scripts/cli/test_interfaces_loopback.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2020-2023 VyOS maintainers and contributors
+# Copyright (C) 2020-2025 VyOS maintainers and contributors
#
# 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
@@ -17,6 +17,7 @@
import unittest
from base_interfaces_test import BasicInterfaceTest
+from base_interfaces_test import MSG_TESTCASE_UNSUPPORTED
from netifaces import interfaces
from vyos.utils.network import is_intf_addr_assigned
@@ -53,7 +54,7 @@ class LoopbackInterfaceTest(BasicInterfaceTest.TestCase):
self.assertTrue(is_intf_addr_assigned('lo', addr))
def test_interface_disable(self):
- self.skipTest('not supported')
+ self.skipTest(MSG_TESTCASE_UNSUPPORTED)
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_interfaces_virtual-ethernet.py b/smoketest/scripts/cli/test_interfaces_virtual-ethernet.py
index c6a4613a7..b2af86139 100755
--- a/smoketest/scripts/cli/test_interfaces_virtual-ethernet.py
+++ b/smoketest/scripts/cli/test_interfaces_virtual-ethernet.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2023-2024 VyOS maintainers and contributors
+# Copyright (C) 2023-2025 VyOS maintainers and contributors
#
# 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
@@ -34,9 +34,6 @@ class VEthInterfaceTest(BasicInterfaceTest.TestCase):
# call base-classes classmethod
super(VEthInterfaceTest, cls).setUpClass()
- def test_vif_8021q_mtu_limits(self):
- self.skipTest('not supported')
-
# As we always need a pair of veth interfaces, we can not rely on the base
# class check to determine if there is a dhcp6c or dhclient instance running.
# This test will always fail as there is an instance running on the peer
diff --git a/smoketest/scripts/cli/test_interfaces_vxlan.py b/smoketest/scripts/cli/test_interfaces_vxlan.py
index b2076b43b..132496124 100755
--- a/smoketest/scripts/cli/test_interfaces_vxlan.py
+++ b/smoketest/scripts/cli/test_interfaces_vxlan.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2020-2023 VyOS maintainers and contributors
+# Copyright (C) 2020-2025 VyOS maintainers and contributors
#
# 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
@@ -114,6 +114,30 @@ class VXLANInterfaceTest(BasicInterfaceTest.TestCase):
self.assertEqual(Interface(interface).get_admin_state(), 'up')
ttl += 10
+
+ def test_vxlan_group_remote_error(self):
+ intf = 'vxlan60'
+ options = [
+ 'group 239.4.4.5',
+ 'mtu 1420',
+ 'remote 192.168.0.254',
+ 'source-address 192.168.0.1',
+ 'source-interface eth0',
+ 'vni 60'
+ ]
+ for option in options:
+ opts = option.split()
+ self.cli_set(self._base_path + [intf] + opts)
+
+ # verify() - Both group and remote cannot be specified
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+
+ # Remove blocking CLI option
+ self.cli_delete(self._base_path + [intf, 'group'])
+ self.cli_commit()
+
+
def test_vxlan_external(self):
interface = 'vxlan0'
source_address = '192.0.2.1'
diff --git a/smoketest/scripts/cli/test_interfaces_wireguard.py b/smoketest/scripts/cli/test_interfaces_wireguard.py
index 4b994a659..f8cd18cf2 100755
--- a/smoketest/scripts/cli/test_interfaces_wireguard.py
+++ b/smoketest/scripts/cli/test_interfaces_wireguard.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2020-2023 VyOS maintainers and contributors
+# Copyright (C) 2020-2025 VyOS maintainers and contributors
#
# 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
@@ -17,27 +17,33 @@
import os
import unittest
-from base_vyostest_shim import VyOSUnitTestSHIM
+from base_interfaces_test import BasicInterfaceTest
from vyos.configsession import ConfigSessionError
from vyos.utils.file import read_file
from vyos.utils.process import cmd
+from vyos.utils.process import is_systemd_service_running
base_path = ['interfaces', 'wireguard']
-
-class WireGuardInterfaceTest(VyOSUnitTestSHIM.TestCase):
+domain_resolver = 'vyos-domain-resolver.service'
+class WireGuardInterfaceTest(BasicInterfaceTest.TestCase):
@classmethod
def setUpClass(cls):
- super(WireGuardInterfaceTest, cls).setUpClass()
-
- cls._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']
- cls._interfaces = ['wg0', 'wg1']
+ cls._base_path = ['interfaces', 'wireguard']
+ cls._options = {
+ 'wg0': ['private-key wBbGJJXYllwDcw63AFjiIR6ZlsvqvAf3eDwog64Dp0Q=',
+ 'peer RED public-key 6hkkfxN4VUQLu36NLZr47I7ST/FkQl2clPWr+9a6ZH8=',
+ 'peer RED allowed-ips 169.254.0.0/16',
+ 'port 5678'],
+ 'wg1': ['private-key QFwnBHlHYspehvpklBKb7cikM+QMkEy2p6gfsg06S08=',
+ 'peer BLUE public-key hRJLmP8SVU9/MLmPmYmpOa+RTB4F/zhDqA+/QDuW1Hg=',
+ 'peer BLUE allowed-ips 169.254.0.0/16',
+ 'port 4567'],
+ }
+ cls._interfaces = list(cls._options)
- def tearDown(self):
- self.cli_delete(base_path)
- self.cli_commit()
+ super(WireGuardInterfaceTest, cls).setUpClass()
- def test_01_wireguard_peer(self):
+ def test_wireguard_peer(self):
# Create WireGuard interfaces with associated peers
for intf in self._interfaces:
peer = 'foo-' + intf
@@ -64,7 +70,7 @@ class WireGuardInterfaceTest(VyOSUnitTestSHIM.TestCase):
self.assertTrue(os.path.isdir(f'/sys/class/net/{intf}'))
- def test_02_wireguard_add_remove_peer(self):
+ def test_wireguard_add_remove_peer(self):
# T2939: Create WireGuard interfaces with associated peers.
# Remove one of the configured peers.
# T4774: Test prevention of duplicate peer public keys
@@ -102,7 +108,7 @@ class WireGuardInterfaceTest(VyOSUnitTestSHIM.TestCase):
self.cli_delete(base_path + [interface, 'peer', 'PEER01'])
self.cli_commit()
- def test_03_wireguard_same_public_key(self):
+ def test_wireguard_same_public_key(self):
# T5413: Test prevention of equality interface public key and peer's
# public key
interface = 'wg0'
@@ -115,45 +121,40 @@ class WireGuardInterfaceTest(VyOSUnitTestSHIM.TestCase):
self.cli_set(base_path + [interface, 'private-key', privkey])
self.cli_set(base_path + [interface, 'peer', 'PEER01', 'public-key', pubkey_fail])
- self.cli_set(base_path + [interface, 'peer', 'PEER01', 'port', port])
self.cli_set(base_path + [interface, 'peer', 'PEER01', 'allowed-ips', '10.205.212.10/32'])
self.cli_set(base_path + [interface, 'peer', 'PEER01', 'address', '192.0.2.1'])
# The same pubkey as the interface wg0
with self.assertRaises(ConfigSessionError):
self.cli_commit()
-
self.cli_set(base_path + [interface, 'peer', 'PEER01', 'public-key', pubkey_ok])
+ # If address is defined for a peer, so must be the peer port
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+ self.cli_set(base_path + [interface, 'peer', 'PEER01', 'port', port])
+
# Commit peers
self.cli_commit()
self.assertTrue(os.path.isdir(f'/sys/class/net/{interface}'))
- def test_04_wireguard_threaded(self):
+ def test_wireguard_threaded(self):
# T5409: Test adding threaded option on interface.
- # Test prevention for adding threaded
- # if no enabled peer is configured.
- interface = 'wg0'
- port = '12345'
- privkey = 'OOjcXGfgQlAuM6q8Z9aAYduCua7pxf7UKYvIqoUPoGQ='
- pubkey = 'ebFx/1G0ti8tvuZd94sEIosAZZIznX+dBAKG/8DFm0I='
+ for intf in self._interfaces:
+ for option in self._options.get(intf, []):
+ self.cli_set(self._base_path + [intf] + option.split())
- self.cli_set(base_path + [interface, 'address', '172.16.0.1/24'])
- self.cli_set(base_path + [interface, 'private-key', privkey])
-
- self.cli_set(base_path + [interface, 'peer', 'PEER01', 'port', port])
- self.cli_set(base_path + [interface, 'peer', 'PEER01', 'public-key', pubkey])
- self.cli_set(base_path + [interface, 'peer', 'PEER01', 'allowed-ips', '10.205.212.10/32'])
- self.cli_set(base_path + [interface, 'peer', 'PEER01', 'address', '192.0.2.1'])
- self.cli_set(base_path + [interface, 'per-client-thread'])
+ self.cli_set(base_path + [intf, 'per-client-thread'])
# Commit peers
self.cli_commit()
- tmp = read_file(f'/sys/class/net/{interface}/threaded')
- self.assertTrue(tmp, "1")
- def test_05_wireguard_peer_pubkey_change(self):
+ for intf in self._interfaces:
+ tmp = read_file(f'/sys/class/net/{intf}/threaded')
+ self.assertTrue(tmp, "1")
+
+ def test_wireguard_peer_pubkey_change(self):
# T5707 changing WireGuard CLI public key of a peer - it's not removed
def get_peers(interface) -> list:
@@ -171,7 +172,6 @@ class WireGuardInterfaceTest(VyOSUnitTestSHIM.TestCase):
peers.append(items[0])
return peers
-
interface = 'wg1337'
port = '1337'
privkey = 'iJi4lb2HhkLx2KSAGOjji2alKkYsJjSPkHkrcpxgEVU='
@@ -200,5 +200,41 @@ class WireGuardInterfaceTest(VyOSUnitTestSHIM.TestCase):
self.assertNotIn(pubkey_1, peers)
self.assertIn(pubkey_2, peers)
+ def test_wireguard_hostname(self):
+ # T4930: Test dynamic endpoint support
+ interface = 'wg1234'
+ port = '54321'
+ privkey = 'UOWIeZKNzijhgu0bPRy2PB3gnuOBLfQax5GiYfkmU3A='
+ pubkey = '4nG5NfhHBQUq/DnwT0RjRoBCqAh3VrRHqdQgzC/xujk='
+
+ base_interface_path = base_path + [interface]
+ self.cli_set(base_interface_path + ['address', '172.16.0.1/24'])
+ self.cli_set(base_interface_path + ['private-key', privkey])
+
+ peer_base_path = base_interface_path + ['peer', 'dynamic01']
+ self.cli_set(peer_base_path + ['port', port])
+ self.cli_set(peer_base_path + ['public-key', pubkey])
+ self.cli_set(peer_base_path + ['allowed-ips', '169.254.0.0/16'])
+ self.cli_set(peer_base_path + ['address', '192.0.2.1'])
+ self.cli_set(peer_base_path + ['host-name', 'wg.vyos.net'])
+
+ # Peer address and host-name are mutually exclusive
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+
+ self.cli_delete(peer_base_path + ['address'])
+
+ # Commit peers
+ self.cli_commit()
+
+ # Ensure the service is running which checks for DNS changes
+ self.assertTrue(is_systemd_service_running(domain_resolver))
+
+ self.cli_delete(base_interface_path)
+ self.cli_commit()
+
+ # Ensure the service is no longer running after WireGuard interface is deleted
+ self.assertFalse(is_systemd_service_running(domain_resolver))
+
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_interfaces_wireless.py b/smoketest/scripts/cli/test_interfaces_wireless.py
index b8b18f30f..1c69c1be5 100755
--- a/smoketest/scripts/cli/test_interfaces_wireless.py
+++ b/smoketest/scripts/cli/test_interfaces_wireless.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2020-2024 VyOS maintainers and contributors
+# Copyright (C) 2020-2025 VyOS maintainers and contributors
#
# 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
@@ -64,13 +64,23 @@ class WirelessInterfaceTest(BasicInterfaceTest.TestCase):
# call base-classes classmethod
super(WirelessInterfaceTest, cls).setUpClass()
- # T5245 - currently testcases are disabled
- cls._test_ipv6 = False
- cls._test_vlan = False
+ # If any wireless interface is based on mac80211_hwsim, disable all
+ # VLAN related testcases. See T5245, T7325
+ tmp = read_file('/proc/modules')
+ if 'mac80211_hwsim' in tmp:
+ cls._test_ipv6 = False
+ cls._test_vlan = False
+ cls._test_qinq = False
+
+ # Loading mac80211_hwsim module created two WIFI Interfaces in the
+ # background (wlan0 and wlan1), remove them to have a clean test start.
+ # This must happen AFTER the above check for unsupported drivers
+ for interface in cls._interfaces:
+ if interface_exists(interface):
+ call(f'sudo iw dev {interface} del')
cls.cli_set(cls, wifi_cc_path + [country])
-
def test_wireless_add_single_ip_address(self):
# derived method to check if member interfaces are enslaved properly
super().test_add_single_ip_address()
@@ -627,9 +637,4 @@ class WirelessInterfaceTest(BasicInterfaceTest.TestCase):
if __name__ == '__main__':
check_kmod('mac80211_hwsim')
- # loading the module created two WIFI Interfaces in the background (wlan0 and wlan1)
- # remove them to have a clean test start
- for interface in ['wlan0', 'wlan1']:
- if interface_exists(interface):
- call(f'sudo iw dev {interface} del')
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_load-balancing_haproxy.py b/smoketest/scripts/cli/test_load-balancing_haproxy.py
index 967eb3869..833e0a92b 100755
--- a/smoketest/scripts/cli/test_load-balancing_haproxy.py
+++ b/smoketest/scripts/cli/test_load-balancing_haproxy.py
@@ -14,11 +14,14 @@
# 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 textwrap
import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
from vyos.configsession import ConfigSessionError
+from vyos.template import get_default_port
from vyos.utils.process import process_named_running
from vyos.utils.file import read_file
@@ -131,7 +134,25 @@ ZXLrtgVJR9W020qTurO2f91qfU8646n11hR9ObBB1IYbagOU0Pw1Nrq/FRp/u2tx
7i7xFz2WEiQeSCPaKYOiqM3t
"""
+haproxy_service_name = 'https_front'
+haproxy_backend_name = 'bk-01'
+def parse_haproxy_config() -> dict:
+ config_str = read_file(HAPROXY_CONF)
+ section_pattern = re.compile(r'^(global|defaults|frontend\s+\S+|backend\s+\S+)', re.MULTILINE)
+ sections = {}
+
+ matches = list(section_pattern.finditer(config_str))
+
+ for i, match in enumerate(matches):
+ section_name = match.group(1).strip()
+ start = match.end()
+ end = matches[i + 1].start() if i + 1 < len(matches) else len(config_str)
+ section_body = config_str[start:end]
+ dedented_body = textwrap.dedent(section_body).strip()
+ sections[section_name] = dedented_body
+
+ return sections
class TestLoadBalancingReverseProxy(VyOSUnitTestSHIM.TestCase):
def tearDown(self):
# Check for running process
@@ -146,14 +167,14 @@ class TestLoadBalancingReverseProxy(VyOSUnitTestSHIM.TestCase):
self.assertFalse(process_named_running(PROCESS_NAME))
def base_config(self):
- self.cli_set(base_path + ['service', 'https_front', 'mode', 'http'])
- self.cli_set(base_path + ['service', 'https_front', 'port', '4433'])
- self.cli_set(base_path + ['service', 'https_front', 'backend', 'bk-01'])
+ self.cli_set(base_path + ['service', haproxy_service_name, 'mode', 'http'])
+ self.cli_set(base_path + ['service', haproxy_service_name, 'port', '4433'])
+ self.cli_set(base_path + ['service', haproxy_service_name, 'backend', haproxy_backend_name])
- self.cli_set(base_path + ['backend', 'bk-01', 'mode', 'http'])
- self.cli_set(base_path + ['backend', 'bk-01', 'server', 'bk-01', 'address', '192.0.2.11'])
- self.cli_set(base_path + ['backend', 'bk-01', 'server', 'bk-01', 'port', '9090'])
- self.cli_set(base_path + ['backend', 'bk-01', 'server', 'bk-01', 'send-proxy'])
+ self.cli_set(base_path + ['backend', haproxy_backend_name, 'mode', 'http'])
+ self.cli_set(base_path + ['backend', haproxy_backend_name, 'server', haproxy_backend_name, 'address', '192.0.2.11'])
+ self.cli_set(base_path + ['backend', haproxy_backend_name, 'server', haproxy_backend_name, 'port', '9090'])
+ self.cli_set(base_path + ['backend', haproxy_backend_name, 'server', haproxy_backend_name, 'send-proxy'])
self.cli_set(base_path + ['global-parameters', 'max-connections', '1000'])
@@ -167,15 +188,15 @@ class TestLoadBalancingReverseProxy(VyOSUnitTestSHIM.TestCase):
self.cli_set(['pki', 'certificate', 'smoketest', 'certificate', valid_cert.replace('\n','')])
self.cli_set(['pki', 'certificate', 'smoketest', 'private', 'key', valid_cert_private_key.replace('\n','')])
- def test_01_lb_reverse_proxy_domain(self):
+ def test_reverse_proxy_domain(self):
domains_bk_first = ['n1.example.com', 'n2.example.com', 'n3.example.com']
domain_bk_second = 'n5.example.com'
- frontend = 'https_front'
+ frontend = 'vyos_smoketest'
front_port = '4433'
bk_server_first = '192.0.2.11'
bk_server_second = '192.0.2.12'
- bk_first_name = 'bk-01'
- bk_second_name = 'bk-02'
+ bk_first_name = 'vyosbk-01'
+ bk_second_name = 'vyosbk-02'
bk_server_port = '9090'
mode = 'http'
rule_ten = '10'
@@ -241,9 +262,9 @@ class TestLoadBalancingReverseProxy(VyOSUnitTestSHIM.TestCase):
self.assertIn(f'server {bk_second_name} {bk_server_second}:{bk_server_port}', config)
self.assertIn(f'server {bk_second_name} {bk_server_second}:{bk_server_port} backup', config)
- def test_02_lb_reverse_proxy_cert_not_exists(self):
+ def test_reverse_proxy_cert_not_exists(self):
self.base_config()
- self.cli_set(base_path + ['service', 'https_front', 'ssl', 'certificate', 'cert'])
+ self.cli_set(base_path + ['service', haproxy_service_name, 'ssl', 'certificate', 'cert'])
with self.assertRaises(ConfigSessionError) as e:
self.cli_commit()
@@ -253,19 +274,19 @@ class TestLoadBalancingReverseProxy(VyOSUnitTestSHIM.TestCase):
self.configure_pki()
self.base_config()
- self.cli_set(base_path + ['service', 'https_front', 'ssl', 'certificate', 'cert'])
+ self.cli_set(base_path + ['service', haproxy_service_name, 'ssl', 'certificate', 'cert'])
with self.assertRaises(ConfigSessionError) as e:
self.cli_commit()
# self.assertIn('\nCertificate "cert" does not exist\n', str(e.exception))
- self.cli_delete(base_path + ['service', 'https_front', 'ssl', 'certificate', 'cert'])
- self.cli_set(base_path + ['service', 'https_front', 'ssl', 'certificate', 'smoketest'])
+ self.cli_delete(base_path + ['service', haproxy_service_name, 'ssl', 'certificate', 'cert'])
+ self.cli_set(base_path + ['service', haproxy_service_name, 'ssl', 'certificate', 'smoketest'])
self.cli_commit()
- def test_03_lb_reverse_proxy_ca_not_exists(self):
+ def test_reverse_proxy_ca_not_exists(self):
self.base_config()
- self.cli_set(base_path + ['backend', 'bk-01', 'ssl', 'ca-certificate', 'ca-test'])
+ self.cli_set(base_path + ['backend', haproxy_backend_name, 'ssl', 'ca-certificate', 'ca-test'])
with self.assertRaises(ConfigSessionError) as e:
self.cli_commit()
@@ -275,40 +296,40 @@ class TestLoadBalancingReverseProxy(VyOSUnitTestSHIM.TestCase):
self.configure_pki()
self.base_config()
- self.cli_set(base_path + ['backend', 'bk-01', 'ssl', 'ca-certificate', 'ca-test'])
+ self.cli_set(base_path + ['backend', haproxy_backend_name, 'ssl', 'ca-certificate', 'ca-test'])
with self.assertRaises(ConfigSessionError) as e:
self.cli_commit()
# self.assertIn('\nCA certificate "ca-test" does not exist\n', str(e.exception))
- self.cli_delete(base_path + ['backend', 'bk-01', 'ssl', 'ca-certificate', 'ca-test'])
- self.cli_set(base_path + ['backend', 'bk-01', 'ssl', 'ca-certificate', 'smoketest'])
+ self.cli_delete(base_path + ['backend', haproxy_backend_name, 'ssl', 'ca-certificate', 'ca-test'])
+ self.cli_set(base_path + ['backend', haproxy_backend_name, 'ssl', 'ca-certificate', 'smoketest'])
self.cli_commit()
- def test_04_lb_reverse_proxy_backend_ssl_no_verify(self):
+ def test_reverse_proxy_backend_ssl_no_verify(self):
# Setup base
self.configure_pki()
self.base_config()
# Set no-verify option
- self.cli_set(base_path + ['backend', 'bk-01', 'ssl', 'no-verify'])
+ self.cli_set(base_path + ['backend', haproxy_backend_name, 'ssl', 'no-verify'])
self.cli_commit()
# Test no-verify option
config = read_file(HAPROXY_CONF)
- self.assertIn('server bk-01 192.0.2.11:9090 send-proxy ssl verify none', config)
+ self.assertIn(f'server {haproxy_backend_name} 192.0.2.11:9090 send-proxy ssl verify none', config)
# Test setting ca-certificate alongside no-verify option fails, to test config validation
- self.cli_set(base_path + ['backend', 'bk-01', 'ssl', 'ca-certificate', 'smoketest'])
+ self.cli_set(base_path + ['backend', haproxy_backend_name, 'ssl', 'ca-certificate', 'smoketest'])
with self.assertRaises(ConfigSessionError) as e:
self.cli_commit()
- def test_05_lb_reverse_proxy_backend_http_check(self):
+ def test_reverse_proxy_backend_http_check(self):
# Setup base
self.base_config()
# Set http-check
- self.cli_set(base_path + ['backend', 'bk-01', 'http-check', 'method', 'get'])
+ self.cli_set(base_path + ['backend', haproxy_backend_name, 'http-check', 'method', 'get'])
self.cli_commit()
# Test http-check
@@ -317,8 +338,8 @@ class TestLoadBalancingReverseProxy(VyOSUnitTestSHIM.TestCase):
self.assertIn('http-check send meth GET', config)
# Set http-check with uri and status
- self.cli_set(base_path + ['backend', 'bk-01', 'http-check', 'uri', '/health'])
- self.cli_set(base_path + ['backend', 'bk-01', 'http-check', 'expect', 'status', '200'])
+ self.cli_set(base_path + ['backend', haproxy_backend_name, 'http-check', 'uri', '/health'])
+ self.cli_set(base_path + ['backend', haproxy_backend_name, 'http-check', 'expect', 'status', '200'])
self.cli_commit()
# Test http-check with uri and status
@@ -328,8 +349,8 @@ class TestLoadBalancingReverseProxy(VyOSUnitTestSHIM.TestCase):
self.assertIn('http-check expect status 200', config)
# Set http-check with string
- self.cli_delete(base_path + ['backend', 'bk-01', 'http-check', 'expect', 'status', '200'])
- self.cli_set(base_path + ['backend', 'bk-01', 'http-check', 'expect', 'string', 'success'])
+ self.cli_delete(base_path + ['backend', haproxy_backend_name, 'http-check', 'expect', 'status', '200'])
+ self.cli_set(base_path + ['backend', haproxy_backend_name, 'http-check', 'expect', 'string', 'success'])
self.cli_commit()
# Test http-check with string
@@ -339,11 +360,11 @@ class TestLoadBalancingReverseProxy(VyOSUnitTestSHIM.TestCase):
self.assertIn('http-check expect string success', config)
# Test configuring both http-check & health-check fails validation script
- self.cli_set(base_path + ['backend', 'bk-01', 'health-check', 'ldap'])
+ self.cli_set(base_path + ['backend', haproxy_backend_name, 'health-check', 'ldap'])
with self.assertRaises(ConfigSessionError) as e:
self.cli_commit()
- def test_06_lb_reverse_proxy_tcp_mode(self):
+ def test_reverse_proxy_tcp_mode(self):
frontend = 'tcp_8443'
mode = 'tcp'
front_port = '8433'
@@ -390,27 +411,27 @@ class TestLoadBalancingReverseProxy(VyOSUnitTestSHIM.TestCase):
self.assertIn(f'mode {mode}', config)
self.assertIn(f'server {bk_name} {bk_server}:{bk_server_port}', config)
- def test_07_lb_reverse_proxy_http_response_headers(self):
+ def test_reverse_proxy_http_response_headers(self):
# Setup base
self.configure_pki()
self.base_config()
# Set example headers in both frontend and backend
- self.cli_set(base_path + ['service', 'https_front', 'http-response-headers', 'Cache-Control', 'value', 'max-age=604800'])
- self.cli_set(base_path + ['backend', 'bk-01', 'http-response-headers', 'Proxy-Backend-ID', 'value', 'bk-01'])
+ self.cli_set(base_path + ['service', haproxy_service_name, 'http-response-headers', 'Cache-Control', 'value', 'max-age=604800'])
+ self.cli_set(base_path + ['backend', haproxy_backend_name, 'http-response-headers', 'Proxy-Backend-ID', 'value', haproxy_backend_name])
self.cli_commit()
# Test headers are present in generated configuration file
config = read_file(HAPROXY_CONF)
self.assertIn('http-response set-header Cache-Control \'max-age=604800\'', config)
- self.assertIn('http-response set-header Proxy-Backend-ID \'bk-01\'', config)
+ self.assertIn(f'http-response set-header Proxy-Backend-ID \'{haproxy_backend_name}\'', config)
# Test setting alongside modes other than http is blocked by validation conditions
- self.cli_set(base_path + ['service', 'https_front', 'mode', 'tcp'])
+ self.cli_set(base_path + ['service', haproxy_service_name, 'mode', 'tcp'])
with self.assertRaises(ConfigSessionError) as e:
self.cli_commit()
- def test_08_lb_reverse_proxy_tcp_health_checks(self):
+ def test_reverse_proxy_tcp_health_checks(self):
# Setup PKI
self.configure_pki()
@@ -458,7 +479,7 @@ class TestLoadBalancingReverseProxy(VyOSUnitTestSHIM.TestCase):
config = read_file(HAPROXY_CONF)
self.assertIn(f'option smtpchk', config)
- def test_09_lb_reverse_proxy_logging(self):
+ def test_reverse_proxy_logging(self):
# Setup base
self.base_config()
self.cli_commit()
@@ -477,7 +498,7 @@ class TestLoadBalancingReverseProxy(VyOSUnitTestSHIM.TestCase):
self.assertIn('log /dev/log local2 warning', config)
# Test backend logging options
- backend_path = base_path + ['backend', 'bk-01']
+ backend_path = base_path + ['backend', haproxy_backend_name]
self.cli_set(backend_path + ['logging', 'facility', 'local3', 'level', 'debug'])
self.cli_set(backend_path + ['logging', 'facility', 'local4', 'level', 'info'])
self.cli_commit()
@@ -488,7 +509,7 @@ class TestLoadBalancingReverseProxy(VyOSUnitTestSHIM.TestCase):
self.assertIn('log /dev/log local4 info', config)
# Test service logging options
- service_path = base_path + ['service', 'https_front']
+ service_path = base_path + ['service', haproxy_service_name]
self.cli_set(service_path + ['logging', 'facility', 'local5', 'level', 'notice'])
self.cli_set(service_path + ['logging', 'facility', 'local6', 'level', 'crit'])
self.cli_commit()
@@ -498,5 +519,97 @@ class TestLoadBalancingReverseProxy(VyOSUnitTestSHIM.TestCase):
self.assertIn('log /dev/log local5 notice', config)
self.assertIn('log /dev/log local6 crit', config)
+ def test_reverse_proxy_http_compression(self):
+ # Setup base
+ self.configure_pki()
+ self.base_config()
+
+ # Configure compression in frontend
+ http_comp_path = base_path + ['service', haproxy_service_name, 'http-compression']
+ self.cli_set(http_comp_path + ['algorithm', 'gzip'])
+ self.cli_set(http_comp_path + ['mime-type', 'text/html'])
+ self.cli_set(http_comp_path + ['mime-type', 'text/javascript'])
+ self.cli_set(http_comp_path + ['mime-type', 'text/plain'])
+ self.cli_commit()
+
+ # Test compression is present in generated configuration file
+ config = read_file(HAPROXY_CONF)
+ self.assertIn('filter compression', config)
+ self.assertIn('compression algo gzip', config)
+ self.assertIn('compression type text/html text/javascript text/plain', config)
+
+ # Test setting compression without specifying any mime-types fails verification
+ self.cli_delete(base_path + ['service', haproxy_service_name, 'http-compression', 'mime-type'])
+ with self.assertRaises(ConfigSessionError) as e:
+ self.cli_commit()
+
+ def test_reverse_proxy_timeout(self):
+ t_default_check = '5'
+ t_default_client = '50'
+ t_default_connect = '10'
+ t_default_server ='50'
+ t_check = '4'
+ t_client = '300'
+ t_connect = '12'
+ t_server ='120'
+ t_front_client = '600'
+
+ self.base_config()
+ self.cli_commit()
+ # Check default timeout options
+ config_entries = (
+ f'timeout check {t_default_check}s',
+ f'timeout connect {t_default_connect}s',
+ f'timeout client {t_default_client}s',
+ f'timeout server {t_default_server}s',
+ )
+ # Check default timeout options
+ config = read_file(HAPROXY_CONF)
+ for config_entry in config_entries:
+ self.assertIn(config_entry, config)
+
+ # Set custom timeout options
+ self.cli_set(base_path + ['timeout', 'check', t_check])
+ self.cli_set(base_path + ['timeout', 'client', t_client])
+ self.cli_set(base_path + ['timeout', 'connect', t_connect])
+ self.cli_set(base_path + ['timeout', 'server', t_server])
+ self.cli_set(base_path + ['service', haproxy_service_name, 'timeout', 'client', t_front_client])
+
+ self.cli_commit()
+
+ # Check custom timeout options
+ config_entries = (
+ f'timeout check {t_check}s',
+ f'timeout connect {t_connect}s',
+ f'timeout client {t_client}s',
+ f'timeout server {t_server}s',
+ f'timeout client {t_front_client}s',
+ )
+
+ # Check configured options
+ config = read_file(HAPROXY_CONF)
+ for config_entry in config_entries:
+ self.assertIn(config_entry, config)
+
+ def test_reverse_proxy_http_redirect(self):
+ self.base_config()
+ self.cli_set(base_path + ['service', haproxy_service_name, 'redirect-http-to-https'])
+
+ self.cli_commit()
+
+ config = parse_haproxy_config()
+ frontend_name = f'frontend {haproxy_service_name}-http'
+ self.assertIn(frontend_name, config.keys())
+ self.assertIn('mode http', config[frontend_name])
+ self.assertIn('bind [::]:80 v4v6', config[frontend_name])
+ self.assertIn('acl acme_acl path_beg /.well-known/acme-challenge/', config[frontend_name])
+ self.assertIn('use_backend buildin_acme_certbot if acme_acl', config[frontend_name])
+ self.assertIn('redirect scheme https code 301 if !acme_acl', config[frontend_name])
+
+ backend_name = 'backend buildin_acme_certbot'
+ self.assertIn(backend_name, config.keys())
+ port = get_default_port('certbot_haproxy')
+ self.assertIn(f'server localhost 127.0.0.1:{port}', config[backend_name])
+
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_load-balancing_wan.py b/smoketest/scripts/cli/test_load-balancing_wan.py
index 92b4000b8..32e5f6915 100755
--- a/smoketest/scripts/cli/test_load-balancing_wan.py
+++ b/smoketest/scripts/cli/test_load-balancing_wan.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2022-2024 VyOS maintainers and contributors
+# Copyright (C) 2022-2025 VyOS maintainers and contributors
#
# 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
@@ -14,10 +14,13 @@
# 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 time
from base_vyostest_shim import VyOSUnitTestSHIM
+from vyos.utils.file import chmod_755
+from vyos.utils.file import write_file
from vyos.utils.process import call
from vyos.utils.process import cmd
@@ -54,6 +57,16 @@ class TestLoadBalancingWan(VyOSUnitTestSHIM.TestCase):
self.cli_delete(base_path)
self.cli_commit()
+ removed_chains = [
+ 'wlb_mangle_isp_veth1',
+ 'wlb_mangle_isp_veth2',
+ 'wlb_mangle_isp_eth201',
+ 'wlb_mangle_isp_eth202'
+ ]
+
+ for chain in removed_chains:
+ self.verify_nftables_chain_exists('ip vyos_wanloadbalance', chain, inverse=True)
+
def test_table_routes(self):
ns1 = 'ns201'
ns2 = 'ns202'
@@ -93,6 +106,7 @@ class TestLoadBalancingWan(VyOSUnitTestSHIM.TestCase):
cmd_in_netns(ns3, 'ip link set dev eth0 up')
# Set load-balancing configuration
+ self.cli_set(base_path + ['wan', 'hook', '/bin/true'])
self.cli_set(base_path + ['wan', 'interface-health', iface1, 'failure-count', '2'])
self.cli_set(base_path + ['wan', 'interface-health', iface1, 'nexthop', '203.0.113.1'])
self.cli_set(base_path + ['wan', 'interface-health', iface1, 'success-count', '1'])
@@ -102,7 +116,8 @@ class TestLoadBalancingWan(VyOSUnitTestSHIM.TestCase):
self.cli_set(base_path + ['wan', 'rule', '10', 'inbound-interface', iface3])
self.cli_set(base_path + ['wan', 'rule', '10', 'source', 'address', '198.51.100.0/24'])
-
+ self.cli_set(base_path + ['wan', 'rule', '10', 'interface', iface1])
+ self.cli_set(base_path + ['wan', 'rule', '10', 'interface', iface2])
# commit changes
self.cli_commit()
@@ -127,7 +142,6 @@ class TestLoadBalancingWan(VyOSUnitTestSHIM.TestCase):
delete_netns(ns3)
def test_check_chains(self):
-
ns1 = 'nsA'
ns2 = 'nsB'
ns3 = 'nsC'
@@ -137,43 +151,28 @@ class TestLoadBalancingWan(VyOSUnitTestSHIM.TestCase):
container_iface1 = 'ceth0'
container_iface2 = 'ceth1'
container_iface3 = 'ceth2'
- mangle_isp1 = """table ip mangle {
- chain ISP_veth1 {
- counter ct mark set 0xc9
- counter meta mark set 0xc9
- counter accept
+ mangle_isp1 = """table ip vyos_wanloadbalance {
+ chain wlb_mangle_isp_veth1 {
+ meta mark set 0x000000c9 ct mark set 0x000000c9 counter accept
}
}"""
- mangle_isp2 = """table ip mangle {
- chain ISP_veth2 {
- counter ct mark set 0xca
- counter meta mark set 0xca
- counter accept
+ mangle_isp2 = """table ip vyos_wanloadbalance {
+ chain wlb_mangle_isp_veth2 {
+ meta mark set 0x000000ca ct mark set 0x000000ca counter accept
}
}"""
- mangle_prerouting = """table ip mangle {
- chain PREROUTING {
+ mangle_prerouting = """table ip vyos_wanloadbalance {
+ chain wlb_mangle_prerouting {
type filter hook prerouting priority mangle; policy accept;
- counter jump WANLOADBALANCE_PRE
- }
-}"""
- mangle_wanloadbalance_pre = """table ip mangle {
- chain WANLOADBALANCE_PRE {
- iifname "veth3" ip saddr 198.51.100.0/24 ct state new meta random & 2147483647 < 1073741824 counter jump ISP_veth1
- iifname "veth3" ip saddr 198.51.100.0/24 ct state new counter jump ISP_veth2
+ iifname "veth3" ip saddr 198.51.100.0/24 ct state new limit rate 5/second burst 5 packets counter numgen random mod 11 vmap { 0 : jump wlb_mangle_isp_veth1, 1-10 : jump wlb_mangle_isp_veth2 }
iifname "veth3" ip saddr 198.51.100.0/24 counter meta mark set ct mark
}
}"""
- nat_wanloadbalance = """table ip nat {
- chain WANLOADBALANCE {
- ct mark 0xc9 counter snat to 203.0.113.10
- ct mark 0xca counter snat to 192.0.2.10
- }
-}"""
- nat_vyos_pre_snat_hook = """table ip nat {
- chain VYOS_PRE_SNAT_HOOK {
+ nat_wanloadbalance = """table ip vyos_wanloadbalance {
+ chain wlb_nat_postrouting {
type nat hook postrouting priority srcnat - 1; policy accept;
- counter jump WANLOADBALANCE
+ ct mark 0x000000c9 counter snat to 203.0.113.10
+ ct mark 0x000000ca counter snat to 192.0.2.10
}
}"""
@@ -214,7 +213,7 @@ class TestLoadBalancingWan(VyOSUnitTestSHIM.TestCase):
self.cli_set(base_path + ['wan', 'rule', '10', 'inbound-interface', iface3])
self.cli_set(base_path + ['wan', 'rule', '10', 'source', 'address', '198.51.100.0/24'])
self.cli_set(base_path + ['wan', 'rule', '10', 'interface', iface1])
- self.cli_set(base_path + ['wan', 'rule', '10', 'interface', iface2])
+ self.cli_set(base_path + ['wan', 'rule', '10', 'interface', iface2, 'weight', '10'])
# commit changes
self.cli_commit()
@@ -222,25 +221,19 @@ class TestLoadBalancingWan(VyOSUnitTestSHIM.TestCase):
time.sleep(5)
# Check mangle chains
- tmp = cmd(f'sudo nft -s list chain mangle ISP_{iface1}')
+ tmp = cmd(f'sudo nft -s list chain ip vyos_wanloadbalance wlb_mangle_isp_{iface1}')
self.assertEqual(tmp, mangle_isp1)
- tmp = cmd(f'sudo nft -s list chain mangle ISP_{iface2}')
+ tmp = cmd(f'sudo nft -s list chain ip vyos_wanloadbalance wlb_mangle_isp_{iface2}')
self.assertEqual(tmp, mangle_isp2)
- tmp = cmd(f'sudo nft -s list chain mangle PREROUTING')
+ tmp = cmd('sudo nft -s list chain ip vyos_wanloadbalance wlb_mangle_prerouting')
self.assertEqual(tmp, mangle_prerouting)
- tmp = cmd(f'sudo nft -s list chain mangle WANLOADBALANCE_PRE')
- self.assertEqual(tmp, mangle_wanloadbalance_pre)
-
# Check nat chains
- tmp = cmd(f'sudo nft -s list chain nat WANLOADBALANCE')
+ tmp = cmd('sudo nft -s list chain ip vyos_wanloadbalance wlb_nat_postrouting')
self.assertEqual(tmp, nat_wanloadbalance)
- tmp = cmd(f'sudo nft -s list chain nat VYOS_PRE_SNAT_HOOK')
- self.assertEqual(tmp, nat_vyos_pre_snat_hook)
-
# Delete veth interfaces and netns
for iface in [iface1, iface2, iface3]:
call(f'sudo ip link del dev {iface}')
@@ -249,6 +242,85 @@ class TestLoadBalancingWan(VyOSUnitTestSHIM.TestCase):
delete_netns(ns2)
delete_netns(ns3)
+ def test_criteria_failover_hook(self):
+ isp1_iface = 'eth0'
+ isp2_iface = 'eth1'
+ lan_iface = 'eth2'
+
+ hook_path = '/tmp/wlb_hook.sh'
+ hook_output_path = '/tmp/wlb_hook_output'
+ hook_script = f"""
+#!/bin/sh
+
+ifname=$WLB_INTERFACE_NAME
+state=$WLB_INTERFACE_STATE
+
+echo "$ifname - $state" > {hook_output_path}
+"""
+
+ write_file(hook_path, hook_script)
+ chmod_755(hook_path)
+
+ self.cli_set(['interfaces', 'ethernet', isp1_iface, 'address', '203.0.113.2/30'])
+ self.cli_set(['interfaces', 'ethernet', isp2_iface, 'address', '192.0.2.2/30'])
+ self.cli_set(['interfaces', 'ethernet', lan_iface, 'address', '198.51.100.2/30'])
+
+ self.cli_set(base_path + ['wan', 'hook', hook_path])
+ self.cli_set(base_path + ['wan', 'interface-health', isp1_iface, 'failure-count', '1'])
+ self.cli_set(base_path + ['wan', 'interface-health', isp1_iface, 'nexthop', '203.0.113.2'])
+ self.cli_set(base_path + ['wan', 'interface-health', isp1_iface, 'success-count', '1'])
+ self.cli_set(base_path + ['wan', 'interface-health', isp2_iface, 'failure-count', '1'])
+ self.cli_set(base_path + ['wan', 'interface-health', isp2_iface, 'nexthop', '192.0.2.2'])
+ self.cli_set(base_path + ['wan', 'interface-health', isp2_iface, 'success-count', '1'])
+ self.cli_set(base_path + ['wan', 'rule', '5', 'exclude'])
+ self.cli_set(base_path + ['wan', 'rule', '5', 'inbound-interface', 'eth*'])
+ self.cli_set(base_path + ['wan', 'rule', '5', 'destination', 'address', '10.0.0.0/8'])
+ self.cli_set(base_path + ['wan', 'rule', '10', 'failover'])
+ self.cli_set(base_path + ['wan', 'rule', '10', 'inbound-interface', lan_iface])
+ self.cli_set(base_path + ['wan', 'rule', '10', 'protocol', 'udp'])
+ self.cli_set(base_path + ['wan', 'rule', '10', 'source', 'address', '198.51.100.0/24'])
+ self.cli_set(base_path + ['wan', 'rule', '10', 'source', 'port', '53'])
+ self.cli_set(base_path + ['wan', 'rule', '10', 'destination', 'address', '192.0.2.0/24'])
+ self.cli_set(base_path + ['wan', 'rule', '10', 'destination', 'port', '53'])
+ self.cli_set(base_path + ['wan', 'rule', '10', 'interface', isp1_iface])
+ self.cli_set(base_path + ['wan', 'rule', '10', 'interface', isp1_iface, 'weight', '10'])
+ self.cli_set(base_path + ['wan', 'rule', '10', 'interface', isp2_iface])
+
+ # commit changes
+ self.cli_commit()
+
+ time.sleep(5)
+
+ # Verify isp1 + criteria
+
+ nftables_search = [
+ [f'iifname "eth*"', 'ip daddr 10.0.0.0/8', 'return'],
+ [f'iifname "{lan_iface}"', 'ip saddr 198.51.100.0/24', 'udp sport 53', 'ip daddr 192.0.2.0/24', 'udp dport 53', f'jump wlb_mangle_isp_{isp1_iface}']
+ ]
+
+ self.verify_nftables_chain(nftables_search, 'ip vyos_wanloadbalance', 'wlb_mangle_prerouting')
+
+ # Trigger failure on isp1 health check
+
+ self.cli_delete(['interfaces', 'ethernet', isp1_iface, 'address', '203.0.113.2/30'])
+ self.cli_commit()
+
+ time.sleep(10)
+
+ # Verify failover to isp2
+
+ nftables_search = [
+ [f'iifname "{lan_iface}"', f'jump wlb_mangle_isp_{isp2_iface}']
+ ]
+
+ self.verify_nftables_chain(nftables_search, 'ip vyos_wanloadbalance', 'wlb_mangle_prerouting')
+
+ # Verify hook output
+
+ self.assertTrue(os.path.exists(hook_output_path))
+
+ with open(hook_output_path, 'r') as f:
+ self.assertIn('eth0 - FAILED', f.read())
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_nat.py b/smoketest/scripts/cli/test_nat.py
index 0beafcc6c..b33ef2617 100755
--- a/smoketest/scripts/cli/test_nat.py
+++ b/smoketest/scripts/cli/test_nat.py
@@ -84,7 +84,7 @@ class TestNAT(VyOSUnitTestSHIM.TestCase):
address_group = 'smoketest_addr'
address_group_member = '192.0.2.1'
interface_group = 'smoketest_ifaces'
- interface_group_member = 'bond.99'
+ interface_group_member = 'eth0'
self.cli_set(['firewall', 'group', 'address-group', address_group, 'address', address_group_member])
self.cli_set(['firewall', 'group', 'interface-group', interface_group, 'interface', interface_group_member])
diff --git a/smoketest/scripts/cli/test_nat66.py b/smoketest/scripts/cli/test_nat66.py
index 52ad8e3ef..d4b5d6aa4 100755
--- a/smoketest/scripts/cli/test_nat66.py
+++ b/smoketest/scripts/cli/test_nat66.py
@@ -227,6 +227,35 @@ class TestNAT66(VyOSUnitTestSHIM.TestCase):
self.verify_nftables(nftables_search, 'ip6 vyos_nat')
+ def test_source_nat66_network_group(self):
+ address_group = 'smoketest_addr'
+ address_group_member = 'fc00::1'
+ network_group = 'smoketest_net'
+ network_group_member = 'fc00::/64'
+ translation_prefix = 'fc01::/64'
+
+ self.cli_set(['firewall', 'group', 'ipv6-address-group', address_group, 'address', address_group_member])
+ self.cli_set(['firewall', 'group', 'ipv6-network-group', network_group, 'network', network_group_member])
+
+ self.cli_set(src_path + ['rule', '1', 'destination', 'group', 'address-group', address_group])
+ self.cli_set(src_path + ['rule', '1', 'translation', 'address', translation_prefix])
+
+ self.cli_set(src_path + ['rule', '2', 'destination', 'group', 'network-group', network_group])
+ self.cli_set(src_path + ['rule', '2', 'translation', 'address', translation_prefix])
+
+ self.cli_commit()
+
+ nftables_search = [
+ [f'set A6_{address_group}'],
+ [f'elements = {{ {address_group_member} }}'],
+ [f'set N6_{network_group}'],
+ [f'elements = {{ {network_group_member} }}'],
+ ['ip6 daddr', f'@A6_{address_group}', 'snat prefix to fc01::/64'],
+ ['ip6 daddr', f'@N6_{network_group}', 'snat prefix to fc01::/64']
+ ]
+
+ self.verify_nftables(nftables_search, 'ip6 vyos_nat')
+
def test_nat66_no_rules(self):
# T3206: deleting all rules but keep the direction 'destination' or
# 'source' resulteds in KeyError: 'rule'.
diff --git a/smoketest/scripts/cli/test_policy.py b/smoketest/scripts/cli/test_policy.py
index 7ea1b610e..985097726 100755
--- a/smoketest/scripts/cli/test_policy.py
+++ b/smoketest/scripts/cli/test_policy.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2021-2023 VyOS maintainers and contributors
+# Copyright (C) 2021-2025 VyOS maintainers and contributors
#
# 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
@@ -17,6 +17,7 @@
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
@@ -24,6 +25,17 @@ 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()
@@ -1137,6 +1149,16 @@ class TestPolicy(VyOSUnitTestSHIM.TestCase):
},
},
},
+ 'vrf-match': {
+ 'rule': {
+ '10': {
+ 'action': 'permit',
+ 'match': {
+ 'source-vrf': 'TEST',
+ },
+ },
+ },
+ },
}
self.cli_set(['policy', 'access-list', access_list, 'rule', '10', 'action', 'permit'])
@@ -1248,6 +1270,8 @@ class TestPolicy(VyOSUnitTestSHIM.TestCase):
self.cli_set(path + ['rule', rule, 'match', 'rpki', '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']])
@@ -1426,6 +1450,9 @@ class TestPolicy(VyOSUnitTestSHIM.TestCase):
if 'rpki-valid' in rule_config['match']:
tmp = f'match rpki 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)
diff --git a/smoketest/scripts/cli/test_policy_local-route.py b/smoketest/scripts/cli/test_policy_local-route.py
index 8d6ba40dc..a4239b8a1 100644
--- a/smoketest/scripts/cli/test_policy_local-route.py
+++ b/smoketest/scripts/cli/test_policy_local-route.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2024 VyOS maintainers and contributors
+# Copyright (C) 2024-2025 VyOS maintainers and contributors
#
# 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
@@ -17,6 +17,7 @@
import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
+from base_vyostest_shim import CSTORE_GUARD_TIME
interface = 'eth0'
mark = '100'
@@ -32,6 +33,8 @@ class TestPolicyLocalRoute(VyOSUnitTestSHIM.TestCase):
# 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])
diff --git a/smoketest/scripts/cli/test_policy_route.py b/smoketest/scripts/cli/test_policy_route.py
index 672865eb0..15ddd857e 100755
--- a/smoketest/scripts/cli/test_policy_route.py
+++ b/smoketest/scripts/cli/test_policy_route.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2021-2023 VyOS maintainers and contributors
+# Copyright (C) 2021-2025 VyOS maintainers and contributors
#
# 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
@@ -17,6 +17,7 @@
import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
+from base_vyostest_shim import CSTORE_GUARD_TIME
mark = '100'
conn_mark = '555'
@@ -36,6 +37,8 @@ class TestPolicyRoute(VyOSUnitTestSHIM.TestCase):
# 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])
@@ -304,5 +307,39 @@ class TestPolicyRoute(VyOSUnitTestSHIM.TestCase):
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)
diff --git a/smoketest/scripts/cli/test_protocols_babel.py b/smoketest/scripts/cli/test_protocols_babel.py
index 12f6ecf38..3a9ee2d62 100755
--- a/smoketest/scripts/cli/test_protocols_babel.py
+++ b/smoketest/scripts/cli/test_protocols_babel.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2024 VyOS maintainers and contributors
+# Copyright (C) 2024-2025 VyOS maintainers and contributors
#
# 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
@@ -17,6 +17,7 @@
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
@@ -38,6 +39,8 @@ class TestProtocolsBABEL(VyOSUnitTestSHIM.TestCase):
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
@@ -62,14 +65,14 @@ class TestProtocolsBABEL(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
- frrconfig = self.getFRRconfig('router babel', endsection='^exit', daemon=babel_daemon)
+ 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', 'ospf', 'rip', 'static']
+ 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'])
@@ -81,7 +84,7 @@ class TestProtocolsBABEL(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
- frrconfig = self.getFRRconfig('router babel', endsection='^exit', daemon=babel_daemon, empty_retry=5)
+ 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:
@@ -150,7 +153,7 @@ class TestProtocolsBABEL(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
- frrconfig = self.getFRRconfig('router babel', endsection='^exit', daemon=babel_daemon)
+ 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)
@@ -198,11 +201,11 @@ class TestProtocolsBABEL(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
- frrconfig = self.getFRRconfig('router babel', endsection='^exit', daemon=babel_daemon)
+ 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', daemon=babel_daemon)
+ 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)
diff --git a/smoketest/scripts/cli/test_protocols_bfd.py b/smoketest/scripts/cli/test_protocols_bfd.py
index 32e39e8f7..2205cd9de 100755
--- a/smoketest/scripts/cli/test_protocols_bfd.py
+++ b/smoketest/scripts/cli/test_protocols_bfd.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2021-2023 VyOS maintainers and contributors
+# Copyright (C) 2021-2024 VyOS maintainers and contributors
#
# 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
@@ -17,12 +17,13 @@
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']
-frr_endsection = '^ exit'
dum_if = 'dum1001'
vrf_name = 'red'
@@ -87,6 +88,9 @@ class TestProtocolsBFD(VyOSUnitTestSHIM.TestCase):
# 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)
@@ -131,7 +135,7 @@ class TestProtocolsBFD(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify FRR bgpd configuration
- frrconfig = self.getFRRconfig('bfd', endsection='^exit', daemon=bfd_daemon)
+ frrconfig = self.getFRRconfig('bfd', endsection='^exit')
for peer, peer_config in peers.items():
tmp = f'peer {peer}'
if 'multihop' in peer_config:
@@ -144,8 +148,8 @@ class TestProtocolsBFD(VyOSUnitTestSHIM.TestCase):
tmp += f' vrf {peer_config["vrf"]}'
self.assertIn(tmp, frrconfig)
- peerconfig = self.getFRRconfig(f' peer {peer}', end='', endsection=frr_endsection,
- daemon=bfd_daemon)
+ 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:
@@ -207,7 +211,8 @@ class TestProtocolsBFD(VyOSUnitTestSHIM.TestCase):
# Verify FRR bgpd configuration
for profile, profile_config in profiles.items():
- config = self.getFRRconfig(f' profile {profile}', endsection=frr_endsection)
+ 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:
@@ -229,8 +234,8 @@ class TestProtocolsBFD(VyOSUnitTestSHIM.TestCase):
self.assertNotIn(f'shutdown', config)
for peer, peer_config in peers.items():
- peerconfig = self.getFRRconfig(f' peer {peer}', end='',
- endsection=frr_endsection, daemon=bfd_daemon)
+ 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)
diff --git a/smoketest/scripts/cli/test_protocols_bgp.py b/smoketest/scripts/cli/test_protocols_bgp.py
index cdf18a051..8403dcc37 100755
--- a/smoketest/scripts/cli/test_protocols_bgp.py
+++ b/smoketest/scripts/cli/test_protocols_bgp.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2021-2024 VyOS maintainers and contributors
+# Copyright (C) 2021-2025 VyOS maintainers and contributors
#
# 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
@@ -19,6 +19,7 @@ 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
@@ -200,6 +201,9 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
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'])
@@ -217,7 +221,7 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
self.cli_delete(base_path)
self.cli_commit()
- frrconfig = self.getFRRconfig('router bgp', endsection='^exit', daemon=bgp_daemon)
+ frrconfig = self.getFRRconfig('router bgp', endsection='^exit')
self.assertNotIn(f'router bgp', frrconfig)
# check process health and continuity
@@ -372,7 +376,7 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify FRR bgpd configuration
- frrconfig = self.getFRRconfig(f'router bgp {ASN}', daemon=bgp_daemon)
+ 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)
@@ -398,18 +402,21 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
self.assertNotIn(f'bgp ebgp-requires-policy', frrconfig)
self.assertIn(f' no bgp suppress-duplicates', frrconfig)
- afiv4_config = self.getFRRconfig(' address-family ipv4 unicast',
- endsection='^ exit-address-family', daemon=bgp_daemon)
+ 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(' address-family ipv4 labeled-unicast',
- endsection='^ exit-address-family', daemon=bgp_daemon)
+ 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(' address-family ipv6 unicast',
- endsection='^ exit-address-family', daemon=bgp_daemon)
+ 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)
@@ -516,7 +523,7 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify FRR bgpd configuration
- frrconfig = self.getFRRconfig(f'router bgp {ASN}', endsection='^exit', daemon=bgp_daemon)
+ frrconfig = self.getFRRconfig(f'router bgp {ASN}', endsection='^exit')
self.assertIn(f'router bgp {ASN}', frrconfig)
for peer, peer_config in neighbor_config.items():
@@ -621,7 +628,7 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify FRR bgpd configuration
- frrconfig = self.getFRRconfig(f'router bgp {ASN}', endsection='^exit', daemon=bgp_daemon)
+ 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():
@@ -648,10 +655,71 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
}
# We want to redistribute ...
- redistributes = ['connected', 'isis', 'kernel', 'ospf', 'rip', 'static']
- for redistribute in redistributes:
- self.cli_set(base_path + ['address-family', 'ipv4-unicast',
- 'redistribute', 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['metric'])
+ if 'route_map' in table_config:
+ self.cli_set(['policy', 'route-map', table_config['route_map'], 'rule', '10', 'action'], value='permit')
+ self.cli_set(proto_path + [table, 'route-map'], value=table_config['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',
@@ -670,12 +738,31 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify FRR bgpd configuration
- frrconfig = self.getFRRconfig(f'router bgp {ASN}', endsection='^exit', daemon=bgp_daemon)
+ frrconfig = self.getFRRconfig(f'router bgp {ASN}', endsection='^exit')
self.assertIn(f'router bgp {ASN}', frrconfig)
- self.assertIn(f' address-family ipv4 unicast', frrconfig)
-
- for redistribute in redistributes:
- self.assertIn(f' redistribute {redistribute}', 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)
@@ -688,6 +775,10 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
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' : {
@@ -700,10 +791,67 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
}
# We want to redistribute ...
- redistributes = ['connected', 'kernel', 'ospfv3', 'ripng', 'static']
- for redistribute in redistributes:
- self.cli_set(base_path + ['address-family', 'ipv6-unicast',
- 'redistribute', 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['metric'])
+ if 'route_map' in table_config:
+ self.cli_set(['policy', 'route-map', table_config['route_map'], 'rule', '10', 'action'], value='permit')
+ self.cli_set(proto_path + [table, 'route-map'], value=table_config['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',
@@ -716,24 +864,47 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify FRR bgpd configuration
- frrconfig = self.getFRRconfig(f'router bgp {ASN}', endsection='^exit', daemon=bgp_daemon)
+ frrconfig = self.getFRRconfig(f'router bgp {ASN}', endsection='^exit')
self.assertIn(f'router bgp {ASN}', frrconfig)
- self.assertIn(f' address-family ipv6 unicast', 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(f' no bgp ebgp-requires-policy', frrconfig)
-
- for redistribute in redistributes:
- # FRR calls this OSPF6
- if redistribute == 'ospfv3':
- redistribute = 'ospf6'
- self.assertIn(f' redistribute {redistribute}', frrconfig)
+ 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'
@@ -758,7 +929,7 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify FRR bgpd configuration
- frrconfig = self.getFRRconfig(f'router bgp {ASN}', endsection='^exit', daemon=bgp_daemon)
+ 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)
@@ -793,7 +964,7 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify FRR bgpd configuration
- frrconfig = self.getFRRconfig(f'router bgp {ASN}', endsection='^exit', daemon=bgp_daemon)
+ 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)
@@ -806,7 +977,7 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
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')
+ 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)
@@ -849,7 +1020,7 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify FRR distances configuration
- frrconfig = self.getFRRconfig(f'router bgp {ASN}')
+ 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)
@@ -887,7 +1058,7 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify FRR bgpd configuration
- frrconfig = self.getFRRconfig(f'router bgp {ASN}')
+ frrconfig = self.getFRRconfig(f'router bgp {ASN}', endsection='^exit')
self.assertIn(f'router bgp {ASN}', frrconfig)
self.assertIn(f' address-family ipv6 unicast', frrconfig)
@@ -895,7 +1066,7 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
self.assertIn(f' import vrf {vrf}', frrconfig)
# Verify FRR bgpd configuration
- frr_vrf_config = self.getFRRconfig(f'router bgp {ASN} vrf {vrf}')
+ 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)
@@ -913,7 +1084,7 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify FRR bgpd configuration
- frrconfig = self.getFRRconfig(f'router bgp {ASN}')
+ 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)
@@ -930,7 +1101,7 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify FRR bgpd configuration
- frrconfig = self.getFRRconfig(f'router bgp {ASN}')
+ 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)
@@ -962,11 +1133,13 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify FRR bgpd configuration
- frrconfig = self.getFRRconfig(f'router bgp {ASN}')
+ 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' address-family {afi} unicast', endsection='exit-address-family', daemon=bgp_daemon)
+ 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)
@@ -1011,7 +1184,7 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
- frrconfig = self.getFRRconfig(f'router bgp {ASN}')
+ 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)
@@ -1036,7 +1209,7 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
- frrconfig = self.getFRRconfig(f'router bgp {ASN}')
+ 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)
@@ -1061,8 +1234,8 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
base_path + ['address-family', import_afi, 'import', 'vrf',
import_vrf])
self.cli_commit()
- frrconfig = self.getFRRconfig(f'router bgp {ASN}')
- frrconfig_vrf = self.getFRRconfig(f'router bgp {ASN} vrf {import_vrf}')
+ 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)
@@ -1084,8 +1257,8 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
base_path + ['address-family', import_afi, 'import', 'vrf',
import_vrf])
self.cli_commit()
- frrconfig = self.getFRRconfig(f'router bgp {ASN}')
- frrconfig_vrf = self.getFRRconfig(f'router bgp {ASN} vrf {import_vrf}')
+ 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)
@@ -1098,8 +1271,8 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
# 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}')
- frrconfig_vrf = self.getFRRconfig(f'router bgp {ASN} vrf {import_vrf}')
+ 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)
@@ -1115,8 +1288,8 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
'vpn', 'export',
import_rd])
self.cli_commit()
- frrconfig = self.getFRRconfig(f'router bgp {ASN}')
- frrconfig_vrf = self.getFRRconfig(f'router bgp {ASN} vrf {import_vrf}')
+ 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)
@@ -1145,7 +1318,7 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
for interface in interfaces:
- frrconfig = self.getFRRconfig(f'interface {interface}')
+ frrconfig = self.getFRRconfig(f'interface {interface}', endsection='^exit')
self.assertIn(f'interface {interface}', frrconfig)
self.assertIn(f' mpls bgp forwarding', frrconfig)
@@ -1159,7 +1332,7 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
for interface in interfaces:
- frrconfig = self.getFRRconfig(f'interface {interface}')
+ 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'])
@@ -1179,7 +1352,7 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
self.cli_delete(base_path + ['address-family', 'ipv4-unicast', 'sid'])
self.cli_commit()
- frrconfig = self.getFRRconfig(f'router bgp {ASN}')
+ 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)
@@ -1194,17 +1367,22 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
- frrconfig = self.getFRRconfig(f'router bgp {ASN}')
+ 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(' address-family ipv4 unicast')
+ 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(' address-family ipv6 unicast')
+
+ 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}', afiv4_config)
+ self.assertIn(f' nexthop vpn export {nexthop_ipv6}', afiv6_config)
def test_bgp_25_ipv4_labeled_unicast_peer_group(self):
pg_ipv4 = 'foo4'
@@ -1218,14 +1396,16 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
- frrconfig = self.getFRRconfig(f'router bgp {ASN}')
+ 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(' address-family ipv4 labeled-unicast')
+ 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)
@@ -1242,14 +1422,16 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
- frrconfig = self.getFRRconfig(f'router bgp {ASN}')
+ 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(' address-family ipv6 labeled-unicast')
+ 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)
@@ -1261,7 +1443,8 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
self.cli_set(base_path + ['peer-group', 'peer1', 'remote-as', 'internal'])
self.cli_commit()
- conf = self.getFRRconfig(' address-family l2vpn evpn')
+ conf = self.getFRRconfig(f'router bgp {ASN}', endsection='^exit',
+ substring=' address-family l2vpn evpn', endsubsection='^ exit-address-family')
self.assertIn('neighbor peer1 route-reflector-client', conf)
@@ -1300,7 +1483,7 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
self.cli_set(base_path + ['neighbor', int_neighbors[1], 'remote-as', ASN])
self.cli_commit()
- conf = self.getFRRconfig(f'router bgp {ASN}')
+ conf = self.getFRRconfig(f'router bgp {ASN}', endsection='^exit')
_common_config_check(conf)
# test add internal remote-as to external group
@@ -1315,7 +1498,7 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
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}')
+ 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)
@@ -1327,7 +1510,7 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
self.cli_set(base_path + ['neighbor', ext_neighbors[1], 'remote-as', 'external'])
self.cli_commit()
- conf = self.getFRRconfig(f'router bgp {ASN}')
+ 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)
@@ -1352,11 +1535,47 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
- conf = self.getFRRconfig(f'router bgp {ASN}')
+ 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'
@@ -1405,7 +1624,7 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify bgpd bmp configuration
- frrconfig = self.getFRRconfig(f'router bgp {ASN}')
+ 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)
diff --git a/smoketest/scripts/cli/test_protocols_isis.py b/smoketest/scripts/cli/test_protocols_isis.py
index bde32a1ca..14e833fd9 100755
--- a/smoketest/scripts/cli/test_protocols_isis.py
+++ b/smoketest/scripts/cli/test_protocols_isis.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2021-2024 VyOS maintainers and contributors
+# Copyright (C) 2021-2025 VyOS maintainers and contributors
#
# 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
@@ -17,13 +17,14 @@
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'
@@ -39,6 +40,8 @@ class TestProtocolsISIS(VyOSUnitTestSHIM.TestCase):
# 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
@@ -51,17 +54,12 @@ class TestProtocolsISIS(VyOSUnitTestSHIM.TestCase):
# check process health and continuity
self.assertEqual(self.daemon_pid, process_named_running(isis_daemon))
- def isis_base_config(self):
- self.cli_set(base_path + ['net', net])
- for interface in self._interfaces:
- self.cli_set(base_path + ['interface', interface])
-
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'])
@@ -73,14 +71,18 @@ class TestProtocolsISIS(VyOSUnitTestSHIM.TestCase):
with self.assertRaises(ConfigSessionError):
self.cli_commit()
- self.isis_base_config()
+ 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()
- self.cli_set(base_path + ['redistribute', 'ipv4', 'connected', 'level-2', 'route-map', route_map])
+ 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'])
@@ -88,14 +90,15 @@ class TestProtocolsISIS(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify all changes
- tmp = self.getFRRconfig(f'router isis {domain}', daemon=isis_daemon)
+ 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)
- self.assertIn(f' redistribute ipv4 connected level-2 route-map {route_map}', 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}', daemon=isis_daemon)
+ tmp = self.getFRRconfig(f'interface {interface}', endsection='^exit')
self.assertIn(f' ip router isis {domain}', tmp)
self.assertIn(f' ipv6 router isis {domain}', tmp)
@@ -124,11 +127,11 @@ class TestProtocolsISIS(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify FRR isisd configuration
- tmp = self.getFRRconfig(f'router isis {domain}', daemon=isis_daemon)
+ 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}', daemon=isis_daemon)
+ 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)
@@ -141,7 +144,10 @@ class TestProtocolsISIS(VyOSUnitTestSHIM.TestCase):
metric = '50'
route_map = 'default-foo-'
- self.isis_base_config()
+ 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'])
@@ -152,7 +158,7 @@ class TestProtocolsISIS(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify all changes
- tmp = self.getFRRconfig(f'router isis {domain}', daemon=isis_daemon)
+ tmp = self.getFRRconfig(f'router isis {domain}', endsection='^exit')
self.assertIn(f' net {net}', tmp)
for afi in ['ipv4', 'ipv6']:
@@ -160,11 +166,10 @@ class TestProtocolsISIS(VyOSUnitTestSHIM.TestCase):
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.isis_base_config()
+ 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}'])
@@ -187,13 +192,13 @@ class TestProtocolsISIS(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify all changes
- tmp = self.getFRRconfig(f'router isis {domain}', daemon=isis_daemon)
+ 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}', daemon=isis_daemon)
+ 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):
@@ -235,12 +240,12 @@ class TestProtocolsISIS(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify all changes
- tmp = self.getFRRconfig(f'router isis {domain}', daemon=isis_daemon)
+ 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}', daemon=isis_daemon)
+ 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)
@@ -252,7 +257,6 @@ class TestProtocolsISIS(VyOSUnitTestSHIM.TestCase):
global_block_high = "399"
local_block_low = "400"
local_block_high = "499"
- interface = 'lo'
maximum_stack_size = '5'
prefix_one = '192.168.0.1/32'
prefix_two = '192.168.0.2/32'
@@ -264,7 +268,9 @@ class TestProtocolsISIS(VyOSUnitTestSHIM.TestCase):
prefix_four_value = '65000'
self.cli_set(base_path + ['net', net])
- self.cli_set(base_path + ['interface', interface])
+ 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])
@@ -283,7 +289,7 @@ class TestProtocolsISIS(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify all changes
- tmp = self.getFRRconfig(f'router isis {domain}', daemon=isis_daemon)
+ 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)
@@ -305,7 +311,7 @@ class TestProtocolsISIS(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify main ISIS changes
- tmp = self.getFRRconfig(f'router isis {domain}', daemon=isis_daemon)
+ 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)
@@ -318,7 +324,7 @@ class TestProtocolsISIS(VyOSUnitTestSHIM.TestCase):
for interface in self._interfaces:
# Verify interface changes for holddown
- tmp = self.getFRRconfig(f'interface {interface}', daemon=isis_daemon)
+ 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)
@@ -332,7 +338,7 @@ class TestProtocolsISIS(VyOSUnitTestSHIM.TestCase):
for interface in self._interfaces:
# Verify interface changes for disable
- tmp = self.getFRRconfig(f'interface {interface}', daemon=isis_daemon)
+ 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)
@@ -355,7 +361,7 @@ class TestProtocolsISIS(VyOSUnitTestSHIM.TestCase):
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}', daemon=isis_daemon)
+ 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'])
@@ -365,7 +371,7 @@ class TestProtocolsISIS(VyOSUnitTestSHIM.TestCase):
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}', daemon=isis_daemon)
+ 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'])
@@ -376,7 +382,7 @@ class TestProtocolsISIS(VyOSUnitTestSHIM.TestCase):
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}', daemon=isis_daemon)
+ 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'])
@@ -388,7 +394,7 @@ class TestProtocolsISIS(VyOSUnitTestSHIM.TestCase):
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}', daemon=isis_daemon)
+ 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'])
@@ -408,7 +414,7 @@ class TestProtocolsISIS(VyOSUnitTestSHIM.TestCase):
for topology in topologies:
self.cli_set(base_path + ['topology', topology])
self.cli_commit()
- tmp = self.getFRRconfig(f'router isis {domain}', daemon=isis_daemon)
+ tmp = self.getFRRconfig(f'router isis {domain}', endsection='^exit')
self.assertIn(f' net {net}', tmp)
self.assertIn(f' topology {topology}', tmp)
diff --git a/smoketest/scripts/cli/test_protocols_mpls.py b/smoketest/scripts/cli/test_protocols_mpls.py
index 88528973d..3840c24f4 100755
--- a/smoketest/scripts/cli/test_protocols_mpls.py
+++ b/smoketest/scripts/cli/test_protocols_mpls.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2021-2023 VyOS maintainers and contributors
+# Copyright (C) 2021-2025 VyOS maintainers and contributors
#
# 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
@@ -17,6 +17,8 @@
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
@@ -72,10 +74,11 @@ class TestProtocolsMPLS(VyOSUnitTestSHIM.TestCase):
# 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)
@@ -106,15 +109,86 @@ class TestProtocolsMPLS(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Validate configuration
- frrconfig = self.getFRRconfig('mpls ldp', daemon=ldpd_daemon)
+ 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(' address-family ipv4', daemon=ldpd_daemon)
+ 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)
diff --git a/smoketest/scripts/cli/test_protocols_nhrp.py b/smoketest/scripts/cli/test_protocols_nhrp.py
index 43ae4abf2..73a760945 100755
--- a/smoketest/scripts/cli/test_protocols_nhrp.py
+++ b/smoketest/scripts/cli/test_protocols_nhrp.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2021-2024 VyOS maintainers and contributors
+# Copyright (C) 2021-2025 VyOS maintainers and contributors
#
# 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
@@ -17,14 +17,12 @@
import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
-
-from vyos.firewall import find_nftables_rule
from vyos.utils.process import process_named_running
-from vyos.utils.file import read_file
tunnel_path = ['interfaces', 'tunnel']
nhrp_path = ['protocols', 'nhrp']
vpn_path = ['vpn', 'ipsec']
+PROCESS_NAME = 'nhrpd'
class TestProtocolsNHRP(VyOSUnitTestSHIM.TestCase):
@classmethod
@@ -41,29 +39,41 @@ class TestProtocolsNHRP(VyOSUnitTestSHIM.TestCase):
self.cli_delete(tunnel_path)
self.cli_commit()
- def test_config(self):
+ def test_01_nhrp_config(self):
tunnel_if = "tun100"
- tunnel_source = "192.0.2.1"
+ tunnel_ip = '172.16.253.134/32'
+ tunnel_source = "192.0.2.134"
tunnel_encapsulation = "gre"
esp_group = "ESP-HUB"
ike_group = "IKE-HUB"
nhrp_secret = "vyos123"
nhrp_profile = "NHRPVPN"
+ nhrp_holdtime = '300'
+ nhs_tunnelip = '172.16.253.1'
+ nhs_nbmaip = '192.0.2.1'
+ map_tunnelip = '172.16.253.135'
+ map_nbmaip = "192.0.2.135"
+ nhrp_networkid = '1'
ipsec_secret = "secret"
-
+ multicat_log_group = '2'
+ redirect_log_group = '1'
# Tunnel
- self.cli_set(tunnel_path + [tunnel_if, "address", "172.16.253.134/29"])
+ self.cli_set(tunnel_path + [tunnel_if, "address", tunnel_ip])
self.cli_set(tunnel_path + [tunnel_if, "encapsulation", tunnel_encapsulation])
self.cli_set(tunnel_path + [tunnel_if, "source-address", tunnel_source])
self.cli_set(tunnel_path + [tunnel_if, "enable-multicast"])
self.cli_set(tunnel_path + [tunnel_if, "parameters", "ip", "key", "1"])
# NHRP
- self.cli_set(nhrp_path + ["tunnel", tunnel_if, "cisco-authentication", nhrp_secret])
- self.cli_set(nhrp_path + ["tunnel", tunnel_if, "holding-time", "300"])
- self.cli_set(nhrp_path + ["tunnel", tunnel_if, "multicast", "dynamic"])
+ self.cli_set(nhrp_path + ["tunnel", tunnel_if, "authentication", nhrp_secret])
+ self.cli_set(nhrp_path + ["tunnel", tunnel_if, "holdtime", nhrp_holdtime])
+ self.cli_set(nhrp_path + ["tunnel", tunnel_if, "multicast", nhs_tunnelip])
self.cli_set(nhrp_path + ["tunnel", tunnel_if, "redirect"])
self.cli_set(nhrp_path + ["tunnel", tunnel_if, "shortcut"])
+ self.cli_set(nhrp_path + ["tunnel", tunnel_if, "registration-no-unique"])
+ self.cli_set(nhrp_path + ["tunnel", tunnel_if, "network-id", nhrp_networkid])
+ self.cli_set(nhrp_path + ["tunnel", tunnel_if, "nhs", "tunnel-ip", nhs_tunnelip, "nbma", nhs_nbmaip])
+ self.cli_set(nhrp_path + ["tunnel", tunnel_if, "map", "tunnel-ip", map_tunnelip, "nbma", map_nbmaip])
# IKE/ESP Groups
self.cli_set(vpn_path + ["esp-group", esp_group, "lifetime", "1800"])
@@ -93,29 +103,40 @@ class TestProtocolsNHRP(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
- opennhrp_lines = [
- f'interface {tunnel_if} #hub {nhrp_profile}',
- f'cisco-authentication {nhrp_secret}',
- f'holding-time 300',
- f'shortcut',
- f'multicast dynamic',
- f'redirect'
+ frrconfig = self.getFRRconfig(f'interface {tunnel_if}', endsection='^exit')
+ self.assertIn(f'interface {tunnel_if}', frrconfig)
+ self.assertIn(f' ip nhrp authentication {nhrp_secret}', frrconfig)
+ self.assertIn(f' ip nhrp holdtime {nhrp_holdtime}', frrconfig)
+ self.assertIn(f' ip nhrp map multicast {nhs_tunnelip}', frrconfig)
+ self.assertIn(f' ip nhrp redirect', frrconfig)
+ self.assertIn(f' ip nhrp registration no-unique', frrconfig)
+ self.assertIn(f' ip nhrp shortcut', frrconfig)
+ self.assertIn(f' ip nhrp network-id {nhrp_networkid}', frrconfig)
+ self.assertIn(f' ip nhrp nhs {nhs_tunnelip} nbma {nhs_nbmaip}', frrconfig)
+ self.assertIn(f' ip nhrp map {map_tunnelip} {map_nbmaip}', frrconfig)
+ self.assertIn(f' tunnel protection vici profile dmvpn-{nhrp_profile}-{tunnel_if}-child',
+ frrconfig)
+
+ nftables_search_multicast = [
+ ['chain VYOS_NHRP_MULTICAST_OUTPUT'],
+ ['type filter hook output priority filter + 10; policy accept;'],
+ [f'oifname "{tunnel_if}"', 'ip daddr 224.0.0.0/24', 'counter', f'log group {multicat_log_group}'],
+ [f'oifname "{tunnel_if}"', 'ip daddr 224.0.0.0/24', 'counter', 'drop'],
+ ['chain VYOS_NHRP_MULTICAST_FORWARD'],
+ ['type filter hook output priority filter + 10; policy accept;'],
+ [f'oifname "{tunnel_if}"', 'ip daddr 224.0.0.0/4', 'counter', f'log group {multicat_log_group}'],
+ [f'oifname "{tunnel_if}"', 'ip daddr 224.0.0.0/4', 'counter', 'drop']
]
- tmp_opennhrp_conf = read_file('/run/opennhrp/opennhrp.conf')
-
- for line in opennhrp_lines:
- self.assertIn(line, tmp_opennhrp_conf)
-
- firewall_matches = [
- f'ip protocol {tunnel_encapsulation}',
- f'ip saddr {tunnel_source}',
- f'ip daddr 224.0.0.0/4',
- f'comment "VYOS_NHRP_{tunnel_if}"'
+ nftables_search_redirect = [
+ ['chain VYOS_NHRP_REDIRECT_FORWARD'],
+ ['type filter hook forward priority filter + 10; policy accept;'],
+ [f'iifname "{tunnel_if}" oifname "{tunnel_if}"', 'meter loglimit-0 size 65535 { ip daddr & 255.255.255.0 . ip saddr & 255.255.255.0 timeout 1m limit rate 4/minute burst 1 packets }', 'counter', f'log group {redirect_log_group}']
]
+ self.verify_nftables(nftables_search_multicast, 'ip vyos_nhrp_multicast')
+ self.verify_nftables(nftables_search_redirect, 'ip vyos_nhrp_redirect')
- self.assertTrue(find_nftables_rule('ip vyos_nhrp_filter', 'VYOS_NHRP_OUTPUT', firewall_matches) is not None)
- self.assertTrue(process_named_running('opennhrp'))
+ self.assertTrue(process_named_running(PROCESS_NAME))
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_protocols_openfabric.py b/smoketest/scripts/cli/test_protocols_openfabric.py
index f1372d7ab..323b6cd74 100644
--- a/smoketest/scripts/cli/test_protocols_openfabric.py
+++ b/smoketest/scripts/cli/test_protocols_openfabric.py
@@ -17,6 +17,8 @@
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
@@ -40,6 +42,8 @@ class TestProtocolsOpenFabric(VyOSUnitTestSHIM.TestCase):
# 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)
@@ -75,14 +79,14 @@ class TestProtocolsOpenFabric(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify all changes
- tmp = self.getFRRconfig(f'router openfabric {domain}', daemon=openfabric_daemon)
+ 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}', daemon=openfabric_daemon)
+ 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)
@@ -101,12 +105,12 @@ class TestProtocolsOpenFabric(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify FRR openfabric configuration
- tmp = self.getFRRconfig(f'router openfabric {domain}', daemon=openfabric_daemon)
+ 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}', daemon=openfabric_daemon)
+ 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)
@@ -137,11 +141,11 @@ class TestProtocolsOpenFabric(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify all changes
- tmp = self.getFRRconfig(f'router openfabric {domain}', daemon=openfabric_daemon)
+ 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}', daemon=openfabric_daemon)
+ 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):
@@ -165,22 +169,21 @@ class TestProtocolsOpenFabric(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify FRR openfabric configuration
- tmp = self.getFRRconfig(f'router openfabric {domain}', daemon=openfabric_daemon)
+ 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}', daemon=openfabric_daemon)
+ 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}', daemon=openfabric_daemon)
+ 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}', daemon=openfabric_daemon)
+ tmp = self.getFRRconfig(f'interface {interface}', endsection='^exit')
self.assertIn(f' ip router openfabric {domain_2}', tmp)
-
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_protocols_ospf.py b/smoketest/scripts/cli/test_protocols_ospf.py
index 599bf3930..ea55fa031 100755
--- a/smoketest/scripts/cli/test_protocols_ospf.py
+++ b/smoketest/scripts/cli/test_protocols_ospf.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2021-2024 VyOS maintainers and contributors
+# Copyright (C) 2021-2025 VyOS maintainers and contributors
#
# 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
@@ -18,6 +18,7 @@ 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
@@ -45,6 +46,8 @@ class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase):
# 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):
@@ -56,7 +59,7 @@ class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase):
self.cli_delete(base_path)
self.cli_commit()
- frrconfig = self.getFRRconfig('router ospf', endsection='^exit', daemon=ospf_daemon)
+ frrconfig = self.getFRRconfig('router ospf', endsection='^exit')
self.assertNotIn(f'router ospf', frrconfig)
# check process health and continuity
@@ -68,7 +71,7 @@ class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify FRR ospfd configuration
- frrconfig = self.getFRRconfig('router ospf', endsection='^exit', daemon=ospf_daemon)
+ 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
@@ -96,7 +99,7 @@ class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify FRR ospfd configuration
- frrconfig = self.getFRRconfig('router ospf', endsection='^exit', daemon=ospf_daemon)
+ 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)
@@ -112,7 +115,6 @@ class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase):
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'
@@ -128,14 +130,13 @@ class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify FRR ospfd configuration
- frrconfig = self.getFRRconfig('router ospf', endsection='^exit', daemon=ospf_daemon)
+ 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'
@@ -149,7 +150,7 @@ class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify FRR ospfd configuration
- frrconfig = self.getFRRconfig('router ospf', endsection='^exit', daemon=ospf_daemon)
+ 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)
@@ -159,10 +160,9 @@ class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify FRR ospfd configuration
- frrconfig = self.getFRRconfig('router ospf', endsection='^exit', daemon=ospf_daemon)
+ 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'
@@ -201,7 +201,7 @@ class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify FRR ospfd configuration
- frrconfig = self.getFRRconfig('router ospf', endsection='^exit', daemon=ospf_daemon)
+ 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
@@ -224,7 +224,7 @@ class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase):
self.cli_set(base_path + ['distance', 'ospf', 'inter-area', inter_area])
self.cli_commit()
- frrconfig = self.getFRRconfig('router ospf', endsection='^exit', daemon=ospf_daemon)
+ 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
@@ -247,7 +247,7 @@ class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify FRR ospfd configuration
- frrconfig = self.getFRRconfig('router ospf', endsection='^exit', daemon=ospf_daemon)
+ 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
@@ -255,7 +255,7 @@ class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase):
def test_ospf_07_redistribute(self):
metric = '15'
metric_type = '1'
- redistribute = ['babel', 'bgp', 'connected', 'isis', 'kernel', 'rip', 'static']
+ redistribute = ['babel', 'bgp', 'connected', 'isis', 'kernel', 'nhrp', 'rip', 'static']
for protocol in redistribute:
self.cli_set(base_path + ['redistribute', protocol, 'metric', metric])
@@ -266,7 +266,7 @@ class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify FRR ospfd configuration
- frrconfig = self.getFRRconfig('router ospf', endsection='^exit', daemon=ospf_daemon)
+ frrconfig = self.getFRRconfig('router ospf', endsection='^exit')
self.assertIn(f'router ospf', frrconfig)
for protocol in redistribute:
self.assertIn(f' redistribute {protocol} metric {metric} metric-type {metric_type} route-map {route_map}', frrconfig)
@@ -294,7 +294,7 @@ class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify FRR ospfd configuration
- frrconfig = self.getFRRconfig('router ospf', endsection='^exit', daemon=ospf_daemon)
+ 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)
@@ -326,13 +326,13 @@ class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase):
# commit changes
self.cli_commit()
- frrconfig = self.getFRRconfig('router ospf', endsection='^exit', daemon=ospf_daemon)
+ 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}')
+ 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)
@@ -350,7 +350,7 @@ class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase):
for interface in interfaces:
# T5467: It must also be removed from FRR config
- frrconfig = self.getFRRconfig(f'interface {interface}', daemon=ospf_daemon)
+ 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)
@@ -371,11 +371,11 @@ class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify FRR ospfd configuration
- frrconfig = self.getFRRconfig('router ospf', endsection='^exit', daemon=ospf_daemon)
+ frrconfig = self.getFRRconfig('router ospf', endsection='^exit')
self.assertIn(f'router ospf', frrconfig)
for interface in interfaces:
- config = self.getFRRconfig(f'interface {interface}', daemon=ospf_daemon)
+ config = self.getFRRconfig(f'interface {interface}', endsection='^exit')
self.assertIn(f'interface {interface}', config)
self.assertIn(f' ip ospf area {area}', config)
@@ -398,17 +398,17 @@ class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify FRR ospfd configuration
- frrconfig = self.getFRRconfig('router ospf', endsection='^exit', daemon=ospf_daemon)
+ 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}', daemon=ospf_daemon)
+ 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}', daemon=ospf_daemon)
+ frrconfig = self.getFRRconfig(f'interface {vrf_iface}', endsection='^exit')
self.assertIn(f'interface {vrf_iface}', frrconfig)
self.assertIn(f' ip ospf area {area}', frrconfig)
@@ -418,7 +418,7 @@ class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# T5467: It must also be removed from FRR config
- frrconfig = self.getFRRconfig(f'interface {vrf_iface}', daemon=ospf_daemon)
+ 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)
@@ -444,7 +444,7 @@ class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify FRR ospfd configuration
- frrconfig = self.getFRRconfig('router ospf', endsection='^exit', daemon=ospf_daemon)
+ 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)
@@ -477,7 +477,7 @@ class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify all changes
- frrconfig = self.getFRRconfig('router ospf', endsection='^exit', daemon=ospf_daemon)
+ 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)
@@ -495,7 +495,7 @@ class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify main OSPF changes
- frrconfig = self.getFRRconfig('router ospf', endsection='^exit', daemon=ospf_daemon)
+ 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)
@@ -508,7 +508,7 @@ class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase):
for interface in interfaces:
# Verify interface changes for holddown
- config = self.getFRRconfig(f'interface {interface}', daemon=ospf_daemon)
+ 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)
@@ -522,7 +522,7 @@ class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase):
for interface in interfaces:
# Verify interface changes for disable
- config = self.getFRRconfig(f'interface {interface}', daemon=ospf_daemon)
+ 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)
@@ -544,7 +544,7 @@ class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify FRR ospfd configuration
- frrconfig = self.getFRRconfig('router ospf', endsection='^exit', daemon=ospf_daemon)
+ 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)
@@ -570,7 +570,7 @@ class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify FRR ospfd configuration
- frrconfig = self.getFRRconfig('router ospf', endsection='^exit', daemon=ospf_daemon, empty_retry=60)
+ frrconfig = self.getFRRconfig('router ospf', endsection='^exit', empty_retry=60)
self.assertIn(f'router ospf', frrconfig)
self.assertIn(f' network {network} area {area1}', frrconfig)
diff --git a/smoketest/scripts/cli/test_protocols_ospfv3.py b/smoketest/scripts/cli/test_protocols_ospfv3.py
index d961a4fdc..5da4c7c98 100755
--- a/smoketest/scripts/cli/test_protocols_ospfv3.py
+++ b/smoketest/scripts/cli/test_protocols_ospfv3.py
@@ -17,6 +17,7 @@
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
@@ -44,6 +45,8 @@ class TestProtocolsOSPFv3(VyOSUnitTestSHIM.TestCase):
# 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):
@@ -54,7 +57,7 @@ class TestProtocolsOSPFv3(VyOSUnitTestSHIM.TestCase):
self.cli_delete(base_path)
self.cli_commit()
- frrconfig = self.getFRRconfig('router ospf6', endsection='^exit', daemon=ospf6_daemon)
+ frrconfig = self.getFRRconfig('router ospf6', endsection='^exit')
self.assertNotIn(f'router ospf6', frrconfig)
# check process health and continuity
@@ -81,7 +84,7 @@ class TestProtocolsOSPFv3(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify FRR ospfd configuration
- frrconfig = self.getFRRconfig('router ospf6', endsection='^exit', daemon=ospf6_daemon)
+ 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)
@@ -89,7 +92,7 @@ class TestProtocolsOSPFv3(VyOSUnitTestSHIM.TestCase):
self.assertIn(f' area {default_area} export-list {acl_name}', frrconfig)
for interface in interfaces:
- if_config = self.getFRRconfig(f'interface {interface}', daemon=ospf6_daemon)
+ 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])
@@ -110,7 +113,7 @@ class TestProtocolsOSPFv3(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify FRR ospfd configuration
- frrconfig = self.getFRRconfig('router ospf6', endsection='^exit', daemon=ospf6_daemon)
+ 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)
@@ -134,7 +137,7 @@ class TestProtocolsOSPFv3(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify FRR ospfd configuration
- frrconfig = self.getFRRconfig('router ospf6', endsection='^exit', daemon=ospf6_daemon)
+ 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)
@@ -165,13 +168,13 @@ class TestProtocolsOSPFv3(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify FRR ospfd configuration
- frrconfig = self.getFRRconfig('router ospf6', endsection='^exit', daemon=ospf6_daemon)
+ 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}', daemon=ospf6_daemon)
+ 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)
@@ -188,7 +191,7 @@ class TestProtocolsOSPFv3(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
for interface in interfaces:
- if_config = self.getFRRconfig(f'interface {interface}', daemon=ospf6_daemon)
+ 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)
@@ -204,7 +207,7 @@ class TestProtocolsOSPFv3(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify FRR ospfd configuration
- frrconfig = self.getFRRconfig('router ospf6', endsection='^exit', daemon=ospf6_daemon)
+ 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)
@@ -230,7 +233,7 @@ class TestProtocolsOSPFv3(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify FRR ospfd configuration
- frrconfig = self.getFRRconfig('router ospf6', endsection='^exit', daemon=ospf6_daemon)
+ 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)
@@ -250,7 +253,7 @@ class TestProtocolsOSPFv3(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify FRR ospfd configuration
- frrconfig = self.getFRRconfig('router ospf6', endsection='^exit', daemon=ospf6_daemon)
+ 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)
@@ -259,7 +262,7 @@ class TestProtocolsOSPFv3(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify FRR ospfd configuration
- frrconfig = self.getFRRconfig('router ospf6', endsection='^exit', daemon=ospf6_daemon)
+ 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)
@@ -285,15 +288,15 @@ class TestProtocolsOSPFv3(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify FRR ospfd configuration
- frrconfig = self.getFRRconfig('router ospf6', endsection='^exit', daemon=ospf6_daemon)
+ 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}', daemon=ospf6_daemon)
+ 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}', daemon=ospf6_daemon)
+ 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)
@@ -303,7 +306,7 @@ class TestProtocolsOSPFv3(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# T5467: It must also be removed from FRR config
- frrconfig = self.getFRRconfig(f'interface {vrf_iface}', daemon=ospf6_daemon)
+ 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)
@@ -329,7 +332,7 @@ class TestProtocolsOSPFv3(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify FRR ospfd configuration
- frrconfig = self.getFRRconfig('router ospf6', endsection='^exit', daemon=ospf6_daemon)
+ 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)
diff --git a/smoketest/scripts/cli/test_protocols_pim.py b/smoketest/scripts/cli/test_protocols_pim.py
index 98b9d57aa..cc62769b3 100755
--- a/smoketest/scripts/cli/test_protocols_pim.py
+++ b/smoketest/scripts/cli/test_protocols_pim.py
@@ -17,6 +17,7 @@
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
@@ -26,6 +27,16 @@ 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))
@@ -57,11 +68,11 @@ class TestProtocolsPIM(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify FRR pimd configuration
- frrconfig = self.getFRRconfig('router pim', endsection='^exit', daemon=pim_daemon)
+ frrconfig = self.getFRRconfig('router pim', endsection='^exit')
self.assertIn(f' rp {rp} {group}', frrconfig)
for interface in interfaces:
- frrconfig = self.getFRRconfig(f'interface {interface}', daemon=pim_daemon)
+ 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)
@@ -108,7 +119,7 @@ class TestProtocolsPIM(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify FRR pimd configuration
- frrconfig = self.getFRRconfig('router pim', endsection='^exit', daemon=pim_daemon)
+ 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)
@@ -170,11 +181,11 @@ class TestProtocolsPIM(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
- frrconfig = self.getFRRconfig(daemon=pim_daemon)
+ frrconfig = self.getFRRconfig()
self.assertIn(f'ip igmp watermark-warn {watermark_warning}', frrconfig)
for interface in interfaces:
- frrconfig = self.getFRRconfig(f'interface {interface}', daemon=pim_daemon)
+ 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)
diff --git a/smoketest/scripts/cli/test_protocols_pim6.py b/smoketest/scripts/cli/test_protocols_pim6.py
index f69eb4ae1..4ed8fcf7a 100755
--- a/smoketest/scripts/cli/test_protocols_pim6.py
+++ b/smoketest/scripts/cli/test_protocols_pim6.py
@@ -17,6 +17,8 @@
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
@@ -34,6 +36,8 @@ class TestProtocolsPIMv6(VyOSUnitTestSHIM.TestCase):
# 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)
@@ -52,7 +56,7 @@ class TestProtocolsPIMv6(VyOSUnitTestSHIM.TestCase):
# Verify FRR pim6d configuration
for interface in interfaces:
- config = self.getFRRconfig(f'interface {interface}', daemon=pim6_daemon)
+ 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)
@@ -65,7 +69,7 @@ class TestProtocolsPIMv6(VyOSUnitTestSHIM.TestCase):
# Verify FRR pim6d configuration
for interface in interfaces:
- config = self.getFRRconfig(f'interface {interface}', daemon=pim6_daemon)
+ 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)
@@ -88,7 +92,7 @@ class TestProtocolsPIMv6(VyOSUnitTestSHIM.TestCase):
# Verify FRR pim6d configuration
for interface in interfaces:
- config = self.getFRRconfig(f'interface {interface}', daemon=pim6_daemon)
+ config = self.getFRRconfig(f'interface {interface}', endsection='^exit')
self.assertIn(f'interface {interface}', config)
self.assertIn(f' ipv6 mld join-group ff18::1234', config)
@@ -100,7 +104,7 @@ class TestProtocolsPIMv6(VyOSUnitTestSHIM.TestCase):
# Verify FRR pim6d configuration
for interface in interfaces:
- config = self.getFRRconfig(f'interface {interface}', daemon=pim6_daemon)
+ 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)
@@ -128,14 +132,14 @@ class TestProtocolsPIMv6(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify FRR pim6d configuration
- config = self.getFRRconfig('router pim6', endsection='^exit', daemon=pim6_daemon)
+ 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}', daemon=pim6_daemon)
+ 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)
diff --git a/smoketest/scripts/cli/test_protocols_rip.py b/smoketest/scripts/cli/test_protocols_rip.py
index 33706a9c9..27b543803 100755
--- a/smoketest/scripts/cli/test_protocols_rip.py
+++ b/smoketest/scripts/cli/test_protocols_rip.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2021-2023 VyOS maintainers and contributors
+# Copyright (C) 2021-2025 VyOS maintainers and contributors
#
# 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
@@ -17,6 +17,7 @@
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
@@ -39,6 +40,8 @@ class TestProtocolsRIP(VyOSUnitTestSHIM.TestCase):
# 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'])
@@ -66,7 +69,7 @@ class TestProtocolsRIP(VyOSUnitTestSHIM.TestCase):
self.cli_delete(base_path)
self.cli_commit()
- frrconfig = self.getFRRconfig('router rip')
+ frrconfig = self.getFRRconfig('router rip', endsection='^exit')
self.assertNotIn(f'router rip', frrconfig)
# check process health and continuity
@@ -79,7 +82,7 @@ class TestProtocolsRIP(VyOSUnitTestSHIM.TestCase):
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', 'ospf', 'static']
+ redistribute = ['bgp', 'connected', 'isis', 'kernel', 'nhrp', 'ospf', 'static']
timer_garbage = '888'
timer_timeout = '1000'
timer_update = '90'
@@ -116,7 +119,7 @@ class TestProtocolsRIP(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify FRR ripd configuration
- frrconfig = self.getFRRconfig('router rip')
+ 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)
@@ -175,10 +178,10 @@ class TestProtocolsRIP(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify FRR configuration
- frrconfig = self.getFRRconfig('router rip')
+ frrconfig = self.getFRRconfig('router rip', endsection='^exit')
self.assertIn(f'version {tx_version}', frrconfig)
- frrconfig = self.getFRRconfig(f'interface {interface}')
+ 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)
diff --git a/smoketest/scripts/cli/test_protocols_ripng.py b/smoketest/scripts/cli/test_protocols_ripng.py
index b10df9679..d2066b825 100755
--- a/smoketest/scripts/cli/test_protocols_ripng.py
+++ b/smoketest/scripts/cli/test_protocols_ripng.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2021-2023 VyOS maintainers and contributors
+# Copyright (C) 2021-2025 VyOS maintainers and contributors
#
# 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
@@ -17,6 +17,7 @@
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
@@ -40,6 +41,8 @@ class TestProtocolsRIPng(VyOSUnitTestSHIM.TestCase):
# 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'])
@@ -66,7 +69,7 @@ class TestProtocolsRIPng(VyOSUnitTestSHIM.TestCase):
self.cli_delete(base_path)
self.cli_commit()
- frrconfig = self.getFRRconfig('router ripng')
+ frrconfig = self.getFRRconfig('router ripng', endsection='^exit')
self.assertNotIn(f'router ripng', frrconfig)
# check process health and continuity
@@ -113,7 +116,7 @@ class TestProtocolsRIPng(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify FRR ospfd configuration
- frrconfig = self.getFRRconfig('router ripng')
+ 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)
diff --git a/smoketest/scripts/cli/test_protocols_rpki.py b/smoketest/scripts/cli/test_protocols_rpki.py
index 23738717a..0addf7fee 100755
--- a/smoketest/scripts/cli/test_protocols_rpki.py
+++ b/smoketest/scripts/cli/test_protocols_rpki.py
@@ -17,6 +17,7 @@
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
@@ -111,12 +112,14 @@ class TestProtocolsRPKI(VyOSUnitTestSHIM.TestCase):
# 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()
- frrconfig = self.getFRRconfig('rpki')
+ frrconfig = self.getFRRconfig('rpki', endsection='^exit')
self.assertNotIn(f'rpki', frrconfig)
# check process health and continuity
@@ -153,7 +156,7 @@ class TestProtocolsRPKI(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify FRR configuration
- frrconfig = self.getFRRconfig('rpki')
+ frrconfig = self.getFRRconfig('rpki', endsection='^exit')
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)
@@ -192,7 +195,7 @@ class TestProtocolsRPKI(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify FRR configuration
- frrconfig = self.getFRRconfig('rpki')
+ frrconfig = self.getFRRconfig('rpki', endsection='^exit')
for cache_name, cache_config in cache.items():
port = cache_config['port']
preference = cache_config['preference']
@@ -245,5 +248,41 @@ class TestProtocolsRPKI(VyOSUnitTestSHIM.TestCase):
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'])
+
+ # Configure a TCP cache server
+ self.cli_set(base_path + ['cache', peer, 'port', port])
+ self.cli_set(base_path + ['cache', peer, 'preference', preference])
+ self.cli_set(base_path + ['cache', peer, 'source-address', source_address])
+ self.cli_commit()
+
+ # Verify FRR configuration
+ frrconfig = self.getFRRconfig('rpki')
+ 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(base_path + ['cache', peer, 'ssh', 'username', username])
+ self.cli_set(base_path + ['cache', peer, 'ssh', 'key', rpki_key_name])
+ self.cli_commit()
+
+ # Verify FRR configuration
+ frrconfig = self.getFRRconfig('rpki')
+ 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)
diff --git a/smoketest/scripts/cli/test_protocols_segment-routing.py b/smoketest/scripts/cli/test_protocols_segment-routing.py
index 624985476..94c808733 100755
--- a/smoketest/scripts/cli/test_protocols_segment-routing.py
+++ b/smoketest/scripts/cli/test_protocols_segment-routing.py
@@ -17,6 +17,7 @@
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
@@ -26,6 +27,7 @@ from vyos.utils.system import sysctl_read
base_path = ['protocols', 'segment-routing']
+
class TestProtocolsSegmentRouting(VyOSUnitTestSHIM.TestCase):
@classmethod
def setUpClass(cls):
@@ -36,6 +38,8 @@ class TestProtocolsSegmentRouting(VyOSUnitTestSHIM.TestCase):
# 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)
@@ -47,14 +51,64 @@ class TestProtocolsSegmentRouting(VyOSUnitTestSHIM.TestCase):
def test_srv6(self):
interfaces = Section.interfaces('ethernet', vlan=False)
locators = {
- 'foo' : { 'prefix' : '2001:a::/64' },
- 'foo' : { 'prefix' : '2001:b::/64', 'usid' : {} },
+ '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']])
+ 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):
@@ -65,16 +119,33 @@ class TestProtocolsSegmentRouting(VyOSUnitTestSHIM.TestCase):
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(f'segment-routing', daemon=zebra_daemon)
- self.assertIn(f'segment-routing', frrconfig)
- self.assertIn(f' srv6', frrconfig)
- self.assertIn(f' locators', frrconfig)
+ 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 {locator_config["prefix"]} block-len 40 node-len 24 func-bits 16', 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)
@@ -86,8 +157,12 @@ class TestProtocolsSegmentRouting(VyOSUnitTestSHIM.TestCase):
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
+ 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:
@@ -96,8 +171,12 @@ class TestProtocolsSegmentRouting(VyOSUnitTestSHIM.TestCase):
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
+ 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]
@@ -106,5 +185,6 @@ class TestProtocolsSegmentRouting(VyOSUnitTestSHIM.TestCase):
self.assertEqual(sysctl_read(f'net.ipv6.conf.{first_if}.seg6_enabled'), '0')
+
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_protocols_static.py b/smoketest/scripts/cli/test_protocols_static.py
index a2cde0237..79d6b3af4 100755
--- a/smoketest/scripts/cli/test_protocols_static.py
+++ b/smoketest/scripts/cli/test_protocols_static.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2021-2024 VyOS maintainers and contributors
+# Copyright (C) 2021-2025 VyOS maintainers and contributors
#
# 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
@@ -14,14 +14,20 @@
# 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']
@@ -163,16 +169,20 @@ class TestProtocolsStatic(VyOSUnitTestSHIM.TestCase):
@classmethod
def setUpClass(cls):
super(TestProtocolsStatic, cls).setUpClass()
+ cls.cli_delete(cls, base_path)
cls.cli_delete(cls, ['vrf'])
- cls.cli_set(cls, ['vrf', 'name', 'black', 'table', '43210'])
+ # 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='')
@@ -181,6 +191,7 @@ class TestProtocolsStatic(VyOSUnitTestSHIM.TestCase):
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):
@@ -315,6 +326,7 @@ class TestProtocolsStatic(VyOSUnitTestSHIM.TestCase):
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'
@@ -409,6 +421,7 @@ class TestProtocolsStatic(VyOSUnitTestSHIM.TestCase):
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
@@ -567,5 +580,44 @@ class TestProtocolsStatic(VyOSUnitTestSHIM.TestCase):
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)
+
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_service_dhcp-server.py b/smoketest/scripts/cli/test_service_dhcp-server.py
index f891bf295..e421f04d2 100755
--- a/smoketest/scripts/cli/test_service_dhcp-server.py
+++ b/smoketest/scripts/cli/test_service_dhcp-server.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2020-2024 VyOS maintainers and contributors
+# Copyright (C) 2020-2025 VyOS maintainers and contributors
#
# 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
@@ -15,6 +15,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
+import re
import unittest
from json import loads
@@ -22,15 +23,21 @@ from json import loads
from base_vyostest_shim import VyOSUnitTestSHIM
from vyos.configsession import ConfigSessionError
+from vyos.kea import kea_add_lease
+from vyos.kea import kea_delete_lease
+from vyos.utils.process import cmd
from vyos.utils.process import process_named_running
from vyos.utils.file import read_file
from vyos.template import inc_ip
from vyos.template import dec_ip
PROCESS_NAME = 'kea-dhcp4'
+D2_PROCESS_NAME = 'kea-dhcp-ddns'
CTRL_PROCESS_NAME = 'kea-ctrl-agent'
KEA4_CONF = '/run/kea/kea-dhcp4.conf'
+KEA4_D2_CONF = '/run/kea/kea-dhcp-ddns.conf'
KEA4_CTRL = '/run/kea/dhcp4-ctrl-socket'
+HOSTSD_CLIENT = '/usr/bin/vyos-hostsd-client'
base_path = ['service', 'dhcp-server']
interface = 'dum8765'
subnet = '192.0.2.0/25'
@@ -39,15 +46,18 @@ dns_1 = inc_ip(subnet, 2)
dns_2 = inc_ip(subnet, 3)
domain_name = 'vyos.net'
+
class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase):
@classmethod
def setUpClass(cls):
super(TestServiceDHCPServer, cls).setUpClass()
- # Clear out current configuration to allow running this test on a live system
+ # Clear out current configuration to allow running this test on a live system
cls.cli_delete(cls, base_path)
cidr_mask = subnet.split('/')[-1]
- cls.cli_set(cls, ['interfaces', 'dummy', interface, 'address', f'{router}/{cidr_mask}'])
+ cls.cli_set(
+ cls, ['interfaces', 'dummy', interface, 'address', f'{router}/{cidr_mask}']
+ )
@classmethod
def tearDownClass(cls):
@@ -69,7 +79,7 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase):
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"
+ assert False, 'Invalid type'
current = current[key]
@@ -88,19 +98,26 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase):
self.assertTrue(key in base_obj)
self.assertEqual(base_obj[key], value)
+ def verify_service_running(self):
+ tmp = cmd('tail -n 100 /var/log/messages | grep kea')
+ self.assertTrue(process_named_running(PROCESS_NAME), msg=f'Service not running, log: {tmp}')
+
def test_dhcp_single_pool_range(self):
shared_net_name = 'SMOKE-1'
range_0_start = inc_ip(subnet, 10)
- range_0_stop = inc_ip(subnet, 20)
+ range_0_stop = inc_ip(subnet, 20)
range_1_start = inc_ip(subnet, 40)
- range_1_stop = inc_ip(subnet, 50)
+ range_1_stop = inc_ip(subnet, 50)
self.cli_set(base_path + ['listen-interface', interface])
+ self.cli_set(base_path + ['shared-network-name', shared_net_name, 'ping-check'])
+
pool = base_path + ['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])
@@ -121,55 +138,90 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase):
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)
+ 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})
+ 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}'})
+ 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})
+ 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}'})
+ 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}'})
+ obj,
+ ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'pools'],
+ {'pool': f'{range_1_start} - {range_1_stop}'},
+ )
# Check for running process
- self.assertTrue(process_named_running(PROCESS_NAME))
+ self.verify_service_running()
def test_dhcp_single_pool_options(self):
shared_net_name = 'SMOKE-0815'
- range_0_start = inc_ip(subnet, 10)
- range_0_stop = inc_ip(subnet, 20)
- smtp_server = '1.2.3.4'
- time_server = '4.3.2.1'
- tftp_server = 'tftp.vyos.io'
- search_domains = ['foo.vyos.net', 'bar.vyos.net']
- bootfile_name = 'vyos'
- bootfile_server = '192.0.2.1'
- wpad = 'http://wpad.vyos.io/foo/bar'
- server_identifier = bootfile_server
+ range_0_start = inc_ip(subnet, 10)
+ range_0_stop = inc_ip(subnet, 20)
+ smtp_server = '1.2.3.4'
+ time_server = '4.3.2.1'
+ tftp_server = 'tftp.vyos.io'
+ search_domains = ['foo.vyos.net', 'bar.vyos.net']
+ bootfile_name = 'vyos'
+ bootfile_server = '192.0.2.1'
+ wpad = 'http://wpad.vyos.io/foo/bar'
+ server_identifier = bootfile_server
ipv6_only_preferred = '300'
+ capwap_access_controller = '192.168.2.125'
pool = base_path + ['shared-network-name', shared_net_name, 'subnet', subnet]
self.cli_set(pool + ['subnet-id', '1'])
@@ -189,8 +241,16 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase):
self.cli_set(pool + ['option', 'bootfile-server', bootfile_server])
self.cli_set(pool + ['option', 'wpad-url', wpad])
self.cli_set(pool + ['option', 'server-identifier', server_identifier])
+ self.cli_set(
+ pool + ['option', 'capwap-controller', capwap_access_controller]
+ )
+
+ static_route = '10.0.0.0/24'
+ static_route_nexthop = '192.0.2.1'
- self.cli_set(pool + ['option', 'static-route', '10.0.0.0/24', 'next-hop', '192.0.2.1'])
+ self.cli_set(
+ pool + ['option', 'static-route', static_route, 'next-hop', static_route_nexthop]
+ )
self.cli_set(pool + ['option', 'ipv6-only-preferred', ipv6_only_preferred])
self.cli_set(pool + ['option', 'time-zone', 'Europe/London'])
@@ -203,95 +263,133 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase):
config = read_file(KEA4_CONF)
obj = loads(config)
- 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'], 'boot-file-name', bootfile_name)
- self.verify_config_value(obj, ['Dhcp4', 'shared-networks', 0, 'subnet4'], 'next-server', bootfile_server)
- 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)
+ 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'],
+ 'boot-file-name',
+ bootfile_name,
+ )
+ self.verify_config_value(
+ obj,
+ ['Dhcp4', 'shared-networks', 0, 'subnet4'],
+ 'next-server',
+ bootfile_server,
+ )
+ 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 options
self.verify_config_object(
- obj,
- ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'],
- {'name': 'domain-name', 'data': domain_name})
+ 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}'})
+ 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': 'domain-search', 'data': ', '.join(search_domains)})
+ obj,
+ ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'],
+ {'name': 'domain-search', 'data': ', '.join(search_domains)},
+ )
self.verify_config_object(
- obj,
- ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'],
- {'name': 'pop-server', 'data': smtp_server})
+ obj,
+ ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'],
+ {'name': 'pop-server', 'data': smtp_server},
+ )
self.verify_config_object(
- obj,
- ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'],
- {'name': 'smtp-server', 'data': smtp_server})
+ obj,
+ ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'],
+ {'name': 'smtp-server', 'data': smtp_server},
+ )
self.verify_config_object(
- obj,
- ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'],
- {'name': 'time-servers', 'data': time_server})
+ obj,
+ ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'],
+ {'name': 'time-servers', 'data': time_server},
+ )
self.verify_config_object(
- obj,
- ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'],
- {'name': 'routers', 'data': router})
+ obj,
+ ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'],
+ {'name': 'routers', 'data': router},
+ )
self.verify_config_object(
- obj,
- ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'],
- {'name': 'dhcp-server-identifier', 'data': server_identifier})
+ obj,
+ ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'],
+ {'name': 'dhcp-server-identifier', 'data': server_identifier},
+ )
self.verify_config_object(
- obj,
- ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'],
- {'name': 'tftp-server-name', 'data': tftp_server})
+ obj,
+ ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'],
+ {'name': 'capwap-ac-v4', 'data': capwap_access_controller},
+ )
self.verify_config_object(
- obj,
- ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'],
- {'name': 'wpad-url', 'data': wpad})
+ obj,
+ ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'],
+ {'name': 'tftp-server-name', 'data': tftp_server},
+ )
self.verify_config_object(
- obj,
- ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'],
- {'name': 'rfc3442-static-route', 'data': '24,10,0,0,192,0,2,1, 0,192,0,2,1'})
+ obj,
+ ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'],
+ {'name': 'wpad-url', 'data': wpad},
+ )
self.verify_config_object(
- obj,
- ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'],
- {'name': 'windows-static-route', 'data': '24,10,0,0,192,0,2,1'})
+ obj,
+ ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'],
+ {
+ 'name': 'classless-static-route',
+ 'data': f'{static_route} - {static_route_nexthop}, 0.0.0.0/0 - {router}',
+ },
+ )
self.verify_config_object(
- obj,
- ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'],
- {'name': 'v6-only-preferred', 'data': ipv6_only_preferred})
+ obj,
+ ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'],
+ {'name': 'v6-only-preferred', 'data': ipv6_only_preferred},
+ )
self.verify_config_object(
- obj,
- ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'],
- {'name': 'ip-forwarding', 'data': "true"})
+ obj,
+ ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'],
+ {'name': 'ip-forwarding', 'data': 'true'},
+ )
# Time zone
self.verify_config_object(
- obj,
- ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'],
- {'name': 'pcode', 'data': 'GMT0BST,M3.5.0/1,M10.5.0'})
+ obj,
+ ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'],
+ {'name': 'pcode', 'data': 'GMT0BST,M3.5.0/1,M10.5.0'},
+ )
self.verify_config_object(
- obj,
- ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'],
- {'name': 'tcode', 'data': 'Europe/London'})
+ obj,
+ ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'],
+ {'name': 'tcode', 'data': 'Europe/London'},
+ )
# Verify pools
self.verify_config_object(
- obj,
- ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'pools'],
- {'pool': f'{range_0_start} - {range_0_stop}'})
+ obj,
+ ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'pools'],
+ {'pool': f'{range_0_start} - {range_0_stop}'},
+ )
# Check for running process
- self.assertTrue(process_named_running(PROCESS_NAME))
+ self.verify_service_running()
def test_dhcp_single_pool_options_scoped(self):
shared_net_name = 'SMOKE-2'
range_0_start = inc_ip(subnet, 10)
- range_0_stop = inc_ip(subnet, 20)
+ range_0_stop = inc_ip(subnet, 20)
range_router = inc_ip(subnet, 5)
range_dns_1 = inc_ip(subnet, 6)
@@ -320,40 +418,58 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase):
config = read_file(KEA4_CONF)
obj = loads(config)
- 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'], 'valid-lifetime', 86400)
- self.verify_config_value(obj, ['Dhcp4', 'shared-networks', 0, 'subnet4'], 'max-valid-lifetime', 86400)
+ 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'], 'valid-lifetime', 86400
+ )
+ self.verify_config_value(
+ obj, ['Dhcp4', 'shared-networks', 0, 'subnet4'], 'max-valid-lifetime', 86400
+ )
# Verify shared-network options
self.verify_config_object(
- obj,
- ['Dhcp4', 'shared-networks', 0, 'option-data'],
- {'name': 'domain-name', 'data': domain_name})
+ obj,
+ ['Dhcp4', 'shared-networks', 0, 'option-data'],
+ {'name': 'domain-name', 'data': domain_name},
+ )
self.verify_config_object(
- obj,
- ['Dhcp4', 'shared-networks', 0, 'option-data'],
- {'name': 'domain-name-servers', 'data': f'{dns_1}, {dns_2}'})
+ obj,
+ ['Dhcp4', 'shared-networks', 0, 'option-data'],
+ {'name': 'domain-name-servers', 'data': f'{dns_1}, {dns_2}'},
+ )
self.verify_config_object(
- obj,
- ['Dhcp4', 'shared-networks', 0, 'option-data'],
- {'name': 'routers', 'data': router})
+ obj,
+ ['Dhcp4', 'shared-networks', 0, 'option-data'],
+ {'name': 'routers', 'data': router},
+ )
# Verify range options
self.verify_config_object(
- obj,
- ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'pools', 0, 'option-data'],
- {'name': 'domain-name-servers', 'data': f'{range_dns_1}, {range_dns_2}'})
+ obj,
+ ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'pools', 0, 'option-data'],
+ {'name': 'domain-name-servers', 'data': f'{range_dns_1}, {range_dns_2}'},
+ )
self.verify_config_object(
- obj,
- ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'pools', 0, 'option-data'],
- {'name': 'routers', 'data': range_router})
+ obj,
+ ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'pools', 0, 'option-data'],
+ {'name': 'routers', 'data': range_router},
+ )
# Verify pool
- self.verify_config_value(obj, ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'pools'], 'pool', f'{range_0_start} - {range_0_stop}')
+ self.verify_config_value(
+ obj,
+ ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'pools'],
+ 'pool',
+ f'{range_0_start} - {range_0_stop}',
+ )
# Check for running process
- self.assertTrue(process_named_running(PROCESS_NAME))
+ self.verify_service_running()
def test_dhcp_single_pool_static_mapping(self):
shared_net_name = 'SMOKE-2'
@@ -375,18 +491,31 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase):
for client in ['client1', 'client2', 'client3']:
mac = '00:50:00:00:00:{}'.format(client_base)
self.cli_set(pool + ['static-mapping', client, 'mac', mac])
- self.cli_set(pool + ['static-mapping', client, 'ip-address', inc_ip(subnet, client_base)])
+ self.cli_set(
+ pool
+ + ['static-mapping', client, 'ip-address', inc_ip(subnet, client_base)]
+ )
client_base += 1
# cannot have both mac-address and duid set
with self.assertRaises(ConfigSessionError):
- self.cli_set(pool + ['static-mapping', 'client1', 'duid', '00:01:00:01:12:34:56:78:aa:bb:cc:dd:ee:11'])
+ self.cli_set(
+ pool
+ + [
+ 'static-mapping',
+ 'client1',
+ 'duid',
+ '00:01:00:01:12:34:56:78:aa:bb:cc:dd:ee:11',
+ ]
+ )
self.cli_commit()
self.cli_delete(pool + ['static-mapping', 'client1', 'duid'])
# cannot have mappings with duplicate IP addresses
self.cli_set(pool + ['static-mapping', 'dupe1', 'mac', '00:50:00:00:fe:ff'])
- self.cli_set(pool + ['static-mapping', 'dupe1', 'ip-address', inc_ip(subnet, 10)])
+ self.cli_set(
+ pool + ['static-mapping', 'dupe1', 'ip-address', inc_ip(subnet, 10)]
+ )
with self.assertRaises(ConfigSessionError):
self.cli_commit()
# Should allow disabled duplicate
@@ -396,17 +525,38 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase):
# cannot have mappings with duplicate MAC addresses
self.cli_set(pool + ['static-mapping', 'dupe2', 'mac', '00:50:00:00:00:10'])
- self.cli_set(pool + ['static-mapping', 'dupe2', 'ip-address', inc_ip(subnet, 120)])
+ self.cli_set(
+ pool + ['static-mapping', 'dupe2', 'ip-address', inc_ip(subnet, 120)]
+ )
with self.assertRaises(ConfigSessionError):
self.cli_commit()
self.cli_delete(pool + ['static-mapping', 'dupe2'])
-
# cannot have mappings with duplicate MAC addresses
- self.cli_set(pool + ['static-mapping', 'dupe3', 'duid', '00:01:02:03:04:05:06:07:aa:aa:aa:aa:aa:01'])
- self.cli_set(pool + ['static-mapping', 'dupe3', 'ip-address', inc_ip(subnet, 121)])
- self.cli_set(pool + ['static-mapping', 'dupe4', 'duid', '00:01:02:03:04:05:06:07:aa:aa:aa:aa:aa:01'])
- self.cli_set(pool + ['static-mapping', 'dupe4', 'ip-address', inc_ip(subnet, 121)])
+ self.cli_set(
+ pool
+ + [
+ 'static-mapping',
+ 'dupe3',
+ 'duid',
+ '00:01:02:03:04:05:06:07:aa:aa:aa:aa:aa:01',
+ ]
+ )
+ self.cli_set(
+ pool + ['static-mapping', 'dupe3', 'ip-address', inc_ip(subnet, 121)]
+ )
+ self.cli_set(
+ pool
+ + [
+ 'static-mapping',
+ 'dupe4',
+ 'duid',
+ '00:01:02:03:04:05:06:07:aa:aa:aa:aa:aa:01',
+ ]
+ )
+ self.cli_set(
+ pool + ['static-mapping', 'dupe4', 'ip-address', inc_ip(subnet, 121)]
+ )
with self.assertRaises(ConfigSessionError):
self.cli_commit()
self.cli_delete(pool + ['static-mapping', 'dupe3'])
@@ -418,25 +568,38 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase):
config = read_file(KEA4_CONF)
obj = loads(config)
- 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'], 'valid-lifetime', 86400)
- self.verify_config_value(obj, ['Dhcp4', 'shared-networks', 0, 'subnet4'], 'max-valid-lifetime', 86400)
+ 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'], 'valid-lifetime', 86400
+ )
+ self.verify_config_value(
+ obj, ['Dhcp4', 'shared-networks', 0, 'subnet4'], 'max-valid-lifetime', 86400
+ )
# Verify options
self.verify_config_object(
- obj,
- ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'],
- {'name': 'domain-name', 'data': domain_name})
+ 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}'})
+ 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})
+ obj,
+ ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'],
+ {'name': 'routers', 'data': router},
+ )
client_base = 10
for client in ['client1', 'client2', 'client3']:
@@ -444,14 +607,15 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase):
ip = inc_ip(subnet, client_base)
self.verify_config_object(
- obj,
- ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'reservations'],
- {'hostname': client, 'hw-address': mac, 'ip-address': ip})
+ obj,
+ ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'reservations'],
+ {'hostname': client, 'hw-address': mac, 'ip-address': ip},
+ )
client_base += 1
# Check for running process
- self.assertTrue(process_named_running(PROCESS_NAME))
+ self.verify_service_running()
def test_dhcp_multiple_pools(self):
lease_time = '14400'
@@ -463,11 +627,16 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase):
dns_1 = inc_ip(subnet, 2)
range_0_start = inc_ip(subnet, 10)
- range_0_stop = inc_ip(subnet, 20)
+ range_0_stop = inc_ip(subnet, 20)
range_1_start = inc_ip(subnet, 30)
- range_1_stop = inc_ip(subnet, 40)
-
- pool = base_path + ['shared-network-name', shared_net_name, 'subnet', subnet]
+ range_1_stop = inc_ip(subnet, 40)
+
+ pool = base_path + [
+ 'shared-network-name',
+ shared_net_name,
+ 'subnet',
+ subnet,
+ ]
self.cli_set(pool + ['subnet-id', str(int(network) + 1)])
# we use the first subnet IP address as default gateway
self.cli_set(pool + ['option', 'default-router', router])
@@ -484,7 +653,15 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase):
for client in ['client1', 'client2', 'client3', 'client4']:
mac = '02:50:00:00:00:{}'.format(client_base)
self.cli_set(pool + ['static-mapping', client, 'mac', mac])
- self.cli_set(pool + ['static-mapping', client, 'ip-address', inc_ip(subnet, client_base)])
+ self.cli_set(
+ pool
+ + [
+ 'static-mapping',
+ client,
+ 'ip-address',
+ inc_ip(subnet, client_base),
+ ]
+ )
client_base += 1
# commit changes
@@ -500,37 +677,64 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase):
dns_1 = inc_ip(subnet, 2)
range_0_start = inc_ip(subnet, 10)
- range_0_stop = inc_ip(subnet, 20)
+ range_0_stop = inc_ip(subnet, 20)
range_1_start = inc_ip(subnet, 30)
- range_1_stop = inc_ip(subnet, 40)
+ range_1_stop = inc_ip(subnet, 40)
- self.verify_config_value(obj, ['Dhcp4', 'shared-networks'], 'name', shared_net_name)
- self.verify_config_value(obj, ['Dhcp4', 'shared-networks', int(network), 'subnet4'], 'subnet', subnet)
- self.verify_config_value(obj, ['Dhcp4', 'shared-networks', int(network), 'subnet4'], 'id', int(network) + 1)
- self.verify_config_value(obj, ['Dhcp4', 'shared-networks', int(network), 'subnet4'], 'valid-lifetime', int(lease_time))
- self.verify_config_value(obj, ['Dhcp4', 'shared-networks', int(network), 'subnet4'], 'max-valid-lifetime', int(lease_time))
+ self.verify_config_value(
+ obj, ['Dhcp4', 'shared-networks'], 'name', shared_net_name
+ )
+ self.verify_config_value(
+ obj,
+ ['Dhcp4', 'shared-networks', int(network), 'subnet4'],
+ 'subnet',
+ subnet,
+ )
+ self.verify_config_value(
+ obj,
+ ['Dhcp4', 'shared-networks', int(network), 'subnet4'],
+ 'id',
+ int(network) + 1,
+ )
+ self.verify_config_value(
+ obj,
+ ['Dhcp4', 'shared-networks', int(network), 'subnet4'],
+ 'valid-lifetime',
+ int(lease_time),
+ )
+ self.verify_config_value(
+ obj,
+ ['Dhcp4', 'shared-networks', int(network), 'subnet4'],
+ 'max-valid-lifetime',
+ int(lease_time),
+ )
self.verify_config_object(
- obj,
- ['Dhcp4', 'shared-networks', int(network), 'subnet4', 0, 'option-data'],
- {'name': 'domain-name', 'data': domain_name})
+ obj,
+ ['Dhcp4', 'shared-networks', int(network), 'subnet4', 0, 'option-data'],
+ {'name': 'domain-name', 'data': domain_name},
+ )
self.verify_config_object(
- obj,
- ['Dhcp4', 'shared-networks', int(network), 'subnet4', 0, 'option-data'],
- {'name': 'domain-name-servers', 'data': dns_1})
+ obj,
+ ['Dhcp4', 'shared-networks', int(network), 'subnet4', 0, 'option-data'],
+ {'name': 'domain-name-servers', 'data': dns_1},
+ )
self.verify_config_object(
- obj,
- ['Dhcp4', 'shared-networks', int(network), 'subnet4', 0, 'option-data'],
- {'name': 'routers', 'data': router})
+ obj,
+ ['Dhcp4', 'shared-networks', int(network), 'subnet4', 0, 'option-data'],
+ {'name': 'routers', 'data': router},
+ )
self.verify_config_object(
- obj,
- ['Dhcp4', 'shared-networks', int(network), 'subnet4', 0, 'pools'],
- {'pool': f'{range_0_start} - {range_0_stop}'})
+ obj,
+ ['Dhcp4', 'shared-networks', int(network), 'subnet4', 0, 'pools'],
+ {'pool': f'{range_0_start} - {range_0_stop}'},
+ )
self.verify_config_object(
- obj,
- ['Dhcp4', 'shared-networks', int(network), 'subnet4', 0, 'pools'],
- {'pool': f'{range_1_start} - {range_1_stop}'})
+ obj,
+ ['Dhcp4', 'shared-networks', int(network), 'subnet4', 0, 'pools'],
+ {'pool': f'{range_1_start} - {range_1_stop}'},
+ )
client_base = 60
for client in ['client1', 'client2', 'client3', 'client4']:
@@ -538,20 +742,28 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase):
ip = inc_ip(subnet, client_base)
self.verify_config_object(
- obj,
- ['Dhcp4', 'shared-networks', int(network), 'subnet4', 0, 'reservations'],
- {'hostname': client, 'hw-address': mac, 'ip-address': ip})
+ obj,
+ [
+ 'Dhcp4',
+ 'shared-networks',
+ int(network),
+ 'subnet4',
+ 0,
+ 'reservations',
+ ],
+ {'hostname': client, 'hw-address': mac, 'ip-address': ip},
+ )
client_base += 1
# Check for running process
- self.assertTrue(process_named_running(PROCESS_NAME))
+ self.verify_service_running()
def test_dhcp_exclude_not_in_range(self):
# T3180: verify else path when slicing DHCP ranges and exclude address
# is not part of the DHCP range
range_0_start = inc_ip(subnet, 10)
- range_0_stop = inc_ip(subnet, 20)
+ range_0_stop = inc_ip(subnet, 20)
pool = base_path + ['shared-network-name', 'EXCLUDE-TEST', 'subnet', subnet]
self.cli_set(pool + ['subnet-id', '1'])
@@ -567,38 +779,42 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase):
config = read_file(KEA4_CONF)
obj = loads(config)
- self.verify_config_value(obj, ['Dhcp4', 'shared-networks'], 'name', 'EXCLUDE-TEST')
- self.verify_config_value(obj, ['Dhcp4', 'shared-networks', 0, 'subnet4'], 'subnet', subnet)
+ self.verify_config_value(
+ obj, ['Dhcp4', 'shared-networks'], 'name', 'EXCLUDE-TEST'
+ )
+ self.verify_config_value(
+ obj, ['Dhcp4', 'shared-networks', 0, 'subnet4'], 'subnet', subnet
+ )
pool_obj = {
'pool': f'{range_0_start} - {range_0_stop}',
- 'option-data': [{'name': 'routers', 'data': router}]
+ 'option-data': [{'name': 'routers', 'data': router}],
}
# Verify options
self.verify_config_object(
- obj,
- ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'],
- {'name': 'routers', 'data': router})
+ 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_obj)
+ obj, ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'pools'], pool_obj
+ )
# Check for running process
- self.assertTrue(process_named_running(PROCESS_NAME))
+ self.verify_service_running()
def test_dhcp_exclude_in_range(self):
# T3180: verify else path when slicing DHCP ranges and exclude address
# is not part of the DHCP range
range_0_start = inc_ip(subnet, 10)
- range_0_stop = inc_ip(subnet, 100)
+ range_0_stop = inc_ip(subnet, 100)
# the DHCP exclude addresse is blanked out of the range which is done
# by slicing one range into two ranges
- exclude_addr = inc_ip(range_0_start, 20)
+ exclude_addr = inc_ip(range_0_start, 20)
range_0_stop_excl = dec_ip(exclude_addr, 1)
range_0_start_excl = inc_ip(exclude_addr, 1)
@@ -616,37 +832,42 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase):
config = read_file(KEA4_CONF)
obj = loads(config)
- self.verify_config_value(obj, ['Dhcp4', 'shared-networks'], 'name', 'EXCLUDE-TEST-2')
- self.verify_config_value(obj, ['Dhcp4', 'shared-networks', 0, 'subnet4'], 'subnet', subnet)
+ self.verify_config_value(
+ obj, ['Dhcp4', 'shared-networks'], 'name', 'EXCLUDE-TEST-2'
+ )
+ self.verify_config_value(
+ obj, ['Dhcp4', 'shared-networks', 0, 'subnet4'], 'subnet', subnet
+ )
pool_obj = {
'pool': f'{range_0_start} - {range_0_stop_excl}',
- 'option-data': [{'name': 'routers', 'data': router}]
+ 'option-data': [{'name': 'routers', 'data': router}],
}
pool_exclude_obj = {
'pool': f'{range_0_start_excl} - {range_0_stop}',
- 'option-data': [{'name': 'routers', 'data': router}]
+ 'option-data': [{'name': 'routers', 'data': router}],
}
# Verify options
self.verify_config_object(
- obj,
- ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'],
- {'name': 'routers', 'data': router})
+ obj,
+ ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'],
+ {'name': 'routers', 'data': router},
+ )
self.verify_config_object(
- obj,
- ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'pools'],
- pool_obj)
+ obj, ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'pools'], pool_obj
+ )
self.verify_config_object(
- obj,
- ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'pools'],
- pool_exclude_obj)
+ obj,
+ ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'pools'],
+ pool_exclude_obj,
+ )
# Check for running process
- self.assertTrue(process_named_running(PROCESS_NAME))
+ self.verify_service_running()
def test_dhcp_relay_server(self):
# Listen on specific address and return DHCP leases from a non
@@ -657,7 +878,7 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase):
relay_router = inc_ip(relay_subnet, 1)
range_0_start = '10.0.1.0'
- range_0_stop = '10.0.250.255'
+ range_0_stop = '10.0.250.255'
pool = base_path + ['shared-network-name', 'RELAY', 'subnet', relay_subnet]
self.cli_set(pool + ['subnet-id', '1'])
@@ -671,31 +892,37 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase):
config = read_file(KEA4_CONF)
obj = loads(config)
- self.verify_config_value(obj, ['Dhcp4', 'interfaces-config'], 'interfaces', [f'{interface}/{router}'])
+ self.verify_config_value(
+ obj, ['Dhcp4', 'interfaces-config'], 'interfaces', [f'{interface}/{router}']
+ )
self.verify_config_value(obj, ['Dhcp4', 'shared-networks'], 'name', 'RELAY')
- self.verify_config_value(obj, ['Dhcp4', 'shared-networks', 0, 'subnet4'], 'subnet', relay_subnet)
+ self.verify_config_value(
+ obj, ['Dhcp4', 'shared-networks', 0, 'subnet4'], 'subnet', relay_subnet
+ )
# Verify options
self.verify_config_object(
- obj,
- ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'],
- {'name': 'routers', 'data': relay_router})
+ obj,
+ ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'],
+ {'name': 'routers', 'data': relay_router},
+ )
# Verify pools
self.verify_config_object(
- obj,
- ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'pools'],
- {'pool': f'{range_0_start} - {range_0_stop}'})
+ obj,
+ ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'pools'],
+ {'pool': f'{range_0_start} - {range_0_stop}'},
+ )
# Check for running process
- self.assertTrue(process_named_running(PROCESS_NAME))
+ self.verify_service_running()
def test_dhcp_high_availability(self):
shared_net_name = 'FAILOVER'
failover_name = 'VyOS-Failover'
range_0_start = inc_ip(subnet, 10)
- range_0_stop = inc_ip(subnet, 20)
+ range_0_stop = inc_ip(subnet, 20)
pool = base_path + ['shared-network-name', shared_net_name, 'subnet', subnet]
self.cli_set(pool + ['subnet-id', '1'])
@@ -712,7 +939,9 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase):
failover_local = router
failover_remote = inc_ip(router, 1)
- self.cli_set(base_path + ['high-availability', 'source-address', failover_local])
+ self.cli_set(
+ base_path + ['high-availability', 'source-address', failover_local]
+ )
self.cli_set(base_path + ['high-availability', 'name', failover_name])
self.cli_set(base_path + ['high-availability', 'remote', failover_remote])
self.cli_set(base_path + ['high-availability', 'status', 'primary'])
@@ -725,43 +954,78 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase):
obj = loads(config)
# Verify failover
- self.verify_config_value(obj, ['Dhcp4', 'control-socket'], 'socket-name', KEA4_CTRL)
+ self.verify_config_value(
+ obj, ['Dhcp4', 'control-socket'], 'socket-name', KEA4_CTRL
+ )
self.verify_config_object(
obj,
- ['Dhcp4', 'hooks-libraries', 0, 'parameters', 'high-availability', 0, 'peers'],
- {'name': os.uname()[1], 'url': f'http://{failover_local}:647/', 'role': 'primary', 'auto-failover': True})
+ [
+ 'Dhcp4',
+ 'hooks-libraries',
+ 0,
+ 'parameters',
+ 'high-availability',
+ 0,
+ 'peers',
+ ],
+ {
+ 'name': os.uname()[1],
+ 'url': f'http://{failover_local}:647/',
+ 'role': 'primary',
+ 'auto-failover': True,
+ },
+ )
self.verify_config_object(
obj,
- ['Dhcp4', 'hooks-libraries', 0, 'parameters', 'high-availability', 0, 'peers'],
- {'name': failover_name, 'url': f'http://{failover_remote}:647/', 'role': 'secondary', 'auto-failover': True})
-
- self.verify_config_value(obj, ['Dhcp4', 'shared-networks'], 'name', shared_net_name)
- self.verify_config_value(obj, ['Dhcp4', 'shared-networks', 0, 'subnet4'], 'subnet', subnet)
+ [
+ 'Dhcp4',
+ 'hooks-libraries',
+ 0,
+ 'parameters',
+ 'high-availability',
+ 0,
+ 'peers',
+ ],
+ {
+ 'name': failover_name,
+ 'url': f'http://{failover_remote}:647/',
+ 'role': 'secondary',
+ 'auto-failover': True,
+ },
+ )
+
+ self.verify_config_value(
+ obj, ['Dhcp4', 'shared-networks'], 'name', shared_net_name
+ )
+ self.verify_config_value(
+ obj, ['Dhcp4', 'shared-networks', 0, 'subnet4'], 'subnet', subnet
+ )
# Verify options
self.verify_config_object(
- obj,
- ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'],
- {'name': 'routers', 'data': router})
+ 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}'})
+ obj,
+ ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'pools'],
+ {'pool': f'{range_0_start} - {range_0_stop}'},
+ )
# Check for running process
- self.assertTrue(process_named_running(PROCESS_NAME))
- self.assertTrue(process_named_running(CTRL_PROCESS_NAME))
+ self.verify_service_running()
def test_dhcp_high_availability_standby(self):
shared_net_name = 'FAILOVER'
failover_name = 'VyOS-Failover'
range_0_start = inc_ip(subnet, 10)
- range_0_stop = inc_ip(subnet, 20)
+ range_0_stop = inc_ip(subnet, 20)
pool = base_path + ['shared-network-name', shared_net_name, 'subnet', subnet]
self.cli_set(pool + ['subnet-id', '1'])
@@ -774,7 +1038,9 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase):
failover_local = router
failover_remote = inc_ip(router, 1)
- self.cli_set(base_path + ['high-availability', 'source-address', failover_local])
+ self.cli_set(
+ base_path + ['high-availability', 'source-address', failover_local]
+ )
self.cli_set(base_path + ['high-availability', 'name', failover_name])
self.cli_set(base_path + ['high-availability', 'remote', failover_remote])
self.cli_set(base_path + ['high-availability', 'status', 'secondary'])
@@ -787,61 +1053,383 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase):
obj = loads(config)
# Verify failover
- self.verify_config_value(obj, ['Dhcp4', 'control-socket'], 'socket-name', KEA4_CTRL)
+ self.verify_config_value(
+ obj, ['Dhcp4', 'control-socket'], 'socket-name', KEA4_CTRL
+ )
self.verify_config_object(
obj,
- ['Dhcp4', 'hooks-libraries', 0, 'parameters', 'high-availability', 0, 'peers'],
- {'name': os.uname()[1], 'url': f'http://{failover_local}:647/', 'role': 'standby', 'auto-failover': True})
+ [
+ 'Dhcp4',
+ 'hooks-libraries',
+ 0,
+ 'parameters',
+ 'high-availability',
+ 0,
+ 'peers',
+ ],
+ {
+ 'name': os.uname()[1],
+ 'url': f'http://{failover_local}:647/',
+ 'role': 'standby',
+ 'auto-failover': True,
+ },
+ )
self.verify_config_object(
obj,
- ['Dhcp4', 'hooks-libraries', 0, 'parameters', 'high-availability', 0, 'peers'],
- {'name': failover_name, 'url': f'http://{failover_remote}:647/', 'role': 'primary', 'auto-failover': True})
+ [
+ 'Dhcp4',
+ 'hooks-libraries',
+ 0,
+ 'parameters',
+ 'high-availability',
+ 0,
+ 'peers',
+ ],
+ {
+ 'name': failover_name,
+ 'url': f'http://{failover_remote}:647/',
+ 'role': 'primary',
+ 'auto-failover': True,
+ },
+ )
+
+ self.verify_config_value(
+ obj, ['Dhcp4', 'shared-networks'], 'name', shared_net_name
+ )
+ self.verify_config_value(
+ obj, ['Dhcp4', 'shared-networks', 0, 'subnet4'], 'subnet', subnet
+ )
+
+ # Verify options
+ 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}'},
+ )
+
+ # Check for running process
+ self.verify_service_running()
+
+ def test_dhcp_dynamic_dns_update(self):
+ shared_net_name = 'SMOKE-1DDNS'
+
+ range_0_start = inc_ip(subnet, 10)
+ range_0_stop = inc_ip(subnet, 20)
+
+ self.cli_set(base_path + ['listen-interface', interface])
+
+ ddns = base_path + ['dynamic-dns-update']
+
+ self.cli_set(ddns + ['send-updates', 'enable'])
+ self.cli_set(ddns + ['conflict-resolution', 'enable'])
+ self.cli_set(ddns + ['override-no-update', 'enable'])
+ self.cli_set(ddns + ['override-client-update', 'enable'])
+ self.cli_set(ddns + ['replace-client-name', 'always'])
+ self.cli_set(ddns + ['update-on-renew', 'enable'])
+
+ self.cli_set(ddns + ['tsig-key', 'domain-lan-updates', 'algorithm', 'sha256'])
+ self.cli_set(ddns + ['tsig-key', 'domain-lan-updates', 'secret', 'SXQncyBXZWRuZXNkYXkgbWFoIGR1ZGVzIQ=='])
+ self.cli_set(ddns + ['tsig-key', 'reverse-0-168-192', 'algorithm', 'sha256'])
+ self.cli_set(ddns + ['tsig-key', 'reverse-0-168-192', 'secret', 'VGhhbmsgR29kIGl0J3MgRnJpZGF5IQ=='])
+ self.cli_set(ddns + ['forward-domain', 'domain.lan', 'dns-server', '1', 'address', '192.168.0.1'])
+ self.cli_set(ddns + ['forward-domain', 'domain.lan', 'dns-server', '2', 'address', '100.100.0.1'])
+ self.cli_set(ddns + ['forward-domain', 'domain.lan', 'key-name', 'domain-lan-updates'])
+ self.cli_set(ddns + ['reverse-domain', '0.168.192.in-addr.arpa', 'dns-server', '1', 'address', '192.168.0.1'])
+ self.cli_set(ddns + ['reverse-domain', '0.168.192.in-addr.arpa', 'dns-server', '1', 'port', '1053'])
+ self.cli_set(ddns + ['reverse-domain', '0.168.192.in-addr.arpa', 'dns-server', '2', 'address', '100.100.0.1'])
+ self.cli_set(ddns + ['reverse-domain', '0.168.192.in-addr.arpa', 'dns-server', '2', 'port', '1153'])
+ self.cli_set(ddns + ['reverse-domain', '0.168.192.in-addr.arpa', 'key-name', 'reverse-0-168-192'])
+
+ shared = base_path + ['shared-network-name', shared_net_name]
+
+ self.cli_set(shared + ['dynamic-dns-update', 'send-updates', 'enable'])
+ self.cli_set(shared + ['dynamic-dns-update', 'conflict-resolution', 'enable'])
+ self.cli_set(shared + ['dynamic-dns-update', 'ttl-percent', '75'])
+
+ pool = shared + [ 'subnet', subnet]
+ self.cli_set(pool + ['subnet-id', '1'])
+
+ self.cli_set(pool + ['range', '0', 'start', range_0_start])
+ self.cli_set(pool + ['range', '0', 'stop', range_0_stop])
+
+ self.cli_set(pool + ['dynamic-dns-update', 'send-updates', 'enable'])
+ self.cli_set(pool + ['dynamic-dns-update', 'generated-prefix', 'myfunnyprefix'])
+ self.cli_set(pool + ['dynamic-dns-update', 'qualifying-suffix', 'suffix.lan'])
+ self.cli_set(pool + ['dynamic-dns-update', 'hostname-char-set', 'xXyYzZ'])
+ self.cli_set(pool + ['dynamic-dns-update', 'hostname-char-replacement', '_xXx_'])
+
+ self.cli_commit()
+
+ config = read_file(KEA4_CONF)
+ d2_config = read_file(KEA4_D2_CONF)
+
+ obj = loads(config)
+ d2_obj = loads(d2_config)
+
+ # Verify global DDNS parameters in the main config file
+ self.verify_config_value(
+ obj,
+ ['Dhcp4'], 'dhcp-ddns',
+ {'enable-updates': True, 'server-ip': '127.0.0.1', 'server-port': 53001, 'sender-ip': '', 'sender-port': 0,
+ 'max-queue-size': 1024, 'ncr-protocol': 'UDP', 'ncr-format': 'JSON'})
+
+ self.verify_config_value(obj, ['Dhcp4'], 'ddns-send-updates', True)
+ self.verify_config_value(obj, ['Dhcp4'], 'ddns-use-conflict-resolution', True)
+ self.verify_config_value(obj, ['Dhcp4'], 'ddns-override-no-update', True)
+ self.verify_config_value(obj, ['Dhcp4'], 'ddns-override-client-update', True)
+ self.verify_config_value(obj, ['Dhcp4'], 'ddns-replace-client-name', 'always')
+ self.verify_config_value(obj, ['Dhcp4'], 'ddns-update-on-renew', True)
+
+ # Verify scoped DDNS parameters in the main config file
self.verify_config_value(obj, ['Dhcp4', 'shared-networks'], 'name', shared_net_name)
+ self.verify_config_value(obj, ['Dhcp4', 'shared-networks'], 'ddns-send-updates', True)
+ self.verify_config_value(obj, ['Dhcp4', 'shared-networks'], 'ddns-use-conflict-resolution', True)
+ self.verify_config_value(obj, ['Dhcp4', 'shared-networks'], 'ddns-ttl-percent', 0.75)
+
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'], 'ddns-send-updates', True)
+ self.verify_config_value(obj, ['Dhcp4', 'shared-networks', 0, 'subnet4'], 'ddns-generated-prefix', 'myfunnyprefix')
+ self.verify_config_value(obj, ['Dhcp4', 'shared-networks', 0, 'subnet4'], 'ddns-qualifying-suffix', 'suffix.lan')
+ self.verify_config_value(obj, ['Dhcp4', 'shared-networks', 0, 'subnet4'], 'hostname-char-set', 'xXyYzZ')
+ self.verify_config_value(obj, ['Dhcp4', 'shared-networks', 0, 'subnet4'], 'hostname-char-replacement', '_xXx_')
- # Verify options
+ # Verify keys and domains configuration in the D2 config
self.verify_config_object(
- obj,
- ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'],
- {'name': 'routers', 'data': router})
+ d2_obj,
+ ['DhcpDdns', 'tsig-keys'],
+ {'name': 'domain-lan-updates', 'algorithm': 'HMAC-SHA256', 'secret': 'SXQncyBXZWRuZXNkYXkgbWFoIGR1ZGVzIQ=='}
+ )
+ self.verify_config_object(
+ d2_obj,
+ ['DhcpDdns', 'tsig-keys'],
+ {'name': 'reverse-0-168-192', 'algorithm': 'HMAC-SHA256', 'secret': 'VGhhbmsgR29kIGl0J3MgRnJpZGF5IQ=='}
+ )
- # Verify pools
+ self.verify_config_value(d2_obj, ['DhcpDdns', 'forward-ddns', 'ddns-domains', 0], 'name', 'domain.lan')
+ self.verify_config_value(d2_obj, ['DhcpDdns', 'forward-ddns', 'ddns-domains', 0], 'key-name', 'domain-lan-updates')
self.verify_config_object(
- obj,
- ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'pools'],
- {'pool': f'{range_0_start} - {range_0_stop}'})
+ d2_obj,
+ ['DhcpDdns', 'forward-ddns', 'ddns-domains', 0, 'dns-servers'],
+ {'ip-address': '192.168.0.1'}
+ )
+ self.verify_config_object(
+ d2_obj,
+ ['DhcpDdns', 'forward-ddns', 'ddns-domains', 0, 'dns-servers'],
+ {'ip-address': '100.100.0.1'}
+ )
+
+ self.verify_config_value(d2_obj, ['DhcpDdns', 'reverse-ddns', 'ddns-domains', 0], 'name', '0.168.192.in-addr.arpa')
+ self.verify_config_value(d2_obj, ['DhcpDdns', 'reverse-ddns', 'ddns-domains', 0], 'key-name', 'reverse-0-168-192')
+ self.verify_config_object(
+ d2_obj,
+ ['DhcpDdns', 'reverse-ddns', 'ddns-domains', 0, 'dns-servers'],
+ {'ip-address': '192.168.0.1', 'port': 1053}
+ )
+ self.verify_config_object(
+ d2_obj,
+ ['DhcpDdns', 'reverse-ddns', 'ddns-domains', 0, 'dns-servers'],
+ {'ip-address': '100.100.0.1', 'port': 1153}
+ )
# Check for running process
self.assertTrue(process_named_running(PROCESS_NAME))
- self.assertTrue(process_named_running(CTRL_PROCESS_NAME))
+ self.assertTrue(process_named_running(D2_PROCESS_NAME))
def test_dhcp_on_interface_with_vrf(self):
self.cli_set(['interfaces', 'ethernet', 'eth1', 'address', '10.1.1.1/30'])
self.cli_set(['interfaces', 'ethernet', 'eth1', 'vrf', 'SMOKE-DHCP'])
- self.cli_set(['protocols', 'static', 'route', '10.1.10.0/24', 'interface', 'eth1', 'vrf', 'SMOKE-DHCP'])
- self.cli_set(['vrf', 'name', 'SMOKE-DHCP', 'protocols', 'static', 'route', '10.1.10.0/24', 'next-hop', '10.1.1.2'])
+ self.cli_set(
+ [
+ 'protocols',
+ 'static',
+ 'route',
+ '10.1.10.0/24',
+ 'interface',
+ 'eth1',
+ 'vrf',
+ 'SMOKE-DHCP',
+ ]
+ )
+ self.cli_set(
+ [
+ 'vrf',
+ 'name',
+ 'SMOKE-DHCP',
+ 'protocols',
+ 'static',
+ 'route',
+ '10.1.10.0/24',
+ 'next-hop',
+ '10.1.1.2',
+ ]
+ )
self.cli_set(['vrf', 'name', 'SMOKE-DHCP', 'table', '1000'])
- self.cli_set(base_path + ['shared-network-name', 'SMOKE-DHCP-NETWORK', 'subnet', '10.1.10.0/24', 'subnet-id', '1'])
- self.cli_set(base_path + ['shared-network-name', 'SMOKE-DHCP-NETWORK', 'subnet', '10.1.10.0/24', 'option', 'default-router', '10.1.10.1'])
- self.cli_set(base_path + ['shared-network-name', 'SMOKE-DHCP-NETWORK', 'subnet', '10.1.10.0/24', 'option', 'name-server', '1.1.1.1'])
- self.cli_set(base_path + ['shared-network-name', 'SMOKE-DHCP-NETWORK', 'subnet', '10.1.10.0/24', 'range', '1', 'start', '10.1.10.10'])
- self.cli_set(base_path + ['shared-network-name', 'SMOKE-DHCP-NETWORK', 'subnet', '10.1.10.0/24', 'range', '1', 'stop', '10.1.10.20'])
+ self.cli_set(
+ base_path
+ + [
+ 'shared-network-name',
+ 'SMOKE-DHCP-NETWORK',
+ 'subnet',
+ '10.1.10.0/24',
+ 'subnet-id',
+ '1',
+ ]
+ )
+ self.cli_set(
+ base_path
+ + [
+ 'shared-network-name',
+ 'SMOKE-DHCP-NETWORK',
+ 'subnet',
+ '10.1.10.0/24',
+ 'option',
+ 'default-router',
+ '10.1.10.1',
+ ]
+ )
+ self.cli_set(
+ base_path
+ + [
+ 'shared-network-name',
+ 'SMOKE-DHCP-NETWORK',
+ 'subnet',
+ '10.1.10.0/24',
+ 'option',
+ 'name-server',
+ '1.1.1.1',
+ ]
+ )
+ self.cli_set(
+ base_path
+ + [
+ 'shared-network-name',
+ 'SMOKE-DHCP-NETWORK',
+ 'subnet',
+ '10.1.10.0/24',
+ 'range',
+ '1',
+ 'start',
+ '10.1.10.10',
+ ]
+ )
+ self.cli_set(
+ base_path
+ + [
+ 'shared-network-name',
+ 'SMOKE-DHCP-NETWORK',
+ 'subnet',
+ '10.1.10.0/24',
+ 'range',
+ '1',
+ 'stop',
+ '10.1.10.20',
+ ]
+ )
self.cli_set(base_path + ['listen-address', '10.1.1.1'])
self.cli_commit()
config = read_file(KEA4_CONF)
obj = loads(config)
- self.verify_config_value(obj, ['Dhcp4', 'interfaces-config'], 'interfaces', ['eth1/10.1.1.1'])
+ self.verify_config_value(
+ obj, ['Dhcp4', 'interfaces-config'], 'interfaces', ['eth1/10.1.1.1']
+ )
self.cli_delete(['interfaces', 'ethernet', 'eth1', 'vrf', 'SMOKE-DHCP'])
- self.cli_delete(['protocols', 'static', 'route', '10.1.10.0/24', 'interface', 'eth1', 'vrf'])
+ self.cli_delete(
+ ['protocols', 'static', 'route', '10.1.10.0/24', 'interface', 'eth1', 'vrf']
+ )
self.cli_delete(['vrf', 'name', 'SMOKE-DHCP'])
self.cli_commit()
+ def test_dhcp_hostsd_lease_sync(self):
+ shared_net_name = 'SMOKE-LEASE-SYNC'
+ domain_name = 'sync.private'
+
+ client_range = range(1, 4)
+ subnet_range_start = inc_ip(subnet, 10)
+ subnet_range_stop = inc_ip(subnet, 20)
+
+ def internal_cleanup():
+ for seq in client_range:
+ ip_addr = inc_ip(subnet, seq)
+ kea_delete_lease(4, ip_addr)
+ cmd(
+ f'{HOSTSD_CLIENT} --delete-hosts --tag dhcp-server-{ip_addr} --apply'
+ )
+
+ self.addClassCleanup(internal_cleanup)
+
+ pool = base_path + ['shared-network-name', shared_net_name, 'subnet', subnet]
+ self.cli_set(pool + ['subnet-id', '1'])
+ self.cli_set(pool + ['option', 'domain-name', domain_name])
+ self.cli_set(pool + ['range', '0', 'start', subnet_range_start])
+ self.cli_set(pool + ['range', '0', 'stop', subnet_range_stop])
+
+ # commit changes
+ self.cli_commit()
+
+ config = read_file(KEA4_CONF)
+ obj = loads(config)
+
+ 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
+ )
+
+ # 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, 'pools'],
+ {'pool': f'{subnet_range_start} - {subnet_range_stop}'},
+ )
+
+ # Check for running process
+ self.verify_service_running()
+
+ # All up and running, now test vyos-hostsd store
+
+ # 1. Inject leases into kea
+ for seq in client_range:
+ client = f'client{seq}'
+ mac = f'00:50:00:00:00:{seq:02}'
+ ip = inc_ip(subnet, seq)
+ kea_add_lease(4, ip, host_name=client, mac_address=mac)
+
+ # 2. Verify that leases are not available in vyos-hostsd
+ tag_regex = re.escape(f'dhcp-server-{subnet.rsplit(".", 1)[0]}')
+ host_json = cmd(f'{HOSTSD_CLIENT} --get-hosts {tag_regex}')
+ self.assertFalse(host_json.strip('{}'))
+
+ # 3. Restart the service to trigger vyos-hostsd sync and wait for it to start
+ self.assertTrue(process_named_running(PROCESS_NAME, timeout=30))
+
+ # 4. Verify that leases are synced and available in vyos-hostsd
+ tag_regex = re.escape(f'dhcp-server-{subnet.rsplit(".", 1)[0]}')
+ host_json = cmd(f'{HOSTSD_CLIENT} --get-hosts {tag_regex}')
+ self.assertTrue(host_json)
+
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_service_dhcpv6-server.py b/smoketest/scripts/cli/test_service_dhcpv6-server.py
index 6ecf6c1cf..6535ca72d 100755
--- a/smoketest/scripts/cli/test_service_dhcpv6-server.py
+++ b/smoketest/scripts/cli/test_service_dhcpv6-server.py
@@ -108,6 +108,7 @@ class TestServiceDHCPv6Server(VyOSUnitTestSHIM.TestCase):
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])
@@ -157,6 +158,10 @@ class TestServiceDHCPv6Server(VyOSUnitTestSHIM.TestCase):
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,
diff --git a/smoketest/scripts/cli/test_service_dns_dynamic.py b/smoketest/scripts/cli/test_service_dns_dynamic.py
index 28380f6d4..522102e67 100755
--- a/smoketest/scripts/cli/test_service_dns_dynamic.py
+++ b/smoketest/scripts/cli/test_service_dns_dynamic.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2019-2024 VyOS maintainers and contributors
+# Copyright (C) 2019-2025 VyOS maintainers and contributors
#
# 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
@@ -20,8 +20,10 @@ import tempfile
from base_vyostest_shim import VyOSUnitTestSHIM
from vyos.configsession import ConfigSessionError
+from vyos.utils.file import read_file
from vyos.utils.process import cmd
from vyos.utils.process import process_named_running
+from vyos.xml_ref import default_value
DDCLIENT_SYSTEMD_UNIT = '/run/systemd/system/ddclient.service.d/override.conf'
DDCLIENT_CONF = '/run/ddclient/ddclient.conf'
@@ -29,6 +31,7 @@ DDCLIENT_PNAME = 'ddclient'
base_path = ['service', 'dns', 'dynamic']
name_path = base_path + ['name']
+default_interval = default_value(base_path + ['interval'])
server = 'ddns.vyos.io'
hostname = 'test.ddns.vyos.io'
zone = 'vyos.io'
@@ -38,13 +41,17 @@ ttl = '300'
interface = 'eth0'
class TestServiceDDNS(VyOSUnitTestSHIM.TestCase):
- def setUp(self):
- # Always start with a clean CLI instance
- self.cli_delete(base_path)
+ @classmethod
+ def setUpClass(cls):
+ super(TestServiceDDNS, 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)
def tearDown(self):
# Check for running process
- self.assertTrue(process_named_running(DDCLIENT_PNAME, timeout=5))
+ self.assertTrue(process_named_running(DDCLIENT_PNAME, timeout=10))
# Delete DDNS configuration
self.cli_delete(base_path)
@@ -91,12 +98,14 @@ class TestServiceDDNS(VyOSUnitTestSHIM.TestCase):
# Check the generating config parameters
ddclient_conf = cmd(f'sudo cat {DDCLIENT_CONF}')
- # default value 300 seconds
- self.assertIn(f'daemon=300', ddclient_conf)
self.assertIn(f'usev4=ifv4', ddclient_conf)
self.assertIn(f'ifv4={interface}', ddclient_conf)
self.assertIn(f'password=\'{password}\'', ddclient_conf)
+ # Check default interval of 300 seconds
+ systemd_override = read_file(DDCLIENT_SYSTEMD_UNIT)
+ self.assertIn(f'--daemon {default_interval}', systemd_override)
+
for opt in details.keys():
if opt == 'username':
login = details[opt]
@@ -136,7 +145,6 @@ class TestServiceDDNS(VyOSUnitTestSHIM.TestCase):
# Check the generating config parameters
ddclient_conf = cmd(f'sudo cat {DDCLIENT_CONF}')
- self.assertIn(f'daemon={interval}', ddclient_conf)
self.assertIn(f'usev6=ifv6', ddclient_conf)
self.assertIn(f'ifv6={interface}', ddclient_conf)
self.assertIn(f'protocol={proto}', ddclient_conf)
@@ -146,6 +154,10 @@ class TestServiceDDNS(VyOSUnitTestSHIM.TestCase):
self.assertIn(f'min-interval={wait_time}', ddclient_conf)
self.assertIn(f'max-interval={expiry_time_good}', ddclient_conf)
+ # default value 300 seconds
+ systemd_override = read_file(DDCLIENT_SYSTEMD_UNIT)
+ self.assertIn(f'--daemon {interval}', systemd_override)
+
# IPv4+IPv6 dual DDNS service configuration
def test_03_dyndns_service_dual_stack(self):
services = {'cloudflare': {'protocol': 'cloudflare', 'zone': zone},
@@ -335,9 +347,10 @@ class TestServiceDDNS(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Check for process in VRF
- systemd_override = cmd(f'cat {DDCLIENT_SYSTEMD_UNIT}')
- self.assertIn(f'ExecStart=ip vrf exec {vrf_name} /usr/bin/ddclient -file {DDCLIENT_CONF}',
- systemd_override)
+ systemd_override = read_file(DDCLIENT_SYSTEMD_UNIT)
+ self.assertIn(f'ExecStart=ip vrf exec {vrf_name} /usr/bin/ddclient ' \
+ f'--file {DDCLIENT_CONF} --cache {DDCLIENT_CONF.replace("conf", "cache")} ' \
+ f'--foreground --daemon {default_interval}', systemd_override)
# Check for process in VRF
proc = cmd(f'ip vrf pids {vrf_name}')
diff --git a/smoketest/scripts/cli/test_service_https.py b/smoketest/scripts/cli/test_service_https.py
index 04c4a2e51..b4fe35d81 100755
--- a/smoketest/scripts/cli/test_service_https.py
+++ b/smoketest/scripts/cli/test_service_https.py
@@ -16,6 +16,7 @@
import unittest
import json
+import psutil
from requests import request
from urllib3.exceptions import InsecureRequestWarning
@@ -113,6 +114,29 @@ class TestHTTPSService(VyOSUnitTestSHIM.TestCase):
# Check for stopped process
self.assertFalse(process_named_running(PROCESS_NAME))
+ def test_listen_address(self):
+ test_prefix = ['192.0.2.1/26', '2001:db8:1::ffff/64']
+ test_addr = [ i.split('/')[0] for i in test_prefix ]
+ for i, addr in enumerate(test_prefix):
+ self.cli_set(['interfaces', 'dummy', f'dum{i}', 'address', addr])
+
+ key = 'MySuperSecretVyOS'
+ self.cli_set(base_path + ['api', 'keys', 'id', 'key-01', 'key', key])
+ # commit base config first, for testing update of listen-address
+ self.cli_commit()
+
+ for addr in test_addr:
+ self.cli_set(base_path + ['listen-address', addr])
+ self.cli_commit()
+
+ res = set()
+ t = psutil.net_connections(kind="tcp")
+ for c in t:
+ if c.laddr.port == 443:
+ res.add(c.laddr.ip)
+
+ self.assertEqual(res, set(test_addr))
+
def test_certificate(self):
cert_name = 'test_https'
dh_name = 'dh-test'
diff --git a/smoketest/scripts/cli/test_service_ids_ddos-protection.py b/smoketest/scripts/cli/test_service_ids_ddos-protection.py
deleted file mode 100755
index 91b056eea..000000000
--- a/smoketest/scripts/cli/test_service_ids_ddos-protection.py
+++ /dev/null
@@ -1,116 +0,0 @@
-#!/usr/bin/env python3
-#
-# Copyright (C) 2022 VyOS maintainers and contributors
-#
-# 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 base_vyostest_shim import VyOSUnitTestSHIM
-
-from vyos.configsession import ConfigSessionError
-from vyos.utils.process import process_named_running
-from vyos.utils.file import read_file
-
-PROCESS_NAME = 'fastnetmon'
-FASTNETMON_CONF = '/run/fastnetmon/fastnetmon.conf'
-NETWORKS_CONF = '/run/fastnetmon/networks_list'
-EXCLUDED_NETWORKS_CONF = '/run/fastnetmon/excluded_networks_list'
-base_path = ['service', 'ids', 'ddos-protection']
-
-class TestServiceIDS(VyOSUnitTestSHIM.TestCase):
- @classmethod
- def setUpClass(cls):
- super(TestServiceIDS, 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)
-
- def tearDown(self):
- # Check for running process
- self.assertTrue(process_named_running(PROCESS_NAME))
-
- # delete test config
- self.cli_delete(base_path)
- self.cli_commit()
-
- self.assertFalse(os.path.exists(FASTNETMON_CONF))
- self.assertFalse(process_named_running(PROCESS_NAME))
-
- def test_fastnetmon(self):
- networks = ['10.0.0.0/24', '10.5.5.0/24', '2001:db8:10::/64', '2001:db8:20::/64']
- excluded_networks = ['10.0.0.1/32', '2001:db8:10::1/128']
- interfaces = ['eth0', 'eth1']
- fps = '3500'
- mbps = '300'
- pps = '60000'
-
- self.cli_set(base_path + ['mode', 'mirror'])
- # Required network!
- with self.assertRaises(ConfigSessionError):
- self.cli_commit()
- for tmp in networks:
- self.cli_set(base_path + ['network', tmp])
-
- # optional excluded-network!
- with self.assertRaises(ConfigSessionError):
- self.cli_commit()
- for tmp in excluded_networks:
- self.cli_set(base_path + ['excluded-network', tmp])
-
- # Required interface(s)!
- with self.assertRaises(ConfigSessionError):
- self.cli_commit()
- for tmp in interfaces:
- self.cli_set(base_path + ['listen-interface', tmp])
-
- self.cli_set(base_path + ['direction', 'in'])
- self.cli_set(base_path + ['threshold', 'general', 'fps', fps])
- self.cli_set(base_path + ['threshold', 'general', 'pps', pps])
- self.cli_set(base_path + ['threshold', 'general', 'mbps', mbps])
-
- # commit changes
- self.cli_commit()
-
- # Check configured port
- config = read_file(FASTNETMON_CONF)
- self.assertIn(f'mirror_afpacket = on', config)
- self.assertIn(f'process_incoming_traffic = on', config)
- self.assertIn(f'process_outgoing_traffic = off', config)
- self.assertIn(f'ban_for_flows = on', config)
- self.assertIn(f'threshold_flows = {fps}', config)
- self.assertIn(f'ban_for_bandwidth = on', config)
- self.assertIn(f'threshold_mbps = {mbps}', config)
- self.assertIn(f'ban_for_pps = on', config)
- self.assertIn(f'threshold_pps = {pps}', config)
- # default
- self.assertIn(f'enable_ban = on', config)
- self.assertIn(f'enable_ban_ipv6 = on', config)
- self.assertIn(f'ban_time = 1900', config)
-
- tmp = ','.join(interfaces)
- self.assertIn(f'interfaces = {tmp}', config)
-
-
- network_config = read_file(NETWORKS_CONF)
- for tmp in networks:
- self.assertIn(f'{tmp}', network_config)
-
- excluded_network_config = read_file(EXCLUDED_NETWORKS_CONF)
- for tmp in excluded_networks:
- self.assertIn(f'{tmp}', excluded_network_config)
-
-if __name__ == '__main__':
- unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_service_ipoe-server.py b/smoketest/scripts/cli/test_service_ipoe-server.py
index ab0898d17..3b3c205cd 100755
--- a/smoketest/scripts/cli/test_service_ipoe-server.py
+++ b/smoketest/scripts/cli/test_service_ipoe-server.py
@@ -260,7 +260,7 @@ delegate={delegate_2_prefix},{delegate_mask},name={pool_name}"""
tmp = ','.join(vlans)
self.assertIn(f'{interface},{tmp}', conf['ipoe']['vlan-mon'])
- def test_ipoe_server_static_client_ip(self):
+ def test_ipoe_server_static_client_ip_address(self):
mac_address = '08:00:27:2f:d8:06'
ip_address = '192.0.2.100'
@@ -274,7 +274,7 @@ delegate={delegate_2_prefix},{delegate_mask},name={pool_name}"""
interface,
'mac',
mac_address,
- 'static-ip',
+ 'ip-address',
ip_address,
]
)
@@ -295,6 +295,28 @@ delegate={delegate_2_prefix},{delegate_mask},name={pool_name}"""
tmp = re.findall(regex, tmp)
self.assertTrue(tmp)
+ def test_ipoe_server_start_session(self):
+ start_session = 'auto'
+
+ # Configuration of local authentication for PPPoE server
+ self.basic_config()
+ self.cli_commit()
+
+ # Validate configuration values
+ conf = ConfigParser(allow_no_value=True, delimiters='=', strict=False)
+ conf.read(self._config_file)
+ # if 'start-session' option is not set the default value is 'dhcp'
+ self.assertIn(f'start=dhcpv4', conf['ipoe']['interface'])
+
+ # change 'start-session' option to 'auto'
+ self.set(['interface', interface, 'start-session', start_session])
+ self.cli_commit()
+
+ # Validate changed configuration values
+ conf = ConfigParser(allow_no_value=True, delimiters='=', strict=False)
+ conf.read(self._config_file)
+ self.assertIn(f'start={start_session}', conf['ipoe']['interface'])
+
@unittest.skip("PPP is not a part of IPoE")
def test_accel_ppp_options(self):
pass
diff --git a/smoketest/scripts/cli/test_service_lldp.py b/smoketest/scripts/cli/test_service_lldp.py
index 9d72ef78f..c73707e0d 100755
--- a/smoketest/scripts/cli/test_service_lldp.py
+++ b/smoketest/scripts/cli/test_service_lldp.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2022-2024 VyOS maintainers and contributors
+# Copyright (C) 2022-2025 VyOS maintainers and contributors
#
# 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
@@ -117,6 +117,8 @@ class TestServiceLLDP(VyOSUnitTestSHIM.TestCase):
config = read_file(LLDPD_CONF)
self.assertIn(f'configure ports {interface} med location elin "{elin}"', config)
+ # This is the CLI default mode
+ self.assertIn(f'configure ports {interface} lldp status rx-and-tx', config)
self.assertIn(f'configure system interface pattern "{interface}"', config)
def test_06_lldp_snmp(self):
@@ -134,5 +136,50 @@ class TestServiceLLDP(VyOSUnitTestSHIM.TestCase):
self.cli_delete(['service', 'snmp'])
+ def test_07_lldp_interface_mode(self):
+ interfaces = Section.interfaces('ethernet', vlan=False)
+
+ # set interface mode to 'tx'
+ self.cli_set(base_path + ['interface', 'all'])
+ for interface in interfaces:
+ self.cli_set(base_path + ['interface', interface, 'mode', 'disable'])
+ # commit changes
+ self.cli_commit()
+
+ # verify configuration
+ config = read_file(LLDPD_CONF)
+ for interface in interfaces:
+ self.assertIn(f'configure ports {interface} lldp status disable', config)
+
+ # Change configuration to rx-only
+ for interface in interfaces:
+ self.cli_set(base_path + ['interface', interface, 'mode', 'rx'])
+ # commit changes
+ self.cli_commit()
+ # verify configuration
+ config = read_file(LLDPD_CONF)
+ for interface in interfaces:
+ self.assertIn(f'configure ports {interface} lldp status rx-only', config)
+
+ # Change configuration to tx-only
+ for interface in interfaces:
+ self.cli_set(base_path + ['interface', interface, 'mode', 'tx'])
+ # commit changes
+ self.cli_commit()
+ # verify configuration
+ config = read_file(LLDPD_CONF)
+ for interface in interfaces:
+ self.assertIn(f'configure ports {interface} lldp status tx-only', config)
+
+ # Change configuration to rx-only
+ for interface in interfaces:
+ self.cli_set(base_path + ['interface', interface, 'mode', 'rx-tx'])
+ # commit changes
+ self.cli_commit()
+ # verify configuration
+ config = read_file(LLDPD_CONF)
+ for interface in interfaces:
+ self.assertIn(f'configure ports {interface} lldp status rx-and-tx', config)
+
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_service_monitoring_network_event.py b/smoketest/scripts/cli/test_service_monitoring_network_event.py
new file mode 100644
index 000000000..3c9b4bf7f
--- /dev/null
+++ b/smoketest/scripts/cli/test_service_monitoring_network_event.py
@@ -0,0 +1,65 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2024 VyOS maintainers and contributors
+#
+# 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 vyos.utils.file import read_json
+
+
+base_path = ['service', 'monitoring', 'network-event']
+
+
+def get_logger_config():
+ return read_json('/run/vyos-network-event-logger.conf')
+
+
+class TestMonitoringNetworkEvent(VyOSUnitTestSHIM.TestCase):
+ @classmethod
+ def setUpClass(cls):
+ super(TestMonitoringNetworkEvent, 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)
+
+ def tearDown(self):
+ self.cli_delete(base_path)
+ self.cli_commit()
+
+ def test_network_event_log(self):
+ expected_config = {
+ 'event': {
+ 'route': {},
+ 'link': {},
+ 'addr': {},
+ 'neigh': {},
+ 'rule': {},
+ },
+ 'queue_size': '10000'
+ }
+
+ self.cli_set(base_path + ['event', 'route'])
+ self.cli_set(base_path + ['event', 'link'])
+ self.cli_set(base_path + ['event', 'addr'])
+ self.cli_set(base_path + ['event', 'neigh'])
+ self.cli_set(base_path + ['event', 'rule'])
+ self.cli_set(base_path + ['queue-size', '10000'])
+ self.cli_commit()
+ self.assertEqual(expected_config, get_logger_config())
+
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_service_monitoring_prometheus.py b/smoketest/scripts/cli/test_service_monitoring_prometheus.py
index dae103e4b..6e7f8c808 100755
--- a/smoketest/scripts/cli/test_service_monitoring_prometheus.py
+++ b/smoketest/scripts/cli/test_service_monitoring_prometheus.py
@@ -14,6 +14,7 @@
# 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 base_vyostest_shim import VyOSUnitTestSHIM
@@ -22,12 +23,14 @@ from vyos.utils.file import read_file
NODE_EXPORTER_PROCESS_NAME = 'node_exporter'
FRR_EXPORTER_PROCESS_NAME = 'frr_exporter'
+BLACKBOX_EXPORTER_PROCESS_NAME = 'blackbox_exporter'
base_path = ['service', 'monitoring', 'prometheus']
listen_if = 'dum3421'
listen_ip = '192.0.2.1'
node_exporter_service_file = '/etc/systemd/system/node_exporter.service'
frr_exporter_service_file = '/etc/systemd/system/frr_exporter.service'
+blackbox_exporter_service_file = '/etc/systemd/system/blackbox_exporter.service'
class TestMonitoringPrometheus(VyOSUnitTestSHIM.TestCase):
@@ -53,6 +56,7 @@ class TestMonitoringPrometheus(VyOSUnitTestSHIM.TestCase):
def test_01_node_exporter(self):
self.cli_set(base_path + ['node-exporter', 'listen-address', listen_ip])
+ self.cli_set(base_path + ['node-exporter', 'collectors', 'textfile'])
# commit changes
self.cli_commit()
@@ -60,6 +64,11 @@ class TestMonitoringPrometheus(VyOSUnitTestSHIM.TestCase):
file_content = read_file(node_exporter_service_file)
self.assertIn(f'{listen_ip}:9100', file_content)
+ self.assertTrue(os.path.isdir('/run/node_exporter/collector'))
+ self.assertIn(
+ '--collector.textfile.directory=/run/node_exporter/collector', file_content
+ )
+
# Check for running process
self.assertTrue(process_named_running(NODE_EXPORTER_PROCESS_NAME))
@@ -75,6 +84,78 @@ class TestMonitoringPrometheus(VyOSUnitTestSHIM.TestCase):
# Check for running process
self.assertTrue(process_named_running(FRR_EXPORTER_PROCESS_NAME))
+ def test_03_blackbox_exporter(self):
+ self.cli_set(base_path + ['blackbox-exporter', 'listen-address', listen_ip])
+
+ # commit changes
+ self.cli_commit()
+
+ file_content = read_file(blackbox_exporter_service_file)
+ self.assertIn(f'{listen_ip}:9115', file_content)
+
+ # Check for running process
+ self.assertTrue(process_named_running(BLACKBOX_EXPORTER_PROCESS_NAME))
+
+ def test_04_blackbox_exporter_with_config(self):
+ self.cli_set(base_path + ['blackbox-exporter', 'listen-address', listen_ip])
+ self.cli_set(
+ base_path
+ + [
+ 'blackbox-exporter',
+ 'modules',
+ 'dns',
+ 'name',
+ 'dns_ip4',
+ 'preferred-ip-protocol',
+ 'ipv4',
+ ]
+ )
+ self.cli_set(
+ base_path
+ + [
+ 'blackbox-exporter',
+ 'modules',
+ 'dns',
+ 'name',
+ 'dns_ip4',
+ 'query-name',
+ 'vyos.io',
+ ]
+ )
+ self.cli_set(
+ base_path
+ + [
+ 'blackbox-exporter',
+ 'modules',
+ 'dns',
+ 'name',
+ 'dns_ip4',
+ 'query-type',
+ 'A',
+ ]
+ )
+ self.cli_set(
+ base_path
+ + [
+ 'blackbox-exporter',
+ 'modules',
+ 'icmp',
+ 'name',
+ 'icmp_ip6',
+ 'preferred-ip-protocol',
+ 'ipv6',
+ ]
+ )
+
+ # commit changes
+ self.cli_commit()
+
+ file_content = read_file(blackbox_exporter_service_file)
+ self.assertIn(f'{listen_ip}:9115', file_content)
+
+ # Check for running process
+ self.assertTrue(process_named_running(BLACKBOX_EXPORTER_PROCESS_NAME))
+
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_service_router-advert.py b/smoketest/scripts/cli/test_service_router-advert.py
index 6dbb6add4..33ce93ef4 100755
--- a/smoketest/scripts/cli/test_service_router-advert.py
+++ b/smoketest/scripts/cli/test_service_router-advert.py
@@ -252,6 +252,118 @@ class TestServiceRADVD(VyOSUnitTestSHIM.TestCase):
tmp = get_config_value('AdvIntervalOpt')
self.assertEqual(tmp, 'off')
+ def test_auto_ignore(self):
+ isp_prefix = '2001:db8::/64'
+ ula_prefixes = ['fd00::/64', 'fd01::/64']
+
+ # configure wildcard prefix
+ self.cli_set(base_path + ['prefix', '::/64'])
+
+ # test auto-ignore CLI behaviors with no prefix overrides
+ # set auto-ignore for all three prefixes
+ self.cli_set(base_path + ['auto-ignore', isp_prefix])
+
+ for ula_prefix in ula_prefixes:
+ self.cli_set(base_path + ['auto-ignore', ula_prefix])
+
+ # commit and reload config
+ self.cli_commit()
+ config = read_file(RADVD_CONF)
+
+ # ensure autoignoreprefixes block is generated in config file
+ tmp = f'autoignoreprefixes' + ' {'
+ self.assertIn(tmp, config)
+
+ # ensure all three prefixes are contained in the block
+ self.assertIn(f' {isp_prefix};', config)
+ for ula_prefix in ula_prefixes:
+ self.assertIn(f' {ula_prefix};', config)
+
+ # remove a prefix and verify it's gone
+ self.cli_delete(base_path + ['auto-ignore', ula_prefixes[1]])
+
+ # commit and reload config
+ self.cli_commit()
+ config = read_file(RADVD_CONF)
+
+ self.assertNotIn(f' {ula_prefixes[1]};', config)
+
+ # ensure remaining two prefixes are still present
+ self.assertIn(f' {ula_prefixes[0]};', config)
+ self.assertIn(f' {isp_prefix};', config)
+
+ # remove the remaining two prefixes and verify the config block is gone
+ self.cli_delete(base_path + ['auto-ignore', ula_prefixes[0]])
+ self.cli_delete(base_path + ['auto-ignore', isp_prefix])
+
+ # commit and reload config
+ self.cli_commit()
+ config = read_file(RADVD_CONF)
+
+ tmp = f'autoignoreprefixes' + ' {'
+ self.assertNotIn(tmp, config)
+
+ # test wildcard prefix overrides, with and without auto-ignore CLI configuration
+ newline = '\n'
+ left_curly = '{'
+ right_curly = '}'
+
+ # override ULA prefixes
+ for ula_prefix in ula_prefixes:
+ self.cli_set(base_path + ['prefix', ula_prefix])
+
+ # commit and reload config
+ self.cli_commit()
+ config = read_file(RADVD_CONF)
+
+ # ensure autoignoreprefixes block is generated in config file with both prefixes
+ tmp = f'autoignoreprefixes' + f' {left_curly}{newline} {ula_prefixes[0]};{newline} {ula_prefixes[1]};{newline} {right_curly};'
+ self.assertIn(tmp, config)
+
+ # remove a ULA prefix and ensure there is only one prefix in the config block
+ self.cli_delete(base_path + ['prefix', ula_prefixes[0]])
+
+ # commit and reload config
+ self.cli_commit()
+ config = read_file(RADVD_CONF)
+
+ # ensure autoignoreprefixes block is generated in config file with only one prefix
+ tmp = f'autoignoreprefixes' + f' {left_curly}{newline} {ula_prefixes[1]};{newline} {right_curly};'
+ self.assertIn(tmp, config)
+
+ # exclude a prefix with auto-ignore CLI syntax
+ self.cli_set(base_path + ['auto-ignore', ula_prefixes[0]])
+
+ # commit and reload config
+ self.cli_commit()
+ config = read_file(RADVD_CONF)
+
+ # verify that both prefixes appear in config block once again
+ tmp = f'autoignoreprefixes' + f' {left_curly}{newline} {ula_prefixes[0]};{newline} {ula_prefixes[1]};{newline} {right_curly};'
+ self.assertIn(tmp, config)
+
+ # override first ULA prefix again
+ # first ULA is auto-ignored in CLI, it must appear only once in config
+ self.cli_set(base_path + ['prefix', ula_prefixes[0]])
+
+ # commit and reload config
+ self.cli_commit()
+ config = read_file(RADVD_CONF)
+
+ # verify that both prefixes appear uniquely
+ tmp = f'autoignoreprefixes' + f' {left_curly}{newline} {ula_prefixes[0]};{newline} {ula_prefixes[1]};{newline} {right_curly};'
+ self.assertIn(tmp, config)
+
+ # remove wildcard prefix and verify config block is gone
+ self.cli_delete(base_path + ['prefix', '::/64'])
+
+ # commit and reload config
+ self.cli_commit()
+ config = read_file(RADVD_CONF)
+
+ # verify config block is gone
+ tmp = f'autoignoreprefixes' + ' {'
+ self.assertNotIn(tmp, config)
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_service_webproxy.py b/smoketest/scripts/cli/test_service_webproxy.py
index 2b3f6d21c..ab4707a61 100755
--- a/smoketest/scripts/cli/test_service_webproxy.py
+++ b/smoketest/scripts/cli/test_service_webproxy.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2020-2022 VyOS maintainers and contributors
+# Copyright (C) 2020-2025 VyOS maintainers and contributors
#
# 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
@@ -297,6 +297,22 @@ class TestServiceWebProxy(VyOSUnitTestSHIM.TestCase):
# Check for running process
self.assertTrue(process_named_running(PROCESS_NAME))
+ def test_06_nocache_domain_proxy(self):
+ domains_nocache = ['test1.net', 'test2.net']
+ self.cli_set(base_path + ['listen-address', listen_ip])
+ for domain in domains_nocache:
+ self.cli_set(base_path + ['domain-noncache', domain])
+ # commit changes
+ self.cli_commit()
+
+ config = read_file(PROXY_CONF)
+
+ for domain in domains_nocache:
+ self.assertIn(f'acl NOCACHE dstdomain {domain}', config)
+ self.assertIn(f'no_cache deny NOCACHE', config)
+
+ # Check for running process
+ self.assertTrue(process_named_running(PROCESS_NAME))
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_system_flow-accounting.py b/smoketest/scripts/cli/test_system_flow-accounting.py
index 515134220..9d7942789 100755
--- a/smoketest/scripts/cli/test_system_flow-accounting.py
+++ b/smoketest/scripts/cli/test_system_flow-accounting.py
@@ -97,111 +97,6 @@ class TestSystemFlowAccounting(VyOSUnitTestSHIM.TestCase):
self.assertIn(f'syslog: {syslog}', uacctd)
self.assertIn(f'plugins: memory', uacctd)
- def test_sflow(self):
- sampling_rate = '4000'
- source_address = '192.0.2.1'
- dummy_if = 'dum3841'
- agent_address = '192.0.2.2'
-
- sflow_server = {
- '1.2.3.4' : { },
- '5.6.7.8' : { 'port' : '6000' },
- }
-
- self.cli_set(['interfaces', 'dummy', dummy_if, 'address', agent_address + '/32'])
- self.cli_set(['interfaces', 'dummy', dummy_if, 'address', source_address + '/32'])
- self.cli_set(base_path + ['disable-imt'])
-
- # You need to configure at least one interface for flow-accounting
- with self.assertRaises(ConfigSessionError):
- self.cli_commit()
- for interface in Section.interfaces('ethernet'):
- self.cli_set(base_path + ['interface', interface])
-
-
- # You need to configure at least one sFlow or NetFlow protocol, or not
- # set "disable-imt" for flow-accounting
- with self.assertRaises(ConfigSessionError):
- self.cli_commit()
-
- self.cli_set(base_path + ['sflow', 'agent-address', agent_address])
- self.cli_set(base_path + ['sflow', 'sampling-rate', sampling_rate])
- self.cli_set(base_path + ['sflow', 'source-address', source_address])
- for server, server_config in sflow_server.items():
- self.cli_set(base_path + ['sflow', 'server', server])
- if 'port' in server_config:
- self.cli_set(base_path + ['sflow', 'server', server, 'port', server_config['port']])
-
- # commit changes
- self.cli_commit()
-
- uacctd = read_file(uacctd_conf)
-
- # when 'disable-imt' is not configured on the CLI it must be present
- self.assertNotIn(f'imt_path: /tmp/uacctd.pipe', uacctd)
- self.assertNotIn(f'imt_mem_pools_number: 169', uacctd)
- self.assertNotIn(f'plugins: memory', uacctd)
-
- for server, server_config in sflow_server.items():
- plugin_name = server.replace('.', '-')
- if 'port' in server_config:
- self.assertIn(f'sfprobe_receiver[sf_{plugin_name}]: {server}', uacctd)
- else:
- self.assertIn(f'sfprobe_receiver[sf_{plugin_name}]: {server}:6343', uacctd)
-
- self.assertIn(f'sfprobe_agentip[sf_{plugin_name}]: {agent_address}', uacctd)
- self.assertIn(f'sampling_rate[sf_{plugin_name}]: {sampling_rate}', uacctd)
- self.assertIn(f'sfprobe_source_ip[sf_{plugin_name}]: {source_address}', uacctd)
-
- self.cli_delete(['interfaces', 'dummy', dummy_if])
-
- def test_sflow_ipv6(self):
- sampling_rate = '100'
- sflow_server = {
- '2001:db8::1' : { },
- '2001:db8::2' : { 'port' : '6000' },
- }
-
- self.cli_set(base_path + ['disable-imt'])
-
- # You need to configure at least one interface for flow-accounting
- with self.assertRaises(ConfigSessionError):
- self.cli_commit()
- for interface in Section.interfaces('ethernet'):
- self.cli_set(base_path + ['interface', interface])
-
-
- # You need to configure at least one sFlow or NetFlow protocol, or not
- # set "disable-imt" for flow-accounting
- with self.assertRaises(ConfigSessionError):
- self.cli_commit()
-
- self.cli_set(base_path + ['sflow', 'sampling-rate', sampling_rate])
- for server, server_config in sflow_server.items():
- self.cli_set(base_path + ['sflow', 'server', server])
- if 'port' in server_config:
- self.cli_set(base_path + ['sflow', 'server', server, 'port', server_config['port']])
-
- # commit changes
- self.cli_commit()
-
- uacctd = read_file(uacctd_conf)
-
- # when 'disable-imt' is not configured on the CLI it must be present
- self.assertNotIn(f'imt_path: /tmp/uacctd.pipe', uacctd)
- self.assertNotIn(f'imt_mem_pools_number: 169', uacctd)
- self.assertNotIn(f'plugins: memory', uacctd)
-
- for server, server_config in sflow_server.items():
- tmp_srv = server
- tmp_srv = tmp_srv.replace(':', '-')
-
- if 'port' in server_config:
- self.assertIn(f'sfprobe_receiver[sf_{tmp_srv}]: {bracketize_ipv6(server)}', uacctd)
- else:
- self.assertIn(f'sfprobe_receiver[sf_{tmp_srv}]: {bracketize_ipv6(server)}:6343', uacctd)
- self.assertIn(f'sampling_rate[sf_{tmp_srv}]: {sampling_rate}', uacctd)
-
def test_netflow(self):
engine_id = '33'
max_flows = '667'
@@ -288,8 +183,8 @@ class TestSystemFlowAccounting(VyOSUnitTestSHIM.TestCase):
self.assertIn(f'nfprobe_timeouts[nf_{tmp_srv}]: expint={tmo_expiry}:general={tmo_flow}:icmp={tmo_icmp}:maxlife={tmo_max}:tcp.fin={tmo_tcp_fin}:tcp={tmo_tcp_generic}:tcp.rst={tmo_tcp_rst}:udp={tmo_udp}', uacctd)
-
self.cli_delete(['interfaces', 'dummy', dummy_if])
+
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_system_ip.py b/smoketest/scripts/cli/test_system_ip.py
index 7d730f7b2..5b6ef2046 100755
--- a/smoketest/scripts/cli/test_system_ip.py
+++ b/smoketest/scripts/cli/test_system_ip.py
@@ -20,8 +20,6 @@ from base_vyostest_shim import VyOSUnitTestSHIM
from vyos.configsession import ConfigSessionError
from vyos.utils.system import sysctl_read
from vyos.xml_ref import default_value
-from vyos.frrender import mgmt_daemon
-from vyos.frrender import zebra_daemon
base_path = ['system', 'ip']
@@ -45,13 +43,13 @@ class TestSystemIP(VyOSUnitTestSHIM.TestCase):
self.cli_set(base_path + ['disable-forwarding'])
self.cli_commit()
self.assertEqual(sysctl_read('net.ipv4.conf.all.forwarding'), '0')
- frrconfig = self.getFRRconfig('', end='', daemon=zebra_daemon)
+ frrconfig = self.getFRRconfig('', end='')
self.assertIn('no ip forwarding', frrconfig)
self.cli_delete(base_path + ['disable-forwarding'])
self.cli_commit()
self.assertEqual(sysctl_read('net.ipv4.conf.all.forwarding'), '1')
- frrconfig = self.getFRRconfig('', end='', daemon=zebra_daemon)
+ frrconfig = self.getFRRconfig('', end='')
self.assertNotIn('no ip forwarding', frrconfig)
def test_system_ip_multipath(self):
@@ -91,7 +89,7 @@ class TestSystemIP(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify route-map properly applied to FRR
- frrconfig = self.getFRRconfig('ip protocol', end='', daemon=mgmt_daemon)
+ frrconfig = self.getFRRconfig('ip protocol', end='')
for protocol in protocols:
self.assertIn(f'ip protocol {protocol} route-map route-map-{protocol}', frrconfig)
@@ -102,7 +100,7 @@ class TestSystemIP(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify route-map properly applied to FRR
- frrconfig = self.getFRRconfig('ip protocol', end='', daemon=mgmt_daemon)
+ frrconfig = self.getFRRconfig('ip protocol', end='')
self.assertNotIn(f'ip protocol', frrconfig)
def test_system_ip_protocol_non_existing_route_map(self):
@@ -121,13 +119,13 @@ class TestSystemIP(VyOSUnitTestSHIM.TestCase):
self.cli_set(base_path + ['nht', 'no-resolve-via-default'])
self.cli_commit()
# Verify CLI config applied to FRR
- frrconfig = self.getFRRconfig('', end='', daemon=mgmt_daemon)
+ frrconfig = self.getFRRconfig('', end='')
self.assertIn(f'no ip nht resolve-via-default', frrconfig)
self.cli_delete(base_path + ['nht', 'no-resolve-via-default'])
self.cli_commit()
# Verify CLI config removed to FRR
- frrconfig = self.getFRRconfig('', end='', daemon=mgmt_daemon)
+ frrconfig = self.getFRRconfig('', end='')
self.assertNotIn(f'no ip nht resolve-via-default', frrconfig)
if __name__ == '__main__':
diff --git a/smoketest/scripts/cli/test_system_ipv6.py b/smoketest/scripts/cli/test_system_ipv6.py
index ebf620204..26f281bb4 100755
--- a/smoketest/scripts/cli/test_system_ipv6.py
+++ b/smoketest/scripts/cli/test_system_ipv6.py
@@ -21,8 +21,6 @@ from base_vyostest_shim import VyOSUnitTestSHIM
from vyos.configsession import ConfigSessionError
from vyos.utils.system import sysctl_read
from vyos.xml_ref import default_value
-from vyos.frrender import mgmt_daemon
-from vyos.frrender import zebra_daemon
base_path = ['system', 'ipv6']
@@ -46,13 +44,13 @@ class TestSystemIPv6(VyOSUnitTestSHIM.TestCase):
self.cli_set(base_path + ['disable-forwarding'])
self.cli_commit()
self.assertEqual(sysctl_read('net.ipv6.conf.all.forwarding'), '0')
- frrconfig = self.getFRRconfig('', end='', daemon=zebra_daemon)
+ frrconfig = self.getFRRconfig('', end='')
self.assertIn('no ipv6 forwarding', frrconfig)
self.cli_delete(base_path + ['disable-forwarding'])
self.cli_commit()
self.assertEqual(sysctl_read('net.ipv6.conf.all.forwarding'), '1')
- frrconfig = self.getFRRconfig('', end='', daemon=zebra_daemon)
+ frrconfig = self.getFRRconfig('', end='')
self.assertNotIn('no ipv6 forwarding', frrconfig)
def test_system_ipv6_strict_dad(self):
@@ -109,7 +107,7 @@ class TestSystemIPv6(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify route-map properly applied to FRR
- frrconfig = self.getFRRconfig('ipv6 protocol', end='', daemon=mgmt_daemon)
+ frrconfig = self.getFRRconfig('ipv6 protocol', end='')
for protocol in protocols:
# VyOS and FRR use a different name for OSPFv3 (IPv6)
if protocol == 'ospfv3':
@@ -123,7 +121,7 @@ class TestSystemIPv6(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify route-map properly applied to FRR
- frrconfig = self.getFRRconfig('ipv6 protocol', end='', daemon=mgmt_daemon)
+ frrconfig = self.getFRRconfig('ipv6 protocol', end='')
self.assertNotIn(f'ipv6 protocol', frrconfig)
def test_system_ipv6_protocol_non_existing_route_map(self):
@@ -142,13 +140,13 @@ class TestSystemIPv6(VyOSUnitTestSHIM.TestCase):
self.cli_set(base_path + ['nht', 'no-resolve-via-default'])
self.cli_commit()
# Verify CLI config applied to FRR
- frrconfig = self.getFRRconfig('', end='', daemon=mgmt_daemon)
+ frrconfig = self.getFRRconfig('', end='')
self.assertIn(f'no ipv6 nht resolve-via-default', frrconfig)
self.cli_delete(base_path + ['nht', 'no-resolve-via-default'])
self.cli_commit()
# Verify CLI config removed to FRR
- frrconfig = self.getFRRconfig('', end='', daemon=mgmt_daemon)
+ frrconfig = self.getFRRconfig('', end='')
self.assertNotIn(f'no ipv6 nht resolve-via-default', frrconfig)
if __name__ == '__main__':
diff --git a/smoketest/scripts/cli/test_system_login.py b/smoketest/scripts/cli/test_system_login.py
index 28abba012..71dec68d8 100755
--- a/smoketest/scripts/cli/test_system_login.py
+++ b/smoketest/scripts/cli/test_system_login.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2019-2024 VyOS maintainers and contributors
+# Copyright (C) 2019-2025 VyOS maintainers and contributors
#
# 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
@@ -14,23 +14,37 @@
# 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 re
import unittest
+import jinja2
+import secrets
+import string
+import paramiko
+import shutil
from base_vyostest_shim import VyOSUnitTestSHIM
from gzip import GzipFile
-from subprocess import Popen, PIPE
+from subprocess import Popen
+from subprocess import PIPE
from pwd import getpwall
from vyos.configsession import ConfigSessionError
+from vyos.configquery import ConfigTreeQuery
from vyos.utils.auth import get_current_user
from vyos.utils.process import cmd
from vyos.utils.file import read_file
+from vyos.utils.file import write_file
from vyos.template import inc_ip
+from vyos.template import is_ipv6
+from vyos.xml_ref import default_value
base_path = ['system', 'login']
users = ['vyos1', 'vyos-roxx123', 'VyOS-123_super.Nice']
+weak_passwd_user = ['test_user', 'passWord1']
+
+ssh_test_command = '/opt/vyatta/bin/vyatta-op-cmd-wrapper show version'
ssh_pubkey = """
AAAAB3NzaC1yc2EAAAADAQABAAABgQD0NuhUOEtMIKnUVFIHoFatqX/c4mjerXyF
@@ -44,6 +58,71 @@ pHJz8umqkxy3hfw0K7BRFtjWd63sbOP8Q/SDV7LPaIfIxenA9zv2rY7y+AIqTmSr
TTSb0X1zPGxPIRFy5GoGtO9Mm5h4OZk=
"""
+tac_image = 'docker.io/lfkeitel/tacacs_plus:alpine'
+tac_image_path = '/usr/share/vyos/tacplus-alpine.tar'
+TAC_PLUS_TMPL_SRC = """
+id = spawnd {
+ debug redirect = /dev/stdout
+ listen = { port = 49 }
+ spawn = {
+ instances min = 1
+ instances max = 10
+ }
+ background = no
+}
+
+id = tac_plus {
+ debug = ALL
+ log = stdout {
+ destination = /dev/stdout
+ }
+ authorization log group = yes
+ authentication log = stdout
+ authorization log = stdout
+ accounting log = stdout
+
+ host = smoketest {
+ address = {{ source_address }}/32
+ enable = clear enable
+ key = {{ tacacs_secret }}
+ }
+
+ group = admin {
+ default service = permit
+ enable = permit
+ service = shell {
+ default command = permit
+ default attribute = permit
+ set priv-lvl = 15
+ }
+ }
+
+ user = {{ username }} {
+ password = clear {{ password }}
+ member = admin
+ }
+}
+
+"""
+
+radius_image = 'docker.io/dchidell/radius-web:latest'
+radius_image_path = '/usr/share/vyos/radius-latest.tar'
+RADIUS_CLIENTS_TMPL_SRC = """
+client SMOKETEST {
+ secret = {{ radius_key }}
+ nastype = other
+ ipaddr = {{ source_address }}
+}
+
+"""
+RADIUS_USERS_TMPL_SRC = """
+# User configuration
+{{ username }} Cleartext-Password := "{{ password }}"
+ Service-Type = NAS-Prompt-User,
+ Cisco-AVPair = "shell:priv-lvl=15"
+
+"""
+
class TestSystemLogin(VyOSUnitTestSHIM.TestCase):
@classmethod
def setUpClass(cls):
@@ -54,6 +133,37 @@ class TestSystemLogin(VyOSUnitTestSHIM.TestCase):
cls.cli_delete(cls, base_path + ['radius'])
cls.cli_delete(cls, base_path + ['tacacs'])
+ # Load images for smoketest provided in vyos-1x-smoketest
+ if not os.path.exists(tac_image_path):
+ cls.fail(cls, f'{tac_image} image not available')
+ cmd(f'sudo podman load -i {tac_image_path}')
+
+ if not os.path.exists(radius_image_path):
+ cls.fail(cls, f'{radius_image} image not available')
+ cmd(f'sudo podman load -i {radius_image_path}')
+
+ cls.ssh_test_command_result = cls.op_mode(cls, ['show', 'version'])
+
+ # Dynamically start SSH service if it's not running
+ config = ConfigTreeQuery()
+ cls.is_sshd_pre_test = config.exists(['service', 'sshd'])
+ if not cls.is_sshd_pre_test:
+ # Start SSH service
+ cls.cli_set(cls, ['service', 'ssh'])
+
+ @classmethod
+ def tearDownClass(cls):
+ # Stop SSH service - if it was not running before starting the test
+ if not cls.is_sshd_pre_test:
+ cls.cli_set(cls, ['service', 'ssh'])
+ cls.cli_commit(cls)
+
+ super(TestSystemLogin, cls).tearDownClass()
+
+ # Cleanup container images
+ cmd(f'sudo podman image rm -f {tac_image}')
+ cmd(f'sudo podman image rm -f {radius_image}')
+
def tearDown(self):
# Delete individual users from configuration
for user in users:
@@ -83,29 +193,28 @@ class TestSystemLogin(VyOSUnitTestSHIM.TestCase):
self.cli_delete(base_path + ['user', system_user])
def test_system_login_user(self):
- # Check if user can be created and we can SSH to localhost
- self.cli_set(['service', 'ssh', 'port', '22'])
-
for user in users:
- name = "VyOS Roxx " + user
- home_dir = "/tmp/" + user
+ name = f'VyOS Roxx {user}'
+ passwd = f'{user}-pSWd-t3st'
+ home_dir = f'/tmp/smoketest/{user}'
- self.cli_set(base_path + ['user', user, 'authentication', 'plaintext-password', user])
- self.cli_set(base_path + ['user', user, 'full-name', 'VyOS Roxx'])
+ self.cli_set(base_path + ['user', user, 'authentication', 'plaintext-password', passwd])
+ self.cli_set(base_path + ['user', user, 'full-name', name])
self.cli_set(base_path + ['user', user, 'home-directory', home_dir])
self.cli_commit()
for user in users:
+ passwd = f'{user}-pSWd-t3st'
tmp = ['su','-', user]
proc = Popen(tmp, stdin=PIPE, stdout=PIPE, stderr=PIPE)
- tmp = "{}\nuname -a".format(user)
+ tmp = f'{passwd}\nuname -a'
proc.stdin.write(tmp.encode())
proc.stdin.flush()
(stdout, stderr) = proc.communicate()
# stdout is something like this:
- # b'Linux LR1.wue3 5.10.61-amd64-vyos #1 SMP Fri Aug 27 08:55:46 UTC 2021 x86_64 GNU/Linux\n'
+ # b'Linux vyos 6.6.66-vyos 6.6.66-vyos #1 SMP Mon Dec 30 19:05:15 UTC 2024 x86_64 GNU/Linux\n'
self.assertTrue(len(stdout) > 40)
locked_user = users[0]
@@ -123,6 +232,16 @@ class TestSystemLogin(VyOSUnitTestSHIM.TestCase):
tmp = cmd(f'sudo passwd -S {locked_user}')
self.assertIn(f'{locked_user} P ', tmp)
+ def test_system_login_weak_password_warning(self):
+ self.cli_set(base_path + [
+ 'user', weak_passwd_user[0], 'authentication',
+ 'plaintext-password', weak_passwd_user[1]
+ ])
+
+ out = self.cli_commit().strip()
+
+ self.assertIn('WARNING: The password complexity is too low', out)
+ self.cli_delete(base_path + ['user', weak_passwd_user[0]])
def test_system_login_otp(self):
otp_user = 'otp-test_user'
@@ -172,17 +291,71 @@ class TestSystemLogin(VyOSUnitTestSHIM.TestCase):
self.assertIn(f'{option}=y', kernel_config)
def test_system_login_radius_ipv4(self):
- # Verify generated RADIUS configuration files
+ radius_servers = ['100.64.0.4', '100.64.0.5']
+ radius_source = '100.64.0.1'
+ self._system_login_radius_test_helper(radius_servers, radius_source)
- radius_key = 'VyOSsecretVyOS'
- radius_server = '172.16.100.10'
- radius_source = '127.0.0.1'
- radius_port = '2000'
- radius_timeout = '1'
+ def test_system_login_radius_ipv6(self):
+ radius_servers = ['2001:db8::4', '2001:db8::5']
+ radius_source = '2001:db8::1'
+ self._system_login_radius_test_helper(radius_servers, radius_source)
+
+ def _system_login_radius_test_helper(self, radius_servers: list, radius_source: str):
+ # Verify generated RADIUS configuration files
+ radius_key = ''.join(secrets.choice(string.ascii_letters + string.digits) for i in range(10))
+
+ default_port = default_value(base_path + ['radius', 'server', radius_servers[0], 'port'])
+ default_timeout = default_value(base_path + ['radius', 'server', radius_servers[0], 'timeout'])
+
+ dummy_if = 'dum12760'
+
+ # Load container image for FreeRADIUS server
+ radius_config = '/tmp/smoketest-radius-server'
+ radius_container_path = ['container', 'name', 'radius-1']
+
+ # Generate random string with 10 digits
+ username = 'radius-admin'
+ password = ''.join(secrets.choice(string.ascii_letters + string.digits) for i in range(10))
+ radius_source_mask = '32'
+ if is_ipv6(radius_source):
+ radius_source_mask = '128'
+ radius_test_user = {
+ 'username' : username,
+ 'password' : password,
+ 'radius_key' : radius_key,
+ 'source_address' : f'{radius_source}/{radius_source_mask}'
+ }
+
+ tmpl = jinja2.Template(RADIUS_CLIENTS_TMPL_SRC)
+ write_file(f'{radius_config}/clients.cfg', tmpl.render(radius_test_user))
+
+ tmpl = jinja2.Template(RADIUS_USERS_TMPL_SRC)
+ write_file(f'{radius_config}/users', tmpl.render(radius_test_user))
+
+ # Start tac_plus container
+ self.cli_set(radius_container_path + ['allow-host-networks'])
+ self.cli_set(radius_container_path + ['image', radius_image])
+ self.cli_set(radius_container_path + ['volume', 'clients', 'destination', '/etc/raddb/clients.conf'])
+ self.cli_set(radius_container_path + ['volume', 'clients', 'mode', 'ro'])
+ self.cli_set(radius_container_path + ['volume', 'clients', 'source', f'{radius_config}/clients.cfg'])
+ self.cli_set(radius_container_path + ['volume', 'users', 'destination', '/etc/raddb/users'])
+ self.cli_set(radius_container_path + ['volume', 'users', 'mode', 'ro'])
+ self.cli_set(radius_container_path + ['volume', 'users', 'source', f'{radius_config}/users'])
+
+ # Start container
+ self.cli_commit()
- self.cli_set(base_path + ['radius', 'server', radius_server, 'key', radius_key])
- self.cli_set(base_path + ['radius', 'server', radius_server, 'port', radius_port])
- self.cli_set(base_path + ['radius', 'server', radius_server, 'timeout', radius_timeout])
+ # Deinfine RADIUS servers
+ for radius_server in radius_servers:
+ # Use this system as "remote" RADIUS server
+ dummy_address_mask = '32'
+ if is_ipv6(radius_server):
+ dummy_address_mask = '128'
+ self.cli_set(['interfaces', 'dummy', dummy_if, 'address', f'{radius_server}/{dummy_address_mask}'])
+ self.cli_set(base_path + ['radius', 'server', radius_server, 'key', radius_key])
+
+ # Define RADIUS traffic source address
+ self.cli_set(['interfaces', 'dummy', dummy_if, 'address', f'{radius_source}/{radius_source_mask}'])
self.cli_set(base_path + ['radius', 'source-address', radius_source])
self.cli_set(base_path + ['radius', 'source-address', inc_ip(radius_source, 1)])
@@ -195,10 +368,13 @@ class TestSystemLogin(VyOSUnitTestSHIM.TestCase):
# this file must be read with higher permissions
pam_radius_auth_conf = cmd('sudo cat /etc/pam_radius_auth.conf')
- tmp = re.findall(r'\n?{}:{}\s+{}\s+{}\s+{}'.format(radius_server,
- radius_port, radius_key, radius_timeout,
- radius_source), pam_radius_auth_conf)
- self.assertTrue(tmp)
+
+ for radius_server in radius_servers:
+ if is_ipv6(radius_server):
+ # it is essential to escape the [] brackets when searching with a regex
+ radius_server = rf'\[{radius_server}\]'
+ tmp = re.findall(rf'\n?{radius_server}:{default_port}\s+{radius_key}\s+{default_timeout}\s+{radius_source}', pam_radius_auth_conf)
+ self.assertTrue(tmp)
# required, static options
self.assertIn('priv-lvl 15', pam_radius_auth_conf)
@@ -225,59 +401,26 @@ class TestSystemLogin(VyOSUnitTestSHIM.TestCase):
tmp = re.findall(r'group:\s+mapname\s+files', nsswitch_conf)
self.assertTrue(tmp)
- def test_system_login_radius_ipv6(self):
- # Verify generated RADIUS configuration files
+ # Login with proper credentials
+ out, err = self.ssh_send_cmd(ssh_test_command, username, password)
+ # verify login
+ self.assertFalse(err)
+ self.assertEqual(out, self.ssh_test_command_result)
- radius_key = 'VyOS-VyOS'
- radius_server = '2001:db8::1'
- radius_source = '::1'
- radius_port = '4000'
- radius_timeout = '4'
-
- self.cli_set(base_path + ['radius', 'server', radius_server, 'key', radius_key])
- self.cli_set(base_path + ['radius', 'server', radius_server, 'port', radius_port])
- self.cli_set(base_path + ['radius', 'server', radius_server, 'timeout', radius_timeout])
- self.cli_set(base_path + ['radius', 'source-address', radius_source])
- self.cli_set(base_path + ['radius', 'source-address', inc_ip(radius_source, 1)])
-
- # check validate() - Only one IPv4 source-address supported
- with self.assertRaises(ConfigSessionError):
- self.cli_commit()
- self.cli_delete(base_path + ['radius', 'source-address', inc_ip(radius_source, 1)])
+ # Login with invalid credentials
+ with self.assertRaises(paramiko.ssh_exception.AuthenticationException):
+ _, _ = self.ssh_send_cmd(ssh_test_command, username, f'{password}1')
+ # Remove RADIUS configuration
+ self.cli_delete(base_path + ['radius'])
+ # Remove RADIUS container
+ self.cli_delete(radius_container_path)
+ # Remove dummy interface
+ self.cli_delete(['interfaces', 'dummy', dummy_if])
self.cli_commit()
- # this file must be read with higher permissions
- pam_radius_auth_conf = cmd('sudo cat /etc/pam_radius_auth.conf')
- tmp = re.findall(r'\n?\[{}\]:{}\s+{}\s+{}\s+\[{}\]'.format(radius_server,
- radius_port, radius_key, radius_timeout,
- radius_source), pam_radius_auth_conf)
- self.assertTrue(tmp)
-
- # required, static options
- self.assertIn('priv-lvl 15', pam_radius_auth_conf)
- self.assertIn('mapped_priv_user radius_priv_user', pam_radius_auth_conf)
-
- # PAM
- pam_common_account = read_file('/etc/pam.d/common-account')
- self.assertIn('pam_radius_auth.so', pam_common_account)
-
- pam_common_auth = read_file('/etc/pam.d/common-auth')
- self.assertIn('pam_radius_auth.so', pam_common_auth)
-
- pam_common_session = read_file('/etc/pam.d/common-session')
- self.assertIn('pam_radius_auth.so', pam_common_session)
-
- pam_common_session_noninteractive = read_file('/etc/pam.d/common-session-noninteractive')
- self.assertIn('pam_radius_auth.so', pam_common_session_noninteractive)
-
- # NSS
- nsswitch_conf = read_file('/etc/nsswitch.conf')
- tmp = re.findall(r'passwd:\s+mapuid\s+files\s+mapname', nsswitch_conf)
- self.assertTrue(tmp)
-
- tmp = re.findall(r'group:\s+mapname\s+files', nsswitch_conf)
- self.assertTrue(tmp)
+ # Remove rendered tac_plus daemon configuration
+ shutil.rmtree(radius_config)
def test_system_login_max_login_session(self):
max_logins = '2'
@@ -300,11 +443,46 @@ class TestSystemLogin(VyOSUnitTestSHIM.TestCase):
self.cli_delete(base_path + ['max-login-session'])
def test_system_login_tacacs(self):
- tacacs_secret = 'tac_plus_key'
+ tacacs_secret = ''.join(secrets.choice(string.ascii_letters + string.digits) for i in range(10))
tacacs_servers = ['100.64.0.11', '100.64.0.12']
+ source_address = '100.64.0.1'
+ dummy_if = 'dum12759'
+
+ # Load container image for lac_plus daemon
+ tac_plus_config = '/tmp/smoketest-tacacs-server'
+ tac_container_path = ['container', 'name', 'tacacs-1']
+
+ # Generate random string with 10 digits
+ username = 'tactest'
+ password = ''.join(secrets.choice(string.ascii_letters + string.digits) for i in range(10))
+ tac_test_user = {
+ 'username' : username,
+ 'password' : password,
+ 'tacacs_secret' : tacacs_secret,
+ 'source_address' : source_address,
+ }
+
+ tmpl = jinja2.Template(TAC_PLUS_TMPL_SRC)
+ write_file(f'{tac_plus_config}/tac_plus.cfg', tmpl.render(tac_test_user))
+
+ # Start tac_plus container
+ self.cli_set(tac_container_path + ['allow-host-networks'])
+ self.cli_set(tac_container_path + ['image', tac_image])
+ self.cli_set(tac_container_path + ['volume', 'config', 'destination', '/etc/tac_plus'])
+ self.cli_set(tac_container_path + ['volume', 'config', 'mode', 'ro'])
+ self.cli_set(tac_container_path + ['volume', 'config', 'source', tac_plus_config])
+
+ # Start container
+ self.cli_commit()
+
+ # Define TACACS traffic source address
+ self.cli_set(['interfaces', 'dummy', dummy_if, 'address', f'{source_address}/32'])
+ self.cli_set(base_path + ['tacacs', 'source-address', source_address])
- # Enable TACACS
+ # Define TACACS servers
for server in tacacs_servers:
+ # Use this system as "remote" TACACS server
+ self.cli_set(['interfaces', 'dummy', dummy_if, 'address', f'{server}/32'])
self.cli_set(base_path + ['tacacs', 'server', server, 'key', tacacs_secret])
self.cli_commit()
@@ -328,6 +506,11 @@ class TestSystemLogin(VyOSUnitTestSHIM.TestCase):
self.assertIn('service=shell', pam_tacacs_conf)
self.assertIn('protocol=ssh', pam_tacacs_conf)
+ # Verify configured TACACS source address
+ self.assertIn(f'source_ip={source_address}', pam_tacacs_conf)
+ self.assertIn(f'source_ip={source_address}', nss_tacacs_conf)
+
+ # Verify configured TACACS servers
for server in tacacs_servers:
self.assertIn(f'secret={tacacs_secret}', pam_tacacs_conf)
self.assertIn(f'server={server}', pam_tacacs_conf)
@@ -335,6 +518,27 @@ class TestSystemLogin(VyOSUnitTestSHIM.TestCase):
self.assertIn(f'secret={tacacs_secret}', nss_tacacs_conf)
self.assertIn(f'server={server}', nss_tacacs_conf)
+ # Login with proper credentials
+ out, err = self.ssh_send_cmd(ssh_test_command, username, password)
+ # verify login
+ self.assertFalse(err)
+ self.assertEqual(out, self.ssh_test_command_result)
+
+ # Login with invalid credentials
+ with self.assertRaises(paramiko.ssh_exception.AuthenticationException):
+ _, _ = self.ssh_send_cmd(ssh_test_command, username, f'{password}1')
+
+ # Remove TACACS configuration
+ self.cli_delete(base_path + ['tacacs'])
+ # Remove tac_plus container
+ self.cli_delete(tac_container_path)
+ # Remove dummy interface
+ self.cli_delete(['interfaces', 'dummy', dummy_if])
+ self.cli_commit()
+
+ # Remove rendered tac_plus daemon configuration
+ shutil.rmtree(tac_plus_config)
+
def test_delete_current_user(self):
current_user = get_current_user()
diff --git a/smoketest/scripts/cli/test_system_option.py b/smoketest/scripts/cli/test_system_option.py
index f3112cf0b..c7f8c1f3e 100755
--- a/smoketest/scripts/cli/test_system_option.py
+++ b/smoketest/scripts/cli/test_system_option.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2024 VyOS maintainers and contributors
+# Copyright (C) 2024-2025 VyOS maintainers and contributors
#
# 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
@@ -16,14 +16,18 @@
import os
import unittest
+
from base_vyostest_shim import VyOSUnitTestSHIM
+
+from vyos.configsession import ConfigSessionError
+from vyos.utils.cpu import get_cpus
from vyos.utils.file import read_file
from vyos.utils.process import is_systemd_service_active
from vyos.utils.system import sysctl_read
+from vyos.system import image
base_path = ['system', 'option']
-
class TestSystemOption(VyOSUnitTestSHIM.TestCase):
def tearDown(self):
self.cli_delete(base_path)
@@ -96,6 +100,60 @@ class TestSystemOption(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
self.assertFalse(os.path.exists(ssh_client_opt_file))
+ def test_kernel_options(self):
+ amd_pstate_mode = 'active'
+ isolate_cpus = '1,2,3'
+ nohz_full = '2'
+ rcu_no_cbs = '1,2,4-5'
+ default_hp_size = '2M'
+ hp_size_1g = '1G'
+ hp_size_2m = '2M'
+ hp_count_1g = '2'
+ hp_count_2m = '512'
+
+ self.cli_set(['system', 'option', 'kernel', 'cpu', 'disable-nmi-watchdog'])
+ self.cli_set(['system', 'option', 'kernel', 'cpu', 'isolate-cpus', isolate_cpus])
+ self.cli_set(['system', 'option', 'kernel', 'cpu', 'nohz-full', nohz_full])
+ self.cli_set(['system', 'option', 'kernel', 'cpu', 'rcu-no-cbs', rcu_no_cbs])
+ self.cli_set(['system', 'option', 'kernel', 'disable-hpet'])
+ self.cli_set(['system', 'option', 'kernel', 'disable-mce'])
+ self.cli_set(['system', 'option', 'kernel', 'disable-mitigations'])
+ self.cli_set(['system', 'option', 'kernel', 'disable-power-saving'])
+ self.cli_set(['system', 'option', 'kernel', 'disable-softlockup'])
+ self.cli_set(['system', 'option', 'kernel', 'memory', 'disable-numa-balancing'])
+ self.cli_set(['system', 'option', 'kernel', 'memory', 'default-hugepage-size', default_hp_size])
+ self.cli_set(['system', 'option', 'kernel', 'memory', 'hugepage-size', hp_size_1g, 'hugepage-count', hp_count_1g])
+ self.cli_set(['system', 'option', 'kernel', 'memory', 'hugepage-size', hp_size_2m, 'hugepage-count', hp_count_2m])
+ self.cli_set(['system', 'option', 'kernel', 'quiet'])
+
+ self.cli_set(['system', 'option', 'kernel', 'amd-pstate-driver', amd_pstate_mode])
+ cpu_vendor = get_cpus()[0]['vendor_id']
+ if cpu_vendor != 'AuthenticAMD':
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+ self.cli_delete(['system', 'option', 'kernel', 'amd-pstate-driver'])
+
+ self.cli_commit()
+
+ # Read GRUB config file for current running image
+ tmp = read_file(f'{image.grub.GRUB_DIR_VYOS_VERS}/{image.get_running_image()}.cfg')
+ self.assertIn(' mitigations=off', tmp)
+ self.assertIn(' intel_idle.max_cstate=0 processor.max_cstate=1', tmp)
+ self.assertIn(' quiet', tmp)
+ self.assertIn(' nmi_watchdog=0', tmp)
+ self.assertIn(' hpet=disable', tmp)
+ self.assertIn(' mce=off', tmp)
+ self.assertIn(' nosoftlockup', tmp)
+ self.assertIn(f' isolcpus={isolate_cpus}', tmp)
+ self.assertIn(f' nohz_full={nohz_full}', tmp)
+ self.assertIn(f' rcu_nocbs={rcu_no_cbs}', tmp)
+ self.assertIn(f' default_hugepagesz={default_hp_size}', tmp)
+ self.assertIn(f' hugepagesz={hp_size_1g} hugepages={hp_count_1g}', tmp)
+ self.assertIn(f' hugepagesz={hp_size_2m} hugepages={hp_count_2m}', tmp)
+ self.assertIn(' numa_balancing=disable', tmp)
+
+ if cpu_vendor == 'AuthenticAMD':
+ self.assertIn(f' initcall_blacklist=acpi_cpufreq_init amd_pstate={amd_pstate_mode}', tmp)
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_system_sflow.py b/smoketest/scripts/cli/test_system_sflow.py
index 74c065473..700253e2b 100755
--- a/smoketest/scripts/cli/test_system_sflow.py
+++ b/smoketest/scripts/cli/test_system_sflow.py
@@ -96,6 +96,39 @@ class TestSystemFlowAccounting(VyOSUnitTestSHIM.TestCase):
for interface in Section.interfaces('ethernet'):
self.assertIn(f'pcap {{ dev={interface} }}', hsflowd)
+ def test_sflow_ipv6(self):
+ sampling_rate = '100'
+ default_polling = '30'
+ default_port = '6343'
+ sflow_server = {
+ '2001:db8::1': {},
+ '2001:db8::2': {'port': '8023'},
+ }
+
+ for interface in Section.interfaces('ethernet'):
+ self.cli_set(base_path + ['interface', interface])
+
+ self.cli_set(base_path + ['sampling-rate', sampling_rate])
+ for server, server_config in sflow_server.items():
+ self.cli_set(base_path + ['server', server])
+ if 'port' in server_config:
+ self.cli_set(base_path + ['server', server, 'port', server_config['port']])
+
+ # commit changes
+ self.cli_commit()
+
+ # verify configuration
+ hsflowd = read_file(hsflowd_conf)
+
+ self.assertIn(f'sampling={sampling_rate}', hsflowd)
+ self.assertIn(f'polling={default_polling}', hsflowd)
+
+ for server, server_config in sflow_server.items():
+ if 'port' in server_config:
+ self.assertIn(f'collector {{ ip = {server} udpport = {server_config["port"]} }}', hsflowd)
+ else:
+ self.assertIn(f'collector {{ ip = {server} udpport = {default_port} }}', hsflowd)
+
def test_vrf(self):
interface = 'eth0'
server = '192.0.2.1'
diff --git a/smoketest/scripts/cli/test_system_syslog.py b/smoketest/scripts/cli/test_system_syslog.py
index a86711119..f3e1f65ea 100755
--- a/smoketest/scripts/cli/test_system_syslog.py
+++ b/smoketest/scripts/cli/test_system_syslog.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2019-2024 VyOS maintainers and contributors
+# Copyright (C) 2019-2025 VyOS maintainers and contributors
#
# 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
@@ -14,24 +14,33 @@
# 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 base_vyostest_shim import VyOSUnitTestSHIM
+from vyos.configsession import ConfigSessionError
from vyos.utils.file import read_file
+from vyos.utils.process import cmd
from vyos.utils.process import process_named_running
from vyos.xml_ref import default_value
PROCESS_NAME = 'rsyslogd'
-RSYSLOG_CONF = '/etc/rsyslog.d/00-vyos.conf'
+RSYSLOG_CONF = '/run/rsyslog/rsyslog.conf'
base_path = ['system', 'syslog']
-def get_config_value(key):
- tmp = read_file(RSYSLOG_CONF)
- tmp = re.findall(r'\n?{}\s+(.*)'.format(key), tmp)
- return tmp[0]
+dummy_interface = 'dum372874'
+
+def get_config(string=''):
+ """
+ Retrieve current "running configuration" from FRR
+ string: search for a specific start string in the configuration
+ """
+ command = 'cat /run/rsyslog/rsyslog.conf'
+ if string:
+ command += f' | sed -n "/^{string}$/,/}}/p"' # }} required to escape } in f-string
+ return cmd(command)
class TestRSYSLOGService(VyOSUnitTestSHIM.TestCase):
@classmethod
@@ -41,6 +50,7 @@ class TestRSYSLOGService(VyOSUnitTestSHIM.TestCase):
# 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'])
def tearDown(self):
# Check for running process
@@ -50,82 +60,249 @@ class TestRSYSLOGService(VyOSUnitTestSHIM.TestCase):
self.cli_delete(base_path)
self.cli_commit()
+ # The default syslog implementation should make syslog.service a
+ # symlink to itself
+ self.assertEqual(os.readlink('/etc/systemd/system/syslog.service'),
+ '/lib/systemd/system/rsyslog.service')
+
# Check for running process
self.assertFalse(process_named_running(PROCESS_NAME))
- def test_syslog_basic(self):
- host1 = '127.0.0.10'
- host2 = '127.0.0.20'
-
- self.cli_set(base_path + ['host', host1, 'port', '999'])
- self.cli_set(base_path + ['host', host1, 'facility', 'all', 'level', 'all'])
- self.cli_set(base_path + ['host', host2, 'facility', 'kern', 'level', 'err'])
- self.cli_set(base_path + ['console', 'facility', 'all', 'level', 'warning'])
-
+ def test_console(self):
+ level = 'warning'
+ self.cli_set(base_path + ['console', 'facility', 'all', 'level'], value=level)
self.cli_commit()
- # verify log level and facilities in config file
- # *.warning /dev/console
- # *.* @198.51.100.1:999
- # kern.err @192.0.2.1:514
+
+ rsyslog_conf = get_config()
config = [
- get_config_value('\*.\*'),
- get_config_value('kern.err'),
- get_config_value('\*.warning'),
+ f'if prifilt("*.{level}") then {{', # {{ required to escape { in f-string
+ 'action(type="omfile" file="/dev/console")',
]
- expected = [f'@{host1}:999', f'@{host2}:514', '/dev/console']
+ for tmp in config:
+ self.assertIn(tmp, rsyslog_conf)
- for i in range(0, 3):
- self.assertIn(expected[i], config[i])
- # Check for running process
- self.assertTrue(process_named_running(PROCESS_NAME))
-
- def test_syslog_global(self):
+ def test_basic(self):
hostname = 'vyos123'
- domainname = 'example.local'
- self.cli_set(['system', 'host-name', hostname])
- self.cli_set(['system', 'domain-name', domainname])
- self.cli_set(base_path + ['global', 'marker', 'interval', '600'])
- self.cli_set(base_path + ['global', 'preserve-fqdn'])
- self.cli_set(base_path + ['global', 'facility', 'kern', 'level', 'err'])
+ domain_name = 'example.local'
+ default_marker_interval = default_value(base_path + ['marker', 'interval'])
+
+ facility = {
+ 'auth': {'level': 'info'},
+ 'kern': {'level': 'debug'},
+ 'all': {'level': 'notice'},
+ }
+
+ self.cli_set(['system', 'host-name'], value=hostname)
+ self.cli_set(['system', 'domain-name'], value=domain_name)
+ self.cli_set(base_path + ['preserve-fqdn'])
+
+ for tmp, tmp_options in facility.items():
+ level = tmp_options['level']
+ self.cli_set(base_path + ['local', 'facility', tmp, 'level'], value=level)
self.cli_commit()
- config = read_file(RSYSLOG_CONF)
+ config = get_config('')
expected = [
- '$MarkMessagePeriod 600',
- '$PreserveFQDN on',
- 'kern.err',
- f'$LocalHostName {hostname}.{domainname}',
+ f'module(load="immark" interval="{default_marker_interval}")',
+ 'global(preserveFQDN="on")',
+ f'global(localHostname="{hostname}.{domain_name}")',
]
-
for e in expected:
self.assertIn(e, config)
- # Check for running process
- self.assertTrue(process_named_running(PROCESS_NAME))
- def test_syslog_remote(self):
- rhost = '169.254.0.1'
- default_port = default_value(base_path + ['host', rhost, 'port'])
+ config = get_config('#### GLOBAL LOGGING ####')
+ prifilt = []
+ for tmp, tmp_options in facility.items():
+ if tmp == 'all':
+ tmp = '*'
+ level = tmp_options['level']
+ prifilt.append(f'{tmp}.{level}')
+
+ prifilt.sort()
+ prifilt = ','.join(prifilt)
- self.cli_set(base_path + ['global', 'facility', 'all', 'level', 'info'])
- self.cli_set(base_path + ['global', 'facility', 'local7', 'level', 'debug'])
- self.cli_set(base_path + ['host', rhost, 'facility', 'all', 'level', 'all'])
- self.cli_set(base_path + ['host', rhost, 'protocol', 'tcp'])
+ self.assertIn(f'if prifilt("{prifilt}") then {{', config)
+ self.assertIn( ' action(', config)
+ self.assertIn( ' type="omfile"', config)
+ self.assertIn( ' file="/var/log/messages"', config)
+ self.assertIn( ' rotation.sizeLimit="524288"', config)
+ self.assertIn( ' rotation.sizeLimitCommand="/usr/sbin/logrotate /etc/logrotate.d/vyos-rsyslog"', config)
+ self.cli_set(base_path + ['marker', 'disable'])
self.cli_commit()
- config = read_file(RSYSLOG_CONF)
- self.assertIn(f'*.* @@{rhost}:{default_port}', config)
+ config = get_config('')
+ self.assertNotIn('module(load="immark"', config)
+
+ def test_remote(self):
+ dummy_if_path = ['interfaces', 'dummy', dummy_interface]
+ rhosts = {
+ '169.254.0.1': {
+ 'facility': {'auth' : {'level': 'info'}},
+ 'protocol': 'udp',
+ },
+ '2001:db8::1': {
+ 'facility': {'all' : {'level': 'debug'}},
+ 'port': '1514',
+ 'protocol': 'udp',
+ },
+ 'syslog.vyos.net': {
+ 'facility': {'all' : {'level': 'debug'}},
+ 'port': '1515',
+ 'protocol': 'tcp',
+ },
+ '169.254.0.3': {
+ 'facility': {'auth' : {'level': 'info'},
+ 'kern' : {'level': 'debug'},
+ 'all' : {'level': 'notice'},
+ },
+ 'format': ['include-timezone', 'octet-counted'],
+ 'protocol': 'tcp',
+ 'port': '10514',
+ },
+ }
+ default_port = default_value(base_path + ['remote', next(iter(rhosts)), 'port'])
+ default_protocol = default_value(base_path + ['remote', next(iter(rhosts)), 'protocol'])
+
+ for remote, remote_options in rhosts.items():
+ remote_base = base_path + ['remote', remote]
+
+ if 'port' in remote_options:
+ self.cli_set(remote_base + ['port'], value=remote_options['port'])
+
+ if 'facility' in remote_options:
+ for facility, facility_options in remote_options['facility'].items():
+ level = facility_options['level']
+ self.cli_set(remote_base + ['facility', facility, 'level'],
+ value=level)
+
+ if 'format' in remote_options:
+ for format in remote_options['format']:
+ self.cli_set(remote_base + ['format'], value=format)
+
+ if 'protocol' in remote_options:
+ protocol = remote_options['protocol']
+ self.cli_set(remote_base + ['protocol'], value=protocol)
+
+ if 'source_address' in remote_options:
+ source_address = remote_options['source_address']
+ self.cli_set(remote_base + ['source-address', source_address])
+
+ # check validate() - source address does not exist
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+ self.cli_set(dummy_if_path + ['address', f'{source_address}/32'])
- # Change default port and enable "octet-counting" mode
- port = '10514'
- self.cli_set(base_path + ['host', rhost, 'port', port])
- self.cli_set(base_path + ['host', rhost, 'format', 'octet-counted'])
self.cli_commit()
config = read_file(RSYSLOG_CONF)
- self.assertIn(f'*.* @@(o){rhost}:{port}', config)
+ for remote, remote_options in rhosts.items():
+ config = get_config(f'# Remote syslog to {remote}')
+ prifilt = []
+ if 'facility' in remote_options:
+ for facility, facility_options in remote_options['facility'].items():
+ level = facility_options['level']
+ if facility == 'all':
+ facility = '*'
+ prifilt.append(f'{facility}.{level}')
+
+ prifilt.sort()
+ prifilt = ','.join(prifilt)
+ if not prifilt:
+ # Skip test - as we do not render anything if no facility is set
+ continue
+
+ self.assertIn(f'if prifilt("{prifilt}") then {{', config)
+ self.assertIn( ' type="omfwd"', config)
+ self.assertIn(f' target="{remote}"', config)
+
+ port = default_port
+ if 'port' in remote_options:
+ port = remote_options['port']
+ self.assertIn(f'port="{port}"', config)
+
+ protocol = default_protocol
+ if 'protocol' in remote_options:
+ protocol = remote_options['protocol']
+ self.assertIn(f'protocol="{protocol}"', config)
+
+ if 'format' in remote_options:
+ if 'include-timezone' in remote_options['format']:
+ self.assertIn( ' template="RSYSLOG_SyslogProtocol23Format"', config)
+
+ if 'octet-counted' in remote_options['format']:
+ self.assertIn( ' TCP_Framing="octet-counted"', config)
+ else:
+ self.assertIn( ' TCP_Framing="traditional"', config)
+
+ # cleanup dummy interface
+ self.cli_delete(dummy_if_path)
+
+ def test_vrf_source_address(self):
+ rhosts = {
+ '169.254.0.10': { },
+ '169.254.0.11': {
+ 'vrf': {'name' : 'red', 'table' : '12321'},
+ 'source_address' : '169.254.0.11',
+ },
+ '169.254.0.12': {
+ 'vrf': {'name' : 'green', 'table' : '12322'},
+ 'source_address' : '169.254.0.12',
+ },
+ '169.254.0.13': {
+ 'vrf': {'name' : 'blue', 'table' : '12323'},
+ 'source_address' : '169.254.0.13',
+ },
+ }
+
+ for remote, remote_options in rhosts.items():
+ remote_base = base_path + ['remote', remote]
+ self.cli_set(remote_base + ['facility', 'all'])
+
+ vrf = None
+ if 'vrf' in remote_options:
+ vrf = remote_options['vrf']['name']
+ self.cli_set(['vrf', 'name', vrf, 'table'],
+ value=remote_options['vrf']['table'])
+ self.cli_set(remote_base + ['vrf'], value=vrf)
+
+ if 'source_address' in remote_options:
+ source_address = remote_options['source_address']
+ self.cli_set(remote_base + ['source-address'],
+ value=source_address)
+
+ idx = source_address.split('.')[-1]
+ self.cli_set(['interfaces', 'dummy', f'dum{idx}', 'address'],
+ value=f'{source_address}/32')
+ if vrf:
+ self.cli_set(['interfaces', 'dummy', f'dum{idx}', 'vrf'],
+ value=vrf)
+
+ self.cli_commit()
+
+ for remote, remote_options in rhosts.items():
+ config = get_config(f'# Remote syslog to {remote}')
+
+ self.assertIn(f'target="{remote}"', config)
+ if 'vrf' in remote_options:
+ vrf = remote_options['vrf']['name']
+ self.assertIn(f'Device="{vrf}"', config)
+
+ if 'source_address' in remote_options:
+ source_address = remote_options['source_address']
+ self.assertIn(f'Address="{source_address}"', config)
+
+ # Cleanup VRF/Dummy interfaces
+ for remote, remote_options in rhosts.items():
+ if 'vrf' in remote_options:
+ vrf = remote_options['vrf']['name']
+ self.cli_delete(['vrf', 'name', vrf])
+ if 'source_address' in remote_options:
+ source_address = remote_options['source_address']
+ idx = source_address.split('.')[-1]
+ self.cli_delete(['interfaces', 'dummy', f'dum{idx}'])
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_vpn_ipsec.py b/smoketest/scripts/cli/test_vpn_ipsec.py
index f2bea58d1..c1d943bde 100755
--- a/smoketest/scripts/cli/test_vpn_ipsec.py
+++ b/smoketest/scripts/cli/test_vpn_ipsec.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2021-2024 VyOS maintainers and contributors
+# Copyright (C) 2021-2025 VyOS maintainers and contributors
#
# 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
@@ -352,25 +352,129 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase):
self.tearDownPKI()
+ def test_site_to_site_vti_ts_afi(self):
+ local_address = '192.0.2.10'
+ vti = 'vti10'
+ # IKE
+ self.cli_set(base_path + ['ike-group', ike_group, 'key-exchange', 'ikev2'])
+ self.cli_set(base_path + ['ike-group', ike_group, 'disable-mobike'])
+ # ESP
+ self.cli_set(base_path + ['esp-group', esp_group, 'compression'])
+ # VTI interface
+ self.cli_set(vti_path + [vti, 'address', '10.1.1.1/24'])
+
+ # vpn ipsec auth psk <tag> id <x.x.x.x>
+ self.cli_set(base_path + ['authentication', 'psk', connection_name, 'id', local_id])
+ self.cli_set(base_path + ['authentication', 'psk', connection_name, 'id', remote_id])
+ self.cli_set(base_path + ['authentication', 'psk', connection_name, 'id', peer_ip])
+ self.cli_set(base_path + ['authentication', 'psk', connection_name, 'secret', secret])
+
+ # Site to site
+ peer_base_path = base_path + ['site-to-site', 'peer', connection_name]
+ self.cli_set(peer_base_path + ['authentication', 'mode', 'pre-shared-secret'])
+ self.cli_set(peer_base_path + ['connection-type', 'none'])
+ self.cli_set(peer_base_path + ['force-udp-encapsulation'])
+ self.cli_set(peer_base_path + ['ike-group', ike_group])
+ self.cli_set(peer_base_path + ['default-esp-group', esp_group])
+ self.cli_set(peer_base_path + ['local-address', local_address])
+ self.cli_set(peer_base_path + ['remote-address', peer_ip])
+ self.cli_set(peer_base_path + ['vti', 'bind', vti])
+ self.cli_set(peer_base_path + ['vti', 'esp-group', esp_group])
+ self.cli_set(peer_base_path + ['vti', 'traffic-selector', 'local', 'prefix', '0.0.0.0/0'])
+ self.cli_set(peer_base_path + ['vti', 'traffic-selector', 'remote', 'prefix', '192.0.2.1/32'])
+ self.cli_set(peer_base_path + ['vti', 'traffic-selector', 'remote', 'prefix', '192.0.2.3/32'])
+
+ self.cli_commit()
+
+ swanctl_conf = read_file(swanctl_file)
+ if_id = vti.lstrip('vti')
+ # The key defaults to 0 and will match any policies which similarly do
+ # not have a lookup key configuration - thus we shift the key by one
+ # to also support a vti0 interface
+ if_id = str(int(if_id) +1)
+ swanctl_conf_lines = [
+ f'version = 2',
+ f'auth = psk',
+ f'proposals = aes128-sha1-modp1024',
+ f'esp_proposals = aes128-sha1-modp1024',
+ f'local_addrs = {local_address} # dhcp:no',
+ f'mobike = no',
+ f'remote_addrs = {peer_ip}',
+ f'mode = tunnel',
+ f'local_ts = 0.0.0.0/0',
+ f'remote_ts = 192.0.2.1/32,192.0.2.3/32',
+ f'ipcomp = yes',
+ f'start_action = none',
+ f'replay_window = 32',
+ f'if_id_in = {if_id}', # will be 11 for vti10 - shifted by one
+ f'if_id_out = {if_id}',
+ f'updown = "/etc/ipsec.d/vti-up-down {vti}"'
+ ]
+ for line in swanctl_conf_lines:
+ self.assertIn(line, swanctl_conf)
+
+ # Check IPv6 TS
+ self.cli_delete(peer_base_path + ['vti', 'traffic-selector'])
+ self.cli_set(peer_base_path + ['vti', 'traffic-selector', 'local', 'prefix', '::/0'])
+ self.cli_set(peer_base_path + ['vti', 'traffic-selector', 'remote', 'prefix', '::/0'])
+ self.cli_commit()
+ swanctl_conf = read_file(swanctl_file)
+ swanctl_conf_lines = [
+ f'local_ts = ::/0',
+ f'remote_ts = ::/0',
+ f'updown = "/etc/ipsec.d/vti-up-down {vti}"'
+ ]
+ for line in swanctl_conf_lines:
+ self.assertIn(line, swanctl_conf)
+
+ # Check both TS (IPv4 + IPv6)
+ self.cli_delete(peer_base_path + ['vti', 'traffic-selector'])
+ self.cli_commit()
+ swanctl_conf = read_file(swanctl_file)
+ swanctl_conf_lines = [
+ f'local_ts = 0.0.0.0/0,::/0',
+ f'remote_ts = 0.0.0.0/0,::/0',
+ f'updown = "/etc/ipsec.d/vti-up-down {vti}"'
+ ]
+ for line in swanctl_conf_lines:
+ self.assertIn(line, swanctl_conf)
+
+
def test_dmvpn(self):
- tunnel_if = 'tun100'
- nhrp_secret = 'secret'
ike_lifetime = '3600'
esp_lifetime = '1800'
+ tunnel_if = "tun100"
+ tunnel_ip = '172.16.253.134/32'
+ tunnel_source = "192.0.2.134"
+ tunnel_encapsulation = "gre"
+ esp_group = "ESP-HUB"
+ ike_group = "IKE-HUB"
+ nhrp_secret = "vyos123"
+ nhrp_holdtime = '300'
+ nhs_tunnelip = '172.16.253.1'
+ nhs_nbmaip = '192.0.2.1'
+ map_tunnelip = '172.16.253.135'
+ map_nbmaip = "192.0.2.135"
+ nhrp_networkid = '1'
+
# Tunnel
- self.cli_set(tunnel_path + [tunnel_if, 'address', '172.16.253.134/29'])
- self.cli_set(tunnel_path + [tunnel_if, 'encapsulation', 'gre'])
- self.cli_set(tunnel_path + [tunnel_if, 'source-address', '192.0.2.1'])
- self.cli_set(tunnel_path + [tunnel_if, 'enable-multicast'])
- self.cli_set(tunnel_path + [tunnel_if, 'parameters', 'ip', 'key', '1'])
+ self.cli_set(tunnel_path + [tunnel_if, "address", tunnel_ip])
+ self.cli_set(tunnel_path + [tunnel_if, "encapsulation", tunnel_encapsulation])
+ self.cli_set(tunnel_path + [tunnel_if, "source-address", tunnel_source])
+ self.cli_set(tunnel_path + [tunnel_if, "enable-multicast"])
+ self.cli_set(tunnel_path + [tunnel_if, "parameters", "ip", "key", "1"])
# NHRP
- self.cli_set(nhrp_path + ['tunnel', tunnel_if, 'cisco-authentication', nhrp_secret])
- self.cli_set(nhrp_path + ['tunnel', tunnel_if, 'holding-time', '300'])
- self.cli_set(nhrp_path + ['tunnel', tunnel_if, 'multicast', 'dynamic'])
- self.cli_set(nhrp_path + ['tunnel', tunnel_if, 'redirect'])
- self.cli_set(nhrp_path + ['tunnel', tunnel_if, 'shortcut'])
+ self.cli_set(nhrp_path + ["tunnel", tunnel_if, "authentication", nhrp_secret])
+ self.cli_set(nhrp_path + ["tunnel", tunnel_if, "holdtime", nhrp_holdtime])
+ self.cli_set(nhrp_path + ["tunnel", tunnel_if, "multicast", nhs_tunnelip])
+ self.cli_set(nhrp_path + ["tunnel", tunnel_if, "redirect"])
+ self.cli_set(nhrp_path + ["tunnel", tunnel_if, "shortcut"])
+ self.cli_set(nhrp_path + ["tunnel", tunnel_if, "registration-no-unique"])
+ self.cli_set(nhrp_path + ["tunnel", tunnel_if, "network-id", nhrp_networkid])
+ self.cli_set(nhrp_path + ["tunnel", tunnel_if, "nhs", "tunnel-ip", nhs_tunnelip, "nbma", nhs_nbmaip])
+ self.cli_set(nhrp_path + ["tunnel", tunnel_if, "map", "tunnel-ip", map_tunnelip, "nbma", map_nbmaip])
# IKE/ESP Groups
self.cli_set(base_path + ['esp-group', esp_group, 'lifetime', esp_lifetime])
@@ -399,11 +503,11 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase):
swanctl_conf = read_file(swanctl_file)
swanctl_lines = [
- f'proposals = aes128-sha1-modp1024,aes256-sha1-prfsha1-modp1024',
+ f'proposals = aes256-sha1-prfsha1-modp1024',
f'version = 1',
f'rekey_time = {ike_lifetime}s',
f'rekey_time = {esp_lifetime}s',
- f'esp_proposals = aes128-sha1-modp1024,aes256-sha1-modp1024,3des-md5-modp1024',
+ f'esp_proposals = aes256-sha1-modp1024,3des-md5-modp1024',
f'local_ts = dynamic[gre]',
f'remote_ts = dynamic[gre]',
f'mode = transport',
diff --git a/smoketest/scripts/cli/test_vrf.py b/smoketest/scripts/cli/test_vrf.py
index f4ed1a61f..30980f9ec 100755
--- a/smoketest/scripts/cli/test_vrf.py
+++ b/smoketest/scripts/cli/test_vrf.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2020-2024 VyOS maintainers and contributors
+# Copyright (C) 2020-2025 VyOS maintainers and contributors
#
# 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
@@ -18,12 +18,13 @@ import re
import os
import unittest
-from base_vyostest_shim import VyOSUnitTestSHIM
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.frrender import mgmt_daemon
from vyos.ifconfig import Interface
from vyos.ifconfig import Section
from vyos.utils.file import read_file
@@ -52,6 +53,10 @@ class VRFTest(VyOSUnitTestSHIM.TestCase):
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()
@@ -113,7 +118,7 @@ class VRFTest(VyOSUnitTestSHIM.TestCase):
regex = f'{table}\s+{vrf}\s+#\s+{description}'
self.assertTrue(re.findall(regex, iproute2_config))
- frrconfig = self.getFRRconfig(f'vrf {vrf}', daemon=mgmt_daemon)
+ frrconfig = self.getFRRconfig(f'vrf {vrf}', endsection='^exit-vrf')
self.assertIn(f' vni {table}', frrconfig)
self.assertEqual(int(table), get_vrf_tableid(vrf))
@@ -234,7 +239,7 @@ class VRFTest(VyOSUnitTestSHIM.TestCase):
self.assertTrue(interface_exists(vrf))
- frrconfig = self.getFRRconfig(f'vrf {vrf}', daemon=mgmt_daemon)
+ frrconfig = self.getFRRconfig(f'vrf {vrf}', endsection='^exit-vrf')
self.assertIn(f' vni {table}', frrconfig)
self.assertIn(f' ip route {prefix} {next_hop}', frrconfig)
@@ -318,7 +323,7 @@ class VRFTest(VyOSUnitTestSHIM.TestCase):
# Verify route-map properly applied to FRR
for vrf in vrfs:
- frrconfig = self.getFRRconfig(f'vrf {vrf}', daemon=mgmt_daemon)
+ 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)
@@ -333,7 +338,7 @@ class VRFTest(VyOSUnitTestSHIM.TestCase):
# Verify route-map properly is removed from FRR
for vrf in vrfs:
- frrconfig = self.getFRRconfig(f'vrf {vrf}', daemon=mgmt_daemon)
+ 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):
@@ -381,7 +386,7 @@ class VRFTest(VyOSUnitTestSHIM.TestCase):
# Verify route-map properly applied to FRR
for vrf in vrfs:
- frrconfig = self.getFRRconfig(f'vrf {vrf}', daemon=mgmt_daemon)
+ 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)
@@ -400,7 +405,7 @@ class VRFTest(VyOSUnitTestSHIM.TestCase):
# Verify route-map properly is removed from FRR
for vrf in vrfs:
- frrconfig = self.getFRRconfig(f'vrf {vrf}', daemon=mgmt_daemon)
+ frrconfig = self.getFRRconfig(f'vrf {vrf}', endsection='^exit-vrf')
self.assertNotIn(f' ipv6 protocol', frrconfig)
def test_vrf_vni_duplicates(self):
@@ -430,7 +435,7 @@ class VRFTest(VyOSUnitTestSHIM.TestCase):
for vrf in vrfs:
self.assertTrue(interface_exists(vrf))
- frrconfig = self.getFRRconfig(f'vrf {vrf}', daemon=mgmt_daemon)
+ 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)
@@ -452,7 +457,7 @@ class VRFTest(VyOSUnitTestSHIM.TestCase):
for vrf in vrfs:
self.assertTrue(interface_exists(vrf))
- frrconfig = self.getFRRconfig(f'vrf {vrf}', daemon=mgmt_daemon)
+ 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)
@@ -475,7 +480,7 @@ class VRFTest(VyOSUnitTestSHIM.TestCase):
for vrf in vrfs:
self.assertTrue(interface_exists(vrf))
- frrconfig = self.getFRRconfig(f'vrf {vrf}', daemon=mgmt_daemon)
+ 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)
@@ -495,7 +500,7 @@ class VRFTest(VyOSUnitTestSHIM.TestCase):
for vrf in vrfs:
self.assertTrue(interface_exists(vrf))
- frrconfig = self.getFRRconfig(f'vrf {vrf}', daemon=mgmt_daemon)
+ 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)
@@ -503,7 +508,7 @@ class VRFTest(VyOSUnitTestSHIM.TestCase):
# Verify purple VRF/VNI
self.assertTrue(interface_exists(purple))
table = str(int(table) + 10)
- frrconfig = self.getFRRconfig(f'vrf {purple}', daemon=mgmt_daemon)
+ frrconfig = self.getFRRconfig(f'vrf {purple}', endsection='^exit-vrf')
self.assertIn(f' vni {table}', frrconfig)
# Now delete all the VNIs
@@ -518,12 +523,12 @@ class VRFTest(VyOSUnitTestSHIM.TestCase):
for vrf in vrfs:
self.assertTrue(interface_exists(vrf))
- frrconfig = self.getFRRconfig(f'vrf {vrf}', daemon=mgmt_daemon)
+ 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}', daemon=mgmt_daemon)
+ frrconfig = self.getFRRconfig(f'vrf {purple}', endsection='^exit-vrf')
self.assertIn(f' vni {table}', frrconfig)
def test_vrf_ip_ipv6_nht(self):
@@ -541,7 +546,7 @@ class VRFTest(VyOSUnitTestSHIM.TestCase):
# Verify route-map properly applied to FRR
for vrf in vrfs:
- frrconfig = self.getFRRconfig(f'vrf {vrf}', daemon=mgmt_daemon)
+ 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)
@@ -556,7 +561,7 @@ class VRFTest(VyOSUnitTestSHIM.TestCase):
# Verify route-map properly is removed from FRR
for vrf in vrfs:
- frrconfig = self.getFRRconfig(f'vrf {vrf}', daemon=mgmt_daemon)
+ 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)