summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--debian/control3
-rwxr-xr-xdebian/vyos-1x-smoketest.postinst15
-rw-r--r--interface-definitions/container.xml.in2
-rw-r--r--interface-definitions/include/haproxy/rule-backend.xml.i2
-rw-r--r--interface-definitions/include/haproxy/rule-frontend.xml.i5
-rw-r--r--interface-definitions/xml-component-version.xml.in50
-rw-r--r--python/vyos/ifconfig/bond.py4
-rw-r--r--python/vyos/ifconfig/bridge.py4
-rw-r--r--python/vyos/ifconfig/dummy.py5
-rw-r--r--python/vyos/ifconfig/ethernet.py6
-rw-r--r--python/vyos/ifconfig/geneve.py3
-rw-r--r--python/vyos/ifconfig/input.py5
-rw-r--r--python/vyos/ifconfig/interface.py37
-rw-r--r--python/vyos/ifconfig/l2tpv3.py1
-rw-r--r--python/vyos/ifconfig/loopback.py6
-rw-r--r--python/vyos/ifconfig/macsec.py3
-rw-r--r--python/vyos/ifconfig/macvlan.py5
-rw-r--r--python/vyos/ifconfig/pppoe.py1
-rw-r--r--python/vyos/ifconfig/sstpc.py1
-rw-r--r--python/vyos/ifconfig/tunnel.py9
-rw-r--r--python/vyos/ifconfig/veth.py3
-rw-r--r--python/vyos/ifconfig/vrrp.py3
-rw-r--r--python/vyos/ifconfig/vti.py1
-rw-r--r--python/vyos/ifconfig/vtun.py1
-rw-r--r--python/vyos/ifconfig/vxlan.py4
-rw-r--r--python/vyos/ifconfig/wireguard.py9
-rw-r--r--python/vyos/ifconfig/wireless.py1
-rw-r--r--python/vyos/ifconfig/wwan.py1
-rw-r--r--python/vyos/utils/process.py52
-rwxr-xr-xscripts/transclude-template5
-rw-r--r--smoketest/config-tests/bgp-big-as-cloud56
-rw-r--r--smoketest/config-tests/dialup-router-complex22
-rw-r--r--smoketest/config-tests/dialup-router-wireguard-ipv616
-rw-r--r--smoketest/scripts/cli/base_interfaces_test.py4
-rw-r--r--smoketest/scripts/cli/base_vyostest_shim.py5
-rwxr-xr-xsmoketest/scripts/cli/test_container.py37
-rwxr-xr-xsmoketest/scripts/cli/test_nat.py2
-rwxr-xr-xsmoketest/scripts/cli/test_policy.py14
-rw-r--r--smoketest/scripts/cli/test_policy_local-route.py5
-rwxr-xr-xsmoketest/scripts/cli/test_policy_route.py5
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_babel.py3
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_bfd.py7
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_bgp.py4
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_isis.py6
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_mpls.py5
-rw-r--r--smoketest/scripts/cli/test_protocols_openfabric.py4
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_ospf.py3
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_ospfv3.py3
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_pim.py11
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_pim6.py4
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_rip.py3
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_ripng.py5
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_rpki.py3
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_segment-routing.py3
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_static.py5
-rwxr-xr-xsmoketest/scripts/cli/test_system_login.py160
-rwxr-xr-xsmoketest/scripts/cli/test_vrf.py10
-rwxr-xr-xsrc/conf_mode/vrf.py5
-rwxr-xr-xsrc/op_mode/dhcp.py4
-rwxr-xr-xsrc/services/vyos-configd16
60 files changed, 443 insertions, 234 deletions
diff --git a/debian/control b/debian/control
index b1cb5c4ef..a0d475d56 100644
--- a/debian/control
+++ b/debian/control
@@ -40,7 +40,8 @@ Pre-Depends:
libpam-runtime [amd64],
libnss-tacplus [amd64],
libpam-tacplus [amd64],
- libpam-radius-auth [amd64]
+ libpam-radius-auth (= 1.5.0-cl3u7) [amd64],
+ libnss-mapuser (= 1.1.0-cl3u3) [amd64]
Depends:
## Fundamentals
${python3:Depends} (>= 3.10),
diff --git a/debian/vyos-1x-smoketest.postinst b/debian/vyos-1x-smoketest.postinst
index 18612804c..57149af82 100755
--- a/debian/vyos-1x-smoketest.postinst
+++ b/debian/vyos-1x-smoketest.postinst
@@ -1,10 +1,15 @@
#!/bin/sh -e
BUSYBOX_TAG="docker.io/library/busybox:stable"
-OUTPUT_PATH="/usr/share/vyos/busybox-stable.tar"
-
-if [[ -f $OUTPUT_PATH ]]; then
- rm -f $OUTPUT_PATH
+BUSYBOX_PATH="/usr/share/vyos/busybox-stable.tar"
+if [[ -f $BUSYBOX_PATH ]]; then
+ rm -f $BUSYBOX_PATH
fi
+skopeo copy --additional-tag "$BUSYBOX_TAG" "docker://$BUSYBOX_TAG" "docker-archive:/$BUSYBOX_PATH"
-skopeo copy --additional-tag "$BUSYBOX_TAG" "docker://$BUSYBOX_TAG" "docker-archive:/$OUTPUT_PATH"
+TACPLUS_TAG="docker.io/lfkeitel/tacacs_plus:alpine"
+TACPLUS_PATH="/usr/share/vyos/tacplus-alpine.tar"
+if [[ -f $TACPLUS_PATH ]]; then
+ rm -f $TACPLUS_PATH
+fi
+skopeo copy --additional-tag "$TACPLUS_TAG" "docker://$TACPLUS_TAG" "docker-archive:/$TACPLUS_PATH"
diff --git a/interface-definitions/container.xml.in b/interface-definitions/container.xml.in
index ad1815604..04318a7c9 100644
--- a/interface-definitions/container.xml.in
+++ b/interface-definitions/container.xml.in
@@ -131,7 +131,7 @@
<properties>
<help>Add custom environment variables</help>
<constraint>
- <regex>[-_a-zA-Z0-9]+</regex>
+ #include <include/constraint/alpha-numeric-hyphen-underscore.xml.i>
</constraint>
<constraintErrorMessage>Environment variable name must be alphanumeric and can contain hyphen and underscores</constraintErrorMessage>
</properties>
diff --git a/interface-definitions/include/haproxy/rule-backend.xml.i b/interface-definitions/include/haproxy/rule-backend.xml.i
index b2be4fde4..1df9d5dcf 100644
--- a/interface-definitions/include/haproxy/rule-backend.xml.i
+++ b/interface-definitions/include/haproxy/rule-backend.xml.i
@@ -47,7 +47,7 @@
<properties>
<help>Server name</help>
<constraint>
- <regex>[-_a-zA-Z0-9]+</regex>
+ #include <include/constraint/alpha-numeric-hyphen-underscore.xml.i>
</constraint>
<constraintErrorMessage>Server name must be alphanumeric and can contain hyphen and underscores</constraintErrorMessage>
</properties>
diff --git a/interface-definitions/include/haproxy/rule-frontend.xml.i b/interface-definitions/include/haproxy/rule-frontend.xml.i
index 001ae2d80..eabdd8632 100644
--- a/interface-definitions/include/haproxy/rule-frontend.xml.i
+++ b/interface-definitions/include/haproxy/rule-frontend.xml.i
@@ -47,9 +47,12 @@
<properties>
<help>Backend name</help>
<constraint>
- <regex>[-_a-zA-Z0-9]+</regex>
+ #include <include/constraint/alpha-numeric-hyphen-underscore.xml.i>
</constraint>
<constraintErrorMessage>Server name must be alphanumeric and can contain hyphen and underscores</constraintErrorMessage>
+ <completionHelp>
+ <path>load-balancing haproxy backend</path>
+ </completionHelp>
</properties>
</leafNode>
</children>
diff --git a/interface-definitions/xml-component-version.xml.in b/interface-definitions/xml-component-version.xml.in
index 67d86a1d0..70957c5fa 100644
--- a/interface-definitions/xml-component-version.xml.in
+++ b/interface-definitions/xml-component-version.xml.in
@@ -1,52 +1,4 @@
<?xml version="1.0"?>
<interfaceDefinition>
- #include <include/version/bgp-version.xml.i>
- #include <include/version/broadcast-relay-version.xml.i>
- #include <include/version/cluster-version.xml.i>
- #include <include/version/config-management-version.xml.i>
- #include <include/version/conntrack-sync-version.xml.i>
- #include <include/version/conntrack-version.xml.i>
- #include <include/version/container-version.xml.i>
- #include <include/version/dhcp-relay-version.xml.i>
- #include <include/version/dhcp-server-version.xml.i>
- #include <include/version/dhcpv6-server-version.xml.i>
- #include <include/version/dns-dynamic-version.xml.i>
- #include <include/version/dns-forwarding-version.xml.i>
- #include <include/version/firewall-version.xml.i>
- #include <include/version/flow-accounting-version.xml.i>
- #include <include/version/https-version.xml.i>
- #include <include/version/interfaces-version.xml.i>
- #include <include/version/ids-version.xml.i>
- #include <include/version/ipoe-server-version.xml.i>
- #include <include/version/ipsec-version.xml.i>
- #include <include/version/openvpn-version.xml.i>
- #include <include/version/isis-version.xml.i>
- #include <include/version/l2tp-version.xml.i>
- #include <include/version/lldp-version.xml.i>
- #include <include/version/mdns-version.xml.i>
- #include <include/version/monitoring-version.xml.i>
- #include <include/version/nat66-version.xml.i>
- #include <include/version/nat-version.xml.i>
- #include <include/version/ntp-version.xml.i>
- #include <include/version/openconnect-version.xml.i>
- #include <include/version/ospf-version.xml.i>
- #include <include/version/pim-version.xml.i>
- #include <include/version/policy-version.xml.i>
- #include <include/version/pppoe-server-version.xml.i>
- #include <include/version/pptp-version.xml.i>
- #include <include/version/qos-version.xml.i>
- #include <include/version/quagga-version.xml.i>
- #include <include/version/rip-version.xml.i>
- #include <include/version/rpki-version.xml.i>
- #include <include/version/salt-version.xml.i>
- #include <include/version/snmp-version.xml.i>
- #include <include/version/ssh-version.xml.i>
- #include <include/version/sstp-version.xml.i>
- #include <include/version/system-version.xml.i>
- #include <include/version/vrf-version.xml.i>
- #include <include/version/vrrp-version.xml.i>
- #include <include/version/vyos-accel-ppp-version.xml.i>
- #include <include/version/wanloadbalance-version.xml.i>
- #include <include/version/webproxy-version.xml.i>
- #include <include/version/reverseproxy-version.xml.i>
+ #include <include/version/*>
</interfaceDefinition>
diff --git a/python/vyos/ifconfig/bond.py b/python/vyos/ifconfig/bond.py
index 8ba481728..a659b9bd2 100644
--- a/python/vyos/ifconfig/bond.py
+++ b/python/vyos/ifconfig/bond.py
@@ -31,7 +31,6 @@ class BondIf(Interface):
monitoring may be performed.
"""
- iftype = 'bond'
definition = {
**Interface.definition,
** {
@@ -109,6 +108,9 @@ class BondIf(Interface):
]
return options
+ def _create(self):
+ super()._create('bond')
+
def remove(self):
"""
Remove interface from operating system. Removing the interface
diff --git a/python/vyos/ifconfig/bridge.py b/python/vyos/ifconfig/bridge.py
index 917f962b7..d534dade7 100644
--- a/python/vyos/ifconfig/bridge.py
+++ b/python/vyos/ifconfig/bridge.py
@@ -32,7 +32,6 @@ class BridgeIf(Interface):
The Linux bridge code implements a subset of the ANSI/IEEE 802.1d standard.
"""
- iftype = 'bridge'
definition = {
**Interface.definition,
**{
@@ -107,6 +106,9 @@ class BridgeIf(Interface):
},
}}
+ def _create(self):
+ super()._create('bridge')
+
def get_vlan_filter(self):
"""
Get the status of the bridge VLAN filter
diff --git a/python/vyos/ifconfig/dummy.py b/python/vyos/ifconfig/dummy.py
index d45769931..29a1965a3 100644
--- a/python/vyos/ifconfig/dummy.py
+++ b/python/vyos/ifconfig/dummy.py
@@ -22,8 +22,6 @@ class DummyIf(Interface):
interface. The purpose of a dummy interface is to provide a device to route
packets through without actually transmitting them.
"""
-
- iftype = 'dummy'
definition = {
**Interface.definition,
**{
@@ -31,3 +29,6 @@ class DummyIf(Interface):
'prefixes': ['dum', ],
},
}
+
+ def _create(self):
+ super()._create('dummy')
diff --git a/python/vyos/ifconfig/ethernet.py b/python/vyos/ifconfig/ethernet.py
index d0c03dbe0..93727bdf6 100644
--- a/python/vyos/ifconfig/ethernet.py
+++ b/python/vyos/ifconfig/ethernet.py
@@ -33,7 +33,6 @@ class EthernetIf(Interface):
Abstraction of a Linux Ethernet Interface
"""
- iftype = 'ethernet'
definition = {
**Interface.definition,
**{
@@ -119,6 +118,9 @@ class EthernetIf(Interface):
super().__init__(ifname, **kargs)
self.ethtool = Ethtool(ifname)
+ def _create(self):
+ pass
+
def remove(self):
"""
Remove interface from config. Removing the interface deconfigures all
@@ -137,7 +139,7 @@ class EthernetIf(Interface):
# Remove all VLAN subinterfaces - filter with the VLAN dot
for vlan in [
x
- for x in Section.interfaces(self.iftype)
+ for x in Section.interfaces('ethernet')
if x.startswith(f'{self.ifname}.')
]:
Interface(vlan).remove()
diff --git a/python/vyos/ifconfig/geneve.py b/python/vyos/ifconfig/geneve.py
index fbb261a35..f7fddb812 100644
--- a/python/vyos/ifconfig/geneve.py
+++ b/python/vyos/ifconfig/geneve.py
@@ -27,7 +27,6 @@ class GeneveIf(Interface):
https://developers.redhat.com/blog/2019/05/17/an-introduction-to-linux-virtual-interfaces-tunnels/#geneve
https://lwn.net/Articles/644938/
"""
- iftype = 'geneve'
definition = {
**Interface.definition,
**{
@@ -49,7 +48,7 @@ class GeneveIf(Interface):
'parameters.ipv6.flowlabel' : 'flowlabel',
}
- cmd = 'ip link add name {ifname} type {type} id {vni} remote {remote}'
+ cmd = 'ip link add name {ifname} type geneve id {vni} remote {remote}'
for vyos_key, iproute2_key in mapping.items():
# dict_search will return an empty dict "{}" for valueless nodes like
# "parameters.nolearning" - thus we need to test the nodes existence
diff --git a/python/vyos/ifconfig/input.py b/python/vyos/ifconfig/input.py
index 3e5f5790d..201d3cacb 100644
--- a/python/vyos/ifconfig/input.py
+++ b/python/vyos/ifconfig/input.py
@@ -25,8 +25,6 @@ class InputIf(Interface):
a single stack of qdiscs, classes and filters can be shared between
multiple interfaces.
"""
-
- iftype = 'ifb'
definition = {
**Interface.definition,
**{
@@ -34,3 +32,6 @@ class InputIf(Interface):
'prefixes': ['ifb', ],
},
}
+
+ def _create(self):
+ super()._create('ifb')
diff --git a/python/vyos/ifconfig/interface.py b/python/vyos/ifconfig/interface.py
index de821ab60..cb73e2597 100644
--- a/python/vyos/ifconfig/interface.py
+++ b/python/vyos/ifconfig/interface.py
@@ -1,4 +1,4 @@
-# Copyright 2019-2024 VyOS maintainers and contributors <maintainers@vyos.io>
+# Copyright 2019-2025 VyOS maintainers and contributors <maintainers@vyos.io>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@@ -29,7 +29,6 @@ from netifaces import AF_INET6
from netaddr import EUI
from netaddr import mac_unix_expanded
-from vyos.base import ConfigError
from vyos.configdict import list_diff
from vyos.configdict import dict_merge
from vyos.configdict import get_vlan_ids
@@ -74,7 +73,6 @@ class Interface(Control):
OperationalClass = Operational
options = ['debug', 'create']
- required = []
default = {
'debug': True,
'create': True,
@@ -336,22 +334,10 @@ class Interface(Control):
super().__init__(**kargs)
if not self.exists(ifname):
- # Any instance of Interface, such as Interface('eth0') can be used
- # safely to access the generic function in this class as 'type' is
- # unset, the class can not be created
- if not hasattr(self, 'iftype'):
- raise ConfigError(f'Interface "{ifname}" has no "iftype" attribute defined!')
- self.config['type'] = self.iftype
-
# Should an Instance of a child class (EthernetIf, DummyIf, ..)
# be required, then create should be set to False to not accidentally create it.
# In case a subclass does not define it, we use get to set the default to True
- if self.config.get('create',True):
- for k in self.required:
- if k not in kargs:
- name = self.default['type']
- raise ConfigError(f'missing required option {k} for {name} {ifname} creation')
-
+ if self.config.get('create', True):
self._create()
# If we can not connect to the interface then let the caller know
# as the class could not be correctly initialised
@@ -364,13 +350,14 @@ class Interface(Control):
self.operational = self.OperationalClass(ifname)
self.vrrp = VRRP(ifname)
- def _create(self):
+ def _create(self, type: str=''):
# Do not create interface that already exist or exists in netns
netns = self.config.get('netns', None)
if self.exists(f'{self.ifname}', netns=netns):
return
- cmd = 'ip link add dev {ifname} type {type}'.format(**self.config)
+ cmd = f'ip link add dev {self.ifname}'
+ if type: cmd += f' type {type}'
if 'netns' in self.config: cmd = f'ip netns exec {netns} {cmd}'
self._cmd(cmd)
@@ -1423,13 +1410,11 @@ class Interface(Control):
tmp = get_interface_address(self.ifname)
if tmp and 'addr_info' in tmp:
for address_dict in tmp['addr_info']:
- if address_dict['family'] == 'inet':
- # Only remove dynamic assigned addresses
- if 'dynamic' not in address_dict:
- continue
- address = address_dict['local']
- prefixlen = address_dict['prefixlen']
- self.del_addr(f'{address}/{prefixlen}')
+ # Only remove dynamic assigned addresses
+ if address_dict['family'] == 'inet' and 'dynamic' in address_dict:
+ address = address_dict['local']
+ prefixlen = address_dict['prefixlen']
+ self.del_addr(f'{address}/{prefixlen}')
# cleanup old config files
for file in [dhclient_config_file, systemd_override_file, dhclient_lease_file]:
@@ -1954,8 +1939,6 @@ class Interface(Control):
class VLANIf(Interface):
""" Specific class which abstracts 802.1q and 802.1ad (Q-in-Q) VLAN interfaces """
- iftype = 'vlan'
-
def _create(self):
# bail out early if interface already exists
if self.exists(f'{self.ifname}'):
diff --git a/python/vyos/ifconfig/l2tpv3.py b/python/vyos/ifconfig/l2tpv3.py
index c1f2803ee..dfaa006aa 100644
--- a/python/vyos/ifconfig/l2tpv3.py
+++ b/python/vyos/ifconfig/l2tpv3.py
@@ -45,7 +45,6 @@ class L2TPv3If(Interface):
either hot standby or load balancing services. Additionally, link integrity
monitoring may be performed.
"""
- iftype = 'l2tp'
definition = {
**Interface.definition,
**{
diff --git a/python/vyos/ifconfig/loopback.py b/python/vyos/ifconfig/loopback.py
index e1d041839..13e8a2c50 100644
--- a/python/vyos/ifconfig/loopback.py
+++ b/python/vyos/ifconfig/loopback.py
@@ -22,16 +22,20 @@ class LoopbackIf(Interface):
uses to communicate with itself.
"""
_persistent_addresses = ['127.0.0.1/8', '::1/128']
- iftype = 'loopback'
definition = {
**Interface.definition,
**{
'section': 'loopback',
'prefixes': ['lo', ],
'bridgeable': True,
+ 'eternal': 'lo$',
}
}
+ def _create(self):
+ # we can not create this interface as it is managed by the Kernel
+ pass
+
def remove(self):
"""
Loopback interface can not be deleted from operating system. We can
diff --git a/python/vyos/ifconfig/macsec.py b/python/vyos/ifconfig/macsec.py
index 383905814..3b4dc223f 100644
--- a/python/vyos/ifconfig/macsec.py
+++ b/python/vyos/ifconfig/macsec.py
@@ -27,7 +27,6 @@ class MACsecIf(Interface):
other security solutions such as IPsec (layer 3) or TLS (layer 4), as all
those solutions are used for their own specific use cases.
"""
- iftype = 'macsec'
definition = {
**Interface.definition,
**{
@@ -43,7 +42,7 @@ class MACsecIf(Interface):
"""
# create tunnel interface
- cmd = 'ip link add link {source_interface} {ifname} type {type}'.format(**self.config)
+ cmd = 'ip link add link {source_interface} {ifname} type macsec'.format(**self.config)
cmd += f' cipher {self.config["security"]["cipher"]}'
if 'encrypt' in self.config["security"]:
diff --git a/python/vyos/ifconfig/macvlan.py b/python/vyos/ifconfig/macvlan.py
index fb7f1d298..fe948b920 100644
--- a/python/vyos/ifconfig/macvlan.py
+++ b/python/vyos/ifconfig/macvlan.py
@@ -20,7 +20,6 @@ class MACVLANIf(Interface):
"""
Abstraction of a Linux MACvlan interface
"""
- iftype = 'macvlan'
definition = {
**Interface.definition,
**{
@@ -35,12 +34,12 @@ class MACVLANIf(Interface):
down by default.
"""
# please do not change the order when assembling the command
- cmd = 'ip link add {ifname} link {source_interface} type {type} mode {mode}'
+ cmd = 'ip link add {ifname} link {source_interface} type macvlan mode {mode}'
self._cmd(cmd.format(**self.config))
# interface is always A/D down. It needs to be enabled explicitly
self.set_admin_state('down')
def set_mode(self, mode):
- cmd = f'ip link set dev {self.ifname} type {self.iftype} mode {mode}'
+ cmd = f'ip link set dev {self.ifname} type macvlan mode {mode}'
return self._cmd(cmd)
diff --git a/python/vyos/ifconfig/pppoe.py b/python/vyos/ifconfig/pppoe.py
index f80a68d4f..85ca3877e 100644
--- a/python/vyos/ifconfig/pppoe.py
+++ b/python/vyos/ifconfig/pppoe.py
@@ -19,7 +19,6 @@ from vyos.utils.network import get_interface_config
@Interface.register
class PPPoEIf(Interface):
- iftype = 'pppoe'
definition = {
**Interface.definition,
**{
diff --git a/python/vyos/ifconfig/sstpc.py b/python/vyos/ifconfig/sstpc.py
index 50fc6ee6b..d92ef23dc 100644
--- a/python/vyos/ifconfig/sstpc.py
+++ b/python/vyos/ifconfig/sstpc.py
@@ -17,7 +17,6 @@ from vyos.ifconfig.interface import Interface
@Interface.register
class SSTPCIf(Interface):
- iftype = 'sstpc'
definition = {
**Interface.definition,
**{
diff --git a/python/vyos/ifconfig/tunnel.py b/python/vyos/ifconfig/tunnel.py
index 9ba7b31a6..df904f7d5 100644
--- a/python/vyos/ifconfig/tunnel.py
+++ b/python/vyos/ifconfig/tunnel.py
@@ -90,9 +90,8 @@ class TunnelIf(Interface):
# T3357: we do not have the 'encapsulation' in kargs when calling this
# class from op-mode like "show interfaces tunnel"
if 'encapsulation' in kargs:
- self.iftype = kargs['encapsulation']
# The gretap interface has the possibility to act as L2 bridge
- if self.iftype in ['gretap', 'ip6gretap']:
+ if kargs['encapsulation'] in ['gretap', 'ip6gretap']:
# no multicast, ttl or tos for gretap
self.definition = {
**TunnelIf.definition,
@@ -110,10 +109,10 @@ class TunnelIf(Interface):
mapping = { **self.mapping, **self.mapping_ipv4 }
cmd = 'ip tunnel add {ifname} mode {encapsulation}'
- if self.iftype in ['gretap', 'ip6gretap', 'erspan', 'ip6erspan']:
+ if self.config['encapsulation'] in ['gretap', 'ip6gretap', 'erspan', 'ip6erspan']:
cmd = 'ip link add name {ifname} type {encapsulation}'
# ERSPAN requires the serialisation of packets
- if self.iftype in ['erspan', 'ip6erspan']:
+ if self.config['encapsulation'] in ['erspan', 'ip6erspan']:
cmd += ' seq'
for vyos_key, iproute2_key in mapping.items():
@@ -132,7 +131,7 @@ class TunnelIf(Interface):
def _change_options(self):
# gretap interfaces do not support changing any parameter
- if self.iftype in ['gretap', 'ip6gretap', 'erspan', 'ip6erspan']:
+ if self.config['encapsulation'] in ['gretap', 'ip6gretap', 'erspan', 'ip6erspan']:
return
if self.config['encapsulation'] in ['ipip6', 'ip6ip6', 'ip6gre']:
diff --git a/python/vyos/ifconfig/veth.py b/python/vyos/ifconfig/veth.py
index aafbf226a..2c8709d20 100644
--- a/python/vyos/ifconfig/veth.py
+++ b/python/vyos/ifconfig/veth.py
@@ -21,7 +21,6 @@ class VethIf(Interface):
"""
Abstraction of a Linux veth interface
"""
- iftype = 'veth'
definition = {
**Interface.definition,
**{
@@ -46,7 +45,7 @@ class VethIf(Interface):
return
# create virtual-ethernet interface
- cmd = 'ip link add {ifname} type {type}'.format(**self.config)
+ cmd = f'ip link add {self.ifname} type veth'
cmd += f' peer name {self.config["peer_name"]}'
self._cmd(cmd)
diff --git a/python/vyos/ifconfig/vrrp.py b/python/vyos/ifconfig/vrrp.py
index a3657370f..3ee22706c 100644
--- a/python/vyos/ifconfig/vrrp.py
+++ b/python/vyos/ifconfig/vrrp.py
@@ -26,15 +26,12 @@ from vyos.utils.file import read_file
from vyos.utils.file import wait_for_file_write_complete
from vyos.utils.process import process_running
-
class VRRPError(Exception):
pass
-
class VRRPNoData(VRRPError):
pass
-
class VRRP(object):
_vrrp_prefix = '00:00:5E:00:01:'
location = {
diff --git a/python/vyos/ifconfig/vti.py b/python/vyos/ifconfig/vti.py
index 251cbeb36..78f5895f8 100644
--- a/python/vyos/ifconfig/vti.py
+++ b/python/vyos/ifconfig/vti.py
@@ -19,7 +19,6 @@ from vyos.utils.vti_updown_db import vti_updown_db_exists, open_vti_updown_db_re
@Interface.register
class VTIIf(Interface):
- iftype = 'vti'
definition = {
**Interface.definition,
**{
diff --git a/python/vyos/ifconfig/vtun.py b/python/vyos/ifconfig/vtun.py
index 6fb414e56..ee790f275 100644
--- a/python/vyos/ifconfig/vtun.py
+++ b/python/vyos/ifconfig/vtun.py
@@ -17,7 +17,6 @@ from vyos.ifconfig.interface import Interface
@Interface.register
class VTunIf(Interface):
- iftype = 'vtun'
definition = {
**Interface.definition,
**{
diff --git a/python/vyos/ifconfig/vxlan.py b/python/vyos/ifconfig/vxlan.py
index 1023c58d1..58844885b 100644
--- a/python/vyos/ifconfig/vxlan.py
+++ b/python/vyos/ifconfig/vxlan.py
@@ -42,8 +42,6 @@ class VXLANIf(Interface):
For more information please refer to:
https://www.kernel.org/doc/Documentation/networking/vxlan.txt
"""
-
- iftype = 'vxlan'
definition = {
**Interface.definition,
**{
@@ -94,7 +92,7 @@ class VXLANIf(Interface):
remote_list = self.config['remote'][1:]
self.config['remote'] = self.config['remote'][0]
- cmd = 'ip link add {ifname} type {type} dstport {port}'
+ cmd = 'ip link add {ifname} type vxlan dstport {port}'
for vyos_key, iproute2_key in mapping.items():
# dict_search will return an empty dict "{}" for valueless nodes like
# "parameters.nolearning" - thus we need to test the nodes existence
diff --git a/python/vyos/ifconfig/wireguard.py b/python/vyos/ifconfig/wireguard.py
index cccac361d..519012625 100644
--- a/python/vyos/ifconfig/wireguard.py
+++ b/python/vyos/ifconfig/wireguard.py
@@ -26,7 +26,6 @@ from vyos.ifconfig import Interface
from vyos.ifconfig import Operational
from vyos.template import is_ipv6
-
class WireGuardOperational(Operational):
def _dump(self):
"""Dump wireguard data in a python friendly way."""
@@ -160,18 +159,18 @@ class WireGuardOperational(Operational):
@Interface.register
class WireGuardIf(Interface):
OperationalClass = WireGuardOperational
- iftype = 'wireguard'
definition = {
**Interface.definition,
**{
'section': 'wireguard',
- 'prefixes': [
- 'wg',
- ],
+ 'prefixes': ['wg', ],
'bridgeable': False,
},
}
+ def _create(self):
+ super()._create('wireguard')
+
def get_mac(self):
"""Get a synthetic MAC address."""
return self.get_mac_synthetic()
diff --git a/python/vyos/ifconfig/wireless.py b/python/vyos/ifconfig/wireless.py
index 88eaa772b..121f56bd5 100644
--- a/python/vyos/ifconfig/wireless.py
+++ b/python/vyos/ifconfig/wireless.py
@@ -20,7 +20,6 @@ class WiFiIf(Interface):
"""
Handle WIFI/WLAN interfaces.
"""
- iftype = 'wifi'
definition = {
**Interface.definition,
**{
diff --git a/python/vyos/ifconfig/wwan.py b/python/vyos/ifconfig/wwan.py
index 845c9bef9..004a64b39 100644
--- a/python/vyos/ifconfig/wwan.py
+++ b/python/vyos/ifconfig/wwan.py
@@ -17,7 +17,6 @@ from vyos.ifconfig.interface import Interface
@Interface.register
class WWANIf(Interface):
- iftype = 'wwan'
definition = {
**Interface.definition,
**{
diff --git a/python/vyos/utils/process.py b/python/vyos/utils/process.py
index d8aabb822..054088325 100644
--- a/python/vyos/utils/process.py
+++ b/python/vyos/utils/process.py
@@ -20,10 +20,23 @@ from subprocess import PIPE
from subprocess import STDOUT
from subprocess import DEVNULL
+
+def get_wrapper(vrf, netns, auth):
+ wrapper = ''
+ if vrf:
+ wrapper = f'ip vrf exec {vrf} '
+ elif netns:
+ wrapper = f'ip netns exec {netns} '
+ if auth:
+ wrapper = f'{auth} {wrapper}'
+ return wrapper
+
+
def popen(command, flag='', shell=None, input=None, timeout=None, env=None,
- stdout=PIPE, stderr=PIPE, decode='utf-8'):
+ stdout=PIPE, stderr=PIPE, decode='utf-8', auth='', vrf=None,
+ netns=None):
"""
- popen is a wrapper helper aound subprocess.Popen
+ popen is a wrapper helper around subprocess.Popen
with it default setting it will return a tuple (out, err)
out: the output of the program run
err: the error code returned by the program
@@ -45,6 +58,8 @@ def popen(command, flag='', shell=None, input=None, timeout=None, env=None,
- DEVNULL, discard the output
decode: specify the expected text encoding (utf-8, ascii, ...)
the default is explicitely utf-8 which is python's own default
+ vrf: run command in a VRF context
+ netns: run command in the named network namespace
usage:
get both stdout and stderr: popen('command', stdout=PIPE, stderr=STDOUT)
@@ -60,6 +75,16 @@ def popen(command, flag='', shell=None, input=None, timeout=None, env=None,
if not debug.enabled(flag):
flag = 'command'
+ # Must be run as root to execute command in VRF or network namespace
+ if vrf or netns:
+ if os.getuid() != 0:
+ raise OSError(
+ 'Permission denied: cannot execute commands in VRF and netns contexts as an unprivileged user'
+ )
+
+ wrapper = get_wrapper(vrf, netns, auth)
+ command = f'{wrapper} {command}'
+
cmd_msg = f"cmd '{command}'"
debug.message(cmd_msg, flag)
@@ -111,7 +136,7 @@ def popen(command, flag='', shell=None, input=None, timeout=None, env=None,
def run(command, flag='', shell=None, input=None, timeout=None, env=None,
- stdout=DEVNULL, stderr=PIPE, decode='utf-8'):
+ stdout=DEVNULL, stderr=PIPE, decode='utf-8', vrf=None, netns=None):
"""
A wrapper around popen, which discard the stdout and
will return the error code of a command
@@ -122,13 +147,15 @@ def run(command, flag='', shell=None, input=None, timeout=None, env=None,
input=input, timeout=timeout,
env=env, shell=shell,
decode=decode,
+ vrf=vrf,
+ netns=netns,
)
return code
def cmd(command, flag='', shell=None, input=None, timeout=None, env=None,
stdout=PIPE, stderr=PIPE, decode='utf-8', raising=None, message='',
- expect=[0], auth=''):
+ expect=[0], auth='', vrf=None, netns=None):
"""
A wrapper around popen, which returns the stdout and
will raise the error code of a command
@@ -139,13 +166,18 @@ def cmd(command, flag='', shell=None, input=None, timeout=None, env=None,
expect: a list of error codes to consider as normal
"""
decoded, code = popen(
- f'{auth} {command}'.strip(), flag,
+ command, flag,
stdout=stdout, stderr=stderr,
input=input, timeout=timeout,
env=env, shell=shell,
decode=decode,
+ auth=auth,
+ vrf=vrf,
+ netns=netns,
)
if code not in expect:
+ wrapper = get_wrapper(vrf, netns, auth='')
+ command = f'{wrapper} {command}'
feedback = message + '\n' if message else ''
feedback += f'failed to run command: {command}\n'
feedback += f'returned: {decoded}\n'
@@ -159,7 +191,7 @@ def cmd(command, flag='', shell=None, input=None, timeout=None, env=None,
def rc_cmd(command, flag='', shell=None, input=None, timeout=None, env=None,
- stdout=PIPE, stderr=STDOUT, decode='utf-8'):
+ stdout=PIPE, stderr=STDOUT, decode='utf-8', vrf=None, netns=None):
"""
A wrapper around popen, which returns the return code
of a command and stdout
@@ -175,11 +207,14 @@ def rc_cmd(command, flag='', shell=None, input=None, timeout=None, env=None,
input=input, timeout=timeout,
env=env, shell=shell,
decode=decode,
+ vrf=vrf,
+ netns=netns,
)
return code, out
+
def call(command, flag='', shell=None, input=None, timeout=None, env=None,
- stdout=None, stderr=None, decode='utf-8'):
+ stdout=None, stderr=None, decode='utf-8', vrf=None, netns=None):
"""
A wrapper around popen, which print the stdout and
will return the error code of a command
@@ -190,11 +225,14 @@ def call(command, flag='', shell=None, input=None, timeout=None, env=None,
input=input, timeout=timeout,
env=env, shell=shell,
decode=decode,
+ vrf=vrf,
+ netns=netns,
)
if out:
print(out)
return code
+
def process_running(pid_file):
""" Checks if a process with PID in pid_file is running """
from psutil import pid_exists
diff --git a/scripts/transclude-template b/scripts/transclude-template
index 5c6668a84..767583acd 100755
--- a/scripts/transclude-template
+++ b/scripts/transclude-template
@@ -23,6 +23,7 @@
import os
import re
import sys
+import glob
regexp = re.compile(r'^ *#include <(.+)>$')
@@ -34,7 +35,9 @@ def parse_file(filename):
if line:
result = regexp.match(line)
if result:
- lines += parse_file(os.path.join(directory, result.group(1)))
+ res = os.path.join(directory, result.group(1))
+ for g in sorted(glob.glob(res)):
+ lines += parse_file(g)
else:
lines += line
else:
diff --git a/smoketest/config-tests/bgp-big-as-cloud b/smoketest/config-tests/bgp-big-as-cloud
index 8de0cdb02..03efef868 100644
--- a/smoketest/config-tests/bgp-big-as-cloud
+++ b/smoketest/config-tests/bgp-big-as-cloud
@@ -198,44 +198,44 @@ set firewall zone management from peers firewall ipv6-name 'peers-to-management-
set firewall zone management from peers firewall name 'peers-to-management-4'
set firewall zone management from servers firewall ipv6-name 'servers-to-management-6'
set firewall zone management from servers firewall name 'servers-to-management-4'
-set firewall zone management interface 'eth0'
+set firewall zone management member interface 'eth0'
set firewall zone peers default-action 'reject'
set firewall zone peers from management firewall ipv6-name 'management-to-peers-6'
set firewall zone peers from management firewall name 'management-to-peers-4'
set firewall zone peers from servers firewall ipv6-name 'servers-to-peers-6'
set firewall zone peers from servers firewall name 'servers-to-peers-4'
-set firewall zone peers interface 'eth0.4088'
-set firewall zone peers interface 'eth0.4089'
-set firewall zone peers interface 'eth0.11'
-set firewall zone peers interface 'eth0.838'
-set firewall zone peers interface 'eth0.886'
+set firewall zone peers member interface 'eth0.4088'
+set firewall zone peers member interface 'eth0.4089'
+set firewall zone peers member interface 'eth0.11'
+set firewall zone peers member interface 'eth0.838'
+set firewall zone peers member interface 'eth0.886'
set firewall zone servers default-action 'reject'
set firewall zone servers from management firewall ipv6-name 'management-to-servers-6'
set firewall zone servers from management firewall name 'management-to-servers-4'
set firewall zone servers from peers firewall ipv6-name 'peers-to-servers-6'
set firewall zone servers from peers firewall name 'peers-to-servers-4'
-set firewall zone servers interface 'eth0.1001'
-set firewall zone servers interface 'eth0.105'
-set firewall zone servers interface 'eth0.102'
-set firewall zone servers interface 'eth0.1019'
-set firewall zone servers interface 'eth0.1014'
-set firewall zone servers interface 'eth0.1020'
-set firewall zone servers interface 'eth0.1018'
-set firewall zone servers interface 'eth0.1013'
-set firewall zone servers interface 'eth0.1012'
-set firewall zone servers interface 'eth0.1011'
-set firewall zone servers interface 'eth0.1010'
-set firewall zone servers interface 'eth0.1009'
-set firewall zone servers interface 'eth0.1006'
-set firewall zone servers interface 'eth0.1005'
-set firewall zone servers interface 'eth0.1017'
-set firewall zone servers interface 'eth0.1016'
-set firewall zone servers interface 'eth0.1002'
-set firewall zone servers interface 'eth0.1015'
-set firewall zone servers interface 'eth0.1003'
-set firewall zone servers interface 'eth0.1004'
-set firewall zone servers interface 'eth0.1007'
-set firewall zone servers interface 'eth0.1008'
+set firewall zone servers member interface 'eth0.1001'
+set firewall zone servers member interface 'eth0.105'
+set firewall zone servers member interface 'eth0.102'
+set firewall zone servers member interface 'eth0.1019'
+set firewall zone servers member interface 'eth0.1014'
+set firewall zone servers member interface 'eth0.1020'
+set firewall zone servers member interface 'eth0.1018'
+set firewall zone servers member interface 'eth0.1013'
+set firewall zone servers member interface 'eth0.1012'
+set firewall zone servers member interface 'eth0.1011'
+set firewall zone servers member interface 'eth0.1010'
+set firewall zone servers member interface 'eth0.1009'
+set firewall zone servers member interface 'eth0.1006'
+set firewall zone servers member interface 'eth0.1005'
+set firewall zone servers member interface 'eth0.1017'
+set firewall zone servers member interface 'eth0.1016'
+set firewall zone servers member interface 'eth0.1002'
+set firewall zone servers member interface 'eth0.1015'
+set firewall zone servers member interface 'eth0.1003'
+set firewall zone servers member interface 'eth0.1004'
+set firewall zone servers member interface 'eth0.1007'
+set firewall zone servers member interface 'eth0.1008'
set high-availability vrrp group 11-4 address 192.0.68.1/27
set high-availability vrrp group 11-4 interface 'eth0.11'
set high-availability vrrp group 11-4 priority '200'
diff --git a/smoketest/config-tests/dialup-router-complex b/smoketest/config-tests/dialup-router-complex
index 4416ef82e..c693cc382 100644
--- a/smoketest/config-tests/dialup-router-complex
+++ b/smoketest/config-tests/dialup-router-complex
@@ -508,7 +508,7 @@ set firewall zone DMZ from GUEST firewall name 'GUEST-DMZ'
set firewall zone DMZ from LAN firewall name 'LAN-DMZ'
set firewall zone DMZ from LOCAL firewall name 'LOCAL-DMZ'
set firewall zone DMZ from WAN firewall name 'WAN-DMZ'
-set firewall zone DMZ interface 'eth0.50'
+set firewall zone DMZ member interface 'eth0.50'
set firewall zone GUEST default-action 'drop'
set firewall zone GUEST from DMZ firewall name 'DMZ-GUEST'
set firewall zone GUEST from IOT firewall name 'IOT-GUEST'
@@ -517,13 +517,13 @@ set firewall zone GUEST from LOCAL firewall ipv6-name 'ALLOW-ALL-6'
set firewall zone GUEST from LOCAL firewall name 'LOCAL-GUEST'
set firewall zone GUEST from WAN firewall ipv6-name 'ALLOW-ESTABLISHED-6'
set firewall zone GUEST from WAN firewall name 'WAN-GUEST'
-set firewall zone GUEST interface 'eth0.20'
+set firewall zone GUEST member interface 'eth0.20'
set firewall zone IOT default-action 'drop'
set firewall zone IOT from GUEST firewall name 'GUEST-IOT'
set firewall zone IOT from LAN firewall name 'LAN-IOT'
set firewall zone IOT from LOCAL firewall name 'LOCAL-IOT'
set firewall zone IOT from WAN firewall name 'WAN-IOT'
-set firewall zone IOT interface 'eth0.35'
+set firewall zone IOT member interface 'eth0.35'
set firewall zone LAN default-action 'drop'
set firewall zone LAN from DMZ firewall name 'DMZ-LAN'
set firewall zone LAN from GUEST firewall name 'GUEST-LAN'
@@ -532,13 +532,13 @@ set firewall zone LAN from LOCAL firewall ipv6-name 'ALLOW-ALL-6'
set firewall zone LAN from LOCAL firewall name 'LOCAL-LAN'
set firewall zone LAN from WAN firewall ipv6-name 'ALLOW-ESTABLISHED-6'
set firewall zone LAN from WAN firewall name 'WAN-LAN'
-set firewall zone LAN interface 'eth0.5'
-set firewall zone LAN interface 'eth0.10'
-set firewall zone LAN interface 'eth0.100'
-set firewall zone LAN interface 'eth0.201'
-set firewall zone LAN interface 'eth0.202'
-set firewall zone LAN interface 'eth0.203'
-set firewall zone LAN interface 'eth0.204'
+set firewall zone LAN member interface 'eth0.5'
+set firewall zone LAN member interface 'eth0.10'
+set firewall zone LAN member interface 'eth0.100'
+set firewall zone LAN member interface 'eth0.201'
+set firewall zone LAN member interface 'eth0.202'
+set firewall zone LAN member interface 'eth0.203'
+set firewall zone LAN member interface 'eth0.204'
set firewall zone LOCAL default-action 'drop'
set firewall zone LOCAL from DMZ firewall name 'DMZ-LOCAL'
set firewall zone LOCAL from GUEST firewall ipv6-name 'ALLOW-ESTABLISHED-6'
@@ -558,7 +558,7 @@ set firewall zone WAN from LAN firewall ipv6-name 'ALLOW-ALL-6'
set firewall zone WAN from LAN firewall name 'LAN-WAN'
set firewall zone WAN from LOCAL firewall ipv6-name 'ALLOW-ALL-6'
set firewall zone WAN from LOCAL firewall name 'LOCAL-WAN'
-set firewall zone WAN interface 'pppoe0'
+set firewall zone WAN member interface 'pppoe0'
set interfaces dummy dum0 address '172.16.254.30/32'
set interfaces ethernet eth0 duplex 'auto'
set interfaces ethernet eth0 speed 'auto'
diff --git a/smoketest/config-tests/dialup-router-wireguard-ipv6 b/smoketest/config-tests/dialup-router-wireguard-ipv6
index c2cf2e9d8..3e298fb82 100644
--- a/smoketest/config-tests/dialup-router-wireguard-ipv6
+++ b/smoketest/config-tests/dialup-router-wireguard-ipv6
@@ -391,7 +391,7 @@ set firewall zone DMZ from GUEST firewall name 'GUEST-DMZ'
set firewall zone DMZ from LAN firewall name 'LAN-DMZ'
set firewall zone DMZ from LOCAL firewall name 'LOCAL-DMZ'
set firewall zone DMZ from WAN firewall name 'WAN-DMZ'
-set firewall zone DMZ interface 'eth0.50'
+set firewall zone DMZ member interface 'eth0.50'
set firewall zone GUEST default-action 'drop'
set firewall zone GUEST from DMZ firewall name 'DMZ-GUEST'
set firewall zone GUEST from LAN firewall name 'LAN-GUEST'
@@ -399,7 +399,7 @@ set firewall zone GUEST from LOCAL firewall ipv6-name 'ALLOW-ALL-6'
set firewall zone GUEST from LOCAL firewall name 'LOCAL-GUEST'
set firewall zone GUEST from WAN firewall ipv6-name 'ALLOW-ESTABLISHED-6'
set firewall zone GUEST from WAN firewall name 'WAN-GUEST'
-set firewall zone GUEST interface 'eth1.20'
+set firewall zone GUEST member interface 'eth1.20'
set firewall zone LAN default-action 'drop'
set firewall zone LAN from DMZ firewall name 'DMZ-LAN'
set firewall zone LAN from GUEST firewall name 'GUEST-LAN'
@@ -407,10 +407,10 @@ set firewall zone LAN from LOCAL firewall ipv6-name 'ALLOW-ALL-6'
set firewall zone LAN from LOCAL firewall name 'LOCAL-LAN'
set firewall zone LAN from WAN firewall ipv6-name 'ALLOW-ESTABLISHED-6'
set firewall zone LAN from WAN firewall name 'WAN-LAN'
-set firewall zone LAN interface 'eth0.5'
-set firewall zone LAN interface 'eth0.10'
-set firewall zone LAN interface 'wg100'
-set firewall zone LAN interface 'wg200'
+set firewall zone LAN member interface 'eth0.5'
+set firewall zone LAN member interface 'eth0.10'
+set firewall zone LAN member interface 'wg100'
+set firewall zone LAN member interface 'wg200'
set firewall zone LOCAL default-action 'drop'
set firewall zone LOCAL from DMZ firewall name 'DMZ-LOCAL'
set firewall zone LOCAL from GUEST firewall ipv6-name 'ALLOW-ESTABLISHED-6'
@@ -428,8 +428,8 @@ set firewall zone WAN from LAN firewall ipv6-name 'ALLOW-ALL-6'
set firewall zone WAN from LAN firewall name 'LAN-WAN'
set firewall zone WAN from LOCAL firewall ipv6-name 'ALLOW-ALL-6'
set firewall zone WAN from LOCAL firewall name 'LOCAL-WAN'
-set firewall zone WAN interface 'pppoe0'
-set firewall zone WAN interface 'wg666'
+set firewall zone WAN member interface 'pppoe0'
+set firewall zone WAN member interface 'wg666'
set interfaces dummy dum0 address '172.16.254.30/32'
set interfaces ethernet eth0 duplex 'auto'
set interfaces ethernet eth0 offload gro
diff --git a/smoketest/scripts/cli/base_interfaces_test.py b/smoketest/scripts/cli/base_interfaces_test.py
index 593b4b415..c19bfcfe2 100644
--- a/smoketest/scripts/cli/base_interfaces_test.py
+++ b/smoketest/scripts/cli/base_interfaces_test.py
@@ -19,6 +19,7 @@ from netifaces import AF_INET6
from netifaces import ifaddresses
from base_vyostest_shim import VyOSUnitTestSHIM
+from base_vyostest_shim import CSTORE_GUARD_TIME
from vyos.configsession import ConfigSessionError
from vyos.defaults import directories
@@ -181,6 +182,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)
diff --git a/smoketest/scripts/cli/base_vyostest_shim.py b/smoketest/scripts/cli/base_vyostest_shim.py
index 2be25ff22..a54622700 100644
--- a/smoketest/scripts/cli/base_vyostest_shim.py
+++ b/smoketest/scripts/cli/base_vyostest_shim.py
@@ -51,6 +51,9 @@ class VyOSUnitTestSHIM:
# trigger the certain failure condition.
# Use "self.debug = True" in derived classes setUp() method
debug = False
+ # Time to wait after a commit to ensure the CStore is up to date
+ # only required for testcases using FRR
+ _commit_guard_time = 0
@classmethod
def setUpClass(cls):
cls._session = ConfigSession(os.getpid())
@@ -96,7 +99,7 @@ class VyOSUnitTestSHIM:
while run(f'sudo lsof -nP {commit_lock}') == 0:
sleep(0.250)
# Wait for CStore completion for fast non-interactive commits
- sleep(CSTORE_GUARD_TIME)
+ sleep(self._commit_guard_time)
def op_mode(self, path : list) -> None:
"""
diff --git a/smoketest/scripts/cli/test_container.py b/smoketest/scripts/cli/test_container.py
index 51559a7c6..36622cad1 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,10 +27,10 @@ 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):
@@ -42,11 +43,10 @@ class TestContainer(VyOSUnitTestSHIM.TestCase):
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 +55,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)
@@ -78,7 +77,7 @@ class TestContainer(VyOSUnitTestSHIM.TestCase):
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'])
@@ -104,7 +103,7 @@ 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)])
@@ -125,7 +124,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,7 +145,7 @@ 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, '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
@@ -176,7 +175,7 @@ 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, '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
@@ -208,7 +207,7 @@ 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, '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)])
@@ -242,7 +241,7 @@ 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, 'image', busybox_image])
self.cli_set(base_path + ['name', name, 'network', net_name, 'address', str(ip_interface(prefix).ip + 2)])
self.cli_commit()
@@ -258,7 +257,7 @@ 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, 'image', busybox_image])
self.cli_set(base_path + ['name', name, 'network', net_name, 'address', str(ip_interface(prefix).ip + 2)])
self.cli_commit()
@@ -271,7 +270,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,7 +292,7 @@ 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()
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_policy.py b/smoketest/scripts/cli/test_policy.py
index 7ea1b610e..9d4fc0845 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()
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..53761b7d6 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])
diff --git a/smoketest/scripts/cli/test_protocols_babel.py b/smoketest/scripts/cli/test_protocols_babel.py
index fa31722e5..7ecf54600 100755
--- a/smoketest/scripts/cli/test_protocols_babel.py
+++ b/smoketest/scripts/cli/test_protocols_babel.py
@@ -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
diff --git a/smoketest/scripts/cli/test_protocols_bfd.py b/smoketest/scripts/cli/test_protocols_bfd.py
index f7ef3849f..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,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.frrender import bfd_daemon
from vyos.utils.process import process_named_running
@@ -86,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)
diff --git a/smoketest/scripts/cli/test_protocols_bgp.py b/smoketest/scripts/cli/test_protocols_bgp.py
index e5c8486f8..761eb8bfe 100755
--- a/smoketest/scripts/cli/test_protocols_bgp.py
+++ b/smoketest/scripts/cli/test_protocols_bgp.py
@@ -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'])
diff --git a/smoketest/scripts/cli/test_protocols_isis.py b/smoketest/scripts/cli/test_protocols_isis.py
index 92d6ef2a7..598250d28 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,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.utils.process import process_named_running
@@ -38,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
diff --git a/smoketest/scripts/cli/test_protocols_mpls.py b/smoketest/scripts/cli/test_protocols_mpls.py
index 9d8417851..654f2f099 100755
--- a/smoketest/scripts/cli/test_protocols_mpls.py
+++ b/smoketest/scripts/cli/test_protocols_mpls.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 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)
diff --git a/smoketest/scripts/cli/test_protocols_openfabric.py b/smoketest/scripts/cli/test_protocols_openfabric.py
index db0d5e222..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)
diff --git a/smoketest/scripts/cli/test_protocols_ospf.py b/smoketest/scripts/cli/test_protocols_ospf.py
index f862f5889..77882737f 100755
--- a/smoketest/scripts/cli/test_protocols_ospf.py
+++ b/smoketest/scripts/cli/test_protocols_ospf.py
@@ -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):
diff --git a/smoketest/scripts/cli/test_protocols_ospfv3.py b/smoketest/scripts/cli/test_protocols_ospfv3.py
index fd4d4cf08..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):
diff --git a/smoketest/scripts/cli/test_protocols_pim.py b/smoketest/scripts/cli/test_protocols_pim.py
index 1ba24c196..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))
diff --git a/smoketest/scripts/cli/test_protocols_pim6.py b/smoketest/scripts/cli/test_protocols_pim6.py
index 98be54f4c..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)
diff --git a/smoketest/scripts/cli/test_protocols_rip.py b/smoketest/scripts/cli/test_protocols_rip.py
index 78567d12c..671ef8cd5 100755
--- a/smoketest/scripts/cli/test_protocols_rip.py
+++ b/smoketest/scripts/cli/test_protocols_rip.py
@@ -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'])
diff --git a/smoketest/scripts/cli/test_protocols_ripng.py b/smoketest/scripts/cli/test_protocols_ripng.py
index 26da2b62b..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'])
diff --git a/smoketest/scripts/cli/test_protocols_rpki.py b/smoketest/scripts/cli/test_protocols_rpki.py
index 36edbd5c2..ef2f30d3e 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,6 +112,8 @@ 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)
diff --git a/smoketest/scripts/cli/test_protocols_segment-routing.py b/smoketest/scripts/cli/test_protocols_segment-routing.py
index af4ef2adf..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
@@ -37,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)
diff --git a/smoketest/scripts/cli/test_protocols_static.py b/smoketest/scripts/cli/test_protocols_static.py
index 7cfc02e30..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
@@ -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.configsession import ConfigSessionError
from vyos.template import is_ipv6
@@ -170,6 +171,8 @@ class TestProtocolsStatic(VyOSUnitTestSHIM.TestCase):
super(TestProtocolsStatic, cls).setUpClass()
cls.cli_delete(cls, base_path)
cls.cli_delete(cls, ['vrf'])
+ # Enable CSTORE guard time required by FRR related tests
+ cls._commit_guard_time = CSTORE_GUARD_TIME
@classmethod
def tearDownClass(cls):
diff --git a/smoketest/scripts/cli/test_system_login.py b/smoketest/scripts/cli/test_system_login.py
index 28abba012..f6a2c3cb3 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,24 +14,35 @@
# 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.utils.auth import get_current_user
from vyos.utils.process import cmd
+from vyos.utils.process import process_named_running
from vyos.utils.file import read_file
+from vyos.utils.file import write_file
from vyos.template import inc_ip
base_path = ['system', 'login']
users = ['vyos1', 'vyos-roxx123', 'VyOS-123_super.Nice']
+SSH_PROCESS_NAME = 'sshd'
+
ssh_pubkey = """
AAAAB3NzaC1yc2EAAAADAQABAAABgQD0NuhUOEtMIKnUVFIHoFatqX/c4mjerXyF
TlXYfVt6Ls2NZZsUSwHbnhK4BKDrPvVZMW/LycjQPzWW6TGtk6UbZP1WqdviQ9hP
@@ -44,6 +55,53 @@ 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
+ }
+}
+"""
+
class TestSystemLogin(VyOSUnitTestSHIM.TestCase):
@classmethod
def setUpClass(cls):
@@ -54,6 +112,17 @@ class TestSystemLogin(VyOSUnitTestSHIM.TestCase):
cls.cli_delete(cls, base_path + ['radius'])
cls.cli_delete(cls, base_path + ['tacacs'])
+ # Load image 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}')
+
+ @classmethod
+ def tearDownClass(cls):
+ super(TestSystemLogin, cls).tearDownClass()
+ # Cleanup podman image
+ cmd(f'sudo podman image rm -f {tac_image}')
+
def tearDown(self):
# Delete individual users from configuration
for user in users:
@@ -87,11 +156,11 @@ class TestSystemLogin(VyOSUnitTestSHIM.TestCase):
self.cli_set(['service', 'ssh', 'port', '22'])
for user in users:
- name = "VyOS Roxx " + user
- home_dir = "/tmp/" + user
+ name = f'VyOS Roxx {user}'
+ 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, 'full-name', name])
self.cli_set(base_path + ['user', user, 'home-directory', home_dir])
self.cli_commit()
@@ -99,13 +168,13 @@ class TestSystemLogin(VyOSUnitTestSHIM.TestCase):
for user in users:
tmp = ['su','-', user]
proc = Popen(tmp, stdin=PIPE, stdout=PIPE, stderr=PIPE)
- tmp = "{}\nuname -a".format(user)
+ tmp = f'{user}\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,7 +192,6 @@ class TestSystemLogin(VyOSUnitTestSHIM.TestCase):
tmp = cmd(f'sudo passwd -S {locked_user}')
self.assertIn(f'{locked_user} P ', tmp)
-
def test_system_login_otp(self):
otp_user = 'otp-test_user'
otp_password = 'SuperTestPassword'
@@ -300,11 +368,52 @@ 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))
+
+ # Check if SSH service is running
+ ssh_running = process_named_running(SSH_PROCESS_NAME)
+ if not ssh_running:
+ # Start SSH service
+ self.cli_set(['service', 'ssh'])
+
+ # 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 +437,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 +449,32 @@ 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
+ test_command = 'uname -a'
+ out, err = self.ssh_send_cmd(test_command, username, password)
+ # verify login
+ self.assertFalse(err)
+ self.assertEqual(out, cmd(test_command))
+
+ # Login with invalid credentials
+ with self.assertRaises(paramiko.ssh_exception.AuthenticationException):
+ _, _ = self.ssh_send_cmd(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)
+
+ # Stop SSH service if it was not running before
+ if not ssh_running:
+ self.cli_delete(['service', 'ssh'])
+
def test_delete_current_user(self):
current_user = get_current_user()
diff --git a/smoketest/scripts/cli/test_vrf.py b/smoketest/scripts/cli/test_vrf.py
index 1676f8f19..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,10 +18,12 @@ 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.ifconfig import Interface
from vyos.ifconfig import Section
@@ -51,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()
diff --git a/src/conf_mode/vrf.py b/src/conf_mode/vrf.py
index 74780b601..8baf55857 100755
--- a/src/conf_mode/vrf.py
+++ b/src/conf_mode/vrf.py
@@ -149,8 +149,9 @@ def verify(vrf):
f'static routes installed!')
if 'name' in vrf:
- reserved_names = ["add", "all", "broadcast", "default", "delete", "dev",
- "get", "inet", "mtu", "link", "type", "vrf"]
+ reserved_names = ['add', 'all', 'broadcast', 'default', 'delete', 'dev',
+ 'down', 'get', 'inet', 'link', 'mtu', 'type', 'up', 'vrf']
+
table_ids = []
vnis = []
for name, vrf_config in vrf['name'].items():
diff --git a/src/op_mode/dhcp.py b/src/op_mode/dhcp.py
index 1429fd7b1..20f54df25 100755
--- a/src/op_mode/dhcp.py
+++ b/src/op_mode/dhcp.py
@@ -101,8 +101,8 @@ def _get_raw_server_leases(family='inet', pool=None, sorted=None, state=[], orig
lifetime = lease['valid-lft']
expiry = (lease['cltt'] + lifetime)
- lease['start_timestamp'] = datetime.utcfromtimestamp(expiry - lifetime)
- lease['expire_timestamp'] = datetime.utcfromtimestamp(expiry) if expiry else None
+ lease['start_timestamp'] = datetime.fromtimestamp(expiry - lifetime, timezone.utc)
+ lease['expire_timestamp'] = datetime.fromtimestamp(expiry, timezone.utc) if expiry else None
data_lease = {}
data_lease['ip'] = lease['ip-address']
diff --git a/src/services/vyos-configd b/src/services/vyos-configd
index e4655fdf7..b161fe6ba 100755
--- a/src/services/vyos-configd
+++ b/src/services/vyos-configd
@@ -211,9 +211,6 @@ def initialization(socket):
scripts_called = []
setattr(config, 'scripts_called', scripts_called)
- if not hasattr(config, 'frrender_cls'):
- setattr(config, 'frrender_cls', FRRender())
-
return config
@@ -312,8 +309,10 @@ if __name__ == '__main__':
remove_if_file(configd_env_file)
os.symlink(configd_env_set_file, configd_env_file)
- config = None
+ # We only need one long-lived instance of FRRender
+ frr = FRRender()
+ config = None
while True:
# Wait for next request from client
msg = socket.recv().decode()
@@ -332,10 +331,11 @@ if __name__ == '__main__':
scripts_called = getattr(config, 'scripts_called', [])
logger.debug(f'scripts_called: {scripts_called}')
- if hasattr(config, 'frrender_cls') and res == R_SUCCESS:
- frrender_cls = getattr(config, 'frrender_cls')
+ if res == R_SUCCESS:
tmp = get_frrender_dict(config)
- if frrender_cls.generate(tmp):
- frrender_cls.apply()
+ if frr.generate(tmp):
+ # only apply a new FRR configuration if anything changed
+ # in comparison to the previous applied configuration
+ frr.apply()
else:
logger.critical(f'Unexpected message: {message}')