summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/package-smoketest.yml47
-rw-r--r--.gitignore2
-rw-r--r--Makefile19
-rw-r--r--data/config-mode-dependencies/vyos-1x.json3
-rw-r--r--data/configd-include.json114
-rw-r--r--data/templates/firewall/nftables.j22
-rw-r--r--data/templates/stunnel/stunnel_config.j2118
-rw-r--r--debian/control6
-rw-r--r--interface-definitions/include/stunnel/address.xml.i20
-rw-r--r--interface-definitions/include/stunnel/connect.xml.i11
-rw-r--r--interface-definitions/include/stunnel/listen.xml.i11
-rw-r--r--interface-definitions/include/stunnel/protocol-options.xml.i75
-rw-r--r--interface-definitions/include/stunnel/protocol-value-cifs.xml.i6
-rw-r--r--interface-definitions/include/stunnel/protocol-value-connect.xml.i6
-rw-r--r--interface-definitions/include/stunnel/protocol-value-imap.xml.i6
-rw-r--r--interface-definitions/include/stunnel/protocol-value-nntp.xml.i6
-rw-r--r--interface-definitions/include/stunnel/protocol-value-pgsql.xml.i6
-rw-r--r--interface-definitions/include/stunnel/protocol-value-pop3.xml.i6
-rw-r--r--interface-definitions/include/stunnel/protocol-value-proxy.xml.i6
-rw-r--r--interface-definitions/include/stunnel/protocol-value-smtp.xml.i6
-rw-r--r--interface-definitions/include/stunnel/protocol-value-socks.xml.i6
-rw-r--r--interface-definitions/include/stunnel/psk.xml.i30
-rw-r--r--interface-definitions/include/stunnel/ssl.xml.i11
-rw-r--r--interface-definitions/service_stunnel.xml.in130
-rw-r--r--op-mode-definitions/show-kernel-modules.xml.in20
-rw-r--r--python/vyos/base.py7
-rw-r--r--python/vyos/component_version.py248
-rw-r--r--python/vyos/compose_config.py12
-rw-r--r--python/vyos/configtree.py13
-rw-r--r--python/vyos/ethtool.py4
-rw-r--r--python/vyos/load_config.py31
-rw-r--r--python/vyos/migrate.py283
-rw-r--r--python/vyos/migrator.py226
-rw-r--r--python/vyos/utils/auth.py4
-rw-r--r--python/vyos/utils/dict.py2
-rw-r--r--python/vyos/utils/disk.py51
-rw-r--r--python/vyos/utils/kernel.py77
-rw-r--r--python/vyos/utils/system.py13
-rwxr-xr-xscripts/generate-configd-include-json.py34
-rwxr-xr-xsmoketest/bin/vyos-configtest9
-rwxr-xr-xsmoketest/bin/vyos-configtest-pki6
-rw-r--r--smoketest/config-tests/basic-api-service20
-rw-r--r--smoketest/config-tests/basic-vyos47
-rw-r--r--smoketest/config-tests/bgp-azure-ipsec-gateway231
-rw-r--r--smoketest/config-tests/bgp-bfd-communities201
-rw-r--r--smoketest/config-tests/bgp-big-as-cloud850
-rw-r--r--smoketest/config-tests/bgp-dmvpn-hub69
-rw-r--r--smoketest/config-tests/bgp-dmvpn-spoke75
-rw-r--r--smoketest/config-tests/bgp-evpn-l2vpn-leaf55
-rw-r--r--smoketest/config-tests/bgp-evpn-l2vpn-spine48
-rw-r--r--smoketest/config-tests/bgp-evpn-l3vpn-pe-router123
-rw-r--r--smoketest/config-tests/bgp-medium-confederation2
-rw-r--r--smoketest/config-tests/bgp-rpki43
-rw-r--r--smoketest/config-tests/bgp-small-internet-exchange209
-rw-r--r--smoketest/config-tests/bgp-small-ipv4-unicast32
-rw-r--r--smoketest/config-tests/cluster-basic21
-rw-r--r--smoketest/config-tests/container-simple16
-rw-r--r--smoketest/config-tests/dialup-router-complex740
-rw-r--r--smoketest/config-tests/dialup-router-medium-vpn17
-rw-r--r--smoketest/config-tests/dialup-router-wireguard-ipv6431
-rw-r--r--smoketest/config-tests/egp-igp-route-maps46
-rw-r--r--smoketest/config-tests/igmp-pim-small22
-rw-r--r--smoketest/config-tests/ipoe-server27
-rw-r--r--smoketest/config-tests/ipv6-disable31
-rw-r--r--smoketest/config-tests/isis-small44
-rw-r--r--smoketest/config-tests/nat-basic81
-rw-r--r--smoketest/config-tests/ospf-simple10
-rw-r--r--smoketest/config-tests/ospf-small82
-rw-r--r--smoketest/config-tests/pppoe-server47
-rw-r--r--smoketest/config-tests/qos-basic75
-rw-r--r--smoketest/config-tests/rip-router83
-rw-r--r--smoketest/config-tests/rpki-only14
-rw-r--r--smoketest/config-tests/tunnel-broker75
-rw-r--r--smoketest/config-tests/vpn-openconnect-sstp35
-rw-r--r--smoketest/config-tests/vrf-basic65
-rw-r--r--smoketest/config-tests/vrf-bgp-pppoe-underlay186
-rw-r--r--smoketest/config-tests/vrf-ospf59
-rw-r--r--smoketest/config-tests/wireless-basic2
-rw-r--r--smoketest/configs/basic-api-service2
-rw-r--r--smoketest/configs/bgp-dmvpn-hub5
-rw-r--r--smoketest/configs/bgp-evpn-l3vpn-pe-router2
-rw-r--r--smoketest/configs/bgp-rpki1
-rw-r--r--smoketest/configs/isis-small41
-rw-r--r--smoketest/configs/pppoe-server4
-rw-r--r--smoketest/scripts/cli/base_vyostest_shim.py5
-rw-r--r--smoketest/scripts/cli/test_service_stunnel.py624
-rwxr-xr-xsmoketest/scripts/cli/test_system_login.py10
-rwxr-xr-xsmoketest/scripts/cli/test_vpn_openconnect.py52
-rwxr-xr-xsrc/conf_mode/pki.py4
-rw-r--r--src/conf_mode/service_stunnel.py264
-rwxr-xr-xsrc/helpers/add-system-version.py (renamed from src/helpers/system-versions-foot.py)14
-rwxr-xr-xsrc/helpers/run-config-migration.py128
-rwxr-xr-xsrc/helpers/vyos-load-config.py21
-rwxr-xr-xsrc/helpers/vyos-merge-config.py17
-rwxr-xr-xsrc/helpers/vyos-save-config.py15
-rwxr-xr-xsrc/helpers/vyos_net_name10
-rwxr-xr-xsrc/init/vyos-router4
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/bgp/0-to-172
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/bgp/1-to-2112
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/bgp/2-to-363
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/bgp/3-to-487
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/bgp/4-to-577
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/cluster/1-to-265
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/config-management/0-to-143
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/conntrack-sync/1-to-250
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/conntrack/1-to-240
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/conntrack/2-to-351
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/conntrack/3-to-456
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/conntrack/4-to-562
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/container/0-to-1102
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/container/1-to-256
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/dhcp-relay/1-to-242
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/dhcp-server/10-to-1152
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/dhcp-server/4-to-5202
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/dhcp-server/5-to-6132
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/dhcp-server/6-to-794
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/dhcp-server/7-to-898
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/dhcp-server/8-to-982
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/dhcp-server/9-to-1081
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/dhcpv6-server/0-to-147
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/dhcpv6-server/1-to-2100
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/dhcpv6-server/2-to-3102
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/dhcpv6-server/3-to-4101
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/dhcpv6-server/4-to-5140
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/dns-dynamic/0-to-1161
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/dns-dynamic/1-to-285
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/dns-dynamic/2-to-3134
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/dns-dynamic/3-to-487
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/dns-forwarding/0-to-149
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/dns-forwarding/1-to-2111
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/dns-forwarding/2-to-357
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/dns-forwarding/3-to-454
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/firewall/10-to-11312
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/firewall/11-to-1288
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/firewall/12-to-13114
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/firewall/13-to-1464
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/firewall/14-to-1549
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/firewall/15-to-1639
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/firewall/5-to-6160
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/firewall/6-to-7444
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/firewall/7-to-884
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/firewall/8-to-9126
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/firewall/9-to-10104
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/flow-accounting/0-to-194
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/https/0-to-149
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/https/1-to-249
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/https/2-to-388
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/https/3-to-459
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/https/4-to-579
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/https/5-to-6156
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/ids/0-to-174
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/interfaces/0-to-1151
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/interfaces/1-to-242
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/interfaces/10-to-1143
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/interfaces/11-to-1241
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/interfaces/12-to-1342
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/interfaces/13-to-1443
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/interfaces/14-to-1542
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/interfaces/15-to-1642
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/interfaces/16-to-1743
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/interfaces/17-to-1841
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/interfaces/18-to-1945
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/interfaces/19-to-2042
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/interfaces/2-to-341
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/interfaces/20-to-21199
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/interfaces/21-to-2257
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/interfaces/22-to-2347
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/interfaces/23-to-2442
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/interfaces/24-to-2543
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/interfaces/25-to-26643
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/interfaces/26-to-2767
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/interfaces/27-to-2857
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/interfaces/28-to-2963
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/interfaces/29-to-3055
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/interfaces/3-to-442
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/interfaces/30-to-3195
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/interfaces/31-to-3264
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/interfaces/32-to-3353
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/interfaces/4-to-536
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/interfaces/5-to-641
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/interfaces/6-to-742
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/interfaces/7-to-842
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/interfaces/8-to-941
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/interfaces/9-to-1041
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/ipoe-server/1-to-2162
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/ipoe-server/2-to-374
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/ipsec/10-to-11126
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/ipsec/11-to-1256
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/ipsec/12-to-1350
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/ipsec/4-to-555
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/ipsec/5-to-6140
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/ipsec/6-to-760
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/ipsec/7-to-8137
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/ipsec/8-to-950
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/ipsec/9-to-10223
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/isis/0-to-164
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/isis/1-to-255
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/isis/2-to-382
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/l2tp/0-to-146
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/l2tp/1-to-247
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/l2tp/2-to-353
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/l2tp/3-to-4262
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/l2tp/4-to-5105
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/l2tp/5-to-6176
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/l2tp/6-to-766
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/l2tp/7-to-888
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/l2tp/8-to-950
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/lldp/0-to-148
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/lldp/1-to-254
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/monitoring/0-to-183
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/nat/4-to-547
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/nat/5-to-645
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/nat/6-to-791
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/nat/7-to-881
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/nat66/0-to-159
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/nat66/1-to-276
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/nat66/2-to-386
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/ntp/0-to-142
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/ntp/1-to-2101
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/ntp/2-to-369
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/openconnect/0-to-1203
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/openconnect/1-to-249
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/openconnect/2-to-356
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/openvpn/0-to-142
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/openvpn/1-to-227
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/ospf/0-to-178
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/ospf/1-to-2116
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/pim/0-to-186
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/policy/0-to-176
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/policy/1-to-2111
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/policy/2-to-370
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/policy/3-to-4135
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/policy/4-to-5115
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/policy/5-to-680
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/policy/6-to-798
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/policy/7-to-858
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/pppoe-server/0-to-147
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/pppoe-server/1-to-247
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/pppoe-server/2-to-345
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/pppoe-server/3-to-448
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/pppoe-server/4-to-549
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/pppoe-server/5-to-663
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/pppoe-server/6-to-7172
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/pppoe-server/7-to-874
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/pppoe-server/8-to-990
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/pppoe-server/9-to-1066
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/pptp/0-to-143
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/pptp/1-to-248
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/pptp/2-to-398
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/pptp/3-to-453
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/pptp/4-to-582
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/qos/1-to-2282
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/quagga/10-to-1156
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/quagga/2-to-354
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/quagga/3-to-456
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/quagga/4-to-555
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/quagga/5-to-655
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/quagga/6-to-7185
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/quagga/7-to-877
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/quagga/8-to-992
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/quagga/9-to-1082
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/reverse-proxy/0-to-155
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/rip/0-to-156
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/rpki/0-to-189
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/rpki/1-to-296
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/salt/0-to-150
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/snmp/0-to-148
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/snmp/1-to-281
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/snmp/2-to-355
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/ssh/0-to-142
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/ssh/1-to-2118
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/sstp/0-to-149
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/sstp/1-to-247
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/sstp/2-to-349
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/sstp/3-to-4203
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/sstp/4-to-566
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/sstp/5-to-674
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/system/10-to-1144
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/system/11-to-1241
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/system/12-to-1371
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/system/13-to-1441
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/system/14-to-1541
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/system/15-to-1642
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/system/16-to-1748
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/system/17-to-1847
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/system/18-to-19151
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/system/19-to-2074
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/system/20-to-2150
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/system/21-to-2273
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/system/22-to-2359
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/system/23-to-2492
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/system/24-to-2561
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/system/25-to-2699
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/system/26-to-2755
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/system/6-to-774
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/system/7-to-842
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/system/8-to-942
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/vrf/0-to-1223
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/vrf/1-to-281
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/vrf/2-to-3149
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/vrrp/1-to-2346
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/vrrp/2-to-386
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/vrrp/3-to-461
-rw-r--r--[-rwxr-xr-x]src/migration-scripts/webproxy/1-to-244
-rwxr-xr-xsrc/op_mode/kernel_modules.py82
-rwxr-xr-xsrc/op_mode/storage.py66
-rwxr-xr-xsrc/services/vyos-configd10
-rw-r--r--src/shim/vyshim.c11
-rw-r--r--src/systemd/stunnel.service15
-rw-r--r--src/tests/helper.py4
-rw-r--r--src/tests/test_config_diff.py2
-rw-r--r--src/tests/test_config_parser.py2
-rw-r--r--[-rwxr-xr-x]src/tests/test_configd_inspect.py (renamed from smoketest/scripts/cli/test_configd_inspect.py)21
-rw-r--r--src/tests/test_configverify.py4
-rw-r--r--src/tests/test_dependency_graph.py4
-rw-r--r--src/tests/test_dict_search.py4
-rw-r--r--[-rwxr-xr-x]src/tests/test_find_device_file.py7
-rw-r--r--src/tests/test_initial_setup.py2
-rw-r--r--src/tests/test_op_mode.py5
-rw-r--r--src/tests/test_task_scheduler.py4
-rw-r--r--src/tests/test_template.py2
-rw-r--r--src/tests/test_utils.py4
-rw-r--r--src/tests/test_utils_network.py7
-rw-r--r--src/validators/psk-secret39
324 files changed, 13401 insertions, 11574 deletions
diff --git a/.github/workflows/package-smoketest.yml b/.github/workflows/package-smoketest.yml
index 49bd91669..27272a6e2 100644
--- a/.github/workflows/package-smoketest.yml
+++ b/.github/workflows/package-smoketest.yml
@@ -1,13 +1,22 @@
-name: Package ISO Test
+name: VyOS ISO integration Test
on:
- pull_request:
+ pull_request_target:
branches:
- current
+ - circinus
+
+permissions:
+ pull-requests: write
+ contents: read
+
+env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed for PR comments
jobs:
build:
runs-on: ubuntu-24.04
+ timeout-minutes: 45
container:
image: vyos/vyos-build:current
options: --sysctl net.ipv6.conf.lo.disable_ipv6=0 --privileged
@@ -24,9 +33,7 @@ jobs:
- name: Clone vyos-1x source code
uses: actions/checkout@v4
with:
- repository: vyos/vyos-1x
path: packages/vyos-1x
- fetch-tags: true # required for Debian package version
- name: Build vyos-1x package
run: |
cd packages/vyos-1x; dpkg-buildpackage -uc -us -tc -b
@@ -51,6 +58,7 @@ jobs:
cli-smoketests:
needs: build
runs-on: ubuntu-24.04
+ timeout-minutes: 180
container:
image: vyos/vyos-build:current
options: --sysctl net.ipv6.conf.lo.disable_ipv6=0 --privileged
@@ -65,11 +73,21 @@ jobs:
name: vyos-${{ needs.build.outputs.build_version }}
path: build
- name: VyOS CLI smoketests
- run: ls -al; ls -al build; sudo make test
+ run: sudo make test
+ - name: Add PR comment
+ if: always()
+ uses: mshick/add-pr-comment@v2
+ with:
+ message-success: '👍 VyOS CLI smoketests finished successfully!'
+ message-failure: '❌ VyOS CLI smoketests failed!'
+ message-cancelled: '❌ VyOS CLI smoketests cancelled!'
+ allow-repeats: false
+ refresh-message-position: true
config-load-tests:
needs: build
runs-on: ubuntu-24.04
+ timeout-minutes: 90
container:
image: vyos/vyos-build:current
options: --sysctl net.ipv6.conf.lo.disable_ipv6=0 --privileged
@@ -85,10 +103,20 @@ jobs:
path: build
- name: VyOS config tests
run: sudo make testc
+ - name: Add PR comment
+ if: always()
+ uses: mshick/add-pr-comment@v2
+ with:
+ message-success: '👍 VyOS config tests finished successfully!'
+ message-failure: '❌ VyOS config tests failed!'
+ message-cancelled: '❌ VyOS config tests cancelled!'
+ allow-repeats: false
+ refresh-message-position: true
raid1-install-test:
needs: build
runs-on: ubuntu-24.04
+ timeout-minutes: 20
container:
image: vyos/vyos-build:current
options: --sysctl net.ipv6.conf.lo.disable_ipv6=0 --privileged
@@ -104,3 +132,12 @@ jobs:
path: build
- name: VyOS RAID1 install test
run: sudo make testraid
+ - name: Add PR comment
+ if: always()
+ uses: mshick/add-pr-comment@v2
+ with:
+ message-success: '👍 RAID1 Smoketests finished successfully!'
+ message-failure: '❌ RAID1 Smoketests failed!'
+ message-cancelled: '❌ RAID1 action cancelled!'
+ allow-repeats: false
+ refresh-message-position: true
diff --git a/.gitignore b/.gitignore
index 507daceee..01333d5b1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -145,6 +145,8 @@ data/component-versions.json
# vyos-1x XML cache
python/vyos/xml_ref/cache.py
python/vyos/xml_ref/pkg_cache/*_cache.py
+# autogenerated vyos-configd JSON definition
+data/configd-include.json
# We do not use pip
Pipfile
diff --git a/Makefile b/Makefile
index 509b47858..06b4aaa6f 100644
--- a/Makefile
+++ b/Makefile
@@ -78,18 +78,7 @@ vyshim:
$(MAKE) -C $(SHIM_DIR)
.PHONY: all
-all: clean interface_definitions op_mode_definitions check test j2lint vyshim check_migration_scripts_executable
-
-.PHONY: check
-.ONESHELL:
-check:
- @echo "Checking which CLI scripts are not enabled to work with vyos-configd..."
- @for file in `ls src/conf_mode -I__pycache__`
- do
- if ! grep -q $$file data/configd-include.json; then
- echo "* $$file"
- fi
- done
+all: clean interface_definitions op_mode_definitions test j2lint vyshim generate-configd-include-json
.PHONY: clean
clean:
@@ -99,7 +88,7 @@ clean:
$(MAKE) -C $(SHIM_DIR) clean
.PHONY: test
-test:
+test: generate-configd-include-json
set -e; python3 -m compileall -q -x '/vmware-tools/scripts/, /ppp/' .
PYTHONPATH=python/ python3 -m "nose" --with-xunit src --with-coverage --cover-erase --cover-xml --cover-package src/conf_mode,src/op_mode,src/completion,src/helpers,src/validators,src/tests --verbose
@@ -127,6 +116,10 @@ unused-imports:
deb:
dpkg-buildpackage -uc -us -tc -b
+.PHONY: generate-configd-include-json
+generate-configd-include-json:
+ @scripts/generate-configd-include-json.py
+
.PHONY: schema
schema:
trang -I rnc -O rng schema/interface_definition.rnc schema/interface_definition.rng
diff --git a/data/config-mode-dependencies/vyos-1x.json b/data/config-mode-dependencies/vyos-1x.json
index 9623948c2..9361f4e7c 100644
--- a/data/config-mode-dependencies/vyos-1x.json
+++ b/data/config-mode-dependencies/vyos-1x.json
@@ -32,7 +32,8 @@
"reverse_proxy": ["load-balancing_reverse-proxy"],
"rpki": ["protocols_rpki"],
"sstp": ["vpn_sstp"],
- "sstpc": ["interfaces_sstpc"]
+ "sstpc": ["interfaces_sstpc"],
+ "stunnel": ["service_stunnel"]
},
"vpn_ipsec": {
"nhrp": ["protocols_nhrp"]
diff --git a/data/configd-include.json b/data/configd-include.json
deleted file mode 100644
index b92d58c72..000000000
--- a/data/configd-include.json
+++ /dev/null
@@ -1,114 +0,0 @@
-[
-"container.py",
-"firewall.py",
-"high-availability.py",
-"interfaces_bonding.py",
-"interfaces_bridge.py",
-"interfaces_dummy.py",
-"interfaces_ethernet.py",
-"interfaces_geneve.py",
-"interfaces_input.py",
-"interfaces_l2tpv3.py",
-"interfaces_loopback.py",
-"interfaces_macsec.py",
-"interfaces_openvpn.py",
-"interfaces_pppoe.py",
-"interfaces_pseudo-ethernet.py",
-"interfaces_sstpc.py",
-"interfaces_tunnel.py",
-"interfaces_virtual-ethernet.py",
-"interfaces_vti.py",
-"interfaces_vxlan.py",
-"interfaces_wireguard.py",
-"interfaces_wireless.py",
-"interfaces_wwan.py",
-"load-balancing_reverse-proxy.py",
-"load-balancing_wan.py",
-"nat.py",
-"nat64.py",
-"nat66.py",
-"netns.py",
-"pki.py",
-"policy.py",
-"policy_route.py",
-"policy_local-route.py",
-"protocols_babel.py",
-"protocols_bfd.py",
-"protocols_bgp.py",
-"protocols_eigrp.py",
-"protocols_failover.py",
-"protocols_igmp-proxy.py",
-"protocols_isis.py",
-"protocols_mpls.py",
-"protocols_nhrp.py",
-"protocols_ospf.py",
-"protocols_ospfv3.py",
-"protocols_pim.py",
-"protocols_pim6.py",
-"protocols_rip.py",
-"protocols_ripng.py",
-"protocols_rpki.py",
-"protocols_segment-routing.py",
-"protocols_static.py",
-"protocols_static_arp.py",
-"protocols_static_multicast.py",
-"protocols_static_neighbor-proxy.py",
-"qos.py",
-"service_aws_glb.py",
-"service_broadcast-relay.py",
-"service_config-sync.py",
-"service_conntrack-sync.py",
-"service_console-server.py",
-"service_dhcp-relay.py",
-"service_dhcp-server.py",
-"service_dhcpv6-relay.py",
-"service_dhcpv6-server.py",
-"service_dns_dynamic.py",
-"service_dns_forwarding.py",
-"service_event-handler.py",
-"service_https.py",
-"service_ids_ddos-protection.py",
-"service_ipoe-server.py",
-"service_lldp.py",
-"service_mdns_repeater.py",
-"service_monitoring_telegraf.py",
-"service_monitoring_zabbix-agent.py",
-"service_ndp-proxy.py",
-"service_ntp.py",
-"service_pppoe-server.py",
-"service_router-advert.py",
-"service_salt-minion.py",
-"service_sla.py",
-"service_snmp.py",
-"service_ssh.py",
-"service_tftp-server.py",
-"service_webproxy.py",
-"system_acceleration.py",
-"system_config-management.py",
-"system_conntrack.py",
-"system_console.py",
-"system_flow-accounting.py",
-"system_frr.py",
-"system_host-name.py",
-"system_ip.py",
-"system_ipv6.py",
-"system_lcd.py",
-"system_login.py",
-"system_login_banner.py",
-"system_logs.py",
-"system_option.py",
-"system_proxy.py",
-"system_sflow.py",
-"system_sysctl.py",
-"system_syslog.py",
-"system_task-scheduler.py",
-"system_timezone.py",
-"system_update-check.py",
-"system_wireless.py",
-"vpn_ipsec.py",
-"vpn_l2tp.py",
-"vpn_openconnect.py",
-"vpn_pptp.py",
-"vpn_sstp.py",
-"vrf.py"
-]
diff --git a/data/templates/firewall/nftables.j2 b/data/templates/firewall/nftables.j2
index ee34f58fc..68a3bfd87 100644
--- a/data/templates/firewall/nftables.j2
+++ b/data/templates/firewall/nftables.j2
@@ -239,7 +239,7 @@ table ip6 vyos_filter {
{% for prior, conf in ipv6.output.items() %}
chain VYOS_IPV6_OUTPUT_{{ prior }} {
type filter hook output priority {{ prior }}; policy accept;
-{% if global_options.state_policy is vyos_defined %}
+{% if global_options.state_policy is vyos_defined and prior == 'filter' %}
jump VYOS_STATE_POLICY6
{% endif %}
{% if conf.rule is vyos_defined %}
diff --git a/data/templates/stunnel/stunnel_config.j2 b/data/templates/stunnel/stunnel_config.j2
new file mode 100644
index 000000000..52c289fa9
--- /dev/null
+++ b/data/templates/stunnel/stunnel_config.j2
@@ -0,0 +1,118 @@
+; Autogenerated by service_stunnel.py
+
+; Example https://www.stunnel.org/config_unix.html#
+; **************************************************************************
+; * Global options *
+; **************************************************************************
+
+; PID file is created inside the chroot jail (if enabled)
+pid = {{ config_file | replace('.conf', '.pid') }}
+
+; Debugging stuff (may be useful for troubleshooting)
+;foreground = yes
+
+{% if log is vyos_defined %}
+debug = {{ log.level }}
+{% endif %}
+
+;output = /usr/local/var/log/stunnel.log
+
+
+; **************************************************************************
+; * Service definitions *
+; **************************************************************************
+
+; ***************************************** Client mode services ***********
+
+{% if client is vyos_defined %}
+{% for name, config in client.items() %}
+[{{ name }}]
+client = yes
+{% if config.listen.address is vyos_defined %}
+accept = {{ config.listen.address }}:{{ config.listen.port }}
+{% else %}
+accept = {{ config.listen.port }}
+{% endif %}
+{% if config.connect is vyos_defined %}
+{% if config.connect.address is vyos_defined %}
+connect = {{ config.connect.address }}:{{ config.connect.port }}
+{% else %}
+connect = {{ config.connect.port }}
+{% endif %}
+{% endif %}
+{% if config.protocol is vyos_defined %}
+protocol = {{ config.protocol }}
+{% endif %}
+{% if config.options is vyos_defined %}
+{% if config.options.authentication is vyos_defined %}
+protocolAuthentication = {{ config.options.authentication }}
+{% endif %}
+{% if config.options.domain is vyos_defined %}
+protocolDomain = {{ config.options.domain }}
+{% endif %}
+{% if config.options.host is vyos_defined %}
+protocolHost = {{ config.options.host.address }}:{{ config.options.host.port }}
+{% endif %}
+{% if config.options.password is vyos_defined %}
+protocolPassword = {{ config.options.password }}
+{% endif %}
+{% if config.options.username is vyos_defined %}
+protocolUsername = {{ config.options.username }}
+{% endif %}
+{% endif %}
+{% if config.ssl.ca_path is vyos_defined %}
+CApath = {{ config.ssl.ca_path }}
+{% endif %}
+{% if config.ssl.ca_file is vyos_defined %}
+CAfile = {{ config.ssl.ca_file }}
+{% endif %}
+{% if config.ssl.cert is vyos_defined %}
+cert = {{ config.ssl.cert }}
+{% endif %}
+{% if config.ssl.cert_key is vyos_defined %}
+key = {{ config.ssl.cert_key }}
+{% endif %}
+{% if config.psk.file is vyos_defined %}
+PSKsecrets = {{ config.psk.file }}
+{% endif %}
+{% endfor %}
+{% endif %}
+
+
+; ***************************************** Server mode services ***********
+
+{% if server is vyos_defined %}
+{% for name, config in server.items() %}
+[{{ name }}]
+{% if config.listen.address is vyos_defined %}
+accept = {{ config.listen.address }}:{{ config.listen.port }}
+{% else %}
+accept = {{ config.listen.port }}
+{% endif %}
+{% if config.connect is vyos_defined %}
+{% if config.connect.address is vyos_defined %}
+connect = {{ config.connect.address }}:{{ config.connect.port }}
+{% else %}
+connect = {{ config.connect.port }}
+{% endif %}
+{% endif %}
+{% if config.protocol is vyos_defined %}
+protocol = {{ config.protocol }}
+{% endif %}
+{% if config.ssl.ca_path is vyos_defined %}
+CApath = {{ config.ssl.ca_path }}
+{% endif %}
+{% if config.ssl.ca_file is vyos_defined %}
+CAfile = {{ config.ssl.ca_file }}
+{% endif %}
+{% if config.ssl.cert is vyos_defined %}
+cert = {{ config.ssl.cert }}
+{% endif %}
+{% if config.ssl.cert_key is vyos_defined %}
+key = {{ config.ssl.cert_key }}
+{% endif %}
+{% if config.psk.file is vyos_defined %}
+PSKsecrets = {{ config.psk.file }}
+{% endif %}
+{% endfor %}
+{% endif %}
diff --git a/debian/control b/debian/control
index 2e99bdc28..883e08649 100644
--- a/debian/control
+++ b/debian/control
@@ -19,12 +19,18 @@ Build-Depends:
python3-xmltodict,
# For running tests
python3-coverage,
+ python3-hurry.filesize,
+ python3-netaddr,
python3-netifaces,
python3-nose,
python3-jinja2,
+ python3-paramiko,
+ python3-passlib,
python3-psutil,
python3-requests,
python3-setuptools,
+ python3-tabulate,
+ python3-zmq,
quilt,
whois
Standards-Version: 3.9.6
diff --git a/interface-definitions/include/stunnel/address.xml.i b/interface-definitions/include/stunnel/address.xml.i
new file mode 100644
index 000000000..d2901d595
--- /dev/null
+++ b/interface-definitions/include/stunnel/address.xml.i
@@ -0,0 +1,20 @@
+<!-- include start from stunnel/address.xml.i -->
+<leafNode name="address">
+ <properties>
+ <help>Hostname or IP address</help>
+ <valueHelp>
+ <format>ipv4</format>
+ <description>IPv4 address</description>
+ </valueHelp>
+ <valueHelp>
+ <format>hostname</format>
+ <description>hostname</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ip-address"/>
+ <validator name="fqdn"/>
+ </constraint>
+ <constraintErrorMessage>Invalid FQDN or IP address</constraintErrorMessage>
+ </properties>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/include/stunnel/connect.xml.i b/interface-definitions/include/stunnel/connect.xml.i
new file mode 100644
index 000000000..cd6246a00
--- /dev/null
+++ b/interface-definitions/include/stunnel/connect.xml.i
@@ -0,0 +1,11 @@
+<!-- include start from stunnel/connect.xml.i -->
+<node name="connect">
+ <properties>
+ <help>Connect to a remote address</help>
+ </properties>
+ <children>
+ #include <include/stunnel/address.xml.i>
+ #include <include/port-number.xml.i>
+ </children>
+</node>
+<!-- include end -->
diff --git a/interface-definitions/include/stunnel/listen.xml.i b/interface-definitions/include/stunnel/listen.xml.i
new file mode 100644
index 000000000..13d0986ee
--- /dev/null
+++ b/interface-definitions/include/stunnel/listen.xml.i
@@ -0,0 +1,11 @@
+<!-- include start from stunnel/listen.xml.i -->
+<node name="listen">
+ <properties>
+ <help>Accept connections on specified address</help>
+ </properties>
+ <children>
+ #include <include/stunnel/address.xml.i>
+ #include <include/port-number.xml.i>
+ </children>
+</node>
+<!-- include end -->
diff --git a/interface-definitions/include/stunnel/protocol-options.xml.i b/interface-definitions/include/stunnel/protocol-options.xml.i
new file mode 100644
index 000000000..2f0202875
--- /dev/null
+++ b/interface-definitions/include/stunnel/protocol-options.xml.i
@@ -0,0 +1,75 @@
+<!-- include start from stunel/protocol-options.xml.i -->
+<node name="options">
+ <properties>
+ <help>Advanced protocol options</help>
+ </properties>
+ <children>
+ <leafNode name="authentication">
+ <properties>
+ <help>Authentication type for the protocol negotiations</help>
+ <completionHelp>
+ <list>basic ntlm plain login</list>
+ </completionHelp>
+ <valueHelp>
+ <format>basic</format>
+ <description>The default 'connect' authentication type</description>
+ </valueHelp>
+ <valueHelp>
+ <format>ntlm</format>
+ <description>Supported authentication types for the 'connect' protocol</description>
+ </valueHelp>
+ <valueHelp>
+ <format>plain</format>
+ <description>The default 'smtp' authentication type</description>
+ </valueHelp>
+ <valueHelp>
+ <format>login</format>
+ <description>Supported authentication types for the 'smtp' protocol</description>
+ </valueHelp>
+ <constraint>
+ <regex>(basic|ntlm|plain|login)</regex>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="domain">
+ <properties>
+ <help>Domain for the 'connect' protocol.</help>
+ <valueHelp>
+ <format>domain</format>
+ <description>domain</description>
+ </valueHelp>
+ <constraint>
+ <validator name="fqdn"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <node name="host">
+ <properties>
+ <help>Destination address for the 'connect' protocol</help>
+ </properties>
+ <children>
+ #include <include/stunnel/address.xml.i>
+ #include <include/port-number.xml.i>
+ </children>
+ </node>
+ <leafNode name="password">
+ <properties>
+ <help>Password for the protocol negotiations</help>
+ <valueHelp>
+ <format>txt</format>
+ <description>Authentication password</description>
+ </valueHelp>
+ </properties>
+ </leafNode>
+ <leafNode name="username">
+ <properties>
+ <help>Username for the protocol negotiations</help>
+ <valueHelp>
+ <format>txt</format>
+ <description>Authentication username</description>
+ </valueHelp>
+ </properties>
+ </leafNode>
+ </children>
+</node>
+<!-- include end -->
diff --git a/interface-definitions/include/stunnel/protocol-value-cifs.xml.i b/interface-definitions/include/stunnel/protocol-value-cifs.xml.i
new file mode 100644
index 000000000..5b9484750
--- /dev/null
+++ b/interface-definitions/include/stunnel/protocol-value-cifs.xml.i
@@ -0,0 +1,6 @@
+<!-- include start from stunnel/protocol-value-cifs.xml.i -->
+<valueHelp>
+ <format>cifs</format>
+ <description>Proprietary (undocummented) extension of CIFS protocol</description>
+</valueHelp>
+<!-- include end -->
diff --git a/interface-definitions/include/stunnel/protocol-value-connect.xml.i b/interface-definitions/include/stunnel/protocol-value-connect.xml.i
new file mode 100644
index 000000000..3c30e71ca
--- /dev/null
+++ b/interface-definitions/include/stunnel/protocol-value-connect.xml.i
@@ -0,0 +1,6 @@
+<!-- include start from stunnel/protocol-value-connect.xml.i -->
+<valueHelp>
+ <format>connect</format>
+ <description>Based on RFC 2817 - Upgrading to TLS Within HTTP/1.1, section 5.2 - Requesting a Tunnel with CONNECT</description>
+</valueHelp>
+<!-- include end -->
diff --git a/interface-definitions/include/stunnel/protocol-value-imap.xml.i b/interface-definitions/include/stunnel/protocol-value-imap.xml.i
new file mode 100644
index 000000000..033e5479b
--- /dev/null
+++ b/interface-definitions/include/stunnel/protocol-value-imap.xml.i
@@ -0,0 +1,6 @@
+<!-- include start from stunnel/protocol-value-imap.xml.i -->
+<valueHelp>
+ <format>imap</format>
+ <description>Based on RFC 2595 - Using TLS with IMAP, POP3 and ACAP</description>
+</valueHelp>
+<!-- include end -->
diff --git a/interface-definitions/include/stunnel/protocol-value-nntp.xml.i b/interface-definitions/include/stunnel/protocol-value-nntp.xml.i
new file mode 100644
index 000000000..60a6c02c6
--- /dev/null
+++ b/interface-definitions/include/stunnel/protocol-value-nntp.xml.i
@@ -0,0 +1,6 @@
+<!-- include start from stunnel/protocol-value-nntp.xml.i -->
+<valueHelp>
+ <format>nntp</format>
+ <description>Based on RFC 4642 - Using Transport Layer Security (TLS) with Network News Transfer Protocol (NNTP)</description>
+</valueHelp>
+<!-- include end -->
diff --git a/interface-definitions/include/stunnel/protocol-value-pgsql.xml.i b/interface-definitions/include/stunnel/protocol-value-pgsql.xml.i
new file mode 100644
index 000000000..fd3a166ec
--- /dev/null
+++ b/interface-definitions/include/stunnel/protocol-value-pgsql.xml.i
@@ -0,0 +1,6 @@
+<!-- include start from stunnel/protocol-value-pgsql.xml.i -->
+<valueHelp>
+ <format>pgsql</format>
+ <description>Based on PostgreSQL frontend/backend protocol</description>
+</valueHelp>
+<!-- include end -->
diff --git a/interface-definitions/include/stunnel/protocol-value-pop3.xml.i b/interface-definitions/include/stunnel/protocol-value-pop3.xml.i
new file mode 100644
index 000000000..1c8af53e5
--- /dev/null
+++ b/interface-definitions/include/stunnel/protocol-value-pop3.xml.i
@@ -0,0 +1,6 @@
+<!-- include start from stunnel/protocol-value-pop3.xml.i -->
+<valueHelp>
+ <format>pop3</format>
+ <description>Based on RFC 2449 - POP3 Extension Mechanism</description>
+</valueHelp>
+<!-- include end -->
diff --git a/interface-definitions/include/stunnel/protocol-value-proxy.xml.i b/interface-definitions/include/stunnel/protocol-value-proxy.xml.i
new file mode 100644
index 000000000..a4c20d1b0
--- /dev/null
+++ b/interface-definitions/include/stunnel/protocol-value-proxy.xml.i
@@ -0,0 +1,6 @@
+<!-- include start from stunnel/protocol-value-proxy.xml.i -->
+<valueHelp>
+ <format>proxy</format>
+ <description>Passing of the original client IP address with HAProxy PROXY protocol version 1</description>
+</valueHelp>
+<!-- include end -->
diff --git a/interface-definitions/include/stunnel/protocol-value-smtp.xml.i b/interface-definitions/include/stunnel/protocol-value-smtp.xml.i
new file mode 100644
index 000000000..66ca20426
--- /dev/null
+++ b/interface-definitions/include/stunnel/protocol-value-smtp.xml.i
@@ -0,0 +1,6 @@
+<!-- include start from stunnel/protocol-value-smtp.xml.i -->
+<valueHelp>
+ <format>smtp</format>
+ <description>Based on RFC 2487 - SMTP Service Extension for Secure SMTP over TLS</description>
+</valueHelp>
+<!-- include end -->
diff --git a/interface-definitions/include/stunnel/protocol-value-socks.xml.i b/interface-definitions/include/stunnel/protocol-value-socks.xml.i
new file mode 100644
index 000000000..e110be5db
--- /dev/null
+++ b/interface-definitions/include/stunnel/protocol-value-socks.xml.i
@@ -0,0 +1,6 @@
+<!-- include start from stunnel/protocol-value-socks.xml.i -->
+<valueHelp>
+ <format>socks</format>
+ <description>SOCKS versions 4, 4a, and 5 are supported</description>
+</valueHelp>
+<!-- include end -->
diff --git a/interface-definitions/include/stunnel/psk.xml.i b/interface-definitions/include/stunnel/psk.xml.i
new file mode 100644
index 000000000..db11a93d3
--- /dev/null
+++ b/interface-definitions/include/stunnel/psk.xml.i
@@ -0,0 +1,30 @@
+<!-- include start from stunnel/psk.xml.i -->
+<tagNode name="psk">
+ <properties>
+ <help>Pre-shared key name</help>
+ </properties>
+ <children>
+ <leafNode name="id">
+ <properties>
+ <help>ID for authentication</help>
+ <valueHelp>
+ <format>txt</format>
+ <description>ID used for authentication</description>
+ </valueHelp>
+ </properties>
+ </leafNode>
+ <leafNode name="secret">
+ <properties>
+ <help>pre-shared secret key</help>
+ <valueHelp>
+ <format>txt</format>
+ <description>pre-shared secret key are required to be at least 16 bytes long, which implies at least 32 characters for hexadecimal key</description>
+ </valueHelp>
+ <constraint>
+ <validator name="psk-secret"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ </children>
+</tagNode>
+<!-- include end -->
diff --git a/interface-definitions/include/stunnel/ssl.xml.i b/interface-definitions/include/stunnel/ssl.xml.i
new file mode 100644
index 000000000..8aba299e9
--- /dev/null
+++ b/interface-definitions/include/stunnel/ssl.xml.i
@@ -0,0 +1,11 @@
+<!-- include start from stunnel/ssl.xml.i -->
+<node name="ssl">
+ <properties>
+ <help>SSL Certificate, SSL Key and CA</help>
+ </properties>
+ <children>
+ #include <include/pki/ca-certificate-multi.xml.i>
+ #include <include/pki/certificate.xml.i>
+ </children>
+</node>
+<!-- include end -->
diff --git a/interface-definitions/service_stunnel.xml.in b/interface-definitions/service_stunnel.xml.in
new file mode 100644
index 000000000..d88909bc9
--- /dev/null
+++ b/interface-definitions/service_stunnel.xml.in
@@ -0,0 +1,130 @@
+<?xml version="1.0"?>
+<interfaceDefinition>
+ <node name="service">
+ <properties>
+ <help>System services</help>
+ </properties>
+ <children>
+ <node name="stunnel" owner="${vyos_conf_scripts_dir}/service_stunnel.py">
+ <properties>
+ <help>Stunnel TLS Proxy</help>
+ <priority>1000</priority>
+ </properties>
+ <children>
+ <tagNode name="server">
+ <properties>
+ <help>Stunnel server config</help>
+ </properties>
+ <children>
+ #include <include/stunnel/connect.xml.i>
+ #include <include/stunnel/listen.xml.i>
+ #include <include/stunnel/ssl.xml.i>
+ #include <include/stunnel/psk.xml.i>
+ <leafNode name="protocol">
+ <properties>
+ <help>Application protocol to negotiate TLS</help>
+ <completionHelp>
+ <list>cifs imap pgsql pop3 proxy smtp socks</list>
+ </completionHelp>
+ #include <include/stunnel/protocol-value-cifs.xml.i>
+ #include <include/stunnel/protocol-value-imap.xml.i>
+ #include <include/stunnel/protocol-value-pgsql.xml.i>
+ #include <include/stunnel/protocol-value-pop3.xml.i>
+ #include <include/stunnel/protocol-value-proxy.xml.i>
+ #include <include/stunnel/protocol-value-smtp.xml.i>
+ #include <include/stunnel/protocol-value-socks.xml.i>
+ <constraint>
+ <regex>(cifs|imap|pgsql|pop3|proxy|smtp|socks)</regex>
+ </constraint>
+ </properties>
+ </leafNode>
+ </children>
+ </tagNode>
+ <tagNode name="client">
+ <properties>
+ <help>Stunnel client config</help>
+ </properties>
+ <children>
+ #include <include/stunnel/connect.xml.i>
+ #include <include/stunnel/listen.xml.i>
+ #include <include/stunnel/ssl.xml.i>
+ #include <include/stunnel/psk.xml.i>
+ <leafNode name="protocol">
+ <properties>
+ <help>Application protocol to negotiate TLS</help>
+ <completionHelp>
+ <list>cifs connect imap nntp pgsql pop3 proxy smtp socks</list>
+ </completionHelp>
+ #include <include/stunnel/protocol-value-cifs.xml.i>
+ #include <include/stunnel/protocol-value-connect.xml.i>
+ #include <include/stunnel/protocol-value-imap.xml.i>
+ #include <include/stunnel/protocol-value-nntp.xml.i>
+ #include <include/stunnel/protocol-value-pgsql.xml.i>
+ #include <include/stunnel/protocol-value-pop3.xml.i>
+ #include <include/stunnel/protocol-value-proxy.xml.i>
+ #include <include/stunnel/protocol-value-smtp.xml.i>
+ #include <include/stunnel/protocol-value-socks.xml.i>
+ <constraint>
+ <regex>(cifs|connect|imap|nntp|pgsql|pop3|proxy|smtp|socks)</regex>
+ </constraint>
+ </properties>
+ </leafNode>
+ #include <include/stunnel/protocol-options.xml.i>
+ </children>
+ </tagNode>
+ <node name="log">
+ <properties>
+ <help>Service logging</help>
+ </properties>
+ <children>
+ <leafNode name="level">
+ <properties>
+ <help>Specifies log level.</help>
+ <completionHelp>
+ <list>emerg alert crit err warning notice info debug</list>
+ </completionHelp>
+ <valueHelp>
+ <format>emerg</format>
+ <description>Emerg log level</description>
+ </valueHelp>
+ <valueHelp>
+ <format>alert</format>
+ <description>Alert log level</description>
+ </valueHelp>
+ <valueHelp>
+ <format>crit</format>
+ <description>Critical log level</description>
+ </valueHelp>
+ <valueHelp>
+ <format>err</format>
+ <description>Error log level</description>
+ </valueHelp>
+ <valueHelp>
+ <format>warning</format>
+ <description>Warning log level</description>
+ </valueHelp>
+ <valueHelp>
+ <format>notice</format>
+ <description>Notice log level</description>
+ </valueHelp>
+ <valueHelp>
+ <format>info</format>
+ <description>Info log level</description>
+ </valueHelp>
+ <valueHelp>
+ <format>debug</format>
+ <description>Debug log level</description>
+ </valueHelp>
+ <constraint>
+ <regex>(emerg|alert|crit|err|warning|notice|info|debug)</regex>
+ </constraint>
+ </properties>
+ <defaultValue>notice</defaultValue>
+ </leafNode>
+ </children>
+ </node>
+ </children>
+ </node>
+ </children>
+ </node>
+</interfaceDefinition>
diff --git a/op-mode-definitions/show-kernel-modules.xml.in b/op-mode-definitions/show-kernel-modules.xml.in
new file mode 100644
index 000000000..28eb28212
--- /dev/null
+++ b/op-mode-definitions/show-kernel-modules.xml.in
@@ -0,0 +1,20 @@
+<?xml version="1.0"?>
+<interfaceDefinition>
+ <node name="show">
+ <children>
+ <node name="kernel">
+ <properties>
+ <help>Show kernel information</help>
+ </properties>
+ <children>
+ <node name="modules">
+ <properties>
+ <help>Show kernel modules</help>
+ </properties>
+ <command>sudo ${vyos_op_scripts_dir}/kernel_modules.py show</command>
+ </node>
+ </children>
+ </node>
+ </children>
+ </node>
+</interfaceDefinition>
diff --git a/python/vyos/base.py b/python/vyos/base.py
index 054b1d837..ca96d96ce 100644
--- a/python/vyos/base.py
+++ b/python/vyos/base.py
@@ -63,3 +63,10 @@ class ConfigError(Exception):
message = fill(message, width=72)
# Call the base class constructor with the parameters it needs
super().__init__(message)
+
+class MigrationError(Exception):
+ def __init__(self, message):
+ # Reformat the message and trim it to 72 characters in length
+ message = fill(message, width=72)
+ # Call the base class constructor with the parameters it needs
+ super().__init__(message)
diff --git a/python/vyos/component_version.py b/python/vyos/component_version.py
index 9662ebfcf..0c305e5e0 100644
--- a/python/vyos/component_version.py
+++ b/python/vyos/component_version.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
@@ -35,133 +35,173 @@ VyOS 1.2:
import os
import re
import sys
-import fileinput
+from dataclasses import dataclass
+from dataclasses import replace
+from typing import Optional
from vyos.xml_ref import component_version
+from vyos.utils.file import write_file
from vyos.version import get_version
from vyos.defaults import directories
DEFAULT_CONFIG_PATH = os.path.join(directories['config'], 'config.boot')
-def from_string(string_line, vintage='vyos'):
- """
- Get component version dictionary from string.
- Return empty dictionary if string contains no config information
- or raise error if component version string malformed.
- """
- version_dict = {}
-
- if vintage == 'vyos':
- if re.match(r'// vyos-config-version:.+', string_line):
- if not re.match(r'// vyos-config-version:\s+"([\w,-]+@\d+:)+([\w,-]+@\d+)"\s*', string_line):
- raise ValueError(f"malformed configuration string: {string_line}")
+REGEX_WARN_VYOS = r'(// Warning: Do not remove the following line.)'
+REGEX_WARN_VYATTA = r'(/\* Warning: Do not remove the following line. \*/)'
+REGEX_COMPONENT_VERSION_VYOS = r'// vyos-config-version:\s+"([\w@:-]+)"\s*'
+REGEX_COMPONENT_VERSION_VYATTA = r'/\* === vyatta-config-version:\s+"([\w@:-]+)"\s+=== \*/'
+REGEX_RELEASE_VERSION_VYOS = r'// Release version:\s+(\S*)\s*'
+REGEX_RELEASE_VERSION_VYATTA = r'/\* Release version:\s+(\S*)\s*\*/'
- for pair in re.findall(r'([\w,-]+)@(\d+)', string_line):
- version_dict[pair[0]] = int(pair[1])
-
- elif vintage == 'vyatta':
- if re.match(r'/\* === vyatta-config-version:.+=== \*/$', string_line):
- if not re.match(r'/\* === vyatta-config-version:\s+"([\w,-]+@\d+:)+([\w,-]+@\d+)"\s+=== \*/$', string_line):
- raise ValueError(f"malformed configuration string: {string_line}")
+CONFIG_FILE_VERSION = """\
+// Warning: Do not remove the following line.
+// vyos-config-version: "{}"
+// Release version: {}
+"""
- for pair in re.findall(r'([\w,-]+)@(\d+)', string_line):
- version_dict[pair[0]] = int(pair[1])
+warn_filter_vyos = re.compile(REGEX_WARN_VYOS)
+warn_filter_vyatta = re.compile(REGEX_WARN_VYATTA)
+
+regex_filter = { 'vyos': dict(zip(['component', 'release'],
+ [re.compile(REGEX_COMPONENT_VERSION_VYOS),
+ re.compile(REGEX_RELEASE_VERSION_VYOS)])),
+ 'vyatta': dict(zip(['component', 'release'],
+ [re.compile(REGEX_COMPONENT_VERSION_VYATTA),
+ re.compile(REGEX_RELEASE_VERSION_VYATTA)])) }
+
+@dataclass
+class VersionInfo:
+ component: Optional[dict[str,int]] = None
+ release: str = get_version()
+ vintage: str = 'vyos'
+ config_body: Optional[str] = None
+ footer_lines: Optional[list[str]] = None
+
+ def component_is_none(self) -> bool:
+ return bool(self.component is None)
+
+ def config_body_is_none(self) -> bool:
+ return bool(self.config_body is None)
+
+ def update_footer(self):
+ f = CONFIG_FILE_VERSION.format(component_to_string(self.component),
+ self.release)
+ self.footer_lines = f.splitlines()
+
+ def update_syntax(self):
+ self.vintage = 'vyos'
+ self.update_footer()
+
+ def update_release(self, release: str):
+ self.release = release
+ self.update_footer()
+
+ def update_component(self, key: str, version: int):
+ if not isinstance(version, int):
+ raise ValueError('version must be int')
+ if self.component is None:
+ self.component = {}
+ self.component[key] = version
+ self.component = dict(sorted(self.component.items(), key=lambda x: x[0]))
+ self.update_footer()
+
+ def update_config_body(self, config_str: str):
+ self.config_body = config_str
+
+ def write_string(self) -> str:
+ config_body = '' if self.config_body is None else self.config_body
+ footer_lines = [] if self.footer_lines is None else self.footer_lines
+
+ return config_body + '\n' + '\n'.join(footer_lines) + '\n'
+
+ def write(self, config_file):
+ string = self.write_string()
+ try:
+ write_file(config_file, string)
+ except Exception as e:
+ raise ValueError(e) from e
+
+def component_to_string(component: dict) -> str:
+ l = [f'{k}@{v}' for k, v in sorted(component.items(), key=lambda x: x[0])]
+ return ':'.join(l)
+
+def component_from_string(string: str) -> dict:
+ return {k: int(v) for k, v in re.findall(r'([\w,-]+)@(\d+)', string)}
+
+def version_info_from_file(config_file) -> VersionInfo:
+ version_info = VersionInfo()
+ try:
+ with open(config_file) as f:
+ config_str = f.read()
+ except OSError:
+ return None
+
+ if len(parts := warn_filter_vyos.split(config_str)) > 1:
+ vintage = 'vyos'
+ elif len(parts := warn_filter_vyatta.split(config_str)) > 1:
+ vintage = 'vyatta'
else:
- raise ValueError("Unknown config string vintage")
+ version_info.config_body = parts[0] if parts else None
+ return version_info
- return version_dict
+ version_info.vintage = vintage
+ version_info.config_body = parts[0]
+ version_lines = ''.join(parts[1:]).splitlines()
+ version_lines = [k for k in version_lines if k]
+ if len(version_lines) != 3:
+ raise ValueError(f'Malformed version strings: {version_lines}')
-def from_file(config_file_name=DEFAULT_CONFIG_PATH, vintage='vyos'):
- """
- Get component version dictionary parsing config file line by line
- """
- with open(config_file_name, 'r') as f:
- for line_in_config in f:
- version_dict = from_string(line_in_config, vintage=vintage)
- if version_dict:
- return version_dict
+ m = regex_filter[vintage]['component'].match(version_lines[1])
+ if not m:
+ raise ValueError(f'Malformed component string: {version_lines[1]}')
+ version_info.component = component_from_string(m.group(1))
- # no version information
- return {}
+ m = regex_filter[vintage]['release'].match(version_lines[2])
+ if not m:
+ raise ValueError(f'Malformed component string: {version_lines[2]}')
+ version_info.release = m.group(1)
-def from_system():
- """
- Get system component version dict.
- """
- return component_version()
+ version_info.footer_lines = version_lines
-def format_string(ver: dict) -> str:
- """
- Version dict to string.
- """
- keys = list(ver)
- keys.sort()
- l = []
- for k in keys:
- v = ver[k]
- l.append(f'{k}@{v}')
- sep = ':'
- return sep.join(l)
-
-def version_footer(ver: dict, vintage='vyos') -> str:
- """
- Version footer as string.
- """
- ver_str = format_string(ver)
- release = get_version()
- if vintage == 'vyos':
- ret_str = (f'// Warning: Do not remove the following line.\n'
- + f'// vyos-config-version: "{ver_str}"\n'
- + f'// Release version: {release}\n')
- elif vintage == 'vyatta':
- ret_str = (f'/* Warning: Do not remove the following line. */\n'
- + f'/* === vyatta-config-version: "{ver_str}" === */\n'
- + f'/* Release version: {release} */\n')
- else:
- raise ValueError("Unknown config string vintage")
+ return version_info
- return ret_str
-
-def system_footer(vintage='vyos') -> str:
+def version_info_from_system() -> VersionInfo:
"""
- System version footer as string.
+ Return system component versions.
"""
- ver_d = from_system()
- return version_footer(ver_d, vintage=vintage)
+ d = component_version()
+ sort_d = dict(sorted(d.items(), key=lambda x: x[0]))
+ version_info = VersionInfo(
+ component = sort_d,
+ release = get_version(),
+ vintage = 'vyos'
+ )
+
+ return version_info
-def write_version_footer(ver: dict, file_name, vintage='vyos'):
+def version_info_copy(v: VersionInfo) -> VersionInfo:
"""
- Write version footer to file.
+ Make a copy of dataclass.
"""
- footer = version_footer(ver=ver, vintage=vintage)
- if file_name:
- with open(file_name, 'a') as f:
- f.write(footer)
- else:
- sys.stdout.write(footer)
+ return replace(v)
-def write_system_footer(file_name, vintage='vyos'):
+def version_info_prune_component(x: VersionInfo, y: VersionInfo) -> VersionInfo:
"""
- Write system version footer to file.
+ In place pruning of component keys of x not in y.
"""
- ver_d = from_system()
- return write_version_footer(ver_d, file_name=file_name, vintage=vintage)
+ x.component = { k: v for k,v in x.component.items() if k in y.component }
-def remove_footer(file_name):
+def add_system_version(config_str: str = None, out_file: str = None):
"""
- Remove old version footer.
+ Wrap config string with system version and write to out_file.
+ For convenience, calling with no argument will write system version
+ string to stdout, for use in bash scripts.
"""
- for line in fileinput.input(file_name, inplace=True):
- if re.match(r'/\* Warning:.+ \*/$', line):
- continue
- if re.match(r'/\* === vyatta-config-version:.+=== \*/$', line):
- continue
- if re.match(r'/\* Release version:.+ \*/$', line):
- continue
- if re.match('// vyos-config-version:.+', line):
- continue
- if re.match('// Warning:.+', line):
- continue
- if re.match('// Release version:.+', line):
- continue
- sys.stdout.write(line)
+ version_info = version_info_from_system()
+ if config_str is not None:
+ version_info.update_config_body(config_str)
+ version_info.update_footer()
+ if out_file is not None:
+ version_info.write(out_file)
+ else:
+ sys.stdout.write(version_info.write_string())
diff --git a/python/vyos/compose_config.py b/python/vyos/compose_config.py
index efa28babe..79a8718c5 100644
--- a/python/vyos/compose_config.py
+++ b/python/vyos/compose_config.py
@@ -17,12 +17,13 @@
config.
"""
+import traceback
from pathlib import Path
from typing import TypeAlias, Union, Callable
from vyos.configtree import ConfigTree
from vyos.configtree import deep_copy as ct_deep_copy
-from vyos.utils.system import load_as_module
+from vyos.utils.system import load_as_module_source
ConfigObj: TypeAlias = Union[str, ConfigTree]
@@ -54,7 +55,8 @@ class ComposeConfig:
try:
func(self.config_tree)
except Exception as e:
- self.config_tree = self.checkpoint
+ if self.checkpoint_file is not None:
+ self.config_tree = self.checkpoint
raise ComposeConfigError(e) from e
def apply_file(self, func_file: str, func_name: str):
@@ -62,7 +64,7 @@ class ComposeConfig:
"""
try:
mod_name = Path(func_file).stem.replace('-', '_')
- mod = load_as_module(mod_name, func_file)
+ mod = load_as_module_source(mod_name, func_file)
func = getattr(mod, func_name)
except Exception as e:
raise ComposeConfigError(f'Error with {func_file}: {e}') from e
@@ -70,7 +72,9 @@ class ComposeConfig:
try:
self.apply_func(func)
except ComposeConfigError as e:
- raise ComposeConfigError(f'Error in {func_file}: {e}') from e
+ msg = str(e)
+ tb = f'{traceback.format_exc()}'
+ raise ComposeConfigError(f'Error in {func_file}: {msg}\n{tb}') from e
def to_string(self, with_version=False) -> str:
"""Return the rendered config tree.
diff --git a/python/vyos/configtree.py b/python/vyos/configtree.py
index afd6e030b..5775070e2 100644
--- a/python/vyos/configtree.py
+++ b/python/vyos/configtree.py
@@ -15,6 +15,7 @@
import os
import re
import json
+import logging
from ctypes import cdll, c_char_p, c_void_p, c_int, c_bool
@@ -161,6 +162,8 @@ class ConfigTree(object):
self.__version = ''
self.__migration = os.environ.get('VYOS_MIGRATION')
+ if self.__migration:
+ self.migration_log = logging.getLogger('vyos.migrate')
def __del__(self):
if self.__config is not None:
@@ -215,7 +218,7 @@ class ConfigTree(object):
self.__set_add_value(self.__config, path_str, str(value).encode())
if self.__migration:
- print(f"- op: set path: {path} value: {value} replace: {replace}")
+ self.migration_log.info(f"- op: set path: {path} value: {value} replace: {replace}")
def delete(self, path):
check_path(path)
@@ -226,7 +229,7 @@ class ConfigTree(object):
raise ConfigTreeError(f"Path doesn't exist: {path}")
if self.__migration:
- print(f"- op: delete path: {path}")
+ self.migration_log.info(f"- op: delete path: {path}")
def delete_value(self, path, value):
check_path(path)
@@ -242,7 +245,7 @@ class ConfigTree(object):
raise ConfigTreeError()
if self.__migration:
- print(f"- op: delete_value path: {path} value: {value}")
+ self.migration_log.info(f"- op: delete_value path: {path} value: {value}")
def rename(self, path, new_name):
check_path(path)
@@ -258,7 +261,7 @@ class ConfigTree(object):
raise ConfigTreeError("Path [{}] doesn't exist".format(path))
if self.__migration:
- print(f"- op: rename old_path: {path} new_path: {new_path}")
+ self.migration_log.info(f"- op: rename old_path: {path} new_path: {new_path}")
def copy(self, old_path, new_path):
check_path(old_path)
@@ -275,7 +278,7 @@ class ConfigTree(object):
raise ConfigTreeError(msg)
if self.__migration:
- print(f"- op: copy old_path: {old_path} new_path: {new_path}")
+ self.migration_log.info(f"- op: copy old_path: {old_path} new_path: {new_path}")
def exists(self, path):
check_path(path)
diff --git a/python/vyos/ethtool.py b/python/vyos/ethtool.py
index d45c9c272..80bb56fa2 100644
--- a/python/vyos/ethtool.py
+++ b/python/vyos/ethtool.py
@@ -16,6 +16,7 @@
import re
from json import loads
+from vyos.utils.network import interface_exists
from vyos.utils.process import popen
# These drivers do not support using ethtool to change the speed, duplex, or
@@ -64,6 +65,9 @@ class Ethtool:
def __init__(self, ifname):
# Get driver used for interface
+ if not interface_exists(ifname):
+ raise ValueError(f'Interface "{ifname}" does not exist!')
+
out, _ = popen(f'ethtool --driver {ifname}')
driver = re.search(r'driver:\s(\w+)', out)
if driver:
diff --git a/python/vyos/load_config.py b/python/vyos/load_config.py
index af563614d..b910a2f92 100644
--- a/python/vyos/load_config.py
+++ b/python/vyos/load_config.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
@@ -28,9 +28,7 @@ from typing import Union, Literal, TypeAlias, get_type_hints, get_args
from vyos.config import Config
from vyos.configtree import ConfigTree, DiffTree
from vyos.configsource import ConfigSourceSession, VyOSError
-from vyos.component_version import from_string as version_from_string
-from vyos.component_version import from_system as version_from_system
-from vyos.migrator import Migrator, VirtualMigrator, MigratorError
+from vyos.migrate import ConfigMigrate, ConfigMigrateError
from vyos.utils.process import popen, DEVNULL
Variety: TypeAlias = Literal['explicit', 'batch', 'tree', 'legacy']
@@ -51,20 +49,6 @@ def get_proposed_config(config_file: str = None) -> ConfigTree:
config_str = Path(config_file).read_text()
return ConfigTree(config_str)
-def migration_needed(config_obj: ConfigObj) -> bool:
- """Check if a migration is needed for the config object.
- """
- if not isinstance(config_obj, ConfigTree):
- atree = get_proposed_config(config_obj)
- else:
- atree = config_obj
- version_str = atree.get_version_string()
- if not version_str:
- return True
- aversion = version_from_string(version_str.splitlines()[1])
- bversion = version_from_system()
- return aversion != bversion
-
def check_session(strict: bool, switch: Variety) -> None:
"""Check if we are in a config session, with no uncommitted changes, if
strict. This is not needed for legacy load, as these checks are
@@ -128,12 +112,10 @@ def migrate(config_obj: ConfigObj) -> ConfigObj:
else:
config_file = config_obj
- virtual_migration = VirtualMigrator(config_file)
- migration = Migrator(config_file)
+ config_migrate = ConfigMigrate(config_file)
try:
- virtual_migration.run()
- migration.run()
- except MigratorError as e:
+ config_migrate.run()
+ except ConfigMigrateError as e:
raise LoadConfigError(e) from e
else:
if isinstance(config_obj, ConfigTree):
@@ -193,8 +175,7 @@ def load(config_obj: ConfigObj, strict: bool = True,
check_session(strict, switch)
- if migration_needed(config_obj):
- config_obj = migrate(config_obj)
+ config_obj = migrate(config_obj)
func = getattr(thismod, f'load_{switch}')
func(config_obj)
diff --git a/python/vyos/migrate.py b/python/vyos/migrate.py
new file mode 100644
index 000000000..9d1613676
--- /dev/null
+++ b/python/vyos/migrate.py
@@ -0,0 +1,283 @@
+# 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
+# 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, see <http://www.gnu.org/licenses/>.
+
+import os
+import re
+import json
+import logging
+from pathlib import Path
+from grp import getgrnam
+
+from vyos.component_version import VersionInfo
+from vyos.component_version import version_info_from_system
+from vyos.component_version import version_info_from_file
+from vyos.component_version import version_info_copy
+from vyos.component_version import version_info_prune_component
+from vyos.compose_config import ComposeConfig
+from vyos.compose_config import ComposeConfigError
+from vyos.configtree import ConfigTree
+from vyos.defaults import directories as default_dir
+from vyos.defaults import component_version_json
+
+
+log_file = Path(default_dir['config']).joinpath('vyos-migrate.log')
+
+class ConfigMigrateError(Exception):
+ """Raised on error in config migration."""
+
+class ConfigMigrate:
+ # pylint: disable=too-many-instance-attributes
+ # the number is reasonable in this case
+ def __init__(self, config_file: str, force=False,
+ output_file: str = None, checkpoint_file: str = None):
+ self.config_file: str = config_file
+ self.force: bool = force
+ self.system_version: VersionInfo = version_info_from_system()
+ self.file_version: VersionInfo = version_info_from_file(self.config_file)
+ self.compose = None
+ self.output_file = output_file
+ self.checkpoint_file = checkpoint_file
+ self.logger = None
+ self.config_modified = True
+
+ if self.file_version is None:
+ raise ConfigMigrateError(f'failed to read config file {self.config_file}')
+
+ def migration_needed(self) -> bool:
+ return self.system_version.component != self.file_version.component
+
+ def release_update_needed(self) -> bool:
+ return self.system_version.release != self.file_version.release
+
+ def syntax_update_needed(self) -> bool:
+ return self.system_version.vintage != self.file_version.vintage
+
+ def update_release(self):
+ """
+ Update config file release version.
+ """
+ self.file_version.update_release(self.system_version.release)
+
+ def update_syntax(self):
+ """
+ Update config file syntax.
+ """
+ self.file_version.update_syntax()
+
+ @staticmethod
+ def normalize_config_body(version_info: VersionInfo):
+ """
+ This is an interim workaround for the cosmetic issue of node
+ ordering when composing operations on the internal config_tree:
+ ordering is performed on parsing, hence was maintained in the old
+ system which would parse/write on each application of a migration
+ script (~200). Here, we will take the cost of one extra parsing to
+ reorder before save, for easier review.
+ """
+ if not version_info.config_body_is_none():
+ ct = ConfigTree(version_info.config_body)
+ version_info.update_config_body(ct.to_string())
+
+ def write_config(self):
+ if self.output_file is not None:
+ config_file = self.output_file
+ else:
+ config_file = self.config_file
+
+ try:
+ self.file_version.write(config_file)
+ except ValueError as e:
+ raise ConfigMigrateError(f'failed to write {config_file}: {e}') from e
+
+ def init_logger(self):
+ self.logger = logging.getLogger(__name__)
+ self.logger.setLevel(logging.DEBUG)
+
+ fh = ConfigMigrate.group_perm_file_handler(log_file,
+ group='vyattacfg',
+ mode='w')
+ fh.setLevel(logging.INFO)
+ fh_formatter = logging.Formatter('%(message)s')
+ fh.setFormatter(fh_formatter)
+ self.logger.addHandler(fh)
+ ch = logging.StreamHandler()
+ ch.setLevel(logging.WARNING)
+ ch_formatter = logging.Formatter('%(name)s - %(levelname)s - %(message)s')
+ ch.setFormatter(ch_formatter)
+ self.logger.addHandler(ch)
+
+ @staticmethod
+ def group_perm_file_handler(filename, group=None, mode='a'):
+ # pylint: disable=consider-using-with
+ if group is None:
+ return logging.FileHandler(filename, mode)
+ gid = getgrnam(group).gr_gid
+ if not os.path.exists(filename):
+ open(filename, 'a').close()
+ os.chown(filename, -1, gid)
+ os.chmod(filename, 0o664)
+ return logging.FileHandler(filename, mode)
+
+ @staticmethod
+ def sort_function():
+ """
+ Define sort function for migration files as tuples (n, m) for file
+ n-to-m.
+ """
+ numbers = re.compile(r'(\d+)')
+ def func(p: Path):
+ parts = numbers.split(p.stem)
+ return list(map(int, parts[1::2]))
+ return func
+
+ @staticmethod
+ def file_ext(file_path: Path) -> str:
+ """
+ Return an identifier from file name for checkpoint file extension.
+ """
+ return f'{file_path.parent.stem}_{file_path.stem}'
+
+ def run_migration_scripts(self):
+ """
+ Call migration files iteratively.
+ """
+ os.environ['VYOS_MIGRATION'] = '1'
+
+ self.init_logger()
+ self.logger.info("List of applied migration modules:")
+
+ components = list(self.system_version.component)
+ components.sort()
+
+ # T4382: 'bgp' needs to follow 'quagga':
+ if 'bgp' in components and 'quagga' in components:
+ components.insert(components.index('quagga'),
+ components.pop(components.index('bgp')))
+
+ revision: VersionInfo = version_info_copy(self.file_version)
+ # prune retired, for example, zone-policy
+ version_info_prune_component(revision, self.system_version)
+
+ migrate_dir = Path(default_dir['migrate'])
+ sort_func = ConfigMigrate.sort_function()
+
+ for key in components:
+ p = migrate_dir.joinpath(key)
+ script_list = list(p.glob('*-to-*'))
+ script_list = sorted(script_list, key=sort_func)
+
+ if not self.file_version.component_is_none() and not self.force:
+ start = self.file_version.component.get(key, 0)
+ script_list = list(filter(lambda x, st=start: sort_func(x)[0] >= st,
+ script_list))
+
+ if not script_list: # no applicable migration scripts
+ revision.update_component(key, self.system_version.component[key])
+ continue
+
+ for file in script_list:
+ f = file.as_posix()
+ self.logger.info(f'applying {f}')
+ try:
+ self.compose.apply_file(f, func_name='migrate')
+ except ComposeConfigError as e:
+ self.logger.error(e)
+ if self.checkpoint_file:
+ check = f'{self.checkpoint_file}_{ConfigMigrate.file_ext(file)}'
+ revision.update_config_body(self.compose.to_string())
+ ConfigMigrate.normalize_config_body(revision)
+ revision.write(check)
+ break
+ else:
+ revision.update_component(key, sort_func(file)[1])
+
+ revision.update_config_body(self.compose.to_string())
+ ConfigMigrate.normalize_config_body(revision)
+ self.file_version = version_info_copy(revision)
+
+ if revision.component != self.system_version.component:
+ raise ConfigMigrateError(f'incomplete migration: check {log_file} for error')
+
+ del os.environ['VYOS_MIGRATION']
+
+ def save_json_record(self):
+ """
+ Write component versions to a json file
+ """
+ version_file = component_version_json
+
+ try:
+ with open(version_file, 'w') as f:
+ f.write(json.dumps(self.system_version.component,
+ indent=2, sort_keys=True))
+ except OSError:
+ pass
+
+ def load_config(self):
+ """
+ Instantiate a ComposeConfig object with the config string.
+ """
+
+ self.compose = ComposeConfig(self.file_version.config_body, self.checkpoint_file)
+
+ def run(self):
+ """
+ If migration needed, run migration scripts and update config file.
+ If only release version update needed, update release version.
+ """
+ # save system component versions in json file for reference
+ self.save_json_record()
+
+ if not self.migration_needed():
+ if self.release_update_needed():
+ self.update_release()
+ self.write_config()
+ else:
+ self.config_modified = False
+ return
+
+ if self.syntax_update_needed():
+ self.update_syntax()
+ self.write_config()
+
+ self.load_config()
+
+ self.run_migration_scripts()
+
+ self.update_release()
+ self.write_config()
+
+ def run_script(self, test_script: str):
+ """
+ Run a single migration script. For testing this simply provides the
+ body for loading and writing the result; the component string is not
+ updated.
+ """
+
+ self.load_config()
+ self.init_logger()
+
+ os.environ['VYOS_MIGRATION'] = '1'
+
+ try:
+ self.compose.apply_file(test_script, func_name='migrate')
+ except ComposeConfigError as e:
+ self.logger.error(f'config-migration error in {test_script}: {e}')
+ else:
+ self.file_version.update_config_body(self.compose.to_string())
+
+ del os.environ['VYOS_MIGRATION']
+
+ self.write_config()
diff --git a/python/vyos/migrator.py b/python/vyos/migrator.py
deleted file mode 100644
index 872682bc0..000000000
--- a/python/vyos/migrator.py
+++ /dev/null
@@ -1,226 +0,0 @@
-# Copyright 2019-2022 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
-# 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, see <http://www.gnu.org/licenses/>.
-
-import sys
-import os
-import json
-import logging
-
-import vyos.defaults
-import vyos.component_version as component_version
-from vyos.utils.process import cmd
-
-log_file = os.path.join(vyos.defaults.directories['config'], 'vyos-migrate.log')
-
-class MigratorError(Exception):
- pass
-
-class Migrator(object):
- def __init__(self, config_file, force=False, set_vintage='vyos'):
- self._config_file = config_file
- self._force = force
- self._set_vintage = set_vintage
- self._config_file_vintage = None
- self._changed = False
-
- def init_logger(self):
- self.logger = logging.getLogger(__name__)
- self.logger.setLevel(logging.DEBUG)
-
- # on adding the file handler, allow write permission for cfg_group;
- # restore original umask on exit
- mask = os.umask(0o113)
- fh = logging.FileHandler(log_file)
- formatter = logging.Formatter('%(message)s')
- fh.setFormatter(formatter)
- self.logger.addHandler(fh)
- os.umask(mask)
-
- def read_config_file_versions(self):
- """
- Get component versions from config file footer and set vintage;
- return empty dictionary if config string is missing.
- """
- cfg_file = self._config_file
- component_versions = {}
-
- cfg_versions = component_version.from_file(cfg_file, vintage='vyatta')
-
- if cfg_versions:
- self._config_file_vintage = 'vyatta'
- component_versions = cfg_versions
-
- cfg_versions = component_version.from_file(cfg_file, vintage='vyos')
-
- if cfg_versions:
- self._config_file_vintage = 'vyos'
- component_versions = cfg_versions
-
- return component_versions
-
- def update_vintage(self):
- old_vintage = self._config_file_vintage
-
- if self._set_vintage:
- self._config_file_vintage = self._set_vintage
-
- if self._config_file_vintage not in ['vyatta', 'vyos']:
- raise MigratorError("Unknown vintage.")
-
- if self._config_file_vintage == old_vintage:
- return False
- else:
- return True
-
- def run_migration_scripts(self, config_file_versions, system_versions):
- """
- Run migration scripts iteratively, until config file version equals
- system component version.
- """
- os.environ['VYOS_MIGRATION'] = '1'
- self.init_logger()
-
- self.logger.info("List of executed migration scripts:")
-
- cfg_versions = config_file_versions
- sys_versions = system_versions
-
- sys_keys = list(sys_versions.keys())
- sys_keys.sort()
-
- # XXX 'bgp' needs to follow 'quagga':
- if 'bgp' in sys_keys and 'quagga' in sys_keys:
- sys_keys.insert(sys_keys.index('quagga'),
- sys_keys.pop(sys_keys.index('bgp')))
-
- rev_versions = {}
-
- for key in sys_keys:
- sys_ver = sys_versions[key]
- if key in cfg_versions:
- cfg_ver = cfg_versions[key]
- else:
- cfg_ver = 0
-
- migrate_script_dir = os.path.join(
- vyos.defaults.directories['migrate'], key)
-
- while cfg_ver < sys_ver:
- next_ver = cfg_ver + 1
-
- migrate_script = os.path.join(migrate_script_dir,
- '{}-to-{}'.format(cfg_ver, next_ver))
-
- try:
- out = cmd([migrate_script, self._config_file])
- self.logger.info(f'{migrate_script}')
- if out: self.logger.info(out)
- except FileNotFoundError:
- pass
- except Exception as err:
- print("\nMigration script error: {0}: {1}."
- "".format(migrate_script, err))
- sys.exit(1)
-
- cfg_ver = next_ver
- rev_versions[key] = cfg_ver
-
- del os.environ['VYOS_MIGRATION']
- return rev_versions
-
- def write_config_file_versions(self, cfg_versions):
- """
- Write new versions string.
- """
- if self._config_file_vintage == 'vyatta':
- component_version.write_version_footer(cfg_versions,
- self._config_file,
- vintage='vyatta')
-
- if self._config_file_vintage == 'vyos':
- component_version.write_version_footer(cfg_versions,
- self._config_file,
- vintage='vyos')
-
- def save_json_record(self, component_versions: dict):
- """
- Write component versions to a json file
- """
- mask = os.umask(0o113)
- version_file = vyos.defaults.component_version_json
- try:
- with open(version_file, 'w') as f:
- f.write(json.dumps(component_versions, indent=2, sort_keys=True))
- except OSError:
- pass
- finally:
- os.umask(mask)
-
- def run(self):
- """
- Gather component versions from config file and system.
- Run migration scripts.
- Update vintage ('vyatta' or 'vyos'), if needed.
- If changed, remove old versions string from config file, and
- write new versions string.
- """
- cfg_file = self._config_file
-
- cfg_versions = self.read_config_file_versions()
- if self._force:
- # This will force calling all migration scripts:
- cfg_versions = {}
-
- sys_versions = component_version.from_system()
-
- # save system component versions in json file for easy reference
- self.save_json_record(sys_versions)
-
- rev_versions = self.run_migration_scripts(cfg_versions, sys_versions)
-
- if rev_versions != cfg_versions:
- self._changed = True
-
- if self.update_vintage():
- self._changed = True
-
- if not self._changed:
- return
-
- component_version.remove_footer(cfg_file)
-
- self.write_config_file_versions(rev_versions)
-
- def config_changed(self):
- return self._changed
-
-class VirtualMigrator(Migrator):
- def run(self):
- cfg_file = self._config_file
-
- cfg_versions = self.read_config_file_versions()
- if not cfg_versions:
- return
-
- if self.update_vintage():
- self._changed = True
-
- if not self._changed:
- return
-
- component_version.remove_footer(cfg_file)
-
- self.write_config_file_versions(cfg_versions)
-
diff --git a/python/vyos/utils/auth.py b/python/vyos/utils/auth.py
index d014f756f..a0b3e1cae 100644
--- a/python/vyos/utils/auth.py
+++ b/python/vyos/utils/auth.py
@@ -42,6 +42,10 @@ def split_ssh_public_key(key_string, defaultname=""):
def get_current_user() -> str:
import os
current_user = 'nobody'
+ # During CLI "owner" script execution we use SUDO_USER
if 'SUDO_USER' in os.environ:
current_user = os.environ['SUDO_USER']
+ # During op-mode or config-mode interactive CLI we use USER
+ elif 'USER' in os.environ:
+ current_user = os.environ['USER']
return current_user
diff --git a/python/vyos/utils/dict.py b/python/vyos/utils/dict.py
index 062ab9c81..1eb6abcd5 100644
--- a/python/vyos/utils/dict.py
+++ b/python/vyos/utils/dict.py
@@ -34,7 +34,7 @@ def colon_separated_to_dict(data_string, uniquekeys=False):
otherwise they are always lists of strings.
"""
import re
- key_value_re = re.compile('([^:]+)\s*\:\s*(.*)')
+ key_value_re = re.compile(r'([^:]+)\s*\:\s*(.*)')
data_raw = re.split('\n', data_string)
diff --git a/python/vyos/utils/disk.py b/python/vyos/utils/disk.py
index ee540b107..d4271ebe1 100644
--- a/python/vyos/utils/disk.py
+++ b/python/vyos/utils/disk.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
@@ -21,3 +21,52 @@ def device_from_id(id):
for device in path.iterdir():
if device.name.endswith(id):
return device.readlink().stem
+
+def get_storage_stats(directory, human_units=True):
+ """ Return basic storage stats for given directory """
+ from re import sub as re_sub
+ from vyos.utils.process import cmd
+ from vyos.utils.convert import human_to_bytes
+
+ # XXX: using `df -h` and converting human units to bytes
+ # may seem pointless, but there's a reason.
+ # df uses different header field names with `-h` and without it ("Size" vs "1K-blocks")
+ # and outputs values in 1K blocks without `-h`,
+ # so some amount of conversion is needed anyway.
+ # Using `df -h` by default seems simpler.
+ #
+ # This is what the output looks like, as of Debian Buster/Bullseye:
+ # $ df -h -t ext4 --output=source,size,used,avail,pcent
+ # Filesystem Size Used Avail Use%
+ # /dev/sda1 16G 7.6G 7.3G 51%
+
+ out = cmd(f"df -h --output=source,size,used,avail,pcent {directory}")
+ lines = out.splitlines()
+ lists = [l.split() for l in lines]
+ res = {lists[0][i]: lists[1][i] for i in range(len(lists[0]))}
+
+ convert = (lambda x: x) if human_units else human_to_bytes
+
+ stats = {}
+
+ stats["filesystem"] = res["Filesystem"]
+ stats["size"] = convert(res["Size"])
+ stats["used"] = convert(res["Used"])
+ stats["avail"] = convert(res["Avail"])
+ stats["use_percentage"] = re_sub(r'%', '', res["Use%"])
+
+ return stats
+
+def get_persistent_storage_stats(human_units=True):
+ from os.path import exists as path_exists
+
+ persistence_dir = "/usr/lib/live/mount/persistence"
+ if path_exists(persistence_dir):
+ stats = get_storage_stats(persistence_dir, human_units=human_units)
+ else:
+ # If the persistence path doesn't exist,
+ # the system is running from a live CD
+ # and the concept of persistence storage stats is not applicable
+ stats = None
+
+ return stats
diff --git a/python/vyos/utils/kernel.py b/python/vyos/utils/kernel.py
index 1f3bbdffe..847f80108 100644
--- a/python/vyos/utils/kernel.py
+++ b/python/vyos/utils/kernel.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
@@ -36,3 +36,78 @@ def unload_kmod(k_mod):
if os.path.exists(f'/sys/module/{module}'):
if call(f'rmmod {module}') != 0:
raise ConfigError(f'Unloading Kernel module {module} failed')
+
+def list_loaded_modules():
+ """ Returns the list of currently loaded kernel modules """
+ from os import listdir
+ return listdir('/sys/module/')
+
+def get_module_data(module: str):
+ """ Retrieves information about a module """
+ from os import listdir
+ from os.path import isfile, dirname, basename, join
+ from vyos.utils.file import read_file
+
+ def _get_file(path):
+ # Some files inside some modules are not readable at all,
+ # we just skip them.
+ try:
+ return read_file(path)
+ except PermissionError:
+ return None
+
+ mod_path = join('/sys/module', module)
+ mod_data = {"name": module, "fields": {}, "parameters": {}}
+
+ for f in listdir(mod_path):
+ if f in ["sections", "notes", "uevent"]:
+ # The uevent file is not readable
+ # and module build info and memory layout
+ # in notes and sections generally aren't useful
+ # for anything but kernel debugging.
+ pass
+ elif f == "drivers":
+ # Drivers are dir symlinks,
+ # we just list them
+ drivers = listdir(join(mod_path, f))
+ if drivers:
+ mod_data["drivers"] = drivers
+ elif f == "holders":
+ # Holders (module that use this one)
+ # are always symlink to other modules.
+ # We only need the list.
+ holders = listdir(join(mod_path, f))
+ if holders:
+ mod_data["holders"] = holders
+ elif f == "parameters":
+ # Many modules keep their configuration
+ # in the "parameters" subdir.
+ ppath = join(mod_path, "parameters")
+ ps = listdir(ppath)
+ for p in ps:
+ data = _get_file(join(ppath, p))
+ if data:
+ mod_data["parameters"][p] = data
+ else:
+ # Everything else...
+ # There are standard fields like refcount and initstate,
+ # but many modules also keep custom information or settings
+ # in top-level fields.
+ # For now we don't separate well-known and custom fields.
+ if isfile(join(mod_path, f)):
+ data = _get_file(join(mod_path, f))
+ if data:
+ mod_data["fields"][f] = data
+ else:
+ raise RuntimeError(f"Unexpected directory inside module {module}: {f}")
+
+ return mod_data
+
+def lsmod():
+ """ Returns information about all loaded modules.
+ Like lsmod(8), but more detailed.
+ """
+ mods_data = []
+ for m in list_loaded_modules():
+ mods_data.append(get_module_data(m))
+ return mods_data
diff --git a/python/vyos/utils/system.py b/python/vyos/utils/system.py
index f427032a4..fca93d118 100644
--- a/python/vyos/utils/system.py
+++ b/python/vyos/utils/system.py
@@ -99,6 +99,18 @@ def load_as_module(name: str, path: str):
spec.loader.exec_module(mod)
return mod
+def load_as_module_source(name: str, path: str):
+ """ Necessary modification of load_as_module for files without *.py
+ extension """
+ import importlib.util
+ from importlib.machinery import SourceFileLoader
+
+ loader = SourceFileLoader(name, path)
+ spec = importlib.util.spec_from_loader(name, loader)
+ mod = importlib.util.module_from_spec(spec)
+ spec.loader.exec_module(mod)
+ return mod
+
def get_uptime_seconds():
""" Returns system uptime in seconds """
from re import search
@@ -127,4 +139,3 @@ def get_load_averages():
res[15] = float(matches["fifteen"]) / core_count
return res
-
diff --git a/scripts/generate-configd-include-json.py b/scripts/generate-configd-include-json.py
new file mode 100755
index 000000000..b4b627fce
--- /dev/null
+++ b/scripts/generate-configd-include-json.py
@@ -0,0 +1,34 @@
+#!/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 os
+from jinja2 import Template
+
+conf_scripts = 'src/conf_mode'
+configd_include = 'data/configd-include.json'
+
+configd_template = Template("""[
+{% for file in files %}
+"{{ file }}"{{ "," if not loop.last else "" }}
+{% endfor %}
+]
+""", trim_blocks=True)
+
+files = [f for f in os.listdir(conf_scripts) if os.path.isfile(f'{conf_scripts}/{f}')]
+files = sorted(files)
+
+tmp = {'files' : files}
+with open(configd_include, 'w') as f:
+ f.write(configd_template.render(tmp))
diff --git a/smoketest/bin/vyos-configtest b/smoketest/bin/vyos-configtest
index c1b602737..fbf4055ad 100755
--- a/smoketest/bin/vyos-configtest
+++ b/smoketest/bin/vyos-configtest
@@ -55,7 +55,7 @@ def make_test_function(filename, test_path=None):
if test_path:
config_commands = self.session.show(['configuration', 'commands'])
-
+
with open(test_path, 'r') as f:
for line in f.readlines():
if not line or line.startswith("#"):
@@ -83,9 +83,10 @@ if __name__ == '__main__':
test_path = os.path.join(config_test_dir, config)
if not os.path.exists(test_path):
- test_path = None
- else:
- log.info(f'Loaded migration result test for config "{config}"')
+ log.error(f'Missing migration result test for config "{config}"')
+ sys.exit(1)
+
+ log.info(f'Loaded migration result test for config "{config}"')
test_func = make_test_function(config, test_path)
diff --git a/smoketest/bin/vyos-configtest-pki b/smoketest/bin/vyos-configtest-pki
index e753193e9..0f9ecdd41 100755
--- a/smoketest/bin/vyos-configtest-pki
+++ b/smoketest/bin/vyos-configtest-pki
@@ -25,9 +25,9 @@ from vyos.pki import encode_dh_parameters
from vyos.pki import encode_private_key
from vyos.utils.file import write_file
-subject = {'country': 'DE', 'state': 'BY', 'locality': 'Cloud', 'organization': 'VyOS', 'common_name': 'vyos'}
-ca_subject = {'country': 'DE', 'state': 'BY', 'locality': 'Cloud', 'organization': 'VyOS', 'common_name': 'vyos CA'}
-subca_subject = {'country': 'DE', 'state': 'BY', 'locality': 'Cloud', 'organization': 'VyOS', 'common_name': 'vyos SubCA'}
+subject = {'country': 'DE', 'state': 'BY', 'locality': 'Cloud', 'organization': 'VyOS', 'common_name': 'VyOS'}
+ca_subject = {'country': 'DE', 'state': 'BY', 'locality': 'Cloud', 'organization': 'VyOS', 'common_name': 'VyOS CA'}
+subca_subject = {'country': 'DE', 'state': 'BY', 'locality': 'Cloud', 'organization': 'VyOS', 'common_name': 'VyOS SubCA'}
ca_cert = '/config/auth/ovpn_test_ca.pem'
ca_key = '/config/auth/ovpn_test_ca.key'
diff --git a/smoketest/config-tests/basic-api-service b/smoketest/config-tests/basic-api-service
index dc54929b9..3f796f35d 100644
--- a/smoketest/config-tests/basic-api-service
+++ b/smoketest/config-tests/basic-api-service
@@ -1,16 +1,28 @@
set interfaces ethernet eth0 address '192.0.2.1/31'
set interfaces ethernet eth0 address '2001:db8::1234/64'
+set interfaces ethernet eth0 offload gro
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 https allow-client address '172.16.0.0/12'
set service https allow-client address '192.168.0.0/16'
set service https allow-client address '10.0.0.0/8'
set service https allow-client address '2001:db8::/32'
set service https api keys id 1 key 'S3cur3'
+set service ntp allow-client address '0.0.0.0/0'
+set service ntp allow-client address '::/0'
+set service ntp server time1.vyos.net
+set service ntp server time2.vyos.net
+set service ntp server time3.vyos.net
set system config-management commit-revisions '100'
+set system conntrack modules ftp
+set system conntrack modules h323
+set system conntrack modules nfs
+set system conntrack modules pptp
+set system conntrack modules sip
+set system conntrack modules sqlnet
+set system conntrack modules tftp
+set system console device ttyS0 speed '115200'
set system host-name 'vyos'
set system login user vyos authentication encrypted-password '$6$2Ta6TWHd/U$NmrX0x9kexCimeOcYK1MfhMpITF9ELxHcaBU/znBq.X2ukQOj61fVI2UYP/xBzP4QtiTcdkgs7WOQMHWsRymO/'
set system login user vyos authentication plaintext-password ''
-set system console device ttyS0 speed '115200'
+set system syslog global facility all level 'info'
+set system syslog global facility local7 level 'debug'
diff --git a/smoketest/config-tests/basic-vyos b/smoketest/config-tests/basic-vyos
index d676c663d..6ff28ec2e 100644
--- a/smoketest/config-tests/basic-vyos
+++ b/smoketest/config-tests/basic-vyos
@@ -1,5 +1,14 @@
set interfaces ethernet eth0 address '192.168.0.1/24'
set interfaces ethernet eth0 address 'fe88::1/56'
+set interfaces ethernet eth0 duplex 'auto'
+set interfaces ethernet eth0 offload gro
+set interfaces ethernet eth0 speed 'auto'
+set interfaces ethernet eth1 duplex 'auto'
+set interfaces ethernet eth1 offload gro
+set interfaces ethernet eth1 speed 'auto'
+set interfaces ethernet eth2 duplex 'auto'
+set interfaces ethernet eth2 offload gro
+set interfaces ethernet eth2 speed 'auto'
set interfaces ethernet eth2 vif 100 address '100.100.0.1/24'
set interfaces ethernet eth2 vif-s 200 address '100.64.200.254/24'
set interfaces ethernet eth2 vif-s 200 vif-c 201 address '100.64.201.254/24'
@@ -19,17 +28,6 @@ set protocols static arp interface eth2.200.201 address 100.64.201.20 mac '00:50
set protocols static arp interface eth2.200.202 address 100.64.202.30 mac '00:50:00:00:00:30'
set protocols static arp interface eth2.200.202 address 100.64.202.40 mac '00:50:00:00:00:40'
set protocols static route 0.0.0.0/0 next-hop 100.64.0.1
-set service ssh ciphers 'aes128-ctr'
-set service ssh ciphers 'aes192-ctr'
-set service ssh ciphers 'aes256-ctr'
-set service ssh ciphers 'chacha20-poly1305@openssh.com'
-set service ssh ciphers 'rijndael-cbc@lysator.liu.se'
-set service ssh key-exchange 'curve25519-sha256@libssh.org'
-set service ssh key-exchange 'diffie-hellman-group1-sha1'
-set service ssh key-exchange 'diffie-hellman-group-exchange-sha1'
-set service ssh key-exchange 'diffie-hellman-group-exchange-sha256'
-set service ssh listen-address '192.168.0.1'
-set service ssh port '22'
set service dhcp-server shared-network-name LAN authoritative
set service dhcp-server shared-network-name LAN subnet 192.168.0.0/24 option default-router '192.168.0.1'
set service dhcp-server shared-network-name LAN subnet 192.168.0.0/24 option domain-name 'vyos.net'
@@ -66,19 +64,40 @@ set service dns forwarding allow-from '192.168.0.0/16'
set service dns forwarding cache-size '10000'
set service dns forwarding dnssec 'off'
set service dns forwarding listen-address '192.168.0.1'
+set service ssh ciphers 'aes128-ctr'
+set service ssh ciphers 'aes192-ctr'
+set service ssh ciphers 'aes256-ctr'
+set service ssh ciphers 'chacha20-poly1305@openssh.com'
+set service ssh ciphers 'rijndael-cbc@lysator.liu.se'
+set service ssh key-exchange 'curve25519-sha256@libssh.org'
+set service ssh key-exchange 'diffie-hellman-group1-sha1'
+set service ssh key-exchange 'diffie-hellman-group-exchange-sha1'
+set service ssh key-exchange 'diffie-hellman-group-exchange-sha256'
+set service ssh listen-address '192.168.0.1'
+set service ssh port '22'
set system config-management commit-revisions '100'
set system conntrack ignore ipv4 rule 1 destination address '192.0.2.2'
set system conntrack ignore ipv4 rule 1 source address '192.0.2.1'
+set system conntrack modules ftp
+set system conntrack modules h323
+set system conntrack modules nfs
+set system conntrack modules pptp
+set system conntrack modules sip
+set system conntrack modules sqlnet
+set system conntrack modules tftp
+set system console device ttyS0 speed '115200'
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 name-server '192.168.0.1'
-set system syslog global facility auth level 'info'
-set system syslog global preserve-fqdn
set system syslog console facility all level 'emerg'
set system syslog console facility mail level 'info'
+set system syslog global facility all level 'info'
+set system syslog global facility auth level 'info'
+set system syslog global facility local7 level 'debug'
+set system syslog global preserve-fqdn
set system syslog host syslog.vyos.net facility auth level 'warning'
set system syslog host syslog.vyos.net facility local7 level 'notice'
set system syslog host syslog.vyos.net format octet-counted
set system syslog host syslog.vyos.net port '8000'
-set system console device ttyS0 speed '115200'
+set system time-zone 'Europe/Berlin'
diff --git a/smoketest/config-tests/bgp-azure-ipsec-gateway b/smoketest/config-tests/bgp-azure-ipsec-gateway
new file mode 100644
index 000000000..bbd7b961f
--- /dev/null
+++ b/smoketest/config-tests/bgp-azure-ipsec-gateway
@@ -0,0 +1,231 @@
+set firewall global-options all-ping 'enable'
+set firewall global-options broadcast-ping 'disable'
+set firewall global-options ip-src-route 'disable'
+set firewall global-options ipv6-receive-redirects 'disable'
+set firewall global-options ipv6-src-route 'disable'
+set firewall global-options log-martians 'disable'
+set firewall global-options receive-redirects 'disable'
+set firewall global-options send-redirects 'enable'
+set firewall global-options source-validation 'disable'
+set firewall global-options syn-cookies 'enable'
+set firewall global-options twa-hazards-protection 'disable'
+set high-availability vrrp group DMZ-VLAN-3962 address 192.168.34.36/27
+set high-availability vrrp group DMZ-VLAN-3962 interface 'eth1'
+set high-availability vrrp group DMZ-VLAN-3962 preempt-delay '180'
+set high-availability vrrp group DMZ-VLAN-3962 priority '200'
+set high-availability vrrp group DMZ-VLAN-3962 vrid '62'
+set interfaces ethernet eth0 address '192.0.2.189/27'
+set interfaces ethernet eth0 duplex 'auto'
+set interfaces ethernet eth0 offload gro
+set interfaces ethernet eth0 speed 'auto'
+set interfaces ethernet eth1 address '192.168.34.37/27'
+set interfaces ethernet eth1 duplex 'auto'
+set interfaces ethernet eth1 offload gro
+set interfaces ethernet eth1 speed 'auto'
+set interfaces loopback lo
+set interfaces vti vti31 ip adjust-mss '1350'
+set interfaces vti vti32 ip adjust-mss '1350'
+set interfaces vti vti41 ip adjust-mss '1350'
+set interfaces vti vti42 ip adjust-mss '1350'
+set interfaces vti vti51 ip adjust-mss '1350'
+set interfaces vti vti52 ip adjust-mss '1350'
+set policy prefix-list AZURE-BGP-IPv4-in description 'Prefixes received from Azure'
+set policy prefix-list AZURE-BGP-IPv4-in rule 100 action 'permit'
+set policy prefix-list AZURE-BGP-IPv4-in rule 100 le '32'
+set policy prefix-list AZURE-BGP-IPv4-in rule 100 prefix '100.64.0.0/10'
+set policy prefix-list ONPREM-BGP-IPv4-out description 'Prefixes allowed to be announced into Azure'
+set policy prefix-list ONPREM-BGP-IPv4-out rule 100 action 'permit'
+set policy prefix-list ONPREM-BGP-IPv4-out rule 100 prefix '10.0.0.0/8'
+set policy prefix-list ONPREM-BGP-IPv4-out rule 200 action 'permit'
+set policy prefix-list ONPREM-BGP-IPv4-out rule 200 prefix '172.16.0.0/12'
+set policy prefix-list ONPREM-BGP-IPv4-out rule 300 action 'permit'
+set policy prefix-list ONPREM-BGP-IPv4-out rule 300 prefix '192.168.0.0/16'
+set protocols bgp address-family ipv4-unicast network 10.0.0.0/8
+set protocols bgp address-family ipv4-unicast network 172.16.0.0/12
+set protocols bgp address-family ipv4-unicast network 192.168.0.0/16
+set protocols bgp neighbor 100.66.8.36 peer-group 'AZURE'
+set protocols bgp neighbor 100.66.8.36 remote-as '64517'
+set protocols bgp neighbor 100.66.8.37 peer-group 'AZURE'
+set protocols bgp neighbor 100.66.8.37 remote-as '64517'
+set protocols bgp neighbor 100.66.24.36 peer-group 'AZURE'
+set protocols bgp neighbor 100.66.24.36 remote-as '64513'
+set protocols bgp neighbor 100.66.24.37 peer-group 'AZURE'
+set protocols bgp neighbor 100.66.24.37 remote-as '64513'
+set protocols bgp neighbor 100.66.40.36 peer-group 'AZURE'
+set protocols bgp neighbor 100.66.40.36 remote-as '64515'
+set protocols bgp neighbor 100.66.40.37 peer-group 'AZURE'
+set protocols bgp neighbor 100.66.40.37 remote-as '64515'
+set protocols bgp neighbor 192.168.34.38 address-family ipv4-unicast nexthop-self
+set protocols bgp neighbor 192.168.34.38 address-family ipv4-unicast soft-reconfiguration inbound
+set protocols bgp neighbor 192.168.34.38 capability dynamic
+set protocols bgp neighbor 192.168.34.38 password 'VyOSR0xx123'
+set protocols bgp neighbor 192.168.34.38 remote-as '65522'
+set protocols bgp neighbor 192.168.34.38 update-source 'eth1'
+set protocols bgp peer-group AZURE address-family ipv4-unicast maximum-prefix '50'
+set protocols bgp peer-group AZURE address-family ipv4-unicast prefix-list export 'ONPREM-BGP-IPv4-out'
+set protocols bgp peer-group AZURE address-family ipv4-unicast prefix-list import 'AZURE-BGP-IPv4-in'
+set protocols bgp peer-group AZURE ebgp-multihop '2'
+set protocols bgp peer-group AZURE update-source 'eth1'
+set protocols bgp system-as '65522'
+set protocols bgp timers holdtime '30'
+set protocols bgp timers keepalive '5'
+set protocols static route 0.0.0.0/0 next-hop 192.168.34.33
+set protocols static route 51.105.0.0/16 next-hop 192.0.2.161
+set protocols static route 52.143.0.0/16 next-hop 192.0.2.161
+set protocols static route 100.66.8.36/32 interface vti31
+set protocols static route 100.66.8.36/32 interface vti32
+set protocols static route 100.66.8.37/32 interface vti31
+set protocols static route 100.66.8.37/32 interface vti32
+set protocols static route 100.66.24.36/32 interface vti41
+set protocols static route 100.66.24.36/32 interface vti42
+set protocols static route 100.66.24.37/32 interface vti41
+set protocols static route 100.66.24.37/32 interface vti42
+set protocols static route 100.66.40.36/32 interface vti51
+set protocols static route 100.66.40.36/32 interface vti52
+set protocols static route 100.66.40.37/32 interface vti51
+set protocols static route 100.66.40.37/32 interface vti52
+set protocols static route 195.137.175.0/24 next-hop 192.0.2.161
+set protocols static route 212.23.159.0/26 next-hop 192.0.2.161
+set service ntp allow-client address '0.0.0.0/0'
+set service ntp allow-client address '::/0'
+set service ntp server 192.0.2.254
+set service snmp v3 engineid 'ff42'
+set service snmp v3 group default mode 'ro'
+set service snmp v3 group default seclevel 'priv'
+set service snmp v3 group default view 'default'
+set service snmp v3 user VyOS auth encrypted-password '1ad73f4620b8c0dd2de066622f875b161a14adad'
+set service snmp v3 user VyOS auth type 'sha'
+set service snmp v3 user VyOS group 'default'
+set service snmp v3 user VyOS privacy encrypted-password '1ad73f4620b8c0dd2de066622f875b16'
+set service snmp v3 user VyOS privacy type 'aes'
+set service snmp v3 view default oid 1
+set service ssh disable-host-validation
+set service ssh port '22'
+set system config-management commit-revisions '100'
+set system conntrack modules ftp
+set system conntrack modules h323
+set system conntrack modules nfs
+set system conntrack modules pptp
+set system conntrack modules sip
+set system conntrack modules sqlnet
+set system conntrack modules tftp
+set system console device ttyS0 speed '115200'
+set system domain-name 'vyos.net'
+set system flow-accounting interface 'eth1'
+set system flow-accounting interface 'vti31'
+set system flow-accounting interface 'vti32'
+set system flow-accounting interface 'vti41'
+set system flow-accounting interface 'vti42'
+set system flow-accounting interface 'vti51'
+set system flow-accounting interface 'vti52'
+set system flow-accounting netflow server 10.0.1.1 port '2055'
+set system flow-accounting netflow source-address '192.168.34.37'
+set system flow-accounting netflow version '10'
+set system flow-accounting syslog-facility 'daemon'
+set system host-name 'azure-gw-01'
+set system login radius server 192.0.2.253 key 'secret1234'
+set system login radius server 192.0.2.253 port '1812'
+set system login radius server 192.0.2.253 timeout '2'
+set system login radius server 192.0.2.254 key 'secret1234'
+set system login radius server 192.0.2.254 port '1812'
+set system login radius server 192.0.2.254 timeout '2'
+set system login radius source-address '192.168.34.37'
+set system login user vyos authentication encrypted-password '$6$O5gJRlDYQpj$MtrCV9lxMnZPMbcxlU7.FI793MImNHznxGoMFgm3Q6QP3vfKJyOSRCt3Ka/GzFQyW1yZS4NS616NLHaIPPFHc0'
+set system login user vyos authentication plaintext-password ''
+set system logs logrotate messages max-size '20'
+set system logs logrotate messages rotate '10'
+set system name-server '192.0.2.254'
+set system syslog global facility all level 'info'
+set system syslog global facility local7 level 'debug'
+set system syslog host 10.0.9.188 facility all level 'info'
+set system syslog host 10.0.9.188 protocol 'udp'
+set system time-zone 'Europe/Berlin'
+set vpn ipsec authentication psk peer_51-105-0-1 id '51.105.0.1'
+set vpn ipsec authentication psk peer_51-105-0-1 id '192.0.2.189'
+set vpn ipsec authentication psk peer_51-105-0-1 secret 'averysecretpsktowardsazure'
+set vpn ipsec authentication psk peer_51-105-0-2 id '51.105.0.2'
+set vpn ipsec authentication psk peer_51-105-0-2 id '192.0.2.189'
+set vpn ipsec authentication psk peer_51-105-0-2 secret 'averysecretpsktowardsazure'
+set vpn ipsec authentication psk peer_51-105-0-3 id '51.105.0.3'
+set vpn ipsec authentication psk peer_51-105-0-3 id '192.0.2.189'
+set vpn ipsec authentication psk peer_51-105-0-3 secret 'averysecretpsktowardsazure'
+set vpn ipsec authentication psk peer_51-105-0-4 id '51.105.0.4'
+set vpn ipsec authentication psk peer_51-105-0-4 id '192.0.2.189'
+set vpn ipsec authentication psk peer_51-105-0-4 secret 'averysecretpsktowardsazure'
+set vpn ipsec authentication psk peer_51-105-0-5 id '51.105.0.5'
+set vpn ipsec authentication psk peer_51-105-0-5 id '192.0.2.189'
+set vpn ipsec authentication psk peer_51-105-0-5 secret 'averysecretpsktowardsazure'
+set vpn ipsec authentication psk peer_51-105-0-6 id '51.105.0.6'
+set vpn ipsec authentication psk peer_51-105-0-6 id '192.0.2.189'
+set vpn ipsec authentication psk peer_51-105-0-6 secret 'averysecretpsktowardsazure'
+set vpn ipsec esp-group ESP-AZURE lifetime '27000'
+set vpn ipsec esp-group ESP-AZURE mode 'tunnel'
+set vpn ipsec esp-group ESP-AZURE pfs 'disable'
+set vpn ipsec esp-group ESP-AZURE proposal 1 encryption 'aes256'
+set vpn ipsec esp-group ESP-AZURE proposal 1 hash 'sha1'
+set vpn ipsec ike-group IKE-AZURE close-action 'none'
+set vpn ipsec ike-group IKE-AZURE dead-peer-detection action 'restart'
+set vpn ipsec ike-group IKE-AZURE dead-peer-detection interval '2'
+set vpn ipsec ike-group IKE-AZURE dead-peer-detection timeout '15'
+set vpn ipsec ike-group IKE-AZURE key-exchange 'ikev2'
+set vpn ipsec ike-group IKE-AZURE lifetime '27000'
+set vpn ipsec ike-group IKE-AZURE proposal 1 dh-group '2'
+set vpn ipsec ike-group IKE-AZURE proposal 1 encryption 'aes256'
+set vpn ipsec ike-group IKE-AZURE proposal 1 hash 'sha1'
+set vpn ipsec interface 'eth0'
+set vpn ipsec log level '2'
+set vpn ipsec log subsystem 'ike'
+set vpn ipsec site-to-site peer peer_51-105-0-1 authentication mode 'pre-shared-secret'
+set vpn ipsec site-to-site peer peer_51-105-0-1 authentication remote-id '51.105.0.1'
+set vpn ipsec site-to-site peer peer_51-105-0-1 connection-type 'respond'
+set vpn ipsec site-to-site peer peer_51-105-0-1 default-esp-group 'ESP-AZURE'
+set vpn ipsec site-to-site peer peer_51-105-0-1 ike-group 'IKE-AZURE'
+set vpn ipsec site-to-site peer peer_51-105-0-1 ikev2-reauth 'inherit'
+set vpn ipsec site-to-site peer peer_51-105-0-1 local-address '192.0.2.189'
+set vpn ipsec site-to-site peer peer_51-105-0-1 remote-address '51.105.0.1'
+set vpn ipsec site-to-site peer peer_51-105-0-1 vti bind 'vti51'
+set vpn ipsec site-to-site peer peer_51-105-0-2 authentication mode 'pre-shared-secret'
+set vpn ipsec site-to-site peer peer_51-105-0-2 authentication remote-id '51.105.0.2'
+set vpn ipsec site-to-site peer peer_51-105-0-2 connection-type 'respond'
+set vpn ipsec site-to-site peer peer_51-105-0-2 default-esp-group 'ESP-AZURE'
+set vpn ipsec site-to-site peer peer_51-105-0-2 ike-group 'IKE-AZURE'
+set vpn ipsec site-to-site peer peer_51-105-0-2 ikev2-reauth 'inherit'
+set vpn ipsec site-to-site peer peer_51-105-0-2 local-address '192.0.2.189'
+set vpn ipsec site-to-site peer peer_51-105-0-2 remote-address '51.105.0.2'
+set vpn ipsec site-to-site peer peer_51-105-0-2 vti bind 'vti52'
+set vpn ipsec site-to-site peer peer_51-105-0-3 authentication mode 'pre-shared-secret'
+set vpn ipsec site-to-site peer peer_51-105-0-3 authentication remote-id '51.105.0.3'
+set vpn ipsec site-to-site peer peer_51-105-0-3 connection-type 'respond'
+set vpn ipsec site-to-site peer peer_51-105-0-3 ike-group 'IKE-AZURE'
+set vpn ipsec site-to-site peer peer_51-105-0-3 ikev2-reauth 'inherit'
+set vpn ipsec site-to-site peer peer_51-105-0-3 local-address '192.0.2.189'
+set vpn ipsec site-to-site peer peer_51-105-0-3 remote-address '51.105.0.3'
+set vpn ipsec site-to-site peer peer_51-105-0-3 vti bind 'vti32'
+set vpn ipsec site-to-site peer peer_51-105-0-3 vti esp-group 'ESP-AZURE'
+set vpn ipsec site-to-site peer peer_51-105-0-4 authentication mode 'pre-shared-secret'
+set vpn ipsec site-to-site peer peer_51-105-0-4 authentication remote-id '51.105.0.4'
+set vpn ipsec site-to-site peer peer_51-105-0-4 connection-type 'respond'
+set vpn ipsec site-to-site peer peer_51-105-0-4 ike-group 'IKE-AZURE'
+set vpn ipsec site-to-site peer peer_51-105-0-4 ikev2-reauth 'inherit'
+set vpn ipsec site-to-site peer peer_51-105-0-4 local-address '192.0.2.189'
+set vpn ipsec site-to-site peer peer_51-105-0-4 remote-address '51.105.0.4'
+set vpn ipsec site-to-site peer peer_51-105-0-4 vti bind 'vti31'
+set vpn ipsec site-to-site peer peer_51-105-0-4 vti esp-group 'ESP-AZURE'
+set vpn ipsec site-to-site peer peer_51-105-0-5 authentication mode 'pre-shared-secret'
+set vpn ipsec site-to-site peer peer_51-105-0-5 authentication remote-id '51.105.0.5'
+set vpn ipsec site-to-site peer peer_51-105-0-5 connection-type 'respond'
+set vpn ipsec site-to-site peer peer_51-105-0-5 ike-group 'IKE-AZURE'
+set vpn ipsec site-to-site peer peer_51-105-0-5 ikev2-reauth 'inherit'
+set vpn ipsec site-to-site peer peer_51-105-0-5 local-address '192.0.2.189'
+set vpn ipsec site-to-site peer peer_51-105-0-5 remote-address '51.105.0.5'
+set vpn ipsec site-to-site peer peer_51-105-0-5 vti bind 'vti42'
+set vpn ipsec site-to-site peer peer_51-105-0-5 vti esp-group 'ESP-AZURE'
+set vpn ipsec site-to-site peer peer_51-105-0-6 authentication mode 'pre-shared-secret'
+set vpn ipsec site-to-site peer peer_51-105-0-6 authentication remote-id '51.105.0.6'
+set vpn ipsec site-to-site peer peer_51-105-0-6 connection-type 'respond'
+set vpn ipsec site-to-site peer peer_51-105-0-6 ike-group 'IKE-AZURE'
+set vpn ipsec site-to-site peer peer_51-105-0-6 ikev2-reauth 'inherit'
+set vpn ipsec site-to-site peer peer_51-105-0-6 local-address '192.0.2.189'
+set vpn ipsec site-to-site peer peer_51-105-0-6 remote-address '51.105.0.6'
+set vpn ipsec site-to-site peer peer_51-105-0-6 vti bind 'vti41'
+set vpn ipsec site-to-site peer peer_51-105-0-6 vti esp-group 'ESP-AZURE'
diff --git a/smoketest/config-tests/bgp-bfd-communities b/smoketest/config-tests/bgp-bfd-communities
new file mode 100644
index 000000000..6eee0137e
--- /dev/null
+++ b/smoketest/config-tests/bgp-bfd-communities
@@ -0,0 +1,201 @@
+set interfaces ethernet eth0 address '192.0.2.100/25'
+set interfaces ethernet eth0 address '2001:db8::ffff/64'
+set interfaces ethernet eth0 offload gro
+set interfaces loopback lo
+set policy large-community-list ANYCAST_ALL rule 10 action 'permit'
+set policy large-community-list ANYCAST_ALL rule 10 description 'Allow all anycast from anywhere'
+set policy large-community-list ANYCAST_ALL rule 10 regex '4242420696:100:.*'
+set policy large-community-list ANYCAST_INT rule 10 action 'permit'
+set policy large-community-list ANYCAST_INT rule 10 description 'Allow all anycast from int'
+set policy large-community-list ANYCAST_INT rule 10 regex '4242420696:100:1'
+set policy prefix-list BGP-BACKBONE-IN description 'Inbound backbone routes from other sites'
+set policy prefix-list BGP-BACKBONE-IN rule 10 action 'deny'
+set policy prefix-list BGP-BACKBONE-IN rule 10 description 'Block default route'
+set policy prefix-list BGP-BACKBONE-IN rule 10 prefix '0.0.0.0/0'
+set policy prefix-list BGP-BACKBONE-IN rule 20 action 'deny'
+set policy prefix-list BGP-BACKBONE-IN rule 20 description 'Block int primary'
+set policy prefix-list BGP-BACKBONE-IN rule 20 ge '21'
+set policy prefix-list BGP-BACKBONE-IN rule 20 prefix '192.168.0.0/20'
+set policy prefix-list BGP-BACKBONE-IN rule 30 action 'deny'
+set policy prefix-list BGP-BACKBONE-IN rule 30 description 'Block loopbacks'
+set policy prefix-list BGP-BACKBONE-IN rule 30 ge '25'
+set policy prefix-list BGP-BACKBONE-IN rule 30 prefix '192.168.253.0/24'
+set policy prefix-list BGP-BACKBONE-IN rule 40 action 'deny'
+set policy prefix-list BGP-BACKBONE-IN rule 40 description 'Block backbone peering'
+set policy prefix-list BGP-BACKBONE-IN rule 40 ge '25'
+set policy prefix-list BGP-BACKBONE-IN rule 40 prefix '192.168.254.0/24'
+set policy prefix-list BGP-BACKBONE-IN rule 999 action 'permit'
+set policy prefix-list BGP-BACKBONE-IN rule 999 description 'Allow everything else'
+set policy prefix-list BGP-BACKBONE-IN rule 999 ge '1'
+set policy prefix-list BGP-BACKBONE-IN rule 999 prefix '0.0.0.0/0'
+set policy prefix-list BGP-BACKBONE-OUT description 'Outbound backbone routes to other sites'
+set policy prefix-list BGP-BACKBONE-OUT rule 10 action 'permit'
+set policy prefix-list BGP-BACKBONE-OUT rule 10 description 'Int primary'
+set policy prefix-list BGP-BACKBONE-OUT rule 10 ge '23'
+set policy prefix-list BGP-BACKBONE-OUT rule 10 prefix '192.168.0.0/20'
+set policy prefix-list GLOBAL description 'Globally redistributed routes'
+set policy prefix-list GLOBAL rule 10 action 'permit'
+set policy prefix-list GLOBAL rule 10 prefix '192.168.100.1/32'
+set policy prefix-list GLOBAL rule 20 action 'permit'
+set policy prefix-list GLOBAL rule 20 prefix '192.168.7.128/25'
+set policy prefix-list6 BGP-BACKBONE-IN-V6 description 'Inbound backbone routes from other sites'
+set policy prefix-list6 BGP-BACKBONE-IN-V6 rule 10 action 'deny'
+set policy prefix-list6 BGP-BACKBONE-IN-V6 rule 10 description 'Block default route'
+set policy prefix-list6 BGP-BACKBONE-IN-V6 rule 10 prefix '::/0'
+set policy prefix-list6 BGP-BACKBONE-IN-V6 rule 20 action 'deny'
+set policy prefix-list6 BGP-BACKBONE-IN-V6 rule 20 description 'Block int primary'
+set policy prefix-list6 BGP-BACKBONE-IN-V6 rule 20 ge '53'
+set policy prefix-list6 BGP-BACKBONE-IN-V6 rule 20 prefix 'fd52:d62e:8011::/52'
+set policy prefix-list6 BGP-BACKBONE-IN-V6 rule 30 action 'deny'
+set policy prefix-list6 BGP-BACKBONE-IN-V6 rule 30 description 'Block peering and stuff'
+set policy prefix-list6 BGP-BACKBONE-IN-V6 rule 30 ge '53'
+set policy prefix-list6 BGP-BACKBONE-IN-V6 rule 30 prefix 'fd52:d62e:8011:f000::/52'
+set policy prefix-list6 BGP-BACKBONE-IN-V6 rule 999 action 'permit'
+set policy prefix-list6 BGP-BACKBONE-IN-V6 rule 999 description 'Allow everything else'
+set policy prefix-list6 BGP-BACKBONE-IN-V6 rule 999 ge '1'
+set policy prefix-list6 BGP-BACKBONE-IN-V6 rule 999 prefix '::/0'
+set policy prefix-list6 BGP-BACKBONE-OUT-V6 description 'Outbound backbone routes to other sites'
+set policy prefix-list6 BGP-BACKBONE-OUT-V6 rule 10 action 'permit'
+set policy prefix-list6 BGP-BACKBONE-OUT-V6 rule 10 ge '64'
+set policy prefix-list6 BGP-BACKBONE-OUT-V6 rule 10 prefix 'fd52:d62e:8011::/52'
+set policy prefix-list6 GLOBAL-V6 description 'Globally redistributed routes'
+set policy prefix-list6 GLOBAL-V6 rule 10 action 'permit'
+set policy prefix-list6 GLOBAL-V6 rule 10 ge '64'
+set policy prefix-list6 GLOBAL-V6 rule 10 prefix 'fd52:d62e:8011:2::/63'
+set policy route-map BGP-BACKBONE-IN rule 10 action 'permit'
+set policy route-map BGP-BACKBONE-IN rule 10 match ip address prefix-list 'BGP-BACKBONE-IN'
+set policy route-map BGP-BACKBONE-IN rule 20 action 'permit'
+set policy route-map BGP-BACKBONE-IN rule 20 match ipv6 address prefix-list 'BGP-BACKBONE-IN-V6'
+set policy route-map BGP-BACKBONE-IN rule 30 action 'permit'
+set policy route-map BGP-BACKBONE-IN rule 30 match large-community large-community-list 'ANYCAST_ALL'
+set policy route-map BGP-BACKBONE-OUT rule 10 action 'permit'
+set policy route-map BGP-BACKBONE-OUT rule 10 match ip address prefix-list 'BGP-BACKBONE-OUT'
+set policy route-map BGP-BACKBONE-OUT rule 20 action 'permit'
+set policy route-map BGP-BACKBONE-OUT rule 20 match ipv6 address prefix-list 'BGP-BACKBONE-OUT-V6'
+set policy route-map BGP-BACKBONE-OUT rule 30 action 'permit'
+set policy route-map BGP-BACKBONE-OUT rule 30 match large-community large-community-list 'ANYCAST_INT'
+set policy route-map BGP-BACKBONE-OUT rule 30 set as-path prepend '4242420666'
+set policy route-map BGP-REDISTRIBUTE rule 10 action 'permit'
+set policy route-map BGP-REDISTRIBUTE rule 10 description 'Prepend AS and allow VPN and modem'
+set policy route-map BGP-REDISTRIBUTE rule 10 match ip address prefix-list 'GLOBAL'
+set policy route-map BGP-REDISTRIBUTE rule 10 set as-path prepend '4242420666'
+set policy route-map BGP-REDISTRIBUTE rule 20 action 'permit'
+set policy route-map BGP-REDISTRIBUTE rule 20 description 'Allow VPN'
+set policy route-map BGP-REDISTRIBUTE rule 20 match ipv6 address prefix-list 'GLOBAL-V6'
+set protocols bfd peer 192.168.253.1 interval receive '50'
+set protocols bfd peer 192.168.253.1 interval transmit '50'
+set protocols bfd peer 192.168.253.1 multihop
+set protocols bfd peer 192.168.253.1 source address '192.168.253.3'
+set protocols bfd peer 192.168.253.2 interval receive '50'
+set protocols bfd peer 192.168.253.2 interval transmit '50'
+set protocols bfd peer 192.168.253.2 multihop
+set protocols bfd peer 192.168.253.2 source address '192.168.253.3'
+set protocols bfd peer 192.168.253.6 interval receive '50'
+set protocols bfd peer 192.168.253.6 interval transmit '50'
+set protocols bfd peer 192.168.253.6 multihop
+set protocols bfd peer 192.168.253.6 source address '192.168.253.3'
+set protocols bfd peer 192.168.253.7 interval receive '50'
+set protocols bfd peer 192.168.253.7 interval transmit '50'
+set protocols bfd peer 192.168.253.7 multihop
+set protocols bfd peer 192.168.253.7 source address '192.168.253.3'
+set protocols bfd peer 192.168.253.12 interval receive '100'
+set protocols bfd peer 192.168.253.12 interval transmit '100'
+set protocols bfd peer 192.168.253.12 multihop
+set protocols bfd peer 192.168.253.12 source address '192.168.253.3'
+set protocols bfd peer fd52:d62e:8011:fffe:192:168:253:1 interval receive '50'
+set protocols bfd peer fd52:d62e:8011:fffe:192:168:253:1 interval transmit '50'
+set protocols bfd peer fd52:d62e:8011:fffe:192:168:253:1 multihop
+set protocols bfd peer fd52:d62e:8011:fffe:192:168:253:1 source address 'fd52:d62e:8011:fffe:192:168:253:3'
+set protocols bfd peer fd52:d62e:8011:fffe:192:168:253:2 interval receive '50'
+set protocols bfd peer fd52:d62e:8011:fffe:192:168:253:2 interval transmit '50'
+set protocols bfd peer fd52:d62e:8011:fffe:192:168:253:2 multihop
+set protocols bfd peer fd52:d62e:8011:fffe:192:168:253:2 source address 'fd52:d62e:8011:fffe:192:168:253:3'
+set protocols bfd peer fd52:d62e:8011:fffe:192:168:253:6 interval receive '50'
+set protocols bfd peer fd52:d62e:8011:fffe:192:168:253:6 interval transmit '50'
+set protocols bfd peer fd52:d62e:8011:fffe:192:168:253:6 multihop
+set protocols bfd peer fd52:d62e:8011:fffe:192:168:253:6 source address 'fd52:d62e:8011:fffe:192:168:253:3'
+set protocols bfd peer fd52:d62e:8011:fffe:192:168:253:7 interval receive '50'
+set protocols bfd peer fd52:d62e:8011:fffe:192:168:253:7 interval transmit '50'
+set protocols bfd peer fd52:d62e:8011:fffe:192:168:253:7 multihop
+set protocols bfd peer fd52:d62e:8011:fffe:192:168:253:7 source address 'fd52:d62e:8011:fffe:192:168:253:3'
+set protocols bfd peer fd52:d62e:8011:fffe:192:168:253:12 interval receive '100'
+set protocols bfd peer fd52:d62e:8011:fffe:192:168:253:12 interval transmit '100'
+set protocols bfd peer fd52:d62e:8011:fffe:192:168:253:12 multihop
+set protocols bfd peer fd52:d62e:8011:fffe:192:168:253:12 source address 'fd52:d62e:8011:fffe:192:168:253:3'
+set protocols bgp address-family ipv4-unicast redistribute connected route-map 'BGP-REDISTRIBUTE'
+set protocols bgp address-family ipv4-unicast redistribute static route-map 'BGP-REDISTRIBUTE'
+set protocols bgp address-family ipv6-unicast redistribute connected route-map 'BGP-REDISTRIBUTE'
+set protocols bgp neighbor 192.168.253.1 peer-group 'INT'
+set protocols bgp neighbor 192.168.253.2 peer-group 'INT'
+set protocols bgp neighbor 192.168.253.6 peer-group 'DAL13'
+set protocols bgp neighbor 192.168.253.7 peer-group 'DAL13'
+set protocols bgp neighbor 192.168.253.12 address-family ipv4-unicast route-map export 'BGP-BACKBONE-OUT'
+set protocols bgp neighbor 192.168.253.12 address-family ipv4-unicast route-map import 'BGP-BACKBONE-IN'
+set protocols bgp neighbor 192.168.253.12 address-family ipv4-unicast soft-reconfiguration inbound
+set protocols bgp neighbor 192.168.253.12 bfd
+set protocols bgp neighbor 192.168.253.12 ebgp-multihop '2'
+set protocols bgp neighbor 192.168.253.12 remote-as '4242420669'
+set protocols bgp neighbor 192.168.253.12 update-source 'dum0'
+set protocols bgp neighbor fd52:d62e:8011:fffe:192:168:253:1 peer-group 'INTv6'
+set protocols bgp neighbor fd52:d62e:8011:fffe:192:168:253:2 peer-group 'INTv6'
+set protocols bgp neighbor fd52:d62e:8011:fffe:192:168:253:6 peer-group 'DAL13v6'
+set protocols bgp neighbor fd52:d62e:8011:fffe:192:168:253:7 peer-group 'DAL13v6'
+set protocols bgp neighbor fd52:d62e:8011:fffe:192:168:253:12 address-family ipv6-unicast route-map export 'BGP-BACKBONE-OUT'
+set protocols bgp neighbor fd52:d62e:8011:fffe:192:168:253:12 address-family ipv6-unicast route-map import 'BGP-BACKBONE-IN'
+set protocols bgp neighbor fd52:d62e:8011:fffe:192:168:253:12 address-family ipv6-unicast soft-reconfiguration inbound
+set protocols bgp neighbor fd52:d62e:8011:fffe:192:168:253:12 bfd
+set protocols bgp neighbor fd52:d62e:8011:fffe:192:168:253:12 ebgp-multihop '2'
+set protocols bgp neighbor fd52:d62e:8011:fffe:192:168:253:12 remote-as '4242420669'
+set protocols bgp neighbor fd52:d62e:8011:fffe:192:168:253:12 update-source 'dum0'
+set protocols bgp parameters confederation identifier '4242420696'
+set protocols bgp parameters confederation peers '4242420668'
+set protocols bgp parameters confederation peers '4242420669'
+set protocols bgp parameters distance global external '220'
+set protocols bgp parameters distance global internal '220'
+set protocols bgp parameters distance global local '220'
+set protocols bgp parameters graceful-restart
+set protocols bgp peer-group DAL13 address-family ipv4-unicast route-map export 'BGP-BACKBONE-OUT'
+set protocols bgp peer-group DAL13 address-family ipv4-unicast route-map import 'BGP-BACKBONE-IN'
+set protocols bgp peer-group DAL13 address-family ipv4-unicast soft-reconfiguration inbound
+set protocols bgp peer-group DAL13 bfd
+set protocols bgp peer-group DAL13 ebgp-multihop '2'
+set protocols bgp peer-group DAL13 remote-as '4242420668'
+set protocols bgp peer-group DAL13 update-source 'dum0'
+set protocols bgp peer-group DAL13v6 address-family ipv6-unicast route-map export 'BGP-BACKBONE-OUT'
+set protocols bgp peer-group DAL13v6 address-family ipv6-unicast route-map import 'BGP-BACKBONE-IN'
+set protocols bgp peer-group DAL13v6 address-family ipv6-unicast soft-reconfiguration inbound
+set protocols bgp peer-group DAL13v6 bfd
+set protocols bgp peer-group DAL13v6 ebgp-multihop '2'
+set protocols bgp peer-group DAL13v6 remote-as '4242420668'
+set protocols bgp peer-group DAL13v6 update-source 'dum0'
+set protocols bgp peer-group INT address-family ipv4-unicast default-originate
+set protocols bgp peer-group INT address-family ipv4-unicast soft-reconfiguration inbound
+set protocols bgp peer-group INT bfd
+set protocols bgp peer-group INT remote-as '4242420666'
+set protocols bgp peer-group INT update-source 'dum0'
+set protocols bgp peer-group INTv6 address-family ipv6-unicast default-originate
+set protocols bgp peer-group INTv6 address-family ipv6-unicast soft-reconfiguration inbound
+set protocols bgp peer-group INTv6 bfd
+set protocols bgp peer-group INTv6 remote-as '4242420666'
+set protocols bgp peer-group INTv6 update-source 'dum0'
+set protocols bgp system-as '4242420666'
+set service ntp allow-client address '0.0.0.0/0'
+set service ntp allow-client address '::/0'
+set service ntp server 0.pool.ntp.org
+set service ntp server 1.pool.ntp.org
+set service ntp server 2.pool.ntp.org
+set system config-management commit-revisions '200'
+set system conntrack modules ftp
+set system conntrack modules h323
+set system conntrack modules nfs
+set system conntrack modules pptp
+set system conntrack modules sip
+set system conntrack modules sqlnet
+set system conntrack modules tftp
+set system console device ttyS0 speed '115200'
+set system host-name 'vyos'
+set system login user vyos authentication encrypted-password '$6$2Ta6TWHd/U$NmrX0x9kexCimeOcYK1MfhMpITF9ELxHcaBU/znBq.X2ukQOj61fVI2UYP/xBzP4QtiTcdkgs7WOQMHWsRymO/'
+set system login user vyos authentication plaintext-password ''
+set system syslog global facility all level 'info'
+set system syslog global facility local7 level 'debug'
+set system time-zone 'Europe/Berlin'
diff --git a/smoketest/config-tests/bgp-big-as-cloud b/smoketest/config-tests/bgp-big-as-cloud
new file mode 100644
index 000000000..8de0cdb02
--- /dev/null
+++ b/smoketest/config-tests/bgp-big-as-cloud
@@ -0,0 +1,850 @@
+set firewall global-options all-ping 'enable'
+set firewall global-options broadcast-ping 'disable'
+set firewall global-options ip-src-route 'disable'
+set firewall global-options ipv6-receive-redirects 'disable'
+set firewall global-options ipv6-src-route 'disable'
+set firewall global-options log-martians 'enable'
+set firewall global-options receive-redirects 'disable'
+set firewall global-options send-redirects 'enable'
+set firewall global-options source-validation 'disable'
+set firewall global-options syn-cookies 'enable'
+set firewall global-options twa-hazards-protection 'disable'
+set firewall group address-group bgp-peers-4 address '192.0.68.3'
+set firewall group address-group bgp-peers-4 address '192.0.68.2'
+set firewall group address-group bgp-peers-4 address '192.0.176.193'
+set firewall group address-group bgp-peers-4 address '192.0.52.0-192.0.52.255'
+set firewall group address-group bgp-peers-4 address '192.0.53.0-192.0.53.255'
+set firewall group address-group bgp-peers-4 address '192.0.16.209'
+set firewall group address-group bgp-peers-4 address '192.0.192.0-192.0.192.255'
+set firewall group address-group bgp-peers-4 address '192.0.193.0-192.0.193.255'
+set firewall group address-group bgp-peers-4 address '192.0.194.0-192.0.194.255'
+set firewall group address-group bgp-peers-4 address '192.0.195.0-192.0.195.255'
+set firewall group address-group bgp-peers-4 address '192.0.196.0-192.0.196.255'
+set firewall group address-group bgp-peers-4 address '192.0.197.0-192.0.197.255'
+set firewall group address-group bgp-peers-4 address '192.0.198.0-192.0.198.255'
+set firewall group address-group bgp-peers-4 address '192.0.199.0-192.0.199.255'
+set firewall group address-group vrrp-peers-4 address '192.0.68.3'
+set firewall group address-group vrrp-peers-4 address '192.0.160.3'
+set firewall group address-group vrrp-peers-4 address '192.0.98.3'
+set firewall group address-group vrrp-peers-4 address '192.0.71.131'
+set firewall group address-group vrrp-peers-4 address '192.0.84.67'
+set firewall group address-group vrrp-peers-4 address '192.0.71.195'
+set firewall group address-group vrrp-peers-4 address '192.0.71.115'
+set firewall group address-group vrrp-peers-4 address '192.0.70.195'
+set firewall group address-group vrrp-peers-4 address '192.0.70.179'
+set firewall group address-group vrrp-peers-4 address '192.0.70.163'
+set firewall group address-group vrrp-peers-4 address '192.0.70.147'
+set firewall group address-group vrrp-peers-4 address '192.0.70.131'
+set firewall group address-group vrrp-peers-4 address '192.0.70.19'
+set firewall group address-group vrrp-peers-4 address '192.0.70.3'
+set firewall group address-group vrrp-peers-4 address '192.0.71.99'
+set firewall group address-group vrrp-peers-4 address '192.0.68.67'
+set firewall group address-group vrrp-peers-4 address '192.0.71.67'
+set firewall group address-group vrrp-peers-4 address '192.0.71.3'
+set firewall group address-group vrrp-peers-4 address '192.0.68.35'
+set firewall group address-group vrrp-peers-4 address '192.0.68.131'
+set firewall group address-group vrrp-peers-4 address '192.0.69.2'
+set firewall group address-group vrrp-peers-4 address '192.0.70.35'
+set firewall group address-group vrrp-peers-4 address '192.0.70.67'
+set firewall group ipv6-address-group bgp-peers-6 address '2001:db8:c::3'
+set firewall group ipv6-address-group bgp-peers-6 address '2001:db8:1000::2e9'
+set firewall group ipv6-address-group bgp-peers-6 address '2001:db8:24::fb'
+set firewall group ipv6-address-group bgp-peers-6 address '2001:db8:24::fc'
+set firewall group ipv6-address-group bgp-peers-6 address '2001:db8:24::fd'
+set firewall group ipv6-address-group bgp-peers-6 address '2001:db8:24::2e'
+set firewall group ipv6-address-group bgp-peers-6 address '2001:db8:24::3d'
+set firewall group ipv6-address-group bgp-peers-6 address '2001:db8:24::4a'
+set firewall group ipv6-address-group bgp-peers-6 address '2001:db8:24::5e'
+set firewall group ipv6-address-group bgp-peers-6 address '2001:db8:24::7'
+set firewall group ipv6-address-group bgp-peers-6 address '2001:db8:24::11'
+set firewall group ipv6-address-group bgp-peers-6 address '2001:db8:24::18'
+set firewall group ipv6-address-group bgp-peers-6 address '2001:db8:24::20'
+set firewall group ipv6-address-group bgp-peers-6 address '2001:db8:24::22'
+set firewall group ipv6-address-group bgp-peers-6 address '2001:db8:24::31'
+set firewall group ipv6-address-group bgp-peers-6 address '2001:db8:24::58'
+set firewall group ipv6-address-group bgp-peers-6 address '2001:db8:24::64'
+set firewall group ipv6-address-group bgp-peers-6 address '2001:db8:24::a5'
+set firewall group ipv6-address-group bgp-peers-6 address '2001:db8:24::aa'
+set firewall group ipv6-address-group bgp-peers-6 address '2001:db8:24::ab'
+set firewall group ipv6-address-group bgp-peers-6 address '2001:db8:24::b0'
+set firewall group ipv6-address-group bgp-peers-6 address '2001:db8:24::b3'
+set firewall group ipv6-address-group bgp-peers-6 address '2001:db8:24::bd'
+set firewall group ipv6-address-group bgp-peers-6 address '2001:db8:24::c'
+set firewall group ipv6-address-group bgp-peers-6 address '2001:db8:24::d2'
+set firewall group ipv6-address-group bgp-peers-6 address '2001:db8:24::d3'
+set firewall group ipv6-address-group bgp-peers-6 address '2001:db8:838::1'
+set firewall group ipv6-address-group bgp-peers-6 address '2001:db8::1a27:5051:c09d'
+set firewall group ipv6-address-group bgp-peers-6 address '2001:db8::1a27:5051:c19d'
+set firewall group ipv6-address-group bgp-peers-6 address '2001:db8::20ad:0:1'
+set firewall group ipv6-address-group bgp-peers-6 address '2001:db8::2306:0:1'
+set firewall group ipv6-address-group bgp-peers-6 address '2001:db8::2ca:0:1'
+set firewall group ipv6-address-group bgp-peers-6 address '2001:db8::2ca:0:2'
+set firewall group ipv6-address-group bgp-peers-6 address '2001:db8::2ca:0:3'
+set firewall group ipv6-address-group bgp-peers-6 address '2001:db8::2ca:0:4'
+set firewall group ipv6-address-group vrrp-peers-6 address 'fe80::fe89:15cf'
+set firewall group ipv6-network-group AS64512-6 network '2001::/29'
+set firewall group network-group AS64512-4 network '192.0.68.0/22'
+set firewall group network-group AS64512-4 network '192.0.98.0/24'
+set firewall group network-group AS64512-4 network '192.0.160.0/24'
+set firewall group network-group AS64512-4 network '192.0.84.0/22'
+set firewall ipv4 name management-to-local-4 default-action 'reject'
+set firewall ipv4 name management-to-local-4 default-log
+set firewall ipv4 name management-to-local-4 rule 500 action 'return'
+set firewall ipv4 name management-to-local-4 rule 500 protocol 'icmp'
+set firewall ipv4 name management-to-local-4 rule 501 action 'return'
+set firewall ipv4 name management-to-local-4 rule 501 destination port '22'
+set firewall ipv4 name management-to-local-4 rule 501 protocol 'tcp'
+set firewall ipv4 name management-to-local-4 rule 502 action 'return'
+set firewall ipv4 name management-to-local-4 rule 502 destination port 'snmp'
+set firewall ipv4 name management-to-local-4 rule 502 protocol 'udp'
+set firewall ipv4 name management-to-peers-4 default-action 'reject'
+set firewall ipv4 name management-to-peers-4 default-log
+set firewall ipv4 name management-to-servers-4 default-action 'reject'
+set firewall ipv4 name management-to-servers-4 default-log
+set firewall ipv4 name peers-to-local-4 default-action 'reject'
+set firewall ipv4 name peers-to-local-4 default-log
+set firewall ipv4 name peers-to-local-4 rule 500 action 'return'
+set firewall ipv4 name peers-to-local-4 rule 500 protocol 'icmp'
+set firewall ipv4 name peers-to-local-4 rule 501 action 'return'
+set firewall ipv4 name peers-to-local-4 rule 501 protocol 'vrrp'
+set firewall ipv4 name peers-to-local-4 rule 501 source group address-group 'vrrp-peers-4'
+set firewall ipv4 name peers-to-local-4 rule 502 action 'return'
+set firewall ipv4 name peers-to-local-4 rule 502 destination port 'bgp'
+set firewall ipv4 name peers-to-local-4 rule 502 protocol 'tcp'
+set firewall ipv4 name peers-to-local-4 rule 502 source group address-group 'bgp-peers-4'
+set firewall ipv4 name peers-to-local-4 rule 503 action 'return'
+set firewall ipv4 name peers-to-local-4 rule 503 protocol 'tcp'
+set firewall ipv4 name peers-to-local-4 rule 503 source group address-group 'bgp-peers-4'
+set firewall ipv4 name peers-to-local-4 rule 503 source port 'bgp'
+set firewall ipv4 name peers-to-management-4 default-action 'reject'
+set firewall ipv4 name peers-to-management-4 default-log
+set firewall ipv4 name peers-to-servers-4 default-action 'reject'
+set firewall ipv4 name peers-to-servers-4 default-log
+set firewall ipv4 name peers-to-servers-4 rule 9990 action 'reject'
+set firewall ipv4 name peers-to-servers-4 rule 9990 source group network-group 'AS64512-4'
+set firewall ipv4 name peers-to-servers-4 rule 9999 action 'return'
+set firewall ipv4 name peers-to-servers-4 rule 9999 destination group network-group 'AS64512-4'
+set firewall ipv4 name servers-to-local-4 default-action 'reject'
+set firewall ipv4 name servers-to-local-4 default-log
+set firewall ipv4 name servers-to-local-4 rule 500 action 'return'
+set firewall ipv4 name servers-to-local-4 rule 500 protocol 'icmp'
+set firewall ipv4 name servers-to-local-4 rule 501 action 'return'
+set firewall ipv4 name servers-to-local-4 rule 501 protocol 'vrrp'
+set firewall ipv4 name servers-to-local-4 rule 501 source group address-group 'vrrp-peers-4'
+set firewall ipv4 name servers-to-local-4 rule 511 action 'return'
+set firewall ipv4 name servers-to-local-4 rule 511 protocol 'tcp_udp'
+set firewall ipv4 name servers-to-local-4 rule 511 source port '53'
+set firewall ipv4 name servers-to-management-4 default-action 'reject'
+set firewall ipv4 name servers-to-management-4 default-log
+set firewall ipv4 name servers-to-peers-4 default-action 'reject'
+set firewall ipv4 name servers-to-peers-4 default-log
+set firewall ipv4 name servers-to-peers-4 rule 51 action 'return'
+set firewall ipv4 name servers-to-peers-4 rule 51 source group network-group 'AS64512-4'
+set firewall ipv6 name management-to-local-6 default-action 'reject'
+set firewall ipv6 name management-to-local-6 default-log
+set firewall ipv6 name management-to-peers-6 default-action 'reject'
+set firewall ipv6 name management-to-peers-6 default-log
+set firewall ipv6 name management-to-servers-6 default-action 'reject'
+set firewall ipv6 name management-to-servers-6 default-log
+set firewall ipv6 name peers-to-local-6 default-action 'reject'
+set firewall ipv6 name peers-to-local-6 default-log
+set firewall ipv6 name peers-to-local-6 rule 500 action 'return'
+set firewall ipv6 name peers-to-local-6 rule 500 protocol 'ipv6-icmp'
+set firewall ipv6 name peers-to-local-6 rule 501 action 'return'
+set firewall ipv6 name peers-to-local-6 rule 501 protocol 'vrrp'
+set firewall ipv6 name peers-to-local-6 rule 501 source group address-group 'vrrp-peers-6'
+set firewall ipv6 name peers-to-local-6 rule 502 action 'return'
+set firewall ipv6 name peers-to-local-6 rule 502 destination port 'bgp'
+set firewall ipv6 name peers-to-local-6 rule 502 protocol 'tcp'
+set firewall ipv6 name peers-to-local-6 rule 502 source group address-group 'bgp-peers-6'
+set firewall ipv6 name peers-to-local-6 rule 503 action 'return'
+set firewall ipv6 name peers-to-local-6 rule 503 protocol 'tcp'
+set firewall ipv6 name peers-to-local-6 rule 503 source group address-group 'bgp-peers-6'
+set firewall ipv6 name peers-to-local-6 rule 503 source port 'bgp'
+set firewall ipv6 name peers-to-management-6 default-action 'reject'
+set firewall ipv6 name peers-to-management-6 default-log
+set firewall ipv6 name peers-to-servers-6 default-action 'reject'
+set firewall ipv6 name peers-to-servers-6 default-log
+set firewall ipv6 name peers-to-servers-6 rule 9990 action 'reject'
+set firewall ipv6 name peers-to-servers-6 rule 9990 source group network-group 'AS64512-6'
+set firewall ipv6 name peers-to-servers-6 rule 9999 action 'return'
+set firewall ipv6 name peers-to-servers-6 rule 9999 destination group network-group 'AS64512-6'
+set firewall ipv6 name servers-to-local-6 default-action 'reject'
+set firewall ipv6 name servers-to-local-6 default-log
+set firewall ipv6 name servers-to-local-6 rule 500 action 'return'
+set firewall ipv6 name servers-to-local-6 rule 500 protocol 'ipv6-icmp'
+set firewall ipv6 name servers-to-local-6 rule 501 action 'return'
+set firewall ipv6 name servers-to-local-6 rule 501 protocol 'vrrp'
+set firewall ipv6 name servers-to-local-6 rule 501 source group address-group 'vrrp-peers-6'
+set firewall ipv6 name servers-to-local-6 rule 511 action 'return'
+set firewall ipv6 name servers-to-local-6 rule 511 protocol 'tcp_udp'
+set firewall ipv6 name servers-to-local-6 rule 511 source port '53'
+set firewall ipv6 name servers-to-management-6 default-action 'reject'
+set firewall ipv6 name servers-to-management-6 default-log
+set firewall ipv6 name servers-to-peers-6 default-action 'reject'
+set firewall ipv6 name servers-to-peers-6 default-log
+set firewall ipv6 name servers-to-peers-6 rule 51 action 'return'
+set firewall ipv6 name servers-to-peers-6 rule 51 source group network-group 'AS64512-6'
+set firewall zone local default-action 'drop'
+set firewall zone local from management firewall ipv6-name 'management-to-local-6'
+set firewall zone local from management firewall name 'management-to-local-4'
+set firewall zone local from peers firewall ipv6-name 'peers-to-local-6'
+set firewall zone local from peers firewall name 'peers-to-local-4'
+set firewall zone local from servers firewall ipv6-name 'servers-to-local-6'
+set firewall zone local from servers firewall name 'servers-to-local-4'
+set firewall zone local local-zone
+set firewall zone management default-action 'reject'
+set firewall zone management from peers firewall ipv6-name 'peers-to-management-6'
+set firewall zone management from peers firewall name 'peers-to-management-4'
+set firewall zone management from servers firewall ipv6-name 'servers-to-management-6'
+set firewall zone management from servers firewall name 'servers-to-management-4'
+set firewall zone management interface 'eth0'
+set firewall zone peers default-action 'reject'
+set firewall zone peers from management firewall ipv6-name 'management-to-peers-6'
+set firewall zone peers from management firewall name 'management-to-peers-4'
+set firewall zone peers from servers firewall ipv6-name 'servers-to-peers-6'
+set firewall zone peers from servers firewall name 'servers-to-peers-4'
+set firewall zone peers interface 'eth0.4088'
+set firewall zone peers interface 'eth0.4089'
+set firewall zone peers interface 'eth0.11'
+set firewall zone peers interface 'eth0.838'
+set firewall zone peers interface 'eth0.886'
+set firewall zone servers default-action 'reject'
+set firewall zone servers from management firewall ipv6-name 'management-to-servers-6'
+set firewall zone servers from management firewall name 'management-to-servers-4'
+set firewall zone servers from peers firewall ipv6-name 'peers-to-servers-6'
+set firewall zone servers from peers firewall name 'peers-to-servers-4'
+set firewall zone servers interface 'eth0.1001'
+set firewall zone servers interface 'eth0.105'
+set firewall zone servers interface 'eth0.102'
+set firewall zone servers interface 'eth0.1019'
+set firewall zone servers interface 'eth0.1014'
+set firewall zone servers interface 'eth0.1020'
+set firewall zone servers interface 'eth0.1018'
+set firewall zone servers interface 'eth0.1013'
+set firewall zone servers interface 'eth0.1012'
+set firewall zone servers interface 'eth0.1011'
+set firewall zone servers interface 'eth0.1010'
+set firewall zone servers interface 'eth0.1009'
+set firewall zone servers interface 'eth0.1006'
+set firewall zone servers interface 'eth0.1005'
+set firewall zone servers interface 'eth0.1017'
+set firewall zone servers interface 'eth0.1016'
+set firewall zone servers interface 'eth0.1002'
+set firewall zone servers interface 'eth0.1015'
+set firewall zone servers interface 'eth0.1003'
+set firewall zone servers interface 'eth0.1004'
+set firewall zone servers interface 'eth0.1007'
+set firewall zone servers interface 'eth0.1008'
+set high-availability vrrp group 11-4 address 192.0.68.1/27
+set high-availability vrrp group 11-4 interface 'eth0.11'
+set high-availability vrrp group 11-4 priority '200'
+set high-availability vrrp group 11-4 vrid '4'
+set high-availability vrrp group 11-6 address 2001:db8:c::1/64
+set high-availability vrrp group 11-6 interface 'eth0.11'
+set high-availability vrrp group 11-6 priority '200'
+set high-availability vrrp group 11-6 vrid '6'
+set high-availability vrrp group 102-4 address 192.0.98.1/24
+set high-availability vrrp group 102-4 interface 'eth0.102'
+set high-availability vrrp group 102-4 priority '200'
+set high-availability vrrp group 102-4 vrid '4'
+set high-availability vrrp group 102-6 address 2001:db8:0:102::1/64
+set high-availability vrrp group 102-6 interface 'eth0.102'
+set high-availability vrrp group 102-6 priority '200'
+set high-availability vrrp group 102-6 vrid '6'
+set high-availability vrrp group 105-4 address 192.0.160.1/24
+set high-availability vrrp group 105-4 interface 'eth0.105'
+set high-availability vrrp group 105-4 priority '200'
+set high-availability vrrp group 105-4 vrid '4'
+set high-availability vrrp group 105-6 address 2001:db8:0:105::1/64
+set high-availability vrrp group 105-6 interface 'eth0.105'
+set high-availability vrrp group 105-6 priority '200'
+set high-availability vrrp group 105-6 vrid '6'
+set high-availability vrrp group 1001-4 address 192.0.68.33/27
+set high-availability vrrp group 1001-4 interface 'eth0.1001'
+set high-availability vrrp group 1001-4 priority '200'
+set high-availability vrrp group 1001-4 vrid '4'
+set high-availability vrrp group 1001-6 address 2001:db8:0:1001::1/64
+set high-availability vrrp group 1001-6 interface 'eth0.1001'
+set high-availability vrrp group 1001-6 priority '200'
+set high-availability vrrp group 1001-6 vrid '6'
+set high-availability vrrp group 1002-4 address 192.0.68.65/26
+set high-availability vrrp group 1002-4 interface 'eth0.1002'
+set high-availability vrrp group 1002-4 priority '200'
+set high-availability vrrp group 1002-4 vrid '4'
+set high-availability vrrp group 1002-6 address 2001:db8:0:1002::1/64
+set high-availability vrrp group 1002-6 interface 'eth0.1002'
+set high-availability vrrp group 1002-6 priority '200'
+set high-availability vrrp group 1002-6 vrid '6'
+set high-availability vrrp group 1003-4 address 192.0.68.129/25
+set high-availability vrrp group 1003-4 interface 'eth0.1003'
+set high-availability vrrp group 1003-4 priority '200'
+set high-availability vrrp group 1003-4 vrid '4'
+set high-availability vrrp group 1003-6 address 2001:db8:0:1003::1/64
+set high-availability vrrp group 1003-6 interface 'eth0.1003'
+set high-availability vrrp group 1003-6 priority '200'
+set high-availability vrrp group 1003-6 vrid '6'
+set high-availability vrrp group 1004-4 address 192.0.69.1/24
+set high-availability vrrp group 1004-4 interface 'eth0.1004'
+set high-availability vrrp group 1004-4 priority '200'
+set high-availability vrrp group 1004-4 vrid '4'
+set high-availability vrrp group 1004-6 address 2001:db8:0:1004::1/64
+set high-availability vrrp group 1004-6 interface 'eth0.1004'
+set high-availability vrrp group 1004-6 priority '200'
+set high-availability vrrp group 1004-6 vrid '6'
+set high-availability vrrp group 1005-4 address 192.0.70.1/28
+set high-availability vrrp group 1005-4 interface 'eth0.1005'
+set high-availability vrrp group 1005-4 priority '200'
+set high-availability vrrp group 1005-4 vrid '4'
+set high-availability vrrp group 1005-6 address 2001:db8:0:1005::1/64
+set high-availability vrrp group 1005-6 interface 'eth0.1005'
+set high-availability vrrp group 1005-6 priority '200'
+set high-availability vrrp group 1005-6 vrid '6'
+set high-availability vrrp group 1006-4 address 192.0.70.17/28
+set high-availability vrrp group 1006-4 interface 'eth0.1006'
+set high-availability vrrp group 1006-4 priority '200'
+set high-availability vrrp group 1006-4 vrid '4'
+set high-availability vrrp group 1006-6 address 2001:db8:0:1006::1/64
+set high-availability vrrp group 1006-6 interface 'eth0.1006'
+set high-availability vrrp group 1006-6 priority '200'
+set high-availability vrrp group 1006-6 vrid '6'
+set high-availability vrrp group 1007-4 address 192.0.70.33/27
+set high-availability vrrp group 1007-4 interface 'eth0.1007'
+set high-availability vrrp group 1007-4 priority '200'
+set high-availability vrrp group 1007-4 vrid '4'
+set high-availability vrrp group 1007-6 address 2001:db8:0:1007::1/64
+set high-availability vrrp group 1007-6 interface 'eth0.1007'
+set high-availability vrrp group 1007-6 priority '200'
+set high-availability vrrp group 1007-6 vrid '6'
+set high-availability vrrp group 1008-4 address 192.0.70.65/26
+set high-availability vrrp group 1008-4 interface 'eth0.1008'
+set high-availability vrrp group 1008-4 priority '200'
+set high-availability vrrp group 1008-4 vrid '4'
+set high-availability vrrp group 1008-6 address 2001:db8:0:1008::1/64
+set high-availability vrrp group 1008-6 interface 'eth0.1008'
+set high-availability vrrp group 1008-6 priority '200'
+set high-availability vrrp group 1008-6 vrid '6'
+set high-availability vrrp group 1009-4 address 192.0.70.129/28
+set high-availability vrrp group 1009-4 interface 'eth0.1009'
+set high-availability vrrp group 1009-4 priority '200'
+set high-availability vrrp group 1009-4 vrid '4'
+set high-availability vrrp group 1009-6 address 2001:db8:0:1009::1/64
+set high-availability vrrp group 1009-6 interface 'eth0.1009'
+set high-availability vrrp group 1009-6 priority '200'
+set high-availability vrrp group 1009-6 vrid '6'
+set high-availability vrrp group 1010-4 address 192.0.70.145/28
+set high-availability vrrp group 1010-4 interface 'eth0.1010'
+set high-availability vrrp group 1010-4 priority '200'
+set high-availability vrrp group 1010-4 vrid '4'
+set high-availability vrrp group 1010-6 address 2001:db8:0:1010::1/64
+set high-availability vrrp group 1010-6 interface 'eth0.1010'
+set high-availability vrrp group 1010-6 priority '200'
+set high-availability vrrp group 1010-6 vrid '6'
+set high-availability vrrp group 1011-4 address 192.0.70.161/28
+set high-availability vrrp group 1011-4 interface 'eth0.1011'
+set high-availability vrrp group 1011-4 priority '200'
+set high-availability vrrp group 1011-4 vrid '4'
+set high-availability vrrp group 1011-6 address 2001:db8:0:1011::1/64
+set high-availability vrrp group 1011-6 interface 'eth0.1011'
+set high-availability vrrp group 1011-6 priority '200'
+set high-availability vrrp group 1011-6 vrid '6'
+set high-availability vrrp group 1012-4 address 192.0.70.177/28
+set high-availability vrrp group 1012-4 interface 'eth0.1012'
+set high-availability vrrp group 1012-4 priority '200'
+set high-availability vrrp group 1012-4 vrid '4'
+set high-availability vrrp group 1012-6 address 2001:db8:0:1012::1/64
+set high-availability vrrp group 1012-6 interface 'eth0.1012'
+set high-availability vrrp group 1012-6 priority '200'
+set high-availability vrrp group 1012-6 vrid '6'
+set high-availability vrrp group 1013-4 address 192.0.70.193/27
+set high-availability vrrp group 1013-4 interface 'eth0.1013'
+set high-availability vrrp group 1013-4 priority '200'
+set high-availability vrrp group 1013-4 vrid '4'
+set high-availability vrrp group 1013-6 address 2001:db8:0:1013::1/64
+set high-availability vrrp group 1013-6 interface 'eth0.1013'
+set high-availability vrrp group 1013-6 priority '200'
+set high-availability vrrp group 1013-6 vrid '6'
+set high-availability vrrp group 1014-4 address 192.0.84.65/26
+set high-availability vrrp group 1014-4 interface 'eth0.1014'
+set high-availability vrrp group 1014-4 priority '200'
+set high-availability vrrp group 1014-4 vrid '4'
+set high-availability vrrp group 1014-6 address 2001:db8:0:1014::1/64
+set high-availability vrrp group 1014-6 interface 'eth0.1014'
+set high-availability vrrp group 1014-6 priority '200'
+set high-availability vrrp group 1014-6 vrid '6'
+set high-availability vrrp group 1015-4 address 192.0.71.1/26
+set high-availability vrrp group 1015-4 interface 'eth0.1015'
+set high-availability vrrp group 1015-4 priority '200'
+set high-availability vrrp group 1015-4 vrid '4'
+set high-availability vrrp group 1015-6 address 2001:db8:0:1015::1/64
+set high-availability vrrp group 1015-6 interface 'eth0.1015'
+set high-availability vrrp group 1015-6 priority '200'
+set high-availability vrrp group 1015-6 vrid '6'
+set high-availability vrrp group 1016-4 address 192.0.71.65/27
+set high-availability vrrp group 1016-4 interface 'eth0.1016'
+set high-availability vrrp group 1016-4 priority '200'
+set high-availability vrrp group 1016-4 vrid '4'
+set high-availability vrrp group 1016-6 address 2001:db8:0:1016::1/64
+set high-availability vrrp group 1016-6 interface 'eth0.1016'
+set high-availability vrrp group 1016-6 priority '200'
+set high-availability vrrp group 1016-6 vrid '6'
+set high-availability vrrp group 1017-4 address 192.0.71.97/28
+set high-availability vrrp group 1017-4 interface 'eth0.1017'
+set high-availability vrrp group 1017-4 priority '200'
+set high-availability vrrp group 1017-4 vrid '4'
+set high-availability vrrp group 1017-6 address 2001:db8:0:1017::1/64
+set high-availability vrrp group 1017-6 interface 'eth0.1017'
+set high-availability vrrp group 1017-6 priority '200'
+set high-availability vrrp group 1017-6 vrid '6'
+set high-availability vrrp group 1018-4 address 192.0.71.113/28
+set high-availability vrrp group 1018-4 interface 'eth0.1018'
+set high-availability vrrp group 1018-4 priority '200'
+set high-availability vrrp group 1018-4 vrid '4'
+set high-availability vrrp group 1018-6 address 2001:db8:0:1018::1/64
+set high-availability vrrp group 1018-6 interface 'eth0.1018'
+set high-availability vrrp group 1018-6 priority '200'
+set high-availability vrrp group 1018-6 vrid '6'
+set high-availability vrrp group 1019-4 address 192.0.71.129/26
+set high-availability vrrp group 1019-4 interface 'eth0.1019'
+set high-availability vrrp group 1019-4 priority '200'
+set high-availability vrrp group 1019-4 vrid '4'
+set high-availability vrrp group 1019-6 address 2001:db8:0:1019::1/64
+set high-availability vrrp group 1019-6 interface 'eth0.1019'
+set high-availability vrrp group 1019-6 priority '200'
+set high-availability vrrp group 1019-6 vrid '6'
+set high-availability vrrp group 1020-4 address 192.0.71.193/26
+set high-availability vrrp group 1020-4 interface 'eth0.1020'
+set high-availability vrrp group 1020-4 priority '200'
+set high-availability vrrp group 1020-4 vrid '4'
+set high-availability vrrp group 1020-6 address 2001:db8:0:1020::1/64
+set high-availability vrrp group 1020-6 interface 'eth0.1020'
+set high-availability vrrp group 1020-6 priority '200'
+set high-availability vrrp group 1020-6 vrid '6'
+set interfaces ethernet eth0 address '192.0.0.11/16'
+set interfaces ethernet eth0 duplex 'auto'
+set interfaces ethernet eth0 offload gro
+set interfaces ethernet eth0 speed 'auto'
+set interfaces ethernet eth0 vif 11 address '192.0.68.2/27'
+set interfaces ethernet eth0 vif 11 address '2001:db8:c::2/64'
+set interfaces ethernet eth0 vif 102 address '192.0.98.2/24'
+set interfaces ethernet eth0 vif 102 address '2001:db8:0:102::2/64'
+set interfaces ethernet eth0 vif 105 address '192.0.160.2/24'
+set interfaces ethernet eth0 vif 105 address '2001:db8:0:105::2/64'
+set interfaces ethernet eth0 vif 838 address '192.0.16.210/30'
+set interfaces ethernet eth0 vif 838 address '2001:db8:838::2/64'
+set interfaces ethernet eth0 vif 886 address '192.0.193.224/21'
+set interfaces ethernet eth0 vif 886 address '2001:db8::3:669:0:1/64'
+set interfaces ethernet eth0 vif 1001 address '192.0.68.34/27'
+set interfaces ethernet eth0 vif 1001 address '2001:db8:0:1001::2/64'
+set interfaces ethernet eth0 vif 1002 address '192.0.68.66/26'
+set interfaces ethernet eth0 vif 1002 address '2001:db8:0:1002::2/64'
+set interfaces ethernet eth0 vif 1003 address '192.0.68.130/25'
+set interfaces ethernet eth0 vif 1003 address '2001:db8:0:1003::2/64'
+set interfaces ethernet eth0 vif 1004 address '192.0.69.2/24'
+set interfaces ethernet eth0 vif 1004 address '2001:db8:0:1004::2/64'
+set interfaces ethernet eth0 vif 1005 address '192.0.70.2/28'
+set interfaces ethernet eth0 vif 1005 address '2001:db8:0:1005::2/64'
+set interfaces ethernet eth0 vif 1006 address '192.0.70.18/28'
+set interfaces ethernet eth0 vif 1006 address '2001:db8:0:1006::2/64'
+set interfaces ethernet eth0 vif 1007 address '192.0.70.34/27'
+set interfaces ethernet eth0 vif 1007 address '2001:db8:0:1007::2/64'
+set interfaces ethernet eth0 vif 1008 address '192.0.70.66/26'
+set interfaces ethernet eth0 vif 1008 address '2001:db8:0:1008::2/64'
+set interfaces ethernet eth0 vif 1009 address '192.0.70.130/28'
+set interfaces ethernet eth0 vif 1009 address '2001:db8:0:1009::2/64'
+set interfaces ethernet eth0 vif 1010 address '192.0.70.146/28'
+set interfaces ethernet eth0 vif 1010 address '2001:db8:0:1010::2/64'
+set interfaces ethernet eth0 vif 1011 address '192.0.70.162/28'
+set interfaces ethernet eth0 vif 1011 address '2001:db8:0:1011::2/64'
+set interfaces ethernet eth0 vif 1012 address '192.0.70.178/28'
+set interfaces ethernet eth0 vif 1012 address '2001:db8:0:1012::2/64'
+set interfaces ethernet eth0 vif 1013 address '192.0.70.194/27'
+set interfaces ethernet eth0 vif 1013 address '2001:db8:0:1013::3/64'
+set interfaces ethernet eth0 vif 1014 address '192.0.84.66/26'
+set interfaces ethernet eth0 vif 1014 address '2001:db8:0:1014::2/64'
+set interfaces ethernet eth0 vif 1015 address '192.0.71.2/26'
+set interfaces ethernet eth0 vif 1015 address '2001:db8:0:1015::2/64'
+set interfaces ethernet eth0 vif 1016 address '192.0.71.66/27'
+set interfaces ethernet eth0 vif 1016 address '2001:db8:0:1016::2/64'
+set interfaces ethernet eth0 vif 1017 address '192.0.71.98/28'
+set interfaces ethernet eth0 vif 1017 address '2001:db8:0:1017::2/64'
+set interfaces ethernet eth0 vif 1018 address '192.0.71.114/28'
+set interfaces ethernet eth0 vif 1018 address '2001:db8:0:1018::2/64'
+set interfaces ethernet eth0 vif 1019 address '192.0.71.130/26'
+set interfaces ethernet eth0 vif 1019 address '2001:db8:0:1019::2/64'
+set interfaces ethernet eth0 vif 1020 address '192.0.71.194/26'
+set interfaces ethernet eth0 vif 1020 address '2001:db8:0:1020::2/64'
+set interfaces ethernet eth0 vif 4088 address '2001:db8:24::c7/64'
+set interfaces ethernet eth0 vif 4088 address '192.0.52.199/23'
+set interfaces ethernet eth0 vif 4089 address '192.0.176.194/30'
+set interfaces ethernet eth0 vif 4089 address '2001:db8:1000::2ea/126'
+set interfaces loopback lo
+set policy as-path-list AS64512 rule 10 action 'permit'
+set policy as-path-list AS64512 rule 10 regex '^$'
+set policy as-path-list AS64513-AS64514 rule 10 action 'permit'
+set policy as-path-list AS64513-AS64514 rule 10 regex '^64513 64514$'
+set policy prefix-list defaultV4 rule 10 action 'permit'
+set policy prefix-list defaultV4 rule 10 prefix '0.0.0.0/0'
+set policy prefix-list hostrouteV4 rule 10 action 'permit'
+set policy prefix-list hostrouteV4 rule 10 ge '32'
+set policy prefix-list hostrouteV4 rule 10 prefix '192.0.160.0/24'
+set policy prefix-list hostrouteV4 rule 20 action 'permit'
+set policy prefix-list hostrouteV4 rule 20 ge '32'
+set policy prefix-list hostrouteV4 rule 20 prefix '192.0.98.0/24'
+set policy prefix-list hostrouteV4 rule 30 action 'permit'
+set policy prefix-list hostrouteV4 rule 30 ge '32'
+set policy prefix-list hostrouteV4 rule 30 prefix '192.0.68.0/22'
+set policy prefix-list hostrouteV4 rule 40 action 'permit'
+set policy prefix-list hostrouteV4 rule 40 ge '32'
+set policy prefix-list hostrouteV4 rule 40 prefix '192.0.84.0/22'
+set policy prefix-list privateV4 rule 10 action 'permit'
+set policy prefix-list privateV4 rule 10 le '32'
+set policy prefix-list privateV4 rule 10 prefix '192.0.0.0/8'
+set policy prefix-list privateV4 rule 20 action 'permit'
+set policy prefix-list privateV4 rule 20 le '32'
+set policy prefix-list privateV4 rule 20 prefix '192.0.0.0/12'
+set policy prefix-list privateV4 rule 30 action 'permit'
+set policy prefix-list privateV4 rule 30 le '32'
+set policy prefix-list privateV4 rule 30 prefix '192.0.0.0/16'
+set policy prefix-list vyosV4 rule 10 action 'permit'
+set policy prefix-list vyosV4 rule 10 prefix '192.0.160.0/24'
+set policy prefix-list vyosV4 rule 20 action 'permit'
+set policy prefix-list vyosV4 rule 20 prefix '192.0.98.0/24'
+set policy prefix-list vyosV4 rule 30 action 'permit'
+set policy prefix-list vyosV4 rule 30 prefix '192.0.68.0/22'
+set policy prefix-list vyosV4 rule 40 action 'permit'
+set policy prefix-list vyosV4 rule 40 prefix '192.0.84.0/22'
+set policy prefix-list6 all6 rule 10 action 'permit'
+set policy prefix-list6 all6 rule 10 ge '4'
+set policy prefix-list6 all6 rule 10 prefix '2000::/3'
+set policy prefix-list6 hostrouteV6 rule 20 action 'permit'
+set policy prefix-list6 hostrouteV6 rule 20 ge '128'
+set policy prefix-list6 hostrouteV6 rule 20 prefix '2001:db8::/29'
+set policy prefix-list6 privateV6 rule 10 action 'permit'
+set policy prefix-list6 privateV6 rule 10 prefix 'fc00::/7'
+set policy prefix-list6 vyosV6 rule 20 action 'permit'
+set policy prefix-list6 vyosV6 rule 20 prefix '2001:db8::/29'
+set policy route-map ExportRouteMap rule 5 action 'permit'
+set policy route-map ExportRouteMap rule 5 match as-path 'AS64512'
+set policy route-map ExportRouteMap rule 5 match ip address prefix-list 'hostrouteV4'
+set policy route-map ExportRouteMap rule 5 set community replace '65000:666'
+set policy route-map ExportRouteMap rule 10 action 'permit'
+set policy route-map ExportRouteMap rule 10 match as-path 'AS64512'
+set policy route-map ExportRouteMap rule 10 match ip address prefix-list 'vyosV4'
+set policy route-map ExportRouteMap rule 15 action 'permit'
+set policy route-map ExportRouteMap rule 15 match as-path 'AS64512'
+set policy route-map ExportRouteMap rule 15 match ipv6 address prefix-list 'hostrouteV6'
+set policy route-map ExportRouteMap rule 15 set community replace '65000:666'
+set policy route-map ExportRouteMap rule 20 action 'permit'
+set policy route-map ExportRouteMap rule 20 match as-path 'AS64512'
+set policy route-map ExportRouteMap rule 20 match ipv6 address prefix-list 'vyosV6'
+set policy route-map ExportRouteMap rule 100 action 'deny'
+set policy route-map ExportRouteMapAS64513 rule 5 action 'permit'
+set policy route-map ExportRouteMapAS64513 rule 5 match as-path 'AS64512'
+set policy route-map ExportRouteMapAS64513 rule 5 match ip address prefix-list 'hostrouteV4'
+set policy route-map ExportRouteMapAS64513 rule 5 set community replace '64513:666'
+set policy route-map ExportRouteMapAS64513 rule 10 action 'permit'
+set policy route-map ExportRouteMapAS64513 rule 10 match as-path 'AS64512'
+set policy route-map ExportRouteMapAS64513 rule 10 match ip address prefix-list 'vyosV4'
+set policy route-map ExportRouteMapAS64513 rule 15 action 'permit'
+set policy route-map ExportRouteMapAS64513 rule 15 match as-path 'AS64512'
+set policy route-map ExportRouteMapAS64513 rule 15 match ipv6 address prefix-list 'hostrouteV6'
+set policy route-map ExportRouteMapAS64513 rule 15 set community replace '64513:666'
+set policy route-map ExportRouteMapAS64513 rule 20 action 'permit'
+set policy route-map ExportRouteMapAS64513 rule 20 match as-path 'AS64512'
+set policy route-map ExportRouteMapAS64513 rule 20 match ipv6 address prefix-list 'vyosV6'
+set policy route-map ExportRouteMapAS64513 rule 100 action 'deny'
+set policy route-map ExportRouteMapAS64515 rule 10 action 'permit'
+set policy route-map ExportRouteMapAS64515 rule 10 match ipv6 address prefix-list 'all6'
+set policy route-map ExportRouteMapAS64515 rule 20 action 'deny'
+set policy route-map ExportRouteMapAS64515 rule 20 match ip address prefix-list 'defaultV4'
+set policy route-map ExportRouteMapAS64515 rule 100 action 'deny'
+set policy route-map ExportRouteMapAS64516 rule 5 action 'permit'
+set policy route-map ExportRouteMapAS64516 rule 5 match as-path 'AS64512'
+set policy route-map ExportRouteMapAS64516 rule 5 match ip address prefix-list 'hostrouteV4'
+set policy route-map ExportRouteMapAS64516 rule 5 set community replace '65000:666'
+set policy route-map ExportRouteMapAS64516 rule 10 action 'permit'
+set policy route-map ExportRouteMapAS64516 rule 10 match as-path 'AS64512'
+set policy route-map ExportRouteMapAS64516 rule 10 match ip address prefix-list 'vyosV4'
+set policy route-map ExportRouteMapAS64516 rule 15 action 'permit'
+set policy route-map ExportRouteMapAS64516 rule 15 match as-path 'AS64512'
+set policy route-map ExportRouteMapAS64516 rule 15 match ipv6 address prefix-list 'hostrouteV6'
+set policy route-map ExportRouteMapAS64516 rule 15 set community replace '65000:666'
+set policy route-map ExportRouteMapAS64516 rule 20 action 'permit'
+set policy route-map ExportRouteMapAS64516 rule 20 match as-path 'AS64512'
+set policy route-map ExportRouteMapAS64516 rule 20 match ipv6 address prefix-list 'vyosV6'
+set policy route-map ExportRouteMapAS64516 rule 20 set as-path exclude '100 200 300'
+set policy route-map ExportRouteMapAS64516 rule 20 set as-path prepend '64512 64512 64512'
+set policy route-map ExportRouteMapAS64516 rule 100 action 'deny'
+set policy route-map ExportRouteMapAS64517 rule 5 action 'permit'
+set policy route-map ExportRouteMapAS64517 rule 5 match as-path 'AS64512'
+set policy route-map ExportRouteMapAS64517 rule 5 match ip address prefix-list 'hostrouteV4'
+set policy route-map ExportRouteMapAS64517 rule 5 set community replace '64517:666'
+set policy route-map ExportRouteMapAS64517 rule 10 action 'permit'
+set policy route-map ExportRouteMapAS64517 rule 10 match as-path 'AS64512'
+set policy route-map ExportRouteMapAS64517 rule 10 match ip address prefix-list 'vyosV4'
+set policy route-map ExportRouteMapAS64517 rule 15 action 'permit'
+set policy route-map ExportRouteMapAS64517 rule 15 match as-path 'AS64512'
+set policy route-map ExportRouteMapAS64517 rule 15 match ipv6 address prefix-list 'hostrouteV6'
+set policy route-map ExportRouteMapAS64517 rule 15 set community replace '64517:666'
+set policy route-map ExportRouteMapAS64517 rule 20 action 'permit'
+set policy route-map ExportRouteMapAS64517 rule 20 match as-path 'AS64512'
+set policy route-map ExportRouteMapAS64517 rule 20 match ipv6 address prefix-list 'vyosV6'
+set policy route-map ExportRouteMapAS64517 rule 100 action 'deny'
+set policy route-map ImportRouteMap rule 10 action 'deny'
+set policy route-map ImportRouteMap rule 10 match ip address prefix-list 'privateV4'
+set policy route-map ImportRouteMap rule 15 action 'deny'
+set policy route-map ImportRouteMap rule 15 match ipv6 address prefix-list 'privateV6'
+set policy route-map ImportRouteMap rule 20 action 'deny'
+set policy route-map ImportRouteMap rule 20 match ip address prefix-list 'vyosV4'
+set policy route-map ImportRouteMap rule 30 action 'deny'
+set policy route-map ImportRouteMap rule 30 match ipv6 address prefix-list 'vyosV6'
+set policy route-map ImportRouteMap rule 40 action 'deny'
+set policy route-map ImportRouteMap rule 40 match as-path 'AS64512'
+set policy route-map ImportRouteMap rule 50 action 'permit'
+set policy route-map ImportRouteMap rule 50 match as-path 'AS64513-AS64514'
+set policy route-map ImportRouteMap rule 50 set weight '10001'
+set policy route-map ImportRouteMap rule 65535 action 'permit'
+set protocols bgp address-family ipv4-unicast maximum-paths ebgp '8'
+set protocols bgp address-family ipv4-unicast maximum-paths ibgp '16'
+set protocols bgp address-family ipv4-unicast network 192.0.68.0/22
+set protocols bgp address-family ipv4-unicast network 192.0.84.0/22
+set protocols bgp address-family ipv4-unicast network 192.0.98.0/24
+set protocols bgp address-family ipv4-unicast network 192.0.160.0/24
+set protocols bgp address-family ipv4-unicast redistribute static route-map 'ExportRouteMap'
+set protocols bgp address-family ipv6-unicast network 2001:db8::/29
+set protocols bgp address-family ipv6-unicast redistribute static route-map 'ExportRouteMap'
+set protocols bgp neighbor 192.0.16.209 address-family ipv4-unicast route-map export 'ExportRouteMapAS64516'
+set protocols bgp neighbor 192.0.16.209 address-family ipv4-unicast route-map import 'ImportRouteMap'
+set protocols bgp neighbor 192.0.16.209 remote-as '64501'
+set protocols bgp neighbor 192.0.52.12 address-family ipv4-unicast maximum-prefix '300'
+set protocols bgp neighbor 192.0.52.12 address-family ipv4-unicast route-map export 'ExportRouteMap'
+set protocols bgp neighbor 192.0.52.12 address-family ipv4-unicast route-map import 'ImportRouteMap'
+set protocols bgp neighbor 192.0.52.12 remote-as '64511'
+set protocols bgp neighbor 192.0.52.17 address-family ipv4-unicast maximum-prefix '75'
+set protocols bgp neighbor 192.0.52.17 address-family ipv4-unicast route-map export 'ExportRouteMap'
+set protocols bgp neighbor 192.0.52.17 address-family ipv4-unicast route-map import 'ImportRouteMap'
+set protocols bgp neighbor 192.0.52.17 password 'vyosvyos'
+set protocols bgp neighbor 192.0.52.17 remote-as '64512'
+set protocols bgp neighbor 192.0.52.24 address-family ipv4-unicast maximum-prefix '300'
+set protocols bgp neighbor 192.0.52.24 address-family ipv4-unicast route-map export 'ExportRouteMap'
+set protocols bgp neighbor 192.0.52.24 address-family ipv4-unicast route-map import 'ImportRouteMap'
+set protocols bgp neighbor 192.0.52.24 remote-as '64513'
+set protocols bgp neighbor 192.0.52.32 address-family ipv4-unicast maximum-prefix '50'
+set protocols bgp neighbor 192.0.52.32 address-family ipv4-unicast route-map export 'ExportRouteMap'
+set protocols bgp neighbor 192.0.52.32 address-family ipv4-unicast route-map import 'ImportRouteMap'
+set protocols bgp neighbor 192.0.52.32 password 'vyosfoooo'
+set protocols bgp neighbor 192.0.52.32 remote-as '64514'
+set protocols bgp neighbor 192.0.52.34 address-family ipv4-unicast maximum-prefix '10'
+set protocols bgp neighbor 192.0.52.34 address-family ipv4-unicast route-map export 'ExportRouteMap'
+set protocols bgp neighbor 192.0.52.34 address-family ipv4-unicast route-map import 'ImportRouteMap'
+set protocols bgp neighbor 192.0.52.34 remote-as '64515'
+set protocols bgp neighbor 192.0.52.46 address-family ipv4-unicast maximum-prefix '10'
+set protocols bgp neighbor 192.0.52.46 address-family ipv4-unicast route-map export 'ExportRouteMap'
+set protocols bgp neighbor 192.0.52.46 address-family ipv4-unicast route-map import 'ImportRouteMap'
+set protocols bgp neighbor 192.0.52.46 remote-as '64516'
+set protocols bgp neighbor 192.0.52.49 address-family ipv4-unicast maximum-prefix '75'
+set protocols bgp neighbor 192.0.52.49 address-family ipv4-unicast route-map export 'ExportRouteMap'
+set protocols bgp neighbor 192.0.52.49 address-family ipv4-unicast route-map import 'ImportRouteMap'
+set protocols bgp neighbor 192.0.52.49 password 'secret'
+set protocols bgp neighbor 192.0.52.49 remote-as '64517'
+set protocols bgp neighbor 192.0.52.74 address-family ipv4-unicast maximum-prefix '15000'
+set protocols bgp neighbor 192.0.52.74 address-family ipv4-unicast route-map export 'ExportRouteMap'
+set protocols bgp neighbor 192.0.52.74 address-family ipv4-unicast route-map import 'ImportRouteMap'
+set protocols bgp neighbor 192.0.52.74 password 'secretvyos'
+set protocols bgp neighbor 192.0.52.74 remote-as '64518'
+set protocols bgp neighbor 192.0.52.94 address-family ipv4-unicast maximum-prefix '250'
+set protocols bgp neighbor 192.0.52.94 address-family ipv4-unicast route-map export 'ExportRouteMap'
+set protocols bgp neighbor 192.0.52.94 address-family ipv4-unicast route-map import 'ImportRouteMap'
+set protocols bgp neighbor 192.0.52.94 remote-as '64519'
+set protocols bgp neighbor 192.0.52.100 address-family ipv4-unicast maximum-prefix '50'
+set protocols bgp neighbor 192.0.52.100 address-family ipv4-unicast route-map export 'ExportRouteMap'
+set protocols bgp neighbor 192.0.52.100 address-family ipv4-unicast route-map import 'ImportRouteMap'
+set protocols bgp neighbor 192.0.52.100 remote-as '64520'
+set protocols bgp neighbor 192.0.52.119 address-family ipv4-unicast maximum-prefix '30'
+set protocols bgp neighbor 192.0.52.119 address-family ipv4-unicast route-map export 'ExportRouteMap'
+set protocols bgp neighbor 192.0.52.119 address-family ipv4-unicast route-map import 'ImportRouteMap'
+set protocols bgp neighbor 192.0.52.119 remote-as '64521'
+set protocols bgp neighbor 192.0.52.165 address-family ipv4-unicast maximum-prefix '50'
+set protocols bgp neighbor 192.0.52.165 address-family ipv4-unicast route-map export 'ExportRouteMap'
+set protocols bgp neighbor 192.0.52.165 address-family ipv4-unicast route-map import 'ImportRouteMap'
+set protocols bgp neighbor 192.0.52.165 remote-as '64522'
+set protocols bgp neighbor 192.0.52.170 address-family ipv4-unicast maximum-prefix '150000'
+set protocols bgp neighbor 192.0.52.170 address-family ipv4-unicast route-map export 'ExportRouteMap'
+set protocols bgp neighbor 192.0.52.170 address-family ipv4-unicast route-map import 'ImportRouteMap'
+set protocols bgp neighbor 192.0.52.170 remote-as '64523'
+set protocols bgp neighbor 192.0.52.171 address-family ipv4-unicast maximum-prefix '10000'
+set protocols bgp neighbor 192.0.52.171 address-family ipv4-unicast route-map export 'ExportRouteMap'
+set protocols bgp neighbor 192.0.52.171 address-family ipv4-unicast route-map import 'ImportRouteMap'
+set protocols bgp neighbor 192.0.52.171 remote-as '64524'
+set protocols bgp neighbor 192.0.52.179 address-family ipv4-unicast maximum-prefix '20'
+set protocols bgp neighbor 192.0.52.179 address-family ipv4-unicast route-map export 'ExportRouteMap'
+set protocols bgp neighbor 192.0.52.179 address-family ipv4-unicast route-map import 'ImportRouteMap'
+set protocols bgp neighbor 192.0.52.179 remote-as '64525'
+set protocols bgp neighbor 192.0.52.189 address-family ipv4-unicast maximum-prefix '1000'
+set protocols bgp neighbor 192.0.52.189 address-family ipv4-unicast route-map export 'ExportRouteMap'
+set protocols bgp neighbor 192.0.52.189 address-family ipv4-unicast route-map import 'ImportRouteMap'
+set protocols bgp neighbor 192.0.52.189 remote-as '64526'
+set protocols bgp neighbor 192.0.52.210 address-family ipv4-unicast maximum-prefix '15'
+set protocols bgp neighbor 192.0.52.210 address-family ipv4-unicast route-map export 'ExportRouteMap'
+set protocols bgp neighbor 192.0.52.210 address-family ipv4-unicast route-map import 'ImportRouteMap'
+set protocols bgp neighbor 192.0.52.210 remote-as '64527'
+set protocols bgp neighbor 192.0.52.211 address-family ipv4-unicast maximum-prefix '15'
+set protocols bgp neighbor 192.0.52.211 address-family ipv4-unicast route-map export 'ExportRouteMap'
+set protocols bgp neighbor 192.0.52.211 address-family ipv4-unicast route-map import 'ImportRouteMap'
+set protocols bgp neighbor 192.0.52.211 remote-as '64528'
+set protocols bgp neighbor 192.0.52.251 address-family ipv4-unicast route-map export 'ExportRouteMap'
+set protocols bgp neighbor 192.0.52.251 address-family ipv4-unicast route-map import 'ImportRouteMap'
+set protocols bgp neighbor 192.0.52.251 address-family ipv4-unicast weight '1010'
+set protocols bgp neighbor 192.0.52.251 remote-as '64529'
+set protocols bgp neighbor 192.0.52.252 address-family ipv4-unicast route-map export 'ExportRouteMap'
+set protocols bgp neighbor 192.0.52.252 address-family ipv4-unicast weight '1010'
+set protocols bgp neighbor 192.0.52.252 remote-as '64530'
+set protocols bgp neighbor 192.0.52.253 address-family ipv4-unicast route-map export 'ExportRouteMapAS64515'
+set protocols bgp neighbor 192.0.52.253 address-family ipv4-unicast route-map import 'ImportRouteMap'
+set protocols bgp neighbor 192.0.52.253 passive
+set protocols bgp neighbor 192.0.52.253 remote-as '64531'
+set protocols bgp neighbor 192.0.68.3 address-family ipv4-unicast nexthop-self
+set protocols bgp neighbor 192.0.68.3 address-family ipv4-unicast soft-reconfiguration inbound
+set protocols bgp neighbor 192.0.68.3 remote-as '64532'
+set protocols bgp neighbor 192.0.68.3 update-source '192.0.68.2'
+set protocols bgp neighbor 192.0.176.193 address-family ipv4-unicast route-map export 'ExportRouteMapAS64516'
+set protocols bgp neighbor 192.0.176.193 address-family ipv4-unicast route-map import 'ImportRouteMap'
+set protocols bgp neighbor 192.0.176.193 remote-as '64510'
+set protocols bgp neighbor 192.0.192.6 address-family ipv4-unicast maximum-prefix '100'
+set protocols bgp neighbor 192.0.192.6 address-family ipv4-unicast route-map export 'ExportRouteMap'
+set protocols bgp neighbor 192.0.192.6 address-family ipv4-unicast route-map import 'ImportRouteMap'
+set protocols bgp neighbor 192.0.192.6 remote-as '64502'
+set protocols bgp neighbor 192.0.192.157 address-family ipv4-unicast maximum-prefix '350000'
+set protocols bgp neighbor 192.0.192.157 address-family ipv4-unicast route-map export 'ExportRouteMap'
+set protocols bgp neighbor 192.0.192.157 address-family ipv4-unicast route-map import 'ImportRouteMap'
+set protocols bgp neighbor 192.0.192.157 remote-as '64503'
+set protocols bgp neighbor 192.0.192.228 address-family ipv4-unicast maximum-prefix '10000'
+set protocols bgp neighbor 192.0.192.228 address-family ipv4-unicast route-map export 'ExportRouteMap'
+set protocols bgp neighbor 192.0.192.228 address-family ipv4-unicast route-map import 'ImportRouteMap'
+set protocols bgp neighbor 192.0.192.228 remote-as '64504'
+set protocols bgp neighbor 192.0.193.157 address-family ipv4-unicast maximum-prefix '350000'
+set protocols bgp neighbor 192.0.193.157 address-family ipv4-unicast route-map export 'ExportRouteMap'
+set protocols bgp neighbor 192.0.193.157 address-family ipv4-unicast route-map import 'ImportRouteMap'
+set protocols bgp neighbor 192.0.193.157 remote-as '64505'
+set protocols bgp neighbor 192.0.193.202 address-family ipv4-unicast maximum-prefix '10000'
+set protocols bgp neighbor 192.0.193.202 address-family ipv4-unicast route-map export 'ExportRouteMap'
+set protocols bgp neighbor 192.0.193.202 address-family ipv4-unicast route-map import 'ImportRouteMap'
+set protocols bgp neighbor 192.0.193.202 remote-as '64506'
+set protocols bgp neighbor 192.0.193.223 address-family ipv4-unicast maximum-prefix '10000'
+set protocols bgp neighbor 192.0.193.223 address-family ipv4-unicast route-map export 'ExportRouteMap'
+set protocols bgp neighbor 192.0.193.223 address-family ipv4-unicast route-map import 'ImportRouteMap'
+set protocols bgp neighbor 192.0.193.223 remote-as '64507'
+set protocols bgp neighbor 192.0.194.161 address-family ipv4-unicast maximum-prefix '10000'
+set protocols bgp neighbor 192.0.194.161 address-family ipv4-unicast route-map export 'ExportRouteMap'
+set protocols bgp neighbor 192.0.194.161 address-family ipv4-unicast route-map import 'ImportRouteMap'
+set protocols bgp neighbor 192.0.194.161 remote-as '64508'
+set protocols bgp neighbor 192.0.194.171 address-family ipv4-unicast maximum-prefix '10000'
+set protocols bgp neighbor 192.0.194.171 address-family ipv4-unicast route-map export 'ExportRouteMap'
+set protocols bgp neighbor 192.0.194.171 address-family ipv4-unicast route-map import 'ImportRouteMap'
+set protocols bgp neighbor 192.0.194.171 remote-as '64509'
+set protocols bgp neighbor 2001:db8:24::2e address-family ipv6-unicast maximum-prefix '5'
+set protocols bgp neighbor 2001:db8:24::2e address-family ipv6-unicast route-map export 'ExportRouteMap'
+set protocols bgp neighbor 2001:db8:24::2e address-family ipv6-unicast route-map import 'ImportRouteMap'
+set protocols bgp neighbor 2001:db8:24::2e password 'vyossecret'
+set protocols bgp neighbor 2001:db8:24::2e remote-as '64535'
+set protocols bgp neighbor 2001:db8:24::4a address-family ipv6-unicast maximum-prefix '1000'
+set protocols bgp neighbor 2001:db8:24::4a address-family ipv6-unicast route-map export 'ExportRouteMap'
+set protocols bgp neighbor 2001:db8:24::4a address-family ipv6-unicast route-map import 'ImportRouteMap'
+set protocols bgp neighbor 2001:db8:24::4a remote-as '64536'
+set protocols bgp neighbor 2001:db8:24::5e address-family ipv6-unicast maximum-prefix '200'
+set protocols bgp neighbor 2001:db8:24::5e address-family ipv6-unicast route-map export 'ExportRouteMap'
+set protocols bgp neighbor 2001:db8:24::5e address-family ipv6-unicast route-map import 'ImportRouteMap'
+set protocols bgp neighbor 2001:db8:24::5e remote-as '64537'
+set protocols bgp neighbor 2001:db8:24::11 address-family ipv6-unicast maximum-prefix '20'
+set protocols bgp neighbor 2001:db8:24::11 address-family ipv6-unicast route-map export 'ExportRouteMap'
+set protocols bgp neighbor 2001:db8:24::11 address-family ipv6-unicast route-map import 'ImportRouteMap'
+set protocols bgp neighbor 2001:db8:24::11 remote-as '64538'
+set protocols bgp neighbor 2001:db8:24::18 address-family ipv6-unicast maximum-prefix '300'
+set protocols bgp neighbor 2001:db8:24::18 address-family ipv6-unicast route-map export 'ExportRouteMap'
+set protocols bgp neighbor 2001:db8:24::18 address-family ipv6-unicast route-map import 'ImportRouteMap'
+set protocols bgp neighbor 2001:db8:24::18 remote-as '64539'
+set protocols bgp neighbor 2001:db8:24::20 address-family ipv6-unicast maximum-prefix '10'
+set protocols bgp neighbor 2001:db8:24::20 address-family ipv6-unicast route-map export 'ExportRouteMap'
+set protocols bgp neighbor 2001:db8:24::20 address-family ipv6-unicast route-map import 'ImportRouteMap'
+set protocols bgp neighbor 2001:db8:24::20 remote-as '64540'
+set protocols bgp neighbor 2001:db8:24::22 address-family ipv6-unicast maximum-prefix '5'
+set protocols bgp neighbor 2001:db8:24::22 address-family ipv6-unicast route-map export 'ExportRouteMap'
+set protocols bgp neighbor 2001:db8:24::22 address-family ipv6-unicast route-map import 'ImportRouteMap'
+set protocols bgp neighbor 2001:db8:24::22 remote-as '64541'
+set protocols bgp neighbor 2001:db8:24::31 address-family ipv6-unicast maximum-prefix '20'
+set protocols bgp neighbor 2001:db8:24::31 address-family ipv6-unicast route-map export 'ExportRouteMap'
+set protocols bgp neighbor 2001:db8:24::31 address-family ipv6-unicast route-map import 'ImportRouteMap'
+set protocols bgp neighbor 2001:db8:24::31 remote-as '64542'
+set protocols bgp neighbor 2001:db8:24::58 address-family ipv6-unicast maximum-prefix '15'
+set protocols bgp neighbor 2001:db8:24::58 address-family ipv6-unicast route-map export 'ExportRouteMap'
+set protocols bgp neighbor 2001:db8:24::58 address-family ipv6-unicast route-map import 'ImportRouteMap'
+set protocols bgp neighbor 2001:db8:24::58 remote-as '64543'
+set protocols bgp neighbor 2001:db8:24::64 address-family ipv6-unicast maximum-prefix '10'
+set protocols bgp neighbor 2001:db8:24::64 address-family ipv6-unicast route-map export 'ExportRouteMap'
+set protocols bgp neighbor 2001:db8:24::64 address-family ipv6-unicast route-map import 'ImportRouteMap'
+set protocols bgp neighbor 2001:db8:24::64 password 'geheim'
+set protocols bgp neighbor 2001:db8:24::64 remote-as '64544'
+set protocols bgp neighbor 2001:db8:24::a5 address-family ipv6-unicast maximum-prefix '10'
+set protocols bgp neighbor 2001:db8:24::a5 address-family ipv6-unicast route-map export 'ExportRouteMap'
+set protocols bgp neighbor 2001:db8:24::a5 address-family ipv6-unicast route-map import 'ImportRouteMap'
+set protocols bgp neighbor 2001:db8:24::a5 remote-as '64545'
+set protocols bgp neighbor 2001:db8:24::aa address-family ipv6-unicast route-map export 'ExportRouteMap'
+set protocols bgp neighbor 2001:db8:24::aa address-family ipv6-unicast route-map import 'ImportRouteMap'
+set protocols bgp neighbor 2001:db8:24::aa remote-as '64546'
+set protocols bgp neighbor 2001:db8:24::ab address-family ipv6-unicast maximum-prefix '1800'
+set protocols bgp neighbor 2001:db8:24::ab address-family ipv6-unicast route-map export 'ExportRouteMap'
+set protocols bgp neighbor 2001:db8:24::ab address-family ipv6-unicast route-map import 'ImportRouteMap'
+set protocols bgp neighbor 2001:db8:24::ab remote-as '64547'
+set protocols bgp neighbor 2001:db8:24::b0 address-family ipv6-unicast maximum-prefix '5'
+set protocols bgp neighbor 2001:db8:24::b0 address-family ipv6-unicast route-map export 'ExportRouteMap'
+set protocols bgp neighbor 2001:db8:24::b0 address-family ipv6-unicast route-map import 'ImportRouteMap'
+set protocols bgp neighbor 2001:db8:24::b0 password 'secret123'
+set protocols bgp neighbor 2001:db8:24::b0 remote-as '64548'
+set protocols bgp neighbor 2001:db8:838::1 address-family ipv6-unicast route-map export 'ExportRouteMapAS64516'
+set protocols bgp neighbor 2001:db8:838::1 address-family ipv6-unicast route-map import 'ImportRouteMap'
+set protocols bgp neighbor 2001:db8:838::1 remote-as '64533'
+set protocols bgp neighbor 2001:db8:c::3 address-family ipv6-unicast nexthop-self
+set protocols bgp neighbor 2001:db8:c::3 address-family ipv6-unicast soft-reconfiguration inbound
+set protocols bgp neighbor 2001:db8:c::3 remote-as '64534'
+set protocols bgp neighbor 2001:db8:c::3 update-source '2001:db8:c::2'
+set protocols bgp parameters log-neighbor-changes
+set protocols bgp parameters router-id '192.0.68.2'
+set protocols bgp system-as '64500'
+set protocols static route 192.0.68.0/22 blackhole
+set protocols static route 192.0.84.0/22 blackhole
+set protocols static route 192.0.98.0/24 blackhole
+set protocols static route 192.0.160.0/24 blackhole
+set protocols static route6 2001:db8::/29 blackhole
+set service ntp allow-client address '0.0.0.0/0'
+set service ntp allow-client address '::/0'
+set service ntp server 0.pool.ntp.org
+set service ntp server 1.pool.ntp.org
+set service ntp server 2.pool.ntp.org
+set system config-management commit-revisions '100'
+set system conntrack modules ftp
+set system conntrack modules h323
+set system conntrack modules nfs
+set system conntrack modules pptp
+set system conntrack modules sip
+set system conntrack modules sqlnet
+set system conntrack modules tftp
+set system console device ttyS0 speed '115200'
+set system flow-accounting disable-imt
+set system flow-accounting interface 'eth0.4088'
+set system flow-accounting interface 'eth0.4089'
+set system flow-accounting netflow engine-id '1'
+set system flow-accounting netflow server 192.0.2.55 port '2055'
+set system flow-accounting netflow version '9'
+set system flow-accounting sflow server 1.2.3.4 port '1234'
+set system flow-accounting syslog-facility 'daemon'
+set system host-name 'vyos'
+set system login user vyos authentication encrypted-password '$6$2Ta6TWHd/U$NmrX0x9kexCimeOcYK1MfhMpITF9ELxHcaBU/znBq.X2ukQOj61fVI2UYP/xBzP4QtiTcdkgs7WOQMHWsRymO/'
+set system login user vyos authentication plaintext-password ''
+set system name-server '2001:db8::1'
+set system name-server '2001:db8::2'
+set system name-server '192.0.2.1'
+set system name-server '192.0.2.2'
+set system syslog global facility all level 'all'
+set system syslog global preserve-fqdn
+set system time-zone 'Europe/Zurich'
diff --git a/smoketest/config-tests/bgp-dmvpn-hub b/smoketest/config-tests/bgp-dmvpn-hub
new file mode 100644
index 000000000..30521520a
--- /dev/null
+++ b/smoketest/config-tests/bgp-dmvpn-hub
@@ -0,0 +1,69 @@
+set interfaces ethernet eth0 address '100.64.10.1/31'
+set interfaces ethernet eth0 speed 'auto'
+set interfaces ethernet eth0 duplex 'auto'
+set interfaces ethernet eth1 speed 'auto'
+set interfaces ethernet eth1 duplex 'auto'
+set interfaces loopback lo
+set interfaces tunnel tun0 address '192.168.254.62/26'
+set interfaces tunnel tun0 enable-multicast
+set interfaces tunnel tun0 encapsulation 'gre'
+set interfaces tunnel tun0 parameters ip key '1'
+set interfaces tunnel tun0 source-address '100.64.10.1'
+set protocols bgp address-family ipv4-unicast network 172.20.0.0/16
+set protocols bgp neighbor 192.168.254.1 peer-group 'DMVPN'
+set protocols bgp neighbor 192.168.254.1 remote-as '65001'
+set protocols bgp neighbor 192.168.254.2 peer-group 'DMVPN'
+set protocols bgp neighbor 192.168.254.2 remote-as '65002'
+set protocols bgp neighbor 192.168.254.3 peer-group 'DMVPN'
+set protocols bgp neighbor 192.168.254.3 remote-as '65003'
+set protocols bgp parameters log-neighbor-changes
+set protocols bgp peer-group DMVPN address-family ipv4-unicast
+set protocols bgp system-as '65000'
+set protocols bgp timers holdtime '30'
+set protocols bgp timers keepalive '10'
+set protocols nhrp tunnel tun0 cisco-authentication 'secret'
+set protocols nhrp tunnel tun0 holding-time '300'
+set protocols nhrp tunnel tun0 multicast 'dynamic'
+set protocols nhrp tunnel tun0 redirect
+set protocols nhrp tunnel tun0 shortcut
+set protocols static route 0.0.0.0/0 next-hop 100.64.10.0
+set protocols static route 172.20.0.0/16 blackhole distance '200'
+set service ntp allow-client address '0.0.0.0/0'
+set service ntp allow-client address '::/0'
+set service ntp server time1.vyos.net
+set service ntp server time2.vyos.net
+set service ntp server time3.vyos.net
+set system config-management commit-revisions '100'
+set system conntrack modules ftp
+set system conntrack modules h323
+set system conntrack modules nfs
+set system conntrack modules pptp
+set system conntrack modules sip
+set system conntrack modules sqlnet
+set system conntrack modules tftp
+set system console device ttyS0 speed '115200'
+set system host-name 'cpe-4'
+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 name-server '1.1.1.1'
+set system name-server '8.8.8.8'
+set system name-server '9.9.9.9'
+set system syslog global facility all level 'info'
+set system syslog global facility local7 level 'debug'
+set vpn ipsec esp-group ESP-DMVPN lifetime '1800'
+set vpn ipsec esp-group ESP-DMVPN mode 'transport'
+set vpn ipsec esp-group ESP-DMVPN pfs 'dh-group2'
+set vpn ipsec esp-group ESP-DMVPN proposal 1 encryption 'aes256'
+set vpn ipsec esp-group ESP-DMVPN proposal 1 hash 'sha1'
+set vpn ipsec ike-group IKE-DMVPN close-action 'none'
+set vpn ipsec ike-group IKE-DMVPN key-exchange 'ikev1'
+set vpn ipsec ike-group IKE-DMVPN lifetime '3600'
+set vpn ipsec ike-group IKE-DMVPN proposal 1 dh-group '2'
+set vpn ipsec ike-group IKE-DMVPN proposal 1 encryption 'aes256'
+set vpn ipsec ike-group IKE-DMVPN proposal 1 hash 'sha1'
+set vpn ipsec interface 'eth0'
+set vpn ipsec profile NHRPVPN authentication mode 'pre-shared-secret'
+set vpn ipsec profile NHRPVPN authentication pre-shared-secret 'VyOS-topsecret'
+set vpn ipsec profile NHRPVPN bind tunnel 'tun0'
+set vpn ipsec profile NHRPVPN esp-group 'ESP-DMVPN'
+set vpn ipsec profile NHRPVPN ike-group 'IKE-DMVPN'
diff --git a/smoketest/config-tests/bgp-dmvpn-spoke b/smoketest/config-tests/bgp-dmvpn-spoke
new file mode 100644
index 000000000..d1c7bc7c0
--- /dev/null
+++ b/smoketest/config-tests/bgp-dmvpn-spoke
@@ -0,0 +1,75 @@
+set interfaces ethernet eth0 vif 7 description 'PPPoE-UPLINK'
+set interfaces ethernet eth1 address '172.17.1.1/24'
+set interfaces loopback lo
+set interfaces pppoe pppoe1 authentication password 'cpe-1'
+set interfaces pppoe pppoe1 authentication username 'cpe-1'
+set interfaces pppoe pppoe1 no-peer-dns
+set interfaces pppoe pppoe1 source-interface 'eth0.7'
+set interfaces tunnel tun0 address '192.168.254.1/26'
+set interfaces tunnel tun0 enable-multicast
+set interfaces tunnel tun0 encapsulation 'gre'
+set interfaces tunnel tun0 parameters ip key '1'
+set interfaces tunnel tun0 source-address '0.0.0.0'
+set nat source rule 10 log
+set nat source rule 10 outbound-interface name 'pppoe1'
+set nat source rule 10 source address '172.17.0.0/16'
+set nat source rule 10 translation address 'masquerade'
+set protocols bgp address-family ipv4-unicast network 172.17.0.0/16
+set protocols bgp neighbor 192.168.254.62 address-family ipv4-unicast
+set protocols bgp neighbor 192.168.254.62 remote-as '65000'
+set protocols bgp parameters log-neighbor-changes
+set protocols bgp system-as '65001'
+set protocols bgp timers holdtime '30'
+set protocols bgp timers keepalive '10'
+set protocols nhrp tunnel tun0 cisco-authentication 'secret'
+set protocols nhrp tunnel tun0 holding-time '300'
+set protocols nhrp tunnel tun0 map 192.168.254.62/26 nbma-address '100.64.10.1'
+set protocols nhrp tunnel tun0 map 192.168.254.62/26 register
+set protocols nhrp tunnel tun0 multicast 'nhs'
+set protocols nhrp tunnel tun0 redirect
+set protocols nhrp tunnel tun0 shortcut
+set protocols static route 172.17.0.0/16 blackhole distance '200'
+set service dhcp-server shared-network-name LAN-3 subnet 172.17.1.0/24 option default-router '172.17.1.1'
+set service dhcp-server shared-network-name LAN-3 subnet 172.17.1.0/24 option name-server '172.17.1.1'
+set service dhcp-server shared-network-name LAN-3 subnet 172.17.1.0/24 range 0 start '172.17.1.100'
+set service dhcp-server shared-network-name LAN-3 subnet 172.17.1.0/24 range 0 stop '172.17.1.200'
+set service dhcp-server shared-network-name LAN-3 subnet 172.17.1.0/24 subnet-id '1'
+set service ntp allow-client address '0.0.0.0/0'
+set service ntp allow-client address '::/0'
+set service ntp server time1.vyos.net
+set service ntp server time2.vyos.net
+set service ntp server time3.vyos.net
+set system config-management commit-revisions '100'
+set system conntrack modules ftp
+set system conntrack modules h323
+set system conntrack modules nfs
+set system conntrack modules pptp
+set system conntrack modules sip
+set system conntrack modules sqlnet
+set system conntrack modules tftp
+set system console device ttyS0 speed '115200'
+set system host-name 'cpe-1'
+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 name-server '1.1.1.1'
+set system name-server '8.8.8.8'
+set system name-server '9.9.9.9'
+set system syslog global facility all level 'info'
+set system syslog global facility local7 level 'debug'
+set vpn ipsec esp-group ESP-DMVPN lifetime '1800'
+set vpn ipsec esp-group ESP-DMVPN mode 'transport'
+set vpn ipsec esp-group ESP-DMVPN pfs 'dh-group2'
+set vpn ipsec esp-group ESP-DMVPN proposal 1 encryption 'aes256'
+set vpn ipsec esp-group ESP-DMVPN proposal 1 hash 'sha1'
+set vpn ipsec ike-group IKE-DMVPN close-action 'none'
+set vpn ipsec ike-group IKE-DMVPN key-exchange 'ikev1'
+set vpn ipsec ike-group IKE-DMVPN lifetime '3600'
+set vpn ipsec ike-group IKE-DMVPN proposal 1 dh-group '2'
+set vpn ipsec ike-group IKE-DMVPN proposal 1 encryption 'aes256'
+set vpn ipsec ike-group IKE-DMVPN proposal 1 hash 'sha1'
+set vpn ipsec interface 'pppoe1'
+set vpn ipsec profile NHRPVPN authentication mode 'pre-shared-secret'
+set vpn ipsec profile NHRPVPN authentication pre-shared-secret 'VyOS-topsecret'
+set vpn ipsec profile NHRPVPN bind tunnel 'tun0'
+set vpn ipsec profile NHRPVPN esp-group 'ESP-DMVPN'
+set vpn ipsec profile NHRPVPN ike-group 'IKE-DMVPN'
diff --git a/smoketest/config-tests/bgp-evpn-l2vpn-leaf b/smoketest/config-tests/bgp-evpn-l2vpn-leaf
new file mode 100644
index 000000000..315cb9e06
--- /dev/null
+++ b/smoketest/config-tests/bgp-evpn-l2vpn-leaf
@@ -0,0 +1,55 @@
+set interfaces bridge br100 member interface eth3
+set interfaces bridge br100 member interface vxlan100
+set interfaces dummy dum0 address '172.29.0.1/32'
+set interfaces ethernet eth0 address '2001:db8::41/64'
+set interfaces ethernet eth0 address '192.0.2.41/27'
+set interfaces ethernet eth0 description 'Out-of-Band Managament Port'
+set interfaces ethernet eth0 vrf 'MGMT'
+set interfaces ethernet eth1 address '172.29.1.1/31'
+set interfaces ethernet eth1 mtu '1600'
+set interfaces ethernet eth2 address '172.29.2.1/31'
+set interfaces ethernet eth2 mtu '1600'
+set interfaces ethernet eth2 offload gro
+set interfaces ethernet eth3 offload gro
+set interfaces loopback lo
+set interfaces vxlan vxlan100 mtu '1500'
+set interfaces vxlan vxlan100 parameters nolearning
+set interfaces vxlan vxlan100 port '8472'
+set interfaces vxlan vxlan100 source-address '172.29.0.1'
+set interfaces vxlan vxlan100 vni '100'
+set protocols bgp address-family ipv4-unicast maximum-paths ibgp '4'
+set protocols bgp address-family ipv4-unicast redistribute connected
+set protocols bgp address-family l2vpn-evpn advertise-all-vni
+set protocols bgp neighbor 172.29.1.0 peer-group 'evpn'
+set protocols bgp neighbor 172.29.2.0 peer-group 'evpn'
+set protocols bgp parameters log-neighbor-changes
+set protocols bgp peer-group evpn address-family ipv4-unicast nexthop-self
+set protocols bgp peer-group evpn address-family l2vpn-evpn nexthop-self
+set protocols bgp peer-group evpn remote-as '65010'
+set protocols bgp system-as '65010'
+set service lldp interface all
+set service ntp allow-client address '0.0.0.0/0'
+set service ntp allow-client address '::/0'
+set service ntp listen-address '192.0.2.41'
+set service ntp listen-address '2001:db8::41'
+set service ntp server 0.de.pool.ntp.org prefer
+set service ntp vrf 'MGMT'
+set service ssh disable-host-validation
+set service ssh vrf 'MGMT'
+set system config-management commit-revisions '100'
+set system conntrack modules ftp
+set system conntrack modules h323
+set system conntrack modules nfs
+set system conntrack modules pptp
+set system conntrack modules sip
+set system conntrack modules sqlnet
+set system conntrack modules tftp
+set system console device ttyS0 speed '115200'
+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 syslog global facility all level 'info'
+set system syslog global facility local7 level 'debug'
+set vrf name MGMT protocols static route 0.0.0.0/0 next-hop 192.0.2.62
+set vrf name MGMT protocols static route6 ::/0 next-hop 2001:db8::1
+set vrf name MGMT table '1000'
diff --git a/smoketest/config-tests/bgp-evpn-l2vpn-spine b/smoketest/config-tests/bgp-evpn-l2vpn-spine
new file mode 100644
index 000000000..dee29e021
--- /dev/null
+++ b/smoketest/config-tests/bgp-evpn-l2vpn-spine
@@ -0,0 +1,48 @@
+set interfaces ethernet eth0 address '192.0.2.51/27'
+set interfaces ethernet eth0 address '2001:db8::51/64'
+set interfaces ethernet eth0 description 'Out-of-Band Managament Port'
+set interfaces ethernet eth0 vrf 'MGMT'
+set interfaces ethernet eth1 address '172.29.1.0/31'
+set interfaces ethernet eth1 mtu '1600'
+set interfaces ethernet eth2 address '172.29.1.2/31'
+set interfaces ethernet eth2 mtu '1600'
+set interfaces ethernet eth2 offload gro
+set interfaces ethernet eth3 address '172.29.1.4/31'
+set interfaces ethernet eth3 mtu '1600'
+set interfaces ethernet eth3 offload gro
+set interfaces loopback lo
+set protocols bgp address-family ipv4-unicast maximum-paths ibgp '4'
+set protocols bgp address-family ipv4-unicast redistribute connected
+set protocols bgp listen range 172.29.1.0/24 peer-group 'evpn'
+set protocols bgp parameters log-neighbor-changes
+set protocols bgp peer-group evpn address-family ipv4-unicast route-reflector-client
+set protocols bgp peer-group evpn address-family l2vpn-evpn route-reflector-client
+set protocols bgp peer-group evpn capability dynamic
+set protocols bgp peer-group evpn remote-as '65010'
+set protocols bgp system-as '65010'
+set service lldp interface all
+set service ntp allow-client address '0.0.0.0/0'
+set service ntp allow-client address '::/0'
+set service ntp listen-address '192.0.2.51'
+set service ntp listen-address '2001:db8::51'
+set service ntp server 0.de.pool.ntp.org prefer
+set service ntp vrf 'MGMT'
+set service ssh disable-host-validation
+set service ssh vrf 'MGMT'
+set system config-management commit-revisions '100'
+set system conntrack modules ftp
+set system conntrack modules h323
+set system conntrack modules nfs
+set system conntrack modules pptp
+set system conntrack modules sip
+set system conntrack modules sqlnet
+set system conntrack modules tftp
+set system console device ttyS0 speed '115200'
+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 syslog global facility all level 'info'
+set system syslog global facility local7 level 'debug'
+set vrf name MGMT protocols static route 0.0.0.0/0 next-hop 192.0.2.62
+set vrf name MGMT protocols static route6 ::/0 next-hop 2001:db8::1
+set vrf name MGMT table '1000'
diff --git a/smoketest/config-tests/bgp-evpn-l3vpn-pe-router b/smoketest/config-tests/bgp-evpn-l3vpn-pe-router
new file mode 100644
index 000000000..7a2ec9f91
--- /dev/null
+++ b/smoketest/config-tests/bgp-evpn-l3vpn-pe-router
@@ -0,0 +1,123 @@
+set interfaces bridge br2000 address '10.1.1.1/24'
+set interfaces bridge br2000 description 'customer blue'
+set interfaces bridge br2000 member interface eth4
+set interfaces bridge br2000 member interface vxlan2000
+set interfaces bridge br2000 vrf 'blue'
+set interfaces bridge br3000 address '10.2.1.1/24'
+set interfaces bridge br3000 description 'customer red'
+set interfaces bridge br3000 member interface eth5
+set interfaces bridge br3000 member interface vxlan3000
+set interfaces bridge br3000 vrf 'red'
+set interfaces bridge br4000 address '10.3.1.1/24'
+set interfaces bridge br4000 description 'customer green'
+set interfaces bridge br4000 member interface eth6
+set interfaces bridge br4000 member interface vxlan4000
+set interfaces bridge br4000 vrf 'green'
+set interfaces dummy dum0 address '172.29.255.1/32'
+set interfaces ethernet eth0 address '192.0.2.59/27'
+set interfaces ethernet eth0 address '2001:db8:ffff::59/64'
+set interfaces ethernet eth0 description 'Out-of-Band Managament Port'
+set interfaces ethernet eth0 offload gro
+set interfaces ethernet eth0 vrf 'mgmt'
+set interfaces ethernet eth1 address '172.29.0.2/31'
+set interfaces ethernet eth1 description 'link to pe2'
+set interfaces ethernet eth1 mtu '1600'
+set interfaces ethernet eth1 offload gro
+set interfaces ethernet eth2 disable
+set interfaces ethernet eth2 offload gro
+set interfaces ethernet eth3 address '172.29.0.6/31'
+set interfaces ethernet eth3 description 'link to pe3'
+set interfaces ethernet eth3 mtu '1600'
+set interfaces ethernet eth3 offload gro
+set interfaces ethernet eth4 description 'customer blue'
+set interfaces ethernet eth4 offload gro
+set interfaces ethernet eth5 description 'customer red'
+set interfaces ethernet eth5 offload gro
+set interfaces ethernet eth6 description 'customer green'
+set interfaces ethernet eth6 offload gro
+set interfaces loopback lo
+set interfaces vxlan vxlan2000 mtu '1500'
+set interfaces vxlan vxlan2000 parameters nolearning
+set interfaces vxlan vxlan2000 port '4789'
+set interfaces vxlan vxlan2000 source-address '172.29.255.1'
+set interfaces vxlan vxlan2000 vni '2000'
+set interfaces vxlan vxlan3000 mtu '1500'
+set interfaces vxlan vxlan3000 parameters nolearning
+set interfaces vxlan vxlan3000 port '4789'
+set interfaces vxlan vxlan3000 source-address '172.29.255.1'
+set interfaces vxlan vxlan3000 vni '3000'
+set interfaces vxlan vxlan4000 mtu '1500'
+set interfaces vxlan vxlan4000 parameters nolearning
+set interfaces vxlan vxlan4000 port '4789'
+set interfaces vxlan vxlan4000 source-address '172.29.255.1'
+set interfaces vxlan vxlan4000 vni '4000'
+set protocols bgp address-family l2vpn-evpn advertise ipv4 unicast
+set protocols bgp address-family l2vpn-evpn advertise-all-vni
+set protocols bgp neighbor 172.29.255.2 peer-group 'ibgp'
+set protocols bgp neighbor 172.29.255.3 peer-group 'ibgp'
+set protocols bgp parameters log-neighbor-changes
+set protocols bgp parameters router-id '172.29.255.1'
+set protocols bgp peer-group ibgp address-family l2vpn-evpn
+set protocols bgp peer-group ibgp remote-as '100'
+set protocols bgp peer-group ibgp update-source 'dum0'
+set protocols bgp system-as '100'
+set protocols ospf area 0 network '172.29.0.2/31'
+set protocols ospf area 0 network '172.29.0.6/31'
+set protocols ospf interface eth1 network 'point-to-point'
+set protocols ospf interface eth1 passive disable
+set protocols ospf interface eth3 network 'point-to-point'
+set protocols ospf interface eth3 passive disable
+set protocols ospf log-adjacency-changes detail
+set protocols ospf parameters abr-type 'cisco'
+set protocols ospf parameters router-id '172.29.255.1'
+set protocols ospf passive-interface 'default'
+set protocols ospf redistribute connected
+set service lldp interface all
+set service ntp allow-client address '0.0.0.0/0'
+set service ntp allow-client address '::/0'
+set service ntp listen-address '192.0.2.59'
+set service ntp listen-address '2001:db8:ffff::59'
+set service ntp server 192.0.2.251
+set service ntp server 192.0.2.252
+set service ntp server 2001:db8::251
+set service ntp server 2001:db8::252
+set service ntp vrf 'mgmt'
+set service ssh disable-host-validation
+set service ssh port '22'
+set service ssh vrf 'mgmt'
+set system config-management commit-revisions '100'
+set system conntrack modules ftp
+set system conntrack modules h323
+set system conntrack modules nfs
+set system conntrack modules pptp
+set system conntrack modules sip
+set system conntrack modules sqlnet
+set system conntrack modules tftp
+set system console device ttyS0 speed '115200'
+set system domain-name 'vyos.net'
+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 name-server '192.0.2.251'
+set system name-server '192.0.2.252'
+set system name-server '2001:db8::1'
+set system syslog global facility all level 'info'
+set system syslog global facility local7 level 'debug'
+set vrf name blue protocols bgp address-family ipv4-unicast redistribute connected
+set vrf name blue protocols bgp address-family l2vpn-evpn advertise ipv4 unicast
+set vrf name blue protocols bgp system-as '100'
+set vrf name blue table '2000'
+set vrf name blue vni '2000'
+set vrf name green protocols bgp address-family ipv4-unicast redistribute connected
+set vrf name green protocols bgp address-family l2vpn-evpn advertise ipv4 unicast
+set vrf name green protocols bgp system-as '100'
+set vrf name green table '4000'
+set vrf name green vni '4000'
+set vrf name mgmt protocols static route 0.0.0.0/0 next-hop 192.0.2.62
+set vrf name mgmt protocols static route6 ::/0 next-hop 2001:db8:ffff::1
+set vrf name mgmt table '1000'
+set vrf name red protocols bgp address-family ipv4-unicast redistribute connected
+set vrf name red protocols bgp address-family l2vpn-evpn advertise ipv4 unicast
+set vrf name red protocols bgp system-as '100'
+set vrf name red table '3000'
+set vrf name red vni '3000'
diff --git a/smoketest/config-tests/bgp-medium-confederation b/smoketest/config-tests/bgp-medium-confederation
index ea3c2d144..582e28047 100644
--- a/smoketest/config-tests/bgp-medium-confederation
+++ b/smoketest/config-tests/bgp-medium-confederation
@@ -1,7 +1,7 @@
set interfaces dummy dum0 address '1.1.1.1/32'
set interfaces dummy dum0 address '2001:db8::1/128'
-set interfaces ethernet eth0 address 'fd52:100:200:fffe::1/64'
set interfaces ethernet eth0 address '192.168.253.1/24'
+set interfaces ethernet eth0 address 'fd52:100:200:fffe::1/64'
set interfaces ethernet eth1
set interfaces ethernet eth2
set policy route-map BGP-IN rule 10 action 'permit'
diff --git a/smoketest/config-tests/bgp-rpki b/smoketest/config-tests/bgp-rpki
new file mode 100644
index 000000000..44e95ae98
--- /dev/null
+++ b/smoketest/config-tests/bgp-rpki
@@ -0,0 +1,43 @@
+set interfaces ethernet eth0 address '192.0.2.100/25'
+set interfaces ethernet eth0 address '2001:db8::ffff/64'
+set interfaces ethernet eth1 address '100.64.0.1/24'
+set interfaces loopback lo
+set policy route-map ebgp-transit-rpki rule 10 action 'deny'
+set policy route-map ebgp-transit-rpki rule 10 match rpki 'invalid'
+set policy route-map ebgp-transit-rpki rule 20 action 'permit'
+set policy route-map ebgp-transit-rpki rule 20 match rpki 'notfound'
+set policy route-map ebgp-transit-rpki rule 20 set local-preference '20'
+set policy route-map ebgp-transit-rpki rule 30 action 'permit'
+set policy route-map ebgp-transit-rpki rule 30 match rpki 'valid'
+set policy route-map ebgp-transit-rpki rule 30 set local-preference '100'
+set policy route-map ebgp-transit-rpki rule 40 action 'permit'
+set policy route-map ebgp-transit-rpki rule 40 set extcommunity rt '192.0.2.100:100'
+set policy route-map ebgp-transit-rpki rule 40 set extcommunity soo '64500:100'
+set protocols bgp neighbor 1.2.3.4 address-family ipv4-unicast nexthop-self
+set protocols bgp neighbor 1.2.3.4 address-family ipv4-unicast route-map import 'ebgp-transit-rpki'
+set protocols bgp neighbor 1.2.3.4 remote-as '10'
+set protocols bgp system-as '64500'
+set protocols rpki cache 192.0.2.10 port '3323'
+set protocols rpki cache 192.0.2.10 preference '1'
+set protocols static route 0.0.0.0/0 next-hop 192.0.2.1
+set protocols static route6 ::/0 next-hop 2001:db8::1
+set service ntp allow-client address '0.0.0.0/0'
+set service ntp allow-client address '::/0'
+set service ntp server 0.pool.ntp.org
+set service ntp server 1.pool.ntp.org
+set service ntp server 2.pool.ntp.org
+set service ssh
+set system config-management commit-revisions '100'
+set system conntrack modules ftp
+set system conntrack modules h323
+set system conntrack modules nfs
+set system conntrack modules pptp
+set system conntrack modules sip
+set system conntrack modules sqlnet
+set system conntrack modules tftp
+set system console device ttyS0 speed '115200'
+set system host-name 'vyos'
+set system login user vyos authentication encrypted-password '$6$2Ta6TWHd/U$NmrX0x9kexCimeOcYK1MfhMpITF9ELxHcaBU/znBq.X2ukQOj61fVI2UYP/xBzP4QtiTcdkgs7WOQMHWsRymO/'
+set system login user vyos authentication plaintext-password ''
+set system syslog global facility all level 'info'
+set system syslog global facility local7 level 'debug'
diff --git a/smoketest/config-tests/bgp-small-internet-exchange b/smoketest/config-tests/bgp-small-internet-exchange
new file mode 100644
index 000000000..a9dce4dd5
--- /dev/null
+++ b/smoketest/config-tests/bgp-small-internet-exchange
@@ -0,0 +1,209 @@
+set interfaces ethernet eth0 address '192.0.2.100/25'
+set interfaces ethernet eth0 address '2001:db8:aaaa::ffff/64'
+set interfaces ethernet eth1 address '192.0.2.200/25'
+set interfaces ethernet eth1 address '2001:db8:bbbb::ffff/64'
+set interfaces loopback lo
+set policy as-path-list bogon-asns rule 10 action 'permit'
+set policy as-path-list bogon-asns rule 10 description 'RFC 7607'
+set policy as-path-list bogon-asns rule 10 regex '_0_'
+set policy as-path-list bogon-asns rule 20 action 'permit'
+set policy as-path-list bogon-asns rule 20 description 'RFC 4893'
+set policy as-path-list bogon-asns rule 20 regex '_23456_'
+set policy as-path-list bogon-asns rule 30 action 'permit'
+set policy as-path-list bogon-asns rule 30 description 'RFC 5398/6996/7300'
+set policy as-path-list bogon-asns rule 30 regex '_6449[6-9]_|_65[0-4][0-9][0-9]_|_655[0-4][0-9]_|_6555[0-1]_'
+set policy as-path-list bogon-asns rule 40 action 'permit'
+set policy as-path-list bogon-asns rule 40 description 'IANA reserved'
+set policy as-path-list bogon-asns rule 40 regex '_6555[2-9]_|_655[6-9][0-9]_|_65[6-9][0-9][0-9]_|_6[6-9][0-9][0-9][0-]_|_[7-9][0-9][0-9][0-9][0-9]_|_1[0-2][0-9][0-9][0-9][0-9]_|_130[0-9][0-9][0-9]_|_1310[0-6][0-9]_|_13107[01]_'
+set policy prefix-list IX-out-v4 rule 10 action 'permit'
+set policy prefix-list IX-out-v4 rule 10 prefix '10.0.0.0/23'
+set policy prefix-list IX-out-v4 rule 20 action 'permit'
+set policy prefix-list IX-out-v4 rule 20 prefix '10.0.128.0/23'
+set policy prefix-list bogon-v4 rule 10 action 'permit'
+set policy prefix-list bogon-v4 rule 10 le '32'
+set policy prefix-list bogon-v4 rule 10 prefix '0.0.0.0/8'
+set policy prefix-list bogon-v4 rule 20 action 'permit'
+set policy prefix-list bogon-v4 rule 20 le '32'
+set policy prefix-list bogon-v4 rule 20 prefix '10.0.0.0/8'
+set policy prefix-list bogon-v4 rule 30 action 'permit'
+set policy prefix-list bogon-v4 rule 30 le '32'
+set policy prefix-list bogon-v4 rule 30 prefix '100.64.0.0/10'
+set policy prefix-list bogon-v4 rule 40 action 'permit'
+set policy prefix-list bogon-v4 rule 40 le '32'
+set policy prefix-list bogon-v4 rule 40 prefix '127.0.0.0/8'
+set policy prefix-list bogon-v4 rule 50 action 'permit'
+set policy prefix-list bogon-v4 rule 50 le '32'
+set policy prefix-list bogon-v4 rule 50 prefix '169.254.0.0/16'
+set policy prefix-list bogon-v4 rule 60 action 'permit'
+set policy prefix-list bogon-v4 rule 60 le '32'
+set policy prefix-list bogon-v4 rule 60 prefix '172.16.0.0/12'
+set policy prefix-list bogon-v4 rule 70 action 'permit'
+set policy prefix-list bogon-v4 rule 70 le '32'
+set policy prefix-list bogon-v4 rule 70 prefix '192.0.2.0/24'
+set policy prefix-list bogon-v4 rule 80 action 'permit'
+set policy prefix-list bogon-v4 rule 80 le '32'
+set policy prefix-list bogon-v4 rule 80 prefix '192.88.99.0/24'
+set policy prefix-list bogon-v4 rule 90 action 'permit'
+set policy prefix-list bogon-v4 rule 90 le '32'
+set policy prefix-list bogon-v4 rule 90 prefix '192.168.0.0/16'
+set policy prefix-list bogon-v4 rule 100 action 'permit'
+set policy prefix-list bogon-v4 rule 100 le '32'
+set policy prefix-list bogon-v4 rule 100 prefix '198.18.0.0/15'
+set policy prefix-list bogon-v4 rule 110 action 'permit'
+set policy prefix-list bogon-v4 rule 110 le '32'
+set policy prefix-list bogon-v4 rule 110 prefix '198.51.100.0/24'
+set policy prefix-list bogon-v4 rule 120 action 'permit'
+set policy prefix-list bogon-v4 rule 120 le '32'
+set policy prefix-list bogon-v4 rule 120 prefix '203.0.113.0/24'
+set policy prefix-list bogon-v4 rule 130 action 'permit'
+set policy prefix-list bogon-v4 rule 130 le '32'
+set policy prefix-list bogon-v4 rule 130 prefix '224.0.0.0/4'
+set policy prefix-list bogon-v4 rule 140 action 'permit'
+set policy prefix-list bogon-v4 rule 140 le '32'
+set policy prefix-list bogon-v4 rule 140 prefix '240.0.0.0/4'
+set policy prefix-list prefix-filter-v4 rule 10 action 'permit'
+set policy prefix-list prefix-filter-v4 rule 10 ge '25'
+set policy prefix-list prefix-filter-v4 rule 10 prefix '0.0.0.0/0'
+set policy prefix-list6 IX-out-v6 rule 10 action 'permit'
+set policy prefix-list6 IX-out-v6 rule 10 prefix '2001:db8:100::/40'
+set policy prefix-list6 IX-out-v6 rule 20 action 'permit'
+set policy prefix-list6 IX-out-v6 rule 20 prefix '2001:db8:200::/40'
+set policy prefix-list6 bogon-v6 rule 10 action 'permit'
+set policy prefix-list6 bogon-v6 rule 10 description 'RFC 4291 IPv4-compatible, loopback, et al'
+set policy prefix-list6 bogon-v6 rule 10 le '128'
+set policy prefix-list6 bogon-v6 rule 10 prefix '::/8'
+set policy prefix-list6 bogon-v6 rule 20 action 'permit'
+set policy prefix-list6 bogon-v6 rule 20 description 'RFC 6666 Discard-Only'
+set policy prefix-list6 bogon-v6 rule 20 le '128'
+set policy prefix-list6 bogon-v6 rule 20 prefix '0100::/64'
+set policy prefix-list6 bogon-v6 rule 30 action 'permit'
+set policy prefix-list6 bogon-v6 rule 30 description 'RFC 5180 BMWG'
+set policy prefix-list6 bogon-v6 rule 30 le '128'
+set policy prefix-list6 bogon-v6 rule 30 prefix '2001:2::/48'
+set policy prefix-list6 bogon-v6 rule 40 action 'permit'
+set policy prefix-list6 bogon-v6 rule 40 description 'RFC 4843 ORCHID'
+set policy prefix-list6 bogon-v6 rule 40 le '128'
+set policy prefix-list6 bogon-v6 rule 40 prefix '2001:10::/28'
+set policy prefix-list6 bogon-v6 rule 50 action 'permit'
+set policy prefix-list6 bogon-v6 rule 50 description 'RFC 3849 documentation'
+set policy prefix-list6 bogon-v6 rule 50 le '128'
+set policy prefix-list6 bogon-v6 rule 50 prefix '2001:db8::/32'
+set policy prefix-list6 bogon-v6 rule 60 action 'permit'
+set policy prefix-list6 bogon-v6 rule 60 description 'RFC 7526 6to4 anycast relay'
+set policy prefix-list6 bogon-v6 rule 60 le '128'
+set policy prefix-list6 bogon-v6 rule 60 prefix '2002::/16'
+set policy prefix-list6 bogon-v6 rule 70 action 'permit'
+set policy prefix-list6 bogon-v6 rule 70 description 'RFC 3701 old 6bone'
+set policy prefix-list6 bogon-v6 rule 70 le '128'
+set policy prefix-list6 bogon-v6 rule 70 prefix '3ffe::/16'
+set policy prefix-list6 bogon-v6 rule 80 action 'permit'
+set policy prefix-list6 bogon-v6 rule 80 description 'RFC 4193 unique local unicast'
+set policy prefix-list6 bogon-v6 rule 80 le '128'
+set policy prefix-list6 bogon-v6 rule 80 prefix 'fc00::/7'
+set policy prefix-list6 bogon-v6 rule 90 action 'permit'
+set policy prefix-list6 bogon-v6 rule 90 description 'RFC 4291 link local unicast'
+set policy prefix-list6 bogon-v6 rule 90 le '128'
+set policy prefix-list6 bogon-v6 rule 90 prefix 'fe80::/10'
+set policy prefix-list6 bogon-v6 rule 100 action 'permit'
+set policy prefix-list6 bogon-v6 rule 100 description 'RFC 3879 old site local unicast'
+set policy prefix-list6 bogon-v6 rule 100 le '128'
+set policy prefix-list6 bogon-v6 rule 100 prefix 'fec0::/10'
+set policy prefix-list6 bogon-v6 rule 110 action 'permit'
+set policy prefix-list6 bogon-v6 rule 110 description 'RFC 4291 multicast'
+set policy prefix-list6 bogon-v6 rule 110 le '128'
+set policy prefix-list6 bogon-v6 rule 110 prefix 'ff00::/8'
+set policy prefix-list6 prefix-filter-v6 rule 10 action 'permit'
+set policy prefix-list6 prefix-filter-v6 rule 10 ge '49'
+set policy prefix-list6 prefix-filter-v6 rule 10 prefix '::/0'
+set policy route-map IX-in-v4 rule 5 action 'permit'
+set policy route-map IX-in-v4 rule 5 call 'eBGP-IN-v4'
+set policy route-map IX-in-v4 rule 5 on-match next
+set policy route-map IX-in-v4 rule 10 action 'permit'
+set policy route-map IX-in-v6 rule 5 action 'permit'
+set policy route-map IX-in-v6 rule 5 call 'eBGP-IN-v6'
+set policy route-map IX-in-v6 rule 5 on-match next
+set policy route-map IX-in-v6 rule 10 action 'permit'
+set policy route-map IX-out-v4 rule 10 action 'permit'
+set policy route-map IX-out-v4 rule 10 match ip address prefix-list 'IX-out-v4'
+set policy route-map IX-out-v6 rule 10 action 'permit'
+set policy route-map IX-out-v6 rule 10 match ipv6 address prefix-list 'IX-out-v6'
+set policy route-map eBGP-IN-v4 rule 10 action 'deny'
+set policy route-map eBGP-IN-v4 rule 10 match as-path 'bogon-asns'
+set policy route-map eBGP-IN-v4 rule 20 action 'deny'
+set policy route-map eBGP-IN-v4 rule 20 match ip address prefix-list 'bogon-v4'
+set policy route-map eBGP-IN-v4 rule 30 action 'deny'
+set policy route-map eBGP-IN-v4 rule 30 match ip address prefix-list 'prefix-filter-v4'
+set policy route-map eBGP-IN-v4 rule 40 action 'permit'
+set policy route-map eBGP-IN-v4 rule 40 set local-preference '100'
+set policy route-map eBGP-IN-v4 rule 40 set metric '0'
+set policy route-map eBGP-IN-v6 rule 10 action 'deny'
+set policy route-map eBGP-IN-v6 rule 10 match as-path 'bogon-asns'
+set policy route-map eBGP-IN-v6 rule 20 action 'deny'
+set policy route-map eBGP-IN-v6 rule 20 match ipv6 address prefix-list 'bogon-v6'
+set policy route-map eBGP-IN-v6 rule 30 action 'deny'
+set policy route-map eBGP-IN-v6 rule 30 match ipv6 address prefix-list 'prefix-filter-v6'
+set policy route-map eBGP-IN-v6 rule 31 action 'deny'
+set policy route-map eBGP-IN-v6 rule 31 match ipv6 nexthop address '2001:db8::1'
+set policy route-map eBGP-IN-v6 rule 40 action 'permit'
+set policy route-map eBGP-IN-v6 rule 40 set local-preference '100'
+set policy route-map eBGP-IN-v6 rule 40 set metric '0'
+set protocols bgp address-family ipv4-unicast network 10.0.0.0/23
+set protocols bgp address-family ipv4-unicast network 10.0.128.0/23
+set protocols bgp address-family ipv6-unicast network 2001:db8:100::/40
+set protocols bgp address-family ipv6-unicast network 2001:db8:200::/40
+set protocols bgp neighbor 192.0.2.1 description 'Peering: IX-1 (Route Server)'
+set protocols bgp neighbor 192.0.2.1 peer-group 'IXPeeringIPv4'
+set protocols bgp neighbor 192.0.2.1 remote-as '65020'
+set protocols bgp neighbor 192.0.2.2 description 'Peering: IX-1 (Route Server)'
+set protocols bgp neighbor 192.0.2.2 peer-group 'IXPeeringIPv4'
+set protocols bgp neighbor 192.0.2.2 remote-as '65020'
+set protocols bgp neighbor 192.0.2.3 description 'Peering: IX-1 (Route Server)'
+set protocols bgp neighbor 192.0.2.3 peer-group 'IXPeeringIPv4'
+set protocols bgp neighbor 192.0.2.3 remote-as '65020'
+set protocols bgp neighbor 192.0.2.129 description 'Peering: IX-2 (Route Server)'
+set protocols bgp neighbor 192.0.2.129 peer-group 'IXPeeringIPv4'
+set protocols bgp neighbor 192.0.2.129 remote-as '65030'
+set protocols bgp neighbor 192.0.2.130 description 'Peering: IX-2 (Route Server)'
+set protocols bgp neighbor 192.0.2.130 peer-group 'IXPeeringIPv4'
+set protocols bgp neighbor 192.0.2.130 remote-as '65030'
+set protocols bgp neighbor 2001:db8:aaaa::1 description 'Peering: IX-1 (Route Server)'
+set protocols bgp neighbor 2001:db8:aaaa::1 peer-group 'IXPeeringIPv6'
+set protocols bgp neighbor 2001:db8:aaaa::1 remote-as '65020'
+set protocols bgp neighbor 2001:db8:aaaa::2 description 'Peering: IX-1 (Route Server)'
+set protocols bgp neighbor 2001:db8:aaaa::2 peer-group 'IXPeeringIPv6'
+set protocols bgp neighbor 2001:db8:aaaa::2 remote-as '65020'
+set protocols bgp neighbor 2001:db8:bbbb::1 description 'Peering: IX-2 (Route Server)'
+set protocols bgp neighbor 2001:db8:bbbb::1 peer-group 'IXPeeringIPv6'
+set protocols bgp neighbor 2001:db8:bbbb::1 remote-as '65030'
+set protocols bgp neighbor 2001:db8:bbbb::2 description 'Peering: IX-2 (Route Server)'
+set protocols bgp neighbor 2001:db8:bbbb::2 peer-group 'IXPeeringIPv6'
+set protocols bgp neighbor 2001:db8:bbbb::2 remote-as '65030'
+set protocols bgp peer-group IXPeeringIPv4 address-family ipv4-unicast route-map export 'IX-out-v4'
+set protocols bgp peer-group IXPeeringIPv4 address-family ipv4-unicast soft-reconfiguration inbound
+set protocols bgp peer-group IXPeeringIPv6 address-family ipv6-unicast route-map export 'IX-out-v6'
+set protocols bgp peer-group IXPeeringIPv6 address-family ipv6-unicast soft-reconfiguration inbound
+set protocols bgp system-as '65000'
+set protocols static route 10.0.0.0/23 blackhole distance '250'
+set protocols static route 10.0.128.0/23 blackhole distance '250'
+set protocols static route6 2001:db8:100::/40 blackhole distance '250'
+set protocols static route6 2001:db8:200::/40 blackhole distance '250'
+set service ntp allow-client address '0.0.0.0/0'
+set service ntp allow-client address '::/0'
+set service ntp server 0.pool.ntp.org
+set service ntp server 1.pool.ntp.org
+set service ntp server 2.pool.ntp.org
+set service ssh
+set system config-management commit-revisions '100'
+set system conntrack modules ftp
+set system conntrack modules h323
+set system conntrack modules nfs
+set system conntrack modules pptp
+set system conntrack modules sip
+set system conntrack modules sqlnet
+set system conntrack modules tftp
+set system console device ttyS0 speed '115200'
+set system host-name 'vyos'
+set system login user vyos authentication encrypted-password '$6$2Ta6TWHd/U$NmrX0x9kexCimeOcYK1MfhMpITF9ELxHcaBU/znBq.X2ukQOj61fVI2UYP/xBzP4QtiTcdkgs7WOQMHWsRymO/'
+set system login user vyos authentication plaintext-password ''
+set system syslog global facility all level 'info'
+set system syslog global facility local7 level 'debug'
diff --git a/smoketest/config-tests/bgp-small-ipv4-unicast b/smoketest/config-tests/bgp-small-ipv4-unicast
new file mode 100644
index 000000000..b8c0e1246
--- /dev/null
+++ b/smoketest/config-tests/bgp-small-ipv4-unicast
@@ -0,0 +1,32 @@
+set interfaces ethernet eth0 address '192.0.2.1/24'
+set interfaces ethernet eth0 address '2001:db8::1/64'
+set interfaces loopback lo
+set protocols bgp address-family ipv4-unicast network 10.0.150.0/23
+set protocols bgp address-family ipv6-unicast network 2001:db8:200::/40
+set protocols bgp neighbor 192.0.2.10 address-family ipv4-unicast
+set protocols bgp neighbor 192.0.2.10 remote-as '65010'
+set protocols bgp neighbor 192.0.2.11 address-family ipv4-unicast
+set protocols bgp neighbor 192.0.2.11 remote-as '65011'
+set protocols bgp neighbor 2001:db8::10 address-family ipv4-unicast
+set protocols bgp neighbor 2001:db8::10 remote-as '65010'
+set protocols bgp neighbor 2001:db8::11 address-family ipv4-unicast
+set protocols bgp neighbor 2001:db8::11 remote-as '65011'
+set protocols bgp parameters log-neighbor-changes
+set protocols bgp system-as '65001'
+set service ssh disable-host-validation
+set service ssh port '22'
+set system config-management commit-revisions '200'
+set system conntrack modules ftp
+set system conntrack modules h323
+set system conntrack modules nfs
+set system conntrack modules pptp
+set system conntrack modules sip
+set system conntrack modules sqlnet
+set system conntrack modules tftp
+set system console device ttyS0 speed '115200'
+set system domain-name 'vyos.net'
+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 syslog global facility all level 'notice'
+set system syslog global facility local7 level 'debug'
diff --git a/smoketest/config-tests/cluster-basic b/smoketest/config-tests/cluster-basic
new file mode 100644
index 000000000..744c117eb
--- /dev/null
+++ b/smoketest/config-tests/cluster-basic
@@ -0,0 +1,21 @@
+set high-availability vrrp group VyOS address 192.0.2.10/24
+set high-availability vrrp group VyOS address 192.0.2.20/24
+set high-availability vrrp group VyOS advertise-interval '1'
+set high-availability vrrp group VyOS authentication password 'qwerty'
+set high-availability vrrp group VyOS authentication type 'plaintext-password'
+set high-availability vrrp group VyOS interface 'eth1'
+set high-availability vrrp group VyOS vrid '1'
+set interfaces ethernet eth0 duplex 'auto'
+set interfaces ethernet eth0 speed 'auto'
+set interfaces ethernet eth1 address '192.0.2.1/24'
+set interfaces ethernet eth1 duplex 'auto'
+set interfaces ethernet eth1 speed 'auto'
+set interfaces loopback lo
+set system config-management commit-revisions '100'
+set system console device ttyS0 speed '115200'
+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 syslog global facility all level 'info'
+set system syslog global facility local7 level 'debug'
+set system time-zone 'Antarctica/South_Pole'
diff --git a/smoketest/config-tests/container-simple b/smoketest/config-tests/container-simple
index 5af365cf9..fcc665100 100644
--- a/smoketest/config-tests/container-simple
+++ b/smoketest/config-tests/container-simple
@@ -1,8 +1,3 @@
-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'
@@ -11,4 +6,13 @@ set container name c02 allow-host-networks
set container name c02 allow-host-pid
set container name c02 capability 'sys-time'
set container name c02 image 'busybox:stable'
-set container name c02 sysctl parameter kernel.msgmax value '8192' \ No newline at end of file
+set container name c02 sysctl parameter kernel.msgmax value '8192'
+set interfaces ethernet eth0 duplex 'auto'
+set interfaces ethernet eth0 speed 'auto'
+set interfaces ethernet eth1 duplex 'auto'
+set interfaces ethernet eth1 speed 'auto'
+set system config-management commit-revisions '50'
+set system console device ttyS0 speed '115200'
+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 ''
diff --git a/smoketest/config-tests/dialup-router-complex b/smoketest/config-tests/dialup-router-complex
new file mode 100644
index 000000000..4416ef82e
--- /dev/null
+++ b/smoketest/config-tests/dialup-router-complex
@@ -0,0 +1,740 @@
+set firewall global-options all-ping 'enable'
+set firewall global-options broadcast-ping 'disable'
+set firewall global-options ip-src-route 'disable'
+set firewall global-options ipv6-receive-redirects 'disable'
+set firewall global-options ipv6-src-route 'disable'
+set firewall global-options log-martians 'enable'
+set firewall global-options receive-redirects 'disable'
+set firewall global-options send-redirects 'enable'
+set firewall global-options source-validation 'disable'
+set firewall global-options syn-cookies 'enable'
+set firewall global-options timeout icmp '30'
+set firewall global-options timeout other '600'
+set firewall global-options timeout udp other '300'
+set firewall global-options timeout udp stream '300'
+set firewall global-options twa-hazards-protection 'disable'
+set firewall group address-group AUDIO-STREAM address '172.16.35.20'
+set firewall group address-group AUDIO-STREAM address '172.16.35.21'
+set firewall group address-group AUDIO-STREAM address '172.16.35.22'
+set firewall group address-group AUDIO-STREAM address '172.16.35.23'
+set firewall group address-group DMZ-RDP-SERVER address '172.16.33.40'
+set firewall group address-group DMZ-WEBSERVER address '172.16.36.10'
+set firewall group address-group DMZ-WEBSERVER address '172.16.36.40'
+set firewall group address-group DMZ-WEBSERVER address '172.16.36.20'
+set firewall group address-group DOMAIN-CONTROLLER address '172.16.100.10'
+set firewall group address-group DOMAIN-CONTROLLER address '172.16.100.20'
+set firewall group address-group MEDIA-STREAMING-CLIENTS address '172.16.35.241'
+set firewall group address-group MEDIA-STREAMING-CLIENTS address '172.16.35.242'
+set firewall group address-group MEDIA-STREAMING-CLIENTS address '172.16.35.243'
+set firewall group ipv6-network-group LOCAL-ADDRESSES network 'ff02::/64'
+set firewall group ipv6-network-group LOCAL-ADDRESSES network 'fe80::/10'
+set firewall group network-group SSH-IN-ALLOW network '192.0.2.0/24'
+set firewall group network-group SSH-IN-ALLOW network '10.0.0.0/8'
+set firewall group network-group SSH-IN-ALLOW network '172.16.0.0/12'
+set firewall group network-group SSH-IN-ALLOW network '192.168.0.0/16'
+set firewall group port-group SMART-TV-PORTS port '5005-5006'
+set firewall group port-group SMART-TV-PORTS port '80'
+set firewall group port-group SMART-TV-PORTS port '443'
+set firewall group port-group SMART-TV-PORTS port '3722'
+set firewall ipv4 name DMZ-GUEST default-action 'drop'
+set firewall ipv4 name DMZ-GUEST default-log
+set firewall ipv4 name DMZ-GUEST rule 1 action 'return'
+set firewall ipv4 name DMZ-GUEST rule 1 state 'established'
+set firewall ipv4 name DMZ-GUEST rule 1 state 'related'
+set firewall ipv4 name DMZ-GUEST rule 2 action 'drop'
+set firewall ipv4 name DMZ-GUEST rule 2 log
+set firewall ipv4 name DMZ-GUEST rule 2 state 'invalid'
+set firewall ipv4 name DMZ-LAN default-action 'drop'
+set firewall ipv4 name DMZ-LAN default-log
+set firewall ipv4 name DMZ-LAN rule 1 action 'return'
+set firewall ipv4 name DMZ-LAN rule 1 state 'established'
+set firewall ipv4 name DMZ-LAN rule 1 state 'related'
+set firewall ipv4 name DMZ-LAN rule 2 action 'drop'
+set firewall ipv4 name DMZ-LAN rule 2 log
+set firewall ipv4 name DMZ-LAN rule 2 state 'invalid'
+set firewall ipv4 name DMZ-LAN rule 100 action 'return'
+set firewall ipv4 name DMZ-LAN rule 100 description 'NTP and LDAP to AD DC'
+set firewall ipv4 name DMZ-LAN rule 100 destination group address-group 'DOMAIN-CONTROLLER'
+set firewall ipv4 name DMZ-LAN rule 100 destination port '123,389,636'
+set firewall ipv4 name DMZ-LAN rule 100 protocol 'tcp_udp'
+set firewall ipv4 name DMZ-LAN rule 300 action 'return'
+set firewall ipv4 name DMZ-LAN rule 300 destination group address-group 'DMZ-RDP-SERVER'
+set firewall ipv4 name DMZ-LAN rule 300 destination port '3389'
+set firewall ipv4 name DMZ-LAN rule 300 protocol 'tcp_udp'
+set firewall ipv4 name DMZ-LAN rule 300 source address '172.16.36.20'
+set firewall ipv4 name DMZ-LOCAL default-action 'drop'
+set firewall ipv4 name DMZ-LOCAL default-log
+set firewall ipv4 name DMZ-LOCAL rule 1 action 'return'
+set firewall ipv4 name DMZ-LOCAL rule 1 state 'established'
+set firewall ipv4 name DMZ-LOCAL rule 1 state 'related'
+set firewall ipv4 name DMZ-LOCAL rule 2 action 'drop'
+set firewall ipv4 name DMZ-LOCAL rule 2 log
+set firewall ipv4 name DMZ-LOCAL rule 2 state 'invalid'
+set firewall ipv4 name DMZ-LOCAL rule 50 action 'return'
+set firewall ipv4 name DMZ-LOCAL rule 50 destination address '172.16.254.30'
+set firewall ipv4 name DMZ-LOCAL rule 50 destination port '53'
+set firewall ipv4 name DMZ-LOCAL rule 50 protocol 'tcp_udp'
+set firewall ipv4 name DMZ-LOCAL rule 123 action 'return'
+set firewall ipv4 name DMZ-LOCAL rule 123 destination port '123'
+set firewall ipv4 name DMZ-LOCAL rule 123 protocol 'udp'
+set firewall ipv4 name DMZ-LOCAL rule 800 action 'drop'
+set firewall ipv4 name DMZ-LOCAL rule 800 description 'SSH anti brute force'
+set firewall ipv4 name DMZ-LOCAL rule 800 destination port 'ssh'
+set firewall ipv4 name DMZ-LOCAL rule 800 log
+set firewall ipv4 name DMZ-LOCAL rule 800 protocol 'tcp'
+set firewall ipv4 name DMZ-LOCAL rule 800 recent count '4'
+set firewall ipv4 name DMZ-LOCAL rule 800 recent time 'minute'
+set firewall ipv4 name DMZ-LOCAL rule 800 state 'new'
+set firewall ipv4 name DMZ-WAN default-action 'return'
+set firewall ipv4 name GUEST-DMZ default-action 'drop'
+set firewall ipv4 name GUEST-DMZ default-log
+set firewall ipv4 name GUEST-DMZ rule 1 action 'return'
+set firewall ipv4 name GUEST-DMZ rule 1 state 'established'
+set firewall ipv4 name GUEST-DMZ rule 1 state 'related'
+set firewall ipv4 name GUEST-DMZ rule 2 action 'drop'
+set firewall ipv4 name GUEST-DMZ rule 2 log
+set firewall ipv4 name GUEST-DMZ rule 2 state 'invalid'
+set firewall ipv4 name GUEST-DMZ rule 100 action 'return'
+set firewall ipv4 name GUEST-DMZ rule 100 destination port '80,443'
+set firewall ipv4 name GUEST-DMZ rule 100 protocol 'tcp'
+set firewall ipv4 name GUEST-IOT default-action 'drop'
+set firewall ipv4 name GUEST-IOT default-log
+set firewall ipv4 name GUEST-IOT rule 1 action 'return'
+set firewall ipv4 name GUEST-IOT rule 1 state 'established'
+set firewall ipv4 name GUEST-IOT rule 1 state 'related'
+set firewall ipv4 name GUEST-IOT rule 2 action 'drop'
+set firewall ipv4 name GUEST-IOT rule 2 log
+set firewall ipv4 name GUEST-IOT rule 2 state 'invalid'
+set firewall ipv4 name GUEST-IOT rule 100 action 'return'
+set firewall ipv4 name GUEST-IOT rule 100 description 'MEDIA-STREAMING-CLIENTS Devices to GUEST'
+set firewall ipv4 name GUEST-IOT rule 100 destination group address-group 'MEDIA-STREAMING-CLIENTS'
+set firewall ipv4 name GUEST-IOT rule 100 protocol 'tcp_udp'
+set firewall ipv4 name GUEST-IOT rule 110 action 'return'
+set firewall ipv4 name GUEST-IOT rule 110 description 'AUDIO-STREAM Devices to GUEST'
+set firewall ipv4 name GUEST-IOT rule 110 destination group address-group 'AUDIO-STREAM'
+set firewall ipv4 name GUEST-IOT rule 110 protocol 'tcp_udp'
+set firewall ipv4 name GUEST-IOT rule 200 action 'return'
+set firewall ipv4 name GUEST-IOT rule 200 description 'MCAST relay'
+set firewall ipv4 name GUEST-IOT rule 200 destination address '224.0.0.251'
+set firewall ipv4 name GUEST-IOT rule 200 destination port '5353'
+set firewall ipv4 name GUEST-IOT rule 200 protocol 'udp'
+set firewall ipv4 name GUEST-IOT rule 300 action 'return'
+set firewall ipv4 name GUEST-IOT rule 300 description 'BCAST relay'
+set firewall ipv4 name GUEST-IOT rule 300 destination port '1900'
+set firewall ipv4 name GUEST-IOT rule 300 protocol 'udp'
+set firewall ipv4 name GUEST-LAN default-action 'drop'
+set firewall ipv4 name GUEST-LAN default-log
+set firewall ipv4 name GUEST-LAN rule 1 action 'return'
+set firewall ipv4 name GUEST-LAN rule 1 state 'established'
+set firewall ipv4 name GUEST-LAN rule 1 state 'related'
+set firewall ipv4 name GUEST-LAN rule 2 action 'drop'
+set firewall ipv4 name GUEST-LAN rule 2 log
+set firewall ipv4 name GUEST-LAN rule 2 state 'invalid'
+set firewall ipv4 name GUEST-LOCAL default-action 'drop'
+set firewall ipv4 name GUEST-LOCAL default-log
+set firewall ipv4 name GUEST-LOCAL rule 1 action 'return'
+set firewall ipv4 name GUEST-LOCAL rule 1 state 'established'
+set firewall ipv4 name GUEST-LOCAL rule 1 state 'related'
+set firewall ipv4 name GUEST-LOCAL rule 2 action 'drop'
+set firewall ipv4 name GUEST-LOCAL rule 2 log
+set firewall ipv4 name GUEST-LOCAL rule 2 state 'invalid'
+set firewall ipv4 name GUEST-LOCAL rule 10 action 'return'
+set firewall ipv4 name GUEST-LOCAL rule 10 description 'DNS'
+set firewall ipv4 name GUEST-LOCAL rule 10 destination address '172.31.0.254'
+set firewall ipv4 name GUEST-LOCAL rule 10 destination port '53'
+set firewall ipv4 name GUEST-LOCAL rule 10 protocol 'tcp_udp'
+set firewall ipv4 name GUEST-LOCAL rule 11 action 'return'
+set firewall ipv4 name GUEST-LOCAL rule 11 description 'DHCP'
+set firewall ipv4 name GUEST-LOCAL rule 11 destination port '67'
+set firewall ipv4 name GUEST-LOCAL rule 11 protocol 'udp'
+set firewall ipv4 name GUEST-LOCAL rule 15 action 'return'
+set firewall ipv4 name GUEST-LOCAL rule 15 destination address '172.31.0.254'
+set firewall ipv4 name GUEST-LOCAL rule 15 protocol 'icmp'
+set firewall ipv4 name GUEST-LOCAL rule 200 action 'return'
+set firewall ipv4 name GUEST-LOCAL rule 200 description 'MCAST relay'
+set firewall ipv4 name GUEST-LOCAL rule 200 destination address '224.0.0.251'
+set firewall ipv4 name GUEST-LOCAL rule 200 destination port '5353'
+set firewall ipv4 name GUEST-LOCAL rule 200 protocol 'udp'
+set firewall ipv4 name GUEST-LOCAL rule 210 action 'return'
+set firewall ipv4 name GUEST-LOCAL rule 210 description 'AUDIO-STREAM Broadcast'
+set firewall ipv4 name GUEST-LOCAL rule 210 destination port '1900'
+set firewall ipv4 name GUEST-LOCAL rule 210 protocol 'udp'
+set firewall ipv4 name GUEST-WAN default-action 'drop'
+set firewall ipv4 name GUEST-WAN default-log
+set firewall ipv4 name GUEST-WAN rule 1 action 'return'
+set firewall ipv4 name GUEST-WAN rule 1 state 'established'
+set firewall ipv4 name GUEST-WAN rule 1 state 'related'
+set firewall ipv4 name GUEST-WAN rule 2 action 'drop'
+set firewall ipv4 name GUEST-WAN rule 2 log
+set firewall ipv4 name GUEST-WAN rule 2 state 'invalid'
+set firewall ipv4 name GUEST-WAN rule 25 action 'return'
+set firewall ipv4 name GUEST-WAN rule 25 description 'SMTP'
+set firewall ipv4 name GUEST-WAN rule 25 destination port '25,587'
+set firewall ipv4 name GUEST-WAN rule 25 protocol 'tcp'
+set firewall ipv4 name GUEST-WAN rule 53 action 'return'
+set firewall ipv4 name GUEST-WAN rule 53 destination port '53'
+set firewall ipv4 name GUEST-WAN rule 53 protocol 'tcp_udp'
+set firewall ipv4 name GUEST-WAN rule 60 action 'return'
+set firewall ipv4 name GUEST-WAN rule 60 source address '172.31.0.200'
+set firewall ipv4 name GUEST-WAN rule 80 action 'return'
+set firewall ipv4 name GUEST-WAN rule 80 source address '172.31.0.200'
+set firewall ipv4 name GUEST-WAN rule 100 action 'return'
+set firewall ipv4 name GUEST-WAN rule 100 protocol 'icmp'
+set firewall ipv4 name GUEST-WAN rule 110 action 'return'
+set firewall ipv4 name GUEST-WAN rule 110 description 'POP3'
+set firewall ipv4 name GUEST-WAN rule 110 destination port '110,995'
+set firewall ipv4 name GUEST-WAN rule 110 limit rate '10/minute'
+set firewall ipv4 name GUEST-WAN rule 110 protocol 'tcp'
+set firewall ipv4 name GUEST-WAN rule 123 action 'return'
+set firewall ipv4 name GUEST-WAN rule 123 description 'NTP Client'
+set firewall ipv4 name GUEST-WAN rule 123 destination port '123'
+set firewall ipv4 name GUEST-WAN rule 123 protocol 'udp'
+set firewall ipv4 name GUEST-WAN rule 143 action 'return'
+set firewall ipv4 name GUEST-WAN rule 143 description 'IMAP'
+set firewall ipv4 name GUEST-WAN rule 143 destination port '143,993'
+set firewall ipv4 name GUEST-WAN rule 143 protocol 'tcp'
+set firewall ipv4 name GUEST-WAN rule 200 action 'return'
+set firewall ipv4 name GUEST-WAN rule 200 destination port '80,443'
+set firewall ipv4 name GUEST-WAN rule 200 protocol 'tcp'
+set firewall ipv4 name GUEST-WAN rule 500 action 'return'
+set firewall ipv4 name GUEST-WAN rule 500 description 'L2TP IPSec'
+set firewall ipv4 name GUEST-WAN rule 500 destination port '500,4500'
+set firewall ipv4 name GUEST-WAN rule 500 protocol 'udp'
+set firewall ipv4 name GUEST-WAN rule 600 action 'return'
+set firewall ipv4 name GUEST-WAN rule 600 destination port '5222-5224'
+set firewall ipv4 name GUEST-WAN rule 600 protocol 'tcp'
+set firewall ipv4 name GUEST-WAN rule 601 action 'return'
+set firewall ipv4 name GUEST-WAN rule 601 destination port '3478-3497,4500,16384-16387,16393-16402'
+set firewall ipv4 name GUEST-WAN rule 601 protocol 'udp'
+set firewall ipv4 name GUEST-WAN rule 1000 action 'return'
+set firewall ipv4 name GUEST-WAN rule 1000 source address '172.31.0.184'
+set firewall ipv4 name IOT-GUEST default-action 'drop'
+set firewall ipv4 name IOT-GUEST default-log
+set firewall ipv4 name IOT-GUEST rule 1 action 'return'
+set firewall ipv4 name IOT-GUEST rule 1 state 'established'
+set firewall ipv4 name IOT-GUEST rule 1 state 'related'
+set firewall ipv4 name IOT-GUEST rule 2 action 'drop'
+set firewall ipv4 name IOT-GUEST rule 2 log
+set firewall ipv4 name IOT-GUEST rule 2 state 'invalid'
+set firewall ipv4 name IOT-GUEST rule 100 action 'return'
+set firewall ipv4 name IOT-GUEST rule 100 description 'MEDIA-STREAMING-CLIENTS Devices to IOT'
+set firewall ipv4 name IOT-GUEST rule 100 protocol 'tcp_udp'
+set firewall ipv4 name IOT-GUEST rule 100 source group address-group 'MEDIA-STREAMING-CLIENTS'
+set firewall ipv4 name IOT-GUEST rule 110 action 'return'
+set firewall ipv4 name IOT-GUEST rule 110 description 'AUDIO-STREAM Devices to IOT'
+set firewall ipv4 name IOT-GUEST rule 110 protocol 'tcp_udp'
+set firewall ipv4 name IOT-GUEST rule 110 source group address-group 'AUDIO-STREAM'
+set firewall ipv4 name IOT-GUEST rule 200 action 'return'
+set firewall ipv4 name IOT-GUEST rule 200 description 'MCAST relay'
+set firewall ipv4 name IOT-GUEST rule 200 destination address '224.0.0.251'
+set firewall ipv4 name IOT-GUEST rule 200 destination port '5353'
+set firewall ipv4 name IOT-GUEST rule 200 protocol 'udp'
+set firewall ipv4 name IOT-GUEST rule 300 action 'return'
+set firewall ipv4 name IOT-GUEST rule 300 description 'BCAST relay'
+set firewall ipv4 name IOT-GUEST rule 300 destination port '1900'
+set firewall ipv4 name IOT-GUEST rule 300 protocol 'udp'
+set firewall ipv4 name IOT-LAN default-action 'drop'
+set firewall ipv4 name IOT-LAN default-log
+set firewall ipv4 name IOT-LAN rule 1 action 'return'
+set firewall ipv4 name IOT-LAN rule 1 state 'established'
+set firewall ipv4 name IOT-LAN rule 1 state 'related'
+set firewall ipv4 name IOT-LAN rule 2 action 'drop'
+set firewall ipv4 name IOT-LAN rule 2 log
+set firewall ipv4 name IOT-LAN rule 2 state 'invalid'
+set firewall ipv4 name IOT-LAN rule 100 action 'return'
+set firewall ipv4 name IOT-LAN rule 100 description 'AppleTV to LAN'
+set firewall ipv4 name IOT-LAN rule 100 destination group port-group 'SMART-TV-PORTS'
+set firewall ipv4 name IOT-LAN rule 100 protocol 'tcp_udp'
+set firewall ipv4 name IOT-LAN rule 100 source group address-group 'MEDIA-STREAMING-CLIENTS'
+set firewall ipv4 name IOT-LAN rule 110 action 'return'
+set firewall ipv4 name IOT-LAN rule 110 description 'AUDIO-STREAM Devices to LAN'
+set firewall ipv4 name IOT-LAN rule 110 protocol 'tcp_udp'
+set firewall ipv4 name IOT-LAN rule 110 source group address-group 'AUDIO-STREAM'
+set firewall ipv4 name IOT-LOCAL default-action 'drop'
+set firewall ipv4 name IOT-LOCAL default-log
+set firewall ipv4 name IOT-LOCAL rule 1 action 'return'
+set firewall ipv4 name IOT-LOCAL rule 1 state 'established'
+set firewall ipv4 name IOT-LOCAL rule 1 state 'related'
+set firewall ipv4 name IOT-LOCAL rule 2 action 'drop'
+set firewall ipv4 name IOT-LOCAL rule 2 log
+set firewall ipv4 name IOT-LOCAL rule 2 state 'invalid'
+set firewall ipv4 name IOT-LOCAL rule 10 action 'return'
+set firewall ipv4 name IOT-LOCAL rule 10 description 'DNS'
+set firewall ipv4 name IOT-LOCAL rule 10 destination address '172.16.254.30'
+set firewall ipv4 name IOT-LOCAL rule 10 destination port '53'
+set firewall ipv4 name IOT-LOCAL rule 10 protocol 'tcp_udp'
+set firewall ipv4 name IOT-LOCAL rule 11 action 'return'
+set firewall ipv4 name IOT-LOCAL rule 11 description 'DHCP'
+set firewall ipv4 name IOT-LOCAL rule 11 destination port '67'
+set firewall ipv4 name IOT-LOCAL rule 11 protocol 'udp'
+set firewall ipv4 name IOT-LOCAL rule 15 action 'return'
+set firewall ipv4 name IOT-LOCAL rule 15 destination address '172.16.35.254'
+set firewall ipv4 name IOT-LOCAL rule 15 protocol 'icmp'
+set firewall ipv4 name IOT-LOCAL rule 200 action 'return'
+set firewall ipv4 name IOT-LOCAL rule 200 description 'MCAST relay'
+set firewall ipv4 name IOT-LOCAL rule 200 destination address '224.0.0.251'
+set firewall ipv4 name IOT-LOCAL rule 200 destination port '5353'
+set firewall ipv4 name IOT-LOCAL rule 200 protocol 'udp'
+set firewall ipv4 name IOT-LOCAL rule 201 action 'return'
+set firewall ipv4 name IOT-LOCAL rule 201 description 'MCAST relay'
+set firewall ipv4 name IOT-LOCAL rule 201 destination address '172.16.35.254'
+set firewall ipv4 name IOT-LOCAL rule 201 destination port '5353'
+set firewall ipv4 name IOT-LOCAL rule 201 protocol 'udp'
+set firewall ipv4 name IOT-LOCAL rule 210 action 'return'
+set firewall ipv4 name IOT-LOCAL rule 210 description 'AUDIO-STREAM Broadcast'
+set firewall ipv4 name IOT-LOCAL rule 210 destination port '1900,1902,6969'
+set firewall ipv4 name IOT-LOCAL rule 210 protocol 'udp'
+set firewall ipv4 name IOT-WAN default-action 'return'
+set firewall ipv4 name LAN-DMZ default-action 'drop'
+set firewall ipv4 name LAN-DMZ default-log
+set firewall ipv4 name LAN-DMZ rule 1 action 'return'
+set firewall ipv4 name LAN-DMZ rule 1 state 'established'
+set firewall ipv4 name LAN-DMZ rule 1 state 'related'
+set firewall ipv4 name LAN-DMZ rule 2 action 'drop'
+set firewall ipv4 name LAN-DMZ rule 2 log
+set firewall ipv4 name LAN-DMZ rule 2 state 'invalid'
+set firewall ipv4 name LAN-DMZ rule 22 action 'return'
+set firewall ipv4 name LAN-DMZ rule 22 description 'SSH into DMZ'
+set firewall ipv4 name LAN-DMZ rule 22 destination port '22'
+set firewall ipv4 name LAN-DMZ rule 22 protocol 'tcp'
+set firewall ipv4 name LAN-DMZ rule 100 action 'return'
+set firewall ipv4 name LAN-DMZ rule 100 destination group address-group 'DMZ-WEBSERVER'
+set firewall ipv4 name LAN-DMZ rule 100 destination port '22,80,443'
+set firewall ipv4 name LAN-DMZ rule 100 protocol 'tcp'
+set firewall ipv4 name LAN-GUEST default-action 'drop'
+set firewall ipv4 name LAN-GUEST default-log
+set firewall ipv4 name LAN-GUEST rule 1 action 'return'
+set firewall ipv4 name LAN-GUEST rule 1 state 'established'
+set firewall ipv4 name LAN-GUEST rule 1 state 'related'
+set firewall ipv4 name LAN-GUEST rule 2 action 'drop'
+set firewall ipv4 name LAN-GUEST rule 2 log
+set firewall ipv4 name LAN-GUEST rule 2 state 'invalid'
+set firewall ipv4 name LAN-IOT default-action 'return'
+set firewall ipv4 name LAN-LOCAL default-action 'return'
+set firewall ipv4 name LAN-WAN default-action 'return'
+set firewall ipv4 name LOCAL-DMZ default-action 'drop'
+set firewall ipv4 name LOCAL-DMZ default-log
+set firewall ipv4 name LOCAL-DMZ rule 1 action 'return'
+set firewall ipv4 name LOCAL-DMZ rule 1 state 'established'
+set firewall ipv4 name LOCAL-DMZ rule 1 state 'related'
+set firewall ipv4 name LOCAL-DMZ rule 2 action 'drop'
+set firewall ipv4 name LOCAL-DMZ rule 2 log
+set firewall ipv4 name LOCAL-DMZ rule 2 state 'invalid'
+set firewall ipv4 name LOCAL-GUEST default-action 'drop'
+set firewall ipv4 name LOCAL-GUEST default-log
+set firewall ipv4 name LOCAL-GUEST rule 1 action 'return'
+set firewall ipv4 name LOCAL-GUEST rule 1 state 'established'
+set firewall ipv4 name LOCAL-GUEST rule 1 state 'related'
+set firewall ipv4 name LOCAL-GUEST rule 2 action 'drop'
+set firewall ipv4 name LOCAL-GUEST rule 2 log
+set firewall ipv4 name LOCAL-GUEST rule 2 state 'invalid'
+set firewall ipv4 name LOCAL-GUEST rule 5 action 'return'
+set firewall ipv4 name LOCAL-GUEST rule 5 protocol 'icmp'
+set firewall ipv4 name LOCAL-GUEST rule 200 action 'return'
+set firewall ipv4 name LOCAL-GUEST rule 200 description 'MCAST relay'
+set firewall ipv4 name LOCAL-GUEST rule 200 destination address '224.0.0.251'
+set firewall ipv4 name LOCAL-GUEST rule 200 destination port '5353'
+set firewall ipv4 name LOCAL-GUEST rule 200 protocol 'udp'
+set firewall ipv4 name LOCAL-GUEST rule 300 action 'return'
+set firewall ipv4 name LOCAL-GUEST rule 300 description 'BCAST relay'
+set firewall ipv4 name LOCAL-GUEST rule 300 destination port '1900'
+set firewall ipv4 name LOCAL-GUEST rule 300 protocol 'udp'
+set firewall ipv4 name LOCAL-IOT default-action 'drop'
+set firewall ipv4 name LOCAL-IOT default-log
+set firewall ipv4 name LOCAL-IOT rule 1 action 'return'
+set firewall ipv4 name LOCAL-IOT rule 1 state 'established'
+set firewall ipv4 name LOCAL-IOT rule 1 state 'related'
+set firewall ipv4 name LOCAL-IOT rule 2 action 'drop'
+set firewall ipv4 name LOCAL-IOT rule 2 log
+set firewall ipv4 name LOCAL-IOT rule 2 state 'invalid'
+set firewall ipv4 name LOCAL-IOT rule 5 action 'return'
+set firewall ipv4 name LOCAL-IOT rule 5 protocol 'icmp'
+set firewall ipv4 name LOCAL-IOT rule 200 action 'return'
+set firewall ipv4 name LOCAL-IOT rule 200 description 'MCAST relay'
+set firewall ipv4 name LOCAL-IOT rule 200 destination address '224.0.0.251'
+set firewall ipv4 name LOCAL-IOT rule 200 destination port '5353'
+set firewall ipv4 name LOCAL-IOT rule 200 protocol 'udp'
+set firewall ipv4 name LOCAL-IOT rule 300 action 'return'
+set firewall ipv4 name LOCAL-IOT rule 300 description 'BCAST relay'
+set firewall ipv4 name LOCAL-IOT rule 300 destination port '1900,6969'
+set firewall ipv4 name LOCAL-IOT rule 300 protocol 'udp'
+set firewall ipv4 name LOCAL-LAN default-action 'return'
+set firewall ipv4 name LOCAL-WAN default-action 'drop'
+set firewall ipv4 name LOCAL-WAN default-log
+set firewall ipv4 name LOCAL-WAN rule 1 action 'return'
+set firewall ipv4 name LOCAL-WAN rule 1 state 'established'
+set firewall ipv4 name LOCAL-WAN rule 1 state 'related'
+set firewall ipv4 name LOCAL-WAN rule 2 action 'drop'
+set firewall ipv4 name LOCAL-WAN rule 2 log
+set firewall ipv4 name LOCAL-WAN rule 2 state 'invalid'
+set firewall ipv4 name LOCAL-WAN rule 10 action 'return'
+set firewall ipv4 name LOCAL-WAN rule 10 protocol 'icmp'
+set firewall ipv4 name LOCAL-WAN rule 50 action 'return'
+set firewall ipv4 name LOCAL-WAN rule 50 description 'DNS'
+set firewall ipv4 name LOCAL-WAN rule 50 destination port '53'
+set firewall ipv4 name LOCAL-WAN rule 50 protocol 'tcp_udp'
+set firewall ipv4 name LOCAL-WAN rule 80 action 'return'
+set firewall ipv4 name LOCAL-WAN rule 80 destination port '80,443'
+set firewall ipv4 name LOCAL-WAN rule 80 protocol 'tcp'
+set firewall ipv4 name LOCAL-WAN rule 123 action 'return'
+set firewall ipv4 name LOCAL-WAN rule 123 description 'NTP'
+set firewall ipv4 name LOCAL-WAN rule 123 destination port '123'
+set firewall ipv4 name LOCAL-WAN rule 123 protocol 'udp'
+set firewall ipv4 name WAN-DMZ default-action 'drop'
+set firewall ipv4 name WAN-DMZ default-log
+set firewall ipv4 name WAN-DMZ rule 1 action 'return'
+set firewall ipv4 name WAN-DMZ rule 1 state 'established'
+set firewall ipv4 name WAN-DMZ rule 1 state 'related'
+set firewall ipv4 name WAN-DMZ rule 2 action 'drop'
+set firewall ipv4 name WAN-DMZ rule 2 log
+set firewall ipv4 name WAN-DMZ rule 2 state 'invalid'
+set firewall ipv4 name WAN-DMZ rule 100 action 'return'
+set firewall ipv4 name WAN-DMZ rule 100 destination address '172.16.36.10'
+set firewall ipv4 name WAN-DMZ rule 100 destination port '80,443'
+set firewall ipv4 name WAN-DMZ rule 100 protocol 'tcp'
+set firewall ipv4 name WAN-GUEST default-action 'drop'
+set firewall ipv4 name WAN-GUEST default-log
+set firewall ipv4 name WAN-GUEST rule 1 action 'return'
+set firewall ipv4 name WAN-GUEST rule 1 state 'established'
+set firewall ipv4 name WAN-GUEST rule 1 state 'related'
+set firewall ipv4 name WAN-GUEST rule 2 action 'drop'
+set firewall ipv4 name WAN-GUEST rule 2 log
+set firewall ipv4 name WAN-GUEST rule 2 state 'invalid'
+set firewall ipv4 name WAN-GUEST rule 1000 action 'return'
+set firewall ipv4 name WAN-GUEST rule 1000 destination address '172.31.0.184'
+set firewall ipv4 name WAN-GUEST rule 8000 action 'return'
+set firewall ipv4 name WAN-GUEST rule 8000 destination address '172.31.0.200'
+set firewall ipv4 name WAN-GUEST rule 8000 destination port '10000'
+set firewall ipv4 name WAN-GUEST rule 8000 protocol 'udp'
+set firewall ipv4 name WAN-IOT default-action 'drop'
+set firewall ipv4 name WAN-IOT default-log
+set firewall ipv4 name WAN-IOT rule 1 action 'return'
+set firewall ipv4 name WAN-IOT rule 1 state 'established'
+set firewall ipv4 name WAN-IOT rule 1 state 'related'
+set firewall ipv4 name WAN-IOT rule 2 action 'drop'
+set firewall ipv4 name WAN-IOT rule 2 log
+set firewall ipv4 name WAN-IOT rule 2 state 'invalid'
+set firewall ipv4 name WAN-LAN default-action 'drop'
+set firewall ipv4 name WAN-LAN default-log
+set firewall ipv4 name WAN-LAN rule 1 action 'return'
+set firewall ipv4 name WAN-LAN rule 1 state 'established'
+set firewall ipv4 name WAN-LAN rule 1 state 'related'
+set firewall ipv4 name WAN-LAN rule 2 action 'drop'
+set firewall ipv4 name WAN-LAN rule 2 log
+set firewall ipv4 name WAN-LAN rule 2 state 'invalid'
+set firewall ipv4 name WAN-LAN rule 1000 action 'return'
+set firewall ipv4 name WAN-LAN rule 1000 destination address '172.16.33.40'
+set firewall ipv4 name WAN-LAN rule 1000 destination port '3389'
+set firewall ipv4 name WAN-LAN rule 1000 protocol 'tcp'
+set firewall ipv4 name WAN-LAN rule 1000 source group network-group 'SSH-IN-ALLOW'
+set firewall ipv4 name WAN-LOCAL default-action 'drop'
+set firewall ipv4 name WAN-LOCAL default-log
+set firewall ipv4 name WAN-LOCAL rule 1 action 'return'
+set firewall ipv4 name WAN-LOCAL rule 1 state 'established'
+set firewall ipv4 name WAN-LOCAL rule 1 state 'related'
+set firewall ipv4 name WAN-LOCAL rule 2 action 'drop'
+set firewall ipv4 name WAN-LOCAL rule 2 log
+set firewall ipv4 name WAN-LOCAL rule 2 state 'invalid'
+set firewall ipv4 name WAN-LOCAL rule 22 action 'return'
+set firewall ipv4 name WAN-LOCAL rule 22 destination port '22'
+set firewall ipv4 name WAN-LOCAL rule 22 protocol 'tcp'
+set firewall ipv4 name WAN-LOCAL rule 22 source group network-group 'SSH-IN-ALLOW'
+set firewall ipv6 name ALLOW-ALL-6 default-action 'return'
+set firewall ipv6 name ALLOW-BASIC-6 default-action 'drop'
+set firewall ipv6 name ALLOW-BASIC-6 default-log
+set firewall ipv6 name ALLOW-BASIC-6 rule 1 action 'return'
+set firewall ipv6 name ALLOW-BASIC-6 rule 1 state 'established'
+set firewall ipv6 name ALLOW-BASIC-6 rule 1 state 'related'
+set firewall ipv6 name ALLOW-BASIC-6 rule 2 action 'drop'
+set firewall ipv6 name ALLOW-BASIC-6 rule 2 state 'invalid'
+set firewall ipv6 name ALLOW-BASIC-6 rule 10 action 'return'
+set firewall ipv6 name ALLOW-BASIC-6 rule 10 protocol 'ipv6-icmp'
+set firewall ipv6 name ALLOW-BASIC-6 rule 15 action 'return'
+set firewall ipv6 name ALLOW-BASIC-6 rule 15 icmpv6 type '1'
+set firewall ipv6 name ALLOW-BASIC-6 rule 15 protocol 'ipv6-icmp'
+set firewall ipv6 name ALLOW-BASIC-6 rule 16 action 'return'
+set firewall ipv6 name ALLOW-BASIC-6 rule 16 icmpv6 code '1'
+set firewall ipv6 name ALLOW-BASIC-6 rule 16 icmpv6 type '1'
+set firewall ipv6 name ALLOW-BASIC-6 rule 16 protocol 'ipv6-icmp'
+set firewall ipv6 name ALLOW-BASIC-6 rule 17 action 'return'
+set firewall ipv6 name ALLOW-BASIC-6 rule 17 icmpv6 type-name 'destination-unreachable'
+set firewall ipv6 name ALLOW-BASIC-6 rule 17 protocol 'ipv6-icmp'
+set firewall ipv6 name ALLOW-ESTABLISHED-6 default-action 'drop'
+set firewall ipv6 name ALLOW-ESTABLISHED-6 default-log
+set firewall ipv6 name ALLOW-ESTABLISHED-6 rule 1 action 'return'
+set firewall ipv6 name ALLOW-ESTABLISHED-6 rule 1 state 'established'
+set firewall ipv6 name ALLOW-ESTABLISHED-6 rule 1 state 'related'
+set firewall ipv6 name ALLOW-ESTABLISHED-6 rule 2 action 'drop'
+set firewall ipv6 name ALLOW-ESTABLISHED-6 rule 2 state 'invalid'
+set firewall ipv6 name ALLOW-ESTABLISHED-6 rule 10 action 'return'
+set firewall ipv6 name ALLOW-ESTABLISHED-6 rule 10 destination group network-group 'LOCAL-ADDRESSES'
+set firewall ipv6 name ALLOW-ESTABLISHED-6 rule 10 protocol 'ipv6-icmp'
+set firewall ipv6 name ALLOW-ESTABLISHED-6 rule 10 source address 'fe80::/10'
+set firewall ipv6 name ALLOW-ESTABLISHED-6 rule 20 action 'return'
+set firewall ipv6 name ALLOW-ESTABLISHED-6 rule 20 icmpv6 type-name 'echo-request'
+set firewall ipv6 name ALLOW-ESTABLISHED-6 rule 20 protocol 'ipv6-icmp'
+set firewall ipv6 name ALLOW-ESTABLISHED-6 rule 21 action 'return'
+set firewall ipv6 name ALLOW-ESTABLISHED-6 rule 21 icmpv6 type-name 'destination-unreachable'
+set firewall ipv6 name ALLOW-ESTABLISHED-6 rule 21 protocol 'ipv6-icmp'
+set firewall ipv6 name ALLOW-ESTABLISHED-6 rule 22 action 'return'
+set firewall ipv6 name ALLOW-ESTABLISHED-6 rule 22 icmpv6 type-name 'packet-too-big'
+set firewall ipv6 name ALLOW-ESTABLISHED-6 rule 22 protocol 'ipv6-icmp'
+set firewall ipv6 name ALLOW-ESTABLISHED-6 rule 23 action 'return'
+set firewall ipv6 name ALLOW-ESTABLISHED-6 rule 23 icmpv6 type-name 'time-exceeded'
+set firewall ipv6 name ALLOW-ESTABLISHED-6 rule 23 protocol 'ipv6-icmp'
+set firewall ipv6 name ALLOW-ESTABLISHED-6 rule 24 action 'return'
+set firewall ipv6 name ALLOW-ESTABLISHED-6 rule 24 icmpv6 type-name 'parameter-problem'
+set firewall ipv6 name ALLOW-ESTABLISHED-6 rule 24 protocol 'ipv6-icmp'
+set firewall ipv6 name WAN-LOCAL-6 default-action 'drop'
+set firewall ipv6 name WAN-LOCAL-6 default-log
+set firewall ipv6 name WAN-LOCAL-6 rule 1 action 'return'
+set firewall ipv6 name WAN-LOCAL-6 rule 1 state 'established'
+set firewall ipv6 name WAN-LOCAL-6 rule 1 state 'related'
+set firewall ipv6 name WAN-LOCAL-6 rule 2 action 'drop'
+set firewall ipv6 name WAN-LOCAL-6 rule 2 state 'invalid'
+set firewall ipv6 name WAN-LOCAL-6 rule 10 action 'return'
+set firewall ipv6 name WAN-LOCAL-6 rule 10 destination address 'ff02::/64'
+set firewall ipv6 name WAN-LOCAL-6 rule 10 protocol 'ipv6-icmp'
+set firewall ipv6 name WAN-LOCAL-6 rule 10 source address 'fe80::/10'
+set firewall ipv6 name WAN-LOCAL-6 rule 50 action 'return'
+set firewall ipv6 name WAN-LOCAL-6 rule 50 description 'DHCPv6'
+set firewall ipv6 name WAN-LOCAL-6 rule 50 destination address 'fe80::/10'
+set firewall ipv6 name WAN-LOCAL-6 rule 50 destination port '546'
+set firewall ipv6 name WAN-LOCAL-6 rule 50 protocol 'udp'
+set firewall ipv6 name WAN-LOCAL-6 rule 50 source address 'fe80::/10'
+set firewall ipv6 name WAN-LOCAL-6 rule 50 source port '547'
+set firewall zone DMZ default-action 'drop'
+set firewall zone DMZ from GUEST firewall name 'GUEST-DMZ'
+set firewall zone DMZ from LAN firewall name 'LAN-DMZ'
+set firewall zone DMZ from LOCAL firewall name 'LOCAL-DMZ'
+set firewall zone DMZ from WAN firewall name 'WAN-DMZ'
+set firewall zone DMZ interface 'eth0.50'
+set firewall zone GUEST default-action 'drop'
+set firewall zone GUEST from DMZ firewall name 'DMZ-GUEST'
+set firewall zone GUEST from IOT firewall name 'IOT-GUEST'
+set firewall zone GUEST from LAN firewall name 'LAN-GUEST'
+set firewall zone GUEST from LOCAL firewall ipv6-name 'ALLOW-ALL-6'
+set firewall zone GUEST from LOCAL firewall name 'LOCAL-GUEST'
+set firewall zone GUEST from WAN firewall ipv6-name 'ALLOW-ESTABLISHED-6'
+set firewall zone GUEST from WAN firewall name 'WAN-GUEST'
+set firewall zone GUEST interface 'eth0.20'
+set firewall zone IOT default-action 'drop'
+set firewall zone IOT from GUEST firewall name 'GUEST-IOT'
+set firewall zone IOT from LAN firewall name 'LAN-IOT'
+set firewall zone IOT from LOCAL firewall name 'LOCAL-IOT'
+set firewall zone IOT from WAN firewall name 'WAN-IOT'
+set firewall zone IOT interface 'eth0.35'
+set firewall zone LAN default-action 'drop'
+set firewall zone LAN from DMZ firewall name 'DMZ-LAN'
+set firewall zone LAN from GUEST firewall name 'GUEST-LAN'
+set firewall zone LAN from IOT firewall name 'IOT-LAN'
+set firewall zone LAN from LOCAL firewall ipv6-name 'ALLOW-ALL-6'
+set firewall zone LAN from LOCAL firewall name 'LOCAL-LAN'
+set firewall zone LAN from WAN firewall ipv6-name 'ALLOW-ESTABLISHED-6'
+set firewall zone LAN from WAN firewall name 'WAN-LAN'
+set firewall zone LAN interface 'eth0.5'
+set firewall zone LAN interface 'eth0.10'
+set firewall zone LAN interface 'eth0.100'
+set firewall zone LAN interface 'eth0.201'
+set firewall zone LAN interface 'eth0.202'
+set firewall zone LAN interface 'eth0.203'
+set firewall zone LAN interface 'eth0.204'
+set firewall zone LOCAL default-action 'drop'
+set firewall zone LOCAL from DMZ firewall name 'DMZ-LOCAL'
+set firewall zone LOCAL from GUEST firewall ipv6-name 'ALLOW-ESTABLISHED-6'
+set firewall zone LOCAL from GUEST firewall name 'GUEST-LOCAL'
+set firewall zone LOCAL from IOT firewall name 'IOT-LOCAL'
+set firewall zone LOCAL from LAN firewall ipv6-name 'ALLOW-ALL-6'
+set firewall zone LOCAL from LAN firewall name 'LAN-LOCAL'
+set firewall zone LOCAL from WAN firewall ipv6-name 'WAN-LOCAL-6'
+set firewall zone LOCAL from WAN firewall name 'WAN-LOCAL'
+set firewall zone LOCAL local-zone
+set firewall zone WAN default-action 'drop'
+set firewall zone WAN from DMZ firewall name 'DMZ-WAN'
+set firewall zone WAN from GUEST firewall ipv6-name 'ALLOW-ALL-6'
+set firewall zone WAN from GUEST firewall name 'GUEST-WAN'
+set firewall zone WAN from IOT firewall name 'IOT-WAN'
+set firewall zone WAN from LAN firewall ipv6-name 'ALLOW-ALL-6'
+set firewall zone WAN from LAN firewall name 'LAN-WAN'
+set firewall zone WAN from LOCAL firewall ipv6-name 'ALLOW-ALL-6'
+set firewall zone WAN from LOCAL firewall name 'LOCAL-WAN'
+set firewall zone WAN interface 'pppoe0'
+set interfaces dummy dum0 address '172.16.254.30/32'
+set interfaces ethernet eth0 duplex 'auto'
+set interfaces ethernet eth0 speed 'auto'
+set interfaces ethernet eth0 vif 5 address '172.16.37.254/24'
+set interfaces ethernet eth0 vif 10 address '172.16.33.254/24'
+set interfaces ethernet eth0 vif 10 ip adjust-mss '1320'
+set interfaces ethernet eth0 vif 10 ipv6 adjust-mss '1300'
+set interfaces ethernet eth0 vif 20 address '172.31.0.254/24'
+set interfaces ethernet eth0 vif 35 address '172.16.35.254/24'
+set interfaces ethernet eth0 vif 50 address '172.16.36.254/24'
+set interfaces ethernet eth0 vif 100 address '172.16.100.254/24'
+set interfaces ethernet eth0 vif 201 address '172.18.201.254/24'
+set interfaces ethernet eth0 vif 202 address '172.18.202.254/24'
+set interfaces ethernet eth0 vif 203 address '172.18.203.254/24'
+set interfaces ethernet eth0 vif 204 address '172.18.204.254/24'
+set interfaces ethernet eth1 vif 7 description 'FTTH-PPPoE'
+set interfaces loopback lo address '172.16.254.30/32'
+set interfaces pppoe pppoe0 authentication password 'vyos'
+set interfaces pppoe pppoe0 authentication username 'vyos'
+set interfaces pppoe pppoe0 description 'FTTH 100/50MBit'
+set interfaces pppoe pppoe0 dhcpv6-options pd 0 interface eth0.10 address '1'
+set interfaces pppoe pppoe0 dhcpv6-options pd 0 interface eth0.10 sla-id '10'
+set interfaces pppoe pppoe0 dhcpv6-options pd 0 interface eth0.20 address '1'
+set interfaces pppoe pppoe0 dhcpv6-options pd 0 interface eth0.20 sla-id '20'
+set interfaces pppoe pppoe0 dhcpv6-options pd 0 length '56'
+set interfaces pppoe pppoe0 ip adjust-mss '1452'
+set interfaces pppoe pppoe0 ipv6 address autoconf
+set interfaces pppoe pppoe0 ipv6 adjust-mss '1432'
+set interfaces pppoe pppoe0 mtu '1492'
+set interfaces pppoe pppoe0 no-peer-dns
+set interfaces pppoe pppoe0 source-interface 'eth1.7'
+set nat destination rule 100 description 'HTTP(S)'
+set nat destination rule 100 destination port '80,443'
+set nat destination rule 100 inbound-interface name 'pppoe0'
+set nat destination rule 100 log
+set nat destination rule 100 protocol 'tcp'
+set nat destination rule 100 translation address '172.16.36.10'
+set nat destination rule 1000 destination port '3389'
+set nat destination rule 1000 disable
+set nat destination rule 1000 inbound-interface name 'pppoe0'
+set nat destination rule 1000 protocol 'tcp'
+set nat destination rule 1000 translation address '172.16.33.40'
+set nat destination rule 8000 destination port '10000'
+set nat destination rule 8000 inbound-interface name 'pppoe0'
+set nat destination rule 8000 log
+set nat destination rule 8000 protocol 'udp'
+set nat destination rule 8000 translation address '172.31.0.200'
+set nat source rule 100 log
+set nat source rule 100 outbound-interface name 'pppoe0'
+set nat source rule 100 source address '172.16.32.0/19'
+set nat source rule 100 translation address 'masquerade'
+set nat source rule 200 outbound-interface name 'pppoe0'
+set nat source rule 200 source address '172.16.100.0/24'
+set nat source rule 200 translation address 'masquerade'
+set nat source rule 300 outbound-interface name 'pppoe0'
+set nat source rule 300 source address '172.31.0.0/24'
+set nat source rule 300 translation address 'masquerade'
+set nat source rule 400 outbound-interface name 'pppoe0'
+set nat source rule 400 source address '172.18.200.0/21'
+set nat source rule 400 translation address 'masquerade'
+set protocols static route 10.0.0.0/8 blackhole distance '254'
+set protocols static route 169.254.0.0/16 blackhole distance '254'
+set protocols static route 172.16.0.0/12 blackhole distance '254'
+set protocols static route 192.168.0.0/16 blackhole distance '254'
+set protocols static route6 2000::/3 interface pppoe0
+set qos policy shaper QoS bandwidth '50mbit'
+set qos policy shaper QoS default bandwidth '100%'
+set qos policy shaper QoS default burst '15k'
+set qos policy shaper QoS default queue-limit '1000'
+set qos policy shaper QoS default queue-type 'fq-codel'
+set service dhcp-server shared-network-name BACKBONE authoritative
+set service dhcp-server shared-network-name BACKBONE subnet 172.16.37.0/24 lease '86400'
+set service dhcp-server shared-network-name BACKBONE subnet 172.16.37.0/24 option default-router '172.16.37.254'
+set service dhcp-server shared-network-name BACKBONE subnet 172.16.37.0/24 option domain-name 'vyos.net'
+set service dhcp-server shared-network-name BACKBONE subnet 172.16.37.0/24 option domain-search 'vyos.net'
+set service dhcp-server shared-network-name BACKBONE subnet 172.16.37.0/24 option name-server '172.16.254.30'
+set service dhcp-server shared-network-name BACKBONE subnet 172.16.37.0/24 option ntp-server '172.16.254.30'
+set service dhcp-server shared-network-name BACKBONE subnet 172.16.37.0/24 range 0 start '172.16.37.120'
+set service dhcp-server shared-network-name BACKBONE subnet 172.16.37.0/24 range 0 stop '172.16.37.149'
+set service dhcp-server shared-network-name BACKBONE subnet 172.16.37.0/24 static-mapping AP1.wue3 ip-address '172.16.37.231'
+set service dhcp-server shared-network-name BACKBONE subnet 172.16.37.0/24 static-mapping AP1.wue3 mac '18:e8:29:6c:c3:a5'
+set service dhcp-server shared-network-name BACKBONE subnet 172.16.37.0/24 subnet-id '1'
+set service dhcp-server shared-network-name GUEST authoritative
+set service dhcp-server shared-network-name GUEST subnet 172.31.0.0/24 lease '86400'
+set service dhcp-server shared-network-name GUEST subnet 172.31.0.0/24 option default-router '172.31.0.254'
+set service dhcp-server shared-network-name GUEST subnet 172.31.0.0/24 option domain-name 'vyos.net'
+set service dhcp-server shared-network-name GUEST subnet 172.31.0.0/24 option domain-search 'vyos.net'
+set service dhcp-server shared-network-name GUEST subnet 172.31.0.0/24 option name-server '172.31.0.254'
+set service dhcp-server shared-network-name GUEST subnet 172.31.0.0/24 range 0 start '172.31.0.100'
+set service dhcp-server shared-network-name GUEST subnet 172.31.0.0/24 range 0 stop '172.31.0.199'
+set service dhcp-server shared-network-name GUEST subnet 172.31.0.0/24 static-mapping host01 ip-address '172.31.0.200'
+set service dhcp-server shared-network-name GUEST subnet 172.31.0.0/24 static-mapping host01 mac '00:50:00:00:00:01'
+set service dhcp-server shared-network-name GUEST subnet 172.31.0.0/24 static-mapping host02 ip-address '172.31.0.184'
+set service dhcp-server shared-network-name GUEST subnet 172.31.0.0/24 static-mapping host02 mac '00:50:00:00:00:02'
+set service dhcp-server shared-network-name GUEST subnet 172.31.0.0/24 subnet-id '2'
+set service dhcp-server shared-network-name IOT authoritative
+set service dhcp-server shared-network-name IOT subnet 172.16.35.0/24 lease '86400'
+set service dhcp-server shared-network-name IOT subnet 172.16.35.0/24 option default-router '172.16.35.254'
+set service dhcp-server shared-network-name IOT subnet 172.16.35.0/24 option domain-name 'vyos.net'
+set service dhcp-server shared-network-name IOT subnet 172.16.35.0/24 option domain-search 'vyos.net'
+set service dhcp-server shared-network-name IOT subnet 172.16.35.0/24 option name-server '172.16.254.30'
+set service dhcp-server shared-network-name IOT subnet 172.16.35.0/24 option ntp-server '172.16.254.30'
+set service dhcp-server shared-network-name IOT subnet 172.16.35.0/24 range 0 start '172.16.35.101'
+set service dhcp-server shared-network-name IOT subnet 172.16.35.0/24 range 0 stop '172.16.35.149'
+set service dhcp-server shared-network-name IOT subnet 172.16.35.0/24 subnet-id '3'
+set service dhcp-server shared-network-name LAN authoritative
+set service dhcp-server shared-network-name LAN subnet 172.16.33.0/24 lease '86400'
+set service dhcp-server shared-network-name LAN subnet 172.16.33.0/24 option default-router '172.16.33.254'
+set service dhcp-server shared-network-name LAN subnet 172.16.33.0/24 option domain-name 'vyos.net'
+set service dhcp-server shared-network-name LAN subnet 172.16.33.0/24 option domain-search 'vyos.net'
+set service dhcp-server shared-network-name LAN subnet 172.16.33.0/24 option name-server '172.16.254.30'
+set service dhcp-server shared-network-name LAN subnet 172.16.33.0/24 option ntp-server '172.16.254.30'
+set service dhcp-server shared-network-name LAN subnet 172.16.33.0/24 range 0 start '172.16.33.100'
+set service dhcp-server shared-network-name LAN subnet 172.16.33.0/24 range 0 stop '172.16.33.189'
+set service dhcp-server shared-network-name LAN subnet 172.16.33.0/24 subnet-id '4'
+set service dns forwarding allow-from '172.16.0.0/12'
+set service dns forwarding cache-size '0'
+set service dns forwarding domain 16.172.in-addr.arpa addnta
+set service dns forwarding domain 16.172.in-addr.arpa name-server 172.16.100.10
+set service dns forwarding domain 16.172.in-addr.arpa name-server 172.16.100.20
+set service dns forwarding domain 16.172.in-addr.arpa name-server 172.16.110.30
+set service dns forwarding domain 16.172.in-addr.arpa recursion-desired
+set service dns forwarding domain 18.172.in-addr.arpa addnta
+set service dns forwarding domain 18.172.in-addr.arpa name-server 172.16.100.10
+set service dns forwarding domain 18.172.in-addr.arpa name-server 172.16.100.20
+set service dns forwarding domain 18.172.in-addr.arpa name-server 172.16.110.30
+set service dns forwarding domain 18.172.in-addr.arpa recursion-desired
+set service dns forwarding domain vyos.net addnta
+set service dns forwarding domain vyos.net name-server 172.16.100.10
+set service dns forwarding domain vyos.net name-server 172.16.100.20
+set service dns forwarding domain vyos.net name-server 172.16.110.30
+set service dns forwarding domain vyos.net recursion-desired
+set service dns forwarding ignore-hosts-file
+set service dns forwarding listen-address '172.16.254.30'
+set service dns forwarding listen-address '172.31.0.254'
+set service dns forwarding negative-ttl '60'
+set service lldp legacy-protocols cdp
+set service lldp snmp
+set service mdns repeater interface 'eth0.35'
+set service mdns repeater interface 'eth0.10'
+set service ntp allow-client address '172.16.0.0/12'
+set service ntp server 0.pool.ntp.org
+set service ntp server 1.pool.ntp.org
+set service ntp server 2.pool.ntp.org
+set service router-advert interface eth0.10 prefix ::/64 preferred-lifetime '2700'
+set service router-advert interface eth0.10 prefix ::/64 valid-lifetime '5400'
+set service router-advert interface eth0.20 prefix ::/64 preferred-lifetime '2700'
+set service router-advert interface eth0.20 prefix ::/64 valid-lifetime '5400'
+set service snmp community fooBar authorization 'ro'
+set service snmp community fooBar network '172.16.100.0/24'
+set service snmp contact 'VyOS maintainers and contributors <maintainers@vyos.io>'
+set service snmp listen-address 172.16.254.30 port '161'
+set service snmp location 'The Internet'
+set service ssh disable-host-validation
+set service ssh port '22'
+set system config-management commit-revisions '200'
+set system conntrack expect-table-size '2048'
+set system conntrack hash-size '32768'
+set system conntrack modules ftp
+set system conntrack modules h323
+set system conntrack modules nfs
+set system conntrack modules pptp
+set system conntrack modules sqlnet
+set system conntrack modules tftp
+set system conntrack table-size '262144'
+set system conntrack timeout
+set system console device ttyS0 speed '115200'
+set system domain-name 'vyos.net'
+set system host-name 'vyos'
+set system login user vyos authentication encrypted-password '$6$2Ta6TWHd/U$NmrX0x9kexCimeOcYK1MfhMpITF9ELxHcaBU/znBq.X2ukQOj61fVI2UYP/xBzP4QtiTcdkgs7WOQMHWsRymO/'
+set system login user vyos authentication plaintext-password ''
+set system name-server '172.16.254.30'
+set system option ctrl-alt-delete 'ignore'
+set system option reboot-on-panic
+set system option startup-beep
+set system syslog global facility all level 'debug'
+set system syslog global facility local7 level 'debug'
+set system syslog host 172.16.100.1 facility all level 'warning'
+set system time-zone 'Europe/Berlin'
diff --git a/smoketest/config-tests/dialup-router-medium-vpn b/smoketest/config-tests/dialup-router-medium-vpn
index 8c221707f..67af456f4 100644
--- a/smoketest/config-tests/dialup-router-medium-vpn
+++ b/smoketest/config-tests/dialup-router-medium-vpn
@@ -25,18 +25,12 @@ set high-availability vrrp sync-group failover-group member 'LAN'
set interfaces ethernet eth0 duplex 'auto'
set interfaces ethernet eth0 mtu '9000'
set interfaces ethernet eth0 offload gro
-set interfaces ethernet eth0 offload gso
-set interfaces ethernet eth0 offload sg
-set interfaces ethernet eth0 offload tso
set interfaces ethernet eth0 speed 'auto'
set interfaces ethernet eth1 address '192.168.0.250/24'
set interfaces ethernet eth1 duplex 'auto'
set interfaces ethernet eth1 ip source-validation 'strict'
set interfaces ethernet eth1 mtu '9000'
set interfaces ethernet eth1 offload gro
-set interfaces ethernet eth1 offload gso
-set interfaces ethernet eth1 offload sg
-set interfaces ethernet eth1 offload tso
set interfaces ethernet eth1 speed 'auto'
set interfaces loopback lo
set interfaces openvpn vtun0 encryption ncp-ciphers 'aes256'
@@ -130,6 +124,7 @@ set interfaces wireguard wg0 peer red persistent-keepalive '20'
set interfaces wireguard wg0 peer red preshared-key 'CumyXX7osvUT9AwnS+m2TEfCaL0Ptc2LfuZ78Sujuk8='
set interfaces wireguard wg0 peer red public-key 'ALGWvMJCKpHF2tVH3hEIHqUe9iFfAmZATUUok/WQzks='
set interfaces wireguard wg0 port '7777'
+set interfaces wireguard wg0 private-key 'aGx+fvW916Ej7QRnBbW3QMoldhNv1u95/WHz45zDmF0='
set interfaces wireguard wg1 address '10.89.90.2/30'
set interfaces wireguard wg1 ip adjust-mss '1380'
set interfaces wireguard wg1 peer sam address '192.0.2.45'
@@ -140,6 +135,7 @@ set interfaces wireguard wg1 peer sam port '1200'
set interfaces wireguard wg1 peer sam preshared-key 'XpFtzx2Z+nR8pBv9/sSf7I94OkZkVYTz0AeU5Q/QQUE='
set interfaces wireguard wg1 peer sam public-key 'v5zfKGvH6W/lfDXJ0en96lvKo1gfFxMUWxe02+Fj5BU='
set interfaces wireguard wg1 port '7778'
+set interfaces wireguard wg1 private-key 'aGx+fvW916Ej7QRnBbW3QMoldhNv1u95/WHz45zDmF0='
set nat destination rule 50 destination port '49371'
set nat destination rule 50 inbound-interface name 'pppoe0'
set nat destination rule 50 protocol 'tcp_udp'
@@ -295,9 +291,18 @@ set service ssh listen-address '192.168.0.1'
set service ssh listen-address '192.168.10.1'
set service ssh listen-address '192.168.0.250'
set system config-management commit-revisions '100'
+set system conntrack modules ftp
+set system conntrack modules h323
+set system conntrack modules nfs
+set system conntrack modules pptp
+set system conntrack modules sip
+set system conntrack modules sqlnet
+set system conntrack modules tftp
set system console device ttyS0 speed '115200'
set system host-name 'vyos'
set system ip arp table-size '1024'
+set system login user vyos authentication encrypted-password '$6$O5gJRlDYQpj$MtrCV9lxMnZPMbcxlU7.FI793MImNHznxGoMFgm3Q6QP3vfKJyOSRCt3Ka/GzFQyW1yZS4NS616NLHaIPPFHc0'
+set system login user vyos authentication plaintext-password ''
set system name-server '192.168.0.1'
set system name-server 'pppoe0'
set system option ctrl-alt-delete 'ignore'
diff --git a/smoketest/config-tests/dialup-router-wireguard-ipv6 b/smoketest/config-tests/dialup-router-wireguard-ipv6
index 814a62d55..ff4bf89c2 100644
--- a/smoketest/config-tests/dialup-router-wireguard-ipv6
+++ b/smoketest/config-tests/dialup-router-wireguard-ipv6
@@ -1,207 +1,3 @@
-set interfaces dummy dum0 address '172.16.254.30/32'
-set interfaces ethernet eth0 vif 10 address '172.16.33.254/24'
-set interfaces ethernet eth0 vif 10 address '172.16.40.254/24'
-set interfaces ethernet eth0 vif 5 address '172.16.37.254/24'
-set interfaces ethernet eth0 vif 50 address '172.16.36.254/24'
-set interfaces ethernet eth0 ring-buffer rx '256'
-set interfaces ethernet eth0 ring-buffer tx '256'
-set interfaces ethernet eth1 offload gro
-set interfaces ethernet eth1 offload gso
-set interfaces ethernet eth1 offload sg
-set interfaces ethernet eth1 offload tso
-set interfaces ethernet eth1 vif 20 address '172.31.0.254/24'
-set interfaces ethernet eth2 disable
-set interfaces ethernet eth2 offload gro
-set interfaces ethernet eth2 offload gso
-set interfaces ethernet eth2 offload sg
-set interfaces ethernet eth2 offload tso
-set interfaces ethernet eth3 offload gro
-set interfaces ethernet eth3 offload gso
-set interfaces ethernet eth3 offload sg
-set interfaces ethernet eth3 offload tso
-set interfaces ethernet eth3 ring-buffer rx '256'
-set interfaces ethernet eth3 ring-buffer tx '256'
-set interfaces ethernet eth3 vif 7
-set interfaces loopback lo address '172.16.254.30/32'
-set interfaces pppoe pppoe0 authentication password 'vyos'
-set interfaces pppoe pppoe0 authentication username 'vyos'
-set interfaces pppoe pppoe0 dhcpv6-options pd 0 interface eth0.10 address '1'
-set interfaces pppoe pppoe0 dhcpv6-options pd 0 interface eth0.10 sla-id '10'
-set interfaces pppoe pppoe0 dhcpv6-options pd 0 interface eth1.20 address '1'
-set interfaces pppoe pppoe0 dhcpv6-options pd 0 interface eth1.20 sla-id '20'
-set interfaces pppoe pppoe0 dhcpv6-options pd 0 length '56'
-set interfaces pppoe pppoe0 ip adjust-mss '1452'
-set interfaces pppoe pppoe0 ipv6 address autoconf
-set interfaces pppoe pppoe0 ipv6 adjust-mss '1432'
-set interfaces pppoe pppoe0 no-peer-dns
-set interfaces pppoe pppoe0 source-interface 'eth3.7'
-set interfaces wireguard wg100 address '172.16.252.128/31'
-set interfaces wireguard wg100 mtu '1500'
-set interfaces wireguard wg100 peer HR6 address '100.65.151.213'
-set interfaces wireguard wg100 peer HR6 allowed-ips '0.0.0.0/0'
-set interfaces wireguard wg100 peer HR6 port '10100'
-set interfaces wireguard wg100 port '10100'
-set interfaces wireguard wg200 address '172.16.252.130/31'
-set interfaces wireguard wg200 mtu '1500'
-set interfaces wireguard wg200 peer WH56 address '80.151.69.205'
-set interfaces wireguard wg200 peer WH56 allowed-ips '0.0.0.0/0'
-set interfaces wireguard wg200 peer WH56 port '10200'
-set interfaces wireguard wg200 port '10200'
-set interfaces wireguard wg666 address '172.29.0.1/31'
-set interfaces wireguard wg666 mtu '1500'
-set interfaces wireguard wg666 peer WH34 address '100.65.55.1'
-set interfaces wireguard wg666 peer WH34 allowed-ips '0.0.0.0/0'
-set interfaces wireguard wg666 peer WH34 port '10666'
-set interfaces wireguard wg666 port '10666'
-set protocols ospf area 0 network '172.16.37.0/24'
-set protocols ospf area 0 network '172.16.254.30/32'
-set protocols ospf area 0 network '172.18.202.0/24'
-set protocols ospf area 0 network '172.18.203.0/24'
-set protocols ospf area 0 network '172.18.204.0/24'
-set protocols ospf interface eth0.5 authentication md5 key-id 10 md5-key 'ospf'
-set protocols ospf interface eth0.5 dead-interval '40'
-set protocols ospf interface eth0.5 hello-interval '10'
-set protocols ospf interface eth0.5 passive disable
-set protocols ospf interface eth0.5 priority '1'
-set protocols ospf interface eth0.5 retransmit-interval '5'
-set protocols ospf interface eth0.5 transmit-delay '1'
-set protocols ospf log-adjacency-changes detail
-set protocols ospf parameters router-id '172.16.254.30'
-set protocols ospf default-information originate always
-set protocols ospf default-information originate metric-type '2'
-set protocols ospf redistribute connected metric-type '2'
-set protocols ospf redistribute connected route-map 'MAP-OSPF-CONNECTED'
-set protocols static route 10.0.0.0/8 blackhole distance '254'
-set protocols static route 169.254.0.0/16 blackhole distance '254'
-set protocols static route 172.16.0.0/12 blackhole distance '254'
-set protocols static route 172.16.32.0/21 blackhole
-set protocols static route 172.18.0.0/16 blackhole
-set protocols static route 172.29.0.2/31 next-hop 172.29.0.0
-set protocols static route 192.168.0.0/16 blackhole distance '254'
-set protocols static route 192.168.189.0/24 next-hop 172.29.0.0
-set protocols static route6 2000::/3 interface pppoe0
-set protocols bfd peer 172.16.252.129
-set protocols bfd peer 172.16.252.131
-set protocols bfd peer 172.18.254.201
-set protocols bgp address-family ipv4-unicast network 172.16.32.0/21
-set protocols bgp address-family ipv4-unicast network 172.16.100.0/24
-set protocols bgp address-family ipv4-unicast network 172.16.252.128/31
-set protocols bgp address-family ipv4-unicast network 172.16.252.130/31
-set protocols bgp address-family ipv4-unicast network 172.16.254.30/32
-set protocols bgp address-family ipv4-unicast network 172.18.0.0/16
-set protocols bgp neighbor 172.16.252.129 peer-group 'WIREGUARD'
-set protocols bgp neighbor 172.16.252.131 peer-group 'WIREGUARD'
-set protocols bgp neighbor 172.18.254.201 address-family ipv4-unicast nexthop-self
-set protocols bgp neighbor 172.18.254.201 bfd
-set protocols bgp neighbor 172.18.254.201 remote-as '64503'
-set protocols bgp neighbor 172.18.254.201 update-source 'dum0'
-set protocols bgp parameters log-neighbor-changes
-set protocols bgp peer-group WIREGUARD address-family ipv4-unicast soft-reconfiguration inbound
-set protocols bgp peer-group WIREGUARD bfd
-set protocols bgp peer-group WIREGUARD remote-as 'external'
-set protocols bgp system-as '64503'
-set protocols bgp timers holdtime '30'
-set protocols bgp timers keepalive '10'
-set service lldp legacy-protocols cdp
-set service lldp legacy-protocols edp
-set service lldp legacy-protocols fdp
-set service lldp legacy-protocols sonmp
-set service lldp snmp
-set service ntp allow-client address '172.16.0.0/12'
-set service ntp server time1.vyos.net
-set service ntp server time2.vyos.net
-set service dhcp-server shared-network-name BACKBONE authoritative
-set service dhcp-server shared-network-name BACKBONE subnet 172.16.37.0/24 lease '86400'
-set service dhcp-server shared-network-name BACKBONE subnet 172.16.37.0/24 option default-router '172.16.37.254'
-set service dhcp-server shared-network-name BACKBONE subnet 172.16.37.0/24 option domain-name 'vyos.net'
-set service dhcp-server shared-network-name BACKBONE subnet 172.16.37.0/24 option domain-search 'vyos.net'
-set service dhcp-server shared-network-name BACKBONE subnet 172.16.37.0/24 option name-server '172.16.254.30'
-set service dhcp-server shared-network-name BACKBONE subnet 172.16.37.0/24 option ntp-server '172.16.254.30'
-set service dhcp-server shared-network-name BACKBONE subnet 172.16.37.0/24 range 0 start '172.16.37.120'
-set service dhcp-server shared-network-name BACKBONE subnet 172.16.37.0/24 range 0 stop '172.16.37.149'
-set service dhcp-server shared-network-name BACKBONE subnet 172.16.37.0/24 static-mapping AP1 ip-address '172.16.37.231'
-set service dhcp-server shared-network-name BACKBONE subnet 172.16.37.0/24 static-mapping AP1 mac '02:00:00:00:ee:18'
-set service dhcp-server shared-network-name BACKBONE subnet 172.16.37.0/24 static-mapping AP2 ip-address '172.16.37.232'
-set service dhcp-server shared-network-name BACKBONE subnet 172.16.37.0/24 static-mapping AP2 mac '02:00:00:00:52:84'
-set service dhcp-server shared-network-name BACKBONE subnet 172.16.37.0/24 static-mapping AP3 ip-address '172.16.37.233'
-set service dhcp-server shared-network-name BACKBONE subnet 172.16.37.0/24 static-mapping AP3 mac '02:00:00:00:51:c0'
-set service dhcp-server shared-network-name BACKBONE subnet 172.16.37.0/24 static-mapping AP4 ip-address '172.16.37.234'
-set service dhcp-server shared-network-name BACKBONE subnet 172.16.37.0/24 static-mapping AP4 mac '02:00:00:00:e6:fc'
-set service dhcp-server shared-network-name BACKBONE subnet 172.16.37.0/24 static-mapping AP5 ip-address '172.16.37.235'
-set service dhcp-server shared-network-name BACKBONE subnet 172.16.37.0/24 static-mapping AP5 mac '02:00:00:00:c3:50'
-set service dhcp-server shared-network-name BACKBONE subnet 172.16.37.0/24 subnet-id '1'
-set service dhcp-server shared-network-name GUEST authoritative
-set service dhcp-server shared-network-name GUEST subnet 172.31.0.0/24 lease '86400'
-set service dhcp-server shared-network-name GUEST subnet 172.31.0.0/24 option default-router '172.31.0.254'
-set service dhcp-server shared-network-name GUEST subnet 172.31.0.0/24 option domain-name 'vyos.net'
-set service dhcp-server shared-network-name GUEST subnet 172.31.0.0/24 option domain-search 'vyos.net'
-set service dhcp-server shared-network-name GUEST subnet 172.31.0.0/24 option name-server '172.31.0.254'
-set service dhcp-server shared-network-name GUEST subnet 172.31.0.0/24 range 0 start '172.31.0.101'
-set service dhcp-server shared-network-name GUEST subnet 172.31.0.0/24 range 0 stop '172.31.0.199'
-set service dhcp-server shared-network-name GUEST subnet 172.31.0.0/24 subnet-id '2'
-set service dhcp-server shared-network-name LAN authoritative
-set service dhcp-server shared-network-name LAN subnet 172.16.33.0/24 lease '86400'
-set service dhcp-server shared-network-name LAN subnet 172.16.33.0/24 option default-router '172.16.33.254'
-set service dhcp-server shared-network-name LAN subnet 172.16.33.0/24 option domain-name 'vyos.net'
-set service dhcp-server shared-network-name LAN subnet 172.16.33.0/24 option domain-search 'vyos.net'
-set service dhcp-server shared-network-name LAN subnet 172.16.33.0/24 option name-server '172.16.254.30'
-set service dhcp-server shared-network-name LAN subnet 172.16.33.0/24 option ntp-server '172.16.254.30'
-set service dhcp-server shared-network-name LAN subnet 172.16.33.0/24 range 0 start '172.16.33.100'
-set service dhcp-server shared-network-name LAN subnet 172.16.33.0/24 range 0 stop '172.16.33.189'
-set service dhcp-server shared-network-name LAN subnet 172.16.33.0/24 static-mapping four ip-address '172.16.33.214'
-set service dhcp-server shared-network-name LAN subnet 172.16.33.0/24 static-mapping four mac '02:00:00:00:c4:33'
-set service dhcp-server shared-network-name LAN subnet 172.16.33.0/24 static-mapping one ip-address '172.16.33.221'
-set service dhcp-server shared-network-name LAN subnet 172.16.33.0/24 static-mapping one mac '02:00:00:00:eb:a6'
-set service dhcp-server shared-network-name LAN subnet 172.16.33.0/24 static-mapping three ip-address '172.16.33.212'
-set service dhcp-server shared-network-name LAN subnet 172.16.33.0/24 static-mapping three mac '02:00:00:00:12:c7'
-set service dhcp-server shared-network-name LAN subnet 172.16.33.0/24 static-mapping two ip-address '172.16.33.211'
-set service dhcp-server shared-network-name LAN subnet 172.16.33.0/24 static-mapping two mac '02:00:00:00:58:90'
-set service dhcp-server shared-network-name LAN subnet 172.16.33.0/24 subnet-id '3'
-set service dns dynamic name service-vyos-pppoe0 address interface 'pppoe0'
-set service dns dynamic name service-vyos-pppoe0 host-name 'r1.vyos.net'
-set service dns dynamic name service-vyos-pppoe0 password 'vyos'
-set service dns dynamic name service-vyos-pppoe0 protocol 'dyndns2'
-set service dns dynamic name service-vyos-pppoe0 server 'dyndns.vyos.io'
-set service dns dynamic name service-vyos-pppoe0 username 'vyos-vyos'
-set service dns forwarding allow-from '172.16.0.0/12'
-set service dns forwarding domain 16.172.in-addr.arpa addnta
-set service dns forwarding domain 16.172.in-addr.arpa name-server 172.16.100.10
-set service dns forwarding domain 16.172.in-addr.arpa name-server 172.16.100.20
-set service dns forwarding domain 16.172.in-addr.arpa recursion-desired
-set service dns forwarding domain 18.172.in-addr.arpa addnta
-set service dns forwarding domain 18.172.in-addr.arpa name-server 172.16.100.10
-set service dns forwarding domain 18.172.in-addr.arpa name-server 172.16.100.20
-set service dns forwarding domain 18.172.in-addr.arpa recursion-desired
-set service dns forwarding domain vyos.net addnta
-set service dns forwarding domain vyos.net name-server 172.16.100.10
-set service dns forwarding domain vyos.net name-server 172.16.100.20
-set service dns forwarding domain vyos.net recursion-desired
-set service dns forwarding ignore-hosts-file
-set service dns forwarding listen-address '172.16.254.30'
-set service dns forwarding listen-address '172.31.0.254'
-set service dns forwarding negative-ttl '60'
-set service router-advert interface eth0.10 prefix ::/64 preferred-lifetime '2700'
-set service router-advert interface eth0.10 prefix ::/64 valid-lifetime '5400'
-set service router-advert interface eth1.20 prefix ::/64 preferred-lifetime '2700'
-set service router-advert interface eth1.20 prefix ::/64 valid-lifetime '5400'
-set service snmp community ro-community authorization 'ro'
-set service snmp community ro-community network '172.16.100.0/24'
-set service snmp contact 'VyOS'
-set service snmp listen-address 172.16.254.30 port '161'
-set service snmp location 'CLOUD'
-set system conntrack expect-table-size '2048'
-set system conntrack hash-size '32768'
-set system conntrack table-size '262144'
-set system domain-name 'vyos.net'
-set system host-name 'r1'
-set system login user vyos authentication encrypted-password '$6$2Ta6TWHd/U$NmrX0x9kexCimeOcYK1MfhMpITF9ELxHcaBU/znBq.X2ukQOj61fVI2UYP/xBzP4QtiTcdkgs7WOQMHWsRymO/'
-set system login user vyos authentication plaintext-password ''
-set system option ctrl-alt-delete 'ignore'
-set system option performance 'latency'
-set system option startup-beep
-set system syslog global facility all level 'debug'
-set system syslog host 172.16.100.1 facility all level 'warning'
-set system console device ttyS0 speed '115200'
set firewall global-options all-ping 'enable'
set firewall global-options broadcast-ping 'disable'
set firewall global-options ip-src-route 'disable'
@@ -634,6 +430,67 @@ set firewall zone WAN from LOCAL firewall ipv6-name 'ALLOW-ALL-6'
set firewall zone WAN from LOCAL firewall name 'LOCAL-WAN'
set firewall zone WAN interface 'pppoe0'
set firewall zone WAN interface 'wg666'
+set interfaces dummy dum0 address '172.16.254.30/32'
+set interfaces ethernet eth0 duplex 'auto'
+set interfaces ethernet eth0 offload gro
+set interfaces ethernet eth0 ring-buffer rx '256'
+set interfaces ethernet eth0 ring-buffer tx '256'
+set interfaces ethernet eth0 speed 'auto'
+set interfaces ethernet eth0 vif 5 address '172.16.37.254/24'
+set interfaces ethernet eth0 vif 10 address '172.16.33.254/24'
+set interfaces ethernet eth0 vif 10 address '172.16.40.254/24'
+set interfaces ethernet eth0 vif 50 address '172.16.36.254/24'
+set interfaces ethernet eth1 duplex 'auto'
+set interfaces ethernet eth1 offload gro
+set interfaces ethernet eth1 speed 'auto'
+set interfaces ethernet eth1 vif 20 address '172.31.0.254/24'
+set interfaces ethernet eth2 disable
+set interfaces ethernet eth2 duplex 'auto'
+set interfaces ethernet eth2 offload gro
+set interfaces ethernet eth2 speed 'auto'
+set interfaces ethernet eth3 duplex 'auto'
+set interfaces ethernet eth3 offload gro
+set interfaces ethernet eth3 ring-buffer rx '256'
+set interfaces ethernet eth3 ring-buffer tx '256'
+set interfaces ethernet eth3 speed 'auto'
+set interfaces ethernet eth3 vif 7
+set interfaces loopback lo address '172.16.254.30/32'
+set interfaces pppoe pppoe0 authentication password 'vyos'
+set interfaces pppoe pppoe0 authentication username 'vyos'
+set interfaces pppoe pppoe0 dhcpv6-options pd 0 interface eth0.10 address '1'
+set interfaces pppoe pppoe0 dhcpv6-options pd 0 interface eth0.10 sla-id '10'
+set interfaces pppoe pppoe0 dhcpv6-options pd 0 interface eth1.20 address '1'
+set interfaces pppoe pppoe0 dhcpv6-options pd 0 interface eth1.20 sla-id '20'
+set interfaces pppoe pppoe0 dhcpv6-options pd 0 length '56'
+set interfaces pppoe pppoe0 ip adjust-mss '1452'
+set interfaces pppoe pppoe0 ipv6 address autoconf
+set interfaces pppoe pppoe0 ipv6 adjust-mss '1432'
+set interfaces pppoe pppoe0 no-peer-dns
+set interfaces pppoe pppoe0 source-interface 'eth3.7'
+set interfaces wireguard wg100 address '172.16.252.128/31'
+set interfaces wireguard wg100 mtu '1500'
+set interfaces wireguard wg100 peer HR6 address '100.65.151.213'
+set interfaces wireguard wg100 peer HR6 allowed-ips '0.0.0.0/0'
+set interfaces wireguard wg100 peer HR6 port '10100'
+set interfaces wireguard wg100 peer HR6 public-key 'yLpi+UZuI019bmWH2h5fX3gStbpPPPLgEoYMyrdkOnQ='
+set interfaces wireguard wg100 port '10100'
+set interfaces wireguard wg100 private-key 'aGx+fvW916Ej7QRnBbW3QMoldhNv1u95/WHz45zDmF0='
+set interfaces wireguard wg200 address '172.16.252.130/31'
+set interfaces wireguard wg200 mtu '1500'
+set interfaces wireguard wg200 peer WH56 address '80.151.69.205'
+set interfaces wireguard wg200 peer WH56 allowed-ips '0.0.0.0/0'
+set interfaces wireguard wg200 peer WH56 port '10200'
+set interfaces wireguard wg200 peer WH56 public-key 'XQbkj6vnKKBJfJQyThXysU0iGxCvEOEb31kpaZgkrD8='
+set interfaces wireguard wg200 port '10200'
+set interfaces wireguard wg200 private-key 'aGx+fvW916Ej7QRnBbW3QMoldhNv1u95/WHz45zDmF0='
+set interfaces wireguard wg666 address '172.29.0.1/31'
+set interfaces wireguard wg666 mtu '1500'
+set interfaces wireguard wg666 peer WH34 address '100.65.55.1'
+set interfaces wireguard wg666 peer WH34 allowed-ips '0.0.0.0/0'
+set interfaces wireguard wg666 peer WH34 port '10666'
+set interfaces wireguard wg666 peer WH34 public-key 'yaTN4+xAafKM04D+Baeg5GWfbdaw35TE9HQivwRgAk0='
+set interfaces wireguard wg666 port '10666'
+set interfaces wireguard wg666 private-key 'aGx+fvW916Ej7QRnBbW3QMoldhNv1u95/WHz45zDmF0='
set nat destination rule 8000 destination port '10000'
set nat destination rule 8000 inbound-interface name 'pppoe0'
set nat destination rule 8000 protocol 'udp'
@@ -667,8 +524,174 @@ set policy route-map MAP-OSPF-CONNECTED rule 20 action 'permit'
set policy route-map MAP-OSPF-CONNECTED rule 20 match interface 'eth0.10'
set policy route-map MAP-OSPF-CONNECTED rule 40 action 'permit'
set policy route-map MAP-OSPF-CONNECTED rule 40 match interface 'eth0.50'
+set protocols bfd peer 172.16.252.129
+set protocols bfd peer 172.16.252.131
+set protocols bfd peer 172.18.254.201
+set protocols bgp address-family ipv4-unicast network 172.16.32.0/21
+set protocols bgp address-family ipv4-unicast network 172.16.100.0/24
+set protocols bgp address-family ipv4-unicast network 172.16.252.128/31
+set protocols bgp address-family ipv4-unicast network 172.16.252.130/31
+set protocols bgp address-family ipv4-unicast network 172.16.254.30/32
+set protocols bgp address-family ipv4-unicast network 172.18.0.0/16
+set protocols bgp neighbor 172.16.252.129 peer-group 'WIREGUARD'
+set protocols bgp neighbor 172.16.252.131 peer-group 'WIREGUARD'
+set protocols bgp neighbor 172.18.254.201 address-family ipv4-unicast nexthop-self
+set protocols bgp neighbor 172.18.254.201 bfd
+set protocols bgp neighbor 172.18.254.201 remote-as '64503'
+set protocols bgp neighbor 172.18.254.201 update-source 'dum0'
+set protocols bgp parameters log-neighbor-changes
+set protocols bgp peer-group WIREGUARD address-family ipv4-unicast soft-reconfiguration inbound
+set protocols bgp peer-group WIREGUARD bfd
+set protocols bgp peer-group WIREGUARD remote-as 'external'
+set protocols bgp system-as '64503'
+set protocols bgp timers holdtime '30'
+set protocols bgp timers keepalive '10'
+set protocols ospf area 0 network '172.16.254.30/32'
+set protocols ospf area 0 network '172.16.37.0/24'
+set protocols ospf area 0 network '172.18.201.0/24'
+set protocols ospf area 0 network '172.18.202.0/24'
+set protocols ospf area 0 network '172.18.203.0/24'
+set protocols ospf area 0 network '172.18.204.0/24'
+set protocols ospf default-information originate always
+set protocols ospf default-information originate metric-type '2'
+set protocols ospf interface eth0.5 authentication md5 key-id 10 md5-key 'ospf'
+set protocols ospf interface eth0.5 dead-interval '40'
+set protocols ospf interface eth0.5 hello-interval '10'
+set protocols ospf interface eth0.5 passive disable
+set protocols ospf interface eth0.5 priority '1'
+set protocols ospf interface eth0.5 retransmit-interval '5'
+set protocols ospf interface eth0.5 transmit-delay '1'
+set protocols ospf log-adjacency-changes detail
+set protocols ospf parameters abr-type 'cisco'
+set protocols ospf parameters router-id '172.16.254.30'
+set protocols ospf passive-interface 'default'
+set protocols ospf redistribute connected metric-type '2'
+set protocols ospf redistribute connected route-map 'MAP-OSPF-CONNECTED'
+set protocols static route 10.0.0.0/8 blackhole distance '254'
+set protocols static route 169.254.0.0/16 blackhole distance '254'
+set protocols static route 172.16.0.0/12 blackhole distance '254'
+set protocols static route 172.16.32.0/21 blackhole
+set protocols static route 172.18.0.0/16 blackhole
+set protocols static route 172.29.0.2/31 next-hop 172.29.0.0
+set protocols static route 192.168.0.0/16 blackhole distance '254'
+set protocols static route 192.168.189.0/24 next-hop 172.29.0.0
+set protocols static route6 2000::/3 interface pppoe0
set qos policy shaper QoS bandwidth '50mbit'
set qos policy shaper QoS default bandwidth '100%'
set qos policy shaper QoS default burst '15k'
set qos policy shaper QoS default queue-limit '1000'
set qos policy shaper QoS default queue-type 'fq-codel'
+set service dhcp-server shared-network-name BACKBONE authoritative
+set service dhcp-server shared-network-name BACKBONE subnet 172.16.37.0/24 lease '86400'
+set service dhcp-server shared-network-name BACKBONE subnet 172.16.37.0/24 option default-router '172.16.37.254'
+set service dhcp-server shared-network-name BACKBONE subnet 172.16.37.0/24 option domain-name 'vyos.net'
+set service dhcp-server shared-network-name BACKBONE subnet 172.16.37.0/24 option domain-search 'vyos.net'
+set service dhcp-server shared-network-name BACKBONE subnet 172.16.37.0/24 option name-server '172.16.254.30'
+set service dhcp-server shared-network-name BACKBONE subnet 172.16.37.0/24 option ntp-server '172.16.254.30'
+set service dhcp-server shared-network-name BACKBONE subnet 172.16.37.0/24 range 0 start '172.16.37.120'
+set service dhcp-server shared-network-name BACKBONE subnet 172.16.37.0/24 range 0 stop '172.16.37.149'
+set service dhcp-server shared-network-name BACKBONE subnet 172.16.37.0/24 static-mapping AP1 ip-address '172.16.37.231'
+set service dhcp-server shared-network-name BACKBONE subnet 172.16.37.0/24 static-mapping AP1 mac '02:00:00:00:ee:18'
+set service dhcp-server shared-network-name BACKBONE subnet 172.16.37.0/24 static-mapping AP2 ip-address '172.16.37.232'
+set service dhcp-server shared-network-name BACKBONE subnet 172.16.37.0/24 static-mapping AP2 mac '02:00:00:00:52:84'
+set service dhcp-server shared-network-name BACKBONE subnet 172.16.37.0/24 static-mapping AP3 ip-address '172.16.37.233'
+set service dhcp-server shared-network-name BACKBONE subnet 172.16.37.0/24 static-mapping AP3 mac '02:00:00:00:51:c0'
+set service dhcp-server shared-network-name BACKBONE subnet 172.16.37.0/24 static-mapping AP4 ip-address '172.16.37.234'
+set service dhcp-server shared-network-name BACKBONE subnet 172.16.37.0/24 static-mapping AP4 mac '02:00:00:00:e6:fc'
+set service dhcp-server shared-network-name BACKBONE subnet 172.16.37.0/24 static-mapping AP5 ip-address '172.16.37.235'
+set service dhcp-server shared-network-name BACKBONE subnet 172.16.37.0/24 static-mapping AP5 mac '02:00:00:00:c3:50'
+set service dhcp-server shared-network-name BACKBONE subnet 172.16.37.0/24 subnet-id '1'
+set service dhcp-server shared-network-name GUEST authoritative
+set service dhcp-server shared-network-name GUEST subnet 172.31.0.0/24 lease '86400'
+set service dhcp-server shared-network-name GUEST subnet 172.31.0.0/24 option default-router '172.31.0.254'
+set service dhcp-server shared-network-name GUEST subnet 172.31.0.0/24 option domain-name 'vyos.net'
+set service dhcp-server shared-network-name GUEST subnet 172.31.0.0/24 option domain-search 'vyos.net'
+set service dhcp-server shared-network-name GUEST subnet 172.31.0.0/24 option name-server '172.31.0.254'
+set service dhcp-server shared-network-name GUEST subnet 172.31.0.0/24 range 0 start '172.31.0.101'
+set service dhcp-server shared-network-name GUEST subnet 172.31.0.0/24 range 0 stop '172.31.0.199'
+set service dhcp-server shared-network-name GUEST subnet 172.31.0.0/24 subnet-id '2'
+set service dhcp-server shared-network-name LAN authoritative
+set service dhcp-server shared-network-name LAN subnet 172.16.33.0/24 lease '86400'
+set service dhcp-server shared-network-name LAN subnet 172.16.33.0/24 option default-router '172.16.33.254'
+set service dhcp-server shared-network-name LAN subnet 172.16.33.0/24 option domain-name 'vyos.net'
+set service dhcp-server shared-network-name LAN subnet 172.16.33.0/24 option domain-search 'vyos.net'
+set service dhcp-server shared-network-name LAN subnet 172.16.33.0/24 option name-server '172.16.254.30'
+set service dhcp-server shared-network-name LAN subnet 172.16.33.0/24 option ntp-server '172.16.254.30'
+set service dhcp-server shared-network-name LAN subnet 172.16.33.0/24 range 0 start '172.16.33.100'
+set service dhcp-server shared-network-name LAN subnet 172.16.33.0/24 range 0 stop '172.16.33.189'
+set service dhcp-server shared-network-name LAN subnet 172.16.33.0/24 static-mapping four ip-address '172.16.33.214'
+set service dhcp-server shared-network-name LAN subnet 172.16.33.0/24 static-mapping four mac '02:00:00:00:c4:33'
+set service dhcp-server shared-network-name LAN subnet 172.16.33.0/24 static-mapping one ip-address '172.16.33.221'
+set service dhcp-server shared-network-name LAN subnet 172.16.33.0/24 static-mapping one mac '02:00:00:00:eb:a6'
+set service dhcp-server shared-network-name LAN subnet 172.16.33.0/24 static-mapping three ip-address '172.16.33.212'
+set service dhcp-server shared-network-name LAN subnet 172.16.33.0/24 static-mapping three mac '02:00:00:00:12:c7'
+set service dhcp-server shared-network-name LAN subnet 172.16.33.0/24 static-mapping two ip-address '172.16.33.211'
+set service dhcp-server shared-network-name LAN subnet 172.16.33.0/24 static-mapping two mac '02:00:00:00:58:90'
+set service dhcp-server shared-network-name LAN subnet 172.16.33.0/24 subnet-id '3'
+set service dns dynamic name service-vyos-pppoe0 address interface 'pppoe0'
+set service dns dynamic name service-vyos-pppoe0 host-name 'r1.vyos.net'
+set service dns dynamic name service-vyos-pppoe0 password 'vyos'
+set service dns dynamic name service-vyos-pppoe0 protocol 'dyndns2'
+set service dns dynamic name service-vyos-pppoe0 server 'dyndns.vyos.io'
+set service dns dynamic name service-vyos-pppoe0 username 'vyos-vyos'
+set service dns forwarding allow-from '172.16.0.0/12'
+set service dns forwarding domain 16.172.in-addr.arpa addnta
+set service dns forwarding domain 16.172.in-addr.arpa name-server 172.16.100.10
+set service dns forwarding domain 16.172.in-addr.arpa name-server 172.16.100.20
+set service dns forwarding domain 16.172.in-addr.arpa recursion-desired
+set service dns forwarding domain 18.172.in-addr.arpa addnta
+set service dns forwarding domain 18.172.in-addr.arpa name-server 172.16.100.10
+set service dns forwarding domain 18.172.in-addr.arpa name-server 172.16.100.20
+set service dns forwarding domain 18.172.in-addr.arpa recursion-desired
+set service dns forwarding domain vyos.net addnta
+set service dns forwarding domain vyos.net name-server 172.16.100.10
+set service dns forwarding domain vyos.net name-server 172.16.100.20
+set service dns forwarding domain vyos.net recursion-desired
+set service dns forwarding ignore-hosts-file
+set service dns forwarding listen-address '172.16.254.30'
+set service dns forwarding listen-address '172.31.0.254'
+set service dns forwarding negative-ttl '60'
+set service lldp legacy-protocols cdp
+set service lldp legacy-protocols edp
+set service lldp legacy-protocols fdp
+set service lldp legacy-protocols sonmp
+set service lldp snmp
+set service ntp allow-client address '172.16.0.0/12'
+set service ntp server time1.vyos.net
+set service ntp server time2.vyos.net
+set service router-advert interface eth0.10 prefix ::/64 preferred-lifetime '2700'
+set service router-advert interface eth0.10 prefix ::/64 valid-lifetime '5400'
+set service router-advert interface eth1.20 prefix ::/64 preferred-lifetime '2700'
+set service router-advert interface eth1.20 prefix ::/64 valid-lifetime '5400'
+set service snmp community ro-community authorization 'ro'
+set service snmp community ro-community network '172.16.100.0/24'
+set service snmp contact 'VyOS'
+set service snmp listen-address 172.16.254.30 port '161'
+set service snmp location 'CLOUD'
+set service ssh disable-host-validation
+set service ssh port '22'
+set system config-management commit-revisions '200'
+set system conntrack expect-table-size '2048'
+set system conntrack hash-size '32768'
+set system conntrack modules ftp
+set system conntrack modules h323
+set system conntrack modules nfs
+set system conntrack modules pptp
+set system conntrack modules sqlnet
+set system conntrack modules tftp
+set system conntrack table-size '262144'
+set system conntrack timeout
+set system console device ttyS0 speed '115200'
+set system domain-name 'vyos.net'
+set system host-name 'r1'
+set system login user vyos authentication encrypted-password '$6$2Ta6TWHd/U$NmrX0x9kexCimeOcYK1MfhMpITF9ELxHcaBU/znBq.X2ukQOj61fVI2UYP/xBzP4QtiTcdkgs7WOQMHWsRymO/'
+set system login user vyos authentication plaintext-password ''
+set system name-server '172.16.254.30'
+set system option ctrl-alt-delete 'ignore'
+set system option performance 'latency'
+set system option reboot-on-panic
+set system option startup-beep
+set system syslog global facility all level 'debug'
+set system syslog global facility local7 level 'debug'
+set system syslog host 172.16.100.1 facility all level 'warning'
+set system time-zone 'Europe/Berlin'
diff --git a/smoketest/config-tests/egp-igp-route-maps b/smoketest/config-tests/egp-igp-route-maps
new file mode 100644
index 000000000..fc46d25ff
--- /dev/null
+++ b/smoketest/config-tests/egp-igp-route-maps
@@ -0,0 +1,46 @@
+set interfaces ethernet eth0 address '192.0.2.1/25'
+set interfaces ethernet eth0 duplex 'auto'
+set interfaces ethernet eth0 speed 'auto'
+set interfaces ethernet eth1 address '192.0.2.129/25'
+set interfaces ethernet eth1 address '2001:db8::1234/64'
+set interfaces ethernet eth1 duplex 'auto'
+set interfaces ethernet eth1 speed 'auto'
+set interfaces loopback lo
+set policy route-map zebra-bgp rule 10 action 'permit'
+set policy route-map zebra-isis rule 10 action 'permit'
+set policy route-map zebra-ospf rule 10 action 'permit'
+set policy route-map zebra-ospfv3 rule 10 action 'permit'
+set policy route-map zebra-ripng rule 10 action 'permit'
+set policy route-map zebra-static rule 10 action 'permit'
+set protocols bgp system-as '100'
+set protocols isis interface eth0
+set protocols isis net '49.0001.1921.6800.1002.00'
+set protocols ospf area 0 network '192.0.2.0/25'
+set protocols ospf area 0 network '192.0.2.128/25'
+set protocols ospf interface eth0 passive disable
+set protocols ospf interface eth1 passive disable
+set protocols ospf log-adjacency-changes
+set protocols ospf parameters abr-type 'cisco'
+set protocols ospf parameters router-id '1.1.1.1'
+set protocols ospf passive-interface 'default'
+set protocols ospfv3 area 0
+set protocols ospfv3 interface eth1 area '0'
+set protocols ospfv3 parameters router-id '1.1.1.1'
+set protocols ripng interface eth1
+set protocols static
+set system config-management commit-revisions '100'
+set system console device ttyS0 speed '115200'
+set system host-name 'vyos'
+set system ip protocol bgp route-map 'zebra-bgp'
+set system ip protocol isis route-map 'zebra-isis'
+set system ip protocol ospf route-map 'zebra-ospf'
+set system ip protocol static route-map 'zebra-static'
+set system ipv6 protocol ospfv3 route-map 'zebra-ospfv3'
+set system ipv6 protocol ripng route-map 'zebra-ripng'
+set system login user vyos authentication encrypted-password '$6$O5gJRlDYQpj$MtrCV9lxMnZPMbcxlU7.FI793MImNHznxGoMFgm3Q6QP3vfKJyOSRCt3Ka/GzFQyW1yZS4NS616NLHaIPPFHc0'
+set system login user vyos authentication plaintext-password ''
+set system logs logrotate messages max-size '1'
+set system logs logrotate messages rotate '5'
+set system name-server '192.168.0.1'
+set system syslog global facility all level 'info'
+set system time-zone 'Europe/Berlin'
diff --git a/smoketest/config-tests/igmp-pim-small b/smoketest/config-tests/igmp-pim-small
index 207c17d45..909c3d67b 100644
--- a/smoketest/config-tests/igmp-pim-small
+++ b/smoketest/config-tests/igmp-pim-small
@@ -1,5 +1,12 @@
+set interfaces ethernet eth0 duplex 'auto'
+set interfaces ethernet eth0 speed 'auto'
set interfaces ethernet eth1 address '100.64.0.1/24'
+set interfaces ethernet eth1 duplex 'auto'
+set interfaces ethernet eth1 speed 'auto'
set interfaces ethernet eth2 address '172.16.0.2/24'
+set interfaces ethernet eth2 duplex 'auto'
+set interfaces ethernet eth2 offload gro
+set interfaces ethernet eth2 speed 'auto'
set protocols pim interface eth1 igmp join 224.1.0.0 source-address '1.1.1.1'
set protocols pim interface eth1 igmp join 224.1.0.0 source-address '1.1.1.2'
set protocols pim interface eth1 igmp query-interval '1000'
@@ -7,11 +14,24 @@ set protocols pim interface eth1 igmp query-max-response-time '30'
set protocols pim interface eth1 igmp version '2'
set protocols pim interface eth2
set protocols pim rp address 172.16.255.1 group '224.0.0.0/4'
+set service ntp allow-client address '0.0.0.0/0'
+set service ntp allow-client address '::/0'
set service ntp server 0.pool.ntp.org
set service ntp server 1.pool.ntp.org
set service ntp server 2.pool.ntp.org
+set system config-management commit-revisions '200'
+set system conntrack modules ftp
+set system conntrack modules h323
+set system conntrack modules nfs
+set system conntrack modules pptp
+set system conntrack modules sip
+set system conntrack modules sqlnet
+set system conntrack modules tftp
+set system console device ttyS0 speed '115200'
set system domain-name 'vyos.io'
set system host-name 'vyos'
set system login user vyos authentication encrypted-password '$6$2Ta6TWHd/U$NmrX0x9kexCimeOcYK1MfhMpITF9ELxHcaBU/znBq.X2ukQOj61fVI2UYP/xBzP4QtiTcdkgs7WOQMHWsRymO/'
set system login user vyos authentication plaintext-password ''
-set system console device ttyS0 speed '115200'
+set system syslog global facility all level 'info'
+set system syslog global facility local7 level 'debug'
+set system time-zone 'Europe/Berlin'
diff --git a/smoketest/config-tests/ipoe-server b/smoketest/config-tests/ipoe-server
index fb32fdb14..f4a12f502 100644
--- a/smoketest/config-tests/ipoe-server
+++ b/smoketest/config-tests/ipoe-server
@@ -1,9 +1,10 @@
set interfaces ethernet eth0 address 'dhcp'
set interfaces ethernet eth1 address '192.168.0.1/24'
+set interfaces ethernet eth2 offload gro
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 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'
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'
@@ -25,11 +26,23 @@ 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 service ntp allow-client address '0.0.0.0/0'
+set service ntp allow-client address '::/0'
+set service ntp server time1.vyos.net
+set service ntp server time2.vyos.net
+set service ntp server time3.vyos.net
+set service ssh
set system config-management commit-revisions '100'
+set system conntrack modules ftp
+set system conntrack modules h323
+set system conntrack modules nfs
+set system conntrack modules pptp
+set system conntrack modules sip
+set system conntrack modules sqlnet
+set system conntrack modules tftp
+set system console device ttyS0 speed '115200'
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'
+set system syslog global facility all level 'info'
+set system syslog global facility local7 level 'debug'
diff --git a/smoketest/config-tests/ipv6-disable b/smoketest/config-tests/ipv6-disable
new file mode 100644
index 000000000..40e34fa0c
--- /dev/null
+++ b/smoketest/config-tests/ipv6-disable
@@ -0,0 +1,31 @@
+set interfaces ethernet eth0 duplex 'auto'
+set interfaces ethernet eth0 speed 'auto'
+set interfaces ethernet eth0 vif 201 address '172.18.201.10/24'
+set interfaces ethernet eth0 vif 202 address '172.18.202.10/24'
+set interfaces ethernet eth0 vif 203 address '172.18.203.10/24'
+set interfaces ethernet eth0 vif 204 address '172.18.204.10/24'
+set protocols static route 0.0.0.0/0 next-hop 172.18.201.254 distance '10'
+set protocols static route 0.0.0.0/0 next-hop 172.18.202.254 distance '20'
+set protocols static route 0.0.0.0/0 next-hop 172.18.203.254 distance '30'
+set protocols static route 0.0.0.0/0 next-hop 172.18.204.254 distance '40'
+set service ntp allow-client address '0.0.0.0/0'
+set service ntp allow-client address '::/0'
+set service ntp server 172.16.254.20
+set service ntp server 172.16.254.30
+set system config-management commit-revisions '200'
+set system conntrack modules ftp
+set system conntrack modules h323
+set system conntrack modules nfs
+set system conntrack modules pptp
+set system conntrack modules sip
+set system conntrack modules sqlnet
+set system conntrack modules tftp
+set system console device ttyS0 speed '115200'
+set system domain-name 'vyos.net'
+set system host-name 'vyos'
+set system login user vyos authentication encrypted-password '$6$2Ta6TWHd/U$NmrX0x9kexCimeOcYK1MfhMpITF9ELxHcaBU/znBq.X2ukQOj61fVI2UYP/xBzP4QtiTcdkgs7WOQMHWsRymO/'
+set system login user vyos authentication plaintext-password ''
+set system name-server '172.16.254.20'
+set system name-server '172.16.254.30'
+set system syslog global facility all level 'info'
+set system syslog global facility local7 level 'debug'
diff --git a/smoketest/config-tests/isis-small b/smoketest/config-tests/isis-small
new file mode 100644
index 000000000..b322f4e29
--- /dev/null
+++ b/smoketest/config-tests/isis-small
@@ -0,0 +1,44 @@
+set interfaces dummy dum0 address '203.0.113.1/24'
+set interfaces ethernet eth0 duplex 'auto'
+set interfaces ethernet eth0 offload sg
+set interfaces ethernet eth0 offload tso
+set interfaces ethernet eth0 speed 'auto'
+set interfaces ethernet eth1 address '192.0.2.1/24'
+set interfaces ethernet eth1 duplex 'auto'
+set interfaces ethernet eth1 offload sg
+set interfaces ethernet eth1 offload tso
+set interfaces ethernet eth1 speed 'auto'
+set interfaces ethernet eth2 duplex 'auto'
+set interfaces ethernet eth2 offload sg
+set interfaces ethernet eth2 offload tso
+set interfaces ethernet eth2 speed 'auto'
+set interfaces ethernet eth3 duplex 'auto'
+set interfaces ethernet eth3 offload sg
+set interfaces ethernet eth3 offload tso
+set interfaces ethernet eth3 speed 'auto'
+set policy prefix-list EXPORT-ISIS rule 10 action 'permit'
+set policy prefix-list EXPORT-ISIS rule 10 prefix '203.0.113.0/24'
+set policy route-map EXPORT-ISIS rule 10 action 'permit'
+set policy route-map EXPORT-ISIS rule 10 match ip address prefix-list 'EXPORT-ISIS'
+set protocols isis interface eth1 bfd
+set protocols isis net '49.0001.1921.6800.1002.00'
+set protocols isis redistribute ipv4 connected level-2 route-map 'EXPORT-ISIS'
+set system config-management commit-revisions '200'
+set system conntrack modules ftp
+set system conntrack modules h323
+set system conntrack modules nfs
+set system conntrack modules pptp
+set system conntrack modules sip
+set system conntrack modules sqlnet
+set system conntrack modules tftp
+set system console device ttyS0 speed '115200'
+set system domain-name 'vyos.io'
+set system host-name 'vyos'
+set system login user vyos authentication encrypted-password '$6$2Ta6TWHd/U$NmrX0x9kexCimeOcYK1MfhMpITF9ELxHcaBU/znBq.X2ukQOj61fVI2UYP/xBzP4QtiTcdkgs7WOQMHWsRymO/'
+set system login user vyos authentication plaintext-password ''
+set service ntp server time1.vyos.net
+set service ntp server time2.vyos.net
+set service ntp server time3.vyos.net
+set system syslog global facility all level 'info'
+set system syslog global facility local7 level 'debug'
+set system time-zone 'Europe/Berlin'
diff --git a/smoketest/config-tests/nat-basic b/smoketest/config-tests/nat-basic
index 9fea08b02..471add3b3 100644
--- a/smoketest/config-tests/nat-basic
+++ b/smoketest/config-tests/nat-basic
@@ -1,25 +1,17 @@
-set interfaces ethernet eth0 offload rps
+set interfaces bonding bond10 hash-policy 'layer3+4'
+set interfaces bonding bond10 member interface 'eth2'
+set interfaces bonding bond10 member interface 'eth3'
+set interfaces bonding bond10 mode '802.3ad'
+set interfaces bonding bond10 vif 50 address '192.168.189.1/24'
set interfaces ethernet eth0 disable
+set interfaces ethernet eth0 offload gro
+set interfaces ethernet eth0 offload rps
set interfaces ethernet eth1 offload gro
-set interfaces ethernet eth1 offload gso
set interfaces ethernet eth1 offload rps
-set interfaces ethernet eth1 offload sg
-set interfaces ethernet eth1 offload tso
set interfaces ethernet eth2 offload gro
-set interfaces ethernet eth2 offload gso
set interfaces ethernet eth2 offload rps
-set interfaces ethernet eth2 offload sg
-set interfaces ethernet eth2 offload tso
set interfaces ethernet eth3 offload gro
-set interfaces ethernet eth3 offload gso
set interfaces ethernet eth3 offload rps
-set interfaces ethernet eth3 offload sg
-set interfaces ethernet eth3 offload tso
-set interfaces bonding bond10 hash-policy 'layer3+4'
-set interfaces bonding bond10 member interface 'eth2'
-set interfaces bonding bond10 member interface 'eth3'
-set interfaces bonding bond10 mode '802.3ad'
-set interfaces bonding bond10 vif 50 address '192.168.189.1/24'
set interfaces loopback lo
set interfaces pppoe pppoe7 authentication password 'vyos'
set interfaces pppoe pppoe7 authentication username 'vyos'
@@ -31,30 +23,6 @@ set interfaces pppoe pppoe7 ipv6 adjust-mss '1432'
set interfaces pppoe pppoe7 mtu '1492'
set interfaces pppoe pppoe7 no-peer-dns
set interfaces pppoe pppoe7 source-interface 'eth1'
-set service lldp interface eth1 disable
-set service ntp allow-client address '192.168.189.0/24'
-set service ntp server time1.vyos.net
-set service ntp server time2.vyos.net
-set service ntp listen-address '192.168.189.1'
-set service ssh dynamic-protection
-set service dhcp-server shared-network-name LAN subnet 192.168.189.0/24 lease '604800'
-set service dhcp-server shared-network-name LAN subnet 192.168.189.0/24 option default-router '192.168.189.1'
-set service dhcp-server shared-network-name LAN subnet 192.168.189.0/24 option domain-name 'vyos.net'
-set service dhcp-server shared-network-name LAN subnet 192.168.189.0/24 option name-server '1.1.1.1'
-set service dhcp-server shared-network-name LAN subnet 192.168.189.0/24 option name-server '9.9.9.9'
-set service dhcp-server shared-network-name LAN subnet 192.168.189.0/24 range 0 start '192.168.189.20'
-set service dhcp-server shared-network-name LAN subnet 192.168.189.0/24 range 0 stop '192.168.189.254'
-set service dhcp-server shared-network-name LAN subnet 192.168.189.0/24 subnet-id '1'
-set service router-advert interface bond10.50 prefix ::/64 preferred-lifetime '2700'
-set service router-advert interface bond10.50 prefix ::/64 valid-lifetime '5400'
-set system config-management commit-revisions '100'
-set system domain-name 'vyos.net'
-set system host-name 'R1'
-set system login user vyos authentication encrypted-password '$6$2Ta6TWHd/U$NmrX0x9kexCimeOcYK1MfhMpITF9ELxHcaBU/znBq.X2ukQOj61fVI2UYP/xBzP4QtiTcdkgs7WOQMHWsRymO/'
-set system login user vyos authentication plaintext-password ''
-set system name-server '1.1.1.1'
-set system name-server '9.9.9.9'
-set system console device ttyS0 speed '115200'
set nat destination rule 1000 destination port '3389'
set nat destination rule 1000 inbound-interface name 'pppoe7'
set nat destination rule 1000 protocol 'tcp'
@@ -83,3 +51,38 @@ set nat source rule 50 translation port '10300'
set nat source rule 100 outbound-interface name 'pppoe7'
set nat source rule 100 source address '192.168.189.0/24'
set nat source rule 100 translation address 'masquerade'
+set service dhcp-server shared-network-name LAN subnet 192.168.189.0/24 lease '604800'
+set service dhcp-server shared-network-name LAN subnet 192.168.189.0/24 option default-router '192.168.189.1'
+set service dhcp-server shared-network-name LAN subnet 192.168.189.0/24 option domain-name 'vyos.net'
+set service dhcp-server shared-network-name LAN subnet 192.168.189.0/24 option name-server '1.1.1.1'
+set service dhcp-server shared-network-name LAN subnet 192.168.189.0/24 option name-server '9.9.9.9'
+set service dhcp-server shared-network-name LAN subnet 192.168.189.0/24 range 0 start '192.168.189.20'
+set service dhcp-server shared-network-name LAN subnet 192.168.189.0/24 range 0 stop '192.168.189.254'
+set service dhcp-server shared-network-name LAN subnet 192.168.189.0/24 subnet-id '1'
+set service lldp interface all
+set service lldp interface eth1 disable
+set service ntp allow-client address '192.168.189.0/24'
+set service ntp listen-address '192.168.189.1'
+set service ntp server time1.vyos.net
+set service ntp server time2.vyos.net
+set service router-advert interface bond10.50 prefix ::/64 preferred-lifetime '2700'
+set service router-advert interface bond10.50 prefix ::/64 valid-lifetime '5400'
+set service ssh disable-host-validation
+set service ssh dynamic-protection
+set system config-management commit-revisions '100'
+set system conntrack modules ftp
+set system conntrack modules h323
+set system conntrack modules nfs
+set system conntrack modules pptp
+set system conntrack modules sip
+set system conntrack modules sqlnet
+set system conntrack modules tftp
+set system console device ttyS0 speed '115200'
+set system domain-name 'vyos.net'
+set system host-name 'R1'
+set system login user vyos authentication encrypted-password '$6$2Ta6TWHd/U$NmrX0x9kexCimeOcYK1MfhMpITF9ELxHcaBU/znBq.X2ukQOj61fVI2UYP/xBzP4QtiTcdkgs7WOQMHWsRymO/'
+set system login user vyos authentication plaintext-password ''
+set system name-server '1.1.1.1'
+set system name-server '9.9.9.9'
+set system syslog global facility all level 'info'
+set system syslog global facility local7 level 'debug'
diff --git a/smoketest/config-tests/ospf-simple b/smoketest/config-tests/ospf-simple
index 13d5e7038..355709448 100644
--- a/smoketest/config-tests/ospf-simple
+++ b/smoketest/config-tests/ospf-simple
@@ -1,9 +1,11 @@
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 ethernet eth1
+set interfaces ethernet eth2
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 area 0 network '193.201.42.160/28'
+set protocols ospf area 0 network '10.66.66.0/24'
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'
@@ -14,7 +16,9 @@ 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 console device ttyS0 speed '115200'
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'
+set system syslog global facility all level 'info'
+set system syslog global facility local7 level 'debug'
diff --git a/smoketest/config-tests/ospf-small b/smoketest/config-tests/ospf-small
new file mode 100644
index 000000000..a7f8b682c
--- /dev/null
+++ b/smoketest/config-tests/ospf-small
@@ -0,0 +1,82 @@
+set interfaces dummy dum0 address '172.18.254.200/32'
+set interfaces ethernet eth0 duplex 'auto'
+set interfaces ethernet eth0 speed 'auto'
+set interfaces ethernet eth0 vif 201 address '172.18.201.9/24'
+set interfaces ethernet eth0 vif 202 address '172.18.202.9/24'
+set interfaces ethernet eth0 vif 203 address '172.18.203.9/24'
+set interfaces ethernet eth1 duplex 'auto'
+set interfaces ethernet eth1 speed 'auto'
+set protocols ospf area 0 network '172.18.201.0/24'
+set protocols ospf area 0 network '172.18.202.0/24'
+set protocols ospf area 0 network '172.18.203.0/24'
+set protocols ospf area 0 network '172.18.254.200/32'
+set protocols ospf interface eth0.201 authentication md5 key-id 10 md5-key 'OSPFVyOSNET'
+set protocols ospf interface eth0.201 dead-interval '40'
+set protocols ospf interface eth0.201 hello-interval '10'
+set protocols ospf interface eth0.201 passive disable
+set protocols ospf interface eth0.201 priority '1'
+set protocols ospf interface eth0.201 retransmit-interval '5'
+set protocols ospf interface eth0.201 transmit-delay '1'
+set protocols ospf interface eth0.202 authentication md5 key-id 10 md5-key 'OSPFVyOSNET'
+set protocols ospf interface eth0.202 dead-interval '40'
+set protocols ospf interface eth0.202 hello-interval '10'
+set protocols ospf interface eth0.202 passive disable
+set protocols ospf interface eth0.202 priority '1'
+set protocols ospf interface eth0.202 retransmit-interval '5'
+set protocols ospf interface eth0.202 transmit-delay '1'
+set protocols ospf interface eth0.203 authentication md5 key-id 10 md5-key 'OSPFVyOSNET'
+set protocols ospf interface eth0.203 dead-interval '40'
+set protocols ospf interface eth0.203 hello-interval '10'
+set protocols ospf interface eth0.203 passive disable
+set protocols ospf interface eth0.203 priority '1'
+set protocols ospf interface eth0.203 retransmit-interval '5'
+set protocols ospf interface eth0.203 transmit-delay '1'
+set protocols ospf log-adjacency-changes
+set protocols ospf parameters abr-type 'cisco'
+set protocols ospf parameters router-id '172.18.254.200'
+set protocols ospf passive-interface 'default'
+set protocols ospfv3 area 0.0.0.0
+set protocols ospfv3 interface eth0.201 area '0.0.0.0'
+set protocols ospfv3 interface eth0.201 bfd
+set protocols ospfv3 interface eth0.201 cost '40'
+set protocols ospfv3 interface eth0.202 area '0.0.0.0'
+set protocols ospfv3 interface eth0.202 bfd
+set protocols ospfv3 interface eth0.202 cost '40'
+set protocols ospfv3 interface eth0.203 area '0.0.0.0'
+set protocols ospfv3 interface eth0.203 bfd
+set protocols ospfv3 interface eth0.203 cost '40'
+set protocols ospfv3 interface eth1 area '0.0.0.0'
+set protocols ospfv3 interface eth1 bfd
+set protocols ospfv3 interface eth1 cost '60'
+set protocols ospfv3 interface eth1 mtu-ignore
+set protocols ospfv3 interface eth1 network 'broadcast'
+set protocols ospfv3 interface eth1 priority '20'
+set service ntp allow-client address '0.0.0.0/0'
+set service ntp allow-client address '::/0'
+set service ntp server time1.vyos.net
+set service ntp server time2.vyos.net
+set service ssh disable-host-validation
+set service ssh port '22'
+set system config-management commit-revisions '200'
+set system conntrack modules ftp
+set system conntrack modules h323
+set system conntrack modules nfs
+set system conntrack modules pptp
+set system conntrack modules sip
+set system conntrack modules sqlnet
+set system conntrack modules tftp
+set system console device ttyS0 speed '115200'
+set system domain-name 'vyos.net'
+set system host-name 'vyos'
+set system login user vyos authentication encrypted-password '$6$2Ta6TWHd/U$NmrX0x9kexCimeOcYK1MfhMpITF9ELxHcaBU/znBq.X2ukQOj61fVI2UYP/xBzP4QtiTcdkgs7WOQMHWsRymO/'
+set system login user vyos authentication plaintext-password ''
+set system name-server '172.16.254.30'
+set system sysctl parameter net.ipv4.conf.eth0.tag value '1'
+set system sysctl parameter net.ipv4.conf.eth1.tag value '1'
+set system sysctl parameter net.ipv4.igmp_max_memberships value '5'
+set system sysctl parameter net.ipv4.ipfrag_time value '4'
+set system sysctl parameter net.mpls.default_ttl value '10'
+set system sysctl parameter net.mpls.ip_ttl_propagate value '0'
+set system syslog global facility all level 'info'
+set system syslog global facility local7 level 'debug'
+set system time-zone 'Europe/Berlin'
diff --git a/smoketest/config-tests/pppoe-server b/smoketest/config-tests/pppoe-server
new file mode 100644
index 000000000..34fbea215
--- /dev/null
+++ b/smoketest/config-tests/pppoe-server
@@ -0,0 +1,47 @@
+set interfaces ethernet eth0 address 'dhcp'
+set interfaces ethernet eth1 address '192.168.0.1/24'
+set interfaces ethernet eth1 speed 'auto'
+set interfaces ethernet eth1 duplex 'auto'
+set interfaces ethernet eth2 speed 'auto'
+set interfaces ethernet eth2 duplex 'auto'
+set interfaces loopback lo
+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'
+set service ntp allow-client address '0.0.0.0/0'
+set service ntp allow-client address '::/0'
+set service ntp server 0.pool.ntp.org
+set service ntp server 1.pool.ntp.org
+set service ntp server 2.pool.ntp.org
+set service pppoe-server access-concentrator 'ACN'
+set service pppoe-server authentication local-users username foo password 'bar'
+set service pppoe-server authentication local-users username foo rate-limit download '20480'
+set service pppoe-server authentication local-users username foo rate-limit upload '10240'
+set service pppoe-server authentication mode 'local'
+set service pppoe-server client-ip-pool default-range-pool range '10.0.0.0/24'
+set service pppoe-server client-ip-pool default-range-pool range '10.0.1.0/24'
+set service pppoe-server client-ip-pool default-range-pool range '10.0.2.0/24'
+set service pppoe-server default-pool 'default-range-pool'
+set service pppoe-server gateway-address '192.168.0.2'
+set service pppoe-server interface eth1
+set service pppoe-server interface eth2 vlan '10'
+set service pppoe-server interface eth2 vlan '20'
+set service pppoe-server interface eth2 vlan '30-40'
+set service pppoe-server interface eth2 vlan '50-60'
+set service pppoe-server name-server '192.168.0.1'
+set service pppoe-server ppp-options disable-ccp
+set service ssh
+set system config-management commit-revisions '100'
+set system conntrack modules ftp
+set system conntrack modules h323
+set system conntrack modules nfs
+set system conntrack modules pptp
+set system conntrack modules sip
+set system conntrack modules sqlnet
+set system conntrack modules tftp
+set system console device ttyS0 speed '115200'
+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 syslog global facility all level 'info'
+set system syslog global facility local7 level 'debug'
diff --git a/smoketest/config-tests/qos-basic b/smoketest/config-tests/qos-basic
new file mode 100644
index 000000000..0e198b80c
--- /dev/null
+++ b/smoketest/config-tests/qos-basic
@@ -0,0 +1,75 @@
+set interfaces ethernet eth0 address '10.1.1.100/24'
+set interfaces ethernet eth1 address '10.2.1.1/24'
+set interfaces ethernet eth2 address '10.9.9.1/24'
+set interfaces ethernet eth2 vif 200
+set interfaces loopback lo
+set qos interface eth0 egress 'FS'
+set qos interface eth1 egress 'ISPC'
+set qos interface eth2 egress 'MY-HTB'
+set qos interface eth2.200 egress 'foo-emulate'
+set qos policy network-emulator foo-emulate bandwidth '300mbit'
+set qos policy shaper FS bandwidth 'auto'
+set qos policy shaper FS class 10 bandwidth '100%'
+set qos policy shaper FS class 10 burst '15k'
+set qos policy shaper FS class 10 match ADDRESS10 ip source address '172.17.1.2/32'
+set qos policy shaper FS class 10 queue-type 'fair-queue'
+set qos policy shaper FS class 20 bandwidth '100%'
+set qos policy shaper FS class 20 burst '15k'
+set qos policy shaper FS class 20 match ADDRESS20 ip source address '172.17.1.3/32'
+set qos policy shaper FS class 20 queue-type 'fair-queue'
+set qos policy shaper FS class 30 bandwidth '100%'
+set qos policy shaper FS class 30 burst '15k'
+set qos policy shaper FS class 30 match ADDRESS30 ip source address '172.17.1.4/32'
+set qos policy shaper FS class 30 queue-type 'fair-queue'
+set qos policy shaper FS default bandwidth '10%'
+set qos policy shaper FS default burst '15k'
+set qos policy shaper FS default ceiling '100%'
+set qos policy shaper FS default priority '7'
+set qos policy shaper FS default queue-type 'fair-queue'
+set qos policy shaper ISPC bandwidth '600mbit'
+set qos policy shaper ISPC default bandwidth '50%'
+set qos policy shaper ISPC default burst '768k'
+set qos policy shaper ISPC default ceiling '100%'
+set qos policy shaper ISPC default queue-type 'fq-codel'
+set qos policy shaper ISPC description 'Outbound Traffic Shaper - ISPC'
+set qos policy shaper MY-HTB bandwidth '10mbit'
+set qos policy shaper MY-HTB class 30 bandwidth '10%'
+set qos policy shaper MY-HTB class 30 burst '15k'
+set qos policy shaper MY-HTB class 30 ceiling '50%'
+set qos policy shaper MY-HTB class 30 match ADDRESS30 ip source address '10.1.1.0/24'
+set qos policy shaper MY-HTB class 30 priority '5'
+set qos policy shaper MY-HTB class 30 queue-type 'fair-queue'
+set qos policy shaper MY-HTB class 40 bandwidth '90%'
+set qos policy shaper MY-HTB class 40 burst '15k'
+set qos policy shaper MY-HTB class 40 ceiling '100%'
+set qos policy shaper MY-HTB class 40 match ADDRESS40 ip source address '10.2.1.0/24'
+set qos policy shaper MY-HTB class 40 priority '5'
+set qos policy shaper MY-HTB class 40 queue-type 'fair-queue'
+set qos policy shaper MY-HTB class 50 bandwidth '100%'
+set qos policy shaper MY-HTB class 50 burst '15k'
+set qos policy shaper MY-HTB class 50 match ADDRESS50 ipv6 source address '2001:db8::1/64'
+set qos policy shaper MY-HTB class 50 queue-type 'fair-queue'
+set qos policy shaper MY-HTB default bandwidth '10%'
+set qos policy shaper MY-HTB default burst '15k'
+set qos policy shaper MY-HTB default ceiling '100%'
+set qos policy shaper MY-HTB default priority '7'
+set qos policy shaper MY-HTB default queue-type 'fair-queue'
+set service ntp allow-client address '0.0.0.0/0'
+set service ntp allow-client address '::/0'
+set service ntp server time1.vyos.net
+set service ntp server time2.vyos.net
+set service ntp server time3.vyos.net
+set system config-management commit-revisions '10'
+set system conntrack modules ftp
+set system conntrack modules h323
+set system conntrack modules nfs
+set system conntrack modules pptp
+set system conntrack modules sip
+set system conntrack modules sqlnet
+set system conntrack modules tftp
+set system console device ttyS0 speed '115200'
+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 syslog global facility all level 'info'
+set system syslog global facility local7 level 'debug'
diff --git a/smoketest/config-tests/rip-router b/smoketest/config-tests/rip-router
new file mode 100644
index 000000000..829aafbd5
--- /dev/null
+++ b/smoketest/config-tests/rip-router
@@ -0,0 +1,83 @@
+set interfaces dummy dum0 address '192.0.2.0/32'
+set interfaces ethernet eth0 address '172.18.202.10/24'
+set interfaces ethernet eth0 duplex 'auto'
+set interfaces ethernet eth0 speed 'auto'
+set interfaces ethernet eth1 duplex 'auto'
+set interfaces ethernet eth1 speed 'auto'
+set interfaces ethernet eth1 vif 20
+set interfaces ethernet eth1 vif-s 200 vif-c 2000
+set interfaces ethernet eth1 vif-s 200 vif-c 3000
+set policy access-list6 198 rule 10 action 'permit'
+set policy access-list6 198 rule 10 source any
+set policy access-list6 199 rule 20 action 'deny'
+set policy access-list6 199 rule 20 source any
+set policy prefix-list6 bar-prefix rule 200 action 'deny'
+set policy prefix-list6 bar-prefix rule 200 prefix '2001:db8::/32'
+set policy prefix-list6 foo-prefix rule 100 action 'permit'
+set policy prefix-list6 foo-prefix rule 100 prefix '2001:db8::/32'
+set policy route-map FooBar123 rule 10 action 'permit'
+set protocols rip default-distance '20'
+set protocols rip default-information originate
+set protocols rip interface eth0 authentication md5 1 password 'VyOSsecure'
+set protocols rip interface eth0 split-horizon poison-reverse
+set protocols rip interface eth1.20 authentication plaintext-password 'VyOSsecure'
+set protocols rip interface eth1.20 split-horizon poison-reverse
+set protocols rip interface eth1.200 authentication md5 1 password 'VyOSsecure'
+set protocols rip interface eth1.200 split-horizon disable
+set protocols rip interface eth1.200.2000 authentication md5 1 password 'VyOSsecure'
+set protocols rip interface eth1.200.3000 split-horizon disable
+set protocols rip network '192.168.0.0/24'
+set protocols rip redistribute connected
+set protocols ripng aggregate-address '2001:db8:1000::/48'
+set protocols ripng default-information originate
+set protocols ripng default-metric '8'
+set protocols ripng distribute-list access-list in '198'
+set protocols ripng distribute-list access-list out '199'
+set protocols ripng distribute-list interface eth0 access-list in '198'
+set protocols ripng distribute-list interface eth0 access-list out '199'
+set protocols ripng distribute-list interface eth0 prefix-list in 'foo-prefix'
+set protocols ripng distribute-list interface eth0 prefix-list out 'bar-prefix'
+set protocols ripng distribute-list interface eth1 access-list in '198'
+set protocols ripng distribute-list interface eth1 access-list out '199'
+set protocols ripng distribute-list interface eth1 prefix-list in 'foo-prefix'
+set protocols ripng distribute-list interface eth1 prefix-list out 'bar-prefix'
+set protocols ripng distribute-list interface eth2 access-list in '198'
+set protocols ripng distribute-list interface eth2 access-list out '199'
+set protocols ripng distribute-list interface eth2 prefix-list in 'foo-prefix'
+set protocols ripng distribute-list interface eth2 prefix-list out 'bar-prefix'
+set protocols ripng distribute-list prefix-list in 'foo-prefix'
+set protocols ripng distribute-list prefix-list out 'bar-prefix'
+set protocols ripng interface eth0 split-horizon poison-reverse
+set protocols ripng interface eth1.20 split-horizon disable
+set protocols ripng interface eth1.200 split-horizon poison-reverse
+set protocols ripng interface eth1.200.3000 split-horizon poison-reverse
+set protocols ripng network '2001:db8:1000::/64'
+set protocols ripng network '2001:db8:1001::/64'
+set protocols ripng network '2001:db8:2000::/64'
+set protocols ripng network '2001:db8:2001::/64'
+set protocols ripng passive-interface 'default'
+set protocols ripng redistribute connected metric '8'
+set protocols ripng redistribute connected route-map 'FooBar123'
+set protocols ripng redistribute static metric '8'
+set protocols ripng redistribute static route-map 'FooBar123'
+set protocols ripng route '2001:db8:1000::/64'
+set service ntp allow-client address '0.0.0.0/0'
+set service ntp allow-client address '::/0'
+set service ntp server 0.pool.ntp.org
+set service ntp server 1.pool.ntp.org
+set service ntp server 2.pool.ntp.org
+set service ssh port '22'
+set system config-management commit-revisions '100'
+set system conntrack modules ftp
+set system conntrack modules h323
+set system conntrack modules nfs
+set system conntrack modules pptp
+set system conntrack modules sip
+set system conntrack modules sqlnet
+set system conntrack modules tftp
+set system console device ttyS0 speed '115200'
+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 syslog global facility all level 'info'
+set system syslog global facility local7 level 'debug'
diff --git a/smoketest/config-tests/rpki-only b/smoketest/config-tests/rpki-only
index 569463b12..dcbc7673d 100644
--- a/smoketest/config-tests/rpki-only
+++ b/smoketest/config-tests/rpki-only
@@ -1,5 +1,7 @@
set interfaces ethernet eth0 address '192.0.2.1/24'
set interfaces ethernet eth0 address '2001:db8::1/64'
+set interfaces ethernet eth0 duplex 'auto'
+set interfaces ethernet eth0 speed 'auto'
set interfaces loopback lo
set pki openssh rpki-5.6.7.8 private key 'b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABFwAAAAdzc2gtcnNhAAAAAwEAAQAAAQEAweDyflDFR4qyEwETbJkZ2ZZc+sJNiDTvYpwGsWIkju49lJSxHe1xKf8FhwfyMu40Snt1yDlRmmmz4CsbLgbuZGMPvXG11e34+C0pSVUvpF6aqRTeLl1pDRK7Rnjgm3su+I8SRLQR4qbLG6VXWOFuVpwiqbExLaU0hFYTPNP+dArNpsWEEKsohk6pTXdhg3VzWp3vCMjl2JTshDa3lD7p2xISSAReEY0fnfEAmQzH4Z6DIwwGdFuMWoQIg+oFBM9ARrO2/FIjRsz6AecR/WeU72JEw4aJic1/cAJQA6PiQBHwkuo3Wll1tbpxeRZoB2NQG22ETyJLvhfTaooNLT9HpQAAA8joU5dM6FOXTAAAAAdzc2gtcnNhAAABAQDB4PJ+UMVHirITARNsmRnZllz6wk2INO9inAaxYiSO7j2UlLEd7XEp/wWHB/Iy7jRKe3XIOVGaabPgKxsuBu5kYw+9cbXV7fj4LSlJVS+kXpqpFN4uXWkNErtGeOCbey74jxJEtBHipssbpVdY4W5WnCKpsTEtpTSEVhM80/50Cs2mxYQQqyiGTqlNd2GDdXNane8IyOXYlOyENreUPunbEhJIBF4RjR+d8QCZDMfhnoMjDAZ0W4xahAiD6gUEz0BGs7b8UiNGzPoB5xH9Z5TvYkTDhomJzX9wAlADo+JAEfCS6jdaWXW1unF5FmgHY1AbbYRPIku+F9Nqig0tP0elAAAAAwEAAQAAAQACkDlUjzfUhtJs6uY5WNrdJB5NmHUS+HQzzxFNlhkapK6+wKqI1UNaRUtq6iF7J+gcFf7MK2nXS098BsXguWm8fQzPuemoDvHsQhiaJhyvpSqRUrvPTB/f8t/0AhQiKiJIWgfpTaIw53inAGwjujNNxNm2eafHTThhCYxOkRT7rsT6bnSio6yeqPy5QHg7IKFztp5FXDUyiOS3aX3SvzQcDUkMXALdvzX50t1XIk+X48Rgkq72dL4VpV2oMNDu3hM6FqBUplf9Mv3s51FNSma/cibCQoVufrIfoqYjkNTjIpYFUcq4zZ0/KvgXgzSsy9VN/4TtbalrOuu7X/SHJbvhAAAAgGPFsXgONYQvXxCnK1dIueozgaZg1I/n522E2ZCOXBW4dYJVyNpppwRreDzuFzTDEe061MpNHfScjVBJCCulivFYWscL6oaGsryDbFxO3QmB4I98UBqrds2yan9/JGc6EYe299yvaHy7Y64+NC0+fN8H2RAZ61T4w10JrCaJRyvzAAAAgQDvBfuV1U7o9k/fbU+U7W2UYnWblpOZAMfi1XQP6IJJeyWs90PdTdXh+l0eIQrCawIiRJytNfxMmbD4huwTf77fWiyCcPznmALQ7ex/yJ+W5Z0V4dPGF3h7o1uiS236JhQ7mfcliCkhp/1PIklBIMPcCp0zl+s9wMv2hX7w1Pah9QAAAIEAz6YgU9Xute+J+dBwoWxEQ+igR6KE55Um7O9AvSrqnCm9r7lSFsXC2ErYOxoDSJ3yIBEV0b4XAGn6tbbVIs3jS8BnLHxclAHQecOx1PGn7PKbnPW0oJRq/X9QCIEelKYvlykpayn7uZooTXqcDaPZxfPpmPdye8chVJvdygi7kPEAAAAMY3BvQExSMS53dWUzAQIDBAUGBw=='
set pki openssh rpki-5.6.7.8 public key 'AAAAB3NzaC1yc2EAAAADAQABAAABAQDB4PJ+UMVHirITARNsmRnZllz6wk2INO9inAaxYiSO7j2UlLEd7XEp/wWHB/Iy7jRKe3XIOVGaabPgKxsuBu5kYw+9cbXV7fj4LSlJVS+kXpqpFN4uXWkNErtGeOCbey74jxJEtBHipssbpVdY4W5WnCKpsTEtpTSEVhM80/50Cs2mxYQQqyiGTqlNd2GDdXNane8IyOXYlOyENreUPunbEhJIBF4RjR+d8QCZDMfhnoMjDAZ0W4xahAiD6gUEz0BGs7b8UiNGzPoB5xH9Z5TvYkTDhomJzX9wAlADo+JAEfCS6jdaWXW1unF5FmgHY1AbbYRPIku+F9Nqig0tP0el'
@@ -14,6 +16,7 @@ set policy route-map ROUTES-IN rule 30 action 'deny'
set policy route-map ROUTES-IN rule 30 match rpki 'invalid'
set protocols bgp neighbor 192.0.2.200 address-family ipv4-unicast route-map import 'ROUTES-IN'
set protocols bgp neighbor 192.0.2.200 remote-as '200'
+set protocols bgp neighbor 2001:db8::200 address-family ipv4-unicast
set protocols bgp neighbor 2001:db8::200 address-family ipv6-unicast route-map import 'ROUTES-IN'
set protocols bgp neighbor 2001:db8::200 remote-as '200'
set protocols bgp system-as '100'
@@ -23,8 +26,17 @@ set protocols rpki cache 5.6.7.8 port '2222'
set protocols rpki cache 5.6.7.8 preference '20'
set protocols rpki cache 5.6.7.8 ssh key 'rpki-5.6.7.8'
set protocols rpki cache 5.6.7.8 ssh username 'vyos'
+set system config-management commit-revisions '200'
+set system conntrack modules ftp
+set system conntrack modules h323
+set system conntrack modules nfs
+set system conntrack modules pptp
+set system conntrack modules sip
+set system conntrack modules sqlnet
+set system conntrack modules tftp
+set system console device ttyS0 speed '115200'
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 syslog global facility all level 'debug'
-set system console device ttyS0 speed '115200'
+set system syslog global facility local7 level 'debug'
diff --git a/smoketest/config-tests/tunnel-broker b/smoketest/config-tests/tunnel-broker
new file mode 100644
index 000000000..ee6301c85
--- /dev/null
+++ b/smoketest/config-tests/tunnel-broker
@@ -0,0 +1,75 @@
+set interfaces dummy dum0 address '192.0.2.0/32'
+set interfaces dummy dum1 address '192.0.2.1/32'
+set interfaces dummy dum2 address '192.0.2.2/32'
+set interfaces dummy dum3 address '192.0.2.3/32'
+set interfaces dummy dum4 address '192.0.2.4/32'
+set interfaces ethernet eth0 address '172.18.202.10/24'
+set interfaces ethernet eth0 duplex 'auto'
+set interfaces ethernet eth0 speed 'auto'
+set interfaces l2tpv3 l2tpeth10 destination-port '5010'
+set interfaces l2tpv3 l2tpeth10 encapsulation 'ip'
+set interfaces l2tpv3 l2tpeth10 peer-session-id '110'
+set interfaces l2tpv3 l2tpeth10 peer-tunnel-id '10'
+set interfaces l2tpv3 l2tpeth10 remote '172.18.202.110'
+set interfaces l2tpv3 l2tpeth10 session-id '110'
+set interfaces l2tpv3 l2tpeth10 source-address '172.18.202.10'
+set interfaces l2tpv3 l2tpeth10 source-port '5010'
+set interfaces l2tpv3 l2tpeth10 tunnel-id '10'
+set interfaces l2tpv3 l2tpeth20 destination-port '5020'
+set interfaces l2tpv3 l2tpeth20 encapsulation 'ip'
+set interfaces l2tpv3 l2tpeth20 peer-session-id '120'
+set interfaces l2tpv3 l2tpeth20 peer-tunnel-id '20'
+set interfaces l2tpv3 l2tpeth20 remote '172.18.202.120'
+set interfaces l2tpv3 l2tpeth20 session-id '120'
+set interfaces l2tpv3 l2tpeth20 source-address '172.18.202.10'
+set interfaces l2tpv3 l2tpeth20 source-port '5020'
+set interfaces l2tpv3 l2tpeth20 tunnel-id '20'
+set interfaces l2tpv3 l2tpeth30 destination-port '5030'
+set interfaces l2tpv3 l2tpeth30 encapsulation 'ip'
+set interfaces l2tpv3 l2tpeth30 peer-session-id '130'
+set interfaces l2tpv3 l2tpeth30 peer-tunnel-id '30'
+set interfaces l2tpv3 l2tpeth30 remote '172.18.202.130'
+set interfaces l2tpv3 l2tpeth30 session-id '130'
+set interfaces l2tpv3 l2tpeth30 source-address '172.18.202.10'
+set interfaces l2tpv3 l2tpeth30 source-port '5030'
+set interfaces l2tpv3 l2tpeth30 tunnel-id '30'
+set interfaces tunnel tun100 address '172.16.0.1/30'
+set interfaces tunnel tun100 encapsulation 'gretap'
+set interfaces tunnel tun100 remote '192.0.2.100'
+set interfaces tunnel tun100 source-address '192.0.2.1'
+set interfaces tunnel tun200 address '172.16.0.5/30'
+set interfaces tunnel tun200 encapsulation 'gre'
+set interfaces tunnel tun200 remote '192.0.2.101'
+set interfaces tunnel tun200 source-interface 'eth0'
+set interfaces tunnel tun300 address '172.16.0.9/30'
+set interfaces tunnel tun300 encapsulation 'ipip'
+set interfaces tunnel tun300 remote '192.0.2.102'
+set interfaces tunnel tun300 source-address '192.0.2.2'
+set interfaces tunnel tun400 address '172.16.0.13/30'
+set interfaces tunnel tun400 encapsulation 'gretap'
+set interfaces tunnel tun400 remote '192.0.2.103'
+set interfaces tunnel tun400 source-address '192.0.2.3'
+set interfaces tunnel tun500 address '172.16.0.17/30'
+set interfaces tunnel tun500 encapsulation 'gre'
+set interfaces tunnel tun500 remote '192.0.2.104'
+set interfaces tunnel tun500 source-address '192.0.2.4'
+set protocols static route 0.0.0.0/0 next-hop 172.18.202.254
+set service ntp allow-client address '0.0.0.0/0'
+set service ntp allow-client address '::/0'
+set service ntp server 0.pool.ntp.org
+set service ntp server 1.pool.ntp.org
+set service ntp server 2.pool.ntp.org
+set system config-management commit-revisions '100'
+set system conntrack modules ftp
+set system conntrack modules h323
+set system conntrack modules nfs
+set system conntrack modules pptp
+set system conntrack modules sip
+set system conntrack modules sqlnet
+set system conntrack modules tftp
+set system console device ttyS0 speed '115200'
+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 syslog global facility all level 'info'
+set system syslog global facility local7 level 'debug'
diff --git a/smoketest/config-tests/vpn-openconnect-sstp b/smoketest/config-tests/vpn-openconnect-sstp
new file mode 100644
index 000000000..28d7d5daa
--- /dev/null
+++ b/smoketest/config-tests/vpn-openconnect-sstp
@@ -0,0 +1,35 @@
+set interfaces ethernet eth0 address '192.168.150.1/24'
+set service ntp allow-client address '0.0.0.0/0'
+set service ntp allow-client address '::/0'
+set service ntp server time1.vyos.net
+set service ntp server time2.vyos.net
+set service ntp server time3.vyos.net
+set system config-management commit-revisions '100'
+set system conntrack modules ftp
+set system conntrack modules h323
+set system conntrack modules nfs
+set system conntrack modules pptp
+set system conntrack modules sip
+set system conntrack modules sqlnet
+set system conntrack modules tftp
+set system console device ttyS0 speed '115200'
+set system host-name 'vyos'
+set system login user vyos authentication encrypted-password '$6$2Ta6TWHd/U$NmrX0x9kexCimeOcYK1MfhMpITF9ELxHcaBU/znBq.X2ukQOj61fVI2UYP/xBzP4QtiTcdkgs7WOQMHWsRymO/'
+set system login user vyos authentication plaintext-password ''
+set system syslog global facility all level 'info'
+set system syslog global facility local7 level 'debug'
+set vpn openconnect authentication local-users username test password 'test'
+set vpn openconnect authentication mode local 'password'
+set vpn openconnect network-settings client-ip-settings subnet '192.168.160.0/24'
+set vpn openconnect ssl ca-certificate 'openconnect'
+set vpn openconnect ssl certificate 'openconnect'
+set vpn openconnect tls-version-min '1.0'
+set vpn sstp authentication local-users username test password 'test'
+set vpn sstp authentication mode 'local'
+set vpn sstp authentication protocols 'mschap-v2'
+set vpn sstp client-ip-pool default-range-pool range '192.168.170.0/24'
+set vpn sstp default-pool 'default-range-pool'
+set vpn sstp gateway-address '192.168.150.1'
+set vpn sstp port '8443'
+set vpn sstp ssl ca-certificate 'sstp'
+set vpn sstp ssl certificate 'sstp'
diff --git a/smoketest/config-tests/vrf-basic b/smoketest/config-tests/vrf-basic
new file mode 100644
index 000000000..1d2874a60
--- /dev/null
+++ b/smoketest/config-tests/vrf-basic
@@ -0,0 +1,65 @@
+set interfaces ethernet eth0 address '192.0.2.1/24'
+set interfaces ethernet eth1 duplex 'auto'
+set interfaces ethernet eth1 speed 'auto'
+set interfaces ethernet eth1 vrf 'green'
+set interfaces ethernet eth2 vrf 'red'
+set protocols static route 0.0.0.0/0 next-hop 192.0.2.254 distance '10'
+set protocols static table 10 route 1.0.0.0/8 interface eth0 distance '20'
+set protocols static table 10 route 2.0.0.0/8 interface eth0 distance '20'
+set protocols static table 10 route 3.0.0.0/8 interface eth0 distance '20'
+set protocols static table 20 route 4.0.0.0/8 interface eth0 distance '20'
+set protocols static table 20 route 5.0.0.0/8 interface eth0 distance '50'
+set protocols static table 20 route 6.0.0.0/8 interface eth0 distance '60'
+set protocols static table 20 route 11.0.0.0/8 next-hop 1.1.1.1 interface 'eth0'
+set protocols static table 20 route 12.0.0.0/8 next-hop 1.1.1.1 interface 'eth0'
+set protocols static table 20 route 13.0.0.0/8 next-hop 1.1.1.1 interface 'eth0'
+set protocols static table 20 route6 2001:db8:100::/40 interface eth1 distance '20'
+set protocols static table 20 route6 2001:db8::/40 interface eth1 distance '10'
+set protocols static table 30 route 14.0.0.0/8 next-hop 2.2.1.1 interface 'eth1'
+set protocols static table 30 route 15.0.0.0/8 next-hop 2.2.1.1 interface 'eth1'
+set protocols static table 30 route6 2001:db8:200::/40 interface eth1 distance '20'
+set service ntp allow-client address '0.0.0.0/0'
+set service ntp allow-client address '::/0'
+set service ntp server 0.pool.ntp.org
+set service ntp server 1.pool.ntp.org
+set service ntp server 2.pool.ntp.org
+set system config-management commit-revisions '100'
+set system conntrack modules ftp
+set system conntrack modules h323
+set system conntrack modules nfs
+set system conntrack modules pptp
+set system conntrack modules sip
+set system conntrack modules sqlnet
+set system conntrack modules tftp
+set system console device ttyS0 speed '115200'
+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 syslog global facility all level 'info'
+set system syslog global facility local7 level 'debug'
+set system time-zone 'Europe/Berlin'
+set vrf name green protocols static route 20.0.0.0/8 next-hop 1.1.1.1 interface 'eth1'
+set vrf name green protocols static route 20.0.0.0/8 next-hop 1.1.1.1 vrf 'default'
+set vrf name green protocols static route 21.0.0.0/8 next-hop 2.2.1.1 interface 'eth1'
+set vrf name green protocols static route 21.0.0.0/8 next-hop 2.2.1.1 vrf 'default'
+set vrf name green protocols static route 100.0.0.0/8 interface eth0 distance '200'
+set vrf name green protocols static route 100.0.0.0/8 interface eth0 vrf 'default'
+set vrf name green protocols static route 101.0.0.0/8 interface eth0 vrf 'default'
+set vrf name green protocols static route 101.0.0.0/8 interface eth1
+set vrf name green protocols static route6 2001:db8:100::/40 next-hop fe80::1 interface 'eth0'
+set vrf name green protocols static route6 2001:db8:100::/40 next-hop fe80::1 vrf 'default'
+set vrf name green protocols static route6 2001:db8:300::/40 interface eth1 distance '20'
+set vrf name green protocols static route6 2001:db8:300::/40 interface eth1 vrf 'default'
+set vrf name green table '1000'
+set vrf name red protocols static route 30.0.0.0/8 next-hop 1.1.1.1 interface 'eth1'
+set vrf name red protocols static route 40.0.0.0/8 next-hop 2.2.1.1 interface 'eth1'
+set vrf name red protocols static route 40.0.0.0/8 next-hop 2.2.1.1 vrf 'default'
+set vrf name red protocols static route 103.0.0.0/8 interface eth0 distance '201'
+set vrf name red protocols static route 103.0.0.0/8 interface eth0 vrf 'default'
+set vrf name red protocols static route 104.0.0.0/8 interface eth0 vrf 'default'
+set vrf name red protocols static route 104.0.0.0/8 interface eth1 vrf 'default'
+set vrf name red protocols static route6 2001:db8:100::/40 next-hop fe80::1 interface 'eth0'
+set vrf name red protocols static route6 2001:db8:100::/40 next-hop fe80::1 vrf 'default'
+set vrf name red protocols static route6 2001:db8:400::/40 interface eth1 distance '24'
+set vrf name red protocols static route6 2001:db8:400::/40 interface eth1 vrf 'default'
+set vrf name red table '2000'
diff --git a/smoketest/config-tests/vrf-bgp-pppoe-underlay b/smoketest/config-tests/vrf-bgp-pppoe-underlay
new file mode 100644
index 000000000..bd64c914a
--- /dev/null
+++ b/smoketest/config-tests/vrf-bgp-pppoe-underlay
@@ -0,0 +1,186 @@
+set interfaces bridge br50 address '192.168.0.1/24'
+set interfaces bridge br50 member interface eth0.50
+set interfaces bridge br50 member interface eth2
+set interfaces bridge br50 member interface eth3
+set interfaces dummy dum0 address '100.64.51.252/32'
+set interfaces dummy dum0 address '2001:db8:200:ffff::1/128'
+set interfaces dummy dum0 vrf 'vyos-test-01'
+set interfaces ethernet eth0 offload gro
+set interfaces ethernet eth0 offload rps
+set interfaces ethernet eth0 ring-buffer rx '256'
+set interfaces ethernet eth0 ring-buffer tx '256'
+set interfaces ethernet eth0 vif 5 address '2001:db8:200:f0::114/64'
+set interfaces ethernet eth0 vif 5 address '100.64.50.121/28'
+set interfaces ethernet eth0 vif 5 vrf 'vyos-test-01'
+set interfaces ethernet eth0 vif 10 address '2001:db8:200:10::ffff/64'
+set interfaces ethernet eth0 vif 10 address '2001:db8:200::ffff/64'
+set interfaces ethernet eth0 vif 10 address '100.64.50.62/26'
+set interfaces ethernet eth0 vif 10 vrf 'vyos-test-01'
+set interfaces ethernet eth0 vif 15 address '100.64.50.78/28'
+set interfaces ethernet eth0 vif 15 address '2001:db8:200:15::ffff/64'
+set interfaces ethernet eth0 vif 15 vrf 'vyos-test-01'
+set interfaces ethernet eth0 vif 50 description 'Member of bridge br50'
+set interfaces ethernet eth0 vif 110 address '100.64.51.190/27'
+set interfaces ethernet eth0 vif 110 address '100.64.51.158/28'
+set interfaces ethernet eth0 vif 110 address '2001:db8:200:101::ffff/64'
+set interfaces ethernet eth0 vif 110 vrf 'vyos-test-01'
+set interfaces ethernet eth0 vif 410 address '100.64.51.206/28'
+set interfaces ethernet eth0 vif 410 address '2001:db8:200:104::ffff/64'
+set interfaces ethernet eth0 vif 410 vrf 'vyos-test-01'
+set interfaces ethernet eth0 vif 500 address '100.64.51.238/28'
+set interfaces ethernet eth0 vif 500 address '2001:db8:200:50::ffff/64'
+set interfaces ethernet eth0 vif 500 vrf 'vyos-test-01'
+set interfaces ethernet eth0 vif 520 address '100.64.50.190/28'
+set interfaces ethernet eth0 vif 520 address '2001:db8:200:520::ffff/64'
+set interfaces ethernet eth0 vif 520 vrf 'vyos-test-01'
+set interfaces ethernet eth0 vif 666 address '2001:db8:200:ff::101:1/112'
+set interfaces ethernet eth0 vif 666 address '100.64.51.223/31'
+set interfaces ethernet eth0 vif 666 vrf 'vyos-test-01'
+set interfaces ethernet eth0 vif 800 address '2001:db8:200:ff::104:1/112'
+set interfaces ethernet eth0 vif 800 address '100.64.51.212/31'
+set interfaces ethernet eth0 vif 800 vrf 'vyos-test-01'
+set interfaces ethernet eth0 vif 810 address '100.64.51.30/27'
+set interfaces ethernet eth0 vif 810 address '2001:db8:200:102::ffff/64'
+set interfaces ethernet eth0 vif 810 vrf 'vyos-test-01'
+set interfaces ethernet eth1 offload gro
+set interfaces ethernet eth1 offload rps
+set interfaces ethernet eth1 ring-buffer rx '256'
+set interfaces ethernet eth1 ring-buffer tx '256'
+set interfaces ethernet eth2 offload gro
+set interfaces ethernet eth3 offload gro
+set interfaces loopback lo
+set interfaces pppoe pppoe7 authentication password 'vyos'
+set interfaces pppoe pppoe7 authentication username 'vyos'
+set interfaces pppoe pppoe7 dhcpv6-options pd 0 interface br50 address '1'
+set interfaces pppoe pppoe7 dhcpv6-options pd 0 length '56'
+set interfaces pppoe pppoe7 ip adjust-mss '1452'
+set interfaces pppoe pppoe7 ipv6 address autoconf
+set interfaces pppoe pppoe7 ipv6 adjust-mss '1432'
+set interfaces pppoe pppoe7 mtu '1492'
+set interfaces pppoe pppoe7 no-peer-dns
+set interfaces pppoe pppoe7 source-interface 'eth1'
+set interfaces virtual-ethernet veth0 address '100.64.51.220/31'
+set interfaces virtual-ethernet veth0 address '2001:db8:200:ff::105:1/112'
+set interfaces virtual-ethernet veth0 description 'Core: connect vyos-test-01 and default VRF'
+set interfaces virtual-ethernet veth0 peer-name 'veth1'
+set interfaces virtual-ethernet veth1 address '100.64.51.221/31'
+set interfaces virtual-ethernet veth1 address '2001:db8:200:ff::105:2/112'
+set interfaces virtual-ethernet veth1 description 'Core: connect vyos-test-01 and default VRF'
+set interfaces virtual-ethernet veth1 peer-name 'veth0'
+set interfaces virtual-ethernet veth1 vrf 'vyos-test-01'
+set interfaces wireguard wg500 address '100.64.51.209/31'
+set interfaces wireguard wg500 mtu '1500'
+set interfaces wireguard wg500 peer A address '192.0.2.1'
+set interfaces wireguard wg500 peer A allowed-ips '0.0.0.0/0'
+set interfaces wireguard wg500 peer A port '5500'
+set interfaces wireguard wg500 peer A public-key 'KGSXF4QckzGe7f7CT+r6VZ5brOD/pVYk8yvrxOQ+X0Y='
+set interfaces wireguard wg500 port '5500'
+set interfaces wireguard wg500 private-key 'iLJh6Me6AdPJtNv3dgGhUbtyFxExxmNU4v0Fs6YE2Xc='
+set interfaces wireguard wg500 vrf 'vyos-test-01'
+set interfaces wireguard wg501 address '2001:db8:200:ff::102:2/112'
+set interfaces wireguard wg501 mtu '1500'
+set interfaces wireguard wg501 peer A address '2001:db8:300::1'
+set interfaces wireguard wg501 peer A allowed-ips '::/0'
+set interfaces wireguard wg501 peer A port '5501'
+set interfaces wireguard wg501 peer A public-key 'OF+1OJ+VfQ0Yw1mgVtQ2ion4CnAdy8Bvx7yEiO4+Pn8='
+set interfaces wireguard wg501 port '5501'
+set interfaces wireguard wg501 private-key '0MP5X0PW58O4q2LDpuIXgZ0ySyAoWH8/kdpvQccCbUU='
+set interfaces wireguard wg501 vrf 'vyos-test-01'
+set interfaces wireguard wg666 address '172.29.0.0/31'
+set interfaces wireguard wg666 mtu '1500'
+set interfaces wireguard wg666 peer B allowed-ips '0.0.0.0/0'
+set interfaces wireguard wg666 peer B public-key '2HT+RfwcqJMYNYzdmtmpem8Ht0dL37o31APHVwmh024='
+set interfaces wireguard wg666 port '50666'
+set interfaces wireguard wg666 private-key 'zvPnp2MLAoX7SotuHLFLDyy4sdlD7ttbD1xNEqA3mkU='
+set nat source rule 100 outbound-interface name 'pppoe7'
+set nat source rule 100 source address '192.168.0.0/24'
+set nat source rule 100 translation address 'masquerade'
+set policy prefix-list AS100-origin-v4 rule 10 action 'permit'
+set policy prefix-list AS100-origin-v4 rule 10 prefix '100.64.0.0/12'
+set policy prefix-list AS100-origin-v4 rule 100 action 'permit'
+set policy prefix-list AS100-origin-v4 rule 100 prefix '0.0.0.0/0'
+set policy prefix-list AS200-origin-v4 rule 10 action 'permit'
+set policy prefix-list AS200-origin-v4 rule 10 prefix '10.0.0.0/8'
+set policy prefix-list AS200-origin-v4 rule 20 action 'permit'
+set policy prefix-list AS200-origin-v4 rule 20 prefix '172.16.0.0/12'
+set policy prefix-list6 AS100-origin-v6 rule 10 action 'permit'
+set policy prefix-list6 AS100-origin-v6 rule 10 prefix '2001:db8:200::/40'
+set policy prefix-list6 AS200-origin-v6 rule 10 action 'permit'
+set policy prefix-list6 AS200-origin-v6 rule 10 prefix '2001:db8:100::/40'
+set protocols static route 100.64.50.0/23 next-hop 100.64.51.221
+set protocols static route 192.0.2.255/32 interface pppoe7
+set protocols static route6 2001:db8:ffff:ffff:ffff:ffff:ffff:ffff/128 interface pppoe7
+set qos interface pppoe7 egress 'isp-out'
+set qos policy shaper isp-out bandwidth '38mbit'
+set qos policy shaper isp-out default bandwidth '100%'
+set qos policy shaper isp-out default burst '15k'
+set qos policy shaper isp-out default queue-limit '1000'
+set qos policy shaper isp-out default queue-type 'fq-codel'
+set service router-advert interface br50 prefix ::/64 preferred-lifetime '2700'
+set service router-advert interface br50 prefix ::/64 valid-lifetime '5400'
+set service router-advert interface eth0.500 default-preference 'high'
+set service router-advert interface eth0.500 name-server '2001:db8:200::1'
+set service router-advert interface eth0.500 name-server '2001:db8:200::2'
+set service router-advert interface eth0.500 prefix 2001:db8:200:50::/64 valid-lifetime 'infinity'
+set service router-advert interface eth0.520 default-preference 'high'
+set service router-advert interface eth0.520 name-server '2001:db8:200::1'
+set service router-advert interface eth0.520 name-server '2001:db8:200::2'
+set service router-advert interface eth0.520 prefix 2001:db8:200:520::/64 valid-lifetime 'infinity'
+set service ssh disable-host-validation
+set service ssh dynamic-protection allow-from '100.64.0.0/10'
+set service ssh dynamic-protection allow-from '2001:db8:200::/40'
+set system config-management commit-revisions '100'
+set system conntrack modules ftp
+set system conntrack modules h323
+set system conntrack modules nfs
+set system conntrack modules pptp
+set system conntrack modules sip
+set system conntrack modules sqlnet
+set system conntrack modules tftp
+set system console device ttyS0 speed '115200'
+set system domain-name 'vyos.net'
+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 name-server '192.168.0.1'
+set system syslog global facility all level 'info'
+set system syslog global facility local7 level 'debug'
+set system time-zone 'Europe/Berlin'
+set vrf bind-to-all
+set vrf name vyos-test-01 protocols bgp address-family ipv4-unicast network 100.64.50.0/23
+set vrf name vyos-test-01 protocols bgp address-family ipv6-unicast network 2001:db8:200:ffff::1/128
+set vrf name vyos-test-01 protocols bgp neighbor 100.64.51.208 peer-group 'AS100v4'
+set vrf name vyos-test-01 protocols bgp neighbor 100.64.51.222 address-family ipv4-unicast default-originate
+set vrf name vyos-test-01 protocols bgp neighbor 100.64.51.222 address-family ipv4-unicast maximum-prefix '10'
+set vrf name vyos-test-01 protocols bgp neighbor 100.64.51.222 address-family ipv4-unicast prefix-list export 'AS100-origin-v4'
+set vrf name vyos-test-01 protocols bgp neighbor 100.64.51.222 address-family ipv4-unicast prefix-list import 'AS200-origin-v4'
+set vrf name vyos-test-01 protocols bgp neighbor 100.64.51.222 address-family ipv4-unicast soft-reconfiguration inbound
+set vrf name vyos-test-01 protocols bgp neighbor 100.64.51.222 capability dynamic
+set vrf name vyos-test-01 protocols bgp neighbor 100.64.51.222 remote-as '200'
+set vrf name vyos-test-01 protocols bgp neighbor 100.64.51.251 peer-group 'AS100v4'
+set vrf name vyos-test-01 protocols bgp neighbor 100.64.51.251 shutdown
+set vrf name vyos-test-01 protocols bgp neighbor 100.64.51.254 peer-group 'AS100v4'
+set vrf name vyos-test-01 protocols bgp neighbor 100.64.51.254 shutdown
+set vrf name vyos-test-01 protocols bgp neighbor 2001:db8:200:ff::101:2 address-family ipv6-unicast maximum-prefix '10'
+set vrf name vyos-test-01 protocols bgp neighbor 2001:db8:200:ff::101:2 address-family ipv6-unicast prefix-list export 'AS100-origin-v6'
+set vrf name vyos-test-01 protocols bgp neighbor 2001:db8:200:ff::101:2 address-family ipv6-unicast prefix-list import 'AS200-origin-v6'
+set vrf name vyos-test-01 protocols bgp neighbor 2001:db8:200:ff::101:2 address-family ipv6-unicast soft-reconfiguration inbound
+set vrf name vyos-test-01 protocols bgp neighbor 2001:db8:200:ff::101:2 capability dynamic
+set vrf name vyos-test-01 protocols bgp neighbor 2001:db8:200:ff::101:2 remote-as '200'
+set vrf name vyos-test-01 protocols bgp neighbor 2001:db8:200:ffff::2 peer-group 'AS100v6'
+set vrf name vyos-test-01 protocols bgp neighbor 2001:db8:200:ffff::2 shutdown
+set vrf name vyos-test-01 protocols bgp neighbor 2001:db8:200:ffff::a peer-group 'AS100v6'
+set vrf name vyos-test-01 protocols bgp peer-group AS100v4 address-family ipv4-unicast nexthop-self
+set vrf name vyos-test-01 protocols bgp peer-group AS100v4 capability dynamic
+set vrf name vyos-test-01 protocols bgp peer-group AS100v4 remote-as 'internal'
+set vrf name vyos-test-01 protocols bgp peer-group AS100v4 update-source 'dum0'
+set vrf name vyos-test-01 protocols bgp peer-group AS100v6 address-family ipv6-unicast nexthop-self
+set vrf name vyos-test-01 protocols bgp peer-group AS100v6 capability dynamic
+set vrf name vyos-test-01 protocols bgp peer-group AS100v6 remote-as 'internal'
+set vrf name vyos-test-01 protocols bgp peer-group AS100v6 update-source 'dum0'
+set vrf name vyos-test-01 protocols bgp system-as '100'
+set vrf name vyos-test-01 protocols static route 100.64.50.0/23 blackhole
+set vrf name vyos-test-01 protocols static route 100.64.51.32/27 next-hop 100.64.51.5
+set vrf name vyos-test-01 protocols static route 192.168.0.0/24 next-hop 100.64.51.220
+set vrf name vyos-test-01 protocols static route6 2001:db8:2fe:ffff::/64 next-hop 2001:db8:200:102::5
+set vrf name vyos-test-01 table '1000'
diff --git a/smoketest/config-tests/vrf-ospf b/smoketest/config-tests/vrf-ospf
new file mode 100644
index 000000000..fd14615e0
--- /dev/null
+++ b/smoketest/config-tests/vrf-ospf
@@ -0,0 +1,59 @@
+set interfaces ethernet eth0 address '192.0.2.1/24'
+set interfaces ethernet eth0 offload gro
+set interfaces ethernet eth1 offload gro
+set interfaces ethernet eth1 vrf 'red'
+set interfaces ethernet eth2 offload gro
+set interfaces ethernet eth2 vrf 'blue'
+set protocols ospf area 0 network '192.0.2.0/24'
+set protocols ospf interface eth0 authentication md5 key-id 10 md5-key 'ospfkey'
+set protocols ospf interface eth0 passive disable
+set protocols ospf log-adjacency-changes
+set protocols ospf parameters abr-type 'cisco'
+set protocols ospf parameters router-id '1.2.3.4'
+set protocols ospf passive-interface 'default'
+set service ntp allow-client address '0.0.0.0/0'
+set service ntp allow-client address '::/0'
+set service ntp server 0.pool.ntp.org
+set service ntp server 1.pool.ntp.org
+set service ntp server 2.pool.ntp.org
+set system config-management commit-revisions '100'
+set system conntrack modules ftp
+set system conntrack modules h323
+set system conntrack modules nfs
+set system conntrack modules pptp
+set system conntrack modules sip
+set system conntrack modules sqlnet
+set system conntrack modules tftp
+set system console device ttyS0 speed '115200'
+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 syslog global facility all level 'info'
+set system syslog global facility local7 level 'debug'
+set system time-zone 'Europe/Berlin'
+set vrf name blue protocols ospf area 0 network '172.18.201.0/24'
+set vrf name blue protocols ospf interface eth2 authentication md5 key-id 30 md5-key 'vyoskey456'
+set vrf name blue protocols ospf interface eth2 dead-interval '40'
+set vrf name blue protocols ospf interface eth2 hello-interval '10'
+set vrf name blue protocols ospf interface eth2 passive disable
+set vrf name blue protocols ospf interface eth2 priority '1'
+set vrf name blue protocols ospf interface eth2 retransmit-interval '5'
+set vrf name blue protocols ospf interface eth2 transmit-delay '1'
+set vrf name blue protocols ospf log-adjacency-changes
+set vrf name blue protocols ospf parameters abr-type 'cisco'
+set vrf name blue protocols ospf parameters router-id '5.6.7.8'
+set vrf name blue protocols ospf passive-interface 'default'
+set vrf name blue table '2000'
+set vrf name red protocols ospf area 0 network '172.18.202.0/24'
+set vrf name red protocols ospf interface eth1 authentication md5 key-id 20 md5-key 'vyoskey123'
+set vrf name red protocols ospf interface eth1 dead-interval '40'
+set vrf name red protocols ospf interface eth1 hello-interval '10'
+set vrf name red protocols ospf interface eth1 passive disable
+set vrf name red protocols ospf interface eth1 priority '1'
+set vrf name red protocols ospf interface eth1 retransmit-interval '5'
+set vrf name red protocols ospf interface eth1 transmit-delay '1'
+set vrf name red protocols ospf log-adjacency-changes
+set vrf name red protocols ospf parameters abr-type 'cisco'
+set vrf name red protocols ospf parameters router-id '9.10.11.12'
+set vrf name red protocols ospf passive-interface 'default'
+set vrf name red table '1000'
diff --git a/smoketest/config-tests/wireless-basic b/smoketest/config-tests/wireless-basic
index 77db29c2f..d9e6c8fac 100644
--- a/smoketest/config-tests/wireless-basic
+++ b/smoketest/config-tests/wireless-basic
@@ -20,6 +20,6 @@ set system console device ttyS0 speed '115200'
set system domain-name 'dev.vyos.net'
set system host-name 'WR1'
set system login user vyos authentication encrypted-password '$6$O5gJRlDYQpj$MtrCV9lxMnZPMbcxlU7.FI793MImNHznxGoMFgm3Q6QP3vfKJyOSRCt3Ka/GzFQyW1yZS4NS616NLHaIPPFHc0'
-set system wireless country-code 'es'
set system syslog global facility all level 'info'
set system syslog global facility local7 level 'debug'
+set system wireless country-code 'es'
diff --git a/smoketest/configs/basic-api-service b/smoketest/configs/basic-api-service
index f997ccd73..d5364d3e6 100644
--- a/smoketest/configs/basic-api-service
+++ b/smoketest/configs/basic-api-service
@@ -3,8 +3,6 @@ interfaces {
address 192.0.2.1/31
address 2001:db8::1234/64
}
- ethernet eth1 {
- }
loopback lo {
}
}
diff --git a/smoketest/configs/bgp-dmvpn-hub b/smoketest/configs/bgp-dmvpn-hub
index fc5aadd8f..fc0be5e07 100644
--- a/smoketest/configs/bgp-dmvpn-hub
+++ b/smoketest/configs/bgp-dmvpn-hub
@@ -1,8 +1,12 @@
interfaces {
ethernet eth0 {
address 100.64.10.1/31
+ speed auto
+ duplex auto
}
ethernet eth1 {
+ speed auto
+ duplex auto
}
loopback lo {
}
@@ -171,4 +175,3 @@ vpn {
// Warning: Do not remove the following line.
// vyos-config-version: "broadcast-relay@1:cluster@1:config-management@1:conntrack@3:conntrack-sync@2: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.0-epa3
-
diff --git a/smoketest/configs/bgp-evpn-l3vpn-pe-router b/smoketest/configs/bgp-evpn-l3vpn-pe-router
index b1ca7fae3..c676463b8 100644
--- a/smoketest/configs/bgp-evpn-l3vpn-pe-router
+++ b/smoketest/configs/bgp-evpn-l3vpn-pe-router
@@ -38,7 +38,7 @@ interfaces {
ethernet eth0 {
address 192.0.2.59/27
address 2001:db8:ffff::59/64
- description "out-of-band management"
+ description "Out-of-Band Managament Port"
vrf mgmt
}
ethernet eth1 {
diff --git a/smoketest/configs/bgp-rpki b/smoketest/configs/bgp-rpki
index dffab4c69..5588f15c9 100644
--- a/smoketest/configs/bgp-rpki
+++ b/smoketest/configs/bgp-rpki
@@ -4,6 +4,7 @@ interfaces {
address 2001:db8::ffff/64
}
ethernet eth1 {
+ address 100.64.0.1/24
}
loopback lo {
}
diff --git a/smoketest/configs/isis-small b/smoketest/configs/isis-small
index 5a4201988..79a2f042f 100644
--- a/smoketest/configs/isis-small
+++ b/smoketest/configs/isis-small
@@ -4,19 +4,35 @@ interfaces {
}
ethernet eth0 {
duplex auto
+ offload {
+ sg
+ tso
+ }
speed auto
}
ethernet eth1 {
address 192.0.2.1/24
duplex auto
+ offload {
+ sg
+ tso
+ }
speed auto
}
ethernet eth2 {
duplex auto
+ offload {
+ sg
+ tso
+ }
speed auto
}
ethernet eth3 {
duplex auto
+ offload {
+ sg
+ tso
+ }
speed auto
}
}
@@ -41,9 +57,9 @@ policy {
}
}
protocols {
- isis FOO {
+ isis {
interface eth1 {
- bfd
+ bfd
}
net 49.0001.1921.6800.1002.00
redistribute {
@@ -61,6 +77,17 @@ system {
config-management {
commit-revisions 200
}
+ conntrack {
+ modules {
+ ftp
+ h323
+ nfs
+ pptp
+ sip
+ sqlnet
+ tftp
+ }
+ }
console {
device ttyS0 {
speed 115200
@@ -77,11 +104,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 {
@@ -99,5 +126,5 @@ system {
// Warning: Do not remove the following line.
-// vyos-config-version: "broadcast-relay@1:cluster@1:config-management@1:conntrack@1:conntrack-sync@1:dhcp-relay@2:dhcp-server@5:dhcpv6-server@1:dns-forwarding@3:firewall@5:https@2:interfaces@18:ipoe-server@1:ipsec@5:l2tp@3:lldp@1:mdns@1:nat@5:ntp@1:pppoe-server@5:pptp@2:qos@1:quagga@7:rpki@1:salt@1:snmp@2:ssh@2:sstp@3:system@20:vrrp@2:vyos-accel-ppp@2:wanloadbalance@3:webproxy@2:zone-policy@1"
-// Release version: 1.3.0-rc1
+// vyos-config-version: "broadcast-relay@1:cluster@1:config-management@1:conntrack@3:conntrack-sync@2: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.0
diff --git a/smoketest/configs/pppoe-server b/smoketest/configs/pppoe-server
index ff5815e29..a01a45115 100644
--- a/smoketest/configs/pppoe-server
+++ b/smoketest/configs/pppoe-server
@@ -4,8 +4,12 @@ interfaces {
}
ethernet eth1 {
address 192.168.0.1/24
+ speed auto
+ duplex auto
}
ethernet eth2 {
+ speed auto
+ duplex auto
}
loopback lo {
}
diff --git a/smoketest/scripts/cli/base_vyostest_shim.py b/smoketest/scripts/cli/base_vyostest_shim.py
index efaa74fe0..4bcc50453 100644
--- a/smoketest/scripts/cli/base_vyostest_shim.py
+++ b/smoketest/scripts/cli/base_vyostest_shim.py
@@ -74,6 +74,11 @@ class VyOSUnitTestSHIM:
print('del ' + ' '.join(config))
self._session.delete(config)
+ def cli_discard(self):
+ if self.debug:
+ print('DISCARD')
+ self._session.discard()
+
def cli_commit(self):
self._session.commit()
# during a commit there is a process opening commit_lock, and run() returns 0
diff --git a/smoketest/scripts/cli/test_service_stunnel.py b/smoketest/scripts/cli/test_service_stunnel.py
new file mode 100644
index 000000000..3aeffd09e
--- /dev/null
+++ b/smoketest/scripts/cli/test_service_stunnel.py
@@ -0,0 +1,624 @@
+#!/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 re
+import unittest
+
+from base_vyostest_shim import VyOSUnitTestSHIM
+
+from vyos.configsession import ConfigSessionError
+from vyos.utils.process import process_named_running
+from vyos.utils.file import read_file
+
+
+PROCESS_NAME = 'stunnel'
+STUNNEL_CONF = '/run/stunnel/stunnel.conf'
+base_path = ['service', 'stunnel']
+
+ca_certificate = """
+MIIDnTCCAoWgAwIBAgIUcSMo/zT/GUAyH3uM3Hr3qjCDmMUwDQYJKoZIhvcNAQELBQAwVzELMAkGA1U
+EBhMCR0IxEzARBgNVBAgMClNvbWUtU3RhdGUxEjAQBgNVBAcMCVNvbWUtQ2l0eTENMAsGA1UECgwEVn
+lPUzEQMA4GA1UEAwwHdnlvcy5pbzAeFw0yNDA2MDQwNjU1MDFaFw0yOTA2MDMwNjU1MDFaMFcxCzAJB
+gNVBAYTAkdCMRMwEQYDVQQIDApTb21lLVN0YXRlMRIwEAYDVQQHDAlTb21lLUNpdHkxDTALBgNVBAoM
+BFZ5T1MxEDAOBgNVBAMMB3Z5b3MuaW8wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCzN7B
+Zw0OBBgeGL7KCKdDIUfBEhh08+3V8Nm7K23mU/pYd3bR5WXt9VWkW5YWUw1hr1N3qEQ2AZX8TrIDj37
+zzy1jyDCvJHGWnKTOOAboNIInP+PvUQrSH8SDAw/+/KjKKgM069NFhGq9TTHg4BAYC0GsZL+JE3Ptee
+cIVmekf5Dw+vnD0Mlwx5Ouaf/9OwRcGhfwEkIORQLXDuMayOI/JdFbaDVlA6Z/d8GLp3Xlc8/l5XFtg
+fvMNQSB9B69Cs4qwU/yey8tPWeDBiW6Cx2XOnKqiNBaCY1BzvSH+hmHcos1DOLHgEZ3d2zaNn2mrhmB
+Ry7/5Ww7O5PoF00OB9WHFAgMBAAGjYTBfMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB
+0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcDATAdBgNVHQ4EFgQU4zgMpOMOweRZUbeNewJnh5xZL
+XwwDQYJKoZIhvcNAQELBQADggEBAAEK+jXvCKuC/n8qu9XFcLYfO3kUKPlXD30V61KRZilHyYGYu0MY
+sSNeX8+K7CpeAo06HHocrrDfCKltoLFuix7qblr2DEub+v3V21pllMfThkz9FsXWFGfmOyI7sXNXUg9
+cVQHzj2SvMj+IfnJoCIuYnigmlKVTuxV31iYv2RpML/PBw29xI0G/AsmXZK4wOQ0eA9gU+ggURE98hG
+8f4DRpGVnlyP1d+P2Va0bsl3Yek9QfrotnmE1EzwZzPZyCL9rv8oDjfJ98O3YqoNSRNvD+Glke2ZlTj
+WFw+uCj0GTki5+V40E9X9Rwcje+s/5zWDBfu0akufcI1nsu++rZz/s=
+"""
+
+ca_private_key = """
+MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCzN7BZw0OBBgeGL7KCKdDIUfBEhh0
+8+3V8Nm7K23mU/pYd3bR5WXt9VWkW5YWUw1hr1N3qEQ2AZX8TrIDj37zzy1jyDCvJHGWnKTOOAboNII
+nP+PvUQrSH8SDAw/+/KjKKgM069NFhGq9TTHg4BAYC0GsZL+JE3PteecIVmekf5Dw+vnD0Mlwx5Ouaf
+/9OwRcGhfwEkIORQLXDuMayOI/JdFbaDVlA6Z/d8GLp3Xlc8/l5XFtgfvMNQSB9B69Cs4qwU/yey8tP
+WeDBiW6Cx2XOnKqiNBaCY1BzvSH+hmHcos1DOLHgEZ3d2zaNn2mrhmBRy7/5Ww7O5PoF00OB9WHFAgM
+BAAECggEAHFC/pacCutdrh+lwUD1wFb5QclsoMnLeYJYvEhD0GDTTHfvh4ExhhO9iL7Jq1RK6HStgNn
+OkSPWASuj14kr+zRwDPRbsMhWw/+S0FwsxzJIoA/poO2SgplvUG3C8LwVpP9XS1y5ICIoRSl1qHxuPo
+ZExYqTcoJmzg31ES2pqWVXPx14DdpE6yvSL2XwFS4mb291OkydnvKSBcK0MwgEWLQHouzMjihJ1MCXx
+7NXsOxFX76OpmywMW7EtTLEngxL9b61hCYwWeNxmx9pN8qRzmvayKl40VLyqAlVcElZ7aEK0+O/Qpsu
+QhsFRjA4HcXUqlHbvh92OqX+QmBU2RIZ27wKBgQDnJ8E8cJOlJ9ZvFBfw8az49IX9E72oxb2yaXm0Eo
+OQ2Jz88+b2jzWqf3wdGvigNO25DbdYYodR7iJJo4OYPuyAnkJMWdPQ91ADo7ABwJiBqtUHC+Gvq5Rmm
+I2T3T4+Vqu5Sa8lVfHWfv7Pnb++I5/7bH+VuGspyf+0NcpPh9UfIwKBgQDGet1wh0+2378HnnQNb10w
+wxDiMC2hP+3RGPB6bKHLJ60LE+Ed2KFY+j8Q1+jQk9eMe6A75bwB/q6rMO1evpauCHTJoA863HxXtuL
+P9ozVpDk9k4TbiSOsD0s8TXL3XG1ANshk4VfuLboKj4MEwiuxfGt6QGpsgLfHcmlkFIM99wKBgQDeea
+C97wvrVOBJoGk6eSAlrBKZdTqBCXB+Go4MBhWifxj5TDXq8AKSyohF6wOIDekOxmjEJHBhJnTRsxKgo
+U82qxrcKUh4Qs878Xsg9KDTi/vkAEeCr/zwkbsRqUqS7Q/yET0FDibobuoIIKe+9MKxVcel7g0V91in
+tW22BeHVSQKBgQCKctwSiaCWTP8A/ouvb3ZO9FLLpJW/vEtUpxPgIfS+NH/lkUlfu2PZID5rrmAtVmN
+uEDJWdcsujQwkSC3cABA1d5qXpnnZMkHeIamXLUFSKYrwI/3x8XibpdNyTgga+jMPLuecTwA6GVWD1l
+WrNRKrbMG/9j0GUMdhbbKMaC6gQwKBgQCC9EUZBqCXS167OZNPQN4oKx6nJ9uTKUVyPigr12cMpPL6t
+JwZAVVwSnyg+cVznNrMhAnG547c0NnoOe+nd9zczLJOuQHHLSMZUH08c2ZWtwpwbHDWI55hfZL4te8e
+dEcxanXNYAfSMMtOoA+LmcCtfvqld/EucAN4mKTPGmWPQg==
+"""
+
+srv_certificate = """
+MIIDsTCCApmgAwIBAgIUIvMZU3zc3iYl6JzbDLSvr8NOK5swDQYJKoZIhvcNAQELBQAwVzELMAkGA1U
+EBhMCR0IxEzARBgNVBAgMClNvbWUtU3RhdGUxEjAQBgNVBAcMCVNvbWUtQ2l0eTENMAsGA1UECgwEVn
+lPUzEQMA4GA1UEAwwHdnlvcy5pbzAeFw0yNDA2MDQwNjU1NDVaFw0yNTA2MDQwNjU1NDVaMFcxCzAJB
+gNVBAYTAkdCMRMwEQYDVQQIDApTb21lLVN0YXRlMRIwEAYDVQQHDAlTb21lLUNpdHkxDTALBgNVBAoM
+BFZ5T1MxEDAOBgNVBAMMB3Z5b3MuaW8wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCtani
+zx0h1fEH0pMRBB7V7nUAnSOAiCRUNpeTz6RoUqH0y/UaxM+kqitUm+MSAWxEJAXW4ZlxNzU+tC6DOwP
+d+7/rZsT6fKeCbMIs8Es9VaXd2sZzb7DajEygeyIy1b3JGXIiNJ9KcOxzhmu5VHe+6qLCO3FDt4iFIr
+HXJxwQKm8qL6zgn7f9kboQYBHKOhY8x+ghkhLYAwMlvIHGwjF+I/p65J1LOBhAsmOLcX0/CygKXz5qe
+wyG16zNft6OWPIOBTs56NnNlW6EdqomxBM5SWr888qEjUy0ruUpAH4Ug8SloL+AeDW+TqUUcfoOiTi9
+3ZJ/t9YRj0+wQw4vakpUTAgMBAAGjdTBzMAwGA1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQDAgeAMBMGA1
+UdJQQMMAoGCCsGAQUFBwMBMB0GA1UdDgQWBBTCubAbczcJE76YabOv+2oVV1zNSzAfBgNVHSMEGDAWg
+BTjOAyk4w7B5FlRt417AmeHnFktfDANBgkqhkiG9w0BAQsFAAOCAQEAjW9ovWDgEoUi8DWwNVtudKiT
+6ylJTSMqY7C+qJlRHpnZ64TNZFXI0BldYZr0QXGsZ257m9m9BiUcZr6UR0hywy4SiyxuteufniKIp9E
+vqv0aJhdTXO+l5msaGWu7YvWYqXW0m3rA9oiNYyBcNSFzlwiyvztYUmFFPrvhFHVSt+DuxZSltdf78G
+exS4YRMCTI+cuCfBt65Vkss4bNJH7kyWVc5aSQ/vKitMxB10gzsUa7psgS6LsBWxnehd3HKBPaHiWG9
+ssHKhHJWfjifgz0K1Y0/vi33USPJ1cBhWWx/dolXWmSmpfqXpD3Q84YjVWIRnFpQzwbT650v/H+fwB1
+zw==
+"""
+
+srv_private_key = """
+MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCtanizx0h1fEH0pMRBB7V7nUAnSOA
+iCRUNpeTz6RoUqH0y/UaxM+kqitUm+MSAWxEJAXW4ZlxNzU+tC6DOwPd+7/rZsT6fKeCbMIs8Es9VaX
+d2sZzb7DajEygeyIy1b3JGXIiNJ9KcOxzhmu5VHe+6qLCO3FDt4iFIrHXJxwQKm8qL6zgn7f9kboQYB
+HKOhY8x+ghkhLYAwMlvIHGwjF+I/p65J1LOBhAsmOLcX0/CygKXz5qewyG16zNft6OWPIOBTs56NnNl
+W6EdqomxBM5SWr888qEjUy0ruUpAH4Ug8SloL+AeDW+TqUUcfoOiTi93ZJ/t9YRj0+wQw4vakpUTAgM
+BAAECggEAEa54SyBSb4QxYg/bYM636Y2G3oU229GK6il+4YMOy99tZeG0L6+IInR7DO5ddBbqSD2esq
+QL3PTw9EcUvi/9AYjXeL3H5vOeo+7Rq4OMIfx5wp+Ty6AB5s5hD1kfG7AWzzzHwYNiHS2Gdtb/XldfO
+5bP6xO5/rSenynSbWCTir8yakfoDenT12CXWzU+T10MKhoTXb/Uao+bMjziKEviK6OWq0vsLlDqyOAE
+Va685s7T0vHTfSs+yK9pqVypHXbkH1nJCoi9P4pcJ4Sslc3meStv3bqg8T62Ufv8QkQLTfJyKZlR1aV
+9ZjWT84YoH1XRnnkAZ+BMC267sHeBJbu6EQKBgQDbIUjQh/iPlkK77tFa//gSMD5ouJtuwtdS1MJ44p
+C81A140vjpkSCdU8zWRifi+akR1k6fXCp6VFUFvTCXkGlpbD4TNjCCRJjS4SoQ89jEnePQ2iS59jkn3
+V3OPNitOzk0jEm/x3R5wNdPlSX6+pLiUZAtMgcmCMv205VOkeqx8QKBgQDKmB3FtEfkKRrGkOJgaEkY
+iXp9b0Uy8peBoTcdqMMnXSlm9+CfIdhSwbQDiAhEcUeCE/S6TDqaMS+ekKFfs6kDlaJMStGsy81lwr5
+W/oZOldajDCu1CDInc+czkep10lsHdQwr71zXntiK3Fwq8Mr3ROBSpaH+DWIjILiQIOMzQwKBgQC7Mt
+UUqIQUjkZWbG/XcMLJLwOxzLukRLlUXsQAJ3WEixczN/BDAKM/JB7ikq5yfdwMi+tAwqjbNn4n1/bSF
+CGpWToyiWGpd9aimI6qStbNKSE9A47KeulbAAaqMFreqrB1Dr/WIRuFA9QsfXsjzLp8szcbFRj8ShmM
+tDZiF8/K0QKBgQCYbb0wzESu8RJZRhddC/m7QWzsxXReMdI2UTLj2N8EVf7ZnzTc5h0Znu4vHgGCZWy
+0/QjLxqDs9Ibsmcsg807+CG51UnHRvgFLSCvnzlcE943nXTfhXEpIDtdsoKO0hFHDGZjP0aeb/8LTL5
+sVH9jGFIdnB4ILYMxuu6bBokzvewKBgBWbjPppjrM46bZ0rwEYCcG0F/k6TKkw4pjyrDR4B0XsrqTjK
+0yz0ga7FHe10saeS2cXMqygdkjhWLZ6Zhrp0LAEzhEvdiBYeRH37J9Bvwo2YIHakox4hJCSXNnELs/A
+GhUb5YIISNnZnZZeUD/Z0IJXJryjk9eUbhDCgEZRVzeT
+"""
+
+
+def get_config_value(key):
+ tmp = read_file(STUNNEL_CONF)
+ tmp = re.findall(f'\n?{key}\s+(.*)', tmp)
+ return tmp
+
+
+def read_config():
+ config = {'global': {},
+ 'services': {}
+ }
+ service_pattern = re.compile(r'\[(.+?)\]')
+ key_value_pattern = re.compile(r'(\S+)\s*=\s*(.+)')
+ service = None
+
+ for line in read_file(STUNNEL_CONF).split('\n'):
+ line.strip()
+ if not line or line.startswith(';'):
+ continue
+ if service_pattern.match(line):
+ service = line.strip('[]')
+ config['services'][service] = {}
+ key_value_match = key_value_pattern.match(line)
+ if key_value_match:
+ key, value = key_value_match.group(1), key_value_match.group(2)
+ if service:
+ apply_value(config['services'][service], key, value)
+ else:
+ apply_value(config['global'], key, value)
+
+ return config
+
+
+def apply_value(service_config, key, value):
+ if service_config.get(key) is None:
+ service_config[key] = value
+ else:
+ if not isinstance(service_config[key], list):
+ service_config[key] = [
+ service_config[key]]
+ else:
+ service_config[key].append(value)
+
+
+class TestServiceStunnel(VyOSUnitTestSHIM.TestCase):
+ maxDiff = None
+ @classmethod
+ def setUpClass(cls):
+ super(TestServiceStunnel, cls).setUpClass()
+
+ # ensure we can also run this test on a live system - so lets clean
+ # out the current configuration :)
+ cls.cli_delete(cls, base_path)
+ cls.is_valid_conf = True
+
+ def tearDown(self):
+ # Check for running process
+ if self.is_valid_conf:
+ self.assertTrue(process_named_running(PROCESS_NAME))
+ self.is_valid_conf = True
+ # delete testing Stunnel config
+ self.cli_delete(base_path)
+ self.cli_delete(['pki'])
+ self.cli_commit()
+
+ # Check for stopped process
+ self.assertFalse(process_named_running(PROCESS_NAME))
+
+ def set_pki(self):
+ self.cli_set(['pki', 'ca', 'ca-1', 'certificate', ca_certificate.replace('\n','')])
+ self.cli_set(['pki', 'ca', 'ca-1', 'private', 'key', ca_private_key.replace('\n','')])
+ self.cli_set(['pki', 'certificate', 'srv-1', 'certificate', srv_certificate.replace('\n','')])
+ self.cli_set(['pki', 'certificate', 'srv-1', 'private', 'key', srv_private_key.replace('\n','')])
+ self.cli_commit()
+
+ def test_01_stunnel_simple_client(self):
+ service = 'app1'
+ self.cli_set(base_path + ['client', service, 'connect', 'port', '9001'])
+
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+
+ self.cli_set(base_path + ['client', service, 'listen', 'port', '8001'])
+
+ self.cli_commit()
+ config = read_config()
+
+ self.assertEqual('/run/stunnel/stunnel.pid', config['global']['pid'])
+ self.assertEqual('notice', config['global']['debug'])
+ self.assertListEqual([service], list(config['services']))
+ self.assertEqual('8001', config['services'][service]['accept'])
+ self.assertEqual('9001', config['services'][service]['connect'])
+ self.assertEqual('yes', config['services'][service]['client'])
+
+ def test_02_stunnel_simple_server(self):
+ service = 'ser1'
+ self.set_pki()
+ self.cli_set(base_path + ['server', service, 'connect', 'port', '8080'])
+ self.cli_set(base_path + ['server', service, 'ssl', 'certificate', 'srv-1'])
+
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+
+ self.cli_set(base_path + ['server', service, 'listen', 'port', '9001'])
+
+ self.cli_commit()
+ config = read_config()
+
+ self.assertEqual('/run/stunnel/stunnel.pid', config['global']['pid'])
+ self.assertEqual('notice', config['global']['debug'])
+ self.assertListEqual([service], list(config['services']))
+ self.assertEqual('9001', config['services'][service]['accept'])
+ self.assertEqual('8080', config['services'][service]['connect'])
+ self.assertIsNone(config['services'][service].get('client'))
+ self.assertEqual('/run/stunnel/server-ser1-srv-1.pem', config['services'][service]['cert'])
+ self.assertEqual('/run/stunnel/server-ser1-srv-1.pem.key', config['services'][service]['key'])
+
+ def test_03_multy_services(self):
+ self.set_pki()
+ clients = ['app1', 'app2', 'app3']
+ servers = ['serv1', 'serv2', 'serv3']
+ port = 80
+ for service in clients:
+ port += 1
+ self.cli_set(base_path + ['client', service, 'listen', 'port', f'{port}'])
+ port += 1
+ self.cli_set(base_path + ['client', service, 'connect', 'port', f'{port}'])
+ if service == 'app2':
+ self.cli_set(base_path + ['client', service, 'connect', 'address', f'192.168.0.10'])
+ self.cli_set(base_path + ['client', service, 'listen', 'address', '127.0.0.1'])
+ self.cli_set(base_path + ['client', service, 'protocol', 'connect'])
+ self.cli_set(base_path + ['client', service, 'options', 'authentication', 'basic'])
+ self.cli_set(base_path + ['client', service, 'options', 'domain', 'basic.com'])
+ self.cli_set(base_path + ['client', service, 'options', 'host', 'address', '127.0.0.1'])
+ self.cli_set(base_path + ['client', service, 'options', 'host', 'port', '5000'])
+ self.cli_set(base_path + ['client', service, 'options', 'password', 'test_pass'])
+ self.cli_set(base_path + ['client', service, 'options', 'username', 'test'])
+ if service == 'app3':
+ self.cli_set(base_path + ['client', service, 'ssl', 'ca-certificate', 'ca-1'])
+ self.cli_set(base_path + ['client', service, 'ssl', 'certificate', 'srv-1'])
+
+ for service in servers:
+ port += 1
+ self.cli_set(base_path + ['server', service, 'listen', 'port', f'{port}'])
+ port += 1
+ self.cli_set(base_path + ['server', service, 'connect', 'port', f'{port}'])
+ self.cli_set(base_path + ['server', service, 'ssl', 'certificate', 'srv-1'])
+ if service == 'serv2':
+ self.cli_set(base_path + ['server', service, 'ssl', 'ca-certificate', 'ca-1'])
+ self.cli_set(base_path + ['server', service, 'connect', 'address', f'google.com'])
+ self.cli_set(base_path + ['server', service, 'listen', 'address', f'127.0.0.1'])
+ if service == 'serv3':
+ self.cli_set(base_path + ['server', service, 'connect', 'address', f'10.18.105.10'])
+ self.cli_set(base_path + ['server', service, 'protocol', 'imap'])
+
+ self.cli_commit()
+ config = read_config()
+
+ self.assertEqual('/run/stunnel/stunnel.pid', config['global']['pid'])
+ self.assertListEqual(clients + servers, list(config['services']))
+ self.assertDictEqual(config['services'], {
+ 'app1': {
+ 'client': 'yes',
+ 'accept': '81',
+ 'connect': '82'
+ },
+ 'app2': {
+ 'client': 'yes',
+ 'accept': '127.0.0.1:83',
+ 'connect': '192.168.0.10:84',
+ 'protocol': 'connect',
+ 'protocolAuthentication': 'basic',
+ 'protocolDomain': 'basic.com',
+ 'protocolHost': '127.0.0.1:5000',
+ 'protocolPassword': 'test_pass',
+ 'protocolUsername': 'test'
+ },
+ 'app3': {
+ 'client': 'yes',
+ 'accept': '85',
+ 'connect': '86',
+ 'CApath': '/run/stunnel/ca',
+ 'CAfile': 'client-app3-ca.pem',
+ 'cert': '/run/stunnel/client-app3-srv-1.pem',
+ 'key': '/run/stunnel/client-app3-srv-1.pem.key'
+ },
+ 'serv1': {
+ 'accept': '87',
+ 'connect': '88',
+ 'cert': '/run/stunnel/server-serv1-srv-1.pem',
+ 'key': '/run/stunnel/server-serv1-srv-1.pem.key'
+ },
+ 'serv2': {
+ 'accept': '127.0.0.1:89',
+ 'connect': 'google.com:90',
+ 'CApath': '/run/stunnel/ca',
+ 'CAfile': 'server-serv2-ca.pem',
+ 'cert': '/run/stunnel/server-serv2-srv-1.pem',
+ 'key': '/run/stunnel/server-serv2-srv-1.pem.key'
+ },
+ 'serv3': {
+ 'accept': '91',
+ 'connect': '10.18.105.10:92',
+ 'protocol': 'imap',
+ 'cert': '/run/stunnel/server-serv3-srv-1.pem',
+ 'key': '/run/stunnel/server-serv3-srv-1.pem.key'
+ }
+ })
+
+ def test_04_cert_problems(self):
+ service = 'app1'
+ self.cli_set(base_path + ['client', service, 'connect', 'port', '9001'])
+ self.cli_set(base_path + ['client', service, 'listen', 'port', '8001'])
+ self.cli_set(base_path + ['client', service, 'ssl', 'ca-certificate', 'ca-2'])
+
+ # ca not exist in pki
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+
+ self.cli_delete(base_path + ['client', service, 'ssl', 'ca-certificate', 'ca-2'])
+ self.cli_set(base_path + ['client', service, 'ssl', 'certificate', 'srv-2'])
+
+ # cert not exist in pki
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+ self.cli_delete(base_path)
+
+ self.cli_set(base_path + ['server', service, 'connect', 'port', '8080'])
+ self.cli_set(base_path + ['server', service, 'listen', 'port', '9001'])
+
+ # Create server without any cert
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+
+ self.cli_set(base_path + ['server', service, 'ssl', 'ca-certificate', 'ca-2'])
+ # ca not exist in pki
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+
+ self.cli_delete(base_path + ['server', service, 'ssl', 'ca-certificate', 'ca-2'])
+ self.cli_set(base_path + ['server', service, 'ssl', 'certificate', 'srv-2'])
+ # cert not exist in pki
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+
+ self.is_valid_conf = False
+
+ def test_05_psk_auth(self):
+ modes = ['client', 'server']
+ psk_id_1 = 'psk_id_1'
+ psk_secret_1 = '1234567890ABCDEF1234567890ABCDEF'
+ psk_id_2 = 'psk_id_2'
+ psk_secret_2 = '1234567890ABCDEF1234567890ABCDEA'
+ expected_config = {
+ 'global': {'pid': '/run/stunnel/stunnel.pid',
+ 'debug': 'notice'},
+ 'services': {}}
+ port = 8000
+ for mode in modes:
+ service = f'{mode}-one'
+ psk_secrets = f'/run/stunnel/psk/{mode}_{service}.txt'
+ expected_config['services'][service] = {
+ 'PSKsecrets': psk_secrets,
+ }
+ port += 1
+ expected_config['services'][service]['accept'] = f'{port}'
+ self.cli_set(base_path + [mode, service, 'listen', 'port', f'{port}'])
+ port += 1
+ expected_config['services'][service]['connect'] = f'{port}'
+ self.cli_set(base_path + [mode, service, 'connect', 'port', f'{port}'])
+ self.cli_set(base_path + [mode, service, 'psk', 'smoketest1', 'id', psk_id_1])
+ with self.assertRaises(ConfigSessionError):
+ self.cli_set(base_path + [mode, service, 'psk', 'smoketest1', 'secret', '123'])
+ with self.assertRaises(ConfigSessionError):
+ self.cli_set(base_path + [mode, service, 'psk', 'smoketest1', 'secret', '1234567890ABCDEF1234567890ABCDEZ'])
+ self.cli_set(base_path + [mode, service, 'psk', 'smoketest1', 'secret', psk_secret_1])
+ self.cli_set(base_path + [mode, service, 'psk', 'smoketest2', 'id', psk_id_2])
+ self.cli_set(base_path + [mode, service, 'psk', 'smoketest2', 'secret', psk_secret_2])
+ if mode != 'server':
+ expected_config['services'][service]['client'] = 'yes'
+
+ self.cli_commit()
+ config = read_config()
+
+ self.assertDictEqual(expected_config, config)
+
+ self.assertListEqual([f'{psk_id_1}:{psk_secret_1}',
+ f'{psk_id_2}:{psk_secret_2}'],
+ [line for line in read_file(psk_secrets).split('\n')])
+
+ def test_06_socks_proxy(self):
+ server_port = '9001'
+ client_port = '9000'
+ srv_name = 'srv-one'
+ cli_name = 'cli-one'
+ expected_config = {
+ 'global': {'pid': '/run/stunnel/stunnel.pid',
+ 'debug': 'notice'},
+ 'services': {
+ 'cli-one': {
+ 'PSKsecrets': f'/run/stunnel/psk/client_{cli_name}.txt',
+ 'client': 'yes',
+ 'accept': client_port,
+ 'connect': server_port
+ },
+ 'srv-one': {
+ 'PSKsecrets': f'/run/stunnel/psk/server_{srv_name}.txt',
+ 'accept': server_port,
+ 'protocol': 'socks'
+ }
+ }}
+
+ self.cli_set(base_path + ['server', srv_name, 'listen', 'port', server_port])
+ self.cli_set(base_path + ['server', srv_name, 'connect', 'port', '9005'])
+ self.cli_set(base_path + ['server', srv_name, 'protocol', 'socks'])
+ self.cli_set(base_path + ['server', srv_name, 'psk', 'sock_proxy', 'id', cli_name])
+ self.cli_set(base_path + ['server', srv_name, 'psk', 'sock_proxy', 'secret', '1234567890ABCDEF1234567890ABCDEF'])
+
+ self.cli_set(base_path + ['client', cli_name, 'listen', 'port', client_port])
+ self.cli_set(base_path + ['client', cli_name, 'connect', 'port', server_port])
+ self.cli_set(base_path + ['client', cli_name, 'psk', 'sock_proxy', 'id', cli_name])
+ self.cli_set(base_path + ['client', cli_name, 'psk', 'sock_proxy', 'secret', '1234567890ABCDEF1234567890ABCDEF'])
+
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+
+ self.cli_delete(base_path + ['server', srv_name, 'connect'])
+ self.cli_commit()
+ config = read_config()
+
+ self.assertDictEqual(expected_config, config)
+
+ def test_07_available_port(self):
+ expected_config = {
+ 'global': {'pid': '/run/stunnel/stunnel.pid',
+ 'debug': 'notice'},
+ 'services': {
+ 'app1': {
+ 'client': 'yes',
+ 'accept': '8001',
+ 'connect': '9001'
+ },
+ 'srv1': {
+ 'PSKsecrets': f'/run/stunnel/psk/server_srv1.txt',
+ 'accept': '127.0.0.1:8002',
+ 'connect': '9001'
+ }
+ }}
+ self.cli_set(base_path + ['client', 'app1', 'connect', 'port', '9001'])
+ self.cli_set(base_path + ['client', 'app1', 'listen', 'port', '8001'])
+
+ self.cli_set(base_path + ['server', 'srv1', 'connect', 'port', '9001'])
+ self.cli_set(base_path + ['server', 'srv1', 'listen', 'address',
+ '127.0.0.1'])
+ self.cli_set(base_path + ['server', 'srv1', 'listen', 'port', '8001'])
+ self.cli_set(base_path + ['server', 'srv1', 'psk', 'smoketest1',
+ 'id', 'foo'])
+ self.cli_set(base_path + ['server', 'srv1', 'psk', 'smoketest1',
+ 'secret', '1234567890ABCDEF1234567890ABCDEF'])
+
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+
+ self.cli_set(base_path + ['server', 'srv1', 'listen', 'port', '8002'])
+ self.cli_commit()
+
+ config = read_config()
+ self.assertDictEqual(expected_config, config)
+
+ def test_08_two_endpoints(self):
+ expected_config = {
+ 'global': {'pid': '/run/stunnel/stunnel.pid',
+ 'debug': 'notice'},
+ 'services': {
+ 'app1': {
+ 'client': 'yes',
+ 'accept': '8001',
+ 'connect': '9001'
+ }
+ }}
+
+ self.cli_set(base_path + ['client', 'app1', 'listen', 'port', '8001'])
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+ self.cli_set(base_path + ['client', 'app1', 'connect', 'port', '9001'])
+ self.cli_commit()
+
+ config = read_config()
+ self.assertDictEqual(expected_config, config)
+
+ def test_09_pki_still_used(self):
+ service = 'ser1'
+ self.set_pki()
+ self.cli_set(base_path + ['server', service, 'connect', 'port', '8080'])
+ self.cli_set(base_path + ['server', service, 'listen', 'port', '9001'])
+ self.cli_set(base_path + ['server', service, 'ssl', 'certificate', 'srv-1'])
+ self.cli_commit()
+
+ self.cli_delete(['pki'])
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+
+ self.cli_delete(base_path)
+ self.cli_commit()
+
+ self.is_valid_conf = False
+
+ def test_99_protocols(self):
+ self.set_pki()
+ service = 'one'
+ proto_address = 'google.com'
+ proto_port = '80'
+ modes = ['client', 'server']
+ protocols = ['cifs', 'connect', 'imap', 'nntp', 'pgsql', 'pop3',
+ 'proxy', 'smtp', 'socks']
+ options = ['authentication', 'domain', 'host', 'password', 'username']
+
+ for protocol in protocols:
+ for mode in modes:
+ expected_config = {
+ 'global': {'pid': '/run/stunnel/stunnel.pid',
+ 'debug': 'notice'},
+ 'services': {'one': {
+ 'accept': '8001',
+ 'protocol': protocol,
+ }}}
+ if not(mode == 'server' and protocol == 'socks'):
+ self.cli_set(base_path + [mode, service, 'connect', 'port', '9001'])
+ expected_config['services']['one']['connect'] = '9001'
+ self.cli_set(base_path + [mode, service, 'listen', 'port', '8001'])
+
+ if mode == 'server':
+ expected_config['services'][service]['cert'] = '/run/stunnel/server-one-srv-1.pem'
+ expected_config['services'][service]['key'] = '/run/stunnel/server-one-srv-1.pem.key'
+ self.cli_set(base_path + [mode, service, 'ssl',
+ 'certificate', 'srv-1'])
+ else:
+ expected_config['services'][service]['client'] = 'yes'
+
+ # protocols connect and nntp is only supported in client mode.
+ if mode == 'server' and protocol in ['connect', 'nntp']:
+ with self.assertRaises(ConfigSessionError):
+ self.cli_set(base_path + [mode, service, 'protocol', protocol])
+ # self.cli_commit()
+ else:
+ self.cli_set(base_path + [mode, service, 'protocol', protocol])
+ self.cli_commit()
+ config = read_config()
+
+ self.assertDictEqual(expected_config, config)
+
+ expected_config['services'][service]['protocolDomain'] = 'valdomain'
+ expected_config['services'][service]['protocolPassword'] = 'valpassword'
+ expected_config['services'][service]['protocolUsername'] = 'valusername'
+
+ for option in options:
+ if option == 'authentication':
+ expected_config['services'][service]['protocolAuthentication'] = \
+ 'basic' if protocol == 'connect' else 'plain'
+ continue
+
+ if option == 'host' and mode != 'server':
+ expected_config['services'][service]['protocolHost'] = \
+ f'{proto_address}:{proto_port}'
+ self.cli_set(base_path + [mode, service, 'options',
+ option, 'address', f'{proto_address}'])
+ self.cli_set(base_path + [mode, service, 'options',
+ option, 'port', f'{proto_port}'])
+ continue
+ if mode == 'server':
+ with self.assertRaises(ConfigSessionError):
+ self.cli_set(
+ base_path + [mode, service, 'options', option, f'val{option}'])
+ else:
+ self.cli_set(
+ base_path + [mode, service, 'options', option, f'val{option}'])
+ # Additional option is only supported in the 'connect' and 'smtp' protocols.
+ if mode != 'server':
+ if protocol not in ['connect', 'smtp']:
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+ else:
+ if protocol == 'smtp':
+ # Protocol smtp does not support options domain and host
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+
+ self.cli_delete(
+ base_path + [mode, service, 'options', 'domain'])
+ self.cli_delete(
+ base_path + [mode, service, 'options', 'host'])
+ del expected_config['services'][service]['protocolDomain']
+ del expected_config['services'][service]['protocolHost']
+
+ self.cli_commit()
+ config = read_config()
+
+ self.assertDictEqual(expected_config, config)
+
+ self.cli_delete(base_path)
+ self.cli_commit()
+
+ self.is_valid_conf = False
+
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_system_login.py b/smoketest/scripts/cli/test_system_login.py
index 3f249660d..28abba012 100755
--- a/smoketest/scripts/cli/test_system_login.py
+++ b/smoketest/scripts/cli/test_system_login.py
@@ -24,6 +24,7 @@ from subprocess import Popen, PIPE
from pwd import getpwall
from vyos.configsession import ConfigSessionError
+from vyos.utils.auth import get_current_user
from vyos.utils.process import cmd
from vyos.utils.file import read_file
from vyos.template import inc_ip
@@ -334,5 +335,14 @@ class TestSystemLogin(VyOSUnitTestSHIM.TestCase):
self.assertIn(f'secret={tacacs_secret}', nss_tacacs_conf)
self.assertIn(f'server={server}', nss_tacacs_conf)
+ def test_delete_current_user(self):
+ current_user = get_current_user()
+
+ # We are not allowed to delete the current user
+ self.cli_delete(base_path + ['user', current_user])
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+ self.cli_discard()
+
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_vpn_openconnect.py b/smoketest/scripts/cli/test_vpn_openconnect.py
index a2e426dc7..dcce229e2 100755
--- a/smoketest/scripts/cli/test_vpn_openconnect.py
+++ b/smoketest/scripts/cli/test_vpn_openconnect.py
@@ -106,32 +106,32 @@ 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=
+MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCg7Mjl6+rs8Bdk
+jqgl2QDuHfrH2mTDCeB7WuNTnIz0BPDtlmwIdqhU7LdCB/zUSABAa6LBe/Z/bKWC
+RKyq8fU2/4uWECe975IMXOfFdYT6KA78DROvOi32JZmln0LAXV+538eb+g19xNto
+BhPO8igiNevfkV+nJehRK/41ATj+assTOv87vaSX7WqyaP/ZqkIdQD9Kc3cqB4Js
+YjkWcniHL9yk4oY3cjKK8PJ1pi4FqgFHt2hA+Ic+NvbAhc47K9otP8FM4jkSii3M
+ZfHA6Czb43BtbR+YEiWPzBhzE2bCuIgeRUumMF1Z+CAT6U7Cpx3XPh+Ac2RnDa8w
+KeQ1eqE1AgMBAAECggEAEDDaoqVqmMWsONoQiWRMr2h1RZvPxP7OpuKVWiF3XgrM
+Ob9HZc+Ybpj1dC+NDMekvNaHhMuF2Lqz6UgjDjzzVMH/x4yfDwFWUqebSxbglvGm
+Vk4zg48JNkmArLT6GJQccD1XXjZZmqSOhagM4KalCpIdxfvgoZbTCa2xMSCLHS+1
+HCDcmpCoeXM6ZBPTn0NbjRDAqIzCwcq2veG7RSz040obk8h7nrdv7jhxRGmtPmPF
+zKgGLNn6GnL7AwYVMiidjj/ntvM4B1OMs9MwUYbtpg98TWcWyu+ZRakUrnVf9z2a
+IHCKyuJvke/PNqMgw+L8KV4/478XxWhXfl7K1F3nMQKBgQDRBUDYNFH0wC4MMWsA
++RGwyz7RlzACChDJCMtA/agbW06gUoE9UYf8KtLQQQYljlLJHxHGD72QnuM+sowG
+GXnbD4BabA9TQiQUG5c6boznTy1uU1gt8T0Zl0mmC7vIMoMBVd5bb0qrZvuR123k
+DGYn6crug9uvMIYSSlhGmBGTJQKBgQDFGC3vfkCyXzLoYy+RIs/rXgyBF1PUYQty
+DgL0N811L0H7a8JhFnt4FvodUbxv2ob+1kIc9e3yXT6FsGyO7IDOnqgeQKy74bYq
+VPZZuf1FOFb9fuxf00pn1FmhAF4OuSWkhVhrKkyrZwdD8ArjLK253J94dogjdKAY
+fN1csaOA0QKBgD0zUZI8d4a3QoRVb+RACTr/t6v8nZTrR5DlX0XvP2qLKJFutuKy
+XaOrEkDh2R/j9T9oNncMos+WhikUdEVQ7koC1u0i2LXjFtdAYN4+Akmz+DRmeNoy
+2VYF4w2YP+pVR+B7OPkCtBVNuPkx3743Fy42mTGPMCKyjX8Lf59j5Tl1AoGBAI3s
+k2dZqozHMIlWovIH92CtIKP0gFD2cJ94p3fklvZDSWgaeKYg4lffc8uZB/AjlAH9
+ly3ziZx0uIjcOc/RTg96/+SI/dls9xgUhjCmVVJ692ki9GMsau/JYaEl+pTvjcOi
+ocDJfNwQHJM3Tx+3FII59DtyXyXo3T/E6kHNSMeBAoGAR9M48DTspv9OH1S7X6yR
+6MtMY5ltsBmB3gPhQFxiDKBvARkIkAPqObQ9TG/VuOz2Purq0Oz7SHsY2jiFDd2K
+EGo6JfG61NDdIhiQC99ztSgt7NtvSCnX22SfVDWoFxSK+tek7tvDVXAXCNy4ZESM
+EUGJ6NDHImb80aF+xZ3wYKw=
"""
PROCESS_NAME = 'ocserv-main'
diff --git a/src/conf_mode/pki.py b/src/conf_mode/pki.py
index 4a0e86f32..215b22b37 100755
--- a/src/conf_mode/pki.py
+++ b/src/conf_mode/pki.py
@@ -85,6 +85,10 @@ sync_search = [
{
'keys': ['certificate', 'ca_certificate'],
'path': ['vpn', 'sstp'],
+ },
+ {
+ 'keys': ['certificate', 'ca_certificate'],
+ 'path': ['service', 'stunnel'],
}
]
diff --git a/src/conf_mode/service_stunnel.py b/src/conf_mode/service_stunnel.py
new file mode 100644
index 000000000..8ec762548
--- /dev/null
+++ b/src/conf_mode/service_stunnel.py
@@ -0,0 +1,264 @@
+#!/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 os
+from shutil import rmtree
+
+from sys import exit
+
+from netifaces import AF_INET
+from psutil import net_if_addrs
+
+from vyos.config import Config
+from vyos.configverify import verify_pki_ca_certificate
+from vyos.configverify import verify_pki_certificate
+from vyos.pki import encode_certificate
+from vyos.pki import encode_private_key
+from vyos.pki import find_chain
+from vyos.pki import load_certificate
+from vyos.pki import load_private_key
+from vyos.utils.dict import dict_search
+from vyos.utils.file import makedir
+from vyos.utils.file import write_file
+from vyos.utils.network import check_port_availability
+from vyos.utils.network import is_listen_port_bind_service
+from vyos.utils.process import call
+from vyos.template import render
+from vyos import ConfigError
+from vyos import airbag
+airbag.enable()
+
+stunnel_dir = '/run/stunnel'
+config_file = f'{stunnel_dir}/stunnel.conf'
+stunnel_ca_dir = f'{stunnel_dir}/ca'
+stunnel_psk_dir = f'{stunnel_dir}/psk'
+
+# config based on
+# http://man.he.net/man8/stunnel4
+
+
+def get_config(config=None):
+ if config:
+ conf = config
+ else:
+ conf = Config()
+ base = ['service', 'stunnel']
+ if not conf.exists(base):
+ return None
+
+ stunnel = conf.get_config_dict(base,
+ get_first_key=True,
+ key_mangling=('-', '_'),
+ no_tag_node_value_mangle=True,
+ with_recursive_defaults=True,
+ with_pki=True)
+ stunnel['config_file'] = config_file
+ return stunnel
+
+
+def verify(stunnel):
+ if not stunnel:
+ return None
+
+ stunnel_listen_addresses = list()
+ for mode, conf in stunnel.items():
+ if mode not in ['server', 'client']:
+ continue
+
+ for app, app_conf in conf.items():
+ # connect, listen, exec and some protocols e.g. socks on server mode are endpoints.
+ endpoints = 0
+ if 'socks' == app_conf.get('protocol') and mode == 'server':
+ if 'connect' in app_conf:
+ raise ConfigError("The 'connect' option cannot be used with the 'socks' protocol in server mode.")
+ endpoints += 1
+
+ for item in ['connect', 'listen']:
+ if item in app_conf:
+ endpoints += 1
+ if 'port' not in app_conf[item]:
+ raise ConfigError(f'{mode} [{app}]: {item} port number is required!')
+ elif item == 'listen':
+ raise ConfigError(f'{mode} [{app}]: {item} port number is required!')
+
+ if endpoints != 2:
+ raise ConfigError(f'{mode} [{app}]: connect port number is required!')
+
+ if 'address' in app_conf['listen']:
+ laddresses = [dict_search('listen.address', app_conf)]
+ else:
+ laddresses = list()
+ ifaces = net_if_addrs()
+ for iface_name, iface_addresses in ifaces.items():
+ for iface_addr in iface_addresses:
+ if iface_addr.family == AF_INET:
+ laddresses.append(iface_addr.address)
+
+ lport = int(dict_search('listen.port', app_conf))
+
+ for address in laddresses:
+ if f'{address}:{lport}' in stunnel_listen_addresses:
+ raise ConfigError(
+ f'{mode} [{app}]: Address {address}:{lport} already '
+ f'in use by other stunnel service')
+
+ stunnel_listen_addresses.append(f'{address}:{lport}')
+ if not check_port_availability(address, lport, 'tcp') and \
+ not is_listen_port_bind_service(lport, 'stunnel'):
+ raise ConfigError(
+ f'{mode} [{app}]: Address {address}:{lport} already in use')
+
+ if 'options' in app_conf:
+ protocol = app_conf.get('protocol')
+ if protocol not in ['connect', 'smtp']:
+ raise ConfigError("Additional option is only supported in the 'connect' and 'smtp' protocols.")
+ if protocol == 'smtp' and ('domain' in app_conf['options'] or 'host' in app_conf['options']):
+ raise ConfigError("Protocol 'smtp' does not support options 'domain' and 'host'.")
+
+ # set default authentication option
+ if 'authentication' not in app_conf['options']:
+ app_conf['options']['authentication'] = 'basic' if protocol == 'connect' else 'plain'
+
+ for option, option_config in app_conf['options'].items():
+ if option == 'authentication':
+ if protocol == 'connect' and option_config not in ['basic', 'ntlm']:
+ raise ConfigError("Supported authentication types for the 'connect' protocol are 'basic' or 'ntlm'")
+ elif protocol == 'smtp' and option_config not in ['plain', 'login']:
+ raise ConfigError("Supported authentication types for the 'smtp' protocol are 'plain' or 'login'")
+ if option == 'host':
+ if 'address' not in option_config:
+ raise ConfigError('Address is required for option host.')
+ if 'port' not in option_config:
+ raise ConfigError('Port is required for option host.')
+
+ # check pki certs
+ for key in ['ca_certificate', 'certificate']:
+ tmp = dict_search(f'ssl.{key}', app_conf)
+ if mode == 'server' and key != 'ca_certificate' and not tmp and 'psk' not in app_conf:
+ raise ConfigError(f'{mode} [{app}]: TLS server needs a certificate or PSK')
+ if tmp:
+ if key == 'ca_certificate':
+ for ca_cert in tmp:
+ verify_pki_ca_certificate(stunnel, ca_cert)
+ else:
+ verify_pki_certificate(stunnel, tmp)
+
+ #check psk
+ if 'psk' in app_conf:
+ for psk, psk_conf in app_conf['psk'].items():
+ if 'id' not in psk_conf or 'secret' not in psk_conf:
+ raise ConfigError(
+ f'Authentication psk "{psk}" missing "id" or "secret"')
+
+
+def generate(stunnel):
+ if not stunnel or ('client' not in stunnel and 'server' not in stunnel):
+ if os.path.isdir(stunnel_dir):
+ rmtree(stunnel_dir, ignore_errors=True)
+
+ return None
+ makedir(stunnel_dir)
+
+ exist_files = list()
+ current_files = [config_file, config_file.replace('.conf', 'pid')]
+ for root, dirs, files in os.walk(stunnel_dir):
+ for file in files:
+ exist_files.append(os.path.join(root, file))
+
+ loaded_ca_certs = {load_certificate(c['certificate'])
+ for c in stunnel['pki']['ca'].values()} if 'pki' in stunnel and 'ca' in stunnel['pki'] else {}
+
+ for mode, conf in stunnel.items():
+ if mode not in ['server', 'client']:
+ continue
+
+ for app, app_conf in conf.items():
+ if 'ssl' in app_conf:
+ if 'certificate' in app_conf['ssl']:
+ cert_name = app_conf['ssl']['certificate']
+
+ pki_cert = stunnel['pki']['certificate'][cert_name]
+ cert_file_path = os.path.join(stunnel_dir,
+ f'{mode}-{app}-{cert_name}.pem')
+ cert_key_path = os.path.join(stunnel_dir,
+ f'{mode}-{app}-{cert_name}.pem.key')
+ app_conf['ssl']['cert'] = cert_file_path
+
+ loaded_pki_cert = load_certificate(pki_cert['certificate'])
+ cert_full_chain = find_chain(loaded_pki_cert, loaded_ca_certs)
+
+ write_file(cert_file_path,
+ '\n'.join(encode_certificate(c) for c in cert_full_chain))
+ current_files.append(cert_file_path)
+
+ if 'private' in pki_cert and 'key' in pki_cert['private']:
+ app_conf['ssl']['cert_key'] = cert_key_path
+ loaded_key = load_private_key(pki_cert['private']['key'],
+ passphrase=None, wrap_tags=True)
+ key_pem = encode_private_key(loaded_key, passphrase=None)
+ write_file(cert_key_path, key_pem, mode=0o600)
+ current_files.append(cert_key_path)
+
+ if 'ca_certificate' in app_conf['ssl']:
+ app_conf['ssl']['ca_path'] = stunnel_ca_dir
+ app_conf['ssl']['ca_file'] = f'{mode}-{app}-ca.pem'
+ ca_cert_file_path = os.path.join(stunnel_ca_dir, app_conf['ssl']['ca_file'])
+ ca_chains = []
+
+ for ca_name in app_conf['ssl']['ca_certificate']:
+ pki_ca_cert = stunnel['pki']['ca'][ca_name]
+ loaded_ca_cert = load_certificate(pki_ca_cert['certificate'])
+ ca_full_chain = find_chain(loaded_ca_cert, loaded_ca_certs)
+ ca_chains.append(
+ '\n'.join(encode_certificate(c) for c in ca_full_chain))
+
+ write_file(ca_cert_file_path, '\n'.join(ca_chains))
+ current_files.append(ca_cert_file_path)
+
+ if 'psk' in app_conf:
+ psk_data = list()
+ psk_file_path = os.path.join(stunnel_psk_dir, f'{mode}_{app}.txt')
+
+ for _, psk_conf in app_conf['psk'].items():
+ psk_data.append(f'{psk_conf["id"]}:{psk_conf["secret"]}')
+
+ write_file(psk_file_path, '\n'.join(psk_data))
+ app_conf['psk']['file'] = psk_file_path
+ current_files.append(psk_file_path)
+
+ for file in exist_files:
+ if file not in current_files:
+ os.unlink(file)
+
+ render(config_file, 'stunnel/stunnel_config.j2', stunnel)
+
+
+def apply(stunnel):
+ if not stunnel or ('client' not in stunnel and 'server' not in stunnel):
+ call('systemctl stop stunnel.service')
+ else:
+ call('systemctl restart stunnel.service')
+
+
+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/helpers/system-versions-foot.py b/src/helpers/add-system-version.py
index 9614f0d28..5270ee7d3 100755
--- a/src/helpers/system-versions-foot.py
+++ b/src/helpers/add-system-version.py
@@ -1,6 +1,6 @@
#!/usr/bin/python3
-# 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
@@ -15,14 +15,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 vyos.defaults
-from vyos.component_version import write_system_footer
+from vyos.component_version import add_system_version
-sys.stdout.write("\n\n")
-if vyos.defaults.cfg_vintage == 'vyos':
- write_system_footer(None, vintage='vyos')
-elif vyos.defaults.cfg_vintage == 'vyatta':
- write_system_footer(None, vintage='vyatta')
-else:
- write_system_footer(None, vintage='vyos')
+add_system_version()
diff --git a/src/helpers/run-config-migration.py b/src/helpers/run-config-migration.py
index ce647ad0a..e6ce97363 100755
--- a/src/helpers/run-config-migration.py
+++ b/src/helpers/run-config-migration.py
@@ -1,86 +1,78 @@
-#!/usr/bin/python3
-
-# Copyright 2019 VyOS maintainers and contributors <maintainers@vyos.io>
+#!/usr/bin/env python3
+#
+# Copyright (C) 2019-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; either
-# version 2.1 of the License, or (at your option) any later version.
+# 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 library is distributed in the hope that it will be useful,
+# 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
-# Lesser General Public License for more details.
+# 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 Lesser General Public License
-# along with this library. If not, see <http://www.gnu.org/licenses/>.
+# 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
-import datetime
-
-from vyos.utils.process import cmd
-from vyos.migrator import Migrator, VirtualMigrator
-
-def main():
- argparser = argparse.ArgumentParser(
- formatter_class=argparse.RawTextHelpFormatter)
- argparser.add_argument('config_file', type=str,
- help="configuration file to migrate")
- argparser.add_argument('--force', action='store_true',
- help="Force calling of all migration scripts.")
- argparser.add_argument('--set-vintage', type=str,
- choices=['vyatta', 'vyos'],
- help="Set the format for the config version footer in config"
- " file:\n"
- "set to 'vyatta':\n"
- "(for '/* === vyatta-config-version ... */' format)\n"
- "or 'vyos':\n"
- "(for '// vyos-config-version ...' format).")
- argparser.add_argument('--virtual', action='store_true',
- help="Update the format of the trailing comments in"
- " config file,\nfrom 'vyatta' to 'vyos'; no migration"
- " scripts are run.")
- args = argparser.parse_args()
+import time
+from argparse import ArgumentParser
+from shutil import copyfile
- config_file_name = args.config_file
- force_on = args.force
- vintage = args.set_vintage
- virtual = args.virtual
+from vyos.migrate import ConfigMigrate
+from vyos.migrate import ConfigMigrateError
- if not os.access(config_file_name, os.R_OK):
- print("Read error: {}.".format(config_file_name))
- sys.exit(1)
+parser = ArgumentParser()
+parser.add_argument('config_file', type=str,
+ help="configuration file to migrate")
+parser.add_argument('--test-script', type=str,
+ help="test named script")
+parser.add_argument('--output-file', type=str,
+ help="write to named output file instead of config file")
+parser.add_argument('--force', action='store_true',
+ help="force run of all migration scripts")
- if not os.access(config_file_name, os.W_OK):
- print("Write error: {}.".format(config_file_name))
- sys.exit(1)
+args = parser.parse_args()
- separator = "."
- backup_file_name = separator.join([config_file_name,
- '{0:%Y-%m-%d-%H%M%S}'.format(datetime.datetime.now()),
- 'pre-migration'])
+config_file = args.config_file
+out_file = args.output_file
+test_script = args.test_script
+force = args.force
- cmd(f'cp -p {config_file_name} {backup_file_name}')
+if not os.access(config_file, os.R_OK):
+ print(f"Config file '{config_file}' not readable")
+ sys.exit(1)
- if not virtual:
- virtual_migration = VirtualMigrator(config_file_name)
- virtual_migration.run()
+if out_file is None:
+ if not os.access(config_file, os.W_OK):
+ print(f"Config file '{config_file}' not writeable")
+ sys.exit(1)
+else:
+ try:
+ open(out_file, 'w').close()
+ except OSError:
+ print(f"Output file '{out_file}' not writeable")
+ sys.exit(1)
- migration = Migrator(config_file_name, force=force_on)
- migration.run()
+config_migrate = ConfigMigrate(config_file, force=force, output_file=out_file)
- if not migration.config_changed():
- os.remove(backup_file_name)
- else:
- virtual_migration = VirtualMigrator(config_file_name,
- set_vintage=vintage)
+if test_script:
+ # run_script and exit
+ config_migrate.run_script(test_script)
+ sys.exit(0)
- virtual_migration.run()
+backup = None
+if out_file is None:
+ timestr = time.strftime("%Y%m%d-%H%M%S")
+ backup = f'{config_file}.{timestr}.pre-migration'
+ copyfile(config_file, backup)
- if not virtual_migration.config_changed():
- os.remove(backup_file_name)
+try:
+ config_migrate.run()
+except ConfigMigrateError as e:
+ print(f'Error: {e}')
+ sys.exit(1)
-if __name__ == '__main__':
- main()
+if backup is not None and not config_migrate.config_modified:
+ os.unlink(backup)
diff --git a/src/helpers/vyos-load-config.py b/src/helpers/vyos-load-config.py
index 4ec865454..16083fd41 100755
--- a/src/helpers/vyos-load-config.py
+++ b/src/helpers/vyos-load-config.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
@@ -30,7 +30,8 @@ import tempfile
import vyos.defaults
import vyos.remote
from vyos.configsource import ConfigSourceSession, VyOSError
-from vyos.migrator import Migrator, VirtualMigrator, MigratorError
+from vyos.migrate import ConfigMigrate
+from vyos.migrate import ConfigMigrateError
class LoadConfig(ConfigSourceSession):
"""A subclass for calling 'loadFile'.
@@ -81,22 +82,16 @@ with tempfile.NamedTemporaryFile() as fp:
with open(fp.name, 'w') as fd:
fd.write(config_string)
- virtual_migration = VirtualMigrator(fp.name)
+ config_migrate = ConfigMigrate(fp.name)
try:
- virtual_migration.run()
- except MigratorError as err:
- sys.exit('{}'.format(err))
-
- migration = Migrator(fp.name)
- try:
- migration.run()
- except MigratorError as err:
- sys.exit('{}'.format(err))
+ config_migrate.run()
+ except ConfigMigrateError as err:
+ sys.exit(err)
try:
config.load_config(fp.name)
except VyOSError as err:
- sys.exit('{}'.format(err))
+ sys.exit(err)
if config.session_changed():
print("Load complete. Use 'commit' to make changes effective.")
diff --git a/src/helpers/vyos-merge-config.py b/src/helpers/vyos-merge-config.py
index 35424626e..5ef845ac2 100755
--- a/src/helpers/vyos-merge-config.py
+++ b/src/helpers/vyos-merge-config.py
@@ -22,7 +22,8 @@ import vyos.remote
from vyos.config import Config
from vyos.configtree import ConfigTree
-from vyos.migrator import Migrator, VirtualMigrator
+from vyos.migrate import ConfigMigrate
+from vyos.migrate import ConfigMigrateError
from vyos.utils.process import cmd
from vyos.utils.process import DEVNULL
@@ -61,15 +62,11 @@ with tempfile.NamedTemporaryFile() as file_to_migrate:
with open(file_to_migrate.name, 'w') as fd:
fd.write(config_file)
- virtual_migration = VirtualMigrator(file_to_migrate.name)
- virtual_migration.run()
-
- migration = Migrator(file_to_migrate.name)
- migration.run()
-
- if virtual_migration.config_changed() or migration.config_changed():
- with open(file_to_migrate.name, 'r') as fd:
- config_file = fd.read()
+ config_migrate = ConfigMigrate(file_to_migrate.name)
+ try:
+ config_migrate.run()
+ except ConfigMigrateError as e:
+ sys.exit(e)
merge_config_tree = ConfigTree(config_file)
diff --git a/src/helpers/vyos-save-config.py b/src/helpers/vyos-save-config.py
index 518bd9864..fa2ea0ce4 100755
--- a/src/helpers/vyos-save-config.py
+++ b/src/helpers/vyos-save-config.py
@@ -23,7 +23,7 @@ from argparse import ArgumentParser
from vyos.config import Config
from vyos.remote import urlc
-from vyos.component_version import system_footer
+from vyos.component_version import add_system_version
from vyos.defaults import directories
DEFAULT_CONFIG_PATH = os.path.join(directories['config'], 'config.boot')
@@ -50,14 +50,13 @@ if re.match(r'\w+:/', save_file):
config = Config()
ct = config.get_config_tree(effective=True)
+# pylint: disable=consider-using-with
write_file = save_file if remote_save is None else NamedTemporaryFile(delete=False).name
-with open(write_file, 'w') as f:
- # config_tree is None before boot configuration is complete;
- # automated saves should check boot_configuration_complete
- if ct is not None:
- f.write(ct.to_string())
- f.write("\n")
- f.write(system_footer())
+
+# config_tree is None before boot configuration is complete;
+# automated saves should check boot_configuration_complete
+config_str = None if ct is None else ct.to_string()
+add_system_version(config_str, write_file)
if json_file is not None and ct is not None:
try:
diff --git a/src/helpers/vyos_net_name b/src/helpers/vyos_net_name
index 8c0992414..518e204f9 100755
--- a/src/helpers/vyos_net_name
+++ b/src/helpers/vyos_net_name
@@ -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
@@ -26,7 +26,7 @@ from vyos.configtree import ConfigTree
from vyos.defaults import directories
from vyos.utils.process import cmd
from vyos.utils.boot import boot_configuration_complete
-from vyos.migrator import VirtualMigrator
+from vyos.migrate import ConfigMigrate
vyos_udev_dir = directories['vyos_udev_dir']
vyos_log_dir = '/run/udev/log'
@@ -147,8 +147,10 @@ def get_configfile_interfaces() -> dict:
with tempfile.NamedTemporaryFile() as fp:
with open(fp.name, 'w') as fd:
fd.write(config_file)
- virtual_migration = VirtualMigrator(fp.name)
- virtual_migration.run()
+ config_migrate = ConfigMigrate(fp.name)
+ if config_migrate.syntax_update_needed():
+ config_migrate.update_syntax()
+ config_migrate.write_config()
with open(fp.name) as fd:
config_file = fd.read()
diff --git a/src/init/vyos-router b/src/init/vyos-router
index 59004fdc1..8825cc16a 100755
--- a/src/init/vyos-router
+++ b/src/init/vyos-router
@@ -134,14 +134,14 @@ init_bootfile () {
if [ ! -r $DEFAULT_BOOTFILE ]; then
if [ -f $vyos_data_dir/config.boot.default ]; then
cp $vyos_data_dir/config.boot.default $DEFAULT_BOOTFILE
- $vyos_libexec_dir/system-versions-foot.py >> $DEFAULT_BOOTFILE
+ $vyos_libexec_dir/add-system-version.py >> $DEFAULT_BOOTFILE
fi
fi
if [ ! -r $BOOTFILE ] ; then
if [ -f $DEFAULT_BOOTFILE ]; then
cp $DEFAULT_BOOTFILE $BOOTFILE
else
- $vyos_libexec_dir/system-versions-foot.py > $BOOTFILE
+ $vyos_libexec_dir/add-system-version.py > $BOOTFILE
fi
chgrp ${GROUP} $BOOTFILE
chmod 660 $BOOTFILE
diff --git a/src/migration-scripts/bgp/0-to-1 b/src/migration-scripts/bgp/0-to-1
index 5b8e8a163..a2f3343d8 100755..100644
--- a/src/migration-scripts/bgp/0-to-1
+++ b/src/migration-scripts/bgp/0-to-1
@@ -1,60 +1,40 @@
-#!/usr/bin/env python3
+# Copyright 2021-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2021 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 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# T3417: migrate BGP tagNode to node as we can only have one BGP process
-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 = ['protocols', 'bgp']
-config = ConfigTree(config_file)
-
-if not config.exists(base) or not config.is_tag(base):
- # Nothing to do
- exit(0)
+def migrate(config: ConfigTree) -> None:
+ base = ['protocols', 'bgp']
-# Only one BGP process is supported, thus this operation is savea
-asn = config.list_nodes(base)
-bgp_base = base + asn
+ if not config.exists(base) or not config.is_tag(base):
+ # Nothing to do
+ return
-# We need a temporary copy of the config
-tmp_base = ['protocols', 'bgp2']
-config.copy(bgp_base, tmp_base)
+ # Only one BGP process is supported, thus this operation is savea
+ asn = config.list_nodes(base)
+ bgp_base = base + asn
-# Now it's save to delete the old configuration
-config.delete(base)
+ # We need a temporary copy of the config
+ tmp_base = ['protocols', 'bgp2']
+ config.copy(bgp_base, tmp_base)
-# Rename temporary copy to new final config and set new "local-as" option
-config.rename(tmp_base, 'bgp')
-config.set(base + ['local-as'], value=asn[0])
+ # Now it's save to delete the old configuration
+ config.delete(base)
-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)
+ # Rename temporary copy to new final config and set new "local-as" option
+ config.rename(tmp_base, 'bgp')
+ config.set(base + ['local-as'], value=asn[0])
diff --git a/src/migration-scripts/bgp/1-to-2 b/src/migration-scripts/bgp/1-to-2
index a40d86e67..c0fc3b05a 100755..100644
--- a/src/migration-scripts/bgp/1-to-2
+++ b/src/migration-scripts/bgp/1-to-2
@@ -1,84 +1,64 @@
-#!/usr/bin/env python3
+# Copyright 2021-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2021-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; either
+# version 2.1 of the License, or (at your option) any later version.
#
-# 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# T3741: no-ipv4-unicast is now enabled by default
# T5937: Migrate IPv6 BGP Neighbor Peer Groups
-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]
+base = ['protocols', 'bgp']
-with open(file_name, 'r') as f:
- config_file = f.read()
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ # Nothing to do
+ return
-base = ['protocols', 'bgp']
-config = ConfigTree(config_file)
+ # This is now a default option - simply delete it.
+ # As it was configured explicitly - we can also bail out early as we need to
+ # do nothing!
+ if config.exists(base + ['parameters', 'default', 'no-ipv4-unicast']):
+ config.delete(base + ['parameters', 'default', 'no-ipv4-unicast'])
-if not config.exists(base):
- # Nothing to do
- exit(0)
+ # Check if the "default" node is now empty, if so - remove it
+ if len(config.list_nodes(base + ['parameters', 'default'])) == 0:
+ config.delete(base + ['parameters', 'default'])
-# This is now a default option - simply delete it.
-# As it was configured explicitly - we can also bail out early as we need to
-# do nothing!
-if config.exists(base + ['parameters', 'default', 'no-ipv4-unicast']):
- config.delete(base + ['parameters', 'default', 'no-ipv4-unicast'])
+ # Check if the "default" node is now empty, if so - remove it
+ if len(config.list_nodes(base + ['parameters'])) == 0:
+ config.delete(base + ['parameters'])
+ else:
+ # As we now install a new default option into BGP we need to migrate all
+ # existing BGP neighbors and restore the old behavior
+ if config.exists(base + ['neighbor']):
+ for neighbor in config.list_nodes(base + ['neighbor']):
+ peer_group = base + ['neighbor', neighbor, 'peer-group']
+ if config.exists(peer_group):
+ peer_group_name = config.return_value(peer_group)
+ # peer group enables old behavior for neighbor - bail out
+ if config.exists(base + ['peer-group', peer_group_name, 'address-family', 'ipv4-unicast']):
+ continue
- # Check if the "default" node is now empty, if so - remove it
- if len(config.list_nodes(base + ['parameters', 'default'])) == 0:
- config.delete(base + ['parameters', 'default'])
+ afi_ipv4 = base + ['neighbor', neighbor, 'address-family', 'ipv4-unicast']
+ if not config.exists(afi_ipv4):
+ config.set(afi_ipv4)
- # Check if the "default" node is now empty, if so - remove it
- if len(config.list_nodes(base + ['parameters'])) == 0:
- config.delete(base + ['parameters'])
-else:
- # As we now install a new default option into BGP we need to migrate all
- # existing BGP neighbors and restore the old behavior
+ # Migrate IPv6 AFI peer-group
if config.exists(base + ['neighbor']):
for neighbor in config.list_nodes(base + ['neighbor']):
- peer_group = base + ['neighbor', neighbor, 'peer-group']
- if config.exists(peer_group):
- peer_group_name = config.return_value(peer_group)
- # peer group enables old behavior for neighbor - bail out
- if config.exists(base + ['peer-group', peer_group_name, 'address-family', 'ipv4-unicast']):
- continue
-
- afi_ipv4 = base + ['neighbor', neighbor, 'address-family', 'ipv4-unicast']
- if not config.exists(afi_ipv4):
- config.set(afi_ipv4)
-
-# Migrate IPv6 AFI peer-group
-if config.exists(base + ['neighbor']):
- for neighbor in config.list_nodes(base + ['neighbor']):
- tmp_path = base + ['neighbor', neighbor, 'address-family', 'ipv6-unicast', 'peer-group']
- if config.exists(tmp_path):
- peer_group = config.return_value(tmp_path)
- config.set(base + ['neighbor', neighbor, 'peer-group'], value=peer_group)
- config.delete(tmp_path)
-
-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)
+ tmp_path = base + ['neighbor', neighbor, 'address-family', 'ipv6-unicast', 'peer-group']
+ if config.exists(tmp_path):
+ peer_group = config.return_value(tmp_path)
+ config.set(base + ['neighbor', neighbor, 'peer-group'], value=peer_group)
+ config.delete(tmp_path)
diff --git a/src/migration-scripts/bgp/2-to-3 b/src/migration-scripts/bgp/2-to-3
index 34d321a96..d8bc34db6 100755..100644
--- a/src/migration-scripts/bgp/2-to-3
+++ b/src/migration-scripts/bgp/2-to-3
@@ -1,51 +1,30 @@
-#!/usr/bin/env python3
+# Copyright 2022-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2022 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 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# T4257: Discussion on changing BGP autonomous system number syntax
-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)
-
-# Check if BGP is even configured. Then check if local-as exists, then add the system-as, then remove the local-as. This is for global configuration.
-if config.exists(['protocols', 'bgp']):
- if config.exists(['protocols', 'bgp', 'local-as']):
- config.rename(['protocols', 'bgp', 'local-as'], 'system-as')
-
-# Check if vrf names are configured. Then check if local-as exists inside of a name, then add the system-as, then remove the local-as. This is for vrf configuration.
-if config.exists(['vrf', 'name']):
- for vrf in config.list_nodes(['vrf', 'name']):
- if config.exists(['vrf', f'name {vrf}', 'protocols', 'bgp', 'local-as']):
- config.rename(['vrf', f'name {vrf}', 'protocols', 'bgp', 'local-as'], 'system-as')
-
-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)
+def migrate(config: ConfigTree) -> None:
+ # Check if BGP is even configured. Then check if local-as exists, then add the system-as, then remove the local-as. This is for global configuration.
+ if config.exists(['protocols', 'bgp']):
+ if config.exists(['protocols', 'bgp', 'local-as']):
+ config.rename(['protocols', 'bgp', 'local-as'], 'system-as')
+
+ # Check if vrf names are configured. Then check if local-as exists inside of a name, then add the system-as, then remove the local-as. This is for vrf configuration.
+ if config.exists(['vrf', 'name']):
+ for vrf in config.list_nodes(['vrf', 'name']):
+ if config.exists(['vrf', f'name {vrf}', 'protocols', 'bgp', 'local-as']):
+ config.rename(['vrf', f'name {vrf}', 'protocols', 'bgp', 'local-as'], 'system-as')
diff --git a/src/migration-scripts/bgp/3-to-4 b/src/migration-scripts/bgp/3-to-4
index 894cdda2b..842aef0ce 100755..100644
--- a/src/migration-scripts/bgp/3-to-4
+++ b/src/migration-scripts/bgp/3-to-4
@@ -1,64 +1,43 @@
-#!/usr/bin/env python3
+# Copyright 2023-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2023 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 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# T5150: Rework CLI definitions to apply route-maps between routing daemons
# and zebra/kernel
-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)
-
-bgp_base = ['protocols', 'bgp']
-# Check if BGP is configured - if so, migrate the CLI node
-if config.exists(bgp_base):
- if config.exists(bgp_base + ['route-map']):
- tmp = config.return_value(bgp_base + ['route-map'])
-
- config.set(['system', 'ip', 'protocol', 'bgp', 'route-map'], value=tmp)
- config.set_tag(['system', 'ip', 'protocol'])
- config.delete(bgp_base + ['route-map'])
-
-
-# Check if vrf names are configured. Check if BGP is configured - if so, migrate
-# the CLI node(s)
-if config.exists(['vrf', 'name']):
- for vrf in config.list_nodes(['vrf', 'name']):
- vrf_base = ['vrf', 'name', vrf]
- if config.exists(vrf_base + ['protocols', 'bgp', 'route-map']):
- tmp = config.return_value(vrf_base + ['protocols', 'bgp', 'route-map'])
-
- config.set(vrf_base + ['ip', 'protocol', 'bgp', 'route-map'], value=tmp)
- config.set_tag(vrf_base + ['ip', 'protocol', 'bgp'])
- config.delete(vrf_base + ['protocols', 'bgp', 'route-map'])
-
-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)
+def migrate(config: ConfigTree) -> None:
+ bgp_base = ['protocols', 'bgp']
+ # Check if BGP is configured - if so, migrate the CLI node
+ if config.exists(bgp_base):
+ if config.exists(bgp_base + ['route-map']):
+ tmp = config.return_value(bgp_base + ['route-map'])
+
+ config.set(['system', 'ip', 'protocol', 'bgp', 'route-map'], value=tmp)
+ config.set_tag(['system', 'ip', 'protocol'])
+ config.delete(bgp_base + ['route-map'])
+
+
+ # Check if vrf names are configured. Check if BGP is configured - if so, migrate
+ # the CLI node(s)
+ if config.exists(['vrf', 'name']):
+ for vrf in config.list_nodes(['vrf', 'name']):
+ vrf_base = ['vrf', 'name', vrf]
+ if config.exists(vrf_base + ['protocols', 'bgp', 'route-map']):
+ tmp = config.return_value(vrf_base + ['protocols', 'bgp', 'route-map'])
+
+ config.set(vrf_base + ['ip', 'protocol', 'bgp', 'route-map'], value=tmp)
+ config.set_tag(vrf_base + ['ip', 'protocol', 'bgp'])
+ config.delete(vrf_base + ['protocols', 'bgp', 'route-map'])
diff --git a/src/migration-scripts/bgp/4-to-5 b/src/migration-scripts/bgp/4-to-5
index c4eb9ec72..d779eb11e 100755..100644
--- a/src/migration-scripts/bgp/4-to-5
+++ b/src/migration-scripts/bgp/4-to-5
@@ -1,67 +1,46 @@
-#!/usr/bin/env python3
+# Copyright 2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 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; either
+# version 2.1 of the License, or (at your option) any later version.
#
-# 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# Delete 'protocols bgp address-family ipv6-unicast route-target vpn
# import/export', if 'protocols bgp address-family ipv6-unicast
# route-target vpn both' exists
-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)
-
-bgp_base = ['protocols', 'bgp']
-# Delete 'import/export' in default vrf if 'both' exists
-if config.exists(bgp_base):
- for address_family in ['ipv4-unicast', 'ipv6-unicast']:
- rt_path = bgp_base + ['address-family', address_family, 'route-target',
- 'vpn']
- if config.exists(rt_path + ['both']):
- if config.exists(rt_path + ['import']):
- config.delete(rt_path + ['import'])
- if config.exists(rt_path + ['export']):
- config.delete(rt_path + ['export'])
-
-# Delete import/export in vrfs if both exists
-if config.exists(['vrf', 'name']):
- for vrf in config.list_nodes(['vrf', 'name']):
- vrf_base = ['vrf', 'name', vrf]
+def migrate(config: ConfigTree) -> None:
+ bgp_base = ['protocols', 'bgp']
+ # Delete 'import/export' in default vrf if 'both' exists
+ if config.exists(bgp_base):
for address_family in ['ipv4-unicast', 'ipv6-unicast']:
- rt_path = vrf_base + bgp_base + ['address-family', address_family,
- 'route-target', 'vpn']
+ rt_path = bgp_base + ['address-family', address_family, 'route-target',
+ 'vpn']
if config.exists(rt_path + ['both']):
if config.exists(rt_path + ['import']):
config.delete(rt_path + ['import'])
if config.exists(rt_path + ['export']):
config.delete(rt_path + ['export'])
-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)
+ # Delete import/export in vrfs if both exists
+ if config.exists(['vrf', 'name']):
+ for vrf in config.list_nodes(['vrf', 'name']):
+ vrf_base = ['vrf', 'name', vrf]
+ for address_family in ['ipv4-unicast', 'ipv6-unicast']:
+ rt_path = vrf_base + bgp_base + ['address-family', address_family,
+ 'route-target', 'vpn']
+ if config.exists(rt_path + ['both']):
+ if config.exists(rt_path + ['import']):
+ config.delete(rt_path + ['import'])
+ if config.exists(rt_path + ['export']):
+ config.delete(rt_path + ['export'])
diff --git a/src/migration-scripts/cluster/1-to-2 b/src/migration-scripts/cluster/1-to-2
index a2e589155..5ca4531ea 100755..100644
--- a/src/migration-scripts/cluster/1-to-2
+++ b/src/migration-scripts/cluster/1-to-2
@@ -1,40 +1,28 @@
-#!/usr/bin/env python3
+# Copyright 2023-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2023 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 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,
+# 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 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/>.
+# 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, see <http://www.gnu.org/licenses/>.
import re
import sys
from vyos.configtree import ConfigTree
+from vyos.base import MigrationError
-if __name__ == '__main__':
- if len(sys.argv) < 2:
- print("Must specify file name!")
- sys.exit(1)
-
- file_name = sys.argv[1]
-
- with open(file_name, 'r') as f:
- config_file = f.read()
-
- config = ConfigTree(config_file)
-
+def migrate(config: ConfigTree) -> None:
if not config.exists(['cluster']):
# Cluster is not set -- nothing to do at all
- sys.exit(0)
+ return
# If at least one cluster group is defined, we have real work to do.
# If there are no groups, we remove the top-level cluster node at the end of this script anyway.
@@ -102,8 +90,9 @@ if __name__ == '__main__':
services["other"].append(s)
if services["other"]:
- print("Cluster config includes non-IP address services and cannot be migrated", file=sys.stderr)
- sys.exit(1)
+ err_str = "Cluster config includes non-IP address services and cannot be migrated"
+ print(err_str, file=sys.stderr)
+ raise MigrationError(err_str)
# Cluster allowed virtual IPs for different interfaces within a single group.
# VRRP groups are by definition bound to interfaces, so we cannot migrate such configurations.
@@ -123,14 +112,16 @@ if __name__ == '__main__':
if global_interface is not None:
ips.append({"ip": ip, "interface": global_interface})
else:
- print("Error: cluster has groups with IPs without interfaces and 'cluster interface' is not specified.", file=sys.stderr)
- sys.exit(1)
+ err_str = "Cluster group has addresses without interfaces and 'cluster interface' is not specified."
+ print(f'Error: {err_str}', file=sys.stderr)
+ raise MigrationError(err_str)
# Then we check if all addresses are for the same interface.
intfs_set = set(map(lambda i: i["interface"], ips))
if len(intfs_set) > 1:
- print("Error: cluster group has addresses for different interfaces", file=sys.stderr)
- sys.exit(1)
+ err_str = "Cluster group has addresses for different interfaces"
+ print(f'Error: {err_str}', file=sys.stderr)
+ raise MigrationError(err_str)
# If we got this far, the group is migratable.
@@ -143,8 +134,9 @@ if __name__ == '__main__':
# If there's already a VRRP group with exactly the same name,
# we probably shouldn't try to make up a unique name, just leave migration to the user...
if config.exists(vrrp_path):
- print("Error: VRRP group with the same name as a cluster group already exists", file=sys.stderr)
- sys.exit(1)
+ err_str = "VRRP group with the same name already exists"
+ print(f'Error: {err_str}', file=sys.stderr)
+ raise MigrationError(err_str)
config.set(vrrp_path + ['interface'], value=interface)
for a in addresses:
@@ -184,10 +176,3 @@ if __name__ == '__main__':
# Finally, clean up the old cluster node
config.delete(['cluster'])
-
- 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))
- sys.exit(1)
diff --git a/src/migration-scripts/config-management/0-to-1 b/src/migration-scripts/config-management/0-to-1
index 6528fd136..44c685630 100755..100644
--- a/src/migration-scripts/config-management/0-to-1
+++ b/src/migration-scripts/config-management/0-to-1
@@ -1,31 +1,24 @@
-#!/usr/bin/env python3
+# Copyright 2018-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
+# 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, see <http://www.gnu.org/licenses/>.
# Add commit-revisions option if it doesn't exist
-import sys
-
from vyos.configtree import ConfigTree
-if len(sys.argv) < 2:
- print("Must specify file name!")
- sys.exit(1)
-
-file_name = sys.argv[1]
-
-with open(file_name, 'r') as f:
- config_file = f.read()
-
-config = ConfigTree(config_file)
-
-if config.exists(['system', 'config-management', 'commit-revisions']):
- # Nothing to do
- sys.exit(0)
-else:
+def migrate(config: ConfigTree) -> None:
+ if config.exists(['system', 'config-management', 'commit-revisions']):
+ # Nothing to do
+ return
config.set(['system', 'config-management', 'commit-revisions'], value='200')
-
- 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))
- sys.exit(1)
diff --git a/src/migration-scripts/conntrack-sync/1-to-2 b/src/migration-scripts/conntrack-sync/1-to-2
index a8e1007f3..3e10e98c3 100755..100644
--- a/src/migration-scripts/conntrack-sync/1-to-2
+++ b/src/migration-scripts/conntrack-sync/1-to-2
@@ -1,44 +1,31 @@
-#!/usr/bin/env python3
+# Copyright 2021-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2021 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 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# VyOS 1.2 crux allowed configuring a lower or upper case loglevel. This
# is no longer supported as the input data is validated and will lead to
# an error. If user specifies an upper case logleve, make it lowercase
-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 = ['service', 'conntrack-sync']
-config = ConfigTree(config_file)
-if not config.exists(base):
- # Nothing to do
- exit(0)
-else:
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ # Nothing to do
+ return
+
base_accept_proto = base + ['accept-protocol']
if config.exists(base_accept_proto):
tmp = config.return_value(base_accept_proto)
@@ -57,10 +44,3 @@ else:
base_cluster = base + ['failover-mechanism', 'cluster']
if config.exists(base_cluster):
config.delete(base_cluster)
-
- 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/conntrack/1-to-2 b/src/migration-scripts/conntrack/1-to-2
index c4fe667fc..0a4fb3de9 100755..100644
--- a/src/migration-scripts/conntrack/1-to-2
+++ b/src/migration-scripts/conntrack/1-to-2
@@ -1,32 +1,26 @@
-#!/usr/bin/env python3
+# 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
+# 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, see <http://www.gnu.org/licenses/>.
# Delete "set system conntrack modules gre" option
-import sys
-
from vyos.configtree import ConfigTree
-if len(sys.argv) < 2:
- print("Must specify file name!")
- sys.exit(1)
-
-file_name = sys.argv[1]
-with open(file_name, 'r') as f:
- config_file = f.read()
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(['system', 'conntrack', 'modules', 'gre']):
+ return
-config = ConfigTree(config_file)
-
-if not config.exists(['system', 'conntrack', 'modules', 'gre']):
- # Nothing to do
- sys.exit(0)
-else:
# Delete abandoned node
config.delete(['system', 'conntrack', 'modules', 'gre'])
-
- 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))
- sys.exit(1)
diff --git a/src/migration-scripts/conntrack/2-to-3 b/src/migration-scripts/conntrack/2-to-3
index 6bb42be1e..5ad4e6350 100755..100644
--- a/src/migration-scripts/conntrack/2-to-3
+++ b/src/migration-scripts/conntrack/2-to-3
@@ -1,36 +1,31 @@
-#!/usr/bin/env python3
+# 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
+# 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, see <http://www.gnu.org/licenses/>.
# Conntrack syntax version 3
# Enables all conntrack modules (previous default behaviour) and omits manually disabled modules.
-import sys
-
from vyos.configtree import ConfigTree
-if len(sys.argv) < 2:
- print('Must specify file name!')
- sys.exit(1)
-
-filename = sys.argv[1]
-
-with open(filename, 'r') as f:
- config = ConfigTree(f.read())
-
module_path = ['system', 'conntrack', 'modules']
-# Go over all conntrack modules available as of v1.3.0.
-for module in ['ftp', 'h323', 'nfs', 'pptp', 'sip', 'sqlnet', 'tftp']:
- # 'disable' is being phased out.
- if config.exists(module_path + [module, 'disable']):
- config.delete(module_path + [module])
- # If it wasn't manually 'disable'd, it was enabled by default.
- else:
- config.set(module_path + [module])
-
-try:
- if config.exists(module_path):
- with open(filename, 'w') as f:
- f.write(config.to_string())
-except OSError as e:
- print(f'Failed to save the modified config: {e}')
- sys.exit(1)
+def migrate(config: ConfigTree) -> None:
+ # Go over all conntrack modules available as of v1.3.0.
+ for module in ['ftp', 'h323', 'nfs', 'pptp', 'sip', 'sqlnet', 'tftp']:
+ # 'disable' is being phased out.
+ if config.exists(module_path + [module, 'disable']):
+ config.delete(module_path + [module])
+ # If it wasn't manually 'disable'd, it was enabled by default.
+ else:
+ config.set(module_path + [module])
diff --git a/src/migration-scripts/conntrack/3-to-4 b/src/migration-scripts/conntrack/3-to-4
index e90c383af..679a260d5 100755..100644
--- a/src/migration-scripts/conntrack/3-to-4
+++ b/src/migration-scripts/conntrack/3-to-4
@@ -1,50 +1,30 @@
-#!/usr/bin/env python3
+# Copyright 2023-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2023 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 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# Add support for IPv6 conntrack ignore, move existing nodes to `system conntrack ignore ipv4`
-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 = ['system', 'conntrack']
-config = ConfigTree(config_file)
-
-if not config.exists(base):
- # Nothing to do
- exit(0)
-if config.exists(base + ['ignore', 'rule']):
- config.set(base + ['ignore', 'ipv4'])
- config.copy(base + ['ignore', 'rule'], base + ['ignore', 'ipv4', 'rule'])
- config.delete(base + ['ignore', 'rule'])
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ # Nothing to do
+ return
-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)
+ if config.exists(base + ['ignore', 'rule']):
+ config.set(base + ['ignore', 'ipv4'])
+ config.copy(base + ['ignore', 'rule'], base + ['ignore', 'ipv4', 'rule'])
+ config.delete(base + ['ignore', 'rule'])
diff --git a/src/migration-scripts/conntrack/4-to-5 b/src/migration-scripts/conntrack/4-to-5
index d2e5fc5fa..775fe7480 100755..100644
--- a/src/migration-scripts/conntrack/4-to-5
+++ b/src/migration-scripts/conntrack/4-to-5
@@ -1,18 +1,17 @@
-#!/usr/bin/env python3
+# Copyright 2023-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2023 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 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# T5779: system conntrack timeout custom
# Before:
@@ -23,37 +22,18 @@
# Extend functionality to ipv6 and move ipv4 custom rules to new node:
# set system conntrack timeout custom [ipv4 | ipv6] rule <rule> ...
-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 = ['system', 'conntrack']
-config = ConfigTree(config_file)
-
-if not config.exists(base):
- # Nothing to do
- exit(0)
-
-if config.exists(base + ['timeout', 'custom', 'rule']):
- for rule in config.list_nodes(base + ['timeout', 'custom', 'rule']):
- if config.exists(base + ['timeout', 'custom', 'rule', rule, 'protocol', 'tcp']):
- config.set(base + ['timeout', 'custom', 'ipv4', 'rule'])
- config.copy(base + ['timeout', 'custom', 'rule', rule], base + ['timeout', 'custom', 'ipv4', 'rule', rule])
- config.delete(base + ['timeout', 'custom', 'rule'])
-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 migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ # Nothing to do
+ return
+
+ if config.exists(base + ['timeout', 'custom', 'rule']):
+ for rule in config.list_nodes(base + ['timeout', 'custom', 'rule']):
+ if config.exists(base + ['timeout', 'custom', 'rule', rule, 'protocol', 'tcp']):
+ config.set(base + ['timeout', 'custom', 'ipv4', 'rule'])
+ config.copy(base + ['timeout', 'custom', 'rule', rule], base + ['timeout', 'custom', 'ipv4', 'rule', rule])
+ config.delete(base + ['timeout', 'custom', 'rule'])
diff --git a/src/migration-scripts/container/0-to-1 b/src/migration-scripts/container/0-to-1
index 6b282e082..99102a5e6 100755..100644
--- a/src/migration-scripts/container/0-to-1
+++ b/src/migration-scripts/container/0-to-1
@@ -1,77 +1,65 @@
-#!/usr/bin/env python3
+# Copyright 2022-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2022 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 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# T4870: change underlaying container filesystem from vfs to overlay
import os
import shutil
-import sys
from vyos.configtree import ConfigTree
from vyos.utils.process import call
-if len(sys.argv) < 2:
- print("Must specify file name!")
- sys.exit(1)
-
-file_name = sys.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 config.exists(base):
- for container in config.list_nodes(base):
- # Stop any given container first
- call(f'sudo systemctl stop vyos-container-{container}.service')
- # Export container image for later re-import to new filesystem. We store
- # the backup on a real disk as a tmpfs (like /tmp) could probably lack
- # memory if a host has too many containers stored.
- image_name = config.return_value(base + [container, 'image'])
- call(f'sudo podman image save --quiet --output /root/{container}.tar --format oci-archive {image_name}')
-# No need to adjust the strage driver online (this is only used for testing and
-# debugging on a live system) - it is already overlay2 when the migration script
-# is run during system update. But the specified driver in the image is actually
-# overwritten by the still present VFS filesystem on disk. Thus podman still
-# thinks it uses VFS until we delete the libpod directory under:
-# /usr/lib/live/mount/persistence/container/storage
-#call('sed -i "s/vfs/overlay2/g" /etc/containers/storage.conf /usr/share/vyos/templates/container/storage.conf.j2')
+def migrate(config: ConfigTree) -> None:
+ # Check if containers exist and we need to perform image manipulation
+ if config.exists(base):
+ for container in config.list_nodes(base):
+ # Stop any given container first
+ call(f'sudo systemctl stop vyos-container-{container}.service')
+ # Export container image for later re-import to new filesystem. We store
+ # the backup on a real disk as a tmpfs (like /tmp) could probably lack
+ # memory if a host has too many containers stored.
+ image_name = config.return_value(base + [container, 'image'])
+ call(f'sudo podman image save --quiet --output /root/{container}.tar --format oci-archive {image_name}')
-base_path = '/usr/lib/live/mount/persistence/container/storage'
-for dir in ['libpod', 'vfs', 'vfs-containers', 'vfs-images', 'vfs-layers']:
- if os.path.exists(f'{base_path}/{dir}'):
- shutil.rmtree(f'{base_path}/{dir}')
+ # No need to adjust the strage driver online (this is only used for testing and
+ # debugging on a live system) - it is already overlay2 when the migration script
+ # is run during system update. But the specified driver in the image is actually
+ # overwritten by the still present VFS filesystem on disk. Thus podman still
+ # thinks it uses VFS until we delete the libpod directory under:
+ # /usr/lib/live/mount/persistence/container/storage
+ #call('sed -i "s/vfs/overlay2/g" /etc/containers/storage.conf /usr/share/vyos/templates/container/storage.conf.j2')
-# Now all remaining information about VFS is gone and we operate in overlayfs2
-# filesystem mode. Time to re-import the images.
-if config.exists(base):
- for container in config.list_nodes(base):
- # Export container image for later re-import to new filesystem
- image_name = config.return_value(base + [container, 'image'])
- image_path = f'/root/{container}.tar'
- call(f'sudo podman image load --quiet --input {image_path}')
+ base_path = '/usr/lib/live/mount/persistence/container/storage'
+ for dir in ['libpod', 'vfs', 'vfs-containers', 'vfs-images', 'vfs-layers']:
+ if os.path.exists(f'{base_path}/{dir}'):
+ shutil.rmtree(f'{base_path}/{dir}')
- # Start any given container first
- call(f'sudo systemctl start vyos-container-{container}.service')
+ # Now all remaining information about VFS is gone and we operate in overlayfs2
+ # filesystem mode. Time to re-import the images.
+ if config.exists(base):
+ for container in config.list_nodes(base):
+ # Export container image for later re-import to new filesystem
+ image_name = config.return_value(base + [container, 'image'])
+ image_path = f'/root/{container}.tar'
+ call(f'sudo podman image load --quiet --input {image_path}')
- # Delete temporary container image
- if os.path.exists(image_path):
- os.unlink(image_path)
+ # Start any given container first
+ call(f'sudo systemctl start vyos-container-{container}.service')
+ # Delete temporary container image
+ if os.path.exists(image_path):
+ os.unlink(image_path)
diff --git a/src/migration-scripts/container/1-to-2 b/src/migration-scripts/container/1-to-2
index 408faf978..c12dd8ebb 100755..100644
--- a/src/migration-scripts/container/1-to-2
+++ b/src/migration-scripts/container/1-to-2
@@ -1,50 +1,32 @@
-#!/usr/bin/env python3
+# Copyright 2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 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; either
+# version 2.1 of the License, or (at your option) any later version.
#
-# 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# 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)
+def migrate(config: ConfigTree) -> None:
-for container in config.list_nodes(base):
- cap_path = base + [container, 'cap-add']
- if config.exists(cap_path):
- config.rename(cap_path, 'capability')
+ # Check if containers exist and we need to perform image manipulation
+ if not config.exists(base):
+ # Nothing to do
+ return
-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)
+ for container in config.list_nodes(base):
+ cap_path = base + [container, 'cap-add']
+ if config.exists(cap_path):
+ config.rename(cap_path, 'capability')
diff --git a/src/migration-scripts/dhcp-relay/1-to-2 b/src/migration-scripts/dhcp-relay/1-to-2
index 508bac6be..54cd8d6c0 100755..100644
--- a/src/migration-scripts/dhcp-relay/1-to-2
+++ b/src/migration-scripts/dhcp-relay/1-to-2
@@ -1,35 +1,29 @@
-#!/usr/bin/env python3
+# Copyright 2018-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
+# 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, see <http://www.gnu.org/licenses/>.
# Delete "set service dhcp-relay relay-options port" option
# Delete "set service dhcpv6-relay listen-port" option
-import sys
-
from vyos.configtree import ConfigTree
-if len(sys.argv) < 2:
- print("Must specify file name!")
- sys.exit(1)
-
-file_name = sys.argv[1]
-
-with open(file_name, 'r') as f:
- config_file = f.read()
+def migrate(config: ConfigTree) -> None:
+ if not (config.exists(['service', 'dhcp-relay', 'relay-options', 'port']) or config.exists(['service', 'dhcpv6-relay', 'listen-port'])):
+ # Nothing to do
+ return
-config = ConfigTree(config_file)
-
-if not (config.exists(['service', 'dhcp-relay', 'relay-options', 'port']) or config.exists(['service', 'dhcpv6-relay', 'listen-port'])):
- # Nothing to do
- sys.exit(0)
-else:
# Delete abandoned node
config.delete(['service', 'dhcp-relay', 'relay-options', 'port'])
# Delete abandoned node
config.delete(['service', 'dhcpv6-relay', 'listen-port'])
-
- 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))
- sys.exit(1)
diff --git a/src/migration-scripts/dhcp-server/10-to-11 b/src/migration-scripts/dhcp-server/10-to-11
index a0dc96ad0..f54a4c7b7 100755..100644
--- a/src/migration-scripts/dhcp-server/10-to-11
+++ b/src/migration-scripts/dhcp-server/10-to-11
@@ -1,48 +1,28 @@
-#!/usr/bin/env python3
+# Copyright 2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 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; either
+# version 2.1 of the License, or (at your option) any later version.
#
-# 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# T6171: rename "service dhcp-server failover" to "service dhcp-server high-availability"
-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 = ['service', 'dhcp-server']
-config = ConfigTree(config_file)
-
-if not config.exists(base):
- # Nothing to do
- exit(0)
-if config.exists(base + ['failover']):
- config.rename(base + ['failover'],'high-availability')
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ # Nothing to do
+ return
-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) \ No newline at end of file
+ if config.exists(base + ['failover']):
+ config.rename(base + ['failover'],'high-availability')
diff --git a/src/migration-scripts/dhcp-server/4-to-5 b/src/migration-scripts/dhcp-server/4-to-5
index d15e0baf5..a655515dc 100755..100644
--- a/src/migration-scripts/dhcp-server/4-to-5
+++ b/src/migration-scripts/dhcp-server/4-to-5
@@ -1,122 +1,118 @@
#!/usr/bin/env python3
+# Copyright 2018-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
+# 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, see <http://www.gnu.org/licenses/>.
+
# Removes boolean operator from:
# - "set service dhcp-server shared-network-name <xyz> subnet 172.31.0.0/24 ip-forwarding enable (true|false)"
# - "set service dhcp-server shared-network-name <xyz> authoritative (true|false)"
# - "set service dhcp-server disabled (true|false)"
-import sys
-
from vyos.configtree import ConfigTree
-if len(sys.argv) < 2:
- print("Must specify file name!")
- sys.exit(1)
-
-file_name = sys.argv[1]
-
-with open(file_name, 'r') as f:
- config_file = f.read()
-
-config = ConfigTree(config_file)
-
-if not config.exists(['service', 'dhcp-server']):
- # Nothing to do
- sys.exit(0)
-else:
- base = ['service', 'dhcp-server']
- # Make node "set service dhcp-server dynamic-dns-update enable (true|false)" valueless
- if config.exists(base + ['dynamic-dns-update']):
- bool_val = config.return_value(base + ['dynamic-dns-update', 'enable'])
-
- # Delete the node with the old syntax
- config.delete(base + ['dynamic-dns-update'])
- if str(bool_val) == 'true':
- # Enable dynamic-dns-update with new syntax
- config.set(base + ['dynamic-dns-update'], value=None)
-
- # Make node "set service dhcp-server disabled (true|false)" valueless
- if config.exists(base + ['disabled']):
- bool_val = config.return_value(base + ['disabled'])
-
- # Delete the node with the old syntax
- config.delete(base + ['disabled'])
- if str(bool_val) == 'true':
- # Now disable DHCP server with the new syntax
- config.set(base + ['disable'], value=None)
-
- # Make node "set service dhcp-server hostfile-update (enable|disable) valueless
- if config.exists(base + ['hostfile-update']):
- bool_val = config.return_value(base + ['hostfile-update'])
-
- # Delete the node with the old syntax incl. all subnodes
- config.delete(base + ['hostfile-update'])
- if str(bool_val) == 'enable':
- # Enable hostfile update with new syntax
- config.set(base + ['hostfile-update'], value=None)
-
- # Run this for every instance if 'shared-network-name'
- for network in config.list_nodes(base + ['shared-network-name']):
- base_network = base + ['shared-network-name', network]
- # format as tag node to avoid loading problems
- config.set_tag(base + ['shared-network-name'])
-
- # Run this for every specified 'subnet'
- for subnet in config.list_nodes(base_network + ['subnet']):
- base_subnet = base_network + ['subnet', subnet]
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(['service', 'dhcp-server']):
+ # Nothing to do
+ return
+ else:
+ base = ['service', 'dhcp-server']
+ # Make node "set service dhcp-server dynamic-dns-update enable (true|false)" valueless
+ if config.exists(base + ['dynamic-dns-update']):
+ bool_val = config.return_value(base + ['dynamic-dns-update', 'enable'])
+
+ # Delete the node with the old syntax
+ config.delete(base + ['dynamic-dns-update'])
+ if str(bool_val) == 'true':
+ # Enable dynamic-dns-update with new syntax
+ config.set(base + ['dynamic-dns-update'], value=None)
+
+ # Make node "set service dhcp-server disabled (true|false)" valueless
+ if config.exists(base + ['disabled']):
+ bool_val = config.return_value(base + ['disabled'])
+
+ # Delete the node with the old syntax
+ config.delete(base + ['disabled'])
+ if str(bool_val) == 'true':
+ # Now disable DHCP server with the new syntax
+ config.set(base + ['disable'], value=None)
+
+ # Make node "set service dhcp-server hostfile-update (enable|disable) valueless
+ if config.exists(base + ['hostfile-update']):
+ bool_val = config.return_value(base + ['hostfile-update'])
+
+ # Delete the node with the old syntax incl. all subnodes
+ config.delete(base + ['hostfile-update'])
+ if str(bool_val) == 'enable':
+ # Enable hostfile update with new syntax
+ config.set(base + ['hostfile-update'], value=None)
+
+ # Run this for every instance if 'shared-network-name'
+ for network in config.list_nodes(base + ['shared-network-name']):
+ base_network = base + ['shared-network-name', network]
# format as tag node to avoid loading problems
- config.set_tag(base_network + ['subnet'])
+ config.set_tag(base + ['shared-network-name'])
- # Make node "set service dhcp-server shared-network-name <xyz> subnet 172.31.0.0/24 ip-forwarding enable" valueless
- if config.exists(base_subnet + ['ip-forwarding', 'enable']):
- bool_val = config.return_value(base_subnet + ['ip-forwarding', 'enable'])
- # Delete the node with the old syntax
- config.delete(base_subnet + ['ip-forwarding'])
- if str(bool_val) == 'true':
- # Recreate node with new syntax
- config.set(base_subnet + ['ip-forwarding'], value=None)
-
- # Rename node "set service dhcp-server shared-network-name <xyz> subnet 172.31.0.0/24 start <172.16.0.4> stop <172.16.0.9>
- if config.exists(base_subnet + ['start']):
- # This is the new "range" id for DHCP lease ranges
- r_id = 0
- for range in config.list_nodes(base_subnet + ['start']):
- range_start = range
- range_stop = config.return_value(base_subnet + ['start', range_start, 'stop'])
+ # Run this for every specified 'subnet'
+ for subnet in config.list_nodes(base_network + ['subnet']):
+ base_subnet = base_network + ['subnet', subnet]
+ # format as tag node to avoid loading problems
+ config.set_tag(base_network + ['subnet'])
+ # Make node "set service dhcp-server shared-network-name <xyz> subnet 172.31.0.0/24 ip-forwarding enable" valueless
+ if config.exists(base_subnet + ['ip-forwarding', 'enable']):
+ bool_val = config.return_value(base_subnet + ['ip-forwarding', 'enable'])
# Delete the node with the old syntax
- config.delete(base_subnet + ['start', range_start])
+ config.delete(base_subnet + ['ip-forwarding'])
+ if str(bool_val) == 'true':
+ # Recreate node with new syntax
+ config.set(base_subnet + ['ip-forwarding'], value=None)
+
+ # Rename node "set service dhcp-server shared-network-name <xyz> subnet 172.31.0.0/24 start <172.16.0.4> stop <172.16.0.9>
+ if config.exists(base_subnet + ['start']):
+ # This is the new "range" id for DHCP lease ranges
+ r_id = 0
+ for range in config.list_nodes(base_subnet + ['start']):
+ range_start = range
+ range_stop = config.return_value(base_subnet + ['start', range_start, 'stop'])
+
+ # Delete the node with the old syntax
+ config.delete(base_subnet + ['start', range_start])
+
+ # Create the node for the new syntax
+ # Note: range is a tag node, counter is its child, not a value
+ config.set(base_subnet + ['range', r_id])
+ config.set(base_subnet + ['range', r_id, 'start'], value=range_start)
+ config.set(base_subnet + ['range', r_id, 'stop'], value=range_stop)
+
+ # format as tag node to avoid loading problems
+ config.set_tag(base_subnet + ['range'])
+
+ # increment range id for possible next range definition
+ r_id += 1
- # Create the node for the new syntax
- # Note: range is a tag node, counter is its child, not a value
- config.set(base_subnet + ['range', r_id])
- config.set(base_subnet + ['range', r_id, 'start'], value=range_start)
- config.set(base_subnet + ['range', r_id, 'stop'], value=range_stop)
+ # Delete the node with the old syntax
+ config.delete(['service', 'dhcp-server', 'shared-network-name', network, 'subnet', subnet, 'start'])
- # format as tag node to avoid loading problems
- config.set_tag(base_subnet + ['range'])
- # increment range id for possible next range definition
- r_id += 1
+ # Make node "set service dhcp-server shared-network-name <xyz> authoritative" valueless
+ if config.exists(['service', 'dhcp-server', 'shared-network-name', network, 'authoritative']):
+ authoritative = config.return_value(['service', 'dhcp-server', 'shared-network-name', network, 'authoritative'])
# Delete the node with the old syntax
- config.delete(['service', 'dhcp-server', 'shared-network-name', network, 'subnet', subnet, 'start'])
+ config.delete(['service', 'dhcp-server', 'shared-network-name', network, 'authoritative'])
-
- # Make node "set service dhcp-server shared-network-name <xyz> authoritative" valueless
- if config.exists(['service', 'dhcp-server', 'shared-network-name', network, 'authoritative']):
- authoritative = config.return_value(['service', 'dhcp-server', 'shared-network-name', network, 'authoritative'])
-
- # Delete the node with the old syntax
- config.delete(['service', 'dhcp-server', 'shared-network-name', network, 'authoritative'])
-
- # Recreate node with new syntax - if required
- if authoritative == "enable":
- config.set(['service', 'dhcp-server', 'shared-network-name', network, 'authoritative'])
-
- 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))
- sys.exit(1)
+ # Recreate node with new syntax - if required
+ if authoritative == "enable":
+ config.set(['service', 'dhcp-server', 'shared-network-name', network, 'authoritative'])
diff --git a/src/migration-scripts/dhcp-server/5-to-6 b/src/migration-scripts/dhcp-server/5-to-6
index f5c766a09..9404cd038 100755..100644
--- a/src/migration-scripts/dhcp-server/5-to-6
+++ b/src/migration-scripts/dhcp-server/5-to-6
@@ -1,87 +1,69 @@
-#!/usr/bin/env python3
+# Copyright 2021-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2021 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 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# T1968: allow multiple static-routes to be configured
# T3838: rename dns-server -> name-server
-import sys
from vyos.configtree import ConfigTree
-if len(sys.argv) < 2:
- print("Must specify file name!")
- sys.exit(1)
-
-file_name = sys.argv[1]
-
-with open(file_name, 'r') as f:
- config_file = f.read()
-
base = ['service', 'dhcp-server']
-config = ConfigTree(config_file)
-
-if not config.exists(base + ['shared-network-name']):
- # Nothing to do
- exit(0)
-
-# Run this for every instance if 'shared-network-name'
-for network in config.list_nodes(base + ['shared-network-name']):
- base_network = base + ['shared-network-name', network]
-
- if not config.exists(base_network + ['subnet']):
- continue
-
- # Run this for every specified 'subnet'
- for subnet in config.list_nodes(base_network + ['subnet']):
- base_subnet = base_network + ['subnet', subnet]
-
- # T1968: allow multiple static-routes to be configured
- if config.exists(base_subnet + ['static-route']):
- prefix = config.return_value(base_subnet + ['static-route', 'destination-subnet'])
- router = config.return_value(base_subnet + ['static-route', 'router'])
- config.delete(base_subnet + ['static-route'])
-
- config.set(base_subnet + ['static-route', prefix, 'next-hop'], value=router)
- config.set_tag(base_subnet + ['static-route'])
-
- # T3838: rename dns-server -> name-server
- if config.exists(base_subnet + ['dns-server']):
- config.rename(base_subnet + ['dns-server'], 'name-server')
-
-
- # T3672: ISC DHCP server only supports one failover peer
- if config.exists(base_subnet + ['failover']):
- # There can only be one failover configuration, if none is present
- # we add the first one
- if not config.exists(base + ['failover']):
- local = config.return_value(base_subnet + ['failover', 'local-address'])
- remote = config.return_value(base_subnet + ['failover', 'peer-address'])
- status = config.return_value(base_subnet + ['failover', 'status'])
- name = config.return_value(base_subnet + ['failover', 'name'])
-
- config.set(base + ['failover', 'remote'], value=remote)
- config.set(base + ['failover', 'source-address'], value=local)
- config.set(base + ['failover', 'status'], value=status)
- config.set(base + ['failover', 'name'], value=name)
-
- config.delete(base_subnet + ['failover'])
- config.set(base_subnet + ['enable-failover'])
-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 migrate(config: ConfigTree) -> None:
+ if not config.exists(base + ['shared-network-name']):
+ # Nothing to do
+ return
+
+ # Run this for every instance if 'shared-network-name'
+ for network in config.list_nodes(base + ['shared-network-name']):
+ base_network = base + ['shared-network-name', network]
+
+ if not config.exists(base_network + ['subnet']):
+ continue
+
+ # Run this for every specified 'subnet'
+ for subnet in config.list_nodes(base_network + ['subnet']):
+ base_subnet = base_network + ['subnet', subnet]
+
+ # T1968: allow multiple static-routes to be configured
+ if config.exists(base_subnet + ['static-route']):
+ prefix = config.return_value(base_subnet + ['static-route', 'destination-subnet'])
+ router = config.return_value(base_subnet + ['static-route', 'router'])
+ config.delete(base_subnet + ['static-route'])
+
+ config.set(base_subnet + ['static-route', prefix, 'next-hop'], value=router)
+ config.set_tag(base_subnet + ['static-route'])
+
+ # T3838: rename dns-server -> name-server
+ if config.exists(base_subnet + ['dns-server']):
+ config.rename(base_subnet + ['dns-server'], 'name-server')
+
+
+ # T3672: ISC DHCP server only supports one failover peer
+ if config.exists(base_subnet + ['failover']):
+ # There can only be one failover configuration, if none is present
+ # we add the first one
+ if not config.exists(base + ['failover']):
+ local = config.return_value(base_subnet + ['failover', 'local-address'])
+ remote = config.return_value(base_subnet + ['failover', 'peer-address'])
+ status = config.return_value(base_subnet + ['failover', 'status'])
+ name = config.return_value(base_subnet + ['failover', 'name'])
+
+ config.set(base + ['failover', 'remote'], value=remote)
+ config.set(base + ['failover', 'source-address'], value=local)
+ config.set(base + ['failover', 'status'], value=status)
+ config.set(base + ['failover', 'name'], value=name)
+
+ config.delete(base_subnet + ['failover'])
+ config.set(base_subnet + ['enable-failover'])
diff --git a/src/migration-scripts/dhcp-server/6-to-7 b/src/migration-scripts/dhcp-server/6-to-7
index e6c298a60..4e6583a31 100755..100644
--- a/src/migration-scripts/dhcp-server/6-to-7
+++ b/src/migration-scripts/dhcp-server/6-to-7
@@ -1,76 +1,58 @@
-#!/usr/bin/env python3
+# Copyright 2022-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 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; either
+# version 2.1 of the License, or (at your option) any later version.
#
-# 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# T6079: Disable duplicate static mappings
-import sys
from vyos.configtree import ConfigTree
-if len(sys.argv) < 2:
- print("Must specify file name!")
- sys.exit(1)
-
-file_name = sys.argv[1]
-
-with open(file_name, 'r') as f:
- config_file = f.read()
-
base = ['service', 'dhcp-server']
-config = ConfigTree(config_file)
-
-if not config.exists(base + ['shared-network-name']):
- # Nothing to do
- exit(0)
-# Run this for every instance if 'shared-network-name'
-for network in config.list_nodes(base + ['shared-network-name']):
- base_network = base + ['shared-network-name', network]
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(base + ['shared-network-name']):
+ # Nothing to do
+ return
- if not config.exists(base_network + ['subnet']):
- continue
+ # Run this for every instance if 'shared-network-name'
+ for network in config.list_nodes(base + ['shared-network-name']):
+ base_network = base + ['shared-network-name', network]
- for subnet in config.list_nodes(base_network + ['subnet']):
- base_subnet = base_network + ['subnet', subnet]
+ if not config.exists(base_network + ['subnet']):
+ continue
- if config.exists(base_subnet + ['static-mapping']):
- used_mac = []
- used_ip = []
+ for subnet in config.list_nodes(base_network + ['subnet']):
+ base_subnet = base_network + ['subnet', subnet]
- for mapping in config.list_nodes(base_subnet + ['static-mapping']):
- base_mapping = base_subnet + ['static-mapping', mapping]
+ if config.exists(base_subnet + ['static-mapping']):
+ used_mac = []
+ used_ip = []
- if config.exists(base_mapping + ['mac-address']):
- mac = config.return_value(base_mapping + ['mac-address'])
+ for mapping in config.list_nodes(base_subnet + ['static-mapping']):
+ base_mapping = base_subnet + ['static-mapping', mapping]
- if mac in used_mac:
- config.set(base_mapping + ['disable'])
- else:
- used_mac.append(mac)
+ if config.exists(base_mapping + ['mac-address']):
+ mac = config.return_value(base_mapping + ['mac-address'])
- if config.exists(base_mapping + ['ip-address']):
- ip = config.return_value(base_mapping + ['ip-address'])
+ if mac in used_mac:
+ config.set(base_mapping + ['disable'])
+ else:
+ used_mac.append(mac)
- if ip in used_ip:
- config.set(base_subnet + ['static-mapping', mapping, 'disable'])
- else:
- used_ip.append(ip)
+ if config.exists(base_mapping + ['ip-address']):
+ ip = config.return_value(base_mapping + ['ip-address'])
-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)
+ if ip in used_ip:
+ config.set(base_subnet + ['static-mapping', mapping, 'disable'])
+ else:
+ used_ip.append(ip)
diff --git a/src/migration-scripts/dhcp-server/7-to-8 b/src/migration-scripts/dhcp-server/7-to-8
index ccf385a30..7fcb62e86 100755..100644
--- a/src/migration-scripts/dhcp-server/7-to-8
+++ b/src/migration-scripts/dhcp-server/7-to-8
@@ -1,18 +1,17 @@
-#!/usr/bin/env python3
+# Copyright 2023-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2023 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 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# T3316: Migrate to Kea
# - global-parameters will not function
@@ -23,65 +22,48 @@
# - ping-check no longer supported
# - failover is default enabled on all subnets that exist on failover servers
-import sys
from vyos.configtree import ConfigTree
-if (len(sys.argv) < 2):
- print("Must specify file name!")
- sys.exit(1)
-
-file_name = sys.argv[1]
-
-with open(file_name, 'r') as f:
- config_file = f.read()
-
base = ['service', 'dhcp-server']
-config = ConfigTree(config_file)
-
-if not config.exists(base):
- # Nothing to do
- sys.exit(0)
-if config.exists(base + ['host-decl-name']):
- config.delete(base + ['host-decl-name'])
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ # Nothing to do
+ return
-if config.exists(base + ['global-parameters']):
- config.delete(base + ['global-parameters'])
+ if config.exists(base + ['host-decl-name']):
+ config.delete(base + ['host-decl-name'])
-if config.exists(base + ['shared-network-name']):
- for network in config.list_nodes(base + ['shared-network-name']):
- base_network = base + ['shared-network-name', network]
+ if config.exists(base + ['global-parameters']):
+ config.delete(base + ['global-parameters'])
- if config.exists(base_network + ['ping-check']):
- config.delete(base_network + ['ping-check'])
+ if config.exists(base + ['shared-network-name']):
+ for network in config.list_nodes(base + ['shared-network-name']):
+ base_network = base + ['shared-network-name', network]
- if config.exists(base_network + ['shared-network-parameters']):
- config.delete(base_network +['shared-network-parameters'])
+ if config.exists(base_network + ['ping-check']):
+ config.delete(base_network + ['ping-check'])
- if not config.exists(base_network + ['subnet']):
- continue
+ if config.exists(base_network + ['shared-network-parameters']):
+ config.delete(base_network +['shared-network-parameters'])
- # Run this for every specified 'subnet'
- for subnet in config.list_nodes(base_network + ['subnet']):
- base_subnet = base_network + ['subnet', subnet]
+ if not config.exists(base_network + ['subnet']):
+ continue
- if config.exists(base_subnet + ['enable-failover']):
- config.delete(base_subnet + ['enable-failover'])
+ # Run this for every specified 'subnet'
+ for subnet in config.list_nodes(base_network + ['subnet']):
+ base_subnet = base_network + ['subnet', subnet]
- if config.exists(base_subnet + ['ping-check']):
- config.delete(base_subnet + ['ping-check'])
+ if config.exists(base_subnet + ['enable-failover']):
+ config.delete(base_subnet + ['enable-failover'])
- if config.exists(base_subnet + ['subnet-parameters']):
- config.delete(base_subnet + ['subnet-parameters'])
+ if config.exists(base_subnet + ['ping-check']):
+ config.delete(base_subnet + ['ping-check'])
- if config.exists(base_subnet + ['static-mapping']):
- for mapping in config.list_nodes(base_subnet + ['static-mapping']):
- if config.exists(base_subnet + ['static-mapping', mapping, 'static-mapping-parameters']):
- config.delete(base_subnet + ['static-mapping', mapping, 'static-mapping-parameters'])
+ if config.exists(base_subnet + ['subnet-parameters']):
+ config.delete(base_subnet + ['subnet-parameters'])
-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)
+ if config.exists(base_subnet + ['static-mapping']):
+ for mapping in config.list_nodes(base_subnet + ['static-mapping']):
+ if config.exists(base_subnet + ['static-mapping', mapping, 'static-mapping-parameters']):
+ config.delete(base_subnet + ['static-mapping', mapping, 'static-mapping-parameters'])
diff --git a/src/migration-scripts/dhcp-server/8-to-9 b/src/migration-scripts/dhcp-server/8-to-9
index 151aa6d7b..5843e9fda 100755..100644
--- a/src/migration-scripts/dhcp-server/8-to-9
+++ b/src/migration-scripts/dhcp-server/8-to-9
@@ -1,65 +1,47 @@
-#!/usr/bin/env python3
+# Copyright 2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2023 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 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# T3316:
# - Adjust hostname to have valid FQDN characters only (underscores aren't allowed anymore)
# - Rename "service dhcp-server shared-network-name ... static-mapping <hostname> mac-address ..."
# to "service dhcp-server shared-network-name ... static-mapping <hostname> mac ..."
-import sys
import re
from vyos.configtree import ConfigTree
-if len(sys.argv) < 2:
- print("Must specify file name!")
- sys.exit(1)
-
-file_name = sys.argv[1]
-
-with open(file_name, 'r') as f:
- config_file = f.read()
-
base = ['service', 'dhcp-server', 'shared-network-name']
-config = ConfigTree(config_file)
-
-if not config.exists(base):
- # Nothing to do
- sys.exit(0)
-
-for network in config.list_nodes(base):
- # Run this for every specified 'subnet'
- if config.exists(base + [network, 'subnet']):
- for subnet in config.list_nodes(base + [network, 'subnet']):
- base_subnet = base + [network, 'subnet', subnet]
- if config.exists(base_subnet + ['static-mapping']):
- for hostname in config.list_nodes(base_subnet + ['static-mapping']):
- base_mapping = base_subnet + ['static-mapping', hostname]
-
- # Rename the 'mac-address' node to 'mac'
- if config.exists(base_mapping + ['mac-address']):
- config.rename(base_mapping + ['mac-address'], 'mac')
-
- # Adjust hostname to have valid FQDN characters only
- new_hostname = re.sub(r'[^a-zA-Z0-9-.]', '-', hostname)
- if new_hostname != hostname:
- config.rename(base_mapping, new_hostname)
-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 migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ # Nothing to do
+ return
+
+ for network in config.list_nodes(base):
+ # Run this for every specified 'subnet'
+ if config.exists(base + [network, 'subnet']):
+ for subnet in config.list_nodes(base + [network, 'subnet']):
+ base_subnet = base + [network, 'subnet', subnet]
+ if config.exists(base_subnet + ['static-mapping']):
+ for hostname in config.list_nodes(base_subnet + ['static-mapping']):
+ base_mapping = base_subnet + ['static-mapping', hostname]
+
+ # Rename the 'mac-address' node to 'mac'
+ if config.exists(base_mapping + ['mac-address']):
+ config.rename(base_mapping + ['mac-address'], 'mac')
+
+ # Adjust hostname to have valid FQDN characters only
+ new_hostname = re.sub(r'[^a-zA-Z0-9-.]', '-', hostname)
+ if new_hostname != hostname:
+ config.rename(base_mapping, new_hostname)
diff --git a/src/migration-scripts/dhcp-server/9-to-10 b/src/migration-scripts/dhcp-server/9-to-10
index a459b65b5..eda97550d 100755..100644
--- a/src/migration-scripts/dhcp-server/9-to-10
+++ b/src/migration-scripts/dhcp-server/9-to-10
@@ -1,41 +1,25 @@
-#!/usr/bin/env python3
+# Copyright 2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 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; either
+# version 2.1 of the License, or (at your option) any later version.
#
-# 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# T3316:
# - Migrate dhcp options under new option node
# - Add subnet IDs to existing subnets
-import sys
from vyos.configtree import ConfigTree
-if len(sys.argv) < 2:
- print("Must specify file name!")
- sys.exit(1)
-
-file_name = sys.argv[1]
-
-with open(file_name, 'r') as f:
- config_file = f.read()
-
base = ['service', 'dhcp-server', 'shared-network-name']
-config = ConfigTree(config_file)
-
-if not config.exists(base):
- # Nothing to do
- sys.exit(0)
option_nodes = ['bootfile-name', 'bootfile-server', 'bootfile-size', 'captive-portal',
'client-prefix-length', 'default-router', 'domain-name', 'domain-search',
@@ -44,31 +28,30 @@ option_nodes = ['bootfile-name', 'bootfile-server', 'bootfile-size', 'captive-po
'tftp-server-name', 'time-offset', 'time-server', 'time-zone',
'vendor-option', 'wins-server', 'wpad-url']
-subnet_id = 1
-for network in config.list_nodes(base):
- for option in option_nodes:
- if config.exists(base + [network, option]):
- config.set(base + [network, 'option'])
- config.copy(base + [network, option], base + [network, 'option', option])
- config.delete(base + [network, option])
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ # Nothing to do
+ return
+
+ subnet_id = 1
- if config.exists(base + [network, 'subnet']):
- for subnet in config.list_nodes(base + [network, 'subnet']):
- base_subnet = base + [network, 'subnet', subnet]
+ for network in config.list_nodes(base):
+ for option in option_nodes:
+ if config.exists(base + [network, option]):
+ config.set(base + [network, 'option'])
+ config.copy(base + [network, option], base + [network, 'option', option])
+ config.delete(base + [network, option])
- for option in option_nodes:
- if config.exists(base_subnet + [option]):
- config.set(base_subnet + ['option'])
- config.copy(base_subnet + [option], base_subnet + ['option', option])
- config.delete(base_subnet + [option])
+ if config.exists(base + [network, 'subnet']):
+ for subnet in config.list_nodes(base + [network, 'subnet']):
+ base_subnet = base + [network, 'subnet', subnet]
- config.set(base_subnet + ['subnet-id'], value=subnet_id)
- subnet_id += 1
+ for option in option_nodes:
+ if config.exists(base_subnet + [option]):
+ config.set(base_subnet + ['option'])
+ config.copy(base_subnet + [option], base_subnet + ['option', option])
+ config.delete(base_subnet + [option])
-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)
+ config.set(base_subnet + ['subnet-id'], value=subnet_id)
+ subnet_id += 1
diff --git a/src/migration-scripts/dhcpv6-server/0-to-1 b/src/migration-scripts/dhcpv6-server/0-to-1
index deae1ca29..fd9b2d739 100755..100644
--- a/src/migration-scripts/dhcpv6-server/0-to-1
+++ b/src/migration-scripts/dhcpv6-server/0-to-1
@@ -1,39 +1,29 @@
-#!/usr/bin/env python3
+# Copyright 202-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# 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 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# combine both sip-server-address and sip-server-name nodes to common sip-server
-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]
+base = ['service', 'dhcpv6-server', 'shared-network-name']
-with open(file_name, 'r') as f:
- config_file = f.read()
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ # Nothing to do
+ return
-config = ConfigTree(config_file)
-base = ['service', 'dhcpv6-server', 'shared-network-name']
-if not config.exists(base):
- # Nothing to do
- exit(0)
-else:
# we need to run this for every configured network
for network in config.list_nodes(base):
for subnet in config.list_nodes(base + [network, 'subnet']):
@@ -52,10 +42,3 @@ else:
# Write new CLI value for sip-server
for server in sip_server:
config.set(base + [network, 'subnet', subnet, 'sip-server'], value=server, replace=False)
-
- 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/dhcpv6-server/1-to-2 b/src/migration-scripts/dhcpv6-server/1-to-2
index cc5a8900a..ad307495c 100755..100644
--- a/src/migration-scripts/dhcpv6-server/1-to-2
+++ b/src/migration-scripts/dhcpv6-server/1-to-2
@@ -1,18 +1,17 @@
-#!/usr/bin/env python3
+# Copyright 2023-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2023 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 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# T3316: Migrate to Kea
# - Kea was meant to have support for key "prefix-highest" under PD which would allow an address range
@@ -20,67 +19,50 @@
# Ref: https://lists.isc.org/pipermail/kea-users/2022-November/003686.html
# - Remove prefix temporary value, convert to multi leafNode (https://kea.readthedocs.io/en/kea-2.2.0/arm/dhcp6-srv.html#dhcpv6-server-limitations)
-import sys
from vyos.configtree import ConfigTree
from vyos.utils.network import ipv6_prefix_length
-if (len(sys.argv) < 1):
- print("Must specify file name!")
- sys.exit(1)
-
-file_name = sys.argv[1]
-
-with open(file_name, 'r') as f:
- config_file = f.read()
-
base = ['service', 'dhcpv6-server', 'shared-network-name']
-config = ConfigTree(config_file)
-
-if not config.exists(base):
- # Nothing to do
- exit(0)
-for network in config.list_nodes(base):
- if not config.exists(base + [network, 'subnet']):
- continue
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ # Nothing to do
+ return
- for subnet in config.list_nodes(base + [network, 'subnet']):
- # Delete temporary value under address-range prefix, convert tagNode to leafNode multi
- if config.exists(base + [network, 'subnet', subnet, 'address-range', 'prefix']):
- prefix_base = base + [network, 'subnet', subnet, 'address-range', 'prefix']
- prefixes = config.list_nodes(prefix_base)
-
- config.delete(prefix_base)
+ for network in config.list_nodes(base):
+ if not config.exists(base + [network, 'subnet']):
+ continue
- for prefix in prefixes:
- config.set(prefix_base, value=prefix, replace=False)
+ for subnet in config.list_nodes(base + [network, 'subnet']):
+ # Delete temporary value under address-range prefix, convert tagNode to leafNode multi
+ if config.exists(base + [network, 'subnet', subnet, 'address-range', 'prefix']):
+ prefix_base = base + [network, 'subnet', subnet, 'address-range', 'prefix']
+ prefixes = config.list_nodes(prefix_base)
+
+ config.delete(prefix_base)
- if config.exists(base + [network, 'subnet', subnet, 'prefix-delegation', 'prefix']):
- prefix_base = base + [network, 'subnet', subnet, 'prefix-delegation', 'prefix']
+ for prefix in prefixes:
+ config.set(prefix_base, value=prefix, replace=False)
- config.set(prefix_base)
- config.set_tag(prefix_base)
+ if config.exists(base + [network, 'subnet', subnet, 'prefix-delegation', 'prefix']):
+ prefix_base = base + [network, 'subnet', subnet, 'prefix-delegation', 'prefix']
- for start in config.list_nodes(base + [network, 'subnet', subnet, 'prefix-delegation', 'start']):
- path = base + [network, 'subnet', subnet, 'prefix-delegation', 'start', start]
+ config.set(prefix_base)
+ config.set_tag(prefix_base)
- delegated_length = config.return_value(path + ['prefix-length'])
- stop = config.return_value(path + ['stop'])
+ for start in config.list_nodes(base + [network, 'subnet', subnet, 'prefix-delegation', 'start']):
+ path = base + [network, 'subnet', subnet, 'prefix-delegation', 'start', start]
- prefix_length = ipv6_prefix_length(start, stop)
+ delegated_length = config.return_value(path + ['prefix-length'])
+ stop = config.return_value(path + ['stop'])
- # This range could not be converted into a simple prefix length and must be skipped
- if not prefix_length:
- continue
+ prefix_length = ipv6_prefix_length(start, stop)
- config.set(prefix_base + [start, 'delegated-length'], value=delegated_length)
- config.set(prefix_base + [start, 'prefix-length'], value=prefix_length)
+ # This range could not be converted into a simple prefix length and must be skipped
+ if not prefix_length:
+ continue
- config.delete(base + [network, 'subnet', subnet, 'prefix-delegation', 'start'])
+ config.set(prefix_base + [start, 'delegated-length'], value=delegated_length)
+ config.set(prefix_base + [start, 'prefix-length'], value=prefix_length)
-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)
+ config.delete(base + [network, 'subnet', subnet, 'prefix-delegation', 'start'])
diff --git a/src/migration-scripts/dhcpv6-server/2-to-3 b/src/migration-scripts/dhcpv6-server/2-to-3
index f4bdc1d1e..b44798d18 100755..100644
--- a/src/migration-scripts/dhcpv6-server/2-to-3
+++ b/src/migration-scripts/dhcpv6-server/2-to-3
@@ -1,18 +1,17 @@
-#!/usr/bin/env python3
+# Copyright 2023-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2023 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 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# T3316:
# - Adjust hostname to have valid FQDN characters only (underscores aren't allowed anymore)
@@ -22,57 +21,40 @@
# - Rename "service dhcpv6-server shared-network-name ... static-mapping <hostname> mac-address ..."
# to "service dhcpv6-server shared-network-name ... static-mapping <hostname> mac ..."
-import sys
import re
from vyos.configtree import ConfigTree
-if len(sys.argv) < 2:
- print("Must specify file name!")
- sys.exit(1)
-
-file_name = sys.argv[1]
-
-with open(file_name, 'r') as f:
- config_file = f.read()
-
base = ['service', 'dhcpv6-server', 'shared-network-name']
-config = ConfigTree(config_file)
-
-if not config.exists(base):
- # Nothing to do
- sys.exit(0)
-
-for network in config.list_nodes(base):
- # Run this for every specified 'subnet'
- if config.exists(base + [network, 'subnet']):
- for subnet in config.list_nodes(base + [network, 'subnet']):
- base_subnet = base + [network, 'subnet', subnet]
- if config.exists(base_subnet + ['static-mapping']):
- for hostname in config.list_nodes(base_subnet + ['static-mapping']):
- base_mapping = base_subnet + ['static-mapping', hostname]
- if config.exists(base_mapping + ['identifier']):
-
- # Adjust duid to comply with duid format (a:3:b:04:... => 0a:03:0b:04:...)
- duid = config.return_value(base_mapping + ['identifier'])
- new_duid = ':'.join(x.rjust(2,'0') for x in duid.split(':'))
- if new_duid != duid:
- config.set(base_mapping + ['identifier'], new_duid)
-
- # Rename the 'identifier' node to 'duid'
- config.rename(base_mapping + ['identifier'], 'duid')
-
- # Rename the 'mac-address' node to 'mac'
- if config.exists(base_mapping + ['mac-address']):
- config.rename(base_mapping + ['mac-address'], 'mac')
-
- # Adjust hostname to have valid FQDN characters only
- new_hostname = re.sub(r'[^a-zA-Z0-9-.]', '-', hostname)
- if new_hostname != hostname:
- config.rename(base_mapping, new_hostname)
-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 migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ # Nothing to do
+ return
+
+ for network in config.list_nodes(base):
+ # Run this for every specified 'subnet'
+ if config.exists(base + [network, 'subnet']):
+ for subnet in config.list_nodes(base + [network, 'subnet']):
+ base_subnet = base + [network, 'subnet', subnet]
+ if config.exists(base_subnet + ['static-mapping']):
+ for hostname in config.list_nodes(base_subnet + ['static-mapping']):
+ base_mapping = base_subnet + ['static-mapping', hostname]
+ if config.exists(base_mapping + ['identifier']):
+
+ # Adjust duid to comply with duid format (a:3:b:04:... => 0a:03:0b:04:...)
+ duid = config.return_value(base_mapping + ['identifier'])
+ new_duid = ':'.join(x.rjust(2,'0') for x in duid.split(':'))
+ if new_duid != duid:
+ config.set(base_mapping + ['identifier'], new_duid)
+
+ # Rename the 'identifier' node to 'duid'
+ config.rename(base_mapping + ['identifier'], 'duid')
+
+ # Rename the 'mac-address' node to 'mac'
+ if config.exists(base_mapping + ['mac-address']):
+ config.rename(base_mapping + ['mac-address'], 'mac')
+
+ # Adjust hostname to have valid FQDN characters only
+ new_hostname = re.sub(r'[^a-zA-Z0-9-.]', '-', hostname)
+ if new_hostname != hostname:
+ config.rename(base_mapping, new_hostname)
diff --git a/src/migration-scripts/dhcpv6-server/3-to-4 b/src/migration-scripts/dhcpv6-server/3-to-4
index 7efc492a5..e38e36505 100755..100644
--- a/src/migration-scripts/dhcpv6-server/3-to-4
+++ b/src/migration-scripts/dhcpv6-server/3-to-4
@@ -1,89 +1,72 @@
-#!/usr/bin/env python3
+# Copyright 2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 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; either
+# version 2.1 of the License, or (at your option) any later version.
#
-# 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# T3316:
# - Add subnet IDs to existing subnets
# - Move options to option node
# - Migrate address-range to range tagNode
-import sys
from vyos.configtree import ConfigTree
-if len(sys.argv) < 2:
- print("Must specify file name!")
- sys.exit(1)
-
-file_name = sys.argv[1]
-
-with open(file_name, 'r') as f:
- config_file = f.read()
-
base = ['service', 'dhcpv6-server', 'shared-network-name']
-config = ConfigTree(config_file)
-
-if not config.exists(base):
- # Nothing to do
- sys.exit(0)
option_nodes = ['captive-portal', 'domain-search', 'name-server',
'nis-domain', 'nis-server', 'nisplus-domain', 'nisplus-server',
'sip-server', 'sntp-server', 'vendor-option']
-subnet_id = 1
-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]
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ # Nothing to do
+ return
+
+ subnet_id = 1
- if config.exists(base_subnet + ['address-range']):
- config.set(base_subnet + ['range'])
- config.set_tag(base_subnet + ['range'])
+ 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]
- range_id = 1
+ if config.exists(base_subnet + ['address-range']):
+ config.set(base_subnet + ['range'])
+ config.set_tag(base_subnet + ['range'])
- if config.exists(base_subnet + ['address-range', 'prefix']):
- for prefix in config.return_values(base_subnet + ['address-range', 'prefix']):
- config.set(base_subnet + ['range', range_id, 'prefix'], value=prefix)
+ range_id = 1
- range_id += 1
+ if config.exists(base_subnet + ['address-range', 'prefix']):
+ for prefix in config.return_values(base_subnet + ['address-range', 'prefix']):
+ config.set(base_subnet + ['range', range_id, 'prefix'], value=prefix)
- if config.exists(base_subnet + ['address-range', 'start']):
- for start in config.list_nodes(base_subnet + ['address-range', 'start']):
- stop = config.return_value(base_subnet + ['address-range', 'start', start, 'stop'])
+ range_id += 1
- config.set(base_subnet + ['range', range_id, 'start'], value=start)
- config.set(base_subnet + ['range', range_id, 'stop'], value=stop)
+ if config.exists(base_subnet + ['address-range', 'start']):
+ for start in config.list_nodes(base_subnet + ['address-range', 'start']):
+ stop = config.return_value(base_subnet + ['address-range', 'start', start, 'stop'])
- range_id += 1
+ config.set(base_subnet + ['range', range_id, 'start'], value=start)
+ config.set(base_subnet + ['range', range_id, 'stop'], value=stop)
- config.delete(base_subnet + ['address-range'])
+ range_id += 1
- for option in option_nodes:
- if config.exists(base_subnet + [option]):
- config.set(base_subnet + ['option'])
- config.copy(base_subnet + [option], base_subnet + ['option', option])
- config.delete(base_subnet + [option])
+ config.delete(base_subnet + ['address-range'])
- config.set(base_subnet + ['subnet-id'], value=subnet_id)
- subnet_id += 1
+ for option in option_nodes:
+ if config.exists(base_subnet + [option]):
+ config.set(base_subnet + ['option'])
+ config.copy(base_subnet + [option], base_subnet + ['option', option])
+ config.delete(base_subnet + [option])
-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)
+ config.set(base_subnet + ['subnet-id'], value=subnet_id)
+ subnet_id += 1
diff --git a/src/migration-scripts/dhcpv6-server/4-to-5 b/src/migration-scripts/dhcpv6-server/4-to-5
index 55fda91b3..ad18e1a84 100755..100644
--- a/src/migration-scripts/dhcpv6-server/4-to-5
+++ b/src/migration-scripts/dhcpv6-server/4-to-5
@@ -1,91 +1,73 @@
-#!/usr/bin/env python3
+# Copyright 2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 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; either
+# version 2.1 of the License, or (at your option) any later version.
#
-# 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# T5993: Check if subnet is locally accessible and assign interface to subnet
-import sys
from ipaddress import ip_network
from vyos.configtree import ConfigTree
-if (len(sys.argv) < 1):
- print("Must specify file name!")
- sys.exit(1)
-
-file_name = sys.argv[1]
-
-with open(file_name, 'r') as f:
- config_file = f.read()
-
base = ['service', 'dhcpv6-server', 'shared-network-name']
-config = ConfigTree(config_file)
-
-if not config.exists(base):
- # Nothing to do
- exit(0)
-
-def find_subnet_interface(subnet):
- subnet_net = ip_network(subnet)
-
- def check_addr(if_path):
- if config.exists(if_path + ['address']):
- for addr in config.return_values(if_path + ['address']):
- try:
- if ip_network(addr, strict=False) == subnet_net:
- return True
- except:
- pass # interface address was probably "dhcp" or other magic string
- return None
-
- for iftype in config.list_nodes(['interfaces']):
- for ifname in config.list_nodes(['interfaces', iftype]):
- if_base = ['interfaces', iftype, ifname]
-
- if check_addr(if_base):
- return ifname
-
- if config.exists(if_base + ['vif']):
- for vif in config.list_nodes(if_base + ['vif']):
- if check_addr(if_base + ['vif', vif]):
- return f'{ifname}.{vif}'
-
- if config.exists(if_base + ['vif-s']):
- for vifs in config.list_nodes(if_base + ['vif-s']):
- if check_addr(if_base + ['vif-s', vifs]):
- return f'{ifname}.{vifs}'
-
- if config.exists(if_base + ['vif-s', vifs, 'vif-c']):
- for vifc in config.list_nodes(if_base + ['vif-s', vifs, 'vif-c']):
- if check_addr(if_base + ['vif-s', vifs, 'vif-c', vifc]):
- return f'{ifname}.{vifs}.{vifc}'
-
- return False
-
-for network in config.list_nodes(base):
- if not config.exists(base + [network, 'subnet']):
- continue
-
- for subnet in config.list_nodes(base + [network, 'subnet']):
- subnet_interface = find_subnet_interface(subnet)
-
- if subnet_interface:
- config.set(base + [network, 'subnet', subnet, 'interface'], value=subnet_interface)
-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 migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ # Nothing to do
+ return
+
+ def find_subnet_interface(subnet):
+ subnet_net = ip_network(subnet)
+
+ def check_addr(if_path):
+ if config.exists(if_path + ['address']):
+ for addr in config.return_values(if_path + ['address']):
+ try:
+ if ip_network(addr, strict=False) == subnet_net:
+ return True
+ except:
+ pass # interface address was probably "dhcp" or other magic string
+ return None
+
+ for iftype in config.list_nodes(['interfaces']):
+ for ifname in config.list_nodes(['interfaces', iftype]):
+ if_base = ['interfaces', iftype, ifname]
+
+ if check_addr(if_base):
+ return ifname
+
+ if config.exists(if_base + ['vif']):
+ for vif in config.list_nodes(if_base + ['vif']):
+ if check_addr(if_base + ['vif', vif]):
+ return f'{ifname}.{vif}'
+
+ if config.exists(if_base + ['vif-s']):
+ for vifs in config.list_nodes(if_base + ['vif-s']):
+ if check_addr(if_base + ['vif-s', vifs]):
+ return f'{ifname}.{vifs}'
+
+ if config.exists(if_base + ['vif-s', vifs, 'vif-c']):
+ for vifc in config.list_nodes(if_base + ['vif-s', vifs, 'vif-c']):
+ if check_addr(if_base + ['vif-s', vifs, 'vif-c', vifc]):
+ return f'{ifname}.{vifs}.{vifc}'
+
+ return False
+
+ for network in config.list_nodes(base):
+ if not config.exists(base + [network, 'subnet']):
+ continue
+
+ for subnet in config.list_nodes(base + [network, 'subnet']):
+ subnet_interface = find_subnet_interface(subnet)
+
+ if subnet_interface:
+ config.set(base + [network, 'subnet', subnet, 'interface'], value=subnet_interface)
diff --git a/src/migration-scripts/dns-dynamic/0-to-1 b/src/migration-scripts/dns-dynamic/0-to-1
index b7674a9c8..6a91b36af 100755..100644
--- a/src/migration-scripts/dns-dynamic/0-to-1
+++ b/src/migration-scripts/dns-dynamic/0-to-1
@@ -1,18 +1,17 @@
-#!/usr/bin/env python3
-
-# Copyright (C) 2023 VyOS maintainers and contributors
+# Copyright 2023-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License version 2 or later as
-# published by the Free Software Foundation.
+# This 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 program is distributed in the hope that it will be useful,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# T5144:
# - migrate "service dns dynamic interface ..."
@@ -27,7 +26,6 @@
# - apply service protocol mapping upfront, they are not 'auto-detected' anymore
# - migrate web-options url to stricter format
-import sys
import re
from vyos.configtree import ConfigTree
@@ -45,84 +43,67 @@ service_protocol_mapping = {
'zoneedit': 'zoneedit1'
}
-if len(sys.argv) < 2:
- print("Must specify file name!")
- sys.exit(1)
-
-file_name = sys.argv[1]
-
-with open(file_name, 'r') as f:
- config_file = f.read()
-
-config = ConfigTree(config_file)
-
old_base_path = ['service', 'dns', 'dynamic', 'interface']
new_base_path = ['service', 'dns', 'dynamic', 'address']
-if not config.exists(old_base_path):
- # Nothing to do
- sys.exit(0)
-
-# Migrate "service dns dynamic interface"
-# to "service dns dynamic address"
-config.rename(old_base_path, new_base_path[-1])
-
-for address in config.list_nodes(new_base_path):
- # Migrate "service dns dynamic interface <interface> rfc2136 <config> record"
- # to "service dns dynamic address <address> rfc2136 <config> host-name"
- if config.exists(new_base_path + [address, 'rfc2136']):
- for rfc_cfg in config.list_nodes(new_base_path + [address, 'rfc2136']):
- if config.exists(new_base_path + [address, 'rfc2136', rfc_cfg, 'record']):
- config.rename(new_base_path + [address, 'rfc2136', rfc_cfg, 'record'], 'host-name')
-
- # Migrate "service dns dynamic interface <interface> service <config> login"
- # to "service dns dynamic address <address> service <config> username"
- if config.exists(new_base_path + [address, 'service']):
- for svc_cfg in config.list_nodes(new_base_path + [address, 'service']):
- if config.exists(new_base_path + [address, 'service', svc_cfg, 'login']):
- config.rename(new_base_path + [address, 'service', svc_cfg, 'login'], 'username')
- # Apply global 'ipv6-enable' to per <config> 'ip-version: ipv6'
- if config.exists(new_base_path + [address, 'ipv6-enable']):
- config.set(new_base_path + [address, 'service', svc_cfg, 'ip-version'], 'ipv6')
- config.delete(new_base_path + [address, 'ipv6-enable'])
- # Apply service protocol mapping upfront, they are not 'auto-detected' anymore
- if svc_cfg in service_protocol_mapping:
- config.set(new_base_path + [address, 'service', svc_cfg, 'protocol'],
- service_protocol_mapping.get(svc_cfg))
-
- # If use-web is set, then:
- # Move "service dns dynamic address <address> <service|rfc2136> <service> ..."
- # to "service dns dynamic address web <service|rfc2136> <service>-<address> ..."
- # Move "service dns dynamic address web use-web ..."
- # to "service dns dynamic address web web-options ..."
- # Note: The config is named <service>-<address> to avoid name conflict with old entries
- if config.exists(new_base_path + [address, 'use-web']):
- for svc_type in ['rfc2136', 'service']:
- if config.exists(new_base_path + [address, svc_type]):
- config.set(new_base_path + ['web', svc_type])
- config.set_tag(new_base_path + ['web', svc_type])
- for svc_cfg in config.list_nodes(new_base_path + [address, svc_type]):
- config.copy(new_base_path + [address, svc_type, svc_cfg],
- new_base_path + ['web', svc_type, f'{svc_cfg}-{address}'])
-
- # Multiple web-options were not supported, so copy only the first one
- # Also, migrate web-options url to stricter format and transition
- # checkip.dyndns.org to https://domains.google.com/checkip for better
- # TLS support (see: https://github.com/ddclient/ddclient/issues/597)
- if not config.exists(new_base_path + ['web', 'web-options']):
- config.copy(new_base_path + [address, 'use-web'], new_base_path + ['web', 'web-options'])
- if config.exists(new_base_path + ['web', 'web-options', 'url']):
- url = config.return_value(new_base_path + ['web', 'web-options', 'url'])
- if re.search("^(https?://)?checkip\.dyndns\.org", url):
- config.set(new_base_path + ['web', 'web-options', 'url'], 'https://domains.google.com/checkip')
- if not url.startswith(('http://', 'https://')):
- config.set(new_base_path + ['web', 'web-options', 'url'], f'https://{url}')
-
- config.delete(new_base_path + [address])
-
-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))
- sys.exit(1)
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(old_base_path):
+ # Nothing to do
+ return
+
+ # Migrate "service dns dynamic interface"
+ # to "service dns dynamic address"
+ config.rename(old_base_path, new_base_path[-1])
+
+ for address in config.list_nodes(new_base_path):
+ # Migrate "service dns dynamic interface <interface> rfc2136 <config> record"
+ # to "service dns dynamic address <address> rfc2136 <config> host-name"
+ if config.exists(new_base_path + [address, 'rfc2136']):
+ for rfc_cfg in config.list_nodes(new_base_path + [address, 'rfc2136']):
+ if config.exists(new_base_path + [address, 'rfc2136', rfc_cfg, 'record']):
+ config.rename(new_base_path + [address, 'rfc2136', rfc_cfg, 'record'], 'host-name')
+
+ # Migrate "service dns dynamic interface <interface> service <config> login"
+ # to "service dns dynamic address <address> service <config> username"
+ if config.exists(new_base_path + [address, 'service']):
+ for svc_cfg in config.list_nodes(new_base_path + [address, 'service']):
+ if config.exists(new_base_path + [address, 'service', svc_cfg, 'login']):
+ config.rename(new_base_path + [address, 'service', svc_cfg, 'login'], 'username')
+ # Apply global 'ipv6-enable' to per <config> 'ip-version: ipv6'
+ if config.exists(new_base_path + [address, 'ipv6-enable']):
+ config.set(new_base_path + [address, 'service', svc_cfg, 'ip-version'], 'ipv6')
+ config.delete(new_base_path + [address, 'ipv6-enable'])
+ # Apply service protocol mapping upfront, they are not 'auto-detected' anymore
+ if svc_cfg in service_protocol_mapping:
+ config.set(new_base_path + [address, 'service', svc_cfg, 'protocol'],
+ service_protocol_mapping.get(svc_cfg))
+
+ # If use-web is set, then:
+ # Move "service dns dynamic address <address> <service|rfc2136> <service> ..."
+ # to "service dns dynamic address web <service|rfc2136> <service>-<address> ..."
+ # Move "service dns dynamic address web use-web ..."
+ # to "service dns dynamic address web web-options ..."
+ # Note: The config is named <service>-<address> to avoid name conflict with old entries
+ if config.exists(new_base_path + [address, 'use-web']):
+ for svc_type in ['rfc2136', 'service']:
+ if config.exists(new_base_path + [address, svc_type]):
+ config.set(new_base_path + ['web', svc_type])
+ config.set_tag(new_base_path + ['web', svc_type])
+ for svc_cfg in config.list_nodes(new_base_path + [address, svc_type]):
+ config.copy(new_base_path + [address, svc_type, svc_cfg],
+ new_base_path + ['web', svc_type, f'{svc_cfg}-{address}'])
+
+ # Multiple web-options were not supported, so copy only the first one
+ # Also, migrate web-options url to stricter format and transition
+ # checkip.dyndns.org to https://domains.google.com/checkip for better
+ # TLS support (see: https://github.com/ddclient/ddclient/issues/597)
+ if not config.exists(new_base_path + ['web', 'web-options']):
+ config.copy(new_base_path + [address, 'use-web'], new_base_path + ['web', 'web-options'])
+ if config.exists(new_base_path + ['web', 'web-options', 'url']):
+ url = config.return_value(new_base_path + ['web', 'web-options', 'url'])
+ if re.search("^(https?://)?checkip\.dyndns\.org", url):
+ config.set(new_base_path + ['web', 'web-options', 'url'], 'https://domains.google.com/checkip')
+ if not url.startswith(('http://', 'https://')):
+ config.set(new_base_path + ['web', 'web-options', 'url'], f'https://{url}')
+
+ config.delete(new_base_path + [address])
diff --git a/src/migration-scripts/dns-dynamic/1-to-2 b/src/migration-scripts/dns-dynamic/1-to-2
index 8b599b57a..5dca9e32f 100755..100644
--- a/src/migration-scripts/dns-dynamic/1-to-2
+++ b/src/migration-scripts/dns-dynamic/1-to-2
@@ -1,18 +1,17 @@
-#!/usr/bin/env python3
-
-# Copyright (C) 2023 VyOS maintainers and contributors
+# Copyright 2023-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License version 2 or later as
-# published by the Free Software Foundation.
+# This 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 program is distributed in the hope that it will be useful,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# T5708:
# - migrate "service dns dynamic timeout ..."
@@ -21,50 +20,32 @@
# - migrate "service dns dynamic address <interface> service <service> protocol dnsexit"
# to "service dns dynamic address <interface> service <service> protocol dnsexit2"
-import sys
from vyos.configtree import ConfigTree
-if len(sys.argv) < 2:
- print("Must specify file name!")
- sys.exit(1)
-
-file_name = sys.argv[1]
-
-with open(file_name, 'r') as f:
- config_file = f.read()
-
-config = ConfigTree(config_file)
-
base_path = ['service', 'dns', 'dynamic']
timeout_path = base_path + ['timeout']
address_path = base_path + ['address']
-if not config.exists(base_path):
- # Nothing to do
- sys.exit(0)
-
-# Migrate "service dns dynamic timeout ..."
-# to "service dns dynamic interval ..."
-if config.exists(timeout_path):
- config.rename(timeout_path, 'interval')
-
-# Remove "service dns dynamic address <interface> web-options ..." when <interface> != "web"
-for address in config.list_nodes(address_path):
- if config.exists(address_path + [address, 'web-options']) and address != 'web':
- config.delete(address_path + [address, 'web-options'])
-
-# Migrate "service dns dynamic address <interface> service <service> protocol dnsexit"
-# to "service dns dynamic address <interface> service <service> protocol dnsexit2"
-for address in config.list_nodes(address_path):
- for svc_cfg in config.list_nodes(address_path + [address, 'service']):
- if config.exists(address_path + [address, 'service', svc_cfg, 'protocol']):
- protocol = config.return_value(address_path + [address, 'service', svc_cfg, 'protocol'])
- if protocol == 'dnsexit':
- config.set(address_path + [address, 'service', svc_cfg, 'protocol'], 'dnsexit2')
-
-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))
- sys.exit(1)
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(base_path):
+ # Nothing to do
+ return
+
+ # Migrate "service dns dynamic timeout ..."
+ # to "service dns dynamic interval ..."
+ if config.exists(timeout_path):
+ config.rename(timeout_path, 'interval')
+
+ # Remove "service dns dynamic address <interface> web-options ..." when <interface> != "web"
+ for address in config.list_nodes(address_path):
+ if config.exists(address_path + [address, 'web-options']) and address != 'web':
+ config.delete(address_path + [address, 'web-options'])
+
+ # Migrate "service dns dynamic address <interface> service <service> protocol dnsexit"
+ # to "service dns dynamic address <interface> service <service> protocol dnsexit2"
+ for address in config.list_nodes(address_path):
+ for svc_cfg in config.list_nodes(address_path + [address, 'service']):
+ if config.exists(address_path + [address, 'service', svc_cfg, 'protocol']):
+ protocol = config.return_value(address_path + [address, 'service', svc_cfg, 'protocol'])
+ if protocol == 'dnsexit':
+ config.set(address_path + [address, 'service', svc_cfg, 'protocol'], 'dnsexit2')
diff --git a/src/migration-scripts/dns-dynamic/2-to-3 b/src/migration-scripts/dns-dynamic/2-to-3
index 4e0aa37d5..9aafc41a4 100755..100644
--- a/src/migration-scripts/dns-dynamic/2-to-3
+++ b/src/migration-scripts/dns-dynamic/2-to-3
@@ -1,18 +1,17 @@
-#!/usr/bin/env python3
-
-# Copyright (C) 2023 VyOS maintainers and contributors
+# Copyright 2023-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License version 2 or later as
-# published by the Free Software Foundation.
+# This 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 program is distributed in the hope that it will be useful,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# T5791:
# - migrate "service dns dynamic address web web-options ..."
@@ -23,7 +22,6 @@
# to "service dns dynamic name <service> address <interface> ..."
# - normalize the all service names to conform with name constraints
-import sys
import re
from unicodedata import normalize
from vyos.configtree import ConfigTree
@@ -41,79 +39,61 @@ def normalize_name(name):
return name
-
-if len(sys.argv) < 2:
- print("Must specify file name!")
- sys.exit(1)
-
-file_name = sys.argv[1]
-
-with open(file_name, 'r') as f:
- config_file = f.read()
-
-config = ConfigTree(config_file)
-
base_path = ['service', 'dns', 'dynamic']
address_path = base_path + ['address']
name_path = base_path + ['name']
-if not config.exists(address_path):
- # Nothing to do
- sys.exit(0)
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(address_path):
+ # Nothing to do
+ return
-# config.copy does not recursively create a path, so initialize the name path as tagged node
-if not config.exists(name_path):
- config.set(name_path)
- config.set_tag(name_path)
+ # config.copy does not recursively create a path, so initialize the name path as tagged node
+ if not config.exists(name_path):
+ config.set(name_path)
+ config.set_tag(name_path)
-for address in config.list_nodes(address_path):
+ for address in config.list_nodes(address_path):
- address_path_tag = address_path + [address]
+ address_path_tag = address_path + [address]
+
+ # Move web-option as a configuration in each service instead of top level web-option
+ if config.exists(address_path_tag + ['web-options']) and address == 'web':
+ for svc_type in ['service', 'rfc2136']:
+ if config.exists(address_path_tag + [svc_type]):
+ for svc_cfg in config.list_nodes(address_path_tag + [svc_type]):
+ config.copy(address_path_tag + ['web-options'],
+ address_path_tag + [svc_type, svc_cfg, 'web-options'])
+ config.delete(address_path_tag + ['web-options'])
- # Move web-option as a configuration in each service instead of top level web-option
- if config.exists(address_path_tag + ['web-options']) and address == 'web':
for svc_type in ['service', 'rfc2136']:
if config.exists(address_path_tag + [svc_type]):
+ # Set protocol to 'nsupdate' for RFC2136 configuration
+ if svc_type == 'rfc2136':
+ for rfc_cfg in config.list_nodes(address_path_tag + ['rfc2136']):
+ config.set(address_path_tag + ['rfc2136', rfc_cfg, 'protocol'], 'nsupdate')
+
+ # Add address as config value in each service before moving the service path
+ # And then copy the services from 'address <interface> service <service>'
+ # to 'name (service|rfc2136)-<service>-<address>'
+ # Note: The new service is named (service|rfc2136)-<service>-<address>
+ # to avoid name conflict with old entries
for svc_cfg in config.list_nodes(address_path_tag + [svc_type]):
- config.copy(address_path_tag + ['web-options'],
- address_path_tag + [svc_type, svc_cfg, 'web-options'])
- config.delete(address_path_tag + ['web-options'])
-
- for svc_type in ['service', 'rfc2136']:
- if config.exists(address_path_tag + [svc_type]):
- # Set protocol to 'nsupdate' for RFC2136 configuration
- if svc_type == 'rfc2136':
- for rfc_cfg in config.list_nodes(address_path_tag + ['rfc2136']):
- config.set(address_path_tag + ['rfc2136', rfc_cfg, 'protocol'], 'nsupdate')
-
- # Add address as config value in each service before moving the service path
- # And then copy the services from 'address <interface> service <service>'
- # to 'name (service|rfc2136)-<service>-<address>'
- # Note: The new service is named (service|rfc2136)-<service>-<address>
- # to avoid name conflict with old entries
- for svc_cfg in config.list_nodes(address_path_tag + [svc_type]):
- config.set(address_path_tag + [svc_type, svc_cfg, 'address'], address)
- config.copy(address_path_tag + [svc_type, svc_cfg],
- name_path + ['-'.join([svc_type, svc_cfg, address])])
-
-# Finally cleanup the old address path
-config.delete(address_path)
-
-# Normalize the all service names to conform with name constraints
-index = 1
-for name in config.list_nodes(name_path):
- new_name = normalize_name(name)
- if new_name != name:
- # Append index if there is still a name conflicts after normalization
- # For example, "foo-?(" and "foo-!)" both normalize to "foo-"
- if config.exists(name_path + [new_name]):
- new_name = f'{new_name}-{index}'
- index += 1
- config.rename(name_path + [name], new_name)
-
-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))
- sys.exit(1)
+ config.set(address_path_tag + [svc_type, svc_cfg, 'address'], address)
+ config.copy(address_path_tag + [svc_type, svc_cfg],
+ name_path + ['-'.join([svc_type, svc_cfg, address])])
+
+ # Finally cleanup the old address path
+ config.delete(address_path)
+
+ # Normalize the all service names to conform with name constraints
+ index = 1
+ for name in config.list_nodes(name_path):
+ new_name = normalize_name(name)
+ if new_name != name:
+ # Append index if there is still a name conflicts after normalization
+ # For example, "foo-?(" and "foo-!)" both normalize to "foo-"
+ if config.exists(name_path + [new_name]):
+ new_name = f'{new_name}-{index}'
+ index += 1
+ config.rename(name_path + [name], new_name)
diff --git a/src/migration-scripts/dns-dynamic/3-to-4 b/src/migration-scripts/dns-dynamic/3-to-4
index b888a3b6b..c8e1ffeee 100755..100644
--- a/src/migration-scripts/dns-dynamic/3-to-4
+++ b/src/migration-scripts/dns-dynamic/3-to-4
@@ -1,18 +1,17 @@
-#!/usr/bin/env python3
-
-# Copyright (C) 2024 VyOS maintainers and contributors
+# Copyright 2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License version 2 or later as
-# published by the Free Software Foundation.
+# This 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 program is distributed in the hope that it will be useful,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# T5966:
# - migrate "service dns dynamic name <service> address <interface>"
@@ -22,55 +21,37 @@
# to "service dns dynamic name <service> address web ..."
# when <interface> == 'web'
-import sys
from vyos.configtree import ConfigTree
-if len(sys.argv) < 2:
- print("Must specify file name!")
- sys.exit(1)
-
-file_name = sys.argv[1]
-
-with open(file_name, 'r') as f:
- config_file = f.read()
-
-config = ConfigTree(config_file)
-
base_path = ['service', 'dns', 'dynamic', 'name']
-if not config.exists(base_path):
- # Nothing to do
- sys.exit(0)
-
-for service in config.list_nodes(base_path):
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(base_path):
+ # Nothing to do
+ return
- service_path = base_path + [service]
+ for service in config.list_nodes(base_path):
- if config.exists(service_path + ['address']):
- address = config.return_value(service_path + ['address'])
- # 'address' is not a leaf node anymore, delete it first
- config.delete(service_path + ['address'])
+ service_path = base_path + [service]
- # When address is an interface (not 'web'), move it to 'address interface'
- if address != 'web':
- config.set(service_path + ['address', 'interface'], address)
+ if config.exists(service_path + ['address']):
+ address = config.return_value(service_path + ['address'])
+ # 'address' is not a leaf node anymore, delete it first
+ config.delete(service_path + ['address'])
- else: # address == 'web'
- # Relocate optional 'web-options' directly under 'address web'
- if config.exists(service_path + ['web-options']):
- # config.copy does not recursively create a path, so initialize it
- config.set(service_path + ['address'])
- config.copy(service_path + ['web-options'],
- service_path + ['address', 'web'])
- config.delete(service_path + ['web-options'])
+ # When address is an interface (not 'web'), move it to 'address interface'
+ if address != 'web':
+ config.set(service_path + ['address', 'interface'], address)
- # ensure that valueless 'address web' still exists even if there are no 'web-options'
- if not config.exists(service_path + ['address', 'web']):
- config.set(service_path + ['address', 'web'])
+ else: # address == 'web'
+ # Relocate optional 'web-options' directly under 'address web'
+ if config.exists(service_path + ['web-options']):
+ # config.copy does not recursively create a path, so initialize it
+ config.set(service_path + ['address'])
+ config.copy(service_path + ['web-options'],
+ service_path + ['address', 'web'])
+ config.delete(service_path + ['web-options'])
-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))
- sys.exit(1)
+ # ensure that valueless 'address web' still exists even if there are no 'web-options'
+ if not config.exists(service_path + ['address', 'web']):
+ config.set(service_path + ['address', 'web'])
diff --git a/src/migration-scripts/dns-forwarding/0-to-1 b/src/migration-scripts/dns-forwarding/0-to-1
index 7f4343652..264ffb40d 100755..100644
--- a/src/migration-scripts/dns-forwarding/0-to-1
+++ b/src/migration-scripts/dns-forwarding/0-to-1
@@ -1,50 +1,31 @@
-#!/usr/bin/env python3
+# Copyright 2019-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2019 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 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,
+# 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 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/>.
+# 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, see <http://www.gnu.org/licenses/>.
# This migration script will check if there is a allow-from directive configured
# for the dns forwarding service - if not, the node will be created with the old
# default values of 0.0.0.0/0 and ::/0
-import sys
from vyos.configtree import ConfigTree
-if len(sys.argv) < 2:
- print("Must specify file name!")
- sys.exit(1)
-
-file_name = sys.argv[1]
-
-with open(file_name, 'r') as f:
- config_file = f.read()
+base = ['service', 'dns', 'forwarding']
-config = ConfigTree(config_file)
+def migrate(config: ConfigTree)-> None:
+ if not config.exists(base):
+ # Nothing to do
+ return
-base = ['service', 'dns', 'forwarding']
-if not config.exists(base):
- # Nothing to do
- sys.exit(0)
-else:
if not config.exists(base + ['allow-from']):
config.set(base + ['allow-from'], value='0.0.0.0/0', replace=False)
config.set(base + ['allow-from'], value='::/0', replace=False)
-
- 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))
- sys.exit(1)
diff --git a/src/migration-scripts/dns-forwarding/1-to-2 b/src/migration-scripts/dns-forwarding/1-to-2
index 7df2d47e2..15ed1e136 100755..100644
--- a/src/migration-scripts/dns-forwarding/1-to-2
+++ b/src/migration-scripts/dns-forwarding/1-to-2
@@ -1,19 +1,17 @@
-#!/usr/bin/env python3
+# Copyright 2019-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2019 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 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,
+# 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 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/>.
+# 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, see <http://www.gnu.org/licenses/>.
# This migration script will remove the deprecated 'listen-on' statement
# from the dns forwarding service and will add the corresponding
@@ -21,66 +19,49 @@
# on interface addresses and not on interface names.
from ipaddress import ip_interface
-from sys import argv, exit
from vyos.ifconfig import Interface
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', 'dns', 'forwarding']
-if not config.exists(base + ['listen-on']):
- # Nothing to do
- exit(0)
-listen_intf = config.return_values(base + ['listen-on'])
-# Delete node with abandoned command
-config.delete(base + ['listen-on'])
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(base + ['listen-on']):
+ # Nothing to do
+ return
-# retrieve interface addresses for every configured listen-on interface
-listen_addr = []
-for intf in listen_intf:
- # we need to evaluate the interface section before manipulating the 'intf' variable
- section = Interface.section(intf)
- if not section:
- raise ValueError(f'Invalid interface name {intf}')
+ listen_intf = config.return_values(base + ['listen-on'])
+ # Delete node with abandoned command
+ config.delete(base + ['listen-on'])
- # we need to treat vif and vif-s interfaces differently,
- # both "real interfaces" use dots for vlan identifiers - those
- # need to be exchanged with vif and vif-s identifiers
- if intf.count('.') == 1:
- # this is a regular VLAN interface
- intf = intf.split('.')[0] + ' vif ' + intf.split('.')[1]
- elif intf.count('.') == 2:
- # this is a QinQ VLAN interface
- intf = intf.split('.')[0] + ' vif-s ' + intf.split('.')[1] + ' vif-c ' + intf.split('.')[2]
+ # retrieve interface addresses for every configured listen-on interface
+ listen_addr = []
+ for intf in listen_intf:
+ # we need to evaluate the interface section before manipulating the 'intf' variable
+ section = Interface.section(intf)
+ if not section:
+ raise ValueError(f'Invalid interface name {intf}')
- # retrieve corresponding interface addresses in CIDR format
- # those need to be converted in pure IP addresses without network information
- path = ['interfaces', section, intf, 'address']
- try:
- for addr in config.return_values(path):
- listen_addr.append( ip_interface(addr).ip )
- except:
- # Some interface types do not use "address" option (e.g. OpenVPN)
- # and may not even have a fixed address
- print("Could not retrieve the address of the interface {} from the config".format(intf))
- print("You will need to update your DNS forwarding configuration manually")
+ # we need to treat vif and vif-s interfaces differently,
+ # both "real interfaces" use dots for vlan identifiers - those
+ # need to be exchanged with vif and vif-s identifiers
+ if intf.count('.') == 1:
+ # this is a regular VLAN interface
+ intf = intf.split('.')[0] + ' vif ' + intf.split('.')[1]
+ elif intf.count('.') == 2:
+ # this is a QinQ VLAN interface
+ intf = intf.split('.')[0] + ' vif-s ' + intf.split('.')[1] + ' vif-c ' + intf.split('.')[2]
-for addr in listen_addr:
- config.set(base + ['listen-address'], value=addr, replace=False)
+ # retrieve corresponding interface addresses in CIDR format
+ # those need to be converted in pure IP addresses without network information
+ path = ['interfaces', section, intf, 'address']
+ try:
+ for addr in config.return_values(path):
+ listen_addr.append( ip_interface(addr).ip )
+ except:
+ # Some interface types do not use "address" option (e.g. OpenVPN)
+ # and may not even have a fixed address
+ print("Could not retrieve the address of the interface {} from the config".format(intf))
+ print("You will need to update your DNS forwarding configuration manually")
-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)
+ for addr in listen_addr:
+ config.set(base + ['listen-address'], value=addr, replace=False)
diff --git a/src/migration-scripts/dns-forwarding/2-to-3 b/src/migration-scripts/dns-forwarding/2-to-3
index d7ff9e260..729c1f00a 100755..100644
--- a/src/migration-scripts/dns-forwarding/2-to-3
+++ b/src/migration-scripts/dns-forwarding/2-to-3
@@ -1,51 +1,32 @@
-#!/usr/bin/env python3
+# Copyright 2020-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# 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 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,
+# 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 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/>.
+# 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, see <http://www.gnu.org/licenses/>.
# Sets the new options "addnta" and "recursion-desired" for all
# 'dns forwarding domain' as this is usually desired
-import sys
from vyos.configtree import ConfigTree
-if len(sys.argv) < 2:
- print("Must specify file name!")
- sys.exit(1)
-
-file_name = sys.argv[1]
-
-with open(file_name, 'r') as f:
- config_file = f.read()
-
-config = ConfigTree(config_file)
-
base = ['service', 'dns', 'forwarding']
-if not config.exists(base):
- # Nothing to do
- sys.exit(0)
-if config.exists(base + ['domain']):
- for domain in config.list_nodes(base + ['domain']):
- domain_base = base + ['domain', domain]
- config.set(domain_base + ['addnta'])
- config.set(domain_base + ['recursion-desired'])
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ # Nothing to do
+ return
- 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))
- sys.exit(1)
+ if config.exists(base + ['domain']):
+ for domain in config.list_nodes(base + ['domain']):
+ domain_base = base + ['domain', domain]
+ config.set(domain_base + ['addnta'])
+ config.set(domain_base + ['recursion-desired'])
diff --git a/src/migration-scripts/dns-forwarding/3-to-4 b/src/migration-scripts/dns-forwarding/3-to-4
index 3d5316ed4..b02c0b7ca 100755..100644
--- a/src/migration-scripts/dns-forwarding/3-to-4
+++ b/src/migration-scripts/dns-forwarding/3-to-4
@@ -1,49 +1,31 @@
-#!/usr/bin/env python3
-
-# Copyright (C) 2023 VyOS maintainers and contributors
+# Copyright 2023-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License version 2 or later as
-# published by the Free Software Foundation.
+# This 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 program is distributed in the hope that it will be useful,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# T5115: migrate "service dns forwarding domain example.com server" to
# "service dns forwarding domain example.com name-server"
-import sys
from vyos.configtree import ConfigTree
-if len(sys.argv) < 2:
- print("Must specify file name!")
- sys.exit(1)
-
-file_name = sys.argv[1]
-
-with open(file_name, 'r') as f:
- config_file = f.read()
-
-config = ConfigTree(config_file)
-
base = ['service', 'dns', 'forwarding', 'domain']
-if not config.exists(base):
- # Nothing to do
- sys.exit(0)
-for domain in config.list_nodes(base):
- if config.exists(base + [domain, 'server']):
- config.copy(base + [domain, 'server'], base + [domain, 'name-server'])
- config.delete(base + [domain, 'server'])
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ # Nothing to do
+ return
-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))
- sys.exit(1)
+ for domain in config.list_nodes(base):
+ if config.exists(base + [domain, 'server']):
+ config.copy(base + [domain, 'server'], base + [domain, 'name-server'])
+ config.delete(base + [domain, 'server'])
diff --git a/src/migration-scripts/firewall/10-to-11 b/src/migration-scripts/firewall/10-to-11
index 854d5a558..70a170940 100755..100644
--- a/src/migration-scripts/firewall/10-to-11
+++ b/src/migration-scripts/firewall/10-to-11
@@ -1,18 +1,17 @@
-#!/usr/bin/env python3
+# Copyright 2023-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2023-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; either
+# version 2.1 of the License, or (at your option) any later version.
#
-# 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# T5160: Firewall re-writing
@@ -37,171 +36,152 @@
# 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>
-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 = ['firewall']
-config = ConfigTree(config_file)
-
-if not config.exists(base):
- # Nothing to do
- exit(0)
-
-### Migration of state policies
-if config.exists(base + ['state-policy']):
- for state in config.list_nodes(base + ['state-policy']):
- action = config.return_value(base + ['state-policy', state, 'action'])
- config.set(base + ['global-options', 'state-policy', state, 'action'], value=action)
- if config.exists(base + ['state-policy', state, 'log']):
- config.set(base + ['global-options', 'state-policy', state, 'log'], value='enable')
- config.delete(base + ['state-policy'])
-
-## migration of global options:
-for option in ['all-ping', 'broadcast-ping', 'config-trap', 'ip-src-route', 'ipv6-receive-redirects', 'ipv6-src-route', 'log-martians',
- 'receive-redirects', 'resolver-cache', 'resolver-internal', 'send-redirects', 'source-validation', 'syn-cookies', 'twa-hazards-protection']:
- if config.exists(base + [option]):
- if option != 'config-trap':
- val = config.return_value(base + [option])
- config.set(base + ['global-options', option], value=val)
- config.delete(base + [option])
-
-### Migration of firewall name and ipv6-name
-### Also migrate legacy 'accept' behaviour
-if config.exists(base + ['name']):
- config.set(['firewall', 'ipv4', 'name'])
- config.set_tag(['firewall', 'ipv4', 'name'])
-
- for ipv4name in config.list_nodes(base + ['name']):
- config.copy(base + ['name', ipv4name], base + ['ipv4', 'name', ipv4name])
-
- if config.exists(base + ['ipv4', 'name', ipv4name, 'default-action']):
- action = config.return_value(base + ['ipv4', 'name', ipv4name, 'default-action'])
-
- if action == 'accept':
- config.set(base + ['ipv4', 'name', ipv4name, 'default-action'], value='return')
-
- if config.exists(base + ['ipv4', 'name', ipv4name, 'rule']):
- for rule_id in config.list_nodes(base + ['ipv4', 'name', ipv4name, 'rule']):
- action = config.return_value(base + ['ipv4', 'name', ipv4name, 'rule', rule_id, 'action'])
+
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ # Nothing to do
+ return
+
+ ### Migration of state policies
+ if config.exists(base + ['state-policy']):
+ for state in config.list_nodes(base + ['state-policy']):
+ action = config.return_value(base + ['state-policy', state, 'action'])
+ config.set(base + ['global-options', 'state-policy', state, 'action'], value=action)
+ if config.exists(base + ['state-policy', state, 'log']):
+ config.set(base + ['global-options', 'state-policy', state, 'log'], value='enable')
+ config.delete(base + ['state-policy'])
+
+ ## migration of global options:
+ for option in ['all-ping', 'broadcast-ping', 'config-trap', 'ip-src-route', 'ipv6-receive-redirects', 'ipv6-src-route', 'log-martians',
+ 'receive-redirects', 'resolver-cache', 'resolver-internal', 'send-redirects', 'source-validation', 'syn-cookies', 'twa-hazards-protection']:
+ if config.exists(base + [option]):
+ if option != 'config-trap':
+ val = config.return_value(base + [option])
+ config.set(base + ['global-options', option], value=val)
+ config.delete(base + [option])
+
+ ### Migration of firewall name and ipv6-name
+ ### Also migrate legacy 'accept' behaviour
+ if config.exists(base + ['name']):
+ config.set(['firewall', 'ipv4', 'name'])
+ config.set_tag(['firewall', 'ipv4', 'name'])
+
+ for ipv4name in config.list_nodes(base + ['name']):
+ config.copy(base + ['name', ipv4name], base + ['ipv4', 'name', ipv4name])
+
+ if config.exists(base + ['ipv4', 'name', ipv4name, 'default-action']):
+ action = config.return_value(base + ['ipv4', 'name', ipv4name, 'default-action'])
if action == 'accept':
- config.set(base + ['ipv4', 'name', ipv4name, 'rule', rule_id, 'action'], value='return')
+ config.set(base + ['ipv4', 'name', ipv4name, 'default-action'], value='return')
- config.delete(base + ['name'])
+ if config.exists(base + ['ipv4', 'name', ipv4name, 'rule']):
+ for rule_id in config.list_nodes(base + ['ipv4', 'name', ipv4name, 'rule']):
+ action = config.return_value(base + ['ipv4', 'name', ipv4name, 'rule', rule_id, 'action'])
-if config.exists(base + ['ipv6-name']):
- config.set(['firewall', 'ipv6', 'name'])
- config.set_tag(['firewall', 'ipv6', 'name'])
+ if action == 'accept':
+ config.set(base + ['ipv4', 'name', ipv4name, 'rule', rule_id, 'action'], value='return')
- for ipv6name in config.list_nodes(base + ['ipv6-name']):
- config.copy(base + ['ipv6-name', ipv6name], base + ['ipv6', 'name', ipv6name])
+ config.delete(base + ['name'])
- if config.exists(base + ['ipv6', 'name', ipv6name, 'default-action']):
- action = config.return_value(base + ['ipv6', 'name', ipv6name, 'default-action'])
+ if config.exists(base + ['ipv6-name']):
+ config.set(['firewall', 'ipv6', 'name'])
+ config.set_tag(['firewall', 'ipv6', 'name'])
- if action == 'accept':
- config.set(base + ['ipv6', 'name', ipv6name, 'default-action'], value='return')
+ for ipv6name in config.list_nodes(base + ['ipv6-name']):
+ config.copy(base + ['ipv6-name', ipv6name], base + ['ipv6', 'name', ipv6name])
- if config.exists(base + ['ipv6', 'name', ipv6name, 'rule']):
- for rule_id in config.list_nodes(base + ['ipv6', 'name', ipv6name, 'rule']):
- action = config.return_value(base + ['ipv6', 'name', ipv6name, 'rule', rule_id, 'action'])
+ if config.exists(base + ['ipv6', 'name', ipv6name, 'default-action']):
+ action = config.return_value(base + ['ipv6', 'name', ipv6name, 'default-action'])
if action == 'accept':
- config.set(base + ['ipv6', 'name', ipv6name, 'rule', rule_id, 'action'], value='return')
-
- config.delete(base + ['ipv6-name'])
-
-### Migration of firewall interface
-if config.exists(base + ['interface']):
- fwd_ipv4_rule = 5
- inp_ipv4_rule = 5
- fwd_ipv6_rule = 5
- inp_ipv6_rule = 5
- for direction in ['in', 'out', 'local']:
- for iface in config.list_nodes(base + ['interface']):
- if config.exists(base + ['interface', iface, direction]):
- if config.exists(base + ['interface', iface, direction, 'name']):
- target = config.return_value(base + ['interface', iface, direction, 'name'])
- if direction == 'in':
- # Add default-action== accept for compatibility reasons:
- config.set(base + ['ipv4', 'forward', 'filter', 'default-action'], value='accept')
- new_base = base + ['ipv4', 'forward', 'filter', 'rule']
- config.set(new_base)
- config.set_tag(new_base)
- config.set(new_base + [fwd_ipv4_rule, 'inbound-interface', 'interface-name'], value=iface)
- config.set(new_base + [fwd_ipv4_rule, 'action'], value='jump')
- config.set(new_base + [fwd_ipv4_rule, 'jump-target'], value=target)
- fwd_ipv4_rule = fwd_ipv4_rule + 5
- elif direction == 'out':
- # Add default-action== accept for compatibility reasons:
- config.set(base + ['ipv4', 'forward', 'filter', 'default-action'], value='accept')
- new_base = base + ['ipv4', 'forward', 'filter', 'rule']
- config.set(new_base)
- config.set_tag(new_base)
- config.set(new_base + [fwd_ipv4_rule, 'outbound-interface', 'interface-name'], value=iface)
- config.set(new_base + [fwd_ipv4_rule, 'action'], value='jump')
- config.set(new_base + [fwd_ipv4_rule, 'jump-target'], value=target)
- fwd_ipv4_rule = fwd_ipv4_rule + 5
- else:
- # Add default-action== accept for compatibility reasons:
- config.set(base + ['ipv4', 'input', 'filter', 'default-action'], value='accept')
- new_base = base + ['ipv4', 'input', 'filter', 'rule']
- config.set(new_base)
- config.set_tag(new_base)
- config.set(new_base + [inp_ipv4_rule, 'inbound-interface', 'interface-name'], value=iface)
- config.set(new_base + [inp_ipv4_rule, 'action'], value='jump')
- config.set(new_base + [inp_ipv4_rule, 'jump-target'], value=target)
- inp_ipv4_rule = inp_ipv4_rule + 5
-
- if config.exists(base + ['interface', iface, direction, 'ipv6-name']):
- target = config.return_value(base + ['interface', iface, direction, 'ipv6-name'])
- if direction == 'in':
- # Add default-action== accept for compatibility reasons:
- config.set(base + ['ipv6', 'forward', 'filter', 'default-action'], value='accept')
- new_base = base + ['ipv6', 'forward', 'filter', 'rule']
- config.set(new_base)
- config.set_tag(new_base)
- config.set(new_base + [fwd_ipv6_rule, 'inbound-interface', 'interface-name'], value=iface)
- config.set(new_base + [fwd_ipv6_rule, 'action'], value='jump')
- config.set(new_base + [fwd_ipv6_rule, 'jump-target'], value=target)
- fwd_ipv6_rule = fwd_ipv6_rule + 5
- elif direction == 'out':
- # Add default-action== accept for compatibility reasons:
- config.set(base + ['ipv6', 'forward', 'filter', 'default-action'], value='accept')
- new_base = base + ['ipv6', 'forward', 'filter', 'rule']
- config.set(new_base)
- config.set_tag(new_base)
- config.set(new_base + [fwd_ipv6_rule, 'outbound-interface', 'interface-name'], value=iface)
- config.set(new_base + [fwd_ipv6_rule, 'action'], value='jump')
- config.set(new_base + [fwd_ipv6_rule, 'jump-target'], value=target)
- fwd_ipv6_rule = fwd_ipv6_rule + 5
- else:
- new_base = base + ['ipv6', 'input', 'filter', 'rule']
- # Add default-action== accept for compatibility reasons:
- config.set(base + ['ipv6', 'input', 'filter', 'default-action'], value='accept')
- config.set(new_base)
- config.set_tag(new_base)
- config.set(new_base + [inp_ipv6_rule, 'inbound-interface', 'interface-name'], value=iface)
- config.set(new_base + [inp_ipv6_rule, 'action'], value='jump')
- config.set(new_base + [inp_ipv6_rule, 'jump-target'], value=target)
- inp_ipv6_rule = inp_ipv6_rule + 5
-
- config.delete(base + ['interface'])
-
-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)
+ config.set(base + ['ipv6', 'name', ipv6name, 'default-action'], value='return')
+
+ if config.exists(base + ['ipv6', 'name', ipv6name, 'rule']):
+ for rule_id in config.list_nodes(base + ['ipv6', 'name', ipv6name, 'rule']):
+ action = config.return_value(base + ['ipv6', 'name', ipv6name, 'rule', rule_id, 'action'])
+
+ if action == 'accept':
+ config.set(base + ['ipv6', 'name', ipv6name, 'rule', rule_id, 'action'], value='return')
+
+ config.delete(base + ['ipv6-name'])
+
+ ### Migration of firewall interface
+ if config.exists(base + ['interface']):
+ fwd_ipv4_rule = 5
+ inp_ipv4_rule = 5
+ fwd_ipv6_rule = 5
+ inp_ipv6_rule = 5
+ for direction in ['in', 'out', 'local']:
+ for iface in config.list_nodes(base + ['interface']):
+ if config.exists(base + ['interface', iface, direction]):
+ if config.exists(base + ['interface', iface, direction, 'name']):
+ target = config.return_value(base + ['interface', iface, direction, 'name'])
+ if direction == 'in':
+ # Add default-action== accept for compatibility reasons:
+ config.set(base + ['ipv4', 'forward', 'filter', 'default-action'], value='accept')
+ new_base = base + ['ipv4', 'forward', 'filter', 'rule']
+ config.set(new_base)
+ config.set_tag(new_base)
+ config.set(new_base + [fwd_ipv4_rule, 'inbound-interface', 'interface-name'], value=iface)
+ config.set(new_base + [fwd_ipv4_rule, 'action'], value='jump')
+ config.set(new_base + [fwd_ipv4_rule, 'jump-target'], value=target)
+ fwd_ipv4_rule = fwd_ipv4_rule + 5
+ elif direction == 'out':
+ # Add default-action== accept for compatibility reasons:
+ config.set(base + ['ipv4', 'forward', 'filter', 'default-action'], value='accept')
+ new_base = base + ['ipv4', 'forward', 'filter', 'rule']
+ config.set(new_base)
+ config.set_tag(new_base)
+ config.set(new_base + [fwd_ipv4_rule, 'outbound-interface', 'interface-name'], value=iface)
+ config.set(new_base + [fwd_ipv4_rule, 'action'], value='jump')
+ config.set(new_base + [fwd_ipv4_rule, 'jump-target'], value=target)
+ fwd_ipv4_rule = fwd_ipv4_rule + 5
+ else:
+ # Add default-action== accept for compatibility reasons:
+ config.set(base + ['ipv4', 'input', 'filter', 'default-action'], value='accept')
+ new_base = base + ['ipv4', 'input', 'filter', 'rule']
+ config.set(new_base)
+ config.set_tag(new_base)
+ config.set(new_base + [inp_ipv4_rule, 'inbound-interface', 'interface-name'], value=iface)
+ config.set(new_base + [inp_ipv4_rule, 'action'], value='jump')
+ config.set(new_base + [inp_ipv4_rule, 'jump-target'], value=target)
+ inp_ipv4_rule = inp_ipv4_rule + 5
+
+ if config.exists(base + ['interface', iface, direction, 'ipv6-name']):
+ target = config.return_value(base + ['interface', iface, direction, 'ipv6-name'])
+ if direction == 'in':
+ # Add default-action== accept for compatibility reasons:
+ config.set(base + ['ipv6', 'forward', 'filter', 'default-action'], value='accept')
+ new_base = base + ['ipv6', 'forward', 'filter', 'rule']
+ config.set(new_base)
+ config.set_tag(new_base)
+ config.set(new_base + [fwd_ipv6_rule, 'inbound-interface', 'interface-name'], value=iface)
+ config.set(new_base + [fwd_ipv6_rule, 'action'], value='jump')
+ config.set(new_base + [fwd_ipv6_rule, 'jump-target'], value=target)
+ fwd_ipv6_rule = fwd_ipv6_rule + 5
+ elif direction == 'out':
+ # Add default-action== accept for compatibility reasons:
+ config.set(base + ['ipv6', 'forward', 'filter', 'default-action'], value='accept')
+ new_base = base + ['ipv6', 'forward', 'filter', 'rule']
+ config.set(new_base)
+ config.set_tag(new_base)
+ config.set(new_base + [fwd_ipv6_rule, 'outbound-interface', 'interface-name'], value=iface)
+ config.set(new_base + [fwd_ipv6_rule, 'action'], value='jump')
+ config.set(new_base + [fwd_ipv6_rule, 'jump-target'], value=target)
+ fwd_ipv6_rule = fwd_ipv6_rule + 5
+ else:
+ new_base = base + ['ipv6', 'input', 'filter', 'rule']
+ # Add default-action== accept for compatibility reasons:
+ config.set(base + ['ipv6', 'input', 'filter', 'default-action'], value='accept')
+ config.set(new_base)
+ config.set_tag(new_base)
+ config.set(new_base + [inp_ipv6_rule, 'inbound-interface', 'interface-name'], value=iface)
+ config.set(new_base + [inp_ipv6_rule, 'action'], value='jump')
+ config.set(new_base + [inp_ipv6_rule, 'jump-target'], value=target)
+ inp_ipv6_rule = inp_ipv6_rule + 5
+
+ config.delete(base + ['interface'])
diff --git a/src/migration-scripts/firewall/11-to-12 b/src/migration-scripts/firewall/11-to-12
index f9122e74c..80a74cca9 100755..100644
--- a/src/migration-scripts/firewall/11-to-12
+++ b/src/migration-scripts/firewall/11-to-12
@@ -1,18 +1,17 @@
-#!/usr/bin/env python3
+# Copyright 2023-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2023-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; either
+# version 2.1 of the License, or (at your option) any later version.
#
-# 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# T5681: Firewall re-writing. Simplify cli when mathcing interface
# From
@@ -22,50 +21,31 @@
# set firewall ... rule <rule> [inbound-interface | outboubd-interface] name <iface>
# set firewall ... rule <rule> [inbound-interface | outboubd-interface] group <iface_group>
-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 = ['firewall']
-config = ConfigTree(config_file)
-
-if not config.exists(base):
- # Nothing to do
- exit(0)
-
-## Migration from base chains
-#if config.exists(base + ['interface', iface, direction]):
-for family in ['ipv4', 'ipv6']:
- if config.exists(base + [family]):
- for hook in ['forward', 'input', 'output', 'name']:
- if config.exists(base + [family, hook]):
- for priority in config.list_nodes(base + [family, hook]):
- if config.exists(base + [family, hook, priority, 'rule']):
- for rule in config.list_nodes(base + [family, hook, priority, 'rule']):
- for direction in ['inbound-interface', 'outbound-interface']:
- if config.exists(base + [family, hook, priority, 'rule', rule, direction]):
- if config.exists(base + [family, hook, priority, 'rule', rule, direction, 'interface-name']):
- iface = config.return_value(base + [family, hook, priority, 'rule', rule, direction, 'interface-name'])
- config.set(base + [family, hook, priority, 'rule', rule, direction, 'name'], value=iface)
- config.delete(base + [family, hook, priority, 'rule', rule, direction, 'interface-name'])
- elif config.exists(base + [family, hook, priority, 'rule', rule, direction, 'interface-group']):
- group = config.return_value(base + [family, hook, priority, 'rule', rule, direction, 'interface-group'])
- config.set(base + [family, hook, priority, 'rule', rule, direction, 'group'], value=group)
- config.delete(base + [family, hook, priority, 'rule', rule, direction, 'interface-group'])
-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 migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ # Nothing to do
+ return
+
+ ## Migration from base chains
+ #if config.exists(base + ['interface', iface, direction]):
+ for family in ['ipv4', 'ipv6']:
+ if config.exists(base + [family]):
+ for hook in ['forward', 'input', 'output', 'name']:
+ if config.exists(base + [family, hook]):
+ for priority in config.list_nodes(base + [family, hook]):
+ if config.exists(base + [family, hook, priority, 'rule']):
+ for rule in config.list_nodes(base + [family, hook, priority, 'rule']):
+ for direction in ['inbound-interface', 'outbound-interface']:
+ if config.exists(base + [family, hook, priority, 'rule', rule, direction]):
+ if config.exists(base + [family, hook, priority, 'rule', rule, direction, 'interface-name']):
+ iface = config.return_value(base + [family, hook, priority, 'rule', rule, direction, 'interface-name'])
+ config.set(base + [family, hook, priority, 'rule', rule, direction, 'name'], value=iface)
+ config.delete(base + [family, hook, priority, 'rule', rule, direction, 'interface-name'])
+ elif config.exists(base + [family, hook, priority, 'rule', rule, direction, 'interface-group']):
+ group = config.return_value(base + [family, hook, priority, 'rule', rule, direction, 'interface-group'])
+ config.set(base + [family, hook, priority, 'rule', rule, direction, 'group'], value=group)
+ config.delete(base + [family, hook, priority, 'rule', rule, direction, 'interface-group'])
diff --git a/src/migration-scripts/firewall/12-to-13 b/src/migration-scripts/firewall/12-to-13
index d72ba834d..d7b801cd3 100755..100644
--- a/src/migration-scripts/firewall/12-to-13
+++ b/src/migration-scripts/firewall/12-to-13
@@ -1,18 +1,17 @@
-#!/usr/bin/env python3
+# Copyright 2023-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2023-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; either
+# version 2.1 of the License, or (at your option) any later version.
#
-# 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# T5729: Switch to valueless whenever is possible.
# From
@@ -25,65 +24,46 @@
# set firewall ... rule <rule> state <state>
# Remove command if log=disable or <state>=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()
-
base = ['firewall']
-config = ConfigTree(config_file)
-
-if not config.exists(base):
- # Nothing to do
- exit(0)
-# State Policy logs:
-if config.exists(base + ['global-options', 'state-policy']):
- for state in config.list_nodes(base + ['global-options', 'state-policy']):
- if config.exists(base + ['global-options', 'state-policy', state, 'log']):
- log_value = config.return_value(base + ['global-options', 'state-policy', state, 'log'])
- config.delete(base + ['global-options', 'state-policy', state, 'log'])
- if log_value == 'enable':
- config.set(base + ['global-options', 'state-policy', state, 'log'])
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ # Nothing to do
+ return
-for family in ['ipv4', 'ipv6', 'bridge']:
- if config.exists(base + [family]):
- for hook in ['forward', 'input', 'output', 'name']:
- if config.exists(base + [family, hook]):
- for priority in config.list_nodes(base + [family, hook]):
- if config.exists(base + [family, hook, priority, 'rule']):
- for rule in config.list_nodes(base + [family, hook, priority, 'rule']):
- # Log
- if config.exists(base + [family, hook, priority, 'rule', rule, 'log']):
- log_value = config.return_value(base + [family, hook, priority, 'rule', rule, 'log'])
- config.delete(base + [family, hook, priority, 'rule', rule, 'log'])
- if log_value == 'enable':
- config.set(base + [family, hook, priority, 'rule', rule, 'log'])
- # State
- if config.exists(base + [family, hook, priority, 'rule', rule, 'state']):
- flag_enable = 'False'
- for state in ['established', 'invalid', 'new', 'related']:
- if config.exists(base + [family, hook, priority, 'rule', rule, 'state', state]):
- state_value = config.return_value(base + [family, hook, priority, 'rule', rule, 'state', state])
- config.delete(base + [family, hook, priority, 'rule', rule, 'state', state])
- if state_value == 'enable':
- config.set(base + [family, hook, priority, 'rule', rule, 'state'], value=state, replace=False)
- flag_enable = 'True'
- if flag_enable == 'False':
- config.delete(base + [family, hook, priority, 'rule', rule, 'state'])
+ # State Policy logs:
+ if config.exists(base + ['global-options', 'state-policy']):
+ for state in config.list_nodes(base + ['global-options', 'state-policy']):
+ if config.exists(base + ['global-options', 'state-policy', state, 'log']):
+ log_value = config.return_value(base + ['global-options', 'state-policy', state, 'log'])
+ config.delete(base + ['global-options', 'state-policy', state, 'log'])
+ if log_value == 'enable':
+ config.set(base + ['global-options', 'state-policy', state, 'log'])
-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)
+ for family in ['ipv4', 'ipv6', 'bridge']:
+ if config.exists(base + [family]):
+ for hook in ['forward', 'input', 'output', 'name']:
+ if config.exists(base + [family, hook]):
+ for priority in config.list_nodes(base + [family, hook]):
+ if config.exists(base + [family, hook, priority, 'rule']):
+ for rule in config.list_nodes(base + [family, hook, priority, 'rule']):
+ # Log
+ if config.exists(base + [family, hook, priority, 'rule', rule, 'log']):
+ log_value = config.return_value(base + [family, hook, priority, 'rule', rule, 'log'])
+ config.delete(base + [family, hook, priority, 'rule', rule, 'log'])
+ if log_value == 'enable':
+ config.set(base + [family, hook, priority, 'rule', rule, 'log'])
+ # State
+ if config.exists(base + [family, hook, priority, 'rule', rule, 'state']):
+ flag_enable = 'False'
+ for state in ['established', 'invalid', 'new', 'related']:
+ if config.exists(base + [family, hook, priority, 'rule', rule, 'state', state]):
+ state_value = config.return_value(base + [family, hook, priority, 'rule', rule, 'state', state])
+ config.delete(base + [family, hook, priority, 'rule', rule, 'state', state])
+ if state_value == 'enable':
+ config.set(base + [family, hook, priority, 'rule', rule, 'state'], value=state, replace=False)
+ flag_enable = 'True'
+ if flag_enable == 'False':
+ config.delete(base + [family, hook, priority, 'rule', rule, 'state'])
diff --git a/src/migration-scripts/firewall/13-to-14 b/src/migration-scripts/firewall/13-to-14
index f45ff0674..723b0aea2 100755..100644
--- a/src/migration-scripts/firewall/13-to-14
+++ b/src/migration-scripts/firewall/13-to-14
@@ -1,18 +1,17 @@
-#!/usr/bin/env python3
+# Copyright 2023-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2023 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 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# T5834: Rename 'enable-default-log' to 'default-log'
# From
@@ -22,38 +21,19 @@
# set firewall ... filter default-log
# set firewall ... name <name> default-log
-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 = ['firewall']
-config = ConfigTree(config_file)
-
-if not config.exists(base):
- # Nothing to do
- exit(0)
-
-for family in ['ipv4', 'ipv6', 'bridge']:
- if config.exists(base + [family]):
- for hook in ['forward', 'input', 'output', 'name']:
- if config.exists(base + [family, hook]):
- for priority in config.list_nodes(base + [family, hook]):
- if config.exists(base + [family, hook, priority, 'enable-default-log']):
- config.rename(base + [family, hook, priority, 'enable-default-log'], 'default-log')
-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 migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ # Nothing to do
+ return
+
+ for family in ['ipv4', 'ipv6', 'bridge']:
+ if config.exists(base + [family]):
+ for hook in ['forward', 'input', 'output', 'name']:
+ if config.exists(base + [family, hook]):
+ for priority in config.list_nodes(base + [family, hook]):
+ if config.exists(base + [family, hook, priority, 'enable-default-log']):
+ config.rename(base + [family, hook, priority, 'enable-default-log'], 'default-log')
diff --git a/src/migration-scripts/firewall/14-to-15 b/src/migration-scripts/firewall/14-to-15
index 735839365..e4a2aaee4 100755..100644
--- a/src/migration-scripts/firewall/14-to-15
+++ b/src/migration-scripts/firewall/14-to-15
@@ -1,46 +1,25 @@
-#!/usr/bin/env python3
+# Copyright 2024 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
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
#
-# 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# 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
+def migrate(config: ConfigTree) -> None:
+ if config.exists(['system', 'ip', 'disable-directed-broadcast']):
+ config.set(['firewall', 'global-options', 'directed-broadcast'], value='disable')
+ config.delete(['system', 'ip', 'disable-directed-broadcast'])
diff --git a/src/migration-scripts/firewall/15-to-16 b/src/migration-scripts/firewall/15-to-16
index 28df1256e..8e28bba6f 100755..100644
--- a/src/migration-scripts/firewall/15-to-16
+++ b/src/migration-scripts/firewall/15-to-16
@@ -18,39 +18,20 @@
# from: set system conntrack timeout ..
# to: set firewall global-options timeout ...
-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()
-
firewall_base = ['firewall', 'global-options']
conntrack_base = ['system', 'conntrack', 'timeout']
-config = ConfigTree(config_file)
-
-if not config.exists(conntrack_base):
- # Nothing to do
- exit(0)
-for protocol in ['icmp', 'tcp', 'udp', 'other']:
- if config.exists(conntrack_base + [protocol]):
- if not config.exists(firewall_base + ['timeout']):
- config.set(firewall_base + ['timeout'])
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(conntrack_base):
+ # Nothing to do
+ return
- config.copy(conntrack_base + [protocol], firewall_base + ['timeout', protocol])
- config.delete(conntrack_base + [protocol])
+ for protocol in ['icmp', 'tcp', 'udp', 'other']:
+ if config.exists(conntrack_base + [protocol]):
+ if not config.exists(firewall_base + ['timeout']):
+ config.set(firewall_base + ['timeout'])
-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)
+ config.copy(conntrack_base + [protocol], firewall_base + ['timeout', protocol])
+ config.delete(conntrack_base + [protocol])
diff --git a/src/migration-scripts/firewall/5-to-6 b/src/migration-scripts/firewall/5-to-6
index e1eaea7a1..d01684787 100755..100644
--- a/src/migration-scripts/firewall/5-to-6
+++ b/src/migration-scripts/firewall/5-to-6
@@ -1,105 +1,85 @@
-#!/usr/bin/env python3
+# Copyright 2021-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2021 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 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# T3090: migrate "firewall options interface <name> adjust-mss" to the
# individual interface.
-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!")
- exit(1)
-
-file_name = argv[1]
-
-with open(file_name, 'r') as f:
- config_file = f.read()
-
base = ['firewall', 'options', 'interface']
-config = ConfigTree(config_file)
-
-if not config.exists(base):
- # Nothing to do
- exit(0)
-
-for interface in config.list_nodes(base):
- if config.exists(base + [interface, 'disable']):
- continue
- if config.exists(base + [interface, 'adjust-mss']):
- section = Section.section(interface)
- tmp = config.return_value(base + [interface, 'adjust-mss'])
-
- vlan = interface.split('.')
- base_interface_path = ['interfaces', section, vlan[0]]
-
- if len(vlan) == 1:
- # Normal interface, no VLAN
- config.set(base_interface_path + ['ip', 'adjust-mss'], value=tmp)
- elif len(vlan) == 2:
- # Regular VIF or VIF-S interface - we need to check the config
- vif = vlan[1]
- if config.exists(base_interface_path + ['vif', vif]):
- config.set(base_interface_path + ['vif', vif, 'ip', 'adjust-mss'], value=tmp)
- elif config.exists(base_interface_path + ['vif-s', vif]):
- config.set(base_interface_path + ['vif-s', vif, 'ip', 'adjust-mss'], value=tmp)
- elif len(vlan) == 3:
- # VIF-S interface with VIF-C subinterface
- vif_s = vlan[1]
- vif_c = vlan[2]
- config.set(base_interface_path + ['vif-s', vif_s, 'vif-c', vif_c, 'ip', 'adjust-mss'], value=tmp)
- config.set_tag(base_interface_path + ['vif-s'])
- config.set_tag(base_interface_path + ['vif-s', vif_s, 'vif-c'])
-
- if config.exists(base + [interface, 'adjust-mss6']):
- section = Section.section(interface)
- tmp = config.return_value(base + [interface, 'adjust-mss6'])
-
- vlan = interface.split('.')
- base_interface_path = ['interfaces', section, vlan[0]]
-
- if len(vlan) == 1:
- # Normal interface, no VLAN
- config.set(['interfaces', section, interface, 'ipv6', 'adjust-mss'], value=tmp)
- elif len(vlan) == 2:
- # Regular VIF or VIF-S interface - we need to check the config
- vif = vlan[1]
- if config.exists(base_interface_path + ['vif', vif]):
- config.set(base_interface_path + ['vif', vif, 'ipv6', 'adjust-mss'], value=tmp)
- config.set_tag(base_interface_path + ['vif'])
- elif config.exists(base_interface_path + ['vif-s', vif]):
- config.set(base_interface_path + ['vif-s', vif, 'ipv6', 'adjust-mss'], value=tmp)
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ # Nothing to do
+ return
+
+ for interface in config.list_nodes(base):
+ if config.exists(base + [interface, 'disable']):
+ continue
+
+ if config.exists(base + [interface, 'adjust-mss']):
+ section = Section.section(interface)
+ tmp = config.return_value(base + [interface, 'adjust-mss'])
+
+ vlan = interface.split('.')
+ base_interface_path = ['interfaces', section, vlan[0]]
+
+ if len(vlan) == 1:
+ # Normal interface, no VLAN
+ config.set(base_interface_path + ['ip', 'adjust-mss'], value=tmp)
+ elif len(vlan) == 2:
+ # Regular VIF or VIF-S interface - we need to check the config
+ vif = vlan[1]
+ if config.exists(base_interface_path + ['vif', vif]):
+ config.set(base_interface_path + ['vif', vif, 'ip', 'adjust-mss'], value=tmp)
+ elif config.exists(base_interface_path + ['vif-s', vif]):
+ config.set(base_interface_path + ['vif-s', vif, 'ip', 'adjust-mss'], value=tmp)
+ elif len(vlan) == 3:
+ # VIF-S interface with VIF-C subinterface
+ vif_s = vlan[1]
+ vif_c = vlan[2]
+ config.set(base_interface_path + ['vif-s', vif_s, 'vif-c', vif_c, 'ip', 'adjust-mss'], value=tmp)
config.set_tag(base_interface_path + ['vif-s'])
- elif len(vlan) == 3:
- # VIF-S interface with VIF-C subinterface
- vif_s = vlan[1]
- vif_c = vlan[2]
- config.set(base_interface_path + ['vif-s', vif_s, 'vif-c', vif_c, 'ipv6', 'adjust-mss'], value=tmp)
- config.set_tag(base_interface_path + ['vif-s'])
- config.set_tag(base_interface_path + ['vif-s', vif_s, 'vif-c'])
-
-config.delete(['firewall', 'options'])
+ config.set_tag(base_interface_path + ['vif-s', vif_s, 'vif-c'])
+
+ if config.exists(base + [interface, 'adjust-mss6']):
+ section = Section.section(interface)
+ tmp = config.return_value(base + [interface, 'adjust-mss6'])
+
+ vlan = interface.split('.')
+ base_interface_path = ['interfaces', section, vlan[0]]
+
+ if len(vlan) == 1:
+ # Normal interface, no VLAN
+ config.set(['interfaces', section, interface, 'ipv6', 'adjust-mss'], value=tmp)
+ elif len(vlan) == 2:
+ # Regular VIF or VIF-S interface - we need to check the config
+ vif = vlan[1]
+ if config.exists(base_interface_path + ['vif', vif]):
+ config.set(base_interface_path + ['vif', vif, 'ipv6', 'adjust-mss'], value=tmp)
+ config.set_tag(base_interface_path + ['vif'])
+ elif config.exists(base_interface_path + ['vif-s', vif]):
+ config.set(base_interface_path + ['vif-s', vif, 'ipv6', 'adjust-mss'], value=tmp)
+ config.set_tag(base_interface_path + ['vif-s'])
+ elif len(vlan) == 3:
+ # VIF-S interface with VIF-C subinterface
+ vif_s = vlan[1]
+ vif_c = vlan[2]
+ config.set(base_interface_path + ['vif-s', vif_s, 'vif-c', vif_c, 'ipv6', 'adjust-mss'], value=tmp)
+ config.set_tag(base_interface_path + ['vif-s'])
+ config.set_tag(base_interface_path + ['vif-s', vif_s, 'vif-c'])
-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)
+ config.delete(['firewall', 'options'])
diff --git a/src/migration-scripts/firewall/6-to-7 b/src/migration-scripts/firewall/6-to-7
index 938044c6d..1afbc780b 100755..100644
--- a/src/migration-scripts/firewall/6-to-7
+++ b/src/migration-scripts/firewall/6-to-7
@@ -1,18 +1,17 @@
-#!/usr/bin/env python3
+# Copyright 2021-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2021-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; either
+# version 2.1 of the License, or (at your option) any later version.
#
-# 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# T2199: Remove unavailable nodes due to XML/Python implementation using nftables
# monthdays: nftables does not have a monthdays equivalent
@@ -23,28 +22,11 @@
import re
-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()
-
max_len_description = 255
base = ['firewall']
-config = ConfigTree(config_file)
-
-if not config.exists(base):
- # Nothing to do
- exit(0)
icmp_remove = ['any']
icmp_translations = {
@@ -107,216 +89,216 @@ 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]):
- name_description = base + ['group', group_type, group_name, 'description']
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ # Nothing to do
+ return
+
+ v4_found = False
+ v6_found = False
+ 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]):
+ name_description = base + ['group', group_type, group_name, 'description']
+ 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']):
+ name_description = base + ['name', name, 'description']
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']):
- name_description = base + ['name', name, 'description']
- if config.exists(name_description):
- tmp = config.return_value(name_description)
- config.set(name_description, value=tmp[:max_len_description])
-
- if not config.exists(base + ['name', name, 'rule']):
- continue
-
- for rule in config.list_nodes(base + ['name', name, 'rule']):
- rule_description = base + ['name', name, 'rule', rule, 'description']
- if config.exists(rule_description):
- tmp = config.return_value(rule_description)
- config.set(rule_description, value=tmp[:max_len_description])
-
- rule_recent = base + ['name', name, 'rule', rule, 'recent']
- rule_time = base + ['name', name, 'rule', rule, 'time']
- rule_tcp_flags = base + ['name', name, 'rule', rule, 'tcp', 'flags']
- rule_icmp = base + ['name', name, 'rule', rule, 'icmp']
-
- if config.exists(rule_time + ['monthdays']):
- config.delete(rule_time + ['monthdays'])
-
- if config.exists(rule_time + ['utc']):
- config.delete(rule_time + ['utc'])
-
- if config.exists(rule_recent + ['time']):
- tmp = int(config.return_value(rule_recent + ['time']))
- unit = 'minute'
- if tmp > 600:
- unit = 'hour'
- elif tmp < 10:
- unit = 'second'
- config.set(rule_recent + ['time'], value=unit)
-
- if config.exists(rule_tcp_flags):
- tmp = config.return_value(rule_tcp_flags)
- config.delete(rule_tcp_flags)
- for flag in tmp.split(","):
- if flag[0] == '!':
- config.set(rule_tcp_flags + ['not', flag[1:].lower()])
- else:
- config.set(rule_tcp_flags + [flag.lower()])
-
- if config.exists(rule_icmp + ['type-name']):
- tmp = config.return_value(rule_icmp + ['type-name'])
- if tmp in icmp_remove:
- config.delete(rule_icmp + ['type-name'])
- elif tmp in icmp_translations:
- translate = icmp_translations[tmp]
- if isinstance(translate, str):
- config.set(rule_icmp + ['type-name'], value=translate)
- elif isinstance(translate, list):
+
+ if not config.exists(base + ['name', name, 'rule']):
+ continue
+
+ for rule in config.list_nodes(base + ['name', name, 'rule']):
+ rule_description = base + ['name', name, 'rule', rule, 'description']
+ if config.exists(rule_description):
+ tmp = config.return_value(rule_description)
+ config.set(rule_description, value=tmp[:max_len_description])
+
+ rule_recent = base + ['name', name, 'rule', rule, 'recent']
+ rule_time = base + ['name', name, 'rule', rule, 'time']
+ rule_tcp_flags = base + ['name', name, 'rule', rule, 'tcp', 'flags']
+ rule_icmp = base + ['name', name, 'rule', rule, 'icmp']
+
+ if config.exists(rule_time + ['monthdays']):
+ config.delete(rule_time + ['monthdays'])
+
+ if config.exists(rule_time + ['utc']):
+ config.delete(rule_time + ['utc'])
+
+ if config.exists(rule_recent + ['time']):
+ tmp = int(config.return_value(rule_recent + ['time']))
+ unit = 'minute'
+ if tmp > 600:
+ unit = 'hour'
+ elif tmp < 10:
+ unit = 'second'
+ config.set(rule_recent + ['time'], value=unit)
+
+ if config.exists(rule_tcp_flags):
+ tmp = config.return_value(rule_tcp_flags)
+ config.delete(rule_tcp_flags)
+ for flag in tmp.split(","):
+ if flag[0] == '!':
+ config.set(rule_tcp_flags + ['not', flag[1:].lower()])
+ else:
+ config.set(rule_tcp_flags + [flag.lower()])
+
+ if config.exists(rule_icmp + ['type-name']):
+ tmp = config.return_value(rule_icmp + ['type-name'])
+ if tmp in icmp_remove:
config.delete(rule_icmp + ['type-name'])
- config.set(rule_icmp + ['type'], value=translate[0])
- config.set(rule_icmp + ['code'], value=translate[1])
-
- 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 + "_"
+ elif tmp in icmp_translations:
+ translate = icmp_translations[tmp]
+ if isinstance(translate, str):
+ config.set(rule_icmp + ['type-name'], value=translate)
+ elif isinstance(translate, list):
+ config.delete(rule_icmp + ['type-name'])
+ config.set(rule_icmp + ['type'], value=translate[0])
+ config.set(rule_icmp + ['code'], value=translate[1])
+
+ 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)
- 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']):
- name_description = base + ['ipv6-name', name, 'description']
- if config.exists(name_description):
- tmp = config.return_value(name_description)
- config.set(name_description, value=tmp[:max_len_description])
-
- if not config.exists(base + ['ipv6-name', name, 'rule']):
- continue
-
- for rule in config.list_nodes(base + ['ipv6-name', name, 'rule']):
- rule_description = base + ['ipv6-name', name, 'rule', rule, 'description']
- if config.exists(rule_description):
- tmp = config.return_value(rule_description)
- config.set(rule_description, value=tmp[:max_len_description])
-
- rule_recent = base + ['ipv6-name', name, 'rule', rule, 'recent']
- rule_time = base + ['ipv6-name', name, 'rule', rule, 'time']
- rule_tcp_flags = base + ['ipv6-name', name, 'rule', rule, 'tcp', 'flags']
- rule_icmp = base + ['ipv6-name', name, 'rule', rule, 'icmpv6']
-
- if config.exists(rule_time + ['monthdays']):
- config.delete(rule_time + ['monthdays'])
-
- if config.exists(rule_time + ['utc']):
- config.delete(rule_time + ['utc'])
-
- if config.exists(rule_recent + ['time']):
- tmp = int(config.return_value(rule_recent + ['time']))
- unit = 'minute'
- if tmp > 600:
- unit = 'hour'
- elif tmp < 10:
- unit = 'second'
- config.set(rule_recent + ['time'], value=unit)
-
- if config.exists(rule_tcp_flags):
- tmp = config.return_value(rule_tcp_flags)
- config.delete(rule_tcp_flags)
- for flag in tmp.split(","):
- if flag[0] == '!':
- config.set(rule_tcp_flags + ['not', flag[1:].lower()])
- else:
- config.set(rule_tcp_flags + [flag.lower()])
-
- if config.exists(base + ['ipv6-name', name, 'rule', rule, 'protocol']):
- tmp = config.return_value(base + ['ipv6-name', name, 'rule', rule, 'protocol'])
- if tmp == 'icmpv6':
- config.set(base + ['ipv6-name', name, 'rule', rule, 'protocol'], value='ipv6-icmp')
-
- if config.exists(rule_icmp + ['type']):
- tmp = config.return_value(rule_icmp + ['type'])
- type_code_match = re.match(r'^(\d+)(?:/(\d+))?$', tmp)
-
- if type_code_match:
- config.set(rule_icmp + ['type'], value=type_code_match[1])
- if type_code_match[2]:
- config.set(rule_icmp + ['code'], value=type_code_match[2])
- elif tmp in icmpv6_remove:
- config.delete(rule_icmp + ['type'])
- elif tmp in icmpv6_translations:
- translate = icmpv6_translations[tmp]
- if isinstance(translate, str):
+ 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']):
+ name_description = base + ['ipv6-name', name, 'description']
+ if config.exists(name_description):
+ tmp = config.return_value(name_description)
+ config.set(name_description, value=tmp[:max_len_description])
+
+ if not config.exists(base + ['ipv6-name', name, 'rule']):
+ continue
+
+ for rule in config.list_nodes(base + ['ipv6-name', name, 'rule']):
+ rule_description = base + ['ipv6-name', name, 'rule', rule, 'description']
+ if config.exists(rule_description):
+ tmp = config.return_value(rule_description)
+ config.set(rule_description, value=tmp[:max_len_description])
+
+ rule_recent = base + ['ipv6-name', name, 'rule', rule, 'recent']
+ rule_time = base + ['ipv6-name', name, 'rule', rule, 'time']
+ rule_tcp_flags = base + ['ipv6-name', name, 'rule', rule, 'tcp', 'flags']
+ rule_icmp = base + ['ipv6-name', name, 'rule', rule, 'icmpv6']
+
+ if config.exists(rule_time + ['monthdays']):
+ config.delete(rule_time + ['monthdays'])
+
+ if config.exists(rule_time + ['utc']):
+ config.delete(rule_time + ['utc'])
+
+ if config.exists(rule_recent + ['time']):
+ tmp = int(config.return_value(rule_recent + ['time']))
+ unit = 'minute'
+ if tmp > 600:
+ unit = 'hour'
+ elif tmp < 10:
+ unit = 'second'
+ config.set(rule_recent + ['time'], value=unit)
+
+ if config.exists(rule_tcp_flags):
+ tmp = config.return_value(rule_tcp_flags)
+ config.delete(rule_tcp_flags)
+ for flag in tmp.split(","):
+ if flag[0] == '!':
+ config.set(rule_tcp_flags + ['not', flag[1:].lower()])
+ else:
+ config.set(rule_tcp_flags + [flag.lower()])
+
+ if config.exists(base + ['ipv6-name', name, 'rule', rule, 'protocol']):
+ tmp = config.return_value(base + ['ipv6-name', name, 'rule', rule, 'protocol'])
+ if tmp == 'icmpv6':
+ config.set(base + ['ipv6-name', name, 'rule', rule, 'protocol'], value='ipv6-icmp')
+
+ if config.exists(rule_icmp + ['type']):
+ tmp = config.return_value(rule_icmp + ['type'])
+ type_code_match = re.match(r'^(\d+)(?:/(\d+))?$', tmp)
+
+ if type_code_match:
+ config.set(rule_icmp + ['type'], value=type_code_match[1])
+ if type_code_match[2]:
+ config.set(rule_icmp + ['code'], value=type_code_match[2])
+ elif tmp in icmpv6_remove:
config.delete(rule_icmp + ['type'])
- config.set(rule_icmp + ['type-name'], value=translate)
- elif isinstance(translate, list):
- config.set(rule_icmp + ['type'], value=translate[0])
- config.set(rule_icmp + ['code'], value=translate[1])
- else:
- config.rename(rule_icmp + ['type'], 'type-name')
-
- 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 + "_"
+ elif tmp in icmpv6_translations:
+ translate = icmpv6_translations[tmp]
+ if isinstance(translate, str):
+ config.delete(rule_icmp + ['type'])
+ config.set(rule_icmp + ['type-name'], value=translate)
+ elif isinstance(translate, list):
+ config.set(rule_icmp + ['type'], value=translate[0])
+ config.set(rule_icmp + ['code'], value=translate[1])
+ else:
+ config.rename(rule_icmp + ['type'], 'type-name')
+
+ 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)
- 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())
-except OSError as e:
- print("Failed to save the modified config: {}".format(e))
- exit(1)
+ 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])
diff --git a/src/migration-scripts/firewall/7-to-8 b/src/migration-scripts/firewall/7-to-8
index bbaba113a..f46994ce2 100755..100644
--- a/src/migration-scripts/firewall/7-to-8
+++ b/src/migration-scripts/firewall/7-to-8
@@ -1,43 +1,25 @@
-#!/usr/bin/env python3
+# Copyright 2022-2024 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
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
#
-# 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# T2199: Migrate interface firewall nodes to firewall interfaces <ifname> <direction> name/ipv6-name <name>
# T2199: Migrate zone-policy to firewall node
-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 = ['firewall']
zone_base = ['zone-policy']
-config = ConfigTree(config_file)
-
-if not config.exists(base) and not config.exists(zone_base):
- # Nothing to do
- exit(0)
def migrate_interface(config, iftype, ifname, vif=None, vifs=None, vifc=None):
if_path = ['interfaces', iftype, ifname]
@@ -63,33 +45,31 @@ def migrate_interface(config, iftype, ifname, vif=None, vifs=None, vifc=None):
config.copy(if_path + ['firewall'], ['firewall', 'interface', ifname_full])
config.delete(if_path + ['firewall'])
-for iftype in config.list_nodes(['interfaces']):
- for ifname in config.list_nodes(['interfaces', iftype]):
- migrate_interface(config, iftype, ifname)
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(base) and not config.exists(zone_base):
+ # Nothing to do
+ return
- if config.exists(['interfaces', iftype, ifname, 'vif']):
- for vif in config.list_nodes(['interfaces', iftype, ifname, 'vif']):
- migrate_interface(config, iftype, ifname, vif=vif)
+ for iftype in config.list_nodes(['interfaces']):
+ for ifname in config.list_nodes(['interfaces', iftype]):
+ migrate_interface(config, iftype, ifname)
- if config.exists(['interfaces', iftype, ifname, 'vif-s']):
- for vifs in config.list_nodes(['interfaces', iftype, ifname, 'vif-s']):
- migrate_interface(config, iftype, ifname, vifs=vifs)
+ if config.exists(['interfaces', iftype, ifname, 'vif']):
+ for vif in config.list_nodes(['interfaces', iftype, ifname, 'vif']):
+ migrate_interface(config, iftype, ifname, vif=vif)
- if config.exists(['interfaces', iftype, ifname, 'vif-s', vifs, 'vif-c']):
- for vifc in config.list_nodes(['interfaces', iftype, ifname, 'vif-s', vifs, 'vif-c']):
- migrate_interface(config, iftype, ifname, vifs=vifs, vifc=vifc)
+ if config.exists(['interfaces', iftype, ifname, 'vif-s']):
+ for vifs in config.list_nodes(['interfaces', iftype, ifname, 'vif-s']):
+ migrate_interface(config, iftype, ifname, vifs=vifs)
-if config.exists(zone_base + ['zone']):
- config.set(['firewall', 'zone'])
- config.set_tag(['firewall', 'zone'])
+ if config.exists(['interfaces', iftype, ifname, 'vif-s', vifs, 'vif-c']):
+ for vifc in config.list_nodes(['interfaces', iftype, ifname, 'vif-s', vifs, 'vif-c']):
+ migrate_interface(config, iftype, ifname, vifs=vifs, vifc=vifc)
- for zone in config.list_nodes(zone_base + ['zone']):
- config.copy(zone_base + ['zone', zone], ['firewall', 'zone', zone])
- config.delete(zone_base)
+ if config.exists(zone_base + ['zone']):
+ config.set(['firewall', 'zone'])
+ config.set_tag(['firewall', 'zone'])
-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)
+ for zone in config.list_nodes(zone_base + ['zone']):
+ config.copy(zone_base + ['zone', zone], ['firewall', 'zone', zone])
+ config.delete(zone_base)
diff --git a/src/migration-scripts/firewall/8-to-9 b/src/migration-scripts/firewall/8-to-9
index 6e019beb2..3c9e84662 100755..100644
--- a/src/migration-scripts/firewall/8-to-9
+++ b/src/migration-scripts/firewall/8-to-9
@@ -1,18 +1,17 @@
-#!/usr/bin/env python3
+# Copyright 2022-2024 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
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
#
-# 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# T4780: Add firewall interface group
# cli changes from:
@@ -20,69 +19,50 @@
# To
# set firewall [name | ipv6-name] <name> rule <number> [inbound-interface | outbound-interface] [interface-name | interface-group] <interface_name | interface_group>
-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 = ['firewall']
-config = ConfigTree(config_file)
-
-if not config.exists(base):
- # Nothing to do
- exit(0)
-
-if config.exists(base + ['name']):
- for name in config.list_nodes(base + ['name']):
- if not config.exists(base + ['name', name, 'rule']):
- continue
-
- for rule in config.list_nodes(base + ['name', name, 'rule']):
- rule_iiface = base + ['name', name, 'rule', rule, 'inbound-interface']
- rule_oiface = base + ['name', name, 'rule', rule, 'outbound-interface']
-
- if config.exists(rule_iiface):
- tmp = config.return_value(rule_iiface)
- config.delete(rule_iiface)
- config.set(rule_iiface + ['interface-name'], value=tmp)
-
- if config.exists(rule_oiface):
- tmp = config.return_value(rule_oiface)
- config.delete(rule_oiface)
- config.set(rule_oiface + ['interface-name'], value=tmp)
-
-
-if config.exists(base + ['ipv6-name']):
- for name in config.list_nodes(base + ['ipv6-name']):
- if not config.exists(base + ['ipv6-name', name, 'rule']):
- continue
-
- for rule in config.list_nodes(base + ['ipv6-name', name, 'rule']):
- rule_iiface = base + ['ipv6-name', name, 'rule', rule, 'inbound-interface']
- rule_oiface = base + ['ipv6-name', name, 'rule', rule, 'outbound-interface']
-
- if config.exists(rule_iiface):
- tmp = config.return_value(rule_iiface)
- config.delete(rule_iiface)
- config.set(rule_iiface + ['interface-name'], value=tmp)
-
- if config.exists(rule_oiface):
- tmp = config.return_value(rule_oiface)
- config.delete(rule_oiface)
- config.set(rule_oiface + ['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 migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ # Nothing to do
+ return
+
+ if config.exists(base + ['name']):
+ for name in config.list_nodes(base + ['name']):
+ if not config.exists(base + ['name', name, 'rule']):
+ continue
+
+ for rule in config.list_nodes(base + ['name', name, 'rule']):
+ rule_iiface = base + ['name', name, 'rule', rule, 'inbound-interface']
+ rule_oiface = base + ['name', name, 'rule', rule, 'outbound-interface']
+
+ if config.exists(rule_iiface):
+ tmp = config.return_value(rule_iiface)
+ config.delete(rule_iiface)
+ config.set(rule_iiface + ['interface-name'], value=tmp)
+
+ if config.exists(rule_oiface):
+ tmp = config.return_value(rule_oiface)
+ config.delete(rule_oiface)
+ config.set(rule_oiface + ['interface-name'], value=tmp)
+
+
+ if config.exists(base + ['ipv6-name']):
+ for name in config.list_nodes(base + ['ipv6-name']):
+ if not config.exists(base + ['ipv6-name', name, 'rule']):
+ continue
+
+ for rule in config.list_nodes(base + ['ipv6-name', name, 'rule']):
+ rule_iiface = base + ['ipv6-name', name, 'rule', rule, 'inbound-interface']
+ rule_oiface = base + ['ipv6-name', name, 'rule', rule, 'outbound-interface']
+
+ if config.exists(rule_iiface):
+ tmp = config.return_value(rule_iiface)
+ config.delete(rule_iiface)
+ config.set(rule_iiface + ['interface-name'], value=tmp)
+
+ if config.exists(rule_oiface):
+ tmp = config.return_value(rule_oiface)
+ config.delete(rule_oiface)
+ config.set(rule_oiface + ['interface-name'], value=tmp)
diff --git a/src/migration-scripts/firewall/9-to-10 b/src/migration-scripts/firewall/9-to-10
index ce509a731..306a53a86 100755..100644
--- a/src/migration-scripts/firewall/9-to-10
+++ b/src/migration-scripts/firewall/9-to-10
@@ -1,18 +1,17 @@
-#!/usr/bin/env python3
+# Copyright 2023-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2023-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; either
+# version 2.1 of the License, or (at your option) any later version.
#
-# 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# T5050: Log options
# cli changes from:
@@ -20,58 +19,39 @@
# To
# set firewall [name | ipv6-name] <name> rule <number> log-options level <log_level>
-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 = ['firewall']
-config = ConfigTree(config_file)
-
-if not config.exists(base):
- # Nothing to do
- exit(0)
-
-if config.exists(base + ['name']):
- for name in config.list_nodes(base + ['name']):
- if not config.exists(base + ['name', name, 'rule']):
- continue
-
- for rule in config.list_nodes(base + ['name', name, 'rule']):
- 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):
- tmp = config.return_value(rule_log_level)
- config.delete(rule_log_level)
- config.set(log_options_base + ['level'], value=tmp)
-
-if config.exists(base + ['ipv6-name']):
- for name in config.list_nodes(base + ['ipv6-name']):
- if not config.exists(base + ['ipv6-name', name, 'rule']):
- continue
-
- for rule in config.list_nodes(base + ['ipv6-name', name, 'rule']):
- 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):
- tmp = config.return_value(rule_log_level)
- config.delete(rule_log_level)
- config.set(log_options_base + ['level'], 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 migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ # Nothing to do
+ return
+
+ if config.exists(base + ['name']):
+ for name in config.list_nodes(base + ['name']):
+ if not config.exists(base + ['name', name, 'rule']):
+ continue
+
+ for rule in config.list_nodes(base + ['name', name, 'rule']):
+ 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):
+ tmp = config.return_value(rule_log_level)
+ config.delete(rule_log_level)
+ config.set(log_options_base + ['level'], value=tmp)
+
+ if config.exists(base + ['ipv6-name']):
+ for name in config.list_nodes(base + ['ipv6-name']):
+ if not config.exists(base + ['ipv6-name', name, 'rule']):
+ continue
+
+ for rule in config.list_nodes(base + ['ipv6-name', name, 'rule']):
+ 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):
+ tmp = config.return_value(rule_log_level)
+ config.delete(rule_log_level)
+ config.set(log_options_base + ['level'], value=tmp)
diff --git a/src/migration-scripts/flow-accounting/0-to-1 b/src/migration-scripts/flow-accounting/0-to-1
index 0f790fd9c..77670e3ef 100755..100644
--- a/src/migration-scripts/flow-accounting/0-to-1
+++ b/src/migration-scripts/flow-accounting/0-to-1
@@ -1,69 +1,51 @@
-#!/usr/bin/env python3
+# Copyright 2021-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2021 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 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# T4099: flow-accounting: sync "source-ip" and "source-address" between netflow
# and sflow ion CLI
# T4105: flow-accounting: drop "sflow agent-address auto"
-from sys import argv
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 = ['system', 'flow-accounting']
-config = ConfigTree(config_file)
-
-if not config.exists(base):
- # Nothing to do
- exit(0)
-
-# T4099
-tmp = base + ['netflow', 'source-ip']
-if config.exists(tmp):
- config.rename(tmp, 'source-address')
-
-# T4105
-tmp = base + ['sflow', 'agent-address']
-if config.exists(tmp):
- value = config.return_value(tmp)
- if value == 'auto':
- # delete the "auto"
- config.delete(tmp)
-
- # 1) check if BGP router-id is set
- # 2) check if OSPF router-id is set
- # 3) check if OSPFv3 router-id is set
- router_id = None
- for protocol in ['bgp', 'ospf', 'ospfv3']:
- if config.exists(['protocols', protocol, 'parameters', 'router-id']):
- router_id = config.return_value(['protocols', protocol, 'parameters', 'router-id'])
- break
- if router_id:
- config.set(tmp, value=router_id)
-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 migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ # Nothing to do
+ return
+
+ # T4099
+ tmp = base + ['netflow', 'source-ip']
+ if config.exists(tmp):
+ config.rename(tmp, 'source-address')
+
+ # T4105
+ tmp = base + ['sflow', 'agent-address']
+ if config.exists(tmp):
+ value = config.return_value(tmp)
+ if value == 'auto':
+ # delete the "auto"
+ config.delete(tmp)
+
+ # 1) check if BGP router-id is set
+ # 2) check if OSPF router-id is set
+ # 3) check if OSPFv3 router-id is set
+ router_id = None
+ for protocol in ['bgp', 'ospf', 'ospfv3']:
+ if config.exists(['protocols', protocol, 'parameters', 'router-id']):
+ router_id = config.return_value(['protocols', protocol, 'parameters', 'router-id'])
+ break
+ if router_id:
+ config.set(tmp, value=router_id)
diff --git a/src/migration-scripts/https/0-to-1 b/src/migration-scripts/https/0-to-1
index 23809f5ad..52fe3f2ad 100755..100644
--- a/src/migration-scripts/https/0-to-1
+++ b/src/migration-scripts/https/0-to-1
@@ -1,42 +1,30 @@
-#!/usr/bin/env python3
+# Copyright 202-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# 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 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# * Move server block directives under 'virtual-host' tag node, instead of
# relying on 'listen-address' tag node
-import sys
-
from vyos.configtree import ConfigTree
-if (len(sys.argv) < 2):
- print("Must specify file name!")
- sys.exit(1)
-
-file_name = sys.argv[1]
-
-with open(file_name, 'r') as f:
- config_file = f.read()
+old_base = ['service', 'https', 'listen-address']
-config = ConfigTree(config_file)
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(old_base):
+ # Nothing to do
+ return
-old_base = ['service', 'https', 'listen-address']
-if not config.exists(old_base):
- # Nothing to do
- sys.exit(0)
-else:
new_base = ['service', 'https', 'virtual-host']
config.set(new_base)
config.set_tag(new_base)
@@ -60,10 +48,3 @@ else:
index += 1
config.delete(old_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))
- sys.exit(1)
diff --git a/src/migration-scripts/https/1-to-2 b/src/migration-scripts/https/1-to-2
index 1a2cdc1e7..dad7ac1f0 100755..100644
--- a/src/migration-scripts/https/1-to-2
+++ b/src/migration-scripts/https/1-to-2
@@ -1,42 +1,30 @@
-#!/usr/bin/env python3
+# Copyright 2020-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# 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 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# * Move 'api virtual-host' list to 'api-restrict virtual-host' so it
# is owned by service_https.py
-import sys
-
from vyos.configtree import ConfigTree
-if (len(sys.argv) < 2):
- print("Must specify file name!")
- sys.exit(1)
-
-file_name = sys.argv[1]
-
-with open(file_name, 'r') as f:
- config_file = f.read()
+old_base = ['service', 'https', 'api', 'virtual-host']
-config = ConfigTree(config_file)
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(old_base):
+ # Nothing to do
+ return
-old_base = ['service', 'https', 'api', 'virtual-host']
-if not config.exists(old_base):
- # Nothing to do
- sys.exit(0)
-else:
new_base = ['service', 'https', 'api-restrict', 'virtual-host']
config.set(new_base)
@@ -45,10 +33,3 @@ else:
config.set(new_base, value=name, replace=False)
config.delete(old_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))
- sys.exit(1)
diff --git a/src/migration-scripts/https/2-to-3 b/src/migration-scripts/https/2-to-3
index 2beba6d2b..1125caebf 100755..100644
--- a/src/migration-scripts/https/2-to-3
+++ b/src/migration-scripts/https/2-to-3
@@ -1,23 +1,20 @@
-#!/usr/bin/env python3
+# Copyright 2021-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2021 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 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# * Migrate system signed certificate to use PKI
-import sys
-
from vyos.configtree import ConfigTree
from vyos.pki import create_certificate
from vyos.pki import create_certificate_request
@@ -25,23 +22,9 @@ from vyos.pki import create_private_key
from vyos.pki import encode_certificate
from vyos.pki import encode_private_key
-if len(sys.argv) < 2:
- print("Must specify file name!")
- sys.exit(1)
-
-file_name = sys.argv[1]
-
-with open(file_name, 'r') as f:
- config_file = f.read()
-
-config = ConfigTree(config_file)
-
base = ['service', 'https', 'certificates']
pki_base = ['pki']
-if not config.exists(base + ['system-generated-certificate']):
- sys.exit(0)
-
def wrapped_pem_to_config_value(pem):
out = []
for line in pem.strip().split("\n"):
@@ -50,37 +33,34 @@ def wrapped_pem_to_config_value(pem):
out.append(line)
return "".join(out)
-if not config.exists(pki_base + ['certificate']):
- config.set(pki_base + ['certificate'])
- config.set_tag(pki_base + ['certificate'])
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(base + ['system-generated-certificate']):
+ return
-valid_days = 365
-if config.exists(base + ['system-generated-certificate', 'lifetime']):
- valid_days = int(config.return_value(base + ['system-generated-certificate', 'lifetime']))
+ if not config.exists(pki_base + ['certificate']):
+ config.set(pki_base + ['certificate'])
+ config.set_tag(pki_base + ['certificate'])
-key = create_private_key('rsa', 2048)
-subject = {'country': 'GB', 'state': 'N/A', 'locality': 'N/A', 'organization': 'VyOS', 'common_name': 'vyos'}
-cert_req = create_certificate_request(subject, key, ['vyos'])
-cert = create_certificate(cert_req, cert_req, key, valid_days)
+ valid_days = 365
+ if config.exists(base + ['system-generated-certificate', 'lifetime']):
+ valid_days = int(config.return_value(base + ['system-generated-certificate', 'lifetime']))
-if cert:
- cert_pem = encode_certificate(cert)
- config.set(pki_base + ['certificate', 'generated_https', 'certificate'], value=wrapped_pem_to_config_value(cert_pem))
+ key = create_private_key('rsa', 2048)
+ subject = {'country': 'GB', 'state': 'N/A', 'locality': 'N/A', 'organization': 'VyOS', 'common_name': 'vyos'}
+ cert_req = create_certificate_request(subject, key, ['vyos'])
+ cert = create_certificate(cert_req, cert_req, key, valid_days)
-if key:
- key_pem = encode_private_key(key)
- config.set(pki_base + ['certificate', 'generated_https', 'private', 'key'], value=wrapped_pem_to_config_value(key_pem))
+ if cert:
+ cert_pem = encode_certificate(cert)
+ config.set(pki_base + ['certificate', 'generated_https', 'certificate'], value=wrapped_pem_to_config_value(cert_pem))
-if cert and key:
- config.set(base + ['certificate'], value='generated_https')
-else:
- print('Failed to migrate system-generated-certificate from https service')
+ if key:
+ key_pem = encode_private_key(key)
+ config.set(pki_base + ['certificate', 'generated_https', 'private', 'key'], value=wrapped_pem_to_config_value(key_pem))
-config.delete(base + ['system-generated-certificate'])
+ if cert and key:
+ config.set(base + ['certificate'], value='generated_https')
+ else:
+ print('Failed to migrate system-generated-certificate from https service')
-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))
- sys.exit(1)
+ config.delete(base + ['system-generated-certificate'])
diff --git a/src/migration-scripts/https/3-to-4 b/src/migration-scripts/https/3-to-4
index b3cfca201..c01236cc6 100755..100644
--- a/src/migration-scripts/https/3-to-4
+++ b/src/migration-scripts/https/3-to-4
@@ -1,53 +1,34 @@
-#!/usr/bin/env python3
+# Copyright 2022-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2022 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 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# T4768 rename node 'gql' to 'graphql'.
-import sys
-
from vyos.configtree import ConfigTree
-if len(sys.argv) < 2:
- print("Must specify file name!")
- sys.exit(1)
-
-file_name = sys.argv[1]
-
-with open(file_name, 'r') as f:
- config_file = f.read()
-
-config = ConfigTree(config_file)
-
old_base = ['service', 'https', 'api', 'gql']
-if not config.exists(old_base):
- # Nothing to do
- sys.exit(0)
-
new_base = ['service', 'https', 'api', 'graphql']
-config.set(new_base)
-nodes = config.list_nodes(old_base)
-for node in nodes:
- config.copy(old_base + [node], new_base + [node])
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(old_base):
+ # Nothing to do
+ return
+
+ config.set(new_base)
-config.delete(old_base)
+ nodes = config.list_nodes(old_base)
+ for node in nodes:
+ config.copy(old_base + [node], new_base + [node])
-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))
- sys.exit(1)
+ config.delete(old_base)
diff --git a/src/migration-scripts/https/4-to-5 b/src/migration-scripts/https/4-to-5
index 0dfb6ac19..0f1c7901f 100755..100644
--- a/src/migration-scripts/https/4-to-5
+++ b/src/migration-scripts/https/4-to-5
@@ -1,62 +1,43 @@
-#!/usr/bin/env python3
+# Copyright 2023-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2023 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 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# T5762: http: api: smoketests fail as they can not establish IPv6 connection
# to uvicorn backend server, always make the UNIX domain socket the
# default way of communication
-import sys
-
from vyos.configtree import ConfigTree
-if len(sys.argv) < 2:
- print("Must specify file name!")
- sys.exit(1)
-
-file_name = sys.argv[1]
-
-with open(file_name, 'r') as f:
- config_file = f.read()
-
-config = ConfigTree(config_file)
-
base = ['service', 'https']
-if not config.exists(base):
- # Nothing to do
- sys.exit(0)
-
-# Delete "socket" CLI option - we always use UNIX domain sockets for
-# NGINX <-> API server communication
-if config.exists(base + ['api', 'socket']):
- config.delete(base + ['api', 'socket'])
-
-# There is no need for an API service port, as UNIX domain sockets
-# are used
-if config.exists(base + ['api', 'port']):
- config.delete(base + ['api', 'port'])
-
-# rename listen-port -> port ver virtual-host
-if config.exists(base + ['virtual-host']):
- for vhost in config.list_nodes(base + ['virtual-host']):
- if config.exists(base + ['virtual-host', vhost, 'listen-port']):
- config.rename(base + ['virtual-host', vhost, 'listen-port'], 'port')
-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))
- sys.exit(1)
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ # Nothing to do
+ return
+
+ # Delete "socket" CLI option - we always use UNIX domain sockets for
+ # NGINX <-> API server communication
+ if config.exists(base + ['api', 'socket']):
+ config.delete(base + ['api', 'socket'])
+
+ # There is no need for an API service port, as UNIX domain sockets
+ # are used
+ if config.exists(base + ['api', 'port']):
+ config.delete(base + ['api', 'port'])
+
+ # rename listen-port -> port ver virtual-host
+ if config.exists(base + ['virtual-host']):
+ for vhost in config.list_nodes(base + ['virtual-host']):
+ if config.exists(base + ['virtual-host', vhost, 'listen-port']):
+ config.rename(base + ['virtual-host', vhost, 'listen-port'], 'port')
diff --git a/src/migration-scripts/https/5-to-6 b/src/migration-scripts/https/5-to-6
index 72e9e31f7..6ef6976b6 100755..100644
--- a/src/migration-scripts/https/5-to-6
+++ b/src/migration-scripts/https/5-to-6
@@ -1,25 +1,23 @@
-#!/usr/bin/env python3
+# Copyright 2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 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; either
+# version 2.1 of the License, or (at your option) any later version.
#
-# 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# T5886: Add support for ACME protocol (LetsEncrypt), migrate https certbot
# to new "pki certificate" CLI tree
# T5902: Remove virtual-host
import os
-import sys
from vyos.configtree import ConfigTree
from vyos.defaults import directories
@@ -27,81 +25,65 @@ from vyos.utils.process import cmd
vyos_certbot_dir = directories['certbot']
-if len(sys.argv) < 2:
- print("Must specify file name!")
- sys.exit(1)
-
-file_name = sys.argv[1]
-
-with open(file_name, 'r') as f:
- config_file = f.read()
-
-config = ConfigTree(config_file)
-
base = ['service', 'https']
-if not config.exists(base):
- # Nothing to do
- sys.exit(0)
-
-if config.exists(base + ['certificates', 'certbot']):
- # both domain-name and email must be set on CLI - ensured by previous verify()
- domain_names = config.return_values(base + ['certificates', 'certbot', 'domain-name'])
- email = config.return_value(base + ['certificates', 'certbot', 'email'])
- config.delete(base + ['certificates', 'certbot'])
-
- # Set default certname based on domain-name
- cert_name = 'https-' + domain_names[0].split('.')[0]
- # Overwrite certname from previous certbot calls if available
- # We can not use python code like os.scandir due to filesystem permissions.
- # This must be run as root
- certbot_live = f'{vyos_certbot_dir}/live/' # we need the trailing /
- if os.path.exists(certbot_live):
- tmp = cmd(f'sudo find {certbot_live} -maxdepth 1 -type d')
- tmp = tmp.split() # tmp = ['/config/auth/letsencrypt/live', '/config/auth/letsencrypt/live/router.vyos.net']
- tmp.remove(certbot_live)
- cert_name = tmp[0].replace(certbot_live, '')
- config.set(['pki', 'certificate', cert_name, 'acme', 'email'], value=email)
- config.set_tag(['pki', 'certificate'])
- for domain in domain_names:
- config.set(['pki', 'certificate', cert_name, 'acme', 'domain-name'], value=domain, replace=False)
-
- # Update Webserver certificate
- config.set(base + ['certificates', 'certificate'], value=cert_name)
-
-if config.exists(base + ['virtual-host']):
- allow_client = []
- listen_port = []
- listen_address = []
- for virtual_host in config.list_nodes(base + ['virtual-host']):
- allow_path = base + ['virtual-host', virtual_host, 'allow-client', 'address']
- if config.exists(allow_path):
- tmp = config.return_values(allow_path)
- allow_client.extend(tmp)
-
- port_path = base + ['virtual-host', virtual_host, 'port']
- if config.exists(port_path):
- tmp = config.return_value(port_path)
- listen_port.append(tmp)
-
- listen_address_path = base + ['virtual-host', virtual_host, 'listen-address']
- if config.exists(listen_address_path):
- tmp = config.return_value(listen_address_path)
- listen_address.append(tmp)
-
- config.delete(base + ['virtual-host'])
- for client in allow_client:
- config.set(base + ['allow-client', 'address'], value=client, replace=False)
-
- # clear listen-address if "all" were specified
- if '*' in listen_address:
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ # Nothing to do
+ return
+
+ if config.exists(base + ['certificates', 'certbot']):
+ # both domain-name and email must be set on CLI - ensured by previous verify()
+ domain_names = config.return_values(base + ['certificates', 'certbot', 'domain-name'])
+ email = config.return_value(base + ['certificates', 'certbot', 'email'])
+ config.delete(base + ['certificates', 'certbot'])
+
+ # Set default certname based on domain-name
+ cert_name = 'https-' + domain_names[0].split('.')[0]
+ # Overwrite certname from previous certbot calls if available
+ # We can not use python code like os.scandir due to filesystem permissions.
+ # This must be run as root
+ certbot_live = f'{vyos_certbot_dir}/live/' # we need the trailing /
+ if os.path.exists(certbot_live):
+ tmp = cmd(f'sudo find {certbot_live} -maxdepth 1 -type d')
+ tmp = tmp.split() # tmp = ['/config/auth/letsencrypt/live', '/config/auth/letsencrypt/live/router.vyos.net']
+ tmp.remove(certbot_live)
+ cert_name = tmp[0].replace(certbot_live, '')
+
+ config.set(['pki', 'certificate', cert_name, 'acme', 'email'], value=email)
+ config.set_tag(['pki', 'certificate'])
+ for domain in domain_names:
+ config.set(['pki', 'certificate', cert_name, 'acme', 'domain-name'], value=domain, replace=False)
+
+ # Update Webserver certificate
+ config.set(base + ['certificates', 'certificate'], value=cert_name)
+
+ if config.exists(base + ['virtual-host']):
+ allow_client = []
+ listen_port = []
listen_address = []
- for address in listen_address:
- config.set(base + ['listen-address'], value=address, replace=False)
-
-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))
- sys.exit(1)
+ for virtual_host in config.list_nodes(base + ['virtual-host']):
+ allow_path = base + ['virtual-host', virtual_host, 'allow-client', 'address']
+ if config.exists(allow_path):
+ tmp = config.return_values(allow_path)
+ allow_client.extend(tmp)
+
+ port_path = base + ['virtual-host', virtual_host, 'port']
+ if config.exists(port_path):
+ tmp = config.return_value(port_path)
+ listen_port.append(tmp)
+
+ listen_address_path = base + ['virtual-host', virtual_host, 'listen-address']
+ if config.exists(listen_address_path):
+ tmp = config.return_value(listen_address_path)
+ listen_address.append(tmp)
+
+ config.delete(base + ['virtual-host'])
+ for client in allow_client:
+ config.set(base + ['allow-client', 'address'], value=client, replace=False)
+
+ # clear listen-address if "all" were specified
+ if '*' in listen_address:
+ listen_address = []
+ for address in listen_address:
+ config.set(base + ['listen-address'], value=address, replace=False)
diff --git a/src/migration-scripts/ids/0-to-1 b/src/migration-scripts/ids/0-to-1
index 8b7850a1a..1b963e839 100755..100644
--- a/src/migration-scripts/ids/0-to-1
+++ b/src/migration-scripts/ids/0-to-1
@@ -1,56 +1,38 @@
-#!/usr/bin/env python3
+# Copyright 2022-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2022 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 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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 sys import argv
-from sys import exit
+# T4557: Migrate threshold and add new threshold types
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 = ['service', 'ids', 'ddos-protection']
-config = ConfigTree(config_file)
-
-if not config.exists(base + ['threshold']):
- # Nothing to do
- exit(0)
-else:
- if config.exists(base + ['threshold', 'fps']):
- tmp = config.return_value(base + ['threshold', 'fps'])
- config.delete(base + ['threshold', 'fps'])
- config.set(base + ['threshold', 'general', 'fps'], value=tmp)
- if config.exists(base + ['threshold', 'mbps']):
- tmp = config.return_value(base + ['threshold', 'mbps'])
- config.delete(base + ['threshold', 'mbps'])
- config.set(base + ['threshold', 'general', 'mbps'], value=tmp)
- if config.exists(base + ['threshold', 'pps']):
- tmp = config.return_value(base + ['threshold', 'pps'])
- config.delete(base + ['threshold', 'pps'])
- config.set(base + ['threshold', 'general', 'pps'], value=tmp)
-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)
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(base + ['threshold']):
+ # Nothing to do
+ return
+ else:
+ if config.exists(base + ['threshold', 'fps']):
+ tmp = config.return_value(base + ['threshold', 'fps'])
+ config.delete(base + ['threshold', 'fps'])
+ config.set(base + ['threshold', 'general', 'fps'], value=tmp)
+ if config.exists(base + ['threshold', 'mbps']):
+ tmp = config.return_value(base + ['threshold', 'mbps'])
+ config.delete(base + ['threshold', 'mbps'])
+ config.set(base + ['threshold', 'general', 'mbps'], value=tmp)
+ if config.exists(base + ['threshold', 'pps']):
+ tmp = config.return_value(base + ['threshold', 'pps'])
+ config.delete(base + ['threshold', 'pps'])
+ config.set(base + ['threshold', 'general', 'pps'], value=tmp)
diff --git a/src/migration-scripts/interfaces/0-to-1 b/src/migration-scripts/interfaces/0-to-1
index 25f6842eb..7c135e76e 100755..100644
--- a/src/migration-scripts/interfaces/0-to-1
+++ b/src/migration-scripts/interfaces/0-to-1
@@ -1,11 +1,23 @@
-#!/usr/bin/env python3
+# 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
+# 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, see <http://www.gnu.org/licenses/>.
# Change syntax of bridge interface
# - move interface based bridge-group to actual bridge (de-nest)
# - make stp and igmp-snooping nodes valueless
# https://vyos.dev/T1556
-import sys
from vyos.configtree import ConfigTree
def migrate_bridge(config, tree, intf):
@@ -36,83 +48,66 @@ def migrate_bridge(config, tree, intf):
config.delete(tree_bridge)
-if __name__ == '__main__':
- if len(sys.argv) < 2:
- print("Must specify file name!")
- sys.exit(1)
-
- file_name = sys.argv[1]
-
- with open(file_name, 'r') as f:
- config_file = f.read()
-
- config = ConfigTree(config_file)
+def migrate(config: ConfigTree) -> None:
base = ['interfaces', 'bridge']
if not config.exists(base):
# Nothing to do
- sys.exit(0)
- else:
- #
- # make stp and igmp-snooping nodes valueless
- #
- for br in config.list_nodes(base):
- # STP: check if enabled
- if config.exists(base + [br, 'stp']):
- stp_val = config.return_value(base + [br, 'stp'])
- # STP: delete node with old syntax
- config.delete(base + [br, 'stp'])
- # STP: set new node - if enabled
- if stp_val == "true":
- config.set(base + [br, 'stp'], value=None)
-
- # igmp-snooping: check if enabled
- if config.exists(base + [br, 'igmp-snooping', 'querier']):
- igmp_val = config.return_value(base + [br, 'igmp-snooping', 'querier'])
- # igmp-snooping: delete node with old syntax
- config.delete(base + [br, 'igmp-snooping', 'querier'])
- # igmp-snooping: set new node - if enabled
- if igmp_val == "enable":
- config.set(base + [br, 'igmp', 'querier'], value=None)
-
- #
- # move interface based bridge-group to actual bridge (de-nest)
- #
- bridge_types = ['bonding', 'ethernet', 'l2tpv3', 'openvpn', 'vxlan', 'wireless']
- for type in bridge_types:
- if not config.exists(['interfaces', type]):
- continue
-
- for interface in config.list_nodes(['interfaces', type]):
- # check if bridge-group exists
- bridge_group = ['interfaces', type, interface]
- if config.exists(bridge_group + ['bridge-group']):
- migrate_bridge(config, bridge_group, interface)
-
- # We also need to migrate VLAN interfaces
- vlan_base = ['interfaces', type, interface, 'vif']
- if config.exists(vlan_base):
- for vlan in config.list_nodes(vlan_base):
- intf = "{}.{}".format(interface, vlan)
- migrate_bridge(config, vlan_base + [vlan], intf)
-
- # And then we have service VLANs (vif-s) interfaces
- vlan_base = ['interfaces', type, interface, 'vif-s']
- if config.exists(vlan_base):
- for vif_s in config.list_nodes(vlan_base):
- intf = "{}.{}".format(interface, vif_s)
- migrate_bridge(config, vlan_base + [vif_s], intf)
-
- # Every service VLAN can have multiple customer VLANs (vif-c)
- vlan_c = ['interfaces', type, interface, 'vif-s', vif_s, 'vif-c']
- if config.exists(vlan_c):
- for vif_c in config.list_nodes(vlan_c):
- intf = "{}.{}.{}".format(interface, vif_s, vif_c)
- migrate_bridge(config, vlan_c + [vif_c], intf)
-
- 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))
- sys.exit(1)
+ return
+
+ #
+ # make stp and igmp-snooping nodes valueless
+ #
+ for br in config.list_nodes(base):
+ # STP: check if enabled
+ if config.exists(base + [br, 'stp']):
+ stp_val = config.return_value(base + [br, 'stp'])
+ # STP: delete node with old syntax
+ config.delete(base + [br, 'stp'])
+ # STP: set new node - if enabled
+ if stp_val == "true":
+ config.set(base + [br, 'stp'], value=None)
+
+ # igmp-snooping: check if enabled
+ if config.exists(base + [br, 'igmp-snooping', 'querier']):
+ igmp_val = config.return_value(base + [br, 'igmp-snooping', 'querier'])
+ # igmp-snooping: delete node with old syntax
+ config.delete(base + [br, 'igmp-snooping', 'querier'])
+ # igmp-snooping: set new node - if enabled
+ if igmp_val == "enable":
+ config.set(base + [br, 'igmp', 'querier'], value=None)
+
+ #
+ # move interface based bridge-group to actual bridge (de-nest)
+ #
+ bridge_types = ['bonding', 'ethernet', 'l2tpv3', 'openvpn', 'vxlan', 'wireless']
+ for type in bridge_types:
+ if not config.exists(['interfaces', type]):
+ continue
+
+ for interface in config.list_nodes(['interfaces', type]):
+ # check if bridge-group exists
+ bridge_group = ['interfaces', type, interface]
+ if config.exists(bridge_group + ['bridge-group']):
+ migrate_bridge(config, bridge_group, interface)
+
+ # We also need to migrate VLAN interfaces
+ vlan_base = ['interfaces', type, interface, 'vif']
+ if config.exists(vlan_base):
+ for vlan in config.list_nodes(vlan_base):
+ intf = "{}.{}".format(interface, vlan)
+ migrate_bridge(config, vlan_base + [vlan], intf)
+
+ # And then we have service VLANs (vif-s) interfaces
+ vlan_base = ['interfaces', type, interface, 'vif-s']
+ if config.exists(vlan_base):
+ for vif_s in config.list_nodes(vlan_base):
+ intf = "{}.{}".format(interface, vif_s)
+ migrate_bridge(config, vlan_base + [vif_s], intf)
+
+ # Every service VLAN can have multiple customer VLANs (vif-c)
+ vlan_c = ['interfaces', type, interface, 'vif-s', vif_s, 'vif-c']
+ if config.exists(vlan_c):
+ for vif_c in config.list_nodes(vlan_c):
+ intf = "{}.{}.{}".format(interface, vif_s, vif_c)
+ migrate_bridge(config, vlan_c + [vif_c], intf)
diff --git a/src/migration-scripts/interfaces/1-to-2 b/src/migration-scripts/interfaces/1-to-2
index c95623c2b..ebf02b028 100755..100644
--- a/src/migration-scripts/interfaces/1-to-2
+++ b/src/migration-scripts/interfaces/1-to-2
@@ -1,28 +1,31 @@
-#!/usr/bin/env python3
+# 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
+# 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, see <http://www.gnu.org/licenses/>.
# Change syntax of bond interface
# - move interface based bond-group to actual bond (de-nest)
# https://vyos.dev/T1614
-import sys
from vyos.configtree import ConfigTree
-if len(sys.argv) < 2:
- print("Must specify file name!")
- sys.exit(1)
-
-file_name = sys.argv[1]
-
-with open(file_name, 'r') as f:
- config_file = f.read()
-
-config = ConfigTree(config_file)
base = ['interfaces', 'bonding']
-if not config.exists(base):
- # Nothing to do
- sys.exit(0)
-else:
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ # Nothing to do
+ return
+
#
# move interface based bond-group to actual bond (de-nest)
#
@@ -54,10 +57,3 @@ else:
#
# so we simply disable arp_interval by setting it to 0 and miimon will take care about the link
config.set(base + [bond, 'arp-monitor', 'interval'], value='0')
-
- 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))
- sys.exit(1)
diff --git a/src/migration-scripts/interfaces/10-to-11 b/src/migration-scripts/interfaces/10-to-11
index cafaa3fa4..8a562f2d0 100755..100644
--- a/src/migration-scripts/interfaces/10-to-11
+++ b/src/migration-scripts/interfaces/10-to-11
@@ -1,41 +1,31 @@
-#!/usr/bin/env python3
+# Copyright 2020-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# 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 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# rename WWAN (wirelessmodem) serial interface from non persistent ttyUSB2 to
# a bus like name, e.g. "usb0b1.3p1.3"
import os
-from sys import exit, argv
from vyos.configtree import ConfigTree
-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()
+base = ['interfaces', 'wirelessmodem']
- config = ConfigTree(config_file)
- base = ['interfaces', 'wirelessmodem']
+def migrate(config: ConfigTree) -> None:
if not config.exists(base):
# Nothing to do
- exit(0)
+ return
for wwan in config.list_nodes(base):
if config.exists(base + [wwan, 'device']):
@@ -46,10 +36,3 @@ if __name__ == '__main__':
device_file = os.path.realpath(os.path.join(root, file))
if os.path.basename(device_file) == device:
config.set(base + [wwan, 'device'], value=file, replace=True)
-
- 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/interfaces/11-to-12 b/src/migration-scripts/interfaces/11-to-12
index e9eb7f939..132cecbb7 100755..100644
--- a/src/migration-scripts/interfaces/11-to-12
+++ b/src/migration-scripts/interfaces/11-to-12
@@ -1,37 +1,25 @@
-#!/usr/bin/env python3
+# Copyright 2020-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# 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 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# - rename 'dhcpv6-options prefix-delegation' from single node to a new tag node
# 'dhcpv6-options pd 0'
# - delete 'sla-len' from CLI - value is calculated on demand
-from sys import exit, argv
from vyos.configtree import ConfigTree
-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)
-
+def migrate(config: ConfigTree) -> None:
for type in config.list_nodes(['interfaces']):
for interface in config.list_nodes(['interfaces', type]):
# cache current config tree
@@ -49,10 +37,3 @@ if __name__ == '__main__':
sla_config = new_base + [pd, 'interface', tmp, 'sla-len']
if config.exists(sla_config):
config.delete(sla_config)
-
- 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/interfaces/12-to-13 b/src/migration-scripts/interfaces/12-to-13
index ef1d93903..585deb898 100755..100644
--- a/src/migration-scripts/interfaces/12-to-13
+++ b/src/migration-scripts/interfaces/12-to-13
@@ -1,18 +1,17 @@
-#!/usr/bin/env python3
+# Copyright 2020-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# 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 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# - T2903: Change vif-s ethertype from numeric number to literal
# - 0x88a8 -> 802.1ad
@@ -20,20 +19,9 @@
# - T2905: Change WWAN "ondemand" node to "connect-on-demand" to have identical
# CLI nodes for both types of dialer interfaces
-from sys import exit, argv
from vyos.configtree import ConfigTree
-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)
-
+def migrate(config: ConfigTree) -> None:
#
# T2903
#
@@ -61,11 +49,3 @@ if __name__ == '__main__':
for interface in config.list_nodes(wwan_base):
if config.exists(wwan_base + [interface, 'ondemand']):
config.rename(wwan_base + [interface, 'ondemand'], 'connect-on-demand')
-
- 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/interfaces/13-to-14 b/src/migration-scripts/interfaces/13-to-14
index b20d8b4db..45d8e3b5f 100755..100644
--- a/src/migration-scripts/interfaces/13-to-14
+++ b/src/migration-scripts/interfaces/13-to-14
@@ -1,39 +1,29 @@
-#!/usr/bin/env python3
+# Copyright 2020-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# 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 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# T3043: rename Wireless interface security mode 'both' to 'wpa+wpa2'
# T3043: move "system wifi-regulatory-domain" to indicidual wireless interface
-from sys import exit, argv
from vyos.configtree import ConfigTree
-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)
+def migrate(config: ConfigTree) -> None:
base = ['interfaces', 'wireless']
+
if not config.exists(base):
# Nothing to do
- exit(0)
+ return
country_code = ''
cc_cli = ['system', 'wifi-regulatory-domain']
@@ -50,10 +40,3 @@ if __name__ == '__main__':
if country_code:
config.set(base + [wifi, 'country-code'], value=country_code)
-
- 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/interfaces/14-to-15 b/src/migration-scripts/interfaces/14-to-15
index e21251f86..d45d59bba 100755..100644
--- a/src/migration-scripts/interfaces/14-to-15
+++ b/src/migration-scripts/interfaces/14-to-15
@@ -1,39 +1,28 @@
-#!/usr/bin/env python3
+# Copyright 2020-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# 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 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# T3048: remove smp-affinity node from ethernet and use tuned instead
-from sys import exit, argv
from vyos.configtree import ConfigTree
-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)
+def migrate(config: ConfigTree) -> None:
base = ['interfaces', 'ethernet']
if not config.exists(base):
# Nothing to do
- exit(0)
+ return
migrate = False
for interface in config.list_nodes(base):
@@ -47,10 +36,3 @@ if __name__ == '__main__':
if migrate:
config.set(['system', 'options', 'performance'], value='throughput')
-
- 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/interfaces/15-to-16 b/src/migration-scripts/interfaces/15-to-16
index ae3441b9f..c9abdb5f8 100755..100644
--- a/src/migration-scripts/interfaces/15-to-16
+++ b/src/migration-scripts/interfaces/15-to-16
@@ -1,48 +1,30 @@
-#!/usr/bin/env python3
+# Copyright 2020-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# 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 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# remove pppoe "ipv6 enable" option
-from sys import exit, argv
from vyos.configtree import ConfigTree
-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)
+def migrate(config: ConfigTree) -> None:
base = ['interfaces', 'pppoe']
if not config.exists(base):
# Nothing to do
- exit(0)
+ return
for interface in config.list_nodes(base):
ipv6_enable = base + [interface, 'ipv6', 'enable']
if config.exists(ipv6_enable):
config.delete(ipv6_enable)
-
- 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/interfaces/16-to-17 b/src/migration-scripts/interfaces/16-to-17
index 75f160686..7d241ac68 100755..100644
--- a/src/migration-scripts/interfaces/16-to-17
+++ b/src/migration-scripts/interfaces/16-to-17
@@ -1,40 +1,28 @@
-#!/usr/bin/env python3
+# Copyright 2020-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# 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 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# Command line migration of port mirroring
# https://vyos.dev/T3089
-import sys
from vyos.configtree import ConfigTree
-if __name__ == '__main__':
- if len(sys.argv) < 2:
- print("Must specify file name!")
- sys.exit(1)
-
- file_name = sys.argv[1]
-
- with open(file_name, 'r') as f:
- config_file = f.read()
-
- config = ConfigTree(config_file)
+def migrate(config: ConfigTree) -> None:
base = ['interfaces', 'ethernet']
if not config.exists(base):
# Nothing to do
- sys.exit(0)
+ return
for interface in config.list_nodes(base):
mirror_old_base = base + [interface, 'mirror']
@@ -43,10 +31,3 @@ if __name__ == '__main__':
if config.exists(mirror_old_base):
config.delete(mirror_old_base)
config.set(mirror_old_base + ['ingress'],intf[0])
-
- 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))
- sys.exit(1)
diff --git a/src/migration-scripts/interfaces/17-to-18 b/src/migration-scripts/interfaces/17-to-18
index 51486ac37..f45695a88 100755..100644
--- a/src/migration-scripts/interfaces/17-to-18
+++ b/src/migration-scripts/interfaces/17-to-18
@@ -1,37 +1,25 @@
-#!/usr/bin/env python3
+# Copyright 2020-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# 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 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# T3043: Move "system wifi-regulatory-domain" to indicidual wireless interface.
# Country Code will be migratred from upper to lower case.
# T3140: Relax ethernet interface offload-options
-from sys import exit, argv
from vyos.configtree import ConfigTree
-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)
-
+def migrate(config: ConfigTree) -> None:
# T3140: Cleanup ethernet offload-options, remove on/off value and use
# valueless nodes instead.
eth_base = ['interfaces', 'ethernet']
@@ -62,10 +50,3 @@ if __name__ == '__main__':
if config.exists(ccode):
tmp = config.return_value(ccode)
config.set(ccode, value=tmp.lower(), replace=True)
-
- 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/interfaces/18-to-19 b/src/migration-scripts/interfaces/18-to-19
index c3209f250..ae1a07adb 100755..100644
--- a/src/migration-scripts/interfaces/18-to-19
+++ b/src/migration-scripts/interfaces/18-to-19
@@ -1,24 +1,20 @@
-#!/usr/bin/env python3
+# Copyright 2021-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2021 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 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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
-from sys import argv
-from sys import exit
-
from vyos.configtree import ConfigTree
def replace_nat_interfaces(config, old, new):
@@ -40,21 +36,11 @@ def replace_nat_interfaces(config, old, new):
config.set(conf_rule + ['outbound-interface'], value=new)
-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)
+def migrate(config: ConfigTree) -> None:
base = ['interfaces', 'wirelessmodem']
if not config.exists(base):
# Nothing to do
- exit(0)
+ return
new_base = ['interfaces', 'wwan']
config.set(new_base)
@@ -98,10 +84,3 @@ if __name__ == '__main__':
# the new wwan interface use regular IP addressing
config.set(new_base + [interface, 'address'], value='dhcp')
-
- 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/interfaces/19-to-20 b/src/migration-scripts/interfaces/19-to-20
index 05abae898..7ee6302e2 100755..100644
--- a/src/migration-scripts/interfaces/19-to-20
+++ b/src/migration-scripts/interfaces/19-to-20
@@ -1,34 +1,21 @@
-#!/usr/bin/env python3
+# Copyright 2021-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2021 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 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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 sys import argv
-from sys import exit
from vyos.configtree import ConfigTree
-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)
-
+def migrate(config: ConfigTree) -> None:
for type in ['tunnel', 'l2tpv3']:
base = ['interfaces', type]
if not config.exists(base):
@@ -52,10 +39,3 @@ if __name__ == '__main__':
remote_ip_path = base + [interface, 'remote-ip']
if config.exists(remote_ip_path):
config.rename(remote_ip_path, 'remote')
-
- 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/interfaces/2-to-3 b/src/migration-scripts/interfaces/2-to-3
index 15c3bc8be..695dcbf7a 100755..100644
--- a/src/migration-scripts/interfaces/2-to-3
+++ b/src/migration-scripts/interfaces/2-to-3
@@ -1,28 +1,31 @@
-#!/usr/bin/env python3
+# 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
+# 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, see <http://www.gnu.org/licenses/>.
# Change syntax of openvpn encryption settings
# - move cipher from encryption to encryption cipher
# https://vyos.dev/T1704
-import sys
from vyos.configtree import ConfigTree
-if len(sys.argv) < 2:
- print("Must specify file name!")
- sys.exit(1)
-
-file_name = sys.argv[1]
-
-with open(file_name, 'r') as f:
- config_file = f.read()
-
-config = ConfigTree(config_file)
base = ['interfaces', 'openvpn']
-if not config.exists(base):
- # Nothing to do
- sys.exit(0)
-else:
+def migrate(config: ConfigTree) -> None:
+
+ if not config.exists(base):
+ # Nothing to do
+ return
#
# move cipher from "encryption" to "encryption cipher"
#
@@ -35,9 +38,3 @@ else:
config.delete(['interfaces', 'openvpn', intf, 'encryption'])
# Add new syntax to config
config.set(['interfaces', 'openvpn', intf, 'encryption', 'cipher'], value=cipher)
- 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))
- sys.exit(1)
diff --git a/src/migration-scripts/interfaces/20-to-21 b/src/migration-scripts/interfaces/20-to-21
index 14ad0fe4d..0b6895177 100755..100644
--- a/src/migration-scripts/interfaces/20-to-21
+++ b/src/migration-scripts/interfaces/20-to-21
@@ -1,120 +1,107 @@
-#!/usr/bin/env python3
+# Copyright 2021-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2021 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 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# T3619: mirror Linux Kernel defaults for ethernet offloading options into VyOS
# CLI. See https://vyos.dev/T3619#102254 for all the details.
# T3787: Remove deprecated UDP fragmentation offloading option
-from sys import argv
-
from vyos.ethtool import Ethtool
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()
+from vyos.utils.network import interface_exists
base = ['interfaces', 'ethernet']
-config = ConfigTree(config_file)
-
-if not config.exists(base):
- exit(0)
-
-for ifname in config.list_nodes(base):
- eth = Ethtool(ifname)
-
- # If GRO is enabled by the Kernel - we reflect this on the CLI. If GRO is
- # enabled via CLI but not supported by the NIC - we remove it from the CLI
- configured = config.exists(base + [ifname, 'offload', 'gro'])
- enabled, fixed = eth.get_generic_receive_offload()
- if configured and fixed:
- config.delete(base + [ifname, 'offload', 'gro'])
- elif enabled and not fixed:
- config.set(base + [ifname, 'offload', 'gro'])
-
- # If GSO is enabled by the Kernel - we reflect this on the CLI. If GSO is
- # enabled via CLI but not supported by the NIC - we remove it from the CLI
- configured = config.exists(base + [ifname, 'offload', 'gso'])
- enabled, fixed = eth.get_generic_segmentation_offload()
- if configured and fixed:
- config.delete(base + [ifname, 'offload', 'gso'])
- elif enabled and not fixed:
- config.set(base + [ifname, 'offload', 'gso'])
-
- # If LRO is enabled by the Kernel - we reflect this on the CLI. If LRO is
- # enabled via CLI but not supported by the NIC - we remove it from the CLI
- configured = config.exists(base + [ifname, 'offload', 'lro'])
- enabled, fixed = eth.get_large_receive_offload()
- if configured and fixed:
- config.delete(base + [ifname, 'offload', 'lro'])
- elif enabled and not fixed:
- config.set(base + [ifname, 'offload', 'lro'])
-
- # If SG is enabled by the Kernel - we reflect this on the CLI. If SG is
- # enabled via CLI but not supported by the NIC - we remove it from the CLI
- configured = config.exists(base + [ifname, 'offload', 'sg'])
- enabled, fixed = eth.get_scatter_gather()
- if configured and fixed:
- config.delete(base + [ifname, 'offload', 'sg'])
- elif enabled and not fixed:
- config.set(base + [ifname, 'offload', 'sg'])
-
- # If TSO is enabled by the Kernel - we reflect this on the CLI. If TSO is
- # enabled via CLI but not supported by the NIC - we remove it from the CLI
- configured = config.exists(base + [ifname, 'offload', 'tso'])
- enabled, fixed = eth.get_tcp_segmentation_offload()
- if configured and fixed:
- config.delete(base + [ifname, 'offload', 'tso'])
- elif enabled and not fixed:
- config.set(base + [ifname, 'offload', 'tso'])
-
- # Remove deprecated UDP fragmentation offloading option
- if config.exists(base + [ifname, 'offload', 'ufo']):
- config.delete(base + [ifname, 'offload', 'ufo'])
-
- # Also while processing the interface configuration, not all adapters support
- # changing the speed and duplex settings. If the desired speed and duplex
- # values do not work for the NIC driver, we change them back to the default
- # value of "auto" - which will be applied if the CLI node is deleted.
- speed_path = base + [ifname, 'speed']
- duplex_path = base + [ifname, 'duplex']
- # speed and duplex must always be set at the same time if not set to "auto"
- if config.exists(speed_path) and config.exists(duplex_path):
- speed = config.return_value(speed_path)
- duplex = config.return_value(duplex_path)
- if speed != 'auto' and duplex != 'auto':
- if not eth.check_speed_duplex(speed, duplex):
- config.delete(speed_path)
- config.delete(duplex_path)
-
- # Also while processing the interface configuration, not all adapters support
- # changing disabling flow-control - or change this setting. If disabling
- # flow-control is not supported by the NIC, we remove the setting from CLI
- flow_control_path = base + [ifname, 'disable-flow-control']
- if config.exists(flow_control_path):
- if not eth.check_flow_control():
- config.delete(flow_control_path)
-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 migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ return
+
+ for ifname in config.list_nodes(base):
+ # Bail out early if interface vanished from system
+ if not interface_exists(ifname):
+ continue
+
+ eth = Ethtool(ifname)
+
+ # If GRO is enabled by the Kernel - we reflect this on the CLI. If GRO is
+ # enabled via CLI but not supported by the NIC - we remove it from the CLI
+ configured = config.exists(base + [ifname, 'offload', 'gro'])
+ enabled, fixed = eth.get_generic_receive_offload()
+ if configured and fixed:
+ config.delete(base + [ifname, 'offload', 'gro'])
+ elif enabled and not fixed:
+ config.set(base + [ifname, 'offload', 'gro'])
+
+ # If GSO is enabled by the Kernel - we reflect this on the CLI. If GSO is
+ # enabled via CLI but not supported by the NIC - we remove it from the CLI
+ configured = config.exists(base + [ifname, 'offload', 'gso'])
+ enabled, fixed = eth.get_generic_segmentation_offload()
+ if configured and fixed:
+ config.delete(base + [ifname, 'offload', 'gso'])
+ elif enabled and not fixed:
+ config.set(base + [ifname, 'offload', 'gso'])
+
+ # If LRO is enabled by the Kernel - we reflect this on the CLI. If LRO is
+ # enabled via CLI but not supported by the NIC - we remove it from the CLI
+ configured = config.exists(base + [ifname, 'offload', 'lro'])
+ enabled, fixed = eth.get_large_receive_offload()
+ if configured and fixed:
+ config.delete(base + [ifname, 'offload', 'lro'])
+ elif enabled and not fixed:
+ config.set(base + [ifname, 'offload', 'lro'])
+
+ # If SG is enabled by the Kernel - we reflect this on the CLI. If SG is
+ # enabled via CLI but not supported by the NIC - we remove it from the CLI
+ configured = config.exists(base + [ifname, 'offload', 'sg'])
+ enabled, fixed = eth.get_scatter_gather()
+ if configured and fixed:
+ config.delete(base + [ifname, 'offload', 'sg'])
+ elif enabled and not fixed:
+ config.set(base + [ifname, 'offload', 'sg'])
+
+ # If TSO is enabled by the Kernel - we reflect this on the CLI. If TSO is
+ # enabled via CLI but not supported by the NIC - we remove it from the CLI
+ configured = config.exists(base + [ifname, 'offload', 'tso'])
+ enabled, fixed = eth.get_tcp_segmentation_offload()
+ if configured and fixed:
+ config.delete(base + [ifname, 'offload', 'tso'])
+ elif enabled and not fixed:
+ config.set(base + [ifname, 'offload', 'tso'])
+
+ # Remove deprecated UDP fragmentation offloading option
+ if config.exists(base + [ifname, 'offload', 'ufo']):
+ config.delete(base + [ifname, 'offload', 'ufo'])
+
+ # Also while processing the interface configuration, not all adapters support
+ # changing the speed and duplex settings. If the desired speed and duplex
+ # values do not work for the NIC driver, we change them back to the default
+ # value of "auto" - which will be applied if the CLI node is deleted.
+ speed_path = base + [ifname, 'speed']
+ duplex_path = base + [ifname, 'duplex']
+ # speed and duplex must always be set at the same time if not set to "auto"
+ if config.exists(speed_path) and config.exists(duplex_path):
+ speed = config.return_value(speed_path)
+ duplex = config.return_value(duplex_path)
+ if speed != 'auto' and duplex != 'auto':
+ if not eth.check_speed_duplex(speed, duplex):
+ config.delete(speed_path)
+ config.delete(duplex_path)
+
+ # Also while processing the interface configuration, not all adapters support
+ # changing disabling flow-control - or change this setting. If disabling
+ # flow-control is not supported by the NIC, we remove the setting from CLI
+ flow_control_path = base + [ifname, 'disable-flow-control']
+ if config.exists(flow_control_path):
+ if not eth.check_flow_control():
+ config.delete(flow_control_path)
diff --git a/src/migration-scripts/interfaces/21-to-22 b/src/migration-scripts/interfaces/21-to-22
index 1838eb1c0..046eb10c6 100755..100644
--- a/src/migration-scripts/interfaces/21-to-22
+++ b/src/migration-scripts/interfaces/21-to-22
@@ -1,46 +1,29 @@
-#!/usr/bin/env python3
+# Copyright 2021-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2021 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 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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 sys import argv
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 = ['interfaces', 'tunnel']
-
-if not config.exists(base):
- exit(0)
+def migrate(config: ConfigTree) -> None:
+ base = ['interfaces', 'tunnel']
-for interface in config.list_nodes(base):
- path = base + [interface, 'dhcp-interface']
- if config.exists(path):
- tmp = config.return_value(path)
- config.delete(path)
- config.set(base + [interface, 'source-interface'], value=tmp)
+ if not config.exists(base):
+ return
-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)
+ for interface in config.list_nodes(base):
+ path = base + [interface, 'dhcp-interface']
+ if config.exists(path):
+ tmp = config.return_value(path)
+ config.delete(path)
+ config.set(base + [interface, 'source-interface'], value=tmp)
diff --git a/src/migration-scripts/interfaces/22-to-23 b/src/migration-scripts/interfaces/22-to-23
index 04e023e77..31f7fa2ff 100755..100644
--- a/src/migration-scripts/interfaces/22-to-23
+++ b/src/migration-scripts/interfaces/22-to-23
@@ -1,39 +1,29 @@
-#!/usr/bin/env python3
+# Copyright 2021-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2021-2023 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 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,
+# 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 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/>.
+# 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, see <http://www.gnu.org/licenses/>.
+
# Deletes Wireguard peers if they have the same public key as the router has.
-import sys
+
from vyos.configtree import ConfigTree
from vyos.utils.network import is_wireguard_key_pair
-if __name__ == '__main__':
- if len(sys.argv) < 2:
- print("Must specify file name!")
- sys.exit(1)
-
- file_name = sys.argv[1]
-
- with open(file_name, 'r') as f:
- config_file = f.read()
-
- config = ConfigTree(config_file)
+def migrate(config: ConfigTree) -> None:
base = ['interfaces', 'wireguard']
if not config.exists(base):
# Nothing to do
- sys.exit(0)
+ return
+
for interface in config.list_nodes(base):
if not config.exists(base + [interface, 'private-key']):
continue
@@ -48,10 +38,3 @@ if __name__ == '__main__':
if not config.exists(peer_base + ['disable']) \
and is_wireguard_key_pair(private_key, peer_public_key):
config.set(peer_base + ['disable'])
-
- 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))
- sys.exit(1)
diff --git a/src/migration-scripts/interfaces/23-to-24 b/src/migration-scripts/interfaces/23-to-24
index 8b21fce51..b72ceee49 100755..100644
--- a/src/migration-scripts/interfaces/23-to-24
+++ b/src/migration-scripts/interfaces/23-to-24
@@ -1,21 +1,18 @@
-#!/usr/bin/env python3
+# Copyright 2021-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2021 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 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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 sys import argv
-from sys import exit
from vyos.configtree import ConfigTree
def migrate_ospf(config, path, interface):
@@ -74,17 +71,7 @@ def migrate_ripng(config, path, interface):
if len(config.list_nodes(path[:-1])) == 0:
config.delete(path[:-1])
-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)
-
+def migrate(config: ConfigTree) -> None:
#
# Migrate "interface ethernet eth0 ip ospf" to "protocols ospf interface eth0"
#
@@ -136,10 +123,3 @@ if __name__ == '__main__':
migrate_ripng(config, vif_s_ipv6_base, ifname)
migrate_ospf(config, vif_s_ip_base, ifname)
migrate_ospfv3(config, vif_s_ipv6_base, ifname)
-
- 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/interfaces/24-to-25 b/src/migration-scripts/interfaces/24-to-25
index 8fd79ecc6..9f8cc80ec 100755..100644
--- a/src/migration-scripts/interfaces/24-to-25
+++ b/src/migration-scripts/interfaces/24-to-25
@@ -1,41 +1,29 @@
-#!/usr/bin/env python3
+# Copyright 2021-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2021 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 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# A VTI interface also requires an IPSec configuration - VyOS 1.2 supported
# having a VTI interface in the CLI but no IPSec configuration - drop VTI
# configuration if this is the case for VyOS 1.4
-import sys
from vyos.configtree import ConfigTree
-if __name__ == '__main__':
- if len(sys.argv) < 2:
- print("Must specify file name!")
- sys.exit(1)
-
- file_name = sys.argv[1]
-
- with open(file_name, 'r') as f:
- config_file = f.read()
-
- config = ConfigTree(config_file)
+def migrate(config: ConfigTree) -> None:
base = ['interfaces', 'vti']
if not config.exists(base):
# Nothing to do
- sys.exit(0)
+ return
ipsec_base = ['vpn', 'ipsec', 'site-to-site', 'peer']
for interface in config.list_nodes(base):
@@ -51,10 +39,3 @@ if __name__ == '__main__':
break
if not found:
config.delete(base + [interface])
-
- 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))
- sys.exit(1)
diff --git a/src/migration-scripts/interfaces/25-to-26 b/src/migration-scripts/interfaces/25-to-26
index 9aa6ea5e3..7a4032d10 100755..100644
--- a/src/migration-scripts/interfaces/25-to-26
+++ b/src/migration-scripts/interfaces/25-to-26
@@ -1,24 +1,22 @@
-#!/usr/bin/env python3
+# Copyright 2021-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2021-2023 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 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# Migrate Wireguard to store keys in CLI
# Migrate EAPoL to PKI configuration
import os
-import sys
from vyos.configtree import ConfigTree
from vyos.pki import CERT_BEGIN
@@ -53,335 +51,318 @@ def read_file_for_pki(config_auth_path):
return output
-if len(sys.argv) < 2:
- print("Must specify file name!")
- sys.exit(1)
-
-file_name = sys.argv[1]
-
-with open(file_name, 'r') as f:
- config_file = f.read()
-
-config = ConfigTree(config_file)
-
AUTH_DIR = '/config/auth'
pki_base = ['pki']
-# OpenVPN
-base = ['interfaces', 'openvpn']
+def migrate(config: ConfigTree) -> None:
+ # OpenVPN
+ base = ['interfaces', 'openvpn']
+
+ if config.exists(base):
+ for interface in config.list_nodes(base):
+ x509_base = base + [interface, 'tls']
+ pki_name = f'openvpn_{interface}'
+
+ if config.exists(base + [interface, 'shared-secret-key-file']):
+ if not config.exists(pki_base + ['openvpn', 'shared-secret']):
+ config.set(pki_base + ['openvpn', 'shared-secret'])
+ config.set_tag(pki_base + ['openvpn', 'shared-secret'])
+
+ key_file = config.return_value(base + [interface, 'shared-secret-key-file'])
+ key = read_file_for_pki(key_file)
+ key_pki_name = f'{pki_name}_shared'
+
+ if key:
+ config.set(pki_base + ['openvpn', 'shared-secret', key_pki_name, 'key'], value=wrapped_pem_to_config_value(key))
+ config.set(pki_base + ['openvpn', 'shared-secret', key_pki_name, 'version'], value='1')
+ config.set(base + [interface, 'shared-secret-key'], value=key_pki_name)
+ else:
+ print(f'Failed to migrate shared-secret-key on openvpn interface {interface}')
+
+ config.delete(base + [interface, 'shared-secret-key-file'])
+
+ if not config.exists(base + [interface, 'tls']):
+ continue
+
+ if config.exists(base + [interface, 'tls', 'auth-file']):
+ if not config.exists(pki_base + ['openvpn', 'shared-secret']):
+ config.set(pki_base + ['openvpn', 'shared-secret'])
+ config.set_tag(pki_base + ['openvpn', 'shared-secret'])
+
+ key_file = config.return_value(base + [interface, 'tls', 'auth-file'])
+ key = read_file_for_pki(key_file)
+ key_pki_name = f'{pki_name}_auth'
+
+ if key:
+ config.set(pki_base + ['openvpn', 'shared-secret', key_pki_name, 'key'], value=wrapped_pem_to_config_value(key))
+ config.set(pki_base + ['openvpn', 'shared-secret', key_pki_name, 'version'], value='1')
+ config.set(base + [interface, 'tls', 'auth-key'], value=key_pki_name)
+ else:
+ print(f'Failed to migrate auth-key on openvpn interface {interface}')
+
+ config.delete(base + [interface, 'tls', 'auth-file'])
+
+ if config.exists(base + [interface, 'tls', 'crypt-file']):
+ if not config.exists(pki_base + ['openvpn', 'shared-secret']):
+ config.set(pki_base + ['openvpn', 'shared-secret'])
+ config.set_tag(pki_base + ['openvpn', 'shared-secret'])
+
+ key_file = config.return_value(base + [interface, 'tls', 'crypt-file'])
+ key = read_file_for_pki(key_file)
+ key_pki_name = f'{pki_name}_crypt'
+
+ if key:
+ config.set(pki_base + ['openvpn', 'shared-secret', key_pki_name, 'key'], value=wrapped_pem_to_config_value(key))
+ config.set(pki_base + ['openvpn', 'shared-secret', key_pki_name, 'version'], value='1')
+ config.set(base + [interface, 'tls', 'crypt-key'], value=key_pki_name)
+ else:
+ print(f'Failed to migrate crypt-key on openvpn interface {interface}')
+
+ config.delete(base + [interface, 'tls', 'crypt-file'])
+
+ ca_certs = {}
+
+ if config.exists(x509_base + ['ca-cert-file']):
+ if not config.exists(pki_base + ['ca']):
+ config.set(pki_base + ['ca'])
+ config.set_tag(pki_base + ['ca'])
+
+ cert_file = config.return_value(x509_base + ['ca-cert-file'])
+ cert_path = os.path.join(AUTH_DIR, cert_file)
+
+ if os.path.isfile(cert_path):
+ if not os.access(cert_path, os.R_OK):
+ run(f'sudo chmod 644 {cert_path}')
+
+ with open(cert_path, 'r') as f:
+ certs_str = f.read()
+ certs_data = certs_str.split(CERT_BEGIN)
+ index = 1
+ for cert_data in certs_data[1:]:
+ cert = load_certificate(CERT_BEGIN + cert_data, wrap_tags=False)
+
+ if cert:
+ ca_certs[f'{pki_name}_{index}'] = cert
+ cert_pem = encode_certificate(cert)
+ config.set(pki_base + ['ca', f'{pki_name}_{index}', 'certificate'], value=wrapped_pem_to_config_value(cert_pem))
+ config.set(x509_base + ['ca-certificate'], value=f'{pki_name}_{index}', replace=False)
+ else:
+ print(f'Failed to migrate CA certificate on openvpn interface {interface}')
+
+ index += 1
+ else:
+ print(f'Failed to migrate CA certificate on openvpn interface {interface}')
+
+ config.delete(x509_base + ['ca-cert-file'])
+
+ if config.exists(x509_base + ['crl-file']):
+ if not config.exists(pki_base + ['ca']):
+ config.set(pki_base + ['ca'])
+ config.set_tag(pki_base + ['ca'])
+
+ crl_file = config.return_value(x509_base + ['crl-file'])
+ crl_path = os.path.join(AUTH_DIR, crl_file)
+ crl = None
+ crl_ca_name = None
+
+ if os.path.isfile(crl_path):
+ if not os.access(crl_path, os.R_OK):
+ run(f'sudo chmod 644 {crl_path}')
+
+ with open(crl_path, 'r') as f:
+ crl_data = f.read()
+ crl = load_crl(crl_data, wrap_tags=False)
+
+ for ca_name, ca_cert in ca_certs.items():
+ if verify_crl(crl, ca_cert):
+ crl_ca_name = ca_name
+ break
+
+ if crl and crl_ca_name:
+ crl_pem = encode_certificate(crl)
+ config.set(pki_base + ['ca', crl_ca_name, 'crl'], value=wrapped_pem_to_config_value(crl_pem))
+ else:
+ print(f'Failed to migrate CRL on openvpn interface {interface}')
+
+ config.delete(x509_base + ['crl-file'])
+
+ if config.exists(x509_base + ['cert-file']):
+ if not config.exists(pki_base + ['certificate']):
+ config.set(pki_base + ['certificate'])
+ config.set_tag(pki_base + ['certificate'])
+
+ cert_file = config.return_value(x509_base + ['cert-file'])
+ cert_path = os.path.join(AUTH_DIR, cert_file)
+ cert = None
+
+ if os.path.isfile(cert_path):
+ if not os.access(cert_path, os.R_OK):
+ run(f'sudo chmod 644 {cert_path}')
+
+ with open(cert_path, 'r') as f:
+ cert_data = f.read()
+ cert = load_certificate(cert_data, wrap_tags=False)
+
+ if cert:
+ cert_pem = encode_certificate(cert)
+ config.set(pki_base + ['certificate', pki_name, 'certificate'], value=wrapped_pem_to_config_value(cert_pem))
+ config.set(x509_base + ['certificate'], value=pki_name)
+ else:
+ print(f'Failed to migrate certificate on openvpn interface {interface}')
+
+ config.delete(x509_base + ['cert-file'])
+
+ if config.exists(x509_base + ['key-file']):
+ key_file = config.return_value(x509_base + ['key-file'])
+ key_path = os.path.join(AUTH_DIR, key_file)
+ key = None
+
+ if os.path.isfile(key_path):
+ if not os.access(key_path, os.R_OK):
+ run(f'sudo chmod 644 {key_path}')
+
+ with open(key_path, 'r') as f:
+ key_data = f.read()
+ key = load_private_key(key_data, passphrase=None, wrap_tags=False)
+
+ if key:
+ key_pem = encode_private_key(key, passphrase=None)
+ config.set(pki_base + ['certificate', pki_name, 'private', 'key'], value=wrapped_pem_to_config_value(key_pem))
+ else:
+ print(f'Failed to migrate private key on openvpn interface {interface}')
+
+ config.delete(x509_base + ['key-file'])
+
+ if config.exists(x509_base + ['dh-file']):
+ if not config.exists(pki_base + ['dh']):
+ config.set(pki_base + ['dh'])
+ config.set_tag(pki_base + ['dh'])
+
+ dh_file = config.return_value(x509_base + ['dh-file'])
+ dh_path = os.path.join(AUTH_DIR, dh_file)
+ dh = None
+
+ if os.path.isfile(dh_path):
+ if not os.access(dh_path, os.R_OK):
+ run(f'sudo chmod 644 {dh_path}')
+
+ with open(dh_path, 'r') as f:
+ dh_data = f.read()
+ dh = load_dh_parameters(dh_data, wrap_tags=False)
+
+ if dh:
+ dh_pem = encode_dh_parameters(dh)
+ config.set(pki_base + ['dh', pki_name, 'parameters'], value=wrapped_pem_to_config_value(dh_pem))
+ config.set(x509_base + ['dh-params'], value=pki_name)
+ else:
+ print(f'Failed to migrate DH parameters on openvpn interface {interface}')
+
+ config.delete(x509_base + ['dh-file'])
+
+ # Wireguard
+ base = ['interfaces', 'wireguard']
+
+ if config.exists(base):
+ for interface in config.list_nodes(base):
+ private_key_path = base + [interface, 'private-key']
+
+ key_file = 'default'
+ if config.exists(private_key_path):
+ key_file = config.return_value(private_key_path)
+
+ full_key_path = f'/config/auth/wireguard/{key_file}/private.key'
+
+ if not os.path.exists(full_key_path):
+ print(f'Could not find wireguard private key for migration on interface "{interface}"')
+ continue
+
+ with open(full_key_path, 'r') as f:
+ key_data = f.read().strip()
+ config.set(private_key_path, value=key_data)
+
+ for peer in config.list_nodes(base + [interface, 'peer']):
+ config.rename(base + [interface, 'peer', peer, 'pubkey'], 'public-key')
+
+ # Ethernet EAPoL
+ base = ['interfaces', 'ethernet']
+
+ if config.exists(base):
+ for interface in config.list_nodes(base):
+ if not config.exists(base + [interface, 'eapol']):
+ continue
+
+ x509_base = base + [interface, 'eapol']
+ pki_name = f'eapol_{interface}'
+
+ if config.exists(x509_base + ['ca-cert-file']):
+ if not config.exists(pki_base + ['ca']):
+ config.set(pki_base + ['ca'])
+ config.set_tag(pki_base + ['ca'])
+
+ cert_file = config.return_value(x509_base + ['ca-cert-file'])
+ cert_path = os.path.join(AUTH_DIR, cert_file)
+ cert = None
+
+ if os.path.isfile(cert_path):
+ if not os.access(cert_path, os.R_OK):
+ run(f'sudo chmod 644 {cert_path}')
+
+ with open(cert_path, 'r') as f:
+ cert_data = f.read()
+ cert = load_certificate(cert_data, wrap_tags=False)
+
+ if cert:
+ cert_pem = encode_certificate(cert)
+ config.set(pki_base + ['ca', pki_name, 'certificate'], value=wrapped_pem_to_config_value(cert_pem))
+ config.set(x509_base + ['ca-certificate'], value=pki_name)
+ else:
+ print(f'Failed to migrate CA certificate on eapol config for interface {interface}')
+
+ config.delete(x509_base + ['ca-cert-file'])
-if config.exists(base):
- for interface in config.list_nodes(base):
- x509_base = base + [interface, 'tls']
- pki_name = f'openvpn_{interface}'
+ if config.exists(x509_base + ['cert-file']):
+ if not config.exists(pki_base + ['certificate']):
+ config.set(pki_base + ['certificate'])
+ config.set_tag(pki_base + ['certificate'])
- if config.exists(base + [interface, 'shared-secret-key-file']):
- if not config.exists(pki_base + ['openvpn', 'shared-secret']):
- config.set(pki_base + ['openvpn', 'shared-secret'])
- config.set_tag(pki_base + ['openvpn', 'shared-secret'])
+ cert_file = config.return_value(x509_base + ['cert-file'])
+ cert_path = os.path.join(AUTH_DIR, cert_file)
+ cert = None
- key_file = config.return_value(base + [interface, 'shared-secret-key-file'])
- key = read_file_for_pki(key_file)
- key_pki_name = f'{pki_name}_shared'
+ if os.path.isfile(cert_path):
+ if not os.access(cert_path, os.R_OK):
+ run(f'sudo chmod 644 {cert_path}')
- if key:
- config.set(pki_base + ['openvpn', 'shared-secret', key_pki_name, 'key'], value=wrapped_pem_to_config_value(key))
- config.set(pki_base + ['openvpn', 'shared-secret', key_pki_name, 'version'], value='1')
- config.set(base + [interface, 'shared-secret-key'], value=key_pki_name)
- else:
- print(f'Failed to migrate shared-secret-key on openvpn interface {interface}')
+ with open(cert_path, 'r') as f:
+ cert_data = f.read()
+ cert = load_certificate(cert_data, wrap_tags=False)
- config.delete(base + [interface, 'shared-secret-key-file'])
+ if cert:
+ cert_pem = encode_certificate(cert)
+ config.set(pki_base + ['certificate', pki_name, 'certificate'], value=wrapped_pem_to_config_value(cert_pem))
+ config.set(x509_base + ['certificate'], value=pki_name)
+ else:
+ print(f'Failed to migrate certificate on eapol config for interface {interface}')
- if not config.exists(base + [interface, 'tls']):
- continue
+ config.delete(x509_base + ['cert-file'])
- if config.exists(base + [interface, 'tls', 'auth-file']):
- if not config.exists(pki_base + ['openvpn', 'shared-secret']):
- config.set(pki_base + ['openvpn', 'shared-secret'])
- config.set_tag(pki_base + ['openvpn', 'shared-secret'])
-
- key_file = config.return_value(base + [interface, 'tls', 'auth-file'])
- key = read_file_for_pki(key_file)
- key_pki_name = f'{pki_name}_auth'
-
- if key:
- config.set(pki_base + ['openvpn', 'shared-secret', key_pki_name, 'key'], value=wrapped_pem_to_config_value(key))
- config.set(pki_base + ['openvpn', 'shared-secret', key_pki_name, 'version'], value='1')
- config.set(base + [interface, 'tls', 'auth-key'], value=key_pki_name)
- else:
- print(f'Failed to migrate auth-key on openvpn interface {interface}')
-
- config.delete(base + [interface, 'tls', 'auth-file'])
-
- if config.exists(base + [interface, 'tls', 'crypt-file']):
- if not config.exists(pki_base + ['openvpn', 'shared-secret']):
- config.set(pki_base + ['openvpn', 'shared-secret'])
- config.set_tag(pki_base + ['openvpn', 'shared-secret'])
-
- key_file = config.return_value(base + [interface, 'tls', 'crypt-file'])
- key = read_file_for_pki(key_file)
- key_pki_name = f'{pki_name}_crypt'
-
- if key:
- config.set(pki_base + ['openvpn', 'shared-secret', key_pki_name, 'key'], value=wrapped_pem_to_config_value(key))
- config.set(pki_base + ['openvpn', 'shared-secret', key_pki_name, 'version'], value='1')
- config.set(base + [interface, 'tls', 'crypt-key'], value=key_pki_name)
- else:
- print(f'Failed to migrate crypt-key on openvpn interface {interface}')
-
- config.delete(base + [interface, 'tls', 'crypt-file'])
-
- ca_certs = {}
-
- if config.exists(x509_base + ['ca-cert-file']):
- if not config.exists(pki_base + ['ca']):
- config.set(pki_base + ['ca'])
- config.set_tag(pki_base + ['ca'])
-
- cert_file = config.return_value(x509_base + ['ca-cert-file'])
- cert_path = os.path.join(AUTH_DIR, cert_file)
-
- if os.path.isfile(cert_path):
- if not os.access(cert_path, os.R_OK):
- run(f'sudo chmod 644 {cert_path}')
-
- with open(cert_path, 'r') as f:
- certs_str = f.read()
- certs_data = certs_str.split(CERT_BEGIN)
- index = 1
- for cert_data in certs_data[1:]:
- cert = load_certificate(CERT_BEGIN + cert_data, wrap_tags=False)
-
- if cert:
- ca_certs[f'{pki_name}_{index}'] = cert
- cert_pem = encode_certificate(cert)
- config.set(pki_base + ['ca', f'{pki_name}_{index}', 'certificate'], value=wrapped_pem_to_config_value(cert_pem))
- config.set(x509_base + ['ca-certificate'], value=f'{pki_name}_{index}', replace=False)
- else:
- print(f'Failed to migrate CA certificate on openvpn interface {interface}')
-
- index += 1
- else:
- print(f'Failed to migrate CA certificate on openvpn interface {interface}')
-
- config.delete(x509_base + ['ca-cert-file'])
-
- if config.exists(x509_base + ['crl-file']):
- if not config.exists(pki_base + ['ca']):
- config.set(pki_base + ['ca'])
- config.set_tag(pki_base + ['ca'])
-
- crl_file = config.return_value(x509_base + ['crl-file'])
- crl_path = os.path.join(AUTH_DIR, crl_file)
- crl = None
- crl_ca_name = None
-
- if os.path.isfile(crl_path):
- if not os.access(crl_path, os.R_OK):
- run(f'sudo chmod 644 {crl_path}')
-
- with open(crl_path, 'r') as f:
- crl_data = f.read()
- crl = load_crl(crl_data, wrap_tags=False)
-
- for ca_name, ca_cert in ca_certs.items():
- if verify_crl(crl, ca_cert):
- crl_ca_name = ca_name
- break
-
- if crl and crl_ca_name:
- crl_pem = encode_certificate(crl)
- config.set(pki_base + ['ca', crl_ca_name, 'crl'], value=wrapped_pem_to_config_value(crl_pem))
- else:
- print(f'Failed to migrate CRL on openvpn interface {interface}')
-
- config.delete(x509_base + ['crl-file'])
-
- if config.exists(x509_base + ['cert-file']):
- if not config.exists(pki_base + ['certificate']):
- config.set(pki_base + ['certificate'])
- config.set_tag(pki_base + ['certificate'])
-
- cert_file = config.return_value(x509_base + ['cert-file'])
- cert_path = os.path.join(AUTH_DIR, cert_file)
- cert = None
-
- if os.path.isfile(cert_path):
- if not os.access(cert_path, os.R_OK):
- run(f'sudo chmod 644 {cert_path}')
-
- with open(cert_path, 'r') as f:
- cert_data = f.read()
- cert = load_certificate(cert_data, wrap_tags=False)
-
- if cert:
- cert_pem = encode_certificate(cert)
- config.set(pki_base + ['certificate', pki_name, 'certificate'], value=wrapped_pem_to_config_value(cert_pem))
- config.set(x509_base + ['certificate'], value=pki_name)
- else:
- print(f'Failed to migrate certificate on openvpn interface {interface}')
-
- config.delete(x509_base + ['cert-file'])
-
- if config.exists(x509_base + ['key-file']):
- key_file = config.return_value(x509_base + ['key-file'])
- key_path = os.path.join(AUTH_DIR, key_file)
- key = None
-
- if os.path.isfile(key_path):
- if not os.access(key_path, os.R_OK):
- run(f'sudo chmod 644 {key_path}')
-
- with open(key_path, 'r') as f:
- key_data = f.read()
- key = load_private_key(key_data, passphrase=None, wrap_tags=False)
-
- if key:
- key_pem = encode_private_key(key, passphrase=None)
- config.set(pki_base + ['certificate', pki_name, 'private', 'key'], value=wrapped_pem_to_config_value(key_pem))
- else:
- print(f'Failed to migrate private key on openvpn interface {interface}')
-
- config.delete(x509_base + ['key-file'])
-
- if config.exists(x509_base + ['dh-file']):
- if not config.exists(pki_base + ['dh']):
- config.set(pki_base + ['dh'])
- config.set_tag(pki_base + ['dh'])
-
- dh_file = config.return_value(x509_base + ['dh-file'])
- dh_path = os.path.join(AUTH_DIR, dh_file)
- dh = None
-
- if os.path.isfile(dh_path):
- if not os.access(dh_path, os.R_OK):
- run(f'sudo chmod 644 {dh_path}')
-
- with open(dh_path, 'r') as f:
- dh_data = f.read()
- dh = load_dh_parameters(dh_data, wrap_tags=False)
-
- if dh:
- dh_pem = encode_dh_parameters(dh)
- config.set(pki_base + ['dh', pki_name, 'parameters'], value=wrapped_pem_to_config_value(dh_pem))
- config.set(x509_base + ['dh-params'], value=pki_name)
- else:
- print(f'Failed to migrate DH parameters on openvpn interface {interface}')
-
- config.delete(x509_base + ['dh-file'])
-
-# Wireguard
-base = ['interfaces', 'wireguard']
-
-if config.exists(base):
- for interface in config.list_nodes(base):
- private_key_path = base + [interface, 'private-key']
-
- key_file = 'default'
- if config.exists(private_key_path):
- key_file = config.return_value(private_key_path)
-
- full_key_path = f'/config/auth/wireguard/{key_file}/private.key'
+ if config.exists(x509_base + ['key-file']):
+ key_file = config.return_value(x509_base + ['key-file'])
+ key_path = os.path.join(AUTH_DIR, key_file)
+ key = None
+
+ if os.path.isfile(key_path):
+ if not os.access(key_path, os.R_OK):
+ run(f'sudo chmod 644 {key_path}')
- if not os.path.exists(full_key_path):
- print(f'Could not find wireguard private key for migration on interface "{interface}"')
- continue
-
- with open(full_key_path, 'r') as f:
- key_data = f.read().strip()
- config.set(private_key_path, value=key_data)
-
- for peer in config.list_nodes(base + [interface, 'peer']):
- config.rename(base + [interface, 'peer', peer, 'pubkey'], 'public-key')
+ with open(key_path, 'r') as f:
+ key_data = f.read()
+ key = load_private_key(key_data, passphrase=None, wrap_tags=False)
-# Ethernet EAPoL
-base = ['interfaces', 'ethernet']
-
-if config.exists(base):
- for interface in config.list_nodes(base):
- if not config.exists(base + [interface, 'eapol']):
- continue
+ if key:
+ key_pem = encode_private_key(key, passphrase=None)
+ config.set(pki_base + ['certificate', pki_name, 'private', 'key'], value=wrapped_pem_to_config_value(key_pem))
+ else:
+ print(f'Failed to migrate private key on eapol config for interface {interface}')
- x509_base = base + [interface, 'eapol']
- pki_name = f'eapol_{interface}'
-
- if config.exists(x509_base + ['ca-cert-file']):
- if not config.exists(pki_base + ['ca']):
- config.set(pki_base + ['ca'])
- config.set_tag(pki_base + ['ca'])
-
- cert_file = config.return_value(x509_base + ['ca-cert-file'])
- cert_path = os.path.join(AUTH_DIR, cert_file)
- cert = None
-
- if os.path.isfile(cert_path):
- if not os.access(cert_path, os.R_OK):
- run(f'sudo chmod 644 {cert_path}')
-
- with open(cert_path, 'r') as f:
- cert_data = f.read()
- cert = load_certificate(cert_data, wrap_tags=False)
-
- if cert:
- cert_pem = encode_certificate(cert)
- config.set(pki_base + ['ca', pki_name, 'certificate'], value=wrapped_pem_to_config_value(cert_pem))
- config.set(x509_base + ['ca-certificate'], value=pki_name)
- else:
- print(f'Failed to migrate CA certificate on eapol config for interface {interface}')
-
- config.delete(x509_base + ['ca-cert-file'])
-
- if config.exists(x509_base + ['cert-file']):
- if not config.exists(pki_base + ['certificate']):
- config.set(pki_base + ['certificate'])
- config.set_tag(pki_base + ['certificate'])
-
- cert_file = config.return_value(x509_base + ['cert-file'])
- cert_path = os.path.join(AUTH_DIR, cert_file)
- cert = None
-
- if os.path.isfile(cert_path):
- if not os.access(cert_path, os.R_OK):
- run(f'sudo chmod 644 {cert_path}')
-
- with open(cert_path, 'r') as f:
- cert_data = f.read()
- cert = load_certificate(cert_data, wrap_tags=False)
-
- if cert:
- cert_pem = encode_certificate(cert)
- config.set(pki_base + ['certificate', pki_name, 'certificate'], value=wrapped_pem_to_config_value(cert_pem))
- config.set(x509_base + ['certificate'], value=pki_name)
- else:
- print(f'Failed to migrate certificate on eapol config for interface {interface}')
-
- config.delete(x509_base + ['cert-file'])
-
- if config.exists(x509_base + ['key-file']):
- key_file = config.return_value(x509_base + ['key-file'])
- key_path = os.path.join(AUTH_DIR, key_file)
- key = None
-
- if os.path.isfile(key_path):
- if not os.access(key_path, os.R_OK):
- run(f'sudo chmod 644 {key_path}')
-
- with open(key_path, 'r') as f:
- key_data = f.read()
- key = load_private_key(key_data, passphrase=None, wrap_tags=False)
-
- if key:
- key_pem = encode_private_key(key, passphrase=None)
- config.set(pki_base + ['certificate', pki_name, 'private', 'key'], value=wrapped_pem_to_config_value(key_pem))
- else:
- print(f'Failed to migrate private key on eapol config for interface {interface}')
-
- config.delete(x509_base + ['key-file'])
-
-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))
- sys.exit(1)
+ config.delete(x509_base + ['key-file'])
diff --git a/src/migration-scripts/interfaces/26-to-27 b/src/migration-scripts/interfaces/26-to-27
index 429ab650f..3f58de02c 100755..100644
--- a/src/migration-scripts/interfaces/26-to-27
+++ b/src/migration-scripts/interfaces/26-to-27
@@ -1,52 +1,35 @@
-#!/usr/bin/env python3
+# Copyright 2022-2024 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
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
#
-# 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# T4384: pppoe: replace default-route CLI option with common CLI nodes already
# present for DHCP
-from sys import argv
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 = ['interfaces', 'pppoe']
-config = ConfigTree(config_file)
-
-if not config.exists(base):
- exit(0)
-
-for ifname in config.list_nodes(base):
- tmp_config = base + [ifname, 'default-route']
- if config.exists(tmp_config):
- # Retrieve current config value
- value = config.return_value(tmp_config)
- # Delete old Config node
- config.delete(tmp_config)
- if value == 'none':
- config.set(base + [ifname, 'no-default-route'])
-
-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)
+def migrate(config: ConfigTree) -> None:
+ base = ['interfaces', 'pppoe']
+
+ if not config.exists(base):
+ return
+
+ for ifname in config.list_nodes(base):
+ tmp_config = base + [ifname, 'default-route']
+ if config.exists(tmp_config):
+ # Retrieve current config value
+ value = config.return_value(tmp_config)
+ # Delete old Config node
+ config.delete(tmp_config)
+ if value == 'none':
+ config.set(base + [ifname, 'no-default-route'])
diff --git a/src/migration-scripts/interfaces/27-to-28 b/src/migration-scripts/interfaces/27-to-28
index 9f5e93b5f..eb9363e39 100755..100644
--- a/src/migration-scripts/interfaces/27-to-28
+++ b/src/migration-scripts/interfaces/27-to-28
@@ -1,48 +1,29 @@
-#!/usr/bin/env python3
+# Copyright 2023-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2023-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; either
+# version 2.1 of the License, or (at your option) any later version.
#
-# 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# T4995: pppoe, wwan, sstpc-client rename "authentication user" CLI node
# to "authentication username"
-from sys import argv
-
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)
-
-for type in ['pppoe', 'sstpc-client', 'wwam']:
- base = ['interfaces', type]
- if not config.exists(base):
- continue
- for interface in config.list_nodes(base):
- auth_base = base + [interface, 'authentication', 'user']
- if config.exists(auth_base):
- config.rename(auth_base, 'username')
-
-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)
+def migrate(config: ConfigTree) -> None:
+ for type in ['pppoe', 'sstpc-client', 'wwam']:
+ base = ['interfaces', type]
+ if not config.exists(base):
+ continue
+ for interface in config.list_nodes(base):
+ auth_base = base + [interface, 'authentication', 'user']
+ if config.exists(auth_base):
+ config.rename(auth_base, 'username')
diff --git a/src/migration-scripts/interfaces/28-to-29 b/src/migration-scripts/interfaces/28-to-29
index 0437977dc..886d49e2c 100755..100644
--- a/src/migration-scripts/interfaces/28-to-29
+++ b/src/migration-scripts/interfaces/28-to-29
@@ -1,52 +1,35 @@
-#!/usr/bin/env python3
+# Copyright 2023-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2023-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; either
+# version 2.1 of the License, or (at your option) any later version.
#
-# 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# T5034: tunnel: rename "multicast enable" CLI node to "enable-multicast"
# valueless node.
-from sys import argv
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 = ['interfaces', 'tunnel']
-config = ConfigTree(config_file)
-
-if not config.exists(base):
- exit(0)
-
-for ifname in config.list_nodes(base):
- multicast_base = base + [ifname, 'multicast']
- if config.exists(multicast_base):
- tmp = config.return_value(multicast_base)
- print(tmp)
- # Delete old Config node
- config.delete(multicast_base)
- if tmp == 'enable':
- config.set(base + [ifname, 'enable-multicast'])
-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)
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ return
+
+ for ifname in config.list_nodes(base):
+ multicast_base = base + [ifname, 'multicast']
+ if config.exists(multicast_base):
+ tmp = config.return_value(multicast_base)
+ print(tmp)
+ # Delete old Config node
+ config.delete(multicast_base)
+ if tmp == 'enable':
+ config.set(base + [ifname, 'enable-multicast'])
diff --git a/src/migration-scripts/interfaces/29-to-30 b/src/migration-scripts/interfaces/29-to-30
index 80aad1d44..7b32d871e 100755..100644
--- a/src/migration-scripts/interfaces/29-to-30
+++ b/src/migration-scripts/interfaces/29-to-30
@@ -1,47 +1,30 @@
-#!/usr/bin/env python3
+# Copyright 2023-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2023-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; either
+# version 2.1 of the License, or (at your option) any later version.
#
-# 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# T5286: remove XDP support in favour of VPP
-from sys import argv
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()
-
supports_xdp = ['bonding', 'ethernet']
-config = ConfigTree(config_file)
-
-for if_type in supports_xdp:
- base = ['interfaces', if_type]
- if not config.exists(base):
- continue
- for interface in config.list_nodes(base):
- if_base = base + [interface]
- if config.exists(if_base + ['xdp']):
- config.delete(if_base + ['xdp'])
-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)
+def migrate(config: ConfigTree) -> None:
+ for if_type in supports_xdp:
+ base = ['interfaces', if_type]
+ if not config.exists(base):
+ continue
+ for interface in config.list_nodes(base):
+ if_base = base + [interface]
+ if config.exists(if_base + ['xdp']):
+ config.delete(if_base + ['xdp'])
diff --git a/src/migration-scripts/interfaces/3-to-4 b/src/migration-scripts/interfaces/3-to-4
index c7fd7d01d..4e56200e1 100755..100644
--- a/src/migration-scripts/interfaces/3-to-4
+++ b/src/migration-scripts/interfaces/3-to-4
@@ -1,27 +1,30 @@
-#!/usr/bin/env python3
+# 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
+# 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, see <http://www.gnu.org/licenses/>.
# Change syntax of wireless interfaces
# Migrate boolean nodes to valueless
-import sys
from vyos.configtree import ConfigTree
-if len(sys.argv) < 2:
- print("Must specify file name!")
- sys.exit(1)
-
-file_name = sys.argv[1]
-
-with open(file_name, 'r') as f:
- config_file = f.read()
-
-config = ConfigTree(config_file)
base = ['interfaces', 'wireless']
-if not config.exists(base):
- # Nothing to do
- sys.exit(0)
-else:
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ # Nothing to do
+ return
+
for wifi in config.list_nodes(base):
# as converting a node to bool is always the same, we can script it
to_bool_nodes = ['capabilities ht 40MHz-incapable',
@@ -88,10 +91,3 @@ else:
# delete old radius-server nodes
config.delete(base + [wifi, 'security', 'wpa', 'radius-server'])
-
- 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))
- sys.exit(1)
diff --git a/src/migration-scripts/interfaces/30-to-31 b/src/migration-scripts/interfaces/30-to-31
index 894106ef4..7e509dd86 100755..100644
--- a/src/migration-scripts/interfaces/30-to-31
+++ b/src/migration-scripts/interfaces/30-to-31
@@ -1,71 +1,56 @@
#!/usr/bin/env python3
+# Copyright 2023-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2021-2023 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 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,
+# 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 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/>.
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
#
-# Deletes Wireguard peers if they have the same public key as the router has.
+# 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/>.
+
+# T5254: Fixed changing ethernet when it is a bond member
import json
-from sys import argv
-from sys import exit
from vyos.configtree import ConfigTree
from vyos.ifconfig import EthernetIf
from vyos.ifconfig import BondIf
from vyos.utils.dict import dict_to_paths_values
-if len(argv) < 2:
- print("Must specify file name!")
- exit(1)
+base = ['interfaces', 'bonding']
-file_name = argv[1]
-with open(file_name, 'r') as f:
- config_file = f.read()
- base = ['interfaces', 'bonding']
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ # Nothing to do
+ return
-config = ConfigTree(config_file)
-if not config.exists(base):
- # Nothing to do
- exit(0)
-for bond in config.list_nodes(base):
- member_base = base + [bond, 'member', 'interface']
- if config.exists(member_base):
- for interface in config.return_values(member_base):
- if_base = ['interfaces', 'ethernet', interface]
- if config.exists(if_base):
- config_ethernet = json.loads(config.get_subtree(if_base).to_json())
- eth_dict_paths = dict_to_paths_values(config_ethernet)
- for option_path, option_value in eth_dict_paths.items():
- # If option is allowed for changing then continue
- converted_path = option_path.replace('-','_')
- if converted_path in EthernetIf.get_bond_member_allowed_options():
- continue
- # if option is inherited from bond then continue
- if converted_path in BondIf.get_inherit_bond_options():
- continue
- option_path_list = option_path.split('.')
- config.delete(if_base + option_path_list)
- del option_path_list[-1]
- # delete empty node from config
- while len(option_path_list) > 0:
- if config.list_nodes(if_base + option_path_list):
- break
+ for bond in config.list_nodes(base):
+ member_base = base + [bond, 'member', 'interface']
+ if config.exists(member_base):
+ for interface in config.return_values(member_base):
+ if_base = ['interfaces', 'ethernet', interface]
+ if config.exists(if_base):
+ config_ethernet = json.loads(config.get_subtree(if_base).to_json())
+ eth_dict_paths = dict_to_paths_values(config_ethernet)
+ for option_path, option_value in eth_dict_paths.items():
+ # If option is allowed for changing then continue
+ converted_path = option_path.replace('-','_')
+ if converted_path in EthernetIf.get_bond_member_allowed_options():
+ continue
+ # if option is inherited from bond then continue
+ if converted_path in BondIf.get_inherit_bond_options():
+ continue
+ option_path_list = option_path.split('.')
config.delete(if_base + option_path_list)
del option_path_list[-1]
-
-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)
+ # delete empty node from config
+ while len(option_path_list) > 0:
+ if config.list_nodes(if_base + option_path_list):
+ break
+ config.delete(if_base + option_path_list)
+ del option_path_list[-1]
diff --git a/src/migration-scripts/interfaces/31-to-32 b/src/migration-scripts/interfaces/31-to-32
index 0fc27b70a..24077ed24 100755..100644
--- a/src/migration-scripts/interfaces/31-to-32
+++ b/src/migration-scripts/interfaces/31-to-32
@@ -1,55 +1,37 @@
-#!/usr/bin/env python3
+# Copyright 2023-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2023 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 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,
+# 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 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/>.
+# 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, see <http://www.gnu.org/licenses/>.
+
# T5671: change port to IANA assigned default port
# T5759: change default MTU 1450 -> 1500
-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 = ['interfaces', 'vxlan']
-config = ConfigTree(config_file)
-if not config.exists(base):
- # Nothing to do
- exit(0)
-
-for vxlan in config.list_nodes(base):
- if config.exists(base + [vxlan, 'external']):
- config.delete(base + [vxlan, 'external'])
- config.set(base + [vxlan, 'parameters', 'external'])
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ # Nothing to do
+ return
- if not config.exists(base + [vxlan, 'port']):
- config.set(base + [vxlan, 'port'], value='8472')
+ for vxlan in config.list_nodes(base):
+ if config.exists(base + [vxlan, 'external']):
+ config.delete(base + [vxlan, 'external'])
+ config.set(base + [vxlan, 'parameters', 'external'])
- if not config.exists(base + [vxlan, 'mtu']):
- config.set(base + [vxlan, 'mtu'], value='1450')
+ if not config.exists(base + [vxlan, 'port']):
+ config.set(base + [vxlan, 'port'], value='8472')
-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)
+ if not config.exists(base + [vxlan, 'mtu']):
+ config.set(base + [vxlan, 'mtu'], value='1450')
diff --git a/src/migration-scripts/interfaces/32-to-33 b/src/migration-scripts/interfaces/32-to-33
index caf588474..c7b1c5b36 100755..100644
--- a/src/migration-scripts/interfaces/32-to-33
+++ b/src/migration-scripts/interfaces/32-to-33
@@ -16,42 +16,25 @@
#
# T6318: WiFi country-code should be set system-wide instead of per-device
-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 = ['interfaces', 'wireless']
-config = ConfigTree(config_file)
-if not config.exists(base):
- # Nothing to do
- exit(0)
-
-installed = False
-for interface in config.list_nodes(base):
- cc_path = base + [interface, 'country-code']
- if config.exists(cc_path):
- tmp = config.return_value(cc_path)
- config.delete(cc_path)
-
- # There can be only ONE wireless country-code per device, everything
- # else makes no sense as a WIFI router can not operate in two
- # different countries
- if not installed:
- config.set(['system', 'wireless', 'country-code'], value=tmp)
- installed = True
-
-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)
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ # Nothing to do
+ return
+
+ installed = False
+ for interface in config.list_nodes(base):
+ cc_path = base + [interface, 'country-code']
+ if config.exists(cc_path):
+ tmp = config.return_value(cc_path)
+ config.delete(cc_path)
+
+ # There can be only ONE wireless country-code per device, everything
+ # else makes no sense as a WIFI router can not operate in two
+ # different countries
+ if not installed:
+ config.set(['system', 'wireless', 'country-code'], value=tmp)
+ installed = True
diff --git a/src/migration-scripts/interfaces/4-to-5 b/src/migration-scripts/interfaces/4-to-5
index 68d81e846..93fa7c393 100755..100644
--- a/src/migration-scripts/interfaces/4-to-5
+++ b/src/migration-scripts/interfaces/4-to-5
@@ -1,9 +1,21 @@
-#!/usr/bin/env python3
+# 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
+# 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, see <http://www.gnu.org/licenses/>.
# De-nest PPPoE interfaces
# Migrate boolean nodes to valueless
-import sys
from vyos.configtree import ConfigTree
def migrate_dialer(config, tree, intf):
@@ -55,18 +67,7 @@ def migrate_dialer(config, tree, intf):
if config.exists(ipv6_ra):
config.delete(ipv6_ra)
-
-if __name__ == '__main__':
- if len(sys.argv) < 2:
- print("Must specify file name!")
- exit(1)
-
- file_name = sys.argv[1]
-
- with open(file_name, 'r') as f:
- config_file = f.read()
-
- config = ConfigTree(config_file)
+def migrate(config: ConfigTree) -> None:
pppoe_links = ['bonding', 'ethernet']
for link_type in pppoe_links:
@@ -103,10 +104,3 @@ if __name__ == '__main__':
# Add interface description that this is required for PPPoE
if not config.exists(vlan_if + ['description']):
config.set(vlan_if + ['description'], value='PPPoE link interface')
-
- 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))
- sys.exit(1)
diff --git a/src/migration-scripts/interfaces/5-to-6 b/src/migration-scripts/interfaces/5-to-6
index 9d9a49c2d..44c32ba63 100755..100644
--- a/src/migration-scripts/interfaces/5-to-6
+++ b/src/migration-scripts/interfaces/5-to-6
@@ -1,23 +1,21 @@
-#!/usr/bin/env python3
+# Copyright 202-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2020-2021 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 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# Migrate IPv6 router advertisments from a nested interface configuration to
# a denested "service router-advert"
-import sys
from vyos.configtree import ConfigTree
def copy_rtradv(c, old_base, interface):
@@ -97,17 +95,7 @@ def copy_rtradv(c, old_base, interface):
if tmp == '0':
c.delete(new_base + ['link-mtu'])
-if __name__ == '__main__':
- if len(sys.argv) < 2:
- print("Must specify file name!")
- exit(1)
-
- file_name = sys.argv[1]
- with open(file_name, 'r') as f:
- config_file = f.read()
-
- config = ConfigTree(config_file)
-
+def migrate(config: ConfigTree) -> None:
# list all individual interface types like dummy, ethernet and so on
for if_type in config.list_nodes(['interfaces']):
base_if_type = ['interfaces', if_type]
@@ -124,10 +112,3 @@ if __name__ == '__main__':
old_base = vif_base + [vif, 'ipv6', 'router-advert']
vlan_name = f'{intf}.{vif}'
copy_rtradv(config, old_base, vlan_name)
-
- 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))
- sys.exit(1)
diff --git a/src/migration-scripts/interfaces/6-to-7 b/src/migration-scripts/interfaces/6-to-7
index 49b853d90..e60121eec 100755..100644
--- a/src/migration-scripts/interfaces/6-to-7
+++ b/src/migration-scripts/interfaces/6-to-7
@@ -1,39 +1,28 @@
-#!/usr/bin/env python3
+# Copyright 2020-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# 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 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# Remove network provider name from CLI and rather use provider APN from CLI
-import sys
from vyos.configtree import ConfigTree
-if __name__ == '__main__':
- if len(sys.argv) < 2:
- print("Must specify file name!")
- exit(1)
-
- file_name = sys.argv[1]
- with open(file_name, 'r') as f:
- config_file = f.read()
-
- config = ConfigTree(config_file)
+def migrate(config: ConfigTree) -> None:
base = ['interfaces', 'wirelessmodem']
if not config.exists(base):
# Nothing to do
- sys.exit(0)
+ return
# list all individual wwan/wireless modem interfaces
for i in config.list_nodes(base):
@@ -54,10 +43,3 @@ if __name__ == '__main__':
# uniform CLI experience
if config.exists(iface + ['no-dns']):
config.rename(iface + ['no-dns'], 'no-peer-dns')
-
- 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))
- sys.exit(1)
diff --git a/src/migration-scripts/interfaces/7-to-8 b/src/migration-scripts/interfaces/7-to-8
index 9343a48a8..43ae320ab 100755..100644
--- a/src/migration-scripts/interfaces/7-to-8
+++ b/src/migration-scripts/interfaces/7-to-8
@@ -1,25 +1,23 @@
-#!/usr/bin/env python3
+# Copyright 2020-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2020-2023 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 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# Split WireGuard endpoint into address / port nodes to make use of common
# validators
import os
-from sys import exit, argv
from vyos.configtree import ConfigTree
from vyos.utils.permission import chown
from vyos.utils.permission import chmod_750
@@ -36,23 +34,14 @@ def migrate_default_keys():
os.rename(f'{kdir}/private.key', f'{location}/private.key')
os.rename(f'{kdir}/public.key', f'{location}/public.key')
-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)
+def migrate(config: ConfigTree) -> None:
base = ['interfaces', 'wireguard']
migrate_default_keys()
if not config.exists(base):
# Nothing to do
- exit(0)
+ return
# list all individual wireguard interface isntance
for i in config.list_nodes(base):
@@ -68,10 +57,3 @@ if __name__ == '__main__':
# setup new nodes
config.set(base_peer + ['address'], value=address)
config.set(base_peer + ['port'], value=port)
-
- 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/interfaces/8-to-9 b/src/migration-scripts/interfaces/8-to-9
index 960962be7..bae1b34fa 100755..100644
--- a/src/migration-scripts/interfaces/8-to-9
+++ b/src/migration-scripts/interfaces/8-to-9
@@ -1,37 +1,25 @@
-#!/usr/bin/env python3
+# Copyright 2020-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# 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 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# Rename link nodes to source-interface for the following interface types:
# - vxlan
# - pseudo-ethernet
-from sys import exit, argv
from vyos.configtree import ConfigTree
-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)
-
+def migrate(config: ConfigTree) -> None:
for if_type in ['vxlan', 'pseudo-ethernet']:
base = ['interfaces', if_type]
if not config.exists(base):
@@ -43,10 +31,3 @@ if __name__ == '__main__':
iface = base + [i]
if config.exists(iface + ['link']):
config.rename(iface + ['link'], 'source-interface')
-
- 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/interfaces/9-to-10 b/src/migration-scripts/interfaces/9-to-10
index e9b8cb784..cdfd7d432 100755..100644
--- a/src/migration-scripts/interfaces/9-to-10
+++ b/src/migration-scripts/interfaces/9-to-10
@@ -1,38 +1,26 @@
-#!/usr/bin/env python3
+# Copyright 2020-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# 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 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# - rename CLI node 'dhcpv6-options delgate' to 'dhcpv6-options prefix-delegation
# interface'
# - rename CLI node 'interface-id' for prefix-delegation to 'address' as it
# represents the local interface IPv6 address assigned by DHCPv6-PD
-from sys import exit, argv
from vyos.configtree import ConfigTree
-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)
-
+def migrate(config: ConfigTree) -> None:
for intf_type in config.list_nodes(['interfaces']):
for intf in config.list_nodes(['interfaces', intf_type]):
# cache current config tree
@@ -55,10 +43,3 @@ if __name__ == '__main__':
# delete old noe
config.delete(base_path)
-
- 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 6a7111541..034eacb10 100755..100644
--- a/src/migration-scripts/ipoe-server/1-to-2
+++ b/src/migration-scripts/ipoe-server/1-to-2
@@ -1,18 +1,17 @@
-#!/usr/bin/env python3
+# Copyright 2023-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2023-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; either
+# version 2.1 of the License, or (at your option) any later version.
#
-# 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# - T4703: merge vlan-id and vlan-range to vlan CLI node
# L2|L3 -> l2|l3
@@ -28,87 +27,68 @@
# 1. The first pool that contains next-poll.
# 2. Else, the first pool in the list
-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 = ['service', 'ipoe-server']
-if not config.exists(base):
- 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]):
- 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:
- f.write(config.to_string())
-except OSError as e:
- print("Failed to save the modified config: {}".format(e))
- exit(1)
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ return
+
+ 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)
diff --git a/src/migration-scripts/ipoe-server/2-to-3 b/src/migration-scripts/ipoe-server/2-to-3
index 0909315a8..dcd15e595 100755..100644
--- a/src/migration-scripts/ipoe-server/2-to-3
+++ b/src/migration-scripts/ipoe-server/2-to-3
@@ -1,58 +1,40 @@
-#!/usr/bin/env python3
+# Copyright 2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 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; either
+# version 2.1 of the License, or (at your option) any later version.
#
-# 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# Migrating to named ipv6 pools
-from sys import argv
-from sys import exit
from vyos.configtree import ConfigTree
-if len(argv) < 2:
- print("Must specify file name!")
- exit(1)
+base = ['service', 'ipoe-server']
+pool_base = base + ['client-ipv6-pool']
-file_name = argv[1]
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ return
-with open(file_name, 'r') as f:
- config_file = f.read()
+ if not config.exists(pool_base):
+ return
-config = ConfigTree(config_file)
-base = ['service', 'ipoe-server']
-pool_base = base + ['client-ipv6-pool']
-if not config.exists(base):
- exit(0)
-
-if not config.exists(pool_base):
- exit(0)
-
-ipv6_pool_name = 'ipv6-pool'
-config.copy(pool_base, pool_base + [ipv6_pool_name])
-
-if config.exists(pool_base + ['prefix']):
- config.delete(pool_base + ['prefix'])
- config.set(base + ['default-ipv6-pool'], value=ipv6_pool_name)
-if config.exists(pool_base + ['delegate']):
- config.delete(pool_base + ['delegate'])
-
-# format as tag node
-config.set_tag(pool_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)
+ ipv6_pool_name = 'ipv6-pool'
+ config.copy(pool_base, pool_base + [ipv6_pool_name])
+
+ if config.exists(pool_base + ['prefix']):
+ config.delete(pool_base + ['prefix'])
+ config.set(base + ['default-ipv6-pool'], value=ipv6_pool_name)
+ if config.exists(pool_base + ['delegate']):
+ config.delete(pool_base + ['delegate'])
+
+ # format as tag node
+ config.set_tag(pool_base)
diff --git a/src/migration-scripts/ipsec/10-to-11 b/src/migration-scripts/ipsec/10-to-11
index 509216267..6c4ccb553 100755..100644
--- a/src/migration-scripts/ipsec/10-to-11
+++ b/src/migration-scripts/ipsec/10-to-11
@@ -1,83 +1,63 @@
-#!/usr/bin/env python3
+# Copyright 2023-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2023 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 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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 sys import argv
-from sys import exit
+# T4916: Rewrite IPsec peer authentication and psk migration
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 = ['vpn', 'ipsec']
-config = ConfigTree(config_file)
-
-if not config.exists(base):
- # Nothing to do
- exit(0)
-
-# PEER changes
-if config.exists(base + ['site-to-site', 'peer']):
- for peer in config.list_nodes(base + ['site-to-site', 'peer']):
- peer_base = base + ['site-to-site', 'peer', peer]
-
- # replace: 'ipsec site-to-site peer <tag> authentication pre-shared-secret xxx'
- # => 'ipsec authentication psk <tag> secret xxx'
- if config.exists(peer_base + ['authentication', 'pre-shared-secret']):
- tmp = config.return_value(peer_base + ['authentication', 'pre-shared-secret'])
- config.delete(peer_base + ['authentication', 'pre-shared-secret'])
- config.set(base + ['authentication', 'psk', peer, 'secret'], value=tmp)
- # format as tag node to avoid loading problems
- config.set_tag(base + ['authentication', 'psk'])
-
- # Get id's from peers for "ipsec auth psk <tag> id xxx"
- if config.exists(peer_base + ['authentication', 'local-id']):
- local_id = config.return_value(peer_base + ['authentication', 'local-id'])
- config.set(base + ['authentication', 'psk', peer, 'id'], value=local_id, replace=False)
- if config.exists(peer_base + ['authentication', 'remote-id']):
- remote_id = config.return_value(peer_base + ['authentication', 'remote-id'])
- config.set(base + ['authentication', 'psk', peer, 'id'], value=remote_id, replace=False)
-
- if config.exists(peer_base + ['local-address']):
- tmp = config.return_value(peer_base + ['local-address'])
- config.set(base + ['authentication', 'psk', peer, 'id'], value=tmp, replace=False)
- if config.exists(peer_base + ['remote-address']):
- tmp = config.return_values(peer_base + ['remote-address'])
- if tmp:
- for remote_addr in tmp:
- if remote_addr == 'any':
- remote_addr = '%any'
- config.set(base + ['authentication', 'psk', peer, 'id'], value=remote_addr, replace=False)
-
- # get DHCP peer interface as psk dhcp-interface
- if config.exists(peer_base + ['dhcp-interface']):
- tmp = config.return_value(peer_base + ['dhcp-interface'])
- config.set(base + ['authentication', 'psk', peer, 'dhcp-interface'], value=tmp)
-
-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)
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ # Nothing to do
+ return
+
+ # PEER changes
+ if config.exists(base + ['site-to-site', 'peer']):
+ for peer in config.list_nodes(base + ['site-to-site', 'peer']):
+ peer_base = base + ['site-to-site', 'peer', peer]
+
+ # replace: 'ipsec site-to-site peer <tag> authentication pre-shared-secret xxx'
+ # => 'ipsec authentication psk <tag> secret xxx'
+ if config.exists(peer_base + ['authentication', 'pre-shared-secret']):
+ tmp = config.return_value(peer_base + ['authentication', 'pre-shared-secret'])
+ config.delete(peer_base + ['authentication', 'pre-shared-secret'])
+ config.set(base + ['authentication', 'psk', peer, 'secret'], value=tmp)
+ # format as tag node to avoid loading problems
+ config.set_tag(base + ['authentication', 'psk'])
+
+ # Get id's from peers for "ipsec auth psk <tag> id xxx"
+ if config.exists(peer_base + ['authentication', 'local-id']):
+ local_id = config.return_value(peer_base + ['authentication', 'local-id'])
+ config.set(base + ['authentication', 'psk', peer, 'id'], value=local_id, replace=False)
+ if config.exists(peer_base + ['authentication', 'remote-id']):
+ remote_id = config.return_value(peer_base + ['authentication', 'remote-id'])
+ config.set(base + ['authentication', 'psk', peer, 'id'], value=remote_id, replace=False)
+
+ if config.exists(peer_base + ['local-address']):
+ tmp = config.return_value(peer_base + ['local-address'])
+ config.set(base + ['authentication', 'psk', peer, 'id'], value=tmp, replace=False)
+ if config.exists(peer_base + ['remote-address']):
+ tmp = config.return_values(peer_base + ['remote-address'])
+ if tmp:
+ for remote_addr in tmp:
+ if remote_addr == 'any':
+ remote_addr = '%any'
+ config.set(base + ['authentication', 'psk', peer, 'id'], value=remote_addr, replace=False)
+
+ # get DHCP peer interface as psk dhcp-interface
+ if config.exists(peer_base + ['dhcp-interface']):
+ tmp = config.return_value(peer_base + ['dhcp-interface'])
+ config.set(base + ['authentication', 'psk', peer, 'dhcp-interface'], value=tmp)
diff --git a/src/migration-scripts/ipsec/11-to-12 b/src/migration-scripts/ipsec/11-to-12
index 4833d0876..fc65f1825 100755..100644
--- a/src/migration-scripts/ipsec/11-to-12
+++ b/src/migration-scripts/ipsec/11-to-12
@@ -1,51 +1,31 @@
-#!/usr/bin/env python3
+# Copyright 2023-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2023-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; either
+# version 2.1 of the License, or (at your option) any later version.
#
-# 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# Remove legacy ipsec.conf and ipsec.secrets - Not supported with swanctl
-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 = ['vpn', 'ipsec']
-config = ConfigTree(config_file)
-
-if not config.exists(base):
- # Nothing to do
- exit(0)
-if config.exists(base + ['include-ipsec-conf']):
- config.delete(base + ['include-ipsec-conf'])
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ # Nothing to do
+ return
-if config.exists(base + ['include-ipsec-secrets']):
- config.delete(base + ['include-ipsec-secrets'])
+ if config.exists(base + ['include-ipsec-conf']):
+ config.delete(base + ['include-ipsec-conf'])
-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)
+ if config.exists(base + ['include-ipsec-secrets']):
+ config.delete(base + ['include-ipsec-secrets'])
diff --git a/src/migration-scripts/ipsec/12-to-13 b/src/migration-scripts/ipsec/12-to-13
index d90c70314..ffe766eb2 100755..100644
--- a/src/migration-scripts/ipsec/12-to-13
+++ b/src/migration-scripts/ipsec/12-to-13
@@ -1,43 +1,30 @@
-#!/usr/bin/env python3
+# Copyright 2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 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; either
+# version 2.1 of the License, or (at your option) any later version.
#
-# 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# 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
-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 = ['vpn', 'ipsec', 'ike-group']
-config = ConfigTree(config_file)
-if not config.exists(base):
- # Nothing to do
- exit(0)
-else:
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ # Nothing to do
+ return
+
for ike_group in config.list_nodes(base):
base_dpd_action = base + [ike_group, 'dead-peer-detection', 'action']
base_close_action = base + [ike_group, 'close-action']
@@ -48,10 +35,3 @@ else:
config.set(base_close_action, 'trap', replace=True)
if config.return_value(base_close_action) == 'restart':
config.set(base_close_action, 'start', replace=True)
-
-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/ipsec/4-to-5 b/src/migration-scripts/ipsec/4-to-5
index 772d05787..a88a543d3 100755..100644
--- a/src/migration-scripts/ipsec/4-to-5
+++ b/src/migration-scripts/ipsec/4-to-5
@@ -1,47 +1,28 @@
-#!/usr/bin/env python3
+# Copyright 2019-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2019 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 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# log-modes have changed, keyword all to any
-import sys
-
from vyos.configtree import ConfigTree
-if len(sys.argv) < 2:
- print("Must specify file name!")
- sys.exit(1)
-
-file_name = sys.argv[1]
-
-with open(file_name, 'r') as f:
- config_file = f.read()
-
-ctree = ConfigTree(config_file)
-
-if not ctree.exists(['vpn', 'ipsec', 'logging','log-modes']):
- # Nothing to do
- sys.exit(0)
-else:
- lmodes = ctree.return_values(['vpn', 'ipsec', 'logging','log-modes'])
- for mode in lmodes:
- if mode == 'all':
- ctree.set(['vpn', 'ipsec', 'logging','log-modes'], value='any', replace=True)
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(['vpn', 'ipsec', 'logging','log-modes']):
+ # Nothing to do
+ return
- try:
- open(file_name,'w').write(ctree.to_string())
- except OSError as e:
- print("Failed to save the modified config: {}".format(e))
- sys.exit(1)
+ lmodes = config.return_values(['vpn', 'ipsec', 'logging','log-modes'])
+ for mode in lmodes:
+ if mode == 'all':
+ config.set(['vpn', 'ipsec', 'logging','log-modes'], value='any', replace=True)
diff --git a/src/migration-scripts/ipsec/5-to-6 b/src/migration-scripts/ipsec/5-to-6
index 7d7c777c6..373428d61 100755..100644
--- a/src/migration-scripts/ipsec/5-to-6
+++ b/src/migration-scripts/ipsec/5-to-6
@@ -1,93 +1,73 @@
-#!/usr/bin/env python3
+# Copyright 2021-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2021 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 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# Remove deprecated strongSwan options from VyOS CLI
# - vpn ipsec nat-traversal enable
# - vpn ipsec nat-networks allowed-network
-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 = ['vpn', 'ipsec']
-config = ConfigTree(config_file)
-
-if not config.exists(base):
- # Nothing to do
- exit(0)
-
-# Delete CLI nodes whose config options got removed by strongSwan
-for cli_node in ['nat-traversal', 'nat-networks']:
- if config.exists(base + [cli_node]):
- config.delete(base + [cli_node])
-
-# Remove options only valid in Openswan
-if config.exists(base + ['site-to-site', 'peer']):
- for peer in config.list_nodes(base + ['site-to-site', 'peer']):
- if not config.exists(base + ['site-to-site', 'peer', peer, 'tunnel']):
- continue
- for tunnel in config.list_nodes(base + ['site-to-site', 'peer', peer, 'tunnel']):
- # allow-public-networks - Sets a value in ipsec.conf that was only ever valid in Openswan on kernel 2.6
- nat_networks = base + ['site-to-site', 'peer', peer, 'tunnel', tunnel, 'allow-nat-networks']
- if config.exists(nat_networks):
- config.delete(nat_networks)
-
- # allow-nat-networks - Also sets a value only valid in Openswan
- public_networks = base + ['site-to-site', 'peer', peer, 'tunnel', tunnel, 'allow-public-networks']
- if config.exists(public_networks):
- config.delete(public_networks)
-
-# Rename "logging log-level" and "logging log-modes" to something more human friendly
-log = base + ['logging']
-if config.exists(log):
- config.rename(log, 'log')
- log = base + ['log']
-
-log_level = log + ['log-level']
-if config.exists(log_level):
- config.rename(log_level, 'level')
-
-log_mode = log + ['log-modes']
-if config.exists(log_mode):
- config.rename(log_mode, 'subsystem')
-
-# Rename "ipsec-interfaces interface" to "interface"
-base_interfaces = base + ['ipsec-interfaces', 'interface']
-if config.exists(base_interfaces):
- config.copy(base_interfaces, base + ['interface'])
- config.delete(base + ['ipsec-interfaces'])
-
-# Remove deprecated "auto-update" option
-tmp = base + ['auto-update']
-if config.exists(tmp):
- config.delete(tmp)
-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)
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ # Nothing to do
+ return
+
+ # Delete CLI nodes whose config options got removed by strongSwan
+ for cli_node in ['nat-traversal', 'nat-networks']:
+ if config.exists(base + [cli_node]):
+ config.delete(base + [cli_node])
+
+ # Remove options only valid in Openswan
+ if config.exists(base + ['site-to-site', 'peer']):
+ for peer in config.list_nodes(base + ['site-to-site', 'peer']):
+ if not config.exists(base + ['site-to-site', 'peer', peer, 'tunnel']):
+ continue
+ for tunnel in config.list_nodes(base + ['site-to-site', 'peer', peer, 'tunnel']):
+ # allow-public-networks - Sets a value in ipsec.conf that was only ever valid in Openswan on kernel 2.6
+ nat_networks = base + ['site-to-site', 'peer', peer, 'tunnel', tunnel, 'allow-nat-networks']
+ if config.exists(nat_networks):
+ config.delete(nat_networks)
+
+ # allow-nat-networks - Also sets a value only valid in Openswan
+ public_networks = base + ['site-to-site', 'peer', peer, 'tunnel', tunnel, 'allow-public-networks']
+ if config.exists(public_networks):
+ config.delete(public_networks)
+
+ # Rename "logging log-level" and "logging log-modes" to something more human friendly
+ log = base + ['logging']
+ if config.exists(log):
+ config.rename(log, 'log')
+ log = base + ['log']
+
+ log_level = log + ['log-level']
+ if config.exists(log_level):
+ config.rename(log_level, 'level')
+
+ log_mode = log + ['log-modes']
+ if config.exists(log_mode):
+ config.rename(log_mode, 'subsystem')
+
+ # Rename "ipsec-interfaces interface" to "interface"
+ base_interfaces = base + ['ipsec-interfaces', 'interface']
+ if config.exists(base_interfaces):
+ config.copy(base_interfaces, base + ['interface'])
+ config.delete(base + ['ipsec-interfaces'])
+
+ # Remove deprecated "auto-update" option
+ tmp = base + ['auto-update']
+ if config.exists(tmp):
+ config.delete(tmp)
diff --git a/src/migration-scripts/ipsec/6-to-7 b/src/migration-scripts/ipsec/6-to-7
index f8b6de560..5679477c0 100755..100644
--- a/src/migration-scripts/ipsec/6-to-7
+++ b/src/migration-scripts/ipsec/6-to-7
@@ -1,26 +1,22 @@
-#!/usr/bin/env python3
+# Copyright 2021-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2021-2023 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 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# Migrate /config/auth certificates and keys into PKI configuration
import os
-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
@@ -29,27 +25,27 @@ from vyos.pki import encode_certificate
from vyos.pki import encode_private_key
from vyos.utils.process import run
-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()
-
pki_base = ['pki']
ipsec_site_base = ['vpn', 'ipsec', 'site-to-site', 'peer']
-config = ConfigTree(config_file)
-changes_made = False
-
AUTH_DIR = '/config/auth'
def wrapped_pem_to_config_value(pem):
return "".join(pem.strip().split("\n")[1:-1])
-if config.exists(ipsec_site_base):
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(ipsec_site_base):
+ return
+
+ migration_needed = False
+ for peer in config.list_nodes(ipsec_site_base):
+ if config.exists(ipsec_site_base + [peer, 'authentication', 'x509']):
+ migration_needed = True
+ break
+
+ if not migration_needed:
+ return
+
config.set(pki_base + ['ca'])
config.set_tag(pki_base + ['ca'])
@@ -60,8 +56,6 @@ if config.exists(ipsec_site_base):
if not config.exists(ipsec_site_base + [peer, 'authentication', 'x509']):
continue
- changes_made = True
-
peer_x509_base = ipsec_site_base + [peer, 'authentication', 'x509']
pki_name = 'peer_' + peer.replace(".", "-").replace("@", "")
@@ -159,11 +153,3 @@ if config.exists(ipsec_site_base):
print(f'Failed to migrate private key on peer "{peer}"')
config.delete(peer_x509_base + ['key'])
-
-if changes_made:
- 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))
- sys.exit(1)
diff --git a/src/migration-scripts/ipsec/7-to-8 b/src/migration-scripts/ipsec/7-to-8
index 9acc737d5..481f00d29 100755..100644
--- a/src/migration-scripts/ipsec/7-to-8
+++ b/src/migration-scripts/ipsec/7-to-8
@@ -1,18 +1,17 @@
-#!/usr/bin/env python3
+# Copyright 2021-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2021-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; either
+# version 2.1 of the License, or (at your option) any later version.
#
-# 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# Migrate rsa keys into PKI configuration
@@ -22,29 +21,15 @@ import struct
from cryptography.hazmat.primitives.asymmetric import rsa
-from sys import argv
-from sys import exit
-
from vyos.configtree import ConfigTree
from vyos.pki import load_private_key
from vyos.pki import encode_public_key
from vyos.pki import encode_private_key
-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()
-
pki_base = ['pki']
ipsec_site_base = ['vpn', 'ipsec', 'site-to-site', 'peer']
rsa_keys_base = ['vpn', 'rsa-keys']
-config = ConfigTree(config_file)
-
LOCAL_KEY_PATHS = ['/config/auth/', '/config/ipsec.d/rsa-keys/']
def migrate_from_vyatta_key(data):
@@ -60,65 +45,59 @@ def wrapped_pem_to_config_value(pem):
local_key_name = 'localhost'
-if config.exists(rsa_keys_base):
- if not config.exists(pki_base + ['key-pair']):
- config.set(pki_base + ['key-pair'])
- config.set_tag(pki_base + ['key-pair'])
-
- if config.exists(rsa_keys_base + ['local-key', 'file']):
- local_file = config.return_value(rsa_keys_base + ['local-key', 'file'])
- local_path = None
- local_key = None
-
- for path in LOCAL_KEY_PATHS:
- full_path = os.path.join(path, local_file)
- if os.path.exists(full_path):
- local_path = full_path
- break
-
- if local_path:
- with open(local_path, 'r') as f:
- local_key_data = f.read()
- local_key = load_private_key(local_key_data, wrap_tags=False)
-
- if local_key:
- local_key_pem = encode_private_key(local_key)
- config.set(pki_base + ['key-pair', local_key_name, 'private', 'key'], value=wrapped_pem_to_config_value(local_key_pem))
- else:
- print('Failed to migrate local RSA key')
-
- if config.exists(rsa_keys_base + ['rsa-key-name']):
- for rsa_name in config.list_nodes(rsa_keys_base + ['rsa-key-name']):
- if not config.exists(rsa_keys_base + ['rsa-key-name', rsa_name, 'rsa-key']):
- continue
+def migrate(config: ConfigTree) -> None:
+ if config.exists(rsa_keys_base):
+ if not config.exists(pki_base + ['key-pair']):
+ config.set(pki_base + ['key-pair'])
+ config.set_tag(pki_base + ['key-pair'])
+
+ if config.exists(rsa_keys_base + ['local-key', 'file']):
+ local_file = config.return_value(rsa_keys_base + ['local-key', 'file'])
+ local_path = None
+ local_key = None
+
+ for path in LOCAL_KEY_PATHS:
+ full_path = os.path.join(path, local_file)
+ if os.path.exists(full_path):
+ local_path = full_path
+ break
+
+ if local_path:
+ with open(local_path, 'r') as f:
+ local_key_data = f.read()
+ local_key = load_private_key(local_key_data, wrap_tags=False)
+
+ if local_key:
+ local_key_pem = encode_private_key(local_key)
+ config.set(pki_base + ['key-pair', local_key_name, 'private', 'key'], value=wrapped_pem_to_config_value(local_key_pem))
+ else:
+ print('Failed to migrate local RSA key')
- vyatta_key = config.return_value(rsa_keys_base + ['rsa-key-name', rsa_name, 'rsa-key'])
- public_key = migrate_from_vyatta_key(vyatta_key)
+ if config.exists(rsa_keys_base + ['rsa-key-name']):
+ for rsa_name in config.list_nodes(rsa_keys_base + ['rsa-key-name']):
+ if not config.exists(rsa_keys_base + ['rsa-key-name', rsa_name, 'rsa-key']):
+ continue
- if public_key:
- public_key_pem = encode_public_key(public_key)
- config.set(pki_base + ['key-pair', rsa_name, 'public', 'key'], value=wrapped_pem_to_config_value(public_key_pem))
- else:
- print(f'Failed to migrate rsa-key "{rsa_name}"')
+ vyatta_key = config.return_value(rsa_keys_base + ['rsa-key-name', rsa_name, 'rsa-key'])
+ public_key = migrate_from_vyatta_key(vyatta_key)
- config.delete(rsa_keys_base)
+ if public_key:
+ public_key_pem = encode_public_key(public_key)
+ config.set(pki_base + ['key-pair', rsa_name, 'public', 'key'], value=wrapped_pem_to_config_value(public_key_pem))
+ else:
+ print(f'Failed to migrate rsa-key "{rsa_name}"')
-if config.exists(ipsec_site_base):
- for peer in config.list_nodes(ipsec_site_base):
- mode = config.return_value(ipsec_site_base + [peer, 'authentication', 'mode'])
+ config.delete(rsa_keys_base)
- if mode != 'rsa':
- continue
+ if config.exists(ipsec_site_base):
+ for peer in config.list_nodes(ipsec_site_base):
+ mode = config.return_value(ipsec_site_base + [peer, 'authentication', 'mode'])
- config.set(ipsec_site_base + [peer, 'authentication', 'rsa', 'local-key'], value=local_key_name)
+ if mode != 'rsa':
+ continue
- remote_key_name = config.return_value(ipsec_site_base + [peer, 'authentication', 'rsa-key-name'])
- config.set(ipsec_site_base + [peer, 'authentication', 'rsa', 'remote-key'], value=remote_key_name)
- config.delete(ipsec_site_base + [peer, 'authentication', 'rsa-key-name'])
+ config.set(ipsec_site_base + [peer, 'authentication', 'rsa', 'local-key'], value=local_key_name)
-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))
- sys.exit(1)
+ remote_key_name = config.return_value(ipsec_site_base + [peer, 'authentication', 'rsa-key-name'])
+ config.set(ipsec_site_base + [peer, 'authentication', 'rsa', 'remote-key'], value=remote_key_name)
+ config.delete(ipsec_site_base + [peer, 'authentication', 'rsa-key-name'])
diff --git a/src/migration-scripts/ipsec/8-to-9 b/src/migration-scripts/ipsec/8-to-9
index c08411f83..7f325139f 100755..100644
--- a/src/migration-scripts/ipsec/8-to-9
+++ b/src/migration-scripts/ipsec/8-to-9
@@ -1,48 +1,30 @@
-#!/usr/bin/env python3
+# Copyright 2022-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2022 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 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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 sys import argv
-from sys import exit
+# T4288 : close-action is missing in swanctl.conf
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 = ['vpn', 'ipsec', 'ike-group']
-config = ConfigTree(config_file)
-if not config.exists(base):
- # Nothing to do
- exit(0)
-else:
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ # Nothing to do
+ return
+
for ike_group in config.list_nodes(base):
base_closeaction = base + [ike_group, 'close-action']
if config.exists(base_closeaction) and config.return_value(base_closeaction) == 'clear':
config.set(base_closeaction, 'none', replace=True)
-
-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/ipsec/9-to-10 b/src/migration-scripts/ipsec/9-to-10
index bc10e1997..321a75973 100755..100644
--- a/src/migration-scripts/ipsec/9-to-10
+++ b/src/migration-scripts/ipsec/9-to-10
@@ -1,131 +1,114 @@
-#!/usr/bin/env python3
+# Copyright 2022-2024 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
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
#
-# 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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 re
+# T4118: Change vpn ipsec syntax for IKE ESP and peer
+# T4879: IPsec migration script remote-id for peer name eq address
-from sys import argv
-from sys import exit
+import re
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 = ['vpn', 'ipsec']
-config = ConfigTree(config_file)
-
-if not config.exists(base):
- # Nothing to do
- exit(0)
-
-# IKE changes, T4118:
-if config.exists(base + ['ike-group']):
- for ike_group in config.list_nodes(base + ['ike-group']):
- # replace 'ipsec ike-group <tag> mobike disable'
- # => 'ipsec ike-group <tag> disable-mobike'
- mobike = base + ['ike-group', ike_group, 'mobike']
- if config.exists(mobike):
- if config.return_value(mobike) == 'disable':
- config.set(base + ['ike-group', ike_group, 'disable-mobike'])
- config.delete(mobike)
-
- # replace 'ipsec ike-group <tag> ikev2-reauth yes'
- # => 'ipsec ike-group <tag> ikev2-reauth'
- reauth = base + ['ike-group', ike_group, 'ikev2-reauth']
- if config.exists(reauth):
- if config.return_value(reauth) == 'yes':
- config.delete(reauth)
- config.set(reauth)
- else:
- config.delete(reauth)
-
-# ESP changes
-# replace 'ipsec esp-group <tag> compression enable'
-# => 'ipsec esp-group <tag> compression'
-if config.exists(base + ['esp-group']):
- for esp_group in config.list_nodes(base + ['esp-group']):
- compression = base + ['esp-group', esp_group, 'compression']
- if config.exists(compression):
- if config.return_value(compression) == 'enable':
- config.delete(compression)
- config.set(compression)
- else:
- config.delete(compression)
-
-# PEER changes
-if config.exists(base + ['site-to-site', 'peer']):
- for peer in config.list_nodes(base + ['site-to-site', 'peer']):
- peer_base = base + ['site-to-site', 'peer', peer]
-
- # replace: 'peer <tag> id x'
- # => 'peer <tag> local-id x'
- if config.exists(peer_base + ['authentication', 'id']):
- config.rename(peer_base + ['authentication', 'id'], 'local-id')
-
- # For the peer '@foo' set remote-id 'foo' if remote-id is not defined
- # For the peer '192.0.2.1' set remote-id '192.0.2.1' if remote-id is not defined
- if not config.exists(peer_base + ['authentication', 'remote-id']):
- tmp = peer.replace('@', '') if peer.startswith('@') else peer
- config.set(peer_base + ['authentication', 'remote-id'], value=tmp)
-
- # replace: 'peer <tag> force-encapsulation enable'
- # => 'peer <tag> force-udp-encapsulation'
- force_enc = peer_base + ['force-encapsulation']
- if config.exists(force_enc):
- if config.return_value(force_enc) == 'enable':
- config.delete(force_enc)
- config.set(peer_base + ['force-udp-encapsulation'])
- else:
- config.delete(force_enc)
-
- # add option: 'peer <tag> remote-address x.x.x.x'
- remote_address = peer
- if peer.startswith('@'):
- remote_address = 'any'
- config.set(peer_base + ['remote-address'], value=remote_address)
- # Peer name it is swanctl connection name and shouldn't contain dots or colons
- # rename peer:
- # peer 192.0.2.1 => peer peer_192-0-2-1
- # peer 2001:db8::2 => peer peer_2001-db8--2
- # peer @foo => peer peer_foo
- re_peer_name = re.sub(':|\.', '-', peer)
- if re_peer_name.startswith('@'):
- re_peer_name = re.sub('@', '', re_peer_name)
- new_peer_name = f'peer_{re_peer_name}'
-
- config.rename(peer_base, new_peer_name)
-
-# remote-access/road-warrior changes
-if config.exists(base + ['remote-access', 'connection']):
- for connection in config.list_nodes(base + ['remote-access', 'connection']):
- ra_base = base + ['remote-access', 'connection', connection]
- # replace: 'remote-access connection <tag> authentication id x'
- # => 'remote-access connection <tag> authentication local-id x'
- if config.exists(ra_base + ['authentication', 'id']):
- config.rename(ra_base + ['authentication', 'id'], 'local-id')
-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)
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ # Nothing to do
+ return
+
+ # IKE changes, T4118:
+ if config.exists(base + ['ike-group']):
+ for ike_group in config.list_nodes(base + ['ike-group']):
+ # replace 'ipsec ike-group <tag> mobike disable'
+ # => 'ipsec ike-group <tag> disable-mobike'
+ mobike = base + ['ike-group', ike_group, 'mobike']
+ if config.exists(mobike):
+ if config.return_value(mobike) == 'disable':
+ config.set(base + ['ike-group', ike_group, 'disable-mobike'])
+ config.delete(mobike)
+
+ # replace 'ipsec ike-group <tag> ikev2-reauth yes'
+ # => 'ipsec ike-group <tag> ikev2-reauth'
+ reauth = base + ['ike-group', ike_group, 'ikev2-reauth']
+ if config.exists(reauth):
+ if config.return_value(reauth) == 'yes':
+ config.delete(reauth)
+ config.set(reauth)
+ else:
+ config.delete(reauth)
+
+ # ESP changes
+ # replace 'ipsec esp-group <tag> compression enable'
+ # => 'ipsec esp-group <tag> compression'
+ if config.exists(base + ['esp-group']):
+ for esp_group in config.list_nodes(base + ['esp-group']):
+ compression = base + ['esp-group', esp_group, 'compression']
+ if config.exists(compression):
+ if config.return_value(compression) == 'enable':
+ config.delete(compression)
+ config.set(compression)
+ else:
+ config.delete(compression)
+
+ # PEER changes
+ if config.exists(base + ['site-to-site', 'peer']):
+ for peer in config.list_nodes(base + ['site-to-site', 'peer']):
+ peer_base = base + ['site-to-site', 'peer', peer]
+
+ # replace: 'peer <tag> id x'
+ # => 'peer <tag> local-id x'
+ if config.exists(peer_base + ['authentication', 'id']):
+ config.rename(peer_base + ['authentication', 'id'], 'local-id')
+
+ # For the peer '@foo' set remote-id 'foo' if remote-id is not defined
+ # For the peer '192.0.2.1' set remote-id '192.0.2.1' if remote-id is not defined
+ if not config.exists(peer_base + ['authentication', 'remote-id']):
+ tmp = peer.replace('@', '') if peer.startswith('@') else peer
+ config.set(peer_base + ['authentication', 'remote-id'], value=tmp)
+
+ # replace: 'peer <tag> force-encapsulation enable'
+ # => 'peer <tag> force-udp-encapsulation'
+ force_enc = peer_base + ['force-encapsulation']
+ if config.exists(force_enc):
+ if config.return_value(force_enc) == 'enable':
+ config.delete(force_enc)
+ config.set(peer_base + ['force-udp-encapsulation'])
+ else:
+ config.delete(force_enc)
+
+ # add option: 'peer <tag> remote-address x.x.x.x'
+ remote_address = peer
+ if peer.startswith('@'):
+ remote_address = 'any'
+ config.set(peer_base + ['remote-address'], value=remote_address)
+ # Peer name it is swanctl connection name and shouldn't contain dots or colons
+ # rename peer:
+ # peer 192.0.2.1 => peer peer_192-0-2-1
+ # peer 2001:db8::2 => peer peer_2001-db8--2
+ # peer @foo => peer peer_foo
+ re_peer_name = re.sub(':|\.', '-', peer)
+ if re_peer_name.startswith('@'):
+ re_peer_name = re.sub('@', '', re_peer_name)
+ new_peer_name = f'peer_{re_peer_name}'
+
+ config.rename(peer_base, new_peer_name)
+
+ # remote-access/road-warrior changes
+ if config.exists(base + ['remote-access', 'connection']):
+ for connection in config.list_nodes(base + ['remote-access', 'connection']):
+ ra_base = base + ['remote-access', 'connection', connection]
+ # replace: 'remote-access connection <tag> authentication id x'
+ # => 'remote-access connection <tag> authentication local-id x'
+ if config.exists(ra_base + ['authentication', 'id']):
+ config.rename(ra_base + ['authentication', 'id'], 'local-id')
diff --git a/src/migration-scripts/isis/0-to-1 b/src/migration-scripts/isis/0-to-1
index 0149c0c1f..e24288558 100755..100644
--- a/src/migration-scripts/isis/0-to-1
+++ b/src/migration-scripts/isis/0-to-1
@@ -1,56 +1,36 @@
-#!/usr/bin/env python3
+# Copyright 2021-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2021 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 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# T3417: migrate IS-IS tagNode to node as we can only have one IS-IS process
-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 = ['protocols', 'isis']
-config = ConfigTree(config_file)
-
-if not config.exists(base):
- # Nothing to do
- exit(0)
-# We need a temporary copy of the config
-tmp_base = ['protocols', 'isis2']
-config.copy(base, tmp_base)
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ # Nothing to do
+ return
-# Now it's save to delete the old configuration
-config.delete(base)
+ # We need a temporary copy of the config
+ tmp_base = ['protocols', 'isis2']
+ config.copy(base, tmp_base)
-# Rename temporary copy to new final config (IS-IS domain key is static and no
-# longer required to be set via CLI)
-config.rename(tmp_base, 'isis')
+ # Now it's save to delete the old configuration
+ config.delete(base)
-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)
+ # Rename temporary copy to new final config (IS-IS domain key is static and no
+ # longer required to be set via CLI)
+ config.rename(tmp_base, 'isis')
diff --git a/src/migration-scripts/isis/1-to-2 b/src/migration-scripts/isis/1-to-2
index 9c110bf2a..0fc92a6de 100755..100644
--- a/src/migration-scripts/isis/1-to-2
+++ b/src/migration-scripts/isis/1-to-2
@@ -1,46 +1,27 @@
-#!/usr/bin/env python3
+# Copyright 2022-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2022 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 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# T4739 refactor, and remove "on" from segment routing from the configuration
-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)
-
-# Check if ISIS segment routing is configured. Then check if segment routing "on" exists, then delete the "on" as it is no longer needed. This is for global configuration.
-if config.exists(['protocols', 'isis']):
- if config.exists(['protocols', 'isis', 'segment-routing']):
- if config.exists(['protocols', 'isis', 'segment-routing', 'enable']):
- config.delete(['protocols', 'isis', 'segment-routing', 'enable'])
-
-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)
+def migrate(config: ConfigTree) -> None:
+ # Check if ISIS segment routing is configured. Then check if segment
+ # routing "on" exists, then delete the "on" as it is no longer needed.
+ # This is for global configuration.
+ if config.exists(['protocols', 'isis']):
+ if config.exists(['protocols', 'isis', 'segment-routing']):
+ if config.exists(['protocols', 'isis', 'segment-routing', 'enable']):
+ config.delete(['protocols', 'isis', 'segment-routing', 'enable'])
diff --git a/src/migration-scripts/isis/2-to-3 b/src/migration-scripts/isis/2-to-3
index 78e3c1715..afb9f2340 100755..100644
--- a/src/migration-scripts/isis/2-to-3
+++ b/src/migration-scripts/isis/2-to-3
@@ -1,63 +1,43 @@
-#!/usr/bin/env python3
+# Copyright 2023-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2023 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 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# T5150: Rework CLI definitions to apply route-maps between routing daemons
# and zebra/kernel
-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)
-
isis_base = ['protocols', 'isis']
-# Check if IS-IS is configured - if so, migrate the CLI node
-if config.exists(isis_base):
- if config.exists(isis_base + ['route-map']):
- tmp = config.return_value(isis_base + ['route-map'])
-
- config.set(['system', 'ip', 'protocol', 'isis', 'route-map'], value=tmp)
- config.set_tag(['system', 'ip', 'protocol'])
- config.delete(isis_base + ['route-map'])
-
-# Check if vrf names are configured. Check if IS-IS is configured - if so,
-# migrate the CLI node(s)
-if config.exists(['vrf', 'name']):
- for vrf in config.list_nodes(['vrf', 'name']):
- vrf_base = ['vrf', 'name', vrf]
- if config.exists(vrf_base + ['protocols', 'isis', 'route-map']):
- tmp = config.return_value(vrf_base + ['protocols', 'isis', 'route-map'])
-
- config.set(vrf_base + ['ip', 'protocol', 'isis', 'route-map'], value=tmp)
- config.set_tag(vrf_base + ['ip', 'protocol', 'isis'])
- config.delete(vrf_base + ['protocols', 'isis', 'route-map'])
-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)
+def migrate(config: ConfigTree) -> None:
+ # Check if IS-IS is configured - if so, migrate the CLI node
+ if config.exists(isis_base):
+ if config.exists(isis_base + ['route-map']):
+ tmp = config.return_value(isis_base + ['route-map'])
+
+ config.set(['system', 'ip', 'protocol', 'isis', 'route-map'], value=tmp)
+ config.set_tag(['system', 'ip', 'protocol'])
+ config.delete(isis_base + ['route-map'])
+
+ # Check if vrf names are configured. Check if IS-IS is configured - if so,
+ # migrate the CLI node(s)
+ if config.exists(['vrf', 'name']):
+ for vrf in config.list_nodes(['vrf', 'name']):
+ vrf_base = ['vrf', 'name', vrf]
+ if config.exists(vrf_base + ['protocols', 'isis', 'route-map']):
+ tmp = config.return_value(vrf_base + ['protocols', 'isis', 'route-map'])
+
+ config.set(vrf_base + ['ip', 'protocol', 'isis', 'route-map'], value=tmp)
+ config.set_tag(vrf_base + ['ip', 'protocol', 'isis'])
+ config.delete(vrf_base + ['protocols', 'isis', 'route-map'])
diff --git a/src/migration-scripts/l2tp/0-to-1 b/src/migration-scripts/l2tp/0-to-1
index 15d229822..f0cb6af96 100755..100644
--- a/src/migration-scripts/l2tp/0-to-1
+++ b/src/migration-scripts/l2tp/0-to-1
@@ -1,29 +1,32 @@
-#!/usr/bin/env python3
-
+# Copyright 2018-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
+# 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, see <http://www.gnu.org/licenses/>.
+
+# T987: Unclutter L2TP/IPSec RADIUS configuration nodes
# Unclutter L2TP VPN configuiration - move radius-server top level tag
# nodes to a regular node which now also configures the radius source address
# used when querying a radius server
-import sys
-
from vyos.configtree import ConfigTree
-if len(sys.argv) < 2:
- print("Must specify file name!")
- sys.exit(1)
-
-file_name = sys.argv[1]
-
-with open(file_name, 'r') as f:
- config_file = f.read()
+cfg_base = ['vpn', 'l2tp', 'remote-access', 'authentication']
-config = ConfigTree(config_file)
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(cfg_base):
+ # Nothing to do
+ return
-cfg_base = ['vpn', 'l2tp', 'remote-access', 'authentication']
-if not config.exists(cfg_base):
- # Nothing to do
- sys.exit(0)
-else:
# Migrate "vpn l2tp authentication radius-source-address" to new
# "vpn l2tp authentication radius source-address"
if config.exists(cfg_base + ['radius-source-address']):
@@ -51,10 +54,3 @@ else:
# delete top level tag node
if config.exists(cfg_base + ['radius-server']):
config.delete(cfg_base + ['radius-server'])
-
- 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))
- sys.exit(1)
diff --git a/src/migration-scripts/l2tp/1-to-2 b/src/migration-scripts/l2tp/1-to-2
index 2ffb91c53..468d564ac 100755..100644
--- a/src/migration-scripts/l2tp/1-to-2
+++ b/src/migration-scripts/l2tp/1-to-2
@@ -1,33 +1,28 @@
-#!/usr/bin/env python3
-
-# Delete depricated outside-nexthop address
-
-import sys
+# 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
+# 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, see <http://www.gnu.org/licenses/>.
+
+# T1858: Delete deprecated outside-nexthop
from vyos.configtree import ConfigTree
-if len(sys.argv) < 2:
- print("Must specify file name!")
- sys.exit(1)
-
-file_name = sys.argv[1]
-
-with open(file_name, 'r') as f:
- config_file = f.read()
+cfg_base = ['vpn', 'l2tp', 'remote-access']
-config = ConfigTree(config_file)
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(cfg_base):
+ # Nothing to do
+ return
-cfg_base = ['vpn', 'l2tp', 'remote-access']
-if not config.exists(cfg_base):
- # Nothing to do
- sys.exit(0)
-else:
if config.exists(cfg_base + ['outside-nexthop']):
config.delete(cfg_base + ['outside-nexthop'])
-
- 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))
- sys.exit(1)
diff --git a/src/migration-scripts/l2tp/2-to-3 b/src/migration-scripts/l2tp/2-to-3
index 8527c2d4a..00fabb6b6 100755..100644
--- a/src/migration-scripts/l2tp/2-to-3
+++ b/src/migration-scripts/l2tp/2-to-3
@@ -1,40 +1,32 @@
-#!/usr/bin/env python3
+# Copyright 2020-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2020-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; either
+# version 2.1 of the License, or (at your option) any later version.
#
-# 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
-# - remove primary/secondary identifier from nameserver
-# - TODO: remove radius server req-limit
+# T2264: combine IPv4/IPv6 name-server CLI syntax
+# T2264: combine WINS CLI syntax
+# T2264: remove RADIUS req-limit node
+# T2264: migrate IPv6 prefix node to common CLI style
-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]
+base = ['vpn', 'l2tp', 'remote-access']
-with open(file_name, 'r') as f:
- config_file = f.read()
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ # Nothing to do
+ return
-config = ConfigTree(config_file)
-base = ['vpn', 'l2tp', 'remote-access']
-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):
@@ -98,10 +90,3 @@ else:
prefix = p.split(',')[0]
mask = p.split(',')[1]
config.set(ipv6_base + ['delegate', prefix, 'delegate-prefix'], value=mask)
-
- 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/l2tp/3-to-4 b/src/migration-scripts/l2tp/3-to-4
index 14b86ff04..01c3fa844 100755..100644
--- a/src/migration-scripts/l2tp/3-to-4
+++ b/src/migration-scripts/l2tp/3-to-4
@@ -1,26 +1,22 @@
-#!/usr/bin/env python3
+# Copyright 2021-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2021-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; either
+# version 2.1 of the License, or (at your option) any later version.
#
-# 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
-# - remove primary/secondary identifier from nameserver
-# - TODO: remove radius server req-limit
+# T2816: T3642: Move IPSec/L2TP code into vpn_ipsec.py and update to use PKI.
import os
-from sys import argv
-from sys import exit
from vyos.configtree import ConfigTree
from vyos.pki import load_certificate
from vyos.pki import load_private_key
@@ -28,141 +24,125 @@ from vyos.pki import encode_certificate
from vyos.pki import encode_private_key
from vyos.utils.process import run
-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 = ['vpn', 'l2tp', 'remote-access', 'ipsec-settings']
pki_base = ['pki']
-if not config.exists(base):
- exit(0)
-
AUTH_DIR = '/config/auth'
def wrapped_pem_to_config_value(pem):
return "".join(pem.strip().split("\n")[1:-1])
-if not config.exists(base + ['authentication', 'x509']):
- exit(0)
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ return
+
+ if not config.exists(base + ['authentication', 'x509']):
+ return
+
+ x509_base = base + ['authentication', 'x509']
+ pki_name = 'l2tp_remote_access'
+
+ if not config.exists(pki_base + ['ca']):
+ config.set(pki_base + ['ca'])
+ config.set_tag(pki_base + ['ca'])
+
+ if not config.exists(pki_base + ['certificate']):
+ config.set(pki_base + ['certificate'])
+ config.set_tag(pki_base + ['certificate'])
+
+ if config.exists(x509_base + ['ca-cert-file']):
+ cert_file = config.return_value(x509_base + ['ca-cert-file'])
+ cert_path = os.path.join(AUTH_DIR, cert_file)
+ cert = None
+
+ if os.path.isfile(cert_path):
+ if not os.access(cert_path, os.R_OK):
+ run(f'sudo chmod 644 {cert_path}')
+
+ with open(cert_path, 'r') as f:
+ cert_data = f.read()
+ cert = load_certificate(cert_data, wrap_tags=False)
+
+ if cert:
+ cert_pem = encode_certificate(cert)
+ config.set(pki_base + ['ca', pki_name, 'certificate'], value=wrapped_pem_to_config_value(cert_pem))
+ config.set(x509_base + ['ca-certificate'], value=pki_name)
+ else:
+ print(f'Failed to migrate CA certificate on l2tp remote-access config')
+
+ config.delete(x509_base + ['ca-cert-file'])
+
+ if config.exists(x509_base + ['crl-file']):
+ crl_file = config.return_value(x509_base + ['crl-file'])
+ crl_path = os.path.join(AUTH_DIR, crl_file)
+ crl = None
+
+ if os.path.isfile(crl_path):
+ if not os.access(crl_path, os.R_OK):
+ run(f'sudo chmod 644 {crl_path}')
+
+ with open(crl_path, 'r') as f:
+ crl_data = f.read()
+ crl = load_certificate(crl_data, wrap_tags=False)
+
+ if crl:
+ crl_pem = encode_certificate(crl)
+ config.set(pki_base + ['ca', pki_name, 'crl'], value=wrapped_pem_to_config_value(crl_pem))
+ else:
+ print(f'Failed to migrate CRL on l2tp remote-access config')
+
+ config.delete(x509_base + ['crl-file'])
+
+ if config.exists(x509_base + ['server-cert-file']):
+ cert_file = config.return_value(x509_base + ['server-cert-file'])
+ cert_path = os.path.join(AUTH_DIR, cert_file)
+ cert = None
+
+ if os.path.isfile(cert_path):
+ if not os.access(cert_path, os.R_OK):
+ run(f'sudo chmod 644 {cert_path}')
+
+ with open(cert_path, 'r') as f:
+ cert_data = f.read()
+ cert = load_certificate(cert_data, wrap_tags=False)
+
+ if cert:
+ cert_pem = encode_certificate(cert)
+ config.set(pki_base + ['certificate', pki_name, 'certificate'], value=wrapped_pem_to_config_value(cert_pem))
+ config.set(x509_base + ['certificate'], value=pki_name)
+ else:
+ print(f'Failed to migrate certificate on l2tp remote-access config')
+
+ config.delete(x509_base + ['server-cert-file'])
+
+ if config.exists(x509_base + ['server-key-file']):
+ key_file = config.return_value(x509_base + ['server-key-file'])
+ key_passphrase = None
-x509_base = base + ['authentication', 'x509']
-pki_name = 'l2tp_remote_access'
+ if config.exists(x509_base + ['server-key-password']):
+ key_passphrase = config.return_value(x509_base + ['server-key-password'])
-if not config.exists(pki_base + ['ca']):
- config.set(pki_base + ['ca'])
- config.set_tag(pki_base + ['ca'])
+ key_path = os.path.join(AUTH_DIR, key_file)
+ key = None
-if not config.exists(pki_base + ['certificate']):
- config.set(pki_base + ['certificate'])
- config.set_tag(pki_base + ['certificate'])
+ if os.path.isfile(key_path):
+ if not os.access(key_path, os.R_OK):
+ run(f'sudo chmod 644 {key_path}')
-if config.exists(x509_base + ['ca-cert-file']):
- cert_file = config.return_value(x509_base + ['ca-cert-file'])
- cert_path = os.path.join(AUTH_DIR, cert_file)
- cert = None
-
- if os.path.isfile(cert_path):
- if not os.access(cert_path, os.R_OK):
- run(f'sudo chmod 644 {cert_path}')
-
- with open(cert_path, 'r') as f:
- cert_data = f.read()
- cert = load_certificate(cert_data, wrap_tags=False)
-
- if cert:
- cert_pem = encode_certificate(cert)
- config.set(pki_base + ['ca', pki_name, 'certificate'], value=wrapped_pem_to_config_value(cert_pem))
- config.set(x509_base + ['ca-certificate'], value=pki_name)
- else:
- print(f'Failed to migrate CA certificate on l2tp remote-access config')
-
- config.delete(x509_base + ['ca-cert-file'])
-
-if config.exists(x509_base + ['crl-file']):
- crl_file = config.return_value(x509_base + ['crl-file'])
- crl_path = os.path.join(AUTH_DIR, crl_file)
- crl = None
-
- if os.path.isfile(crl_path):
- if not os.access(crl_path, os.R_OK):
- run(f'sudo chmod 644 {crl_path}')
-
- with open(crl_path, 'r') as f:
- crl_data = f.read()
- crl = load_certificate(crl_data, wrap_tags=False)
-
- if crl:
- crl_pem = encode_certificate(crl)
- config.set(pki_base + ['ca', pki_name, 'crl'], value=wrapped_pem_to_config_value(crl_pem))
- else:
- print(f'Failed to migrate CRL on l2tp remote-access config')
-
- config.delete(x509_base + ['crl-file'])
-
-if config.exists(x509_base + ['server-cert-file']):
- cert_file = config.return_value(x509_base + ['server-cert-file'])
- cert_path = os.path.join(AUTH_DIR, cert_file)
- cert = None
-
- if os.path.isfile(cert_path):
- if not os.access(cert_path, os.R_OK):
- run(f'sudo chmod 644 {cert_path}')
-
- with open(cert_path, 'r') as f:
- cert_data = f.read()
- cert = load_certificate(cert_data, wrap_tags=False)
-
- if cert:
- cert_pem = encode_certificate(cert)
- config.set(pki_base + ['certificate', pki_name, 'certificate'], value=wrapped_pem_to_config_value(cert_pem))
- config.set(x509_base + ['certificate'], value=pki_name)
- else:
- print(f'Failed to migrate certificate on l2tp remote-access config')
-
- config.delete(x509_base + ['server-cert-file'])
-
-if config.exists(x509_base + ['server-key-file']):
- key_file = config.return_value(x509_base + ['server-key-file'])
- key_passphrase = None
-
- if config.exists(x509_base + ['server-key-password']):
- key_passphrase = config.return_value(x509_base + ['server-key-password'])
-
- key_path = os.path.join(AUTH_DIR, key_file)
- key = None
-
- if os.path.isfile(key_path):
- if not os.access(key_path, os.R_OK):
- run(f'sudo chmod 644 {key_path}')
-
- with open(key_path, 'r') as f:
- key_data = f.read()
- key = load_private_key(key_data, passphrase=key_passphrase, wrap_tags=False)
-
- if key:
- key_pem = encode_private_key(key, passphrase=key_passphrase)
- config.set(pki_base + ['certificate', pki_name, 'private', 'key'], value=wrapped_pem_to_config_value(key_pem))
-
- if key_passphrase:
- config.set(pki_base + ['certificate', pki_name, 'private', 'password-protected'])
- config.set(x509_base + ['private-key-passphrase'], value=key_passphrase)
- else:
- print(f'Failed to migrate private key on l2tp remote-access config')
-
- config.delete(x509_base + ['server-key-file'])
- if config.exists(x509_base + ['server-key-password']):
- config.delete(x509_base + ['server-key-password'])
-
-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)
+ with open(key_path, 'r') as f:
+ key_data = f.read()
+ key = load_private_key(key_data, passphrase=key_passphrase, wrap_tags=False)
+
+ if key:
+ key_pem = encode_private_key(key, passphrase=key_passphrase)
+ config.set(pki_base + ['certificate', pki_name, 'private', 'key'], value=wrapped_pem_to_config_value(key_pem))
+
+ if key_passphrase:
+ config.set(pki_base + ['certificate', pki_name, 'private', 'password-protected'])
+ config.set(x509_base + ['private-key-passphrase'], value=key_passphrase)
+ else:
+ print(f'Failed to migrate private key on l2tp remote-access config')
+
+ config.delete(x509_base + ['server-key-file'])
+ if config.exists(x509_base + ['server-key-password']):
+ config.delete(x509_base + ['server-key-password'])
diff --git a/src/migration-scripts/l2tp/4-to-5 b/src/migration-scripts/l2tp/4-to-5
index b7f4d2677..56d451b8d 100755..100644
--- a/src/migration-scripts/l2tp/4-to-5
+++ b/src/migration-scripts/l2tp/4-to-5
@@ -1,85 +1,68 @@
-#!/usr/bin/env python3
+# Copyright 2023-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2023-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; either
+# version 2.1 of the License, or (at your option) any later version.
#
-# 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# - move all pool to named pools
# 'start-stop' migrate to namedpool 'default-range-pool'
# 'subnet' migrate to namedpool 'default-subnet-pool'
# 'default-subnet-pool' is the next pool for 'default-range-pool'
-from sys import argv
-from sys import exit
from vyos.configtree import ConfigTree
from vyos.base import Warning
-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 = ['vpn', 'l2tp', 'remote-access']
pool_base = base + ['client-ip-pool']
-if not config.exists(base):
- exit(0)
-if not config.exists(pool_base):
- exit(0)
-default_pool = ''
-range_pool_name = 'default-range-pool'
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ return
-if config.exists(pool_base + ['start']) and config.exists(pool_base + ['stop']):
- def is_legalrange(ip1: str, ip2: str, mask: str):
- from ipaddress import IPv4Interface
- interface1 = IPv4Interface(f'{ip1}/{mask}')
+ if not config.exists(pool_base):
+ return
- interface2 = IPv4Interface(f'{ip2}/{mask}')
- return interface1.network.network_address == interface2.network.network_address and interface2.ip > interface1.ip
+ default_pool = ''
+ range_pool_name = 'default-range-pool'
- start_ip = config.return_value(pool_base + ['start'])
- stop_ip = config.return_value(pool_base + ['stop'])
- if is_legalrange(start_ip, stop_ip,'24'):
- ip_range = f'{start_ip}-{stop_ip}'
- config.set(pool_base + [range_pool_name, 'range'], value=ip_range, replace=False)
- default_pool = range_pool_name
- else:
- Warning(
- f'L2TP client-ip-pool range start-ip:{start_ip} and stop-ip:{stop_ip} can not be migrated.')
+ if config.exists(pool_base + ['start']) and config.exists(pool_base + ['stop']):
+ def is_legalrange(ip1: str, ip2: str, mask: str):
+ from ipaddress import IPv4Interface
+ interface1 = IPv4Interface(f'{ip1}/{mask}')
+
+ interface2 = IPv4Interface(f'{ip2}/{mask}')
+ return interface1.network.network_address == interface2.network.network_address and interface2.ip > interface1.ip
- config.delete(pool_base + ['start'])
- config.delete(pool_base + ['stop'])
+ start_ip = config.return_value(pool_base + ['start'])
+ stop_ip = config.return_value(pool_base + ['stop'])
+ if is_legalrange(start_ip, stop_ip,'24'):
+ ip_range = f'{start_ip}-{stop_ip}'
+ config.set(pool_base + [range_pool_name, 'range'], value=ip_range, replace=False)
+ default_pool = range_pool_name
+ else:
+ Warning(
+ f'L2TP client-ip-pool range start-ip:{start_ip} and stop-ip:{stop_ip} can not be migrated.')
-if config.exists(pool_base + ['subnet']):
- for subnet in config.return_values(pool_base + ['subnet']):
- config.set(pool_base + [range_pool_name, 'range'], value=subnet, replace=False)
+ config.delete(pool_base + ['start'])
+ config.delete(pool_base + ['stop'])
- config.delete(pool_base + ['subnet'])
- default_pool = range_pool_name
+ if config.exists(pool_base + ['subnet']):
+ for subnet in config.return_values(pool_base + ['subnet']):
+ config.set(pool_base + [range_pool_name, 'range'], value=subnet, replace=False)
-if default_pool:
- config.set(base + ['default-pool'], value=default_pool)
-# format as tag node
-config.set_tag(pool_base)
+ config.delete(pool_base + ['subnet'])
+ default_pool = range_pool_name
-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)
+ if default_pool:
+ config.set(base + ['default-pool'], value=default_pool)
+ # format as tag node
+ config.set_tag(pool_base)
diff --git a/src/migration-scripts/l2tp/5-to-6 b/src/migration-scripts/l2tp/5-to-6
index ac40b89c8..cc9f948a6 100755..100644
--- a/src/migration-scripts/l2tp/5-to-6
+++ b/src/migration-scripts/l2tp/5-to-6
@@ -1,106 +1,88 @@
-#!/usr/bin/env python3
+# Copyright 2023-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 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; either
+# version 2.1 of the License, or (at your option) any later version.
#
-# 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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 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 = ['vpn', 'l2tp', 'remote-access']
-if not config.exists(base):
- exit(0)
-
-#migrate idle to ppp option lcp-echo-timeout
-idle_path = base + ['idle']
-if config.exists(idle_path):
- config.set(base + ['ppp-options', 'lcp-echo-timeout'],
- value=config.return_value(idle_path))
- config.delete(idle_path)
-
-#migrate mppe from authentication to ppp-otion
-mppe_path = base + ['authentication', 'mppe']
-if config.exists(mppe_path):
- config.set(base + ['ppp-options', 'mppe'],
- value=config.return_value(mppe_path))
- config.delete(mppe_path)
-
-#migrate require to protocol
-require_path = base + ['authentication', 'require']
-if config.exists(require_path):
- protocols = list(config.return_values(require_path))
- for protocol in protocols:
- config.set(base + ['authentication', 'protocols'], value=protocol,
- replace=False)
- config.delete(require_path)
-else:
- config.set(base + ['authentication', 'protocols'], value='mschap-v2')
-
-#migrate default gateway if not exist
-if not config.exists(base + ['gateway-address']):
- config.set(base + ['gateway-address'], value='10.255.255.0')
-
-#migrate authentication radius timeout
-rad_timeout_path = base + ['authentication', 'radius', 'timeout']
-if config.exists(rad_timeout_path):
- if int(config.return_value(rad_timeout_path)) > 60:
- config.set(rad_timeout_path, value=60)
-
-#migrate authentication radius acct timeout
-rad_acct_timeout_path = base + ['authentication', 'radius', 'acct-timeout']
-if config.exists(rad_acct_timeout_path):
- if int(config.return_value(rad_acct_timeout_path)) > 60:
- config.set(rad_acct_timeout_path,value=60)
-
-#migrate authentication radius max-try
-rad_max_try_path = base + ['authentication', 'radius', 'max-try']
-if config.exists(rad_max_try_path):
- if int(config.return_value(rad_max_try_path)) > 20:
- config.set(rad_max_try_path, value=20)
-
-#migrate dae-server to dynamic-author
-dae_path_old = base + ['authentication', 'radius', 'dae-server']
-dae_path_new = base + ['authentication', 'radius', 'dynamic-author']
-
-if config.exists(dae_path_old + ['ip-address']):
- config.set(dae_path_new + ['server'],
- value=config.return_value(dae_path_old + ['ip-address']))
-
-if config.exists(dae_path_old + ['port']):
- config.set(dae_path_new + ['port'],
- value=config.return_value(dae_path_old + ['port']))
-
-if config.exists(dae_path_old + ['secret']):
- config.set(dae_path_new + ['key'],
- value=config.return_value(dae_path_old + ['secret']))
-
-if config.exists(dae_path_old):
- config.delete(dae_path_old)
-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 migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ return
+
+ #migrate idle to ppp option lcp-echo-timeout
+ idle_path = base + ['idle']
+ if config.exists(idle_path):
+ config.set(base + ['ppp-options', 'lcp-echo-timeout'],
+ value=config.return_value(idle_path))
+ config.delete(idle_path)
+
+ #migrate mppe from authentication to ppp-otion
+ mppe_path = base + ['authentication', 'mppe']
+ if config.exists(mppe_path):
+ config.set(base + ['ppp-options', 'mppe'],
+ value=config.return_value(mppe_path))
+ config.delete(mppe_path)
+
+ #migrate require to protocol
+ require_path = base + ['authentication', 'require']
+ if config.exists(require_path):
+ protocols = list(config.return_values(require_path))
+ for protocol in protocols:
+ config.set(base + ['authentication', 'protocols'], value=protocol,
+ replace=False)
+ config.delete(require_path)
+ else:
+ config.set(base + ['authentication', 'protocols'], value='mschap-v2')
+
+ #migrate default gateway if not exist
+ if not config.exists(base + ['gateway-address']):
+ config.set(base + ['gateway-address'], value='10.255.255.0')
+
+ #migrate authentication radius timeout
+ rad_timeout_path = base + ['authentication', 'radius', 'timeout']
+ if config.exists(rad_timeout_path):
+ if int(config.return_value(rad_timeout_path)) > 60:
+ config.set(rad_timeout_path, value=60)
+
+ #migrate authentication radius acct timeout
+ rad_acct_timeout_path = base + ['authentication', 'radius', 'acct-timeout']
+ if config.exists(rad_acct_timeout_path):
+ if int(config.return_value(rad_acct_timeout_path)) > 60:
+ config.set(rad_acct_timeout_path,value=60)
+
+ #migrate authentication radius max-try
+ rad_max_try_path = base + ['authentication', 'radius', 'max-try']
+ if config.exists(rad_max_try_path):
+ if int(config.return_value(rad_max_try_path)) > 20:
+ config.set(rad_max_try_path, value=20)
+
+ #migrate dae-server to dynamic-author
+ dae_path_old = base + ['authentication', 'radius', 'dae-server']
+ dae_path_new = base + ['authentication', 'radius', 'dynamic-author']
+
+ if config.exists(dae_path_old + ['ip-address']):
+ config.set(dae_path_new + ['server'],
+ value=config.return_value(dae_path_old + ['ip-address']))
+
+ if config.exists(dae_path_old + ['port']):
+ config.set(dae_path_new + ['port'],
+ value=config.return_value(dae_path_old + ['port']))
+
+ if config.exists(dae_path_old + ['secret']):
+ config.set(dae_path_new + ['key'],
+ value=config.return_value(dae_path_old + ['secret']))
+
+ if config.exists(dae_path_old):
+ config.delete(dae_path_old)
diff --git a/src/migration-scripts/l2tp/6-to-7 b/src/migration-scripts/l2tp/6-to-7
index 1c536585c..4dba5974e 100755..100644
--- a/src/migration-scripts/l2tp/6-to-7
+++ b/src/migration-scripts/l2tp/6-to-7
@@ -1,57 +1,39 @@
-#!/usr/bin/env python3
+# Copyright 2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 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; either
+# version 2.1 of the License, or (at your option) any later version.
#
-# 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# Migrating to named ipv6 pools
-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 = ['vpn', 'l2tp', 'remote-access']
pool_base = base + ['client-ipv6-pool']
-if not config.exists(base):
- exit(0)
-if not config.exists(pool_base):
- exit(0)
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ return
-ipv6_pool_name = 'ipv6-pool'
-config.copy(pool_base, pool_base + [ipv6_pool_name])
+ if not config.exists(pool_base):
+ return
-if config.exists(pool_base + ['prefix']):
- config.delete(pool_base + ['prefix'])
- config.set(base + ['default-ipv6-pool'], value=ipv6_pool_name)
-if config.exists(pool_base + ['delegate']):
- config.delete(pool_base + ['delegate'])
-# format as tag node
-config.set_tag(pool_base)
+ ipv6_pool_name = 'ipv6-pool'
+ config.copy(pool_base, pool_base + [ipv6_pool_name])
-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)
+ if config.exists(pool_base + ['prefix']):
+ config.delete(pool_base + ['prefix'])
+ config.set(base + ['default-ipv6-pool'], value=ipv6_pool_name)
+ if config.exists(pool_base + ['delegate']):
+ config.delete(pool_base + ['delegate'])
+ # format as tag node
+ config.set_tag(pool_base)
diff --git a/src/migration-scripts/l2tp/7-to-8 b/src/migration-scripts/l2tp/7-to-8
index e429ed057..527906fc8 100755..100644
--- a/src/migration-scripts/l2tp/7-to-8
+++ b/src/migration-scripts/l2tp/7-to-8
@@ -1,65 +1,47 @@
-#!/usr/bin/env python3
+# Copyright 2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 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; either
+# version 2.1 of the License, or (at your option) any later version.
#
-# 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# Migrate from 'ccp-disable' to 'ppp-options.disable-ccp'
# Migration ipv6 options
-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 = ['vpn', 'l2tp', 'remote-access']
-if not config.exists(base):
- exit(0)
-
-#CCP migration
-if config.exists(base + ['ccp-disable']):
- config.delete(base + ['ccp-disable'])
- config.set(base + ['ppp-options', 'disable-ccp'])
-
-#IPV6 options migrations
-if config.exists(base + ['ppp-options','ipv6-peer-intf-id']):
- intf_peer_id = config.return_value(base + ['ppp-options','ipv6-peer-intf-id'])
- if intf_peer_id == 'ipv4':
- intf_peer_id = 'ipv4-addr'
- config.set(base + ['ppp-options','ipv6-peer-interface-id'], value=intf_peer_id, replace=True)
- config.delete(base + ['ppp-options','ipv6-peer-intf-id'])
-
-if config.exists(base + ['ppp-options','ipv6-intf-id']):
- intf_id = config.return_value(base + ['ppp-options','ipv6-intf-id'])
- config.set(base + ['ppp-options','ipv6-interface-id'], value=intf_id, replace=True)
- config.delete(base + ['ppp-options','ipv6-intf-id'])
-
-if config.exists(base + ['ppp-options','ipv6-accept-peer-intf-id']):
- config.set(base + ['ppp-options','ipv6-accept-peer-interface-id'])
- config.delete(base + ['ppp-options','ipv6-accept-peer-intf-id'])
-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 migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ return
+
+ #CCP migration
+ if config.exists(base + ['ccp-disable']):
+ config.delete(base + ['ccp-disable'])
+ config.set(base + ['ppp-options', 'disable-ccp'])
+
+ #IPV6 options migrations
+ if config.exists(base + ['ppp-options','ipv6-peer-intf-id']):
+ intf_peer_id = config.return_value(base + ['ppp-options','ipv6-peer-intf-id'])
+ if intf_peer_id == 'ipv4':
+ intf_peer_id = 'ipv4-addr'
+ config.set(base + ['ppp-options','ipv6-peer-interface-id'], value=intf_peer_id, replace=True)
+ config.delete(base + ['ppp-options','ipv6-peer-intf-id'])
+
+ if config.exists(base + ['ppp-options','ipv6-intf-id']):
+ intf_id = config.return_value(base + ['ppp-options','ipv6-intf-id'])
+ config.set(base + ['ppp-options','ipv6-interface-id'], value=intf_id, replace=True)
+ config.delete(base + ['ppp-options','ipv6-intf-id'])
+
+ if config.exists(base + ['ppp-options','ipv6-accept-peer-intf-id']):
+ config.set(base + ['ppp-options','ipv6-accept-peer-interface-id'])
+ config.delete(base + ['ppp-options','ipv6-accept-peer-intf-id'])
diff --git a/src/migration-scripts/l2tp/8-to-9 b/src/migration-scripts/l2tp/8-to-9
index 672180e25..e6b689e80 100755..100644
--- a/src/migration-scripts/l2tp/8-to-9
+++ b/src/migration-scripts/l2tp/8-to-9
@@ -1,46 +1,28 @@
-#!/usr/bin/env python3
+# Copyright 2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 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; either
+# version 2.1 of the License, or (at your option) any later version.
#
-# 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# Deleted 'dhcp-interface' from l2tp
-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 = ['vpn', 'l2tp', 'remote-access']
-if not config.exists(base):
- exit(0)
-# deleting unused dhcp-interface
-if config.exists(base + ['dhcp-interface']):
- config.delete(base + ['dhcp-interface'])
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ return
-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)
+ # deleting unused dhcp-interface
+ if config.exists(base + ['dhcp-interface']):
+ config.delete(base + ['dhcp-interface'])
diff --git a/src/migration-scripts/lldp/0-to-1 b/src/migration-scripts/lldp/0-to-1
index a99356062..c16e7e84b 100755..100644
--- a/src/migration-scripts/lldp/0-to-1
+++ b/src/migration-scripts/lldp/0-to-1
@@ -1,49 +1,31 @@
-#!/usr/bin/env python3
+# Copyright 2020-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# 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 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# Delete "set service lldp interface <interface> location civic-based" option
# as it was broken most of the time anyways
-import sys
-
from vyos.configtree import ConfigTree
-if len(sys.argv) < 2:
- print("Must specify file name!")
- sys.exit(1)
-
-file_name = sys.argv[1]
+base = ['service', 'lldp', 'interface']
-with open(file_name, 'r') as f:
- config_file = f.read()
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ # Nothing to do
+ return
-config = ConfigTree(config_file)
-base = ['service', 'lldp', 'interface']
-if not config.exists(base):
- # Nothing to do
- sys.exit(0)
-else:
# Delete nodes with abandoned CLI syntax
for interface in config.list_nodes(base):
if config.exists(base + [interface, 'location', 'civic-based']):
config.delete(base + [interface, 'location', 'civic-based'])
-
- 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))
- sys.exit(1)
diff --git a/src/migration-scripts/lldp/1-to-2 b/src/migration-scripts/lldp/1-to-2
index 35efb25db..7f233a725 100755..100644
--- a/src/migration-scripts/lldp/1-to-2
+++ b/src/migration-scripts/lldp/1-to-2
@@ -1,48 +1,30 @@
-#!/usr/bin/env python3
+# Copyright 2023-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2023 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 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# T5855: migrate "set service lldp snmp enable" -> `set service lldp snmp"
-import sys
-
from vyos.configtree import ConfigTree
-if len(sys.argv) < 2:
- print("Must specify file name!")
- sys.exit(1)
-
-file_name = sys.argv[1]
-
-with open(file_name, 'r') as f:
- config_file = f.read()
-
-config = ConfigTree(config_file)
base = ['service', 'lldp']
-if not config.exists(base):
- # Nothing to do
- sys.exit(0)
-if config.exists(base + ['snmp']):
- enabled = config.exists(base + ['snmp', 'enable'])
- config.delete(base + ['snmp'])
- if enabled: config.set(base + ['snmp'])
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ # Nothing to do
+ return
-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))
- sys.exit(1)
+ if config.exists(base + ['snmp']):
+ enabled = config.exists(base + ['snmp', 'enable'])
+ config.delete(base + ['snmp'])
+ if enabled: config.set(base + ['snmp'])
diff --git a/src/migration-scripts/monitoring/0-to-1 b/src/migration-scripts/monitoring/0-to-1
index 384d22f8c..92f824325 100755..100644
--- a/src/migration-scripts/monitoring/0-to-1
+++ b/src/migration-scripts/monitoring/0-to-1
@@ -14,58 +14,53 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-# T3417: migrate IS-IS tagNode to node as we can only have one IS-IS process
+# 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
+# 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, see <http://www.gnu.org/licenses/>.
-from sys import argv
-from sys import exit
+# T3417: migrate IS-IS tagNode to node as we can only have one IS-IS process
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 = ['service', 'monitoring', 'telegraf']
-config = ConfigTree(config_file)
-
-if not config.exists(base):
- # Nothing to do
- exit(0)
-
-if config.exists(base + ['authentication', 'organization']):
- tmp = config.return_value(base + ['authentication', 'organization'])
- config.delete(base + ['authentication', 'organization'])
- config.set(base + ['influxdb', 'authentication', 'organization'], value=tmp)
-if config.exists(base + ['authentication', 'token']):
- tmp = config.return_value(base + ['authentication', 'token'])
- config.delete(base + ['authentication', 'token'])
- config.set(base + ['influxdb', 'authentication', 'token'], value=tmp)
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ # Nothing to do
+ return
-if config.exists(base + ['bucket']):
- tmp = config.return_value(base + ['bucket'])
- config.delete(base + ['bucket'])
- config.set(base + ['influxdb', 'bucket'], value=tmp)
+ if config.exists(base + ['authentication', 'organization']):
+ tmp = config.return_value(base + ['authentication', 'organization'])
+ config.delete(base + ['authentication', 'organization'])
+ config.set(base + ['influxdb', 'authentication', 'organization'], value=tmp)
-if config.exists(base + ['port']):
- tmp = config.return_value(base + ['port'])
- config.delete(base + ['port'])
- config.set(base + ['influxdb', 'port'], value=tmp)
+ if config.exists(base + ['authentication', 'token']):
+ tmp = config.return_value(base + ['authentication', 'token'])
+ config.delete(base + ['authentication', 'token'])
+ config.set(base + ['influxdb', 'authentication', 'token'], value=tmp)
-if config.exists(base + ['url']):
- tmp = config.return_value(base + ['url'])
- config.delete(base + ['url'])
- config.set(base + ['influxdb', 'url'], value=tmp)
+ if config.exists(base + ['bucket']):
+ tmp = config.return_value(base + ['bucket'])
+ config.delete(base + ['bucket'])
+ config.set(base + ['influxdb', 'bucket'], value=tmp)
+ if config.exists(base + ['port']):
+ tmp = config.return_value(base + ['port'])
+ config.delete(base + ['port'])
+ config.set(base + ['influxdb', 'port'], value=tmp)
-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)
+ if config.exists(base + ['url']):
+ tmp = config.return_value(base + ['url'])
+ config.delete(base + ['url'])
+ config.set(base + ['influxdb', 'url'], value=tmp)
diff --git a/src/migration-scripts/nat/4-to-5 b/src/migration-scripts/nat/4-to-5
index ce215d455..e1919da50 100755..100644
--- a/src/migration-scripts/nat/4-to-5
+++ b/src/migration-scripts/nat/4-to-5
@@ -1,40 +1,28 @@
-#!/usr/bin/env python3
+# Copyright 2020-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# 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 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# Drop the enable/disable from the nat "log" node. If log node is specified
# it is "enabled"
-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()
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(['nat']):
+ # Nothing to do
+ return
-config = ConfigTree(config_file)
-
-if not config.exists(['nat']):
- # Nothing to do
- exit(0)
-else:
for direction in ['source', 'destination']:
# If a node doesn't exist, we obviously have nothing to do.
if not config.exists(['nat', direction]):
@@ -55,10 +43,3 @@ else:
config.delete(base + ['log'])
if tmp == 'enable':
config.set(base + ['log'])
-
- 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/nat/5-to-6 b/src/migration-scripts/nat/5-to-6
index cfe98ddcf..a583d4eb6 100755..100644
--- a/src/migration-scripts/nat/5-to-6
+++ b/src/migration-scripts/nat/5-to-6
@@ -1,18 +1,17 @@
-#!/usr/bin/env python3
+# Copyright 2023-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 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; either
+# version 2.1 of the License, or (at your option) any later version.
#
-# 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# T5643: move from 'set nat [source|destination] rule X [inbound-interface|outbound interface] <iface>'
# to
@@ -23,7 +22,7 @@
# '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
@@ -62,21 +61,10 @@ def _func_T6100(conf, base_path):
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)
-
+def migrate(config: ConfigTree) -> None:
if not config.exists(['nat']):
# Nothing to do
- exit(0)
+ return
for direction in ['source', 'destination']:
# If a node doesn't exist, we obviously have nothing to do.
@@ -92,10 +80,3 @@ if __name__ == '__main__':
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/nat/6-to-7 b/src/migration-scripts/nat/6-to-7
index a2e735394..81b413e36 100755..100644
--- a/src/migration-scripts/nat/6-to-7
+++ b/src/migration-scripts/nat/6-to-7
@@ -1,18 +1,17 @@
-#!/usr/bin/env python3
+# Copyright 2023-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2023 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 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# T5681: Firewall re-writing. Simplify cli when mathcing interface
# From
@@ -23,49 +22,31 @@
# 'set nat [source|destination] rule X [inbound-interface|outbound interface] group <iface_group>'
# Also remove command if interface == any
-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]):
- if config.exists(base + [iface, 'interface-name']):
- tmp = config.return_value(base + [iface, 'interface-name'])
- if tmp != 'any':
- config.delete(base + [iface, 'interface-name'])
- config.set(base + [iface, 'name'], value=tmp)
- else:
- config.delete(base + [iface])
-
-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 migrate(config: ConfigTree) -> None:
+ if not config.exists(['nat']):
+ # Nothing to do
+ return
+
+ 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]):
+ if config.exists(base + [iface, 'interface-name']):
+ tmp = config.return_value(base + [iface, 'interface-name'])
+ if tmp != 'any':
+ config.delete(base + [iface, 'interface-name'])
+ config.set(base + [iface, 'name'], value=tmp)
+ else:
+ config.delete(base + [iface])
diff --git a/src/migration-scripts/nat/7-to-8 b/src/migration-scripts/nat/7-to-8
index ab2ffa6d3..9ae389ef1 100755..100644
--- a/src/migration-scripts/nat/7-to-8
+++ b/src/migration-scripts/nat/7-to-8
@@ -1,62 +1,43 @@
-#!/usr/bin/env python3
+# Copyright 2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 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; either
+# version 2.1 of the License, or (at your option) any later version.
#
-# 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# T6345: random - In kernel 5.0 and newer this is the same as fully-random.
# In earlier kernels the port mapping will be randomized using a seeded
# MD5 hash mix using source and destination address and destination port.
# drop fully-random from CLI
-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']):
- port_mapping = ['nat', direction, 'rule', rule, 'translation', 'options', 'port-mapping']
- if config.exists(port_mapping):
- tmp = config.return_value(port_mapping)
- if tmp == 'fully-random':
- config.set(port_mapping, value='random')
-
-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)
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(['nat']):
+ # Nothing to do
+ return
+
+ 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']):
+ port_mapping = ['nat', direction, 'rule', rule, 'translation', 'options', 'port-mapping']
+ if config.exists(port_mapping):
+ tmp = config.return_value(port_mapping)
+ if tmp == 'fully-random':
+ config.set(port_mapping, value='random')
diff --git a/src/migration-scripts/nat66/0-to-1 b/src/migration-scripts/nat66/0-to-1
index 444b2315f..b3c6bf4cc 100755..100644
--- a/src/migration-scripts/nat66/0-to-1
+++ b/src/migration-scripts/nat66/0-to-1
@@ -1,33 +1,20 @@
-#!/usr/bin/env python3
+# Copyright 2020-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# 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 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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 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)
-
def merge_npt(config,base,rule):
merge_base = ['nat66','source','rule',rule]
# Configure migration functions
@@ -51,21 +38,15 @@ def merge_npt(config,base,rule):
tmp = config.return_value(base + ['translation','prefix'])
config.set(merge_base + ['translation','address'],value=tmp)
-if not config.exists(['nat', 'nptv6']):
- # Nothing to do
- exit(0)
-
-for rule in config.list_nodes(['nat', 'nptv6', 'rule']):
- base = ['nat', 'nptv6', 'rule', rule]
- # Merge 'nat nptv6' to 'nat66 source'
- merge_npt(config,base,rule)
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(['nat', 'nptv6']):
+ # Nothing to do
+ return
-# Delete the original NPT configuration
-config.delete(['nat','nptv6']);
+ for rule in config.list_nodes(['nat', 'nptv6', 'rule']):
+ base = ['nat', 'nptv6', 'rule', rule]
+ # Merge 'nat nptv6' to 'nat66 source'
+ merge_npt(config,base,rule)
-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)
+ # Delete the original NPT configuration
+ config.delete(['nat','nptv6']);
diff --git a/src/migration-scripts/nat66/1-to-2 b/src/migration-scripts/nat66/1-to-2
index b7d4e3f6b..f49940ae0 100755..100644
--- a/src/migration-scripts/nat66/1-to-2
+++ b/src/migration-scripts/nat66/1-to-2
@@ -14,50 +14,48 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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
+# 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, see <http://www.gnu.org/licenses/>.
+
# T5681: Firewall re-writing. Simplify cli when mathcing interface
# From
# 'set nat66 [source|destination] rule X [inbound-interface|outbound interface] <iface>'
# to
# 'set nat66 [source|destination] rule X [inbound-interface|outbound interface] name <iface>'
-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(['nat66']):
- # 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(['nat66', 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(['nat66', direction]):
- continue
-
- for rule in config.list_nodes(['nat66', direction, 'rule']):
- base = ['nat66', direction, 'rule', rule]
- for iface in ['inbound-interface','outbound-interface']:
- if config.exists(base + [iface]):
- tmp = config.return_value(base + [iface])
- config.delete(base + [iface])
- config.set(base + [iface, '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 migrate(config: ConfigTree) -> None:
+ if not config.exists(['nat66']):
+ # Nothing to do
+ return
+
+ for direction in ['source', 'destination']:
+ # If a node doesn't exist, we obviously have nothing to do.
+ if not config.exists(['nat66', 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(['nat66', direction]):
+ continue
+
+ for rule in config.list_nodes(['nat66', direction, 'rule']):
+ base = ['nat66', direction, 'rule', rule]
+ for iface in ['inbound-interface','outbound-interface']:
+ if config.exists(base + [iface]):
+ tmp = config.return_value(base + [iface])
+ config.delete(base + [iface])
+ config.set(base + [iface, 'name'], value=tmp)
diff --git a/src/migration-scripts/nat66/2-to-3 b/src/migration-scripts/nat66/2-to-3
index f34f170b3..55d5f4b2b 100755..100644
--- a/src/migration-scripts/nat66/2-to-3
+++ b/src/migration-scripts/nat66/2-to-3
@@ -1,61 +1,45 @@
-#!/usr/bin/env python3
+# Copyright 2023-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2023 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 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-from sys import argv,exit
-from vyos.configtree import ConfigTree
+# 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/>.
-if len(argv) < 2:
- print("Must specify file name!")
- exit(1)
+# T2898: add ndp-proxy service
-file_name = argv[1]
-
-with open(file_name, 'r') as f:
- config_file = f.read()
+from vyos.configtree import ConfigTree
base = ['nat66', 'source']
new_base = ['service', 'ndp-proxy', 'interface']
-config = ConfigTree(config_file)
-if not config.exists(base):
- # Nothing to do
- exit(0)
-
-for rule in config.list_nodes(base + ['rule']):
- base_rule = base + ['rule', rule]
-
- interface = None
- if config.exists(base_rule + ['outbound-interface', 'name']):
- interface = config.return_value(base_rule + ['outbound-interface', 'name'])
- else:
- continue
-
- prefix_base = base_rule + ['source', 'prefix']
- if config.exists(prefix_base):
- prefix = config.return_value(prefix_base)
- config.set(new_base + [interface, 'prefix', prefix, 'mode'], value='static')
- config.set_tag(new_base)
- config.set_tag(new_base + [interface, 'prefix'])
-
- if config.exists(base_rule + ['disable']):
- config.set(new_base + [interface, 'prefix', prefix, 'disable'])
-
-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 migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ # Nothing to do
+ return
+
+ for rule in config.list_nodes(base + ['rule']):
+ base_rule = base + ['rule', rule]
+
+ interface = None
+ if config.exists(base_rule + ['outbound-interface', 'name']):
+ interface = config.return_value(base_rule + ['outbound-interface', 'name'])
+ else:
+ continue
+
+ prefix_base = base_rule + ['source', 'prefix']
+ if config.exists(prefix_base):
+ prefix = config.return_value(prefix_base)
+ config.set(new_base + [interface, 'prefix', prefix, 'mode'], value='static')
+ config.set_tag(new_base)
+ config.set_tag(new_base + [interface, 'prefix'])
+
+ if config.exists(base_rule + ['disable']):
+ config.set(new_base + [interface, 'prefix', prefix, 'disable'])
diff --git a/src/migration-scripts/ntp/0-to-1 b/src/migration-scripts/ntp/0-to-1
index cbce45b9b..01f5a460a 100755..100644
--- a/src/migration-scripts/ntp/0-to-1
+++ b/src/migration-scripts/ntp/0-to-1
@@ -1,36 +1,32 @@
#!/usr/bin/env python3
-# Delete "set system ntp server <n> dynamic" option
+# Copyright 2018-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
+# 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, see <http://www.gnu.org/licenses/>.
-import sys
+# Delete "set system ntp server <n> dynamic" option
from vyos.configtree import ConfigTree
-if len(sys.argv) < 2:
- print("Must specify file name!")
- sys.exit(1)
-
-file_name = sys.argv[1]
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(['system', 'ntp', 'server']):
+ # Nothing to do
+ return
-with open(file_name, 'r') as f:
- config_file = f.read()
-
-config = ConfigTree(config_file)
-
-if not config.exists(['system', 'ntp', 'server']):
- # Nothing to do
- sys.exit(0)
-else:
# Delete abandoned leaf node if found inside tag node for
# "set system ntp server <n> dynamic"
base = ['system', 'ntp', 'server']
for server in config.list_nodes(base):
if config.exists(base + [server, 'dynamic']):
config.delete(base + [server, 'dynamic'])
-
- 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))
- sys.exit(1)
diff --git a/src/migration-scripts/ntp/1-to-2 b/src/migration-scripts/ntp/1-to-2
index fd1f15d91..fd7b08221 100755..100644
--- a/src/migration-scripts/ntp/1-to-2
+++ b/src/migration-scripts/ntp/1-to-2
@@ -1,72 +1,53 @@
-#!/usr/bin/env python3
-
-# Copyright (C) 2023 VyOS maintainers and contributors
+# Copyright 2023-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License version 2 or later as
-# published by the Free Software Foundation.
+# This 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 program is distributed in the hope that it will be useful,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# T3008: move from ntpd to chrony and migrate "system ntp" to "service ntp"
-import sys
-
from vyos.configtree import ConfigTree
-if len(sys.argv) < 2:
- print("Must specify file name!")
- sys.exit(1)
-
-file_name = sys.argv[1]
-
-with open(file_name, 'r') as f:
- config_file = f.read()
-
-config = ConfigTree(config_file)
-
base_path = ['system', 'ntp']
new_base_path = ['service', 'ntp']
-if not config.exists(base_path):
- # Nothing to do
- sys.exit(0)
-
-# config.copy does not recursively create a path, so create ['service'] if
-# it doesn't yet exist, such as for config.boot.default
-if not config.exists(['service']):
- config.set(['service'])
-
-# copy "system ntp" to "service ntp"
-config.copy(base_path, new_base_path)
-config.delete(base_path)
-
-# chrony does not support the preempt option, drop it
-for server in config.list_nodes(new_base_path + ['server']):
- server_base = new_base_path + ['server', server]
- if config.exists(server_base + ['preempt']):
- config.delete(server_base + ['preempt'])
-
-# Rename "allow-clients" -> "allow-client"
-if config.exists(new_base_path + ['allow-clients']):
- config.rename(new_base_path + ['allow-clients'], 'allow-client')
-
-# By default VyOS 1.3 allowed NTP queries for all networks - in chrony we
-# explicitly disable this behavior and clients need to be specified using the
-# allow-client CLI option. In order to be fully backwards compatible, we specify
-# 0.0.0.0/0 and ::/0 as allow networks if not specified otherwise explicitly.
-if not config.exists(new_base_path + ['allow-client']):
- config.set(new_base_path + ['allow-client', 'address'], value='0.0.0.0/0', replace=False)
- config.set(new_base_path + ['allow-client', 'address'], value='::/0', replace=False)
-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))
- sys.exit(1)
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(base_path):
+ # Nothing to do
+ return
+
+ # config.copy does not recursively create a path, so create ['service'] if
+ # it doesn't yet exist, such as for config.boot.default
+ if not config.exists(['service']):
+ config.set(['service'])
+
+ # copy "system ntp" to "service ntp"
+ config.copy(base_path, new_base_path)
+ config.delete(base_path)
+
+ # chrony does not support the preempt option, drop it
+ for server in config.list_nodes(new_base_path + ['server']):
+ server_base = new_base_path + ['server', server]
+ if config.exists(server_base + ['preempt']):
+ config.delete(server_base + ['preempt'])
+
+ # Rename "allow-clients" -> "allow-client"
+ if config.exists(new_base_path + ['allow-clients']):
+ config.rename(new_base_path + ['allow-clients'], 'allow-client')
+
+ # By default VyOS 1.3 allowed NTP queries for all networks - in chrony we
+ # explicitly disable this behavior and clients need to be specified using the
+ # allow-client CLI option. In order to be fully backwards compatible, we specify
+ # 0.0.0.0/0 and ::/0 as allow networks if not specified otherwise explicitly.
+ if not config.exists(new_base_path + ['allow-client']):
+ config.set(new_base_path + ['allow-client', 'address'], value='0.0.0.0/0', replace=False)
+ config.set(new_base_path + ['allow-client', 'address'], value='::/0', replace=False)
diff --git a/src/migration-scripts/ntp/2-to-3 b/src/migration-scripts/ntp/2-to-3
index a4351845e..bbda90351 100755..100644
--- a/src/migration-scripts/ntp/2-to-3
+++ b/src/migration-scripts/ntp/2-to-3
@@ -1,62 +1,43 @@
-#!/usr/bin/env python3
-
-# Copyright (C) 2023 VyOS maintainers and contributors
+# Copyright 2023-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License version 2 or later as
-# published by the Free Software Foundation.
+# This 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 program is distributed in the hope that it will be useful,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# T5154: allow only one ip address per family for parameter 'listen-address'
# Allow only one interface for parameter 'interface'
# If more than one are specified, remove such entries
-import sys
-
from vyos.configtree import ConfigTree
from vyos.template import is_ipv4
from vyos.template import is_ipv6
-if len(sys.argv) < 2:
- print("Must specify file name!")
- sys.exit(1)
-
-file_name = sys.argv[1]
-
-with open(file_name, 'r') as f:
- config_file = f.read()
-
-config = ConfigTree(config_file)
-
base_path = ['service', 'ntp']
-if not config.exists(base_path):
- # Nothing to do
- sys.exit(0)
-if config.exists(base_path + ['listen-address']) and (len([addr for addr in config.return_values(base_path + ['listen-address']) if is_ipv4(addr)]) > 1):
- for addr in config.return_values(base_path + ['listen-address']):
- if is_ipv4(addr):
- config.delete_value(base_path + ['listen-address'], addr)
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(base_path):
+ # Nothing to do
+ return
-if config.exists(base_path + ['listen-address']) and (len([addr for addr in config.return_values(base_path + ['listen-address']) if is_ipv6(addr)]) > 1):
- for addr in config.return_values(base_path + ['listen-address']):
- if is_ipv6(addr):
- config.delete_value(base_path + ['listen-address'], addr)
+ if config.exists(base_path + ['listen-address']) and (len([addr for addr in config.return_values(base_path + ['listen-address']) if is_ipv4(addr)]) > 1):
+ for addr in config.return_values(base_path + ['listen-address']):
+ if is_ipv4(addr):
+ config.delete_value(base_path + ['listen-address'], addr)
-if config.exists(base_path + ['interface']):
- if len(config.return_values(base_path + ['interface'])) > 1:
- config.delete(base_path + ['interface'])
+ if config.exists(base_path + ['listen-address']) and (len([addr for addr in config.return_values(base_path + ['listen-address']) if is_ipv6(addr)]) > 1):
+ for addr in config.return_values(base_path + ['listen-address']):
+ if is_ipv6(addr):
+ config.delete_value(base_path + ['listen-address'], addr)
-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))
- sys.exit(1)
+ if config.exists(base_path + ['interface']):
+ if len(config.return_values(base_path + ['interface'])) > 1:
+ config.delete(base_path + ['interface'])
diff --git a/src/migration-scripts/openconnect/0-to-1 b/src/migration-scripts/openconnect/0-to-1
index c64b16cb2..aa5a97eee 100755..100644
--- a/src/migration-scripts/openconnect/0-to-1
+++ b/src/migration-scripts/openconnect/0-to-1
@@ -1,25 +1,22 @@
-#!/usr/bin/env python3
+# Copyright 2021-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2021-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; either
+# version 2.1 of the License, or (at your option) any later version.
#
-# 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# - Update SSL to use PKI configuration
import os
-from sys import argv
-from sys import exit
from vyos.configtree import ConfigTree
from vyos.pki import load_certificate
from vyos.pki import load_private_key
@@ -27,109 +24,93 @@ from vyos.pki import encode_certificate
from vyos.pki import encode_private_key
from vyos.utils.process import run
-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 = ['vpn', 'openconnect']
pki_base = ['pki']
-if not config.exists(base):
- exit(0)
-
AUTH_DIR = '/config/auth'
def wrapped_pem_to_config_value(pem):
return "".join(pem.strip().split("\n")[1:-1])
-if not config.exists(base + ['ssl']):
- exit(0)
-
-x509_base = base + ['ssl']
-pki_name = 'openconnect'
-
-if not config.exists(pki_base + ['ca']):
- config.set(pki_base + ['ca'])
- config.set_tag(pki_base + ['ca'])
-
-if not config.exists(pki_base + ['certificate']):
- config.set(pki_base + ['certificate'])
- config.set_tag(pki_base + ['certificate'])
-
-if config.exists(x509_base + ['ca-cert-file']):
- cert_file = config.return_value(x509_base + ['ca-cert-file'])
- cert_path = os.path.join(AUTH_DIR, cert_file)
- cert = None
-
- if os.path.isfile(cert_path):
- if not os.access(cert_path, os.R_OK):
- run(f'sudo chmod 644 {cert_path}')
-
- with open(cert_path, 'r') as f:
- cert_data = f.read()
- cert = load_certificate(cert_data, wrap_tags=False)
-
- if cert:
- cert_pem = encode_certificate(cert)
- config.set(pki_base + ['ca', pki_name, 'certificate'], value=wrapped_pem_to_config_value(cert_pem))
- config.set(x509_base + ['ca-certificate'], value=pki_name)
- else:
- print(f'Failed to migrate CA certificate on openconnect config')
-
- config.delete(x509_base + ['ca-cert-file'])
-
-if config.exists(x509_base + ['cert-file']):
- cert_file = config.return_value(x509_base + ['cert-file'])
- cert_path = os.path.join(AUTH_DIR, cert_file)
- cert = None
-
- if os.path.isfile(cert_path):
- if not os.access(cert_path, os.R_OK):
- run(f'sudo chmod 644 {cert_path}')
-
- with open(cert_path, 'r') as f:
- cert_data = f.read()
- cert = load_certificate(cert_data, wrap_tags=False)
-
- if cert:
- cert_pem = encode_certificate(cert)
- config.set(pki_base + ['certificate', pki_name, 'certificate'], value=wrapped_pem_to_config_value(cert_pem))
- config.set(x509_base + ['certificate'], value=pki_name)
- else:
- print(f'Failed to migrate certificate on openconnect config')
-
- config.delete(x509_base + ['cert-file'])
-
-if config.exists(x509_base + ['key-file']):
- key_file = config.return_value(x509_base + ['key-file'])
- key_path = os.path.join(AUTH_DIR, key_file)
- key = None
-
- if os.path.isfile(key_path):
- if not os.access(key_path, os.R_OK):
- run(f'sudo chmod 644 {key_path}')
-
- with open(key_path, 'r') as f:
- key_data = f.read()
- key = load_private_key(key_data, passphrase=None, wrap_tags=False)
-
- if key:
- key_pem = encode_private_key(key, passphrase=None)
- config.set(pki_base + ['certificate', pki_name, 'private', 'key'], value=wrapped_pem_to_config_value(key_pem))
- else:
- print(f'Failed to migrate private key on openconnect config')
-
- config.delete(x509_base + ['key-file'])
-
-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 migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ return
+
+ if not config.exists(base + ['ssl']):
+ return
+
+ x509_base = base + ['ssl']
+ pki_name = 'openconnect'
+
+ if not config.exists(pki_base + ['ca']):
+ config.set(pki_base + ['ca'])
+ config.set_tag(pki_base + ['ca'])
+
+ if not config.exists(pki_base + ['certificate']):
+ config.set(pki_base + ['certificate'])
+ config.set_tag(pki_base + ['certificate'])
+
+ if config.exists(x509_base + ['ca-cert-file']):
+ cert_file = config.return_value(x509_base + ['ca-cert-file'])
+ cert_path = os.path.join(AUTH_DIR, cert_file)
+ cert = None
+
+ if os.path.isfile(cert_path):
+ if not os.access(cert_path, os.R_OK):
+ run(f'sudo chmod 644 {cert_path}')
+
+ with open(cert_path, 'r') as f:
+ cert_data = f.read()
+ cert = load_certificate(cert_data, wrap_tags=False)
+
+ if cert:
+ cert_pem = encode_certificate(cert)
+ config.set(pki_base + ['ca', pki_name, 'certificate'], value=wrapped_pem_to_config_value(cert_pem))
+ config.set(x509_base + ['ca-certificate'], value=pki_name)
+ else:
+ print(f'Failed to migrate CA certificate on openconnect config')
+
+ config.delete(x509_base + ['ca-cert-file'])
+
+ if config.exists(x509_base + ['cert-file']):
+ cert_file = config.return_value(x509_base + ['cert-file'])
+ cert_path = os.path.join(AUTH_DIR, cert_file)
+ cert = None
+
+ if os.path.isfile(cert_path):
+ if not os.access(cert_path, os.R_OK):
+ run(f'sudo chmod 644 {cert_path}')
+
+ with open(cert_path, 'r') as f:
+ cert_data = f.read()
+ cert = load_certificate(cert_data, wrap_tags=False)
+
+ if cert:
+ cert_pem = encode_certificate(cert)
+ config.set(pki_base + ['certificate', pki_name, 'certificate'], value=wrapped_pem_to_config_value(cert_pem))
+ config.set(x509_base + ['certificate'], value=pki_name)
+ else:
+ print(f'Failed to migrate certificate on openconnect config')
+
+ config.delete(x509_base + ['cert-file'])
+
+ if config.exists(x509_base + ['key-file']):
+ key_file = config.return_value(x509_base + ['key-file'])
+ key_path = os.path.join(AUTH_DIR, key_file)
+ key = None
+
+ if os.path.isfile(key_path):
+ if not os.access(key_path, os.R_OK):
+ run(f'sudo chmod 644 {key_path}')
+
+ with open(key_path, 'r') as f:
+ key_data = f.read()
+ key = load_private_key(key_data, passphrase=None, wrap_tags=False)
+
+ if key:
+ key_pem = encode_private_key(key, passphrase=None)
+ config.set(pki_base + ['certificate', pki_name, 'private', 'key'], value=wrapped_pem_to_config_value(key_pem))
+ else:
+ print(f'Failed to migrate private key on openconnect config')
+
+ config.delete(x509_base + ['key-file'])
diff --git a/src/migration-scripts/openconnect/1-to-2 b/src/migration-scripts/openconnect/1-to-2
index 7978aa56e..4f74b44df 100755..100644
--- a/src/migration-scripts/openconnect/1-to-2
+++ b/src/migration-scripts/openconnect/1-to-2
@@ -1,42 +1,29 @@
-#!/usr/bin/env python3
+# Copyright 2022-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2022 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 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# Delete depricated outside-nexthop address
-import sys
-
from vyos.configtree import ConfigTree
-if len(sys.argv) < 2:
- print("Must specify file name!")
- sys.exit(1)
-
-file_name = sys.argv[1]
-
-with open(file_name, 'r') as f:
- config_file = f.read()
-
-config = ConfigTree(config_file)
-
cfg_base = ['vpn', 'openconnect']
-if not config.exists(cfg_base):
- # Nothing to do
- sys.exit(0)
-else:
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(cfg_base):
+ # Nothing to do
+ return
+
if config.exists(cfg_base + ['authentication', 'mode']):
if config.return_value(cfg_base + ['authentication', 'mode']) == 'radius':
# if "mode value radius", change to "mode + valueless node radius"
@@ -46,9 +33,3 @@ else:
# if "mode local", change to "mode + node local value password"
config.delete_value(cfg_base + ['authentication', 'mode'], 'local')
config.set(cfg_base + ['authentication', 'mode', 'local'], value='password')
- 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))
- sys.exit(1)
diff --git a/src/migration-scripts/openconnect/2-to-3 b/src/migration-scripts/openconnect/2-to-3
index e78fc8a91..00e13ecb0 100755..100644
--- a/src/migration-scripts/openconnect/2-to-3
+++ b/src/migration-scripts/openconnect/2-to-3
@@ -1,50 +1,30 @@
-#!/usr/bin/env python3
+# Copyright 2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 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; either
+# version 2.1 of the License, or (at your option) any later version.
#
-# 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# T4982: Retain prior default TLS version (v1.0) when upgrading installations with existing openconnect configurations
-import sys
-
from vyos.configtree import ConfigTree
-if len(sys.argv) < 2:
- print("Must specify file name!")
- sys.exit(1)
-
-file_name = sys.argv[1]
-
-with open(file_name, 'r') as f:
- config_file = f.read()
-
-
-config = ConfigTree(config_file)
cfg_base = ['vpn', 'openconnect']
-# bail out early if service is unconfigured
-if not config.exists(cfg_base):
- sys.exit(0)
-
-# new default is TLS 1.2 - set explicit old default value of TLS 1.0 for upgraded configurations to keep compatibility
-tls_min_path = cfg_base + ['tls-version-min']
-if not config.exists(tls_min_path):
- config.set(tls_min_path, value='1.0')
+def migrate(config: ConfigTree) -> None:
+ # bail out early if service is unconfigured
+ if not config.exists(cfg_base):
+ return
-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))
- sys.exit(1)
+ # new default is TLS 1.2 - set explicit old default value of TLS 1.0 for upgraded configurations to keep compatibility
+ tls_min_path = cfg_base + ['tls-version-min']
+ if not config.exists(tls_min_path):
+ config.set(tls_min_path, value='1.0')
diff --git a/src/migration-scripts/openvpn/0-to-1 b/src/migration-scripts/openvpn/0-to-1
index 24bb38d3c..e5db731ed 100755..100644
--- a/src/migration-scripts/openvpn/0-to-1
+++ b/src/migration-scripts/openvpn/0-to-1
@@ -1,26 +1,27 @@
-#!/usr/bin/env python3
+# 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
+# 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, see <http://www.gnu.org/licenses/>.
# Removes outdated ciphers (DES and Blowfish) from OpenVPN configs
-import sys
-
from vyos.configtree import ConfigTree
-if len(sys.argv) < 2:
- print("Must specify file name!")
- sys.exit(1)
-
-file_name = sys.argv[1]
-
-with open(file_name, 'r') as f:
- config_file = f.read()
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(['interfaces', 'openvpn']):
+ # Nothing to do
+ return
-config = ConfigTree(config_file)
-
-if not config.exists(['interfaces', 'openvpn']):
- # Nothing to do
- sys.exit(0)
-else:
ovpn_intfs = config.list_nodes(['interfaces', 'openvpn'])
for i in ovpn_intfs:
# Remove DES and Blowfish from 'encryption cipher'
@@ -40,10 +41,3 @@ else:
if config.exists(['interfaces', 'openvpn', i, 'encryption']) and \
(config.list_nodes(['interfaces', 'openvpn', i, 'encryption']) == []):
config.delete(['interfaces', 'openvpn', i, 'encryption'])
-
- 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))
- sys.exit(1)
diff --git a/src/migration-scripts/openvpn/1-to-2 b/src/migration-scripts/openvpn/1-to-2
index 1f82a2128..b7b7d4c77 100755..100644
--- a/src/migration-scripts/openvpn/1-to-2
+++ b/src/migration-scripts/openvpn/1-to-2
@@ -17,25 +17,13 @@
# Removes --cipher option (deprecated) from OpenVPN configs
# and moves it to --data-ciphers for server and client modes
-import sys
-
from vyos.configtree import ConfigTree
-if len(sys.argv) < 2:
- print("Must specify file name!")
- sys.exit(1)
-
-file_name = sys.argv[1]
-
-with open(file_name, 'r') as f:
- config_file = f.read()
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(['interfaces', 'openvpn']):
+ # Nothing to do
+ return
-config = ConfigTree(config_file)
-
-if not config.exists(['interfaces', 'openvpn']):
- # Nothing to do
- sys.exit(0)
-else:
ovpn_intfs = config.list_nodes(['interfaces', 'openvpn'])
for i in ovpn_intfs:
# Remove 'encryption cipher' and add this value to 'encryption ncp-ciphers'
@@ -65,10 +53,3 @@ else:
for c in ncp_ciphers:
config.set(ncp_cipher_path, value=c, replace=False)
-
- 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))
- sys.exit(1)
diff --git a/src/migration-scripts/ospf/0-to-1 b/src/migration-scripts/ospf/0-to-1
index 4085423a2..a1f810960 100755..100644
--- a/src/migration-scripts/ospf/0-to-1
+++ b/src/migration-scripts/ospf/0-to-1
@@ -1,22 +1,20 @@
-#!/usr/bin/env python3
+# Copyright 2021-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2021-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; either
+# version 2.1 of the License, or (at your option) any later version.
#
-# 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# T3753: upgrade to FRR8 and move CLI options to better fit with the new FRR CLI
-from sys import argv
from vyos.configtree import ConfigTree
def ospf_passive_migration(config, ospf_base):
@@ -40,45 +38,29 @@ def ospf_passive_migration(config, ospf_base):
config.set_tag(ospf_base + ['interface'])
config.delete(ospf_base + ['passive-interface-exclude'])
-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)
-
ospfv3_base = ['protocols', 'ospfv3']
-if config.exists(ospfv3_base):
- area_base = ospfv3_base + ['area']
- if config.exists(area_base):
- for area in config.list_nodes(area_base):
- if not config.exists(area_base + [area, 'interface']):
- continue
- for interface in config.return_values(area_base + [area, 'interface']):
- config.set(ospfv3_base + ['interface', interface, 'area'], value=area)
- config.set_tag(ospfv3_base + ['interface'])
+def migrate(config: ConfigTree) -> None:
+ if config.exists(ospfv3_base):
+ area_base = ospfv3_base + ['area']
+ if config.exists(area_base):
+ for area in config.list_nodes(area_base):
+ if not config.exists(area_base + [area, 'interface']):
+ continue
- config.delete(area_base + [area, 'interface'])
+ for interface in config.return_values(area_base + [area, 'interface']):
+ config.set(ospfv3_base + ['interface', interface, 'area'], value=area)
+ config.set_tag(ospfv3_base + ['interface'])
-# Migrate OSPF syntax in default VRF
-ospf_base = ['protocols', 'ospf']
-ospf_passive_migration(config, ospf_base)
+ config.delete(area_base + [area, 'interface'])
-vrf_base = ['vrf', 'name']
-if config.exists(vrf_base):
- for vrf in config.list_nodes(vrf_base):
- vrf_ospf_base = vrf_base + [vrf, 'protocols', 'ospf']
- if config.exists(vrf_ospf_base):
- ospf_passive_migration(config, vrf_ospf_base)
+ # Migrate OSPF syntax in default VRF
+ ospf_base = ['protocols', 'ospf']
+ ospf_passive_migration(config, ospf_base)
-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)
+ vrf_base = ['vrf', 'name']
+ if config.exists(vrf_base):
+ for vrf in config.list_nodes(vrf_base):
+ vrf_ospf_base = vrf_base + [vrf, 'protocols', 'ospf']
+ if config.exists(vrf_ospf_base):
+ ospf_passive_migration(config, vrf_ospf_base)
diff --git a/src/migration-scripts/ospf/1-to-2 b/src/migration-scripts/ospf/1-to-2
index ba9499c60..5368d8dd7 100755..100644
--- a/src/migration-scripts/ospf/1-to-2
+++ b/src/migration-scripts/ospf/1-to-2
@@ -1,80 +1,60 @@
-#!/usr/bin/env python3
+# Copyright 2023-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2023 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 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# T5150: Rework CLI definitions to apply route-maps between routing daemons
# and zebra/kernel
-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)
-
ospf_base = ['protocols', 'ospf']
-# Check if OSPF is configured - if so, migrate the CLI node
-if config.exists(ospf_base):
- if config.exists(ospf_base + ['route-map']):
- tmp = config.return_value(ospf_base + ['route-map'])
-
- config.set(['system', 'ip', 'protocol', 'ospf', 'route-map'], value=tmp)
- config.set_tag(['system', 'ip', 'protocol'])
- config.delete(ospf_base + ['route-map'])
-
-ospfv3_base = ['protocols', 'ospfv3']
-# Check if OSPFv3 is configured - if so, migrate the CLI node
-if config.exists(ospfv3_base):
- if config.exists(ospfv3_base + ['route-map']):
- tmp = config.return_value(ospfv3_base + ['route-map'])
-
- config.set(['system', 'ipv6', 'protocol', 'ospfv3', 'route-map'], value=tmp)
- config.set_tag(['system', 'ipv6', 'protocol'])
- config.delete(ospfv3_base + ['route-map'])
-
-# Check if vrf names are configured. Check if OSPF/OSPFv3 is configured - if so,
-# migrate the CLI node(s)
-if config.exists(['vrf', 'name']):
- for vrf in config.list_nodes(['vrf', 'name']):
- vrf_base = ['vrf', 'name', vrf]
- if config.exists(vrf_base + ['protocols', 'ospf', 'route-map']):
- tmp = config.return_value(vrf_base + ['protocols', 'ospf', 'route-map'])
-
- config.set(vrf_base + ['ip', 'protocol', 'ospf', 'route-map'], value=tmp)
- config.set_tag(vrf_base + ['ip', 'protocol', 'ospf'])
- config.delete(vrf_base + ['protocols', 'ospf', 'route-map'])
-
- if config.exists(vrf_base + ['protocols', 'ospfv3', 'route-map']):
- tmp = config.return_value(vrf_base + ['protocols', 'ospfv3', 'route-map'])
-
- config.set(vrf_base + ['ipv6', 'protocol', 'ospfv3', 'route-map'], value=tmp)
- config.set_tag(vrf_base + ['ipv6', 'protocol', 'ospfv6'])
- config.delete(vrf_base + ['protocols', 'ospfv3', 'route-map'])
-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)
+def migrate(config: ConfigTree) -> None:
+ # Check if OSPF is configured - if so, migrate the CLI node
+ if config.exists(ospf_base):
+ if config.exists(ospf_base + ['route-map']):
+ tmp = config.return_value(ospf_base + ['route-map'])
+
+ config.set(['system', 'ip', 'protocol', 'ospf', 'route-map'], value=tmp)
+ config.set_tag(['system', 'ip', 'protocol'])
+ config.delete(ospf_base + ['route-map'])
+
+ ospfv3_base = ['protocols', 'ospfv3']
+ # Check if OSPFv3 is configured - if so, migrate the CLI node
+ if config.exists(ospfv3_base):
+ if config.exists(ospfv3_base + ['route-map']):
+ tmp = config.return_value(ospfv3_base + ['route-map'])
+
+ config.set(['system', 'ipv6', 'protocol', 'ospfv3', 'route-map'], value=tmp)
+ config.set_tag(['system', 'ipv6', 'protocol'])
+ config.delete(ospfv3_base + ['route-map'])
+
+ # Check if vrf names are configured. Check if OSPF/OSPFv3 is configured - if so,
+ # migrate the CLI node(s)
+ if config.exists(['vrf', 'name']):
+ for vrf in config.list_nodes(['vrf', 'name']):
+ vrf_base = ['vrf', 'name', vrf]
+ if config.exists(vrf_base + ['protocols', 'ospf', 'route-map']):
+ tmp = config.return_value(vrf_base + ['protocols', 'ospf', 'route-map'])
+
+ config.set(vrf_base + ['ip', 'protocol', 'ospf', 'route-map'], value=tmp)
+ config.set_tag(vrf_base + ['ip', 'protocol', 'ospf'])
+ config.delete(vrf_base + ['protocols', 'ospf', 'route-map'])
+
+ if config.exists(vrf_base + ['protocols', 'ospfv3', 'route-map']):
+ tmp = config.return_value(vrf_base + ['protocols', 'ospfv3', 'route-map'])
+
+ config.set(vrf_base + ['ipv6', 'protocol', 'ospfv3', 'route-map'], value=tmp)
+ config.set_tag(vrf_base + ['ipv6', 'protocol', 'ospfv6'])
+ config.delete(vrf_base + ['protocols', 'ospfv3', 'route-map'])
diff --git a/src/migration-scripts/pim/0-to-1 b/src/migration-scripts/pim/0-to-1
index bf8af733c..ce24b23ba 100755..100644
--- a/src/migration-scripts/pim/0-to-1
+++ b/src/migration-scripts/pim/0-to-1
@@ -1,72 +1,54 @@
-#!/usr/bin/env python3
+# Copyright 2023-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2023 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 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# T5736: igmp: migrate "protocols igmp" to "protocols pim"
-import sys
from vyos.configtree import ConfigTree
-if len(sys.argv) < 2:
- print("Must specify file name!")
- sys.exit(1)
-
-file_name = sys.argv[1]
-
-with open(file_name, 'r') as f:
- config_file = f.read()
-
-config = ConfigTree(config_file)
-
base = ['protocols', 'igmp']
pim_base = ['protocols', 'pim']
-if not config.exists(base):
- # Nothing to do
- sys.exit(0)
-for interface in config.list_nodes(base + ['interface']):
- base_igmp_iface = base + ['interface', interface]
- pim_base_iface = pim_base + ['interface', interface]
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ # Nothing to do
+ return
- # Create IGMP note under PIM interface
- if not config.exists(pim_base_iface + ['igmp']):
- config.set(pim_base_iface + ['igmp'])
+ for interface in config.list_nodes(base + ['interface']):
+ base_igmp_iface = base + ['interface', interface]
+ pim_base_iface = pim_base + ['interface', interface]
- if config.exists(base_igmp_iface + ['join']):
- config.copy(base_igmp_iface + ['join'], pim_base_iface + ['igmp', 'join'])
- config.set_tag(pim_base_iface + ['igmp', 'join'])
+ # Create IGMP note under PIM interface
+ if not config.exists(pim_base_iface + ['igmp']):
+ config.set(pim_base_iface + ['igmp'])
- new_join_base = pim_base_iface + ['igmp', 'join']
- for address in config.list_nodes(new_join_base):
- if config.exists(new_join_base + [address, 'source']):
- config.rename(new_join_base + [address, 'source'], 'source-address')
+ if config.exists(base_igmp_iface + ['join']):
+ config.copy(base_igmp_iface + ['join'], pim_base_iface + ['igmp', 'join'])
+ config.set_tag(pim_base_iface + ['igmp', 'join'])
- if config.exists(base_igmp_iface + ['query-interval']):
- config.copy(base_igmp_iface + ['query-interval'], pim_base_iface + ['igmp', 'query-interval'])
+ new_join_base = pim_base_iface + ['igmp', 'join']
+ for address in config.list_nodes(new_join_base):
+ if config.exists(new_join_base + [address, 'source']):
+ config.rename(new_join_base + [address, 'source'], 'source-address')
- if config.exists(base_igmp_iface + ['query-max-response-time']):
- config.copy(base_igmp_iface + ['query-max-response-time'], pim_base_iface + ['igmp', 'query-max-response-time'])
+ if config.exists(base_igmp_iface + ['query-interval']):
+ config.copy(base_igmp_iface + ['query-interval'], pim_base_iface + ['igmp', 'query-interval'])
- if config.exists(base_igmp_iface + ['version']):
- config.copy(base_igmp_iface + ['version'], pim_base_iface + ['igmp', 'version'])
+ if config.exists(base_igmp_iface + ['query-max-response-time']):
+ config.copy(base_igmp_iface + ['query-max-response-time'], pim_base_iface + ['igmp', 'query-max-response-time'])
-config.delete(base)
+ if config.exists(base_igmp_iface + ['version']):
+ config.copy(base_igmp_iface + ['version'], pim_base_iface + ['igmp', 'version'])
-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))
- sys.exit(1)
+ config.delete(base)
diff --git a/src/migration-scripts/policy/0-to-1 b/src/migration-scripts/policy/0-to-1
index 8508b734a..837946c37 100755..100644
--- a/src/migration-scripts/policy/0-to-1
+++ b/src/migration-scripts/policy/0-to-1
@@ -1,65 +1,43 @@
-#!/usr/bin/env python3
+# Copyright 2021-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2021 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 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# T3631: route-map: migrate "set extcommunity-rt" and "set extcommunity-soo"
# to "set extcommunity rt|soo" to match FRR syntax
-
-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 = ['policy', 'route-map']
-config = ConfigTree(config_file)
-
-if not config.exists(base):
- # Nothing to do
- exit(0)
-
-for route_map in config.list_nodes(base):
- if not config.exists(base + [route_map, 'rule']):
- continue
- for rule in config.list_nodes(base + [route_map, 'rule']):
- base_rule = base + [route_map, 'rule', rule]
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ # Nothing to do
+ return
- if config.exists(base_rule + ['set', 'extcommunity-rt']):
- tmp = config.return_value(base_rule + ['set', 'extcommunity-rt'])
- config.delete(base_rule + ['set', 'extcommunity-rt'])
- config.set(base_rule + ['set', 'extcommunity', 'rt'], value=tmp)
+ for route_map in config.list_nodes(base):
+ if not config.exists(base + [route_map, 'rule']):
+ continue
+ for rule in config.list_nodes(base + [route_map, 'rule']):
+ base_rule = base + [route_map, 'rule', rule]
+ if config.exists(base_rule + ['set', 'extcommunity-rt']):
+ tmp = config.return_value(base_rule + ['set', 'extcommunity-rt'])
+ config.delete(base_rule + ['set', 'extcommunity-rt'])
+ config.set(base_rule + ['set', 'extcommunity', 'rt'], value=tmp)
- if config.exists(base_rule + ['set', 'extcommunity-soo']):
- tmp = config.return_value(base_rule + ['set', 'extcommunity-soo'])
- config.delete(base_rule + ['set', 'extcommunity-soo'])
- config.set(base_rule + ['set', 'extcommunity', 'soo'], value=tmp)
-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)
+ if config.exists(base_rule + ['set', 'extcommunity-soo']):
+ tmp = config.return_value(base_rule + ['set', 'extcommunity-soo'])
+ config.delete(base_rule + ['set', 'extcommunity-soo'])
+ config.set(base_rule + ['set', 'extcommunity', 'soo'], value=tmp)
diff --git a/src/migration-scripts/policy/1-to-2 b/src/migration-scripts/policy/1-to-2
index c7a983bba..ba3e48db0 100755..100644
--- a/src/migration-scripts/policy/1-to-2
+++ b/src/migration-scripts/policy/1-to-2
@@ -1,86 +1,67 @@
-#!/usr/bin/env python3
+# Copyright 2022-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2022 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 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# T4170: rename "policy ipv6-route" to "policy route6" to match common
# IPv4/IPv6 schema
# T4178: Update tcp flags to use multi value node
-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 = ['policy']
-config = ConfigTree(config_file)
-if not config.exists(base):
- # Nothing to do
- exit(0)
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ # Nothing to do
+ return
-if config.exists(base + ['ipv6-route']):
- config.rename(base + ['ipv6-route'],'route6')
- config.set_tag(['policy', 'route6'])
+ if config.exists(base + ['ipv6-route']):
+ config.rename(base + ['ipv6-route'],'route6')
+ config.set_tag(['policy', 'route6'])
-for route in ['route', 'route6']:
- if config.exists(base + [route]):
- for name in config.list_nodes(base + [route]):
- if config.exists(base + [route, name, 'rule']):
- for rule in config.list_nodes(base + [route, name, 'rule']):
- rule_tcp_flags = base + [route, name, 'rule', rule, 'tcp', 'flags']
+ for route in ['route', 'route6']:
+ if config.exists(base + [route]):
+ for name in config.list_nodes(base + [route]):
+ if config.exists(base + [route, name, 'rule']):
+ for rule in config.list_nodes(base + [route, name, 'rule']):
+ rule_tcp_flags = base + [route, name, 'rule', rule, 'tcp', 'flags']
- if config.exists(rule_tcp_flags):
- tmp = config.return_value(rule_tcp_flags)
- config.delete(rule_tcp_flags)
- for flag in tmp.split(","):
+ if config.exists(rule_tcp_flags):
+ tmp = config.return_value(rule_tcp_flags)
+ config.delete(rule_tcp_flags)
for flag in tmp.split(","):
- if flag[0] == '!':
- config.set(rule_tcp_flags + ['not', flag[1:].lower()])
- else:
- config.set(rule_tcp_flags + [flag.lower()])
+ for flag in tmp.split(","):
+ if flag[0] == '!':
+ config.set(rule_tcp_flags + ['not', flag[1:].lower()])
+ else:
+ config.set(rule_tcp_flags + [flag.lower()])
-if config.exists(['interfaces']):
- def if_policy_rename(config, path):
- if config.exists(path + ['policy', 'ipv6-route']):
- config.rename(path + ['policy', 'ipv6-route'], 'route6')
+ if config.exists(['interfaces']):
+ def if_policy_rename(config, path):
+ if config.exists(path + ['policy', 'ipv6-route']):
+ config.rename(path + ['policy', 'ipv6-route'], 'route6')
- for if_type in config.list_nodes(['interfaces']):
- for ifname in config.list_nodes(['interfaces', if_type]):
- if_path = ['interfaces', if_type, ifname]
- if_policy_rename(config, if_path)
+ for if_type in config.list_nodes(['interfaces']):
+ for ifname in config.list_nodes(['interfaces', if_type]):
+ if_path = ['interfaces', if_type, ifname]
+ if_policy_rename(config, if_path)
- for vif_type in ['vif', 'vif-s']:
- if config.exists(if_path + [vif_type]):
- for vifname in config.list_nodes(if_path + [vif_type]):
- if_policy_rename(config, if_path + [vif_type, vifname])
+ for vif_type in ['vif', 'vif-s']:
+ if config.exists(if_path + [vif_type]):
+ for vifname in config.list_nodes(if_path + [vif_type]):
+ if_policy_rename(config, if_path + [vif_type, vifname])
- if config.exists(if_path + [vif_type, vifname, 'vif-c']):
- for vifcname in config.list_nodes(if_path + [vif_type, vifname, 'vif-c']):
- if_policy_rename(config, if_path + [vif_type, vifname, 'vif-c', vifcname])
-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)
+ if config.exists(if_path + [vif_type, vifname, 'vif-c']):
+ for vifcname in config.list_nodes(if_path + [vif_type, vifname, 'vif-c']):
+ if_policy_rename(config, if_path + [vif_type, vifname, 'vif-c', vifcname])
diff --git a/src/migration-scripts/policy/2-to-3 b/src/migration-scripts/policy/2-to-3
index 8a62c8e6f..399a55387 100755..100644
--- a/src/migration-scripts/policy/2-to-3
+++ b/src/migration-scripts/policy/2-to-3
@@ -1,58 +1,38 @@
-#!/usr/bin/env python3
+# Copyright 2022-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2022 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 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# T3976: change cli
# from: set policy route-map FOO rule 10 match ipv6 nexthop 'h:h:h:h:h:h:h:h'
# to: set policy route-map FOO rule 10 match ipv6 nexthop address 'h:h:h:h:h:h:h:h'
-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 = ['policy', 'route-map']
-config = ConfigTree(config_file)
-
-if not config.exists(base):
- # Nothing to do
- exit(0)
-
-for route_map in config.list_nodes(base):
- if not config.exists(base + [route_map, 'rule']):
- continue
- for rule in config.list_nodes(base + [route_map, 'rule']):
- base_rule = base + [route_map, 'rule', rule]
-
- if config.exists(base_rule + ['match', 'ipv6', 'nexthop']):
- tmp = config.return_value(base_rule + ['match', 'ipv6', 'nexthop'])
- config.delete(base_rule + ['match', 'ipv6', 'nexthop'])
- config.set(base_rule + ['match', 'ipv6', 'nexthop', 'address'], value=tmp)
-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) \ No newline at end of file
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ # Nothing to do
+ return
+
+ for route_map in config.list_nodes(base):
+ if not config.exists(base + [route_map, 'rule']):
+ continue
+ for rule in config.list_nodes(base + [route_map, 'rule']):
+ base_rule = base + [route_map, 'rule', rule]
+
+ if config.exists(base_rule + ['match', 'ipv6', 'nexthop']):
+ tmp = config.return_value(base_rule + ['match', 'ipv6', 'nexthop'])
+ config.delete(base_rule + ['match', 'ipv6', 'nexthop'])
+ config.set(base_rule + ['match', 'ipv6', 'nexthop', 'address'], value=tmp)
diff --git a/src/migration-scripts/policy/3-to-4 b/src/migration-scripts/policy/3-to-4
index 476fa3af2..5d4959def 100755..100644
--- a/src/migration-scripts/policy/3-to-4
+++ b/src/migration-scripts/policy/3-to-4
@@ -1,18 +1,17 @@
-#!/usr/bin/env python3
+# Copyright 2022-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2022 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 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# T4660: change cli
# from: set policy route-map FOO rule 10 set community 'TEXT'
@@ -33,9 +32,6 @@
# Multiple value
# to: set policy route-map FOO rule 10 set extcommunity [rt|soo] <community>
-from sys import argv
-from sys import exit
-
from vyos.configtree import ConfigTree
@@ -96,67 +92,52 @@ def extcommunity_migrate(config: ConfigTree, rule: list[str]) -> None:
config.set(rule + ['soo'], value=community, replace=False)
-if len(argv) < 2:
- print("Must specify file name!")
- exit(1)
-
-file_name: str = argv[1]
-
-with open(file_name, 'r') as f:
- config_file = f.read()
-
base: list[str] = ['policy', 'route-map']
-config = ConfigTree(config_file)
-
-if not config.exists(base):
- # Nothing to do
- exit(0)
-
-for route_map in config.list_nodes(base):
- if not config.exists(base + [route_map, 'rule']):
- continue
- for rule in config.list_nodes(base + [route_map, 'rule']):
- base_rule: list[str] = base + [route_map, 'rule', rule, 'set']
-
- # IF additive presents in coummunity then comm-list is redundant
- isAdditive: bool = True
- #### Change Set community ########
- if config.exists(base_rule + ['community']):
- isAdditive = community_migrate(config,
- base_rule + ['community'])
-
- #### Change Set community-list delete migrate ########
- if config.exists(base_rule + ['comm-list', 'comm-list']):
- if isAdditive:
- tmp = config.return_value(
- base_rule + ['comm-list', 'comm-list'])
- config.delete(base_rule + ['comm-list'])
- config.set(base_rule + ['community', 'delete'], value=tmp)
- else:
- config.delete(base_rule + ['comm-list'])
-
- isAdditive = False
- #### Change Set large-community ########
- if config.exists(base_rule + ['large-community']):
- isAdditive = community_migrate(config,
- base_rule + ['large-community'])
-
- #### Change Set large-community delete by List ########
- if config.exists(base_rule + ['large-comm-list-delete']):
- if isAdditive:
- tmp = config.return_value(
- base_rule + ['large-comm-list-delete'])
- config.delete(base_rule + ['large-comm-list-delete'])
- config.set(base_rule + ['large-community', 'delete'],
- value=tmp)
- else:
- config.delete(base_rule + ['large-comm-list-delete'])
-
- #### Change Set extcommunity ########
- extcommunity_migrate(config, base_rule + ['extcommunity'])
-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)
+
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ # Nothing to do
+ return
+
+ for route_map in config.list_nodes(base):
+ if not config.exists(base + [route_map, 'rule']):
+ continue
+ for rule in config.list_nodes(base + [route_map, 'rule']):
+ base_rule: list[str] = base + [route_map, 'rule', rule, 'set']
+
+ # IF additive presents in coummunity then comm-list is redundant
+ isAdditive: bool = True
+ #### Change Set community ########
+ if config.exists(base_rule + ['community']):
+ isAdditive = community_migrate(config,
+ base_rule + ['community'])
+
+ #### Change Set community-list delete migrate ########
+ if config.exists(base_rule + ['comm-list', 'comm-list']):
+ if isAdditive:
+ tmp = config.return_value(
+ base_rule + ['comm-list', 'comm-list'])
+ config.delete(base_rule + ['comm-list'])
+ config.set(base_rule + ['community', 'delete'], value=tmp)
+ else:
+ config.delete(base_rule + ['comm-list'])
+
+ isAdditive = False
+ #### Change Set large-community ########
+ if config.exists(base_rule + ['large-community']):
+ isAdditive = community_migrate(config,
+ base_rule + ['large-community'])
+
+ #### Change Set large-community delete by List ########
+ if config.exists(base_rule + ['large-comm-list-delete']):
+ if isAdditive:
+ tmp = config.return_value(
+ base_rule + ['large-comm-list-delete'])
+ config.delete(base_rule + ['large-comm-list-delete'])
+ config.set(base_rule + ['large-community', 'delete'],
+ value=tmp)
+ else:
+ config.delete(base_rule + ['large-comm-list-delete'])
+
+ #### Change Set extcommunity ########
+ extcommunity_migrate(config, base_rule + ['extcommunity'])
diff --git a/src/migration-scripts/policy/4-to-5 b/src/migration-scripts/policy/4-to-5
index 738850f67..0ecfdfd5e 100755..100644
--- a/src/migration-scripts/policy/4-to-5
+++ b/src/migration-scripts/policy/4-to-5
@@ -1,39 +1,24 @@
-#!/usr/bin/env python3
+# Copyright 2022-2024 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
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
#
-# 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# T2199: Migrate interface policy nodes to policy route <name> interface <ifname>
-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()
-
base4 = ['policy', 'route']
base6 = ['policy', 'route6']
-config = ConfigTree(config_file)
-
def delete_orphaned_interface_policy(config, iftype, ifname, vif=None, vifs=None, vifc=None):
"""Delete unexpected policy on interfaces in cases when
@@ -55,35 +40,6 @@ def delete_orphaned_interface_policy(config, iftype, ifname, vif=None, vifs=None
config.delete(if_path + ['policy'])
-
-if not config.exists(base4) and not config.exists(base6):
- # Delete orphaned nodes on interfaces T5941
- for iftype in config.list_nodes(['interfaces']):
- for ifname in config.list_nodes(['interfaces', iftype]):
- delete_orphaned_interface_policy(config, iftype, ifname)
-
- if config.exists(['interfaces', iftype, ifname, 'vif']):
- for vif in config.list_nodes(['interfaces', iftype, ifname, 'vif']):
- delete_orphaned_interface_policy(config, iftype, ifname, vif=vif)
-
- if config.exists(['interfaces', iftype, ifname, 'vif-s']):
- for vifs in config.list_nodes(['interfaces', iftype, ifname, 'vif-s']):
- delete_orphaned_interface_policy(config, iftype, ifname, vifs=vifs)
-
- if config.exists(['interfaces', iftype, ifname, 'vif-s', vifs, 'vif-c']):
- for vifc in config.list_nodes(['interfaces', iftype, ifname, 'vif-s', vifs, 'vif-c']):
- delete_orphaned_interface_policy(config, iftype, ifname, vifs=vifs, vifc=vifc)
-
- 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)
-
- # Nothing to do
- exit(0)
-
def migrate_interface(config, iftype, ifname, vif=None, vifs=None, vifc=None):
if_path = ['interfaces', iftype, ifname]
ifname_full = ifname
@@ -111,25 +67,40 @@ def migrate_interface(config, iftype, ifname, vif=None, vifs=None, vifc=None):
config.delete(if_path + ['policy'])
-for iftype in config.list_nodes(['interfaces']):
- for ifname in config.list_nodes(['interfaces', iftype]):
- migrate_interface(config, iftype, ifname)
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(base4) and not config.exists(base6):
+ # Delete orphaned nodes on interfaces T5941
+ for iftype in config.list_nodes(['interfaces']):
+ for ifname in config.list_nodes(['interfaces', iftype]):
+ delete_orphaned_interface_policy(config, iftype, ifname)
+
+ if config.exists(['interfaces', iftype, ifname, 'vif']):
+ for vif in config.list_nodes(['interfaces', iftype, ifname, 'vif']):
+ delete_orphaned_interface_policy(config, iftype, ifname, vif=vif)
- if config.exists(['interfaces', iftype, ifname, 'vif']):
- for vif in config.list_nodes(['interfaces', iftype, ifname, 'vif']):
- migrate_interface(config, iftype, ifname, vif=vif)
+ if config.exists(['interfaces', iftype, ifname, 'vif-s']):
+ for vifs in config.list_nodes(['interfaces', iftype, ifname, 'vif-s']):
+ delete_orphaned_interface_policy(config, iftype, ifname, vifs=vifs)
- if config.exists(['interfaces', iftype, ifname, 'vif-s']):
- for vifs in config.list_nodes(['interfaces', iftype, ifname, 'vif-s']):
- migrate_interface(config, iftype, ifname, vifs=vifs)
+ if config.exists(['interfaces', iftype, ifname, 'vif-s', vifs, 'vif-c']):
+ for vifc in config.list_nodes(['interfaces', iftype, ifname, 'vif-s', vifs, 'vif-c']):
+ delete_orphaned_interface_policy(config, iftype, ifname, vifs=vifs, vifc=vifc)
- if config.exists(['interfaces', iftype, ifname, 'vif-s', vifs, 'vif-c']):
- for vifc in config.list_nodes(['interfaces', iftype, ifname, 'vif-s', vifs, 'vif-c']):
- migrate_interface(config, iftype, ifname, vifs=vifs, vifc=vifc)
+ # Nothing to do
+ return
+
+ for iftype in config.list_nodes(['interfaces']):
+ for ifname in config.list_nodes(['interfaces', iftype]):
+ migrate_interface(config, iftype, ifname)
-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)
+ if config.exists(['interfaces', iftype, ifname, 'vif']):
+ for vif in config.list_nodes(['interfaces', iftype, ifname, 'vif']):
+ migrate_interface(config, iftype, ifname, vif=vif)
+
+ if config.exists(['interfaces', iftype, ifname, 'vif-s']):
+ for vifs in config.list_nodes(['interfaces', iftype, ifname, 'vif-s']):
+ migrate_interface(config, iftype, ifname, vifs=vifs)
+
+ if config.exists(['interfaces', iftype, ifname, 'vif-s', vifs, 'vif-c']):
+ for vifc in config.list_nodes(['interfaces', iftype, ifname, 'vif-s', vifs, 'vif-c']):
+ migrate_interface(config, iftype, ifname, vifs=vifs, vifc=vifc)
diff --git a/src/migration-scripts/policy/5-to-6 b/src/migration-scripts/policy/5-to-6
index 86287d578..acba0b4be 100755..100644
--- a/src/migration-scripts/policy/5-to-6
+++ b/src/migration-scripts/policy/5-to-6
@@ -1,62 +1,42 @@
-#!/usr/bin/env python3
+# Copyright 2023-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2023 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 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# T5165: Migrate policy local-route rule <tag> destination|source
-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()
-
base4 = ['policy', 'local-route']
base6 = ['policy', 'local-route6']
-config = ConfigTree(config_file)
-
-if not config.exists(base4) and not config.exists(base6):
- # Nothing to do
- exit(0)
-
-# replace 'policy local-route{v6} rule <tag> destination|source <x.x.x.x>'
-# => 'policy local-route{v6} rule <tag> destination|source address <x.x.x.x>'
-for base in [base4, base6]:
- if config.exists(base + ['rule']):
- for rule in config.list_nodes(base + ['rule']):
- dst_path = base + ['rule', rule, 'destination']
- src_path = base + ['rule', rule, 'source']
- # Destination
- if config.exists(dst_path):
- for dst_addr in config.return_values(dst_path):
- config.set(dst_path + ['address'], value=dst_addr, replace=False)
- # Source
- if config.exists(src_path):
- for src_addr in config.return_values(src_path):
- config.set(src_path + ['address'], value=src_addr, replace=False)
-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 migrate(config: ConfigTree) -> None:
+ if not config.exists(base4) and not config.exists(base6):
+ # Nothing to do
+ return
+
+ # replace 'policy local-route{v6} rule <tag> destination|source <x.x.x.x>'
+ # => 'policy local-route{v6} rule <tag> destination|source address <x.x.x.x>'
+ for base in [base4, base6]:
+ if config.exists(base + ['rule']):
+ for rule in config.list_nodes(base + ['rule']):
+ dst_path = base + ['rule', rule, 'destination']
+ src_path = base + ['rule', rule, 'source']
+ # Destination
+ if config.exists(dst_path):
+ for dst_addr in config.return_values(dst_path):
+ config.set(dst_path + ['address'], value=dst_addr, replace=False)
+ # Source
+ if config.exists(src_path):
+ for src_addr in config.return_values(src_path):
+ config.set(src_path + ['address'], value=src_addr, replace=False)
diff --git a/src/migration-scripts/policy/6-to-7 b/src/migration-scripts/policy/6-to-7
index cdefc6837..69aa703c5 100755..100644
--- a/src/migration-scripts/policy/6-to-7
+++ b/src/migration-scripts/policy/6-to-7
@@ -1,18 +1,17 @@
-#!/usr/bin/env python3
+# Copyright 2023-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2023-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; either
+# version 2.1 of the License, or (at your option) any later version.
#
-# 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# T5729: Switch to valueless whenever is possible.
# From
@@ -22,55 +21,36 @@
# set policy [route | route6] ... rule <rule> log
# Remove command if log=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()
-
base = ['policy']
-config = ConfigTree(config_file)
-
-if not config.exists(base):
- # Nothing to do
- exit(0)
-
-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']):
- # Log
- if config.exists(base + [family, policy_name, 'rule', rule, 'log']):
- log_value = config.return_value(base + [family, policy_name, 'rule', rule, 'log'])
- config.delete(base + [family, policy_name, 'rule', rule, 'log'])
- if log_value == 'enable':
- config.set(base + [family, policy_name, 'rule', rule, 'log'])
- # State
- if config.exists(base + [family, policy_name, 'rule', rule, 'state']):
- flag_enable = 'False'
- for state in ['established', 'invalid', 'new', 'related']:
- if config.exists(base + [family, policy_name, 'rule', rule, 'state', state]):
- state_value = config.return_value(base + [family, policy_name, 'rule', rule, 'state', state])
- config.delete(base + [family, policy_name, 'rule', rule, 'state', state])
- if state_value == 'enable':
- config.set(base + [family, policy_name, 'rule', rule, 'state'], value=state, replace=False)
- flag_enable = 'True'
- if flag_enable == 'False':
- config.delete(base + [family, policy_name, 'rule', rule, 'state'])
-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 migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ # Nothing to do
+ return
+
+ 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']):
+ # Log
+ if config.exists(base + [family, policy_name, 'rule', rule, 'log']):
+ log_value = config.return_value(base + [family, policy_name, 'rule', rule, 'log'])
+ config.delete(base + [family, policy_name, 'rule', rule, 'log'])
+ if log_value == 'enable':
+ config.set(base + [family, policy_name, 'rule', rule, 'log'])
+ # State
+ if config.exists(base + [family, policy_name, 'rule', rule, 'state']):
+ flag_enable = 'False'
+ for state in ['established', 'invalid', 'new', 'related']:
+ if config.exists(base + [family, policy_name, 'rule', rule, 'state', state]):
+ state_value = config.return_value(base + [family, policy_name, 'rule', rule, 'state', state])
+ config.delete(base + [family, policy_name, 'rule', rule, 'state', state])
+ if state_value == 'enable':
+ config.set(base + [family, policy_name, 'rule', rule, 'state'], value=state, replace=False)
+ flag_enable = 'True'
+ if flag_enable == 'False':
+ config.delete(base + [family, policy_name, 'rule', rule, 'state'])
diff --git a/src/migration-scripts/policy/7-to-8 b/src/migration-scripts/policy/7-to-8
index 73eece1a6..a887f37fe 100755..100644
--- a/src/migration-scripts/policy/7-to-8
+++ b/src/migration-scripts/policy/7-to-8
@@ -1,18 +1,17 @@
-#!/usr/bin/env python3
+# Copyright 2023-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2023 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 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# T5834: Rename 'enable-default-log' to 'default-log'
# From
@@ -20,37 +19,18 @@
# To
# set policy [route | route 6] <route> default-log
-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 = ['policy']
-config = ConfigTree(config_file)
-
-if not config.exists(base):
- # Nothing to do
- exit(0)
-for family in ['route', 'route6']:
- if config.exists(base + [family]):
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ # Nothing to do
+ return
- for policy_name in config.list_nodes(base + [family]):
- if config.exists(base + [family, policy_name, 'enable-default-log']):
- config.rename(base + [family, policy_name, 'enable-default-log'], 'default-log')
+ for family in ['route', 'route6']:
+ if config.exists(base + [family]):
-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)
+ for policy_name in config.list_nodes(base + [family]):
+ if config.exists(base + [family, policy_name, 'enable-default-log']):
+ config.rename(base + [family, policy_name, 'enable-default-log'], 'default-log')
diff --git a/src/migration-scripts/pppoe-server/0-to-1 b/src/migration-scripts/pppoe-server/0-to-1
index 4d36f8545..8c9a24fbe 100755..100644
--- a/src/migration-scripts/pppoe-server/0-to-1
+++ b/src/migration-scripts/pppoe-server/0-to-1
@@ -1,50 +1,33 @@
-#!/usr/bin/env python3
+# Copyright 2020-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# 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 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# Convert "service pppoe-server authentication radius-server node key"
# to: "service pppoe-server authentication radius-server node secret"
-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()
-
-ctree = ConfigTree(config_file)
base = ['service', 'pppoe-server', 'authentication', 'radius-server']
-if not ctree.exists(base):
- # Nothing to do
- exit(0)
-else:
+def migrate(ctree: ConfigTree) -> None:
+ if not ctree.exists(base):
+ # Nothing to do
+ return
+
nodes = ctree.list_nodes(base)
for node in nodes:
if ctree.exists(base + [node, 'key']):
val = ctree.return_value(base + [node, 'key'])
ctree.set(base + [node, 'secret'], value=val, replace=False)
ctree.delete(base + [node, 'key'])
-
- try:
- open(file_name,'w').write(ctree.to_string())
- except OSError as e:
- print("Failed to save the modified config: {}".format(e))
- exit(1)
diff --git a/src/migration-scripts/pppoe-server/1-to-2 b/src/migration-scripts/pppoe-server/1-to-2
index b266893c0..c9c968bff 100755..100644
--- a/src/migration-scripts/pppoe-server/1-to-2
+++ b/src/migration-scripts/pppoe-server/1-to-2
@@ -1,39 +1,29 @@
-#!/usr/bin/env python3
+# Copyright 2020-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2020-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; either
+# version 2.1 of the License, or (at your option) any later version.
#
-# 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# change mppe node to a leaf node with value prefer
-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]
+base = ['service', 'pppoe-server']
-with open(file_name, 'r') as f:
- config_file = f.read()
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ # Nothing to do
+ return
-config = ConfigTree(config_file)
-base = ['service', 'pppoe-server']
-if not config.exists(base):
- # Nothing to do
- exit(0)
-else:
mppe_base = base + ['ppp-options', 'mppe']
if config.exists(mppe_base):
# get current values
@@ -49,10 +39,3 @@ else:
config.set(mppe_base, value='prefer')
elif 'deny' in tmp:
config.set(mppe_base, value='deny')
-
- 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/pppoe-server/2-to-3 b/src/migration-scripts/pppoe-server/2-to-3
index a7be060df..160cffdf8 100755..100644
--- a/src/migration-scripts/pppoe-server/2-to-3
+++ b/src/migration-scripts/pppoe-server/2-to-3
@@ -1,48 +1,31 @@
-#!/usr/bin/env python3
+# Copyright 2020-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# 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 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# Convert "service pppoe-server interface ethX" to: "service pppoe-server interface ethX {}"
-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()
-
-ctree = ConfigTree(config_file)
cbase = ['service', 'pppoe-server','interface']
-if not ctree.exists(cbase):
- exit(0)
-else:
+def migrate(ctree: ConfigTree) -> None:
+ if not ctree.exists(cbase):
+ return
+
nics = ctree.return_values(cbase)
# convert leafNode to a tagNode
ctree.set(cbase)
ctree.set_tag(cbase)
for nic in nics:
ctree.set(cbase + [nic])
-
- try:
- open(file_name,'w').write(ctree.to_string())
- 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 477ed6f22..29dd62201 100755..100644
--- a/src/migration-scripts/pppoe-server/3-to-4
+++ b/src/migration-scripts/pppoe-server/3-to-4
@@ -1,39 +1,28 @@
-#!/usr/bin/env python3
+# Copyright 2020-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2020-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; either
+# version 2.1 of the License, or (at your option) any later version.
#
-# 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# - remove primary/secondary identifier from nameserver
-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', 'pppoe-server']
-if not config.exists(base):
- # Nothing to do
- exit(0)
-else:
+
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ # Nothing to do
+ return
# Migrate IPv4 DNS servers
dns_base = base + ['dns-servers']
@@ -130,10 +119,3 @@ else:
prefix = p.split(',')[0]
mask = p.split(',')[1]
config.set(ipv6_base + ['delegate', prefix, 'delegation-prefix'], value=mask)
-
- 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/pppoe-server/4-to-5 b/src/migration-scripts/pppoe-server/4-to-5
index 5850db673..03fbfb247 100755..100644
--- a/src/migration-scripts/pppoe-server/4-to-5
+++ b/src/migration-scripts/pppoe-server/4-to-5
@@ -1,49 +1,30 @@
-#!/usr/bin/env python3
+# Copyright 2020-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# 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 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# - rename local-ip to gateway-address
from vyos.configtree import ConfigTree
-from sys import argv
-from sys import exit
-
-if len(argv) < 2:
- print("Must specify file name!")
- exit(1)
-file_name = argv[1]
+base_path = ['service', 'pppoe-server']
-with open(file_name, 'r') as f:
- config_file = f.read()
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(base_path):
+ # Nothing to do
+ return
-config = ConfigTree(config_file)
-base_path = ['service', 'pppoe-server']
-if not config.exists(base_path):
- # Nothing to do
- exit(0)
-else:
config_gw = base_path + ['local-ip']
if config.exists(config_gw):
config.rename(config_gw, 'gateway-address')
config.delete(config_gw)
-
- 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/pppoe-server/5-to-6 b/src/migration-scripts/pppoe-server/5-to-6
index e079ae684..13de8f8d2 100755..100644
--- a/src/migration-scripts/pppoe-server/5-to-6
+++ b/src/migration-scripts/pppoe-server/5-to-6
@@ -1,52 +1,33 @@
-#!/usr/bin/env python3
+# Copyright 2022-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2022 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 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# - T4703: merge vlan-id and vlan-range to vlan CLI node
from vyos.configtree import ConfigTree
-from sys import argv
-from sys import exit
-
-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_path = ['service', 'pppoe-server', 'interface']
-if not config.exists(base_path):
- # Nothing to do
- exit(0)
-
-for interface in config.list_nodes(base_path):
- for vlan in ['vlan-id', 'vlan-range']:
- if config.exists(base_path + [interface, vlan]):
- print(interface, vlan)
- for tmp in config.return_values(base_path + [interface, vlan]):
- config.set(base_path + [interface, 'vlan'], value=tmp, replace=False)
- config.delete(base_path + [interface, vlan])
-
-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)
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(base_path):
+ # Nothing to do
+ return
+
+ for interface in config.list_nodes(base_path):
+ for vlan in ['vlan-id', 'vlan-range']:
+ if config.exists(base_path + [interface, vlan]):
+ print(interface, vlan)
+ for tmp in config.return_values(base_path + [interface, vlan]):
+ config.set(base_path + [interface, 'vlan'], value=tmp, replace=False)
+ config.delete(base_path + [interface, vlan])
diff --git a/src/migration-scripts/pppoe-server/6-to-7 b/src/migration-scripts/pppoe-server/6-to-7
index d51c1c9d8..79745a0c6 100755..100644
--- a/src/migration-scripts/pppoe-server/6-to-7
+++ b/src/migration-scripts/pppoe-server/6-to-7
@@ -1,18 +1,17 @@
-#!/usr/bin/env python3
+# Copyright 2023-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2023-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; either
+# version 2.1 of the License, or (at your option) any later version.
#
-# 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# - move all pool to named pools
# 'start-stop' migrate to namedpool 'default-range-pool'
@@ -24,94 +23,77 @@
# If there are not named pools, namedless pool will be default.
# 2. If authentication mode = 'radius' then namedless pool will be default
-from sys import argv
-from sys import exit
from vyos.configtree import ConfigTree
from vyos.base import Warning
-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', 'pppoe-server']
pool_base = base + ['client-ip-pool']
-if not config.exists(base):
- exit(0)
-
-if not config.exists(pool_base):
- exit(0)
-
-default_pool = ''
-range_pool_name = 'default-range-pool'
-
-#Default nameless pools migrations
-if config.exists(pool_base + ['start']) and config.exists(pool_base + ['stop']):
- def is_legalrange(ip1: str, ip2: str, mask: str):
- from ipaddress import IPv4Interface
- interface1 = IPv4Interface(f'{ip1}/{mask}')
- interface2 = IPv4Interface(f'{ip2}/{mask}')
- return interface1.network.network_address == interface2.network.network_address and interface2.ip > interface1.ip
- start_ip = config.return_value(pool_base + ['start'])
- stop_ip = config.return_value(pool_base + ['stop'])
- if is_legalrange(start_ip, stop_ip, '24'):
- ip_range = f'{start_ip}-{stop_ip}'
- config.set(pool_base + [range_pool_name, 'range'], value=ip_range, replace=False)
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ return
+
+ if not config.exists(pool_base):
+ return
+
+ default_pool = ''
+ range_pool_name = 'default-range-pool'
+
+ #Default nameless pools migrations
+ if config.exists(pool_base + ['start']) and config.exists(pool_base + ['stop']):
+ def is_legalrange(ip1: str, ip2: str, mask: str):
+ from ipaddress import IPv4Interface
+ interface1 = IPv4Interface(f'{ip1}/{mask}')
+ interface2 = IPv4Interface(f'{ip2}/{mask}')
+ return interface1.network.network_address == interface2.network.network_address and interface2.ip > interface1.ip
+
+ start_ip = config.return_value(pool_base + ['start'])
+ stop_ip = config.return_value(pool_base + ['stop'])
+ if is_legalrange(start_ip, stop_ip, '24'):
+ ip_range = f'{start_ip}-{stop_ip}'
+ config.set(pool_base + [range_pool_name, 'range'], value=ip_range, replace=False)
+ default_pool = range_pool_name
+ else:
+ Warning(
+ f'PPPoE client-ip-pool range start-ip:{start_ip} and stop-ip:{stop_ip} can not be migrated.')
+ config.delete(pool_base + ['start'])
+ config.delete(pool_base + ['stop'])
+
+ if config.exists(pool_base + ['subnet']):
default_pool = range_pool_name
- else:
- Warning(
- f'PPPoE client-ip-pool range start-ip:{start_ip} and stop-ip:{stop_ip} can not be migrated.')
- config.delete(pool_base + ['start'])
- config.delete(pool_base + ['stop'])
-
-if config.exists(pool_base + ['subnet']):
- default_pool = range_pool_name
- for subnet in config.return_values(pool_base + ['subnet']):
- config.set(pool_base + [range_pool_name, 'range'], value=subnet, replace=False)
- config.delete(pool_base + ['subnet'])
-
-gateway = ''
-if config.exists(base + ['gateway-address']):
- gateway = config.return_value(base + ['gateway-address'])
-
-#named pool migration
-namedpools_base = pool_base + ['name']
-if config.exists(namedpools_base):
- if config.exists(base + ['authentication', 'mode']):
- if config.return_value(base + ['authentication', 'mode']) == 'local':
- if config.list_nodes(namedpools_base):
- default_pool = config.list_nodes(namedpools_base)[0]
-
- 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)
- 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 gateway:
- if config.exists(pool_path + ['gateway-address']):
- gateway = config.return_value(pool_path + ['gateway-address'])
-
- config.delete(namedpools_base)
-
-if gateway:
- config.set(base + ['gateway-address'], value=gateway)
-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:
- f.write(config.to_string())
-except OSError as e:
- print("Failed to save the modified config: {}".format(e))
- exit(1)
+ for subnet in config.return_values(pool_base + ['subnet']):
+ config.set(pool_base + [range_pool_name, 'range'], value=subnet, replace=False)
+ config.delete(pool_base + ['subnet'])
+
+ gateway = ''
+ if config.exists(base + ['gateway-address']):
+ gateway = config.return_value(base + ['gateway-address'])
+
+ #named pool migration
+ namedpools_base = pool_base + ['name']
+ if config.exists(namedpools_base):
+ if config.exists(base + ['authentication', 'mode']):
+ if config.return_value(base + ['authentication', 'mode']) == 'local':
+ if config.list_nodes(namedpools_base):
+ default_pool = config.list_nodes(namedpools_base)[0]
+
+ 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)
+ 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 gateway:
+ if config.exists(pool_path + ['gateway-address']):
+ gateway = config.return_value(pool_path + ['gateway-address'])
+
+ config.delete(namedpools_base)
+
+ if gateway:
+ config.set(base + ['gateway-address'], value=gateway)
+ if default_pool:
+ config.set(base + ['default-pool'], value=default_pool)
+ # format as tag node
+ config.set_tag(pool_base)
diff --git a/src/migration-scripts/pppoe-server/7-to-8 b/src/migration-scripts/pppoe-server/7-to-8
index 0381f0bf9..90e4fa053 100755..100644
--- a/src/migration-scripts/pppoe-server/7-to-8
+++ b/src/migration-scripts/pppoe-server/7-to-8
@@ -1,58 +1,40 @@
-#!/usr/bin/env python3
+# Copyright 2023-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2023-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; either
+# version 2.1 of the License, or (at your option) any later version.
#
-# 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# Migrating to named ipv6 pools
-from sys import argv
-from sys import exit
from vyos.configtree import ConfigTree
-if len(argv) < 2:
- print("Must specify file name!")
- exit(1)
+base = ['service', 'pppoe-server']
+pool_base = base + ['client-ipv6-pool']
-file_name = argv[1]
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ return
-with open(file_name, 'r') as f:
- config_file = f.read()
+ if not config.exists(pool_base):
+ return
-config = ConfigTree(config_file)
-base = ['service', 'pppoe-server']
-pool_base = base + ['client-ipv6-pool']
-if not config.exists(base):
- exit(0)
-
-if not config.exists(pool_base):
- exit(0)
-
-ipv6_pool_name = 'ipv6-pool'
-config.copy(pool_base, pool_base + [ipv6_pool_name])
-
-if config.exists(pool_base + ['prefix']):
- config.delete(pool_base + ['prefix'])
- config.set(base + ['default-ipv6-pool'], value=ipv6_pool_name)
-if config.exists(pool_base + ['delegate']):
- config.delete(pool_base + ['delegate'])
-
-# format as tag node
-config.set_tag(pool_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)
+ ipv6_pool_name = 'ipv6-pool'
+ config.copy(pool_base, pool_base + [ipv6_pool_name])
+
+ if config.exists(pool_base + ['prefix']):
+ config.delete(pool_base + ['prefix'])
+ config.set(base + ['default-ipv6-pool'], value=ipv6_pool_name)
+ if config.exists(pool_base + ['delegate']):
+ config.delete(pool_base + ['delegate'])
+
+ # format as tag node
+ config.set_tag(pool_base)
diff --git a/src/migration-scripts/pppoe-server/8-to-9 b/src/migration-scripts/pppoe-server/8-to-9
index 4932a766f..e7e0aaa2c 100755..100644
--- a/src/migration-scripts/pppoe-server/8-to-9
+++ b/src/migration-scripts/pppoe-server/8-to-9
@@ -1,66 +1,48 @@
-#!/usr/bin/env python3
+# Copyright 2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 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; either
+# version 2.1 of the License, or (at your option) any later version.
#
-# 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# Change from 'ccp' to 'disable-ccp' in ppp-option section
# Migration ipv6 options
-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 = ['service', 'pppoe-server']
-if not config.exists(base):
- exit(0)
-
-#CCP migration
-if config.exists(base + ['ppp-options', 'ccp']):
- config.delete(base + ['ppp-options', 'ccp'])
-else:
- config.set(base + ['ppp-options', 'disable-ccp'])
-
-#IPV6 options migrations
-if config.exists(base + ['ppp-options','ipv6-peer-intf-id']):
- intf_peer_id = config.return_value(base + ['ppp-options','ipv6-peer-intf-id'])
- if intf_peer_id == 'ipv4':
- intf_peer_id = 'ipv4-addr'
- config.set(base + ['ppp-options','ipv6-peer-interface-id'], value=intf_peer_id, replace=True)
- config.delete(base + ['ppp-options','ipv6-peer-intf-id'])
-
-if config.exists(base + ['ppp-options','ipv6-intf-id']):
- intf_id = config.return_value(base + ['ppp-options','ipv6-intf-id'])
- config.set(base + ['ppp-options','ipv6-interface-id'], value=intf_id, replace=True)
- config.delete(base + ['ppp-options','ipv6-intf-id'])
-
-if config.exists(base + ['ppp-options','ipv6-accept-peer-intf-id']):
- config.set(base + ['ppp-options','ipv6-accept-peer-interface-id'])
- config.delete(base + ['ppp-options','ipv6-accept-peer-intf-id'])
-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 migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ return
+
+ #CCP migration
+ if config.exists(base + ['ppp-options', 'ccp']):
+ config.delete(base + ['ppp-options', 'ccp'])
+ else:
+ config.set(base + ['ppp-options', 'disable-ccp'])
+
+ #IPV6 options migrations
+ if config.exists(base + ['ppp-options','ipv6-peer-intf-id']):
+ intf_peer_id = config.return_value(base + ['ppp-options','ipv6-peer-intf-id'])
+ if intf_peer_id == 'ipv4':
+ intf_peer_id = 'ipv4-addr'
+ config.set(base + ['ppp-options','ipv6-peer-interface-id'], value=intf_peer_id, replace=True)
+ config.delete(base + ['ppp-options','ipv6-peer-intf-id'])
+
+ if config.exists(base + ['ppp-options','ipv6-intf-id']):
+ intf_id = config.return_value(base + ['ppp-options','ipv6-intf-id'])
+ config.set(base + ['ppp-options','ipv6-interface-id'], value=intf_id, replace=True)
+ config.delete(base + ['ppp-options','ipv6-intf-id'])
+
+ if config.exists(base + ['ppp-options','ipv6-accept-peer-intf-id']):
+ config.set(base + ['ppp-options','ipv6-accept-peer-interface-id'])
+ config.delete(base + ['ppp-options','ipv6-accept-peer-intf-id'])
diff --git a/src/migration-scripts/pppoe-server/9-to-10 b/src/migration-scripts/pppoe-server/9-to-10
index e0c782f04..d3475e8ff 100755..100644
--- a/src/migration-scripts/pppoe-server/9-to-10
+++ b/src/migration-scripts/pppoe-server/9-to-10
@@ -1,56 +1,38 @@
-#!/usr/bin/env python3
+# Copyright 2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 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; either
+# version 2.1 of the License, or (at your option) any later version.
#
-# 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# Migration of pado-delay options
-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 = ['service', 'pppoe-server', 'pado-delay']
-if not config.exists(base):
- exit(0)
-pado_delay = {}
-for delay in config.list_nodes(base):
- sessions = config.return_value(base + [delay, 'sessions'])
- pado_delay[delay] = sessions
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ return
-# need to define delay for latest sessions
-sorted_delays = dict(sorted(pado_delay.items(), key=lambda k_v: int(k_v[1])))
-last_delay = list(sorted_delays)[-1]
+ pado_delay = {}
+ for delay in config.list_nodes(base):
+ sessions = config.return_value(base + [delay, 'sessions'])
+ pado_delay[delay] = sessions
-# Rename last delay -> disable
-tmp = base + [last_delay]
-if config.exists(tmp):
- config.rename(tmp, 'disable')
+ # need to define delay for latest sessions
+ sorted_delays = dict(sorted(pado_delay.items(), key=lambda k_v: int(k_v[1])))
+ last_delay = list(sorted_delays)[-1]
-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)
+ # Rename last delay -> disable
+ tmp = base + [last_delay]
+ if config.exists(tmp):
+ config.rename(tmp, 'disable')
diff --git a/src/migration-scripts/pptp/0-to-1 b/src/migration-scripts/pptp/0-to-1
index 1b7697c11..dd0b6f57e 100755..100644
--- a/src/migration-scripts/pptp/0-to-1
+++ b/src/migration-scripts/pptp/0-to-1
@@ -1,29 +1,31 @@
-#!/usr/bin/env python3
+# Copyright 2018-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
+# 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, see <http://www.gnu.org/licenses/>.
# Unclutter PPTP VPN configuiration - move radius-server top level tag
# nodes to a regular node which now also configures the radius source address
# used when querying a radius server
-import sys
-
from vyos.configtree import ConfigTree
-if len(sys.argv) < 2:
- print("Must specify file name!")
- sys.exit(1)
-
-file_name = sys.argv[1]
-
-with open(file_name, 'r') as f:
- config_file = f.read()
+cfg_base = ['vpn', 'pptp', 'remote-access', 'authentication']
-config = ConfigTree(config_file)
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(cfg_base):
+ # Nothing to do
+ return
-cfg_base = ['vpn', 'pptp', 'remote-access', 'authentication']
-if not config.exists(cfg_base):
- # Nothing to do
- sys.exit(0)
-else:
# Migrate "vpn pptp authentication radius-source-address" to new
# "vpn pptp authentication radius source-address"
if config.exists(cfg_base + ['radius-source-address']):
@@ -50,10 +52,3 @@ else:
# delete top level tag node
if config.exists(cfg_base + ['radius-server']):
config.delete(cfg_base + ['radius-server'])
-
- 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))
- sys.exit(1)
diff --git a/src/migration-scripts/pptp/1-to-2 b/src/migration-scripts/pptp/1-to-2
index 99624dceb..1e7601193 100755..100644
--- a/src/migration-scripts/pptp/1-to-2
+++ b/src/migration-scripts/pptp/1-to-2
@@ -1,41 +1,30 @@
-#!/usr/bin/env python3
+# Copyright 2020-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# 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 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# - migrate dns-servers node to common name-servers
# - remove radios req-limit node
-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]
+base = ['vpn', 'pptp', 'remote-access']
-with open(file_name, 'r') as f:
- config_file = f.read()
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ # Nothing to do
+ return
-config = ConfigTree(config_file)
-base = ['vpn', 'pptp', 'remote-access']
-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):
@@ -62,10 +51,3 @@ else:
for server in config.list_nodes(radius_base + ['server']):
if config.exists(radius_base + ['server', server, 'req-limit']):
config.delete(radius_base + ['server', server, 'req-limit'])
-
- 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/pptp/2-to-3 b/src/migration-scripts/pptp/2-to-3
index 42c4dedf4..8b0d6d865 100755..100644
--- a/src/migration-scripts/pptp/2-to-3
+++ b/src/migration-scripts/pptp/2-to-3
@@ -1,73 +1,55 @@
-#!/usr/bin/env python3
+# Copyright 2023-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2023-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; either
+# version 2.1 of the License, or (at your option) any later version.
#
-# 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# - move all pool to named pools
# 'start-stop' migrate to namedpool 'default-range-pool'
# 'default-subnet-pool' is the next pool for 'default-range-pool'
-from sys import argv
-from sys import exit
from vyos.configtree import ConfigTree
from vyos.base import Warning
-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 = ['vpn', 'pptp', 'remote-access']
pool_base = base + ['client-ip-pool']
-if not config.exists(base):
- exit(0)
-
-if not config.exists(pool_base):
- exit(0)
-
-range_pool_name = 'default-range-pool'
-
-if config.exists(pool_base + ['start']) and config.exists(pool_base + ['stop']):
- def is_legalrange(ip1: str, ip2: str, mask: str):
- from ipaddress import IPv4Interface
- interface1 = IPv4Interface(f'{ip1}/{mask}')
- interface2 = IPv4Interface(f'{ip2}/{mask}')
- return interface1.network.network_address == interface2.network.network_address and interface2.ip > interface1.ip
-
- start_ip = config.return_value(pool_base + ['start'])
- stop_ip = config.return_value(pool_base + ['stop'])
- if is_legalrange(start_ip, stop_ip, '24'):
- ip_range = f'{start_ip}-{stop_ip}'
- config.set(pool_base + [range_pool_name, 'range'], value=ip_range, replace=False)
- config.set(base + ['default-pool'], value=range_pool_name)
- else:
- Warning(
- f'PPTP client-ip-pool range start-ip:{start_ip} and stop-ip:{stop_ip} can not be migrated.')
-
- config.delete(pool_base + ['start'])
- config.delete(pool_base + ['stop'])
-# format as tag node
-config.set_tag(pool_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)
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ return
+
+ if not config.exists(pool_base):
+ return
+
+ range_pool_name = 'default-range-pool'
+
+ if config.exists(pool_base + ['start']) and config.exists(pool_base + ['stop']):
+ def is_legalrange(ip1: str, ip2: str, mask: str):
+ from ipaddress import IPv4Interface
+ interface1 = IPv4Interface(f'{ip1}/{mask}')
+ interface2 = IPv4Interface(f'{ip2}/{mask}')
+ return interface1.network.network_address == interface2.network.network_address and interface2.ip > interface1.ip
+
+ start_ip = config.return_value(pool_base + ['start'])
+ stop_ip = config.return_value(pool_base + ['stop'])
+ if is_legalrange(start_ip, stop_ip, '24'):
+ ip_range = f'{start_ip}-{stop_ip}'
+ config.set(pool_base + [range_pool_name, 'range'], value=ip_range, replace=False)
+ config.set(base + ['default-pool'], value=range_pool_name)
+ else:
+ Warning(
+ f'PPTP client-ip-pool range start-ip:{start_ip} and stop-ip:{stop_ip} can not be migrated.')
+
+ config.delete(pool_base + ['start'])
+ config.delete(pool_base + ['stop'])
+ # format as tag node
+ config.set_tag(pool_base)
diff --git a/src/migration-scripts/pptp/3-to-4 b/src/migration-scripts/pptp/3-to-4
index ebd343028..2dabd8475 100755..100644
--- a/src/migration-scripts/pptp/3-to-4
+++ b/src/migration-scripts/pptp/3-to-4
@@ -1,48 +1,29 @@
-#!/usr/bin/env python3
+# Copyright 2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 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; either
+# version 2.1 of the License, or (at your option) any later version.
#
-# 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# - Move 'mppe' from 'authentication' node to 'ppp-options'
-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 = ['vpn', 'pptp', 'remote-access']
-if not config.exists(base):
- exit(0)
-
-if config.exists(base + ['authentication','mppe']):
- mppe = config.return_value(base + ['authentication','mppe'])
- config.set(base + ['ppp-options', 'mppe'], value=mppe, replace=True)
- config.delete(base + ['authentication','mppe'])
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ return
-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)
+ if config.exists(base + ['authentication','mppe']):
+ mppe = config.return_value(base + ['authentication','mppe'])
+ config.set(base + ['ppp-options', 'mppe'], value=mppe, replace=True)
+ config.delete(base + ['authentication','mppe'])
diff --git a/src/migration-scripts/pptp/4-to-5 b/src/migration-scripts/pptp/4-to-5
index 83632b6d8..c906f58c4 100755..100644
--- a/src/migration-scripts/pptp/4-to-5
+++ b/src/migration-scripts/pptp/4-to-5
@@ -1,63 +1,43 @@
-#!/usr/bin/env python3
+# Copyright 2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 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; either
+# version 2.1 of the License, or (at your option) any later version.
#
-# 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# - Move 'require' from 'protocols' in 'authentication' node
# - Migrate to new default values in radius timeout and acct-timeout
-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 = ['vpn', 'pptp', 'remote-access']
-if not config.exists(base):
- exit(0)
-
-#migrate require to protocols
-require_path = base + ['authentication', 'require']
-if config.exists(require_path):
- protocols = list(config.return_values(require_path))
- for protocol in protocols:
- config.set(base + ['authentication', 'protocols'], value=protocol,
- replace=False)
- config.delete(require_path)
-else:
- config.set(base + ['authentication', 'protocols'], value='mschap-v2')
-
-radius_path = base + ['authentication', 'radius']
-if config.exists(radius_path):
- if not config.exists(radius_path + ['timeout']):
- config.set(radius_path + ['timeout'], value=3)
- if not config.exists(radius_path + ['acct-timeout']):
- config.set(radius_path + ['acct-timeout'], value=3)
-
-
-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 migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ return
+
+ #migrate require to protocols
+ require_path = base + ['authentication', 'require']
+ if config.exists(require_path):
+ protocols = list(config.return_values(require_path))
+ for protocol in protocols:
+ config.set(base + ['authentication', 'protocols'], value=protocol,
+ replace=False)
+ config.delete(require_path)
+ else:
+ config.set(base + ['authentication', 'protocols'], value='mschap-v2')
+
+ radius_path = base + ['authentication', 'radius']
+ if config.exists(radius_path):
+ if not config.exists(radius_path + ['timeout']):
+ config.set(radius_path + ['timeout'], value=3)
+ if not config.exists(radius_path + ['acct-timeout']):
+ config.set(radius_path + ['acct-timeout'], value=3)
diff --git a/src/migration-scripts/qos/1-to-2 b/src/migration-scripts/qos/1-to-2
index 666811e5a..c43d8fa47 100755..100644
--- a/src/migration-scripts/qos/1-to-2
+++ b/src/migration-scripts/qos/1-to-2
@@ -1,20 +1,17 @@
-#!/usr/bin/env python3
+# Copyright 2022-2024 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
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
#
-# 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-from sys import argv,exit
+# 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 vyos.base import Warning
from vyos.configtree import ConfigTree
@@ -28,18 +25,6 @@ def bandwidth_percent_to_val(interface, percent) -> int:
speed = int(speed) *1000000 # convert to MBit/s
return speed * int(percent) // 100 # integer division
-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 = ['traffic-policy']
-config = ConfigTree(config_file)
-
def delete_orphaned_interface_policy(config, iftype, ifname, vif=None, vifs=None, vifc=None):
"""Delete unexpected traffic-policy on interfaces in cases when
@@ -62,133 +47,122 @@ def delete_orphaned_interface_policy(config, iftype, ifname, vif=None, vifs=None
config.delete(if_path + ['traffic-policy'])
-if not config.exists(base):
- # Delete orphaned nodes on interfaces T5941
- for iftype in config.list_nodes(['interfaces']):
- for ifname in config.list_nodes(['interfaces', iftype]):
- delete_orphaned_interface_policy(config, iftype, ifname)
-
- if config.exists(['interfaces', iftype, ifname, 'vif']):
- for vif in config.list_nodes(['interfaces', iftype, ifname, 'vif']):
- delete_orphaned_interface_policy(config, iftype, ifname, vif=vif)
-
- if config.exists(['interfaces', iftype, ifname, 'vif-s']):
- for vifs in config.list_nodes(['interfaces', iftype, ifname, 'vif-s']):
- delete_orphaned_interface_policy(config, iftype, ifname, vifs=vifs)
-
- if config.exists(['interfaces', iftype, ifname, 'vif-s', vifs, 'vif-c']):
- for vifc in config.list_nodes(['interfaces', iftype, ifname, 'vif-s', vifs, 'vif-c']):
- delete_orphaned_interface_policy(config, iftype, ifname, vifs=vifs, vifc=vifc)
-
- 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)
-
- # Nothing to do
- exit(0)
-
-iface_config = {}
-
-if config.exists(['interfaces']):
- def get_qos(config, interface, interface_base):
- if config.exists(interface_base):
- tmp = { interface : {} }
- if config.exists(interface_base + ['in']):
- tmp[interface]['ingress'] = config.return_value(interface_base + ['in'])
- if config.exists(interface_base + ['out']):
- tmp[interface]['egress'] = config.return_value(interface_base + ['out'])
- config.delete(interface_base)
- return tmp
- return None
-
- # Migrate "interface ethernet eth0 traffic-policy in|out" to "qos interface eth0 ingress|egress"
- for type in config.list_nodes(['interfaces']):
- for interface in config.list_nodes(['interfaces', type]):
- interface_base = ['interfaces', type, interface, 'traffic-policy']
- tmp = get_qos(config, interface, interface_base)
- if tmp: iface_config.update(tmp)
-
- vif_path = ['interfaces', type, interface, 'vif']
- if config.exists(vif_path):
- for vif in config.list_nodes(vif_path):
- vif_interface_base = vif_path + [vif, 'traffic-policy']
- ifname = f'{interface}.{vif}'
- tmp = get_qos(config, ifname, vif_interface_base)
- if tmp: iface_config.update(tmp)
-
- vif_s_path = ['interfaces', type, interface, 'vif-s']
- if config.exists(vif_s_path):
- for vif_s in config.list_nodes(vif_s_path):
- vif_s_interface_base = vif_s_path + [vif_s, 'traffic-policy']
- ifname = f'{interface}.{vif_s}'
- tmp = get_qos(config, ifname, vif_s_interface_base)
- if tmp: iface_config.update(tmp)
-
- # vif-c interfaces MUST be migrated before their parent vif-s
- # interface as the migrate_*() functions delete the path!
- vif_c_path = ['interfaces', type, interface, 'vif-s', vif_s, 'vif-c']
- if config.exists(vif_c_path):
- for vif_c in config.list_nodes(vif_c_path):
- vif_c_interface_base = vif_c_path + [vif_c, 'traffic-policy']
- ifname = f'{interface}.{vif_s}.{vif_c}'
- tmp = get_qos(config, ifname, vif_s_interface_base)
- if tmp: iface_config.update(tmp)
-
-
-# Now we have the information which interface uses which QoS policy.
-# Interface binding will be moved to the qos CLi tree
-config.set(['qos'])
-config.copy(base, ['qos', 'policy'])
-config.delete(base)
-
-# Now map the interface policy binding to the new CLI syntax
-if len(iface_config):
- config.set(['qos', 'interface'])
- config.set_tag(['qos', 'interface'])
-
-for interface, interface_config in iface_config.items():
- config.set(['qos', 'interface', interface])
- config.set_tag(['qos', 'interface', interface])
- if 'ingress' in interface_config:
- config.set(['qos', 'interface', interface, 'ingress'], value=interface_config['ingress'])
- if 'egress' in interface_config:
- config.set(['qos', 'interface', interface, 'egress'], value=interface_config['egress'])
-
-# Remove "burst" CLI node from network emulator
-netem_base = ['qos', 'policy', 'network-emulator']
-if config.exists(netem_base):
- for policy_name in config.list_nodes(netem_base):
- if config.exists(netem_base + [policy_name, 'burst']):
- config.delete(netem_base + [policy_name, 'burst'])
-
-# Change bandwidth unit MBit -> mbit as tc only supports mbit
-base = ['qos', 'policy']
-if config.exists(base):
- for policy_type in config.list_nodes(base):
- for policy in config.list_nodes(base + [policy_type]):
- policy_base = base + [policy_type, policy]
- if config.exists(policy_base + ['bandwidth']):
- tmp = config.return_value(policy_base + ['bandwidth'])
- config.set(policy_base + ['bandwidth'], value=tmp.lower())
-
- if config.exists(policy_base + ['class']):
- for cls in config.list_nodes(policy_base + ['class']):
- cls_base = policy_base + ['class', cls]
- if config.exists(cls_base + ['bandwidth']):
- tmp = config.return_value(cls_base + ['bandwidth'])
- config.set(cls_base + ['bandwidth'], value=tmp.lower())
-
- if config.exists(policy_base + ['default', 'bandwidth']):
+def migrate(config: ConfigTree) -> None:
+ base = ['traffic-policy']
+
+ if not config.exists(base):
+ # Delete orphaned nodes on interfaces T5941
+ for iftype in config.list_nodes(['interfaces']):
+ for ifname in config.list_nodes(['interfaces', iftype]):
+ delete_orphaned_interface_policy(config, iftype, ifname)
+
+ if config.exists(['interfaces', iftype, ifname, 'vif']):
+ for vif in config.list_nodes(['interfaces', iftype, ifname, 'vif']):
+ delete_orphaned_interface_policy(config, iftype, ifname, vif=vif)
+
+ if config.exists(['interfaces', iftype, ifname, 'vif-s']):
+ for vifs in config.list_nodes(['interfaces', iftype, ifname, 'vif-s']):
+ delete_orphaned_interface_policy(config, iftype, ifname, vifs=vifs)
+
+ if config.exists(['interfaces', iftype, ifname, 'vif-s', vifs, 'vif-c']):
+ for vifc in config.list_nodes(['interfaces', iftype, ifname, 'vif-s', vifs, 'vif-c']):
+ delete_orphaned_interface_policy(config, iftype, ifname, vifs=vifs, vifc=vifc)
+
+ # Nothing to do
+ return
+
+ iface_config = {}
+
+ if config.exists(['interfaces']):
+ def get_qos(config, interface, interface_base):
+ if config.exists(interface_base):
+ tmp = { interface : {} }
+ if config.exists(interface_base + ['in']):
+ tmp[interface]['ingress'] = config.return_value(interface_base + ['in'])
+ if config.exists(interface_base + ['out']):
+ tmp[interface]['egress'] = config.return_value(interface_base + ['out'])
+ config.delete(interface_base)
+ return tmp
+ return None
+
+ # Migrate "interface ethernet eth0 traffic-policy in|out" to "qos interface eth0 ingress|egress"
+ for type in config.list_nodes(['interfaces']):
+ for interface in config.list_nodes(['interfaces', type]):
+ interface_base = ['interfaces', type, interface, 'traffic-policy']
+ tmp = get_qos(config, interface, interface_base)
+ if tmp: iface_config.update(tmp)
+
+ vif_path = ['interfaces', type, interface, 'vif']
+ if config.exists(vif_path):
+ for vif in config.list_nodes(vif_path):
+ vif_interface_base = vif_path + [vif, 'traffic-policy']
+ ifname = f'{interface}.{vif}'
+ tmp = get_qos(config, ifname, vif_interface_base)
+ if tmp: iface_config.update(tmp)
+
+ vif_s_path = ['interfaces', type, interface, 'vif-s']
+ if config.exists(vif_s_path):
+ for vif_s in config.list_nodes(vif_s_path):
+ vif_s_interface_base = vif_s_path + [vif_s, 'traffic-policy']
+ ifname = f'{interface}.{vif_s}'
+ tmp = get_qos(config, ifname, vif_s_interface_base)
+ if tmp: iface_config.update(tmp)
+
+ # vif-c interfaces MUST be migrated before their parent vif-s
+ # interface as the migrate_*() functions delete the path!
+ vif_c_path = ['interfaces', type, interface, 'vif-s', vif_s, 'vif-c']
+ if config.exists(vif_c_path):
+ for vif_c in config.list_nodes(vif_c_path):
+ vif_c_interface_base = vif_c_path + [vif_c, 'traffic-policy']
+ ifname = f'{interface}.{vif_s}.{vif_c}'
+ tmp = get_qos(config, ifname, vif_s_interface_base)
+ if tmp: iface_config.update(tmp)
+
+
+ # Now we have the information which interface uses which QoS policy.
+ # Interface binding will be moved to the qos CLi tree
+ config.set(['qos'])
+ config.copy(base, ['qos', 'policy'])
+ config.delete(base)
+
+ # Now map the interface policy binding to the new CLI syntax
+ if len(iface_config):
+ config.set(['qos', 'interface'])
+ config.set_tag(['qos', 'interface'])
+
+ for interface, interface_config in iface_config.items():
+ config.set(['qos', 'interface', interface])
+ config.set_tag(['qos', 'interface', interface])
+ if 'ingress' in interface_config:
+ config.set(['qos', 'interface', interface, 'ingress'], value=interface_config['ingress'])
+ if 'egress' in interface_config:
+ config.set(['qos', 'interface', interface, 'egress'], value=interface_config['egress'])
+
+ # Remove "burst" CLI node from network emulator
+ netem_base = ['qos', 'policy', 'network-emulator']
+ if config.exists(netem_base):
+ for policy_name in config.list_nodes(netem_base):
+ if config.exists(netem_base + [policy_name, 'burst']):
+ config.delete(netem_base + [policy_name, 'burst'])
+
+ # Change bandwidth unit MBit -> mbit as tc only supports mbit
+ base = ['qos', 'policy']
+ if config.exists(base):
+ for policy_type in config.list_nodes(base):
+ for policy in config.list_nodes(base + [policy_type]):
+ policy_base = base + [policy_type, policy]
+ if config.exists(policy_base + ['bandwidth']):
+ tmp = config.return_value(policy_base + ['bandwidth'])
+ config.set(policy_base + ['bandwidth'], value=tmp.lower())
+
+ if config.exists(policy_base + ['class']):
+ for cls in config.list_nodes(policy_base + ['class']):
+ cls_base = policy_base + ['class', cls]
+ if config.exists(cls_base + ['bandwidth']):
+ tmp = config.return_value(cls_base + ['bandwidth'])
+ config.set(cls_base + ['bandwidth'], value=tmp.lower())
+
if config.exists(policy_base + ['default', 'bandwidth']):
- tmp = config.return_value(policy_base + ['default', 'bandwidth'])
- config.set(policy_base + ['default', 'bandwidth'], 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)
+ if config.exists(policy_base + ['default', 'bandwidth']):
+ tmp = config.return_value(policy_base + ['default', 'bandwidth'])
+ config.set(policy_base + ['default', 'bandwidth'], value=tmp.lower())
diff --git a/src/migration-scripts/quagga/10-to-11 b/src/migration-scripts/quagga/10-to-11
index 0ed4f5df6..15dbbb193 100755..100644
--- a/src/migration-scripts/quagga/10-to-11
+++ b/src/migration-scripts/quagga/10-to-11
@@ -1,51 +1,31 @@
-#!/usr/bin/env python3
+# Copyright 2023-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2023 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 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# T5150: Rework CLI definitions to apply route-maps between routing daemons
# and zebra/kernel
-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)
-
static_base = ['protocols', 'static']
-# Check if static routes are configured - if so, migrate the CLI node
-if config.exists(static_base):
- if config.exists(static_base + ['route-map']):
- tmp = config.return_value(static_base + ['route-map'])
- config.set(['system', 'ip', 'protocol', 'static', 'route-map'], value=tmp)
- config.set_tag(['system', 'ip', 'protocol'])
- config.delete(static_base + ['route-map'])
+def migrate(config: ConfigTree) -> None:
+ # Check if static routes are configured - if so, migrate the CLI node
+ if config.exists(static_base):
+ if config.exists(static_base + ['route-map']):
+ tmp = config.return_value(static_base + ['route-map'])
-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)
+ config.set(['system', 'ip', 'protocol', 'static', 'route-map'], value=tmp)
+ config.set_tag(['system', 'ip', 'protocol'])
+ config.delete(static_base + ['route-map'])
diff --git a/src/migration-scripts/quagga/2-to-3 b/src/migration-scripts/quagga/2-to-3
index 96b56da70..d62c387ba 100755..100644
--- a/src/migration-scripts/quagga/2-to-3
+++ b/src/migration-scripts/quagga/2-to-3
@@ -1,37 +1,21 @@
-#!/usr/bin/env python3
+# Copyright 2018-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2018 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 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,
+# 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 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/>.
-#
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
#
-
-import sys
+# 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 vyos.configtree import ConfigTree
-if len(sys.argv) < 2:
- print("Must specify file name!")
- sys.exit(1)
-
-file_name = sys.argv[1]
-
-with open(file_name, 'r') as f:
- config_file = f.read()
-
-config = ConfigTree(config_file)
-
def migrate_neighbor(config, neighbor_path, neighbor):
if config.exists(neighbor_path):
neighbors = config.list_nodes(neighbor_path)
@@ -117,10 +101,11 @@ def migrate_neighbor(config, neighbor_path, neighbor):
config.delete(neighbor_path + [neighbor, 'disable-send-community'])
-if not config.exists(['protocols', 'bgp']):
- # Nothing to do
- sys.exit(0)
-else:
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(['protocols', 'bgp']):
+ # Nothing to do
+ return
+
# Just to avoid writing it so many times
af_path = ['address-family', 'ipv4-unicast']
@@ -132,7 +117,7 @@ else:
bgp_path = ['protocols', 'bgp', asn]
else:
# There's actually no BGP, just its empty shell
- sys.exit(0)
+ return
## Move global IPv4-specific BGP options to "address-family ipv4-unicast"
@@ -194,10 +179,3 @@ else:
config.set(bgp_path + af_path + ['redistribute', redistribute, 'route-map'], value=redist_route_map)
config.delete(redistribute_path)
-
- 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))
- sys.exit(1)
diff --git a/src/migration-scripts/quagga/3-to-4 b/src/migration-scripts/quagga/3-to-4
index 1e8c8e2f2..81cf139f6 100755..100644
--- a/src/migration-scripts/quagga/3-to-4
+++ b/src/migration-scripts/quagga/3-to-4
@@ -1,20 +1,17 @@
-#!/usr/bin/env python3
+# Copyright 2019-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# 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 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,
+# 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 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/>.
-#
+# 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, see <http://www.gnu.org/licenses/>.
# Between 1.2.3 and 1.2.4, FRR added per-neighbor enforce-first-as option.
# Unfortunately they also removed the global enforce-first-as option,
@@ -23,26 +20,14 @@
# To emulate the effect of the original option, we insert it in every neighbor
# if the config used to have the original global option
-import sys
-
from vyos.configtree import ConfigTree
-if len(sys.argv) < 2:
- print("Must specify file name!")
- sys.exit(1)
-
-file_name = sys.argv[1]
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(['protocols', 'bgp']):
+ # Nothing to do
+ return
-with open(file_name, 'r') as f:
- config_file = f.read()
-
-config = ConfigTree(config_file)
-
-if not config.exists(['protocols', 'bgp']):
- # Nothing to do
- sys.exit(0)
-else:
# Check if BGP is actually configured and obtain the ASN
asn_list = config.list_nodes(['protocols', 'bgp'])
if asn_list:
@@ -50,7 +35,7 @@ else:
asn = asn_list[0]
else:
# There's actually no BGP, just its empty shell
- sys.exit(0)
+ return
# Check if BGP enforce-first-as option is set
enforce_first_as_path = ['protocols', 'bgp', asn, 'parameters', 'enforce-first-as']
@@ -64,13 +49,4 @@ else:
config.set(['protocols', 'bgp', asn, 'neighbor', p, 'enforce-first-as'])
else:
# Do nothing
- sys.exit(0)
-
- # Save a new configuration file
- 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))
- sys.exit(1)
-
+ return
diff --git a/src/migration-scripts/quagga/4-to-5 b/src/migration-scripts/quagga/4-to-5
index fcb496a9c..27b995431 100755..100644
--- a/src/migration-scripts/quagga/4-to-5
+++ b/src/migration-scripts/quagga/4-to-5
@@ -1,41 +1,26 @@
-#!/usr/bin/env python3
+# Copyright 2020-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2019 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 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,
+# 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 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/>.
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
#
-#
-
-import sys
+# 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 vyos.configtree import ConfigTree
-if len(sys.argv) < 2:
- print("Must specify file name!")
- sys.exit(1)
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(['protocols', 'bgp']):
+ # Nothing to do
+ return
-file_name = sys.argv[1]
-
-with open(file_name, 'r') as f:
- config_file = f.read()
-
-config = ConfigTree(config_file)
-
-if not config.exists(['protocols', 'bgp']):
- # Nothing to do
- sys.exit(0)
-else:
# Check if BGP is actually configured and obtain the ASN
asn_list = config.list_nodes(['protocols', 'bgp'])
if asn_list:
@@ -43,7 +28,7 @@ else:
asn = asn_list[0]
else:
# There's actually no BGP, just its empty shell
- sys.exit(0)
+ return
# Check if BGP scan-time parameter exist
scan_time_param = ['protocols', 'bgp', asn, 'parameters', 'scan-time']
@@ -52,12 +37,4 @@ else:
config.delete(scan_time_param)
else:
# Do nothing
- sys.exit(0)
-
- # Save a new configuration file
- 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))
- sys.exit(1)
+ return
diff --git a/src/migration-scripts/quagga/5-to-6 b/src/migration-scripts/quagga/5-to-6
index f075fc2e7..08fd070de 100755..100644
--- a/src/migration-scripts/quagga/5-to-6
+++ b/src/migration-scripts/quagga/5-to-6
@@ -1,42 +1,26 @@
-#!/usr/bin/env python3
+# Copyright 2020-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# 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 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-# * Remove parameter 'disable-network-import-check' which, as implemented,
-# had no effect on boot.
-
-import sys
+# 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 vyos.configtree import ConfigTree
-if len(sys.argv) < 2:
- print("Must specify file name!")
- sys.exit(1)
-
-file_name = sys.argv[1]
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(['protocols', 'bgp']):
+ # Nothing to do
+ return
-with open(file_name, 'r') as f:
- config_file = f.read()
-
-config = ConfigTree(config_file)
-
-if not config.exists(['protocols', 'bgp']):
- # Nothing to do
- sys.exit(0)
-else:
# Check if BGP is actually configured and obtain the ASN
asn_list = config.list_nodes(['protocols', 'bgp'])
if asn_list:
@@ -44,7 +28,7 @@ else:
asn = asn_list[0]
else:
# There's actually no BGP, just its empty shell
- sys.exit(0)
+ return
# Check if BGP parameter disable-network-import-check exists
param = ['protocols', 'bgp', asn, 'parameters', 'disable-network-import-check']
@@ -53,11 +37,4 @@ else:
config.delete(param)
else:
# Do nothing
- sys.exit(0)
-
- 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))
- sys.exit(1)
+ return
diff --git a/src/migration-scripts/quagga/6-to-7 b/src/migration-scripts/quagga/6-to-7
index ed295a95c..095baac03 100755..100644
--- a/src/migration-scripts/quagga/6-to-7
+++ b/src/migration-scripts/quagga/6-to-7
@@ -1,116 +1,97 @@
-#!/usr/bin/env python3
+# Copyright 2021-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2021 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 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# - T3037, BGP address-family ipv6-unicast capability dynamic does not exist in
# FRR, there is only a base, per neighbor dynamic capability, migrate config
-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!")
- exit(1)
-
-file_name = argv[1]
-
-with open(file_name, 'r') as f:
- config_file = f.read()
-
base = ['protocols', 'bgp']
-config = ConfigTree(config_file)
-
-if not config.exists(base):
- # Nothing to do
- exit(0)
-
-# Check if BGP is actually configured and obtain the ASN
-asn_list = config.list_nodes(base)
-if asn_list:
- # There's always just one BGP node, if any
- bgp_base = base + [asn_list[0]]
-
- for neighbor_type in ['neighbor', 'peer-group']:
- if not config.exists(bgp_base + [neighbor_type]):
- continue
- for neighbor in config.list_nodes(bgp_base + [neighbor_type]):
- # T2844 - add IPv4 AFI disable-send-community support
- send_comm_path = bgp_base + [neighbor_type, neighbor, 'disable-send-community']
- if config.exists(send_comm_path):
- new_base = bgp_base + [neighbor_type, neighbor, 'address-family', 'ipv4-unicast']
- config.set(new_base)
- config.copy(send_comm_path, new_base + ['disable-send-community'])
- config.delete(send_comm_path)
-
- cap_dynamic = False
- peer_group = None
- for afi in ['ipv4-unicast', 'ipv6-unicast']:
- afi_path = bgp_base + [neighbor_type, neighbor, 'address-family', afi]
- # Exit loop early if AFI does not exist
- if not config.exists(afi_path):
- continue
-
- cap_path = afi_path + ['capability', 'dynamic']
- if config.exists(cap_path):
- cap_dynamic = True
- config.delete(cap_path)
-
- # We have now successfully migrated the address-family
- # specific dynamic capability to the neighbor/peer-group
- # level. If this has been the only option under the
- # address-family nodes, we can clean them up by checking if
- # no other nodes are left under that tree and if so, delete
- # the parent.
- #
- # We walk from the most inner node to the most outer one.
- cleanup = -1
- while len(config.list_nodes(cap_path[:cleanup])) == 0:
- config.delete(cap_path[:cleanup])
- cleanup -= 1
-
- peer_group_path = afi_path + ['peer-group']
- if config.exists(peer_group_path):
- if ((is_ipv4(neighbor) and afi == 'ipv4-unicast') or
- (is_ipv6(neighbor) and afi == 'ipv6-unicast')):
- peer_group = config.return_value(peer_group_path)
-
- config.delete(peer_group_path)
-
- # We have now successfully migrated the address-family
- # specific peer-group to the neighbor level. If this has
- # been the only option under the address-family nodes, we
- # can clean them up by checking if no other nodes are left
- # under that tree and if so, delete the parent.
- #
- # We walk from the most inner node to the most outer one.
- cleanup = -1
- while len(config.list_nodes(peer_group_path[:cleanup])) == 0:
- config.delete(peer_group_path[:cleanup])
- cleanup -= 1
-
- if cap_dynamic:
- config.set(bgp_base + [neighbor_type, neighbor, 'capability', 'dynamic'])
- if peer_group:
- config.set(bgp_base + [neighbor_type, neighbor, 'peer-group'], value=peer_group)
-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 migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ # Nothing to do
+ return
+
+ # Check if BGP is actually configured and obtain the ASN
+ asn_list = config.list_nodes(base)
+ if asn_list:
+ # There's always just one BGP node, if any
+ bgp_base = base + [asn_list[0]]
+
+ for neighbor_type in ['neighbor', 'peer-group']:
+ if not config.exists(bgp_base + [neighbor_type]):
+ continue
+ for neighbor in config.list_nodes(bgp_base + [neighbor_type]):
+ # T2844 - add IPv4 AFI disable-send-community support
+ send_comm_path = bgp_base + [neighbor_type, neighbor, 'disable-send-community']
+ if config.exists(send_comm_path):
+ new_base = bgp_base + [neighbor_type, neighbor, 'address-family', 'ipv4-unicast']
+ config.set(new_base)
+ config.copy(send_comm_path, new_base + ['disable-send-community'])
+ config.delete(send_comm_path)
+
+ cap_dynamic = False
+ peer_group = None
+ for afi in ['ipv4-unicast', 'ipv6-unicast']:
+ afi_path = bgp_base + [neighbor_type, neighbor, 'address-family', afi]
+ # Exit loop early if AFI does not exist
+ if not config.exists(afi_path):
+ continue
+
+ cap_path = afi_path + ['capability', 'dynamic']
+ if config.exists(cap_path):
+ cap_dynamic = True
+ config.delete(cap_path)
+
+ # We have now successfully migrated the address-family
+ # specific dynamic capability to the neighbor/peer-group
+ # level. If this has been the only option under the
+ # address-family nodes, we can clean them up by checking if
+ # no other nodes are left under that tree and if so, delete
+ # the parent.
+ #
+ # We walk from the most inner node to the most outer one.
+ cleanup = -1
+ while len(config.list_nodes(cap_path[:cleanup])) == 0:
+ config.delete(cap_path[:cleanup])
+ cleanup -= 1
+
+ peer_group_path = afi_path + ['peer-group']
+ if config.exists(peer_group_path):
+ if ((is_ipv4(neighbor) and afi == 'ipv4-unicast') or
+ (is_ipv6(neighbor) and afi == 'ipv6-unicast')):
+ peer_group = config.return_value(peer_group_path)
+
+ config.delete(peer_group_path)
+
+ # We have now successfully migrated the address-family
+ # specific peer-group to the neighbor level. If this has
+ # been the only option under the address-family nodes, we
+ # can clean them up by checking if no other nodes are left
+ # under that tree and if so, delete the parent.
+ #
+ # We walk from the most inner node to the most outer one.
+ cleanup = -1
+ while len(config.list_nodes(peer_group_path[:cleanup])) == 0:
+ config.delete(peer_group_path[:cleanup])
+ cleanup -= 1
+
+ if cap_dynamic:
+ config.set(bgp_base + [neighbor_type, neighbor, 'capability', 'dynamic'])
+ if peer_group:
+ config.set(bgp_base + [neighbor_type, neighbor, 'peer-group'], value=peer_group)
diff --git a/src/migration-scripts/quagga/7-to-8 b/src/migration-scripts/quagga/7-to-8
index 8f11bf390..d9de26d15 100755..100644
--- a/src/migration-scripts/quagga/7-to-8
+++ b/src/migration-scripts/quagga/7-to-8
@@ -1,61 +1,42 @@
-#!/usr/bin/env python3
+# Copyright 2021-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2021 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 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# - T3391: Migrate "maximum-paths" setting from "protocols bgp asn maximum-paths"
# under the IPv4 address-family tree. Reason is we currently have no way in
# configuring this for IPv6 address-family. This mimics the FRR configuration.
-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 = ['protocols', 'bgp']
-config = ConfigTree(config_file)
-
-if not config.exists(base):
- # Nothing to do
- exit(0)
-
-# Check if BGP is actually configured and obtain the ASN
-asn_list = config.list_nodes(base)
-if asn_list:
- # There's always just one BGP node, if any
- bgp_base = base + [asn_list[0]]
-
- maximum_paths = bgp_base + ['maximum-paths']
- if config.exists(maximum_paths):
- for bgp_type in ['ebgp', 'ibgp']:
- if config.exists(maximum_paths + [bgp_type]):
- new_base = bgp_base + ['address-family', 'ipv4-unicast', 'maximum-paths']
- config.set(new_base)
- config.copy(maximum_paths + [bgp_type], new_base + [bgp_type])
- config.delete(maximum_paths)
-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 migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ # Nothing to do
+ return
+
+ # Check if BGP is actually configured and obtain the ASN
+ asn_list = config.list_nodes(base)
+ if asn_list:
+ # There's always just one BGP node, if any
+ bgp_base = base + [asn_list[0]]
+
+ maximum_paths = bgp_base + ['maximum-paths']
+ if config.exists(maximum_paths):
+ for bgp_type in ['ebgp', 'ibgp']:
+ if config.exists(maximum_paths + [bgp_type]):
+ new_base = bgp_base + ['address-family', 'ipv4-unicast', 'maximum-paths']
+ config.set(new_base)
+ config.copy(maximum_paths + [bgp_type], new_base + [bgp_type])
+ config.delete(maximum_paths)
diff --git a/src/migration-scripts/quagga/8-to-9 b/src/migration-scripts/quagga/8-to-9
index 0f683d5a1..eece6c15d 100755..100644
--- a/src/migration-scripts/quagga/8-to-9
+++ b/src/migration-scripts/quagga/8-to-9
@@ -1,24 +1,20 @@
-#!/usr/bin/env python3
+# Copyright 2021-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2021 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 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# - T2450: drop interface-route and interface-route6 from "protocols static"
-from sys import argv
-from sys import exit
-
from vyos.configtree import ConfigTree
def migrate_interface_route(config, base, path, route_route6):
@@ -84,54 +80,38 @@ def migrate_route(config, base, path, route_route6):
config.rename(vrf_path, 'vrf')
-if len(argv) < 2:
- print("Must specify file name!")
- exit(1)
+base = ['protocols', 'static']
-file_name = argv[1]
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ # Nothing to do
+ return
-with open(file_name, 'r') as f:
- config_file = f.read()
+ # Migrate interface-route into route
+ migrate_interface_route(config, base, ['interface-route'], 'route')
-base = ['protocols', 'static']
+ # Migrate interface-route6 into route6
+ migrate_interface_route(config, base, ['interface-route6'], 'route6')
-config = ConfigTree(config_file)
-if not config.exists(base):
- # Nothing to do
- exit(0)
+ # Cleanup nodes inside route
+ migrate_route(config, base, ['route'], 'route')
-# Migrate interface-route into route
-migrate_interface_route(config, base, ['interface-route'], 'route')
+ # Cleanup nodes inside route6
+ migrate_route(config, base, ['route6'], 'route6')
-# Migrate interface-route6 into route6
-migrate_interface_route(config, base, ['interface-route6'], 'route6')
+ #
+ # PBR table cleanup
+ table_path = base + ['table']
+ if config.exists(table_path):
+ for table in config.list_nodes(table_path):
+ # Migrate interface-route into route
+ migrate_interface_route(config, table_path + [table], ['interface-route'], 'route')
-# Cleanup nodes inside route
-migrate_route(config, base, ['route'], 'route')
+ # Migrate interface-route6 into route6
+ migrate_interface_route(config, table_path + [table], ['interface-route6'], 'route6')
-# Cleanup nodes inside route6
-migrate_route(config, base, ['route6'], 'route6')
+ # Cleanup nodes inside route
+ migrate_route(config, table_path + [table], ['route'], 'route')
-#
-# PBR table cleanup
-table_path = base + ['table']
-if config.exists(table_path):
- for table in config.list_nodes(table_path):
- # Migrate interface-route into route
- migrate_interface_route(config, table_path + [table], ['interface-route'], 'route')
-
- # Migrate interface-route6 into route6
- migrate_interface_route(config, table_path + [table], ['interface-route6'], 'route6')
-
- # Cleanup nodes inside route
- migrate_route(config, table_path + [table], ['route'], 'route')
-
- # Cleanup nodes inside route6
- migrate_route(config, table_path + [table], ['route6'], 'route6')
-
-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)
+ # Cleanup nodes inside route6
+ migrate_route(config, table_path + [table], ['route6'], 'route6')
diff --git a/src/migration-scripts/quagga/9-to-10 b/src/migration-scripts/quagga/9-to-10
index 3731762f7..4ac1f0b7d 100755..100644
--- a/src/migration-scripts/quagga/9-to-10
+++ b/src/migration-scripts/quagga/9-to-10
@@ -1,62 +1,42 @@
-#!/usr/bin/env python3
+# Copyright 2022-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2022 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 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# re-organize route-map as-path
-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 = ['policy', 'route-map']
-config = ConfigTree(config_file)
-if not config.exists(base):
- # Nothing to do
- exit(0)
-
-for route_map in config.list_nodes(base):
- # Bail out Early
- if not config.exists(base + [route_map, 'rule']):
- continue
-
- for rule in config.list_nodes(base + [route_map, 'rule']):
- rule_base = base + [route_map, 'rule', rule]
- if config.exists(rule_base + ['set', 'as-path-exclude']):
- tmp = config.return_value(rule_base + ['set', 'as-path-exclude'])
- config.delete(rule_base + ['set', 'as-path-exclude'])
- config.set(rule_base + ['set', 'as-path', 'exclude'], value=tmp)
-
- if config.exists(rule_base + ['set', 'as-path-prepend']):
- tmp = config.return_value(rule_base + ['set', 'as-path-prepend'])
- config.delete(rule_base + ['set', 'as-path-prepend'])
- config.set(rule_base + ['set', 'as-path', 'prepend'], 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 migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ # Nothing to do
+ return
+
+ for route_map in config.list_nodes(base):
+ # Bail out Early
+ if not config.exists(base + [route_map, 'rule']):
+ continue
+
+ for rule in config.list_nodes(base + [route_map, 'rule']):
+ rule_base = base + [route_map, 'rule', rule]
+ if config.exists(rule_base + ['set', 'as-path-exclude']):
+ tmp = config.return_value(rule_base + ['set', 'as-path-exclude'])
+ config.delete(rule_base + ['set', 'as-path-exclude'])
+ config.set(rule_base + ['set', 'as-path', 'exclude'], value=tmp)
+
+ if config.exists(rule_base + ['set', 'as-path-prepend']):
+ tmp = config.return_value(rule_base + ['set', 'as-path-prepend'])
+ config.delete(rule_base + ['set', 'as-path-prepend'])
+ config.set(rule_base + ['set', 'as-path', 'prepend'], value=tmp)
diff --git a/src/migration-scripts/reverse-proxy/0-to-1 b/src/migration-scripts/reverse-proxy/0-to-1
index d61493815..b495474a6 100755..100644
--- a/src/migration-scripts/reverse-proxy/0-to-1
+++ b/src/migration-scripts/reverse-proxy/0-to-1
@@ -1,48 +1,31 @@
-#!/usr/bin/env python3
+# Copyright 2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 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; either
+# version 2.1 of the License, or (at your option) any later version.
#
-# 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# T6409: Remove unused 'backend bk-example parameters' node
-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 = ['load-balancing', 'reverse-proxy', 'backend']
-if not config.exists(base):
- # Nothing to do
- exit(0)
-# we need to run this for every configured network
-for backend in config.list_nodes(base):
- param_node = base + [backend, 'parameters']
- if config.exists(param_node):
- config.delete(param_node)
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ # Nothing to do
+ return
-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)
+ # we need to run this for every configured network
+ for backend in config.list_nodes(base):
+ param_node = base + [backend, 'parameters']
+ if config.exists(param_node):
+ config.delete(param_node)
diff --git a/src/migration-scripts/rip/0-to-1 b/src/migration-scripts/rip/0-to-1
index 08a866374..6d41bcf58 100755..100644
--- a/src/migration-scripts/rip/0-to-1
+++ b/src/migration-scripts/rip/0-to-1
@@ -1,51 +1,31 @@
-#!/usr/bin/env python3
+# Copyright 2023-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2023 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 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# T5150: Rework CLI definitions to apply route-maps between routing daemons
# and zebra/kernel
-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)
-
ripng_base = ['protocols', 'ripng']
-# Check if RIPng is configured - if so, migrate the CLI node
-if config.exists(ripng_base):
- if config.exists(ripng_base + ['route-map']):
- tmp = config.return_value(ripng_base + ['route-map'])
- config.set(['system', 'ipv6', 'protocol', 'ripng', 'route-map'], value=tmp)
- config.set_tag(['system', 'ipv6', 'protocol'])
- config.delete(ripng_base + ['route-map'])
+def migrate(config: ConfigTree) -> None:
+ # Check if RIPng is configured - if so, migrate the CLI node
+ if config.exists(ripng_base):
+ if config.exists(ripng_base + ['route-map']):
+ tmp = config.return_value(ripng_base + ['route-map'])
-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)
+ config.set(['system', 'ipv6', 'protocol', 'ripng', 'route-map'], value=tmp)
+ config.set_tag(['system', 'ipv6', 'protocol'])
+ config.delete(ripng_base + ['route-map'])
diff --git a/src/migration-scripts/rpki/0-to-1 b/src/migration-scripts/rpki/0-to-1
index a7b5d07d5..b6e781fa9 100755..100644
--- a/src/migration-scripts/rpki/0-to-1
+++ b/src/migration-scripts/rpki/0-to-1
@@ -1,63 +1,44 @@
-#!/usr/bin/env python3
+# Copyright 2021-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2021 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 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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 sys import exit
-from sys import argv
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 = ['protocols', 'rpki']
-config = ConfigTree(config_file)
-
-# Nothing to do
-if not config.exists(base):
- exit(0)
-
-if config.exists(base + ['cache']):
- preference = 1
- for cache in config.list_nodes(base + ['cache']):
- address_node = base + ['cache', cache, 'address']
- if config.exists(address_node):
- address = config.return_value(address_node)
- # We do not longer support the address leafNode, RPKI cache server
- # IP address is now used from the tagNode
- config.delete(address_node)
- # VyOS 1.2 had no per instance preference, setting new defaults
- config.set(base + ['cache', cache, 'preference'], value=preference)
- # Increase preference for the next caching peer - actually VyOS 1.2
- # supported only one but better save then sorry (T3253)
- preference += 1
-
- # T3293: If the RPKI cache name equals the configured address,
- # renaming is not possible, as rename expects the new path to not
- # exist.
- if not config.exists(base + ['cache', address]):
- config.rename(base + ['cache', cache], address)
-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 migrate(config: ConfigTree) -> None:
+ # Nothing to do
+ if not config.exists(base):
+ return
+
+ if config.exists(base + ['cache']):
+ preference = 1
+ for cache in config.list_nodes(base + ['cache']):
+ address_node = base + ['cache', cache, 'address']
+ if config.exists(address_node):
+ address = config.return_value(address_node)
+ # We do not longer support the address leafNode, RPKI cache server
+ # IP address is now used from the tagNode
+ config.delete(address_node)
+ # VyOS 1.2 had no per instance preference, setting new defaults
+ config.set(base + ['cache', cache, 'preference'], value=preference)
+ # Increase preference for the next caching peer - actually VyOS 1.2
+ # supported only one but better save then sorry (T3253)
+ preference += 1
+
+ # T3293: If the RPKI cache name equals the configured address,
+ # renaming is not possible, as rename expects the new path to not
+ # exist.
+ if not config.exists(base + ['cache', address]):
+ config.rename(base + ['cache', cache], address)
diff --git a/src/migration-scripts/rpki/1-to-2 b/src/migration-scripts/rpki/1-to-2
index 50d4a3dfc..855236d6c 100755..100644
--- a/src/migration-scripts/rpki/1-to-2
+++ b/src/migration-scripts/rpki/1-to-2
@@ -1,73 +1,53 @@
-#!/usr/bin/env python3
+# Copyright 2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 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; either
+# version 2.1 of the License, or (at your option) any later version.
#
-# 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# T6011: rpki: known-hosts-file is no longer supported bxy FRR CLI,
# remove VyOS CLI node
-from sys import exit
-from sys import argv
-
from vyos.configtree import ConfigTree
from vyos.pki import OPENSSH_KEY_BEGIN
from vyos.pki import OPENSSH_KEY_END
from vyos.utils.file import read_file
-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 = ['protocols', 'rpki']
-config = ConfigTree(config_file)
-
-# Nothing to do
-if not config.exists(base):
- exit(0)
-
-if config.exists(base + ['cache']):
- for cache in config.list_nodes(base + ['cache']):
- ssh_node = base + ['cache', cache, 'ssh']
- if config.exists(ssh_node + ['known-hosts-file']):
- config.delete(ssh_node + ['known-hosts-file'])
-
- if config.exists(base + ['cache', cache, 'ssh']):
- private_key_node = base + ['cache', cache, 'ssh', 'private-key-file']
- private_key_file = config.return_value(private_key_node)
- private_key = read_file(private_key_file).replace(OPENSSH_KEY_BEGIN, '').replace(OPENSSH_KEY_END, '').replace('\n','')
-
- public_key_node = base + ['cache', cache, 'ssh', 'public-key-file']
- public_key_file = config.return_value(public_key_node)
- public_key = read_file(public_key_file).split()
-
- config.set(['pki', 'openssh', f'rpki-{cache}', 'private', 'key'], value=private_key)
- config.set(['pki', 'openssh', f'rpki-{cache}', 'public', 'key'], value=public_key[1])
- config.set(['pki', 'openssh', f'rpki-{cache}', 'public', 'type'], value=public_key[0])
- config.set_tag(['pki', 'openssh'])
- config.set(ssh_node + ['key'], value=f'rpki-{cache}')
-
- config.delete(private_key_node)
- config.delete(public_key_node)
-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 migrate(config: ConfigTree) -> None:
+ # Nothing to do
+ if not config.exists(base):
+ return
+
+ if config.exists(base + ['cache']):
+ for cache in config.list_nodes(base + ['cache']):
+ ssh_node = base + ['cache', cache, 'ssh']
+ if config.exists(ssh_node + ['known-hosts-file']):
+ config.delete(ssh_node + ['known-hosts-file'])
+
+ if config.exists(base + ['cache', cache, 'ssh']):
+ private_key_node = base + ['cache', cache, 'ssh', 'private-key-file']
+ private_key_file = config.return_value(private_key_node)
+ private_key = read_file(private_key_file).replace(OPENSSH_KEY_BEGIN, '').replace(OPENSSH_KEY_END, '').replace('\n','')
+
+ public_key_node = base + ['cache', cache, 'ssh', 'public-key-file']
+ public_key_file = config.return_value(public_key_node)
+ public_key = read_file(public_key_file).split()
+
+ config.set(['pki', 'openssh', f'rpki-{cache}', 'private', 'key'], value=private_key)
+ config.set(['pki', 'openssh', f'rpki-{cache}', 'public', 'key'], value=public_key[1])
+ config.set(['pki', 'openssh', f'rpki-{cache}', 'public', 'type'], value=public_key[0])
+ config.set_tag(['pki', 'openssh'])
+ config.set(ssh_node + ['key'], value=f'rpki-{cache}')
+
+ config.delete(private_key_node)
+ config.delete(public_key_node)
diff --git a/src/migration-scripts/salt/0-to-1 b/src/migration-scripts/salt/0-to-1
index 481d9de8f..3990a88dc 100755..100644
--- a/src/migration-scripts/salt/0-to-1
+++ b/src/migration-scripts/salt/0-to-1
@@ -1,43 +1,30 @@
-#!/usr/bin/env python3
+# Copyright 2020-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# 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 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# Delete log_file, log_level and user nodes
# rename hash_type to hash
# rename mine_interval to interval
-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', 'salt-minion']
-if not config.exists(base):
- # Nothing to do
- exit(0)
-else:
+
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ # Nothing to do
+ return
# delete nodes which are now populated with sane defaults
for node in ['log_file', 'log_level', 'user']:
@@ -49,10 +36,3 @@ else:
if config.exists(base + ['mine_interval']):
config.rename(base + ['mine_interval'], 'interval')
-
- 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/snmp/0-to-1 b/src/migration-scripts/snmp/0-to-1
index b1e61b958..03b190cb7 100755..100644
--- a/src/migration-scripts/snmp/0-to-1
+++ b/src/migration-scripts/snmp/0-to-1
@@ -1,38 +1,27 @@
-#!/usr/bin/env python3
+# Copyright 2019-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2019 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 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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
from vyos.configtree import ConfigTree
-if len(sys.argv) < 2:
- print("Must specify file name!")
- sys.exit(1)
-
-file_name = sys.argv[1]
-
-with open(file_name, 'r') as f:
- config_file = f.read()
-
-config = ConfigTree(config_file)
config_base = ['service', 'snmp', 'v3']
-if not config.exists(config_base):
- # Nothing to do
- sys.exit(0)
-else:
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(config_base):
+ # Nothing to do
+ return
+
# we no longer support a per trap target engine ID (https://vyos.dev/T818)
if config.exists(config_base + ['v3', 'trap-target']):
for target in config.list_nodes(config_base + ['v3', 'trap-target']):
@@ -47,10 +36,3 @@ else:
# https://vyos.dev/T1769
if config.exists(config_base + ['v3', 'tsm']):
config.delete(config_base + ['v3', 'tsm'])
-
- 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))
- sys.exit(1)
diff --git a/src/migration-scripts/snmp/1-to-2 b/src/migration-scripts/snmp/1-to-2
index e02cd1aa1..0120f8acb 100755..100644
--- a/src/migration-scripts/snmp/1-to-2
+++ b/src/migration-scripts/snmp/1-to-2
@@ -1,22 +1,25 @@
-#!/usr/bin/env python3
+# Copyright 2020-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# 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 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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 sys import argv, exit
from vyos.configtree import ConfigTree
+# We no longer support hashed values prefixed with '0x' to unclutter
+# CLI and also calculate the hases in advance instead of retrieving
+# them after service startup - which was always a bad idea
+prefix = '0x'
+
def migrate_keys(config, path):
# authentication: rename node 'encrypted-key' -> 'encrypted-password'
config_path_auth = path + ['auth', 'encrypted-key']
@@ -42,48 +45,26 @@ def migrate_keys(config, path):
tmp = tmp.replace(prefix, '')
config.set(config_path_priv, value=tmp)
-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)
+def migrate(config: ConfigTree) -> None:
config_base = ['service', 'snmp', 'v3']
if not config.exists(config_base):
# Nothing to do
- exit(0)
- else:
- # We no longer support hashed values prefixed with '0x' to unclutter
- # CLI and also calculate the hases in advance instead of retrieving
- # them after service startup - which was always a bad idea
- prefix = '0x'
-
- config_engineid = config_base + ['engineid']
- if config.exists(config_engineid):
- tmp = config.return_value(config_engineid)
- if tmp.startswith(prefix):
- tmp = tmp.replace(prefix, '')
- config.set(config_engineid, value=tmp)
+ return
- config_user = config_base + ['user']
- if config.exists(config_user):
- for user in config.list_nodes(config_user):
- migrate_keys(config, config_user + [user])
+ config_engineid = config_base + ['engineid']
+ if config.exists(config_engineid):
+ tmp = config.return_value(config_engineid)
+ if tmp.startswith(prefix):
+ tmp = tmp.replace(prefix, '')
+ config.set(config_engineid, value=tmp)
- config_trap = config_base + ['trap-target']
- if config.exists(config_trap):
- for trap in config.list_nodes(config_trap):
- migrate_keys(config, config_trap + [trap])
+ config_user = config_base + ['user']
+ if config.exists(config_user):
+ for user in config.list_nodes(config_user):
+ migrate_keys(config, config_user + [user])
- 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)
+ config_trap = config_base + ['trap-target']
+ if config.exists(config_trap):
+ for trap in config.list_nodes(config_trap):
+ migrate_keys(config, config_trap + [trap])
diff --git a/src/migration-scripts/snmp/2-to-3 b/src/migration-scripts/snmp/2-to-3
index ab9b5dcba..6d828b619 100755..100644
--- a/src/migration-scripts/snmp/2-to-3
+++ b/src/migration-scripts/snmp/2-to-3
@@ -1,18 +1,17 @@
-#!/usr/bin/env python3
+# Copyright 2024 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
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
#
-# 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# T4857: Implement FRR SNMP recomendations
# cli changes from:
@@ -20,35 +19,15 @@
# To
# set service snmp oid-enable ip-forward
-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 = ['service snmp']
-config = ConfigTree(config_file)
-
-if not config.exists(base):
- # Nothing to do
- exit(0)
-
-if config.exists(base + ['oid-enable']):
- config.delete(base + ['oid-enable'])
- config.set(base + ['oid-enable'], 'ip-forward')
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ # Nothing to do
+ return
-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)
+ if config.exists(base + ['oid-enable']):
+ config.delete(base + ['oid-enable'])
+ config.set(base + ['oid-enable'], 'ip-forward')
diff --git a/src/migration-scripts/ssh/0-to-1 b/src/migration-scripts/ssh/0-to-1
index 2595599ac..65b68f509 100755..100644
--- a/src/migration-scripts/ssh/0-to-1
+++ b/src/migration-scripts/ssh/0-to-1
@@ -1,32 +1,26 @@
-#!/usr/bin/env python3
+# 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
+# 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, see <http://www.gnu.org/licenses/>.
# Delete "service ssh allow-root" option
-import sys
-
from vyos.configtree import ConfigTree
-if len(sys.argv) < 2:
- print("Must specify file name!")
- sys.exit(1)
-
-file_name = sys.argv[1]
-
-with open(file_name, 'r') as f:
- config_file = f.read()
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(['service', 'ssh', 'allow-root']):
+ # Nothing to do
+ return
-config = ConfigTree(config_file)
-
-if not config.exists(['service', 'ssh', 'allow-root']):
- # Nothing to do
- sys.exit(0)
-else:
# Delete node with abandoned command
config.delete(['service', 'ssh', 'allow-root'])
-
- 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))
- sys.exit(1)
diff --git a/src/migration-scripts/ssh/1-to-2 b/src/migration-scripts/ssh/1-to-2
index 79d65d7d4..b601db3b4 100755..100644
--- a/src/migration-scripts/ssh/1-to-2
+++ b/src/migration-scripts/ssh/1-to-2
@@ -1,81 +1,63 @@
-#!/usr/bin/env python3
+# Copyright 2020-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2020-2022 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 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# VyOS 1.2 crux allowed configuring a lower or upper case loglevel. This
# is no longer supported as the input data is validated and will lead to
# an error. If user specifies an upper case logleve, make it lowercase
-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()
-
base = ['service', 'ssh']
-config = ConfigTree(config_file)
-
-if not config.exists(base):
- # Nothing to do
- exit(0)
-
-path_loglevel = base + ['loglevel']
-if config.exists(path_loglevel):
- # red in configured loglevel and convert it to lower case
- tmp = config.return_value(path_loglevel).lower()
- # VyOS 1.2 had no proper value validation on the CLI thus the
- # user could use any arbitrary values - sanitize them
- if tmp not in ['quiet', 'fatal', 'error', 'info', 'verbose']:
- tmp = 'info'
- config.set(path_loglevel, value=tmp)
-
-# T4273: migrate ssh cipher list to multi node
-path_ciphers = base + ['ciphers']
-if config.exists(path_ciphers):
- tmp = []
- # get curtrent cipher list - comma delimited
- for cipher in config.return_values(path_ciphers):
- tmp.extend(cipher.split(','))
- # delete old cipher suite representation
- config.delete(path_ciphers)
-
- for cipher in tmp:
- config.set(path_ciphers, value=cipher, replace=False)
-
-# T4273: migrate ssh key-exchange list to multi node
-path_kex = base + ['key-exchange']
-if config.exists(path_kex):
- tmp = []
- # get curtrent cipher list - comma delimited
- for kex in config.return_values(path_kex):
- tmp.extend(kex.split(','))
- # delete old cipher suite representation
- config.delete(path_kex)
-
- for kex in tmp:
- config.set(path_kex, value=kex, replace=False)
-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 migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ # Nothing to do
+ return
+
+ path_loglevel = base + ['loglevel']
+ if config.exists(path_loglevel):
+ # red in configured loglevel and convert it to lower case
+ tmp = config.return_value(path_loglevel).lower()
+ # VyOS 1.2 had no proper value validation on the CLI thus the
+ # user could use any arbitrary values - sanitize them
+ if tmp not in ['quiet', 'fatal', 'error', 'info', 'verbose']:
+ tmp = 'info'
+ config.set(path_loglevel, value=tmp)
+
+ # T4273: migrate ssh cipher list to multi node
+ path_ciphers = base + ['ciphers']
+ if config.exists(path_ciphers):
+ tmp = []
+ # get curtrent cipher list - comma delimited
+ for cipher in config.return_values(path_ciphers):
+ tmp.extend(cipher.split(','))
+ # delete old cipher suite representation
+ config.delete(path_ciphers)
+
+ for cipher in tmp:
+ config.set(path_ciphers, value=cipher, replace=False)
+
+ # T4273: migrate ssh key-exchange list to multi node
+ path_kex = base + ['key-exchange']
+ if config.exists(path_kex):
+ tmp = []
+ # get curtrent cipher list - comma delimited
+ for kex in config.return_values(path_kex):
+ tmp.extend(kex.split(','))
+ # delete old cipher suite representation
+ config.delete(path_kex)
+
+ for kex in tmp:
+ config.set(path_kex, value=kex, replace=False)
diff --git a/src/migration-scripts/sstp/0-to-1 b/src/migration-scripts/sstp/0-to-1
index 150127aaf..1bd7d6c6b 100755..100644
--- a/src/migration-scripts/sstp/0-to-1
+++ b/src/migration-scripts/sstp/0-to-1
@@ -1,19 +1,17 @@
-#!/usr/bin/env python3
+# Copyright 2020-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2020-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; either
+# version 2.1 of the License, or (at your option) any later version.
#
-# 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
+# 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/>.
# - migrate from "service sstp-server" to "vpn sstp"
# - remove primary/secondary identifier from nameserver
@@ -23,25 +21,15 @@
# - do not migrate radius server req-limit, use default of unlimited
# - migrate SSL certificate path
-import sys
-
from vyos.configtree import ConfigTree
-if len(sys.argv) < 2:
- print("Must specify file name!")
- sys.exit(1)
-
-file_name = sys.argv[1]
+old_base = ['service', 'sstp-server']
-with open(file_name, 'r') as f:
- config_file = f.read()
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(old_base):
+ # Nothing to do
+ return
-config = ConfigTree(config_file)
-old_base = ['service', 'sstp-server']
-if not config.exists(old_base):
- # Nothing to do
- sys.exit(0)
-else:
# ensure new base path exists
if not config.exists(['vpn']):
config.set(['vpn'])
@@ -119,10 +107,3 @@ else:
if config.exists(new_ssl + ['server-key']):
config.rename(new_ssl + ['server-key'], 'key-file')
-
- 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))
- sys.exit(1)
diff --git a/src/migration-scripts/sstp/1-to-2 b/src/migration-scripts/sstp/1-to-2
index f7ecbb6d4..2349e3c9f 100755..100644
--- a/src/migration-scripts/sstp/1-to-2
+++ b/src/migration-scripts/sstp/1-to-2
@@ -1,45 +1,35 @@
-#!/usr/bin/env python3
+# Copyright 2020-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# 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 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# - migrate relative path SSL certificate to absolute path, as certs are only
# allowed to stored in /config/user-data/sstp/ this is pretty straight
# forward move. Delete certificates from source directory
import os
-import sys
from shutil import copy2
from stat import S_IRUSR, S_IWUSR, S_IRGRP, S_IROTH
from vyos.configtree import ConfigTree
-if len(sys.argv) < 2:
- print("Must specify file name!")
- sys.exit(1)
-
-file_name = sys.argv[1]
+base_path = ['vpn', 'sstp', 'ssl']
-with open(file_name, 'r') as f:
- config_file = f.read()
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(base_path):
+ # Nothing to do
+ return
-config = ConfigTree(config_file)
-base_path = ['vpn', 'sstp', 'ssl']
-if not config.exists(base_path):
- # Nothing to do
- sys.exit(0)
-else:
cert_path_old ='/config/user-data/sstp/'
cert_path_new ='/config/auth/sstp/'
@@ -101,10 +91,3 @@ else:
# check if old certificate directory exists but is empty
if os.path.isdir(cert_path_old) and not os.listdir(cert_path_old):
os.rmdir(cert_path_old)
-
- 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))
- sys.exit(1)
diff --git a/src/migration-scripts/sstp/2-to-3 b/src/migration-scripts/sstp/2-to-3
index 245db7ad6..4255a896e 100755..100644
--- a/src/migration-scripts/sstp/2-to-3
+++ b/src/migration-scripts/sstp/2-to-3
@@ -1,41 +1,30 @@
-#!/usr/bin/env python3
+# Copyright 2020-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# 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 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# - Rename SSTP ppp-settings node to ppp-options to make use of a common
# Jinja Template to render Accel-PPP services
from vyos.configtree import ConfigTree
-from sys import argv
-from sys import exit
-
-if len(argv) < 2:
- print("Must specify file name!")
- exit(1)
-file_name = argv[1]
+base_path = ['vpn', 'sstp']
-with open(file_name, 'r') as f:
- config_file = f.read()
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(base_path):
+ # Nothing to do
+ return
-config = ConfigTree(config_file)
-base_path = ['vpn', 'sstp']
-if not config.exists(base_path):
- # Nothing to do
- exit(0)
-else:
if config.exists(base_path + ['ppp-settings']):
config.rename(base_path + ['ppp-settings'], 'ppp-options')
@@ -68,11 +57,3 @@ else:
config_nw_settings = base_path + ['network-settings']
if config.exists(config_nw_settings):
config.delete(config_nw_settings)
-
- 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/sstp/3-to-4 b/src/migration-scripts/sstp/3-to-4
index 5b7757e60..fd10985de 100755..100644
--- a/src/migration-scripts/sstp/3-to-4
+++ b/src/migration-scripts/sstp/3-to-4
@@ -1,25 +1,22 @@
-#!/usr/bin/env python3
+# Copyright 2021-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2021-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; either
+# version 2.1 of the License, or (at your option) any later version.
#
-# 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# - Update SSL to use PKI configuration
import os
-from sys import argv
-from sys import exit
from vyos.configtree import ConfigTree
from vyos.pki import load_certificate
from vyos.pki import load_private_key
@@ -27,109 +24,93 @@ from vyos.pki import encode_certificate
from vyos.pki import encode_private_key
from vyos.utils.process import run
-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 = ['vpn', 'sstp']
pki_base = ['pki']
-if not config.exists(base):
- exit(0)
-
AUTH_DIR = '/config/auth'
def wrapped_pem_to_config_value(pem):
return "".join(pem.strip().split("\n")[1:-1])
-if not config.exists(base + ['ssl']):
- exit(0)
-
-x509_base = base + ['ssl']
-pki_name = 'sstp'
-
-if not config.exists(pki_base + ['ca']):
- config.set(pki_base + ['ca'])
- config.set_tag(pki_base + ['ca'])
-
-if not config.exists(pki_base + ['certificate']):
- config.set(pki_base + ['certificate'])
- config.set_tag(pki_base + ['certificate'])
-
-if config.exists(x509_base + ['ca-cert-file']):
- cert_file = config.return_value(x509_base + ['ca-cert-file'])
- cert_path = os.path.join(AUTH_DIR, cert_file)
- cert = None
-
- if os.path.isfile(cert_path):
- if not os.access(cert_path, os.R_OK):
- run(f'sudo chmod 644 {cert_path}')
-
- with open(cert_path, 'r') as f:
- cert_data = f.read()
- cert = load_certificate(cert_data, wrap_tags=False)
-
- if cert:
- cert_pem = encode_certificate(cert)
- config.set(pki_base + ['ca', pki_name, 'certificate'], value=wrapped_pem_to_config_value(cert_pem))
- config.set(x509_base + ['ca-certificate'], value=pki_name)
- else:
- print(f'Failed to migrate CA certificate on sstp config')
-
- config.delete(x509_base + ['ca-cert-file'])
-
-if config.exists(x509_base + ['cert-file']):
- cert_file = config.return_value(x509_base + ['cert-file'])
- cert_path = os.path.join(AUTH_DIR, cert_file)
- cert = None
-
- if os.path.isfile(cert_path):
- if not os.access(cert_path, os.R_OK):
- run(f'sudo chmod 644 {cert_path}')
-
- with open(cert_path, 'r') as f:
- cert_data = f.read()
- cert = load_certificate(cert_data, wrap_tags=False)
-
- if cert:
- cert_pem = encode_certificate(cert)
- config.set(pki_base + ['certificate', pki_name, 'certificate'], value=wrapped_pem_to_config_value(cert_pem))
- config.set(x509_base + ['certificate'], value=pki_name)
- else:
- print(f'Failed to migrate certificate on sstp config')
-
- config.delete(x509_base + ['cert-file'])
-
-if config.exists(x509_base + ['key-file']):
- key_file = config.return_value(x509_base + ['key-file'])
- key_path = os.path.join(AUTH_DIR, key_file)
- key = None
-
- if os.path.isfile(key_path):
- if not os.access(key_path, os.R_OK):
- run(f'sudo chmod 644 {key_path}')
-
- with open(key_path, 'r') as f:
- key_data = f.read()
- key = load_private_key(key_data, passphrase=None, wrap_tags=False)
-
- if key:
- key_pem = encode_private_key(key, passphrase=None)
- config.set(pki_base + ['certificate', pki_name, 'private', 'key'], value=wrapped_pem_to_config_value(key_pem))
- else:
- print(f'Failed to migrate private key on sstp config')
-
- config.delete(x509_base + ['key-file'])
-
-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 migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ return
+
+ if not config.exists(base + ['ssl']):
+ return
+
+ x509_base = base + ['ssl']
+ pki_name = 'sstp'
+
+ if not config.exists(pki_base + ['ca']):
+ config.set(pki_base + ['ca'])
+ config.set_tag(pki_base + ['ca'])
+
+ if not config.exists(pki_base + ['certificate']):
+ config.set(pki_base + ['certificate'])
+ config.set_tag(pki_base + ['certificate'])
+
+ if config.exists(x509_base + ['ca-cert-file']):
+ cert_file = config.return_value(x509_base + ['ca-cert-file'])
+ cert_path = os.path.join(AUTH_DIR, cert_file)
+ cert = None
+
+ if os.path.isfile(cert_path):
+ if not os.access(cert_path, os.R_OK):
+ run(f'sudo chmod 644 {cert_path}')
+
+ with open(cert_path, 'r') as f:
+ cert_data = f.read()
+ cert = load_certificate(cert_data, wrap_tags=False)
+
+ if cert:
+ cert_pem = encode_certificate(cert)
+ config.set(pki_base + ['ca', pki_name, 'certificate'], value=wrapped_pem_to_config_value(cert_pem))
+ config.set(x509_base + ['ca-certificate'], value=pki_name)
+ else:
+ print(f'Failed to migrate CA certificate on sstp config')
+
+ config.delete(x509_base + ['ca-cert-file'])
+
+ if config.exists(x509_base + ['cert-file']):
+ cert_file = config.return_value(x509_base + ['cert-file'])
+ cert_path = os.path.join(AUTH_DIR, cert_file)
+ cert = None
+
+ if os.path.isfile(cert_path):
+ if not os.access(cert_path, os.R_OK):
+ run(f'sudo chmod 644 {cert_path}')
+
+ with open(cert_path, 'r') as f:
+ cert_data = f.read()
+ cert = load_certificate(cert_data, wrap_tags=False)
+
+ if cert:
+ cert_pem = encode_certificate(cert)
+ config.set(pki_base + ['certificate', pki_name, 'certificate'], value=wrapped_pem_to_config_value(cert_pem))
+ config.set(x509_base + ['certificate'], value=pki_name)
+ else:
+ print(f'Failed to migrate certificate on sstp config')
+
+ config.delete(x509_base + ['cert-file'])
+
+ if config.exists(x509_base + ['key-file']):
+ key_file = config.return_value(x509_base + ['key-file'])
+ key_path = os.path.join(AUTH_DIR, key_file)
+ key = None
+
+ if os.path.isfile(key_path):
+ if not os.access(key_path, os.R_OK):
+ run(f'sudo chmod 644 {key_path}')
+
+ with open(key_path, 'r') as f:
+ key_data = f.read()
+ key = load_private_key(key_data, passphrase=None, wrap_tags=False)
+
+ if key:
+ key_pem = encode_private_key(key, passphrase=None)
+ config.set(pki_base + ['certificate', pki_name, 'private', 'key'], value=wrapped_pem_to_config_value(key_pem))
+ else:
+ print(f'Failed to migrate private key on sstp config')
+
+ config.delete(x509_base + ['key-file'])
diff --git a/src/migration-scripts/sstp/4-to-5 b/src/migration-scripts/sstp/4-to-5
index 6907240a0..254e828af 100755..100644
--- a/src/migration-scripts/sstp/4-to-5
+++ b/src/migration-scripts/sstp/4-to-5
@@ -1,59 +1,41 @@
-#!/usr/bin/env python3
+# Copyright 2023-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2023-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; either
+# version 2.1 of the License, or (at your option) any later version.
#
-# 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# - move all pool to named pools
# 'subnet' migrate to namedpool 'default-subnet-pool'
# 'default-subnet-pool' is the next pool for 'default-range-pool'
-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 = ['vpn', 'sstp']
pool_base = base + ['client-ip-pool']
-if not config.exists(base):
- exit(0)
-if not config.exists(pool_base):
- exit(0)
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ return
-range_pool_name = 'default-range-pool'
+ if not config.exists(pool_base):
+ return
-if config.exists(pool_base + ['subnet']):
- default_pool = range_pool_name
- for subnet in config.return_values(pool_base + ['subnet']):
- config.set(pool_base + [range_pool_name, 'range'], value=subnet, replace=False)
- config.delete(pool_base + ['subnet'])
- config.set(base + ['default-pool'], value=default_pool)
-# format as tag node
-config.set_tag(pool_base)
+ range_pool_name = 'default-range-pool'
-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)
+ if config.exists(pool_base + ['subnet']):
+ default_pool = range_pool_name
+ for subnet in config.return_values(pool_base + ['subnet']):
+ config.set(pool_base + [range_pool_name, 'range'], value=subnet, replace=False)
+ config.delete(pool_base + ['subnet'])
+ config.set(base + ['default-pool'], value=default_pool)
+ # format as tag node
+ config.set_tag(pool_base)
diff --git a/src/migration-scripts/sstp/5-to-6 b/src/migration-scripts/sstp/5-to-6
index 43b99044d..fc3cc29b2 100755..100644
--- a/src/migration-scripts/sstp/5-to-6
+++ b/src/migration-scripts/sstp/5-to-6
@@ -1,58 +1,40 @@
-#!/usr/bin/env python3
+# Copyright 2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 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; either
+# version 2.1 of the License, or (at your option) any later version.
#
-# 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# Migrating to named ipv6 pools
-from sys import argv
-from sys import exit
from vyos.configtree import ConfigTree
-if len(argv) < 2:
- print("Must specify file name!")
- exit(1)
+base = ['vpn', 'sstp']
+pool_base = base + ['client-ipv6-pool']
-file_name = argv[1]
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ return
-with open(file_name, 'r') as f:
- config_file = f.read()
+ if not config.exists(pool_base):
+ return
-config = ConfigTree(config_file)
-base = ['vpn', 'sstp']
-pool_base = base + ['client-ipv6-pool']
-if not config.exists(base):
- exit(0)
-
-if not config.exists(pool_base):
- exit(0)
-
-ipv6_pool_name = 'ipv6-pool'
-config.copy(pool_base, pool_base + [ipv6_pool_name])
-
-if config.exists(pool_base + ['prefix']):
- config.delete(pool_base + ['prefix'])
- config.set(base + ['default-ipv6-pool'], value=ipv6_pool_name)
-if config.exists(pool_base + ['delegate']):
- config.delete(pool_base + ['delegate'])
-
-# format as tag node
-config.set_tag(pool_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)
+ ipv6_pool_name = 'ipv6-pool'
+ config.copy(pool_base, pool_base + [ipv6_pool_name])
+
+ if config.exists(pool_base + ['prefix']):
+ config.delete(pool_base + ['prefix'])
+ config.set(base + ['default-ipv6-pool'], value=ipv6_pool_name)
+ if config.exists(pool_base + ['delegate']):
+ config.delete(pool_base + ['delegate'])
+
+ # format as tag node
+ config.set_tag(pool_base)
diff --git a/src/migration-scripts/system/10-to-11 b/src/migration-scripts/system/10-to-11
index 5d662af40..76d7f23cb 100755..100644
--- a/src/migration-scripts/system/10-to-11
+++ b/src/migration-scripts/system/10-to-11
@@ -1,36 +1,32 @@
-#!/usr/bin/env python3
+# 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
+# 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, see <http://www.gnu.org/licenses/>.
# Operator accounts have been deprecated due to a security issue. Those accounts
# will be converted to regular admin accounts.
-import sys
from vyos.configtree import ConfigTree
-if len(sys.argv) < 2:
- print("Must specify file name!")
- sys.exit(1)
-
-file_name = sys.argv[1]
-
-with open(file_name, 'r') as f:
- config_file = f.read()
-
-config = ConfigTree(config_file)
base_level = ['system', 'login', 'user']
-if not config.exists(base_level):
- # Nothing to do, which shouldn't happen anyway
- # only if you wipe the config and reboot.
- sys.exit(0)
-else:
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(base_level):
+ # Nothing to do, which shouldn't happen anyway
+ # only if you wipe the config and reboot.
+ return
+
for user in config.list_nodes(base_level):
if config.exists(base_level + [user, 'level']):
if config.return_value(base_level + [user, 'level']) == 'operator':
config.set(base_level + [user, 'level'], value="admin", replace=True)
-
- try:
- open(file_name,'w').write(config.to_string())
-
- except OSError as e:
- print("Failed to save the modified config: {}".format(e))
- sys.exit(1)
diff --git a/src/migration-scripts/system/11-to-12 b/src/migration-scripts/system/11-to-12
index 880ab56dc..71c359b7e 100755..100644
--- a/src/migration-scripts/system/11-to-12
+++ b/src/migration-scripts/system/11-to-12
@@ -1,28 +1,32 @@
-#!/usr/bin/env python3
+# 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
+# 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, see <http://www.gnu.org/licenses/>.
# Unclutter RADIUS configuration
#
# Move radius-server top level tag nodes to a regular node which allows us
# to specify additional general features for the RADIUS client.
-import sys
from vyos.configtree import ConfigTree
-if len(sys.argv) < 2:
- print("Must specify file name!")
- sys.exit(1)
-
-file_name = sys.argv[1]
+cfg_base = ['system', 'login']
-with open(file_name, 'r') as f:
- config_file = f.read()
+def migrate(config: ConfigTree) -> None:
+ if not (config.exists(cfg_base + ['radius-server']) or config.exists(cfg_base + ['radius-source-address'])):
+ # Nothing to do
+ return
-config = ConfigTree(config_file)
-cfg_base = ['system', 'login']
-if not (config.exists(cfg_base + ['radius-server']) or config.exists(cfg_base + ['radius-source-address'])):
- # Nothing to do
- sys.exit(0)
-else:
#
# Migrate "system login radius-source-address" to "system login radius"
#
@@ -63,10 +67,3 @@ else:
# delete top level tag node
if config.exists(cfg_base + ['radius-server']):
config.delete(cfg_base + ['radius-server'])
-
- 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))
- sys.exit(1)
diff --git a/src/migration-scripts/system/12-to-13 b/src/migration-scripts/system/12-to-13
index e6c4e3802..014edba91 100755..100644
--- a/src/migration-scripts/system/12-to-13
+++ b/src/migration-scripts/system/12-to-13
@@ -1,47 +1,44 @@
-#!/usr/bin/env python3
+# 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
+# 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, see <http://www.gnu.org/licenses/>.
# converts 'set system syslog host <address>:<port>'
# to 'set system syslog host <address> port <port>'
-import sys
import re
from vyos.configtree import ConfigTree
-if len(sys.argv) < 2:
- print("Must specify file name!")
- sys.exit(1)
-
-file_name = sys.argv[1]
-
-with open(file_name, 'r') as f:
- config_file = f.read()
-
-config = ConfigTree(config_file)
cbase = ['system', 'syslog', 'host']
-if not config.exists(cbase):
- sys.exit(0)
-
-for host in config.list_nodes(cbase):
- if re.search(':[0-9]{1,5}$',host):
- h = re.search('^[a-zA-Z\-0-9\.]+', host).group(0)
- p = re.sub(':', '', re.search(':[0-9]+$', host).group(0))
- config.set(cbase + [h])
- config.set(cbase + [h, 'port'], value=p)
- for fac in config.list_nodes(cbase + [host, 'facility']):
- config.set(cbase + [h, 'facility', fac])
- config.set_tag(cbase + [h, 'facility'])
- if config.exists(cbase + [host, 'facility', fac, 'protocol']):
- proto = config.return_value(cbase + [host, 'facility', fac, 'protocol'])
- config.set(cbase + [h, 'facility', fac, 'protocol'], value=proto)
- if config.exists(cbase + [host, 'facility', fac, 'level']):
- lvl = config.return_value(cbase + [host, 'facility', fac, 'level'])
- config.set(cbase + [h, 'facility', fac, 'level'], value=lvl)
- config.delete(cbase + [host])
-
-try:
- open(file_name,'w').write(config.to_string())
-except OSError as e:
- print("Failed to save the modified config: {}".format(e))
- sys.exit(1)
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(cbase):
+ return
+
+ for host in config.list_nodes(cbase):
+ if re.search(':[0-9]{1,5}$',host):
+ h = re.search('^[a-zA-Z\-0-9\.]+', host).group(0)
+ p = re.sub(':', '', re.search(':[0-9]+$', host).group(0))
+ config.set(cbase + [h])
+ config.set(cbase + [h, 'port'], value=p)
+ for fac in config.list_nodes(cbase + [host, 'facility']):
+ config.set(cbase + [h, 'facility', fac])
+ config.set_tag(cbase + [h, 'facility'])
+ if config.exists(cbase + [host, 'facility', fac, 'protocol']):
+ proto = config.return_value(cbase + [host, 'facility', fac, 'protocol'])
+ config.set(cbase + [h, 'facility', fac, 'protocol'], value=proto)
+ if config.exists(cbase + [host, 'facility', fac, 'level']):
+ lvl = config.return_value(cbase + [host, 'facility', fac, 'level'])
+ config.set(cbase + [h, 'facility', fac, 'level'], value=lvl)
+ config.delete(cbase + [host])
diff --git a/src/migration-scripts/system/13-to-14 b/src/migration-scripts/system/13-to-14
index 5b781158b..fbbecbcd3 100755..100644
--- a/src/migration-scripts/system/13-to-14
+++ b/src/migration-scripts/system/13-to-14
@@ -1,4 +1,17 @@
-#!/usr/bin/env python3
+# 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
+# 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, see <http://www.gnu.org/licenses/>.
# Fixup non existent time-zones. Some systems have time-zone set to: Los*
# (Los_Angeles), Den* (Denver), New* (New_York) ... but those are no real IANA
@@ -9,27 +22,18 @@
# Migrate all configured timezones to real IANA assigned timezones!
import re
-import sys
from vyos.configtree import ConfigTree
from vyos.utils.process import cmd
-if len(sys.argv) < 2:
- print("Must specify file name!")
- sys.exit(1)
-
-file_name = sys.argv[1]
+tz_base = ['system', 'time-zone']
-with open(file_name, 'r') as f:
- config_file = f.read()
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(tz_base):
+ # Nothing to do
+ return
-config = ConfigTree(config_file)
-tz_base = ['system', 'time-zone']
-if not config.exists(tz_base):
- # Nothing to do
- sys.exit(0)
-else:
tz = config.return_value(tz_base)
# retrieve all valid timezones
@@ -61,10 +65,3 @@ else:
# replace timezone data is required
config.set(tz_base, value=tz)
-
- 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))
- sys.exit(1)
diff --git a/src/migration-scripts/system/14-to-15 b/src/migration-scripts/system/14-to-15
index feaac37de..281809460 100755..100644
--- a/src/migration-scripts/system/14-to-15
+++ b/src/migration-scripts/system/14-to-15
@@ -1,40 +1,37 @@
-#!/usr/bin/env python3
+# 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
+# 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, see <http://www.gnu.org/licenses/>.
+
# Delete 'system ipv6 blacklist' option as the IPv6 module can no longer be
# blacklisted as it is required by e.g. WireGuard and thus will always be
# loaded.
import os
-import sys
ipv6_blacklist_file = '/etc/modprobe.d/vyatta_blacklist_ipv6.conf'
from vyos.configtree import ConfigTree
-if len(sys.argv) < 2:
- print("Must specify file name!")
- sys.exit(1)
-
-file_name = sys.argv[1]
+ip_base = ['system', 'ipv6']
-with open(file_name, 'r') as f:
- config_file = f.read()
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(ip_base):
+ # Nothing to do
+ return
-config = ConfigTree(config_file)
-ip_base = ['system', 'ipv6']
-if not config.exists(ip_base):
- # Nothing to do
- sys.exit(0)
-else:
# delete 'system ipv6 blacklist' node
if config.exists(ip_base + ['blacklist']):
config.delete(ip_base + ['blacklist'])
if os.path.isfile(ipv6_blacklist_file):
os.unlink(ipv6_blacklist_file)
-
- 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))
- sys.exit(1)
diff --git a/src/migration-scripts/system/15-to-16 b/src/migration-scripts/system/15-to-16
index 2944cdb1e..7db042930 100755..100644
--- a/src/migration-scripts/system/15-to-16
+++ b/src/migration-scripts/system/15-to-16
@@ -1,36 +1,32 @@
-#!/usr/bin/env python3
+# Copyright 2019-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Make 'system options reboot-on-panic' valueless
+# 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, see <http://www.gnu.org/licenses/>.
-import sys
+# Make 'system options reboot-on-panic' valueless
from vyos.configtree import ConfigTree
-if len(sys.argv) < 2:
- print("Must specify file name!")
- sys.exit(1)
-
-file_name = sys.argv[1]
+base = ['system', 'options']
-with open(file_name, 'r') as f:
- config_file = f.read()
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ # Nothing to do
+ return
-config = ConfigTree(config_file)
-base = ['system', 'options']
-if not config.exists(base):
- # Nothing to do
- sys.exit(0)
-else:
if config.exists(base + ['reboot-on-panic']):
reboot = config.return_value(base + ['reboot-on-panic'])
config.delete(base + ['reboot-on-panic'])
# create new valueless node if action was true
if reboot == "true":
config.set(base + ['reboot-on-panic'])
-
- 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))
- sys.exit(1)
diff --git a/src/migration-scripts/system/16-to-17 b/src/migration-scripts/system/16-to-17
index afa171a9b..9fb86af88 100755..100644
--- a/src/migration-scripts/system/16-to-17
+++ b/src/migration-scripts/system/16-to-17
@@ -1,18 +1,17 @@
-#!/usr/bin/env python3
+# Copyright 2020-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2020-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; either
+# version 2.1 of the License, or (at your option) any later version.
#
-# 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# * remove "system login user <user> group" node, Why should be add a user to a
# 3rd party group when the system is fully managed by CLI?
@@ -20,35 +19,18 @@
# This is the only privilege level left and also the default, what is the
# sense in keeping this orphaned node?
-import sys
-
from vyos.configtree import ConfigTree
-if len(sys.argv) < 2:
- print("Must specify file name!")
- sys.exit(1)
-
-file_name = sys.argv[1]
+base = ['system', 'login', 'user']
-with open(file_name, 'r') as f:
- config_file = f.read()
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ # Nothing to do
+ return
-config = ConfigTree(config_file)
-base = ['system', 'login', 'user']
-if not config.exists(base):
- # Nothing to do
- sys.exit(0)
-else:
for user in config.list_nodes(base):
if config.exists(base + [user, 'group']):
config.delete(base + [user, 'group'])
if config.exists(base + [user, 'level']):
config.delete(base + [user, 'level'])
-
- 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))
- sys.exit(1)
diff --git a/src/migration-scripts/system/17-to-18 b/src/migration-scripts/system/17-to-18
index f6adebb06..323ef4e65 100755..100644
--- a/src/migration-scripts/system/17-to-18
+++ b/src/migration-scripts/system/17-to-18
@@ -1,42 +1,32 @@
-#!/usr/bin/env python3
+# Copyright 2020-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# 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 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# remove "system console netconsole"
# remove "system console device <device> modem"
import os
-import sys
from vyos.configtree import ConfigTree
-if len(sys.argv) < 2:
- print("Must specify file name!")
- sys.exit(1)
-
-file_name = sys.argv[1]
+base = ['system', 'console']
-with open(file_name, 'r') as f:
- config_file = f.read()
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ # Nothing to do
+ return
-config = ConfigTree(config_file)
-base = ['system', 'console']
-if not config.exists(base):
- # Nothing to do
- sys.exit(0)
-else:
# remove "system console netconsole" (T2561)
if config.exists(base + ['netconsole']):
config.delete(base + ['netconsole'])
@@ -67,10 +57,3 @@ else:
config.copy(dev_path, base + ['device', usb_device])
# Delete old USB node from config
config.delete(dev_path)
-
- 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))
- sys.exit(1)
diff --git a/src/migration-scripts/system/18-to-19 b/src/migration-scripts/system/18-to-19
index fad1d17a4..5d9788d70 100755..100644
--- a/src/migration-scripts/system/18-to-19
+++ b/src/migration-scripts/system/18-to-19
@@ -1,102 +1,81 @@
-#!/usr/bin/env python3
+# Copyright 2020-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# 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 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,
+# 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 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/>.
+# 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, see <http://www.gnu.org/licenses/>.
# migrate disable-dhcp-nameservers (boolean) to name-servers-dhcp <interface>
# if disable-dhcp-nameservers is set, just remove it
# else retrieve all interface names that have configured dhcp(v6) address and
# add them to the new name-servers-dhcp node
-from sys import argv, exit
from vyos.ifconfig import Interface
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 = ['system']
-if not config.exists(base):
- # Nothing to do
- exit(0)
-
-if config.exists(base + ['disable-dhcp-nameservers']):
- config.delete(base + ['disable-dhcp-nameservers'])
-else:
- dhcp_interfaces = []
-
- # go through all interfaces searching for 'address dhcp(v6)?'
- for sect in Interface.sections():
- sect_base = ['interfaces', sect]
-
- if not config.exists(sect_base):
- continue
- for intf in config.list_nodes(sect_base):
- intf_base = sect_base + [intf]
-
- # try without vlans
- if config.exists(intf_base + ['address']):
- for addr in config.return_values(intf_base + ['address']):
- if addr in ['dhcp', 'dhcpv6']:
- dhcp_interfaces.append(intf)
-
- # try vif
- if config.exists(intf_base + ['vif']):
- for vif in config.list_nodes(intf_base + ['vif']):
- vif_base = intf_base + ['vif', vif]
- if config.exists(vif_base + ['address']):
- for addr in config.return_values(vif_base + ['address']):
- if addr in ['dhcp', 'dhcpv6']:
- dhcp_interfaces.append(f'{intf}.{vif}')
-
- # try vif-s
- if config.exists(intf_base + ['vif-s']):
- for vif_s in config.list_nodes(intf_base + ['vif-s']):
- vif_s_base = intf_base + ['vif-s', vif_s]
- if config.exists(vif_s_base + ['address']):
- for addr in config.return_values(vif_s_base + ['address']):
- if addr in ['dhcp', 'dhcpv6']:
- dhcp_interfaces.append(f'{intf}.{vif_s}')
-
- # try vif-c
- if config.exists(intf_base + ['vif-c']):
- for vif_c in config.list_nodes(vif_s_base + ['vif-c']):
- vif_c_base = vif_s_base + ['vif-c', vif_c]
- if config.exists(vif_c_base + ['address']):
- for addr in config.return_values(vif_c_base + ['address']):
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ # Nothing to do
+ return
+
+ if config.exists(base + ['disable-dhcp-nameservers']):
+ config.delete(base + ['disable-dhcp-nameservers'])
+ else:
+ dhcp_interfaces = []
+
+ # go through all interfaces searching for 'address dhcp(v6)?'
+ for sect in Interface.sections():
+ sect_base = ['interfaces', sect]
+
+ if not config.exists(sect_base):
+ continue
+
+ for intf in config.list_nodes(sect_base):
+ intf_base = sect_base + [intf]
+
+ # try without vlans
+ if config.exists(intf_base + ['address']):
+ for addr in config.return_values(intf_base + ['address']):
+ if addr in ['dhcp', 'dhcpv6']:
+ dhcp_interfaces.append(intf)
+
+ # try vif
+ if config.exists(intf_base + ['vif']):
+ for vif in config.list_nodes(intf_base + ['vif']):
+ vif_base = intf_base + ['vif', vif]
+ if config.exists(vif_base + ['address']):
+ for addr in config.return_values(vif_base + ['address']):
if addr in ['dhcp', 'dhcpv6']:
- dhcp_interfaces.append(f'{intf}.{vif_s}.{vif_c}')
-
- # set new config nodes
- for intf in dhcp_interfaces:
- config.set(base + ['name-servers-dhcp'], value=intf, replace=False)
-
-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)
-
-exit(0)
+ dhcp_interfaces.append(f'{intf}.{vif}')
+
+ # try vif-s
+ if config.exists(intf_base + ['vif-s']):
+ for vif_s in config.list_nodes(intf_base + ['vif-s']):
+ vif_s_base = intf_base + ['vif-s', vif_s]
+ if config.exists(vif_s_base + ['address']):
+ for addr in config.return_values(vif_s_base + ['address']):
+ if addr in ['dhcp', 'dhcpv6']:
+ dhcp_interfaces.append(f'{intf}.{vif_s}')
+
+ # try vif-c
+ if config.exists(intf_base + ['vif-c']):
+ for vif_c in config.list_nodes(vif_s_base + ['vif-c']):
+ vif_c_base = vif_s_base + ['vif-c', vif_c]
+ if config.exists(vif_c_base + ['address']):
+ for addr in config.return_values(vif_c_base + ['address']):
+ if addr in ['dhcp', 'dhcpv6']:
+ dhcp_interfaces.append(f'{intf}.{vif_s}.{vif_c}')
+
+ # set new config nodes
+ for intf in dhcp_interfaces:
+ config.set(base + ['name-servers-dhcp'], value=intf, replace=False)
diff --git a/src/migration-scripts/system/19-to-20 b/src/migration-scripts/system/19-to-20
index 177173c50..cb84e11fc 100755..100644
--- a/src/migration-scripts/system/19-to-20
+++ b/src/migration-scripts/system/19-to-20
@@ -1,62 +1,44 @@
-#!/usr/bin/env python3
+# Copyright 2020-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2020-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; either
+# version 2.1 of the License, or (at your option) any later version.
#
-# 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# T3048: remove smp-affinity node from ethernet and use tuned instead
-from sys import exit, argv
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 = ['system', 'options']
base_new = ['system', 'option']
-config = ConfigTree(config_file)
-
-if not config.exists(base):
- # Nothing to do
- exit(0)
-
-if config.exists(base_new):
- for node in config.list_nodes(base):
- config.copy(base + [node], base_new + [node])
-else:
- config.copy(base, base_new)
-config.delete(base)
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ # Nothing to do
+ return
-# Rename "system option beep-if-fully-booted" -> "system option startup-beep"
-base_beep = base_new + ['beep-if-fully-booted']
-if config.exists(base_beep):
- config.rename(base_beep, 'startup-beep')
+ if config.exists(base_new):
+ for node in config.list_nodes(base):
+ config.copy(base + [node], base_new + [node])
+ else:
+ config.copy(base, base_new)
-# Rename "system option ctrl-alt-del-action" -> "system option ctrl-alt-delete"
-base_ctrl_alt_del = base_new + ['ctrl-alt-del-action']
-if config.exists(base_ctrl_alt_del):
- config.rename(base_ctrl_alt_del, 'ctrl-alt-delete')
+ config.delete(base)
+ # Rename "system option beep-if-fully-booted" -> "system option startup-beep"
+ base_beep = base_new + ['beep-if-fully-booted']
+ if config.exists(base_beep):
+ config.rename(base_beep, 'startup-beep')
-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)
+ # Rename "system option ctrl-alt-del-action" -> "system option ctrl-alt-delete"
+ base_ctrl_alt_del = base_new + ['ctrl-alt-del-action']
+ if config.exists(base_ctrl_alt_del):
+ config.rename(base_ctrl_alt_del, 'ctrl-alt-delete')
diff --git a/src/migration-scripts/system/20-to-21 b/src/migration-scripts/system/20-to-21
index 24e042ce2..71c283da6 100755..100644
--- a/src/migration-scripts/system/20-to-21
+++ b/src/migration-scripts/system/20-to-21
@@ -1,46 +1,30 @@
-#!/usr/bin/env python3
+# Copyright 2021-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2021-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; either
+# version 2.1 of the License, or (at your option) any later version.
#
-# 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# T3795: merge "system name-servers-dhcp" into "system name-server"
-from sys import argv
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 = ['system', 'name-servers-dhcp']
-config = ConfigTree(config_file)
-if not config.exists(base):
- # Nothing to do
- exit(0)
-for interface in config.return_values(base):
- config.set(['system', 'name-server'], value=interface, replace=False)
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ # Nothing to do
+ return
-config.delete(base)
+ for interface in config.return_values(base):
+ config.set(['system', 'name-server'], value=interface, replace=False)
-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)
+ config.delete(base)
diff --git a/src/migration-scripts/system/21-to-22 b/src/migration-scripts/system/21-to-22
index 2a1b603c6..0e68a6856 100755..100644
--- a/src/migration-scripts/system/21-to-22
+++ b/src/migration-scripts/system/21-to-22
@@ -1,55 +1,38 @@
-#!/usr/bin/env python3
+# Copyright 2021-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2021-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; either
+# version 2.1 of the License, or (at your option) any later version.
#
-# 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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 sys import exit, argv
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 = ['system', 'sysctl']
-config = ConfigTree(config_file)
-if not config.exists(base):
- # Nothing to do
- exit(0)
-
-for all_custom in ['all', 'custom']:
- if config.exists(base + [all_custom]):
- for key in config.list_nodes(base + [all_custom]):
- tmp = config.return_value(base + [all_custom, key, 'value'])
- config.set(base + ['parameter', key, 'value'], value=tmp)
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ # Nothing to do
+ return
+
+ for all_custom in ['all', 'custom']:
+ if config.exists(base + [all_custom]):
+ for key in config.list_nodes(base + [all_custom]):
+ tmp = config.return_value(base + [all_custom, key, 'value'])
+ config.set(base + ['parameter', key, 'value'], value=tmp)
+ config.set_tag(base + ['parameter'])
+ config.delete(base + [all_custom])
+
+ for ipv4_param in ['net.ipv4.igmp_max_memberships', 'net.ipv4.ipfrag_time']:
+ if config.exists(base + [ipv4_param]):
+ tmp = config.return_value(base + [ipv4_param])
+ config.set(base + ['parameter', ipv4_param, 'value'], value=tmp)
config.set_tag(base + ['parameter'])
- config.delete(base + [all_custom])
-
-for ipv4_param in ['net.ipv4.igmp_max_memberships', 'net.ipv4.ipfrag_time']:
- if config.exists(base + [ipv4_param]):
- tmp = config.return_value(base + [ipv4_param])
- config.set(base + ['parameter', ipv4_param, 'value'], value=tmp)
- config.set_tag(base + ['parameter'])
- config.delete(base + [ipv4_param])
-
-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)
+ config.delete(base + [ipv4_param])
diff --git a/src/migration-scripts/system/22-to-23 b/src/migration-scripts/system/22-to-23
index f83279b88..e49094e4a 100755..100644
--- a/src/migration-scripts/system/22-to-23
+++ b/src/migration-scripts/system/22-to-23
@@ -1,48 +1,31 @@
-#!/usr/bin/env python3
+# Copyright 2022-2024 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
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
#
-# 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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 sys import exit, argv
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 = ['system', 'ipv6']
-config = ConfigTree(config_file)
-
-if not config.exists(base):
- # Nothing to do
- exit(0)
-# T4346: drop support to disbale IPv6 address family within the OS Kernel
-if config.exists(base + ['disable']):
- config.delete(base + ['disable'])
- # IPv6 address family disable was the only CLI option set - we can cleanup
- # the entire tree
- if len(config.list_nodes(base)) == 0:
- config.delete(base)
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ # Nothing to do
+ return
-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)
+ # T4346: drop support to disbale IPv6 address family within the OS Kernel
+ if config.exists(base + ['disable']):
+ config.delete(base + ['disable'])
+ # IPv6 address family disable was the only CLI option set - we can cleanup
+ # the entire tree
+ if len(config.list_nodes(base)) == 0:
+ config.delete(base)
diff --git a/src/migration-scripts/system/23-to-24 b/src/migration-scripts/system/23-to-24
index 1fd61d83b..feb62bc32 100755..100644
--- a/src/migration-scripts/system/23-to-24
+++ b/src/migration-scripts/system/23-to-24
@@ -1,38 +1,28 @@
-#!/usr/bin/env python3
+# Copyright 2022-2024 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
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
#
-# 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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 ipaddress import ip_interface
from ipaddress import ip_address
-from sys import exit, argv
+
from vyos.configtree import ConfigTree
from vyos.template import is_ipv4
-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 = ['protocols', 'static', 'arp']
tmp_base = ['protocols', 'static', 'arp-tmp']
-config = ConfigTree(config_file)
-def fixup_cli(config, path, interface):
+def fixup_cli(config, path, interface, host):
if config.exists(path + ['address']):
for address in config.return_values(path + ['address']):
tmp = ip_interface(address)
@@ -47,41 +37,35 @@ def fixup_cli(config, path, interface):
config.set_tag(iface_path + [interface, 'address'])
continue
-if not config.exists(base):
- # Nothing to do
- exit(0)
-
-# We need a temporary copy of the config tree as the original one needs to be
-# deleted first due to a change iun thge tagNode structure.
-config.copy(base, tmp_base)
-config.delete(base)
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ # Nothing to do
+ return
-for host in config.list_nodes(tmp_base):
- for type in config.list_nodes(['interfaces']):
- for interface in config.list_nodes(['interfaces', type]):
- if_base = ['interfaces', type, interface]
- fixup_cli(config, if_base, interface)
+ # We need a temporary copy of the config tree as the original one needs to be
+ # deleted first due to a change iun thge tagNode structure.
+ config.copy(base, tmp_base)
+ config.delete(base)
- if config.exists(if_base + ['vif']):
- for vif in config.list_nodes(if_base + ['vif']):
- vif_base = ['interfaces', type, interface, 'vif', vif]
- fixup_cli(config, vif_base, f'{interface}.{vif}')
+ for host in config.list_nodes(tmp_base):
+ for type in config.list_nodes(['interfaces']):
+ for interface in config.list_nodes(['interfaces', type]):
+ if_base = ['interfaces', type, interface]
+ fixup_cli(config, if_base, interface, host)
- if config.exists(if_base + ['vif-s']):
- for vif_s in config.list_nodes(if_base + ['vif-s']):
- vif_s_base = ['interfaces', type, interface, 'vif-s', vif_s]
- fixup_cli(config, vif_s_base, f'{interface}.{vif_s}')
+ if config.exists(if_base + ['vif']):
+ for vif in config.list_nodes(if_base + ['vif']):
+ vif_base = ['interfaces', type, interface, 'vif', vif]
+ fixup_cli(config, vif_base, f'{interface}.{vif}', host)
- if config.exists(if_base + ['vif-s', vif_s, 'vif-c']):
- for vif_c in config.list_nodes(if_base + ['vif-s', vif_s, 'vif-c']):
- vif_c_base = ['interfaces', type, interface, 'vif-s', vif_s, 'vif-c', vif_c]
- fixup_cli(config, vif_c_base, f'{interface}.{vif_s}.{vif_c}')
+ if config.exists(if_base + ['vif-s']):
+ for vif_s in config.list_nodes(if_base + ['vif-s']):
+ vif_s_base = ['interfaces', type, interface, 'vif-s', vif_s]
+ fixup_cli(config, vif_s_base, f'{interface}.{vif_s}', host)
-config.delete(tmp_base)
+ if config.exists(if_base + ['vif-s', vif_s, 'vif-c']):
+ for vif_c in config.list_nodes(if_base + ['vif-s', vif_s, 'vif-c']):
+ vif_c_base = ['interfaces', type, interface, 'vif-s', vif_s, 'vif-c', vif_c]
+ fixup_cli(config, vif_c_base, f'{interface}.{vif_s}.{vif_c}', host)
-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)
+ config.delete(tmp_base)
diff --git a/src/migration-scripts/system/24-to-25 b/src/migration-scripts/system/24-to-25
index 1c81a76e7..bdb89902e 100755..100644
--- a/src/migration-scripts/system/24-to-25
+++ b/src/migration-scripts/system/24-to-25
@@ -1,52 +1,35 @@
-#!/usr/bin/env python3
+# Copyright 2022-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2022 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 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,
+# 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 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/>.
+# 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, see <http://www.gnu.org/licenses/>.
+
# Migrate system syslog global archive to system logs logrotate messages
-from sys import exit, argv
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 = ['system', 'syslog', 'global', 'archive']
-config = ConfigTree(config_file)
-
-if not config.exists(base):
- exit(0)
-if config.exists(base + ['file']):
- tmp = config.return_value(base + ['file'])
- config.set(['system', 'logs', 'logrotate', 'messages', 'rotate'], value=tmp)
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ return
-if config.exists(base + ['size']):
- tmp = config.return_value(base + ['size'])
- tmp = max(round(int(tmp) / 1024), 1) # kb -> mb
- config.set(['system', 'logs', 'logrotate', 'messages', 'max-size'], value=tmp)
+ if config.exists(base + ['file']):
+ tmp = config.return_value(base + ['file'])
+ config.set(['system', 'logs', 'logrotate', 'messages', 'rotate'], value=tmp)
-config.delete(base)
+ if config.exists(base + ['size']):
+ tmp = config.return_value(base + ['size'])
+ tmp = max(round(int(tmp) / 1024), 1) # kb -> mb
+ config.set(['system', 'logs', 'logrotate', 'messages', 'max-size'], value=tmp)
-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)
+ config.delete(base)
diff --git a/src/migration-scripts/system/25-to-26 b/src/migration-scripts/system/25-to-26
index 7bdf3be98..8832f48e5 100755..100644
--- a/src/migration-scripts/system/25-to-26
+++ b/src/migration-scripts/system/25-to-26
@@ -1,39 +1,25 @@
-#!/usr/bin/env python3
+# Copyright 2023-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2023 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 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,
+# 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 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/>.
+# 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, see <http://www.gnu.org/licenses/>.
+
# syslog: migrate deprecated CLI options
# - protocols -> local7
# - security -> auth
-from sys import exit, argv
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 = ['system', 'syslog']
-config = ConfigTree(config_file)
-
-if not config.exists(base):
- exit(0)
def rename_facilities(config, base_tree, facility, facility_new) -> None:
if config.exists(base + [base_tree, 'facility', facility]):
@@ -44,39 +30,36 @@ def rename_facilities(config, base_tree, facility, facility_new) -> None:
# delete old duplicate facility config
config.delete(base + [base_tree, 'facility', facility])
-#
-# Rename protocols and securityy facility to common ones
-#
-replace = {
- 'protocols' : 'local7',
- 'security' : 'auth'
-}
-for facility, facility_new in replace.items():
- rename_facilities(config, 'console', facility, facility_new)
- rename_facilities(config, 'global', facility, facility_new)
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ return
- if config.exists(base + ['host']):
- for host in config.list_nodes(base + ['host']):
- rename_facilities(config, f'host {host}', facility, facility_new)
+ #
+ # Rename protocols and securityy facility to common ones
+ #
+ replace = {
+ 'protocols' : 'local7',
+ 'security' : 'auth'
+ }
+ for facility, facility_new in replace.items():
+ rename_facilities(config, 'console', facility, facility_new)
+ rename_facilities(config, 'global', facility, facility_new)
-#
-# It makes no sense to configure udp/tcp transport per individual facility
-#
-if config.exists(base + ['host']):
- for host in config.list_nodes(base + ['host']):
- protocol = None
- for facility in config.list_nodes(base + ['host', host, 'facility']):
- tmp_path = base + ['host', host, 'facility', facility, 'protocol']
- if config.exists(tmp_path):
- # We can only change the first one
- if protocol == None:
- protocol = config.return_value(tmp_path)
- config.set(base + ['host', host, 'protocol'], value=protocol)
- config.delete(tmp_path)
+ if config.exists(base + ['host']):
+ for host in config.list_nodes(base + ['host']):
+ rename_facilities(config, f'host {host}', facility, facility_new)
-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)
+ #
+ # It makes no sense to configure udp/tcp transport per individual facility
+ #
+ if config.exists(base + ['host']):
+ for host in config.list_nodes(base + ['host']):
+ protocol = None
+ for facility in config.list_nodes(base + ['host', host, 'facility']):
+ tmp_path = base + ['host', host, 'facility', facility, 'protocol']
+ if config.exists(tmp_path):
+ # We can only change the first one
+ if protocol == None:
+ protocol = config.return_value(tmp_path)
+ config.set(base + ['host', host, 'protocol'], value=protocol)
+ config.delete(tmp_path)
diff --git a/src/migration-scripts/system/26-to-27 b/src/migration-scripts/system/26-to-27
index 80bb82cbd..499e16e08 100755..100644
--- a/src/migration-scripts/system/26-to-27
+++ b/src/migration-scripts/system/26-to-27
@@ -1,47 +1,30 @@
-#!/usr/bin/env python3
+# Copyright 2023-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2023 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 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,
+# 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 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/>.
+# 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, see <http://www.gnu.org/licenses/>.
+
# T5877: migrate 'system domain-search domain' to 'system domain-search'
-from sys import exit, argv
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 = ['system', 'domain-search']
-config = ConfigTree(config_file)
-
-if not config.exists(base):
- exit(0)
-if config.exists(base + ['domain']):
- entries = config.return_values(base + ['domain'])
- config.delete(base + ['domain'])
- for entry in entries:
- config.set(base, value=entry, replace=False)
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ return
-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)
+ if config.exists(base + ['domain']):
+ entries = config.return_values(base + ['domain'])
+ config.delete(base + ['domain'])
+ for entry in entries:
+ config.set(base, value=entry, replace=False)
diff --git a/src/migration-scripts/system/6-to-7 b/src/migration-scripts/system/6-to-7
index d24521134..e91ccc4e9 100755..100644
--- a/src/migration-scripts/system/6-to-7
+++ b/src/migration-scripts/system/6-to-7
@@ -1,48 +1,36 @@
-#!/usr/bin/env python3
+# 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
+# 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, see <http://www.gnu.org/licenses/>.
# Change smp_affinity to smp-affinity
-import sys
-
from vyos.configtree import ConfigTree
-if len(sys.argv) < 2:
- print("Must specify file name!")
- sys.exit(1)
-
-file_name = sys.argv[1]
-
-with open(file_name, 'r') as f:
- config_file = f.read()
-
-config = ConfigTree(config_file)
-
-update_required = False
-
-intf_types = config.list_nodes(["interfaces"])
-
-for intf_type in intf_types:
- intf_type_path = ["interfaces", intf_type]
- intfs = config.list_nodes(intf_type_path)
-
- for intf in intfs:
- intf_path = intf_type_path + [intf]
- if not config.exists(intf_path + ["smp_affinity"]):
- # Nothing to do.
- continue
- else:
- # Rename the node.
- old_smp_affinity_path = intf_path + ["smp_affinity"]
- config.rename(old_smp_affinity_path, "smp-affinity")
- update_required = True
-
-if update_required:
- 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))
- sys.exit(1)
-
-
-
+def migrate(config: ConfigTree) -> None:
+ intf_types = config.list_nodes(["interfaces"])
+
+ for intf_type in intf_types:
+ intf_type_path = ["interfaces", intf_type]
+ intfs = config.list_nodes(intf_type_path)
+
+ for intf in intfs:
+ intf_path = intf_type_path + [intf]
+ if not config.exists(intf_path + ["smp_affinity"]):
+ # Nothing to do.
+ continue
+ else:
+ # Rename the node.
+ old_smp_affinity_path = intf_path + ["smp_affinity"]
+ config.rename(old_smp_affinity_path, "smp-affinity")
+ update_required = True
diff --git a/src/migration-scripts/system/7-to-8 b/src/migration-scripts/system/7-to-8
index 5d084d2bf..64dd4dc93 100755..100644
--- a/src/migration-scripts/system/7-to-8
+++ b/src/migration-scripts/system/7-to-8
@@ -1,26 +1,27 @@
-#!/usr/bin/env python3
+# Copyright 2018-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
+# 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, see <http://www.gnu.org/licenses/>.
# Converts "system gateway-address" option to "protocols static route 0.0.0.0/0 next-hop $gw"
-import sys
-
from vyos.configtree import ConfigTree
-if len(sys.argv) < 2:
- print("Must specify file name!")
- sys.exit(1)
-
-file_name = sys.argv[1]
-
-with open(file_name, 'r') as f:
- config_file = f.read()
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(['system', 'gateway-address']):
+ # Nothing to do
+ return
-config = ConfigTree(config_file)
-
-if not config.exists(['system', 'gateway-address']):
- # Nothing to do
- sys.exit(0)
-else:
# Save the address
gw = config.return_value(['system', 'gateway-address'])
@@ -36,10 +37,3 @@ else:
# They must be formatted as such to load correctly.
config.set_tag(['protocols', 'static', 'route'])
config.set_tag(['protocols', 'static', 'route', '0.0.0.0/0', 'next-hop'])
-
- 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))
- sys.exit(1)
diff --git a/src/migration-scripts/system/8-to-9 b/src/migration-scripts/system/8-to-9
index e3bb2bca8..ea5f7af81 100755..100644
--- a/src/migration-scripts/system/8-to-9
+++ b/src/migration-scripts/system/8-to-9
@@ -1,32 +1,26 @@
-#!/usr/bin/env python3
+# Copyright 2018-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
+# 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, see <http://www.gnu.org/licenses/>.
# Deletes "system package" option as it is deprecated
-import sys
-
from vyos.configtree import ConfigTree
-if len(sys.argv) < 2:
- print("Must specify file name!")
- sys.exit(1)
-
-file_name = sys.argv[1]
-
-with open(file_name, 'r') as f:
- config_file = f.read()
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(['system', 'package']):
+ # Nothing to do
+ return
-config = ConfigTree(config_file)
-
-if not config.exists(['system', 'package']):
- # Nothing to do
- sys.exit(0)
-else:
# Delete the node with the old syntax
config.delete(['system', 'package'])
-
- 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))
- sys.exit(1)
diff --git a/src/migration-scripts/vrf/0-to-1 b/src/migration-scripts/vrf/0-to-1
index 8187138d9..70abae2a8 100755..100644
--- a/src/migration-scripts/vrf/0-to-1
+++ b/src/migration-scripts/vrf/0-to-1
@@ -1,132 +1,113 @@
-#!/usr/bin/env python3
+# Copyright 2021-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2021 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 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# - T2450: drop interface-route and interface-route6 from "protocols vrf"
-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 = ['protocols', 'vrf']
-config = ConfigTree(config_file)
-
-if not config.exists(base):
- # Nothing to do
- exit(0)
-
-for vrf in config.list_nodes(base):
- static_base = base + [vrf, 'static']
- if not config.exists(static_base):
- continue
-
- #
- # Migrate interface-route into route
- #
- interface_route_path = static_base + ['interface-route']
- if config.exists(interface_route_path):
- for route in config.list_nodes(interface_route_path):
- interface = config.list_nodes(interface_route_path + [route, 'next-hop-interface'])
-
- tmp = interface_route_path + [route, 'next-hop-interface']
- for interface in config.list_nodes(tmp):
- new_base = static_base + ['route', route, 'interface']
- config.set(new_base)
- config.set_tag(new_base)
- config.copy(tmp + [interface], new_base + [interface])
-
- config.delete(interface_route_path)
-
- #
- # Migrate interface-route6 into route6
- #
- interface_route_path = static_base + ['interface-route6']
- if config.exists(interface_route_path):
- for route in config.list_nodes(interface_route_path):
- interface = config.list_nodes(interface_route_path + [route, 'next-hop-interface'])
-
- tmp = interface_route_path + [route, 'next-hop-interface']
- for interface in config.list_nodes(tmp):
- new_base = static_base + ['route6', route, 'interface']
- config.set(new_base)
- config.set_tag(new_base)
- config.copy(tmp + [interface], new_base + [interface])
-
- config.delete(interface_route_path)
-
- #
- # Cleanup nodes inside route
- #
- route_path = static_base + ['route']
- if config.exists(route_path):
- for route in config.list_nodes(route_path):
- next_hop = route_path + [route, 'next-hop']
- if config.exists(next_hop):
- for gateway in config.list_nodes(next_hop):
- interface_path = next_hop + [gateway, 'next-hop-interface']
- if config.exists(interface_path):
- config.rename(interface_path, 'interface')
- vrf_path = next_hop + [gateway, 'next-hop-vrf']
- if config.exists(vrf_path):
- config.rename(vrf_path, 'vrf')
-
- next_hop = route_path + [route, 'interface']
- if config.exists(next_hop):
- for interface in config.list_nodes(next_hop):
- interface_path = next_hop + [interface, 'next-hop-interface']
- if config.exists(interface_path):
- config.rename(interface_path, 'interface')
- vrf_path = next_hop + [interface, 'next-hop-vrf']
- if config.exists(vrf_path):
- config.rename(vrf_path, 'vrf')
-
- #
- # Cleanup nodes inside route6
- #
- route_path = static_base + ['route6']
- if config.exists(route_path):
- for route in config.list_nodes(route_path):
- next_hop = route_path + [route, 'next-hop']
- if config.exists(next_hop):
- for gateway in config.list_nodes(next_hop):
- vrf_path = next_hop + [gateway, 'next-hop-vrf']
- if config.exists(vrf_path):
- config.rename(vrf_path, 'vrf')
-
- next_hop = route_path + [route, 'interface']
- if config.exists(next_hop):
- for interface in config.list_nodes(next_hop):
- interface_path = next_hop + [interface, 'next-hop-interface']
- if config.exists(interface_path):
- config.rename(interface_path, 'interface')
- vrf_path = next_hop + [interface, 'next-hop-vrf']
- if config.exists(vrf_path):
- config.rename(vrf_path, 'vrf')
-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 migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ # Nothing to do
+ return
+
+ for vrf in config.list_nodes(base):
+ static_base = base + [vrf, 'static']
+ if not config.exists(static_base):
+ continue
+
+ #
+ # Migrate interface-route into route
+ #
+ interface_route_path = static_base + ['interface-route']
+ if config.exists(interface_route_path):
+ for route in config.list_nodes(interface_route_path):
+ interface = config.list_nodes(interface_route_path + [route, 'next-hop-interface'])
+
+ tmp = interface_route_path + [route, 'next-hop-interface']
+ for interface in config.list_nodes(tmp):
+ new_base = static_base + ['route', route, 'interface']
+ config.set(new_base)
+ config.set_tag(new_base)
+ config.copy(tmp + [interface], new_base + [interface])
+
+ config.delete(interface_route_path)
+
+ #
+ # Migrate interface-route6 into route6
+ #
+ interface_route_path = static_base + ['interface-route6']
+ if config.exists(interface_route_path):
+ for route in config.list_nodes(interface_route_path):
+ interface = config.list_nodes(interface_route_path + [route, 'next-hop-interface'])
+
+ tmp = interface_route_path + [route, 'next-hop-interface']
+ for interface in config.list_nodes(tmp):
+ new_base = static_base + ['route6', route, 'interface']
+ config.set(new_base)
+ config.set_tag(new_base)
+ config.copy(tmp + [interface], new_base + [interface])
+
+ config.delete(interface_route_path)
+
+ #
+ # Cleanup nodes inside route
+ #
+ route_path = static_base + ['route']
+ if config.exists(route_path):
+ for route in config.list_nodes(route_path):
+ next_hop = route_path + [route, 'next-hop']
+ if config.exists(next_hop):
+ for gateway in config.list_nodes(next_hop):
+ interface_path = next_hop + [gateway, 'next-hop-interface']
+ if config.exists(interface_path):
+ config.rename(interface_path, 'interface')
+ vrf_path = next_hop + [gateway, 'next-hop-vrf']
+ if config.exists(vrf_path):
+ config.rename(vrf_path, 'vrf')
+
+ next_hop = route_path + [route, 'interface']
+ if config.exists(next_hop):
+ for interface in config.list_nodes(next_hop):
+ interface_path = next_hop + [interface, 'next-hop-interface']
+ if config.exists(interface_path):
+ config.rename(interface_path, 'interface')
+ vrf_path = next_hop + [interface, 'next-hop-vrf']
+ if config.exists(vrf_path):
+ config.rename(vrf_path, 'vrf')
+
+ #
+ # Cleanup nodes inside route6
+ #
+ route_path = static_base + ['route6']
+ if config.exists(route_path):
+ for route in config.list_nodes(route_path):
+ next_hop = route_path + [route, 'next-hop']
+ if config.exists(next_hop):
+ for gateway in config.list_nodes(next_hop):
+ vrf_path = next_hop + [gateway, 'next-hop-vrf']
+ if config.exists(vrf_path):
+ config.rename(vrf_path, 'vrf')
+
+ next_hop = route_path + [route, 'interface']
+ if config.exists(next_hop):
+ for interface in config.list_nodes(next_hop):
+ interface_path = next_hop + [interface, 'next-hop-interface']
+ if config.exists(interface_path):
+ config.rename(interface_path, 'interface')
+ vrf_path = next_hop + [interface, 'next-hop-vrf']
+ if config.exists(vrf_path):
+ config.rename(vrf_path, 'vrf')
diff --git a/src/migration-scripts/vrf/1-to-2 b/src/migration-scripts/vrf/1-to-2
index 52d4c2c7b..557a9ec58 100755..100644
--- a/src/migration-scripts/vrf/1-to-2
+++ b/src/migration-scripts/vrf/1-to-2
@@ -1,62 +1,43 @@
-#!/usr/bin/env python3
+# Copyright 2021-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2021 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 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# - T3344: migrate routing options from "protocols vrf" to "vrf <name> protocols"
-from sys import argv
-from sys import exit
from vyos.configtree import ConfigTree
-if len(argv) < 2:
- print("Must specify file name!")
- exit(1)
+base = ['protocols', 'vrf']
-file_name = argv[1]
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ # Nothing to do
+ return
-with open(file_name, 'r') as f:
- config_file = f.read()
+ vrf_base = ['vrf', 'name']
+ config.set(vrf_base)
+ config.set_tag(vrf_base)
-base = ['protocols', 'vrf']
-config = ConfigTree(config_file)
-
-if not config.exists(base):
- # Nothing to do
- exit(0)
-
-vrf_base = ['vrf', 'name']
-config.set(vrf_base)
-config.set_tag(vrf_base)
-
-# Copy all existing static routes to the new base node under "vrf name <name> protocols static"
-for vrf in config.list_nodes(base):
- static_base = base + [vrf, 'static']
- if not config.exists(static_base):
- continue
-
- new_static_base = vrf_base + [vrf, 'protocols']
- config.set(new_static_base)
- config.copy(static_base, new_static_base + ['static'])
- config.set_tag(new_static_base + ['static', 'route'])
-
-# Now delete the old configuration
-config.delete(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)
+ # Copy all existing static routes to the new base node under "vrf name <name> protocols static"
+ for vrf in config.list_nodes(base):
+ static_base = base + [vrf, 'static']
+ if not config.exists(static_base):
+ continue
+
+ new_static_base = vrf_base + [vrf, 'protocols']
+ config.set(new_static_base)
+ config.copy(static_base, new_static_base + ['static'])
+ config.set_tag(new_static_base + ['static', 'route'])
+
+ # Now delete the old configuration
+ config.delete(base)
diff --git a/src/migration-scripts/vrf/2-to-3 b/src/migration-scripts/vrf/2-to-3
index d45b185ee..acacffb41 100755..100644
--- a/src/migration-scripts/vrf/2-to-3
+++ b/src/migration-scripts/vrf/2-to-3
@@ -1,26 +1,23 @@
-#!/usr/bin/env python3
+# Copyright 2021-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2021 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 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# Since connection tracking zones are int16, VRFs tables maximum value must
# be limited to 65535
# Also, interface names in nftables cannot start from numbers,
# so VRF name should not start from a number
-from sys import argv
-from sys import exit
from random import randrange
from random import choice
from string import ascii_lowercase
@@ -69,76 +66,60 @@ def _search_tables(config_commands, table_num):
return table_items
-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 = ['vrf', 'name']
-config = ConfigTree(config_file)
-
-if not config.exists(base):
- # Nothing to do
- exit(0)
-
-# Get a list of all currently used VRFs and tables
-vrfs_current = {}
-for vrf in config.list_nodes(base):
- vrfs_current[vrf] = int(config.return_value(base + [vrf, 'table']))
-
-# Check VRF names and table numbers
-name_regex = re.compile(r'^\d.*$')
-for vrf_name, vrf_table in vrfs_current.items():
- # Check table number
- if vrf_table > 65535:
- # Find new unused table number
- vrfs_current[vrf_name] = None
- while not vrfs_current[vrf_name]:
- table_random = randrange(100, 65535)
- if table_random not in vrfs_current.values():
- vrfs_current[vrf_name] = table_random
- # Update number to a new one
- config.set(['vrf', 'name', vrf_name, 'table'],
- vrfs_current[vrf_name],
- replace=True)
- # Check config items with old table number and replace to new one
- config_commands = config.to_commands().split('\n')
- table_config_lines = _search_tables(config_commands, vrf_table)
- # Rename table nodes
- if table_config_lines.get('table_tags'):
- for table_config_path in table_config_lines.get('table_tags'):
- config.rename(table_config_path, f'{vrfs_current[vrf_name]}')
- # Replace table values
- if table_config_lines.get('table_values'):
- for table_config_path in table_config_lines.get('table_values'):
- config.set(table_config_path,
- f'{vrfs_current[vrf_name]}',
- replace=True)
-
- # Check VRF name
- if name_regex.match(vrf_name):
- vrf_name_new = None
- while not vrf_name_new:
- vrf_name_rand = f'{choice(ascii_lowercase)}{vrf_name}'[:15]
- if vrf_name_rand not in vrfs_current:
- vrf_name_new = vrf_name_rand
- # Update VRF name to a new one
- config.rename(['vrf', 'name', vrf_name], vrf_name_new)
- # Check config items with old VRF name and replace to new one
- config_commands = config.to_commands().split('\n')
- vrf_config_lines = _search_vrfs(config_commands, vrf_name)
- # Rename VRF to a new name
- if vrf_config_lines:
- for vrf_value_path in vrf_config_lines:
- config.set(vrf_value_path, vrf_name_new, replace=True)
-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 migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ # Nothing to do
+ return
+
+ # Get a list of all currently used VRFs and tables
+ vrfs_current = {}
+ for vrf in config.list_nodes(base):
+ vrfs_current[vrf] = int(config.return_value(base + [vrf, 'table']))
+
+ # Check VRF names and table numbers
+ name_regex = re.compile(r'^\d.*$')
+ for vrf_name, vrf_table in vrfs_current.items():
+ # Check table number
+ if vrf_table > 65535:
+ # Find new unused table number
+ vrfs_current[vrf_name] = None
+ while not vrfs_current[vrf_name]:
+ table_random = randrange(100, 65535)
+ if table_random not in vrfs_current.values():
+ vrfs_current[vrf_name] = table_random
+ # Update number to a new one
+ config.set(['vrf', 'name', vrf_name, 'table'],
+ vrfs_current[vrf_name],
+ replace=True)
+ # Check config items with old table number and replace to new one
+ config_commands = config.to_commands().split('\n')
+ table_config_lines = _search_tables(config_commands, vrf_table)
+ # Rename table nodes
+ if table_config_lines.get('table_tags'):
+ for table_config_path in table_config_lines.get('table_tags'):
+ config.rename(table_config_path, f'{vrfs_current[vrf_name]}')
+ # Replace table values
+ if table_config_lines.get('table_values'):
+ for table_config_path in table_config_lines.get('table_values'):
+ config.set(table_config_path,
+ f'{vrfs_current[vrf_name]}',
+ replace=True)
+
+ # Check VRF name
+ if name_regex.match(vrf_name):
+ vrf_name_new = None
+ while not vrf_name_new:
+ vrf_name_rand = f'{choice(ascii_lowercase)}{vrf_name}'[:15]
+ if vrf_name_rand not in vrfs_current:
+ vrf_name_new = vrf_name_rand
+ # Update VRF name to a new one
+ config.rename(['vrf', 'name', vrf_name], vrf_name_new)
+ # Check config items with old VRF name and replace to new one
+ config_commands = config.to_commands().split('\n')
+ vrf_config_lines = _search_vrfs(config_commands, vrf_name)
+ # Rename VRF to a new name
+ if vrf_config_lines:
+ for vrf_value_path in vrf_config_lines:
+ config.set(vrf_value_path, vrf_name_new, replace=True)
diff --git a/src/migration-scripts/vrrp/1-to-2 b/src/migration-scripts/vrrp/1-to-2
index dba5af81c..8639a7553 100755..100644
--- a/src/migration-scripts/vrrp/1-to-2
+++ b/src/migration-scripts/vrrp/1-to-2
@@ -1,37 +1,23 @@
-#!/usr/bin/env python3
+# Copyright 2018-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2018 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 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,
+# 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 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/>.
+# 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, see <http://www.gnu.org/licenses/>.
import re
-import sys
from vyos.configtree import ConfigTree
-if len(sys.argv) < 2:
- print("Must specify file name!")
- sys.exit(1)
-
-file_name = sys.argv[1]
-
-with open(file_name, 'r') as f:
- config_file = f.read()
-
-config = ConfigTree(config_file)
-
# Convert the old VRRP syntax to the new syntax
# The old approach was to put VRRP groups inside interfaces,
@@ -109,162 +95,156 @@ def get_vrrp_group(path):
# Only if no data is collected from any interface we can conclude that VRRP is not configured
# and exit.
-groups = []
-base_paths = []
-
-if config.exists(["interfaces", "ethernet"]):
- base_paths.append("ethernet")
-if config.exists(["interfaces", "bonding"]):
- base_paths.append("bonding")
-
-for bp in base_paths:
- parent_path = ["interfaces", bp]
-
- parent_intfs = config.list_nodes(parent_path)
-
- for pi in parent_intfs:
- # Extract VRRP groups from the parent interface
- vg_path =[pi, "vrrp", "vrrp-group"]
- if config.exists(parent_path + vg_path):
- pgroups = config.list_nodes(parent_path + vg_path)
- for pg in pgroups:
- g = get_vrrp_group(parent_path + vg_path + [pg])
- g["interface"] = pi
- g["vrid"] = pg
- groups.append(g)
-
- # Delete the VRRP subtree
- # If left in place, configs will not load correctly
- config.delete(parent_path + [pi, "vrrp"])
-
- # Extract VRRP groups from 802.1q VLAN interfaces
- if config.exists(parent_path + [pi, "vif"]):
- vifs = config.list_nodes(parent_path + [pi, "vif"])
- for vif in vifs:
- vif_vg_path = [pi, "vif", vif, "vrrp", "vrrp-group"]
- if config.exists(parent_path + vif_vg_path):
- vifgroups = config.list_nodes(parent_path + vif_vg_path)
- for vif_group in vifgroups:
- g = get_vrrp_group(parent_path + vif_vg_path + [vif_group])
- g["interface"] = "{0}.{1}".format(pi, vif)
- g["vrid"] = vif_group
- groups.append(g)
-
- config.delete(parent_path + [pi, "vif", vif, "vrrp"])
-
- # Extract VRRP groups from 802.3ad QinQ service VLAN interfaces
- if config.exists(parent_path + [pi, "vif-s"]):
- vif_ss = config.list_nodes(parent_path + [pi, "vif-s"])
- for vif_s in vif_ss:
- vifs_vg_path = [pi, "vif-s", vif_s, "vrrp", "vrrp-group"]
- if config.exists(parent_path + vifs_vg_path):
- vifsgroups = config.list_nodes(parent_path + vifs_vg_path)
- for vifs_group in vifsgroups:
- g = get_vrrp_group(parent_path + vifs_vg_path + [vifs_group])
- g["interface"] = "{0}.{1}".format(pi, vif_s)
- g["vrid"] = vifs_group
- groups.append(g)
-
- config.delete(parent_path + [pi, "vif-s", vif_s, "vrrp"])
-
- # Extract VRRP groups from QinQ client VLAN interfaces nested in the vif-s
- if config.exists(parent_path + [pi, "vif-s", vif_s, "vif-c"]):
- vif_cs = config.list_nodes(parent_path + [pi, "vif-s", vif_s, "vif-c"])
- for vif_c in vif_cs:
- vifc_vg_path = [pi, "vif-s", vif_s, "vif-c", vif_c, "vrrp", "vrrp-group"]
- vifcgroups = config.list_nodes(parent_path + vifc_vg_path)
- for vifc_group in vifcgroups:
- g = get_vrrp_group(parent_path + vifc_vg_path + [vifc_group])
- g["interface"] = "{0}.{1}.{2}".format(pi, vif_s, vif_c)
- g["vrid"] = vifc_group
- groups.append(g)
-
- config.delete(parent_path + [pi, "vif-s", vif_s, "vif-c", vif_c, "vrrp"])
-
-# If nothing was collected before this point, it means the config has no VRRP setup
-if not groups:
- sys.exit(0)
-
-# Otherwise, there is VRRP to convert
-
-# Now convert the collected groups to the new syntax
-base_group_path = ["high-availability", "vrrp", "group"]
-sync_path = ["high-availability", "vrrp", "sync-group"]
-
-for g in groups:
- group_name = "{0}-{1}".format(g["interface"], g["vrid"])
- group_path = base_group_path + [group_name]
-
- config.set(group_path + ["interface"], value=g["interface"])
- config.set(group_path + ["vrid"], value=g["vrid"])
-
- if "advertise_interval" in g:
- config.set(group_path + ["advertise-interval"], value=g["advertise_interval"])
-
- if "priority" in g:
- config.set(group_path + ["priority"], value=g["priority"])
-
- if not g["preempt"]:
- config.set(group_path + ["no-preempt"], value=None)
-
- if "preempt_delay" in g:
- config.set(group_path + ["preempt-delay"], value=g["preempt_delay"])
-
- if g["rfc_compatibility"]:
- config.set(group_path + ["rfc3768-compatibility"], value=None)
-
- if g["disable"]:
- config.set(group_path + ["disable"], value=None)
-
- if "hello_source" in g:
- config.set(group_path + ["hello-source-address"], value=g["hello_source"])
-
- if "peer_address" in g:
- config.set(group_path + ["peer-address"], value=g["peer_address"])
-
- if "auth_password" in g:
- config.set(group_path + ["authentication", "password"], value=g["auth_password"])
- if "auth_type" in g:
- config.set(group_path + ["authentication", "type"], value=g["auth_type"])
-
- if "master_script" in g:
- config.set(group_path + ["transition-script", "master"], value=g["master_script"])
- if "backup_script" in g:
- config.set(group_path + ["transition-script", "backup"], value=g["backup_script"])
- if "fault_script" in g:
- config.set(group_path + ["transition-script", "fault"], value=g["fault_script"])
-
- if "health_check_interval" in g:
- config.set(group_path + ["health-check", "interval"], value=g["health_check_interval"])
- if "health_check_count" in g:
- config.set(group_path + ["health-check", "failure-count"], value=g["health_check_count"])
- if "health_check_script" in g:
- config.set(group_path + ["health-check", "script"], value=g["health_check_script"])
-
- # Not that it should ever be absent...
- if "virtual_addresses" in g:
- # The new CLI disallows addresses without prefix length
- # Pre-rewrite configs didn't support IPv6 VRRP, but handle it anyway
- for va in g["virtual_addresses"]:
- if not re.search(r'/', va):
- if re.search(r':', va):
- va = "{0}/128".format(va)
- else:
- va = "{0}/32".format(va)
- config.set(group_path + ["virtual-address"], value=va, replace=False)
-
- # Sync group
- if "sync_group" in g:
- config.set(sync_path + [g["sync_group"], "member"], value=group_name, replace=False)
-
-# Set the tag flag
-config.set_tag(base_group_path)
-if config.exists(sync_path):
- config.set_tag(sync_path)
-
-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))
- sys.exit(1)
+def migrate(config: ConfigTree) -> None:
+ groups = []
+ base_paths = []
+
+ if config.exists(["interfaces", "ethernet"]):
+ base_paths.append("ethernet")
+ if config.exists(["interfaces", "bonding"]):
+ base_paths.append("bonding")
+
+ for bp in base_paths:
+ parent_path = ["interfaces", bp]
+
+ parent_intfs = config.list_nodes(parent_path)
+
+ for pi in parent_intfs:
+ # Extract VRRP groups from the parent interface
+ vg_path =[pi, "vrrp", "vrrp-group"]
+ if config.exists(parent_path + vg_path):
+ pgroups = config.list_nodes(parent_path + vg_path)
+ for pg in pgroups:
+ g = get_vrrp_group(parent_path + vg_path + [pg])
+ g["interface"] = pi
+ g["vrid"] = pg
+ groups.append(g)
+
+ # Delete the VRRP subtree
+ # If left in place, configs will not load correctly
+ config.delete(parent_path + [pi, "vrrp"])
+
+ # Extract VRRP groups from 802.1q VLAN interfaces
+ if config.exists(parent_path + [pi, "vif"]):
+ vifs = config.list_nodes(parent_path + [pi, "vif"])
+ for vif in vifs:
+ vif_vg_path = [pi, "vif", vif, "vrrp", "vrrp-group"]
+ if config.exists(parent_path + vif_vg_path):
+ vifgroups = config.list_nodes(parent_path + vif_vg_path)
+ for vif_group in vifgroups:
+ g = get_vrrp_group(parent_path + vif_vg_path + [vif_group])
+ g["interface"] = "{0}.{1}".format(pi, vif)
+ g["vrid"] = vif_group
+ groups.append(g)
+
+ config.delete(parent_path + [pi, "vif", vif, "vrrp"])
+
+ # Extract VRRP groups from 802.3ad QinQ service VLAN interfaces
+ if config.exists(parent_path + [pi, "vif-s"]):
+ vif_ss = config.list_nodes(parent_path + [pi, "vif-s"])
+ for vif_s in vif_ss:
+ vifs_vg_path = [pi, "vif-s", vif_s, "vrrp", "vrrp-group"]
+ if config.exists(parent_path + vifs_vg_path):
+ vifsgroups = config.list_nodes(parent_path + vifs_vg_path)
+ for vifs_group in vifsgroups:
+ g = get_vrrp_group(parent_path + vifs_vg_path + [vifs_group])
+ g["interface"] = "{0}.{1}".format(pi, vif_s)
+ g["vrid"] = vifs_group
+ groups.append(g)
+
+ config.delete(parent_path + [pi, "vif-s", vif_s, "vrrp"])
+
+ # Extract VRRP groups from QinQ client VLAN interfaces nested in the vif-s
+ if config.exists(parent_path + [pi, "vif-s", vif_s, "vif-c"]):
+ vif_cs = config.list_nodes(parent_path + [pi, "vif-s", vif_s, "vif-c"])
+ for vif_c in vif_cs:
+ vifc_vg_path = [pi, "vif-s", vif_s, "vif-c", vif_c, "vrrp", "vrrp-group"]
+ vifcgroups = config.list_nodes(parent_path + vifc_vg_path)
+ for vifc_group in vifcgroups:
+ g = get_vrrp_group(parent_path + vifc_vg_path + [vifc_group])
+ g["interface"] = "{0}.{1}.{2}".format(pi, vif_s, vif_c)
+ g["vrid"] = vifc_group
+ groups.append(g)
+
+ config.delete(parent_path + [pi, "vif-s", vif_s, "vif-c", vif_c, "vrrp"])
+
+ # If nothing was collected before this point, it means the config has no VRRP setup
+ if not groups:
+ return
+
+ # Otherwise, there is VRRP to convert
+
+ # Now convert the collected groups to the new syntax
+ base_group_path = ["high-availability", "vrrp", "group"]
+ sync_path = ["high-availability", "vrrp", "sync-group"]
+
+ for g in groups:
+ group_name = "{0}-{1}".format(g["interface"], g["vrid"])
+ group_path = base_group_path + [group_name]
+
+ config.set(group_path + ["interface"], value=g["interface"])
+ config.set(group_path + ["vrid"], value=g["vrid"])
+
+ if "advertise_interval" in g:
+ config.set(group_path + ["advertise-interval"], value=g["advertise_interval"])
+
+ if "priority" in g:
+ config.set(group_path + ["priority"], value=g["priority"])
+
+ if not g["preempt"]:
+ config.set(group_path + ["no-preempt"], value=None)
+
+ if "preempt_delay" in g:
+ config.set(group_path + ["preempt-delay"], value=g["preempt_delay"])
+
+ if g["rfc_compatibility"]:
+ config.set(group_path + ["rfc3768-compatibility"], value=None)
+
+ if g["disable"]:
+ config.set(group_path + ["disable"], value=None)
+
+ if "hello_source" in g:
+ config.set(group_path + ["hello-source-address"], value=g["hello_source"])
+
+ if "peer_address" in g:
+ config.set(group_path + ["peer-address"], value=g["peer_address"])
+
+ if "auth_password" in g:
+ config.set(group_path + ["authentication", "password"], value=g["auth_password"])
+ if "auth_type" in g:
+ config.set(group_path + ["authentication", "type"], value=g["auth_type"])
+
+ if "master_script" in g:
+ config.set(group_path + ["transition-script", "master"], value=g["master_script"])
+ if "backup_script" in g:
+ config.set(group_path + ["transition-script", "backup"], value=g["backup_script"])
+ if "fault_script" in g:
+ config.set(group_path + ["transition-script", "fault"], value=g["fault_script"])
+
+ if "health_check_interval" in g:
+ config.set(group_path + ["health-check", "interval"], value=g["health_check_interval"])
+ if "health_check_count" in g:
+ config.set(group_path + ["health-check", "failure-count"], value=g["health_check_count"])
+ if "health_check_script" in g:
+ config.set(group_path + ["health-check", "script"], value=g["health_check_script"])
+
+ # Not that it should ever be absent...
+ if "virtual_addresses" in g:
+ # The new CLI disallows addresses without prefix length
+ # Pre-rewrite configs didn't support IPv6 VRRP, but handle it anyway
+ for va in g["virtual_addresses"]:
+ if not re.search(r'/', va):
+ if re.search(r':', va):
+ va = "{0}/128".format(va)
+ else:
+ va = "{0}/32".format(va)
+ config.set(group_path + ["virtual-address"], value=va, replace=False)
+
+ # Sync group
+ if "sync_group" in g:
+ config.set(sync_path + [g["sync_group"], "member"], value=group_name, replace=False)
+
+ # Set the tag flag
+ config.set_tag(base_group_path)
+ if config.exists(sync_path):
+ config.set_tag(sync_path)
diff --git a/src/migration-scripts/vrrp/2-to-3 b/src/migration-scripts/vrrp/2-to-3
index ed583b489..468918f91 100755..100644
--- a/src/migration-scripts/vrrp/2-to-3
+++ b/src/migration-scripts/vrrp/2-to-3
@@ -1,62 +1,44 @@
-#!/usr/bin/env python3
+# Copyright 2021-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2021 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 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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/>.
# T3847: vrrp config cleanup
-from sys import argv
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 = ['high-availability', 'vrrp']
-config = ConfigTree(config_file)
-
-if not config.exists(base):
- # Nothing to do
- exit(0)
-
-if config.exists(base + ['group']):
- for group in config.list_nodes(base + ['group']):
- group_base = base + ['group', group]
-
- # Deprecated option
- tmp = group_base + ['transition-script', 'mode-force']
- if config.exists(tmp):
- config.delete(tmp)
-
- # Rename virtual-address -> address
- tmp = group_base + ['virtual-address']
- if config.exists(tmp):
- config.rename(tmp, 'address')
-
- # Rename virtual-address-excluded -> excluded-address
- tmp = group_base + ['virtual-address-excluded']
- if config.exists(tmp):
- config.rename(tmp, 'excluded-address')
-
-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)
+
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ # Nothing to do
+ return
+
+ if config.exists(base + ['group']):
+ for group in config.list_nodes(base + ['group']):
+ group_base = base + ['group', group]
+
+ # Deprecated option
+ tmp = group_base + ['transition-script', 'mode-force']
+ if config.exists(tmp):
+ config.delete(tmp)
+
+ # Rename virtual-address -> address
+ tmp = group_base + ['virtual-address']
+ if config.exists(tmp):
+ config.rename(tmp, 'address')
+
+ # Rename virtual-address-excluded -> excluded-address
+ tmp = group_base + ['virtual-address-excluded']
+ if config.exists(tmp):
+ config.rename(tmp, 'excluded-address')
diff --git a/src/migration-scripts/vrrp/3-to-4 b/src/migration-scripts/vrrp/3-to-4
index e5d93578c..9f05cf7a1 100755..100644
--- a/src/migration-scripts/vrrp/3-to-4
+++ b/src/migration-scripts/vrrp/3-to-4
@@ -1,51 +1,32 @@
-#!/usr/bin/env python3
+# Copyright 2023-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
-# Copyright (C) 2023 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 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,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# 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 sys import argv
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 = ['high-availability', 'virtual-server']
-config = ConfigTree(config_file)
-
-if not config.exists(base):
- # Nothing to do
- exit(0)
-
-if config.exists(base):
- for vs in config.list_nodes(base):
- vs_base = base + [vs]
- # If the fwmark is used, the address is not required
- if not config.exists(vs_base + ['fwmark']):
- # add option: 'virtual-server <tag> address x.x.x.x'
- config.set(vs_base + ['address'], value=vs)
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ # Nothing to do
+ return
+ if config.exists(base):
+ for vs in config.list_nodes(base):
+ vs_base = base + [vs]
-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)
+ # If the fwmark is used, the address is not required
+ if not config.exists(vs_base + ['fwmark']):
+ # add option: 'virtual-server <tag> address x.x.x.x'
+ config.set(vs_base + ['address'], value=vs)
diff --git a/src/migration-scripts/webproxy/1-to-2 b/src/migration-scripts/webproxy/1-to-2
index 03f357878..5a4847474 100755..100644
--- a/src/migration-scripts/webproxy/1-to-2
+++ b/src/migration-scripts/webproxy/1-to-2
@@ -1,39 +1,33 @@
-#!/usr/bin/env python3
+# Copyright 2018-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
+# 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, see <http://www.gnu.org/licenses/>.
# migrate old style `webproxy proxy-bypass 1.2.3.4/24`
# to new style `webproxy whitelist destination-address 1.2.3.4/24`
-import sys
-
from vyos.configtree import ConfigTree
-if len(sys.argv) < 2:
- print("Must specify file name!")
- sys.exit(1)
-
-file_name = sys.argv[1]
-
-with open(file_name, 'r') as f:
- config_file = f.read()
+cfg_webproxy_base = ['service', 'webproxy']
-config = ConfigTree(config_file)
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(cfg_webproxy_base + ['proxy-bypass']):
+ # Nothing to do
+ return
-cfg_webproxy_base = ['service', 'webproxy']
-if not config.exists(cfg_webproxy_base + ['proxy-bypass']):
- # Nothing to do
- sys.exit(0)
-else:
bypass_addresses = config.return_values(cfg_webproxy_base + ['proxy-bypass'])
# delete old configuration node
config.delete(cfg_webproxy_base + ['proxy-bypass'])
for bypass_address in bypass_addresses:
# add data to new configuration node
config.set(cfg_webproxy_base + ['whitelist', 'destination-address'], value=bypass_address, replace=False)
-
- # save updated configuration
- 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))
- sys.exit(1)
diff --git a/src/op_mode/kernel_modules.py b/src/op_mode/kernel_modules.py
new file mode 100755
index 000000000..e381a1df7
--- /dev/null
+++ b/src/op_mode/kernel_modules.py
@@ -0,0 +1,82 @@
+#!/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/>.
+#
+# Purpose:
+# Provides commands for retrieving information about kernel modules.
+
+import sys
+import typing
+
+import vyos.opmode
+
+
+lsmod_tmpl = """
+{% for m in modules -%}
+Module: {{m.name}}
+
+{% if m.holders -%}
+Holders: {{m.holders | join(", ")}}
+{%- endif %}
+
+{% if m.drivers -%}
+Drivers: {{m.drivers | join(", ")}}
+{%- endif %}
+
+{% for k in m.fields -%}
+{{k}}: {{m["fields"][k]}}
+{% endfor %}
+{% if m.parameters %}
+
+Parameters:
+
+{% for p in m.parameters -%}
+{{p}}: {{m["parameters"][p]}}
+{% endfor -%}
+{% endif -%}
+
+-------------
+
+{% endfor %}
+"""
+
+def _get_raw_data(module=None):
+ from vyos.utils.kernel import get_module_data, lsmod
+
+ if module:
+ return [get_module_data(module)]
+ else:
+ return lsmod()
+
+def show(raw: bool, module: typing.Optional[str]):
+ from jinja2 import Template
+
+ data = _get_raw_data(module=module)
+
+ if raw:
+ return data
+ else:
+ t = Template(lsmod_tmpl)
+ output = t.render({"modules": data})
+ return output
+
+if __name__ == '__main__':
+ try:
+ res = vyos.opmode.run(sys.modules[__name__])
+ if res:
+ print(res)
+ except (ValueError, vyos.opmode.Error) as e:
+ print(e)
+ sys.exit(1)
diff --git a/src/op_mode/storage.py b/src/op_mode/storage.py
index 6bc3d3a2d..8fd2ffea1 100755
--- a/src/op_mode/storage.py
+++ b/src/op_mode/storage.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
@@ -18,54 +18,38 @@
import sys
import vyos.opmode
-from vyos.utils.process import cmd
-# FIY: As of coreutils from Debian Buster and Bullseye,
-# the outpt looks like this:
-#
-# $ df -h -t ext4 --output=source,size,used,avail,pcent
-# Filesystem Size Used Avail Use%
-# /dev/sda1 16G 7.6G 7.3G 51%
-#
-# Those field names are automatically normalized by vyos.opmode.run,
-# so we don't touch them here,
-# and only normalize values.
-
-def _get_system_storage(only_persistent=False):
- if not only_persistent:
- cmd_str = 'df -h -x squashf'
- else:
- cmd_str = 'df -h -t ext4 --output=source,size,used,avail,pcent'
-
- res = cmd(cmd_str)
-
- return res
-
-def _get_raw_data():
- from re import sub as re_sub
- from vyos.utils.convert import human_to_bytes
+from jinja2 import Template
- out = _get_system_storage(only_persistent=True)
- lines = out.splitlines()
- lists = [l.split() for l in lines]
- res = {lists[0][i]: lists[1][i] for i in range(len(lists[0]))}
-
- res["Size"] = human_to_bytes(res["Size"])
- res["Used"] = human_to_bytes(res["Used"])
- res["Avail"] = human_to_bytes(res["Avail"])
- res["Use%"] = re_sub(r'%', '', res["Use%"])
-
- return res
+output_tmpl = """
+Filesystem: {{filesystem}}
+Size: {{size}}
+Used: {{used}} ({{use_percentage}}%)
+Available: {{avail}} ({{avail_percentage}}%)
+"""
def _get_formatted_output():
return _get_system_storage()
def show(raw: bool):
- if raw:
- return _get_raw_data()
-
- return _get_formatted_output()
+ from vyos.utils.disk import get_persistent_storage_stats
+ if raw:
+ res = get_persistent_storage_stats(human_units=False)
+ if res is None:
+ raise vyos.opmode.DataUnavailable("Storage statistics are not available")
+ else:
+ return res
+ else:
+ data = get_persistent_storage_stats(human_units=True)
+ if data is None:
+ return "Storage statistics are not available"
+ else:
+ data["avail_percentage"] = 100 - int(data["use_percentage"])
+ tmpl = Template(output_tmpl)
+ return tmpl.render(data).strip()
+
+ return output
if __name__ == '__main__':
try:
diff --git a/src/services/vyos-configd b/src/services/vyos-configd
index c89c486e5..d92b539c8 100755
--- a/src/services/vyos-configd
+++ b/src/services/vyos-configd
@@ -179,8 +179,13 @@ def initialization(socket):
pid_string = socket.recv().decode("utf-8", "ignore")
resp = "pid"
socket.send(resp.encode())
+ sudo_user_string = socket.recv().decode("utf-8", "ignore")
+ resp = "sudo_user"
+ socket.send(resp.encode())
logger.debug(f"config session pid is {pid_string}")
+ logger.debug(f"config session sudo_user is {sudo_user_string}")
+
try:
session_out = os.readlink(f"/proc/{pid_string}/fd/1")
session_mode = 'w'
@@ -192,6 +197,8 @@ def initialization(socket):
session_out = script_stdout_log
session_mode = 'a'
+ os.environ['SUDO_USER'] = sudo_user_string
+
try:
configsource = ConfigSourceString(running_config_text=active_string,
session_config_text=session_string)
@@ -266,9 +273,6 @@ if __name__ == '__main__':
cfg_group = grp.getgrnam(CFG_GROUP)
os.setgid(cfg_group.gr_gid)
- os.environ['SUDO_USER'] = 'vyos'
- os.environ['SUDO_GID'] = str(cfg_group.gr_gid)
-
def sig_handler(signum, frame):
shutdown()
diff --git a/src/shim/vyshim.c b/src/shim/vyshim.c
index 41723e7a4..4d836127d 100644
--- a/src/shim/vyshim.c
+++ b/src/shim/vyshim.c
@@ -178,6 +178,13 @@ int initialization(void* Requester)
strsep(&pid_val, "_");
debug_print("config session pid: %s\n", pid_val);
+ char *sudo_user = getenv("SUDO_USER");
+ if (!sudo_user) {
+ char nobody[] = "nobody";
+ sudo_user = nobody;
+ }
+ debug_print("sudo_user is %s\n", sudo_user);
+
debug_print("Sending init announcement\n");
char *init_announce = mkjson(MKJSON_OBJ, 1,
MKJSON_STRING, "type", "init");
@@ -240,6 +247,10 @@ int initialization(void* Requester)
zmq_recv(Requester, buffer, 16, 0);
debug_print("Received pid receipt\n");
+ debug_print("Sending config session sudo_user\n");
+ zmq_send(Requester, sudo_user, strlen(sudo_user), 0);
+ zmq_recv(Requester, buffer, 16, 0);
+ debug_print("Received sudo_user receipt\n");
return 0;
}
diff --git a/src/systemd/stunnel.service b/src/systemd/stunnel.service
new file mode 100644
index 000000000..b260e2984
--- /dev/null
+++ b/src/systemd/stunnel.service
@@ -0,0 +1,15 @@
+[Unit]
+Description=SSL tunneling service
+Documentation=http://man.he.net/man8/stunnel4
+After=network.target
+
+[Service]
+ExecStart=/usr/bin/stunnel /run/stunnel/stunnel.conf
+ExecReload=/bin/kill -HUP $MAINPID
+KillMode=process
+PIDFile=/run/stunnel/stunnel.pid
+Type=forking
+Restart=always
+
+[Install]
+WantedBy=multi-user.target
diff --git a/src/tests/helper.py b/src/tests/helper.py
index f7033148a..cc0710494 100644
--- a/src/tests/helper.py
+++ b/src/tests/helper.py
@@ -1,6 +1,4 @@
-#!/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
diff --git a/src/tests/test_config_diff.py b/src/tests/test_config_diff.py
index 61a2f3487..39e17613a 100644
--- a/src/tests/test_config_diff.py
+++ b/src/tests/test_config_diff.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/src/tests/test_config_parser.py b/src/tests/test_config_parser.py
index c69732daa..9a4f02859 100644
--- a/src/tests/test_config_parser.py
+++ b/src/tests/test_config_parser.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env python3
-#
# Copyright (C) 2018-2024 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
diff --git a/smoketest/scripts/cli/test_configd_inspect.py b/src/tests/test_configd_inspect.py
index af46c6148..98552c8f3 100755..100644
--- a/smoketest/scripts/cli/test_configd_inspect.py
+++ b/src/tests/test_configd_inspect.py
@@ -1,6 +1,4 @@
-#!/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,16 +15,16 @@
import os
import re
import json
-import unittest
+
import warnings
import importlib.util
-from inspect import signature, getsource
+from inspect import signature
+from inspect import getsource
from functools import wraps
+from unittest import TestCase
-from vyos.defaults import directories
-
-INC_FILE = '/usr/share/vyos/configd-include.json'
-CONF_DIR = directories['conf_mode']
+INC_FILE = 'data/configd-include.json'
+CONF_DIR = 'src/conf_mode'
f_list = ['get_config', 'verify', 'generate', 'apply']
@@ -47,7 +45,7 @@ def ignore_deprecation_warning(f):
f(*args, **kwargs)
return decorated_function
-class TestConfigdInclude(unittest.TestCase):
+class TestConfigdInspect(TestCase):
def setUp(self):
with open(INC_FILE) as f:
self.inc_list = json.load(f)
@@ -105,6 +103,3 @@ class TestConfigdInclude(unittest.TestCase):
str_m = getsource(m)
n = str_m.count('my_set')
self.assertEqual(n, 0, f"'{s}' modifies config")
-
-if __name__ == '__main__':
- unittest.main(verbosity=2)
diff --git a/src/tests/test_configverify.py b/src/tests/test_configverify.py
index 15ccdf13d..f1ec65cd2 100644
--- a/src/tests/test_configverify.py
+++ b/src/tests/test_configverify.py
@@ -1,6 +1,4 @@
-#!/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
diff --git a/src/tests/test_dependency_graph.py b/src/tests/test_dependency_graph.py
index f682e87bb..f3f1db376 100644
--- a/src/tests/test_dependency_graph.py
+++ b/src/tests/test_dependency_graph.py
@@ -1,6 +1,4 @@
-#!/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
diff --git a/src/tests/test_dict_search.py b/src/tests/test_dict_search.py
index 2435d89c7..6b4bc933a 100644
--- a/src/tests/test_dict_search.py
+++ b/src/tests/test_dict_search.py
@@ -1,6 +1,4 @@
-#!/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
diff --git a/src/tests/test_find_device_file.py b/src/tests/test_find_device_file.py
index f18043d65..5b90f2034 100755..100644
--- a/src/tests/test_find_device_file.py
+++ b/src/tests/test_find_device_file.py
@@ -1,6 +1,4 @@
-#!/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
@@ -28,8 +26,5 @@ class TestDeviceFile(TestCase):
def test_zero(self):
self.assertEqual(find_device_file('zero'), '/dev/zero')
- def test_input_event(self):
- self.assertEqual(find_device_file('event0'), '/dev/input/event0')
-
def test_non_existing(self):
self.assertFalse(find_device_file('vyos'))
diff --git a/src/tests/test_initial_setup.py b/src/tests/test_initial_setup.py
index f85bf1265..4cd5fb169 100644
--- a/src/tests/test_initial_setup.py
+++ b/src/tests/test_initial_setup.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env python3
-#
# Copyright (C) 2018-2024 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
diff --git a/src/tests/test_op_mode.py b/src/tests/test_op_mode.py
index 90963b3c5..23f709653 100644
--- a/src/tests/test_op_mode.py
+++ b/src/tests/test_op_mode.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
@@ -62,4 +60,3 @@ class TestVyOSOpMode(TestCase):
data = [1, False, "foo"]
self.assertEqual(_normalize_field_names(data), [1, False, "foo"])
-
diff --git a/src/tests/test_task_scheduler.py b/src/tests/test_task_scheduler.py
index 130f825e6..795ffeb9d 100644
--- a/src/tests/test_task_scheduler.py
+++ b/src/tests/test_task_scheduler.py
@@ -1,6 +1,4 @@
-#!/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
diff --git a/src/tests/test_template.py b/src/tests/test_template.py
index dbb86b40b..6377f6da5 100644
--- a/src/tests/test_template.py
+++ b/src/tests/test_template.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env python3
-#
# Copyright (C) 2020-2024 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
diff --git a/src/tests/test_utils.py b/src/tests/test_utils.py
index 9ae329ced..7bfd2618e 100644
--- a/src/tests/test_utils.py
+++ b/src/tests/test_utils.py
@@ -1,6 +1,4 @@
-#!/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
diff --git a/src/tests/test_utils_network.py b/src/tests/test_utils_network.py
index 5a6dc2586..d68dec16f 100644
--- a/src/tests/test_utils_network.py
+++ b/src/tests/test_utils_network.py
@@ -1,6 +1,4 @@
-#!/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
@@ -45,6 +43,3 @@ class TestVyOSUtilsNetwork(TestCase):
self.assertFalse(vyos.utils.network.is_loopback_addr('::2'))
self.assertFalse(vyos.utils.network.is_loopback_addr('192.0.2.1'))
-
-
-
diff --git a/src/validators/psk-secret b/src/validators/psk-secret
new file mode 100644
index 000000000..c91aa95a8
--- /dev/null
+++ b/src/validators/psk-secret
@@ -0,0 +1,39 @@
+#!/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 re
+from sys import argv,exit
+
+if __name__ == '__main__':
+ if len(argv) != 2:
+ exit(1)
+
+ input = argv[1]
+ is_valid = True
+ try:
+ # Convert hexadecimal input to binary form
+ key_bytes = bytes.fromhex(input)
+ except ValueError:
+ is_valid = False
+
+ if is_valid and len(key_bytes) < 16:
+ is_valid = False
+
+ if not is_valid:
+ print(f'Error: {input} is not valid psk secret.')
+ exit(1)
+
+ exit(0) \ No newline at end of file