summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/pr-conflicts.yml4
-rw-r--r--.github/workflows/unused-imports.yml22
-rw-r--r--Makefile11
-rw-r--r--data/templates/accel-ppp/pppoe.config.j29
-rw-r--r--data/templates/conntrackd/conntrackd.conf.j21
-rw-r--r--data/templates/dhcp-client/dhcp6c-script.j231
-rw-r--r--data/templates/dhcp-client/ipv6.j22
-rw-r--r--data/templates/firewall/nftables-cgnat.j247
-rw-r--r--data/templates/frr/bgpd.frr.j28
-rw-r--r--data/templates/ipsec/swanctl/remote_access.j25
-rw-r--r--data/templates/ssh/override.conf.j214
-rw-r--r--debian/control4
-rw-r--r--debian/vyos-1x.postinst9
-rw-r--r--interface-definitions/container.xml.in12
-rw-r--r--interface-definitions/firewall.xml.in26
-rw-r--r--interface-definitions/include/bgp/neighbor-disable-connected-check.xml.i2
-rw-r--r--interface-definitions/include/bgp/protocol-common-config.xml.i6
-rw-r--r--interface-definitions/include/constraint/alpha-numeric-hyphen-underscore-dot.xml.i2
-rw-r--r--interface-definitions/include/constraint/vrf.xml.i6
-rw-r--r--interface-definitions/include/firewall/global-options.xml.i20
-rw-r--r--interface-definitions/include/interface/mtu-68-16000.xml.i1
-rw-r--r--interface-definitions/include/interface/vrf.xml.i1
-rw-r--r--interface-definitions/include/qos/queue-average-packet.xml.i16
-rw-r--r--interface-definitions/include/qos/queue-mark-probability.xml.i16
-rw-r--r--interface-definitions/include/qos/queue-maximum-threshold.xml.i16
-rw-r--r--interface-definitions/include/qos/queue-minimum-threshold.xml.i15
-rw-r--r--interface-definitions/include/version/container-version.xml.i2
-rw-r--r--interface-definitions/include/version/firewall-version.xml.i2
-rw-r--r--interface-definitions/include/vrf-multi.xml.i22
-rw-r--r--interface-definitions/interfaces_bonding.xml.in3
-rw-r--r--interface-definitions/interfaces_bridge.xml.in3
-rw-r--r--interface-definitions/interfaces_dummy.xml.in3
-rw-r--r--interface-definitions/interfaces_vti.xml.in3
-rw-r--r--interface-definitions/interfaces_wireguard.xml.in2
-rw-r--r--interface-definitions/nat_cgnat.xml.in197
-rw-r--r--interface-definitions/qos.xml.in67
-rw-r--r--interface-definitions/service_config-sync.xml.in6
-rw-r--r--interface-definitions/service_conntrack-sync.xml.in6
-rw-r--r--interface-definitions/service_dhcp-server.xml.in21
-rw-r--r--interface-definitions/service_ssh.xml.in2
-rw-r--r--interface-definitions/system_ip.xml.in6
-rw-r--r--interface-definitions/vrf.xml.in5
-rw-r--r--op-mode-definitions/firewall.xml.in241
-rw-r--r--python/vyos/accel_ppp.py9
-rw-r--r--python/vyos/accel_ppp_util.py34
-rw-r--r--python/vyos/config.py4
-rw-r--r--python/vyos/config_mgmt.py17
-rw-r--r--python/vyos/configdict.py8
-rw-r--r--python/vyos/configdiff.py6
-rw-r--r--python/vyos/configsession.py19
-rw-r--r--python/vyos/configtree.py24
-rw-r--r--python/vyos/configverify.py140
-rw-r--r--python/vyos/cpu.py3
-rw-r--r--python/vyos/ethtool.py1
-rw-r--r--python/vyos/firewall.py11
-rw-r--r--python/vyos/frr.py3
-rw-r--r--python/vyos/ifconfig/bond.py3
-rw-r--r--python/vyos/ifconfig/bridge.py7
-rw-r--r--python/vyos/ifconfig/interface.py19
-rw-r--r--python/vyos/ifconfig/section.py2
-rw-r--r--python/vyos/ifconfig/vrrp.py4
-rw-r--r--python/vyos/ifconfig/vxlan.py5
-rw-r--r--python/vyos/ifconfig/wireguard.py3
-rw-r--r--python/vyos/iflag.py38
-rw-r--r--python/vyos/initialsetup.py4
-rw-r--r--python/vyos/ioctl.py5
-rw-r--r--python/vyos/kea.py3
-rw-r--r--python/vyos/nat.py2
-rw-r--r--python/vyos/pki.py2
-rw-r--r--python/vyos/progressbar.py4
-rw-r--r--python/vyos/qos/base.py29
-rw-r--r--python/vyos/qos/priority.py3
-rw-r--r--python/vyos/range_regex.py5
-rw-r--r--python/vyos/system/grub.py12
-rw-r--r--python/vyos/template.py37
-rw-r--r--python/vyos/tpm.py2
-rw-r--r--python/vyos/utils/io.py12
-rw-r--r--python/vyos/utils/network.py4
-rw-r--r--python/vyos/utils/system.py7
-rw-r--r--python/vyos/version.py6
-rw-r--r--python/vyos/xml/.gitignore1
-rw-r--r--python/vyos/xml/__init__.py65
-rw-r--r--python/vyos/xml/cache/__init__.py0
-rw-r--r--python/vyos/xml/definition.py360
-rwxr-xr-xpython/vyos/xml/generate.py70
-rw-r--r--python/vyos/xml/kw.py83
-rw-r--r--python/vyos/xml/load.py300
-rw-r--r--python/vyos/xml/test_xml.py279
-rwxr-xr-xpython/vyos/xml_ref/generate_cache.py7
-rwxr-xr-xscripts/check-pr-title-and-commit-messages.py2
-rw-r--r--smoketest/config-tests/container-simple12
-rw-r--r--smoketest/config-tests/ipoe-server35
-rw-r--r--smoketest/config-tests/ospf-simple20
-rw-r--r--smoketest/configs/container-simple46
-rw-r--r--smoketest/configs/ipoe-server10
-rw-r--r--smoketest/configs/ospf-simple81
-rw-r--r--smoketest/scripts/cli/base_accel_ppp_test.py9
-rw-r--r--smoketest/scripts/cli/base_interfaces_test.py7
-rwxr-xr-xsmoketest/scripts/cli/test_container.py3
-rwxr-xr-xsmoketest/scripts/cli/test_firewall.py1
-rwxr-xr-xsmoketest/scripts/cli/test_high-availability_virtual-server.py5
-rwxr-xr-xsmoketest/scripts/cli/test_high-availability_vrrp.py3
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_macsec.py4
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_openvpn.py3
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_pppoe.py7
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_virtual-ethernet.py3
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_wireless.py4
-rwxr-xr-xsmoketest/scripts/cli/test_load-balancing_reverse-proxy.py171
-rwxr-xr-xsmoketest/scripts/cli/test_load-balancing_wan.py7
-rwxr-xr-xsmoketest/scripts/cli/test_nat.py4
-rwxr-xr-xsmoketest/scripts/cli/test_nat64.py6
-rwxr-xr-xsmoketest/scripts/cli/test_nat66.py5
-rwxr-xr-xsmoketest/scripts/cli/test_netns.py6
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_bgp.py93
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_nhrp.py3
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_segment-routing.py4
-rwxr-xr-xsmoketest/scripts/cli/test_qos.py65
-rwxr-xr-xsmoketest/scripts/cli/test_service_dhcp-server.py90
-rwxr-xr-xsmoketest/scripts/cli/test_service_dhcpv6-relay.py4
-rwxr-xr-xsmoketest/scripts/cli/test_service_https.py6
-rwxr-xr-xsmoketest/scripts/cli/test_service_lldp.py5
-rwxr-xr-xsmoketest/scripts/cli/test_service_monitoring_telegraf.py4
-rwxr-xr-xsmoketest/scripts/cli/test_service_monitoring_zabbix-agent.py4
-rwxr-xr-xsmoketest/scripts/cli/test_service_ndp-proxy.py3
-rwxr-xr-xsmoketest/scripts/cli/test_service_pppoe-server.py22
-rwxr-xr-xsmoketest/scripts/cli/test_service_ssh.py46
-rwxr-xr-xsmoketest/scripts/cli/test_service_upnp.py4
-rwxr-xr-xsmoketest/scripts/cli/test_system_conntrack.py3
-rwxr-xr-xsmoketest/scripts/cli/test_system_flow-accounting.py3
-rwxr-xr-xsmoketest/scripts/cli/test_system_ip.py11
-rwxr-xr-xsmoketest/scripts/cli/test_system_ipv6.py3
-rwxr-xr-xsmoketest/scripts/cli/test_system_login.py2
-rwxr-xr-xsmoketest/scripts/cli/test_system_sflow.py1
-rwxr-xr-xsmoketest/scripts/cli/test_system_syslog.py12
-rwxr-xr-xsmoketest/scripts/cli/test_vpn_ipsec.py5
-rwxr-xr-xsmoketest/scripts/cli/test_vpn_l2tp.py2
-rwxr-xr-xsmoketest/scripts/cli/test_vpn_openconnect.py135
-rwxr-xr-xsmoketest/scripts/cli/test_vpn_pptp.py5
-rwxr-xr-xsmoketest/scripts/cli/test_vrf.py3
-rw-r--r--sphinx/Makefile177
-rw-r--r--sphinx/source/.gitignore1
-rw-r--r--sphinx/source/conf.py261
-rw-r--r--sphinx/source/index.rst22
-rw-r--r--src/completion/list_ipsec_profile_tunnels.py5
-rwxr-xr-xsrc/completion/list_openvpn_clients.py5
-rwxr-xr-xsrc/completion/list_openvpn_users.py5
-rwxr-xr-xsrc/conf_mode/container.py18
-rwxr-xr-xsrc/conf_mode/firewall.py10
-rwxr-xr-xsrc/conf_mode/interfaces_bonding.py10
-rwxr-xr-xsrc/conf_mode/interfaces_ethernet.py34
-rwxr-xr-xsrc/conf_mode/interfaces_geneve.py8
-rwxr-xr-xsrc/conf_mode/interfaces_l2tpv3.py8
-rwxr-xr-xsrc/conf_mode/interfaces_loopback.py4
-rwxr-xr-xsrc/conf_mode/interfaces_macsec.py8
-rwxr-xr-xsrc/conf_mode/interfaces_openvpn.py11
-rwxr-xr-xsrc/conf_mode/interfaces_pppoe.py6
-rwxr-xr-xsrc/conf_mode/interfaces_pseudo-ethernet.py9
-rwxr-xr-xsrc/conf_mode/interfaces_tunnel.py11
-rwxr-xr-xsrc/conf_mode/interfaces_virtual-ethernet.py9
-rwxr-xr-xsrc/conf_mode/interfaces_vti.py4
-rwxr-xr-xsrc/conf_mode/interfaces_vxlan.py10
-rwxr-xr-xsrc/conf_mode/interfaces_wireguard.py3
-rwxr-xr-xsrc/conf_mode/load-balancing_reverse-proxy.py16
-rwxr-xr-xsrc/conf_mode/nat.py12
-rwxr-xr-xsrc/conf_mode/nat64.py3
-rwxr-xr-xsrc/conf_mode/nat66.py6
-rwxr-xr-xsrc/conf_mode/nat_cgnat.py288
-rwxr-xr-xsrc/conf_mode/netns.py5
-rwxr-xr-xsrc/conf_mode/policy_local-route.py11
-rwxr-xr-xsrc/conf_mode/policy_route.py4
-rwxr-xr-xsrc/conf_mode/protocols_babel.py5
-rwxr-xr-xsrc/conf_mode/protocols_bfd.py4
-rwxr-xr-xsrc/conf_mode/protocols_bgp.py21
-rwxr-xr-xsrc/conf_mode/protocols_igmp-proxy.py7
-rwxr-xr-xsrc/conf_mode/protocols_isis.py2
-rwxr-xr-xsrc/conf_mode/protocols_nhrp.py5
-rwxr-xr-xsrc/conf_mode/protocols_ospfv3.py2
-rwxr-xr-xsrc/conf_mode/protocols_rip.py4
-rwxr-xr-xsrc/conf_mode/protocols_ripng.py4
-rwxr-xr-xsrc/conf_mode/protocols_segment-routing.py4
-rwxr-xr-xsrc/conf_mode/protocols_static.py4
-rwxr-xr-xsrc/conf_mode/protocols_static_neighbor-proxy.py12
-rwxr-xr-xsrc/conf_mode/qos.py14
-rwxr-xr-xsrc/conf_mode/service_dhcp-server.py18
-rwxr-xr-xsrc/conf_mode/service_dhcpv6-server.py7
-rwxr-xr-xsrc/conf_mode/service_dns_forwarding.py5
-rwxr-xr-xsrc/conf_mode/service_https.py39
-rwxr-xr-xsrc/conf_mode/service_ipoe-server.py4
-rwxr-xr-xsrc/conf_mode/service_lldp.py3
-rwxr-xr-xsrc/conf_mode/service_mdns_repeater.py8
-rwxr-xr-xsrc/conf_mode/service_pppoe-server.py27
-rwxr-xr-xsrc/conf_mode/service_ssh.py16
-rwxr-xr-xsrc/conf_mode/service_tftp-server.py3
-rwxr-xr-xsrc/conf_mode/service_upnp.py4
-rwxr-xr-xsrc/conf_mode/system_conntrack.py7
-rwxr-xr-xsrc/conf_mode/system_console.py5
-rwxr-xr-xsrc/conf_mode/system_flow-accounting.py10
-rwxr-xr-xsrc/conf_mode/system_frr.py3
-rwxr-xr-xsrc/conf_mode/system_host-name.py6
-rwxr-xr-xsrc/conf_mode/system_ip.py8
-rwxr-xr-xsrc/conf_mode/system_login.py26
-rwxr-xr-xsrc/conf_mode/system_option.py6
-rwxr-xr-xsrc/conf_mode/system_timezone.py3
-rwxr-xr-xsrc/conf_mode/system_update-check.py5
-rwxr-xr-xsrc/conf_mode/vpn_ipsec.py6
-rwxr-xr-xsrc/conf_mode/vpn_openconnect.py52
-rwxr-xr-xsrc/conf_mode/vpn_sstp.py65
-rwxr-xr-xsrc/conf_mode/vrf.py11
-rw-r--r--src/conf_mode/vrf_vni.py3
-rw-r--r--src/etc/dhcp/dhclient-enter-hooks.d/04-vyos-resolvconf16
-rwxr-xr-xsrc/etc/dhcp/dhclient-exit-hooks.d/99-ipsec-dhclient-hook6
-rw-r--r--src/etc/systemd/system/ssh@.service.d/vrf-override.conf13
-rwxr-xr-xsrc/helpers/vyos-config-encrypt.py2
-rwxr-xr-xsrc/helpers/vyos-domain-resolver.py7
-rwxr-xr-xsrc/helpers/vyos-failover.py3
-rwxr-xr-xsrc/helpers/vyos-merge-config.py3
-rwxr-xr-xsrc/helpers/vyos_config_sync.py66
-rwxr-xr-xsrc/init/vyos-router2
-rwxr-xr-xsrc/migration-scripts/conntrack/2-to-31
-rwxr-xr-xsrc/migration-scripts/container/1-to-250
-rwxr-xr-xsrc/migration-scripts/dhcp-server/9-to-103
-rwxr-xr-xsrc/migration-scripts/dhcpv6-server/3-to-41
-rwxr-xr-xsrc/migration-scripts/firewall/10-to-1113
-rwxr-xr-xsrc/migration-scripts/firewall/11-to-127
-rwxr-xr-xsrc/migration-scripts/firewall/12-to-137
-rwxr-xr-xsrc/migration-scripts/firewall/14-to-1546
-rwxr-xr-xsrc/migration-scripts/firewall/6-to-780
-rwxr-xr-xsrc/migration-scripts/firewall/7-to-85
-rwxr-xr-xsrc/migration-scripts/firewall/8-to-99
-rwxr-xr-xsrc/migration-scripts/firewall/9-to-1013
-rwxr-xr-xsrc/migration-scripts/interfaces/26-to-274
-rwxr-xr-xsrc/migration-scripts/interfaces/27-to-283
-rwxr-xr-xsrc/migration-scripts/interfaces/28-to-294
-rwxr-xr-xsrc/migration-scripts/interfaces/29-to-304
-rwxr-xr-xsrc/migration-scripts/ipoe-server/0-to-174
-rwxr-xr-xsrc/migration-scripts/ipoe-server/1-to-2101
-rwxr-xr-xsrc/migration-scripts/ipoe-server/2-to-33
-rwxr-xr-xsrc/migration-scripts/ipsec/11-to-124
-rwxr-xr-xsrc/migration-scripts/ipsec/12-to-132
-rwxr-xr-xsrc/migration-scripts/ipsec/7-to-83
-rwxr-xr-xsrc/migration-scripts/ipsec/9-to-105
-rwxr-xr-xsrc/migration-scripts/l2tp/2-to-36
-rwxr-xr-xsrc/migration-scripts/l2tp/3-to-43
-rwxr-xr-xsrc/migration-scripts/l2tp/4-to-54
-rwxr-xr-xsrc/migration-scripts/l2tp/5-to-66
-rwxr-xr-xsrc/migration-scripts/l2tp/6-to-73
-rwxr-xr-xsrc/migration-scripts/l2tp/7-to-83
-rwxr-xr-xsrc/migration-scripts/l2tp/8-to-95
-rwxr-xr-xsrc/migration-scripts/nat/5-to-6120
-rwxr-xr-xsrc/migration-scripts/openconnect/0-to-13
-rwxr-xr-xsrc/migration-scripts/ospf/0-to-13
-rwxr-xr-xsrc/migration-scripts/policy/4-to-53
-rwxr-xr-xsrc/migration-scripts/policy/5-to-63
-rwxr-xr-xsrc/migration-scripts/policy/6-to-79
-rwxr-xr-xsrc/migration-scripts/pppoe-server/1-to-25
-rwxr-xr-xsrc/migration-scripts/pppoe-server/3-to-44
-rwxr-xr-xsrc/migration-scripts/pppoe-server/6-to-74
-rwxr-xr-xsrc/migration-scripts/pppoe-server/7-to-85
-rwxr-xr-xsrc/migration-scripts/pppoe-server/8-to-93
-rwxr-xr-xsrc/migration-scripts/pptp/2-to-34
-rwxr-xr-xsrc/migration-scripts/pptp/3-to-43
-rwxr-xr-xsrc/migration-scripts/pptp/4-to-53
-rwxr-xr-xsrc/migration-scripts/snmp/2-to-35
-rwxr-xr-xsrc/migration-scripts/sstp/0-to-13
-rwxr-xr-xsrc/migration-scripts/sstp/3-to-43
-rwxr-xr-xsrc/migration-scripts/sstp/4-to-55
-rwxr-xr-xsrc/migration-scripts/sstp/5-to-64
-rwxr-xr-xsrc/migration-scripts/system/15-to-161
-rwxr-xr-xsrc/migration-scripts/system/16-to-173
-rwxr-xr-xsrc/migration-scripts/system/19-to-204
-rwxr-xr-xsrc/migration-scripts/system/20-to-214
-rwxr-xr-xsrc/migration-scripts/system/21-to-224
-rwxr-xr-xsrc/migration-scripts/system/22-to-234
-rwxr-xr-xsrc/migration-scripts/system/23-to-244
-rwxr-xr-xsrc/op_mode/bridge.py4
-rwxr-xr-xsrc/op_mode/conntrack.py26
-rwxr-xr-xsrc/op_mode/conntrack_sync.py3
-rwxr-xr-xsrc/op_mode/container.py4
-rwxr-xr-xsrc/op_mode/dhcp.py10
-rwxr-xr-xsrc/op_mode/firewall.py60
-rwxr-xr-xsrc/op_mode/generate_ovpn_client_file.py5
-rwxr-xr-xsrc/op_mode/generate_tech-support_archive.py2
-rwxr-xr-xsrc/op_mode/ikev2_profile_generator.py3
-rwxr-xr-xsrc/op_mode/image_info.py8
-rwxr-xr-xsrc/op_mode/image_installer.py17
-rwxr-xr-xsrc/op_mode/image_manager.py44
-rwxr-xr-xsrc/op_mode/interfaces_wireless.py3
-rwxr-xr-xsrc/op_mode/openvpn.py7
-rwxr-xr-xsrc/op_mode/otp.py18
-rwxr-xr-xsrc/op_mode/policy_route.py3
-rwxr-xr-xsrc/op_mode/powerctrl.py4
-rwxr-xr-xsrc/op_mode/restart_dhcp_relay.py3
-rwxr-xr-xsrc/op_mode/reverseproxy.py4
-rwxr-xr-xsrc/op_mode/sflow.py3
-rw-r--r--src/op_mode/show_techsupport_report.py4
-rwxr-xr-xsrc/op_mode/snmp.py8
-rwxr-xr-xsrc/op_mode/system.py7
-rwxr-xr-xsrc/op_mode/vpn_ike_sa.py3
-rwxr-xr-xsrc/op_mode/vrrp.py6
-rw-r--r--src/services/api/graphql/generate/composite_function.py4
-rw-r--r--src/services/api/graphql/graphql/auth_token_mutation.py8
-rw-r--r--src/services/api/graphql/graphql/directives.py4
-rw-r--r--src/services/api/graphql/graphql/mutations.py8
-rw-r--r--src/services/api/graphql/graphql/queries.py8
-rw-r--r--src/services/api/graphql/libs/op_mode.py6
-rwxr-xr-xsrc/services/api/graphql/session/composite/system_status.py11
-rw-r--r--src/services/api/graphql/session/session.py3
-rwxr-xr-xsrc/services/vyos-http-api-server38
-rwxr-xr-xsrc/system/keepalived-fifo.py4
-rw-r--r--src/tests/test_config_diff.py3
-rw-r--r--src/tests/test_config_parser.py3
-rw-r--r--src/tests/test_initial_setup.py5
-rw-r--r--src/tests/test_template.py6
313 files changed, 3116 insertions, 3033 deletions
diff --git a/.github/workflows/pr-conflicts.yml b/.github/workflows/pr-conflicts.yml
index 96040cd60..2fd0bb42d 100644
--- a/.github/workflows/pr-conflicts.yml
+++ b/.github/workflows/pr-conflicts.yml
@@ -6,10 +6,10 @@ on:
jobs:
Conflict_Check:
name: 'Check PR status: conflicts and resolution'
- runs-on: ubuntu-22.04
+ runs-on: ubuntu-latest
steps:
- name: check if PRs are dirty
- uses: eps1lon/actions-label-merge-conflict@releases/2.x
+ uses: eps1lon/actions-label-merge-conflict@v3
with:
dirtyLabel: "state: conflict"
removeOnDirtyLabel: "state: conflict resolved"
diff --git a/.github/workflows/unused-imports.yml b/.github/workflows/unused-imports.yml
new file mode 100644
index 000000000..da57bd270
--- /dev/null
+++ b/.github/workflows/unused-imports.yml
@@ -0,0 +1,22 @@
+name: Check for unused imports using Pylint
+on:
+ pull_request_target:
+ branches:
+ - current
+ - sagitta
+
+jobs:
+ Check-Unused-Imports:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v3
+ - name: Set up Python
+ uses: actions/setup-python@v3
+ with:
+ python-version: 3.11
+ - name: Install dependencies
+ run: |
+ python -m pip install --upgrade pip
+ pip install pylint
+ - name: Analysing the code with pylint
+ run: make unused-imports
diff --git a/Makefile b/Makefile
index 23e060125..44a6e35ed 100644
--- a/Makefile
+++ b/Makefile
@@ -6,8 +6,8 @@ SHIM_DIR := src/shim
LIBS := -lzmq
CFLAGS :=
BUILD_ARCH := $(shell dpkg-architecture -q DEB_BUILD_ARCH)
-
J2LINT := $(shell command -v j2lint 2> /dev/null)
+PYLINT_FILES := $(shell git ls-files *.py src/migration-scripts)
config_xml_src = $(wildcard interface-definitions/*.xml.in)
config_xml_obj = $(config_xml_src:.xml.in=.xml)
@@ -112,12 +112,9 @@ endif
sonar:
sonar-scanner -X -Dsonar.login=${SONAR_TOKEN}
-.PHONY: docs
-.ONESHELL:
-docs:
- sphinx-apidoc -o sphinx/source/ python/
- cd sphinx/
- PYTHONPATH=../python make html
+.PHONY: unused-imports
+unused-imports:
+ @pylint --disable=all --enable=W0611 $(PYLINT_FILES)
deb:
dpkg-buildpackage -uc -us -tc -b
diff --git a/data/templates/accel-ppp/pppoe.config.j2 b/data/templates/accel-ppp/pppoe.config.j2
index 6b01958e5..ddf0da518 100644
--- a/data/templates/accel-ppp/pppoe.config.j2
+++ b/data/templates/accel-ppp/pppoe.config.j2
@@ -64,12 +64,13 @@ vlan-mon={{ iface }},{{ iface_config.vlan | join(',') }}
service-name={{ service_name | join(',') }}
{% endif %}
{% if pado_delay %}
-{% set pado_delay_param = namespace(value='0') %}
-{% for delay in pado_delay | sort(attribute='0') %}
+{% set delay_without_sessions = pado_delay.delays_without_sessions[0] | default('0') %}
+{% set pado_delay_param = namespace(value=delay_without_sessions) %}
+{% for delay, sessions in pado_delay.delays_with_sessions | sort(attribute='1') %}
{% if not loop.last %}
-{% set pado_delay_param.value = pado_delay_param.value + ',' + delay + ':' + pado_delay[delay].sessions %}
+{% set pado_delay_param.value = pado_delay_param.value + ',' + delay + ':' + sessions | string %}
{% else %}
-{% set pado_delay_param.value = pado_delay_param.value + ',-1:' + pado_delay[delay].sessions %}
+{% set pado_delay_param.value = pado_delay_param.value + ',-1:' + sessions | string %}
{% endif %}
{% endfor %}
pado-delay={{ pado_delay_param.value }}
diff --git a/data/templates/conntrackd/conntrackd.conf.j2 b/data/templates/conntrackd/conntrackd.conf.j2
index 669b20877..30e619daf 100644
--- a/data/templates/conntrackd/conntrackd.conf.j2
+++ b/data/templates/conntrackd/conntrackd.conf.j2
@@ -4,6 +4,7 @@
Sync {
Mode FTFW {
DisableExternalCache {{ 'on' if disable_external_cache is vyos_defined else 'off' }}
+ StartupResync {{ 'on' if startup_resync is vyos_defined else 'off' }}
}
{% for iface, iface_config in interface.items() %}
{% if iface_config.peer is vyos_defined %}
diff --git a/data/templates/dhcp-client/dhcp6c-script.j2 b/data/templates/dhcp-client/dhcp6c-script.j2
new file mode 100644
index 000000000..14fb25cf6
--- /dev/null
+++ b/data/templates/dhcp-client/dhcp6c-script.j2
@@ -0,0 +1,31 @@
+#!/bin/sh
+# Update DNS information for DHCPv6 clients
+# should be used only if vyos-hostsd is running
+
+if /usr/bin/systemctl -q is-active vyos-hostsd; then
+ hostsd_client="/usr/bin/vyos-hostsd-client"
+ hostsd_changes=
+
+ if [ -n "$new_domain_name" ]; then
+ logmsg info "Deleting search domains with tag \"dhcpv6-{{ ifname }}\" via vyos-hostsd-client"
+ $hostsd_client --delete-search-domains --tag "dhcpv6-{{ ifname }}"
+ logmsg info "Adding domain name \"$new_domain_name\" as search domain with tag \"dhcpv6-{{ ifname }}\" via vyos-hostsd-client"
+ $hostsd_client --add-search-domains "$new_domain_name" --tag "dhcpv6-{{ ifname }}"
+ hostsd_changes=y
+ fi
+
+ if [ -n "$new_domain_name_servers" ]; then
+ logmsg info "Deleting nameservers with tag \"dhcpv6-{{ ifname }}\" via vyos-hostsd-client"
+ $hostsd_client --delete-name-servers --tag "dhcpv6-{{ ifname }}"
+ logmsg info "Adding nameservers \"$new_domain_name_servers\" with tag \"dhcpv6-{{ ifname }}\" via vyos-hostsd-client"
+ $hostsd_client --add-name-servers $new_domain_name_servers --tag "dhcpv6-{{ ifname }}"
+ hostsd_changes=y
+ fi
+
+ if [ $hostsd_changes ]; then
+ logmsg info "Applying changes via vyos-hostsd-client"
+ $hostsd_client --apply
+ else
+ logmsg info "No changes to apply via vyos-hostsd-client"
+ fi
+fi
diff --git a/data/templates/dhcp-client/ipv6.j2 b/data/templates/dhcp-client/ipv6.j2
index b5e55cdd1..311c856c8 100644
--- a/data/templates/dhcp-client/ipv6.j2
+++ b/data/templates/dhcp-client/ipv6.j2
@@ -23,6 +23,7 @@ interface {{ ifname }} {
send ia-pd {{ pd }}; # prefix delegation #{{ pd }}
{% endfor %}
{% endif %}
+ script "{{ dhcp6_script_file }}";
};
{% if address is vyos_defined and 'dhcpv6' in address %}
@@ -59,4 +60,3 @@ id-assoc pd {{ pd }} {
};
{% endfor %}
{% endif %}
-
diff --git a/data/templates/firewall/nftables-cgnat.j2 b/data/templates/firewall/nftables-cgnat.j2
new file mode 100644
index 000000000..79a8e3d5a
--- /dev/null
+++ b/data/templates/firewall/nftables-cgnat.j2
@@ -0,0 +1,47 @@
+#!/usr/sbin/nft -f
+
+add table ip cgnat
+flush table ip cgnat
+
+add map ip cgnat tcp_nat_map { type ipv4_addr: interval ipv4_addr . inet_service ; flags interval ;}
+add map ip cgnat udp_nat_map { type ipv4_addr: interval ipv4_addr . inet_service ; flags interval ;}
+add map ip cgnat icmp_nat_map { type ipv4_addr: interval ipv4_addr . inet_service ; flags interval ;}
+add map ip cgnat other_nat_map { type ipv4_addr: interval ipv4_addr ; flags interval ;}
+flush map ip cgnat tcp_nat_map
+flush map ip cgnat udp_nat_map
+flush map ip cgnat icmp_nat_map
+flush map ip cgnat other_nat_map
+
+table ip cgnat {
+ map tcp_nat_map {
+ type ipv4_addr : interval ipv4_addr . inet_service
+ flags interval
+ elements = { {{ proto_map_elements }} }
+ }
+
+ map udp_nat_map {
+ type ipv4_addr : interval ipv4_addr . inet_service
+ flags interval
+ elements = { {{ proto_map_elements }} }
+ }
+
+ map icmp_nat_map {
+ type ipv4_addr : interval ipv4_addr . inet_service
+ flags interval
+ elements = { {{ proto_map_elements }} }
+ }
+
+ map other_nat_map {
+ type ipv4_addr : interval ipv4_addr
+ flags interval
+ elements = { {{ other_map_elements }} }
+ }
+
+ chain POSTROUTING {
+ type nat hook postrouting priority srcnat; policy accept;
+ ip protocol tcp counter snat ip to ip saddr map @tcp_nat_map
+ ip protocol udp counter snat ip to ip saddr map @udp_nat_map
+ ip protocol icmp counter snat ip to ip saddr map @icmp_nat_map
+ counter snat ip to ip saddr map @other_nat_map
+ }
+}
diff --git a/data/templates/frr/bgpd.frr.j2 b/data/templates/frr/bgpd.frr.j2
index e9422b257..e5bfad59d 100644
--- a/data/templates/frr/bgpd.frr.j2
+++ b/data/templates/frr/bgpd.frr.j2
@@ -290,10 +290,7 @@ router bgp {{ system_as }} {{ 'vrf ' ~ vrf if vrf is vyos_defined }}
{% endif %}
{% if afi_config.aggregate_address is vyos_defined %}
{% for aggregate, aggregate_config in afi_config.aggregate_address.items() %}
- aggregate-address {{ aggregate }}{{ ' as-set' if aggregate_config.as_set is vyos_defined }}{{ ' summary-only' if aggregate_config.summary_only is vyos_defined }}
-{% if aggregate_config.route_map is vyos_defined %}
- aggregate-address {{ aggregate }} route-map {{ aggregate_config.route_map }}
-{% endif %}
+ aggregate-address {{ aggregate }} {{ 'as-set' if aggregate_config.as_set is vyos_defined }} {{ 'summary-only' if aggregate_config.summary_only is vyos_defined }} {{ 'route-map ' ~ aggregate_config.route_map if aggregate_config.route_map is vyos_defined }}
{% endfor %}
{% endif %}
{% if afi_config.maximum_paths.ebgp is vyos_defined %}
@@ -537,6 +534,9 @@ router bgp {{ system_as }} {{ 'vrf ' ~ vrf if vrf is vyos_defined }}
{% if parameters.allow_martian_nexthop is vyos_defined %}
bgp allow-martian-nexthop
{% endif %}
+{% if parameters.disable_ebgp_connected_route_check is vyos_defined %}
+ bgp disable-ebgp-connected-route-check
+{% endif %}
{% if parameters.always_compare_med is vyos_defined %}
bgp always-compare-med
{% endif %}
diff --git a/data/templates/ipsec/swanctl/remote_access.j2 b/data/templates/ipsec/swanctl/remote_access.j2
index af7f2994e..adfa32bde 100644
--- a/data/templates/ipsec/swanctl/remote_access.j2
+++ b/data/templates/ipsec/swanctl/remote_access.j2
@@ -35,6 +35,11 @@
auth = {{ rw_conf.authentication.client_mode }}
eap_id = %any
{% endif %}
+{% if rw_conf.authentication.client_mode is vyos_defined('eap-tls') or rw_conf.authentication.client_mode is vyos_defined('x509') %}
+{# pass all configured CAs as filenames, separated by commas #}
+{# this will produce a string like "MyCA1.pem,MyCA2.pem" #}
+ cacerts = {{ '.pem,'.join(rw_conf.authentication.x509.ca_certificate) ~ '.pem' }}
+{% endif %}
}
children {
ikev2-vpn {
diff --git a/data/templates/ssh/override.conf.j2 b/data/templates/ssh/override.conf.j2
deleted file mode 100644
index 4454ad1b8..000000000
--- a/data/templates/ssh/override.conf.j2
+++ /dev/null
@@ -1,14 +0,0 @@
-{% set vrf_command = 'ip vrf exec ' ~ vrf ~ ' ' if vrf is vyos_defined else '' %}
-[Unit]
-StartLimitIntervalSec=0
-After=vyos-router.service
-ConditionPathExists={{ config_file }}
-
-[Service]
-EnvironmentFile=
-ExecStart=
-ExecStart={{ vrf_command }}/usr/sbin/sshd -f {{ config_file }}
-Restart=always
-RestartPreventExitStatus=
-RestartSec=10
-RuntimeDirectoryPreserve=yes
diff --git a/debian/control b/debian/control
index c5a60f660..801cce269 100644
--- a/debian/control
+++ b/debian/control
@@ -11,6 +11,8 @@ Build-Depends:
libvyosconfig0 (>= 0.0.7),
libzmq3-dev,
python3 (>= 3.10),
+# For QA
+ pylint,
# For generating command definitions
python3-lxml,
python3-xmltodict,
@@ -20,8 +22,8 @@ Build-Depends:
python3-nose,
python3-jinja2,
python3-psutil,
+ python3-requests,
python3-setuptools,
- python3-sphinx,
quilt,
whois
Standards-Version: 3.9.6
diff --git a/debian/vyos-1x.postinst b/debian/vyos-1x.postinst
index 74fd229b4..78e895d6e 100644
--- a/debian/vyos-1x.postinst
+++ b/debian/vyos-1x.postinst
@@ -71,6 +71,7 @@ if ! grep -q '^tacacs' /etc/passwd; then
adduser --quiet tacacs${level} sudo
adduser --quiet tacacs${level} disk
adduser --quiet tacacs${level} frr
+ adduser --quiet tacacs${level} _kea
fi
level=$(( level+1 ))
done 2>&1 | grep -v "User tacacs${level} already exists"
@@ -102,6 +103,7 @@ if ! grep -q '^radius_priv_user' /etc/passwd; then
adduser --quiet radius_priv_user disk
adduser --quiet radius_priv_user users
adduser --quiet radius_priv_user frr
+ adduser --quiet radius_priv_user _kea
fi
# add hostsd group for vyos-hostsd
@@ -192,3 +194,10 @@ systemctl enable vyos-config-cloud-init.service
# Update XML cache
python3 /usr/lib/python3/dist-packages/vyos/xml_ref/update_cache.py
+
+# Generate hardlinks for systemd units for multi VRF support
+# as softlinks will fail in systemd:
+# symlink target name type "ssh.service" does not match source, rejecting.
+if [ ! -f /lib/systemd/system/ssh@.service ]; then
+ ln /lib/systemd/system/ssh.service /lib/systemd/system/ssh@.service
+fi
diff --git a/interface-definitions/container.xml.in b/interface-definitions/container.xml.in
index 7e1f4811a..e7dacea36 100644
--- a/interface-definitions/container.xml.in
+++ b/interface-definitions/container.xml.in
@@ -21,11 +21,11 @@
<valueless/>
</properties>
</leafNode>
- <leafNode name="cap-add">
+ <leafNode name="capability">
<properties>
- <help>Container capabilities/permissions</help>
+ <help>Grant individual Linux capability to container instance</help>
<completionHelp>
- <list>net-admin net-bind-service net-raw setpcap sys-admin sys-module sys-time</list>
+ <list>net-admin net-bind-service net-raw setpcap sys-admin sys-module sys-nice sys-time</list>
</completionHelp>
<valueHelp>
<format>net-admin</format>
@@ -52,11 +52,15 @@
<description>Load, unload and delete kernel modules</description>
</valueHelp>
<valueHelp>
+ <format>sys-nice</format>
+ <description>Permission to set process nice value</description>
+ </valueHelp>
+ <valueHelp>
<format>sys-time</format>
<description>Permission to set system clock</description>
</valueHelp>
<constraint>
- <regex>(net-admin|net-bind-service|net-raw|setpcap|sys-admin|sys-module|sys-time)</regex>
+ <regex>(net-admin|net-bind-service|net-raw|setpcap|sys-admin|sys-module|sys-nice|sys-time)</regex>
</constraint>
<multi/>
</properties>
diff --git a/interface-definitions/firewall.xml.in b/interface-definitions/firewall.xml.in
index 3219471b1..24e63c5ec 100644
--- a/interface-definitions/firewall.xml.in
+++ b/interface-definitions/firewall.xml.in
@@ -56,8 +56,9 @@
<properties>
<help>Firewall address-group</help>
<constraint>
- <regex>[a-zA-Z0-9][\w\-\.]*</regex>
+ #include <include/constraint/alpha-numeric-hyphen-underscore-dot.xml.i>
</constraint>
+ <constraintErrorMessage>Name of firewall group can only contain alphanumeric letters, hyphen, underscores and dot</constraintErrorMessage>
</properties>
<children>
<leafNode name="address">
@@ -96,7 +97,7 @@
<constraint>
<regex>[a-zA-Z_][a-zA-Z0-9]?[\w\-\.]*</regex>
</constraint>
- <constraintErrorMessage>Name of domain-group can only contain alpha-numeric letters, hyphen, underscores and not start with numeric</constraintErrorMessage>
+ <constraintErrorMessage>Name of domain-group can only contain alphanumeric letters, hyphen, underscores and not start with numeric</constraintErrorMessage>
</properties>
<children>
<leafNode name="address">
@@ -124,8 +125,9 @@
<properties>
<help>Firewall dynamic address group</help>
<constraint>
- <regex>[a-zA-Z0-9][\w\-\.]*</regex>
+ #include <include/constraint/alpha-numeric-hyphen-underscore-dot.xml.i>
</constraint>
+ <constraintErrorMessage>Name of firewall group can only contain alphanumeric letters, hyphen, underscores and dot</constraintErrorMessage>
</properties>
<children>
#include <include/generic-description.xml.i>
@@ -148,8 +150,9 @@
<properties>
<help>Firewall interface-group</help>
<constraint>
- <regex>[a-zA-Z0-9][\w\-\.]*</regex>
+ #include <include/constraint/alpha-numeric-hyphen-underscore-dot.xml.i>
</constraint>
+ <constraintErrorMessage>Name of firewall group can only contain alphanumeric letters, hyphen, underscores and dot</constraintErrorMessage>
</properties>
<children>
<leafNode name="interface">
@@ -177,8 +180,9 @@
<properties>
<help>Firewall ipv6-address-group</help>
<constraint>
- <regex>[a-zA-Z0-9][\w\-\.]*</regex>
+ #include <include/constraint/alpha-numeric-hyphen-underscore-dot.xml.i>
</constraint>
+ <constraintErrorMessage>Name of firewall group can only contain alphanumeric letters, hyphen, underscores and dot</constraintErrorMessage>
</properties>
<children>
<leafNode name="address">
@@ -215,8 +219,9 @@
<properties>
<help>Firewall ipv6-network-group</help>
<constraint>
- <regex>[a-zA-Z0-9][\w\-\.]*</regex>
+ #include <include/constraint/alpha-numeric-hyphen-underscore-dot.xml.i>
</constraint>
+ <constraintErrorMessage>Name of firewall group can only contain alphanumeric letters, hyphen, underscores and dot</constraintErrorMessage>
</properties>
<children>
#include <include/generic-description.xml.i>
@@ -248,8 +253,9 @@
<properties>
<help>Firewall mac-group</help>
<constraint>
- <regex>[a-zA-Z0-9][\w\-\.]*</regex>
+ #include <include/constraint/alpha-numeric-hyphen-underscore-dot.xml.i>
</constraint>
+ <constraintErrorMessage>Name of firewall group can only contain alphanumeric letters, hyphen, underscores and dot</constraintErrorMessage>
</properties>
<children>
#include <include/generic-description.xml.i>
@@ -281,8 +287,9 @@
<properties>
<help>Firewall network-group</help>
<constraint>
- <regex>[a-zA-Z0-9][\w\-\.]*</regex>
+ #include <include/constraint/alpha-numeric-hyphen-underscore-dot.xml.i>
</constraint>
+ <constraintErrorMessage>Name of firewall group can only contain alphanumeric letters, hyphen, underscores and dot</constraintErrorMessage>
</properties>
<children>
#include <include/generic-description.xml.i>
@@ -314,8 +321,9 @@
<properties>
<help>Firewall port-group</help>
<constraint>
- <regex>[a-zA-Z0-9][\w\-\.]*</regex>
+ #include <include/constraint/alpha-numeric-hyphen-underscore-dot.xml.i>
</constraint>
+ <constraintErrorMessage>Name of firewall group can only contain alphanumeric letters, hyphen, underscores and dot</constraintErrorMessage>
</properties>
<children>
#include <include/generic-description.xml.i>
diff --git a/interface-definitions/include/bgp/neighbor-disable-connected-check.xml.i b/interface-definitions/include/bgp/neighbor-disable-connected-check.xml.i
index cb8b610b4..aef5a55e9 100644
--- a/interface-definitions/include/bgp/neighbor-disable-connected-check.xml.i
+++ b/interface-definitions/include/bgp/neighbor-disable-connected-check.xml.i
@@ -1,7 +1,7 @@
<!-- include start from bgp/neighbor-disable-connected-check.xml.i -->
<leafNode name="disable-connected-check">
<properties>
- <help>Disable check to see if eBGP peer address is a connected route</help>
+ <help>Allow peerings between eBGP peer using loopback/dummy address</help>
<valueless/>
</properties>
</leafNode>
diff --git a/interface-definitions/include/bgp/protocol-common-config.xml.i b/interface-definitions/include/bgp/protocol-common-config.xml.i
index ca67eaf3c..0f05625a7 100644
--- a/interface-definitions/include/bgp/protocol-common-config.xml.i
+++ b/interface-definitions/include/bgp/protocol-common-config.xml.i
@@ -1249,6 +1249,12 @@
<valueless/>
</properties>
</leafNode>
+ <leafNode name="disable-ebgp-connected-route-check">
+ <properties>
+ <help>Disable checking if nexthop is connected on eBGP session</help>
+ <valueless/>
+ </properties>
+ </leafNode>
<leafNode name="always-compare-med">
<properties>
<help>Always compare MEDs from different neighbors</help>
diff --git a/interface-definitions/include/constraint/alpha-numeric-hyphen-underscore-dot.xml.i b/interface-definitions/include/constraint/alpha-numeric-hyphen-underscore-dot.xml.i
index 7aeb85260..34c94e53c 100644
--- a/interface-definitions/include/constraint/alpha-numeric-hyphen-underscore-dot.xml.i
+++ b/interface-definitions/include/constraint/alpha-numeric-hyphen-underscore-dot.xml.i
@@ -1,3 +1,3 @@
<!-- include start from constraint/alpha-numeric-hyphen-underscore-dot.xml.i -->
-<regex>[-_a-zA-Z0-9.]+</regex>
+<regex>[-_a-zA-Z0-9][\w\-\.\+]*</regex>
<!-- include end -->
diff --git a/interface-definitions/include/constraint/vrf.xml.i b/interface-definitions/include/constraint/vrf.xml.i
new file mode 100644
index 000000000..a1922bb6d
--- /dev/null
+++ b/interface-definitions/include/constraint/vrf.xml.i
@@ -0,0 +1,6 @@
+<!-- include start from constraint/vrf.xml.i -->
+<constraint>
+ <validator name="vrf-name"/>
+</constraint>
+<constraintErrorMessage>VRF instance name must be 15 characters or less and can not\nbe named as regular network interfaces.\nA name must starts from a letter.\n</constraintErrorMessage>
+<!-- include end -->
diff --git a/interface-definitions/include/firewall/global-options.xml.i b/interface-definitions/include/firewall/global-options.xml.i
index 415d85f05..9cd0b3239 100644
--- a/interface-definitions/include/firewall/global-options.xml.i
+++ b/interface-definitions/include/firewall/global-options.xml.i
@@ -44,6 +44,26 @@
</properties>
<defaultValue>disable</defaultValue>
</leafNode>
+ <leafNode name="directed-broadcast">
+ <properties>
+ <help>Policy for handling IPv4 directed broadcast forwarding on all interfaces</help>
+ <completionHelp>
+ <list>enable disable</list>
+ </completionHelp>
+ <valueHelp>
+ <format>enable</format>
+ <description>Enable IPv4 directed broadcast forwarding on all interfaces</description>
+ </valueHelp>
+ <valueHelp>
+ <format>disable</format>
+ <description>Disable IPv4 directed broadcast forwarding on all interfaces</description>
+ </valueHelp>
+ <constraint>
+ <regex>(enable|disable)</regex>
+ </constraint>
+ </properties>
+ <defaultValue>enable</defaultValue>
+ </leafNode>
<leafNode name="ip-src-route">
<properties>
<help>Policy for handling IPv4 packets with source route option</help>
diff --git a/interface-definitions/include/interface/mtu-68-16000.xml.i b/interface-definitions/include/interface/mtu-68-16000.xml.i
index cb666f470..df1b7b716 100644
--- a/interface-definitions/include/interface/mtu-68-16000.xml.i
+++ b/interface-definitions/include/interface/mtu-68-16000.xml.i
@@ -11,6 +11,5 @@
</constraint>
<constraintErrorMessage>MTU must be between 68 and 16000</constraintErrorMessage>
</properties>
- <defaultValue>1500</defaultValue>
</leafNode>
<!-- include end -->
diff --git a/interface-definitions/include/interface/vrf.xml.i b/interface-definitions/include/interface/vrf.xml.i
index 8605f56e8..ef0058f86 100644
--- a/interface-definitions/include/interface/vrf.xml.i
+++ b/interface-definitions/include/interface/vrf.xml.i
@@ -9,6 +9,7 @@
<completionHelp>
<path>vrf name</path>
</completionHelp>
+ #include <include/constraint/vrf.xml.i>
</properties>
</leafNode>
<!-- include end -->
diff --git a/interface-definitions/include/qos/queue-average-packet.xml.i b/interface-definitions/include/qos/queue-average-packet.xml.i
new file mode 100644
index 000000000..2f8bfe266
--- /dev/null
+++ b/interface-definitions/include/qos/queue-average-packet.xml.i
@@ -0,0 +1,16 @@
+<!-- include start from qos/queue-average-packet.xml.i -->
+<leafNode name="average-packet">
+ <properties>
+ <help>Average packet size (bytes)</help>
+ <valueHelp>
+ <format>u32:16-10240</format>
+ <description>Average packet size in bytes</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 16-10240"/>
+ </constraint>
+ <constraintErrorMessage>Average packet size must be between 16 and 10240</constraintErrorMessage>
+ </properties>
+ <defaultValue>1024</defaultValue>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/include/qos/queue-mark-probability.xml.i b/interface-definitions/include/qos/queue-mark-probability.xml.i
new file mode 100644
index 000000000..1a2862845
--- /dev/null
+++ b/interface-definitions/include/qos/queue-mark-probability.xml.i
@@ -0,0 +1,16 @@
+<!-- include start from qos/queue-mark-probability.xml.i -->
+<leafNode name="mark-probability">
+ <properties>
+ <help>Mark probability for random detection</help>
+ <valueHelp>
+ <format>u32</format>
+ <description>Numeric value (1/N)</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--positive"/>
+ </constraint>
+ <constraintErrorMessage>Mark probability must be greater than 0</constraintErrorMessage>
+ </properties>
+ <defaultValue>10</defaultValue>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/include/qos/queue-maximum-threshold.xml.i b/interface-definitions/include/qos/queue-maximum-threshold.xml.i
new file mode 100644
index 000000000..66d17ccc4
--- /dev/null
+++ b/interface-definitions/include/qos/queue-maximum-threshold.xml.i
@@ -0,0 +1,16 @@
+<!-- include start from qos/queue-maximum-threshold.xml.i -->
+<leafNode name="maximum-threshold">
+ <properties>
+ <help>Maximum threshold for random detection</help>
+ <valueHelp>
+ <format>u32:0-4096</format>
+ <description>Maximum threshold in packets</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 0-4096"/>
+ </constraint>
+ <constraintErrorMessage>Threshold must be between 0 and 4096</constraintErrorMessage>
+ </properties>
+ <defaultValue>18</defaultValue>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/include/qos/queue-minimum-threshold.xml.i b/interface-definitions/include/qos/queue-minimum-threshold.xml.i
new file mode 100644
index 000000000..81e12d6e2
--- /dev/null
+++ b/interface-definitions/include/qos/queue-minimum-threshold.xml.i
@@ -0,0 +1,15 @@
+<!-- include start from qos/queue-minimum-threshold.xml.i -->
+<leafNode name="minimum-threshold">
+ <properties>
+ <help>Minimum threshold for random detection</help>
+ <valueHelp>
+ <format>u32:0-4096</format>
+ <description>Minimum threshold in packets</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 0-4096"/>
+ </constraint>
+ <constraintErrorMessage>Threshold must be between 0 and 4096</constraintErrorMessage>
+ </properties>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/include/version/container-version.xml.i b/interface-definitions/include/version/container-version.xml.i
index 129469cec..ed6e942cd 100644
--- a/interface-definitions/include/version/container-version.xml.i
+++ b/interface-definitions/include/version/container-version.xml.i
@@ -1,3 +1,3 @@
<!-- include start from include/version/container-version.xml.i -->
-<syntaxVersion component='container' version='1'></syntaxVersion>
+<syntaxVersion component='container' version='2'></syntaxVersion>
<!-- include end -->
diff --git a/interface-definitions/include/version/firewall-version.xml.i b/interface-definitions/include/version/firewall-version.xml.i
index 6702ee041..fa8e26f78 100644
--- a/interface-definitions/include/version/firewall-version.xml.i
+++ b/interface-definitions/include/version/firewall-version.xml.i
@@ -1,3 +1,3 @@
<!-- include start from include/version/firewall-version.xml.i -->
-<syntaxVersion component='firewall' version='14'></syntaxVersion>
+<syntaxVersion component='firewall' version='15'></syntaxVersion>
<!-- include end -->
diff --git a/interface-definitions/include/vrf-multi.xml.i b/interface-definitions/include/vrf-multi.xml.i
new file mode 100644
index 000000000..0b22894e4
--- /dev/null
+++ b/interface-definitions/include/vrf-multi.xml.i
@@ -0,0 +1,22 @@
+<!-- include start from interface/vrf.xml.i -->
+<leafNode name="vrf">
+ <properties>
+ <help>VRF instance name</help>
+ <completionHelp>
+ <path>vrf name</path>
+ <list>default</list>
+ </completionHelp>
+ <valueHelp>
+ <format>default</format>
+ <description>Explicitly start in default VRF</description>
+ </valueHelp>
+ <valueHelp>
+ <format>txt</format>
+ <description>VRF instance name</description>
+ </valueHelp>
+ #include <include/constraint/vrf.xml.i>
+ <multi/>
+ </properties>
+ <defaultValue>default</defaultValue>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/interfaces_bonding.xml.in b/interface-definitions/interfaces_bonding.xml.in
index 62ee0bdc7..92c0911db 100644
--- a/interface-definitions/interfaces_bonding.xml.in
+++ b/interface-definitions/interfaces_bonding.xml.in
@@ -261,6 +261,9 @@
</children>
</node>
#include <include/interface/mtu-68-16000.xml.i>
+ <leafNode name="mtu">
+ <defaultValue>1500</defaultValue>
+ </leafNode>
<leafNode name="primary">
<properties>
<help>Primary device interface</help>
diff --git a/interface-definitions/interfaces_bridge.xml.in b/interface-definitions/interfaces_bridge.xml.in
index 7fb5f121a..29dd61df5 100644
--- a/interface-definitions/interfaces_bridge.xml.in
+++ b/interface-definitions/interfaces_bridge.xml.in
@@ -41,6 +41,9 @@
#include <include/interface/disable.xml.i>
#include <include/interface/vrf.xml.i>
#include <include/interface/mtu-68-16000.xml.i>
+ <leafNode name="mtu">
+ <defaultValue>1500</defaultValue>
+ </leafNode>
<leafNode name="forwarding-delay">
<properties>
<help>Forwarding delay</help>
diff --git a/interface-definitions/interfaces_dummy.xml.in b/interface-definitions/interfaces_dummy.xml.in
index ef8ee78e7..36b4e41f2 100644
--- a/interface-definitions/interfaces_dummy.xml.in
+++ b/interface-definitions/interfaces_dummy.xml.in
@@ -46,6 +46,9 @@
</children>
</node>
#include <include/interface/mtu-68-16000.xml.i>
+ <leafNode name="mtu">
+ <defaultValue>1500</defaultValue>
+ </leafNode>
#include <include/interface/mirror.xml.i>
#include <include/interface/netns.xml.i>
#include <include/interface/redirect.xml.i>
diff --git a/interface-definitions/interfaces_vti.xml.in b/interface-definitions/interfaces_vti.xml.in
index 158d9afd0..39fb3131e 100644
--- a/interface-definitions/interfaces_vti.xml.in
+++ b/interface-definitions/interfaces_vti.xml.in
@@ -22,6 +22,9 @@
#include <include/interface/ipv4-options.xml.i>
#include <include/interface/ipv6-options.xml.i>
#include <include/interface/mtu-68-16000.xml.i>
+ <leafNode name="mtu">
+ <defaultValue>1500</defaultValue>
+ </leafNode>
#include <include/interface/mirror.xml.i>
#include <include/interface/redirect.xml.i>
#include <include/interface/vrf.xml.i>
diff --git a/interface-definitions/interfaces_wireguard.xml.in b/interface-definitions/interfaces_wireguard.xml.in
index fba1064ef..ce49de038 100644
--- a/interface-definitions/interfaces_wireguard.xml.in
+++ b/interface-definitions/interfaces_wireguard.xml.in
@@ -21,10 +21,10 @@
#include <include/interface/disable.xml.i>
#include <include/port-number.xml.i>
#include <include/interface/mtu-68-16000.xml.i>
- #include <include/interface/mirror.xml.i>
<leafNode name="mtu">
<defaultValue>1420</defaultValue>
</leafNode>
+ #include <include/interface/mirror.xml.i>
#include <include/interface/ipv4-options.xml.i>
#include <include/interface/ipv6-options.xml.i>
<leafNode name="fwmark">
diff --git a/interface-definitions/nat_cgnat.xml.in b/interface-definitions/nat_cgnat.xml.in
new file mode 100644
index 000000000..caa26b4d9
--- /dev/null
+++ b/interface-definitions/nat_cgnat.xml.in
@@ -0,0 +1,197 @@
+<?xml version="1.0"?>
+<interfaceDefinition>
+ <node name="nat">
+ <children>
+ <node name="cgnat" owner="${vyos_conf_scripts_dir}/nat_cgnat.py">
+ <properties>
+ <help>Carrier-grade NAT (CGNAT) parameters</help>
+ <priority>221</priority>
+ </properties>
+ <children>
+ <node name="pool">
+ <properties>
+ <help>External and internal pool parameters</help>
+ </properties>
+ <children>
+ <tagNode name="external">
+ <properties>
+ <help>External pool name</help>
+ <valueHelp>
+ <format>txt</format>
+ <description>External pool name</description>
+ </valueHelp>
+ <constraint>
+ #include <include/constraint/alpha-numeric-hyphen-underscore-dot.xml.i>
+ </constraint>
+ <constraintErrorMessage>Name of pool can only contain alpha-numeric letters, hyphen and underscores</constraintErrorMessage>
+ </properties>
+ <children>
+ <leafNode name="external-port-range">
+ <properties>
+ <help>Port range</help>
+ <valueHelp>
+ <format>range</format>
+ <description>Numbered port range (e.g., 1001-1005)</description>
+ </valueHelp>
+ <constraint>
+ <validator name="port-range"/>
+ </constraint>
+ </properties>
+ <defaultValue>1024-65535</defaultValue>
+ </leafNode>
+ <node name="per-user-limit">
+ <properties>
+ <help>Per user limits for the pool</help>
+ </properties>
+ <children>
+ <leafNode name="port">
+ <properties>
+ <help>Ports per user</help>
+ <valueHelp>
+ <format>u32:1-65535</format>
+ <description>Numeric IP port</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-65535"/>
+ </constraint>
+ </properties>
+ <defaultValue>2000</defaultValue>
+ </leafNode>
+ </children>
+ </node>
+ <tagNode name="range">
+ <properties>
+ <help>Range of IP addresses</help>
+ <valueHelp>
+ <format>ipv4net</format>
+ <description>IPv4 prefix</description>
+ </valueHelp>
+ <valueHelp>
+ <format>ipv4range</format>
+ <description>IPv4 address range</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ipv4-prefix"/>
+ <validator name="ipv4-host"/>
+ <validator name="ipv4-range"/>
+ </constraint>
+ </properties>
+ <children>
+ <leafNode name="seq">
+ <properties>
+ <help>Sequence</help>
+ <valueHelp>
+ <format>u32:1-999999</format>
+ <description>Sequence number</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-999999"/>
+ </constraint>
+ <constraintErrorMessage>Sequence number must be between 1 and 999999</constraintErrorMessage>
+ </properties>
+ </leafNode>
+ </children>
+ </tagNode>
+ </children>
+ </tagNode>
+ <tagNode name="internal">
+ <properties>
+ <help>Internal pool name</help>
+ <valueHelp>
+ <format>txt</format>
+ <description>Internal pool name</description>
+ </valueHelp>
+ <constraint>
+ #include <include/constraint/alpha-numeric-hyphen-underscore-dot.xml.i>
+ </constraint>
+ <constraintErrorMessage>Name of pool can only contain alpha-numeric letters, hyphen and underscores</constraintErrorMessage>
+ </properties>
+ <children>
+ <leafNode name="range">
+ <properties>
+ <help>Range of IP addresses</help>
+ <valueHelp>
+ <format>ipv4net</format>
+ <description>IPv4 prefix</description>
+ </valueHelp>
+ <valueHelp>
+ <format>ipv4range</format>
+ <description>IPv4 address range</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ipv4-prefix"/>
+ <validator name="ipv4-host"/>
+ <validator name="ipv4-range"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ </children>
+ </tagNode>
+ </children>
+ </node>
+ <tagNode name="rule">
+ <properties>
+ <help>Rule</help>
+ <valueHelp>
+ <format>u32:1-999999</format>
+ <description>Number for this CGNAT rule</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-999999"/>
+ </constraint>
+ <constraintErrorMessage>Rule number must be between 1 and 999999</constraintErrorMessage>
+ </properties>
+ <children>
+ <node name="source">
+ <properties>
+ <help>Source parameters</help>
+ </properties>
+ <children>
+ <leafNode name="pool">
+ <properties>
+ <help>Source internal pool</help>
+ <completionHelp>
+ <path>nat cgnat pool internal</path>
+ </completionHelp>
+ <valueHelp>
+ <format>txt</format>
+ <description>Source internal pool name</description>
+ </valueHelp>
+ <constraint>
+ #include <include/constraint/alpha-numeric-hyphen-underscore-dot.xml.i>
+ </constraint>
+ <constraintErrorMessage>Name of pool can only contain alpha-numeric letters, hyphen and underscores</constraintErrorMessage>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ <node name="translation">
+ <properties>
+ <help>Translation parameters</help>
+ </properties>
+ <children>
+ <leafNode name="pool">
+ <properties>
+ <help>Translation external pool</help>
+ <completionHelp>
+ <path>nat cgnat pool external</path>
+ </completionHelp>
+ <valueHelp>
+ <format>txt</format>
+ <description>Translation external pool name</description>
+ </valueHelp>
+ <constraint>
+ #include <include/constraint/alpha-numeric-hyphen-underscore-dot.xml.i>
+ </constraint>
+ <constraintErrorMessage>Name of pool can only contain alpha-numeric letters, hyphen and underscores</constraintErrorMessage>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ </children>
+ </tagNode>
+ </children>
+ </node>
+ </children>
+ </node>
+</interfaceDefinition>
diff --git a/interface-definitions/qos.xml.in b/interface-definitions/qos.xml.in
index 7618c3027..8f9ae3fa6 100644
--- a/interface-definitions/qos.xml.in
+++ b/interface-definitions/qos.xml.in
@@ -470,61 +470,10 @@
</properties>
<children>
#include <include/qos/queue-limit-1-4294967295.xml.i>
- <leafNode name="average-packet">
- <properties>
- <help>Average packet size (bytes)</help>
- <valueHelp>
- <format>u32:16-10240</format>
- <description>Average packet size in bytes</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--range 0-100"/>
- </constraint>
- <constraintErrorMessage>Average packet size must be between 16 and 10240</constraintErrorMessage>
- </properties>
- <defaultValue>1024</defaultValue>
- </leafNode>
- <leafNode name="mark-probability">
- <properties>
- <help>Mark probability for this precedence</help>
- <valueHelp>
- <format>&lt;number&gt;</format>
- <description>Numeric value (1/N)</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--positive"/>
- </constraint>
- <constraintErrorMessage>Mark probability must be greater than 0</constraintErrorMessage>
- </properties>
- <defaultValue>10</defaultValue>
- </leafNode>
- <leafNode name="maximum-threshold">
- <properties>
- <help>Maximum threshold for random detection</help>
- <valueHelp>
- <format>u32:0-4096</format>
- <description>Maximum Threshold in packets</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--range 0-4096"/>
- </constraint>
- <constraintErrorMessage>Threshold must be between 0 and 4096</constraintErrorMessage>
- </properties>
- <defaultValue>18</defaultValue>
- </leafNode>
- <leafNode name="minimum-threshold">
- <properties>
- <help>Minimum threshold for random detection</help>
- <valueHelp>
- <format>u32:0-4096</format>
- <description>Maximum Threshold in packets</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--range 0-4096"/>
- </constraint>
- <constraintErrorMessage>Threshold must be between 0 and 4096</constraintErrorMessage>
- </properties>
- </leafNode>
+ #include <include/qos/queue-average-packet.xml.i>
+ #include <include/qos/queue-maximum-threshold.xml.i>
+ #include <include/qos/queue-minimum-threshold.xml.i>
+ #include <include/qos/queue-mark-probability.xml.i>
</children>
</tagNode>
</children>
@@ -697,6 +646,10 @@
#include <include/qos/interval.xml.i>
#include <include/qos/class-match.xml.i>
#include <include/qos/class-priority.xml.i>
+ #include <include/qos/queue-average-packet.xml.i>
+ #include <include/qos/queue-maximum-threshold.xml.i>
+ #include <include/qos/queue-minimum-threshold.xml.i>
+ #include <include/qos/queue-mark-probability.xml.i>
#include <include/qos/queue-limit-1-4294967295.xml.i>
#include <include/qos/queue-type.xml.i>
<leafNode name="queue-type">
@@ -759,6 +712,10 @@
</properties>
<defaultValue>20</defaultValue>
</leafNode>
+ #include <include/qos/queue-average-packet.xml.i>
+ #include <include/qos/queue-maximum-threshold.xml.i>
+ #include <include/qos/queue-minimum-threshold.xml.i>
+ #include <include/qos/queue-mark-probability.xml.i>
#include <include/qos/queue-limit-1-4294967295.xml.i>
#include <include/qos/queue-type.xml.i>
<leafNode name="queue-type">
diff --git a/interface-definitions/service_config-sync.xml.in b/interface-definitions/service_config-sync.xml.in
index cb51a33b1..e9ea9aa4b 100644
--- a/interface-definitions/service_config-sync.xml.in
+++ b/interface-definitions/service_config-sync.xml.in
@@ -495,6 +495,12 @@
<valueless/>
</properties>
</leafNode>
+ <leafNode name="time-zone">
+ <properties>
+ <help>Local time zone</help>
+ <valueless/>
+ </properties>
+ </leafNode>
</children>
</node>
<leafNode name="vpn">
diff --git a/interface-definitions/service_conntrack-sync.xml.in b/interface-definitions/service_conntrack-sync.xml.in
index 397864867..631c830b4 100644
--- a/interface-definitions/service_conntrack-sync.xml.in
+++ b/interface-definitions/service_conntrack-sync.xml.in
@@ -81,6 +81,12 @@
<multi/>
</properties>
</leafNode>
+ <leafNode name="startup-resync">
+ <properties>
+ <help>Order conntrackd to request a complete conntrack table resync against the other node at startup</help>
+ <valueless/>
+ </properties>
+ </leafNode>
<node name="failover-mechanism">
<properties>
<help>Failover mechanism to use for conntrack-sync</help>
diff --git a/interface-definitions/service_dhcp-server.xml.in b/interface-definitions/service_dhcp-server.xml.in
index 2afa05a8a..cb5f9a804 100644
--- a/interface-definitions/service_dhcp-server.xml.in
+++ b/interface-definitions/service_dhcp-server.xml.in
@@ -22,6 +22,27 @@
</properties>
<children>
#include <include/source-address-ipv4.xml.i>
+ <leafNode name="mode">
+ <properties>
+ <help>Configure high availability mode</help>
+ <completionHelp>
+ <list>active-active active-passive</list>
+ </completionHelp>
+ <valueHelp>
+ <format>active-active</format>
+ <description>Both server attend DHCP requests</description>
+ </valueHelp>
+ <valueHelp>
+ <format>active-passive</format>
+ <description>Only primary server attends DHCP requests</description>
+ </valueHelp>
+ <constraint>
+ <regex>(active-active|active-passive)</regex>
+ </constraint>
+ <constraintErrorMessage>Invalid DHCP high availability mode</constraintErrorMessage>
+ </properties>
+ <defaultValue>active-active</defaultValue>
+ </leafNode>
<leafNode name="remote">
<properties>
<help>IPv4 remote address used for connection</help>
diff --git a/interface-definitions/service_ssh.xml.in b/interface-definitions/service_ssh.xml.in
index 5c893bd35..d9eee1ab8 100644
--- a/interface-definitions/service_ssh.xml.in
+++ b/interface-definitions/service_ssh.xml.in
@@ -262,7 +262,7 @@
</constraint>
</properties>
</leafNode>
- #include <include/interface/vrf.xml.i>
+ #include <include/vrf-multi.xml.i>
</children>
</node>
</children>
diff --git a/interface-definitions/system_ip.xml.in b/interface-definitions/system_ip.xml.in
index 015eb270f..b4b5092fe 100644
--- a/interface-definitions/system_ip.xml.in
+++ b/interface-definitions/system_ip.xml.in
@@ -23,12 +23,6 @@
<valueless/>
</properties>
</leafNode>
- <leafNode name="disable-directed-broadcast">
- <properties>
- <help>Disable IPv4 directed broadcast forwarding on all interfaces</help>
- <valueless/>
- </properties>
- </leafNode>
<node name="multipath">
<properties>
<help>IPv4 multipath settings</help>
diff --git a/interface-definitions/vrf.xml.in b/interface-definitions/vrf.xml.in
index 25f26d0cc..94ed96e4b 100644
--- a/interface-definitions/vrf.xml.in
+++ b/interface-definitions/vrf.xml.in
@@ -16,10 +16,7 @@
<tagNode name="name">
<properties>
<help>Virtual Routing and Forwarding instance</help>
- <constraint>
- <validator name="vrf-name"/>
- </constraint>
- <constraintErrorMessage>VRF instance name must be 15 characters or less and can not\nbe named as regular network interfaces.\nA name must starts from a letter.\n</constraintErrorMessage>
+ #include <include/constraint/vrf.xml.i>
<valueHelp>
<format>txt</format>
<description>VRF instance name</description>
diff --git a/op-mode-definitions/firewall.xml.in b/op-mode-definitions/firewall.xml.in
index 50d52d6ca..6a254ee11 100644
--- a/op-mode-definitions/firewall.xml.in
+++ b/op-mode-definitions/firewall.xml.in
@@ -19,14 +19,36 @@
<path>firewall group ipv6-network-group</path>
</completionHelp>
</properties>
+ <children>
+ <leafNode name="detail">
+ <properties>
+ <help>Show list view of firewall groups</help>
+ <completionHelp>
+ <path>firewall group detail</path>
+ </completionHelp>
+ </properties>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show_group --name $4 --detail $5</command>
+ </leafNode>
+ </children>
<command>sudo ${vyos_op_scripts_dir}/firewall.py --action show_group --name $4</command>
</tagNode>
- <leafNode name="group">
+ <node name="group">
<properties>
<help>Show firewall group</help>
</properties>
+ <children>
+ <leafNode name="detail">
+ <properties>
+ <help>Show list view of firewall group</help>
+ <completionHelp>
+ <path>firewall group detail</path>
+ </completionHelp>
+ </properties>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show_group --detail $4</command>
+ </leafNode>
+ </children>
<command>sudo ${vyos_op_scripts_dir}/firewall.py --action show_group</command>
- </leafNode>
+ </node>
<node name="bridge">
<properties>
<help>Show bridge firewall</help>
@@ -42,6 +64,15 @@
<help>Show bridge forward filter firewall ruleset</help>
</properties>
<children>
+ <leafNode name="detail">
+ <properties>
+ <help>Show list view of bridge forward filter firewall rules</help>
+ <completionHelp>
+ <path>firewall bridge forward filter detail</path>
+ </completionHelp>
+ </properties>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5 --detail $6</command>
+ </leafNode>
<tagNode name="rule">
<properties>
<help>Show summary of bridge forward filter firewall rules</help>
@@ -49,6 +80,17 @@
<path>firewall bridge forward filter rule</path>
</completionHelp>
</properties>
+ <children>
+ <leafNode name="detail">
+ <properties>
+ <help>Show list view of specific bridge forward filter firewall rule</help>
+ <completionHelp>
+ <path>firewall bridge forward filter detail</path>
+ </completionHelp>
+ </properties>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5 --rule $7 --detail $8</command>
+ </leafNode>
+ </children>
<command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5 --rule $7</command>
</tagNode>
</children>
@@ -64,6 +106,15 @@
</completionHelp>
</properties>
<children>
+ <leafNode name="detail">
+ <properties>
+ <help>Show list view of bridge custom firewall chains</help>
+ <completionHelp>
+ <path>firewall bridge name detail</path>
+ </completionHelp>
+ </properties>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5 --detail $6</command>
+ </leafNode>
<tagNode name="rule">
<properties>
<help>Show summary of bridge custom firewall ruleset</help>
@@ -71,6 +122,17 @@
<path>firewall bridge name ${COMP_WORDS[5]} rule</path>
</completionHelp>
</properties>
+ <children>
+ <leafNode name="detail">
+ <properties>
+ <help>Show list view of bridge custom firewall rules</help>
+ <completionHelp>
+ <path>firewall bridge name ${COMP_WORDS[5]} rule detail</path>
+ </completionHelp>
+ </properties>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5 --rule $7 --detail $8</command>
+ </leafNode>
+ </children>
<command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5 --rule $7</command>
</tagNode>
</children>
@@ -94,6 +156,15 @@
<help>Show IPv6 forward filter firewall ruleset</help>
</properties>
<children>
+ <leafNode name="detail">
+ <properties>
+ <help>Show list view of IPv6 forward filter firewall ruleset</help>
+ <completionHelp>
+ <path>firewall ipv6 forward filter detail</path>
+ </completionHelp>
+ </properties>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5 --detail $6</command>
+ </leafNode>
<tagNode name="rule">
<properties>
<help>Show summary of IPv6 forward filter firewall rules</help>
@@ -101,6 +172,17 @@
<path>firewall ipv6 forward filter rule</path>
</completionHelp>
</properties>
+ <children>
+ <leafNode name="detail">
+ <properties>
+ <help>Show list view of IPv6 forward filter firewall rules</help>
+ <completionHelp>
+ <path>firewall ipv6 forward filter rule detail</path>
+ </completionHelp>
+ </properties>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5 --rule $7 --detail $8</command>
+ </leafNode>
+ </children>
<command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5 --rule $7</command>
</tagNode>
</children>
@@ -118,6 +200,15 @@
<help>Show IPv6 forward input firewall ruleset</help>
</properties>
<children>
+ <leafNode name="detail">
+ <properties>
+ <help>Show list view of IPv6 input firewall ruleset</help>
+ <completionHelp>
+ <path>firewall ipv6 input filter detail</path>
+ </completionHelp>
+ </properties>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5 --detail $6</command>
+ </leafNode>
<tagNode name="rule">
<properties>
<help>Show summary of IPv6 input filter firewall rules</help>
@@ -125,6 +216,17 @@
<path>firewall ipv6 input filter rule</path>
</completionHelp>
</properties>
+ <children>
+ <leafNode name="detail">
+ <properties>
+ <help>Show list view of IPv6 input filter firewall rules</help>
+ <completionHelp>
+ <path>firewall ipv6 input filter rule detail</path>
+ </completionHelp>
+ </properties>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5 --rule $7 --detail $8</command>
+ </leafNode>
+ </children>
<command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5 --rule $7</command>
</tagNode>
</children>
@@ -142,6 +244,15 @@
<help>Show IPv6 output filter firewall ruleset</help>
</properties>
<children>
+ <leafNode name="detail">
+ <properties>
+ <help>Show list view of IPv6 output input firewall ruleset</help>
+ <completionHelp>
+ <path>firewall ipv6 output filter detail</path>
+ </completionHelp>
+ </properties>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5 --detail $6</command>
+ </leafNode>
<tagNode name="rule">
<properties>
<help>Show summary of IPv6 output filter firewall rules</help>
@@ -149,6 +260,17 @@
<path>firewall ipv6 output filter rule</path>
</completionHelp>
</properties>
+ <children>
+ <leafNode name="detail">
+ <properties>
+ <help>Show list view of IPv6 output filter firewall rules</help>
+ <completionHelp>
+ <path>firewall ipv6 output filter rule detail</path>
+ </completionHelp>
+ </properties>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5 --rule $7 --detail $8</command>
+ </leafNode>
+ </children>
<command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5 --rule $7</command>
</tagNode>
</children>
@@ -164,6 +286,15 @@
</completionHelp>
</properties>
<children>
+ <leafNode name="detail">
+ <properties>
+ <help>Show list view of IPv6 custom firewall chains</help>
+ <completionHelp>
+ <path>firewall ipv6 name detail</path>
+ </completionHelp>
+ </properties>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5 --detail $6</command>
+ </leafNode>
<tagNode name="rule">
<properties>
<help>Show summary of IPv6 custom firewall ruleset</help>
@@ -171,6 +302,17 @@
<path>firewall ipv6 name ${COMP_WORDS[5]} rule</path>
</completionHelp>
</properties>
+ <children>
+ <leafNode name="detail">
+ <properties>
+ <help>Show list view of IPv6 custom firewall rules</help>
+ <completionHelp>
+ <path>firewall ipv6 name ${COMP_WORDS[5]} rule detail</path>
+ </completionHelp>
+ </properties>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5 --rule $7 --detail $8</command>
+ </leafNode>
+ </children>
<command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5 --rule $7</command>
</tagNode>
</children>
@@ -194,6 +336,15 @@
<help>Show IPv4 forward filter firewall ruleset</help>
</properties>
<children>
+ <leafNode name="detail">
+ <properties>
+ <help>Show list view of IPv4 forward filter firewall ruleset</help>
+ <completionHelp>
+ <path>firewall ipv4 forward filter detail</path>
+ </completionHelp>
+ </properties>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5 --detail $6</command>
+ </leafNode>
<tagNode name="rule">
<properties>
<help>Show summary of IPv4 forward filter firewall rules</help>
@@ -201,6 +352,17 @@
<path>firewall ipv4 forward filter rule</path>
</completionHelp>
</properties>
+ <children>
+ <leafNode name="detail">
+ <properties>
+ <help>Show list view of IPv4 forward filter firewall rules</help>
+ <completionHelp>
+ <path>firewall ipv4 forward filter rule detail</path>
+ </completionHelp>
+ </properties>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5 --rule $7 --detail $8</command>
+ </leafNode>
+ </children>
<command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5 --rule $7</command>
</tagNode>
</children>
@@ -218,6 +380,15 @@
<help>Show IPv4 forward input firewall ruleset</help>
</properties>
<children>
+ <leafNode name="detail">
+ <properties>
+ <help>Show list view of IPv4 input filter firewall ruleset</help>
+ <completionHelp>
+ <path>firewall ipv4 input filter detail</path>
+ </completionHelp>
+ </properties>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5 --detail $6</command>
+ </leafNode>
<tagNode name="rule">
<properties>
<help>Show summary of IPv4 input filter firewall rules</help>
@@ -225,6 +396,17 @@
<path>firewall ipv4 input filter rule</path>
</completionHelp>
</properties>
+ <children>
+ <leafNode name="detail">
+ <properties>
+ <help>Show list view of IPv4 input filter firewall rules</help>
+ <completionHelp>
+ <path>firewall ipv4 input filter rule detail</path>
+ </completionHelp>
+ </properties>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5 --rule $7 --detail $8</command>
+ </leafNode>
+ </children>
<command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5 --rule $7</command>
</tagNode>
</children>
@@ -242,6 +424,15 @@
<help>Show IPv4 output filter firewall ruleset</help>
</properties>
<children>
+ <leafNode name="detail">
+ <properties>
+ <help>Show list view of IPv4 output filter firewall ruleset</help>
+ <completionHelp>
+ <path>firewall ipv4 input output detail</path>
+ </completionHelp>
+ </properties>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5 --detail $6</command>
+ </leafNode>
<tagNode name="rule">
<properties>
<help>Show summary of IPv4 output filter firewall rules</help>
@@ -249,6 +440,17 @@
<path>firewall ipv4 output filter rule</path>
</completionHelp>
</properties>
+ <children>
+ <leafNode name="detail">
+ <properties>
+ <help>Show list view of IPv4 output filter firewall rules</help>
+ <completionHelp>
+ <path>firewall ipv4 input output rule detail</path>
+ </completionHelp>
+ </properties>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5 --rule $7 --detail $8</command>
+ </leafNode>
+ </children>
<command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5 --rule $7</command>
</tagNode>
</children>
@@ -264,6 +466,15 @@
</completionHelp>
</properties>
<children>
+ <leafNode name="detail">
+ <properties>
+ <help>Show list view of IPv4 custom firewall chains</help>
+ <completionHelp>
+ <path>firewall ipv4 name detail</path>
+ </completionHelp>
+ </properties>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5 --detail $6</command>
+ </leafNode>
<tagNode name="rule">
<properties>
<help>Show summary of IPv4 custom firewall ruleset</help>
@@ -271,6 +482,17 @@
<path>firewall ipv4 name ${COMP_WORDS[5]} rule</path>
</completionHelp>
</properties>
+ <children>
+ <leafNode name="detail">
+ <properties>
+ <help>Show list view of IPv4 custom firewall ruleset</help>
+ <completionHelp>
+ <path>firewall ipv4 name ${COMP_WORDS[5]} rule detail</path>
+ </completionHelp>
+ </properties>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5 --rule $7 --detail $8</command>
+ </leafNode>
+ </children>
<command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5 --rule $7</command>
</tagNode>
</children>
@@ -279,12 +501,23 @@
</children>
<command>sudo ${vyos_op_scripts_dir}/firewall.py --action show_family --family $3</command>
</node>
- <leafNode name="statistics">
+ <node name="statistics">
<properties>
<help>Show statistics of firewall application</help>
</properties>
+ <children>
+ <leafNode name="detail">
+ <properties>
+ <help>Show list view of firewall statistics</help>
+ <completionHelp>
+ <path>firewall statistics detail</path>
+ </completionHelp>
+ </properties>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show_statistics --detail $4</command>
+ </leafNode>
+ </children>
<command>sudo ${vyos_op_scripts_dir}/firewall.py --action show_statistics</command>
- </leafNode>
+ </node>
<leafNode name="summary">
<properties>
<help>Show summary of firewall application</help>
diff --git a/python/vyos/accel_ppp.py b/python/vyos/accel_ppp.py
index 0b4f8a9fe..bae695fc3 100644
--- a/python/vyos/accel_ppp.py
+++ b/python/vyos/accel_ppp.py
@@ -1,6 +1,4 @@
-#!/usr/bin/env python3
-#
-# Copyright (C) 2022 VyOS maintainers and contributors
+# Copyright (C) 2022-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
@@ -13,14 +11,9 @@
#
# 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 sys
-
-import vyos.opmode
from vyos.utils.process import rc_cmd
-
def get_server_statistics(accel_statistics, pattern, sep=':') -> dict:
import re
diff --git a/python/vyos/accel_ppp_util.py b/python/vyos/accel_ppp_util.py
index 845b2f5f0..ae75e6654 100644
--- a/python/vyos/accel_ppp_util.py
+++ b/python/vyos/accel_ppp_util.py
@@ -163,13 +163,23 @@ def verify_accel_ppp_authentication(config, local_users=True):
if "key" not in radius_config:
raise ConfigError(f'Missing RADIUS secret key for server "{server}"')
+ if dict_search("server_type", config) == 'ipoe' and dict_search(
+ "authentication.mode", config) == "local":
+ if not dict_search("authentication.interface", config):
+ raise ConfigError(
+ "Authentication mode local requires authentication interface to be configured!"
+ )
+ for interface in dict_search("authentication.interface", config):
+ user_config = config["authentication"]["interface"][interface]
+ if "mac" not in user_config:
+ raise ConfigError(
+ f'Users MAC addreses are not configured for interface "{interface}"')
+
if dict_search('authentication.radius.dynamic_author.server', config):
if not dict_search('authentication.radius.dynamic_author.key', config):
raise ConfigError('DAE/CoA server key required!')
-
-
def verify_accel_ppp_ip_pool(vpn_config):
"""
Common helper function which must be used by Accel-PPP
@@ -192,7 +202,9 @@ def verify_accel_ppp_ip_pool(vpn_config):
default_pool = dict_search("default_pool", vpn_config)
if default_pool:
- if default_pool not in dict_search("client_ip_pool", vpn_config):
+ if not dict_search('client_ip_pool',
+ vpn_config) or default_pool not in dict_search(
+ 'client_ip_pool', vpn_config):
raise ConfigError(f'Default pool "{default_pool}" does not exists')
if 'client_ipv6_pool' in vpn_config:
@@ -204,8 +216,20 @@ def verify_accel_ppp_ip_pool(vpn_config):
if dict_search('authentication.mode', vpn_config) in ['local', 'noauth']:
if not dict_search('client_ip_pool', vpn_config) and not dict_search(
'client_ipv6_pool', vpn_config):
- raise ConfigError(
- "Local auth mode requires local client-ip-pool or client-ipv6-pool to be configured!")
+ if dict_search('server_type', vpn_config) == 'ipoe':
+ if 'interface' in vpn_config:
+ for interface, interface_config in vpn_config['interface'].items():
+ if dict_search('client_subnet', interface_config):
+ break
+ else:
+ raise ConfigError(
+ 'Local auth and noauth mode requires local client-ip-pool \
+ or client-ipv6-pool or client-subnet to be configured!')
+ else:
+ raise ConfigError(
+ "Local auth mode requires local client-ip-pool \
+ or client-ipv6-pool to be configured!")
+
if dict_search('client_ip_pool', vpn_config) and not dict_search(
'default_pool', vpn_config):
Warning("'default-pool' is not defined")
diff --git a/python/vyos/config.py b/python/vyos/config.py
index 7619ad367..cca65f0eb 100644
--- a/python/vyos/config.py
+++ b/python/vyos/config.py
@@ -1,4 +1,4 @@
-# Copyright 2017, 2019-2023 VyOS maintainers and contributors <maintainers@vyos.io>
+# Copyright 2017-2024 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
@@ -60,12 +60,10 @@ In configuration mode, "base" functions like `exists`, `return_value` return val
while functions prefixed "effective" return values from the running config.
In operational mode, all functions return values from the running config.
-
"""
import re
import json
-from copy import deepcopy
from typing import Union
import vyos.configtree
diff --git a/python/vyos/config_mgmt.py b/python/vyos/config_mgmt.py
index 28ccee769..fc51d781c 100644
--- a/python/vyos/config_mgmt.py
+++ b/python/vyos/config_mgmt.py
@@ -1,4 +1,4 @@
-# Copyright 2023 VyOS maintainers and contributors <maintainers@vyos.io>
+# Copyright 2023-2024 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
@@ -19,18 +19,23 @@ import sys
import gzip
import logging
-from typing import Optional, Tuple, Union
+from typing import Optional
+from typing import Tuple
from filecmp import cmp
from datetime import datetime
-from textwrap import dedent, indent
+from textwrap import dedent
from pathlib import Path
from tabulate import tabulate
from shutil import copy, chown
-from urllib.parse import urlsplit, urlunsplit
+from urllib.parse import urlsplit
+from urllib.parse import urlunsplit
from vyos.config import Config
-from vyos.configtree import ConfigTree, ConfigTreeError, show_diff
-from vyos.load_config import load, LoadConfigError
+from vyos.configtree import ConfigTree
+from vyos.configtree import ConfigTreeError
+from vyos.configtree import show_diff
+from vyos.load_config import load
+from vyos.load_config import LoadConfigError
from vyos.defaults import directories
from vyos.version import get_full_version_data
from vyos.utils.io import ask_yes_no
diff --git a/python/vyos/configdict.py b/python/vyos/configdict.py
index 4111d7271..870d7cfda 100644
--- a/python/vyos/configdict.py
+++ b/python/vyos/configdict.py
@@ -1,4 +1,4 @@
-# Copyright 2019-2022 VyOS maintainers and contributors <maintainers@vyos.io>
+# Copyright 2019-2024 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
@@ -203,8 +203,6 @@ def is_member(conf, interface, intftype=None):
empty -> Interface is not a member
key -> Interface is a member of this interface
"""
- from vyos.ifconfig import Section
-
ret_val = {}
intftypes = ['bonding', 'bridge']
@@ -633,7 +631,7 @@ def get_accel_dict(config, base, chap_secrets, with_pki=False):
Return a dictionary with the necessary interface config keys.
"""
- from vyos.utils.system import get_half_cpus
+ from vyos.cpu import get_core_count
from vyos.template import is_ipv4
dict = config.get_config_dict(base, key_mangling=('-', '_'),
@@ -643,7 +641,7 @@ def get_accel_dict(config, base, chap_secrets, with_pki=False):
with_pki=with_pki)
# set CPUs cores to process requests
- dict.update({'thread_count' : get_half_cpus()})
+ dict.update({'thread_count' : get_core_count()})
# we need to store the path to the secrets file
dict.update({'chap_secrets_file' : chap_secrets})
diff --git a/python/vyos/configdiff.py b/python/vyos/configdiff.py
index 03b06c6d9..f975df45d 100644
--- a/python/vyos/configdiff.py
+++ b/python/vyos/configdiff.py
@@ -1,4 +1,4 @@
-# Copyright 2020 VyOS maintainers and contributors <maintainers@vyos.io>
+# Copyright 2020-2024 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
@@ -13,12 +13,12 @@
# You should have received a copy of the GNU Lesser General Public License
# along with this library. If not, see <http://www.gnu.org/licenses/>.
-from enum import IntFlag, auto
+from enum import IntFlag
+from enum import auto
from vyos.config import Config
from vyos.configtree import DiffTree
from vyos.configdict import dict_merge
-from vyos.configdict import list_diff
from vyos.utils.dict import get_sub_dict
from vyos.utils.dict import mangle_dict_keys
from vyos.utils.dict import dict_search_args
diff --git a/python/vyos/configsession.py b/python/vyos/configsession.py
index 90842b749..ab7a631bb 100644
--- a/python/vyos/configsession.py
+++ b/python/vyos/configsession.py
@@ -176,6 +176,25 @@ class ConfigSession(object):
except (ValueError, ConfigSessionError) as e:
raise ConfigSessionError(e)
+ def set_section_tree(self, d: dict):
+ try:
+ if d:
+ for p in dict_to_paths(d):
+ self.set(p)
+ except (ValueError, ConfigSessionError) as e:
+ raise ConfigSessionError(e)
+
+ def load_section_tree(self, mask: dict, d: dict):
+ try:
+ if mask:
+ for p in dict_to_paths(mask):
+ self.delete(p)
+ if d:
+ for p in dict_to_paths(d):
+ self.set(p)
+ except (ValueError, ConfigSessionError) as e:
+ raise ConfigSessionError(e)
+
def comment(self, path, value=None):
if not value:
value = [""]
diff --git a/python/vyos/configtree.py b/python/vyos/configtree.py
index 423fe01ed..e4b282d72 100644
--- a/python/vyos/configtree.py
+++ b/python/vyos/configtree.py
@@ -401,6 +401,30 @@ def union(left, right, libpath=LIBPATH):
return tree
+def mask_inclusive(left, right, libpath=LIBPATH):
+ if not (isinstance(left, ConfigTree) and isinstance(right, ConfigTree)):
+ raise TypeError("Arguments must be instances of ConfigTree")
+
+ try:
+ __lib = cdll.LoadLibrary(libpath)
+ __mask_tree = __lib.mask_tree
+ __mask_tree.argtypes = [c_void_p, c_void_p]
+ __mask_tree.restype = c_void_p
+ __get_error = __lib.get_error
+ __get_error.argtypes = []
+ __get_error.restype = c_char_p
+
+ res = __mask_tree(left._get_config(), right._get_config())
+ except Exception as e:
+ raise ConfigTreeError(e)
+ if not res:
+ msg = __get_error().decode()
+ raise ConfigTreeError(msg)
+
+ tree = ConfigTree(address=res)
+
+ return tree
+
def reference_tree_to_json(from_dir, to_file, libpath=LIBPATH):
try:
__lib = cdll.LoadLibrary(libpath)
diff --git a/python/vyos/configverify.py b/python/vyos/configverify.py
index 6508ccdd9..4cb84194a 100644
--- a/python/vyos/configverify.py
+++ b/python/vyos/configverify.py
@@ -23,8 +23,6 @@
from vyos import ConfigError
from vyos.utils.dict import dict_search
-from vyos.utils.dict import dict_search_recursive
-
# pattern re-used in ipsec migration script
dynamic_interface_pattern = r'(ppp|pppoe|sstpc|l2tp|ipoe)[0-9]+'
@@ -62,8 +60,8 @@ def verify_mtu_parent(config, parent):
mtu = int(config['mtu'])
parent_mtu = int(parent['mtu'])
if mtu > parent_mtu:
- raise ConfigError(f'Interface MTU ({mtu}) too high, ' \
- f'parent interface MTU is {parent_mtu}!')
+ raise ConfigError(f'Interface MTU "{mtu}" too high, ' \
+ f'parent interface MTU is "{parent_mtu}"!')
def verify_mtu_ipv6(config):
"""
@@ -78,7 +76,7 @@ def verify_mtu_ipv6(config):
if int(config['mtu']) < min_mtu:
interface = config['ifname']
error_msg = f'IPv6 address will be configured on interface "{interface}",\n' \
- f'the required minimum MTU is {min_mtu}!'
+ f'the required minimum MTU is "{min_mtu}"!'
if 'address' in config:
for address in config['address']:
@@ -99,10 +97,17 @@ def verify_vrf(config):
Common helper function used by interface implementations to perform
recurring validation of VRF configuration.
"""
- from netifaces import interfaces
- if 'vrf' in config and config['vrf'] != 'default':
- if config['vrf'] not in interfaces():
- raise ConfigError('VRF "{vrf}" does not exist'.format(**config))
+ from vyos.utils.network import interface_exists
+ if 'vrf' in config:
+ vrfs = config['vrf']
+ if isinstance(vrfs, str):
+ vrfs = [vrfs]
+
+ for vrf in vrfs:
+ if vrf == 'default':
+ continue
+ if not interface_exists(vrf):
+ raise ConfigError(f'VRF "{vrf}" does not exist!')
if 'is_bridge_member' in config:
raise ConfigError(
@@ -162,43 +167,6 @@ def verify_tunnel(config):
if 'source_address' in config and is_ipv6(config['source_address']):
raise ConfigError('Can not use local IPv6 address is for mGRE tunnels')
-def verify_eapol(config):
- """
- Common helper function used by interface implementations to perform
- recurring validation of EAPoL configuration.
- """
- if 'eapol' in config:
- if 'certificate' not in config['eapol']:
- raise ConfigError('Certificate must be specified when using EAPoL!')
-
- if 'pki' not in config or 'certificate' not in config['pki']:
- raise ConfigError('Invalid certificate specified for EAPoL')
-
- cert_name = config['eapol']['certificate']
- if cert_name not in config['pki']['certificate']:
- raise ConfigError('Invalid certificate specified for EAPoL')
-
- cert = config['pki']['certificate'][cert_name]
-
- if 'certificate' not in cert or 'private' not in cert or 'key' not in cert['private']:
- raise ConfigError('Invalid certificate/private key specified for EAPoL')
-
- if 'password_protected' in cert['private']:
- raise ConfigError('Encrypted private key cannot be used for EAPoL')
-
- if 'ca_certificate' in config['eapol']:
- if 'ca' not in config['pki']:
- raise ConfigError('Invalid CA certificate specified for EAPoL')
-
- for ca_cert_name in config['eapol']['ca_certificate']:
- if ca_cert_name not in config['pki']['ca']:
- raise ConfigError('Invalid CA certificate specified for EAPoL')
-
- ca_cert = config['pki']['ca'][ca_cert_name]
-
- if 'certificate' not in ca_cert:
- raise ConfigError('Invalid CA certificate specified for EAPoL')
-
def verify_mirror_redirect(config):
"""
Common helper function used by interface implementations to perform
@@ -206,13 +174,13 @@ def verify_mirror_redirect(config):
It makes no sense to mirror traffic back at yourself!
"""
- import os
+ from vyos.utils.network import interface_exists
if {'mirror', 'redirect'} <= set(config):
raise ConfigError('Mirror and redirect can not be enabled at the same time!')
if 'mirror' in config:
for direction, mirror_interface in config['mirror'].items():
- if not os.path.exists(f'/sys/class/net/{mirror_interface}'):
+ if not interface_exists(mirror_interface):
raise ConfigError(f'Requested mirror interface "{mirror_interface}" '\
'does not exist!')
@@ -222,7 +190,7 @@ def verify_mirror_redirect(config):
if 'redirect' in config:
redirect_ifname = config['redirect']
- if not os.path.exists(f'/sys/class/net/{redirect_ifname}'):
+ if not interface_exists(redirect_ifname):
raise ConfigError(f'Requested redirect interface "{redirect_ifname}" '\
'does not exist!')
@@ -276,10 +244,10 @@ def verify_interface_exists(ifname, warning_only=False):
if the interface is defined on the CLI, if it's not found we try if
it exists at the OS level.
"""
- import os
from vyos.base import Warning
from vyos.configquery import ConfigTreeQuery
from vyos.utils.dict import dict_search_recursive
+ from vyos.utils.network import interface_exists
# Check if interface is present in CLI config
config = ConfigTreeQuery()
@@ -288,7 +256,7 @@ def verify_interface_exists(ifname, warning_only=False):
return True
# Interface not found on CLI, try Linux Kernel
- if os.path.exists(f'/sys/class/net/{ifname}'):
+ if interface_exists(ifname):
return True
message = f'Interface "{ifname}" does not exist!'
@@ -304,7 +272,7 @@ def verify_source_interface(config):
required by e.g. peth/MACvlan, MACsec ...
"""
import re
- from netifaces import interfaces
+ from vyos.utils.network import interface_exists
ifname = config['ifname']
if 'source_interface' not in config:
@@ -316,7 +284,7 @@ def verify_source_interface(config):
if tmp.match(src_ifname):
raise ConfigError(f'Can not source "{ifname}" from dynamic interface "{src_ifname}"!')
- if src_ifname not in interfaces():
+ if not interface_exists(src_ifname):
raise ConfigError(f'Specified source-interface {src_ifname} does not exist')
if 'source_interface_is_bridge_member' in config:
@@ -487,3 +455,69 @@ def verify_access_list(access_list, config, version=''):
# Check if the specified ACL exists, if not error out
if dict_search(f'policy.access-list{version}.{access_list}', config) == None:
raise ConfigError(f'Specified access-list{version} "{access_list}" does not exist!')
+
+def verify_pki_certificate(config: dict, cert_name: str, no_password_protected: bool=False):
+ """
+ Common helper function user by PKI consumers to perform recurring
+ validation functions for PEM based certificates
+ """
+ if 'pki' not in config:
+ raise ConfigError('PKI is not configured!')
+
+ if 'certificate' not in config['pki']:
+ raise ConfigError('PKI does not contain any certificates!')
+
+ if cert_name not in config['pki']['certificate']:
+ raise ConfigError(f'Certificate "{cert_name}" not found in configuration!')
+
+ pki_cert = config['pki']['certificate'][cert_name]
+ if 'certificate' not in pki_cert:
+ raise ConfigError(f'PEM certificate for "{cert_name}" missing in configuration!')
+
+ if 'private' not in pki_cert or 'key' not in pki_cert['private']:
+ raise ConfigError(f'PEM private key for "{cert_name}" missing in configuration!')
+
+ if no_password_protected and 'password_protected' in pki_cert['private']:
+ raise ConfigError('Password protected PEM private key is not supported!')
+
+def verify_pki_ca_certificate(config: dict, ca_name: str):
+ """
+ Common helper function user by PKI consumers to perform recurring
+ validation functions for PEM based CA certificates
+ """
+ if 'pki' not in config:
+ raise ConfigError('PKI is not configured!')
+
+ if 'ca' not in config['pki']:
+ raise ConfigError('PKI does not contain any CA certificates!')
+
+ if ca_name not in config['pki']['ca']:
+ raise ConfigError(f'CA Certificate "{ca_name}" not found in configuration!')
+
+ pki_cert = config['pki']['ca'][ca_name]
+ if 'certificate' not in pki_cert:
+ raise ConfigError(f'PEM CA certificate for "{cert_name}" missing in configuration!')
+
+def verify_pki_dh_parameters(config: dict, dh_name: str, min_key_size: int=0):
+ """
+ Common helper function user by PKI consumers to perform recurring
+ validation functions on DH parameters
+ """
+ from vyos.pki import load_dh_parameters
+
+ if 'pki' not in config:
+ raise ConfigError('PKI is not configured!')
+
+ if 'dh' not in config['pki']:
+ raise ConfigError('PKI does not contain any DH parameters!')
+
+ if dh_name not in config['pki']['dh']:
+ raise ConfigError(f'DH parameter "{dh_name}" not found in configuration!')
+
+ if min_key_size:
+ pki_dh = config['pki']['dh'][dh_name]
+ dh_params = load_dh_parameters(pki_dh['parameters'])
+ dh_numbers = dh_params.parameter_numbers()
+ dh_bits = dh_numbers.p.bit_length()
+ if dh_bits < min_key_size:
+ raise ConfigError(f'Minimum DH key-size is {min_key_size} bits!')
diff --git a/python/vyos/cpu.py b/python/vyos/cpu.py
index d2e5f6504..cae5f5f4d 100644
--- a/python/vyos/cpu.py
+++ b/python/vyos/cpu.py
@@ -1,5 +1,4 @@
-#!/usr/bin/env python3
-# Copyright 2022 VyOS maintainers and contributors <maintainers@vyos.io>
+# Copyright (C) 2022-2024 VyOS maintainers and contributors
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
diff --git a/python/vyos/ethtool.py b/python/vyos/ethtool.py
index 5e241fc08..d45c9c272 100644
--- a/python/vyos/ethtool.py
+++ b/python/vyos/ethtool.py
@@ -13,7 +13,6 @@
# You should have received a copy of the GNU Lesser General Public
# License along with this library. If not, see <http://www.gnu.org/licenses/>.
-import os
import re
from json import loads
diff --git a/python/vyos/firewall.py b/python/vyos/firewall.py
index e70b4f0d9..946050a82 100644
--- a/python/vyos/firewall.py
+++ b/python/vyos/firewall.py
@@ -1,6 +1,4 @@
-#!/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
@@ -30,7 +28,6 @@ from vyos.template import is_ipv4
from vyos.template import render
from vyos.utils.dict import dict_search_args
from vyos.utils.dict import dict_search_recursive
-from vyos.utils.process import call
from vyos.utils.process import cmd
from vyos.utils.process import run
@@ -66,7 +63,7 @@ def fqdn_config_parse(firewall):
rule = path[4]
suffix = path[5][0]
set_name = f'{hook_name}_{priority}_{rule}_{suffix}'
-
+
if (path[0] == 'ipv4') and (path[1] == 'forward' or path[1] == 'input' or path[1] == 'output' or path[1] == 'name'):
firewall['ip_fqdn'][set_name] = domain
elif (path[0] == 'ipv6') and (path[1] == 'forward' or path[1] == 'input' or path[1] == 'output' or path[1] == 'name'):
@@ -85,7 +82,7 @@ def fqdn_resolve(fqdn, ipv6=False):
def find_nftables_rule(table, chain, rule_matches=[]):
# Find rule in table/chain that matches all criteria and return the handle
- results = cmd(f'sudo nft -a list chain {table} {chain}').split("\n")
+ results = cmd(f'sudo nft --handle list chain {table} {chain}').split("\n")
for line in results:
if all(rule_match in line for rule_match in rule_matches):
handle_search = re.search('handle (\d+)', line)
@@ -655,7 +652,7 @@ def geoip_update(firewall, force=False):
'ipv6_sets': ipv6_sets
})
- result = run(f'nft -f {nftables_geoip_conf}')
+ result = run(f'nft --file {nftables_geoip_conf}')
if result != 0:
print('Error: GeoIP failed to update firewall')
return False
diff --git a/python/vyos/frr.py b/python/vyos/frr.py
index c3703cbb4..e7743e9d5 100644
--- a/python/vyos/frr.py
+++ b/python/vyos/frr.py
@@ -1,4 +1,4 @@
-# Copyright 2020 VyOS maintainers and contributors <maintainers@vyos.io>
+# Copyright 2020-2024 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
@@ -69,7 +69,6 @@ import tempfile
import re
from vyos import ConfigError
-from vyos.utils.permission import chown
from vyos.utils.process import cmd
from vyos.utils.process import popen
from vyos.utils.process import STDOUT
diff --git a/python/vyos/ifconfig/bond.py b/python/vyos/ifconfig/bond.py
index 45e6e4c16..c6d0f1cff 100644
--- a/python/vyos/ifconfig/bond.py
+++ b/python/vyos/ifconfig/bond.py
@@ -1,4 +1,4 @@
-# Copyright 2019-2022 VyOS maintainers and contributors <maintainers@vyos.io>
+# Copyright 2019-2024 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
@@ -16,7 +16,6 @@
import os
from vyos.ifconfig.interface import Interface
-from vyos.utils.process import cmd
from vyos.utils.dict import dict_search
from vyos.utils.assertion import assert_list
from vyos.utils.assertion import assert_positive
diff --git a/python/vyos/ifconfig/bridge.py b/python/vyos/ifconfig/bridge.py
index 7936e3da5..917f962b7 100644
--- a/python/vyos/ifconfig/bridge.py
+++ b/python/vyos/ifconfig/bridge.py
@@ -13,13 +13,12 @@
# You should have received a copy of the GNU Lesser General Public
# License along with this library. If not, see <http://www.gnu.org/licenses/>.
-from netifaces import interfaces
-
from vyos.ifconfig.interface import Interface
from vyos.utils.assertion import assert_boolean
from vyos.utils.assertion import assert_list
from vyos.utils.assertion import assert_positive
from vyos.utils.dict import dict_search
+from vyos.utils.network import interface_exists
from vyos.configdict import get_vlan_ids
from vyos.configdict import list_diff
@@ -314,7 +313,7 @@ class BridgeIf(Interface):
# remove interface from bridge
tmp = dict_search('member.interface_remove', config)
for member in (tmp or []):
- if member in interfaces():
+ if interface_exists(member):
self.del_port(member)
# enable/disable VLAN Filter
@@ -345,7 +344,7 @@ class BridgeIf(Interface):
for interface, interface_config in tmp.items():
# if interface does yet not exist bail out early and
# add it later
- if interface not in interfaces():
+ if not interface_exists(interface):
continue
# Bridge lower "physical" interface
diff --git a/python/vyos/ifconfig/interface.py b/python/vyos/ifconfig/interface.py
index 56dcde214..1b86982c4 100644
--- a/python/vyos/ifconfig/interface.py
+++ b/python/vyos/ifconfig/interface.py
@@ -35,7 +35,6 @@ from vyos.defaults import directories
from vyos.template import render
from vyos.utils.network import mac2eui64
from vyos.utils.dict import dict_search
-from vyos.utils.file import read_file
from vyos.utils.network import get_interface_config
from vyos.utils.network import get_interface_namespace
from vyos.utils.network import is_netns_interface
@@ -415,7 +414,7 @@ class Interface(Control):
else:
nft_del_element = f'delete element inet vrf_zones ct_iface_map {{ "{self.ifname}" }}'
# Check if deleting is possible first to avoid raising errors
- _, err = self._popen(f'nft -c {nft_del_element}')
+ _, err = self._popen(f'nft --check {nft_del_element}')
if not err:
# Remove map element
self._cmd(f'nft {nft_del_element}')
@@ -1375,15 +1374,19 @@ class Interface(Control):
ifname = self.ifname
config_base = directories['dhcp6_client_dir']
config_file = f'{config_base}/dhcp6c.{ifname}.conf'
+ script_file = f'/etc/wide-dhcpv6/dhcp6c.{ifname}.script' # can not live under /run b/c of noexec mount option
systemd_override_file = f'/run/systemd/system/dhcp6c@{ifname}.service.d/10-override.conf'
systemd_service = f'dhcp6c@{ifname}.service'
- # Rendered client configuration files require the apsolute config path
- self.config['dhcp6_client_dir'] = directories['dhcp6_client_dir']
+ # Rendered client configuration files require additional settings
+ config = deepcopy(self.config)
+ config['dhcp6_client_dir'] = directories['dhcp6_client_dir']
+ config['dhcp6_script_file'] = script_file
- if enable and 'disable' not in self.config:
- render(systemd_override_file, 'dhcp-client/ipv6.override.conf.j2', self.config)
- render(config_file, 'dhcp-client/ipv6.j2', self.config)
+ if enable and 'disable' not in config:
+ render(systemd_override_file, 'dhcp-client/ipv6.override.conf.j2', config)
+ render(config_file, 'dhcp-client/ipv6.j2', config)
+ render(script_file, 'dhcp-client/dhcp6c-script.j2', config, permission=0o755)
# Reload systemd unit definitons as some options are dynamically generated
self._cmd('systemctl daemon-reload')
@@ -1396,6 +1399,8 @@ class Interface(Control):
self._cmd(f'systemctl stop {systemd_service}')
if os.path.isfile(config_file):
os.remove(config_file)
+ if os.path.isfile(script_file):
+ os.remove(script_file)
return None
diff --git a/python/vyos/ifconfig/section.py b/python/vyos/ifconfig/section.py
index 5e98cd510..50273cf67 100644
--- a/python/vyos/ifconfig/section.py
+++ b/python/vyos/ifconfig/section.py
@@ -97,7 +97,7 @@ class Section:
for ifname in interfaces:
ifsection = cls.section(ifname)
- if not ifsection:
+ if not ifsection and not ifname.startswith('vrrp'):
continue
if section and ifsection != section:
diff --git a/python/vyos/ifconfig/vrrp.py b/python/vyos/ifconfig/vrrp.py
index fde903a53..ee9336d1a 100644
--- a/python/vyos/ifconfig/vrrp.py
+++ b/python/vyos/ifconfig/vrrp.py
@@ -1,4 +1,4 @@
-# Copyright 2019-2023 VyOS maintainers and contributors <maintainers@vyos.io>
+# Copyright 2019-2024 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
@@ -18,7 +18,6 @@ import json
import signal
from time import time
-from time import sleep
from tabulate import tabulate
from vyos.configquery import ConfigTreeQuery
@@ -155,4 +154,3 @@ class VRRP(object):
# add to the active list disabled instances
groups.extend(cls.disabled())
return(tabulate(groups, headers))
-
diff --git a/python/vyos/ifconfig/vxlan.py b/python/vyos/ifconfig/vxlan.py
index a2c4aad50..bdb48e303 100644
--- a/python/vyos/ifconfig/vxlan.py
+++ b/python/vyos/ifconfig/vxlan.py
@@ -1,4 +1,4 @@
-# Copyright 2019-2023 VyOS maintainers and contributors <maintainers@vyos.io>
+# Copyright 2019-2024 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
@@ -13,9 +13,6 @@
# You should have received a copy of the GNU Lesser General Public
# License along with this library. If not, see <http://www.gnu.org/licenses/>.
-from json import loads
-
-from vyos import ConfigError
from vyos.configdict import list_diff
from vyos.ifconfig import Interface
from vyos.utils.assertion import assert_list
diff --git a/python/vyos/ifconfig/wireguard.py b/python/vyos/ifconfig/wireguard.py
index 5704f8b64..5b5f25323 100644
--- a/python/vyos/ifconfig/wireguard.py
+++ b/python/vyos/ifconfig/wireguard.py
@@ -1,4 +1,4 @@
-# Copyright 2019-2023 VyOS maintainers and contributors <maintainers@vyos.io>
+# Copyright 2019-2024 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
@@ -25,7 +25,6 @@ from hurry.filesize import alternative
from vyos.ifconfig import Interface
from vyos.ifconfig import Operational
from vyos.template import is_ipv6
-from vyos.base import Warning
class WireGuardOperational(Operational):
def _dump(self):
diff --git a/python/vyos/iflag.py b/python/vyos/iflag.py
index 7ff8e5623..3ce73c1bf 100644
--- a/python/vyos/iflag.py
+++ b/python/vyos/iflag.py
@@ -1,4 +1,4 @@
-# Copyright 2019 VyOS maintainers and contributors <maintainers@vyos.io>
+# Copyright 2019-2024 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
@@ -13,26 +13,24 @@
# You should have received a copy of the GNU Lesser General Public
# License along with this library. If not, see <http://www.gnu.org/licenses/>.
-from enum import Enum, unique, IntEnum
-
+from enum import IntEnum
class IFlag(IntEnum):
""" net/if.h interface flags """
- IFF_UP = 0x1 #: Interface up/down status
- IFF_BROADCAST = 0x2 #: Broadcast address valid
- IFF_DEBUG = 0x4, #: Debugging
- IFF_LOOPBACK = 0x8 #: Is loopback network
- IFF_POINTOPOINT = 0x10 #: Is point-to-point link
- IFF_NOTRAILERS = 0x20 #: Avoid use of trailers
- IFF_RUNNING = 0x40 #: Resources allocated
- IFF_NOARP = 0x80 #: No address resolution protocol
- IFF_PROMISC = 0x100 #: Promiscuous mode
- IFF_ALLMULTI = 0x200 #: Receive all multicast
- IFF_MASTER = 0x400 #: Load balancer master
- IFF_SLAVE = 0x800 #: Load balancer slave
- IFF_MULTICAST = 0x1000 #: Supports multicast
- IFF_PORTSEL = 0x2000 #: Media type adjustable
- IFF_AUTOMEDIA = 0x4000 #: Automatic media type enabled
- IFF_DYNAMIC = 0x8000 #: Is a dial-up device with dynamic address
-
+ IFF_UP = 0x1 #: Interface up/down status
+ IFF_BROADCAST = 0x2 #: Broadcast address valid
+ IFF_DEBUG = 0x4, #: Debugging
+ IFF_LOOPBACK = 0x8 #: Is loopback network
+ IFF_POINTOPOINT = 0x10 #: Is point-to-point link
+ IFF_NOTRAILERS = 0x20 #: Avoid use of trailers
+ IFF_RUNNING = 0x40 #: Resources allocated
+ IFF_NOARP = 0x80 #: No address resolution protocol
+ IFF_PROMISC = 0x100 #: Promiscuous mode
+ IFF_ALLMULTI = 0x200 #: Receive all multicast
+ IFF_MASTER = 0x400 #: Load balancer master
+ IFF_SLAVE = 0x800 #: Load balancer slave
+ IFF_MULTICAST = 0x1000 #: Supports multicast
+ IFF_PORTSEL = 0x2000 #: Media type adjustable
+ IFF_AUTOMEDIA = 0x4000 #: Automatic media type enabled
+ IFF_DYNAMIC = 0x8000 #: Is a dial-up device with dynamic address
diff --git a/python/vyos/initialsetup.py b/python/vyos/initialsetup.py
index 3b280dc6b..cb6b9e459 100644
--- a/python/vyos/initialsetup.py
+++ b/python/vyos/initialsetup.py
@@ -1,7 +1,7 @@
# initialsetup -- functions for setting common values in config file,
# for use in installation and first boot scripts
#
-# Copyright (C) 2018-2023 VyOS maintainers and contributors
+# Copyright (C) 2018-2024 VyOS maintainers and contributors
#
# This library is free software; you can redistribute it and/or modify it under the terms of
# the GNU Lesser General Public License as published by the Free Software Foundation;
@@ -14,8 +14,6 @@
# You should have received a copy of the GNU Lesser General Public License along with this library;
# if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-import vyos.configtree
-
from vyos.utils.auth import make_password_hash
from vyos.utils.auth import split_ssh_public_key
diff --git a/python/vyos/ioctl.py b/python/vyos/ioctl.py
index cfa75aac6..51574c1db 100644
--- a/python/vyos/ioctl.py
+++ b/python/vyos/ioctl.py
@@ -1,4 +1,4 @@
-# Copyright 2019 VyOS maintainers and contributors <maintainers@vyos.io>
+# Copyright 2019-2024 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
@@ -13,7 +13,6 @@
# You should have received a copy of the GNU Lesser General Public
# License along with this library. If not, see <http://www.gnu.org/licenses/>.
-import sys
import os
import socket
import fcntl
@@ -29,7 +28,7 @@ def get_terminal_size():
def get_interface_flags(intf):
""" Pull the SIOCGIFFLAGS """
- nullif = '\0'*256
+ nullif = '\0'*256
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
raw = fcntl.ioctl(sock.fileno(), SIOCGIFFLAGS, intf + nullif)
flags, = struct.unpack('H', raw[16:18])
diff --git a/python/vyos/kea.py b/python/vyos/kea.py
index 89ae7ca81..addfdba49 100644
--- a/python/vyos/kea.py
+++ b/python/vyos/kea.py
@@ -1,4 +1,4 @@
-# Copyright 2023 VyOS maintainers and contributors <maintainers@vyos.io>
+# Copyright 2023-2024 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
@@ -22,7 +22,6 @@ from vyos.template import isc_static_route
from vyos.template import netmask_from_cidr
from vyos.utils.dict import dict_search_args
from vyos.utils.file import file_permissions
-from vyos.utils.file import read_file
from vyos.utils.process import run
kea4_options = {
diff --git a/python/vyos/nat.py b/python/vyos/nat.py
index da2613b16..2ada29add 100644
--- a/python/vyos/nat.py
+++ b/python/vyos/nat.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env python3
-#
# Copyright (C) 2022 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
diff --git a/python/vyos/pki.py b/python/vyos/pki.py
index 02dece471..3c577db4d 100644
--- a/python/vyos/pki.py
+++ b/python/vyos/pki.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env python3
-#
# Copyright (C) 2023-2024 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
diff --git a/python/vyos/progressbar.py b/python/vyos/progressbar.py
index 7bc9d9856..8d1042672 100644
--- a/python/vyos/progressbar.py
+++ b/python/vyos/progressbar.py
@@ -1,4 +1,4 @@
-# Copyright 2023 VyOS maintainers and contributors <maintainers@vyos.io>
+# Copyright 2023-2024 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
@@ -17,12 +17,10 @@ import math
import os
import signal
import subprocess
-import sys
from vyos.utils.io import is_dumb_terminal
from vyos.utils.io import print_error
-
class Progressbar:
def __init__(self, step=None):
self.total = 0.0
diff --git a/python/vyos/qos/base.py b/python/vyos/qos/base.py
index c8e881ee2..f9366c6b1 100644
--- a/python/vyos/qos/base.py
+++ b/python/vyos/qos/base.py
@@ -90,6 +90,23 @@ class QoSBase:
else:
return value
+ def _calc_random_detect_queue_params(self, avg_pkt, max_thr, limit=None, min_thr=None, mark_probability=None):
+ params = dict()
+ avg_pkt = int(avg_pkt)
+ max_thr = int(max_thr)
+ mark_probability = int(mark_probability)
+ limit = int(limit) if limit else 4 * max_thr
+ min_thr = int(min_thr) if min_thr else (9 * max_thr) // 18
+
+ params['avg_pkt'] = avg_pkt
+ params['limit'] = limit * avg_pkt
+ params['min_val'] = min_thr * avg_pkt
+ params['max_val'] = max_thr * avg_pkt
+ params['burst'] = (2 * min_thr + max_thr) // 3
+ params['probability'] = 1 / mark_probability
+
+ return params
+
def _build_base_qdisc(self, config : dict, cls_id : int):
"""
Add/replace qdisc for every class (also default is a class). This is
@@ -144,6 +161,18 @@ class QoSBase:
elif queue_type == 'random-detect':
default_tc += f' red'
+ qparams = self._calc_random_detect_queue_params(
+ avg_pkt=dict_search('average_packet', config),
+ max_thr=dict_search('maximum_threshold', config),
+ limit=dict_search('queue_limit', config),
+ min_thr=dict_search('minimum_threshold', config),
+ mark_probability=dict_search('mark_probability', config)
+ )
+
+ default_tc += f' limit {qparams["limit"]} avpkt {qparams["avg_pkt"]}'
+ default_tc += f' max {qparams["max_val"]} min {qparams["min_val"]}'
+ default_tc += f' burst {qparams["burst"]} probability {qparams["probability"]}'
+
self._cmd(default_tc)
elif queue_type == 'drop-tail':
diff --git a/python/vyos/qos/priority.py b/python/vyos/qos/priority.py
index 8182400f9..7f0a67032 100644
--- a/python/vyos/qos/priority.py
+++ b/python/vyos/qos/priority.py
@@ -1,4 +1,4 @@
-# Copyright 2022 VyOS maintainers and contributors <maintainers@vyos.io>
+# Copyright 2022-2024 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
@@ -14,7 +14,6 @@
# License along with this library. If not, see <http://www.gnu.org/licenses/>.
from vyos.qos.base import QoSBase
-from vyos.utils.dict import dict_search
class Priority(QoSBase):
_parent = 1
diff --git a/python/vyos/range_regex.py b/python/vyos/range_regex.py
index a8190d140..81e9d2e7e 100644
--- a/python/vyos/range_regex.py
+++ b/python/vyos/range_regex.py
@@ -22,7 +22,6 @@ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
'''
-import math
# coding=utf8
@@ -67,7 +66,7 @@ def regex_for_range(min_, max_):
min_ = 0
if max_ >= 0:
- positive_subpatterns = split_to_patterns(min_, max_)
+ positive_subpatterns = split_to_patterns(min_, max_)
negative_only_subpatterns = ['-' + val for val in negative_subpatterns if val not in positive_subpatterns]
positive_only_subpatterns = [val for val in positive_subpatterns if val not in negative_subpatterns]
@@ -139,4 +138,4 @@ def range_to_pattern(start, stop):
if any_digit_count > 1:
pattern += '{{{}}}'.format(any_digit_count)
- return pattern \ No newline at end of file
+ return pattern
diff --git a/python/vyos/system/grub.py b/python/vyos/system/grub.py
index e56f0bec8..faf68c2d1 100644
--- a/python/vyos/system/grub.py
+++ b/python/vyos/system/grub.py
@@ -18,7 +18,6 @@ import platform
from pathlib import Path
from re import MULTILINE, compile as re_compile
from shutil import copy2
-from typing import Union
from uuid import uuid5, NAMESPACE_URL, UUID
from vyos.template import render
@@ -56,7 +55,7 @@ REGEX_KERNEL_CMDLINE: str = r'^BOOT_IMAGE=/(?P<boot_type>boot|live)/((?P<image_v
REGEX_GRUB_BOOT_OPTS: str = r'^\s*set boot_opts="(?P<boot_opts>[^$]+)"$'
-def install(drive_path: str, boot_dir: str, efi_dir: str, id: str = 'VyOS') -> None:
+def install(drive_path: str, boot_dir: str, efi_dir: str, id: str = 'VyOS', chroot : str = "") -> None:
"""Install GRUB for both BIOS and EFI modes (hybrid boot)
Args:
@@ -65,17 +64,22 @@ def install(drive_path: str, boot_dir: str, efi_dir: str, id: str = 'VyOS') -> N
efi_dir (str): a path to '/boot/efi' directory
"""
+ if chroot:
+ chroot_cmd = f"chroot {chroot}"
+ else:
+ chroot_cmd = ""
+
efi_installation_arch = "x86_64"
if platform.machine() == "aarch64":
efi_installation_arch = "arm64"
elif platform.machine() == "x86_64":
cmd(
- f'grub-install --no-floppy --target=i386-pc \
+ f'{chroot_cmd} grub-install --no-floppy --target=i386-pc \
--boot-directory={boot_dir} {drive_path} --force'
)
cmd(
- f'grub-install --no-floppy --recheck --target={efi_installation_arch}-efi \
+ f'{chroot_cmd} grub-install --no-floppy --recheck --target={efi_installation_arch}-efi \
--force-extra-removable --boot-directory={boot_dir} \
--efi-directory={efi_dir} --bootloader-id="{id}" \
--no-uefi-secure-boot'
diff --git a/python/vyos/template.py b/python/vyos/template.py
index bde8e3554..ac77e8a3d 100644
--- a/python/vyos/template.py
+++ b/python/vyos/template.py
@@ -1,4 +1,4 @@
-# Copyright 2019-2023 VyOS maintainers and contributors <maintainers@vyos.io>
+# Copyright 2019-2024 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
@@ -32,8 +32,21 @@ _TESTS = {}
# reuse Environments with identical settings to improve performance
@functools.lru_cache(maxsize=2)
def _get_environment(location=None):
+ from os import getenv
+
if location is None:
- loc_loader=FileSystemLoader(directories["templates"])
+ # Sometimes functions that rely on templates need to be executed outside of VyOS installations:
+ # for example, installer functions are executed for image builds,
+ # and anything may be invoked for testing from a developer's machine.
+ # This environment variable allows running any unmodified code
+ # with a custom template location.
+ location_env_var = getenv("VYOS_TEMPLATE_DIR")
+ if location_env_var:
+ print(f"Using environment variable {location_env_var}")
+ template_dir = location_env_var
+ else:
+ template_dir = directories["templates"]
+ loc_loader=FileSystemLoader(template_dir)
else:
loc_loader=FileSystemLoader(location)
env = Environment(
@@ -294,7 +307,8 @@ def network_from_ipv4(address):
@register_filter('is_interface')
def is_interface(interface):
""" Check if parameter is a valid local interface name """
- return os.path.exists(f'/sys/class/net/{interface}')
+ from vyos.utils.network import interface_exists
+ return interface_exists(interface)
@register_filter('is_ip')
def is_ip(addr):
@@ -794,7 +808,7 @@ def kea_address_json(addresses):
out = []
for address in addresses:
- ifname = is_addr_assigned(address, return_ifname=True)
+ ifname = is_addr_assigned(address, return_ifname=True, include_vrf=True)
if not ifname:
continue
@@ -809,10 +823,19 @@ def kea_high_availability_json(config):
source_addr = config['source_address']
remote_addr = config['remote']
+ ha_mode = 'hot-standby' if config['mode'] == 'active-passive' else 'load-balancing'
+ ha_role = config['status']
+
+ if ha_role == 'primary':
+ peer1_role = 'primary'
+ peer2_role = 'standby' if ha_mode == 'hot-standby' else 'secondary'
+ else:
+ peer1_role = 'standby' if ha_mode == 'hot-standby' else 'secondary'
+ peer2_role = 'primary'
data = {
'this-server-name': os.uname()[1],
- 'mode': 'hot-standby',
+ 'mode': ha_mode,
'heartbeat-delay': 10000,
'max-response-delay': 10000,
'max-ack-delay': 5000,
@@ -821,13 +844,13 @@ def kea_high_availability_json(config):
{
'name': os.uname()[1],
'url': f'http://{source_addr}:647/',
- 'role': 'standby' if config['status'] == 'secondary' else 'primary',
+ 'role': peer1_role,
'auto-failover': True
},
{
'name': config['name'],
'url': f'http://{remote_addr}:647/',
- 'role': 'primary' if config['status'] == 'secondary' else 'standby',
+ 'role': peer2_role,
'auto-failover': True
}]
}
diff --git a/python/vyos/tpm.py b/python/vyos/tpm.py
index f120e10c4..b9f28546f 100644
--- a/python/vyos/tpm.py
+++ b/python/vyos/tpm.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env python3
-#
# Copyright (C) 2024 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
diff --git a/python/vyos/utils/io.py b/python/vyos/utils/io.py
index 0afaf695c..7e6045291 100644
--- a/python/vyos/utils/io.py
+++ b/python/vyos/utils/io.py
@@ -13,7 +13,7 @@
# You should have received a copy of the GNU Lesser General Public
# License along with this library. If not, see <http://www.gnu.org/licenses/>.
-from typing import Callable
+from typing import Callable, Optional
def print_error(str='', end='\n'):
"""
@@ -81,7 +81,8 @@ def is_dumb_terminal():
return os.getenv('TERM') in ['vt100', 'dumb']
def select_entry(l: list, list_msg: str = '', prompt_msg: str = '',
- list_format: Callable = None,) -> str:
+ list_format: Optional[Callable] = None,
+ default_entry: Optional[int] = None) -> str:
"""Select an entry from a list
Args:
@@ -99,6 +100,9 @@ def select_entry(l: list, list_msg: str = '', prompt_msg: str = '',
print(f'\t{i}: {list_format(e)}')
else:
print(f'\t{i}: {e}')
- select = ask_input(prompt_msg, numeric_only=True,
- valid_responses=range(1, len(l)+1))
+ valid_entry = range(1, len(l)+1)
+ if default_entry and default_entry not in valid_entry:
+ default_entry = None
+ select = ask_input(prompt_msg, default=default_entry, numeric_only=True,
+ valid_responses=valid_entry)
return next(filter(lambda x: x[0] == select, en))[1]
diff --git a/python/vyos/utils/network.py b/python/vyos/utils/network.py
index cac59475d..829124b57 100644
--- a/python/vyos/utils/network.py
+++ b/python/vyos/utils/network.py
@@ -310,7 +310,7 @@ def is_ipv6_link_local(addr):
return False
-def is_addr_assigned(ip_address, vrf=None, return_ifname=False) -> bool | str:
+def is_addr_assigned(ip_address, vrf=None, return_ifname=False, include_vrf=False) -> bool | str:
""" Verify if the given IPv4/IPv6 address is assigned to any interface """
from netifaces import interfaces
from vyos.utils.network import get_interface_config
@@ -321,7 +321,7 @@ def is_addr_assigned(ip_address, vrf=None, return_ifname=False) -> bool | str:
# case there is no need to proceed with this data set - continue loop
# with next element
tmp = get_interface_config(interface)
- if dict_search('master', tmp) != vrf:
+ if dict_search('master', tmp) != vrf and not include_vrf:
continue
if is_intf_addr_assigned(interface, ip_address):
diff --git a/python/vyos/utils/system.py b/python/vyos/utils/system.py
index 5d41c0c05..55813a5f7 100644
--- a/python/vyos/utils/system.py
+++ b/python/vyos/utils/system.py
@@ -79,13 +79,6 @@ def sysctl_apply(sysctl_dict: dict[str, str], revert: bool = True) -> bool:
# everything applied
return True
-def get_half_cpus():
- """ return 1/2 of the numbers of available CPUs """
- cpu = os.cpu_count()
- if cpu > 1:
- cpu /= 2
- return int(cpu)
-
def find_device_file(device):
""" Recurively search /dev for the given device file and return its full path.
If no device file was found 'None' is returned """
diff --git a/python/vyos/version.py b/python/vyos/version.py
index 1c5651c83..b5ed2705b 100644
--- a/python/vyos/version.py
+++ b/python/vyos/version.py
@@ -1,4 +1,4 @@
-# Copyright 2017-2023 VyOS maintainers and contributors <maintainers@vyos.io>
+# Copyright 2017-2024 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
@@ -30,7 +30,7 @@ Example of the version data dict::
"""
import os
-import json
+
import requests
import vyos.defaults
@@ -40,10 +40,8 @@ from vyos.utils.process import popen
from vyos.utils.process import run
from vyos.utils.process import DEVNULL
-
version_file = os.path.join(vyos.defaults.directories['data'], 'version.json')
-
def get_version_data(fname=version_file):
"""
Get complete version data
diff --git a/python/vyos/xml/.gitignore b/python/vyos/xml/.gitignore
deleted file mode 100644
index e934adfd1..000000000
--- a/python/vyos/xml/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-cache/
diff --git a/python/vyos/xml/__init__.py b/python/vyos/xml/__init__.py
deleted file mode 100644
index 6db446a40..000000000
--- a/python/vyos/xml/__init__.py
+++ /dev/null
@@ -1,65 +0,0 @@
-# Copyright (C) 2020 VyOS maintainers and contributors
-#
-# This library is free software; you can redistribute it and/or modify it under the terms of
-# the GNU Lesser General Public License as published by the Free Software Foundation;
-# either version 2.1 of the License, or (at your option) any later version.
-#
-# This library 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 Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License along with this library;
-# if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-
-
-from vyos.xml import definition
-from vyos.xml import load
-from vyos.xml import kw
-
-
-def load_configuration(cache=[]):
- if cache:
- return cache[0]
-
- xml = definition.XML()
-
- try:
- from vyos.xml.cache import configuration
- xml.update(configuration.definition)
- cache.append(xml)
- except Exception:
- xml = definition.XML()
- print('no xml configuration cache')
- xml.update(load.xml(load.configuration_definition))
-
- return xml
-
-
-# def is_multi(lpath):
-# return load_configuration().is_multi(lpath)
-
-
-def is_tag(lpath):
- return load_configuration().is_tag(lpath)
-
-
-def is_leaf(lpath, flat=True):
- return load_configuration().is_leaf(lpath, flat)
-
-def component_version():
- return load_configuration().component_version()
-
-def defaults(lpath, flat=False):
- return load_configuration().defaults(lpath, flat)
-
-
-def multi_to_list(lpath, conf):
- return load_configuration().multi_to_list(lpath, conf)
-
-
-if __name__ == '__main__':
- print(defaults(['service'], flat=True))
- print(defaults(['service'], flat=False))
-
- print(is_tag(["system", "login", "user", "vyos", "authentication", "public-keys"]))
- print(is_tag(['protocols', 'static', 'multicast', 'route', '0.0.0.0/0', 'next-hop']))
diff --git a/python/vyos/xml/cache/__init__.py b/python/vyos/xml/cache/__init__.py
deleted file mode 100644
index e69de29bb..000000000
--- a/python/vyos/xml/cache/__init__.py
+++ /dev/null
diff --git a/python/vyos/xml/definition.py b/python/vyos/xml/definition.py
deleted file mode 100644
index bc3892b42..000000000
--- a/python/vyos/xml/definition.py
+++ /dev/null
@@ -1,360 +0,0 @@
-# Copyright (C) 2020 VyOS maintainers and contributors
-#
-# This library is free software; you can redistribute it and/or modify it under the terms of
-# the GNU Lesser General Public License as published by the Free Software Foundation;
-# either version 2.1 of the License, or (at your option) any later version.
-#
-# This library 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 Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License along with this library;
-# if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-
-from vyos.xml import kw
-
-# As we index by key, the name is first and then the data:
-# {'dummy': {
-# '[node]': '[tagNode]',
-# 'address': { ... }
-# } }
-
-# so when we encounter a tagNode, we are really encountering
-# the tagNode data.
-
-
-class XML(dict):
- def __init__(self):
- self[kw.tree] = {}
- self[kw.priorities] = {}
- self[kw.owners] = {}
- self[kw.default] = {}
- self[kw.tags] = []
- self[kw.component_version] = {}
-
- dict.__init__(self)
-
- self.tree = self[kw.tree]
- # the options which matched the last incomplete world we had
- # or the last word in a list
- self.options = []
- # store all the part of the command we processed
- self.inside = []
- # should we check the data pass with the constraints
- self.check = False
- # are we still typing a word
- self.filling = False
- # do what have the tagNode value ?
- self.filled = False
- # last word seen
- self.word = ''
- # do we have all the data we want ?
- self.final = False
- # do we have too much data ?
- self.extra = False
- # what kind of node are we in plain vs data not
- self.plain = True
-
- def reset(self):
- self.tree = self[kw.tree]
- self.options = []
- self.inside = []
- self.check = False
- self.filling = False
- self.filled = False
- self.word = ''
- self.final = False
- self.extra = False
- self.plain = True
-
- # from functools import lru_cache
- # @lru_cache(maxsize=100)
- # XXX: need to use cachetool instead - for later
-
- def traverse(self, cmd):
- self.reset()
-
- # using split() intead of split(' ') eats the final ' '
- words = cmd.split(' ')
- passed = []
- word = ''
- data_node = False
- space = False
-
- while words:
- word = words.pop(0)
- space = word == ''
- perfect = False
- if word in self.tree:
- passed = []
- perfect = True
- self.tree = self.tree[word]
- data_node = self.tree[kw.node]
- self.inside.append(word)
- word = ''
- continue
- if word and data_node:
- passed.append(word)
-
- is_valueless = self.tree.get(kw.valueless, False)
- is_leafNode = data_node == kw.leafNode
- is_dataNode = data_node in (kw.leafNode, kw.tagNode)
- named_options = [_ for _ in self.tree if not kw.found(_)]
-
- if is_leafNode:
- self.final = is_valueless or len(passed) > 0
- self.extra = is_valueless and len(passed) > 0
- self.check = len(passed) >= 1
- else:
- self.final = False
- self.extra = False
- self.check = len(passed) == 1 and not space
-
- if self.final:
- self.word = ' '.join(passed)
- else:
- self.word = word
-
- if self.final:
- self.filling = True
- else:
- self.filling = not perfect and bool(cmd and word != '')
-
- self.filled = self.final or (is_dataNode and len(passed) > 0 and word == '')
-
- if is_dataNode and len(passed) == 0:
- self.options = []
- elif word:
- if data_node != kw.plainNode or len(passed) == 1:
- self.options = [_ for _ in self.tree if _.startswith(word)]
- self.options.sort()
- else:
- self.options = []
- else:
- self.options = named_options
- self.options.sort()
-
- self.plain = not is_dataNode
-
- # self.debug()
-
- return self.word
-
- def speculate(self):
- if len(self.options) == 1:
- self.tree = self.tree[self.options[0]]
- self.word = ''
- if self.tree.get(kw.node,'') not in (kw.tagNode, kw.leafNode):
- self.options = [_ for _ in self.tree if not kw.found(_)]
- self.options.sort()
-
- def checks(self, cmd):
- # as we move thought the named node twice
- # the first time we get the data with the node
- # and the second with the pass parameters
- xml = self[kw.tree]
-
- words = cmd.split(' ')
- send = True
- last = []
- while words:
- word = words.pop(0)
- if word in xml:
- xml = xml[word]
- send = True
- last = []
- continue
- if xml[kw.node] in (kw.tagNode, kw.leafNode):
- if kw.constraint in xml:
- if send:
- yield (word, xml[kw.constraint])
- send = False
- else:
- last.append((word, None))
- if len(last) >= 2:
- yield last[0]
-
- def summary(self):
- yield ('enter', '[ summary ]', str(self.inside))
-
- if kw.help not in self.tree:
- yield ('skip', '[ summary ]', str(self.inside))
- return
-
- if self.filled:
- return
-
- yield('', '', '\nHelp:')
-
- if kw.help in self.tree:
- summary = self.tree[kw.help].get(kw.summary)
- values = self.tree[kw.help].get(kw.valuehelp, [])
- if summary:
- yield(summary, '', '')
- for value in values:
- yield(value[kw.format], value[kw.description], '')
-
- def constraint(self):
- yield ('enter', '[ constraint ]', str(self.inside))
-
- if kw.help in self.tree:
- yield ('skip', '[ constraint ]', str(self.inside))
- return
- if kw.error not in self.tree:
- yield ('skip', '[ constraint ]', str(self.inside))
- return
- if not self.word or self.filling:
- yield ('skip', '[ constraint ]', str(self.inside))
- return
-
- yield('', '', '\nData Constraint:')
-
- yield('', 'constraint', str(self.tree[kw.error]))
-
- def listing(self):
- yield ('enter', '[ listing ]', str(self.inside))
-
- # only show the details when we passed the tagNode data
- if not self.plain and not self.filled:
- yield ('skip', '[ listing ]', str(self.inside))
- return
-
- yield('', '', '\nPossible completions:')
-
- options = list(self.tree.keys())
- options.sort()
- for option in options:
- if kw.found(option):
- continue
- if not option.startswith(self.word):
- continue
- inner = self.tree[option]
- prefix = '+> ' if inner.get(kw.node, '') != kw.leafNode else ' '
- if kw.help in inner:
- yield (prefix + option, inner[kw.help].get(kw.summary), '')
- else:
- yield (prefix + option, '(no help available)', '')
-
- def debug(self):
- print('------')
- print("word '%s'" % self.word)
- print("filling " + str(self.filling))
- print("filled " + str(self.filled))
- print("final " + str(self.final))
- print("extra " + str(self.extra))
- print("plain " + str(self.plain))
- print("options " + str(self.options))
-
- # from functools import lru_cache
- # @lru_cache(maxsize=100)
- # XXX: need to use cachetool instead - for later
-
- def component_version(self) -> dict:
- d = {}
- for k in sorted(self[kw.component_version]):
- d[k] = int(self[kw.component_version][k])
- return d
-
- def defaults(self, lpath, flat):
- d = self[kw.default]
- for k in lpath:
- d = d.get(k, {})
-
- if not flat:
- # _flatten will make this conversion
- d = self.multi_to_list(lpath, d, defaults=True)
-
- r = {}
- for k in d:
- under = k.replace('-','_')
- if isinstance(d[k],dict):
- r[under] = self.defaults(lpath + [k], flat)
- continue
- r[under] = d[k]
- return r
-
- def _flatten(inside, index, d):
- r = {}
- local = inside[index:]
- prefix = '_'.join(_.replace('-','_') for _ in local) + '_' if local else ''
- for k in d:
- under = prefix + k.replace('-','_')
- level = inside + [k]
- if isinstance(d[k],dict):
- r.update(_flatten(level, index, d[k]))
- continue
- if self.is_multi(level, with_tag=False):
- r[under] = [_.strip() for _ in d[k].split(',')]
- continue
- r[under] = d[k]
- return r
-
- return _flatten(lpath, len(lpath), d)
-
- def multi_to_list(self, lpath, conf, defaults=False):
- r = {}
- for k in conf:
- # key mangling could also be done here
- # it would prevent two parsing of the config tree
- # under = k.replace('-','_')
- under = k
- fpath = lpath + [k]
- if isinstance(conf[k],dict):
- r[under] = self.multi_to_list(fpath, conf[k], defaults)
- continue
- value = conf[k]
- if self.is_multi(fpath) and not isinstance(value, list):
- if not defaults:
- value = [value]
- else:
- value = value.split(' ')
- r[under] = value
- return r
-
- # from functools import lru_cache
- # @lru_cache(maxsize=100)
- # XXX: need to use cachetool instead - for later
-
- def _tree(self, lpath, with_tag=True):
- """
- returns the part of the tree searched or None if it does not exists
- if with_tag is set, this is a configuration path (with tagNode names)
- and tag name will be removed from the path when traversing the tree
- """
- tree = self[kw.tree]
- spath = lpath.copy()
- while spath:
- p = spath.pop(0)
- if p not in tree:
- return None
- tree = tree[p]
- if with_tag and spath and tree[kw.node] == kw.tagNode:
- spath.pop(0)
- return tree
-
- def _get(self, lpath, tag, with_tag=True):
- tree = self._tree(lpath, with_tag)
- if tree is None:
- return None
- return tree.get(tag, None)
-
- def is_multi(self, lpath, with_tag=True):
- tree = self._get(lpath, kw.multi, with_tag)
- if tree is None:
- return None
- return tree is True
-
- def is_tag(self, lpath, with_tag=True):
- tree = self._get(lpath, kw.node, with_tag)
- if tree is None:
- return None
- return tree == kw.tagNode
-
- def is_leaf(self, lpath, with_tag=True):
- tree = self._get(lpath, kw.node, with_tag)
- if tree is None:
- return None
- return tree == kw.leafNode
-
- def exists(self, lpath, with_tag=True):
- return self._get(lpath, kw.node, with_tag) is not None
diff --git a/python/vyos/xml/generate.py b/python/vyos/xml/generate.py
deleted file mode 100755
index dfbbadd74..000000000
--- a/python/vyos/xml/generate.py
+++ /dev/null
@@ -1,70 +0,0 @@
-
-#!/usr/bin/env python3
-
-# Copyright (C) 2020 VyOS maintainers and contributors
-#
-# This library is free software; you can redistribute it and/or modify it under the terms of
-# the GNU Lesser General Public License as published by the Free Software Foundation;
-# either version 2.1 of the License, or (at your option) any later version.
-#
-# This library 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 Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License along with this library;
-# if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-
-import os
-import sys
-import pprint
-import argparse
-
-from vyos.xml import kw
-from vyos.xml import load
-
-
-# import json
-# def save_json(fname, loaded):
-# with open(fname, 'w') as w:
-# print(f'saving {fname}')
-# w.write(json.dumps(loaded))
-
-
-def save_dict(fname, loaded):
- with open(fname, 'w') as w:
- print(f'saving {fname}')
- w.write(f'# generated by {__file__}\n\n')
- w.write('definition = ')
- w.write(str(loaded))
-
-
-def main():
- parser = argparse.ArgumentParser(description='generate python file from xml defintions')
- parser.add_argument('--conf-folder', type=str, default=load.configuration_definition, help='XML interface definition folder')
- parser.add_argument('--conf-cache', type=str, default=load.configuration_cache, help='python file with the conf mode dict')
-
- # parser.add_argument('--op-folder', type=str, default=load.operational_definition, help='XML interface definition folder')
- # parser.add_argument('--op-cache', type=str, default=load.operational_cache, help='python file with the conf mode dict')
-
- parser.add_argument('--dry', action='store_true', help='dry run, print to screen')
-
- args = parser.parse_args()
-
- if os.path.exists(load.configuration_cache):
- os.remove(load.configuration_cache)
- # if os.path.exists(load.operational_cache):
- # os.remove(load.operational_cache)
-
- conf = load.xml(args.conf_folder)
- # op = load.xml(args.op_folder)
-
- if args.dry:
- pprint.pprint(conf)
- return
-
- save_dict(args.conf_cache, conf)
- # save_dict(args.op_cache, op)
-
-
-if __name__ == '__main__':
- main()
diff --git a/python/vyos/xml/kw.py b/python/vyos/xml/kw.py
deleted file mode 100644
index 48226ce96..000000000
--- a/python/vyos/xml/kw.py
+++ /dev/null
@@ -1,83 +0,0 @@
-# Copyright (C) 2020 VyOS maintainers and contributors
-#
-# This library is free software; you can redistribute it and/or modify it under the terms of
-# the GNU Lesser General Public License as published by the Free Software Foundation;
-# either version 2.1 of the License, or (at your option) any later version.
-#
-# This library 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 Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License along with this library;
-# if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-
-# all named used as key (keywords) in this module are defined here.
-# using variable name will allow the linter to warn on typos
-# it separates our dict syntax from the xmldict one, making it easy to change
-
-# we are redefining a python keyword "list" for ease
-
-
-def found(word):
- """
- is the word following the format for a keyword
- """
- return word and word[0] == '[' and word[-1] == ']'
-
-
-# root
-
-tree = '[tree]'
-priorities = '[priorities]'
-owners = '[owners]'
-tags = '[tags]'
-default = '[default]'
-component_version = '[component_version]'
-
-# nodes
-
-node = '[node]'
-
-plainNode = '[plainNode]'
-leafNode = '[leafNode]'
-tagNode = '[tagNode]'
-
-owner = '[owner]'
-
-valueless = '[valueless]'
-multi = '[multi]'
-hidden = '[hidden]'
-
-# properties
-
-priority = '[priority]'
-
-completion = '[completion]'
-list = '[list]'
-script = '[script]'
-path = '[path]'
-
-# help
-
-help = '[help]'
-
-summary = '[summary]'
-
-valuehelp = '[valuehelp]'
-format = 'format'
-description = 'description'
-
-# constraint
-
-constraint = '[constraint]'
-name = '[name]'
-
-regex = '[regex]'
-validator = '[validator]'
-argument = '[argument]'
-
-error = '[error]'
-
-# created
-
-node = '[node]'
diff --git a/python/vyos/xml/load.py b/python/vyos/xml/load.py
deleted file mode 100644
index f842ff9ce..000000000
--- a/python/vyos/xml/load.py
+++ /dev/null
@@ -1,300 +0,0 @@
-# Copyright (C) 2020 VyOS maintainers and contributors
-#
-# This library is free software; you can redistribute it and/or modify it under the terms of
-# the GNU Lesser General Public License as published by the Free Software Foundation;
-# either version 2.1 of the License, or (at your option) any later version.
-#
-# This library 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 Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License along with this library;
-# if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-
-import glob
-
-from os.path import join
-from os.path import abspath
-from os.path import dirname
-
-import xmltodict
-
-from vyos import debug
-from vyos.xml import kw
-from vyos.xml import definition
-
-
-# where the files are located
-
-_here = dirname(__file__)
-
-configuration_definition = abspath(join(_here, '..', '..' ,'..', 'interface-definitions'))
-configuration_cache = abspath(join(_here, 'cache', 'configuration.py'))
-
-operational_definition = abspath(join(_here, '..', '..' ,'..', 'op-mode-definitions'))
-operational_cache = abspath(join(_here, 'cache', 'operational.py'))
-
-
-# This code is only ran during the creation of the debian package
-# therefore we accept that failure can be fatal and not handled
-# gracefully.
-
-
-def _fatal(debug_info=''):
- """
- raise a RuntimeError or if in developer mode stop the code
- """
- if not debug.enabled('developer'):
- raise RuntimeError(str(debug_info))
-
- if debug_info:
- print(debug_info)
- breakpoint()
-
-
-def _safe_update(dict1, dict2):
- """
- return a dict made of two, raise if any root key would be overwritten
- """
- if set(dict1).intersection(dict2):
- raise RuntimeError('overlapping configuration')
- return {**dict1, **dict2}
-
-
-def _merge(dict1, dict2):
- """
- merge dict2 in to dict1 and return it
- """
- for k in list(dict2):
- if k not in dict1:
- dict1[k] = dict2[k]
- continue
- if isinstance(dict1[k], dict) and isinstance(dict2[k], dict):
- dict1[k] = _merge(dict1[k], dict2[k])
- elif isinstance(dict1[k], list) and isinstance(dict2[k], list):
- dict1[k].extend(dict2[k])
- elif dict1[k] == dict2[k]:
- continue
- else:
- dict1[k] = dict2[k]
- return dict1
-
-
-def _include(fname, folder=''):
- """
- return the content of a file, including any file referenced with a #include
- """
- if not folder:
- folder = dirname(fname)
- content = ''
- with open(fname, 'r') as r:
- for line in r.readlines():
- if '#include' in line:
- content += _include(join(folder,line.strip()[10:-1]), folder)
- continue
- content += line
- return content
-
-
-def _format_nodes(inside, conf, xml):
- r = {}
- while conf:
- nodetype = ''
- nodename = ''
- if 'node' in conf.keys():
- nodetype = 'node'
- nodename = kw.plainNode
- elif 'leafNode' in conf.keys():
- nodetype = 'leafNode'
- nodename = kw.leafNode
- elif 'tagNode' in conf.keys():
- nodetype = 'tagNode'
- nodename = kw.tagNode
- elif 'syntaxVersion' in conf.keys():
- sv = conf.pop('syntaxVersion')
- if isinstance(sv, list):
- for v in sv:
- xml[kw.component_version][v['@component']] = v['@version']
- else:
- xml[kw.component_version][sv['@component']] = sv['@version']
- continue
- else:
- _fatal(conf.keys())
-
- nodes = conf.pop(nodetype)
- if isinstance(nodes, list):
- for node in nodes:
- name = node.pop('@name')
- into = inside + [name]
- if name in r:
- _merge(r[name], _format_node(into, node, xml))
- else:
- r[name] = _format_node(into, node, xml)
- r[name][kw.node] = nodename
- xml[kw.tags].append(' '.join(into))
- else:
- node = nodes
- name = node.pop('@name')
- into = inside + [name]
- if name in r:
- _merge(r[name], _format_node(inside + [name], node, xml))
- else:
- r[name] = _format_node(inside + [name], node, xml)
- r[name][kw.node] = nodename
- xml[kw.tags].append(' '.join(into))
- return r
-
-
-def _set_validator(r, validator):
- v = {}
- while validator:
- if '@name' in validator:
- v[kw.name] = validator.pop('@name')
- elif '@argument' in validator:
- v[kw.argument] = validator.pop('@argument')
- else:
- _fatal(validator)
- r[kw.constraint][kw.validator].append(v)
-
-
-def _format_node(inside, conf, xml):
- r = {
- kw.valueless: False,
- kw.multi: False,
- kw.hidden: False,
- }
-
- if '@owner' in conf:
- owner = conf.pop('@owner', '')
- r[kw.owner] = owner
- xml[kw.owners][' '.join(inside)] = owner
-
- while conf:
- keys = conf.keys()
- if 'children' in keys:
- children = conf.pop('children')
-
- if isinstance(conf, list):
- for child in children:
- _merge(r, _format_nodes(inside, child, xml))
- else:
- child = children
- _merge(r, _format_nodes(inside, child, xml))
-
- elif 'properties' in keys:
- properties = conf.pop('properties')
-
- while properties:
- if 'help' in properties:
- helpname = properties.pop('help')
- r[kw.help] = {}
- r[kw.help][kw.summary] = helpname
-
- elif 'valueHelp' in properties:
- valuehelps = properties.pop('valueHelp')
- if kw.valuehelp in r[kw.help]:
- _fatal(valuehelps)
- r[kw.help][kw.valuehelp] = []
- if isinstance(valuehelps, list):
- for valuehelp in valuehelps:
- r[kw.help][kw.valuehelp].append(dict(valuehelp))
- else:
- valuehelp = valuehelps
- r[kw.help][kw.valuehelp].append(dict(valuehelp))
-
- elif 'constraint' in properties:
- constraint = properties.pop('constraint')
- r[kw.constraint] = {}
- while constraint:
- if 'regex' in constraint:
- regexes = constraint.pop('regex')
- if kw.regex in kw.constraint:
- _fatal(regexes)
- r[kw.constraint][kw.regex] = []
- if isinstance(regexes, list):
- r[kw.constraint][kw.regex] = []
- for regex in regexes:
- r[kw.constraint][kw.regex].append(regex)
- else:
- regex = regexes
- r[kw.constraint][kw.regex].append(regex)
- elif 'validator' in constraint:
- validators = constraint.pop('validator')
- if kw.validator in r[kw.constraint]:
- _fatal(validators)
- r[kw.constraint][kw.validator] = []
- if isinstance(validators, list):
- for validator in validators:
- _set_validator(r, validator)
- else:
- validator = validators
- _set_validator(r, validator)
- else:
- _fatal(constraint)
-
- elif 'constraintGroup' in properties:
- properties.pop('constraintGroup')
-
- elif 'constraintErrorMessage' in properties:
- r[kw.error] = properties.pop('constraintErrorMessage')
-
- elif 'valueless' in properties:
- properties.pop('valueless')
- r[kw.valueless] = True
-
- elif 'multi' in properties:
- properties.pop('multi')
- r[kw.multi] = True
-
- elif 'hidden' in properties:
- properties.pop('hidden')
- r[kw.hidden] = True
-
- elif 'completionHelp' in properties:
- completionHelp = properties.pop('completionHelp')
- r[kw.completion] = {}
- while completionHelp:
- if 'list' in completionHelp:
- r[kw.completion][kw.list] = completionHelp.pop('list')
- elif 'script' in completionHelp:
- r[kw.completion][kw.script] = completionHelp.pop('script')
- elif 'path' in completionHelp:
- r[kw.completion][kw.path] = completionHelp.pop('path')
- else:
- _fatal(completionHelp.keys())
-
- elif 'priority' in properties:
- priority = int(properties.pop('priority'))
- r[kw.priority] = priority
- xml[kw.priorities].setdefault(priority, []).append(' '.join(inside))
-
- else:
- _fatal(properties.keys())
-
- elif 'defaultValue' in keys:
- default = conf.pop('defaultValue')
- x = xml[kw.default]
- for k in inside[:-1]:
- x = x.setdefault(k,{})
- x[inside[-1]] = '' if default is None else default
-
- else:
- _fatal(conf)
-
- return r
-
-
-def xml(folder):
- """
- read all the xml in the folder
- """
- xml = definition.XML()
- for fname in glob.glob(f'{folder}/*.xml.in'):
- parsed = xmltodict.parse(_include(fname))
- formated = _format_nodes([], parsed['interfaceDefinition'], xml)
- _merge(xml[kw.tree], formated)
- # fix the configuration root node for completion
- # as we moved all the name "up" the chain to use them as index.
- xml[kw.tree][kw.node] = kw.plainNode
- # XXX: do the others
- return xml
diff --git a/python/vyos/xml/test_xml.py b/python/vyos/xml/test_xml.py
deleted file mode 100644
index 3a6f0132d..000000000
--- a/python/vyos/xml/test_xml.py
+++ /dev/null
@@ -1,279 +0,0 @@
-#!/usr/bin/env python3
-#
-# Copyright (C) 2020 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 unittest import TestCase, mock
-
-from vyos.xml import load_configuration
-
-import sys
-
-
-class TestSearch(TestCase):
- def setUp(self):
- self.xml = load_configuration()
-
- def test_(self):
- last = self.xml.traverse("")
- self.assertEqual(last, '')
- self.assertEqual(self.xml.inside, [])
- self.assertEqual(self.xml.options, ['firewall', 'high-availability', 'interfaces', 'nat', 'protocols', 'service', 'system', 'vpn', 'vrf'])
- self.assertEqual(self.xml.filling, False)
- self.assertEqual(self.xml.word, last)
- self.assertEqual(self.xml.check, False)
- self.assertEqual(self.xml.final, False)
- self.assertEqual(self.xml.extra, False)
- self.assertEqual(self.xml.filled, False)
- self.assertEqual(self.xml.plain, True)
-
- def test_i(self):
- last = self.xml.traverse("i")
- self.assertEqual(last, 'i')
- self.assertEqual(self.xml.inside, [])
- self.assertEqual(self.xml.options, ['interfaces'])
- self.assertEqual(self.xml.filling, True)
- self.assertEqual(self.xml.word, last)
- self.assertEqual(self.xml.check, False)
- self.assertEqual(self.xml.final, False)
- self.assertEqual(self.xml.extra, False)
- self.assertEqual(self.xml.filled, False)
- self.assertEqual(self.xml.plain, True)
-
- def test_interfaces(self):
- last = self.xml.traverse("interfaces")
- self.assertEqual(last, '')
- self.assertEqual(self.xml.inside, ['interfaces'])
- self.assertEqual(self.xml.options, ['bonding', 'bridge', 'dummy', 'ethernet', 'geneve', 'l2tpv3', 'loopback', 'macsec', 'openvpn', 'pppoe', 'pseudo-ethernet', 'tunnel', 'vxlan', 'wireguard', 'wireless', 'wwan'])
- self.assertEqual(self.xml.filling, False)
- self.assertEqual(self.xml.word, '')
- self.assertEqual(self.xml.check, False)
- self.assertEqual(self.xml.final, False)
- self.assertEqual(self.xml.extra, False)
- self.assertEqual(self.xml.filled, False)
- self.assertEqual(self.xml.plain, True)
-
- def test_interfaces_space(self):
- last = self.xml.traverse("interfaces ")
- self.assertEqual(last, '')
- self.assertEqual(self.xml.inside, ['interfaces'])
- self.assertEqual(self.xml.options, ['bonding', 'bridge', 'dummy', 'ethernet', 'geneve', 'l2tpv3', 'loopback', 'macsec', 'openvpn', 'pppoe', 'pseudo-ethernet', 'tunnel', 'vxlan', 'wireguard', 'wireless', 'wwan'])
- self.assertEqual(self.xml.filling, False)
- self.assertEqual(self.xml.word, last)
- self.assertEqual(self.xml.check, False)
- self.assertEqual(self.xml.final, False)
- self.assertEqual(self.xml.extra, False)
- self.assertEqual(self.xml.filled, False)
- self.assertEqual(self.xml.plain, True)
-
- def test_interfaces_w(self):
- last = self.xml.traverse("interfaces w")
- self.assertEqual(last, 'w')
- self.assertEqual(self.xml.inside, ['interfaces'])
- self.assertEqual(self.xml.options, ['wireguard', 'wireless', 'wwan'])
- self.assertEqual(self.xml.filling, True)
- self.assertEqual(self.xml.word, last)
- self.assertEqual(self.xml.check, True)
- self.assertEqual(self.xml.final, False)
- self.assertEqual(self.xml.extra, False)
- self.assertEqual(self.xml.filled, False)
- self.assertEqual(self.xml.plain, True)
-
- def test_interfaces_ethernet(self):
- last = self.xml.traverse("interfaces ethernet")
- self.assertEqual(last, '')
- self.assertEqual(self.xml.inside, ['interfaces', 'ethernet'])
- self.assertEqual(self.xml.options, [])
- self.assertEqual(self.xml.filling, False)
- self.assertEqual(self.xml.word, '')
- self.assertEqual(self.xml.check, False)
- self.assertEqual(self.xml.final, False)
- self.assertEqual(self.xml.extra, False)
- self.assertEqual(self.xml.filled, False)
- self.assertEqual(self.xml.plain, False)
-
- def test_interfaces_ethernet_space(self):
- last = self.xml.traverse("interfaces ethernet ")
- self.assertEqual(last, '')
- self.assertEqual(self.xml.inside, ['interfaces', 'ethernet'])
- self.assertEqual(self.xml.options, [])
- self.assertEqual(self.xml.filling, False)
- self.assertEqual(self.xml.word, '')
- self.assertEqual(self.xml.check, False)
- self.assertEqual(self.xml.final, False)
- self.assertEqual(self.xml.extra, False)
- self.assertEqual(self.xml.filled, False)
- self.assertEqual(self.xml.plain, False)
-
- def test_interfaces_ethernet_e(self):
- last = self.xml.traverse("interfaces ethernet e")
- self.assertEqual(last, 'e')
- self.assertEqual(self.xml.inside, ['interfaces', 'ethernet'])
- self.assertEqual(self.xml.options, [])
- self.assertEqual(self.xml.filling, True)
- self.assertEqual(self.xml.word, last)
- self.assertEqual(self.xml.check, True)
- self.assertEqual(self.xml.final, False)
- self.assertEqual(self.xml.extra, False)
- self.assertEqual(self.xml.filled, False)
- self.assertEqual(self.xml.plain, False)
-
- def test_interfaces_la(self):
- last = self.xml.traverse("interfaces ethernet la")
- self.assertEqual(last, 'la')
- self.assertEqual(self.xml.inside, ['interfaces', 'ethernet'])
- self.assertEqual(self.xml.options, [])
- self.assertEqual(self.xml.filling, True)
- self.assertEqual(self.xml.word, last)
- self.assertEqual(self.xml.check, True)
- self.assertEqual(self.xml.final, False)
- self.assertEqual(self.xml.extra, False)
- self.assertEqual(self.xml.filled, False)
- self.assertEqual(self.xml.plain, False)
-
- def test_interfaces_ethernet_lan0(self):
- last = self.xml.traverse("interfaces ethernet lan0")
- self.assertEqual(last, 'lan0')
- self.assertEqual(self.xml.inside, ['interfaces', 'ethernet'])
- self.assertEqual(self.xml.options, [])
- self.assertEqual(self.xml.filling, True)
- self.assertEqual(self.xml.word, last)
- self.assertEqual(self.xml.check, True)
- self.assertEqual(self.xml.final, False)
- self.assertEqual(self.xml.extra, False)
- self.assertEqual(self.xml.filled, False)
- self.assertEqual(self.xml.plain, False)
-
- def test_interfaces_ethernet_lan0_space(self):
- last = self.xml.traverse("interfaces ethernet lan0 ")
- self.assertEqual(last, '')
- self.assertEqual(self.xml.inside, ['interfaces', 'ethernet'])
- self.assertEqual(len(self.xml.options), 19)
- self.assertEqual(self.xml.filling, False)
- self.assertEqual(self.xml.word, last)
- self.assertEqual(self.xml.check, False)
- self.assertEqual(self.xml.final, False)
- self.assertEqual(self.xml.extra, False)
- self.assertEqual(self.xml.filled, True)
- self.assertEqual(self.xml.plain, False)
-
- def test_interfaces_ethernet_lan0_ad(self):
- last = self.xml.traverse("interfaces ethernet lan0 ad")
- self.assertEqual(last, 'ad')
- self.assertEqual(self.xml.inside, ['interfaces', 'ethernet'])
- self.assertEqual(self.xml.options, ['address'])
- self.assertEqual(self.xml.filling, True)
- self.assertEqual(self.xml.word, last)
- self.assertEqual(self.xml.check, False)
- self.assertEqual(self.xml.final, False)
- self.assertEqual(self.xml.extra, False)
- self.assertEqual(self.xml.filled, False)
- self.assertEqual(self.xml.plain, False)
-
- def test_interfaces_ethernet_lan0_address(self):
- last = self.xml.traverse("interfaces ethernet lan0 address")
- self.assertEqual(last, '')
- self.assertEqual(self.xml.inside, ['interfaces', 'ethernet', 'address'])
- self.assertEqual(self.xml.options, [])
- self.assertEqual(self.xml.filling, False)
- self.assertEqual(self.xml.word, last)
- self.assertEqual(self.xml.check, False)
- self.assertEqual(self.xml.final, False)
- self.assertEqual(self.xml.extra, False)
- self.assertEqual(self.xml.filled, False)
- self.assertEqual(self.xml.plain, False)
-
- def test_interfaces_ethernet_lan0_address_space(self):
- last = self.xml.traverse("interfaces ethernet lan0 address ")
- self.assertEqual(last, '')
- self.assertEqual(self.xml.inside, ['interfaces', 'ethernet', 'address'])
- self.assertEqual(self.xml.options, [])
- self.assertEqual(self.xml.filling, False)
- self.assertEqual(self.xml.word, last)
- self.assertEqual(self.xml.check, False)
- self.assertEqual(self.xml.final, False)
- self.assertEqual(self.xml.extra, False)
- self.assertEqual(self.xml.filled, False)
- self.assertEqual(self.xml.plain, False)
-
- def test_interfaces_ethernet_lan0_address_space_11(self):
- last = self.xml.traverse("interfaces ethernet lan0 address 1.1")
- self.assertEqual(last, '1.1')
- self.assertEqual(self.xml.inside, ['interfaces', 'ethernet', 'address'])
- self.assertEqual(self.xml.options, [])
- self.assertEqual(self.xml.filling, True)
- self.assertEqual(self.xml.word, last)
- self.assertEqual(self.xml.check, True)
- self.assertEqual(self.xml.final, True)
- self.assertEqual(self.xml.extra, False)
- self.assertEqual(self.xml.filled, True)
- self.assertEqual(self.xml.plain, False)
-
- def test_interfaces_ethernet_lan0_address_space_1111_32(self):
- last = self.xml.traverse("interfaces ethernet lan0 address 1.1.1.1/32")
- self.assertEqual(last, '1.1.1.1/32')
- self.assertEqual(self.xml.inside, ['interfaces', 'ethernet', 'address'])
- self.assertEqual(self.xml.options, [])
- self.assertEqual(self.xml.filling, True)
- self.assertEqual(self.xml.word, last)
- self.assertEqual(self.xml.check, True)
- self.assertEqual(self.xml.final, True)
- self.assertEqual(self.xml.extra, False)
- self.assertEqual(self.xml.filled, True)
- self.assertEqual(self.xml.plain, False)
-
- def test_interfaces_ethernet_lan0_address_space_1111_32_space(self):
- last = self.xml.traverse("interfaces ethernet lan0 address 1.1.1.1/32 ")
- self.assertEqual(last, '1.1.1.1/32')
- self.assertEqual(self.xml.inside, ['interfaces', 'ethernet', 'address'])
- self.assertEqual(self.xml.options, [])
- self.assertEqual(self.xml.filling, True)
- self.assertEqual(self.xml.word, last)
- self.assertEqual(self.xml.check, True)
- self.assertEqual(self.xml.final, True)
- self.assertEqual(self.xml.extra, False)
- self.assertEqual(self.xml.filled, True)
- self.assertEqual(self.xml.plain, False)
-
- def test_interfaces_ethernet_lan0_address_space_1111_32_space_text(self):
- last = self.xml.traverse("interfaces ethernet lan0 address 1.1.1.1/32 text")
- self.assertEqual(last, '1.1.1.1/32 text')
- self.assertEqual(self.xml.inside, ['interfaces', 'ethernet', 'address'])
- self.assertEqual(self.xml.options, [])
- self.assertEqual(self.xml.filling, True)
- self.assertEqual(self.xml.word, last)
- self.assertEqual(self.xml.check, True)
- self.assertEqual(self.xml.final, True)
- self.assertEqual(self.xml.extra, False)
- self.assertEqual(self.xml.filled, True)
- self.assertEqual(self.xml.plain, False)
-
- def test_interfaces_ethernet_lan0_address_space_1111_32_space_text_space(self):
- last = self.xml.traverse("interfaces ethernet lan0 address 1.1.1.1/32 text ")
- self.assertEqual(last, '1.1.1.1/32 text')
- self.assertEqual(self.xml.inside, ['interfaces', 'ethernet', 'address'])
- self.assertEqual(self.xml.options, [])
- self.assertEqual(self.xml.filling, True)
- self.assertEqual(self.xml.word, last)
- self.assertEqual(self.xml.check, True)
- self.assertEqual(self.xml.final, True)
- self.assertEqual(self.xml.extra, False)
- self.assertEqual(self.xml.filled, True)
- self.assertEqual(self.xml.plain, False)
-
- # Need to add a check for a valuless leafNode
diff --git a/python/vyos/xml_ref/generate_cache.py b/python/vyos/xml_ref/generate_cache.py
index d1ccb0f81..5f3f84dee 100755
--- a/python/vyos/xml_ref/generate_cache.py
+++ b/python/vyos/xml_ref/generate_cache.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2023 VyOS maintainers and contributors
+# Copyright (C) 2023-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
@@ -13,19 +13,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 sys
import json
from argparse import ArgumentParser
from argparse import ArgumentTypeError
-from os import getcwd
-from os import makedirs
from os.path import join
from os.path import abspath
from os.path import dirname
-from os.path import basename
from xmltodict import parse
_here = dirname(__file__)
diff --git a/scripts/check-pr-title-and-commit-messages.py b/scripts/check-pr-title-and-commit-messages.py
index 1d4a3cfe3..001f6cf82 100755
--- a/scripts/check-pr-title-and-commit-messages.py
+++ b/scripts/check-pr-title-and-commit-messages.py
@@ -5,7 +5,6 @@ import sys
import time
import requests
-from pprint import pprint
# Use the same regex for PR title and commit messages for now
title_regex = r'^(([a-zA-Z\-_.]+:\s)?)T\d+:\s+[^\s]+.*'
@@ -50,4 +49,3 @@ if __name__ == '__main__':
# Retrieve every individual commit and check its title
co = requests.get(c["url"]).json()
check_commit_message(co["commit"]["message"])
-
diff --git a/smoketest/config-tests/container-simple b/smoketest/config-tests/container-simple
new file mode 100644
index 000000000..299af64cb
--- /dev/null
+++ b/smoketest/config-tests/container-simple
@@ -0,0 +1,12 @@
+set system config-management commit-revisions '50'
+set system host-name 'vyos'
+set system login user vyos authentication encrypted-password '$6$r/Yw/07NXNY$/ZB.Rjf9jxEV.BYoDyLdH.kH14rU52pOBtrX.4S34qlPt77chflCHvpTCq9a6huLzwaMR50rEICzA5GoIRZlM0'
+set system login user vyos authentication plaintext-password ''
+set system console device ttyS0 speed '115200'
+set container name c01 allow-host-networks
+set container name c01 capability 'net-bind-service'
+set container name c01 capability 'net-raw'
+set container name c01 image 'busybox:stable'
+set container name c02 allow-host-networks
+set container name c02 capability 'sys-time'
+set container name c02 image 'busybox:stable'
diff --git a/smoketest/config-tests/ipoe-server b/smoketest/config-tests/ipoe-server
new file mode 100644
index 000000000..fb32fdb14
--- /dev/null
+++ b/smoketest/config-tests/ipoe-server
@@ -0,0 +1,35 @@
+set interfaces ethernet eth0 address 'dhcp'
+set interfaces ethernet eth1 address '192.168.0.1/24'
+set interfaces loopback lo
+set service ntp server time1.vyos.net
+set service ntp server time2.vyos.net
+set service ntp server time3.vyos.net
+set service ipoe-server authentication interface eth1 mac 08:00:27:2f:d8:06 rate-limit download '1000'
+set service ipoe-server authentication interface eth1 mac 08:00:27:2f:d8:06 rate-limit upload '500'
+set service ipoe-server authentication interface eth1 mac 08:00:27:2f:d8:06 vlan '100'
+set service ipoe-server authentication interface eth2 mac 08:00:27:2f:d8:06
+set service ipoe-server authentication mode 'local'
+set service ipoe-server client-ip-pool POOL1 range '192.0.2.0/24'
+set service ipoe-server client-ipv6-pool ipv6-pool delegate 2001:db8:1::/48 delegation-prefix '56'
+set service ipoe-server client-ipv6-pool ipv6-pool prefix 2001:db8::/48 mask '64'
+set service ipoe-server default-ipv6-pool 'ipv6-pool'
+set service ipoe-server default-pool 'POOL1'
+set service ipoe-server gateway-address '192.0.2.1/24'
+set service ipoe-server interface eth1 mode 'l3'
+set service ipoe-server interface eth1 network 'vlan'
+set service ipoe-server interface eth1 vlan '100'
+set service ipoe-server interface eth1 vlan '200'
+set service ipoe-server interface eth1 vlan '1000-2000'
+set service ipoe-server interface eth1 vlan '2500-2700'
+set service ipoe-server name-server '10.10.1.1'
+set service ipoe-server name-server '10.10.1.2'
+set service ipoe-server name-server '2001:db8:aaa::'
+set service ipoe-server name-server '2001:db8:bbb::'
+set system config-management commit-revisions '100'
+set system host-name 'vyos'
+set system login user vyos authentication encrypted-password '$6$O5gJRlDYQpj$MtrCV9lxMnZPMbcxlU7.FI793MImNHznxGoMFgm3Q6QP3vfKJyOSRCt3Ka/GzFQyW1yZS4NS616NLHaIPPFHc0'
+set system login user vyos authentication plaintext-password ''
+set system console device ttyS0 speed '115200'
+set nat source rule 100 outbound-interface name 'eth0'
+set nat source rule 100 source address '192.168.0.0/24'
+set nat source rule 100 translation address 'masquerade'
diff --git a/smoketest/config-tests/ospf-simple b/smoketest/config-tests/ospf-simple
new file mode 100644
index 000000000..13d5e7038
--- /dev/null
+++ b/smoketest/config-tests/ospf-simple
@@ -0,0 +1,20 @@
+set interfaces ethernet eth0 vif 20 address '193.201.42.173/28'
+set interfaces ethernet eth0 vif 666 address '10.66.66.1/24'
+set interfaces loopback lo
+set protocols ospf area 0 network '10.66.66.0/24'
+set protocols ospf area 0 network '193.201.42.160/28'
+set protocols ospf area 0 area-type normal
+set protocols ospf interface eth0.20 cost '999'
+set protocols ospf interface eth0.20 dead-interval '4'
+set protocols ospf interface eth0.20 hello-interval '1'
+set protocols ospf interface eth0.20 priority '255'
+set protocols ospf interface eth0.20 retransmit-interval '5'
+set protocols ospf interface eth0.20 transmit-delay '1'
+set protocols ospf interface eth0.666 passive
+set protocols ospf log-adjacency-changes detail
+set protocols static route 0.0.0.0/0 next-hop 193.201.42.170 distance '130'
+set system config-management commit-revisions '100'
+set system host-name 'lab-vyos-r1'
+set system login user vyos authentication encrypted-password '$6$R.OnGzfXSfl6J$Iba/hl9bmjBs0VPtZ2zdW.Snh/nHuvxUwi0R6ruypgW63iKEbicJH.uUst8xZCyByURblxRtjAC1lAnYfIt.b0'
+set system login user vyos authentication plaintext-password ''
+set system console device ttyS0 speed '115200'
diff --git a/smoketest/configs/container-simple b/smoketest/configs/container-simple
new file mode 100644
index 000000000..05efe05e9
--- /dev/null
+++ b/smoketest/configs/container-simple
@@ -0,0 +1,46 @@
+container {
+ name c01 {
+ allow-host-networks
+ cap-add net-bind-service
+ cap-add net-raw
+ image busybox:stable
+ }
+ name c02 {
+ allow-host-networks
+ cap-add sys-time
+ image busybox:stable
+ }
+}
+interfaces {
+ ethernet eth0 {
+ duplex auto
+ speed auto
+ }
+ ethernet eth1 {
+ duplex auto
+ speed auto
+ }
+}
+system {
+ config-management {
+ commit-revisions 50
+ }
+ console {
+ device ttyS0 {
+ speed 115200
+ }
+ }
+ host-name vyos
+ login {
+ user vyos {
+ authentication {
+ encrypted-password $6$r/Yw/07NXNY$/ZB.Rjf9jxEV.BYoDyLdH.kH14rU52pOBtrX.4S34qlPt77chflCHvpTCq9a6huLzwaMR50rEICzA5GoIRZlM0
+ plaintext-password ""
+ }
+ }
+ }
+}
+
+// Warning: Do not remove the following line.
+// vyos-config-version: "broadcast-relay@1:cluster@1:config-management@1:conntrack@3:conntrack-sync@2:container@1:dhcp-relay@2:dhcp-server@6:dhcpv6-server@1:dns-forwarding@3:firewall@5:https@2:interfaces@23:ipoe-server@1:ipsec@5:isis@1:l2tp@3:lldp@1:mdns@1:nat@5:ntp@1:pppoe-server@5:pptp@2:qos@1:quagga@8:rpki@1:salt@1:snmp@2:ssh@2:sstp@3:system@21:vrrp@2:vyos-accel-ppp@2:wanloadbalance@3:webproxy@2:zone-policy@1"
+// Release version: 1.3.6
diff --git a/smoketest/configs/ipoe-server b/smoketest/configs/ipoe-server
index a375e91de..fdd554b7d 100644
--- a/smoketest/configs/ipoe-server
+++ b/smoketest/configs/ipoe-server
@@ -56,7 +56,6 @@ service {
}
}
interface eth1 {
- client-subnet 192.168.0.0/24
network vlan
network-mode L3
vlan-id 100
@@ -64,9 +63,6 @@ service {
vlan-range 1000-2000
vlan-range 2500-2700
}
- interface eth2 {
- client-subnet 192.168.1.0/24
- }
name-server 10.10.1.1
name-server 10.10.1.2
name-server 2001:db8:aaa::
@@ -94,11 +90,11 @@ system {
}
}
ntp {
- server 0.pool.ntp.org {
+ server time1.vyos.net {
}
- server 1.pool.ntp.org {
+ server time2.vyos.net {
}
- server 2.pool.ntp.org {
+ server time3.vyos.net {
}
}
syslog {
diff --git a/smoketest/configs/ospf-simple b/smoketest/configs/ospf-simple
new file mode 100644
index 000000000..0427062ae
--- /dev/null
+++ b/smoketest/configs/ospf-simple
@@ -0,0 +1,81 @@
+interfaces {
+ ethernet eth0 {
+ vif 20 {
+ address 193.201.42.173/28
+ ip {
+ ospf {
+ cost 999
+ dead-interval 4
+ hello-interval 1
+ priority 255
+ retransmit-interval 5
+ transmit-delay 1
+ }
+ }
+ }
+ vif 666 {
+ address 10.66.66.1/24
+ }
+ }
+ ethernet eth1 {
+ }
+ ethernet eth2 {
+ }
+ loopback lo {
+ }
+}
+protocols {
+ ospf {
+ area 0 {
+ area-type {
+ normal
+ }
+ network 193.201.42.160/28
+ network 10.66.66.0/24
+ }
+ log-adjacency-changes {
+ detail
+ }
+ passive-interface eth0.666
+ }
+ static {
+ route 0.0.0.0/0 {
+ next-hop 193.201.42.170 {
+ distance 130
+ }
+ }
+ }
+}
+system {
+ config-management {
+ commit-revisions 100
+ }
+ console {
+ device ttyS0 {
+ speed 115200
+ }
+ }
+ host-name lab-vyos-r1
+ login {
+ user vyos {
+ authentication {
+ encrypted-password $6$R.OnGzfXSfl6J$Iba/hl9bmjBs0VPtZ2zdW.Snh/nHuvxUwi0R6ruypgW63iKEbicJH.uUst8xZCyByURblxRtjAC1lAnYfIt.b0
+ plaintext-password ""
+ }
+ }
+ }
+ syslog {
+ global {
+ facility all {
+ level info
+ }
+ facility protocols {
+ level debug
+ }
+ }
+ }
+}
+
+// Warning: Do not remove the following line.
+// vyos-config-version: "broadcast-relay@1:cluster@1:config-management@1:conntrack@3:conntrack-sync@2:container@1:dhcp-relay@2:dhcp-server@6:dhcpv6-server@1:dns-forwarding@3:firewall@5:https@2:interfaces@22:ipoe-server@1:ipsec@5:isis@1:l2tp@3:lldp@1:mdns@1:nat@5:ntp@1:pppoe-server@5:pptp@2:qos@1:quagga@8:rpki@1:salt@1:snmp@2:ssh@2:sstp@3:system@21:vrrp@2:vyos-accel-ppp@2:wanloadbalance@3:webproxy@2:zone-policy@1"
+// Release version: 1.3.4
diff --git a/smoketest/scripts/cli/base_accel_ppp_test.py b/smoketest/scripts/cli/base_accel_ppp_test.py
index ac4bbcfe5..383adc445 100644
--- a/smoketest/scripts/cli/base_accel_ppp_test.py
+++ b/smoketest/scripts/cli/base_accel_ppp_test.py
@@ -11,21 +11,18 @@
#
# 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 unittest
+import re
from base_vyostest_shim import VyOSUnitTestSHIM
from configparser import ConfigParser
-from vyos.configsession import ConfigSession
from vyos.configsession import ConfigSessionError
from vyos.template import is_ipv4
-from vyos.utils.system import get_half_cpus
+from vyos.cpu import get_core_count
from vyos.utils.process import process_named_running
from vyos.utils.process import cmd
-
class BasicAccelPPPTest:
class TestCase(VyOSUnitTestSHIM.TestCase):
@classmethod
@@ -132,7 +129,7 @@ class BasicAccelPPPTest:
return out
def verify(self, conf):
- self.assertEqual(conf["core"]["thread-count"], str(get_half_cpus()))
+ self.assertEqual(conf["core"]["thread-count"], str(get_core_count()))
def test_accel_name_servers(self):
# Verify proper Name-Server configuration for IPv4 and IPv6
diff --git a/smoketest/scripts/cli/base_interfaces_test.py b/smoketest/scripts/cli/base_interfaces_test.py
index 9b705c801..9be2c2f1a 100644
--- a/smoketest/scripts/cli/base_interfaces_test.py
+++ b/smoketest/scripts/cli/base_interfaces_test.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2019-2023 VyOS maintainers and contributors
+# Copyright (C) 2019-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
@@ -12,8 +12,6 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-from binascii import hexlify
-
from netifaces import AF_INET
from netifaces import AF_INET6
from netifaces import ifaddresses
@@ -521,8 +519,7 @@ class BasicInterfaceTest:
base = self._base_path + [interface, 'vif', vlan]
self.cli_set(base + ['mtu', mtu_9000])
- # check validate() - VIF MTU must not be larger the parent interface
- # MTU size.
+ # check validate() - Interface MTU "9000" too high, parent interface MTU is "1500"!
with self.assertRaises(ConfigSessionError):
self.cli_commit()
diff --git a/smoketest/scripts/cli/test_container.py b/smoketest/scripts/cli/test_container.py
index 9094e27dd..3201883b8 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-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
@@ -24,7 +24,6 @@ from ipaddress import ip_interface
from vyos.configsession import ConfigSessionError
from vyos.utils.process import cmd
from vyos.utils.process import process_named_running
-from vyos.utils.file import read_file
base_path = ['container']
cont_image = 'busybox:stable' # busybox is included in vyos-build
diff --git a/smoketest/scripts/cli/test_firewall.py b/smoketest/scripts/cli/test_firewall.py
index fe6977252..c47562714 100755
--- a/smoketest/scripts/cli/test_firewall.py
+++ b/smoketest/scripts/cli/test_firewall.py
@@ -27,6 +27,7 @@ from vyos.utils.process import run
sysfs_config = {
'all_ping': {'sysfs': '/proc/sys/net/ipv4/icmp_echo_ignore_all', 'default': '0', 'test_value': 'disable'},
'broadcast_ping': {'sysfs': '/proc/sys/net/ipv4/icmp_echo_ignore_broadcasts', 'default': '1', 'test_value': 'enable'},
+ 'directed_broadcast': {'sysfs': '/proc/sys/net/ipv4/conf/all/bc_forwarding', 'default': '1', 'test_value': 'disable'},
'ip_src_route': {'sysfs': '/proc/sys/net/ipv4/conf/*/accept_source_route', 'default': '0', 'test_value': 'enable'},
'ipv6_receive_redirects': {'sysfs': '/proc/sys/net/ipv6/conf/*/accept_redirects', 'default': '0', 'test_value': 'enable'},
'ipv6_src_route': {'sysfs': '/proc/sys/net/ipv6/conf/*/accept_source_route', 'default': '-1', 'test_value': 'enable'},
diff --git a/smoketest/scripts/cli/test_high-availability_virtual-server.py b/smoketest/scripts/cli/test_high-availability_virtual-server.py
index 51ccfa4df..2dbf4a5f2 100755
--- a/smoketest/scripts/cli/test_high-availability_virtual-server.py
+++ b/smoketest/scripts/cli/test_high-availability_virtual-server.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2021-2022 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
@@ -18,12 +18,9 @@ import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
-from vyos.configsession import ConfigSessionError
from vyos.ifconfig.vrrp import VRRP
-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
PROCESS_NAME = 'keepalived'
KEEPALIVED_CONF = VRRP.location['config']
diff --git a/smoketest/scripts/cli/test_high-availability_vrrp.py b/smoketest/scripts/cli/test_high-availability_vrrp.py
index 9ba06aef6..aa9fa432e 100755
--- a/smoketest/scripts/cli/test_high-availability_vrrp.py
+++ b/smoketest/scripts/cli/test_high-availability_vrrp.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2021 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
@@ -22,7 +22,6 @@ from vyos.configsession import ConfigSessionError
from vyos.ifconfig.vrrp import VRRP
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
PROCESS_NAME = 'keepalived'
diff --git a/smoketest/scripts/cli/test_interfaces_macsec.py b/smoketest/scripts/cli/test_interfaces_macsec.py
index d8d564792..a4e6840ca 100755
--- a/smoketest/scripts/cli/test_interfaces_macsec.py
+++ b/smoketest/scripts/cli/test_interfaces_macsec.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2020-2023 VyOS maintainers and contributors
+# Copyright (C) 2020-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
@@ -18,14 +18,12 @@ import re
import unittest
from base_interfaces_test import BasicInterfaceTest
-from netifaces import interfaces
from vyos.configsession import ConfigSessionError
from vyos.ifconfig import Section
from vyos.utils.file import read_file
from vyos.utils.network import get_interface_config
from vyos.utils.network import interface_exists
-from vyos.utils.process import cmd
from vyos.utils.process import process_named_running
PROCESS_NAME = 'wpa_supplicant'
diff --git a/smoketest/scripts/cli/test_interfaces_openvpn.py b/smoketest/scripts/cli/test_interfaces_openvpn.py
index 66c348976..e1e9a4ec7 100755
--- a/smoketest/scripts/cli/test_interfaces_openvpn.py
+++ b/smoketest/scripts/cli/test_interfaces_openvpn.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2020-2023 VyOS maintainers and contributors
+# Copyright (C) 2020-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
@@ -28,7 +28,6 @@ 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 address_from_cidr
-from vyos.template import dec_ip
from vyos.template import inc_ip
from vyos.template import last_host_address
from vyos.template import netmask_from_cidr
diff --git a/smoketest/scripts/cli/test_interfaces_pppoe.py b/smoketest/scripts/cli/test_interfaces_pppoe.py
index e99d8b3d1..2683a3122 100755
--- a/smoketest/scripts/cli/test_interfaces_pppoe.py
+++ b/smoketest/scripts/cli/test_interfaces_pppoe.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2019-2023 VyOS maintainers and contributors
+# Copyright (C) 2019-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
@@ -20,6 +20,7 @@ from psutil import process_iter
from base_vyostest_shim import VyOSUnitTestSHIM
from vyos.configsession import ConfigSessionError
+from vyos.xml_ref import default_value
config_file = '/etc/ppp/peers/{}'
base_path = ['interfaces', 'pppoe']
@@ -169,10 +170,10 @@ class PPPoEInterfaceTest(VyOSUnitTestSHIM.TestCase):
for interface in self._interfaces:
user = f'VyOS-user-{interface}'
passwd = f'VyOS-passwd-{interface}'
+ mtu_default = default_value(base_path + [interface, 'mtu'])
- # verify "normal" PPPoE value - 1492 is default MTU
tmp = get_config_value(interface, 'mtu')[1]
- self.assertEqual(tmp, '1492')
+ self.assertEqual(tmp, mtu_default)
tmp = get_config_value(interface, 'user')[1].replace('"', '')
self.assertEqual(tmp, user)
tmp = get_config_value(interface, 'password')[1].replace('"', '')
diff --git a/smoketest/scripts/cli/test_interfaces_virtual-ethernet.py b/smoketest/scripts/cli/test_interfaces_virtual-ethernet.py
index 7874589ca..c6a4613a7 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 VyOS maintainers and contributors
+# Copyright (C) 2023-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
@@ -18,7 +18,6 @@ import unittest
from netifaces import interfaces
-from vyos.ifconfig import Section
from vyos.utils.process import process_named_running
from base_interfaces_test import BasicInterfaceTest
diff --git a/smoketest/scripts/cli/test_interfaces_wireless.py b/smoketest/scripts/cli/test_interfaces_wireless.py
index 95246a7b9..83b00ac0c 100755
--- a/smoketest/scripts/cli/test_interfaces_wireless.py
+++ b/smoketest/scripts/cli/test_interfaces_wireless.py
@@ -25,6 +25,7 @@ from vyos.configsession import ConfigSessionError
from vyos.utils.process import process_named_running
from vyos.utils.kernel import check_kmod
from vyos.utils.file import read_file
+from vyos.xml_ref import default_value
def get_config_value(interface, key):
tmp = read_file(f'/run/hostapd/{interface}.conf')
@@ -127,7 +128,8 @@ class WirelessInterfaceTest(BasicInterfaceTest.TestCase):
# channel
tmp = get_config_value(interface, 'channel')
- self.assertEqual('0', tmp) # default is channel 0
+ cli_default = default_value(self._base_path + [interface, 'channel'])
+ self.assertEqual(cli_default, tmp)
# auto-powersave is special
tmp = get_config_value(interface, 'uapsd_advertisement_enabled')
diff --git a/smoketest/scripts/cli/test_load-balancing_reverse-proxy.py b/smoketest/scripts/cli/test_load-balancing_reverse-proxy.py
index 274b97f22..97304da8b 100755
--- a/smoketest/scripts/cli/test_load-balancing_reverse-proxy.py
+++ b/smoketest/scripts/cli/test_load-balancing_reverse-proxy.py
@@ -27,6 +27,110 @@ HAPROXY_CONF = '/run/haproxy/haproxy.cfg'
base_path = ['load-balancing', 'reverse-proxy']
proxy_interface = 'eth1'
+valid_ca_cert = """
+MIIDnTCCAoWgAwIBAgIUewSDtLiZbhg1YEslMnqRl1shoPcwDQYJKoZIhvcNAQEL
+BQAwVzELMAkGA1UEBhMCR0IxEzARBgNVBAgMClNvbWUtU3RhdGUxEjAQBgNVBAcM
+CVNvbWUtQ2l0eTENMAsGA1UECgwEVnlPUzEQMA4GA1UEAwwHdnlvcy5pbzAeFw0y
+NDA0MDEwNTQ3MzJaFw0yOTAzMzEwNTQ3MzJaMFcxCzAJBgNVBAYTAkdCMRMwEQYD
+VQQIDApTb21lLVN0YXRlMRIwEAYDVQQHDAlTb21lLUNpdHkxDTALBgNVBAoMBFZ5
+T1MxEDAOBgNVBAMMB3Z5b3MuaW8wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
+AoIBAQC/D6W27rfpdPIf16JHs8fx/7VehyCk8m03dPAQqv6wQiHF5xhXaFZER1+c
+nf7oExp9zi/4HJ/KRbcc1loVArXtV0zwAUftBmUeezGVfxhCHKhP89GnV4NB97jj
+klHFSxjEoT/0YvJQ1IV/3Cos1T5O8x14WIi31l7WQGYAyWxUXiP8QxGVmF3odEJo
+O3e7Ew9HFkamvuL6Z6c4uAVMM7uYXme7q0OM49Wu7C9hj39ZKbjG5FFKZTj+zDKg
+SbOiQaFk3blOky/e3ifNjZelGtussYPOMBkUirLvrSGGy7s3lm8Yp5PH5+UkVQB2
+rZyxRdZTC9kh+dShR1s/qcPnDw7lAgMBAAGjYTBfMA8GA1UdEwEB/wQFMAMBAf8w
+DgYDVR0PAQH/BAQDAgGGMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcDATAd
+BgNVHQ4EFgQU/HE2UPn8JQB/9EL52GquPxZqr5MwDQYJKoZIhvcNAQELBQADggEB
+AIkMmqyoMqidTa3lvUPJNl4H+Ef/yPQkTkrsOd3WL8DQysyUdMLdQozr3K1bH5XB
+wRxoXX211nu4WhN18LsFJRCuHBSxmaNkBGFyl+JNvhPUSI6j0somNMCS75KJ0ZDx
+2HZsXmmJFF902VQxCR7vCIrFDrKDYq1e7GQbFS8t46FlpqivQMQWNPt18Bthj/1Y
+lO2GKRWFCX8VlOW7FtDQ6B3oC1oAGHBBGogAx7/0gh9DnYBKT14V/kuWW3RNABZJ
+ewHO1C6icQdnjtaREDyTP4oyL+uyAfXrFfbpti2hc00f8oYPQZYxj1yxl4UAdNij
+mS6YqH/WRioGMe3tBVeSdoo=
+"""
+
+valid_ca_private_key = """
+MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC/D6W27rfpdPIf
+16JHs8fx/7VehyCk8m03dPAQqv6wQiHF5xhXaFZER1+cnf7oExp9zi/4HJ/KRbcc
+1loVArXtV0zwAUftBmUeezGVfxhCHKhP89GnV4NB97jjklHFSxjEoT/0YvJQ1IV/
+3Cos1T5O8x14WIi31l7WQGYAyWxUXiP8QxGVmF3odEJoO3e7Ew9HFkamvuL6Z6c4
+uAVMM7uYXme7q0OM49Wu7C9hj39ZKbjG5FFKZTj+zDKgSbOiQaFk3blOky/e3ifN
+jZelGtussYPOMBkUirLvrSGGy7s3lm8Yp5PH5+UkVQB2rZyxRdZTC9kh+dShR1s/
+qcPnDw7lAgMBAAECggEAGm+j0kf9koPn7Jf9kEZD6CwlgEraLXiNvBqmDOhcDS9Z
+VPTA3XdGWHQ3uofx+VKLW9TntkDfqzEyQP83v6h8W7a0opDKzvUPkMQi/Dh1ttAY
+SdfGrozhUINiRbq9LbtSVgKpwrreJGkDf8mK3GE1Gd9xuHEnmahDvwlyE7HLF3Eh
+2xJDSAPx3OxcjR5hW7vbojhVCyCfuYTlZB86f0Sb8SqxZMt/y2zKmbzoTqpUBWbg
+lBnE7GJoNR07DWjxvEP8r6kQMh670I01SUR42CSK8X8asHhhZHUcggsNno+BBc6K
+sy4HzDIYIay6oy0atcVzKsGrlNCveeAiSEcw7x2yAQKBgQDsXz2FbhXYV5Vbt4wU
+5EWOa7if/+FG+TcVezOF3xlNBgykjXHQaYTYHrJq0qsEFrNT3ZGm9ezY4LdF3BTt
+5z/+i8QlCCw/nr3N7JZx6U5+OJl1j3NLFoFx3+DXo31pgJJEQCHHwdCkF5IuOcZ/
+b3nXkRZ80BVv7XD6F9bMHEwLYQKBgQDO7THcRDbsE6/+7VsTDf0P/JENba3DBBu1
+gjb1ItL5FHJwMgnkUadRZRo0QKye848ugribed39qSoJfNaBJrAT5T8S/9q+lXft
+vXUckcBO1CKNaP9gqF5fPIdNHf64GbmCiiHjOTE3rwJjkxJPpzLXyvgBO4aLeesK
+ThBdW+iWBQKBgD3crz08knsMcQqP/xl4pLuhdbBqR4tLrh7xH4rp2LVP3/8xBZiG
+BT6Kyicq+5cWWdiZJIWN127rYQvnjZK18wmriqomeW4tHX/Ha5hkdyaRqZga8xGz
+0iz7at0E7M2v2JgEMNMW5oQLpzZx6IFxq3G/hyMjUnj4q5jIpG7G+SABAoGBAKgT
+8Ika+4WcpDssrup2VVTT8Tp4GUkroBo6D8vkInvhiObrLi+/x2mM9tD0q4JdEbNU
+yQC454EwFA4q0c2MED/I2QfkvNhLbmO0nVi8ZvlgxEQawjzP5f/zmW8haxI9Cvsm
+mkoH3Zt+UzFwd9ItXFX97p6JrErEmA8Bw7chfXXFAoGACWR/c+s7hnX6gzyah3N1
+Db0xAaS6M9fzogcg2OM1i/6OCOcp4Sh1fmPG7tN45CCnFkhgVoRkSSA5MJAe2I/r
+xFm72VX7567T+4qIFua2iDxIBA/Z4zmj+RYfhHGPYZjdSjprKJxY6QOv5aoluBvE
+mlLy1Hmcry+ukWZtWezZfGY=
+"""
+
+valid_cert = """
+MIIDsTCCApmgAwIBAgIUDKOfYIwwtjww0vAMvJnXnGLhL+0wDQYJKoZIhvcNAQEL
+BQAwVzELMAkGA1UEBhMCR0IxEzARBgNVBAgMClNvbWUtU3RhdGUxEjAQBgNVBAcM
+CVNvbWUtQ2l0eTENMAsGA1UECgwEVnlPUzEQMA4GA1UEAwwHdnlvcy5pbzAeFw0y
+NDA0MDEwNTQ5NTdaFw0yNTA0MDEwNTQ5NTdaMFcxCzAJBgNVBAYTAkdCMRMwEQYD
+VQQIDApTb21lLVN0YXRlMRIwEAYDVQQHDAlTb21lLUNpdHkxDTALBgNVBAoMBFZ5
+T1MxEDAOBgNVBAMMB3Z5b3MuaW8wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
+AoIBAQCHtW25Umt6rqm2gfzqAZg1/VsqefZwAqIUAm2T3VwHQZ/2tNdr8ROWASii
+W5PToC7N8StMwFl2YoIof+MXGMO00toTTJePZOJKjF9U9hL3kuYuY1+yng4fl+E0
+96xVobb2KY4lMZ2rVwmpB7jkNO2LWxbJ6vHKcwMOhlx/8NEKIoVmkBT1Zkgy5dgn
+PgTtJcdVIU75XhQWqBmAUsMmACuZfqSYJbAv3hHz5V+Ejt0dI6mlGM7TXsCC9tKM
+64paIKZooFm78IsxJ26jHpZ8eh+SDBz0VBydBFWXm8VhOJ8NlZ1opAh3AWxFZDGt
+49uOsy82VmUcHPyoZ8DKYkBFHfSpAgMBAAGjdTBzMAwGA1UdEwEB/wQCMAAwDgYD
+VR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMBMB0GA1UdDgQWBBTeTcgM
+pRxAMjVBirjzo2QUu5H5fzAfBgNVHSMEGDAWgBT8cTZQ+fwlAH/0QvnYaq4/Fmqv
+kzANBgkqhkiG9w0BAQsFAAOCAQEAi4dBcH7TIYwWRW6bWRubMA7ztonV4EYb15Zf
+9yNafMWAEEBOii/DFo+j/ky9oInl7ZHw7gTIyXfLEarX/bM6fHOgiyj4zp3u6RnH
+5qlBypu/YCnyPjE/GvV05m2rrXnxZ4rCtcoO4u/HyGbV+jGnCmjShKICKyu1FdMd
+eeZRrLKPO/yghadGH34WVQnrbaorwlbi+NjB6fxmZQx5HE/SyK/9sb6WCpLMGHoy
+MpdQo3lV1ewtL3ElIWDq6mO030Mo5pwpjIU+8yHHNBVzg6mlGVgQPAp0gbUei9aP
+CJ8SLmMEi3NDk0E/sPgVC17e6bf2bx2nRuXROZekG2dd90Iu8g==
+"""
+
+valid_cert_private_key = """
+MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCHtW25Umt6rqm2
+gfzqAZg1/VsqefZwAqIUAm2T3VwHQZ/2tNdr8ROWASiiW5PToC7N8StMwFl2YoIo
+f+MXGMO00toTTJePZOJKjF9U9hL3kuYuY1+yng4fl+E096xVobb2KY4lMZ2rVwmp
+B7jkNO2LWxbJ6vHKcwMOhlx/8NEKIoVmkBT1Zkgy5dgnPgTtJcdVIU75XhQWqBmA
+UsMmACuZfqSYJbAv3hHz5V+Ejt0dI6mlGM7TXsCC9tKM64paIKZooFm78IsxJ26j
+HpZ8eh+SDBz0VBydBFWXm8VhOJ8NlZ1opAh3AWxFZDGt49uOsy82VmUcHPyoZ8DK
+YkBFHfSpAgMBAAECggEABofhw0W/ACEMcAjmpNTFkFCUXPGQXWDVD7EzuIZSNdOv
+yOm4Rbys6H6/B7wwO6KVagoBf1Cw5Xh1YtFPuoZxsZ+liMD6eLc+SB/j/RTYAhPO
+0bvsyK3gSF8w4nGKWLce9M74ZRwThkG6qGijmlDdPyP3r2kn8GoTQzVOWYZbavk/
+H3uE6PsZSWjOY+Mnm3vEmeItPYKGZ5+IP+YiTqZ4NCggBwH7csnR3/kbwY5Ns7jl
+3Av+EAdIeUwDNeMfLTzN7GphJR7gL6YQIhGKxE+W0GHXL2FubnnrFx8G75HFh1ay
+GkJXEqY5Lbd+7VPS0KcQdwhMSSoJsY5GUORUqrU80QKBgQC/0wJSu+Gfe7dONIby
+mnGRppSRIQVRjCjbVIN+Y2h1Kp3aK0qDpV7KFLCiUUtz9rWHR/NB4cDaIW543T55
+/jXUMD2j3EqtbtlsVQfDLQV7DyDrMmBAs4REHmyZmWTzHjCDUO79ahdOlZs34Alz
+wfpX3L3WVYGIAJKZtsUZ8FbrGQKBgQC1HFgVZ1PqP9/pW50RMh06BbQrhWPGiWgH
+Rn5bFthLkp3uqr9bReBq9tu3sqJuAhFudH68wup+Z+fTcHAcNg2Rs+Q+IKnULdB/
+UQHYoPjeWOvHAuOmgn9iD9OD7GCIv8fZmLit09vAsOWq+NKNBKCknGM70CDrvAlQ
+lOAUa34YEQKBgQC5i8GThWiYe3Kzktt1jy6LVDYgq3AZkRl0Diui9UT1EGPfxEAv
+VqZ5kcnJOBlj8h9k25PRBi0k0XGqN1dXaS1oMcFt3ofdenuU7iqz/7htcBTHa9Lu
+wrYNreAeMuISyADlBEQnm5cvzEZ3pZ1++wLMOhjmWY8Rnnwvczrz/CYXAQKBgH+t
+vcNJFvWblkUzWuWWiNgw0TWlUhPTJs2KOuYIku+kK0bohQLZnj6KTZeRjcU0HAnc
+gsScPShkJCEBsWeSC7reMVhDOrbknYpEF6MayJgn5ABm3wqyEQ+WzKzCZcPCQCf8
+7KVPKCsOCrufsv/LdVzXC3ZNYggOhhqS+e4rYbehAoGBAIsq252o3vgrunzS5FZx
+IONA2FvYrxVbDn5aF8WfNSdKFy3CAlt0P+Fm8gYbrKylIfMXpL8Oqc9RJou5onZP
+ZXLrtgVJR9W020qTurO2f91qfU8646n11hR9ObBB1IYbagOU0Pw1Nrq/FRp/u2tx
+7i7xFz2WEiQeSCPaKYOiqM3t
+"""
+
class TestLoadBalancingReverseProxy(VyOSUnitTestSHIM.TestCase):
def tearDown(self):
@@ -35,11 +139,34 @@ class TestLoadBalancingReverseProxy(VyOSUnitTestSHIM.TestCase):
self.cli_delete(['interfaces', 'ethernet', proxy_interface, 'address'])
self.cli_delete(base_path)
+ self.cli_delete(['pki'])
self.cli_commit()
# Process must be terminated after deleting the config
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 + ['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 + ['global-parameters', 'max-connections', '1000'])
+
+ def configure_pki(self):
+
+ # Valid CA
+ self.cli_set(['pki', 'ca', 'smoketest', 'certificate', valid_ca_cert.replace('\n','')])
+ self.cli_set(['pki', 'ca', 'smoketest', 'private', 'key', valid_ca_private_key.replace('\n','')])
+
+ # Valid cert
+ 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):
domains_bk_first = ['n1.example.com', 'n2.example.com', 'n3.example.com']
domain_bk_second = 'n5.example.com'
@@ -109,6 +236,50 @@ 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):
+ self.base_config()
+ self.cli_set(base_path + ['service', 'https_front', 'ssl', 'certificate', 'cert'])
+
+ with self.assertRaises(ConfigSessionError) as e:
+ self.cli_commit()
+ # self.assertIn('\nCertificates does not exist in PKI\n', str(e.exception))
+
+ self.cli_delete(base_path)
+ self.configure_pki()
+
+ self.base_config()
+ self.cli_set(base_path + ['service', 'https_front', '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_commit()
+
+ def test_03_lb_reverse_proxy_ca_not_exists(self):
+ self.base_config()
+ self.cli_set(base_path + ['backend', 'bk-01', 'ssl', 'ca-certificate', 'ca-test'])
+
+ with self.assertRaises(ConfigSessionError) as e:
+ self.cli_commit()
+ # self.assertIn('\nCA certificates does not exist in PKI\n', str(e.exception))
+
+ self.cli_delete(base_path)
+ self.configure_pki()
+
+ self.base_config()
+ self.cli_set(base_path + ['backend', 'bk-01', '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_commit()
+
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 47ca19b27..92b4000b8 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-2023 VyOS maintainers and contributors
+# Copyright (C) 2022-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
@@ -14,20 +14,15 @@
# 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.configsession import ConfigSessionError
-from vyos.ifconfig import Section
from vyos.utils.process import call
from vyos.utils.process import cmd
-
base_path = ['load-balancing']
-
def create_netns(name):
return call(f'sudo ip netns add {name}')
diff --git a/smoketest/scripts/cli/test_nat.py b/smoketest/scripts/cli/test_nat.py
index 43e374398..5161e47fd 100755
--- a/smoketest/scripts/cli/test_nat.py
+++ b/smoketest/scripts/cli/test_nat.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2020-2022 VyOS maintainers and contributors
+# Copyright (C) 2020-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
@@ -14,8 +14,6 @@
# 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 jmespath
-import json
import os
import unittest
diff --git a/smoketest/scripts/cli/test_nat64.py b/smoketest/scripts/cli/test_nat64.py
index b5723ac7e..5c907f6cb 100755
--- a/smoketest/scripts/cli/test_nat64.py
+++ b/smoketest/scripts/cli/test_nat64.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2023 VyOS maintainers and contributors
+# Copyright (C) 2023-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
@@ -19,16 +19,12 @@ import os
import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
-from vyos.configsession import ConfigSessionError
-from vyos.utils.process import cmd
-from vyos.utils.dict import dict_search
base_path = ['nat64']
src_path = base_path + ['source']
jool_nat64_config = '/run/jool/instance-100.json'
-
class TestNAT64(VyOSUnitTestSHIM.TestCase):
@classmethod
def setUpClass(cls):
diff --git a/smoketest/scripts/cli/test_nat66.py b/smoketest/scripts/cli/test_nat66.py
index 400a895ff..e8eeae26f 100755
--- a/smoketest/scripts/cli/test_nat66.py
+++ b/smoketest/scripts/cli/test_nat66.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2020 VyOS maintainers and contributors
+# Copyright (C) 2020-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
@@ -14,9 +14,6 @@
# 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 jmespath
-import json
import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
diff --git a/smoketest/scripts/cli/test_netns.py b/smoketest/scripts/cli/test_netns.py
index fd04dd520..2ac603a69 100755
--- a/smoketest/scripts/cli/test_netns.py
+++ b/smoketest/scripts/cli/test_netns.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
@@ -18,10 +18,6 @@ import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
-from vyos.configsession import ConfigSession
-from vyos.configsession import ConfigSessionError
-from vyos.ifconfig import Interface
-from vyos.ifconfig import Section
from vyos.utils.process import cmd
from vyos.utils.network import is_netns_interface
from vyos.utils.network import get_netns_all
diff --git a/smoketest/scripts/cli/test_protocols_bgp.py b/smoketest/scripts/cli/test_protocols_bgp.py
index 5f238b25a..03daa34aa 100755
--- a/smoketest/scripts/cli/test_protocols_bgp.py
+++ b/smoketest/scripts/cli/test_protocols_bgp.py
@@ -321,6 +321,7 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
tcp_keepalive_probes = '22'
self.cli_set(base_path + ['parameters', 'allow-martian-nexthop'])
+ self.cli_set(base_path + ['parameters', 'disable-ebgp-connected-route-check'])
self.cli_set(base_path + ['parameters', 'no-hard-administrative-reset'])
self.cli_set(base_path + ['parameters', 'log-neighbor-changes'])
self.cli_set(base_path + ['parameters', 'labeled-unicast', 'explicit-null'])
@@ -372,6 +373,7 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
self.assertIn(f'router bgp {ASN}', frrconfig)
self.assertIn(f' bgp router-id {router_id}', frrconfig)
self.assertIn(f' bgp allow-martian-nexthop', frrconfig)
+ self.assertIn(f' bgp disable-ebgp-connected-route-check', frrconfig)
self.assertIn(f' bgp log-neighbor-changes', frrconfig)
self.assertIn(f' bgp default local-preference {local_pref}', frrconfig)
self.assertIn(f' bgp conditional-advertisement timer {cond_adv_timer}', frrconfig)
@@ -628,6 +630,8 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
networks = {
'10.0.0.0/8' : {
'as_set' : '',
+ 'summary_only' : '',
+ 'route_map' : route_map_in,
},
'100.64.0.0/10' : {
'as_set' : '',
@@ -652,6 +656,9 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
if 'summary_only' in network_config:
self.cli_set(base_path + ['address-family', 'ipv4-unicast',
'aggregate-address', network, 'summary-only'])
+ if 'route_map' in network_config:
+ self.cli_set(base_path + ['address-family', 'ipv4-unicast',
+ 'aggregate-address', network, 'route-map', network_config['route_map']])
# commit changes
self.cli_commit()
@@ -666,10 +673,14 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
for network, network_config in networks.items():
self.assertIn(f' network {network}', frrconfig)
+ command = f'aggregate-address {network}'
if 'as_set' in network_config:
- self.assertIn(f' aggregate-address {network} as-set', frrconfig)
+ command = f'{command} as-set'
if 'summary_only' in network_config:
- self.assertIn(f' aggregate-address {network} summary-only', frrconfig)
+ command = f'{command} summary-only'
+ if 'route_map' in network_config:
+ command = f'{command} route-map {network_config["route_map"]}'
+ self.assertIn(command, frrconfig)
def test_bgp_05_afi_ipv6(self):
networks = {
@@ -1241,6 +1252,84 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
with self.assertRaises(ConfigSessionError) as e:
self.cli_commit()
+ self.cli_set(base_path + ['peer-group', 'peer1', 'remote-as', 'internal'])
+ self.cli_commit()
+
+ conf = self.getFRRconfig(' address-family l2vpn evpn')
+
+ self.assertIn('neighbor peer1 route-reflector-client', conf)
+
+ def test_bgp_28_peer_group_member_all_internal_or_external(self):
+ def _common_config_check(conf, include_ras=True):
+ if include_ras:
+ self.assertIn(f'neighbor {int_neighbors[0]} remote-as {ASN}', conf)
+ self.assertIn(f'neighbor {int_neighbors[1]} remote-as {ASN}', conf)
+ self.assertIn(f'neighbor {ext_neighbors[0]} remote-as {int(ASN) + 1}',conf)
+
+ self.assertIn(f'neighbor {int_neighbors[0]} peer-group {int_pg_name}', conf)
+ self.assertIn(f'neighbor {int_neighbors[1]} peer-group {int_pg_name}', conf)
+ self.assertIn(f'neighbor {ext_neighbors[0]} peer-group {ext_pg_name}', conf)
+
+ int_neighbors = ['192.0.2.2', '192.0.2.3']
+ ext_neighbors = ['192.122.2.2', '192.122.2.3']
+ int_pg_name, ext_pg_name = 'SMOKETESTINT', 'SMOKETESTEXT'
+
+ self.cli_set(base_path + ['neighbor', int_neighbors[0], 'peer-group', int_pg_name])
+ self.cli_set(base_path + ['neighbor', int_neighbors[0], 'remote-as', ASN])
+ self.cli_set(base_path + ['peer-group', int_pg_name, 'address-family', 'ipv4-unicast'])
+ self.cli_set(base_path + ['neighbor', ext_neighbors[0], 'peer-group', ext_pg_name])
+ self.cli_set(base_path + ['neighbor', ext_neighbors[0], 'remote-as', f'{int(ASN) + 1}'])
+ self.cli_set(base_path + ['peer-group', ext_pg_name, 'address-family', 'ipv4-unicast'])
+ self.cli_commit()
+
+ # test add external remote-as to internal group
+ self.cli_set(base_path + ['neighbor', int_neighbors[1], 'peer-group', int_pg_name])
+ self.cli_set(base_path + ['neighbor', int_neighbors[1], 'remote-as', f'{int(ASN) + 1}'])
+
+ with self.assertRaises(ConfigSessionError) as e:
+ self.cli_commit()
+ # self.assertIn('\nPeer-group members must be all internal or all external\n', str(e.exception))
+
+ # test add internal remote-as to internal group
+ self.cli_set(base_path + ['neighbor', int_neighbors[1], 'remote-as', ASN])
+ self.cli_commit()
+
+ conf = self.getFRRconfig(f'router bgp {ASN}')
+ _common_config_check(conf)
+
+ # test add internal remote-as to external group
+ self.cli_set(base_path + ['neighbor', ext_neighbors[1], 'peer-group', ext_pg_name])
+ self.cli_set(base_path + ['neighbor', ext_neighbors[1], 'remote-as', ASN])
+
+ with self.assertRaises(ConfigSessionError) as e:
+ self.cli_commit()
+ # self.assertIn('\nPeer-group members must be all internal or all external\n', str(e.exception))
+
+ # test add external remote-as to external group
+ self.cli_set(base_path + ['neighbor', ext_neighbors[1], 'remote-as', f'{int(ASN) + 2}'])
+ self.cli_commit()
+
+ conf = self.getFRRconfig(f'router bgp {ASN}')
+ _common_config_check(conf)
+ self.assertIn(f'neighbor {ext_neighbors[1]} remote-as {int(ASN) + 2}', conf)
+ self.assertIn(f'neighbor {ext_neighbors[1]} peer-group {ext_pg_name}', conf)
+
+ # test named remote-as
+ self.cli_set(base_path + ['neighbor', int_neighbors[0], 'remote-as', 'internal'])
+ self.cli_set(base_path + ['neighbor', int_neighbors[1], 'remote-as', 'internal'])
+ self.cli_set(base_path + ['neighbor', ext_neighbors[0], 'remote-as', 'external'])
+ self.cli_set(base_path + ['neighbor', ext_neighbors[1], 'remote-as', 'external'])
+ self.cli_commit()
+
+ conf = self.getFRRconfig(f'router bgp {ASN}')
+ _common_config_check(conf, include_ras=False)
+
+ self.assertIn(f'neighbor {int_neighbors[0]} remote-as internal', conf)
+ self.assertIn(f'neighbor {int_neighbors[1]} remote-as internal', conf)
+ self.assertIn(f'neighbor {ext_neighbors[0]} remote-as external', conf)
+ self.assertIn(f'neighbor {ext_neighbors[1]} remote-as external', conf)
+ self.assertIn(f'neighbor {ext_neighbors[1]} peer-group {ext_pg_name}', conf)
+
def test_bgp_99_bmp(self):
target_name = 'instance-bmp'
target_address = '127.0.0.1'
diff --git a/smoketest/scripts/cli/test_protocols_nhrp.py b/smoketest/scripts/cli/test_protocols_nhrp.py
index 45ef539f6..43ae4abf2 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-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
@@ -21,7 +21,6 @@ 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
-from vyos.utils.process import call
tunnel_path = ['interfaces', 'tunnel']
nhrp_path = ['protocols', 'nhrp']
diff --git a/smoketest/scripts/cli/test_protocols_segment-routing.py b/smoketest/scripts/cli/test_protocols_segment-routing.py
index 403c05924..daa7f088f 100755
--- a/smoketest/scripts/cli/test_protocols_segment-routing.py
+++ b/smoketest/scripts/cli/test_protocols_segment-routing.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2023 VyOS maintainers and contributors
+# Copyright (C) 2023-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
@@ -14,14 +14,12 @@
# 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.ifconfig import Section
-from vyos.utils.process import cmd
from vyos.utils.process import process_named_running
from vyos.utils.system import sysctl_read
diff --git a/smoketest/scripts/cli/test_qos.py b/smoketest/scripts/cli/test_qos.py
index 46ef68b1d..4f41e36cd 100755
--- a/smoketest/scripts/cli/test_qos.py
+++ b/smoketest/scripts/cli/test_qos.py
@@ -559,7 +559,6 @@ class TestQoS(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
for interface in self._interfaces:
- import pprint
tmp = get_tc_qdisc_json(interface)
self.assertEqual('drr', tmp['kind'])
@@ -582,7 +581,6 @@ class TestQoS(VyOSUnitTestSHIM.TestCase):
dport = int(match_config['dport'])
self.assertEqual(f'{dport:x}', filter['options']['match']['value'])
-
def test_11_shaper(self):
bandwidth = 250
default_bandwidth = 20
@@ -636,6 +634,69 @@ class TestQoS(VyOSUnitTestSHIM.TestCase):
class_bandwidth += 1
class_ceil += 1
+ def test_12_shaper_with_red_queue(self):
+ bandwidth = 100
+ default_bandwidth = 100
+ default_burst = 100
+ interface = self._interfaces[0]
+ class_bandwidth = 50
+ dst_address = '192.0.2.8/32'
+
+ shaper_name = f'qos-shaper-{interface}'
+ self.cli_set(base_path + ['interface', interface, 'egress', shaper_name])
+ self.cli_set(base_path + ['policy', 'shaper', shaper_name, 'bandwidth', f'{bandwidth}mbit'])
+ self.cli_set(base_path + ['policy', 'shaper', shaper_name, 'default', 'bandwidth', f'{default_bandwidth}%'])
+ self.cli_set(base_path + ['policy', 'shaper', shaper_name, 'default', 'burst', f'{default_burst}'])
+ self.cli_set(base_path + ['policy', 'shaper', shaper_name, 'default', 'queue-type', 'random-detect'])
+
+ self.cli_set(base_path + ['policy', 'shaper', shaper_name, 'class', '2', 'bandwidth', f'{class_bandwidth}mbit'])
+ self.cli_set(base_path + ['policy', 'shaper', shaper_name, 'class', '2', 'match', '10', 'ip', 'destination', 'address', dst_address])
+ self.cli_set(base_path + ['policy', 'shaper', shaper_name, 'class', '2', 'queue-type', 'random-detect'])
+
+ # commit changes
+ self.cli_commit()
+
+ # check root htb config
+ output = cmd(f'tc class show dev {interface}')
+
+ config_entries = (
+ f'prio 0 rate {class_bandwidth}Mbit ceil 50Mbit burst 15Kb', # specified class
+ f'prio 7 rate {default_bandwidth}Mbit ceil 100Mbit burst {default_burst}b', # default class
+ )
+ for config_entry in config_entries:
+ self.assertIn(config_entry, output)
+
+ output = cmd(f'tc -d qdisc show dev {interface}')
+ config_entries = (
+ 'qdisc red', # use random detect
+ 'limit 72Kb min 9Kb max 18Kb ewma 3 probability 0.1', # default config for random detect
+ )
+ for config_entry in config_entries:
+ self.assertIn(config_entry, output)
+
+ # test random detect queue params
+ self.cli_set(base_path + ['policy', 'shaper', shaper_name, 'default', 'queue-limit', '1024'])
+ self.cli_set(base_path + ['policy', 'shaper', shaper_name, 'default', 'average-packet', '1024'])
+ self.cli_set(base_path + ['policy', 'shaper', shaper_name, 'default', 'maximum-threshold', '32'])
+ self.cli_set(base_path + ['policy', 'shaper', shaper_name, 'default', 'minimum-threshold', '16'])
+
+ self.cli_set(base_path + ['policy', 'shaper', shaper_name, 'class', '2', 'queue-limit', '1024'])
+ self.cli_set(base_path + ['policy', 'shaper', shaper_name, 'class', '2', 'average-packet', '512'])
+ self.cli_set(base_path + ['policy', 'shaper', shaper_name, 'class', '2', 'maximum-threshold', '32'])
+ self.cli_set(base_path + ['policy', 'shaper', shaper_name, 'class', '2', 'minimum-threshold', '16'])
+ self.cli_set(base_path + ['policy', 'shaper', shaper_name, 'class', '2', 'mark-probability', '20'])
+
+ self.cli_commit()
+
+ output = cmd(f'tc -d qdisc show dev {interface}')
+ config_entries = (
+ 'qdisc red', # use random detect
+ 'limit 1Mb min 16Kb max 32Kb ewma 3 probability 0.1', # default config for random detect
+ 'limit 512Kb min 8Kb max 16Kb ewma 3 probability 0.05', # class config for random detect
+ )
+ for config_entry in config_entries:
+ self.assertIn(config_entry, output)
+
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 24bd14af2..46c4e25a1 100755
--- a/smoketest/scripts/cli/test_service_dhcp-server.py
+++ b/smoketest/scripts/cli/test_service_dhcp-server.py
@@ -699,6 +699,7 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase):
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'])
+ ## No mode defined -> its active-active mode by default
# commit changes
self.cli_commit()
@@ -717,7 +718,7 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase):
self.verify_config_object(
obj,
['Dhcp4', 'hooks-libraries', 0, 'parameters', 'high-availability', 0, 'peers'],
- {'name': failover_name, 'url': f'http://{failover_remote}:647/', 'role': 'standby', 'auto-failover': True})
+ {'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)
@@ -738,5 +739,92 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase):
self.assertTrue(process_named_running(PROCESS_NAME))
self.assertTrue(process_named_running(CTRL_PROCESS_NAME))
+ 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)
+
+ pool = base_path + ['shared-network-name', shared_net_name, 'subnet', subnet]
+ self.cli_set(pool + ['subnet-id', '1'])
+ # we use the first subnet IP address as default gateway
+ self.cli_set(pool + ['option', 'default-router', router])
+ self.cli_set(pool + ['range', '0', 'start', range_0_start])
+ self.cli_set(pool + ['range', '0', 'stop', range_0_stop])
+
+ # failover
+ 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', 'name', failover_name])
+ self.cli_set(base_path + ['high-availability', 'remote', failover_remote])
+ self.cli_set(base_path + ['high-availability', 'status', 'secondary'])
+ self.cli_set(base_path + ['high-availability', 'mode', 'active-passive'])
+
+ # commit changes
+ self.cli_commit()
+
+ config = read_file(KEA4_CONF)
+ obj = loads(config)
+
+ # Verify failover
+ 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})
+
+ 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})
+
+ 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.assertTrue(process_named_running(PROCESS_NAME))
+ self.assertTrue(process_named_running(CTRL_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(['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 + ['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.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(['vrf', 'name', 'SMOKE-DHCP'])
+ self.cli_commit()
+
+
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_service_dhcpv6-relay.py b/smoketest/scripts/cli/test_service_dhcpv6-relay.py
index 4487f4b0f..e634a011f 100755
--- a/smoketest/scripts/cli/test_service_dhcpv6-relay.py
+++ b/smoketest/scripts/cli/test_service_dhcpv6-relay.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2020-2023 VyOS maintainers and contributors
+# Copyright (C) 2020-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
@@ -20,7 +20,6 @@ from base_vyostest_shim import VyOSUnitTestSHIM
from vyos.configsession import ConfigSessionError
from vyos.ifconfig import Section
-from vyos.template import address_from_cidr
from vyos.utils.process import process_named_running
from vyos.utils.file import read_file
@@ -109,4 +108,3 @@ class TestServiceDHCPv6Relay(VyOSUnitTestSHIM.TestCase):
if __name__ == '__main__':
unittest.main(verbosity=2)
-
diff --git a/smoketest/scripts/cli/test_service_https.py b/smoketest/scripts/cli/test_service_https.py
index 94eade2d7..f2a64627f 100755
--- a/smoketest/scripts/cli/test_service_https.py
+++ b/smoketest/scripts/cli/test_service_https.py
@@ -27,6 +27,7 @@ from vyos.utils.file import read_file
from vyos.utils.file import write_file
from vyos.utils.process import call
from vyos.utils.process import process_named_running
+from vyos.xml_ref import default_value
from vyos.configsession import ConfigSessionError
@@ -147,10 +148,8 @@ class TestHTTPSService(VyOSUnitTestSHIM.TestCase):
@ignore_warning(InsecureRequestWarning)
def test_api_auth(self):
- vhost_id = 'example'
address = '127.0.0.1'
- port = '443' # default value
- name = 'localhost'
+ port = default_value(base_path + ['port'])
key = 'MySuperSecretVyOS'
self.cli_set(base_path + ['api', 'keys', 'id', 'key-01', 'key', key])
@@ -420,7 +419,6 @@ class TestHTTPSService(VyOSUnitTestSHIM.TestCase):
url = f'https://{address}/config-file'
url_config = f'https://{address}/configure'
headers = {}
- tmp_file = 'tmp-config.boot'
self.cli_set(base_path + ['api', 'keys', 'id', 'key-01', 'key', key])
self.cli_commit()
diff --git a/smoketest/scripts/cli/test_service_lldp.py b/smoketest/scripts/cli/test_service_lldp.py
index 7e30b43f5..9d72ef78f 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-2023 VyOS maintainers and contributors
+# Copyright (C) 2022-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
@@ -14,15 +14,12 @@
# 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.ifconfig import Section
-from vyos.utils.process import cmd
from vyos.utils.process import process_named_running
from vyos.utils.file import read_file
from vyos.version import get_version_data
diff --git a/smoketest/scripts/cli/test_service_monitoring_telegraf.py b/smoketest/scripts/cli/test_service_monitoring_telegraf.py
index f3355b735..3374411f5 100755
--- a/smoketest/scripts/cli/test_service_monitoring_telegraf.py
+++ b/smoketest/scripts/cli/test_service_monitoring_telegraf.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2021-2022 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
@@ -18,8 +18,6 @@ import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
-from vyos.configsession import ConfigSession
-from vyos.configsession import ConfigSessionError
from vyos.utils.process import process_named_running
from vyos.utils.file import read_file
diff --git a/smoketest/scripts/cli/test_service_monitoring_zabbix-agent.py b/smoketest/scripts/cli/test_service_monitoring_zabbix-agent.py
index cb5f84406..a60dae0a0 100755
--- a/smoketest/scripts/cli/test_service_monitoring_zabbix-agent.py
+++ b/smoketest/scripts/cli/test_service_monitoring_zabbix-agent.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2023 VyOS maintainers and contributors
+# Copyright (C) 2023-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
@@ -14,11 +14,9 @@
# 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
diff --git a/smoketest/scripts/cli/test_service_ndp-proxy.py b/smoketest/scripts/cli/test_service_ndp-proxy.py
index a947ec478..dfdb3f6aa 100755
--- a/smoketest/scripts/cli/test_service_ndp-proxy.py
+++ b/smoketest/scripts/cli/test_service_ndp-proxy.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2023 VyOS maintainers and contributors
+# Copyright (C) 2023-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
@@ -18,7 +18,6 @@ import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
-from vyos.configsession import ConfigSessionError
from vyos.ifconfig import Section
from vyos.utils.process import cmd
from vyos.utils.process import process_named_running
diff --git a/smoketest/scripts/cli/test_service_pppoe-server.py b/smoketest/scripts/cli/test_service_pppoe-server.py
index d7c7aa164..5a48b1f58 100755
--- a/smoketest/scripts/cli/test_service_pppoe-server.py
+++ b/smoketest/scripts/cli/test_service_pppoe-server.py
@@ -148,6 +148,28 @@ class TestServicePPPoEServer(BasicAccelPPPTest.TestCase):
tmp = ','.join(vlans)
self.assertIn(f'vlan-mon={interface},{tmp}', config)
+ def test_pppoe_server_pado_delay(self):
+ delay_without_sessions = '10'
+ delays = {'20': '200', '30': '300'}
+
+ self.basic_config()
+
+ self.set(['pado-delay', delay_without_sessions])
+ self.cli_commit()
+
+ conf = ConfigParser(allow_no_value=True, delimiters='=')
+ conf.read(self._config_file)
+ self.assertEqual(conf['pppoe']['pado-delay'], delay_without_sessions)
+
+ for delay, sessions in delays.items():
+ self.set(['pado-delay', delay, 'sessions', sessions])
+ self.cli_commit()
+
+ conf = ConfigParser(allow_no_value=True, delimiters='=')
+ conf.read(self._config_file)
+
+ self.assertEqual(conf['pppoe']['pado-delay'], '10,20:200,-1:300')
+
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_service_ssh.py b/smoketest/scripts/cli/test_service_ssh.py
index 947d7d568..b09990c92 100755
--- a/smoketest/scripts/cli/test_service_ssh.py
+++ b/smoketest/scripts/cli/test_service_ssh.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2019-2022 VyOS maintainers and contributors
+# Copyright (C) 2019-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
@@ -28,11 +28,11 @@ from vyos.utils.process import cmd
from vyos.utils.process import is_systemd_service_running
from vyos.utils.process import process_named_running
from vyos.utils.file import read_file
+from vyos.xml_ref import default_value
PROCESS_NAME = 'sshd'
SSHD_CONF = '/run/sshd/sshd_config'
base_path = ['service', 'ssh']
-vrf = 'mgmt'
key_rsa = '/etc/ssh/ssh_host_rsa_key'
key_dsa = '/etc/ssh/ssh_host_dsa_key'
@@ -51,6 +51,7 @@ class TestServiceSSH(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
@@ -58,6 +59,7 @@ class TestServiceSSH(VyOSUnitTestSHIM.TestCase):
# delete testing SSH config
self.cli_delete(base_path)
+ self.cli_delete(['vrf'])
self.cli_commit()
self.assertTrue(os.path.isfile(key_rsa))
@@ -77,9 +79,10 @@ class TestServiceSSH(VyOSUnitTestSHIM.TestCase):
# commit changes
self.cli_commit()
- # Check configured port
- port = get_config_value('Port')[0]
- self.assertEqual('22', port)
+ # Check configured port agains CLI default value
+ port = get_config_value('Port')
+ cli_default = default_value(base_path + ['port'])
+ self.assertEqual(port, cli_default)
def test_ssh_single_listen_address(self):
# Check if SSH service can be configured and runs
@@ -141,10 +144,9 @@ class TestServiceSSH(VyOSUnitTestSHIM.TestCase):
for address in addresses:
self.assertIn(address, tmp)
- def test_ssh_vrf(self):
+ def test_ssh_vrf_single(self):
+ vrf = 'mgmt'
# Check if SSH service can be bound to given VRF
- port = '22'
- self.cli_set(base_path + ['port', port])
self.cli_set(base_path + ['vrf', vrf])
# VRF does yet not exist - an error must be thrown
@@ -156,16 +158,32 @@ class TestServiceSSH(VyOSUnitTestSHIM.TestCase):
# commit changes
self.cli_commit()
- # Check configured port
- tmp = get_config_value('Port')
- self.assertIn(port, tmp)
-
# Check for process in VRF
tmp = cmd(f'ip vrf pids {vrf}')
self.assertIn(PROCESS_NAME, tmp)
- # delete VRF
- self.cli_delete(['vrf', 'name', vrf])
+ def test_ssh_vrf_multi(self):
+ # Check if SSH service can be bound to multiple VRFs
+ vrfs = ['red', 'blue', 'green']
+ for vrf in vrfs:
+ self.cli_set(base_path + ['vrf', vrf])
+
+ # VRF does yet not exist - an error must be thrown
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+
+ table = 12345
+ for vrf in vrfs:
+ self.cli_set(['vrf', 'name', vrf, 'table', str(table)])
+ table += 1
+
+ # commit changes
+ self.cli_commit()
+
+ # Check for process in VRF
+ for vrf in vrfs:
+ tmp = cmd(f'ip vrf pids {vrf}')
+ self.assertIn(PROCESS_NAME, tmp)
def test_ssh_login(self):
# Perform SSH login and command execution with a predefined user. The
diff --git a/smoketest/scripts/cli/test_service_upnp.py b/smoketest/scripts/cli/test_service_upnp.py
index c3fb0ec9d..fd67b0ced 100755
--- a/smoketest/scripts/cli/test_service_upnp.py
+++ b/smoketest/scripts/cli/test_service_upnp.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2021-2022 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
@@ -14,12 +14,10 @@
# 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 unittest
from base_vyostest_shim import VyOSUnitTestSHIM
-from vyos.configsession import ConfigSession
from vyos.configsession import ConfigSessionError
from vyos.template import ip_from_cidr
from vyos.utils.file import read_file
diff --git a/smoketest/scripts/cli/test_system_conntrack.py b/smoketest/scripts/cli/test_system_conntrack.py
index 2d76da145..c6d8a5436 100755
--- a/smoketest/scripts/cli/test_system_conntrack.py
+++ b/smoketest/scripts/cli/test_system_conntrack.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
@@ -15,7 +15,6 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
-import re
import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
diff --git a/smoketest/scripts/cli/test_system_flow-accounting.py b/smoketest/scripts/cli/test_system_flow-accounting.py
index 6c761579b..515134220 100755
--- a/smoketest/scripts/cli/test_system_flow-accounting.py
+++ b/smoketest/scripts/cli/test_system_flow-accounting.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2021-2022 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
@@ -21,7 +21,6 @@ from base_vyostest_shim import VyOSUnitTestSHIM
from vyos.configsession import ConfigSessionError
from vyos.ifconfig import Section
from vyos.template import bracketize_ipv6
-from vyos.template import is_ipv6
from vyos.utils.process import cmd
from vyos.utils.process import process_named_running
from vyos.utils.file import read_file
diff --git a/smoketest/scripts/cli/test_system_ip.py b/smoketest/scripts/cli/test_system_ip.py
index ac8b74236..5b0090237 100755
--- a/smoketest/scripts/cli/test_system_ip.py
+++ b/smoketest/scripts/cli/test_system_ip.py
@@ -38,17 +38,6 @@ class TestSystemIP(VyOSUnitTestSHIM.TestCase):
self.assertEqual(read_file(all_forwarding), '0')
- def test_system_ip_directed_broadcast_forwarding(self):
- # Test if IPv4 directed broadcast forwarding can be disabled globally,
- # default is '1' which means forwarding enabled
- bc_forwarding = '/proc/sys/net/ipv4/conf/all/bc_forwarding'
- self.assertEqual(read_file(bc_forwarding), '1')
-
- self.cli_set(base_path + ['disable-directed-broadcast'])
- self.cli_commit()
-
- self.assertEqual(read_file(bc_forwarding), '0')
-
def test_system_ip_multipath(self):
# Test IPv4 multipathing options, options default to off -> '0'
use_neigh = '/proc/sys/net/ipv4/fib_multipath_use_neigh'
diff --git a/smoketest/scripts/cli/test_system_ipv6.py b/smoketest/scripts/cli/test_system_ipv6.py
index bc0f7aa8c..0c77c1dd4 100755
--- a/smoketest/scripts/cli/test_system_ipv6.py
+++ b/smoketest/scripts/cli/test_system_ipv6.py
@@ -19,10 +19,7 @@ import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
from vyos.configsession import ConfigSessionError
-from vyos.template import is_ipv4
from vyos.utils.file import read_file
-from vyos.utils.network import get_interface_config
-from vyos.utils.network import is_intf_addr_assigned
base_path = ['system', 'ipv6']
diff --git a/smoketest/scripts/cli/test_system_login.py b/smoketest/scripts/cli/test_system_login.py
index d93ad952f..3f249660d 100755
--- a/smoketest/scripts/cli/test_system_login.py
+++ b/smoketest/scripts/cli/test_system_login.py
@@ -16,14 +16,12 @@
import re
import unittest
-import paramiko
from base_vyostest_shim import VyOSUnitTestSHIM
from gzip import GzipFile
from subprocess import Popen, PIPE
from pwd import getpwall
-from time import sleep
from vyos.configsession import ConfigSessionError
from vyos.utils.process import cmd
diff --git a/smoketest/scripts/cli/test_system_sflow.py b/smoketest/scripts/cli/test_system_sflow.py
index c0424d915..74c065473 100755
--- a/smoketest/scripts/cli/test_system_sflow.py
+++ b/smoketest/scripts/cli/test_system_sflow.py
@@ -17,7 +17,6 @@
import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
-from time import sleep
from vyos.configsession import ConfigSessionError
from vyos.ifconfig import Section
diff --git a/smoketest/scripts/cli/test_system_syslog.py b/smoketest/scripts/cli/test_system_syslog.py
index 933a5704c..030ec587b 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-2023 VyOS maintainers and contributors
+# Copyright (C) 2019-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
@@ -19,14 +19,8 @@ import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
-from vyos.configsession import ConfigSessionError
-from vyos.template import is_ipv4
-from vyos.template import address_from_cidr
-from vyos.utils.process import call
-from vyos.utils.process import DEVNULL
from vyos.utils.file import read_file
from vyos.utils.process import process_named_running
-from vyos.version import get_version_data
PROCESS_NAME = 'rsyslogd'
RSYSLOG_CONF = '/etc/rsyslog.d/00-vyos.conf'
@@ -38,10 +32,10 @@ def get_config_value(key):
tmp = re.findall(r'\n?{}\s+(.*)'.format(key), tmp)
return tmp[0]
-class TestRSYSLOGService(VyOSUnitTestSHIM.TestCase):
+class TestRSYSLOGService(VyOSUnitTestSHIM.TestCase):
@classmethod
def setUpClass(cls):
- super(TestRSYSLOGService, cls).setUpClass()
+ super(TestRSYSLOGService, cls).setUpClass()
# ensure we can also run this test on a live system - so lets clean
# out the current configuration :)
diff --git a/smoketest/scripts/cli/test_vpn_ipsec.py b/smoketest/scripts/cli/test_vpn_ipsec.py
index ab832e91e..145b5990e 100755
--- a/smoketest/scripts/cli/test_vpn_ipsec.py
+++ b/smoketest/scripts/cli/test_vpn_ipsec.py
@@ -20,7 +20,6 @@ import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
from vyos.configsession import ConfigSessionError
-from vyos.utils.process import call
from vyos.utils.process import process_named_running
from vyos.utils.file import read_file
@@ -758,6 +757,7 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase):
f'id = "{local_id}"',
f'auth = pubkey',
f'certs = peer1.pem',
+ f'cacerts = MyVyOS-CA.pem',
f'auth = eap-tls',
f'eap_id = %any',
f'esp_proposals = aes256-sha512,aes256-sha384,aes256-sha256,aes256-sha1,aes128gcm128-sha256',
@@ -841,6 +841,7 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase):
with self.assertRaises(ConfigSessionError):
self.cli_commit()
self.cli_set(base_path + ['remote-access', 'connection', conn_name, 'authentication', 'x509', 'ca-certificate', ca_name])
+ self.cli_set(base_path + ['remote-access', 'connection', conn_name, 'authentication', 'x509', 'ca-certificate', int_ca_name])
self.cli_set(base_path + ['remote-access', 'connection', conn_name, 'esp-group', esp_group])
self.cli_set(base_path + ['remote-access', 'connection', conn_name, 'ike-group', ike_group])
@@ -868,6 +869,7 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase):
f'id = "{local_id}"',
f'auth = pubkey',
f'certs = peer1.pem',
+ f'cacerts = MyVyOS-CA.pem,MyVyOS-IntCA.pem',
f'esp_proposals = aes256-sha512,aes256-sha384,aes256-sha256,aes256-sha1,aes128gcm128-sha256',
f'rekey_time = {eap_lifetime}s',
f'rand_time = 540s',
@@ -895,6 +897,7 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase):
# Check Root CA, Intermediate CA and Peer cert/key pair is present
self.assertTrue(os.path.exists(os.path.join(CA_PATH, f'{ca_name}.pem')))
+ self.assertTrue(os.path.exists(os.path.join(CA_PATH, f'{int_ca_name}.pem')))
self.assertTrue(os.path.exists(os.path.join(CERT_PATH, f'{peer_name}.pem')))
self.tearDownPKI()
diff --git a/smoketest/scripts/cli/test_vpn_l2tp.py b/smoketest/scripts/cli/test_vpn_l2tp.py
index c3b5b500d..8c4e53895 100755
--- a/smoketest/scripts/cli/test_vpn_l2tp.py
+++ b/smoketest/scripts/cli/test_vpn_l2tp.py
@@ -13,7 +13,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 re
+
import unittest
from base_accel_ppp_test import BasicAccelPPPTest
diff --git a/smoketest/scripts/cli/test_vpn_openconnect.py b/smoketest/scripts/cli/test_vpn_openconnect.py
index c4502fada..96e858fdb 100755
--- a/smoketest/scripts/cli/test_vpn_openconnect.py
+++ b/smoketest/scripts/cli/test_vpn_openconnect.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2020-2022 VyOS maintainers and contributors
+# Copyright (C) 2020-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
@@ -18,6 +18,7 @@ import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
+from vyos.configsession import ConfigSessionError
from vyos.template import ip_from_cidr
from vyos.utils.process import process_named_running
from vyos.utils.file import read_file
@@ -27,25 +28,110 @@ base_path = ['vpn', 'openconnect']
pki_path = ['pki']
+cert_name = 'OCServ'
cert_data = """
-MIICFDCCAbugAwIBAgIUfMbIsB/ozMXijYgUYG80T1ry+mcwCgYIKoZIzj0EAwIw
-WTELMAkGA1UEBhMCR0IxEzARBgNVBAgMClNvbWUtU3RhdGUxEjAQBgNVBAcMCVNv
-bWUtQ2l0eTENMAsGA1UECgwEVnlPUzESMBAGA1UEAwwJVnlPUyBUZXN0MB4XDTIx
-MDcyMDEyNDUxMloXDTI2MDcxOTEyNDUxMlowWTELMAkGA1UEBhMCR0IxEzARBgNV
-BAgMClNvbWUtU3RhdGUxEjAQBgNVBAcMCVNvbWUtQ2l0eTENMAsGA1UECgwEVnlP
-UzESMBAGA1UEAwwJVnlPUyBUZXN0MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE
-01HrLcNttqq4/PtoMua8rMWEkOdBu7vP94xzDO7A8C92ls1v86eePy4QllKCzIw3
-QxBIoCuH2peGRfWgPRdFsKNhMF8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8E
-BAMCAYYwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMB0GA1UdDgQWBBSu
-+JnU5ZC4mkuEpqg2+Mk4K79oeDAKBggqhkjOPQQDAgNHADBEAiBEFdzQ/Bc3Lftz
-ngrY605UhA6UprHhAogKgROv7iR4QgIgEFUxTtW3xXJcnUPWhhUFhyZoqfn8dE93
-+dm/LDnp7C0=
+MIIDsTCCApmgAwIBAgIURNQMaYmRIP/d+/OPWPWmuwkYHbswDQYJKoZIhvcNAQEL
+BQAwVzELMAkGA1UEBhMCR0IxEzARBgNVBAgMClNvbWUtU3RhdGUxEjAQBgNVBAcM
+CVNvbWUtQ2l0eTENMAsGA1UECgwEVnlPUzEQMA4GA1UEAwwHdnlvcy5pbzAeFw0y
+NDA0MDIxNjQxMTRaFw0yNTA0MDIxNjQxMTRaMFcxCzAJBgNVBAYTAkdCMRMwEQYD
+VQQIDApTb21lLVN0YXRlMRIwEAYDVQQHDAlTb21lLUNpdHkxDTALBgNVBAoMBFZ5
+T1MxEDAOBgNVBAMMB3Z5b3MuaW8wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
+AoIBAQDFeexWVV70fBLOxGofWYlcNxJ9JyLviAZZDXrBIYfQnSrYp51yMKRPTH1e
+Sjr7gIxVArAqLoYFgo7frRDkCKg8/izTopxtBTV2XJkLqDGA7DOrtBhgj0zjmF0A
+WWIWi83WHc+sTHSvIqNLCDAZgnnzf1ch3W/na10hBTnFX4Yv6CJ4I7doSIyWzaQr
+RvUXfaNYnvege+RrG5LzkVGxD2EhHyBqfQ2mxvlgqICqKSZkL56a3c/MHAm+7MKl
+2KbSGxwNDs+SpHrCgWVIsl9w0bN2NSAu6GzyfW7V+V1dkiCggLlxXGhGncPMiQ7T
+M7GKQULnQl5o/15GkW72Tg6wUdDpAgMBAAGjdTBzMAwGA1UdEwEB/wQCMAAwDgYD
+VR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMBMB0GA1UdDgQWBBTtil1X
+c6dXA6kxZtZCgjx9QPzeLDAfBgNVHSMEGDAWgBTKMZvYAW1thn/uxX1fpcbP5vKq
+dzANBgkqhkiG9w0BAQsFAAOCAQEARjS+QYJDz+XTdwK/lMF1GhSdacGnOIWRsbRx
+N7odsyBV7Ud5W+Py79n+/PRirw2+jAaGXFmmgdxrcjlM+dZnlO3X0QCIuNdODggD
+0J/u1ICPdm9TcJ2lEdbIE2vm2Q9P5RdQ7En7zg8Wu+rcNPlIxd3pHFOMX79vOcgi
+RkWWII6tyeeT9COYgXUbg37wf2LkVv4b5PcShrfkWZVFWKDKr1maJ+iMwcIlosOe
+Gj3SKe7gKBuPbMRwtocqKAYbW1GH12tA49DNkvxVKxVqnP4nHkwgfOJdpcZAjlyb
+gLkzVKInZwg5EvJ7qtSJirDap9jyuLTfr5TmxbcdEhmAqeS41A==
"""
-key_data = """
-MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgPLpD0Ohhoq0g4nhx
-2KMIuze7ucKUt/lBEB2wc03IxXyhRANCAATTUestw222qrj8+2gy5rysxYSQ50G7
-u8/3jHMM7sDwL3aWzW/zp54/LhCWUoLMjDdDEEigK4fal4ZF9aA9F0Ww
+cert_key_data = """
+MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDFeexWVV70fBLO
+xGofWYlcNxJ9JyLviAZZDXrBIYfQnSrYp51yMKRPTH1eSjr7gIxVArAqLoYFgo7f
+rRDkCKg8/izTopxtBTV2XJkLqDGA7DOrtBhgj0zjmF0AWWIWi83WHc+sTHSvIqNL
+CDAZgnnzf1ch3W/na10hBTnFX4Yv6CJ4I7doSIyWzaQrRvUXfaNYnvege+RrG5Lz
+kVGxD2EhHyBqfQ2mxvlgqICqKSZkL56a3c/MHAm+7MKl2KbSGxwNDs+SpHrCgWVI
+sl9w0bN2NSAu6GzyfW7V+V1dkiCggLlxXGhGncPMiQ7TM7GKQULnQl5o/15GkW72
+Tg6wUdDpAgMBAAECggEACbR8bHZv9GT/9EshNLQ3n3a8wQuCLd0fWWi5A90sKbun
+pj5/6uOVbP5DL7Xx4HgIrYmJyIZBI5aEg11Oi15vjOZ9o9MF4V0UVmJQ9TU0EEl2
+H/X5uA54MWaaCiaFFGWU3UqEG8wldJFSZCFyt7Y6scBW3b0JFF7+6dyyDPoCWWqh
+cNR41Hv0T0eqfXGOXX1JcBlLbqy0QXXeFoLlxV3ouIgWgkKJk7u3vDWCVM/ofP0m
+/GyZYWCEA2JljEQZaVgtk1afFoamrjM4doMiirk+Tix4yGno94HLJdDUynqdLNAd
+ZdKunFVAJau17b1VVPyfgIvIaPRvSGQVQoXH6TuB2QKBgQD5LRYTxsd8WsOwlB2R
+SBYdzDff7c3VuNSAYTp7O2MqWrsoXm2MxLzEJLJUen+jQphL6ti/ObdrSOnKF2So
+SizYeJ1Irx4M4BPSdy/Yt3T/+e+Y4K7iQ7Pdvdc/dlZ5XuNHYzuA/F7Ft/9rhUy9
+jSdQYANX+7h8vL7YrEjvhMMMZQKBgQDK4mG4D7XowLlBWv1fK4n/ErWvYSxH/X+A
+VVnLv4z4aZHyRS2nTfQnb8PKbHJ/65x9yZs8a+6HqE4CAH+0LfZuOI8qn9OksxPZ
+7GuQk/FiVyGXtu18hzlfhzmb0ZTjAalZ5b68DOIhyZIHVketebhljXaB5bfwdIgt
+7vTOfotANQKBgQCWiA5WVDgfgBXIjzJtmkcCKWV3+onnG4oFJLfXysDVzYpTkPhN
+mm0PcbvqHTcOwiSPeIkIvS15usrCM++zW1xMSlF6n5Bf5t8Svr5BBlPAcJW2ncYJ
+Gy2GQDHRPQRwvko/zkscWVpHyCieJCGAQc4GWHqspH2Hnd8Ntsc5K9NJoQKBgFR1
+5/5rM+yghr7pdT9wbbNtg4tuZbPWmYTAg3Bp3vLvaB22pOnYbwMX6SdU/Fm6qVxI
+WMLPn+6Dp2337TICTGvYSemRvdb74hC/9ouquzuYUFjLg5Rq6vyU2+u9VUEnyOuu
+1DePGXi9ZHh/d7mFSbmlKaesDWYh7StKJknsrmXdAoGBAOm+FnzryKkhIq/ELyT9
+8v4wr0lxCcAP3nNb/P5ocv3m7hRLIkf4S9k/gAL+gE/OtdesomQKjOz7noLO+I2H
+rj6ZfC/lhPIRJ4XK5BqgqqH53Zcl/HDoaUjbpmyMvZVoQfUHLut8Y912R6mfm65z
+qXl1L7EdHTY+SdoThNJTpmWb
+"""
+
+ca_name = 'VyOS-CA'
+ca_data = """
+MIIDnTCCAoWgAwIBAgIUFVRURZXSbQ7F0DiSZYfqY0gQORMwDQYJKoZIhvcNAQEL
+BQAwVzELMAkGA1UEBhMCR0IxEzARBgNVBAgMClNvbWUtU3RhdGUxEjAQBgNVBAcM
+CVNvbWUtQ2l0eTENMAsGA1UECgwEVnlPUzEQMA4GA1UEAwwHdnlvcy5pbzAeFw0y
+NDA0MDIxNjQxMDFaFw0yOTA0MDExNjQxMDFaMFcxCzAJBgNVBAYTAkdCMRMwEQYD
+VQQIDApTb21lLVN0YXRlMRIwEAYDVQQHDAlTb21lLUNpdHkxDTALBgNVBAoMBFZ5
+T1MxEDAOBgNVBAMMB3Z5b3MuaW8wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
+AoIBAQCg7Mjl6+rs8Bdkjqgl2QDuHfrH2mTDCeB7WuNTnIz0BPDtlmwIdqhU7LdC
+B/zUSABAa6LBe/Z/bKWCRKyq8fU2/4uWECe975IMXOfFdYT6KA78DROvOi32JZml
+n0LAXV+538eb+g19xNtoBhPO8igiNevfkV+nJehRK/41ATj+assTOv87vaSX7Wqy
+aP/ZqkIdQD9Kc3cqB4JsYjkWcniHL9yk4oY3cjKK8PJ1pi4FqgFHt2hA+Ic+NvbA
+hc47K9otP8FM4jkSii3MZfHA6Czb43BtbR+YEiWPzBhzE2bCuIgeRUumMF1Z+CAT
+6U7Cpx3XPh+Ac2RnDa8wKeQ1eqE1AgMBAAGjYTBfMA8GA1UdEwEB/wQFMAMBAf8w
+DgYDVR0PAQH/BAQDAgGGMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcDATAd
+BgNVHQ4EFgQUyjGb2AFtbYZ/7sV9X6XGz+byqncwDQYJKoZIhvcNAQELBQADggEB
+AArGXCq92vtaUZt528lC34ENPL9bQ7nRAS/ojplAzM9reW3o56sfYWf1M8iwRsJT
+LbAwSnVB929RLlDolNpLwpzd1XaMt61Zcx4MFQmQCd+40dfuvMhluZaxt+F9bC1Z
+cA7uwe/2HrAIULq3sga9LzSph6dNuyd1rGchr4xHCJ7u4WcF0kqi0Hjcn9S/ppEc
+ba2L3rRqZmCbe6Yngx+MS06jonGw0z8F6e8LMkcvJUlNMEC76P+5Byjp4xZGP+y3
+DtIfsfijpb+t1OUe75YmWflTFnHR9GlybNYTxGAl49mFw6LlS1kefXyPtfuReLmv
+n+vZdJAWTq76zAPT3n9FClo=
+"""
+
+ca_key_data = """
+ MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCg7Mjl6+rs8Bd
+ kjqgl2QDuHfrH2mTDCeB7WuNTnIz0BPDtlmwIdqhU7LdCB/zUSABAa6LBe/Z/bK
+ WCRKyq8fU2/4uWECe975IMXOfFdYT6KA78DROvOi32JZmln0LAXV+538eb+g19x
+ NtoBhPO8igiNevfkV+nJehRK/41ATj+assTOv87vaSX7WqyaP/ZqkIdQD9Kc3cq
+ B4JsYjkWcniHL9yk4oY3cjKK8PJ1pi4FqgFHt2hA+Ic+NvbAhc47K9otP8FM4jk
+ Sii3MZfHA6Czb43BtbR+YEiWPzBhzE2bCuIgeRUumMF1Z+CAT6U7Cpx3XPh+Ac2
+ RnDa8wKeQ1eqE1AgMBAAECggEAEDDaoqVqmMWsONoQiWRMr2h1RZvPxP7OpuKVW
+ iF3XgrMOb9HZc+Ybpj1dC+NDMekvNaHhMuF2Lqz6UgjDjzzVMH/x4yfDwFWUqeb
+ SxbglvGmVk4zg48JNkmArLT6GJQccD1XXjZZmqSOhagM4KalCpIdxfvgoZbTCa2
+ xMSCLHS+1HCDcmpCoeXM6ZBPTn0NbjRDAqIzCwcq2veG7RSz040obk8h7nrdv7j
+ hxRGmtPmPFzKgGLNn6GnL7AwYVMiidjj/ntvM4B1OMs9MwUYbtpg98TWcWyu+ZR
+ akUrnVf9z2aIHCKyuJvke/PNqMgw+L8KV4/478XxWhXfl7K1F3nMQKBgQDRBUDY
+ NFH0wC4MMWsA+RGwyz7RlzACChDJCMtA/agbW06gUoE9UYf8KtLQQQYljlLJHxH
+ GD72QnuM+sowGGXnbD4BabA9TQiQUG5c6boznTy1uU1gt8T0Zl0mmC7vIMoMBVd
+ 5bb0qrZvuR123kDGYn6crug9uvMIYSSlhGmBGTJQKBgQDFGC3vfkCyXzLoYy+RI
+ s/rXgyBF1PUYQtyDgL0N811L0H7a8JhFnt4FvodUbxv2ob+1kIc9e3yXT6FsGyO
+ 7IDOnqgeQKy74bYqVPZZuf1FOFb9fuxf00pn1FmhAF4OuSWkhVhrKkyrZwdD8Ar
+ jLK253J94dogjdKAYfN1csaOA0QKBgD0zUZI8d4a3QoRVb+RACTr/t6v8nZTrR5
+ DlX0XvP2qLKJFutuKyXaOrEkDh2R/j9T9oNncMos+WhikUdEVQ7koC1u0i2LXjF
+ tdAYN4+Akmz+DRmeNoy2VYF4w2YP+pVR+B7OPkCtBVNuPkx3743Fy42mTGPMCKy
+ jX8Lf59j5Tl1AoGBAI3sk2dZqozHMIlWovIH92CtIKP0gFD2cJ94p3fklvZDSWg
+ aeKYg4lffc8uZB/AjlAH9ly3ziZx0uIjcOc/RTg96/+SI/dls9xgUhjCmVVJ692
+ ki9GMsau/JYaEl+pTvjcOiocDJfNwQHJM3Tx+3FII59DtyXyXo3T/E6kHNSMeBA
+ oGAR9M48DTspv9OH1S7X6yR6MtMY5ltsBmB3gPhQFxiDKBvARkIkAPqObQ9TG/V
+ uOz2Purq0Oz7SHsY2jiFDd2KEGo6JfG61NDdIhiQC99ztSgt7NtvSCnX22SfVDW
+ oFxSK+tek7tvDVXAXCNy4ZESMEUGJ6NDHImb80aF+xZ3wYKw=
"""
PROCESS_NAME = 'ocserv-main'
@@ -67,9 +153,10 @@ class TestVPNOpenConnect(VyOSUnitTestSHIM.TestCase):
cls.cli_set(cls, ['interfaces', 'dummy', listen_if, 'address', listen_address])
- cls.cli_set(cls, pki_path + ['ca', 'openconnect', 'certificate', cert_data.replace('\n','')])
- cls.cli_set(cls, pki_path + ['certificate', 'openconnect', 'certificate', cert_data.replace('\n','')])
- cls.cli_set(cls, pki_path + ['certificate', 'openconnect', 'private', 'key', key_data.replace('\n','')])
+ cls.cli_set(cls, pki_path + ['ca', cert_name, 'certificate', ca_data.replace('\n','')])
+ cls.cli_set(cls, pki_path + ['ca', cert_name, 'private', 'key', ca_key_data.replace('\n','')])
+ cls.cli_set(cls, pki_path + ['certificate', cert_name, 'certificate', cert_data.replace('\n','')])
+ cls.cli_set(cls, pki_path + ['certificate', cert_name, 'private', 'key', cert_key_data.replace('\n','')])
@classmethod
def tearDownClass(cls):
@@ -108,8 +195,12 @@ class TestVPNOpenConnect(VyOSUnitTestSHIM.TestCase):
for domain in split_dns:
self.cli_set(base_path + ['network-settings', 'split-dns', domain])
- self.cli_set(base_path + ['ssl', 'ca-certificate', 'openconnect'])
- self.cli_set(base_path + ['ssl', 'certificate', 'openconnect'])
+ # SSL certificates are mandatory
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+
+ self.cli_set(base_path + ['ssl', 'ca-certificate', cert_name])
+ self.cli_set(base_path + ['ssl', 'certificate', cert_name])
listen_ip_no_cidr = ip_from_cidr(listen_address)
self.cli_set(base_path + ['listen-address', listen_ip_no_cidr])
diff --git a/smoketest/scripts/cli/test_vpn_pptp.py b/smoketest/scripts/cli/test_vpn_pptp.py
index ac46d210d..25d9a4760 100755
--- a/smoketest/scripts/cli/test_vpn_pptp.py
+++ b/smoketest/scripts/cli/test_vpn_pptp.py
@@ -14,14 +14,9 @@
# 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 unittest
-from configparser import ConfigParser
-from vyos.utils.process import cmd
from base_accel_ppp_test import BasicAccelPPPTest
-from vyos.template import is_ipv4
-
class TestVPNPPTPServer(BasicAccelPPPTest.TestCase):
@classmethod
diff --git a/smoketest/scripts/cli/test_vrf.py b/smoketest/scripts/cli/test_vrf.py
index c96b8e374..f6e4181c0 100755
--- a/smoketest/scripts/cli/test_vrf.py
+++ b/smoketest/scripts/cli/test_vrf.py
@@ -16,7 +16,6 @@
import re
import os
-import json
import unittest
from netifaces import interfaces
@@ -25,8 +24,6 @@ from base_vyostest_shim import VyOSUnitTestSHIM
from vyos.configsession import ConfigSessionError
from vyos.ifconfig import Interface
from vyos.ifconfig import Section
-from vyos.template import is_ipv4
-from vyos.utils.process import cmd
from vyos.utils.file import read_file
from vyos.utils.network import get_interface_config
from vyos.utils.network import is_intf_addr_assigned
diff --git a/sphinx/Makefile b/sphinx/Makefile
deleted file mode 100644
index 1e344639e..000000000
--- a/sphinx/Makefile
+++ /dev/null
@@ -1,177 +0,0 @@
-# Makefile for Sphinx documentation
-#
-
-# You can set these variables from the command line.
-SPHINXOPTS =
-SPHINXBUILD = sphinx-build
-PAPER =
-BUILDDIR = build
-
-# User-friendly check for sphinx-build
-ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1)
-$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/)
-endif
-
-# Internal variables.
-PAPEROPT_a4 = -D latex_paper_size=a4
-PAPEROPT_letter = -D latex_paper_size=letter
-ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source
-# the i18n builder cannot share the environment and doctrees with the others
-I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source
-
-.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
-
-help:
- @echo "Please use \`make <target>' where <target> is one of"
- @echo " html to make standalone HTML files"
- @echo " dirhtml to make HTML files named index.html in directories"
- @echo " singlehtml to make a single large HTML file"
- @echo " pickle to make pickle files"
- @echo " json to make JSON files"
- @echo " htmlhelp to make HTML files and a HTML help project"
- @echo " qthelp to make HTML files and a qthelp project"
- @echo " devhelp to make HTML files and a Devhelp project"
- @echo " epub to make an epub"
- @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
- @echo " latexpdf to make LaTeX files and run them through pdflatex"
- @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
- @echo " text to make text files"
- @echo " man to make manual pages"
- @echo " texinfo to make Texinfo files"
- @echo " info to make Texinfo files and run them through makeinfo"
- @echo " gettext to make PO message catalogs"
- @echo " changes to make an overview of all changed/added/deprecated items"
- @echo " xml to make Docutils-native XML files"
- @echo " pseudoxml to make pseudoxml-XML files for display purposes"
- @echo " linkcheck to check all external links for integrity"
- @echo " doctest to run all doctests embedded in the documentation (if enabled)"
-
-clean:
- rm -rf $(BUILDDIR)/*
-
-html:
- $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
- @echo
- @echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
-
-dirhtml:
- $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
- @echo
- @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
-
-singlehtml:
- $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
- @echo
- @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
-
-pickle:
- $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
- @echo
- @echo "Build finished; now you can process the pickle files."
-
-json:
- $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
- @echo
- @echo "Build finished; now you can process the JSON files."
-
-htmlhelp:
- $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
- @echo
- @echo "Build finished; now you can run HTML Help Workshop with the" \
- ".hhp project file in $(BUILDDIR)/htmlhelp."
-
-qthelp:
- $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
- @echo
- @echo "Build finished; now you can run "qcollectiongenerator" with the" \
- ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
- @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/VyOS.qhcp"
- @echo "To view the help file:"
- @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/VyOS.qhc"
-
-devhelp:
- $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
- @echo
- @echo "Build finished."
- @echo "To view the help file:"
- @echo "# mkdir -p $$HOME/.local/share/devhelp/VyOS"
- @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/VyOS"
- @echo "# devhelp"
-
-epub:
- $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
- @echo
- @echo "Build finished. The epub file is in $(BUILDDIR)/epub."
-
-latex:
- $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
- @echo
- @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
- @echo "Run \`make' in that directory to run these through (pdf)latex" \
- "(use \`make latexpdf' here to do that automatically)."
-
-latexpdf:
- $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
- @echo "Running LaTeX files through pdflatex..."
- $(MAKE) -C $(BUILDDIR)/latex all-pdf
- @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
-
-latexpdfja:
- $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
- @echo "Running LaTeX files through platex and dvipdfmx..."
- $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
- @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
-
-text:
- $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
- @echo
- @echo "Build finished. The text files are in $(BUILDDIR)/text."
-
-man:
- $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
- @echo
- @echo "Build finished. The manual pages are in $(BUILDDIR)/man."
-
-texinfo:
- $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
- @echo
- @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
- @echo "Run \`make' in that directory to run these through makeinfo" \
- "(use \`make info' here to do that automatically)."
-
-info:
- $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
- @echo "Running Texinfo files through makeinfo..."
- make -C $(BUILDDIR)/texinfo info
- @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
-
-gettext:
- $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
- @echo
- @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
-
-changes:
- $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
- @echo
- @echo "The overview file is in $(BUILDDIR)/changes."
-
-linkcheck:
- $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
- @echo
- @echo "Link check complete; look for any errors in the above output " \
- "or in $(BUILDDIR)/linkcheck/output.txt."
-
-doctest:
- $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
- @echo "Testing of doctests in the sources finished, look at the " \
- "results in $(BUILDDIR)/doctest/output.txt."
-
-xml:
- $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
- @echo
- @echo "Build finished. The XML files are in $(BUILDDIR)/xml."
-
-pseudoxml:
- $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
- @echo
- @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."
diff --git a/sphinx/source/.gitignore b/sphinx/source/.gitignore
deleted file mode 100644
index 30d85567b..000000000
--- a/sphinx/source/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-*.rst
diff --git a/sphinx/source/conf.py b/sphinx/source/conf.py
deleted file mode 100644
index 3447259b3..000000000
--- a/sphinx/source/conf.py
+++ /dev/null
@@ -1,261 +0,0 @@
-#!/usr/bin/env python3
-# -*- coding: utf-8 -*-
-#
-# VyOS documentation build configuration file, created by
-# sphinx-quickstart on Wed Jun 20 01:14:27 2018.
-#
-# This file is execfile()d with the current directory set to its
-# containing dir.
-#
-# Note that not all possible configuration values are present in this
-# autogenerated file.
-#
-# All configuration values have a default; values that are commented out
-# serve to show the default.
-
-import sys
-import os
-
-# If extensions (or modules to document with autodoc) are in another directory,
-# add these directories to sys.path here. If the directory is relative to the
-# documentation root, use os.path.abspath to make it absolute, like shown here.
-#sys.path.insert(0, os.path.abspath('.'))
-
-# -- General configuration ------------------------------------------------
-
-# If your documentation needs a minimal Sphinx version, state it here.
-#needs_sphinx = '1.0'
-
-# Add any Sphinx extension module names here, as strings. They can be
-# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
-# ones.
-extensions = [
- 'sphinx.ext.autodoc',
-]
-
-# Add any paths that contain templates here, relative to this directory.
-templates_path = ['_templates']
-
-# The suffix of source filenames.
-source_suffix = '.rst'
-
-# The encoding of source files.
-#source_encoding = 'utf-8-sig'
-
-# The master toctree document.
-master_doc = 'index'
-
-# General information about the project.
-project = 'VyOS'
-copyright = '2018, VyOS maintainers and contributors'
-
-# The version info for the project you're documenting, acts as replacement for
-# |version| and |release|, also used in various other places throughout the
-# built documents.
-#
-# The short X.Y version.
-version = '1.2.0'
-# The full version, including alpha/beta/rc tags.
-release = '1.2.0'
-
-# The language for content autogenerated by Sphinx. Refer to documentation
-# for a list of supported languages.
-#language = None
-
-# There are two options for replacing |today|: either, you set today to some
-# non-false value, then it is used:
-#today = ''
-# Else, today_fmt is used as the format for a strftime call.
-#today_fmt = '%B %d, %Y'
-
-# List of patterns, relative to source directory, that match files and
-# directories to ignore when looking for source files.
-exclude_patterns = []
-
-# The reST default role (used for this markup: `text`) to use for all
-# documents.
-#default_role = None
-
-# If true, '()' will be appended to :func: etc. cross-reference text.
-#add_function_parentheses = True
-
-# If true, the current module name will be prepended to all description
-# unit titles (such as .. function::).
-#add_module_names = True
-
-# If true, sectionauthor and moduleauthor directives will be shown in the
-# output. They are ignored by default.
-#show_authors = False
-
-# The name of the Pygments (syntax highlighting) style to use.
-pygments_style = 'sphinx'
-
-# A list of ignored prefixes for module index sorting.
-#modindex_common_prefix = []
-
-# If true, keep warnings as "system message" paragraphs in the built documents.
-#keep_warnings = False
-
-
-# -- Options for HTML output ----------------------------------------------
-
-# The theme to use for HTML and HTML Help pages. See the documentation for
-# a list of builtin themes.
-html_theme = 'default'
-
-# Theme options are theme-specific and customize the look and feel of a theme
-# further. For a list of options available for each theme, see the
-# documentation.
-#html_theme_options = {}
-
-# Add any paths that contain custom themes here, relative to this directory.
-#html_theme_path = []
-
-# The name for this set of Sphinx documents. If None, it defaults to
-# "<project> v<release> documentation".
-#html_title = None
-
-# A shorter title for the navigation bar. Default is the same as html_title.
-#html_short_title = None
-
-# The name of an image file (relative to this directory) to place at the top
-# of the sidebar.
-#html_logo = None
-
-# The name of an image file (within the static path) to use as favicon of the
-# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
-# pixels large.
-#html_favicon = None
-
-# Add any paths that contain custom static files (such as style sheets) here,
-# relative to this directory. They are copied after the builtin static files,
-# so a file named "default.css" will overwrite the builtin "default.css".
-html_static_path = ['_static']
-
-# Add any extra paths that contain custom files (such as robots.txt or
-# .htaccess) here, relative to this directory. These files are copied
-# directly to the root of the documentation.
-#html_extra_path = []
-
-# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
-# using the given strftime format.
-#html_last_updated_fmt = '%b %d, %Y'
-
-# If true, SmartyPants will be used to convert quotes and dashes to
-# typographically correct entities.
-#html_use_smartypants = True
-
-# Custom sidebar templates, maps document names to template names.
-#html_sidebars = {}
-
-# Additional templates that should be rendered to pages, maps page names to
-# template names.
-#html_additional_pages = {}
-
-# If false, no module index is generated.
-#html_domain_indices = True
-
-# If false, no index is generated.
-#html_use_index = True
-
-# If true, the index is split into individual pages for each letter.
-#html_split_index = False
-
-# If true, links to the reST sources are added to the pages.
-#html_show_sourcelink = True
-
-# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
-#html_show_sphinx = True
-
-# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
-#html_show_copyright = True
-
-# If true, an OpenSearch description file will be output, and all pages will
-# contain a <link> tag referring to it. The value of this option must be the
-# base URL from which the finished HTML is served.
-#html_use_opensearch = ''
-
-# This is the file name suffix for HTML files (e.g. ".xhtml").
-#html_file_suffix = None
-
-# Output file base name for HTML help builder.
-htmlhelp_basename = 'VyOSdoc'
-
-
-# -- Options for LaTeX output ---------------------------------------------
-
-latex_elements = {
-# The paper size ('letterpaper' or 'a4paper').
-#'papersize': 'letterpaper',
-
-# The font size ('10pt', '11pt' or '12pt').
-#'pointsize': '10pt',
-
-# Additional stuff for the LaTeX preamble.
-#'preamble': '',
-}
-
-# Grouping the document tree into LaTeX files. List of tuples
-# (source start file, target name, title,
-# author, documentclass [howto, manual, or own class]).
-latex_documents = [
- ('index', 'VyOS.tex', 'VyOS Documentation',
- 'VyOS maintainers and contributors', 'manual'),
-]
-
-# The name of an image file (relative to this directory) to place at the top of
-# the title page.
-#latex_logo = None
-
-# For "manual" documents, if this is true, then toplevel headings are parts,
-# not chapters.
-#latex_use_parts = False
-
-# If true, show page references after internal links.
-#latex_show_pagerefs = False
-
-# If true, show URL addresses after external links.
-#latex_show_urls = False
-
-# Documents to append as an appendix to all manuals.
-#latex_appendices = []
-
-# If false, no module index is generated.
-#latex_domain_indices = True
-
-
-# -- Options for manual page output ---------------------------------------
-
-# One entry per manual page. List of tuples
-# (source start file, name, description, authors, manual section).
-man_pages = [
- ('index', 'vyos', 'VyOS Documentation',
- ['VyOS maintainers and contributors'], 1)
-]
-
-# If true, show URL addresses after external links.
-#man_show_urls = False
-
-
-# -- Options for Texinfo output -------------------------------------------
-
-# Grouping the document tree into Texinfo files. List of tuples
-# (source start file, target name, title, author,
-# dir menu entry, description, category)
-texinfo_documents = [
- ('index', 'VyOS', 'VyOS Documentation',
- 'VyOS maintainers and contributors', 'VyOS', 'One line description of project.',
- 'Miscellaneous'),
-]
-
-# Documents to append as an appendix to all manuals.
-#texinfo_appendices = []
-
-# If false, no module index is generated.
-#texinfo_domain_indices = True
-
-# How to display URL addresses: 'footnote', 'no', or 'inline'.
-#texinfo_show_urls = 'footnote'
-
-# If true, do not generate a @detailmenu in the "Top" node's menu.
-#texinfo_no_detailmenu = False
diff --git a/sphinx/source/index.rst b/sphinx/source/index.rst
deleted file mode 100644
index c31cac4e5..000000000
--- a/sphinx/source/index.rst
+++ /dev/null
@@ -1,22 +0,0 @@
-.. VyOS documentation master file, created by
- sphinx-quickstart on Wed Jun 20 01:14:27 2018.
- You can adapt this file completely to your liking, but it should at least
- contain the root `toctree` directive.
-
-Welcome to VyOS's documentation!
-================================
-
-Contents:
-
-.. toctree::
- :maxdepth: 2
-
-
-
-Indices and tables
-==================
-
-* :ref:`genindex`
-* :ref:`modindex`
-* :ref:`search`
-
diff --git a/src/completion/list_ipsec_profile_tunnels.py b/src/completion/list_ipsec_profile_tunnels.py
index 4a917dbd6..95a4ca3ce 100644
--- a/src/completion/list_ipsec_profile_tunnels.py
+++ b/src/completion/list_ipsec_profile_tunnels.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2019-2023 VyOS maintainers and contributors
+# Copyright (C) 2019-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
@@ -14,8 +14,6 @@
# 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 sys
import argparse
from vyos.config import Config
@@ -45,4 +43,3 @@ if __name__ == "__main__":
tunnels = get_tunnels_from_ipsecprofile(args.profile)
print(" ".join(tunnels))
-
diff --git a/src/completion/list_openvpn_clients.py b/src/completion/list_openvpn_clients.py
index b443520ea..c1d8eaeb3 100755
--- a/src/completion/list_openvpn_clients.py
+++ b/src/completion/list_openvpn_clients.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2019 VyOS maintainers and contributors
+# Copyright (C) 2019-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
@@ -14,8 +14,6 @@
# 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 sys
import argparse
from vyos.ifconfig import Section
@@ -57,4 +55,3 @@ if __name__ == "__main__":
clients += get_client_from_interface(interface)
print(" ".join(clients))
-
diff --git a/src/completion/list_openvpn_users.py b/src/completion/list_openvpn_users.py
index b2b0149fc..f2c648476 100755
--- a/src/completion/list_openvpn_users.py
+++ b/src/completion/list_openvpn_users.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2019-2021 VyOS maintainers and contributors
+# Copyright (C) 2019-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
@@ -14,8 +14,6 @@
# 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 sys
import argparse
from vyos.config import Config
@@ -45,4 +43,3 @@ if __name__ == "__main__":
users = get_user_from_interface(args.interface)
print(" ".join(users))
-
diff --git a/src/conf_mode/container.py b/src/conf_mode/container.py
index e967bee71..a73a18ffa 100755
--- a/src/conf_mode/container.py
+++ b/src/conf_mode/container.py
@@ -32,6 +32,7 @@ from vyos.utils.file import write_file
from vyos.utils.process import call
from vyos.utils.process import cmd
from vyos.utils.process import run
+from vyos.utils.network import interface_exists
from vyos.template import bracketize_ipv6
from vyos.template import inc_ip
from vyos.template import is_ipv4
@@ -261,12 +262,11 @@ def generate_run_arguments(name, container_config):
restart = container_config['restart']
# Add capability options. Should be in uppercase
- cap_add = ''
- if 'cap_add' in container_config:
- for c in container_config['cap_add']:
- c = c.upper()
- c = c.replace('-', '_')
- cap_add += f' --cap-add={c}'
+ capabilities = ''
+ if 'capability' in container_config:
+ for cap in container_config['capability']:
+ cap = cap.upper().replace('-', '_')
+ capabilities += f' --cap-add={cap}'
# Add a host device to the container /dev/x:/dev/x
device = ''
@@ -329,7 +329,7 @@ def generate_run_arguments(name, container_config):
prop = vol_config['propagation']
volume += f' --volume {svol}:{dvol}:{mode},{prop}'
- container_base_cmd = f'--detach --interactive --tty --replace {cap_add} ' \
+ container_base_cmd = f'--detach --interactive --tty --replace {capabilities} ' \
f'--memory {memory}m --shm-size {shared_memory}m --memory-swap 0 --restart {restart} ' \
f'--name {name} {hostname} {device} {port} {volume} {env_opt} {label} {uid}'
@@ -471,10 +471,10 @@ def apply(container):
# T5147: Networks are started only as soon as there is a consumer.
# If only a network is created in the first place, no need to assign
# it to a VRF as there's no consumer, yet.
- if os.path.exists(f'/sys/class/net/{network_name}'):
+ if interface_exists(network_name):
tmp = Interface(network_name)
- tmp.add_ipv6_eui64_address('fe80::/64')
tmp.set_vrf(network_config.get('vrf', ''))
+ tmp.add_ipv6_eui64_address('fe80::/64')
return None
diff --git a/src/conf_mode/firewall.py b/src/conf_mode/firewall.py
index 810437dda..e96e57154 100755
--- a/src/conf_mode/firewall.py
+++ b/src/conf_mode/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
@@ -18,7 +18,6 @@ import os
import re
from glob import glob
-from json import loads
from sys import exit
from vyos.base import Warning
@@ -31,11 +30,9 @@ from vyos.ethtool import Ethtool
from vyos.firewall import fqdn_config_parse
from vyos.firewall import geoip_update
from vyos.template import render
-from vyos.utils.process import call
-from vyos.utils.process import cmd
from vyos.utils.dict import dict_search_args
from vyos.utils.dict import dict_search_recursive
-from vyos.utils.process import process_named_running
+from vyos.utils.process import call
from vyos.utils.process import rc_cmd
from vyos import ConfigError
from vyos import airbag
@@ -47,6 +44,7 @@ nftables_conf = '/run/nftables.conf'
sysfs_config = {
'all_ping': {'sysfs': '/proc/sys/net/ipv4/icmp_echo_ignore_all', 'enable': '0', 'disable': '1'},
'broadcast_ping': {'sysfs': '/proc/sys/net/ipv4/icmp_echo_ignore_broadcasts', 'enable': '0', 'disable': '1'},
+ 'directed_broadcast' : {'sysfs': '/proc/sys/net/ipv4/conf/all/bc_forwarding', 'enable': '1', 'disable': '0'},
'ip_src_route': {'sysfs': '/proc/sys/net/ipv4/conf/*/accept_source_route'},
'ipv6_receive_redirects': {'sysfs': '/proc/sys/net/ipv6/conf/*/accept_redirects'},
'ipv6_src_route': {'sysfs': '/proc/sys/net/ipv6/conf/*/accept_source_route', 'enable': '0', 'disable': '-1'},
@@ -491,7 +489,7 @@ def apply_sysfs(firewall):
f.write(value)
def apply(firewall):
- install_result, output = rc_cmd(f'nft -f {nftables_conf}')
+ install_result, output = rc_cmd(f'nft --file {nftables_conf}')
if install_result == 1:
raise ConfigError(f'Failed to apply firewall: {output}')
diff --git a/src/conf_mode/interfaces_bonding.py b/src/conf_mode/interfaces_bonding.py
index 8184d8415..371b219c0 100755
--- a/src/conf_mode/interfaces_bonding.py
+++ b/src/conf_mode/interfaces_bonding.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2019-2023 VyOS maintainers and contributors
+# Copyright (C) 2019-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
@@ -14,10 +14,8 @@
# 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
-
from sys import exit
-from netifaces import interfaces
+
from vyos.config import Config
from vyos.configdict import get_interface_dict
from vyos.configdict import is_node_changed
@@ -29,7 +27,6 @@ from vyos.configverify import verify_bridge_delete
from vyos.configverify import verify_dhcpv6
from vyos.configverify import verify_mirror_redirect
from vyos.configverify import verify_mtu_ipv6
-from vyos.configverify import verify_source_interface
from vyos.configverify import verify_vlan_config
from vyos.configverify import verify_vrf
from vyos.ifconfig import BondIf
@@ -38,6 +35,7 @@ from vyos.ifconfig import Section
from vyos.template import render_to_string
from vyos.utils.dict import dict_search
from vyos.utils.dict import dict_to_paths_values
+from vyos.utils.network import interface_exists
from vyos.configdict import has_address_configured
from vyos.configdict import has_vrf_configured
from vyos.configdep import set_dependents, call_dependents
@@ -209,7 +207,7 @@ def verify(bond):
if interface == 'lo':
raise ConfigError('Loopback interface "lo" can not be added to a bond')
- if interface not in interfaces():
+ if not interface_exists(interface):
raise ConfigError(error_msg + 'it does not exist!')
if 'is_bridge_member' in interface_config:
diff --git a/src/conf_mode/interfaces_ethernet.py b/src/conf_mode/interfaces_ethernet.py
index 2c0f846c3..6da7e6a69 100755
--- a/src/conf_mode/interfaces_ethernet.py
+++ b/src/conf_mode/interfaces_ethernet.py
@@ -15,9 +15,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
-import pprint
-from glob import glob
from sys import exit
from vyos.base import Warning
@@ -26,7 +24,6 @@ from vyos.configdict import get_interface_dict
from vyos.configdict import is_node_changed
from vyos.configverify import verify_address
from vyos.configverify import verify_dhcpv6
-from vyos.configverify import verify_eapol
from vyos.configverify import verify_interface_exists
from vyos.configverify import verify_mirror_redirect
from vyos.configverify import verify_mtu
@@ -34,6 +31,8 @@ from vyos.configverify import verify_mtu_ipv6
from vyos.configverify import verify_vlan_config
from vyos.configverify import verify_vrf
from vyos.configverify import verify_bond_bridge_member
+from vyos.configverify import verify_pki_certificate
+from vyos.configverify import verify_pki_ca_certificate
from vyos.ethtool import Ethtool
from vyos.ifconfig import EthernetIf
from vyos.ifconfig import BondIf
@@ -153,6 +152,19 @@ def get_config(config=None):
base = ['interfaces', 'ethernet']
ifname, ethernet = get_interface_dict(conf, base, with_pki=True)
+ # T5862 - default MTU is not acceptable in some environments
+ # There are cloud environments available where the maximum supported
+ # ethernet MTU is e.g. 1450 bytes, thus we clamp this to the adapters
+ # maximum MTU value or 1500 bytes - whatever is lower
+ if 'mtu' not in ethernet:
+ try:
+ ethernet['mtu'] = '1500'
+ max_mtu = EthernetIf(ifname).get_max_mtu()
+ if max_mtu < int(ethernet['mtu']):
+ ethernet['mtu'] = str(max_mtu)
+ except:
+ pass
+
if 'is_bond_member' in ethernet:
update_bond_options(conf, ethernet)
@@ -263,6 +275,22 @@ def verify_allowedbond_changes(ethernet: dict):
f' on interface "{ethernet["ifname"]}".' \
f' Interface is a bond member')
+def verify_eapol(ethernet: dict):
+ """
+ Common helper function used by interface implementations to perform
+ recurring validation of EAPoL configuration.
+ """
+ if 'eapol' not in ethernet:
+ return
+
+ if 'certificate' not in ethernet['eapol']:
+ raise ConfigError('Certificate must be specified when using EAPoL!')
+
+ verify_pki_certificate(ethernet, ethernet['eapol']['certificate'], no_password_protected=True)
+
+ if 'ca_certificate' in ethernet['eapol']:
+ for ca_cert in ethernet['eapol']['ca_certificate']:
+ verify_pki_ca_certificate(ethernet, ca_cert)
def verify(ethernet):
if 'deleted' in ethernet:
diff --git a/src/conf_mode/interfaces_geneve.py b/src/conf_mode/interfaces_geneve.py
index f6694ddde..769139e0f 100755
--- a/src/conf_mode/interfaces_geneve.py
+++ b/src/conf_mode/interfaces_geneve.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2019-2022 VyOS maintainers and contributors
+# Copyright (C) 2019-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
@@ -15,7 +15,6 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from sys import exit
-from netifaces import interfaces
from vyos.config import Config
from vyos.configdict import get_interface_dict
@@ -26,6 +25,7 @@ from vyos.configverify import verify_bridge_delete
from vyos.configverify import verify_mirror_redirect
from vyos.configverify import verify_bond_bridge_member
from vyos.ifconfig import GeneveIf
+from vyos.utils.network import interface_exists
from vyos import ConfigError
from vyos import airbag
@@ -77,8 +77,8 @@ def generate(geneve):
def apply(geneve):
# Check if GENEVE interface already exists
if 'rebuild_required' in geneve or 'delete' in geneve:
- if geneve['ifname'] in interfaces():
- g = GeneveIf(geneve['ifname'])
+ if interface_exists(geneve['ifname']):
+ g = GeneveIf(**geneve)
# GENEVE is super picky and the tunnel always needs to be recreated,
# thus we can simply always delete it first.
g.remove()
diff --git a/src/conf_mode/interfaces_l2tpv3.py b/src/conf_mode/interfaces_l2tpv3.py
index e1db3206e..e25793543 100755
--- a/src/conf_mode/interfaces_l2tpv3.py
+++ b/src/conf_mode/interfaces_l2tpv3.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2019-2020 VyOS maintainers and contributors
+# Copyright (C) 2019-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
@@ -14,10 +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
-
from sys import exit
-from netifaces import interfaces
from vyos.config import Config
from vyos.configdict import get_interface_dict
@@ -30,6 +27,7 @@ from vyos.configverify import verify_bond_bridge_member
from vyos.ifconfig import L2TPv3If
from vyos.utils.kernel import check_kmod
from vyos.utils.network import is_addr_assigned
+from vyos.utils.network import interface_exists
from vyos import ConfigError
from vyos import airbag
airbag.enable()
@@ -87,7 +85,7 @@ def generate(l2tpv3):
def apply(l2tpv3):
# Check if L2TPv3 interface already exists
- if l2tpv3['ifname'] in interfaces():
+ if interface_exists(l2tpv3['ifname']):
# L2TPv3 is picky when changing tunnels/sessions, thus we can simply
# always delete it first.
l = L2TPv3If(**l2tpv3)
diff --git a/src/conf_mode/interfaces_loopback.py b/src/conf_mode/interfaces_loopback.py
index 08d34477a..a784e9ec2 100755
--- a/src/conf_mode/interfaces_loopback.py
+++ b/src/conf_mode/interfaces_loopback.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2019-2020 VyOS maintainers and contributors
+# Copyright (C) 2019-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
@@ -14,8 +14,6 @@
# 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
-
from sys import exit
from vyos.config import Config
diff --git a/src/conf_mode/interfaces_macsec.py b/src/conf_mode/interfaces_macsec.py
index 0a927ac88..eb0ca9a8b 100755
--- a/src/conf_mode/interfaces_macsec.py
+++ b/src/conf_mode/interfaces_macsec.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2020-2023 VyOS maintainers and contributors
+# Copyright (C) 2020-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
@@ -16,7 +16,6 @@
import os
-from netifaces import interfaces
from sys import exit
from vyos.config import Config
@@ -35,6 +34,7 @@ from vyos.ifconfig import Interface
from vyos.template import render
from vyos.utils.process import call
from vyos.utils.dict import dict_search
+from vyos.utils.network import interface_exists
from vyos.utils.process import is_systemd_service_running
from vyos import ConfigError
from vyos import airbag
@@ -172,8 +172,8 @@ def apply(macsec):
if 'deleted' in macsec or 'shutdown_required' in macsec:
call(f'systemctl stop {systemd_service}')
- if macsec['ifname'] in interfaces():
- tmp = MACsecIf(macsec['ifname'])
+ if interface_exists(macsec['ifname']):
+ tmp = MACsecIf(**macsec)
tmp.remove()
if 'deleted' in macsec:
diff --git a/src/conf_mode/interfaces_openvpn.py b/src/conf_mode/interfaces_openvpn.py
index 45569dd21..0ecffd3be 100755
--- a/src/conf_mode/interfaces_openvpn.py
+++ b/src/conf_mode/interfaces_openvpn.py
@@ -16,7 +16,6 @@
import os
import re
-import tempfile
from cryptography.hazmat.primitives.asymmetric import ec
from glob import glob
@@ -26,7 +25,6 @@ from ipaddress import IPv4Network
from ipaddress import IPv6Address
from ipaddress import IPv6Network
from ipaddress import summarize_address_range
-from netifaces import interfaces
from secrets import SystemRandom
from shutil import rmtree
@@ -63,6 +61,7 @@ from vyos.utils.process import call
from vyos.utils.permission import chown
from vyos.utils.process import cmd
from vyos.utils.network import is_addr_assigned
+from vyos.utils.network import interface_exists
from vyos import ConfigError
from vyos import airbag
@@ -199,6 +198,12 @@ def verify_pki(openvpn):
raise ConfigError(f'Cannot use encrypted private key on openvpn interface {interface}')
if 'dh_params' in tls:
+ if 'dh' not in pki:
+ raise ConfigError(f'pki dh is not configured')
+ proposed_dh = tls['dh_params']
+ if proposed_dh not in pki['dh'].keys():
+ raise ConfigError(f"pki dh '{proposed_dh}' is not configured")
+
pki_dh = pki['dh'][tls['dh_params']]
dh_params = load_dh_parameters(pki_dh['parameters'])
dh_numbers = dh_params.parameter_numbers()
@@ -683,7 +688,7 @@ def apply(openvpn):
if os.path.isfile(cleanup_file):
os.unlink(cleanup_file)
- if interface in interfaces():
+ if interface_exists(interface):
VTunIf(interface).remove()
# dynamically load/unload DCO Kernel extension if requested
diff --git a/src/conf_mode/interfaces_pppoe.py b/src/conf_mode/interfaces_pppoe.py
index 42f084309..412676c7d 100755
--- a/src/conf_mode/interfaces_pppoe.py
+++ b/src/conf_mode/interfaces_pppoe.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2019-2021 VyOS maintainers and contributors
+# Copyright (C) 2019-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,16 +17,12 @@
import os
from sys import exit
-from copy import deepcopy
-from netifaces import interfaces
from vyos.config import Config
from vyos.configdict import get_interface_dict
from vyos.configdict import is_node_changed
-from vyos.configdict import get_pppoe_interfaces
from vyos.configverify import verify_authentication
from vyos.configverify import verify_source_interface
-from vyos.configverify import verify_interface_exists
from vyos.configverify import verify_vrf
from vyos.configverify import verify_mtu_ipv6
from vyos.configverify import verify_mirror_redirect
diff --git a/src/conf_mode/interfaces_pseudo-ethernet.py b/src/conf_mode/interfaces_pseudo-ethernet.py
index dce5c2358..446beffd3 100755
--- a/src/conf_mode/interfaces_pseudo-ethernet.py
+++ b/src/conf_mode/interfaces_pseudo-ethernet.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2019-2022 VyOS maintainers and contributors
+# Copyright (C) 2019-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
@@ -15,7 +15,6 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from sys import exit
-from netifaces import interfaces
from vyos.config import Config
from vyos.configdict import get_interface_dict
@@ -29,8 +28,8 @@ from vyos.configverify import verify_source_interface
from vyos.configverify import verify_vlan_config
from vyos.configverify import verify_mtu_parent
from vyos.configverify import verify_mirror_redirect
-from vyos.configverify import verify_bond_bridge_member
from vyos.ifconfig import MACVLANIf
+from vyos.utils.network import interface_exists
from vyos import ConfigError
from vyos import airbag
@@ -84,8 +83,8 @@ def generate(peth):
def apply(peth):
# Check if the MACVLAN interface already exists
if 'rebuild_required' in peth or 'deleted' in peth:
- if peth['ifname'] in interfaces():
- p = MACVLANIf(peth['ifname'])
+ if interface_exists(peth['ifname']):
+ p = MACVLANIf(**peth)
# MACVLAN is always needs to be recreated,
# thus we can simply always delete it first.
p.remove()
diff --git a/src/conf_mode/interfaces_tunnel.py b/src/conf_mode/interfaces_tunnel.py
index efa5ebc64..43ba72857 100755
--- a/src/conf_mode/interfaces_tunnel.py
+++ b/src/conf_mode/interfaces_tunnel.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2018-2022 yOS maintainers and contributors
+# Copyright (C) 2018-2024 yOS 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,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
-
from sys import exit
-from netifaces import interfaces
from vyos.config import Config
from vyos.configdict import get_interface_dict
@@ -31,10 +28,10 @@ from vyos.configverify import verify_vrf
from vyos.configverify import verify_tunnel
from vyos.configverify import verify_bond_bridge_member
from vyos.ifconfig import Interface
-from vyos.ifconfig import Section
from vyos.ifconfig import TunnelIf
-from vyos.utils.network import get_interface_config
from vyos.utils.dict import dict_search
+from vyos.utils.network import get_interface_config
+from vyos.utils.network import interface_exists
from vyos import ConfigError
from vyos import airbag
airbag.enable()
@@ -202,7 +199,7 @@ def apply(tunnel):
if ('deleted' in tunnel or 'encapsulation_changed' in tunnel or encap in
['gretap', 'ip6gretap', 'erspan', 'ip6erspan'] or remote in ['any'] or
'key_changed' in tunnel):
- if interface in interfaces():
+ if interface_exists(interface):
tmp = Interface(interface)
tmp.remove()
if 'deleted' in tunnel:
diff --git a/src/conf_mode/interfaces_virtual-ethernet.py b/src/conf_mode/interfaces_virtual-ethernet.py
index 8efe89c41..cb6104f59 100755
--- a/src/conf_mode/interfaces_virtual-ethernet.py
+++ b/src/conf_mode/interfaces_virtual-ethernet.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2022 VyOS maintainers and contributors
+# Copyright (C) 2022-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
@@ -16,7 +16,6 @@
from sys import exit
-from netifaces import interfaces
from vyos import ConfigError
from vyos import airbag
from vyos.config import Config
@@ -25,7 +24,7 @@ from vyos.configverify import verify_address
from vyos.configverify import verify_bridge_delete
from vyos.configverify import verify_vrf
from vyos.ifconfig import VethIf
-
+from vyos.utils.network import interface_exists
airbag.enable()
def get_config(config=None):
@@ -92,8 +91,8 @@ def generate(peth):
def apply(veth):
# Check if the Veth interface already exists
if 'rebuild_required' in veth or 'deleted' in veth:
- if veth['ifname'] in interfaces():
- p = VethIf(veth['ifname'])
+ if interface_exists(veth['ifname']):
+ p = VethIf(**veth)
p.remove()
if 'deleted' not in veth:
diff --git a/src/conf_mode/interfaces_vti.py b/src/conf_mode/interfaces_vti.py
index 9871810ae..e6a833df7 100755
--- a/src/conf_mode/interfaces_vti.py
+++ b/src/conf_mode/interfaces_vti.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2021 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
@@ -14,14 +14,12 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-from netifaces import interfaces
from sys import exit
from vyos.config import Config
from vyos.configdict import get_interface_dict
from vyos.configverify import verify_mirror_redirect
from vyos.ifconfig import VTIIf
-from vyos.utils.dict import dict_search
from vyos import ConfigError
from vyos import airbag
airbag.enable()
diff --git a/src/conf_mode/interfaces_vxlan.py b/src/conf_mode/interfaces_vxlan.py
index 4251e611b..39365968a 100755
--- a/src/conf_mode/interfaces_vxlan.py
+++ b/src/conf_mode/interfaces_vxlan.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2019-2023 VyOS maintainers and contributors
+# Copyright (C) 2019-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
@@ -14,10 +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
-
from sys import exit
-from netifaces import interfaces
from vyos.base import Warning
from vyos.config import Config
@@ -35,6 +32,7 @@ from vyos.ifconfig import Interface
from vyos.ifconfig import VXLANIf
from vyos.template import is_ipv6
from vyos.utils.dict import dict_search
+from vyos.utils.network import interface_exists
from vyos import ConfigError
from vyos import airbag
airbag.enable()
@@ -212,8 +210,8 @@ def generate(vxlan):
def apply(vxlan):
# Check if the VXLAN interface already exists
if 'rebuild_required' in vxlan or 'delete' in vxlan:
- if vxlan['ifname'] in interfaces():
- v = VXLANIf(vxlan['ifname'])
+ if interface_exists(vxlan['ifname']):
+ v = VXLANIf(**vxlan)
# VXLAN is super picky and the tunnel always needs to be recreated,
# thus we can simply always delete it first.
v.remove()
diff --git a/src/conf_mode/interfaces_wireguard.py b/src/conf_mode/interfaces_wireguard.py
index 79e5d3f44..0e0b77877 100755
--- a/src/conf_mode/interfaces_wireguard.py
+++ b/src/conf_mode/interfaces_wireguard.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2018-2023 VyOS maintainers and contributors
+# Copyright (C) 2018-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,7 +17,6 @@
from sys import exit
from vyos.config import Config
-from vyos.configdict import dict_merge
from vyos.configdict import get_interface_dict
from vyos.configdict import is_node_changed
from vyos.configverify import verify_vrf
diff --git a/src/conf_mode/load-balancing_reverse-proxy.py b/src/conf_mode/load-balancing_reverse-proxy.py
index 7338fe573..694a4e1ea 100755
--- a/src/conf_mode/load-balancing_reverse-proxy.py
+++ b/src/conf_mode/load-balancing_reverse-proxy.py
@@ -20,6 +20,9 @@ from sys import exit
from shutil import rmtree
from vyos.config import Config
+from vyos.configverify import verify_pki_certificate
+from vyos.configverify import verify_pki_ca_certificate
+from vyos.utils.dict import dict_search
from vyos.utils.process import call
from vyos.utils.network import check_port_availability
from vyos.utils.network import is_listen_port_bind_service
@@ -33,8 +36,7 @@ airbag.enable()
load_balancing_dir = '/run/haproxy'
load_balancing_conf_file = f'{load_balancing_dir}/haproxy.cfg'
systemd_service = 'haproxy.service'
-systemd_override = r'/run/systemd/system/haproxy.service.d/10-override.conf'
-
+systemd_override = '/run/systemd/system/haproxy.service.d/10-override.conf'
def get_config(config=None):
if config:
@@ -54,7 +56,6 @@ def get_config(config=None):
return lb
-
def verify(lb):
if not lb:
return None
@@ -83,6 +84,15 @@ def verify(lb):
if {'send_proxy', 'send_proxy_v2'} <= set(bk_server_conf):
raise ConfigError(f'Cannot use both "send-proxy" and "send-proxy-v2" for server "{bk_server}"')
+ for front, front_config in lb['service'].items():
+ for cert in dict_search('ssl.certificate', front_config) or []:
+ verify_pki_certificate(lb, cert)
+
+ for back, back_config in lb['backend'].items():
+ tmp = dict_search('ssl.ca_certificate', front_config)
+ if tmp: verify_pki_ca_certificate(lb, tmp)
+
+
def generate(lb):
if not lb:
# Delete /run/haproxy/haproxy.cfg
diff --git a/src/conf_mode/nat.py b/src/conf_mode/nat.py
index b3f38c04a..4cd9b570d 100755
--- a/src/conf_mode/nat.py
+++ b/src/conf_mode/nat.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2020-2023 VyOS maintainers and contributors
+# Copyright (C) 2020-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
@@ -14,8 +14,6 @@
# 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 jmespath
-import json
import os
from sys import exit
@@ -223,19 +221,19 @@ def generate(nat):
render(nftables_static_nat_conf, 'firewall/nftables-static-nat.j2', nat)
# dry-run newly generated configuration
- tmp = run(f'nft -c -f {nftables_nat_config}')
+ tmp = run(f'nft --check --file {nftables_nat_config}')
if tmp > 0:
raise ConfigError('Configuration file errors encountered!')
- tmp = run(f'nft -c -f {nftables_static_nat_conf}')
+ tmp = run(f'nft --check --file {nftables_static_nat_conf}')
if tmp > 0:
raise ConfigError('Configuration file errors encountered!')
return None
def apply(nat):
- cmd(f'nft -f {nftables_nat_config}')
- cmd(f'nft -f {nftables_static_nat_conf}')
+ cmd(f'nft --file {nftables_nat_config}')
+ cmd(f'nft --file {nftables_static_nat_conf}')
if not nat or 'deleted' in nat:
os.unlink(nftables_nat_config)
diff --git a/src/conf_mode/nat64.py b/src/conf_mode/nat64.py
index 6026c61d0..c1e7ebf85 100755
--- a/src/conf_mode/nat64.py
+++ b/src/conf_mode/nat64.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2023 VyOS maintainers and contributors
+# Copyright (C) 2023-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
@@ -26,7 +26,6 @@ from json import dumps as json_write
from vyos import ConfigError
from vyos import airbag
from vyos.config import Config
-from vyos.configdict import dict_merge
from vyos.configdict import is_node_changed
from vyos.utils.dict import dict_search
from vyos.utils.file import write_file
diff --git a/src/conf_mode/nat66.py b/src/conf_mode/nat66.py
index 4c1ead258..fe017527d 100755
--- a/src/conf_mode/nat66.py
+++ b/src/conf_mode/nat66.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2020-2023 VyOS maintainers and contributors
+# Copyright (C) 2020-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
@@ -14,8 +14,6 @@
# 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 jmespath
-import json
import os
from sys import exit
@@ -106,7 +104,7 @@ def apply(nat):
if not nat:
return None
- cmd(f'nft -f {nftables_nat66_config}')
+ cmd(f'nft --file {nftables_nat66_config}')
call_dependents()
return None
diff --git a/src/conf_mode/nat_cgnat.py b/src/conf_mode/nat_cgnat.py
new file mode 100755
index 000000000..f41d66c66
--- /dev/null
+++ b/src/conf_mode/nat_cgnat.py
@@ -0,0 +1,288 @@
+#!/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 ipaddress
+import jmespath
+import os
+
+from sys import exit
+
+from vyos.config import Config
+from vyos.template import render
+from vyos.utils.process import cmd
+from vyos.utils.process import run
+from vyos import ConfigError
+from vyos import airbag
+
+airbag.enable()
+
+
+nftables_cgnat_config = '/run/nftables-cgnat.nft'
+
+
+class IPOperations:
+ def __init__(self, ip_prefix: str):
+ self.ip_prefix = ip_prefix
+ self.ip_network = ipaddress.ip_network(ip_prefix) if '/' in ip_prefix else None
+
+ def get_ips_count(self) -> int:
+ """Returns the number of IPs in a prefix or range.
+
+ Example:
+ % ip = IPOperations('192.0.2.0/30')
+ % ip.get_ips_count()
+ 4
+ % ip = IPOperations('192.0.2.0-192.0.2.2')
+ % ip.get_ips_count()
+ 3
+ """
+ if '-' in self.ip_prefix:
+ start_ip, end_ip = self.ip_prefix.split('-')
+ start_ip = ipaddress.ip_address(start_ip)
+ end_ip = ipaddress.ip_address(end_ip)
+ return int(end_ip) - int(start_ip) + 1
+ elif '/31' in self.ip_prefix:
+ return 2
+ elif '/32' in self.ip_prefix:
+ return 1
+ else:
+ return sum(
+ 1
+ for _ in [self.ip_network.network_address]
+ + list(self.ip_network.hosts())
+ + [self.ip_network.broadcast_address]
+ )
+
+ def convert_prefix_to_list_ips(self) -> list:
+ """Converts a prefix or IP range to a list of IPs including the network and broadcast addresses.
+
+ Example:
+ % ip = IPOperations('192.0.2.0/30')
+ % ip.convert_prefix_to_list_ips()
+ ['192.0.2.0', '192.0.2.1', '192.0.2.2', '192.0.2.3']
+ %
+ % ip = IPOperations('192.0.0.1-192.0.2.5')
+ % ip.convert_prefix_to_list_ips()
+ ['192.0.2.1', '192.0.2.2', '192.0.2.3', '192.0.2.4', '192.0.2.5']
+ """
+ if '-' in self.ip_prefix:
+ start_ip, end_ip = self.ip_prefix.split('-')
+ start_ip = ipaddress.ip_address(start_ip)
+ end_ip = ipaddress.ip_address(end_ip)
+ return [
+ str(ipaddress.ip_address(ip))
+ for ip in range(int(start_ip), int(end_ip) + 1)
+ ]
+ elif '/31' in self.ip_prefix:
+ return [
+ str(ip)
+ for ip in [
+ self.ip_network.network_address,
+ self.ip_network.broadcast_address,
+ ]
+ ]
+ elif '/32' in self.ip_prefix:
+ return [str(self.ip_network.network_address)]
+ else:
+ return [
+ str(ip)
+ for ip in [self.ip_network.network_address]
+ + list(self.ip_network.hosts())
+ + [self.ip_network.broadcast_address]
+ ]
+
+
+def generate_port_rules(
+ external_hosts: list,
+ internal_hosts: list,
+ port_count: int,
+ global_port_range: str = '1024-65535',
+) -> list:
+ """Generates list of nftables rules for the batch file."""
+ rules = []
+ proto_map_elements = []
+ other_map_elements = []
+ start_port, end_port = map(int, global_port_range.split('-'))
+ total_possible_ports = (end_port - start_port) + 1
+
+ # Calculate the required number of ports per host
+ required_ports_per_host = port_count
+
+ # Check if there are enough external addresses for all internal hosts
+ if required_ports_per_host * len(internal_hosts) > total_possible_ports * len(
+ external_hosts
+ ):
+ raise ConfigError("Not enough ports available for the specified parameters!")
+
+ current_port = start_port
+ current_external_index = 0
+
+ for internal_host in internal_hosts:
+ external_host = external_hosts[current_external_index]
+ next_end_port = current_port + required_ports_per_host - 1
+
+ # If the port range exceeds the end_port, move to the next external host
+ while next_end_port > end_port:
+ current_external_index = (current_external_index + 1) % len(external_hosts)
+ external_host = external_hosts[current_external_index]
+ current_port = start_port
+ next_end_port = current_port + required_ports_per_host - 1
+
+ # Ensure the same port is not assigned to the same external host
+ if any(
+ rule.endswith(f'{external_host}:{current_port}-{next_end_port}')
+ for rule in rules
+ ):
+ raise ConfigError("Not enough ports available for the specified parameters")
+
+ proto_map_elements.append(
+ f'{internal_host} : {external_host} . {current_port}-{next_end_port}'
+ )
+ other_map_elements.append(f'{internal_host} : {external_host}')
+
+ current_port = next_end_port + 1
+ if current_port > end_port:
+ current_port = start_port
+ current_external_index += 1 # Move to the next external host
+
+ return [proto_map_elements, other_map_elements]
+
+
+def get_config(config=None):
+ if config:
+ conf = config
+ else:
+ conf = Config()
+
+ base = ['nat', 'cgnat']
+ config = conf.get_config_dict(
+ base,
+ get_first_key=True,
+ key_mangling=('-', '_'),
+ no_tag_node_value_mangle=True,
+ with_recursive_defaults=True,
+ )
+
+ return config
+
+
+def verify(config):
+ # bail out early - looks like removal from running config
+ if not config:
+ return None
+
+ if 'pool' not in config:
+ raise ConfigError(f'Pool must be defined!')
+ if 'rule' not in config:
+ raise ConfigError(f'Rule must be defined!')
+
+ # As PoC allow only one rule for CGNAT translations
+ # one internal pool and one external pool
+ if len(config['rule']) > 1:
+ raise ConfigError(f'Only one rule is allowed for translations!')
+
+ for pool in ('external', 'internal'):
+ if pool not in config['pool']:
+ raise ConfigError(f'{pool} pool must be defined!')
+ for pool_name, pool_config in config['pool'][pool].items():
+ if 'range' not in pool_config:
+ raise ConfigError(
+ f'Range for "{pool} pool {pool_name}" must be defined!'
+ )
+
+ for rule, rule_config in config['rule'].items():
+ if 'source' not in rule_config:
+ raise ConfigError(f'Rule "{rule}" source pool must be defined!')
+ if 'pool' not in rule_config['source']:
+ raise ConfigError(f'Rule "{rule}" source pool must be defined!')
+
+ if 'translation' not in rule_config:
+ raise ConfigError(f'Rule "{rule}" translation pool must be defined!')
+
+
+def generate(config):
+ if not config:
+ return None
+ # first external pool as we allow only one as PoC
+ ext_pool_name = jmespath.search("rule.*.translation | [0]", config).get('pool')
+ int_pool_name = jmespath.search("rule.*.source | [0]", config).get('pool')
+ ext_query = f"pool.external.{ext_pool_name}.range | keys(@)"
+ int_query = f"pool.internal.{int_pool_name}.range"
+ external_ranges = jmespath.search(ext_query, config)
+ internal_ranges = [jmespath.search(int_query, config)]
+
+ external_list_count = []
+ external_list_hosts = []
+ internal_list_count = []
+ internal_list_hosts = []
+ for ext_range in external_ranges:
+ # External hosts count
+ e_count = IPOperations(ext_range).get_ips_count()
+ external_list_count.append(e_count)
+ # External hosts list
+ e_hosts = IPOperations(ext_range).convert_prefix_to_list_ips()
+ external_list_hosts.extend(e_hosts)
+ for int_range in internal_ranges:
+ # Internal hosts count
+ i_count = IPOperations(int_range).get_ips_count()
+ internal_list_count.append(i_count)
+ # Internal hosts list
+ i_hosts = IPOperations(int_range).convert_prefix_to_list_ips()
+ internal_list_hosts.extend(i_hosts)
+
+ external_host_count = sum(external_list_count)
+ internal_host_count = sum(internal_list_count)
+ ports_per_user = int(
+ jmespath.search(f'pool.external.{ext_pool_name}.per_user_limit.port', config)
+ )
+ external_port_range: str = jmespath.search(
+ f'pool.external.{ext_pool_name}.external_port_range', config
+ )
+
+ proto_maps, other_maps = generate_port_rules(
+ external_list_hosts, internal_list_hosts, ports_per_user, external_port_range
+ )
+
+ config['proto_map_elements'] = ', '.join(proto_maps)
+ config['other_map_elements'] = ', '.join(other_maps)
+
+ render(nftables_cgnat_config, 'firewall/nftables-cgnat.j2', config)
+
+ # dry-run newly generated configuration
+ tmp = run(f'nft --check --file {nftables_cgnat_config}')
+ if tmp > 0:
+ raise ConfigError('Configuration file errors encountered!')
+
+
+def apply(config):
+ if not config:
+ # Cleanup cgnat
+ cmd('nft delete table ip cgnat')
+ if os.path.isfile(nftables_cgnat_config):
+ os.unlink(nftables_cgnat_config)
+ return None
+ cmd(f'nft --file {nftables_cgnat_config}')
+
+
+if __name__ == '__main__':
+ try:
+ c = get_config()
+ verify(c)
+ generate(c)
+ apply(c)
+ except ConfigError as e:
+ print(e)
+ exit(1)
diff --git a/src/conf_mode/netns.py b/src/conf_mode/netns.py
index 7cee33bc6..b57e46a0d 100755
--- a/src/conf_mode/netns.py
+++ b/src/conf_mode/netns.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2021 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,14 +17,11 @@
import os
from sys import exit
-from tempfile import NamedTemporaryFile
from vyos.config import Config
from vyos.configdict import node_changed
-from vyos.ifconfig import Interface
from vyos.utils.process import call
from vyos.utils.dict import dict_search
-from vyos.utils.network import get_interface_config
from vyos import ConfigError
from vyos import airbag
airbag.enable()
diff --git a/src/conf_mode/policy_local-route.py b/src/conf_mode/policy_local-route.py
index 91e4fce2c..f458f4e82 100755
--- a/src/conf_mode/policy_local-route.py
+++ b/src/conf_mode/policy_local-route.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2020-2023 VyOS maintainers and contributors
+# Copyright (C) 2020-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
@@ -14,23 +14,19 @@
# 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
-
from itertools import product
from sys import exit
-from netifaces import interfaces
from vyos.config import Config
from vyos.configdict import dict_merge
from vyos.configdict import node_changed
from vyos.configdict import leaf_node_changed
-from vyos.template import render
+from vyos.configverify import verify_interface_exists
from vyos.utils.process import call
from vyos import ConfigError
from vyos import airbag
airbag.enable()
-
def get_config(config=None):
if config:
@@ -227,8 +223,7 @@ def verify(pbr):
if 'inbound_interface' in pbr_route['rule'][rule]:
interface = pbr_route['rule'][rule]['inbound_interface']
- if interface not in interfaces():
- raise ConfigError(f'Interface "{interface}" does not exist')
+ verify_interface_exists(interface)
return None
diff --git a/src/conf_mode/policy_route.py b/src/conf_mode/policy_route.py
index 6d7a06714..c58fe1bce 100755
--- a/src/conf_mode/policy_route.py
+++ b/src/conf_mode/policy_route.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
@@ -177,7 +177,7 @@ def cleanup_table_marks():
cmd(f'{cmd_str} rule del fwmark {fwmark} table {table}')
def apply(policy):
- install_result = run(f'nft -f {nftables_conf}')
+ install_result = run(f'nft --file {nftables_conf}')
if install_result == 1:
raise ConfigError('Failed to apply policy based routing')
diff --git a/src/conf_mode/protocols_babel.py b/src/conf_mode/protocols_babel.py
index 104711b55..90b6e4a31 100755
--- a/src/conf_mode/protocols_babel.py
+++ b/src/conf_mode/protocols_babel.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
@@ -14,15 +14,12 @@
# 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
-
from sys import exit
from vyos.config import Config
from vyos.config import config_dict_merge
from vyos.configdict import dict_merge
from vyos.configdict import node_changed
-from vyos.configverify import verify_common_route_maps
from vyos.configverify import verify_access_list
from vyos.configverify import verify_prefix_list
from vyos.utils.dict import dict_search
diff --git a/src/conf_mode/protocols_bfd.py b/src/conf_mode/protocols_bfd.py
index 37421efb4..1c01a9013 100755
--- a/src/conf_mode/protocols_bfd.py
+++ b/src/conf_mode/protocols_bfd.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2019-2021 VyOS maintainers and contributors
+# Copyright (C) 2019-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
@@ -14,8 +14,6 @@
# 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
-
from vyos.config import Config
from vyos.configverify import verify_vrf
from vyos.template import is_ipv6
diff --git a/src/conf_mode/protocols_bgp.py b/src/conf_mode/protocols_bgp.py
index f1c59cbde..2b16de775 100755
--- a/src/conf_mode/protocols_bgp.py
+++ b/src/conf_mode/protocols_bgp.py
@@ -285,6 +285,7 @@ def verify(bgp):
elif tmp != 'default':
raise ConfigError(f'{error_msg} "{tmp}"!')
+ peer_groups_context = dict()
# Common verification for both peer-group and neighbor statements
for neighbor in ['neighbor', 'peer_group']:
# bail out early if there is no neighbor or peer-group statement
@@ -301,6 +302,18 @@ def verify(bgp):
raise ConfigError(f'Specified peer-group "{peer_group}" for '\
f'neighbor "{neighbor}" does not exist!')
+ if 'remote_as' in peer_config:
+ is_ibgp = True
+ if peer_config['remote_as'] != 'internal' and \
+ peer_config['remote_as'] != bgp['system_as']:
+ is_ibgp = False
+
+ if peer_group not in peer_groups_context:
+ peer_groups_context[peer_group] = is_ibgp
+ elif peer_groups_context[peer_group] != is_ibgp:
+ raise ConfigError(f'Peer-group members must be '
+ f'all internal or all external')
+
if 'local_role' in peer_config:
#Ensure Local Role has only one value.
if len(peer_config['local_role']) > 1:
@@ -450,15 +463,15 @@ def verify(bgp):
verify_route_map(afi_config['route_map'][tmp], bgp)
if 'route_reflector_client' in afi_config:
- if 'remote_as' in peer_config and peer_config['remote_as'] != 'internal' and peer_config['remote_as'] != bgp['system_as']:
+ peer_group_as = peer_config.get('remote_as')
+
+ if peer_group_as is None or (peer_group_as != 'internal' and peer_group_as != bgp['system_as']):
raise ConfigError('route-reflector-client only supported for iBGP peers')
else:
if 'peer_group' in peer_config:
peer_group_as = dict_search(f'peer_group.{peer_group}.remote_as', bgp)
- if peer_group_as != None and peer_group_as != 'internal' and peer_group_as != bgp['system_as']:
+ if peer_group_as is None or (peer_group_as != 'internal' and peer_group_as != bgp['system_as']):
raise ConfigError('route-reflector-client only supported for iBGP peers')
- else:
- raise ConfigError('route-reflector-client only supported for iBGP peers')
# Throw an error if a peer group is not configured for allow range
for prefix in dict_search('listen.range', bgp) or []:
diff --git a/src/conf_mode/protocols_igmp-proxy.py b/src/conf_mode/protocols_igmp-proxy.py
index 40db417dd..afcef0985 100755
--- a/src/conf_mode/protocols_igmp-proxy.py
+++ b/src/conf_mode/protocols_igmp-proxy.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2018-2020 VyOS maintainers and contributors
+# Copyright (C) 2018-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,10 +17,10 @@
import os
from sys import exit
-from netifaces import interfaces
from vyos.base import Warning
from vyos.config import Config
+from vyos.configverify import verify_interface_exists
from vyos.template import render
from vyos.utils.process import call
from vyos.utils.dict import dict_search
@@ -65,8 +65,7 @@ def verify(igmp_proxy):
upstream = 0
for interface, config in igmp_proxy['interface'].items():
- if interface not in interfaces():
- raise ConfigError(f'Interface "{interface}" does not exist')
+ verify_interface_exists(interface)
if dict_search('role', config) == 'upstream':
upstream += 1
diff --git a/src/conf_mode/protocols_isis.py b/src/conf_mode/protocols_isis.py
index 6c9925b80..9cadfd081 100755
--- a/src/conf_mode/protocols_isis.py
+++ b/src/conf_mode/protocols_isis.py
@@ -14,8 +14,6 @@
# 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
-
from sys import exit
from sys import argv
diff --git a/src/conf_mode/protocols_nhrp.py b/src/conf_mode/protocols_nhrp.py
index c339c6391..0bd68b7d8 100755
--- a/src/conf_mode/protocols_nhrp.py
+++ b/src/conf_mode/protocols_nhrp.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
@@ -19,7 +19,6 @@ import os
from vyos.config import Config
from vyos.configdict import node_changed
from vyos.template import render
-from vyos.utils.process import process_named_running
from vyos.utils.process import run
from vyos import ConfigError
from vyos import airbag
@@ -93,7 +92,7 @@ def generate(nhrp):
return None
def apply(nhrp):
- nft_rc = run(f'nft -f {nhrp_nftables_conf}')
+ nft_rc = run(f'nft --file {nhrp_nftables_conf}')
if nft_rc != 0:
raise ConfigError('Failed to apply NHRP tunnel firewall rules')
diff --git a/src/conf_mode/protocols_ospfv3.py b/src/conf_mode/protocols_ospfv3.py
index afd767dbf..1bb172293 100755
--- a/src/conf_mode/protocols_ospfv3.py
+++ b/src/conf_mode/protocols_ospfv3.py
@@ -14,8 +14,6 @@
# 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
-
from sys import exit
from sys import argv
diff --git a/src/conf_mode/protocols_rip.py b/src/conf_mode/protocols_rip.py
index bd47dfd00..9afac544d 100755
--- a/src/conf_mode/protocols_rip.py
+++ b/src/conf_mode/protocols_rip.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2021-2022 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
@@ -14,8 +14,6 @@
# 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
-
from sys import exit
from vyos.config import Config
diff --git a/src/conf_mode/protocols_ripng.py b/src/conf_mode/protocols_ripng.py
index dd1550033..23416ff96 100755
--- a/src/conf_mode/protocols_ripng.py
+++ b/src/conf_mode/protocols_ripng.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2021 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
@@ -14,8 +14,6 @@
# 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
-
from sys import exit
from vyos.config import Config
diff --git a/src/conf_mode/protocols_segment-routing.py b/src/conf_mode/protocols_segment-routing.py
index d865c2ac0..b36c2ca11 100755
--- a/src/conf_mode/protocols_segment-routing.py
+++ b/src/conf_mode/protocols_segment-routing.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2023 VyOS maintainers and contributors
+# Copyright (C) 2023-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
@@ -14,8 +14,6 @@
# 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
-
from sys import exit
from vyos.config import Config
diff --git a/src/conf_mode/protocols_static.py b/src/conf_mode/protocols_static.py
index 5def8d645..a2373218a 100755
--- a/src/conf_mode/protocols_static.py
+++ b/src/conf_mode/protocols_static.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
@@ -14,8 +14,6 @@
# 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
-
from sys import exit
from sys import argv
diff --git a/src/conf_mode/protocols_static_neighbor-proxy.py b/src/conf_mode/protocols_static_neighbor-proxy.py
index 10cc1e748..8a1ea1df9 100755
--- a/src/conf_mode/protocols_static_neighbor-proxy.py
+++ b/src/conf_mode/protocols_static_neighbor-proxy.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2023 VyOS maintainers and contributors
+# Copyright (C) 2023-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
@@ -14,19 +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 os
-
from sys import exit
from vyos.config import Config
-from vyos.configdict import node_changed
from vyos.utils.process import call
from vyos import ConfigError
from vyos import airbag
-
airbag.enable()
-
def get_config(config=None):
if config:
conf = config
@@ -38,9 +33,7 @@ def get_config(config=None):
return config
-
def verify(config):
-
if 'arp' in config:
for neighbor, neighbor_conf in config['arp'].items():
if 'interface' not in neighbor_conf:
@@ -55,11 +48,9 @@ def verify(config):
f"ARP neighbor-proxy for '{neighbor}' requires an interface to be set!"
)
-
def generate(config):
pass
-
def apply(config):
if not config:
# Cleanup proxy
@@ -83,7 +74,6 @@ def apply(config):
for interface in neighbor_conf['interface']:
call(f'ip -6 neighbor add proxy {neighbor} dev {interface}')
-
if __name__ == '__main__':
try:
c = get_config()
diff --git a/src/conf_mode/qos.py b/src/conf_mode/qos.py
index 4a0b4d0c5..3dfb4bab8 100755
--- a/src/conf_mode/qos.py
+++ b/src/conf_mode/qos.py
@@ -14,15 +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 os
-
from sys import exit
from netifaces import interfaces
-from vyos.base import Warning
from vyos.config import Config
-from vyos.configdep import set_dependents, call_dependents
+from vyos.configdep import set_dependents
+from vyos.configdep import call_dependents
from vyos.configdict import dict_merge
+from vyos.configverify import verify_interface_exists
from vyos.ifconfig import Section
from vyos.qos import CAKE
from vyos.qos import DropTail
@@ -36,8 +35,8 @@ from vyos.qos import RateLimiter
from vyos.qos import RoundRobin
from vyos.qos import TrafficShaper
from vyos.qos import TrafficShaperHFSC
-from vyos.utils.process import run
from vyos.utils.dict import dict_search_recursive
+from vyos.utils.process import run
from vyos import ConfigError
from vyos import airbag
airbag.enable()
@@ -214,11 +213,10 @@ def apply(qos):
return None
for interface, interface_config in qos['interface'].items():
- if not os.path.exists(f'/sys/class/net/{interface}'):
+ if not verify_interface_exists(interface, warning_only=True):
# When shaper is bound to a dialup (e.g. PPPoE) interface it is
# possible that it is yet not availbale when to QoS code runs.
- # Skip the configuration and inform the user
- Warning(f'Interface "{interface}" does not exist!')
+ # Skip the configuration and inform the user via warning_only=True
continue
for direction in ['egress', 'ingress']:
diff --git a/src/conf_mode/service_dhcp-server.py b/src/conf_mode/service_dhcp-server.py
index ba3d69b07..e89448e2d 100755
--- a/src/conf_mode/service_dhcp-server.py
+++ b/src/conf_mode/service_dhcp-server.py
@@ -16,6 +16,7 @@
import os
+from glob import glob
from ipaddress import ip_address
from ipaddress import ip_network
from netaddr import IPRange
@@ -28,6 +29,7 @@ from vyos.template import render
from vyos.utils.dict import dict_search
from vyos.utils.dict import dict_search_args
from vyos.utils.file import chmod_775
+from vyos.utils.file import chown
from vyos.utils.file import makedir
from vyos.utils.file import write_file
from vyos.utils.process import call
@@ -42,6 +44,7 @@ ctrl_config_file = '/run/kea/kea-ctrl-agent.conf'
ctrl_socket = '/run/kea/dhcp4-ctrl-socket'
config_file = '/run/kea/kea-dhcp4.conf'
lease_file = '/config/dhcp/dhcp4-leases.csv'
+lease_file_glob = '/config/dhcp/dhcp4-leases*'
systemd_override = r'/run/systemd/system/kea-ctrl-agent.service.d/10-override.conf'
user_group = '_kea'
@@ -143,8 +146,12 @@ def get_config(config=None):
dhcp['shared_network_name'][network]['subnet'][subnet].update(
{'range' : new_range_dict})
- if dict_search('high_availability.certificate', dhcp):
- dhcp['pki'] = conf.get_config_dict(['pki'], key_mangling=('-', '_'), get_first_key=True, no_tag_node_value_mangle=True)
+ if len(dhcp['high_availability']) == 1:
+ ## only default value for mode is set, need to remove ha node
+ del dhcp['high_availability']
+ else:
+ if dict_search('high_availability.certificate', dhcp):
+ dhcp['pki'] = conf.get_config_dict(['pki'], key_mangling=('-', '_'), get_first_key=True, no_tag_node_value_mangle=True)
return dhcp
@@ -161,7 +168,6 @@ def verify(dhcp):
# Inspect shared-network/subnet
listen_ok = False
subnets = []
- failover_ok = False
shared_networks = len(dhcp['shared_network_name'])
disabled_shared_networks = 0
@@ -316,7 +322,7 @@ def verify(dhcp):
raise ConfigError(f'Invalid CA certificate specified for DHCP high-availability')
for address in (dict_search('listen_address', dhcp) or []):
- if is_addr_assigned(address):
+ if is_addr_assigned(address, include_vrf=True):
listen_ok = True
# no need to probe further networks, we have one that is valid
continue
@@ -351,6 +357,10 @@ def generate(dhcp):
makedir(lease_dir, group='vyattacfg')
chmod_775(lease_dir)
+ # Ensure correct permissions on lease files + backups
+ for file in glob(lease_file_glob):
+ chown(file, user=user_group, group='vyattacfg')
+
# Create lease file if necessary and let kea own it - 'kea-lfc' expects it that way
if not os.path.exists(lease_file):
write_file(lease_file, '', user=user_group, group=user_group, mode=0o644)
diff --git a/src/conf_mode/service_dhcpv6-server.py b/src/conf_mode/service_dhcpv6-server.py
index add83eb0d..c7333dd3a 100755
--- a/src/conf_mode/service_dhcpv6-server.py
+++ b/src/conf_mode/service_dhcpv6-server.py
@@ -16,6 +16,7 @@
import os
+from glob import glob
from ipaddress import ip_address
from ipaddress import ip_network
from sys import exit
@@ -24,6 +25,7 @@ from vyos.config import Config
from vyos.template import render
from vyos.utils.process import call
from vyos.utils.file import chmod_775
+from vyos.utils.file import chown
from vyos.utils.file import makedir
from vyos.utils.file import write_file
from vyos.utils.dict import dict_search
@@ -35,6 +37,7 @@ airbag.enable()
config_file = '/run/kea/kea-dhcp6.conf'
ctrl_socket = '/run/kea/dhcp6-ctrl-socket'
lease_file = '/config/dhcp/dhcp6-leases.csv'
+lease_file_glob = '/config/dhcp/dhcp6-leases*'
user_group = '_kea'
def get_config(config=None):
@@ -224,6 +227,10 @@ def generate(dhcpv6):
makedir(lease_dir, group='vyattacfg')
chmod_775(lease_dir)
+ # Ensure correct permissions on lease files + backups
+ for file in glob(lease_file_glob):
+ chown(file, user=user_group, group='vyattacfg')
+
# Create lease file if necessary and let kea own it - 'kea-lfc' expects it that way
if not os.path.exists(lease_file):
write_file(lease_file, '', user=user_group, group=user_group, mode=0o644)
diff --git a/src/conf_mode/service_dns_forwarding.py b/src/conf_mode/service_dns_forwarding.py
index ecad765f4..7e863073a 100755
--- a/src/conf_mode/service_dns_forwarding.py
+++ b/src/conf_mode/service_dns_forwarding.py
@@ -16,7 +16,6 @@
import os
-from netifaces import interfaces
from sys import exit
from glob import glob
@@ -24,9 +23,9 @@ from vyos.config import Config
from vyos.hostsd_client import Client as hostsd_client
from vyos.template import render
from vyos.template import bracketize_ipv6
+from vyos.utils.network import interface_exists
from vyos.utils.process import call
from vyos.utils.permission import chown
-
from vyos import ConfigError
from vyos import airbag
airbag.enable()
@@ -330,7 +329,7 @@ def apply(dns):
# names (DHCP) to use DNS servers. We need to check if the
# value is an interface name - only if this is the case, add the
# interface based DNS forwarder.
- if interface in interfaces():
+ if interface_exists(interface):
hc.add_name_server_tags_recursor(['dhcp-' + interface,
'dhcpv6-' + interface ])
diff --git a/src/conf_mode/service_https.py b/src/conf_mode/service_https.py
index 46efc3c93..9e58b4c72 100755
--- a/src/conf_mode/service_https.py
+++ b/src/conf_mode/service_https.py
@@ -24,13 +24,14 @@ from time import sleep
from vyos.base import Warning
from vyos.config import Config
from vyos.config import config_dict_merge
-from vyos.configdiff import get_config_diff
from vyos.configverify import verify_vrf
+from vyos.configverify import verify_pki_certificate
+from vyos.configverify import verify_pki_ca_certificate
+from vyos.configverify import verify_pki_dh_parameters
from vyos.defaults import api_config_state
from vyos.pki import wrap_certificate
from vyos.pki import wrap_private_key
from vyos.pki import wrap_dh_parameters
-from vyos.pki import load_dh_parameters
from vyos.template import render
from vyos.utils.dict import dict_search
from vyos.utils.process import call
@@ -84,33 +85,14 @@ def verify(https):
if https is None:
return None
- if 'certificates' in https and 'certificate' in https['certificates']:
- cert_name = https['certificates']['certificate']
- if 'pki' not in https:
- raise ConfigError('PKI is not configured!')
-
- if cert_name not in https['pki']['certificate']:
- raise ConfigError('Invalid certificate in configuration!')
+ if dict_search('certificates.certificate', https) != None:
+ verify_pki_certificate(https, https['certificates']['certificate'])
- pki_cert = https['pki']['certificate'][cert_name]
-
- if 'certificate' not in pki_cert:
- raise ConfigError('Missing certificate in configuration!')
+ tmp = dict_search('certificates.ca_certificate', https)
+ if tmp != None: verify_pki_ca_certificate(https, tmp)
- if 'private' not in pki_cert or 'key' not in pki_cert['private']:
- raise ConfigError('Missing certificate private key in configuration!')
-
- if 'dh_params' in https['certificates']:
- dh_name = https['certificates']['dh_params']
- if dh_name not in https['pki']['dh']:
- raise ConfigError('Invalid DH parameter in configuration!')
-
- pki_dh = https['pki']['dh'][dh_name]
- dh_params = load_dh_parameters(pki_dh['parameters'])
- dh_numbers = dh_params.parameter_numbers()
- dh_bits = dh_numbers.p.bit_length()
- if dh_bits < 2048:
- raise ConfigError(f'Minimum DH key-size is 2048 bits')
+ tmp = dict_search('certificates.dh_params', https)
+ if tmp != None: verify_pki_dh_parameters(https, tmp, 2048)
else:
Warning('No certificate specified, using build-in self-signed certificates. '\
@@ -214,7 +196,8 @@ def apply(https):
https_service_name = 'nginx.service'
if https is None:
- call(f'systemctl stop {http_api_service_name}')
+ if is_systemd_service_active(http_api_service_name):
+ call(f'systemctl stop {http_api_service_name}')
call(f'systemctl stop {https_service_name}')
return
diff --git a/src/conf_mode/service_ipoe-server.py b/src/conf_mode/service_ipoe-server.py
index 852b714eb..11e950782 100755
--- a/src/conf_mode/service_ipoe-server.py
+++ b/src/conf_mode/service_ipoe-server.py
@@ -68,8 +68,8 @@ def verify(ipoe):
for interface, iface_config in ipoe['interface'].items():
verify_interface_exists(interface)
if 'client_subnet' in iface_config and 'vlan' in iface_config:
- raise ConfigError('Option "client-subnet" incompatible with "vlan"!'
- 'Use "ipoe client-ip-pool" instead.')
+ raise ConfigError('Option "client-subnet" and "vlan" are mutually exclusive, '
+ 'use "client-ip-pool" instead!')
verify_accel_ppp_authentication(ipoe, local_users=False)
verify_accel_ppp_ip_pool(ipoe)
diff --git a/src/conf_mode/service_lldp.py b/src/conf_mode/service_lldp.py
index 3c647a0e8..04b1db880 100755
--- a/src/conf_mode/service_lldp.py
+++ b/src/conf_mode/service_lldp.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2017-2022 VyOS maintainers and contributors
+# Copyright (C) 2017-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
@@ -24,7 +24,6 @@ from vyos.utils.network import is_addr_assigned
from vyos.utils.network import is_loopback_addr
from vyos.version import get_version_data
from vyos.utils.process import call
-from vyos.utils.dict import dict_search
from vyos.template import render
from vyos import ConfigError
from vyos import airbag
diff --git a/src/conf_mode/service_mdns_repeater.py b/src/conf_mode/service_mdns_repeater.py
index 6526c23d1..207da5e03 100755
--- a/src/conf_mode/service_mdns_repeater.py
+++ b/src/conf_mode/service_mdns_repeater.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2017-2022 VyOS maintainers and contributors
+# Copyright (C) 2017-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
@@ -18,9 +18,10 @@ import os
from json import loads
from sys import exit
-from netifaces import ifaddresses, interfaces, AF_INET, AF_INET6
+from netifaces import ifaddresses, AF_INET, AF_INET6
from vyos.config import Config
+from vyos.configverify import verify_interface_exists
from vyos.ifconfig.vrrp import VRRP
from vyos.template import render
from vyos.utils.process import call
@@ -64,8 +65,7 @@ def verify(mdns):
# For mdns-repeater to work it is essential that the interfaces has
# an IPv4 address assigned
for interface in mdns['interface']:
- if interface not in interfaces():
- raise ConfigError(f'Interface "{interface}" does not exist!')
+ verify_interface_exists(interface)
if mdns['ip_version'] in ['ipv4', 'both'] and AF_INET not in ifaddresses(interface):
raise ConfigError('mDNS repeater requires an IPv4 address to be '
diff --git a/src/conf_mode/service_pppoe-server.py b/src/conf_mode/service_pppoe-server.py
index c9d1e805f..b9d174933 100755
--- a/src/conf_mode/service_pppoe-server.py
+++ b/src/conf_mode/service_pppoe-server.py
@@ -38,6 +38,16 @@ airbag.enable()
pppoe_conf = r'/run/accel-pppd/pppoe.conf'
pppoe_chap_secrets = r'/run/accel-pppd/pppoe.chap-secrets'
+def convert_pado_delay(pado_delay):
+ new_pado_delay = {'delays_without_sessions': [],
+ 'delays_with_sessions': []}
+ for delay, sessions in pado_delay.items():
+ if not sessions:
+ new_pado_delay['delays_without_sessions'].append(delay)
+ else:
+ new_pado_delay['delays_with_sessions'].append((delay, int(sessions['sessions'])))
+ return new_pado_delay
+
def get_config(config=None):
if config:
conf = config
@@ -54,6 +64,10 @@ def get_config(config=None):
# Multiple named pools require ordered values T5099
pppoe['ordered_named_pools'] = get_pools_in_order(dict_search('client_ip_pool', pppoe))
+ if dict_search('pado_delay', pppoe):
+ pado_delay = dict_search('pado_delay', pppoe)
+ pppoe['pado_delay'] = convert_pado_delay(pado_delay)
+
# reload-or-restart does not implemented in accel-ppp
# use this workaround until it will be implemented
# https://phabricator.accel-ppp.org/T3
@@ -65,6 +79,17 @@ def get_config(config=None):
pppoe['server_type'] = 'pppoe'
return pppoe
+def verify_pado_delay(pppoe):
+ if 'pado_delay' in pppoe:
+ pado_delay = pppoe['pado_delay']
+
+ delays_without_sessions = pado_delay['delays_without_sessions']
+ if len(delays_without_sessions) > 1:
+ raise ConfigError(
+ f'Cannot add more then ONE pado-delay without sessions, '
+ f'but {len(delays_without_sessions)} were set'
+ )
+
def verify(pppoe):
if not pppoe:
return None
@@ -73,7 +98,7 @@ def verify(pppoe):
verify_accel_ppp_ip_pool(pppoe)
verify_accel_ppp_name_servers(pppoe)
verify_accel_ppp_wins_servers(pppoe)
-
+ verify_pado_delay(pppoe)
if 'interface' not in pppoe:
raise ConfigError('At least one listen interface must be defined!')
diff --git a/src/conf_mode/service_ssh.py b/src/conf_mode/service_ssh.py
index ee5e1eca2..9abdd33dc 100755
--- a/src/conf_mode/service_ssh.py
+++ b/src/conf_mode/service_ssh.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2018-2022 VyOS maintainers and contributors
+# Copyright (C) 2018-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
@@ -30,7 +30,6 @@ from vyos import airbag
airbag.enable()
config_file = r'/run/sshd/sshd_config'
-systemd_override = r'/run/systemd/system/ssh.service.d/override.conf'
sshguard_config_file = '/etc/sshguard/sshguard.conf'
sshguard_whitelist = '/etc/sshguard/whitelist'
@@ -81,8 +80,6 @@ def generate(ssh):
if not ssh:
if os.path.isfile(config_file):
os.unlink(config_file)
- if os.path.isfile(systemd_override):
- os.unlink(systemd_override)
return None
@@ -99,13 +96,10 @@ def generate(ssh):
call(f'ssh-keygen -q -N "" -t ed25519 -f {key_ed25519}')
render(config_file, 'ssh/sshd_config.j2', ssh)
- render(systemd_override, 'ssh/override.conf.j2', ssh)
if 'dynamic_protection' in ssh:
render(sshguard_config_file, 'ssh/sshguard_config.j2', ssh)
render(sshguard_whitelist, 'ssh/sshguard_whitelist.j2', ssh)
- # Reload systemd manager configuration
- call('systemctl daemon-reload')
return None
@@ -114,7 +108,7 @@ def apply(ssh):
systemd_service_sshguard = 'sshguard.service'
if not ssh:
# SSH access is removed in the commit
- call(f'systemctl stop {systemd_service_ssh}')
+ call(f'systemctl stop ssh@*.service')
call(f'systemctl stop {systemd_service_sshguard}')
return None
@@ -126,9 +120,13 @@ def apply(ssh):
# we need to restart the service if e.g. the VRF name changed
systemd_action = 'reload-or-restart'
if 'restart_required' in ssh:
+ # this is only true if something for the VRFs changed, thus we
+ # stop all VRF services and only restart then new ones
+ call(f'systemctl stop ssh@*.service')
systemd_action = 'restart'
- call(f'systemctl {systemd_action} {systemd_service_ssh}')
+ for vrf in ssh['vrf']:
+ call(f'systemctl {systemd_action} ssh@{vrf}.service')
return None
if __name__ == '__main__':
diff --git a/src/conf_mode/service_tftp-server.py b/src/conf_mode/service_tftp-server.py
index 3ad346e2e..5b7303c40 100755
--- a/src/conf_mode/service_tftp-server.py
+++ b/src/conf_mode/service_tftp-server.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2018-2020 VyOS maintainers and contributors
+# Copyright (C) 2018-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
@@ -15,7 +15,6 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
-import stat
import pwd
from copy import deepcopy
diff --git a/src/conf_mode/service_upnp.py b/src/conf_mode/service_upnp.py
index cf26bf9ce..0df8dc09e 100755
--- a/src/conf_mode/service_upnp.py
+++ b/src/conf_mode/service_upnp.py
@@ -54,9 +54,7 @@ def get_config(config=None):
def get_all_interface_addr(prefix, filter_dev, filter_family):
list_addr = []
- interfaces = netifaces.interfaces()
-
- for interface in interfaces:
+ for interface in netifaces.interfaces():
if filter_dev and interface in filter_dev:
continue
addrs = netifaces.ifaddresses(interface)
diff --git a/src/conf_mode/system_conntrack.py b/src/conf_mode/system_conntrack.py
index 3d42389f6..031fe63b0 100755
--- a/src/conf_mode/system_conntrack.py
+++ b/src/conf_mode/system_conntrack.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
@@ -15,19 +15,16 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
-import re
from sys import exit
from vyos.config import Config
from vyos.configdep import set_dependents, call_dependents
-from vyos.utils.process import process_named_running
from vyos.utils.dict import dict_search
from vyos.utils.dict import dict_search_args
from vyos.utils.dict import dict_search_recursive
from vyos.utils.process import cmd
from vyos.utils.process import rc_cmd
-from vyos.utils.process import run
from vyos.template import render
from vyos import ConfigError
from vyos import airbag
@@ -223,7 +220,7 @@ def apply(conntrack):
cmd(f'modprobe -a {module_str}')
# Load new nftables ruleset
- install_result, output = rc_cmd(f'nft -f {nftables_ct_file}')
+ install_result, output = rc_cmd(f'nft --file {nftables_ct_file}')
if install_result == 1:
raise ConfigError(f'Failed to apply configuration: {output}')
diff --git a/src/conf_mode/system_console.py b/src/conf_mode/system_console.py
index a888b125e..19bbb8875 100755
--- a/src/conf_mode/system_console.py
+++ b/src/conf_mode/system_console.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2020-2023 VyOS maintainers and contributors
+# Copyright (C) 2020-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
@@ -15,13 +15,10 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
-import re
from pathlib import Path
from vyos.config import Config
from vyos.utils.process import call
-from vyos.utils.file import read_file
-from vyos.utils.file import write_file
from vyos.system import grub_util
from vyos.template import render
from vyos import ConfigError
diff --git a/src/conf_mode/system_flow-accounting.py b/src/conf_mode/system_flow-accounting.py
index 206f513c8..2dacd92da 100755
--- a/src/conf_mode/system_flow-accounting.py
+++ b/src/conf_mode/system_flow-accounting.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2018-2023 VyOS maintainers and contributors
+# Copyright (C) 2018-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
@@ -20,11 +20,10 @@ import re
from sys import exit
from ipaddress import ip_address
-from vyos.base import Warning
from vyos.config import Config
from vyos.config import config_dict_merge
from vyos.configverify import verify_vrf
-from vyos.ifconfig import Section
+from vyos.configverify import verify_interface_exists
from vyos.template import render
from vyos.utils.process import call
from vyos.utils.process import cmd
@@ -184,10 +183,7 @@ def verify(flow_config):
# check that all configured interfaces exists in the system
for interface in flow_config['interface']:
- if interface not in Section.interfaces():
- # Changed from error to warning to allow adding dynamic interfaces
- # and interface templates
- Warning(f'Interface "{interface}" is not presented in the system')
+ verify_interface_exists(interface, warning_only=True)
# check sFlow configuration
if 'sflow' in flow_config:
diff --git a/src/conf_mode/system_frr.py b/src/conf_mode/system_frr.py
index 07f291000..d9ac543d0 100755
--- a/src/conf_mode/system_frr.py
+++ b/src/conf_mode/system_frr.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
@@ -14,7 +14,6 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-from pathlib import Path
from sys import exit
from vyos import ConfigError
diff --git a/src/conf_mode/system_host-name.py b/src/conf_mode/system_host-name.py
index 6204cf247..8975cadb6 100755
--- a/src/conf_mode/system_host-name.py
+++ b/src/conf_mode/system_host-name.py
@@ -71,9 +71,9 @@ def get_config(config=None):
hosts['nameserver'].append(ns)
else:
tmp = ''
- if_type = Section.section(ns)
- if conf.exists(['interfaces', if_type, ns, 'address']):
- tmp = conf.return_values(['interfaces', if_type, ns, 'address'])
+ config_path = Section.get_config_path(ns)
+ if conf.exists(['interfaces', config_path, 'address']):
+ tmp = conf.return_values(['interfaces', config_path, 'address'])
hosts['nameservers_dhcp_interfaces'].update({ ns : tmp })
diff --git a/src/conf_mode/system_ip.py b/src/conf_mode/system_ip.py
index 833f89554..2a0bda91a 100755
--- a/src/conf_mode/system_ip.py
+++ b/src/conf_mode/system_ip.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2019-2023 VyOS maintainers and contributors
+# Copyright (C) 2019-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
@@ -22,7 +22,6 @@ from vyos.configverify import verify_route_map
from vyos.template import render_to_string
from vyos.utils.dict import dict_search
from vyos.utils.file import write_file
-from vyos.utils.process import call
from vyos.utils.process import is_systemd_service_active
from vyos.utils.system import sysctl_write
@@ -82,11 +81,6 @@ def apply(opt):
value = '0' if (tmp != None) else '1'
write_file('/proc/sys/net/ipv4/conf/all/forwarding', value)
- # enable/disable IPv4 directed broadcast forwarding
- tmp = dict_search('disable_directed_broadcast', opt)
- value = '0' if (tmp != None) else '1'
- write_file('/proc/sys/net/ipv4/conf/all/bc_forwarding', value)
-
# configure multipath
tmp = dict_search('multipath.ignore_unreachable_nexthops', opt)
value = '1' if (tmp != None) else '0'
diff --git a/src/conf_mode/system_login.py b/src/conf_mode/system_login.py
index 49306c894..20121f170 100755
--- a/src/conf_mode/system_login.py
+++ b/src/conf_mode/system_login.py
@@ -336,27 +336,31 @@ def apply(login):
command += f' --groups frr,frrvty,vyattacfg,sudo,adm,dip,disk,_kea {user}'
try:
cmd(command)
- # we should not rely on the value stored in
- # user_config['home_directory'], as a crazy user will choose
- # username root or any other system user which will fail.
+ # we should not rely on the value stored in user_config['home_directory'], as a
+ # crazy user will choose username root or any other system user which will fail.
#
# XXX: Should we deny using root at all?
home_dir = getpwnam(user).pw_dir
- # T5875: ensure UID is properly set on home directory if user is re-added
- # the home directory will always exist, as it's created above by --create-home,
- # retrieve current owner of home directory and adjust it on demand
- dir_owner = getpwuid(os.stat(home_dir).st_uid).pw_name
- if dir_owner != user:
- chown(home_dir, user=user, recursive=True)
-
+ # always re-render SSH keys with appropriate permissions
render(f'{home_dir}/.ssh/authorized_keys', 'login/authorized_keys.j2',
user_config, permission=0o600,
formater=lambda _: _.replace("&quot;", '"'),
user=user, group='users')
-
except Exception as e:
raise ConfigError(f'Adding user "{user}" raised exception: "{e}"')
+ # T5875: ensure UID is properly set on home directory if user is re-added
+ # the home directory will always exist, as it's created above by --create-home,
+ # retrieve current owner of home directory and adjust on demand
+ dir_owner = None
+ try:
+ dir_owner = getpwuid(os.stat(home_dir).st_uid).pw_name
+ except:
+ pass
+
+ if dir_owner != user:
+ chown(home_dir, user=user, recursive=True)
+
# Generate 2FA/MFA One-Time-Pad configuration
if dict_search('authentication.otp.key', user_config):
enable_otp = True
diff --git a/src/conf_mode/system_option.py b/src/conf_mode/system_option.py
index 7ed451e16..a2e5db575 100755
--- a/src/conf_mode/system_option.py
+++ b/src/conf_mode/system_option.py
@@ -16,12 +16,12 @@
import os
-from netifaces import interfaces
from sys import exit
from time import sleep
from vyos.config import Config
from vyos.configverify import verify_source_interface
+from vyos.configverify import verify_interface_exists
from vyos.system import grub_util
from vyos.template import render
from vyos.utils.process import cmd
@@ -56,9 +56,7 @@ def verify(options):
if 'http_client' in options:
config = options['http_client']
if 'source_interface' in config:
- if not config['source_interface'] in interfaces():
- raise ConfigError(f'Source interface {source_interface} does not '
- f'exist'.format(**config))
+ verify_interface_exists(config['source_interface'])
if {'source_address', 'source_interface'} <= set(config):
raise ConfigError('Can not define both HTTP source-interface and source-address')
diff --git a/src/conf_mode/system_timezone.py b/src/conf_mode/system_timezone.py
index cd3d4b229..39770fdb4 100755
--- a/src/conf_mode/system_timezone.py
+++ b/src/conf_mode/system_timezone.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2019 VyOS maintainers and contributors
+# Copyright (C) 2019-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
@@ -15,7 +15,6 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import sys
-import os
from copy import deepcopy
from vyos.config import Config
diff --git a/src/conf_mode/system_update-check.py b/src/conf_mode/system_update-check.py
index 8d641a97d..71ac13e51 100755
--- a/src/conf_mode/system_update-check.py
+++ b/src/conf_mode/system_update-check.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2022 VyOS maintainers and contributors
+# Copyright (C) 2022-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
@@ -14,9 +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 json
-import jmespath
from pathlib import Path
from sys import exit
@@ -27,7 +25,6 @@ from vyos import ConfigError
from vyos import airbag
airbag.enable()
-
base = ['system', 'update-check']
service_name = 'vyos-system-update'
service_conf = Path(f'/run/{service_name}.conf')
diff --git a/src/conf_mode/vpn_ipsec.py b/src/conf_mode/vpn_ipsec.py
index 0c2f232df..dc78c755e 100755
--- a/src/conf_mode/vpn_ipsec.py
+++ b/src/conf_mode/vpn_ipsec.py
@@ -21,7 +21,6 @@ import jmespath
from sys import exit
from time import sleep
-from time import time
from vyos.base import Warning
from vyos.config import Config
@@ -47,7 +46,6 @@ from vyos.utils.network import interface_exists
from vyos.utils.dict import dict_search
from vyos.utils.dict import dict_search_args
from vyos.utils.process import call
-from vyos.utils.process import run
from vyos import ConfigError
from vyos import airbag
airbag.enable()
@@ -168,9 +166,7 @@ def verify(ipsec):
for interface in ipsec['interface']:
# exclude check interface for dynamic interfaces
if tmp.match(interface):
- if not interface_exists(interface):
- Warning(f'Interface "{interface}" does not exist yet and cannot be used '
- f'for IPsec until it is up!')
+ verify_interface_exists(interface, warning_only=True)
else:
verify_interface_exists(interface)
diff --git a/src/conf_mode/vpn_openconnect.py b/src/conf_mode/vpn_openconnect.py
index 08e4fc6db..8159fedea 100755
--- a/src/conf_mode/vpn_openconnect.py
+++ b/src/conf_mode/vpn_openconnect.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2018-2023 VyOS maintainers and contributors
+# Copyright (C) 2018-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
@@ -19,6 +19,8 @@ from sys import exit
from vyos.base import Warning
from vyos.config import Config
+from vyos.configverify import verify_pki_certificate
+from vyos.configverify import verify_pki_ca_certificate
from vyos.pki import wrap_certificate
from vyos.pki import wrap_private_key
from vyos.template import render
@@ -75,7 +77,7 @@ def verify(ocserv):
if "accounting" in ocserv:
if "mode" in ocserv["accounting"] and "radius" in ocserv["accounting"]["mode"]:
if not origin["accounting"]['radius']['server']:
- raise ConfigError('Openconnect accounting mode radius requires at least one RADIUS server')
+ raise ConfigError('OpenConnect accounting mode radius requires at least one RADIUS server')
if "authentication" not in ocserv or "mode" not in ocserv["authentication"]:
raise ConfigError('Accounting depends on OpenConnect authentication configuration')
elif "radius" not in ocserv["authentication"]["mode"]:
@@ -89,12 +91,12 @@ def verify(ocserv):
raise ConfigError('OpenConnect authentication modes are mutually-exclusive, remove either local or radius from your configuration')
if "radius" in ocserv["authentication"]["mode"]:
if not ocserv["authentication"]['radius']['server']:
- raise ConfigError('Openconnect authentication mode radius requires at least one RADIUS server')
+ raise ConfigError('OpenConnect authentication mode radius requires at least one RADIUS server')
if "local" in ocserv["authentication"]["mode"]:
if not ocserv.get("authentication", {}).get("local_users"):
- raise ConfigError('openconnect mode local required at least one user')
+ raise ConfigError('OpenConnect mode local required at least one user')
if not ocserv["authentication"]["local_users"]["username"]:
- raise ConfigError('openconnect mode local required at least one user')
+ raise ConfigError('OpenConnect mode local required at least one user')
else:
# For OTP mode: verify that each local user has an OTP key
if "otp" in ocserv["authentication"]["mode"]["local"]:
@@ -127,40 +129,20 @@ def verify(ocserv):
if 'default_config' not in ocserv["authentication"]["identity_based_config"]:
raise ConfigError('OpenConnect identity-based-config enabled but default-config not set')
else:
- raise ConfigError('openconnect authentication mode required')
+ raise ConfigError('OpenConnect authentication mode required')
else:
- raise ConfigError('openconnect authentication credentials required')
+ raise ConfigError('OpenConnect authentication credentials required')
# Check ssl
if 'ssl' not in ocserv:
- raise ConfigError('openconnect ssl required')
+ raise ConfigError('SSL missing on OpenConnect config!')
- if not ocserv['pki'] or 'certificate' not in ocserv['pki']:
- raise ConfigError('PKI not configured')
+ if 'certificate' not in ocserv['ssl']:
+ raise ConfigError('SSL certificate missing on OpenConnect config!')
+ verify_pki_certificate(ocserv, ocserv['ssl']['certificate'])
- ssl = ocserv['ssl']
- if 'certificate' not in ssl:
- raise ConfigError('openconnect ssl certificate required')
-
- cert_name = ssl['certificate']
-
- if cert_name not in ocserv['pki']['certificate']:
- raise ConfigError('Invalid openconnect ssl certificate')
-
- cert = ocserv['pki']['certificate'][cert_name]
-
- if 'certificate' not in cert:
- raise ConfigError('Missing certificate in PKI')
-
- if 'private' not in cert or 'key' not in cert['private']:
- raise ConfigError('Missing private key in PKI')
-
- if 'ca_certificate' in ssl:
- if 'ca' not in ocserv['pki']:
- raise ConfigError('PKI not configured')
-
- if ssl['ca_certificate'] not in ocserv['pki']['ca']:
- raise ConfigError('Invalid openconnect ssl CA certificate')
+ if 'ca_certificate' in ocserv['ssl']:
+ verify_pki_ca_certificate(ocserv, ocserv['ssl']['ca_certificate'])
# Check network settings
if "network_settings" in ocserv:
@@ -172,7 +154,7 @@ def verify(ocserv):
else:
ocserv["network_settings"]["push_route"] = ["default"]
else:
- raise ConfigError('openconnect network settings required')
+ raise ConfigError('OpenConnect network settings required!')
def generate(ocserv):
if not ocserv:
@@ -276,7 +258,7 @@ def apply(ocserv):
break
sleep(0.250)
if counter > 5:
- raise ConfigError('openconnect failed to start, check the logs for details')
+ raise ConfigError('OpenConnect failed to start, check the logs for details')
break
counter += 1
diff --git a/src/conf_mode/vpn_sstp.py b/src/conf_mode/vpn_sstp.py
index 8661a8aff..7490fd0e0 100755
--- a/src/conf_mode/vpn_sstp.py
+++ b/src/conf_mode/vpn_sstp.py
@@ -20,6 +20,8 @@ from sys import exit
from vyos.config import Config
from vyos.configdict import get_accel_dict
+from vyos.configverify import verify_pki_certificate
+from vyos.configverify import verify_pki_ca_certificate
from vyos.pki import wrap_certificate
from vyos.pki import wrap_private_key
from vyos.template import render
@@ -46,51 +48,6 @@ cert_key_path = os.path.join(cfg_dir, 'sstp-cert.key')
ca_cert_file_path = os.path.join(cfg_dir, 'sstp-ca.pem')
-def verify_certificate(config):
- #
- # SSL certificate checks
- #
- if not config['pki']:
- raise ConfigError('PKI is not configured')
-
- if 'ssl' not in config:
- raise ConfigError('SSL missing on SSTP config')
-
- ssl = config['ssl']
-
- # CA
- if 'ca_certificate' not in ssl:
- raise ConfigError('SSL CA certificate missing on SSTP config')
-
- ca_name = ssl['ca_certificate']
-
- if ca_name not in config['pki']['ca']:
- raise ConfigError('Invalid CA certificate on SSTP config')
-
- if 'certificate' not in config['pki']['ca'][ca_name]:
- raise ConfigError('Missing certificate data for CA certificate on SSTP config')
-
- # Certificate
- if 'certificate' not in ssl:
- raise ConfigError('SSL certificate missing on SSTP config')
-
- cert_name = ssl['certificate']
-
- if cert_name not in config['pki']['certificate']:
- raise ConfigError('Invalid certificate on SSTP config')
-
- pki_cert = config['pki']['certificate'][cert_name]
-
- if 'certificate' not in pki_cert:
- raise ConfigError('Missing certificate data for certificate on SSTP config')
-
- if 'private' not in pki_cert or 'key' not in pki_cert['private']:
- raise ConfigError('Missing private key for certificate on SSTP config')
-
- if 'password_protected' in pki_cert['private']:
- raise ConfigError('Encrypted private key is not supported on SSTP config')
-
-
def get_config(config=None):
if config:
conf = config
@@ -124,7 +81,17 @@ def verify(sstp):
verify_accel_ppp_ip_pool(sstp)
verify_accel_ppp_name_servers(sstp)
verify_accel_ppp_wins_servers(sstp)
- verify_certificate(sstp)
+
+ if 'ssl' not in sstp:
+ raise ConfigError('SSL missing on SSTP config!')
+
+ if 'certificate' not in sstp['ssl']:
+ raise ConfigError('SSL certificate missing on SSTP config!')
+ verify_pki_certificate(sstp, sstp['ssl']['certificate'])
+
+ if 'ca_certificate' not in sstp['ssl']:
+ raise ConfigError('SSL CA certificate missing on SSTP config!')
+ verify_pki_ca_certificate(sstp, sstp['ssl']['ca_certificate'])
def generate(sstp):
@@ -154,15 +121,15 @@ def generate(sstp):
def apply(sstp):
+ systemd_service = 'accel-ppp@sstp.service'
if not sstp:
- call('systemctl stop accel-ppp@sstp.service')
+ call(f'systemctl stop {systemd_service}')
for file in [sstp_chap_secrets, sstp_conf]:
if os.path.exists(file):
os.unlink(file)
-
return None
- call('systemctl restart accel-ppp@sstp.service')
+ call(f'systemctl reload-or-restart {systemd_service}')
if __name__ == '__main__':
diff --git a/src/conf_mode/vrf.py b/src/conf_mode/vrf.py
index 16908100f..1fc813189 100755
--- a/src/conf_mode/vrf.py
+++ b/src/conf_mode/vrf.py
@@ -14,8 +14,6 @@
# 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
-
from sys import exit
from json import loads
@@ -33,6 +31,7 @@ from vyos.utils.network import get_vrf_members
from vyos.utils.network import interface_exists
from vyos.utils.process import call
from vyos.utils.process import cmd
+from vyos.utils.process import popen
from vyos.utils.system import sysctl_write
from vyos import ConfigError
from vyos import frr
@@ -227,7 +226,11 @@ def apply(vrf):
# Remove nftables conntrack zone map item
nft_del_element = f'delete element inet vrf_zones ct_iface_map {{ "{tmp}" }}'
- cmd(f'nft {nft_del_element}')
+ # Check if deleting is possible first to avoid raising errors
+ _, err = popen(f'nft --check {nft_del_element}')
+ if not err:
+ # Remove map element
+ cmd(f'nft {nft_del_element}')
# Delete the VRF Kernel interface
call(f'ip link delete dev {tmp}')
@@ -307,7 +310,7 @@ def apply(vrf):
if vrf['conntrack']:
for chain, rule in nftables_rules.items():
cmd(f'nft add rule inet vrf_zones {chain} {rule}')
-
+
if 'name' not in vrf or not vrf['conntrack']:
for chain, rule in nftables_rules.items():
cmd(f'nft flush chain inet vrf_zones {chain}')
diff --git a/src/conf_mode/vrf_vni.py b/src/conf_mode/vrf_vni.py
index 23b341079..8dab164d7 100644
--- a/src/conf_mode/vrf_vni.py
+++ b/src/conf_mode/vrf_vni.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2023 VyOS maintainers and contributors
+# Copyright (C) 2023-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
@@ -19,7 +19,6 @@ from sys import exit
from vyos.config import Config
from vyos.template import render_to_string
-from vyos.utils.dict import dict_search
from vyos import ConfigError
from vyos import frr
from vyos import airbag
diff --git a/src/etc/dhcp/dhclient-enter-hooks.d/04-vyos-resolvconf b/src/etc/dhcp/dhclient-enter-hooks.d/04-vyos-resolvconf
index 518abeaec..9a8a53bfd 100644
--- a/src/etc/dhcp/dhclient-enter-hooks.d/04-vyos-resolvconf
+++ b/src/etc/dhcp/dhclient-enter-hooks.d/04-vyos-resolvconf
@@ -14,14 +14,6 @@ if /usr/bin/systemctl -q is-active vyos-hostsd; then
hostsd_changes=y
fi
- if [ -n "$new_dhcp6_domain_search" ]; then
- logmsg info "Deleting search domains with tag \"dhcpv6-$interface\" via vyos-hostsd-client"
- $hostsd_client --delete-search-domains --tag "dhcpv6-$interface"
- logmsg info "Adding search domain \"$new_dhcp6_domain_search\" with tag \"dhcpv6-$interface\" via vyos-hostsd-client"
- $hostsd_client --add-search-domains "$new_dhcp6_domain_search" --tag "dhcpv6-$interface"
- hostsd_changes=y
- fi
-
if [ -n "$new_domain_name_servers" ]; then
logmsg info "Deleting nameservers with tag \"dhcp-$interface\" via vyos-hostsd-client"
$hostsd_client --delete-name-servers --tag "dhcp-$interface"
@@ -30,14 +22,6 @@ if /usr/bin/systemctl -q is-active vyos-hostsd; then
hostsd_changes=y
fi
- if [ -n "$new_dhcp6_name_servers" ]; then
- logmsg info "Deleting nameservers with tag \"dhcpv6-$interface\" via vyos-hostsd-client"
- $hostsd_client --delete-name-servers --tag "dhcpv6-$interface"
- logmsg info "Adding nameservers \"$new_dhcp6_name_servers\" with tag \"dhcpv6-$interface\" via vyos-hostsd-client"
- $hostsd_client --add-name-servers $new_dhcp6_name_servers --tag "dhcpv6-$interface"
- hostsd_changes=y
- fi
-
if [ $hostsd_changes ]; then
logmsg info "Applying changes via vyos-hostsd-client"
$hostsd_client --apply
diff --git a/src/etc/dhcp/dhclient-exit-hooks.d/99-ipsec-dhclient-hook b/src/etc/dhcp/dhclient-exit-hooks.d/99-ipsec-dhclient-hook
index ebb100e8b..57f803055 100755
--- a/src/etc/dhcp/dhclient-exit-hooks.d/99-ipsec-dhclient-hook
+++ b/src/etc/dhcp/dhclient-exit-hooks.d/99-ipsec-dhclient-hook
@@ -17,7 +17,7 @@
DHCP_HOOK_IFLIST="/tmp/ipsec_dhcp_interfaces"
if ! { [ -f $DHCP_HOOK_IFLIST ] && grep -qw $interface $DHCP_HOOK_IFLIST; }; then
- exit 0
+ return 0
fi
# Re-generate the config on the following events:
@@ -26,10 +26,10 @@ fi
# - REBIND: re-generate if the IP address changed
if [ "$reason" == "RENEW" ] || [ "$reason" == "REBIND" ]; then
if [ "$old_ip_address" == "$new_ip_address" ]; then
- exit 0
+ return 0
fi
elif [ "$reason" != "BOUND" ]; then
- exit 0
+ return 0
fi
# Best effort wait for any active commit to finish
diff --git a/src/etc/systemd/system/ssh@.service.d/vrf-override.conf b/src/etc/systemd/system/ssh@.service.d/vrf-override.conf
new file mode 100644
index 000000000..b8952d86c
--- /dev/null
+++ b/src/etc/systemd/system/ssh@.service.d/vrf-override.conf
@@ -0,0 +1,13 @@
+[Unit]
+StartLimitIntervalSec=0
+After=vyos-router.service
+ConditionPathExists=/run/sshd/sshd_config
+
+[Service]
+EnvironmentFile=
+ExecStart=
+ExecStart=ip vrf exec %i /usr/sbin/sshd -f /run/sshd/sshd_config
+Restart=always
+RestartPreventExitStatus=
+RestartSec=10
+RuntimeDirectoryPreserve=yes
diff --git a/src/helpers/vyos-config-encrypt.py b/src/helpers/vyos-config-encrypt.py
index 8f7359767..0f9c63b1c 100755
--- a/src/helpers/vyos-config-encrypt.py
+++ b/src/helpers/vyos-config-encrypt.py
@@ -15,7 +15,6 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
-import re
import shutil
import sys
@@ -25,7 +24,6 @@ from tempfile import NamedTemporaryFile
from tempfile import TemporaryDirectory
from vyos.tpm import clear_tpm_key
-from vyos.tpm import init_tpm
from vyos.tpm import read_tpm_key
from vyos.tpm import write_tpm_key
from vyos.util import ask_input
diff --git a/src/helpers/vyos-domain-resolver.py b/src/helpers/vyos-domain-resolver.py
index eac3d37af..57cfcabd7 100755
--- a/src/helpers/vyos-domain-resolver.py
+++ b/src/helpers/vyos-domain-resolver.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2022-2023 VyOS maintainers and contributors
+# Copyright (C) 2022-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
@@ -15,7 +15,6 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import json
-import os
import time
from vyos.configdict import dict_merge
@@ -95,7 +94,7 @@ def nft_output(table, set_name, ip_list):
def nft_valid_sets():
try:
valid_sets = []
- sets_json = cmd('nft -j list sets')
+ sets_json = cmd('nft --json list sets')
sets_obj = json.loads(sets_json)
for obj in sets_obj['nftables']:
@@ -155,7 +154,7 @@ def update(firewall):
count += 1
nft_conf_str = "\n".join(conf_lines) + "\n"
- code = run(f'nft -f -', input=nft_conf_str)
+ code = run(f'nft --file -', input=nft_conf_str)
print(f'Updated {count} sets - result: {code}')
diff --git a/src/helpers/vyos-failover.py b/src/helpers/vyos-failover.py
index cc7610370..f34c18916 100755
--- a/src/helpers/vyos-failover.py
+++ b/src/helpers/vyos-failover.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2022-2023 VyOS maintainers and contributors
+# Copyright (C) 2022-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
@@ -16,7 +16,6 @@
import argparse
import json
-import subprocess
import socket
import time
diff --git a/src/helpers/vyos-merge-config.py b/src/helpers/vyos-merge-config.py
index 8997705fe..35424626e 100755
--- a/src/helpers/vyos-merge-config.py
+++ b/src/helpers/vyos-merge-config.py
@@ -1,6 +1,6 @@
#!/usr/bin/python3
-# Copyright 2019-2023 VyOS maintainers and contributors <maintainers@vyos.io>
+# Copyright 2019-2024 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
@@ -16,7 +16,6 @@
# License along with this library. If not, see <http://www.gnu.org/licenses/>.
import sys
-import os
import tempfile
import vyos.defaults
import vyos.remote
diff --git a/src/helpers/vyos_config_sync.py b/src/helpers/vyos_config_sync.py
index 77f7cd810..0604b2837 100755
--- a/src/helpers/vyos_config_sync.py
+++ b/src/helpers/vyos_config_sync.py
@@ -21,9 +21,11 @@ import json
import requests
import urllib3
import logging
-from typing import Optional, List, Union, Dict, Any
+from typing import Optional, List, Tuple, Dict, Any
from vyos.config import Config
+from vyos.configtree import ConfigTree
+from vyos.configtree import mask_inclusive
from vyos.template import bracketize_ipv6
@@ -61,39 +63,45 @@ def post_request(url: str,
-def retrieve_config(section: Optional[List[str]] = None) -> Optional[Dict[str, Any]]:
+def retrieve_config(sections: List[list[str]]) -> Tuple[Dict[str, Any], Dict[str, Any]]:
"""Retrieves the configuration from the local server.
Args:
- section: List[str]: The section of the configuration to retrieve.
- Default is None.
+ sections: List[list[str]]: The list of sections of the configuration
+ to retrieve, given as list of paths.
Returns:
- Optional[Dict[str, Any]]: The retrieved configuration as a
- dictionary, or None if an error occurred.
+ Tuple[Dict[str, Any],Dict[str,Any]]: The tuple (mask, config) where:
+ - mask: The tree of paths of sections, as a dictionary.
+ - config: The subtree of masked config data, as a dictionary.
"""
- if section is None:
- section = []
- conf = Config()
- config = conf.get_config_dict(section, get_first_key=True)
- if config:
- return config
- return None
+ mask = ConfigTree('')
+ for section in sections:
+ mask.set(section)
+ mask_dict = json.loads(mask.to_json())
+
+ config = Config()
+ config_tree = config.get_config_tree()
+ masked = mask_inclusive(config_tree, mask)
+ config_dict = json.loads(masked.to_json())
+ return mask_dict, config_dict
def set_remote_config(
address: str,
key: str,
- commands: List[Dict[str, Any]]) -> Optional[Dict[str, Any]]:
+ op: str,
+ mask: Dict[str, Any],
+ config: Dict[str, Any]) -> Optional[Dict[str, Any]]:
"""Loads the VyOS configuration in JSON format to a remote host.
Args:
address (str): The address of the remote host.
key (str): The key to use for loading the configuration.
- commands (list): List of set/load commands for request, given as:
- [{'op': str, 'path': list[str], 'section': dict},
- ...]
+ op (str): The operation to perform (set or load).
+ mask (dict): The dict of paths in sections.
+ config (dict): The dict of masked config data.
Returns:
Optional[Dict[str, Any]]: The response from the remote host as a
@@ -107,7 +115,9 @@ def set_remote_config(
url = f'https://{address}/configure-section'
data = json.dumps({
- 'commands': commands,
+ 'op': op,
+ 'mask': mask,
+ 'config': config,
'key': key
})
@@ -140,23 +150,15 @@ def config_sync(secondary_address: str,
)
# Sync sections ("nat", "firewall", etc)
- commands = []
- for section in sections:
- config_json = retrieve_config(section=section)
- # Check if config path deesn't exist, for example "set nat"
- # we set empty value for config_json data
- # As we cannot send to the remote host section "nat None" config
- if not config_json:
- config_json = {}
- logger.debug(
- f"Retrieved config for section '{section}': {config_json}")
-
- d = {'op': mode, 'path': section, 'section': config_json}
- commands.append(d)
+ mask_dict, config_dict = retrieve_config(sections)
+ logger.debug(
+ f"Retrieved config for sections '{sections}': {config_dict}")
set_config = set_remote_config(address=secondary_address,
key=secondary_key,
- commands=commands)
+ op=mode,
+ mask=mask_dict,
+ config=config_dict)
logger.debug(f"Set config for sections '{sections}': {set_config}")
diff --git a/src/init/vyos-router b/src/init/vyos-router
index adf892371..06fea140d 100755
--- a/src/init/vyos-router
+++ b/src/init/vyos-router
@@ -430,7 +430,7 @@ start ()
nfct helper add rpc inet6 tcp
nfct helper add rpc inet6 udp
nfct helper add tns inet6 tcp
- nft -f /usr/share/vyos/vyos-firewall-init.conf || log_failure_msg "could not initiate firewall rules"
+ nft --file /usr/share/vyos/vyos-firewall-init.conf || log_failure_msg "could not initiate firewall rules"
# As VyOS does not execute commands that are not present in the CLI we call
# the script by hand to have a single source for the login banner and MOTD
diff --git a/src/migration-scripts/conntrack/2-to-3 b/src/migration-scripts/conntrack/2-to-3
index 6fb457b7f..6bb42be1e 100755
--- a/src/migration-scripts/conntrack/2-to-3
+++ b/src/migration-scripts/conntrack/2-to-3
@@ -6,7 +6,6 @@
import sys
from vyos.configtree import ConfigTree
-from vyos.version import get_version
if len(sys.argv) < 2:
print('Must specify file name!')
diff --git a/src/migration-scripts/container/1-to-2 b/src/migration-scripts/container/1-to-2
new file mode 100755
index 000000000..408faf978
--- /dev/null
+++ b/src/migration-scripts/container/1-to-2
@@ -0,0 +1,50 @@
+#!/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/>.
+
+# T6208: container: rename "cap-add" CLI node to "capability"
+
+from sys import argv
+from sys import exit
+from vyos.configtree import ConfigTree
+
+if len(argv) < 2:
+ print("Must specify file name!")
+ exit(1)
+
+file_name = argv[1]
+
+with open(file_name, 'r') as f:
+ config_file = f.read()
+
+base = ['container', 'name']
+config = ConfigTree(config_file)
+
+# Check if containers exist and we need to perform image manipulation
+if not config.exists(base):
+ # Nothing to do
+ exit(0)
+
+for container in config.list_nodes(base):
+ cap_path = base + [container, 'cap-add']
+ if config.exists(cap_path):
+ config.rename(cap_path, 'capability')
+
+try:
+ with open(file_name, 'w') as f:
+ f.write(config.to_string())
+except OSError as e:
+ print(f'Failed to save the modified config: {e}')
+ exit(1)
diff --git a/src/migration-scripts/dhcp-server/9-to-10 b/src/migration-scripts/dhcp-server/9-to-10
index 810e403a6..a459b65b5 100755
--- a/src/migration-scripts/dhcp-server/9-to-10
+++ b/src/migration-scripts/dhcp-server/9-to-10
@@ -19,7 +19,6 @@
# - Add subnet IDs to existing subnets
import sys
-import re
from vyos.configtree import ConfigTree
if len(sys.argv) < 2:
@@ -57,7 +56,7 @@ for network in config.list_nodes(base):
if config.exists(base + [network, 'subnet']):
for subnet in config.list_nodes(base + [network, 'subnet']):
base_subnet = base + [network, 'subnet', subnet]
-
+
for option in option_nodes:
if config.exists(base_subnet + [option]):
config.set(base_subnet + ['option'])
diff --git a/src/migration-scripts/dhcpv6-server/3-to-4 b/src/migration-scripts/dhcpv6-server/3-to-4
index 4747ebd60..7efc492a5 100755
--- a/src/migration-scripts/dhcpv6-server/3-to-4
+++ b/src/migration-scripts/dhcpv6-server/3-to-4
@@ -20,7 +20,6 @@
# - Migrate address-range to range tagNode
import sys
-import re
from vyos.configtree import ConfigTree
if len(sys.argv) < 2:
diff --git a/src/migration-scripts/firewall/10-to-11 b/src/migration-scripts/firewall/10-to-11
index abb804a28..854d5a558 100755
--- a/src/migration-scripts/firewall/10-to-11
+++ b/src/migration-scripts/firewall/10-to-11
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2023 VyOS maintainers and contributors
+# Copyright (C) 2023-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
@@ -16,12 +16,12 @@
# T5160: Firewall re-writing
-# cli changes from:
+# cli changes from:
# set firewall name <name> ...
# set firewall ipv6-name <name> ...
# To
-# set firewall ipv4 name <name>
-# set firewall ipv6 name <name>
+# set firewall ipv4 name <name>
+# set firewall ipv6 name <name>
## Also from 'firewall interface' removed.
## in and out:
@@ -37,13 +37,10 @@
# set firewall [ipv4 | ipv6] input filter rule <5,10,15,...> action jump
# set firewall [ipv4 | ipv6] input filter rule <5,10,15,...> jump-target <name>
-import re
-
from sys import argv
from sys import exit
from vyos.configtree import ConfigTree
-from vyos.ifconfig import Section
if len(argv) < 2:
print("Must specify file name!")
@@ -207,4 +204,4 @@ try:
f.write(config.to_string())
except OSError as e:
print("Failed to save the modified config: {}".format(e))
- exit(1) \ No newline at end of file
+ exit(1)
diff --git a/src/migration-scripts/firewall/11-to-12 b/src/migration-scripts/firewall/11-to-12
index ba8374d66..f9122e74c 100755
--- a/src/migration-scripts/firewall/11-to-12
+++ b/src/migration-scripts/firewall/11-to-12
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2023 VyOS maintainers and contributors
+# Copyright (C) 2023-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
@@ -22,13 +22,10 @@
# set firewall ... rule <rule> [inbound-interface | outboubd-interface] name <iface>
# set firewall ... rule <rule> [inbound-interface | outboubd-interface] group <iface_group>
-import re
-
from sys import argv
from sys import exit
from vyos.configtree import ConfigTree
-from vyos.ifconfig import Section
if len(argv) < 2:
print("Must specify file name!")
@@ -71,4 +68,4 @@ try:
f.write(config.to_string())
except OSError as e:
print("Failed to save the modified config: {}".format(e))
- exit(1) \ No newline at end of file
+ exit(1)
diff --git a/src/migration-scripts/firewall/12-to-13 b/src/migration-scripts/firewall/12-to-13
index 8396dd9d1..d72ba834d 100755
--- a/src/migration-scripts/firewall/12-to-13
+++ b/src/migration-scripts/firewall/12-to-13
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2023 VyOS maintainers and contributors
+# Copyright (C) 2023-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
@@ -25,13 +25,10 @@
# set firewall ... rule <rule> state <state>
# Remove command if log=disable or <state>=disable
-import re
-
from sys import argv
from sys import exit
from vyos.configtree import ConfigTree
-from vyos.ifconfig import Section
if len(argv) < 2:
print("Must specify file name!")
@@ -89,4 +86,4 @@ try:
f.write(config.to_string())
except OSError as e:
print("Failed to save the modified config: {}".format(e))
- exit(1) \ No newline at end of file
+ exit(1)
diff --git a/src/migration-scripts/firewall/14-to-15 b/src/migration-scripts/firewall/14-to-15
new file mode 100755
index 000000000..735839365
--- /dev/null
+++ b/src/migration-scripts/firewall/14-to-15
@@ -0,0 +1,46 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2022-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/>.
+
+# T5535: Migrate <set system ip disable-directed-broadcast> to <set firewall global-options directed-broadcas [enable|disable]
+
+from sys import argv
+from sys import exit
+
+from vyos.configtree import ConfigTree
+
+if len(argv) < 2:
+ print("Must specify file name!")
+ exit(1)
+
+file_name = argv[1]
+
+with open(file_name, 'r') as f:
+ config_file = f.read()
+
+config = ConfigTree(config_file)
+
+base = ['firewall']
+
+if config.exists(['system', 'ip', 'disable-directed-broadcast']):
+ config.set(['firewall', 'global-options', 'directed-broadcast'], value='disable')
+ config.delete(['system', 'ip', 'disable-directed-broadcast'])
+
+try:
+ with open(file_name, 'w') as f:
+ f.write(config.to_string())
+except OSError as e:
+ print("Failed to save the modified config: {}".format(e))
+ exit(1) \ No newline at end of file
diff --git a/src/migration-scripts/firewall/6-to-7 b/src/migration-scripts/firewall/6-to-7
index 72f07880b..938044c6d 100755
--- a/src/migration-scripts/firewall/6-to-7
+++ b/src/migration-scripts/firewall/6-to-7
@@ -107,6 +107,12 @@ icmpv6_translations = {
'unknown-option': [4, 2]
}
+v4_found = False
+v6_found = False
+v4_groups = ["address-group", "network-group", "port-group"]
+v6_groups = ["ipv6-address-group", "ipv6-network-group", "port-group"]
+translated_dict = {}
+
if config.exists(base + ['group']):
for group_type in config.list_nodes(base + ['group']):
for group_name in config.list_nodes(base + ['group', group_type]):
@@ -114,6 +120,19 @@ if config.exists(base + ['group']):
if config.exists(name_description):
tmp = config.return_value(name_description)
config.set(name_description, value=tmp[:max_len_description])
+ if '+' in group_name:
+ replacement_string = "_"
+ if group_type in v4_groups and not v4_found:
+ v4_found = True
+ if group_type in v6_groups and not v6_found:
+ v6_found = True
+ new_group_name = group_name.replace('+', replacement_string)
+ while config.exists(base + ['group', group_type, new_group_name]):
+ replacement_string = replacement_string + "_"
+ new_group_name = group_name.replace('+', replacement_string)
+ translated_dict[group_name] = new_group_name
+ config.copy(base + ['group', group_type, group_name], base + ['group', group_type, new_group_name])
+ config.delete(base + ['group', group_type, group_name])
if config.exists(base + ['name']):
for name in config.list_nodes(base + ['name']):
@@ -173,11 +192,31 @@ if config.exists(base + ['name']):
config.set(rule_icmp + ['type'], value=translate[0])
config.set(rule_icmp + ['code'], value=translate[1])
- for src_dst in ['destination', 'source']:
- pg_base = base + ['name', name, 'rule', rule, src_dst, 'group', 'port-group']
- proto_base = base + ['name', name, 'rule', rule, 'protocol']
- if config.exists(pg_base) and not config.exists(proto_base):
- config.set(proto_base, value='tcp_udp')
+ for direction in ['destination', 'source']:
+ if config.exists(base + ['name', name, 'rule', rule, direction]):
+ if config.exists(base + ['name', name, 'rule', rule, direction, 'group']) and v4_found:
+ for group_type in config.list_nodes(base + ['name', name, 'rule', rule, direction, 'group']):
+ group_name = config.return_value(base + ['name', name, 'rule', rule, direction, 'group', group_type])
+ if '+' in group_name:
+ if group_name[0] == "!":
+ new_group_name = "!" + translated_dict[group_name[1:]]
+ else:
+ new_group_name = translated_dict[group_name]
+ config.set(base + ['name', name, 'rule', rule, direction, 'group', group_type], value=new_group_name)
+
+ pg_base = base + ['name', name, 'rule', rule, direction, 'group', 'port-group']
+ proto_base = base + ['name', name, 'rule', rule, 'protocol']
+ if config.exists(pg_base) and not config.exists(proto_base):
+ config.set(proto_base, value='tcp_udp')
+
+ if '+' in name:
+ replacement_string = "_"
+ new_name = name.replace('+', replacement_string)
+ while config.exists(base + ['name', new_name]):
+ replacement_string = replacement_string + "_"
+ new_name = name.replace('+', replacement_string)
+ config.copy(base + ['name', name], base + ['name', new_name])
+ config.delete(base + ['name', name])
if config.exists(base + ['ipv6-name']):
for name in config.list_nodes(base + ['ipv6-name']):
@@ -250,12 +289,31 @@ if config.exists(base + ['ipv6-name']):
else:
config.rename(rule_icmp + ['type'], 'type-name')
- for src_dst in ['destination', 'source']:
- pg_base = base + ['ipv6-name', name, 'rule', rule, src_dst, 'group', 'port-group']
- proto_base = base + ['ipv6-name', name, 'rule', rule, 'protocol']
- if config.exists(pg_base) and not config.exists(proto_base):
- config.set(proto_base, value='tcp_udp')
-
+ for direction in ['destination', 'source']:
+ if config.exists(base + ['ipv6-name', name, 'rule', rule, direction]):
+ if config.exists(base + ['ipv6-name', name, 'rule', rule, direction, 'group']) and v6_found:
+ for group_type in config.list_nodes(base + ['ipv6-name', name, 'rule', rule, direction, 'group']):
+ group_name = config.return_value(base + ['ipv6-name', name, 'rule', rule, direction, 'group', group_type])
+ if '+' in group_name:
+ if group_name[0] == "!":
+ new_group_name = "!" + translated_dict[group_name[1:]]
+ else:
+ new_group_name = translated_dict[group_name]
+ config.set(base + ['ipv6-name', name, 'rule', rule, direction, 'group', group_type], value=new_group_name)
+
+ pg_base = base + ['ipv6-name', name, 'rule', rule, direction, 'group', 'port-group']
+ proto_base = base + ['ipv6-name', name, 'rule', rule, 'protocol']
+ if config.exists(pg_base) and not config.exists(proto_base):
+ config.set(proto_base, value='tcp_udp')
+
+ if '+' in name:
+ replacement_string = "_"
+ new_name = name.replace('+', replacement_string)
+ while config.exists(base + ['ipv6-name', new_name]):
+ replacement_string = replacement_string + "_"
+ new_name = name.replace('+', replacement_string)
+ config.copy(base + ['ipv6-name', name], base + ['ipv6-name', new_name])
+ config.delete(base + ['ipv6-name', name])
try:
with open(file_name, 'w') as f:
f.write(config.to_string())
diff --git a/src/migration-scripts/firewall/7-to-8 b/src/migration-scripts/firewall/7-to-8
index d06c3150a..bbaba113a 100755
--- a/src/migration-scripts/firewall/7-to-8
+++ b/src/migration-scripts/firewall/7-to-8
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2022 VyOS maintainers and contributors
+# Copyright (C) 2022-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,13 +17,10 @@
# T2199: Migrate interface firewall nodes to firewall interfaces <ifname> <direction> name/ipv6-name <name>
# T2199: Migrate zone-policy to firewall node
-import re
-
from sys import argv
from sys import exit
from vyos.configtree import ConfigTree
-from vyos.ifconfig import Section
if len(argv) < 2:
print("Must specify file name!")
diff --git a/src/migration-scripts/firewall/8-to-9 b/src/migration-scripts/firewall/8-to-9
index d7647354a..6e019beb2 100755
--- a/src/migration-scripts/firewall/8-to-9
+++ b/src/migration-scripts/firewall/8-to-9
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2022 VyOS maintainers and contributors
+# Copyright (C) 2022-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
@@ -15,18 +15,15 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# T4780: Add firewall interface group
-# cli changes from:
+# cli changes from:
# set firewall [name | ipv6-name] <name> rule <number> [inbound-interface | outbound-interface] <interface_name>
# To
# set firewall [name | ipv6-name] <name> rule <number> [inbound-interface | outbound-interface] [interface-name | interface-group] <interface_name | interface_group>
-import re
-
from sys import argv
from sys import exit
from vyos.configtree import ConfigTree
-from vyos.ifconfig import Section
if len(argv) < 2:
print("Must specify file name!")
@@ -88,4 +85,4 @@ try:
f.write(config.to_string())
except OSError as e:
print("Failed to save the modified config: {}".format(e))
- exit(1) \ No newline at end of file
+ exit(1)
diff --git a/src/migration-scripts/firewall/9-to-10 b/src/migration-scripts/firewall/9-to-10
index a70460718..ce509a731 100755
--- a/src/migration-scripts/firewall/9-to-10
+++ b/src/migration-scripts/firewall/9-to-10
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2023 VyOS maintainers and contributors
+# Copyright (C) 2023-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
@@ -15,18 +15,15 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# T5050: Log options
-# cli changes from:
+# cli changes from:
# set firewall [name | ipv6-name] <name> rule <number> log-level <log_level>
# To
# set firewall [name | ipv6-name] <name> rule <number> log-options level <log_level>
-import re
-
from sys import argv
from sys import exit
from vyos.configtree import ConfigTree
-from vyos.ifconfig import Section
if len(argv) < 2:
print("Must specify file name!")
@@ -50,7 +47,7 @@ if config.exists(base + ['name']):
continue
for rule in config.list_nodes(base + ['name', name, 'rule']):
- log_options_base = base + ['name', name, 'rule', rule, 'log-options']
+ log_options_base = base + ['name', name, 'rule', rule, 'log-options']
rule_log_level = base + ['name', name, 'rule', rule, 'log-level']
if config.exists(rule_log_level):
@@ -64,7 +61,7 @@ if config.exists(base + ['ipv6-name']):
continue
for rule in config.list_nodes(base + ['ipv6-name', name, 'rule']):
- log_options_base = base + ['ipv6-name', name, 'rule', rule, 'log-options']
+ log_options_base = base + ['ipv6-name', name, 'rule', rule, 'log-options']
rule_log_level = base + ['ipv6-name', name, 'rule', rule, 'log-level']
if config.exists(rule_log_level):
@@ -77,4 +74,4 @@ try:
f.write(config.to_string())
except OSError as e:
print("Failed to save the modified config: {}".format(e))
- exit(1) \ No newline at end of file
+ exit(1)
diff --git a/src/migration-scripts/interfaces/26-to-27 b/src/migration-scripts/interfaces/26-to-27
index 4967a29fa..429ab650f 100755
--- a/src/migration-scripts/interfaces/26-to-27
+++ b/src/migration-scripts/interfaces/26-to-27
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2022 VyOS maintainers and contributors
+# Copyright (C) 2022-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
@@ -18,8 +18,6 @@
# present for DHCP
from sys import argv
-
-from vyos.ethtool import Ethtool
from vyos.configtree import ConfigTree
if len(argv) < 2:
diff --git a/src/migration-scripts/interfaces/27-to-28 b/src/migration-scripts/interfaces/27-to-28
index a0d043d11..9f5e93b5f 100755
--- a/src/migration-scripts/interfaces/27-to-28
+++ b/src/migration-scripts/interfaces/27-to-28
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2023 VyOS maintainers and contributors
+# Copyright (C) 2023-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
@@ -19,7 +19,6 @@
from sys import argv
-from vyos.ethtool import Ethtool
from vyos.configtree import ConfigTree
if len(argv) < 2:
diff --git a/src/migration-scripts/interfaces/28-to-29 b/src/migration-scripts/interfaces/28-to-29
index ad5bfa653..0437977dc 100755
--- a/src/migration-scripts/interfaces/28-to-29
+++ b/src/migration-scripts/interfaces/28-to-29
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2023 VyOS maintainers and contributors
+# Copyright (C) 2023-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
@@ -18,8 +18,6 @@
# valueless node.
from sys import argv
-
-from vyos.ethtool import Ethtool
from vyos.configtree import ConfigTree
if len(argv) < 2:
diff --git a/src/migration-scripts/interfaces/29-to-30 b/src/migration-scripts/interfaces/29-to-30
index acb6ee1fb..80aad1d44 100755
--- a/src/migration-scripts/interfaces/29-to-30
+++ b/src/migration-scripts/interfaces/29-to-30
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2023 VyOS maintainers and contributors
+# Copyright (C) 2023-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,8 +17,6 @@
# T5286: remove XDP support in favour of VPP
from sys import argv
-
-from vyos.ethtool import Ethtool
from vyos.configtree import ConfigTree
if len(argv) < 2:
diff --git a/src/migration-scripts/ipoe-server/0-to-1 b/src/migration-scripts/ipoe-server/0-to-1
deleted file mode 100755
index ac9d13abc..000000000
--- a/src/migration-scripts/ipoe-server/0-to-1
+++ /dev/null
@@ -1,74 +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/>.
-
-# - T4703: merge vlan-id and vlan-range to vlan CLI node
-
-# L2|L3 -> l2|l3
-# mac-address -> mac
-# network-mode -> mode
-
-import os
-import sys
-
-from sys import argv, exit
-from vyos.configtree import ConfigTree
-
-if len(argv) < 2:
- print("Must specify file name!")
- exit(1)
-
-file_name = argv[1]
-
-with open(file_name, 'r') as f:
- config_file = f.read()
-
-config = ConfigTree(config_file)
-base = ['service', 'ipoe-server']
-if not config.exists(base):
- # Nothing to do
- exit(0)
-
-if config.exists(base + ['authentication', 'interface']):
- for interface in config.list_nodes(base + ['authentication', 'interface']):
- config.rename(base + ['authentication', 'interface', interface, 'mac-address'], 'mac')
-
- mac_base = base + ['authentication', 'interface', interface, 'mac']
- for mac in config.list_nodes(mac_base):
- vlan_config = mac_base + [mac, 'vlan-id']
- if config.exists(vlan_config):
- config.rename(vlan_config, 'vlan')
-
-for interface in config.list_nodes(base + ['interface']):
- base_path = base + ['interface', interface]
- for vlan in ['vlan-id', 'vlan-range']:
- if config.exists(base_path + [vlan]):
- print(interface, vlan)
- for tmp in config.return_values(base_path + [vlan]):
- config.set(base_path + ['vlan'], value=tmp, replace=False)
- config.delete(base_path + [vlan])
-
- if config.exists(base_path + ['network-mode']):
- tmp = config.return_value(base_path + ['network-mode'])
- config.delete(base_path + ['network-mode'])
- # Change L2|L3 to lower case l2|l3
- config.set(base_path + ['mode'], value=tmp.lower())
-
-try:
- with open(file_name, 'w') as f:
- f.write(config.to_string())
-except OSError as e:
- print("Failed to save the modified config: {}".format(e))
- exit(1)
diff --git a/src/migration-scripts/ipoe-server/1-to-2 b/src/migration-scripts/ipoe-server/1-to-2
index 11d7911e9..6a7111541 100755
--- a/src/migration-scripts/ipoe-server/1-to-2
+++ b/src/migration-scripts/ipoe-server/1-to-2
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2023 VyOS maintainers and contributors
+# Copyright (C) 2023-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
@@ -14,6 +14,11 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# - T4703: merge vlan-id and vlan-range to vlan CLI node
+# L2|L3 -> l2|l3
+# mac-address -> mac
+# network-mode -> mode
+
# - changed cli of all named pools
# - moved gateway-address from pool to global configuration with / netmask
# gateway can exist without pool if radius is used
@@ -23,8 +28,6 @@
# 1. The first pool that contains next-poll.
# 2. Else, the first pool in the list
-import os
-
from sys import argv
from sys import exit
from vyos.configtree import ConfigTree
@@ -41,43 +44,67 @@ with open(file_name, 'r') as f:
config = ConfigTree(config_file)
base = ['service', 'ipoe-server']
-pool_base = base + ['client-ip-pool']
+
if not config.exists(base):
exit(0)
-if not config.exists(pool_base):
- exit(0)
-default_pool = ''
-gateway = ''
-
-#named pool migration
-namedpools_base = pool_base + ['name']
-
-for pool_name in config.list_nodes(namedpools_base):
- pool_path = namedpools_base + [pool_name]
- if config.exists(pool_path + ['subnet']):
- subnet = config.return_value(pool_path + ['subnet'])
- config.set(pool_base + [pool_name, 'range'], value=subnet, replace=False)
- # Get netmask from subnet
- mask = subnet.split("/")[1]
- if config.exists(pool_path + ['next-pool']):
- next_pool = config.return_value(pool_path + ['next-pool'])
- config.set(pool_base + [pool_name, 'next-pool'], value=next_pool)
- if not default_pool:
- default_pool = pool_name
- if config.exists(pool_path + ['gateway-address']) and mask:
- gateway = f'{config.return_value(pool_path + ["gateway-address"])}/{mask}'
- config.set(base + ['gateway-address'], value=gateway, replace=False)
-
-if not default_pool and config.list_nodes(namedpools_base):
- default_pool = config.list_nodes(namedpools_base)[0]
-
-config.delete(namedpools_base)
-
-if default_pool:
- config.set(base + ['default-pool'], value=default_pool)
-# format as tag node
-config.set_tag(pool_base)
+if config.exists(base + ['authentication', 'interface']):
+ for interface in config.list_nodes(base + ['authentication', 'interface']):
+ config.rename(base + ['authentication', 'interface', interface, 'mac-address'], 'mac')
+
+ mac_base = base + ['authentication', 'interface', interface, 'mac']
+ for mac in config.list_nodes(mac_base):
+ vlan_config = mac_base + [mac, 'vlan-id']
+ if config.exists(vlan_config):
+ config.rename(vlan_config, 'vlan')
+
+for interface in config.list_nodes(base + ['interface']):
+ base_path = base + ['interface', interface]
+ for vlan in ['vlan-id', 'vlan-range']:
+ if config.exists(base_path + [vlan]):
+ for tmp in config.return_values(base_path + [vlan]):
+ config.set(base_path + ['vlan'], value=tmp, replace=False)
+ config.delete(base_path + [vlan])
+
+ if config.exists(base_path + ['network-mode']):
+ tmp = config.return_value(base_path + ['network-mode'])
+ config.delete(base_path + ['network-mode'])
+ # Change L2|L3 to lower case l2|l3
+ config.set(base_path + ['mode'], value=tmp.lower())
+
+pool_base = base + ['client-ip-pool']
+if config.exists(pool_base):
+ default_pool = ''
+ gateway = ''
+
+ #named pool migration
+ namedpools_base = pool_base + ['name']
+
+ for pool_name in config.list_nodes(namedpools_base):
+ pool_path = namedpools_base + [pool_name]
+ if config.exists(pool_path + ['subnet']):
+ subnet = config.return_value(pool_path + ['subnet'])
+ config.set(pool_base + [pool_name, 'range'], value=subnet, replace=False)
+ # Get netmask from subnet
+ mask = subnet.split("/")[1]
+ if config.exists(pool_path + ['next-pool']):
+ next_pool = config.return_value(pool_path + ['next-pool'])
+ config.set(pool_base + [pool_name, 'next-pool'], value=next_pool)
+ if not default_pool:
+ default_pool = pool_name
+ if config.exists(pool_path + ['gateway-address']) and mask:
+ gateway = f'{config.return_value(pool_path + ["gateway-address"])}/{mask}'
+ config.set(base + ['gateway-address'], value=gateway, replace=False)
+
+ if not default_pool and config.list_nodes(namedpools_base):
+ default_pool = config.list_nodes(namedpools_base)[0]
+
+ config.delete(namedpools_base)
+
+ if default_pool:
+ config.set(base + ['default-pool'], value=default_pool)
+ # format as tag node
+ config.set_tag(pool_base)
try:
with open(file_name, 'w') as f:
diff --git a/src/migration-scripts/ipoe-server/2-to-3 b/src/migration-scripts/ipoe-server/2-to-3
index d4ae0a7ba..0909315a8 100755
--- a/src/migration-scripts/ipoe-server/2-to-3
+++ b/src/migration-scripts/ipoe-server/2-to-3
@@ -16,13 +16,10 @@
# Migrating to named ipv6 pools
-import os
-
from sys import argv
from sys import exit
from vyos.configtree import ConfigTree
-
if len(argv) < 2:
print("Must specify file name!")
exit(1)
diff --git a/src/migration-scripts/ipsec/11-to-12 b/src/migration-scripts/ipsec/11-to-12
index e34882c23..4833d0876 100755
--- a/src/migration-scripts/ipsec/11-to-12
+++ b/src/migration-scripts/ipsec/11-to-12
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2023 VyOS maintainers and contributors
+# Copyright (C) 2023-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
@@ -16,8 +16,6 @@
# Remove legacy ipsec.conf and ipsec.secrets - Not supported with swanctl
-import re
-
from sys import argv
from sys import exit
diff --git a/src/migration-scripts/ipsec/12-to-13 b/src/migration-scripts/ipsec/12-to-13
index c11f708bd..d90c70314 100755
--- a/src/migration-scripts/ipsec/12-to-13
+++ b/src/migration-scripts/ipsec/12-to-13
@@ -17,8 +17,6 @@
# Changed value of dead-peer-detection.action from hold to trap
# Changed value of close-action from hold to trap and from restart to start
-import re
-
from sys import argv
from sys import exit
diff --git a/src/migration-scripts/ipsec/7-to-8 b/src/migration-scripts/ipsec/7-to-8
index e002db0b1..9acc737d5 100755
--- a/src/migration-scripts/ipsec/7-to-8
+++ b/src/migration-scripts/ipsec/7-to-8
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2021 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
@@ -26,7 +26,6 @@ from sys import argv
from sys import exit
from vyos.configtree import ConfigTree
-from vyos.pki import load_public_key
from vyos.pki import load_private_key
from vyos.pki import encode_public_key
from vyos.pki import encode_private_key
diff --git a/src/migration-scripts/ipsec/9-to-10 b/src/migration-scripts/ipsec/9-to-10
index a4a71d38e..bc10e1997 100755
--- a/src/migration-scripts/ipsec/9-to-10
+++ b/src/migration-scripts/ipsec/9-to-10
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2022 VyOS maintainers and contributors
+# Copyright (C) 2022-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
@@ -20,9 +20,6 @@ from sys import argv
from sys import exit
from vyos.configtree import ConfigTree
-from vyos.template import is_ipv4
-from vyos.template import is_ipv6
-
if len(argv) < 2:
print("Must specify file name!")
diff --git a/src/migration-scripts/l2tp/2-to-3 b/src/migration-scripts/l2tp/2-to-3
index b46b0f22e..8527c2d4a 100755
--- a/src/migration-scripts/l2tp/2-to-3
+++ b/src/migration-scripts/l2tp/2-to-3
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2020 VyOS maintainers and contributors
+# Copyright (C) 2020-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,9 +17,6 @@
# - remove primary/secondary identifier from nameserver
# - TODO: remove radius server req-limit
-import os
-import sys
-
from sys import argv, exit
from vyos.configtree import ConfigTree
@@ -38,7 +35,6 @@ if not config.exists(base):
# Nothing to do
exit(0)
else:
-
# Migrate IPv4 DNS servers
dns_base = base + ['dns-servers']
if config.exists(dns_base):
diff --git a/src/migration-scripts/l2tp/3-to-4 b/src/migration-scripts/l2tp/3-to-4
index 8c2b909b7..14b86ff04 100755
--- a/src/migration-scripts/l2tp/3-to-4
+++ b/src/migration-scripts/l2tp/3-to-4
@@ -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
@@ -23,7 +23,6 @@ from sys import argv
from sys import exit
from vyos.configtree import ConfigTree
from vyos.pki import load_certificate
-from vyos.pki import load_crl
from vyos.pki import load_private_key
from vyos.pki import encode_certificate
from vyos.pki import encode_private_key
diff --git a/src/migration-scripts/l2tp/4-to-5 b/src/migration-scripts/l2tp/4-to-5
index 3176f895a..b7f4d2677 100755
--- a/src/migration-scripts/l2tp/4-to-5
+++ b/src/migration-scripts/l2tp/4-to-5
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2023 VyOS maintainers and contributors
+# Copyright (C) 2023-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
@@ -19,8 +19,6 @@
# 'subnet' migrate to namedpool 'default-subnet-pool'
# 'default-subnet-pool' is the next pool for 'default-range-pool'
-import os
-
from sys import argv
from sys import exit
from vyos.configtree import ConfigTree
diff --git a/src/migration-scripts/l2tp/5-to-6 b/src/migration-scripts/l2tp/5-to-6
index ca0b13dcc..ac40b89c8 100755
--- a/src/migration-scripts/l2tp/5-to-6
+++ b/src/migration-scripts/l2tp/5-to-6
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2023 VyOS maintainers and contributors
+# 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
@@ -14,14 +14,10 @@
# 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
-
from sys import argv
from sys import exit
from vyos.configtree import ConfigTree
-
if len(argv) < 2:
print("Must specify file name!")
exit(1)
diff --git a/src/migration-scripts/l2tp/6-to-7 b/src/migration-scripts/l2tp/6-to-7
index f49c4ab08..1c536585c 100755
--- a/src/migration-scripts/l2tp/6-to-7
+++ b/src/migration-scripts/l2tp/6-to-7
@@ -16,13 +16,10 @@
# Migrating to named ipv6 pools
-import os
-
from sys import argv
from sys import exit
from vyos.configtree import ConfigTree
-
if len(argv) < 2:
print("Must specify file name!")
exit(1)
diff --git a/src/migration-scripts/l2tp/7-to-8 b/src/migration-scripts/l2tp/7-to-8
index 4956e1155..e429ed057 100755
--- a/src/migration-scripts/l2tp/7-to-8
+++ b/src/migration-scripts/l2tp/7-to-8
@@ -17,13 +17,10 @@
# Migrate from 'ccp-disable' to 'ppp-options.disable-ccp'
# Migration ipv6 options
-import os
-
from sys import argv
from sys import exit
from vyos.configtree import ConfigTree
-
if len(argv) < 2:
print("Must specify file name!")
exit(1)
diff --git a/src/migration-scripts/l2tp/8-to-9 b/src/migration-scripts/l2tp/8-to-9
index e85a3892b..672180e25 100755
--- a/src/migration-scripts/l2tp/8-to-9
+++ b/src/migration-scripts/l2tp/8-to-9
@@ -16,13 +16,10 @@
# Deleted 'dhcp-interface' from l2tp
-import os
-
from sys import argv
from sys import exit
from vyos.configtree import ConfigTree
-
if len(argv) < 2:
print("Must specify file name!")
exit(1)
@@ -37,7 +34,7 @@ base = ['vpn', 'l2tp', 'remote-access']
if not config.exists(base):
exit(0)
-#deleting unused dhcp-interface
+# deleting unused dhcp-interface
if config.exists(base + ['dhcp-interface']):
config.delete(base + ['dhcp-interface'])
diff --git a/src/migration-scripts/nat/5-to-6 b/src/migration-scripts/nat/5-to-6
index c83b93d84..cfe98ddcf 100755
--- a/src/migration-scripts/nat/5-to-6
+++ b/src/migration-scripts/nat/5-to-6
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2023 VyOS maintainers and contributors
+# 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
@@ -18,46 +18,84 @@
# to
# 'set nat [source|destination] rule X [inbound-interface|outbound interface] interface-name <iface>'
+# T6100: Migration from 1.3.X to 1.4
+# Change IP/netmask to Network/netmask in
+# 'set nat [source|destination] rule X [source| destination| translation] address <IP/Netmask| !IP/Netmask>'
+
+import ipaddress
from sys import argv,exit
from vyos.configtree import ConfigTree
-if len(argv) < 2:
- print("Must specify file name!")
- exit(1)
-
-file_name = argv[1]
-
-with open(file_name, 'r') as f:
- config_file = f.read()
-
-config = ConfigTree(config_file)
-
-if not config.exists(['nat']):
- # Nothing to do
- exit(0)
-
-for direction in ['source', 'destination']:
- # If a node doesn't exist, we obviously have nothing to do.
- if not config.exists(['nat', direction]):
- continue
-
- # However, we also need to handle the case when a 'source' or 'destination' sub-node does exist,
- # but there are no rules under it.
- if not config.list_nodes(['nat', direction]):
- continue
-
- for rule in config.list_nodes(['nat', direction, 'rule']):
- base = ['nat', direction, 'rule', rule]
- for iface in ['inbound-interface','outbound-interface']:
- if config.exists(base + [iface]):
- tmp = config.return_value(base + [iface])
- if tmp:
- config.delete(base + [iface])
- config.set(base + [iface, 'interface-name'], value=tmp)
-
-try:
- with open(file_name, 'w') as f:
- f.write(config.to_string())
-except OSError as e:
- print("Failed to save the modified config: {}".format(e))
- exit(1)
+
+def _func_T5643(conf, base_path):
+ for iface in ['inbound-interface', 'outbound-interface']:
+ if conf.exists(base_path + [iface]):
+ tmp = conf.return_value(base_path + [iface])
+ if tmp:
+ conf.delete(base_path + [iface])
+ conf.set(base_path + [iface, 'interface-name'], value=tmp)
+ return
+
+
+def _func_T6100(conf, base_path):
+ for addr_type in ['source', 'destination', 'translation']:
+ base_addr_type = base_path + [addr_type]
+ if not conf.exists(base_addr_type) or not conf.exists(
+ base_addr_type + ['address']):
+ continue
+
+ address = conf.return_value(base_addr_type + ['address'])
+
+ if not address or '/' not in address:
+ continue
+
+ negative = ''
+ network = address
+ if '!' in address:
+ negative = '!'
+ network = str(address.split(negative)[1])
+
+ network_ip = ipaddress.ip_network(network, strict=False)
+ if str(network_ip) != network:
+ network = f'{negative}{str(network_ip)}'
+ conf.set(base_addr_type + ['address'], value=network)
+ return
+
+
+if __name__ == '__main__':
+ if len(argv) < 2:
+ print("Must specify file name!")
+ exit(1)
+
+ file_name = argv[1]
+
+ with open(file_name, 'r') as f:
+ config_file = f.read()
+
+ config = ConfigTree(config_file)
+
+ if not config.exists(['nat']):
+ # Nothing to do
+ exit(0)
+
+ for direction in ['source', 'destination']:
+ # If a node doesn't exist, we obviously have nothing to do.
+ if not config.exists(['nat', direction]):
+ continue
+
+ # However, we also need to handle the case when a 'source' or 'destination' sub-node does exist,
+ # but there are no rules under it.
+ if not config.list_nodes(['nat', direction]):
+ continue
+
+ for rule in config.list_nodes(['nat', direction, 'rule']):
+ base = ['nat', direction, 'rule', rule]
+ _func_T5643(config,base)
+ _func_T6100(config,base)
+
+ try:
+ with open(file_name, 'w') as f:
+ f.write(config.to_string())
+ except OSError as e:
+ print("Failed to save the modified config: {}".format(e))
+ exit(1)
diff --git a/src/migration-scripts/openconnect/0-to-1 b/src/migration-scripts/openconnect/0-to-1
index 8be15fad1..c64b16cb2 100755
--- a/src/migration-scripts/openconnect/0-to-1
+++ b/src/migration-scripts/openconnect/0-to-1
@@ -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
@@ -22,7 +22,6 @@ from sys import argv
from sys import exit
from vyos.configtree import ConfigTree
from vyos.pki import load_certificate
-from vyos.pki import load_crl
from vyos.pki import load_private_key
from vyos.pki import encode_certificate
from vyos.pki import encode_private_key
diff --git a/src/migration-scripts/ospf/0-to-1 b/src/migration-scripts/ospf/0-to-1
index a6cb9feb8..4085423a2 100755
--- a/src/migration-scripts/ospf/0-to-1
+++ b/src/migration-scripts/ospf/0-to-1
@@ -31,7 +31,8 @@ def ospf_passive_migration(config, ospf_base):
config.set_tag(ospf_base + ['interface'])
config.delete(ospf_base + ['passive-interface'])
- config.set(ospf_base + ['passive-interface'], value='default')
+ if default:
+ config.set(ospf_base + ['passive-interface'], value='default')
if config.exists(ospf_base + ['passive-interface-exclude']):
for interface in config.return_values(ospf_base + ['passive-interface-exclude']):
diff --git a/src/migration-scripts/policy/4-to-5 b/src/migration-scripts/policy/4-to-5
index 5b8fee17e..738850f67 100755
--- a/src/migration-scripts/policy/4-to-5
+++ b/src/migration-scripts/policy/4-to-5
@@ -16,13 +16,10 @@
# T2199: Migrate interface policy nodes to policy route <name> interface <ifname>
-import re
-
from sys import argv
from sys import exit
from vyos.configtree import ConfigTree
-from vyos.ifconfig import Section
if len(argv) < 2:
print("Must specify file name!")
diff --git a/src/migration-scripts/policy/5-to-6 b/src/migration-scripts/policy/5-to-6
index f1545cddb..86287d578 100755
--- a/src/migration-scripts/policy/5-to-6
+++ b/src/migration-scripts/policy/5-to-6
@@ -16,13 +16,10 @@
# T5165: Migrate policy local-route rule <tag> destination|source
-import re
-
from sys import argv
from sys import exit
from vyos.configtree import ConfigTree
-from vyos.ifconfig import Section
if len(argv) < 2:
print("Must specify file name!")
diff --git a/src/migration-scripts/policy/6-to-7 b/src/migration-scripts/policy/6-to-7
index 727b8487a..cdefc6837 100755
--- a/src/migration-scripts/policy/6-to-7
+++ b/src/migration-scripts/policy/6-to-7
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2023 VyOS maintainers and contributors
+# Copyright (C) 2023-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
@@ -22,13 +22,10 @@
# set policy [route | route6] ... rule <rule> log
# Remove command if log=disable
-import re
-
from sys import argv
from sys import exit
from vyos.configtree import ConfigTree
-from vyos.ifconfig import Section
if len(argv) < 2:
print("Must specify file name!")
@@ -48,7 +45,7 @@ if not config.exists(base):
for family in ['route', 'route6']:
if config.exists(base + [family]):
-
+
for policy_name in config.list_nodes(base + [family]):
if config.exists(base + [family, policy_name, 'rule']):
for rule in config.list_nodes(base + [family, policy_name, 'rule']):
@@ -76,4 +73,4 @@ try:
f.write(config.to_string())
except OSError as e:
print("Failed to save the modified config: {}".format(e))
- exit(1) \ No newline at end of file
+ exit(1)
diff --git a/src/migration-scripts/pppoe-server/1-to-2 b/src/migration-scripts/pppoe-server/1-to-2
index c73899ca1..b266893c0 100755
--- a/src/migration-scripts/pppoe-server/1-to-2
+++ b/src/migration-scripts/pppoe-server/1-to-2
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2020 VyOS maintainers and contributors
+# Copyright (C) 2020-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
@@ -16,8 +16,6 @@
# change mppe node to a leaf node with value prefer
-import os
-
from sys import argv, exit
from vyos.configtree import ConfigTree
@@ -58,4 +56,3 @@ else:
except OSError as e:
print("Failed to save the modified config: {}".format(e))
exit(1)
-
diff --git a/src/migration-scripts/pppoe-server/3-to-4 b/src/migration-scripts/pppoe-server/3-to-4
index c07bbb1df..477ed6f22 100755
--- a/src/migration-scripts/pppoe-server/3-to-4
+++ b/src/migration-scripts/pppoe-server/3-to-4
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2020 VyOS maintainers and contributors
+# Copyright (C) 2020-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
@@ -16,8 +16,6 @@
# - remove primary/secondary identifier from nameserver
-import os
-
from sys import argv, exit
from vyos.configtree import ConfigTree
diff --git a/src/migration-scripts/pppoe-server/6-to-7 b/src/migration-scripts/pppoe-server/6-to-7
index b94ce57f9..d51c1c9d8 100755
--- a/src/migration-scripts/pppoe-server/6-to-7
+++ b/src/migration-scripts/pppoe-server/6-to-7
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2023 VyOS maintainers and contributors
+# Copyright (C) 2023-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
@@ -24,8 +24,6 @@
# If there are not named pools, namedless pool will be default.
# 2. If authentication mode = 'radius' then namedless pool will be default
-import os
-
from sys import argv
from sys import exit
from vyos.configtree import ConfigTree
diff --git a/src/migration-scripts/pppoe-server/7-to-8 b/src/migration-scripts/pppoe-server/7-to-8
index b0d9bb464..0381f0bf9 100755
--- a/src/migration-scripts/pppoe-server/7-to-8
+++ b/src/migration-scripts/pppoe-server/7-to-8
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2023 VyOS maintainers and contributors
+# Copyright (C) 2023-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
@@ -16,13 +16,10 @@
# Migrating to named ipv6 pools
-import os
-
from sys import argv
from sys import exit
from vyos.configtree import ConfigTree
-
if len(argv) < 2:
print("Must specify file name!")
exit(1)
diff --git a/src/migration-scripts/pppoe-server/8-to-9 b/src/migration-scripts/pppoe-server/8-to-9
index ad75c28a1..4932a766f 100755
--- a/src/migration-scripts/pppoe-server/8-to-9
+++ b/src/migration-scripts/pppoe-server/8-to-9
@@ -17,13 +17,10 @@
# Change from 'ccp' to 'disable-ccp' in ppp-option section
# Migration ipv6 options
-import os
-
from sys import argv
from sys import exit
from vyos.configtree import ConfigTree
-
if len(argv) < 2:
print("Must specify file name!")
exit(1)
diff --git a/src/migration-scripts/pptp/2-to-3 b/src/migration-scripts/pptp/2-to-3
index 091cb68ec..42c4dedf4 100755
--- a/src/migration-scripts/pptp/2-to-3
+++ b/src/migration-scripts/pptp/2-to-3
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2023 VyOS maintainers and contributors
+# Copyright (C) 2023-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
@@ -18,8 +18,6 @@
# 'start-stop' migrate to namedpool 'default-range-pool'
# 'default-subnet-pool' is the next pool for 'default-range-pool'
-import os
-
from sys import argv
from sys import exit
from vyos.configtree import ConfigTree
diff --git a/src/migration-scripts/pptp/3-to-4 b/src/migration-scripts/pptp/3-to-4
index 0a8dad2f4..ebd343028 100755
--- a/src/migration-scripts/pptp/3-to-4
+++ b/src/migration-scripts/pptp/3-to-4
@@ -16,13 +16,10 @@
# - Move 'mppe' from 'authentication' node to 'ppp-options'
-import os
-
from sys import argv
from sys import exit
from vyos.configtree import ConfigTree
-
if len(argv) < 2:
print("Must specify file name!")
exit(1)
diff --git a/src/migration-scripts/pptp/4-to-5 b/src/migration-scripts/pptp/4-to-5
index d4b3f9a14..83632b6d8 100755
--- a/src/migration-scripts/pptp/4-to-5
+++ b/src/migration-scripts/pptp/4-to-5
@@ -17,13 +17,10 @@
# - Move 'require' from 'protocols' in 'authentication' node
# - Migrate to new default values in radius timeout and acct-timeout
-import os
-
from sys import argv
from sys import exit
from vyos.configtree import ConfigTree
-
if len(argv) < 2:
print("Must specify file name!")
exit(1)
diff --git a/src/migration-scripts/snmp/2-to-3 b/src/migration-scripts/snmp/2-to-3
index 30911aa27..ab9b5dcba 100755
--- a/src/migration-scripts/snmp/2-to-3
+++ b/src/migration-scripts/snmp/2-to-3
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2022 VyOS maintainers and contributors
+# Copyright (C) 2022-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
@@ -20,13 +20,10 @@
# To
# set service snmp oid-enable ip-forward
-import re
-
from sys import argv
from sys import exit
from vyos.configtree import ConfigTree
-from vyos.ifconfig import Section
if len(argv) < 2:
print("Must specify file name!")
diff --git a/src/migration-scripts/sstp/0-to-1 b/src/migration-scripts/sstp/0-to-1
index e2fe1ea8f..150127aaf 100755
--- a/src/migration-scripts/sstp/0-to-1
+++ b/src/migration-scripts/sstp/0-to-1
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2020 VyOS maintainers and contributors
+# Copyright (C) 2020-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
@@ -23,7 +23,6 @@
# - do not migrate radius server req-limit, use default of unlimited
# - migrate SSL certificate path
-import os
import sys
from vyos.configtree import ConfigTree
diff --git a/src/migration-scripts/sstp/3-to-4 b/src/migration-scripts/sstp/3-to-4
index 00ca7a52d..5b7757e60 100755
--- a/src/migration-scripts/sstp/3-to-4
+++ b/src/migration-scripts/sstp/3-to-4
@@ -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
@@ -22,7 +22,6 @@ from sys import argv
from sys import exit
from vyos.configtree import ConfigTree
from vyos.pki import load_certificate
-from vyos.pki import load_crl
from vyos.pki import load_private_key
from vyos.pki import encode_certificate
from vyos.pki import encode_private_key
diff --git a/src/migration-scripts/sstp/4-to-5 b/src/migration-scripts/sstp/4-to-5
index 95e482713..6907240a0 100755
--- a/src/migration-scripts/sstp/4-to-5
+++ b/src/migration-scripts/sstp/4-to-5
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2023 VyOS maintainers and contributors
+# Copyright (C) 2023-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
@@ -18,13 +18,10 @@
# 'subnet' migrate to namedpool 'default-subnet-pool'
# 'default-subnet-pool' is the next pool for 'default-range-pool'
-import os
-
from sys import argv
from sys import exit
from vyos.configtree import ConfigTree
-
if len(argv) < 2:
print("Must specify file name!")
exit(1)
diff --git a/src/migration-scripts/sstp/5-to-6 b/src/migration-scripts/sstp/5-to-6
index bac9975b2..43b99044d 100755
--- a/src/migration-scripts/sstp/5-to-6
+++ b/src/migration-scripts/sstp/5-to-6
@@ -16,14 +16,10 @@
# Migrating to named ipv6 pools
-import os
-import pprint
-
from sys import argv
from sys import exit
from vyos.configtree import ConfigTree
-
if len(argv) < 2:
print("Must specify file name!")
exit(1)
diff --git a/src/migration-scripts/system/15-to-16 b/src/migration-scripts/system/15-to-16
index aa1c34032..2944cdb1e 100755
--- a/src/migration-scripts/system/15-to-16
+++ b/src/migration-scripts/system/15-to-16
@@ -2,7 +2,6 @@
#
# Make 'system options reboot-on-panic' valueless
-import os
import sys
from vyos.configtree import ConfigTree
diff --git a/src/migration-scripts/system/16-to-17 b/src/migration-scripts/system/16-to-17
index 37e02611d..afa171a9b 100755
--- a/src/migration-scripts/system/16-to-17
+++ b/src/migration-scripts/system/16-to-17
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2020 VyOS maintainers and contributors
+# Copyright (C) 2020-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
@@ -20,7 +20,6 @@
# This is the only privilege level left and also the default, what is the
# sense in keeping this orphaned node?
-import os
import sys
from vyos.configtree import ConfigTree
diff --git a/src/migration-scripts/system/19-to-20 b/src/migration-scripts/system/19-to-20
index c04e6a5a6..177173c50 100755
--- a/src/migration-scripts/system/19-to-20
+++ b/src/migration-scripts/system/19-to-20
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2020 VyOS maintainers and contributors
+# Copyright (C) 2020-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
@@ -16,8 +16,6 @@
# T3048: remove smp-affinity node from ethernet and use tuned instead
-import os
-
from sys import exit, argv
from vyos.configtree import ConfigTree
diff --git a/src/migration-scripts/system/20-to-21 b/src/migration-scripts/system/20-to-21
index 4bcf4edab..24e042ce2 100755
--- a/src/migration-scripts/system/20-to-21
+++ b/src/migration-scripts/system/20-to-21
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2021 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
@@ -16,8 +16,6 @@
# T3795: merge "system name-servers-dhcp" into "system name-server"
-import os
-
from sys import argv
from vyos.configtree import ConfigTree
diff --git a/src/migration-scripts/system/21-to-22 b/src/migration-scripts/system/21-to-22
index 810b634ab..2a1b603c6 100755
--- a/src/migration-scripts/system/21-to-22
+++ b/src/migration-scripts/system/21-to-22
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2021 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
@@ -14,8 +14,6 @@
# 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
-
from sys import exit, argv
from vyos.configtree import ConfigTree
diff --git a/src/migration-scripts/system/22-to-23 b/src/migration-scripts/system/22-to-23
index 8ed198383..f83279b88 100755
--- a/src/migration-scripts/system/22-to-23
+++ b/src/migration-scripts/system/22-to-23
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2022 VyOS maintainers and contributors
+# Copyright (C) 2022-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
@@ -14,8 +14,6 @@
# 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
-
from sys import exit, argv
from vyos.configtree import ConfigTree
diff --git a/src/migration-scripts/system/23-to-24 b/src/migration-scripts/system/23-to-24
index fd68dbf22..1fd61d83b 100755
--- a/src/migration-scripts/system/23-to-24
+++ b/src/migration-scripts/system/23-to-24
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2022 VyOS maintainers and contributors
+# Copyright (C) 2022-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
@@ -14,8 +14,6 @@
# 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
-
from ipaddress import ip_interface
from ipaddress import ip_address
from sys import exit, argv
diff --git a/src/op_mode/bridge.py b/src/op_mode/bridge.py
index 412a4eba8..d04f1541f 100755
--- a/src/op_mode/bridge.py
+++ b/src/op_mode/bridge.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2022-2023 VyOS maintainers and contributors
+# Copyright (C) 2022-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
@@ -19,13 +19,11 @@ import json
import sys
import typing
-from sys import exit
from tabulate import tabulate
from vyos.utils.process import cmd
from vyos.utils.process import rc_cmd
from vyos.utils.process import call
-from vyos.utils.dict import dict_search
import vyos.opmode
diff --git a/src/op_mode/conntrack.py b/src/op_mode/conntrack.py
index 6ea213bec..c379c3e60 100755
--- a/src/op_mode/conntrack.py
+++ b/src/op_mode/conntrack.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2022-2023 VyOS maintainers and contributors
+# Copyright (C) 2022-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
@@ -20,7 +20,6 @@ import xmltodict
from tabulate import tabulate
from vyos.utils.process import cmd
-from vyos.utils.process import run
import vyos.opmode
@@ -63,7 +62,7 @@ def _get_raw_data(family):
def _get_raw_statistics():
entries = []
- data = cmd('sudo conntrack -S')
+ data = cmd('sudo conntrack --stats')
data = data.replace(' \t', '').split('\n')
for entry in data:
entries.append(entry.split())
@@ -71,8 +70,25 @@ def _get_raw_statistics():
def get_formatted_statistics(entries):
- headers = ["CPU", "Found", "Invalid", "Insert", "Insert fail", "Drop", "Early drop", "Errors", "Search restart"]
- output = tabulate(entries, headers, numalign="left")
+ headers = [
+ "CPU",
+ "Found",
+ "Invalid",
+ "Insert",
+ "Insert fail",
+ "Drop",
+ "Early drop",
+ "Errors",
+ "Search restart",
+ "",
+ "",
+ ]
+ # Process each entry to extract and format the values after '='
+ processed_entries = [
+ [value.split('=')[-1] for value in entry]
+ for entry in entries
+ ]
+ output = tabulate(processed_entries, headers, numalign="left")
return output
diff --git a/src/op_mode/conntrack_sync.py b/src/op_mode/conntrack_sync.py
index a38688e45..6c86ff492 100755
--- a/src/op_mode/conntrack_sync.py
+++ b/src/op_mode/conntrack_sync.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2022 VyOS maintainers and contributors
+# Copyright (C) 2022-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
@@ -21,7 +21,6 @@ import xmltodict
import vyos.opmode
-from argparse import ArgumentParser
from vyos.configquery import CliShellApiConfigQuery
from vyos.configquery import ConfigTreeQuery
from vyos.utils.commit import commit_in_progress
diff --git a/src/op_mode/container.py b/src/op_mode/container.py
index d29af8821..05f65df1f 100755
--- a/src/op_mode/container.py
+++ b/src/op_mode/container.py
@@ -17,12 +17,8 @@
import json
import sys
-from sys import exit
-
from vyos.utils.process import cmd
-from vyos.utils.process import call
from vyos.utils.process import rc_cmd
-
import vyos.opmode
def _get_json_data(command: str) -> list:
diff --git a/src/op_mode/dhcp.py b/src/op_mode/dhcp.py
index a2f947400..f6029c748 100755
--- a/src/op_mode/dhcp.py
+++ b/src/op_mode/dhcp.py
@@ -80,14 +80,20 @@ def _get_raw_server_leases(family='inet', pool=None, sorted=None, state=[], orig
:return list
"""
inet_suffix = '6' if family == 'inet6' else '4'
- leases = kea_get_leases(inet_suffix)
+ try:
+ leases = kea_get_leases(inet_suffix)
+ except:
+ raise vyos.opmode.DataUnavailable('Cannot fetch DHCP server lease information')
if pool is None:
pool = _get_dhcp_pools(family=family)
else:
pool = [pool]
- active_config = kea_get_active_config(inet_suffix)
+ try:
+ active_config = kea_get_active_config(inet_suffix)
+ except:
+ raise vyos.opmode.DataUnavailable('Cannot fetch DHCP server configuration')
data = []
for lease in leases:
diff --git a/src/op_mode/firewall.py b/src/op_mode/firewall.py
index 4dcffc412..25554b781 100755
--- a/src/op_mode/firewall.py
+++ b/src/op_mode/firewall.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2023 VyOS maintainers and contributors
+# Copyright (C) 2023-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
@@ -16,9 +16,9 @@
import argparse
import ipaddress
-import json
import re
import tabulate
+import textwrap
from vyos.config import Config
from vyos.utils.process import cmd
@@ -89,6 +89,14 @@ def get_nftables_details(family, hook, priority):
out[rule_id] = rule
return out
+def output_firewall_vertical(rules, headers):
+ for rule in rules:
+ adjusted_rule = rule + [""] * (len(headers) - len(rule)) # account for different header length, like default-action
+ transformed_rule = [[header, textwrap.fill(adjusted_rule[i].replace('\n', ' '), 65)] for i, header in enumerate(headers)] # create key-pair list from headers and rules lists; wrap at 100 char
+
+ print(tabulate.tabulate(transformed_rule, tablefmt="presto"))
+ print()
+
def output_firewall_name(family, hook, priority, firewall_conf, single_rule_id=None):
print(f'\n---------------------------------\n{family} Firewall "{hook} {priority}"\n')
@@ -103,7 +111,7 @@ def output_firewall_name(family, hook, priority, firewall_conf, single_rule_id=N
if 'disable' in rule_conf:
continue
- row = [rule_id, rule_conf['action'], rule_conf['protocol'] if 'protocol' in rule_conf else 'all']
+ row = [rule_id, textwrap.fill(rule_conf.get('description') or '', 50), rule_conf['action'], rule_conf['protocol'] if 'protocol' in rule_conf else 'all']
if rule_id in details:
rule_details = details[rule_id]
row.append(rule_details.get('packets', 0))
@@ -115,7 +123,7 @@ def output_firewall_name(family, hook, priority, firewall_conf, single_rule_id=N
def_action = firewall_conf['default_action'] if 'default_action' in firewall_conf else 'accept'
else:
def_action = firewall_conf['default_action'] if 'default_action' in firewall_conf else 'drop'
- row = ['default', def_action, 'all']
+ row = ['default', '', def_action, 'all']
rule_details = details['default-action']
row.append(rule_details.get('packets', 0))
row.append(rule_details.get('bytes', 0))
@@ -123,8 +131,17 @@ def output_firewall_name(family, hook, priority, firewall_conf, single_rule_id=N
rows.append(row)
if rows:
- header = ['Rule', 'Action', 'Protocol', 'Packets', 'Bytes', 'Conditions']
- print(tabulate.tabulate(rows, header) + '\n')
+ if args.rule:
+ rows.pop()
+
+ if args.detail:
+ header = ['Rule', 'Description', 'Action', 'Protocol', 'Packets', 'Bytes', 'Conditions']
+ output_firewall_vertical(rows, header)
+ else:
+ header = ['Rule', 'Action', 'Protocol', 'Packets', 'Bytes', 'Conditions']
+ for i in rows:
+ rows[rows.index(i)].pop(1)
+ print(tabulate.tabulate(rows, header) + '\n')
def output_firewall_name_statistics(family, hook, prior, prior_conf, single_rule_id=None):
print(f'\n---------------------------------\n{family} Firewall "{hook} {prior}"\n')
@@ -192,7 +209,7 @@ def output_firewall_name_statistics(family, hook, prior, prior_conf, single_rule
if not oiface:
oiface = 'any'
- row = [rule_id]
+ row = [rule_id, textwrap.fill(rule_conf.get('description') or '', 50)]
if rule_id in details:
rule_details = details[rule_id]
row.append(rule_details.get('packets', 0))
@@ -209,7 +226,7 @@ def output_firewall_name_statistics(family, hook, prior, prior_conf, single_rule
if hook in ['input', 'forward', 'output']:
- row = ['default']
+ row = ['default', '']
rule_details = details['default-action']
row.append(rule_details.get('packets', 0))
row.append(rule_details.get('bytes', 0))
@@ -224,7 +241,7 @@ def output_firewall_name_statistics(family, hook, prior, prior_conf, single_rule
rows.append(row)
elif 'default_action' in prior_conf and not single_rule_id:
- row = ['default']
+ row = ['default', '']
if 'default-action' in details:
rule_details = details['default-action']
row.append(rule_details.get('packets', 0))
@@ -240,8 +257,14 @@ def output_firewall_name_statistics(family, hook, prior, prior_conf, single_rule
rows.append(row)
if rows:
- header = ['Rule', 'Packets', 'Bytes', 'Action', 'Source', 'Destination', 'Inbound-Interface', 'Outbound-interface']
- print(tabulate.tabulate(rows, header) + '\n')
+ if args.detail:
+ header = ['Rule', 'Description', 'Packets', 'Bytes', 'Action', 'Source', 'Destination', 'Inbound-Interface', 'Outbound-interface']
+ output_firewall_vertical(rows, header)
+ else:
+ header = ['Rule', 'Packets', 'Bytes', 'Action', 'Source', 'Destination', 'Inbound-Interface', 'Outbound-interface']
+ for i in rows:
+ rows[rows.index(i)].pop(1)
+ print(tabulate.tabulate(rows, header) + '\n')
def show_firewall():
print('Rulesets Information')
@@ -429,7 +452,6 @@ def show_firewall_group(name=None):
return out
- header = ['Name', 'Type', 'References', 'Members']
rows = []
for group_type, group_type_conf in firewall['group'].items():
@@ -441,7 +463,7 @@ def show_firewall_group(name=None):
continue
references = find_references(group_type, group_name)
- row = [group_name, group_type, '\n'.join(references) or 'N/D']
+ row = [group_name, textwrap.fill(group_conf.get('description') or '', 50), group_type, '\n'.join(references) or 'N/D']
if 'address' in group_conf:
row.append("\n".join(sorted(group_conf['address'])))
elif 'network' in group_conf:
@@ -461,13 +483,20 @@ def show_firewall_group(name=None):
if dynamic_type in firewall['group']['dynamic_group']:
for dynamic_name, dynamic_conf in firewall['group']['dynamic_group'][dynamic_type].items():
references = find_references(dynamic_type, dynamic_name)
- row = [dynamic_name, dynamic_type + '(dynamic)', '\n'.join(references) or 'N/D']
+ row = [dynamic_name, textwrap.fill(dynamic_conf.get('description') or '', 50), dynamic_type + '(dynamic)', '\n'.join(references) or 'N/D']
row.append('N/D')
rows.append(row)
if rows:
print('Firewall Groups\n')
- print(tabulate.tabulate(rows, header))
+ if args.detail:
+ header = ['Name', 'Description','Type', 'References', 'Members']
+ output_firewall_vertical(rows, header)
+ else:
+ header = ['Name', 'Type', 'References', 'Members']
+ for i in rows:
+ rows[rows.index(i)].pop(1)
+ print(tabulate.tabulate(rows, header))
def show_summary():
print('Ruleset Summary')
@@ -539,6 +568,7 @@ if __name__ == '__main__':
parser.add_argument('--priority', help='Firewall priority', required=False, action='store', nargs='?', default='')
parser.add_argument('--rule', help='Firewall Rule ID', required=False)
parser.add_argument('--ipv6', help='IPv6 toggle', action='store_true')
+ parser.add_argument('--detail', help='Firewall view select', required=False)
args = parser.parse_args()
diff --git a/src/op_mode/generate_ovpn_client_file.py b/src/op_mode/generate_ovpn_client_file.py
index cec370a07..2d96fe217 100755
--- a/src/op_mode/generate_ovpn_client_file.py
+++ b/src/op_mode/generate_ovpn_client_file.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2022 VyOS maintainers and contributors
+# Copyright (C) 2022-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
@@ -15,15 +15,12 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import argparse
-import os
from jinja2 import Template
from textwrap import fill
from vyos.configquery import ConfigTreeQuery
from vyos.ifconfig import Section
-from vyos.utils.process import cmd
-
client_config = """
diff --git a/src/op_mode/generate_tech-support_archive.py b/src/op_mode/generate_tech-support_archive.py
index c490b0137..41b53cd15 100755
--- a/src/op_mode/generate_tech-support_archive.py
+++ b/src/op_mode/generate_tech-support_archive.py
@@ -120,7 +120,7 @@ if __name__ == '__main__':
# Temporary directory creation
tmp_dir_path = f'{tmp_path}/drops-debug_{time_now}'
tmp_dir: Path = Path(tmp_dir_path)
- tmp_dir.mkdir()
+ tmp_dir.mkdir(parents=True)
report_file: Path = Path(f'{tmp_dir_path}/show_tech-support_report.txt')
report_file.touch()
diff --git a/src/op_mode/ikev2_profile_generator.py b/src/op_mode/ikev2_profile_generator.py
index 5454cc0ce..2b29f94bf 100755
--- a/src/op_mode/ikev2_profile_generator.py
+++ b/src/op_mode/ikev2_profile_generator.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2021 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
@@ -16,7 +16,6 @@
import argparse
-from jinja2 import Template
from sys import exit
from socket import getfqdn
from cryptography.x509.oid import NameOID
diff --git a/src/op_mode/image_info.py b/src/op_mode/image_info.py
index 791001e00..56aefcd6e 100755
--- a/src/op_mode/image_info.py
+++ b/src/op_mode/image_info.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright 2023 VyOS maintainers and contributors <maintainers@vyos.io>
+# Copyright 2023-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
# This file is part of VyOS.
#
@@ -18,12 +18,14 @@
# VyOS. If not, see <https://www.gnu.org/licenses/>.
import sys
-from typing import List, Union
+from typing import Union
from tabulate import tabulate
from vyos import opmode
-from vyos.system import disk, grub, image
+from vyos.system import disk
+from vyos.system import grub
+from vyos.system import image
from vyos.utils.convert import bytes_to_human
diff --git a/src/op_mode/image_installer.py b/src/op_mode/image_installer.py
index b0567305a..9f6949fb3 100755
--- a/src/op_mode/image_installer.py
+++ b/src/op_mode/image_installer.py
@@ -23,7 +23,6 @@ from shutil import copy, chown, rmtree, copytree
from glob import glob
from sys import exit
from os import environ
-from time import sleep
from typing import Union
from urllib.parse import urlparse
from passlib.hosts import linux_context
@@ -57,6 +56,8 @@ MSG_INFO_INSTALL_DISK_CONFIRM: str = 'Installation will delete all data on the d
MSG_INFO_INSTALL_RAID_CONFIRM: str = 'Installation will delete all data on both drives. Continue?'
MSG_INFO_INSTALL_PARTITONING: str = 'Creating partition table...'
MSG_INPUT_CONFIG_FOUND: str = 'An active configuration was found. Would you like to copy it to the new image?'
+MSG_INPUT_CONFIG_CHOICE: str = 'The following config files are available for boot:'
+MSG_INPUT_CONFIG_CHOOSE: str = 'Which file would you like as boot config?'
MSG_INPUT_IMAGE_NAME: str = 'What would you like to name this image?'
MSG_INPUT_IMAGE_DEFAULT: str = 'Would you like to set the new image as the default one for boot?'
MSG_INPUT_PASSWORD: str = 'Please enter a password for the "vyos" user'
@@ -703,6 +704,10 @@ def install_image() -> None:
valid_responses=['K', 'S', 'U'])
console_dict: dict[str, str] = {'K': 'tty', 'S': 'ttyS', 'U': 'ttyUSB'}
+ config_boot_list = ['/opt/vyatta/etc/config/config.boot',
+ '/opt/vyatta/etc/config.boot.default']
+ default_config = config_boot_list[0]
+
disks: dict[str, int] = find_disks()
install_target: Union[disk.DiskDetails, raid.RaidDetails, None] = None
@@ -711,6 +716,14 @@ def install_image() -> None:
if install_target is None:
install_target = ask_single_disk(disks)
+ # if previous install was selected in search_previous_installation,
+ # directory /mnt/config was prepared for copy below; if not, prompt:
+ if not Path('/mnt/config').exists():
+ default_config: str = select_entry(config_boot_list,
+ MSG_INPUT_CONFIG_CHOICE,
+ MSG_INPUT_CONFIG_CHOOSE,
+ default_entry=1) # select_entry indexes from 1
+
# create directories for installation media
prepare_tmp_disr()
@@ -732,7 +745,7 @@ def install_image() -> None:
chown(target_config_dir, group='vyattacfg')
chmod_2775(target_config_dir)
# copy config
- copy('/opt/vyatta/etc/config/config.boot', target_config_dir)
+ copy(default_config, f'{target_config_dir}/config.boot')
configure_authentication(f'{target_config_dir}/config.boot',
user_password)
Path(f'{target_config_dir}/.vyatta_config').touch()
diff --git a/src/op_mode/image_manager.py b/src/op_mode/image_manager.py
index 1510a667c..1cfb5f5a1 100755
--- a/src/op_mode/image_manager.py
+++ b/src/op_mode/image_manager.py
@@ -33,27 +33,31 @@ DELETE_IMAGE_PROMPT_MSG: str = 'Select an image to delete:'
MSG_DELETE_IMAGE_RUNNING: str = 'Currently running image cannot be deleted; reboot into another image first'
MSG_DELETE_IMAGE_DEFAULT: str = 'Default image cannot be deleted; set another image as default first'
-def annotated_list(images_list: list[str]) -> list[str]:
+def annotate_list(images_list: list[str]) -> list[str]:
"""Annotate list of images with additional info
Args:
images_list (list[str]): a list of image names
Returns:
- list[str]: a list of image names with additional info
+ dict[str, str]: a dict of annotations indexed by image name
"""
- index_running: int = None
- index_default: int = None
- try:
- index_running = images_list.index(image.get_running_image())
- index_default = images_list.index(image.get_default_image())
- except ValueError:
- pass
- if index_running is not None:
- images_list[index_running] += ' (running)'
- if index_default is not None:
- images_list[index_default] += ' (default boot)'
- return images_list
+ running = image.get_running_image()
+ default = image.get_default_image()
+ annotated = {}
+ for image_name in images_list:
+ annotated[image_name] = f'{image_name}'
+ if running in images_list:
+ annotated[running] = annotated[running] + ' (running)'
+ if default in images_list:
+ annotated[default] = annotated[default] + ' (default boot)'
+ return annotated
+
+def define_format(images):
+ annotated = annotate_list(images)
+ def format_selection(image_name):
+ return annotated[image_name]
+ return format_selection
@compat.grub_cfg_update
def delete_image(image_name: Optional[str] = None,
@@ -63,14 +67,16 @@ def delete_image(image_name: Optional[str] = None,
Args:
image_name (str): a name of image to delete
"""
- available_images: list[str] = annotated_list(grub.version_list())
+ available_images: list[str] = grub.version_list()
+ format_selection = define_format(available_images)
if image_name is None:
if no_prompt:
exit('An image name is required for delete action')
else:
image_name = select_entry(available_images,
DELETE_IMAGE_LIST_MSG,
- DELETE_IMAGE_PROMPT_MSG)
+ DELETE_IMAGE_PROMPT_MSG,
+ format_selection)
if image_name == image.get_running_image():
exit(MSG_DELETE_IMAGE_RUNNING)
if image_name == image.get_default_image():
@@ -113,14 +119,16 @@ def set_image(image_name: Optional[str] = None,
Args:
image_name (str): an image name
"""
- available_images: list[str] = annotated_list(grub.version_list())
+ available_images: list[str] = grub.version_list()
+ format_selection = define_format(available_images)
if image_name is None:
if not prompt:
exit('An image name is required for set action')
else:
image_name = select_entry(available_images,
SET_IMAGE_LIST_MSG,
- SET_IMAGE_PROMPT_MSG)
+ SET_IMAGE_PROMPT_MSG,
+ format_selection)
if image_name == image.get_default_image():
exit(f'The image "{image_name}" already configured as default')
if image_name not in available_images:
diff --git a/src/op_mode/interfaces_wireless.py b/src/op_mode/interfaces_wireless.py
index 259fd3900..bf6e462f3 100755
--- a/src/op_mode/interfaces_wireless.py
+++ b/src/op_mode/interfaces_wireless.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2023 VyOS maintainers and contributors
+# Copyright (C) 2023-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
@@ -16,7 +16,6 @@
import re
import sys
-import typing
import vyos.opmode
from copy import deepcopy
diff --git a/src/op_mode/openvpn.py b/src/op_mode/openvpn.py
index fd9d2db92..d54a67199 100755
--- a/src/op_mode/openvpn.py
+++ b/src/op_mode/openvpn.py
@@ -205,11 +205,11 @@ def _format_openvpn(data: list) -> str:
intf = d['intf']
l_host = d['local_host']
l_port = d['local_port']
+ out += f'\nOpenVPN status on {intf}\n\n'
for client in d['clients']:
r_host = client['remote_host']
r_port = client['remote_port']
- out += f'\nOpenVPN status on {intf}\n\n'
name = client['name']
remote = r_host + ':' + r_port if r_host and r_port else 'N/A'
tunnel = client['tunnel']
@@ -220,9 +220,8 @@ def _format_openvpn(data: list) -> str:
data_out.append([name, remote, tunnel, local, tx_bytes,
rx_bytes, online_since])
- if data_out:
- out += tabulate(data_out, headers)
- out += "\n"
+ out += tabulate(data_out, headers)
+ out += "\n"
return out
diff --git a/src/op_mode/otp.py b/src/op_mode/otp.py
index 6d4298894..a4ab9b22b 100755
--- a/src/op_mode/otp.py
+++ b/src/op_mode/otp.py
@@ -20,9 +20,7 @@ import sys
import os
import vyos.opmode
from jinja2 import Template
-from vyos.configquery import ConfigTreeQuery
-from vyos.xml import defaults
-from vyos.configdict import dict_merge
+from vyos.config import Config
from vyos.utils.process import popen
@@ -61,7 +59,7 @@ def _check_uname_otp(username:str):
"""
Check if "username" exists and have an OTP key
"""
- config = ConfigTreeQuery()
+ config = Config()
base_key = ['system', 'login', 'user', username, 'authentication', 'otp', 'key']
if not config.exists(base_key):
return None
@@ -71,15 +69,13 @@ def _get_login_otp(username: str, info:str):
"""
Retrieve user settings from configuration and set some defaults
"""
- config = ConfigTreeQuery()
+ config = Config()
base = ['system', 'login', 'user', username]
if not config.exists(base):
return None
- user_otp = config.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True)
- # We have gathered the dict representation of the CLI, but there are default
- # options which we need to update into the dictionary retrived.
- default_values = defaults(['system', 'login', 'user'])
- user_otp = dict_merge(default_values, user_otp)
+ user_otp = config.get_config_dict(base, key_mangling=('-', '_'),
+ get_first_key=True,
+ with_recursive_defaults=True)
result = user_otp['authentication']['otp']
# Filling in the system and default options
result['info'] = info
@@ -94,7 +90,7 @@ def _get_login_otp(username: str, info:str):
result['otp_url'] = ''.join(["otpauth://",token_type_acrn,"/",username,"@",\
result['hostname'],"?secret=",result['key_base32'],"&digits=",\
result['otp_length'],"&period=",result['interval']])
- result['qrcode'],err = popen('qrencode -t ansiutf8', input=result['otp_url'])
+ result['qrcode'],_ = popen('qrencode -t ansiutf8', input=result['otp_url'])
return result
def show_login(raw: bool, username: str, info:str):
diff --git a/src/op_mode/policy_route.py b/src/op_mode/policy_route.py
index eff99de7f..d12465008 100755
--- a/src/op_mode/policy_route.py
+++ b/src/op_mode/policy_route.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2021 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
@@ -20,7 +20,6 @@ import tabulate
from vyos.config import Config
from vyos.utils.process import cmd
-from vyos.utils.dict import dict_search_args
def get_config_policy(conf, name=None, ipv6=False):
config_path = ['policy']
diff --git a/src/op_mode/powerctrl.py b/src/op_mode/powerctrl.py
index c07d0c4bd..6c8f802b5 100755
--- a/src/op_mode/powerctrl.py
+++ b/src/op_mode/powerctrl.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2023 VyOS maintainers and contributors
+# Copyright (C) 2023-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
@@ -18,7 +18,7 @@ import os
import re
from argparse import ArgumentParser
-from datetime import datetime, timedelta, time as type_time, date as type_date
+from datetime import datetime
from sys import exit
from time import time
diff --git a/src/op_mode/restart_dhcp_relay.py b/src/op_mode/restart_dhcp_relay.py
index 3ead97f4c..42626cac4 100755
--- a/src/op_mode/restart_dhcp_relay.py
+++ b/src/op_mode/restart_dhcp_relay.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2018 VyOS maintainers and contributors
+# Copyright (C) 2018-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
@@ -20,7 +20,6 @@
import sys
import argparse
-import os
import vyos.config
from vyos.utils.process import call
diff --git a/src/op_mode/reverseproxy.py b/src/op_mode/reverseproxy.py
index 44ffd7a37..19704182a 100755
--- a/src/op_mode/reverseproxy.py
+++ b/src/op_mode/reverseproxy.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2023 VyOS maintainers and contributors
+# Copyright (C) 2023-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,9 +17,7 @@
import json
import socket
import sys
-import typing
-from sys import exit
from tabulate import tabulate
from vyos.configquery import ConfigTreeQuery
diff --git a/src/op_mode/sflow.py b/src/op_mode/sflow.py
index dca7f44cb..0f3feb35a 100755
--- a/src/op_mode/sflow.py
+++ b/src/op_mode/sflow.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2023 VyOS maintainers and contributors
+# Copyright (C) 2023-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
@@ -20,7 +20,6 @@ import sys
from tabulate import tabulate
from vyos.configquery import ConfigTreeQuery
-from vyos.utils.process import cmd
import vyos.opmode
diff --git a/src/op_mode/show_techsupport_report.py b/src/op_mode/show_techsupport_report.py
index 53144fd52..230fb252d 100644
--- a/src/op_mode/show_techsupport_report.py
+++ b/src/op_mode/show_techsupport_report.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2023 VyOS maintainers and contributors
+# Copyright (C) 2023-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
@@ -14,8 +14,6 @@
# 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
-
from typing import List
from vyos.utils.process import rc_cmd
from vyos.ifconfig import Section
diff --git a/src/op_mode/snmp.py b/src/op_mode/snmp.py
index 43f5d9e0a..3d6cd220a 100755
--- a/src/op_mode/snmp.py
+++ b/src/op_mode/snmp.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2018 VyOS maintainers and contributors
+# Copyright (C) 2018-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
@@ -13,13 +13,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/>.
-#
-# File: snmp.py
-# Purpose:
-# Show SNMP community/remote hosts
-# Used by the "run show snmp community" commands.
-import os
import sys
import argparse
diff --git a/src/op_mode/system.py b/src/op_mode/system.py
index 11a3a8730..854b4b699 100755
--- a/src/op_mode/system.py
+++ b/src/op_mode/system.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2022 VyOS maintainers and contributors
+# Copyright (C) 2022-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
@@ -15,12 +15,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import jmespath
-import json
import sys
-import requests
-import typing
-
-from sys import exit
from vyos.configquery import ConfigTreeQuery
diff --git a/src/op_mode/vpn_ike_sa.py b/src/op_mode/vpn_ike_sa.py
index 7186bdec2..5e2aaae6b 100755
--- a/src/op_mode/vpn_ike_sa.py
+++ b/src/op_mode/vpn_ike_sa.py
@@ -15,14 +15,13 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import argparse
-import re
import sys
import vici
from vyos.utils.process import process_named_running
ike_sa_peer_prefix = """\
-Peer ID / IP Local ID / IP
+Peer ID / IP Local ID / IP
------------ -------------"""
ike_sa_tunnel_prefix = """
diff --git a/src/op_mode/vrrp.py b/src/op_mode/vrrp.py
index b3ab55cc3..60be86065 100755
--- a/src/op_mode/vrrp.py
+++ b/src/op_mode/vrrp.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2018-2023 VyOS maintainers and contributors
+# Copyright (C) 2018-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
@@ -15,14 +15,10 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import sys
-import time
import argparse
-import json
-import tabulate
from vyos.configquery import ConfigTreeQuery
from vyos.ifconfig.vrrp import VRRP
-from vyos.ifconfig.vrrp import VRRPError
from vyos.ifconfig.vrrp import VRRPNoData
parser = argparse.ArgumentParser()
diff --git a/src/services/api/graphql/generate/composite_function.py b/src/services/api/graphql/generate/composite_function.py
index bc9d80fbb..d6626fd1f 100644
--- a/src/services/api/graphql/generate/composite_function.py
+++ b/src/services/api/graphql/generate/composite_function.py
@@ -1,11 +1,7 @@
# typing information for composite functions: those that invoke several
# elementary requests, and return the result as a single dict
-import typing
-
def system_status():
pass
queries = {'system_status': system_status}
-
mutations = {}
-
diff --git a/src/services/api/graphql/graphql/auth_token_mutation.py b/src/services/api/graphql/graphql/auth_token_mutation.py
index 603a13758..a53fa4d60 100644
--- a/src/services/api/graphql/graphql/auth_token_mutation.py
+++ b/src/services/api/graphql/graphql/auth_token_mutation.py
@@ -1,4 +1,4 @@
-# Copyright 2022 VyOS maintainers and contributors <maintainers@vyos.io>
+# Copyright 2022-2024 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
@@ -13,10 +13,10 @@
# You should have received a copy of the GNU Lesser General Public License
# along with this library. If not, see <http://www.gnu.org/licenses/>.
-import jwt
import datetime
-from typing import Any, Dict
-from ariadne import ObjectType, UnionType
+from typing import Any
+from typing import Dict
+from ariadne import ObjectType
from graphql import GraphQLResolveInfo
from .. libs.token_auth import generate_token
diff --git a/src/services/api/graphql/graphql/directives.py b/src/services/api/graphql/graphql/directives.py
index a7919854a..3927aee58 100644
--- a/src/services/api/graphql/graphql/directives.py
+++ b/src/services/api/graphql/graphql/directives.py
@@ -1,4 +1,4 @@
-# Copyright 2021 VyOS maintainers and contributors <maintainers@vyos.io>
+# Copyright 2021-2024 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
@@ -13,7 +13,7 @@
# You should have received a copy of the GNU Lesser General Public License
# along with this library. If not, see <http://www.gnu.org/licenses/>.
-from ariadne import SchemaDirectiveVisitor, ObjectType
+from ariadne import SchemaDirectiveVisitor
from . queries import *
from . mutations import *
diff --git a/src/services/api/graphql/graphql/mutations.py b/src/services/api/graphql/graphql/mutations.py
index 8254e22b1..d115a8e94 100644
--- a/src/services/api/graphql/graphql/mutations.py
+++ b/src/services/api/graphql/graphql/mutations.py
@@ -1,4 +1,4 @@
-# Copyright 2021-2022 VyOS maintainers and contributors <maintainers@vyos.io>
+# Copyright 2021-2024 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
@@ -14,11 +14,13 @@
# along with this library. If not, see <http://www.gnu.org/licenses/>.
from importlib import import_module
-from typing import Any, Dict, Optional
from ariadne import ObjectType, convert_camel_case_to_snake
-from graphql import GraphQLResolveInfo
from makefun import with_signature
+# used below by func_sig
+from typing import Any, Dict, Optional # pylint: disable=W0611
+from graphql import GraphQLResolveInfo # pylint: disable=W0611
+
from .. import state
from .. libs import key_auth
from api.graphql.session.session import Session
diff --git a/src/services/api/graphql/graphql/queries.py b/src/services/api/graphql/graphql/queries.py
index daccc19b2..717098259 100644
--- a/src/services/api/graphql/graphql/queries.py
+++ b/src/services/api/graphql/graphql/queries.py
@@ -1,4 +1,4 @@
-# Copyright 2021-2022 VyOS maintainers and contributors <maintainers@vyos.io>
+# Copyright 2021-2024 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
@@ -14,11 +14,13 @@
# along with this library. If not, see <http://www.gnu.org/licenses/>.
from importlib import import_module
-from typing import Any, Dict, Optional
from ariadne import ObjectType, convert_camel_case_to_snake
-from graphql import GraphQLResolveInfo
from makefun import with_signature
+# used below by func_sig
+from typing import Any, Dict, Optional # pylint: disable=W0611
+from graphql import GraphQLResolveInfo # pylint: disable=W0611
+
from .. import state
from .. libs import key_auth
from api.graphql.session.session import Session
diff --git a/src/services/api/graphql/libs/op_mode.py b/src/services/api/graphql/libs/op_mode.py
index 5022f7d4e..86e38eae6 100644
--- a/src/services/api/graphql/libs/op_mode.py
+++ b/src/services/api/graphql/libs/op_mode.py
@@ -1,4 +1,4 @@
-# Copyright 2022 VyOS maintainers and contributors <maintainers@vyos.io>
+# Copyright 2022-2024 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
@@ -16,7 +16,9 @@
import os
import re
import typing
-from typing import Union, Tuple, Optional
+
+from typing import Union
+from typing import Optional
from humps import decamelize
from vyos.defaults import directories
diff --git a/src/services/api/graphql/session/composite/system_status.py b/src/services/api/graphql/session/composite/system_status.py
index d809f32e3..516a4eff6 100755
--- a/src/services/api/graphql/session/composite/system_status.py
+++ b/src/services/api/graphql/session/composite/system_status.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2022 VyOS maintainers and contributors
+# Copyright (C) 2022-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
@@ -13,15 +13,6 @@
#
# 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 sys
-import json
-import importlib.util
-
-from vyos.defaults import directories
from api.graphql.libs.op_mode import load_op_mode_as_module
diff --git a/src/services/api/graphql/session/session.py b/src/services/api/graphql/session/session.py
index 3c5a062b6..6ae44b9bf 100644
--- a/src/services/api/graphql/session/session.py
+++ b/src/services/api/graphql/session/session.py
@@ -1,4 +1,4 @@
-# Copyright 2021-2022 VyOS maintainers and contributors <maintainers@vyos.io>
+# Copyright 2021-2024 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
@@ -21,7 +21,6 @@ from ariadne import convert_camel_case_to_snake
from vyos.config import Config
from vyos.configtree import ConfigTree
from vyos.defaults import directories
-from vyos.template import render
from vyos.opmode import Error as OpModeError
from api.graphql.libs.op_mode import load_op_mode_as_module, split_compound_op_mode_name
diff --git a/src/services/vyos-http-api-server b/src/services/vyos-http-api-server
index 77870a84c..ecbf6fcf9 100755
--- a/src/services/vyos-http-api-server
+++ b/src/services/vyos-http-api-server
@@ -140,6 +140,14 @@ class ConfigSectionModel(ApiModel, BaseConfigSectionModel):
class ConfigSectionListModel(ApiModel):
commands: List[BaseConfigSectionModel]
+class BaseConfigSectionTreeModel(BaseModel):
+ op: StrictStr
+ mask: Dict
+ config: Dict
+
+class ConfigSectionTreeModel(ApiModel, BaseConfigSectionTreeModel):
+ pass
+
class RetrieveModel(ApiModel):
op: StrictStr
path: List[StrictStr]
@@ -374,7 +382,7 @@ class MultipartRequest(Request):
self.form_err = (400,
f"Malformed command '{c}': missing 'op' field")
if endpoint not in ('/config-file', '/container-image',
- '/image'):
+ '/image', '/configure-section'):
if 'path' not in c:
self.form_err = (400,
f"Malformed command '{c}': missing 'path' field")
@@ -392,12 +400,9 @@ class MultipartRequest(Request):
self.form_err = (400,
f"Malformed command '{c}': 'value' field must be a string")
if endpoint in ('/configure-section'):
- if 'section' not in c:
- self.form_err = (400,
- f"Malformed command '{c}': missing 'section' field")
- elif not isinstance(c['section'], dict):
+ if 'section' not in c and 'config' not in c:
self.form_err = (400,
- f"Malformed command '{c}': 'section' field must be JSON of dict")
+ f"Malformed command '{c}': missing 'section' or 'config' field")
if 'key' not in forms and 'key' not in merge:
self.form_err = (401, "Valid API key is required")
@@ -455,7 +460,8 @@ def call_commit(s: ConfigSession):
logger.warning(f"ConfigSessionError: {e}")
def _configure_op(data: Union[ConfigureModel, ConfigureListModel,
- ConfigSectionModel, ConfigSectionListModel],
+ ConfigSectionModel, ConfigSectionListModel,
+ ConfigSectionTreeModel],
request: Request, background_tasks: BackgroundTasks):
session = app.state.vyos_session
env = session.get_session_env()
@@ -481,7 +487,8 @@ def _configure_op(data: Union[ConfigureModel, ConfigureListModel,
try:
for c in data:
op = c.op
- path = c.path
+ if not isinstance(c, BaseConfigSectionTreeModel):
+ path = c.path
if isinstance(c, BaseConfigureModel):
if c.value:
@@ -495,6 +502,10 @@ def _configure_op(data: Union[ConfigureModel, ConfigureListModel,
elif isinstance(c, BaseConfigSectionModel):
section = c.section
+ elif isinstance(c, BaseConfigSectionTreeModel):
+ mask = c.mask
+ config = c.config
+
if isinstance(c, BaseConfigureModel):
if op == 'set':
session.set(path, value=value)
@@ -514,6 +525,14 @@ def _configure_op(data: Union[ConfigureModel, ConfigureListModel,
session.load_section(path, section)
else:
raise ConfigSessionError(f"'{op}' is not a valid operation")
+
+ elif isinstance(c, BaseConfigSectionTreeModel):
+ if op == 'set':
+ session.set_section_tree(config)
+ elif op == 'load':
+ session.load_section_tree(mask, config)
+ else:
+ raise ConfigSessionError(f"'{op}' is not a valid operation")
# end for
config = Config(session_env=env)
d = get_config_diff(config)
@@ -554,7 +573,8 @@ def configure_op(data: Union[ConfigureModel,
@app.post('/configure-section')
def configure_section_op(data: Union[ConfigSectionModel,
- ConfigSectionListModel],
+ ConfigSectionListModel,
+ ConfigSectionTreeModel],
request: Request, background_tasks: BackgroundTasks):
return _configure_op(data, request, background_tasks)
diff --git a/src/system/keepalived-fifo.py b/src/system/keepalived-fifo.py
index 6d33e372d..24733803a 100755
--- a/src/system/keepalived-fifo.py
+++ b/src/system/keepalived-fifo.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2020-2021 VyOS maintainers and contributors
+# Copyright (C) 2020-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
@@ -20,13 +20,11 @@ import signal
import argparse
import threading
import re
-import json
import logging
from queue import Queue
from logging.handlers import SysLogHandler
-from vyos.ifconfig.vrrp import VRRP
from vyos.configquery import ConfigTreeQuery
from vyos.utils.process import cmd
from vyos.utils.dict import dict_search
diff --git a/src/tests/test_config_diff.py b/src/tests/test_config_diff.py
index f61cbc4a2..61a2f3487 100644
--- a/src/tests/test_config_diff.py
+++ b/src/tests/test_config_diff.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2023 VyOS maintainers and contributors
+# Copyright (C) 2023-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
@@ -14,7 +14,6 @@
# 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 vyos.configtree
from unittest import TestCase
diff --git a/src/tests/test_config_parser.py b/src/tests/test_config_parser.py
index 8148aa79b..c69732daa 100644
--- a/src/tests/test_config_parser.py
+++ b/src/tests/test_config_parser.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2018-2020 VyOS maintainers and contributors
+# Copyright (C) 2018-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
@@ -14,7 +14,6 @@
# 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 vyos.configtree
from unittest import TestCase
diff --git a/src/tests/test_initial_setup.py b/src/tests/test_initial_setup.py
index ba50d06cc..f85bf1265 100644
--- a/src/tests/test_initial_setup.py
+++ b/src/tests/test_initial_setup.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2018 VyOS maintainers and contributors
+# Copyright (C) 2018-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
@@ -14,8 +14,6 @@
# 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 tempfile
import unittest
import vyos.configtree
import vyos.initialsetup as vis
@@ -101,4 +99,3 @@ class TestInitialSetup(TestCase):
if __name__ == "__main__":
unittest.main()
-
diff --git a/src/tests/test_template.py b/src/tests/test_template.py
index aba97015e..dbb86b40b 100644
--- a/src/tests/test_template.py
+++ b/src/tests/test_template.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2020-2023 VyOS maintainers and contributors
+# Copyright (C) 2020-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
@@ -14,9 +14,9 @@
# 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 vyos.template
+from vyos.utils.network import interface_exists
from ipaddress import ip_network
from unittest import TestCase
@@ -26,7 +26,7 @@ class TestVyOSTemplate(TestCase):
def test_is_interface(self):
for interface in ['lo', 'eth0']:
- if os.path.exists(f'/sys/class/net/{interface}'):
+ if interface_exists(interface):
self.assertTrue(vyos.template.is_interface(interface))
else:
self.assertFalse(vyos.template.is_interface(interface))