summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/mergifyio_backport.yml2
-rw-r--r--.github/workflows/pull-request-labels.yml4
-rw-r--r--Pipfile.lock663
-rw-r--r--data/configd-include.json3
-rw-r--r--data/templates/accel-ppp/pppoe.config.j22
-rw-r--r--data/templates/frr/zebra.vrf.route-map.frr.j26
-rw-r--r--data/templates/iproute2/static.conf.j22
-rw-r--r--data/templates/ipsec/swanctl/remote_access.j22
-rw-r--r--data/templates/load-balancing/haproxy.cfg.j244
-rw-r--r--data/templates/ocserv/ocserv_config.j28
-rw-r--r--interface-definitions/include/accel-ppp/radius-additions.xml.i2
-rw-r--r--interface-definitions/include/firewall/global-options.xml.i20
-rw-r--r--interface-definitions/include/haproxy/tcp-request.xml.i22
-rw-r--r--interface-definitions/include/interface/base-reachable-time.xml.i16
-rw-r--r--interface-definitions/include/interface/ipv6-options.xml.i1
-rw-r--r--interface-definitions/include/pppoe-access-concentrator.xml.i4
-rw-r--r--interface-definitions/include/tls-version-min.xml.i29
-rw-r--r--interface-definitions/include/version/firewall-version.xml.i2
-rw-r--r--interface-definitions/include/version/openconnect-version.xml.i2
-rw-r--r--interface-definitions/include/version/pppoe-server-version.xml.i2
-rw-r--r--interface-definitions/interfaces_openvpn.xml.in28
-rw-r--r--interface-definitions/interfaces_tunnel.xml.in2
-rw-r--r--interface-definitions/load-balancing_reverse-proxy.xml.in66
-rw-r--r--interface-definitions/protocols_failover.xml.in6
-rw-r--r--interface-definitions/service_config-sync.xml.in4
-rw-r--r--interface-definitions/service_dns_forwarding.xml.in2
-rw-r--r--interface-definitions/service_pppoe-server.xml.in8
-rw-r--r--interface-definitions/system_ip.xml.in6
-rw-r--r--interface-definitions/vpn_ipsec.xml.in20
-rw-r--r--interface-definitions/vpn_openconnect.xml.in4
-rw-r--r--interface-definitions/vrf.xml.in15
-rw-r--r--python/vyos/firewall.py48
-rw-r--r--python/vyos/ifconfig/interface.py28
-rw-r--r--python/vyos/pki.py2
-rw-r--r--python/vyos/qos/base.py5
-rw-r--r--python/vyos/utils/convert.py23
-rw-r--r--python/vyos/utils/io.py5
-rw-r--r--smoketest/scripts/cli/base_vyostest_shim.py4
-rwxr-xr-xsmoketest/scripts/cli/test_firewall.py1
-rwxr-xr-xsmoketest/scripts/cli/test_load-balancing_reverse-proxy.py104
-rwxr-xr-xsmoketest/scripts/cli/test_qos.py39
-rwxr-xr-xsmoketest/scripts/cli/test_service_pppoe-server.py9
-rwxr-xr-xsmoketest/scripts/cli/test_system_ip.py11
-rwxr-xr-xsmoketest/scripts/cli/test_vpn_ipsec.py5
-rwxr-xr-xsmoketest/scripts/cli/test_vpn_openconnect.py11
-rwxr-xr-xsmoketest/scripts/cli/test_vrf.py55
-rwxr-xr-xsmoketest/scripts/system/test_kernel_options.py76
-rwxr-xr-xsrc/conf_mode/firewall.py1
-rwxr-xr-xsrc/conf_mode/interfaces_openvpn.py6
-rwxr-xr-xsrc/conf_mode/interfaces_wireless.py10
-rwxr-xr-xsrc/conf_mode/load-balancing_reverse-proxy.py8
-rwxr-xr-xsrc/conf_mode/pki.py36
-rwxr-xr-xsrc/conf_mode/protocols_bgp.py62
-rwxr-xr-xsrc/conf_mode/protocols_pim.py14
-rwxr-xr-xsrc/conf_mode/qos.py23
-rwxr-xr-xsrc/conf_mode/service_ipoe-server.py2
-rwxr-xr-xsrc/conf_mode/service_pppoe-server.py19
-rwxr-xr-xsrc/conf_mode/system_host-name.py10
-rwxr-xr-xsrc/conf_mode/system_ip.py5
-rwxr-xr-xsrc/conf_mode/vrf.py19
-rw-r--r--src/conf_mode/vrf_vni.py103
-rw-r--r--src/etc/rsyslog.conf30
-rwxr-xr-xsrc/helpers/vyos-failover.py5
-rwxr-xr-xsrc/helpers/vyos-vrrp-conntracksync.sh4
-rwxr-xr-xsrc/helpers/vyos_config_sync.py23
-rwxr-xr-xsrc/init/vyos-router1
-rwxr-xr-xsrc/migration-scripts/firewall/14-to-1546
-rwxr-xr-xsrc/migration-scripts/openconnect/2-to-350
-rwxr-xr-xsrc/migration-scripts/pppoe-server/9-to-1056
-rwxr-xr-xsrc/op_mode/connect_disconnect.py6
-rwxr-xr-xsrc/op_mode/firewall.py78
-rwxr-xr-xsrc/op_mode/image_installer.py25
-rwxr-xr-xsrc/op_mode/openvpn.py7
-rwxr-xr-xsrc/op_mode/pki.py4
-rwxr-xr-xsrc/op_mode/uptime.py2
-rwxr-xr-xsrc/services/vyos-configd2
76 files changed, 1532 insertions, 548 deletions
diff --git a/.github/workflows/mergifyio_backport.yml b/.github/workflows/mergifyio_backport.yml
index f1f4312c4..d9f863d9a 100644
--- a/.github/workflows/mergifyio_backport.yml
+++ b/.github/workflows/mergifyio_backport.yml
@@ -13,7 +13,7 @@ jobs:
id: regex-match
with:
text: ${{ github.event.comment.body }}
- regex: '[Mm]ergifyio backport '
+ regex: '@[Mm][Ee][Rr][Gg][Ii][Ff][Yy][Ii][Oo] backport '
- uses: actions-ecosystem/action-add-labels@v1
if: ${{ steps.regex-match.outputs.match != '' }}
diff --git a/.github/workflows/pull-request-labels.yml b/.github/workflows/pull-request-labels.yml
index 3398af5b0..43856beaa 100644
--- a/.github/workflows/pull-request-labels.yml
+++ b/.github/workflows/pull-request-labels.yml
@@ -12,9 +12,9 @@ on:
jobs:
add-pr-label:
name: Add PR Labels
- runs-on: ubuntu-20.04
+ runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
- - uses: actions/labeler@v5.0.0
+ - uses: actions/labeler@v5
diff --git a/Pipfile.lock b/Pipfile.lock
index eae5ea9fe..5a048becc 100644
--- a/Pipfile.lock
+++ b/Pipfile.lock
@@ -1,7 +1,7 @@
{
"_meta": {
"hash": {
- "sha256": "5564acff86bcf8ef717129935c288ffed2631f1d795d1c44d0af36779593b89b"
+ "sha256": "2ec6c667a8fe2b96d82639414919deb0462744211709afe93f6a538a0b15dc1f"
},
"pipfile-spec": 6,
"requires": {
@@ -16,46 +16,242 @@
]
},
"default": {
+ "bcrypt": {
+ "hashes": [
+ "sha256:02d9ef8915f72dd6daaef40e0baeef8a017ce624369f09754baf32bb32dba25f",
+ "sha256:1c28973decf4e0e69cee78c68e30a523be441972c826703bb93099868a8ff5b5",
+ "sha256:2a298db2a8ab20056120b45e86c00a0a5eb50ec4075b6142db35f593b97cb3fb",
+ "sha256:33313a1200a3ae90b75587ceac502b048b840fc69e7f7a0905b5f87fac7a1258",
+ "sha256:3566a88234e8de2ccae31968127b0ecccbb4cddb629da744165db72b58d88ca4",
+ "sha256:387e7e1af9a4dd636b9505a465032f2f5cb8e61ba1120e79a0e1cd0b512f3dfc",
+ "sha256:44290ccc827d3a24604f2c8bcd00d0da349e336e6503656cb8192133e27335e2",
+ "sha256:57fa9442758da926ed33a91644649d3e340a71e2d0a5a8de064fb621fd5a3326",
+ "sha256:68e3c6642077b0c8092580c819c1684161262b2e30c4f45deb000c38947bf483",
+ "sha256:69057b9fc5093ea1ab00dd24ede891f3e5e65bee040395fb1e66ee196f9c9b4a",
+ "sha256:6cad43d8c63f34b26aef462b6f5e44fdcf9860b723d2453b5d391258c4c8e966",
+ "sha256:71b8be82bc46cedd61a9f4ccb6c1a493211d031415a34adde3669ee1b0afbb63",
+ "sha256:732b3920a08eacf12f93e6b04ea276c489f1c8fb49344f564cca2adb663b3e4c",
+ "sha256:9800ae5bd5077b13725e2e3934aa3c9c37e49d3ea3d06318010aa40f54c63551",
+ "sha256:a97e07e83e3262599434816f631cc4c7ca2aa8e9c072c1b1a7fec2ae809a1d2d",
+ "sha256:ac621c093edb28200728a9cca214d7e838529e557027ef0581685909acd28b5e",
+ "sha256:b8df79979c5bae07f1db22dcc49cc5bccf08a0380ca5c6f391cbb5790355c0b0",
+ "sha256:b90e216dc36864ae7132cb151ffe95155a37a14e0de3a8f64b49655dd959ff9c",
+ "sha256:ba4e4cc26610581a6329b3937e02d319f5ad4b85b074846bf4fef8a8cf51e7bb",
+ "sha256:ba55e40de38a24e2d78d34c2d36d6e864f93e0d79d0b6ce915e4335aa81d01b1",
+ "sha256:be3ab1071662f6065899fe08428e45c16aa36e28bc42921c4901a191fda6ee42",
+ "sha256:d75fc8cd0ba23f97bae88a6ec04e9e5351ff3c6ad06f38fe32ba50cbd0d11946",
+ "sha256:e51c42750b7585cee7892c2614be0d14107fad9581d1738d954a262556dd1aab",
+ "sha256:ea505c97a5c465ab8c3ba75c0805a102ce526695cd6818c6de3b1a38f6f60da1",
+ "sha256:eb3bd3321517916696233b5e0c67fd7d6281f0ef48e66812db35fc963a422a1c",
+ "sha256:f70d9c61f9c4ca7d57f3bfe88a5ccf62546ffbadf3681bb1e268d9d2e41c91a7",
+ "sha256:fbe188b878313d01b7718390f31528be4010fed1faa798c5a1d0469c9c48c369"
+ ],
+ "markers": "python_version >= '3.7'",
+ "version": "==4.1.2"
+ },
+ "cffi": {
+ "hashes": [
+ "sha256:0c9ef6ff37e974b73c25eecc13952c55bceed9112be2d9d938ded8e856138bcc",
+ "sha256:131fd094d1065b19540c3d72594260f118b231090295d8c34e19a7bbcf2e860a",
+ "sha256:1b8ebc27c014c59692bb2664c7d13ce7a6e9a629be20e54e7271fa696ff2b417",
+ "sha256:2c56b361916f390cd758a57f2e16233eb4f64bcbeee88a4881ea90fca14dc6ab",
+ "sha256:2d92b25dbf6cae33f65005baf472d2c245c050b1ce709cc4588cdcdd5495b520",
+ "sha256:31d13b0f99e0836b7ff893d37af07366ebc90b678b6664c955b54561fc36ef36",
+ "sha256:32c68ef735dbe5857c810328cb2481e24722a59a2003018885514d4c09af9743",
+ "sha256:3686dffb02459559c74dd3d81748269ffb0eb027c39a6fc99502de37d501faa8",
+ "sha256:582215a0e9adbe0e379761260553ba11c58943e4bbe9c36430c4ca6ac74b15ed",
+ "sha256:5b50bf3f55561dac5438f8e70bfcdfd74543fd60df5fa5f62d94e5867deca684",
+ "sha256:5bf44d66cdf9e893637896c7faa22298baebcd18d1ddb6d2626a6e39793a1d56",
+ "sha256:6602bc8dc6f3a9e02b6c22c4fc1e47aa50f8f8e6d3f78a5e16ac33ef5fefa324",
+ "sha256:673739cb539f8cdaa07d92d02efa93c9ccf87e345b9a0b556e3ecc666718468d",
+ "sha256:68678abf380b42ce21a5f2abde8efee05c114c2fdb2e9eef2efdb0257fba1235",
+ "sha256:68e7c44931cc171c54ccb702482e9fc723192e88d25a0e133edd7aff8fcd1f6e",
+ "sha256:6b3d6606d369fc1da4fd8c357d026317fbb9c9b75d36dc16e90e84c26854b088",
+ "sha256:748dcd1e3d3d7cd5443ef03ce8685043294ad6bd7c02a38d1bd367cfd968e000",
+ "sha256:7651c50c8c5ef7bdb41108b7b8c5a83013bfaa8a935590c5d74627c047a583c7",
+ "sha256:7b78010e7b97fef4bee1e896df8a4bbb6712b7f05b7ef630f9d1da00f6444d2e",
+ "sha256:7e61e3e4fa664a8588aa25c883eab612a188c725755afff6289454d6362b9673",
+ "sha256:80876338e19c951fdfed6198e70bc88f1c9758b94578d5a7c4c91a87af3cf31c",
+ "sha256:8895613bcc094d4a1b2dbe179d88d7fb4a15cee43c052e8885783fac397d91fe",
+ "sha256:88e2b3c14bdb32e440be531ade29d3c50a1a59cd4e51b1dd8b0865c54ea5d2e2",
+ "sha256:8f8e709127c6c77446a8c0a8c8bf3c8ee706a06cd44b1e827c3e6a2ee6b8c098",
+ "sha256:9cb4a35b3642fc5c005a6755a5d17c6c8b6bcb6981baf81cea8bfbc8903e8ba8",
+ "sha256:9f90389693731ff1f659e55c7d1640e2ec43ff725cc61b04b2f9c6d8d017df6a",
+ "sha256:a09582f178759ee8128d9270cd1344154fd473bb77d94ce0aeb2a93ebf0feaf0",
+ "sha256:a6a14b17d7e17fa0d207ac08642c8820f84f25ce17a442fd15e27ea18d67c59b",
+ "sha256:a72e8961a86d19bdb45851d8f1f08b041ea37d2bd8d4fd19903bc3083d80c896",
+ "sha256:abd808f9c129ba2beda4cfc53bde801e5bcf9d6e0f22f095e45327c038bfe68e",
+ "sha256:ac0f5edd2360eea2f1daa9e26a41db02dd4b0451b48f7c318e217ee092a213e9",
+ "sha256:b29ebffcf550f9da55bec9e02ad430c992a87e5f512cd63388abb76f1036d8d2",
+ "sha256:b2ca4e77f9f47c55c194982e10f058db063937845bb2b7a86c84a6cfe0aefa8b",
+ "sha256:b7be2d771cdba2942e13215c4e340bfd76398e9227ad10402a8767ab1865d2e6",
+ "sha256:b84834d0cf97e7d27dd5b7f3aca7b6e9263c56308ab9dc8aae9784abb774d404",
+ "sha256:b86851a328eedc692acf81fb05444bdf1891747c25af7529e39ddafaf68a4f3f",
+ "sha256:bcb3ef43e58665bbda2fb198698fcae6776483e0c4a631aa5647806c25e02cc0",
+ "sha256:c0f31130ebc2d37cdd8e44605fb5fa7ad59049298b3f745c74fa74c62fbfcfc4",
+ "sha256:c6a164aa47843fb1b01e941d385aab7215563bb8816d80ff3a363a9f8448a8dc",
+ "sha256:d8a9d3ebe49f084ad71f9269834ceccbf398253c9fac910c4fd7053ff1386936",
+ "sha256:db8e577c19c0fda0beb7e0d4e09e0ba74b1e4c092e0e40bfa12fe05b6f6d75ba",
+ "sha256:dc9b18bf40cc75f66f40a7379f6a9513244fe33c0e8aa72e2d56b0196a7ef872",
+ "sha256:e09f3ff613345df5e8c3667da1d918f9149bd623cd9070c983c013792a9a62eb",
+ "sha256:e4108df7fe9b707191e55f33efbcb2d81928e10cea45527879a4749cbe472614",
+ "sha256:e6024675e67af929088fda399b2094574609396b1decb609c55fa58b028a32a1",
+ "sha256:e70f54f1796669ef691ca07d046cd81a29cb4deb1e5f942003f401c0c4a2695d",
+ "sha256:e715596e683d2ce000574bae5d07bd522c781a822866c20495e52520564f0969",
+ "sha256:e760191dd42581e023a68b758769e2da259b5d52e3103c6060ddc02c9edb8d7b",
+ "sha256:ed86a35631f7bfbb28e108dd96773b9d5a6ce4811cf6ea468bb6a359b256b1e4",
+ "sha256:ee07e47c12890ef248766a6e55bd38ebfb2bb8edd4142d56db91b21ea68b7627",
+ "sha256:fa3a0128b152627161ce47201262d3140edb5a5c3da88d73a1b790a959126956",
+ "sha256:fcc8eb6d5902bb1cf6dc4f187ee3ea80a1eba0a89aba40a5cb20a5087d961357"
+ ],
+ "markers": "platform_python_implementation != 'PyPy'",
+ "version": "==1.16.0"
+ },
+ "cryptography": {
+ "hashes": [
+ "sha256:0270572b8bd2c833c3981724b8ee9747b3ec96f699a9665470018594301439ee",
+ "sha256:111a0d8553afcf8eb02a4fea6ca4f59d48ddb34497aa8706a6cf536f1a5ec576",
+ "sha256:16a48c23a62a2f4a285699dba2e4ff2d1cff3115b9df052cdd976a18856d8e3d",
+ "sha256:1b95b98b0d2af784078fa69f637135e3c317091b615cd0905f8b8a087e86fa30",
+ "sha256:1f71c10d1e88467126f0efd484bd44bca5e14c664ec2ede64c32f20875c0d413",
+ "sha256:2424ff4c4ac7f6b8177b53c17ed5d8fa74ae5955656867f5a8affaca36a27abb",
+ "sha256:2bce03af1ce5a5567ab89bd90d11e7bbdff56b8af3acbbec1faded8f44cb06da",
+ "sha256:329906dcc7b20ff3cad13c069a78124ed8247adcac44b10bea1130e36caae0b4",
+ "sha256:37dd623507659e08be98eec89323469e8c7b4c1407c85112634ae3dbdb926fdd",
+ "sha256:3eaafe47ec0d0ffcc9349e1708be2aaea4c6dd4978d76bf6eb0cb2c13636c6fc",
+ "sha256:5e6275c09d2badf57aea3afa80d975444f4be8d3bc58f7f80d2a484c6f9485c8",
+ "sha256:6fe07eec95dfd477eb9530aef5bead34fec819b3aaf6c5bd6d20565da607bfe1",
+ "sha256:7367d7b2eca6513681127ebad53b2582911d1736dc2ffc19f2c3ae49997496bc",
+ "sha256:7cde5f38e614f55e28d831754e8a3bacf9ace5d1566235e39d91b35502d6936e",
+ "sha256:9481ffe3cf013b71b2428b905c4f7a9a4f76ec03065b05ff499bb5682a8d9ad8",
+ "sha256:98d8dc6d012b82287f2c3d26ce1d2dd130ec200c8679b6213b3c73c08b2b7940",
+ "sha256:a011a644f6d7d03736214d38832e030d8268bcff4a41f728e6030325fea3e400",
+ "sha256:a2913c5375154b6ef2e91c10b5720ea6e21007412f6437504ffea2109b5a33d7",
+ "sha256:a30596bae9403a342c978fb47d9b0ee277699fa53bbafad14706af51fe543d16",
+ "sha256:b03c2ae5d2f0fc05f9a2c0c997e1bc18c8229f392234e8a0194f202169ccd278",
+ "sha256:b6cd2203306b63e41acdf39aa93b86fb566049aeb6dc489b70e34bcd07adca74",
+ "sha256:b7ffe927ee6531c78f81aa17e684e2ff617daeba7f189f911065b2ea2d526dec",
+ "sha256:b8cac287fafc4ad485b8a9b67d0ee80c66bf3574f655d3b97ef2e1082360faf1",
+ "sha256:ba334e6e4b1d92442b75ddacc615c5476d4ad55cc29b15d590cc6b86efa487e2",
+ "sha256:ba3e4a42397c25b7ff88cdec6e2a16c2be18720f317506ee25210f6d31925f9c",
+ "sha256:c41fb5e6a5fe9ebcd58ca3abfeb51dffb5d83d6775405305bfa8715b76521922",
+ "sha256:cd2030f6650c089aeb304cf093f3244d34745ce0cfcc39f20c6fbfe030102e2a",
+ "sha256:cd65d75953847815962c84a4654a84850b2bb4aed3f26fadcc1c13892e1e29f6",
+ "sha256:e4985a790f921508f36f81831817cbc03b102d643b5fcb81cd33df3fa291a1a1",
+ "sha256:e807b3188f9eb0eaa7bbb579b462c5ace579f1cedb28107ce8b48a9f7ad3679e",
+ "sha256:f12764b8fffc7a123f641d7d049d382b73f96a34117e0b637b80643169cec8ac",
+ "sha256:f8837fe1d6ac4a8052a9a8ddab256bc006242696f03368a4009be7ee3075cdb7"
+ ],
+ "markers": "python_version >= '3.7'",
+ "version": "==42.0.5"
+ },
"jinja2": {
"hashes": [
- "sha256:74c935a1b8bb9a3947c50a54766a969d4846290e1e788ea44c1392163723c3bd",
- "sha256:f84be1bb0040caca4cea721fcbbbbd61f9be9464ca236387158b0feea01914a4"
+ "sha256:7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa",
+ "sha256:ac8bd6544d4bb2c9792bf3a159e80bba8fda7f07e81bc3aed565432d5925ba90"
],
"index": "pypi",
- "version": "==2.10.1"
+ "markers": "python_version >= '3.7'",
+ "version": "==3.1.3"
},
"markupsafe": {
"hashes": [
- "sha256:048ef924c1623740e70204aa7143ec592504045ae4429b59c30054cb31e3c432",
- "sha256:130f844e7f5bdd8e9f3f42e7102ef1d49b2e6fdf0d7526df3f87281a532d8c8b",
- "sha256:19f637c2ac5ae9da8bfd98cef74d64b7e1bb8a63038a3505cd182c3fac5eb4d9",
- "sha256:1b8a7a87ad1b92bd887568ce54b23565f3fd7018c4180136e1cf412b405a47af",
- "sha256:1c25694ca680b6919de53a4bb3bdd0602beafc63ff001fea2f2fc16ec3a11834",
- "sha256:1f19ef5d3908110e1e891deefb5586aae1b49a7440db952454b4e281b41620cd",
- "sha256:1fa6058938190ebe8290e5cae6c351e14e7bb44505c4a7624555ce57fbbeba0d",
- "sha256:31cbb1359e8c25f9f48e156e59e2eaad51cd5242c05ed18a8de6dbe85184e4b7",
- "sha256:3e835d8841ae7863f64e40e19477f7eb398674da6a47f09871673742531e6f4b",
- "sha256:4e97332c9ce444b0c2c38dd22ddc61c743eb208d916e4265a2a3b575bdccb1d3",
- "sha256:525396ee324ee2da82919f2ee9c9e73b012f23e7640131dd1b53a90206a0f09c",
- "sha256:52b07fbc32032c21ad4ab060fec137b76eb804c4b9a1c7c7dc562549306afad2",
- "sha256:52ccb45e77a1085ec5461cde794e1aa037df79f473cbc69b974e73940655c8d7",
- "sha256:5c3fbebd7de20ce93103cb3183b47671f2885307df4a17a0ad56a1dd51273d36",
- "sha256:5e5851969aea17660e55f6a3be00037a25b96a9b44d2083651812c99d53b14d1",
- "sha256:5edfa27b2d3eefa2210fb2f5d539fbed81722b49f083b2c6566455eb7422fd7e",
- "sha256:7d263e5770efddf465a9e31b78362d84d015cc894ca2c131901a4445eaa61ee1",
- "sha256:83381342bfc22b3c8c06f2dd93a505413888694302de25add756254beee8449c",
- "sha256:857eebb2c1dc60e4219ec8e98dfa19553dae33608237e107db9c6078b1167856",
- "sha256:98e439297f78fca3a6169fd330fbe88d78b3bb72f967ad9961bcac0d7fdd1550",
- "sha256:bf54103892a83c64db58125b3f2a43df6d2cb2d28889f14c78519394feb41492",
- "sha256:d9ac82be533394d341b41d78aca7ed0e0f4ba5a2231602e2f05aa87f25c51672",
- "sha256:e982fe07ede9fada6ff6705af70514a52beb1b2c3d25d4e873e82114cf3c5401",
- "sha256:edce2ea7f3dfc981c4ddc97add8a61381d9642dc3273737e756517cc03e84dd6",
- "sha256:efdc45ef1afc238db84cb4963aa689c0408912a0239b0721cb172b4016eb31d6",
- "sha256:f137c02498f8b935892d5c0172560d7ab54bc45039de8805075e19079c639a9c",
- "sha256:f82e347a72f955b7017a39708a3667f106e6ad4d10b25f237396a7115d8ed5fd",
- "sha256:fb7c206e01ad85ce57feeaaa0bf784b97fa3cad0d4a5737bc5295785f5c613a1"
- ],
- "version": "==1.1.0"
+ "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf",
+ "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff",
+ "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f",
+ "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3",
+ "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532",
+ "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f",
+ "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617",
+ "sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df",
+ "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4",
+ "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906",
+ "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f",
+ "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4",
+ "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8",
+ "sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371",
+ "sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2",
+ "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465",
+ "sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52",
+ "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6",
+ "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169",
+ "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad",
+ "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2",
+ "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0",
+ "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029",
+ "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f",
+ "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a",
+ "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced",
+ "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5",
+ "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c",
+ "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf",
+ "sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9",
+ "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb",
+ "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad",
+ "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3",
+ "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1",
+ "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46",
+ "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc",
+ "sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a",
+ "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee",
+ "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900",
+ "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5",
+ "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea",
+ "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f",
+ "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5",
+ "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e",
+ "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a",
+ "sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f",
+ "sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50",
+ "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a",
+ "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b",
+ "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4",
+ "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff",
+ "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2",
+ "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46",
+ "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b",
+ "sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf",
+ "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5",
+ "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5",
+ "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab",
+ "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd",
+ "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68"
+ ],
+ "markers": "python_version >= '3.7'",
+ "version": "==2.1.5"
+ },
+ "paramiko": {
+ "hashes": [
+ "sha256:43f0b51115a896f9c00f59618023484cb3a14b98bbceab43394a39c6739b7ee7",
+ "sha256:aac08f26a31dc4dffd92821527d1682d99d52f9ef6851968114a8728f3c274d3"
+ ],
+ "index": "pypi",
+ "markers": "python_version >= '3.6'",
+ "version": "==3.4.0"
+ },
+ "pycparser": {
+ "hashes": [
+ "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6",
+ "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"
+ ],
+ "markers": "python_version >= '3.8'",
+ "version": "==2.22"
+ },
+ "pynacl": {
+ "hashes": [
+ "sha256:06b8f6fa7f5de8d5d2f7573fe8c863c051225a27b61e6860fd047b1775807858",
+ "sha256:0c84947a22519e013607c9be43706dd42513f9e6ae5d39d3613ca1e142fba44d",
+ "sha256:20f42270d27e1b6a29f54032090b972d97f0a1b0948cc52392041ef7831fee93",
+ "sha256:401002a4aaa07c9414132aaed7f6836ff98f59277a234704ff66878c2ee4a0d1",
+ "sha256:52cb72a79269189d4e0dc537556f4740f7f0a9ec41c1322598799b0bdad4ef92",
+ "sha256:61f642bf2378713e2c2e1de73444a3778e5f0a38be6fee0fe532fe30060282ff",
+ "sha256:8ac7448f09ab85811607bdd21ec2464495ac8b7c66d146bf545b0f08fb9220ba",
+ "sha256:a36d4a9dda1f19ce6e03c9a784a2921a4b726b02e1c736600ca9c22029474394",
+ "sha256:a422368fc821589c228f4c49438a368831cb5bbc0eab5ebe1d7fac9dded6567b",
+ "sha256:e46dae94e34b085175f8abb3b0aaa7da40767865ac82c928eeb9e57e1ea8a543"
+ ],
+ "markers": "python_version >= '3.6'",
+ "version": "==1.5.0"
},
"vyos": {
"file": "./python"
@@ -64,128 +260,256 @@
"develop": {
"astroid": {
"hashes": [
- "sha256:35b032003d6a863f5dcd7ec11abd5cd5893428beaa31ab164982403bcb311f22",
- "sha256:6a5d668d7dc69110de01cdf7aeec69a679ef486862a0850cc0fd5571505b6b7e"
+ "sha256:951798f922990137ac090c53af473db7ab4e70c770e6d7fae0cec59f74411819",
+ "sha256:ac248253bfa4bd924a0de213707e7ebeeb3138abeb48d798784ead1e56d419d4"
],
- "version": "==2.1.0"
+ "markers": "python_full_version >= '3.8.0'",
+ "version": "==3.1.0"
},
"coverage": {
"hashes": [
- "sha256:09e47c529ff77bf042ecfe858fb55c3e3eb97aac2c87f0349ab5a7efd6b3939f",
- "sha256:0a1f9b0eb3aa15c990c328535655847b3420231af299386cfe5efc98f9c250fe",
- "sha256:0cc941b37b8c2ececfed341444a456912e740ecf515d560de58b9a76562d966d",
- "sha256:10e8af18d1315de936d67775d3a814cc81d0747a1a0312d84e27ae5610e313b0",
- "sha256:1b4276550b86caa60606bd3572b52769860a81a70754a54acc8ba789ce74d607",
- "sha256:1e8a2627c48266c7b813975335cfdea58c706fe36f607c97d9392e61502dc79d",
- "sha256:2b224052bfd801beb7478b03e8a66f3f25ea56ea488922e98903914ac9ac930b",
- "sha256:447c450a093766744ab53bf1e7063ec82866f27bcb4f4c907da25ad293bba7e3",
- "sha256:46101fc20c6f6568561cdd15a54018bb42980954b79aa46da8ae6f008066a30e",
- "sha256:4710dc676bb4b779c4361b54eb308bc84d64a2fa3d78e5f7228921eccce5d815",
- "sha256:510986f9a280cd05189b42eee2b69fecdf5bf9651d4cd315ea21d24a964a3c36",
- "sha256:5535dda5739257effef56e49a1c51c71f1d37a6e5607bb25a5eee507c59580d1",
- "sha256:5a7524042014642b39b1fcae85fb37556c200e64ec90824ae9ecf7b667ccfc14",
- "sha256:5f55028169ef85e1fa8e4b8b1b91c0b3b0fa3297c4fb22990d46ff01d22c2d6c",
- "sha256:6694d5573e7790a0e8d3d177d7a416ca5f5c150742ee703f3c18df76260de794",
- "sha256:6831e1ac20ac52634da606b658b0b2712d26984999c9d93f0c6e59fe62ca741b",
- "sha256:77f0d9fa5e10d03aa4528436e33423bfa3718b86c646615f04616294c935f840",
- "sha256:828ad813c7cdc2e71dcf141912c685bfe4b548c0e6d9540db6418b807c345ddd",
- "sha256:85a06c61598b14b015d4df233d249cd5abfa61084ef5b9f64a48e997fd829a82",
- "sha256:8cb4febad0f0b26c6f62e1628f2053954ad2c555d67660f28dfb1b0496711952",
- "sha256:a5c58664b23b248b16b96253880b2868fb34358911400a7ba39d7f6399935389",
- "sha256:aaa0f296e503cda4bc07566f592cd7a28779d433f3a23c48082af425d6d5a78f",
- "sha256:ab235d9fe64833f12d1334d29b558aacedfbca2356dfb9691f2d0d38a8a7bfb4",
- "sha256:b3b0c8f660fae65eac74fbf003f3103769b90012ae7a460863010539bb7a80da",
- "sha256:bab8e6d510d2ea0f1d14f12642e3f35cefa47a9b2e4c7cea1852b52bc9c49647",
- "sha256:c45297bbdbc8bb79b02cf41417d63352b70bcb76f1bbb1ee7d47b3e89e42f95d",
- "sha256:d19bca47c8a01b92640c614a9147b081a1974f69168ecd494687c827109e8f42",
- "sha256:d64b4340a0c488a9e79b66ec9f9d77d02b99b772c8b8afd46c1294c1d39ca478",
- "sha256:da969da069a82bbb5300b59161d8d7c8d423bc4ccd3b410a9b4d8932aeefc14b",
- "sha256:ed02c7539705696ecb7dc9d476d861f3904a8d2b7e894bd418994920935d36bb",
- "sha256:ee5b8abc35b549012e03a7b1e86c09491457dba6c94112a2482b18589cc2bdb9"
+ "sha256:00838a35b882694afda09f85e469c96367daa3f3f2b097d846a7216993d37f4c",
+ "sha256:0513b9508b93da4e1716744ef6ebc507aff016ba115ffe8ecff744d1322a7b63",
+ "sha256:09c3255458533cb76ef55da8cc49ffab9e33f083739c8bd4f58e79fecfe288f7",
+ "sha256:09ef9199ed6653989ebbcaacc9b62b514bb63ea2f90256e71fea3ed74bd8ff6f",
+ "sha256:09fa497a8ab37784fbb20ab699c246053ac294d13fc7eb40ec007a5043ec91f8",
+ "sha256:0f9f50e7ef2a71e2fae92774c99170eb8304e3fdf9c8c3c7ae9bab3e7229c5cf",
+ "sha256:137eb07173141545e07403cca94ab625cc1cc6bc4c1e97b6e3846270e7e1fea0",
+ "sha256:1f384c3cc76aeedce208643697fb3e8437604b512255de6d18dae3f27655a384",
+ "sha256:201bef2eea65e0e9c56343115ba3814e896afe6d36ffd37bab783261db430f76",
+ "sha256:38dd60d7bf242c4ed5b38e094baf6401faa114fc09e9e6632374388a404f98e7",
+ "sha256:3b799445b9f7ee8bf299cfaed6f5b226c0037b74886a4e11515e569b36fe310d",
+ "sha256:3ea79bb50e805cd6ac058dfa3b5c8f6c040cb87fe83de10845857f5535d1db70",
+ "sha256:40209e141059b9370a2657c9b15607815359ab3ef9918f0196b6fccce8d3230f",
+ "sha256:41c9c5f3de16b903b610d09650e5e27adbfa7f500302718c9ffd1c12cf9d6818",
+ "sha256:54eb8d1bf7cacfbf2a3186019bcf01d11c666bd495ed18717162f7eb1e9dd00b",
+ "sha256:598825b51b81c808cb6f078dcb972f96af96b078faa47af7dfcdf282835baa8d",
+ "sha256:5fc1de20b2d4a061b3df27ab9b7c7111e9a710f10dc2b84d33a4ab25065994ec",
+ "sha256:623512f8ba53c422fcfb2ce68362c97945095b864cda94a92edbaf5994201083",
+ "sha256:690db6517f09336559dc0b5f55342df62370a48f5469fabf502db2c6d1cffcd2",
+ "sha256:69eb372f7e2ece89f14751fbcbe470295d73ed41ecd37ca36ed2eb47512a6ab9",
+ "sha256:73bfb9c09951125d06ee473bed216e2c3742f530fc5acc1383883125de76d9cd",
+ "sha256:742a76a12aa45b44d236815d282b03cfb1de3b4323f3e4ec933acfae08e54ade",
+ "sha256:7c95949560050d04d46b919301826525597f07b33beba6187d04fa64d47ac82e",
+ "sha256:8130a2aa2acb8788e0b56938786c33c7c98562697bf9f4c7d6e8e5e3a0501e4a",
+ "sha256:8a2b2b78c78293782fd3767d53e6474582f62443d0504b1554370bde86cc8227",
+ "sha256:8ce1415194b4a6bd0cdcc3a1dfbf58b63f910dcb7330fe15bdff542c56949f87",
+ "sha256:9ca28a302acb19b6af89e90f33ee3e1906961f94b54ea37de6737b7ca9d8827c",
+ "sha256:a4cdc86d54b5da0df6d3d3a2f0b710949286094c3a6700c21e9015932b81447e",
+ "sha256:aa5b1c1bfc28384f1f53b69a023d789f72b2e0ab1b3787aae16992a7ca21056c",
+ "sha256:aadacf9a2f407a4688d700e4ebab33a7e2e408f2ca04dbf4aef17585389eff3e",
+ "sha256:ae71e7ddb7a413dd60052e90528f2f65270aad4b509563af6d03d53e979feafd",
+ "sha256:b14706df8b2de49869ae03a5ccbc211f4041750cd4a66f698df89d44f4bd30ec",
+ "sha256:b1a93009cb80730c9bca5d6d4665494b725b6e8e157c1cb7f2db5b4b122ea562",
+ "sha256:b2991665420a803495e0b90a79233c1433d6ed77ef282e8e152a324bbbc5e0c8",
+ "sha256:b2c5edc4ac10a7ef6605a966c58929ec6c1bd0917fb8c15cb3363f65aa40e677",
+ "sha256:b4d33f418f46362995f1e9d4f3a35a1b6322cb959c31d88ae56b0298e1c22357",
+ "sha256:b91cbc4b195444e7e258ba27ac33769c41b94967919f10037e6355e998af255c",
+ "sha256:c74880fc64d4958159fbd537a091d2a585448a8f8508bf248d72112723974cbd",
+ "sha256:c901df83d097649e257e803be22592aedfd5182f07b3cc87d640bbb9afd50f49",
+ "sha256:cac99918c7bba15302a2d81f0312c08054a3359eaa1929c7e4b26ebe41e9b286",
+ "sha256:cc4f1358cb0c78edef3ed237ef2c86056206bb8d9140e73b6b89fbcfcbdd40e1",
+ "sha256:ccd341521be3d1b3daeb41960ae94a5e87abe2f46f17224ba5d6f2b8398016cf",
+ "sha256:ce4b94265ca988c3f8e479e741693d143026632672e3ff924f25fab50518dd51",
+ "sha256:cf271892d13e43bc2b51e6908ec9a6a5094a4df1d8af0bfc360088ee6c684409",
+ "sha256:d5ae728ff3b5401cc320d792866987e7e7e880e6ebd24433b70a33b643bb0384",
+ "sha256:d71eec7d83298f1af3326ce0ff1d0ea83c7cb98f72b577097f9083b20bdaf05e",
+ "sha256:d898fe162d26929b5960e4e138651f7427048e72c853607f2b200909794ed978",
+ "sha256:d89d7b2974cae412400e88f35d86af72208e1ede1a541954af5d944a8ba46c57",
+ "sha256:dfa8fe35a0bb90382837b238fff375de15f0dcdb9ae68ff85f7a63649c98527e",
+ "sha256:e0be5efd5127542ef31f165de269f77560d6cdef525fffa446de6f7e9186cfb2",
+ "sha256:fdfafb32984684eb03c2d83e1e51f64f0906b11e64482df3c5db936ce3839d48",
+ "sha256:ff7687ca3d7028d8a5f0ebae95a6e4827c5616b31a4ee1192bdfde697db110d4"
],
"index": "pypi",
- "version": "==4.5.2"
+ "markers": "python_version >= '3.8'",
+ "version": "==7.4.4"
+ },
+ "dill": {
+ "hashes": [
+ "sha256:3ebe3c479ad625c4553aca177444d89b486b1d84982eeacded644afc0cf797ca",
+ "sha256:c36ca9ffb54365bdd2f8eb3eff7d2a21237f8452b57ace88b1ac615b7e815bd7"
+ ],
+ "markers": "python_version >= '3.11'",
+ "version": "==0.3.8"
},
"isort": {
"hashes": [
- "sha256:1153601da39a25b14ddc54955dbbacbb6b2d19135386699e2ad58517953b34af",
- "sha256:b9c40e9750f3d77e6e4d441d8b0266cf555e7cdabdcff33c4fd06366ca761ef8",
- "sha256:ec9ef8f4a9bc6f71eec99e1806bfa2de401650d996c59330782b89a5555c1497"
- ],
- "version": "==4.3.4"
- },
- "lazy-object-proxy": {
- "hashes": [
- "sha256:0ce34342b419bd8f018e6666bfef729aec3edf62345a53b537a4dcc115746a33",
- "sha256:1b668120716eb7ee21d8a38815e5eb3bb8211117d9a90b0f8e21722c0758cc39",
- "sha256:209615b0fe4624d79e50220ce3310ca1a9445fd8e6d3572a896e7f9146bbf019",
- "sha256:27bf62cb2b1a2068d443ff7097ee33393f8483b570b475db8ebf7e1cba64f088",
- "sha256:27ea6fd1c02dcc78172a82fc37fcc0992a94e4cecf53cb6d73f11749825bd98b",
- "sha256:2c1b21b44ac9beb0fc848d3993924147ba45c4ebc24be19825e57aabbe74a99e",
- "sha256:2df72ab12046a3496a92476020a1a0abf78b2a7db9ff4dc2036b8dd980203ae6",
- "sha256:320ffd3de9699d3892048baee45ebfbbf9388a7d65d832d7e580243ade426d2b",
- "sha256:50e3b9a464d5d08cc5227413db0d1c4707b6172e4d4d915c1c70e4de0bbff1f5",
- "sha256:5276db7ff62bb7b52f77f1f51ed58850e315154249aceb42e7f4c611f0f847ff",
- "sha256:61a6cf00dcb1a7f0c773ed4acc509cb636af2d6337a08f362413c76b2b47a8dd",
- "sha256:6ae6c4cb59f199d8827c5a07546b2ab7e85d262acaccaacd49b62f53f7c456f7",
- "sha256:7661d401d60d8bf15bb5da39e4dd72f5d764c5aff5a86ef52a042506e3e970ff",
- "sha256:7bd527f36a605c914efca5d3d014170b2cb184723e423d26b1fb2fd9108e264d",
- "sha256:7cb54db3535c8686ea12e9535eb087d32421184eacc6939ef15ef50f83a5e7e2",
- "sha256:7f3a2d740291f7f2c111d86a1c4851b70fb000a6c8883a59660d95ad57b9df35",
- "sha256:81304b7d8e9c824d058087dcb89144842c8e0dea6d281c031f59f0acf66963d4",
- "sha256:933947e8b4fbe617a51528b09851685138b49d511af0b6c0da2539115d6d4514",
- "sha256:94223d7f060301b3a8c09c9b3bc3294b56b2188e7d8179c762a1cda72c979252",
- "sha256:ab3ca49afcb47058393b0122428358d2fbe0408cf99f1b58b295cfeb4ed39109",
- "sha256:bd6292f565ca46dee4e737ebcc20742e3b5be2b01556dafe169f6c65d088875f",
- "sha256:cb924aa3e4a3fb644d0c463cad5bc2572649a6a3f68a7f8e4fbe44aaa6d77e4c",
- "sha256:d0fc7a286feac9077ec52a927fc9fe8fe2fabab95426722be4c953c9a8bede92",
- "sha256:ddc34786490a6e4ec0a855d401034cbd1242ef186c20d79d2166d6a4bd449577",
- "sha256:e34b155e36fa9da7e1b7c738ed7767fc9491a62ec6af70fe9da4a057759edc2d",
- "sha256:e5b9e8f6bda48460b7b143c3821b21b452cb3a835e6bbd5dd33aa0c8d3f5137d",
- "sha256:e81ebf6c5ee9684be8f2c87563880f93eedd56dd2b6146d8a725b50b7e5adb0f",
- "sha256:eb91be369f945f10d3a49f5f9be8b3d0b93a4c2be8f8a5b83b0571b8123e0a7a",
- "sha256:f460d1ceb0e4a5dcb2a652db0904224f367c9b3c1470d5a7683c0480e582468b"
- ],
- "version": "==1.3.1"
+ "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109",
+ "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"
+ ],
+ "markers": "python_full_version >= '3.8.0'",
+ "version": "==5.13.2"
},
"lxml": {
"hashes": [
- "sha256:0dd6589fa75d369ba06d2b5f38dae107f76ea127f212f6a7bee134f6df2d1d21",
- "sha256:1afbac344aa68c29e81ab56c1a9411c3663157b5aee5065b7fa030b398d4f7e0",
- "sha256:1baad9d073692421ad5dbbd81430aba6c7f5fdc347f03537ae046ddf2c9b2297",
- "sha256:1d8736421a2358becd3edf20260e41a06a0bf08a560480d3a5734a6bcbacf591",
- "sha256:1e1d9bddc5afaddf0de76246d3f2152f961697ad7439c559f179002682c45801",
- "sha256:1f179dc8b2643715f020f4d119d5529b02cd794c1c8f305868b73b8674d2a03f",
- "sha256:241fb7bdf97cb1df1edfa8f0bcdfd80525d4023dac4523a241907c8b2f44e541",
- "sha256:2f9765ee5acd3dbdcdc0d0c79309e01f7c16bc8d39b49250bf88de7b46daaf58",
- "sha256:312e1e1b1c3ce0c67e0b8105317323e12807955e8186872affb667dbd67971f6",
- "sha256:3273db1a8055ca70257fd3691c6d2c216544e1a70b673543e15cc077d8e9c730",
- "sha256:34dfaa8c02891f9a246b17a732ca3e99c5e42802416628e740a5d1cb2f50ff49",
- "sha256:3aa3f5288af349a0f3a96448ebf2e57e17332d99f4f30b02093b7948bd9f94cc",
- "sha256:51102e160b9d83c1cc435162d90b8e3c8c93b28d18d87b60c56522d332d26879",
- "sha256:56115fc2e2a4140e8994eb9585119a1ae9223b506826089a3ba753a62bd194a6",
- "sha256:69d83de14dbe8fe51dccfd36f88bf0b40f5debeac763edf9f8325180190eba6e",
- "sha256:99fdce94aeaa3ccbdfcb1e23b34273605c5853aa92ec23d84c84765178662c6c",
- "sha256:a7c0cd5b8a20f3093ee4a67374ccb3b8a126743b15a4d759e2a1bf098faac2b2",
- "sha256:abe12886554634ed95416a46701a917784cb2b4c77bfacac6916681d49bbf83d",
- "sha256:b4f67b5183bd5f9bafaeb76ad119e977ba570d2b0e61202f534ac9b5c33b4485",
- "sha256:bdd7c1658475cc1b867b36d5c4ed4bc316be8d3368abe03d348ba906a1f83b0e",
- "sha256:c6f24149a19f611a415a51b9bc5f17b6c2f698e0d6b41ffb3fa9f24d35d05d73",
- "sha256:d1e111b3ab98613115a208c1017f266478b0ab224a67bc8eac670fa0bad7d488",
- "sha256:d6520aa965773bbab6cb7a791d5895b00d02cf9adc93ac2bf4edb9ac1a6addc5",
- "sha256:dd185cde2ccad7b649593b0cda72021bc8a91667417001dbaf24cd746ecb7c11",
- "sha256:de2e5b0828a9d285f909b5d2e9d43f1cf6cf21fe65bc7660bdaa1780c7b58298",
- "sha256:f726444b8e909c4f41b4fde416e1071cf28fa84634bfb4befdf400933b6463af"
+ "sha256:04ab5415bf6c86e0518d57240a96c4d1fcfc3cb370bb2ac2a732b67f579e5a04",
+ "sha256:057cdc6b86ab732cf361f8b4d8af87cf195a1f6dc5b0ff3de2dced242c2015e0",
+ "sha256:058a1308914f20784c9f4674036527e7c04f7be6fb60f5d61353545aa7fcb739",
+ "sha256:08802f0c56ed150cc6885ae0788a321b73505d2263ee56dad84d200cab11c07a",
+ "sha256:0a15438253b34e6362b2dc41475e7f80de76320f335e70c5528b7148cac253a1",
+ "sha256:0c3f67e2aeda739d1cc0b1102c9a9129f7dc83901226cc24dd72ba275ced4218",
+ "sha256:0e7259016bc4345a31af861fdce942b77c99049d6c2107ca07dc2bba2435c1d9",
+ "sha256:0ed777c1e8c99b63037b91f9d73a6aad20fd035d77ac84afcc205225f8f41188",
+ "sha256:0f5d65c39f16717a47c36c756af0fb36144069c4718824b7533f803ecdf91138",
+ "sha256:0f8c09ed18ecb4ebf23e02b8e7a22a05d6411911e6fabef3a36e4f371f4f2585",
+ "sha256:11a04306fcba10cd9637e669fd73aa274c1c09ca64af79c041aa820ea992b637",
+ "sha256:1ae67b4e737cddc96c99461d2f75d218bdf7a0c3d3ad5604d1f5e7464a2f9ffe",
+ "sha256:1c5bb205e9212d0ebddf946bc07e73fa245c864a5f90f341d11ce7b0b854475d",
+ "sha256:1f7785f4f789fdb522729ae465adcaa099e2a3441519df750ebdccc481d961a1",
+ "sha256:200e63525948e325d6a13a76ba2911f927ad399ef64f57898cf7c74e69b71095",
+ "sha256:21c2e6b09565ba5b45ae161b438e033a86ad1736b8c838c766146eff8ceffff9",
+ "sha256:2213afee476546a7f37c7a9b4ad4d74b1e112a6fafffc9185d6d21f043128c81",
+ "sha256:27aa20d45c2e0b8cd05da6d4759649170e8dfc4f4e5ef33a34d06f2d79075d57",
+ "sha256:2a66bf12fbd4666dd023b6f51223aed3d9f3b40fef06ce404cb75bafd3d89536",
+ "sha256:2c9d147f754b1b0e723e6afb7ba1566ecb162fe4ea657f53d2139bbf894d050a",
+ "sha256:2ddfe41ddc81f29a4c44c8ce239eda5ade4e7fc305fb7311759dd6229a080052",
+ "sha256:31e9a882013c2f6bd2f2c974241bf4ba68c85eba943648ce88936d23209a2e01",
+ "sha256:3249cc2989d9090eeac5467e50e9ec2d40704fea9ab72f36b034ea34ee65ca98",
+ "sha256:3545039fa4779be2df51d6395e91a810f57122290864918b172d5dc7ca5bb433",
+ "sha256:394ed3924d7a01b5bd9a0d9d946136e1c2f7b3dc337196d99e61740ed4bc6fe1",
+ "sha256:3a6b45da02336895da82b9d472cd274b22dc27a5cea1d4b793874eead23dd14f",
+ "sha256:3a74c4f27167cb95c1d4af1c0b59e88b7f3e0182138db2501c353555f7ec57f4",
+ "sha256:3d0c3dd24bb4605439bf91068598d00c6370684f8de4a67c2992683f6c309d6b",
+ "sha256:3dbe858ee582cbb2c6294dc85f55b5f19c918c2597855e950f34b660f1a5ede6",
+ "sha256:3dc773b2861b37b41a6136e0b72a1a44689a9c4c101e0cddb6b854016acc0aa8",
+ "sha256:3e183c6e3298a2ed5af9d7a356ea823bccaab4ec2349dc9ed83999fd289d14d5",
+ "sha256:3f7765e69bbce0906a7c74d5fe46d2c7a7596147318dbc08e4a2431f3060e306",
+ "sha256:417d14450f06d51f363e41cace6488519038f940676ce9664b34ebf5653433a5",
+ "sha256:44f6c7caff88d988db017b9b0e4ab04934f11e3e72d478031efc7edcac6c622f",
+ "sha256:491755202eb21a5e350dae00c6d9a17247769c64dcf62d8c788b5c135e179dc4",
+ "sha256:4951e4f7a5680a2db62f7f4ab2f84617674d36d2d76a729b9a8be4b59b3659be",
+ "sha256:52421b41ac99e9d91934e4d0d0fe7da9f02bfa7536bb4431b4c05c906c8c6919",
+ "sha256:530e7c04f72002d2f334d5257c8a51bf409db0316feee7c87e4385043be136af",
+ "sha256:533658f8fbf056b70e434dff7e7aa611bcacb33e01f75de7f821810e48d1bb66",
+ "sha256:5670fb70a828663cc37552a2a85bf2ac38475572b0e9b91283dc09efb52c41d1",
+ "sha256:56c22432809085b3f3ae04e6e7bdd36883d7258fcd90e53ba7b2e463efc7a6af",
+ "sha256:58278b29cb89f3e43ff3e0c756abbd1518f3ee6adad9e35b51fb101c1c1daaec",
+ "sha256:588008b8497667f1ddca7c99f2f85ce8511f8f7871b4a06ceede68ab62dff64b",
+ "sha256:59565f10607c244bc4c05c0c5fa0c190c990996e0c719d05deec7030c2aa8289",
+ "sha256:59689a75ba8d7ffca577aefd017d08d659d86ad4585ccc73e43edbfc7476781a",
+ "sha256:5aea8212fb823e006b995c4dda533edcf98a893d941f173f6c9506126188860d",
+ "sha256:5c670c0406bdc845b474b680b9a5456c561c65cf366f8db5a60154088c92d102",
+ "sha256:5ca1e8188b26a819387b29c3895c47a5e618708fe6f787f3b1a471de2c4a94d9",
+ "sha256:5d077bc40a1fe984e1a9931e801e42959a1e6598edc8a3223b061d30fbd26bbc",
+ "sha256:5d5792e9b3fb8d16a19f46aa8208987cfeafe082363ee2745ea8b643d9cc5b45",
+ "sha256:5dd1537e7cc06efd81371f5d1a992bd5ab156b2b4f88834ca852de4a8ea523fa",
+ "sha256:5ea7b6766ac2dfe4bcac8b8595107665a18ef01f8c8343f00710b85096d1b53a",
+ "sha256:622020d4521e22fb371e15f580d153134bfb68d6a429d1342a25f051ec72df1c",
+ "sha256:627402ad8dea044dde2eccde4370560a2b750ef894c9578e1d4f8ffd54000461",
+ "sha256:644df54d729ef810dcd0f7732e50e5ad1bd0a135278ed8d6bcb06f33b6b6f708",
+ "sha256:64641a6068a16201366476731301441ce93457eb8452056f570133a6ceb15fca",
+ "sha256:64c2baa7774bc22dd4474248ba16fe1a7f611c13ac6123408694d4cc93d66dbd",
+ "sha256:6588c459c5627fefa30139be4d2e28a2c2a1d0d1c265aad2ba1935a7863a4913",
+ "sha256:66bc5eb8a323ed9894f8fa0ee6cb3e3fb2403d99aee635078fd19a8bc7a5a5da",
+ "sha256:68a2610dbe138fa8c5826b3f6d98a7cfc29707b850ddcc3e21910a6fe51f6ca0",
+ "sha256:6935bbf153f9a965f1e07c2649c0849d29832487c52bb4a5c5066031d8b44fd5",
+ "sha256:6992030d43b916407c9aa52e9673612ff39a575523c5f4cf72cdef75365709a5",
+ "sha256:6a014510830df1475176466b6087fc0c08b47a36714823e58d8b8d7709132a96",
+ "sha256:6ab833e4735a7e5533711a6ea2df26459b96f9eec36d23f74cafe03631647c41",
+ "sha256:6cc6ee342fb7fa2471bd9b6d6fdfc78925a697bf5c2bcd0a302e98b0d35bfad3",
+ "sha256:6cf58416653c5901e12624e4013708b6e11142956e7f35e7a83f1ab02f3fe456",
+ "sha256:70a9768e1b9d79edca17890175ba915654ee1725975d69ab64813dd785a2bd5c",
+ "sha256:70ac664a48aa64e5e635ae5566f5227f2ab7f66a3990d67566d9907edcbbf867",
+ "sha256:71e97313406ccf55d32cc98a533ee05c61e15d11b99215b237346171c179c0b0",
+ "sha256:7221d49259aa1e5a8f00d3d28b1e0b76031655ca74bb287123ef56c3db92f213",
+ "sha256:74b28c6334cca4dd704e8004cba1955af0b778cf449142e581e404bd211fb619",
+ "sha256:764b521b75701f60683500d8621841bec41a65eb739b8466000c6fdbc256c240",
+ "sha256:78bfa756eab503673991bdcf464917ef7845a964903d3302c5f68417ecdc948c",
+ "sha256:794f04eec78f1d0e35d9e0c36cbbb22e42d370dda1609fb03bcd7aeb458c6377",
+ "sha256:79bd05260359170f78b181b59ce871673ed01ba048deef4bf49a36ab3e72e80b",
+ "sha256:7a7efd5b6d3e30d81ec68ab8a88252d7c7c6f13aaa875009fe3097eb4e30b84c",
+ "sha256:7c17b64b0a6ef4e5affae6a3724010a7a66bda48a62cfe0674dabd46642e8b54",
+ "sha256:804f74efe22b6a227306dd890eecc4f8c59ff25ca35f1f14e7482bbce96ef10b",
+ "sha256:853e074d4931dbcba7480d4dcab23d5c56bd9607f92825ab80ee2bd916edea53",
+ "sha256:857500f88b17a6479202ff5fe5f580fc3404922cd02ab3716197adf1ef628029",
+ "sha256:865bad62df277c04beed9478fe665b9ef63eb28fe026d5dedcb89b537d2e2ea6",
+ "sha256:88e22fc0a6684337d25c994381ed8a1580a6f5ebebd5ad41f89f663ff4ec2885",
+ "sha256:8b9c07e7a45bb64e21df4b6aa623cb8ba214dfb47d2027d90eac197329bb5e94",
+ "sha256:8de8f9d6caa7f25b204fc861718815d41cbcf27ee8f028c89c882a0cf4ae4134",
+ "sha256:8e77c69d5892cb5ba71703c4057091e31ccf534bd7f129307a4d084d90d014b8",
+ "sha256:9123716666e25b7b71c4e1789ec829ed18663152008b58544d95b008ed9e21e9",
+ "sha256:958244ad566c3ffc385f47dddde4145088a0ab893504b54b52c041987a8c1863",
+ "sha256:96323338e6c14e958d775700ec8a88346014a85e5de73ac7967db0367582049b",
+ "sha256:9676bfc686fa6a3fa10cd4ae6b76cae8be26eb5ec6811d2a325636c460da1806",
+ "sha256:9b0ff53900566bc6325ecde9181d89afadc59c5ffa39bddf084aaedfe3b06a11",
+ "sha256:9b9ec9c9978b708d488bec36b9e4c94d88fd12ccac3e62134a9d17ddba910ea9",
+ "sha256:9c6ad0fbf105f6bcc9300c00010a2ffa44ea6f555df1a2ad95c88f5656104817",
+ "sha256:9ca66b8e90daca431b7ca1408cae085d025326570e57749695d6a01454790e95",
+ "sha256:9e2addd2d1866fe112bc6f80117bcc6bc25191c5ed1bfbcf9f1386a884252ae8",
+ "sha256:a0af35bd8ebf84888373630f73f24e86bf016642fb8576fba49d3d6b560b7cbc",
+ "sha256:a2b44bec7adf3e9305ce6cbfa47a4395667e744097faed97abb4728748ba7d47",
+ "sha256:a2dfe7e2473f9b59496247aad6e23b405ddf2e12ef0765677b0081c02d6c2c0b",
+ "sha256:a55ee573116ba208932e2d1a037cc4b10d2c1cb264ced2184d00b18ce585b2c0",
+ "sha256:a7baf9ffc238e4bf401299f50e971a45bfcc10a785522541a6e3179c83eabf0a",
+ "sha256:a8d5c70e04aac1eda5c829a26d1f75c6e5286c74743133d9f742cda8e53b9c2f",
+ "sha256:a91481dbcddf1736c98a80b122afa0f7296eeb80b72344d7f45dc9f781551f56",
+ "sha256:ab31a88a651039a07a3ae327d68ebdd8bc589b16938c09ef3f32a4b809dc96ef",
+ "sha256:abc25c3cab9ec7fcd299b9bcb3b8d4a1231877e425c650fa1c7576c5107ab851",
+ "sha256:adfb84ca6b87e06bc6b146dc7da7623395db1e31621c4785ad0658c5028b37d7",
+ "sha256:afbbdb120d1e78d2ba8064a68058001b871154cc57787031b645c9142b937a62",
+ "sha256:afd5562927cdef7c4f5550374acbc117fd4ecc05b5007bdfa57cc5355864e0a4",
+ "sha256:b070bbe8d3f0f6147689bed981d19bbb33070225373338df755a46893528104a",
+ "sha256:b0b58fbfa1bf7367dde8a557994e3b1637294be6cf2169810375caf8571a085c",
+ "sha256:b560e3aa4b1d49e0e6c847d72665384db35b2f5d45f8e6a5c0072e0283430533",
+ "sha256:b6241d4eee5f89453307c2f2bfa03b50362052ca0af1efecf9fef9a41a22bb4f",
+ "sha256:b6787b643356111dfd4032b5bffe26d2f8331556ecb79e15dacb9275da02866e",
+ "sha256:bcbf4af004f98793a95355980764b3d80d47117678118a44a80b721c9913436a",
+ "sha256:beb72935a941965c52990f3a32d7f07ce869fe21c6af8b34bf6a277b33a345d3",
+ "sha256:bf2e2458345d9bffb0d9ec16557d8858c9c88d2d11fed53998512504cd9df49b",
+ "sha256:c2d35a1d047efd68027817b32ab1586c1169e60ca02c65d428ae815b593e65d4",
+ "sha256:c38d7b9a690b090de999835f0443d8aa93ce5f2064035dfc48f27f02b4afc3d0",
+ "sha256:c6f2c8372b98208ce609c9e1d707f6918cc118fea4e2c754c9f0812c04ca116d",
+ "sha256:c817d420c60a5183953c783b0547d9eb43b7b344a2c46f69513d5952a78cddf3",
+ "sha256:c8ba129e6d3b0136a0f50345b2cb3db53f6bda5dd8c7f5d83fbccba97fb5dcb5",
+ "sha256:c94e75445b00319c1fad60f3c98b09cd63fe1134a8a953dcd48989ef42318534",
+ "sha256:cc4691d60512798304acb9207987e7b2b7c44627ea88b9d77489bbe3e6cc3bd4",
+ "sha256:cc518cea79fd1e2f6c90baafa28906d4309d24f3a63e801d855e7424c5b34144",
+ "sha256:cd53553ddad4a9c2f1f022756ae64abe16da1feb497edf4d9f87f99ec7cf86bd",
+ "sha256:cf22b41fdae514ee2f1691b6c3cdeae666d8b7fa9434de445f12bbeee0cf48dd",
+ "sha256:d38c8f50ecf57f0463399569aa388b232cf1a2ffb8f0a9a5412d0db57e054860",
+ "sha256:d3be9b2076112e51b323bdf6d5a7f8a798de55fb8d95fcb64bd179460cdc0704",
+ "sha256:d4f2cc7060dc3646632d7f15fe68e2fa98f58e35dd5666cd525f3b35d3fed7f8",
+ "sha256:d7520db34088c96cc0e0a3ad51a4fd5b401f279ee112aa2b7f8f976d8582606d",
+ "sha256:d793bebb202a6000390a5390078e945bbb49855c29c7e4d56a85901326c3b5d9",
+ "sha256:da052e7962ea2d5e5ef5bc0355d55007407087392cf465b7ad84ce5f3e25fe0f",
+ "sha256:dae0ed02f6b075426accbf6b2863c3d0a7eacc1b41fb40f2251d931e50188dad",
+ "sha256:ddc678fb4c7e30cf830a2b5a8d869538bc55b28d6c68544d09c7d0d8f17694dc",
+ "sha256:df2e6f546c4df14bc81f9498bbc007fbb87669f1bb707c6138878c46b06f6510",
+ "sha256:e02c5175f63effbd7c5e590399c118d5db6183bbfe8e0d118bdb5c2d1b48d937",
+ "sha256:e196a4ff48310ba62e53a8e0f97ca2bca83cdd2fe2934d8b5cb0df0a841b193a",
+ "sha256:e233db59c8f76630c512ab4a4daf5a5986da5c3d5b44b8e9fc742f2a24dbd460",
+ "sha256:e32be23d538753a8adb6c85bd539f5fd3b15cb987404327c569dfc5fd8366e85",
+ "sha256:e3d30321949861404323c50aebeb1943461a67cd51d4200ab02babc58bd06a86",
+ "sha256:e89580a581bf478d8dcb97d9cd011d567768e8bc4095f8557b21c4d4c5fea7d0",
+ "sha256:e998e304036198b4f6914e6a1e2b6f925208a20e2042563d9734881150c6c246",
+ "sha256:ec42088248c596dbd61d4ae8a5b004f97a4d91a9fd286f632e42e60b706718d7",
+ "sha256:efa7b51824aa0ee957ccd5a741c73e6851de55f40d807f08069eb4c5a26b2baa",
+ "sha256:f0a1bc63a465b6d72569a9bba9f2ef0334c4e03958e043da1920299100bc7c08",
+ "sha256:f18a5a84e16886898e51ab4b1d43acb3083c39b14c8caeb3589aabff0ee0b270",
+ "sha256:f2a9efc53d5b714b8df2b4b3e992accf8ce5bbdfe544d74d5c6766c9e1146a3a",
+ "sha256:f3bbbc998d42f8e561f347e798b85513ba4da324c2b3f9b7969e9c45b10f6169",
+ "sha256:f42038016852ae51b4088b2862126535cc4fc85802bfe30dea3500fdfaf1864e",
+ "sha256:f443cdef978430887ed55112b491f670bba6462cea7a7742ff8f14b7abb98d75",
+ "sha256:f51969bac61441fd31f028d7b3b45962f3ecebf691a510495e5d2cd8c8092dbd",
+ "sha256:f8aca2e3a72f37bfc7b14ba96d4056244001ddcc18382bd0daa087fd2e68a354",
+ "sha256:f9737bf36262046213a28e789cc82d82c6ef19c85a0cf05e75c670a33342ac2c",
+ "sha256:fd6037392f2d57793ab98d9e26798f44b8b4da2f2464388588f48ac52c489ea1",
+ "sha256:feaa45c0eae424d3e90d78823f3828e7dc42a42f21ed420db98da2c4ecf0a2cb",
+ "sha256:ff097ae562e637409b429a7ac958a20aab237a0378c42dabaa1e3abf2f896e5f",
+ "sha256:ff46d772d5f6f73564979cd77a4fffe55c916a05f3cb70e7c9c0590059fb29ef"
],
"index": "pypi",
- "version": "==4.3.0"
+ "markers": "python_version >= '3.6'",
+ "version": "==5.2.1"
},
"mccabe": {
"hashes": [
- "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42",
- "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"
+ "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325",
+ "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"
],
- "version": "==0.6.1"
+ "markers": "python_version >= '3.6'",
+ "version": "==0.7.0"
},
"nose": {
"hashes": [
@@ -196,53 +520,30 @@
"index": "pypi",
"version": "==1.3.7"
},
+ "platformdirs": {
+ "hashes": [
+ "sha256:0614df2a2f37e1a662acbd8e2b25b92ccf8632929bc6d43467e17fe89c75e068",
+ "sha256:ef0cc731df711022c174543cb70a9b5bd22e5a9337c8624ef2c2ceb8ddad8768"
+ ],
+ "markers": "python_version >= '3.8'",
+ "version": "==4.2.0"
+ },
"pylint": {
"hashes": [
- "sha256:689de29ae747642ab230c6d37be2b969bf75663176658851f456619aacf27492",
- "sha256:771467c434d0d9f081741fec1d64dfb011ed26e65e12a28fe06ca2f61c4d556c"
+ "sha256:507a5b60953874766d8a366e8e8c7af63e058b26345cfcb5f91f89d987fd6b74",
+ "sha256:6a69beb4a6f63debebaab0a3477ecd0f559aa726af4954fc948c51f7a2549e23"
],
"index": "pypi",
- "version": "==2.2.2"
- },
- "six": {
- "hashes": [
- "sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c",
- "sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73"
- ],
- "version": "==1.12.0"
- },
- "typed-ast": {
- "hashes": [
- "sha256:023625bfa9359e29bd6e24cac2a4503495b49761d48a5f1e38333fc4ac4d93fe",
- "sha256:07591f7a5fdff50e2e566c4c1e9df545c75d21e27d98d18cb405727ed0ef329c",
- "sha256:153e526b0f4ffbfada72d0bb5ffe8574ba02803d2f3a9c605c8cf99dfedd72a2",
- "sha256:3ad2bdcd46a4a1518d7376e9f5016d17718a9ed3c6a3f09203d832f6c165de4a",
- "sha256:3ea98c84df53ada97ee1c5159bb3bc784bd734231235a1ede14c8ae0775049f7",
- "sha256:51a7141ccd076fa561af107cfb7a8b6d06a008d92451a1ac7e73149d18e9a827",
- "sha256:52c93cd10e6c24e7ac97e8615da9f224fd75c61770515cb323316c30830ddb33",
- "sha256:6344c84baeda3d7b33e157f0b292e4dd53d05ddb57a63f738178c01cac4635c9",
- "sha256:64699ca1b3bd5070bdeb043e6d43bc1d0cebe08008548f4a6bee782b0ecce032",
- "sha256:74903f2e56bbffe29282ef8a5487d207d10be0f8513b41aff787d954a4cf91c9",
- "sha256:7891710dba83c29ee2bd51ecaa82f60f6bede40271af781110c08be134207bf2",
- "sha256:91976c56224e26c256a0de0f76d2004ab885a29423737684b4f7ebdd2f46dde2",
- "sha256:9bad678a576ecc71f25eba9f1e3fd8d01c28c12a2834850b458428b3e855f062",
- "sha256:b4726339a4c180a8b6ad9d8b50d2b6dc247e1b79b38fe2290549c98e82e4fd15",
- "sha256:ba36f6aa3f8933edf94ea35826daf92cbb3ec248b89eccdc053d4a815d285357",
- "sha256:bbc96bde544fd19e9ef168e4dfa5c3dfe704bfa78128fa76f361d64d6b0f731a",
- "sha256:c0c927f1e44469056f7f2dada266c79b577da378bbde3f6d2ada726d131e4824",
- "sha256:c0f9a3708008aa59f560fa1bd22385e05b79b8e38e0721a15a8402b089243442",
- "sha256:f0bf6f36ff9c5643004171f11d2fdc745aa3953c5aacf2536a0685db9ceb3fb1",
- "sha256:f5be39a0146be663cbf210a4d95c3c58b2d7df7b043c9047c5448e358f0550a2",
- "sha256:fcd198bf19d9213e5cbf2cde2b9ef20a9856e716f76f9476157f90ae6de06cc6"
- ],
- "markers": "python_version < '3.7' and implementation_name == 'cpython'",
- "version": "==1.2.0"
- },
- "wrapt": {
- "hashes": [
- "sha256:4aea003270831cceb8a90ff27c4031da6ead7ec1886023b80ce0dfe0adf61533"
- ],
- "version": "==1.11.1"
+ "markers": "python_full_version >= '3.8.0'",
+ "version": "==3.1.0"
+ },
+ "tomlkit": {
+ "hashes": [
+ "sha256:5cd82d48a3dd89dee1f9d64420aa20ae65cfbd00668d6f094d7578a78efbb77b",
+ "sha256:7ca1cfc12232806517a8515047ba66a19369e71edf2439d0f5824f91032b6cc3"
+ ],
+ "markers": "python_version >= '3.7'",
+ "version": "==0.12.4"
}
}
}
diff --git a/data/configd-include.json b/data/configd-include.json
index dc00f0698..0c767f987 100644
--- a/data/configd-include.json
+++ b/data/configd-include.json
@@ -108,6 +108,5 @@
"vpn_openconnect.py",
"vpn_pptp.py",
"vpn_sstp.py",
-"vrf.py",
-"vrf_vni.py"
+"vrf.py"
]
diff --git a/data/templates/accel-ppp/pppoe.config.j2 b/data/templates/accel-ppp/pppoe.config.j2
index ddf0da518..42bc8440c 100644
--- a/data/templates/accel-ppp/pppoe.config.j2
+++ b/data/templates/accel-ppp/pppoe.config.j2
@@ -67,7 +67,7 @@ service-name={{ service_name | join(',') }}
{% set delay_without_sessions = pado_delay.delays_without_sessions[0] | default('0') %}
{% set pado_delay_param = namespace(value=delay_without_sessions) %}
{% for delay, sessions in pado_delay.delays_with_sessions | sort(attribute='1') %}
-{% if not loop.last %}
+{% if not delay == 'disable' %}
{% set pado_delay_param.value = pado_delay_param.value + ',' + delay + ':' + sessions | string %}
{% else %}
{% set pado_delay_param.value = pado_delay_param.value + ',-1:' + sessions | string %}
diff --git a/data/templates/frr/zebra.vrf.route-map.frr.j2 b/data/templates/frr/zebra.vrf.route-map.frr.j2
index f1cc6fe66..8ebb82511 100644
--- a/data/templates/frr/zebra.vrf.route-map.frr.j2
+++ b/data/templates/frr/zebra.vrf.route-map.frr.j2
@@ -1,10 +1,6 @@
!
{% if name is vyos_defined %}
{% for vrf, vrf_config in name.items() %}
-{# code path required for vrf_vni.py as we will only render the required VR configuration and not all of them #}
-{% if only_vrf is vyos_defined and vrf is not vyos_defined(only_vrf) %}
-{% continue %}
-{% endif %}
vrf {{ vrf }}
{% if vrf_config.ip.nht.no_resolve_via_default is vyos_defined %}
no ip nht resolve-via-default
@@ -25,7 +21,7 @@ vrf {{ vrf }}
ipv6 protocol {{ protocol_name }} route-map {{ protocol_config.route_map }}
{% endfor %}
{% endif %}
-{% if vrf_config.vni is vyos_defined and no_vni is not vyos_defined %}
+{% if vrf_config.vni is vyos_defined %}
vni {{ vrf_config.vni }}
{% endif %}
exit-vrf
diff --git a/data/templates/iproute2/static.conf.j2 b/data/templates/iproute2/static.conf.j2
index 10c9bdab7..249483ab3 100644
--- a/data/templates/iproute2/static.conf.j2
+++ b/data/templates/iproute2/static.conf.j2
@@ -2,7 +2,7 @@
{% if table is vyos_defined %}
{% for t, t_options in table.items() %}
{% if t_options.description is vyos_defined %}
-{{ "%-6s" | format(t) }} {{ "%-40s" | format(t_options.description) }}
+{{ "%-6s" | format(t) }} {{ "%-40s" | format(t_options.description | replace(" ", "_")) }}
{% endif %}
{% endfor %}
{% endif %}
diff --git a/data/templates/ipsec/swanctl/remote_access.j2 b/data/templates/ipsec/swanctl/remote_access.j2
index adfa32bde..6bced88c7 100644
--- a/data/templates/ipsec/swanctl/remote_access.j2
+++ b/data/templates/ipsec/swanctl/remote_access.j2
@@ -33,7 +33,7 @@
auth = pubkey
{% elif rw_conf.authentication.client_mode.startswith("eap") %}
auth = {{ rw_conf.authentication.client_mode }}
- eap_id = %any
+ eap_id = {{ '%any' if rw_conf.authentication.eap_id == 'any' else rw_conf.authentication.eap_id }}
{% endif %}
{% if rw_conf.authentication.client_mode is vyos_defined('eap-tls') or rw_conf.authentication.client_mode is vyos_defined('x509') %}
{# pass all configured CAs as filenames, separated by commas #}
diff --git a/data/templates/load-balancing/haproxy.cfg.j2 b/data/templates/load-balancing/haproxy.cfg.j2
index 849cef74d..7917c8257 100644
--- a/data/templates/load-balancing/haproxy.cfg.j2
+++ b/data/templates/load-balancing/haproxy.cfg.j2
@@ -69,11 +69,23 @@ frontend {{ front }}
{% endif %}
{% if front_config.mode is vyos_defined %}
mode {{ front_config.mode }}
+{% if front_config.tcp_request.inspect_delay is vyos_defined %}
+ tcp-request inspect-delay {{ front_config.tcp_request.inspect_delay }}
+{% endif %}
+{# add tcp-request related directive if ssl is configed #}
+{% if front_config.mode is vyos_defined('tcp') and front_config.rule is vyos_defined %}
+{% for rule, rule_config in front_config.rule.items() %}
+{% if rule_config.ssl is vyos_defined %}
+ tcp-request content accept if { req_ssl_hello_type 1 }
+{% break %}
+{% endif %}
+{% endfor %}
+{% endif %}
{% endif %}
{% if front_config.rule is vyos_defined %}
{% for rule, rule_config in front_config.rule.items() %}
# rule {{ rule }}
-{% if rule_config.domain_name is vyos_defined and rule_config.set.backend is vyos_defined %}
+{% if rule_config.domain_name is vyos_defined %}
{% set rule_options = 'hdr(host)' %}
{% if rule_config.ssl is vyos_defined %}
{% set ssl_rule_translate = {'req-ssl-sni': 'req_ssl_sni', 'ssl-fc-sni': 'ssl_fc_sni', 'ssl-fc-sni-end': 'ssl_fc_sni_end'} %}
@@ -82,16 +94,20 @@ frontend {{ front }}
{% for domain in rule_config.domain_name %}
acl {{ rule }} {{ rule_options }} -i {{ domain }}
{% endfor %}
- use_backend {{ rule_config.set.backend }} if {{ rule }}
{% endif %}
{# path url #}
-{% if rule_config.url_path is vyos_defined and rule_config.set.redirect_location is vyos_defined %}
+{% if rule_config.url_path is vyos_defined %}
{% set path_mod_translate = {'begin': '-i -m beg', 'end': '-i -m end', 'exact': ''} %}
{% for path, path_config in rule_config.url_path.items() %}
{% for url in path_config %}
acl {{ rule }} path {{ path_mod_translate[path] }} {{ url }}
{% endfor %}
{% endfor %}
+{% endif %}
+{% if rule_config.set.backend is vyos_defined %}
+ use_backend {{ rule_config.set.backend }} if {{ rule }}
+{% endif %}
+{% if rule_config.set.redirect_location is vyos_defined %}
http-request redirect location {{ rule_config.set.redirect_location }} code 301 if {{ rule }}
{% endif %}
{# endpath #}
@@ -110,6 +126,26 @@ frontend {{ front }}
{% if backend is vyos_defined %}
{% for back, back_config in backend.items() %}
backend {{ back }}
+{% if back_config.http_check is vyos_defined %}
+ option httpchk
+{% endif %}
+{% set send = '' %}
+{% if back_config.http_check.method is vyos_defined %}
+{% set send = send + ' meth ' + back_config.http_check.method | upper %}
+{% endif %}
+{% if back_config.http_check.uri is vyos_defined %}
+{% set send = send + ' uri ' + back_config.http_check.uri %}
+{% endif %}
+{% if send != '' %}
+ http-check send{{ send }}
+{% endif %}
+{% if back_config.http_check.expect is vyos_defined %}
+{% if back_config.http_check.expect.status is vyos_defined %}
+ http-check expect status {{ back_config.http_check.expect.status }}
+{% elif back_config.http_check.expect.string is vyos_defined %}
+ http-check expect string {{ back_config.http_check.expect.string }}
+{% endif %}
+{% endif %}
{% if back_config.balance is vyos_defined %}
{% set balance_translate = {'least-connection': 'leastconn', 'round-robin': 'roundrobin', 'source-address': 'source'} %}
balance {{ balance_translate[back_config.balance] }}
@@ -150,7 +186,7 @@ backend {{ back }}
{% endfor %}
{% endif %}
{% if back_config.server is vyos_defined %}
-{% set ssl_back = 'ssl ca-file /run/haproxy/' ~ back_config.ssl.ca_certificate ~ '.pem' if back_config.ssl.ca_certificate is vyos_defined else '' %}
+{% set ssl_back = 'ssl ca-file /run/haproxy/' ~ back_config.ssl.ca_certificate ~ '.pem' if back_config.ssl.ca_certificate is vyos_defined else ('ssl verify none' if back_config.ssl.no_verify is vyos_defined else '') %}
{% for server, server_config in back_config.server.items() %}
server {{ server }} {{ server_config.address }}:{{ server_config.port }}{{ ' check' if server_config.check is vyos_defined }}{{ ' backup' if server_config.backup is vyos_defined }}{{ ' send-proxy' if server_config.send_proxy is vyos_defined }}{{ ' send-proxy-v2' if server_config.send_proxy_v2 is vyos_defined }} {{ ssl_back }}
{% endfor %}
diff --git a/data/templates/ocserv/ocserv_config.j2 b/data/templates/ocserv/ocserv_config.j2
index b5e890c32..81f777031 100644
--- a/data/templates/ocserv/ocserv_config.j2
+++ b/data/templates/ocserv/ocserv_config.j2
@@ -61,7 +61,15 @@ keepalive = 300
dpd = 60
mobile-dpd = 300
switch-to-tcp-timeout = 30
+{% if tls_version_min == '1.0' %}
tls-priorities = "NORMAL:%SERVER_PRECEDENCE:%COMPAT:-RSA:-VERS-SSL3.0:-ARCFOUR-128"
+{% elif tls_version_min == '1.1' %}
+tls-priorities = "NORMAL:%SERVER_PRECEDENCE:%COMPAT:-RSA:-VERS-SSL3.0:-ARCFOUR-128:-VERS-TLS1.0"
+{% elif tls_version_min == '1.2' %}
+tls-priorities = "NORMAL:%SERVER_PRECEDENCE:%COMPAT:-RSA:-VERS-SSL3.0:-ARCFOUR-128:-VERS-TLS1.0:-VERS-TLS1.1"
+{% elif tls_version_min == '1.3' %}
+tls-priorities = "NORMAL:%SERVER_PRECEDENCE:%COMPAT:-RSA:-VERS-SSL3.0:-ARCFOUR-128:-VERS-TLS1.0:-VERS-TLS1.1:-VERS-TLS1.2"
+{% endif %}
auth-timeout = 240
idle-timeout = 1200
mobile-idle-timeout = 1800
diff --git a/interface-definitions/include/accel-ppp/radius-additions.xml.i b/interface-definitions/include/accel-ppp/radius-additions.xml.i
index cdd0bf300..3c2eb09eb 100644
--- a/interface-definitions/include/accel-ppp/radius-additions.xml.i
+++ b/interface-definitions/include/accel-ppp/radius-additions.xml.i
@@ -122,7 +122,7 @@
</constraint>
<valueHelp>
<format>ipv4</format>
- <description>IPv4 address for aynamic authorization server</description>
+ <description>IPv4 address for dynamic authorization server</description>
</valueHelp>
</properties>
</leafNode>
diff --git a/interface-definitions/include/firewall/global-options.xml.i b/interface-definitions/include/firewall/global-options.xml.i
index 415d85f05..9cd0b3239 100644
--- a/interface-definitions/include/firewall/global-options.xml.i
+++ b/interface-definitions/include/firewall/global-options.xml.i
@@ -44,6 +44,26 @@
</properties>
<defaultValue>disable</defaultValue>
</leafNode>
+ <leafNode name="directed-broadcast">
+ <properties>
+ <help>Policy for handling IPv4 directed broadcast forwarding on all interfaces</help>
+ <completionHelp>
+ <list>enable disable</list>
+ </completionHelp>
+ <valueHelp>
+ <format>enable</format>
+ <description>Enable IPv4 directed broadcast forwarding on all interfaces</description>
+ </valueHelp>
+ <valueHelp>
+ <format>disable</format>
+ <description>Disable IPv4 directed broadcast forwarding on all interfaces</description>
+ </valueHelp>
+ <constraint>
+ <regex>(enable|disable)</regex>
+ </constraint>
+ </properties>
+ <defaultValue>enable</defaultValue>
+ </leafNode>
<leafNode name="ip-src-route">
<properties>
<help>Policy for handling IPv4 packets with source route option</help>
diff --git a/interface-definitions/include/haproxy/tcp-request.xml.i b/interface-definitions/include/haproxy/tcp-request.xml.i
new file mode 100644
index 000000000..3d60bd8ad
--- /dev/null
+++ b/interface-definitions/include/haproxy/tcp-request.xml.i
@@ -0,0 +1,22 @@
+<!-- include start from haproxy/tcp-request.xml.i -->
+<node name="tcp-request">
+ <properties>
+ <help>TCP request directive</help>
+ </properties>
+ <children>
+ <leafNode name="inspect-delay">
+ <properties>
+ <help>Set the maximum allowed time to wait for data during content inspection</help>
+ <valueHelp>
+ <format>u32:1-65535</format>
+ <description>The timeout value specified in milliseconds</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-65535"/>
+ </constraint>
+ <constraintErrorMessage>The timeout value must be in range 1 to 65535 milliseconds</constraintErrorMessage>
+ </properties>
+ </leafNode>
+ </children>
+</node>
+<!-- include end -->
diff --git a/interface-definitions/include/interface/base-reachable-time.xml.i b/interface-definitions/include/interface/base-reachable-time.xml.i
new file mode 100644
index 000000000..fb0d70101
--- /dev/null
+++ b/interface-definitions/include/interface/base-reachable-time.xml.i
@@ -0,0 +1,16 @@
+<!-- include start from interface/base-reachable-time.xml.i -->
+<leafNode name="base-reachable-time">
+ <properties>
+ <help>Base reachable time in seconds</help>
+ <valueHelp>
+ <format>u32:1-86400</format>
+ <description>Base reachable time in seconds</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-86400"/>
+ </constraint>
+ <constraintErrorMessage>Base reachable time must be between 1 and 86400 seconds</constraintErrorMessage>
+ </properties>
+ <defaultValue>30</defaultValue>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/include/interface/ipv6-options.xml.i b/interface-definitions/include/interface/ipv6-options.xml.i
index edb4a74f9..ec6ec64ee 100644
--- a/interface-definitions/include/interface/ipv6-options.xml.i
+++ b/interface-definitions/include/interface/ipv6-options.xml.i
@@ -5,6 +5,7 @@
</properties>
<children>
#include <include/interface/adjust-mss.xml.i>
+ #include <include/interface/base-reachable-time.xml.i>
#include <include/interface/disable-forwarding.xml.i>
#include <include/interface/ipv6-accept-dad.xml.i>
#include <include/interface/ipv6-address.xml.i>
diff --git a/interface-definitions/include/pppoe-access-concentrator.xml.i b/interface-definitions/include/pppoe-access-concentrator.xml.i
index ccfcc1c49..8a75dae08 100644
--- a/interface-definitions/include/pppoe-access-concentrator.xml.i
+++ b/interface-definitions/include/pppoe-access-concentrator.xml.i
@@ -3,9 +3,9 @@
<properties>
<help>Access concentrator name</help>
<constraint>
- <regex>[a-zA-Z0-9]{1,100}</regex>
+ #include <include/constraint/alpha-numeric-hyphen-underscore.xml.i>
</constraint>
- <constraintErrorMessage>Access-concentrator name must be alphanumerical only (max. 100 characters)</constraintErrorMessage>
+ <constraintErrorMessage>Access-concentrator name can only contain alpha-numeric letters, hyphen and underscores(max. 100 characters)</constraintErrorMessage>
</properties>
</leafNode>
<!-- include end -->
diff --git a/interface-definitions/include/tls-version-min.xml.i b/interface-definitions/include/tls-version-min.xml.i
new file mode 100644
index 000000000..b3dcbad49
--- /dev/null
+++ b/interface-definitions/include/tls-version-min.xml.i
@@ -0,0 +1,29 @@
+<!-- include start from tls-version-min.xml.i -->
+<leafNode name="tls-version-min">
+ <properties>
+ <help>Specify the minimum required TLS version</help>
+ <completionHelp>
+ <list>1.0 1.1 1.2 1.3</list>
+ </completionHelp>
+ <valueHelp>
+ <format>1.0</format>
+ <description>TLS v1.0</description>
+ </valueHelp>
+ <valueHelp>
+ <format>1.1</format>
+ <description>TLS v1.1</description>
+ </valueHelp>
+ <valueHelp>
+ <format>1.2</format>
+ <description>TLS v1.2</description>
+ </valueHelp>
+ <valueHelp>
+ <format>1.3</format>
+ <description>TLS v1.3</description>
+ </valueHelp>
+ <constraint>
+ <regex>(1.0|1.1|1.2|1.3)</regex>
+ </constraint>
+ </properties>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/include/version/firewall-version.xml.i b/interface-definitions/include/version/firewall-version.xml.i
index 6702ee041..fa8e26f78 100644
--- a/interface-definitions/include/version/firewall-version.xml.i
+++ b/interface-definitions/include/version/firewall-version.xml.i
@@ -1,3 +1,3 @@
<!-- include start from include/version/firewall-version.xml.i -->
-<syntaxVersion component='firewall' version='14'></syntaxVersion>
+<syntaxVersion component='firewall' version='15'></syntaxVersion>
<!-- include end -->
diff --git a/interface-definitions/include/version/openconnect-version.xml.i b/interface-definitions/include/version/openconnect-version.xml.i
index 654806278..15097eebe 100644
--- a/interface-definitions/include/version/openconnect-version.xml.i
+++ b/interface-definitions/include/version/openconnect-version.xml.i
@@ -1,3 +1,3 @@
<!-- include start from include/version/openconnect-version.xml.i -->
-<syntaxVersion component='openconnect' version='2'></syntaxVersion>
+<syntaxVersion component='openconnect' version='3'></syntaxVersion>
<!-- include end -->
diff --git a/interface-definitions/include/version/pppoe-server-version.xml.i b/interface-definitions/include/version/pppoe-server-version.xml.i
index c253c58d9..61de1277a 100644
--- a/interface-definitions/include/version/pppoe-server-version.xml.i
+++ b/interface-definitions/include/version/pppoe-server-version.xml.i
@@ -1,3 +1,3 @@
<!-- include start from include/version/pppoe-server-version.xml.i -->
-<syntaxVersion component='pppoe-server' version='9'></syntaxVersion>
+<syntaxVersion component='pppoe-server' version='10'></syntaxVersion>
<!-- include end -->
diff --git a/interface-definitions/interfaces_openvpn.xml.in b/interface-definitions/interfaces_openvpn.xml.in
index 389b5b5c9..7b46f32b3 100644
--- a/interface-definitions/interfaces_openvpn.xml.in
+++ b/interface-definitions/interfaces_openvpn.xml.in
@@ -739,33 +739,7 @@
<constraintErrorMessage>Peer certificate fingerprint must be a colon-separated SHA256 hex digest</constraintErrorMessage>
</properties>
</leafNode>
- <leafNode name="tls-version-min">
- <properties>
- <help>Specify the minimum required TLS version</help>
- <completionHelp>
- <list>1.0 1.1 1.2 1.3</list>
- </completionHelp>
- <valueHelp>
- <format>1.0</format>
- <description>TLS v1.0</description>
- </valueHelp>
- <valueHelp>
- <format>1.1</format>
- <description>TLS v1.1</description>
- </valueHelp>
- <valueHelp>
- <format>1.2</format>
- <description>TLS v1.2</description>
- </valueHelp>
- <valueHelp>
- <format>1.3</format>
- <description>TLS v1.3</description>
- </valueHelp>
- <constraint>
- <regex>(1.0|1.1|1.2|1.3)</regex>
- </constraint>
- </properties>
- </leafNode>
+ #include <include/tls-version-min.xml.i>
<leafNode name="role">
<properties>
<help>TLS negotiation role</help>
diff --git a/interface-definitions/interfaces_tunnel.xml.in b/interface-definitions/interfaces_tunnel.xml.in
index 99d9b34c6..fe1dad371 100644
--- a/interface-definitions/interfaces_tunnel.xml.in
+++ b/interface-definitions/interfaces_tunnel.xml.in
@@ -20,7 +20,7 @@
#include <include/interface/address-ipv4-ipv6.xml.i>
#include <include/interface/disable.xml.i>
#include <include/interface/disable-link-detect.xml.i>
- #include <include/interface/mtu-64-8024.xml.i>
+ #include <include/interface/mtu-68-16000.xml.i>
<leafNode name="mtu">
<defaultValue>1476</defaultValue>
</leafNode>
diff --git a/interface-definitions/load-balancing_reverse-proxy.xml.in b/interface-definitions/load-balancing_reverse-proxy.xml.in
index 2c2742dff..6a3b3cef1 100644
--- a/interface-definitions/load-balancing_reverse-proxy.xml.in
+++ b/interface-definitions/load-balancing_reverse-proxy.xml.in
@@ -38,6 +38,7 @@
#include <include/haproxy/mode.xml.i>
#include <include/port-number.xml.i>
#include <include/haproxy/rule-frontend.xml.i>
+ #include <include/haproxy/tcp-request.xml.i>
<leafNode name="redirect-http-to-https">
<properties>
<help>Redirect HTTP to HTTPS</help>
@@ -102,6 +103,65 @@
</leafNode>
</children>
</node>
+ <node name="http-check">
+ <properties>
+ <help>HTTP check configuration</help>
+ </properties>
+ <children>
+ <leafNode name="method">
+ <properties>
+ <help>HTTP method used for health check</help>
+ <completionHelp>
+ <list>options head get post put</list>
+ </completionHelp>
+ <valueHelp>
+ <format>options|head|get|post|put</format>
+ <description>HTTP method used for health checking</description>
+ </valueHelp>
+ <constraint>
+ <regex>(options|head|get|post|put)</regex>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="uri">
+ <properties>
+ <help>URI used for HTTP health check (Example: '/' or '/health')</help>
+ <constraint>
+ <regex>^\/([^?#\s]*)(\?[^#\s]*)?$</regex>
+ </constraint>
+ </properties>
+ </leafNode>
+ <node name="expect">
+ <properties>
+ <help>Expected response for the health check to pass</help>
+ </properties>
+ <children>
+ <leafNode name="status">
+ <properties>
+ <help>Expected response status code for the health check to pass</help>
+ <valueHelp>
+ <format>u32:200-399</format>
+ <description>Expected response code</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 200-399"/>
+ </constraint>
+ <constraintErrorMessage>Status code must be in range 200-399</constraintErrorMessage>
+ </properties>
+ </leafNode>
+ <leafNode name="string">
+ <properties>
+ <help>Expected to be in response body for the health check to pass</help>
+ <valueHelp>
+ <format>txt</format>
+ <description>A string expected to be in the response</description>
+ </valueHelp>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ </children>
+ </node>
#include <include/haproxy/rule-backend.xml.i>
<tagNode name="server">
<properties>
@@ -157,6 +217,12 @@
</properties>
<children>
#include <include/pki/ca-certificate.xml.i>
+ <leafNode name="no-verify">
+ <properties>
+ <help>Do not attempt to verify SSL certificates for backend servers</help>
+ <valueless/>
+ </properties>
+ </leafNode>
</children>
</node>
#include <include/haproxy/timeout.xml.i>
diff --git a/interface-definitions/protocols_failover.xml.in b/interface-definitions/protocols_failover.xml.in
index c0caec68e..f70975949 100644
--- a/interface-definitions/protocols_failover.xml.in
+++ b/interface-definitions/protocols_failover.xml.in
@@ -124,6 +124,12 @@
</properties>
<defaultValue>1</defaultValue>
</leafNode>
+ <leafNode name="onlink">
+ <properties>
+ <help>The next hop is directly connected to the interface, even if it does not match interface prefix</help>
+ <valueless/>
+ </properties>
+ </leafNode>
</children>
</tagNode>
</children>
diff --git a/interface-definitions/service_config-sync.xml.in b/interface-definitions/service_config-sync.xml.in
index e9ea9aa4b..648c14aee 100644
--- a/interface-definitions/service_config-sync.xml.in
+++ b/interface-definitions/service_config-sync.xml.in
@@ -34,6 +34,10 @@
</constraint>
</properties>
</leafNode>
+ #include <include/port-number.xml.i>
+ <leafNode name="port">
+ <defaultValue>443</defaultValue>
+ </leafNode>
<leafNode name="timeout">
<properties>
<help>Connection API timeout</help>
diff --git a/interface-definitions/service_dns_forwarding.xml.in b/interface-definitions/service_dns_forwarding.xml.in
index a54618e82..b52b4bda3 100644
--- a/interface-definitions/service_dns_forwarding.xml.in
+++ b/interface-definitions/service_dns_forwarding.xml.in
@@ -115,7 +115,7 @@
<description>An absolute DNS domain name</description>
</valueHelp>
<constraint>
- <validator name="fqdn"/>
+ <regex>((?!-)[-_a-zA-Z0-9.]{1,63}|@|any)(?&lt;!\.)</regex>
</constraint>
</properties>
<children>
diff --git a/interface-definitions/service_pppoe-server.xml.in b/interface-definitions/service_pppoe-server.xml.in
index 9b5e4d3fb..5d357c2f9 100644
--- a/interface-definitions/service_pppoe-server.xml.in
+++ b/interface-definitions/service_pppoe-server.xml.in
@@ -74,11 +74,19 @@
<properties>
<help>PADO delays</help>
<valueHelp>
+ <format>disable</format>
+ <description>Disable new connections</description>
+ </valueHelp>
+ <completionHelp>
+ <list>disable</list>
+ </completionHelp>
+ <valueHelp>
<format>u32:1-999999</format>
<description>Number in ms</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 1-999999"/>
+ <regex>disable</regex>
</constraint>
<constraintErrorMessage>Invalid PADO delay</constraintErrorMessage>
</properties>
diff --git a/interface-definitions/system_ip.xml.in b/interface-definitions/system_ip.xml.in
index 015eb270f..b4b5092fe 100644
--- a/interface-definitions/system_ip.xml.in
+++ b/interface-definitions/system_ip.xml.in
@@ -23,12 +23,6 @@
<valueless/>
</properties>
</leafNode>
- <leafNode name="disable-directed-broadcast">
- <properties>
- <help>Disable IPv4 directed broadcast forwarding on all interfaces</help>
- <valueless/>
- </properties>
- </leafNode>
<node name="multipath">
<properties>
<help>IPv4 multipath settings</help>
diff --git a/interface-definitions/vpn_ipsec.xml.in b/interface-definitions/vpn_ipsec.xml.in
index 833019d68..7f425d982 100644
--- a/interface-definitions/vpn_ipsec.xml.in
+++ b/interface-definitions/vpn_ipsec.xml.in
@@ -768,6 +768,26 @@
<children>
#include <include/ipsec/authentication-id.xml.i>
#include <include/ipsec/authentication-x509.xml.i>
+ <leafNode name="eap-id">
+ <properties>
+ <help>Remote EAP ID for client authentication</help>
+ <valueHelp>
+ <format>txt</format>
+ <description>Remote EAP ID for client authentication</description>
+ </valueHelp>
+ <completionHelp>
+ <list>any</list>
+ </completionHelp>
+ <valueHelp>
+ <format>any</format>
+ <description>Allow any EAP ID</description>
+ </valueHelp>
+ <constraint>
+ <regex>[[:ascii:]]{1,64}</regex>
+ </constraint>
+ </properties>
+ <defaultValue>any</defaultValue>
+ </leafNode>
<leafNode name="client-mode">
<properties>
<help>Client authentication mode</help>
diff --git a/interface-definitions/vpn_openconnect.xml.in b/interface-definitions/vpn_openconnect.xml.in
index 736084f8b..7849d6886 100644
--- a/interface-definitions/vpn_openconnect.xml.in
+++ b/interface-definitions/vpn_openconnect.xml.in
@@ -266,6 +266,10 @@
<valueless/>
</properties>
</leafNode>
+ #include <include/tls-version-min.xml.i>
+ <leafNode name="tls-version-min">
+ <defaultValue>1.2</defaultValue>
+ </leafNode>
<node name="ssl">
<properties>
<help>SSL Certificate, SSL Key and CA</help>
diff --git a/interface-definitions/vrf.xml.in b/interface-definitions/vrf.xml.in
index 94ed96e4b..a20be995a 100644
--- a/interface-definitions/vrf.xml.in
+++ b/interface-definitions/vrf.xml.in
@@ -120,20 +120,7 @@
<constraintErrorMessage>VRF routing table must be in range from 100 to 65535</constraintErrorMessage>
</properties>
</leafNode>
- <leafNode name="vni" owner="${vyos_conf_scripts_dir}/vrf_vni.py $VAR(../@)">
- <properties>
- <help>Virtual Network Identifier</help>
- <!-- must be after BGP to keep correct order when removing L3VNIs in FRR -->
- <priority>822</priority>
- <valueHelp>
- <format>u32:0-16777214</format>
- <description>VXLAN virtual network identifier</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--range 0-16777214"/>
- </constraint>
- </properties>
- </leafNode>
+ #include <include/vni.xml.i>
</children>
</tagNode>
</children>
diff --git a/python/vyos/firewall.py b/python/vyos/firewall.py
index 946050a82..d7b7b80a8 100644
--- a/python/vyos/firewall.py
+++ b/python/vyos/firewall.py
@@ -32,7 +32,6 @@ from vyos.utils.process import cmd
from vyos.utils.process import run
# Conntrack
-
def conntrack_required(conf):
required_nodes = ['nat', 'nat66', 'load-balancing wan']
@@ -454,8 +453,28 @@ def parse_rule(rule_conf, hook, fw_name, rule_id, ip_name):
else:
output.append(f'set update ip{def_suffix} saddr @DA{def_suffix}_{dyn_group}')
+ set_table = False
if 'set' in rule_conf:
- output.append(parse_policy_set(rule_conf['set'], def_suffix))
+ # Parse set command used in policy route:
+ if 'connection_mark' in rule_conf['set']:
+ conn_mark = rule_conf['set']['connection_mark']
+ output.append(f'ct mark set {conn_mark}')
+ if 'dscp' in rule_conf['set']:
+ dscp = rule_conf['set']['dscp']
+ output.append(f'ip{def_suffix} dscp set {dscp}')
+ if 'mark' in rule_conf['set']:
+ mark = rule_conf['set']['mark']
+ output.append(f'meta mark set {mark}')
+ if 'table' in rule_conf['set']:
+ set_table = True
+ table = rule_conf['set']['table']
+ if table == 'main':
+ table = '254'
+ mark = 0x7FFFFFFF - int(table)
+ output.append(f'meta mark set {mark}')
+ if 'tcp_mss' in rule_conf['set']:
+ mss = rule_conf['set']['tcp_mss']
+ output.append(f'tcp option maxseg size set {mss}')
if 'action' in rule_conf:
# Change action=return to action=action
@@ -489,7 +508,8 @@ def parse_rule(rule_conf, hook, fw_name, rule_id, ip_name):
output.append(f'wscale {synproxy_ws} timestamp sack-perm')
else:
- output.append('return')
+ if set_table:
+ output.append('return')
output.append(f'comment "{family}-{hook}-{fw_name}-{rule_id}"')
return " ".join(output)
@@ -521,28 +541,6 @@ def parse_time(time):
out.append(f'day {{{",".join(out_days)}}}')
return " ".join(out)
-def parse_policy_set(set_conf, def_suffix):
- out = []
- if 'connection_mark' in set_conf:
- conn_mark = set_conf['connection_mark']
- out.append(f'ct mark set {conn_mark}')
- if 'dscp' in set_conf:
- dscp = set_conf['dscp']
- out.append(f'ip{def_suffix} dscp set {dscp}')
- if 'mark' in set_conf:
- mark = set_conf['mark']
- out.append(f'meta mark set {mark}')
- if 'table' in set_conf:
- table = set_conf['table']
- if table == 'main':
- table = '254'
- mark = 0x7FFFFFFF - int(table)
- out.append(f'meta mark set {mark}')
- if 'tcp_mss' in set_conf:
- mss = set_conf['tcp_mss']
- out.append(f'tcp option maxseg size set {mss}')
- return " ".join(out)
-
# GeoIP
nftables_geoip_conf = '/run/nftables-geoip.conf'
diff --git a/python/vyos/ifconfig/interface.py b/python/vyos/ifconfig/interface.py
index 1b86982c4..f0897bc21 100644
--- a/python/vyos/ifconfig/interface.py
+++ b/python/vyos/ifconfig/interface.py
@@ -1,4 +1,4 @@
-# Copyright 2019-2023 VyOS maintainers and contributors <maintainers@vyos.io>
+# Copyright 2019-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@@ -193,6 +193,9 @@ class Interface(Control):
'validate': assert_positive,
'location': '/proc/sys/net/ipv6/conf/{ifname}/dad_transmits',
},
+ 'ipv6_cache_tmo': {
+ 'location': '/proc/sys/net/ipv6/neigh/{ifname}/base_reachable_time_ms',
+ },
'path_cost': {
# XXX: we should set a maximum
'validate': assert_positive,
@@ -261,6 +264,9 @@ class Interface(Control):
'ipv6_dad_transmits': {
'location': '/proc/sys/net/ipv6/conf/{ifname}/dad_transmits',
},
+ 'ipv6_cache_tmo': {
+ 'location': '/proc/sys/net/ipv6/neigh/{ifname}/base_reachable_time_ms',
+ },
'proxy_arp': {
'location': '/proc/sys/net/ipv4/conf/{ifname}/proxy_arp',
},
@@ -613,6 +619,21 @@ class Interface(Control):
return None
return self.set_interface('arp_cache_tmo', tmo)
+ def set_ipv6_cache_tmo(self, tmo):
+ """
+ Set IPv6 cache timeout value in seconds. Internal Kernel representation
+ is in milliseconds.
+
+ Example:
+ >>> from vyos.ifconfig import Interface
+ >>> Interface('eth0').set_ipv6_cache_tmo(40)
+ """
+ tmo = str(int(tmo) * 1000)
+ tmp = self.get_interface('ipv6_cache_tmo')
+ if tmp == tmo:
+ return None
+ return self.set_interface('ipv6_cache_tmo', tmo)
+
def _cleanup_mss_rules(self, table, ifname):
commands = []
results = self._cmd(f'nft -a list chain {table} VYOS_TCP_MSS').split("\n")
@@ -1698,6 +1719,11 @@ class Interface(Control):
for addr in tmp:
self.add_ipv6_eui64_address(addr)
+ # Configure IPv6 base time in milliseconds - has default value
+ tmp = dict_search('ipv6.base_reachable_time', config)
+ value = tmp if (tmp != None) else '30'
+ self.set_ipv6_cache_tmo(value)
+
# re-add ourselves to any bridge we might have fallen out of
if 'is_bridge_member' in config:
tmp = config.get('is_bridge_member')
diff --git a/python/vyos/pki.py b/python/vyos/pki.py
index 3c577db4d..27fe793a8 100644
--- a/python/vyos/pki.py
+++ b/python/vyos/pki.py
@@ -146,7 +146,7 @@ def create_certificate_request(subject, private_key, subject_alt_names=[]):
if isinstance(obj, ipaddress.IPv4Address) or isinstance(obj, ipaddress.IPv6Address):
alt_names.append(x509.IPAddress(obj))
elif isinstance(obj, str):
- alt_names.append(x509.DNSName(obj))
+ alt_names.append(x509.RFC822Name(obj) if '@' in obj else x509.DNSName(obj))
if alt_names:
builder = builder.add_extension(x509.SubjectAlternativeName(alt_names), critical=False)
diff --git a/python/vyos/qos/base.py b/python/vyos/qos/base.py
index f9366c6b1..4173a1a43 100644
--- a/python/vyos/qos/base.py
+++ b/python/vyos/qos/base.py
@@ -246,6 +246,7 @@ class QoSBase:
filter_cmd_base += ' protocol all'
if 'match' in cls_config:
+ is_filtered = False
for index, (match, match_config) in enumerate(cls_config['match'].items(), start=1):
filter_cmd = filter_cmd_base
if self.qostype == 'shaper' and 'prio ' not in filter_cmd:
@@ -330,11 +331,13 @@ class QoSBase:
cls = int(cls)
filter_cmd += f' flowid {self._parent:x}:{cls:x}'
self._cmd(filter_cmd)
+ is_filtered = True
vlan_expression = "match.*.vif"
match_vlan = jmespath.search(vlan_expression, cls_config)
- if any(tmp in ['exceed', 'bandwidth', 'burst'] for tmp in cls_config):
+ if any(tmp in ['exceed', 'bandwidth', 'burst'] for tmp in cls_config) \
+ and is_filtered:
# For "vif" "basic match" is used instead of "action police" T5961
if not match_vlan:
filter_cmd += f' action police'
diff --git a/python/vyos/utils/convert.py b/python/vyos/utils/convert.py
index c02f0071e..41e65081f 100644
--- a/python/vyos/utils/convert.py
+++ b/python/vyos/utils/convert.py
@@ -1,4 +1,4 @@
-# Copyright 2023 VyOS maintainers and contributors <maintainers@vyos.io>
+# Copyright 2023-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@@ -19,38 +19,43 @@ def seconds_to_human(s, separator=""):
"""
s = int(s)
+ year = 60 * 60 * 24 * 365.25
week = 60 * 60 * 24 * 7
day = 60 * 60 * 24
hour = 60 * 60
- remainder = 0
- result = ""
+ result = []
+
+ years = s // year
+ if years > 0:
+ result.append(f'{int(years)}y')
+ s = int(s % year)
weeks = s // week
if weeks > 0:
- result = "{0}w".format(weeks)
+ result.append(f'{weeks}w')
s = s % week
days = s // day
if days > 0:
- result = "{0}{1}{2}d".format(result, separator, days)
+ result.append(f'{days}d')
s = s % day
hours = s // hour
if hours > 0:
- result = "{0}{1}{2}h".format(result, separator, hours)
+ result.append(f'{hours}h')
s = s % hour
minutes = s // 60
if minutes > 0:
- result = "{0}{1}{2}m".format(result, separator, minutes)
+ result.append(f'{minutes}m')
s = s % 60
seconds = s
if seconds > 0:
- result = "{0}{1}{2}s".format(result, separator, seconds)
+ result.append(f'{seconds}s')
- return result
+ return separator.join(result)
def bytes_to_human(bytes, initial_exponent=0, precision=2,
int_below_exponent=0):
diff --git a/python/vyos/utils/io.py b/python/vyos/utils/io.py
index 7e6045291..a8c430f28 100644
--- a/python/vyos/utils/io.py
+++ b/python/vyos/utils/io.py
@@ -27,7 +27,7 @@ def print_error(str='', end='\n'):
sys.stderr.flush()
def ask_input(question, default='', numeric_only=False, valid_responses=[],
- no_echo=False):
+ no_echo=False, non_empty=False):
from getpass import getpass
question_out = question
if default:
@@ -48,6 +48,9 @@ def ask_input(question, default='', numeric_only=False, valid_responses=[],
if valid_responses and response not in valid_responses:
print("Invalid value, try again.")
continue
+ if non_empty and not response:
+ print("Non-empty value required; try again.")
+ continue
break
return response
diff --git a/smoketest/scripts/cli/base_vyostest_shim.py b/smoketest/scripts/cli/base_vyostest_shim.py
index c49d3e76c..efaa74fe0 100644
--- a/smoketest/scripts/cli/base_vyostest_shim.py
+++ b/smoketest/scripts/cli/base_vyostest_shim.py
@@ -1,4 +1,4 @@
-# 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
@@ -47,6 +47,8 @@ class VyOSUnitTestSHIM:
def setUpClass(cls):
cls._session = ConfigSession(os.getpid())
cls._session.save_config(save_config)
+ if os.path.exists('/tmp/vyos.smoketest.debug'):
+ cls.debug = True
pass
@classmethod
diff --git a/smoketest/scripts/cli/test_firewall.py b/smoketest/scripts/cli/test_firewall.py
index fe6977252..c47562714 100755
--- a/smoketest/scripts/cli/test_firewall.py
+++ b/smoketest/scripts/cli/test_firewall.py
@@ -27,6 +27,7 @@ from vyos.utils.process import run
sysfs_config = {
'all_ping': {'sysfs': '/proc/sys/net/ipv4/icmp_echo_ignore_all', 'default': '0', 'test_value': 'disable'},
'broadcast_ping': {'sysfs': '/proc/sys/net/ipv4/icmp_echo_ignore_broadcasts', 'default': '1', 'test_value': 'enable'},
+ 'directed_broadcast': {'sysfs': '/proc/sys/net/ipv4/conf/all/bc_forwarding', 'default': '1', 'test_value': 'disable'},
'ip_src_route': {'sysfs': '/proc/sys/net/ipv4/conf/*/accept_source_route', 'default': '0', 'test_value': 'enable'},
'ipv6_receive_redirects': {'sysfs': '/proc/sys/net/ipv6/conf/*/accept_redirects', 'default': '0', 'test_value': 'enable'},
'ipv6_src_route': {'sysfs': '/proc/sys/net/ipv6/conf/*/accept_source_route', 'default': '-1', 'test_value': 'enable'},
diff --git a/smoketest/scripts/cli/test_load-balancing_reverse-proxy.py b/smoketest/scripts/cli/test_load-balancing_reverse-proxy.py
index 97304da8b..c8b17316f 100755
--- a/smoketest/scripts/cli/test_load-balancing_reverse-proxy.py
+++ b/smoketest/scripts/cli/test_load-balancing_reverse-proxy.py
@@ -180,6 +180,7 @@ class TestLoadBalancingReverseProxy(VyOSUnitTestSHIM.TestCase):
mode = 'http'
rule_ten = '10'
rule_twenty = '20'
+ rule_thirty = '30'
send_proxy = 'send-proxy'
max_connections = '1000'
@@ -192,6 +193,8 @@ class TestLoadBalancingReverseProxy(VyOSUnitTestSHIM.TestCase):
self.cli_set(base_path + ['service', frontend, 'rule', rule_ten, 'set', 'backend', bk_first_name])
self.cli_set(base_path + ['service', frontend, 'rule', rule_twenty, 'domain-name', domain_bk_second])
self.cli_set(base_path + ['service', frontend, 'rule', rule_twenty, 'set', 'backend', bk_second_name])
+ self.cli_set(base_path + ['service', frontend, 'rule', rule_thirty, 'url-path', 'end', '/test'])
+ self.cli_set(base_path + ['service', frontend, 'rule', rule_thirty, 'set', 'backend', bk_second_name])
self.cli_set(back_base + [bk_first_name, 'mode', mode])
self.cli_set(back_base + [bk_first_name, 'server', bk_first_name, 'address', bk_server_first])
@@ -222,6 +225,8 @@ class TestLoadBalancingReverseProxy(VyOSUnitTestSHIM.TestCase):
self.assertIn(f'use_backend {bk_first_name} if {rule_ten}', config)
self.assertIn(f'acl {rule_twenty} hdr(host) -i {domain_bk_second}', config)
self.assertIn(f'use_backend {bk_second_name} if {rule_twenty}', config)
+ self.assertIn(f'acl {rule_thirty} path -i -m end /test', config)
+ self.assertIn(f'use_backend {bk_second_name} if {rule_thirty}', config)
# Backend
self.assertIn(f'backend {bk_first_name}', config)
@@ -280,6 +285,105 @@ class TestLoadBalancingReverseProxy(VyOSUnitTestSHIM.TestCase):
self.cli_set(base_path + ['backend', 'bk-01', 'ssl', 'ca-certificate', 'smoketest'])
self.cli_commit()
+ def test_04_lb_reverse_proxy_backend_ssl_no_verify(self):
+ # Setup base
+ self.configure_pki()
+ self.base_config()
+
+ # Set no-verify option
+ self.cli_set(base_path + ['backend', 'bk-01', 'ssl', 'no-verify'])
+ self.cli_commit()
+
+ # Test no-verify option
+ config = read_file(HAPROXY_CONF)
+ self.assertIn('server bk-01 192.0.2.11:9090 send-proxy ssl verify none', config)
+
+ # Test setting ca-certificate alongside no-verify option fails, to test config validation
+ self.cli_set(base_path + ['backend', 'bk-01', 'ssl', 'ca-certificate', 'smoketest'])
+ with self.assertRaises(ConfigSessionError) as e:
+ self.cli_commit()
+
+ def test_05_lb_reverse_proxy_backend_http_check(self):
+ # Setup base
+ self.base_config()
+
+ # Set http-check
+ self.cli_set(base_path + ['backend', 'bk-01', 'http-check', 'method', 'get'])
+ self.cli_commit()
+
+ # Test http-check
+ config = read_file(HAPROXY_CONF)
+ self.assertIn('option httpchk', config)
+ self.assertIn('http-check send meth GET', config)
+
+ # Set http-check with uri and status
+ self.cli_set(base_path + ['backend', 'bk-01', 'http-check', 'uri', '/health'])
+ self.cli_set(base_path + ['backend', 'bk-01', 'http-check', 'expect', 'status', '200'])
+ self.cli_commit()
+
+ # Test http-check with uri and status
+ config = read_file(HAPROXY_CONF)
+ self.assertIn('option httpchk', config)
+ self.assertIn('http-check send meth GET uri /health', config)
+ self.assertIn('http-check expect status 200', config)
+
+ # Set http-check with string
+ self.cli_delete(base_path + ['backend', 'bk-01', 'http-check', 'expect', 'status', '200'])
+ self.cli_set(base_path + ['backend', 'bk-01', 'http-check', 'expect', 'string', 'success'])
+ self.cli_commit()
+
+ # Test http-check with string
+ config = read_file(HAPROXY_CONF)
+ self.assertIn('option httpchk', config)
+ self.assertIn('http-check send meth GET uri /health', config)
+ self.assertIn('http-check expect string success', config)
+
+ def test_06_lb_reverse_proxy_tcp_mode(self):
+ frontend = 'tcp_8443'
+ mode = 'tcp'
+ front_port = '8433'
+ tcp_request_delay = "5000"
+ rule_thirty = '30'
+ domain_bk = 'n6.example.com'
+ ssl_opt = "req-ssl-sni"
+ bk_name = 'bk-03'
+ bk_server = '192.0.2.11'
+ bk_server_port = '9090'
+
+ back_base = base_path + ['backend']
+
+ self.cli_set(base_path + ['service', frontend, 'mode', mode])
+ self.cli_set(base_path + ['service', frontend, 'port', front_port])
+ self.cli_set(base_path + ['service', frontend, 'tcp-request', 'inspect-delay', tcp_request_delay])
+
+ self.cli_set(base_path + ['service', frontend, 'rule', rule_thirty, 'domain-name', domain_bk])
+ self.cli_set(base_path + ['service', frontend, 'rule', rule_thirty, 'ssl', ssl_opt])
+ self.cli_set(base_path + ['service', frontend, 'rule', rule_thirty, 'set', 'backend', bk_name])
+
+ self.cli_set(back_base + [bk_name, 'mode', mode])
+ self.cli_set(back_base + [bk_name, 'server', bk_name, 'address', bk_server])
+ self.cli_set(back_base + [bk_name, 'server', bk_name, 'port', bk_server_port])
+
+ # commit changes
+ self.cli_commit()
+
+ config = read_file(HAPROXY_CONF)
+
+ # Frontend
+ self.assertIn(f'frontend {frontend}', config)
+ self.assertIn(f'bind :::{front_port} v4v6', config)
+ self.assertIn(f'mode {mode}', config)
+
+ self.assertIn(f'tcp-request inspect-delay {tcp_request_delay}', config)
+ self.assertIn(f"tcp-request content accept if {{ req_ssl_hello_type 1 }}", config)
+ self.assertIn(f'acl {rule_thirty} req_ssl_sni -i {domain_bk}', config)
+ self.assertIn(f'use_backend {bk_name} if {rule_thirty}', config)
+
+ # Backend
+ self.assertIn(f'backend {bk_name}', config)
+ self.assertIn(f'balance roundrobin', config)
+ self.assertIn(f'mode {mode}', config)
+ self.assertIn(f'server {bk_name} {bk_server}:{bk_server_port}', config)
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_qos.py b/smoketest/scripts/cli/test_qos.py
index 4f41e36cd..fef1ff23a 100755
--- a/smoketest/scripts/cli/test_qos.py
+++ b/smoketest/scripts/cli/test_qos.py
@@ -697,6 +697,45 @@ class TestQoS(VyOSUnitTestSHIM.TestCase):
for config_entry in config_entries:
self.assertIn(config_entry, output)
+ def test_13_shaper_delete_only_rule(self):
+ default_bandwidth = 100
+ default_burst = 100
+ interface = self._interfaces[0]
+ class_bandwidth = 50
+ class_ceiling = 5
+ src_address = '10.1.1.0/24'
+
+ shaper_name = f'qos-shaper-{interface}'
+ self.cli_set(base_path + ['interface', interface, 'egress', shaper_name])
+ self.cli_set(base_path + ['policy', 'shaper', shaper_name, 'bandwidth', f'10mbit'])
+ self.cli_set(base_path + ['policy', 'shaper', shaper_name, 'default', 'bandwidth', f'{default_bandwidth}mbit'])
+ self.cli_set(base_path + ['policy', 'shaper', shaper_name, 'default', 'burst', f'{default_burst}'])
+
+ self.cli_set(base_path + ['policy', 'shaper', shaper_name, 'class', '30', 'bandwidth', f'{class_bandwidth}mbit'])
+ self.cli_set(base_path + ['policy', 'shaper', shaper_name, 'class', '30', 'ceiling', f'{class_ceiling}mbit'])
+ self.cli_set(base_path + ['policy', 'shaper', shaper_name, 'class', '30', 'match', 'ADDRESS30', 'ip', 'source', 'address', src_address])
+ self.cli_set(base_path + ['policy', 'shaper', shaper_name, 'class', '30', 'match', 'ADDRESS30', 'description', 'smoketest'])
+ self.cli_set(base_path + ['policy', 'shaper', shaper_name, 'class', '30', 'priority', '5'])
+ self.cli_set(base_path + ['policy', 'shaper', shaper_name, 'class', '30', 'queue-type', 'fair-queue'])
+
+ # commit changes
+ self.cli_commit()
+ # check root htb config
+ output = cmd(f'tc class show dev {interface}')
+
+ config_entries = (
+ f'prio 5 rate {class_bandwidth}Mbit ceil {class_ceiling}Mbit burst 15Kb', # specified class
+ f'prio 7 rate {default_bandwidth}Mbit ceil 100Mbit burst {default_burst}b', # default class
+ )
+ for config_entry in config_entries:
+ self.assertIn(config_entry, output)
+
+ self.assertTrue('' != cmd(f'tc filter show dev {interface}'))
+ # self.cli_delete(base_path + ['policy', 'shaper', shaper_name, 'class', '30', 'match', 'ADDRESS30'])
+ self.cli_delete(base_path + ['policy', 'shaper', shaper_name, 'class', '30', 'match', 'ADDRESS30', 'ip', 'source', 'address', src_address])
+ self.cli_commit()
+ self.assertEqual('', cmd(f'tc filter show dev {interface}'))
+
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_service_pppoe-server.py b/smoketest/scripts/cli/test_service_pppoe-server.py
index 5a48b1f58..97c63d4cb 100755
--- a/smoketest/scripts/cli/test_service_pppoe-server.py
+++ b/smoketest/scripts/cli/test_service_pppoe-server.py
@@ -168,7 +168,14 @@ class TestServicePPPoEServer(BasicAccelPPPTest.TestCase):
conf = ConfigParser(allow_no_value=True, delimiters='=')
conf.read(self._config_file)
- self.assertEqual(conf['pppoe']['pado-delay'], '10,20:200,-1:300')
+ self.assertEqual(conf['pppoe']['pado-delay'], '10,20:200,30:300')
+
+ self.set(['pado-delay', 'disable', 'sessions', '400'])
+ self.cli_commit()
+
+ conf = ConfigParser(allow_no_value=True, delimiters='=')
+ conf.read(self._config_file)
+ self.assertEqual(conf['pppoe']['pado-delay'], '10,20:200,30:300,-1:400')
if __name__ == '__main__':
diff --git a/smoketest/scripts/cli/test_system_ip.py b/smoketest/scripts/cli/test_system_ip.py
index ac8b74236..5b0090237 100755
--- a/smoketest/scripts/cli/test_system_ip.py
+++ b/smoketest/scripts/cli/test_system_ip.py
@@ -38,17 +38,6 @@ class TestSystemIP(VyOSUnitTestSHIM.TestCase):
self.assertEqual(read_file(all_forwarding), '0')
- def test_system_ip_directed_broadcast_forwarding(self):
- # Test if IPv4 directed broadcast forwarding can be disabled globally,
- # default is '1' which means forwarding enabled
- bc_forwarding = '/proc/sys/net/ipv4/conf/all/bc_forwarding'
- self.assertEqual(read_file(bc_forwarding), '1')
-
- self.cli_set(base_path + ['disable-directed-broadcast'])
- self.cli_commit()
-
- self.assertEqual(read_file(bc_forwarding), '0')
-
def test_system_ip_multipath(self):
# Test IPv4 multipathing options, options default to off -> '0'
use_neigh = '/proc/sys/net/ipv4/fib_multipath_use_neigh'
diff --git a/smoketest/scripts/cli/test_vpn_ipsec.py b/smoketest/scripts/cli/test_vpn_ipsec.py
index 145b5990e..27356d70e 100755
--- a/smoketest/scripts/cli/test_vpn_ipsec.py
+++ b/smoketest/scripts/cli/test_vpn_ipsec.py
@@ -782,6 +782,11 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase):
self.assertTrue(os.path.exists(os.path.join(CA_PATH, f'{ca_name}.pem')))
self.assertTrue(os.path.exists(os.path.join(CERT_PATH, f'{peer_name}.pem')))
+ # Test setting of custom EAP ID
+ self.cli_set(base_path + ['remote-access', 'connection', conn_name, 'authentication', 'eap-id', 'eap-user@vyos.net'])
+ self.cli_commit()
+ self.assertIn(r'eap_id = eap-user@vyos.net', read_file(swanctl_file))
+
self.tearDownPKI()
def test_remote_access_x509(self):
diff --git a/smoketest/scripts/cli/test_vpn_openconnect.py b/smoketest/scripts/cli/test_vpn_openconnect.py
index 96e858fdb..a2e426dc7 100755
--- a/smoketest/scripts/cli/test_vpn_openconnect.py
+++ b/smoketest/scripts/cli/test_vpn_openconnect.py
@@ -210,6 +210,9 @@ class TestVPNOpenConnect(VyOSUnitTestSHIM.TestCase):
# Verify configuration
daemon_config = read_file(config_file)
+ # Verify TLS string (with default setting)
+ self.assertIn('tls-priorities = "NORMAL:%SERVER_PRECEDENCE:%COMPAT:-RSA:-VERS-SSL3.0:-ARCFOUR-128:-VERS-TLS1.0:-VERS-TLS1.1"', daemon_config)
+
# authentication mode local password-otp
self.assertIn(f'auth = "plain[passwd=/run/ocserv/ocpasswd,otp=/run/ocserv/users.oath]"', daemon_config)
self.assertIn(f'listen-host = {listen_ip_no_cidr}', daemon_config)
@@ -253,5 +256,13 @@ class TestVPNOpenConnect(VyOSUnitTestSHIM.TestCase):
self.assertIn('included-http-headers = Pragma: no-cache', daemon_config)
self.assertIn('included-http-headers = Cache-control: no-store, no-cache', daemon_config)
+ # Set TLS version to the highest security (v1.3 min)
+ self.cli_set(base_path + ['tls-version-min', '1.3'])
+ self.cli_commit()
+
+ # Verify TLS string
+ daemon_config = read_file(config_file)
+ self.assertIn('tls-priorities = "NORMAL:%SERVER_PRECEDENCE:%COMPAT:-RSA:-VERS-SSL3.0:-ARCFOUR-128:-VERS-TLS1.0:-VERS-TLS1.1:-VERS-TLS1.2"', daemon_config)
+
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_vrf.py b/smoketest/scripts/cli/test_vrf.py
index f6e4181c0..243397dc2 100755
--- a/smoketest/scripts/cli/test_vrf.py
+++ b/smoketest/scripts/cli/test_vrf.py
@@ -18,7 +18,6 @@ import re
import os
import unittest
-from netifaces import interfaces
from base_vyostest_shim import VyOSUnitTestSHIM
from vyos.configsession import ConfigSessionError
@@ -27,6 +26,7 @@ from vyos.ifconfig import Section
from vyos.utils.file import read_file
from vyos.utils.network import get_interface_config
from vyos.utils.network import is_intf_addr_assigned
+from vyos.utils.network import interface_exists
from vyos.utils.system import sysctl_read
base_path = ['vrf']
@@ -60,7 +60,7 @@ class VRFTest(VyOSUnitTestSHIM.TestCase):
self.cli_delete(base_path)
self.cli_commit()
for vrf in vrfs:
- self.assertNotIn(vrf, interfaces())
+ self.assertFalse(interface_exists(vrf))
def test_vrf_vni_and_table_id(self):
base_table = '1000'
@@ -89,7 +89,7 @@ class VRFTest(VyOSUnitTestSHIM.TestCase):
iproute2_config = read_file('/etc/iproute2/rt_tables.d/vyos-vrf.conf')
for vrf in vrfs:
description = f'VyOS-VRF-{vrf}'
- self.assertTrue(vrf in interfaces())
+ self.assertTrue(interface_exists(vrf))
vrf_if = Interface(vrf)
# validate proper interface description
self.assertEqual(vrf_if.get_alias(), description)
@@ -131,7 +131,7 @@ class VRFTest(VyOSUnitTestSHIM.TestCase):
loopbacks = ['127.0.0.1', '::1']
for vrf in vrfs:
# Ensure VRF was created
- self.assertIn(vrf, interfaces())
+ self.assertTrue(interface_exists(vrf))
# Verify IP forwarding is 1 (enabled)
self.assertEqual(sysctl_read(f'net.ipv4.conf.{vrf}.forwarding'), '1')
self.assertEqual(sysctl_read(f'net.ipv6.conf.{vrf}.forwarding'), '1')
@@ -171,7 +171,7 @@ class VRFTest(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Check if VRF has been created
- self.assertTrue(vrf in interfaces())
+ self.assertTrue(interface_exists(vrf))
table = str(int(table) + 1)
self.cli_set(base + ['table', table])
@@ -228,7 +228,7 @@ class VRFTest(VyOSUnitTestSHIM.TestCase):
next_hop = f'192.0.{table}.1'
prefix = f'10.0.{table}.0/24'
- self.assertTrue(vrf in interfaces())
+ self.assertTrue(interface_exists(vrf))
frrconfig = self.getFRRconfig(f'vrf {vrf}')
self.assertIn(f' vni {table}', frrconfig)
@@ -261,7 +261,7 @@ class VRFTest(VyOSUnitTestSHIM.TestCase):
# Apply VRF config
self.cli_commit()
# Ensure VRF got created
- self.assertIn(vrf, interfaces())
+ self.assertTrue(interface_exists(vrf))
# ... and IP addresses are still assigned
for address in addresses:
self.assertTrue(is_intf_addr_assigned(interface, address))
@@ -293,7 +293,7 @@ class VRFTest(VyOSUnitTestSHIM.TestCase):
loopbacks = ['127.0.0.1', '::1']
for vrf in vrfs:
# Ensure VRF was created
- self.assertIn(vrf, interfaces())
+ self.assertTrue(interface_exists(vrf))
# Verify IP forwarding is 0 (disabled)
self.assertEqual(sysctl_read(f'net.ipv4.conf.{vrf}.forwarding'), '0')
self.assertEqual(sysctl_read(f'net.ipv6.conf.{vrf}.forwarding'), '0')
@@ -425,7 +425,7 @@ class VRFTest(VyOSUnitTestSHIM.TestCase):
# Verify VRF configuration
table = base_table
for vrf in vrfs:
- self.assertTrue(vrf in interfaces())
+ self.assertTrue(interface_exists(vrf))
frrconfig = self.getFRRconfig(f'vrf {vrf}')
self.assertIn(f' vni {table}', frrconfig)
@@ -447,7 +447,7 @@ class VRFTest(VyOSUnitTestSHIM.TestCase):
# Verify VRF configuration
table = base_table
for vrf in vrfs:
- self.assertTrue(vrf in interfaces())
+ self.assertTrue(interface_exists(vrf))
frrconfig = self.getFRRconfig(f'vrf {vrf}')
self.assertIn(f' vni {table}', frrconfig)
@@ -470,13 +470,39 @@ class VRFTest(VyOSUnitTestSHIM.TestCase):
# Verify VRF configuration
table = base_table
for vrf in vrfs:
- self.assertTrue(vrf in interfaces())
+ self.assertTrue(interface_exists(vrf))
frrconfig = self.getFRRconfig(f'vrf {vrf}')
self.assertIn(f' vni {table}', frrconfig)
# Increment table ID for the next run
table = str(int(table) + 2)
+
+ # add a new VRF with VNI - this must not delete any existing VRF/VNI
+ purple = 'purple'
+ table = str(int(table) + 10)
+ self.cli_set(base_path + ['name', purple, 'table', table])
+ self.cli_set(base_path + ['name', purple, 'vni', table])
+
+ # commit changes
+ self.cli_commit()
+
+ # Verify VRF configuration
+ table = base_table
+ for vrf in vrfs:
+ self.assertTrue(interface_exists(vrf))
+
+ frrconfig = self.getFRRconfig(f'vrf {vrf}')
+ self.assertIn(f' vni {table}', frrconfig)
+ # Increment table ID for the next run
+ table = str(int(table) + 2)
+
+ # Verify purple VRF/VNI
+ self.assertTrue(interface_exists(purple))
+ table = str(int(table) + 10)
+ frrconfig = self.getFRRconfig(f'vrf {purple}')
+ self.assertIn(f' vni {table}', frrconfig)
+
# Now delete all the VNIs
for vrf in vrfs:
base = base_path + ['name', vrf]
@@ -487,11 +513,16 @@ class VRFTest(VyOSUnitTestSHIM.TestCase):
# Verify no VNI is defined
for vrf in vrfs:
- self.assertTrue(vrf in interfaces())
+ self.assertTrue(interface_exists(vrf))
frrconfig = self.getFRRconfig(f'vrf {vrf}')
self.assertNotIn('vni', frrconfig)
+ # Verify purple VNI remains
+ self.assertTrue(interface_exists(purple))
+ frrconfig = self.getFRRconfig(f'vrf {purple}')
+ self.assertIn(f' vni {table}', frrconfig)
+
def test_vrf_ip_ipv6_nht(self):
table = '6910'
diff --git a/smoketest/scripts/system/test_kernel_options.py b/smoketest/scripts/system/test_kernel_options.py
index 0e3cbd0ed..18922d93d 100755
--- a/smoketest/scripts/system/test_kernel_options.py
+++ b/smoketest/scripts/system/test_kernel_options.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2020-2023 VyOS maintainers and contributors
+# Copyright (C) 2020-2024 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
@@ -14,28 +14,38 @@
# 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 gzip
import re
import os
import platform
import unittest
-from vyos.utils.process import call
-from vyos.utils.file import read_file
-
kernel = platform.release()
-config = read_file(f'/boot/config-{kernel}')
-CONFIG = '/proc/config.gz'
class TestKernelModules(unittest.TestCase):
""" VyOS makes use of a lot of Kernel drivers, modules and features. The
required modules which are essential for VyOS should be tested that they are
available in the Kernel that is run. """
+ _config_data = None
+
+ @classmethod
+ def setUpClass(cls):
+ import gzip
+ from vyos.utils.process import call
+
+ super(TestKernelModules, cls).setUpClass()
+ CONFIG = '/proc/config.gz'
+
+ if not os.path.isfile(CONFIG):
+ call('sudo modprobe configs')
+
+ with gzip.open(CONFIG, 'rt') as f:
+ cls._config_data = f.read()
+
def test_bond_interface(self):
# The bond/lacp interface must be enabled in the OS Kernel
for option in ['CONFIG_BONDING']:
- tmp = re.findall(f'{option}=(y|m)', config)
+ tmp = re.findall(f'{option}=(y|m)', self._config_data)
self.assertTrue(tmp)
def test_bridge_interface(self):
@@ -43,7 +53,7 @@ class TestKernelModules(unittest.TestCase):
for option in ['CONFIG_BRIDGE',
'CONFIG_BRIDGE_IGMP_SNOOPING',
'CONFIG_BRIDGE_VLAN_FILTERING']:
- tmp = re.findall(f'{option}=(y|m)', config)
+ tmp = re.findall(f'{option}=(y|m)', self._config_data)
self.assertTrue(tmp)
def test_dropmon_enabled(self):
@@ -53,47 +63,53 @@ class TestKernelModules(unittest.TestCase):
'CONFIG_BPF_EVENTS=y',
'CONFIG_TRACEPOINTS=y'
]
- if not os.path.isfile(CONFIG):
- call('sudo modprobe configs')
- with gzip.open(CONFIG, 'rt') as f:
- config_data = f.read()
for option in options_to_check:
- self.assertIn(option, config_data,
- f"Option {option} is not present in /proc/config.gz")
+ self.assertIn(option, self._config_data)
def test_synproxy_enabled(self):
options_to_check = [
'CONFIG_NFT_SYNPROXY',
'CONFIG_IP_NF_TARGET_SYNPROXY'
]
- if not os.path.isfile(CONFIG):
- call('sudo modprobe configs')
- with gzip.open(CONFIG, 'rt') as f:
- config_data = f.read()
for option in options_to_check:
- tmp = re.findall(f'{option}=(y|m)', config_data)
+ tmp = re.findall(f'{option}=(y|m)', self._config_data)
self.assertTrue(tmp)
def test_qemu_support(self):
- for option in ['CONFIG_VIRTIO_BLK', 'CONFIG_SCSI_VIRTIO',
- 'CONFIG_VIRTIO_NET', 'CONFIG_VIRTIO_CONSOLE',
- 'CONFIG_VIRTIO', 'CONFIG_VIRTIO_PCI',
- 'CONFIG_VIRTIO_BALLOON', 'CONFIG_CRYPTO_DEV_VIRTIO',
- 'CONFIG_X86_PLATFORM_DEVICES']:
- tmp = re.findall(f'{option}=(y|m)', config)
+ options_to_check = [
+ 'CONFIG_VIRTIO_BLK', 'CONFIG_SCSI_VIRTIO',
+ 'CONFIG_VIRTIO_NET', 'CONFIG_VIRTIO_CONSOLE',
+ 'CONFIG_VIRTIO', 'CONFIG_VIRTIO_PCI',
+ 'CONFIG_VIRTIO_BALLOON', 'CONFIG_CRYPTO_DEV_VIRTIO',
+ 'CONFIG_X86_PLATFORM_DEVICES'
+ ]
+ for option in options_to_check:
+ tmp = re.findall(f'{option}=(y|m)', self._config_data)
self.assertTrue(tmp)
def test_vmware_support(self):
for option in ['CONFIG_VMXNET3']:
- tmp = re.findall(f'{option}=(y|m)', config)
+ tmp = re.findall(f'{option}=(y|m)', self._config_data)
self.assertTrue(tmp)
def test_container_cgroup_support(self):
- for option in ['CONFIG_CGROUPS', 'CONFIG_MEMCG', 'CONFIG_CGROUP_PIDS', 'CONFIG_CGROUP_BPF']:
- tmp = re.findall(f'{option}=(y|m)', config)
+ options_to_check = [
+ 'CONFIG_CGROUPS', 'CONFIG_MEMCG',
+ 'CONFIG_CGROUP_PIDS', 'CONFIG_CGROUP_BPF'
+ ]
+ for option in options_to_check:
+ tmp = re.findall(f'{option}=(y|m)', self._config_data)
+ self.assertTrue(tmp)
+
+ def test_ip_routing_support(self):
+ options_to_check = [
+ 'CONFIG_IP_ADVANCED_ROUTER', 'CONFIG_IP_MULTIPLE_TABLES',
+ 'CONFIG_IP_ROUTE_MULTIPATH'
+ ]
+ for option in options_to_check:
+ tmp = re.findall(f'{option}=(y|m)', self._config_data)
self.assertTrue(tmp)
if __name__ == '__main__':
unittest.main(verbosity=2)
-
diff --git a/src/conf_mode/firewall.py b/src/conf_mode/firewall.py
index 3cf618363..e96e57154 100755
--- a/src/conf_mode/firewall.py
+++ b/src/conf_mode/firewall.py
@@ -44,6 +44,7 @@ nftables_conf = '/run/nftables.conf'
sysfs_config = {
'all_ping': {'sysfs': '/proc/sys/net/ipv4/icmp_echo_ignore_all', 'enable': '0', 'disable': '1'},
'broadcast_ping': {'sysfs': '/proc/sys/net/ipv4/icmp_echo_ignore_broadcasts', 'enable': '0', 'disable': '1'},
+ 'directed_broadcast' : {'sysfs': '/proc/sys/net/ipv4/conf/all/bc_forwarding', 'enable': '1', 'disable': '0'},
'ip_src_route': {'sysfs': '/proc/sys/net/ipv4/conf/*/accept_source_route'},
'ipv6_receive_redirects': {'sysfs': '/proc/sys/net/ipv6/conf/*/accept_redirects'},
'ipv6_src_route': {'sysfs': '/proc/sys/net/ipv6/conf/*/accept_source_route', 'enable': '0', 'disable': '-1'},
diff --git a/src/conf_mode/interfaces_openvpn.py b/src/conf_mode/interfaces_openvpn.py
index 505ec55c6..0ecffd3be 100755
--- a/src/conf_mode/interfaces_openvpn.py
+++ b/src/conf_mode/interfaces_openvpn.py
@@ -198,6 +198,12 @@ def verify_pki(openvpn):
raise ConfigError(f'Cannot use encrypted private key on openvpn interface {interface}')
if 'dh_params' in tls:
+ if 'dh' not in pki:
+ raise ConfigError(f'pki dh is not configured')
+ proposed_dh = tls['dh_params']
+ if proposed_dh not in pki['dh'].keys():
+ raise ConfigError(f"pki dh '{proposed_dh}' is not configured")
+
pki_dh = pki['dh'][tls['dh_params']]
dh_params = load_dh_parameters(pki_dh['parameters'])
dh_numbers = dh_params.parameter_numbers()
diff --git a/src/conf_mode/interfaces_wireless.py b/src/conf_mode/interfaces_wireless.py
index 02b4a2500..c0a17c0bc 100755
--- a/src/conf_mode/interfaces_wireless.py
+++ b/src/conf_mode/interfaces_wireless.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2019-2020 VyOS maintainers and contributors
+# Copyright (C) 2019-2024 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
@@ -31,8 +31,9 @@ from vyos.configverify import verify_vrf
from vyos.configverify import verify_bond_bridge_member
from vyos.ifconfig import WiFiIf
from vyos.template import render
-from vyos.utils.process import call
from vyos.utils.dict import dict_search
+from vyos.utils.kernel import check_kmod
+from vyos.utils.process import call
from vyos import ConfigError
from vyos import airbag
airbag.enable()
@@ -118,6 +119,10 @@ def verify(wifi):
if 'physical_device' not in wifi:
raise ConfigError('You must specify a physical-device "phy"')
+ physical_device = wifi['physical_device']
+ if not os.path.exists(f'/sys/class/ieee80211/{physical_device}'):
+ raise ConfigError(f'Wirelss interface PHY "{physical_device}" does not exist!')
+
if 'type' not in wifi:
raise ConfigError('You must specify a WiFi mode')
@@ -266,6 +271,7 @@ def apply(wifi):
if __name__ == '__main__':
try:
+ check_kmod('mac80211')
c = get_config()
verify(c)
generate(c)
diff --git a/src/conf_mode/load-balancing_reverse-proxy.py b/src/conf_mode/load-balancing_reverse-proxy.py
index 694a4e1ea..1569d8d71 100755
--- a/src/conf_mode/load-balancing_reverse-proxy.py
+++ b/src/conf_mode/load-balancing_reverse-proxy.py
@@ -75,6 +75,10 @@ def verify(lb):
raise ConfigError(f'"TCP" port "{tmp_port}" is used by another service')
for back, back_config in lb['backend'].items():
+ if 'http-check' in back_config:
+ http_check = back_config['http-check']
+ if 'expect' in http_check and 'status' in http_check['expect'] and 'string' in http_check['expect']:
+ raise ConfigError(f'"expect status" and "expect string" can not be configured together!')
if 'server' not in back_config:
raise ConfigError(f'"{back} server" must be configured!')
for bk_server, bk_server_conf in back_config['server'].items():
@@ -84,6 +88,10 @@ def verify(lb):
if {'send_proxy', 'send_proxy_v2'} <= set(bk_server_conf):
raise ConfigError(f'Cannot use both "send-proxy" and "send-proxy-v2" for server "{bk_server}"')
+ if 'ssl' in back_config:
+ if {'no_verify', 'ca_certificate'} <= set(back_config['ssl']):
+ raise ConfigError(f'backend {back} cannot have both ssl options no-verify and ca-certificate set!')
+
for front, front_config in lb['service'].items():
for cert in dict_search('ssl.certificate', front_config) or []:
verify_pki_certificate(lb, cert)
diff --git a/src/conf_mode/pki.py b/src/conf_mode/pki.py
index 3ab6ac5c3..8deec0e85 100755
--- a/src/conf_mode/pki.py
+++ b/src/conf_mode/pki.py
@@ -24,6 +24,8 @@ from vyos.config import config_dict_merge
from vyos.configdep import set_dependents
from vyos.configdep import call_dependents
from vyos.configdict import node_changed
+from vyos.configdiff import Diff
+from vyos.configdiff import get_config_diff
from vyos.defaults import directories
from vyos.pki import is_ca_certificate
from vyos.pki import load_certificate
@@ -136,32 +138,32 @@ def get_config(config=None):
if len(argv) > 1 and argv[1] == 'certbot_renew':
pki['certbot_renew'] = {}
- tmp = node_changed(conf, base + ['ca'], recursive=True)
+ tmp = node_changed(conf, base + ['ca'], recursive=True, expand_nodes=Diff.DELETE | Diff.ADD)
if tmp:
if 'changed' not in pki: pki.update({'changed':{}})
pki['changed'].update({'ca' : tmp})
- tmp = node_changed(conf, base + ['certificate'], recursive=True)
+ tmp = node_changed(conf, base + ['certificate'], recursive=True, expand_nodes=Diff.DELETE | Diff.ADD)
if tmp:
if 'changed' not in pki: pki.update({'changed':{}})
pki['changed'].update({'certificate' : tmp})
- tmp = node_changed(conf, base + ['dh'], recursive=True)
+ tmp = node_changed(conf, base + ['dh'], recursive=True, expand_nodes=Diff.DELETE | Diff.ADD)
if tmp:
if 'changed' not in pki: pki.update({'changed':{}})
pki['changed'].update({'dh' : tmp})
- tmp = node_changed(conf, base + ['key-pair'], recursive=True)
+ tmp = node_changed(conf, base + ['key-pair'], recursive=True, expand_nodes=Diff.DELETE | Diff.ADD)
if tmp:
if 'changed' not in pki: pki.update({'changed':{}})
pki['changed'].update({'key_pair' : tmp})
- tmp = node_changed(conf, base + ['openssh'], recursive=True)
+ tmp = node_changed(conf, base + ['openssh'], recursive=True, expand_nodes=Diff.DELETE | Diff.ADD)
if tmp:
if 'changed' not in pki: pki.update({'changed':{}})
pki['changed'].update({'openssh' : tmp})
- tmp = node_changed(conf, base + ['openvpn', 'shared-secret'], recursive=True)
+ tmp = node_changed(conf, base + ['openvpn', 'shared-secret'], recursive=True, expand_nodes=Diff.DELETE | Diff.ADD)
if tmp:
if 'changed' not in pki: pki.update({'changed':{}})
pki['changed'].update({'openvpn' : tmp})
@@ -198,6 +200,7 @@ def get_config(config=None):
pki['system'] = conf.get_config_dict([], key_mangling=('-', '_'),
get_first_key=True,
no_tag_node_value_mangle=True)
+ D = get_config_diff(conf)
for search in sync_search:
for key in search['keys']:
@@ -217,15 +220,22 @@ def get_config(config=None):
if not search_dict:
continue
for found_name, found_path in dict_search_recursive(search_dict, key):
- if found_name == item_name:
- path = search['path']
- path_str = ' '.join(path + found_path)
- print(f'PKI: Updating config: {path_str} {found_name}')
+ if isinstance(found_name, list) and item_name not in found_name:
+ continue
+
+ if isinstance(found_name, str) and found_name != item_name:
+ continue
+
+ path = search['path']
+ path_str = ' '.join(path + found_path)
+ print(f'PKI: Updating config: {path_str} {item_name}')
- if path[0] == 'interfaces':
- ifname = found_path[0]
+ if path[0] == 'interfaces':
+ ifname = found_path[0]
+ if not D.node_changed_presence(path + [ifname]):
set_dependents(path[1], conf, ifname)
- else:
+ else:
+ if not D.node_changed_presence(path):
set_dependents(path[1], conf)
return pki
diff --git a/src/conf_mode/protocols_bgp.py b/src/conf_mode/protocols_bgp.py
index 2b16de775..44409c0e3 100755
--- a/src/conf_mode/protocols_bgp.py
+++ b/src/conf_mode/protocols_bgp.py
@@ -31,6 +31,7 @@ from vyos.utils.dict import dict_search
from vyos.utils.network import get_interface_vrf
from vyos.utils.network import is_addr_assigned
from vyos.utils.process import process_named_running
+from vyos.utils.process import call
from vyos import ConfigError
from vyos import frr
from vyos import airbag
@@ -50,13 +51,8 @@ def get_config(config=None):
# eqivalent of the C foo ? 'a' : 'b' statement
base = vrf and ['vrf', 'name', vrf, 'protocols', 'bgp'] or base_path
- bgp = conf.get_config_dict(
- base,
- key_mangling=('-', '_'),
- get_first_key=True,
- no_tag_node_value_mangle=True,
- with_recursive_defaults=True,
- )
+ bgp = conf.get_config_dict(base, key_mangling=('-', '_'),
+ get_first_key=True, no_tag_node_value_mangle=True)
bgp['dependent_vrfs'] = conf.get_config_dict(['vrf', 'name'],
key_mangling=('-', '_'),
@@ -75,22 +71,29 @@ def get_config(config=None):
if vrf:
bgp.update({'vrf' : vrf})
# We can not delete the BGP VRF instance if there is a L3VNI configured
+ # FRR L3VNI must be deleted first otherwise we will see error:
+ # "FRR error: Please unconfigure l3vni 3000"
tmp = ['vrf', 'name', vrf, 'vni']
- if conf.exists(tmp):
- bgp.update({'vni' : conf.return_value(tmp)})
+ if conf.exists_effective(tmp):
+ bgp.update({'vni' : conf.return_effective_value(tmp)})
# We can safely delete ourself from the dependent vrf list
if vrf in bgp['dependent_vrfs']:
del bgp['dependent_vrfs'][vrf]
- bgp['dependent_vrfs'].update({'default': {'protocols': {
- 'bgp': conf.get_config_dict(base_path, key_mangling=('-', '_'),
- get_first_key=True,
- no_tag_node_value_mangle=True)}}})
+ bgp['dependent_vrfs'].update({'default': {'protocols': {
+ 'bgp': conf.get_config_dict(base_path, key_mangling=('-', '_'),
+ get_first_key=True,
+ no_tag_node_value_mangle=True)}}})
+
if not conf.exists(base):
# If bgp instance is deleted then mark it
bgp.update({'deleted' : ''})
return bgp
+ # We have gathered the dict representation of the CLI, but there are default
+ # options which we need to update into the dictionary retrived.
+ bgp = conf.merge_defaults(bgp, recursive=True)
+
# We also need some additional information from the config, prefix-lists
# and route-maps for instance. They will be used in verify().
#
@@ -242,10 +245,6 @@ def verify(bgp):
if verify_vrf_as_import(bgp['vrf'], tmp_afi, bgp['dependent_vrfs']):
raise ConfigError(f'Cannot delete VRF instance "{bgp["vrf"]}", ' \
'unconfigure "import vrf" commands!')
- # We can not delete the BGP instance if a L3VNI instance exists
- if 'vni' in bgp:
- raise ConfigError(f'Cannot delete VRF instance "{bgp["vrf"]}", ' \
- f'unconfigure VNI "{bgp["vni"]}" first!')
else:
# We are running in the default VRF context, thus we can not delete
# our main BGP instance if there are dependent BGP VRF instances.
@@ -254,7 +253,11 @@ def verify(bgp):
if vrf != 'default':
if dict_search('protocols.bgp', vrf_options):
raise ConfigError('Cannot delete default BGP instance, ' \
- 'dependent VRF instance(s) exist!')
+ 'dependent VRF instance(s) exist(s)!')
+ if 'vni' in vrf_options:
+ raise ConfigError('Cannot delete default BGP instance, ' \
+ 'dependent L3VNI exists!')
+
return None
if 'system_as' not in bgp:
@@ -473,6 +476,22 @@ def verify(bgp):
if peer_group_as is None or (peer_group_as != 'internal' and peer_group_as != bgp['system_as']):
raise ConfigError('route-reflector-client only supported for iBGP peers')
+ # T5833 not all AFIs are supported for VRF
+ if 'vrf' in bgp and 'address_family' in peer_config:
+ unsupported_vrf_afi = {
+ 'ipv4_flowspec',
+ 'ipv6_flowspec',
+ 'ipv4_labeled_unicast',
+ 'ipv6_labeled_unicast',
+ 'ipv4_vpn',
+ 'ipv6_vpn',
+ }
+ for afi in peer_config['address_family']:
+ if afi in unsupported_vrf_afi:
+ raise ConfigError(
+ f"VRF is not allowed for address-family '{afi.replace('_', '-')}'"
+ )
+
# Throw an error if a peer group is not configured for allow range
for prefix in dict_search('listen.range', bgp) or []:
# we can not use dict_search() here as prefix contains dots ...
@@ -591,6 +610,13 @@ def generate(bgp):
return None
def apply(bgp):
+ if 'deleted' in bgp:
+ # We need to ensure that the L3VNI is deleted first.
+ # This is not possible with old config backend
+ # priority bug
+ if {'vrf', 'vni'} <= set(bgp):
+ call('vtysh -c "conf t" -c "vrf {vrf}" -c "no vni {vni}"'.format(**bgp))
+
bgp_daemon = 'bgpd'
# Save original configuration prior to starting any commit actions
diff --git a/src/conf_mode/protocols_pim.py b/src/conf_mode/protocols_pim.py
index 09c3be8df..d450d11ca 100755
--- a/src/conf_mode/protocols_pim.py
+++ b/src/conf_mode/protocols_pim.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2020-2023 VyOS maintainers and contributors
+# Copyright (C) 2020-2024 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
@@ -16,6 +16,7 @@
import os
+from ipaddress import IPv4Address
from ipaddress import IPv4Network
from signal import SIGTERM
from sys import exit
@@ -32,6 +33,9 @@ from vyos import frr
from vyos import airbag
airbag.enable()
+RESERVED_MC_NET = '224.0.0.0/24'
+
+
def get_config(config=None):
if config:
conf = config
@@ -92,9 +96,15 @@ def verify(pim):
if 'interface' not in pim:
raise ConfigError('PIM require defined interfaces!')
- for interface in pim['interface']:
+ for interface, interface_config in pim['interface'].items():
verify_interface_exists(interface)
+ # Check join group in reserved net
+ if 'igmp' in interface_config and 'join' in interface_config['igmp']:
+ for join_addr in interface_config['igmp']['join']:
+ if IPv4Address(join_addr) in IPv4Network(RESERVED_MC_NET):
+ raise ConfigError(f'Groups within {RESERVED_MC_NET} are reserved and cannot be joined!')
+
if 'rp' in pim:
if 'address' not in pim['rp']:
raise ConfigError('PIM rendezvous point needs to be defined!')
diff --git a/src/conf_mode/qos.py b/src/conf_mode/qos.py
index 3dfb4bab8..ccfc8f6b8 100755
--- a/src/conf_mode/qos.py
+++ b/src/conf_mode/qos.py
@@ -70,6 +70,22 @@ def get_shaper(qos, interface_config, direction):
return (map_vyops_tc[shaper_type], shaper_config)
+
+def _clean_conf_dict(conf):
+ """
+ Delete empty nodes from config e.g.
+ match ADDRESS30 {
+ ip {
+ source {}
+ }
+ }
+ """
+ if isinstance(conf, dict):
+ return {node: _clean_conf_dict(val) for node, val in conf.items() if val != {} and _clean_conf_dict(val) != {}}
+ else:
+ return conf
+
+
def get_config(config=None):
if config:
conf = config
@@ -120,6 +136,13 @@ def get_config(config=None):
if 'queue_limit' not in qos['policy'][policy][p_name]['precedence'][precedence]:
qos['policy'][policy][p_name]['precedence'][precedence]['queue_limit'] = \
str(int(4 * max_thr))
+ # cleanup empty match config
+ if 'class' in p_config:
+ for cls, cls_config in p_config['class'].items():
+ if 'match' in cls_config:
+ cls_config['match'] = _clean_conf_dict(cls_config['match'])
+ if cls_config['match'] == {}:
+ del cls_config['match']
return qos
diff --git a/src/conf_mode/service_ipoe-server.py b/src/conf_mode/service_ipoe-server.py
index 11e950782..28b7fb03c 100755
--- a/src/conf_mode/service_ipoe-server.py
+++ b/src/conf_mode/service_ipoe-server.py
@@ -66,7 +66,7 @@ def verify(ipoe):
raise ConfigError('No IPoE interface configured')
for interface, iface_config in ipoe['interface'].items():
- verify_interface_exists(interface)
+ verify_interface_exists(interface, warning_only=True)
if 'client_subnet' in iface_config and 'vlan' in iface_config:
raise ConfigError('Option "client-subnet" and "vlan" are mutually exclusive, '
'use "client-ip-pool" instead!')
diff --git a/src/conf_mode/service_pppoe-server.py b/src/conf_mode/service_pppoe-server.py
index b9d174933..c95f976d3 100755
--- a/src/conf_mode/service_pppoe-server.py
+++ b/src/conf_mode/service_pppoe-server.py
@@ -84,12 +84,29 @@ def verify_pado_delay(pppoe):
pado_delay = pppoe['pado_delay']
delays_without_sessions = pado_delay['delays_without_sessions']
+ if 'disable' in delays_without_sessions:
+ raise ConfigError(
+ 'Number of sessions must be specified for "pado-delay disable"'
+ )
+
if len(delays_without_sessions) > 1:
raise ConfigError(
f'Cannot add more then ONE pado-delay without sessions, '
f'but {len(delays_without_sessions)} were set'
)
+ if 'disable' in [delay[0] for delay in pado_delay['delays_with_sessions']]:
+ # need to sort delays by sessions to verify if there is no delay
+ # for sessions after disabling
+ sorted_pado_delay = sorted(pado_delay['delays_with_sessions'], key=lambda k_v: k_v[1])
+ last_delay = sorted_pado_delay[-1]
+
+ if last_delay[0] != 'disable':
+ raise ConfigError(
+ f'Cannot add pado-delay after disabled sessions, but '
+ f'"pado-delay {last_delay[0]} sessions {last_delay[1]}" was set'
+ )
+
def verify(pppoe):
if not pppoe:
return None
@@ -105,7 +122,7 @@ def verify(pppoe):
# Check is interface exists in the system
for interface in pppoe['interface']:
- verify_interface_exists(interface)
+ verify_interface_exists(interface, warning_only=True)
return None
diff --git a/src/conf_mode/system_host-name.py b/src/conf_mode/system_host-name.py
index 8975cadb6..3f245f166 100755
--- a/src/conf_mode/system_host-name.py
+++ b/src/conf_mode/system_host-name.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2018-2023 VyOS maintainers and contributors
+# Copyright (C) 2018-2024 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
@@ -22,6 +22,7 @@ import vyos.hostsd_client
from vyos.base import Warning
from vyos.config import Config
+from vyos.configdict import leaf_node_changed
from vyos.ifconfig import Section
from vyos.template import is_ip
from vyos.utils.process import cmd
@@ -37,6 +38,7 @@ default_config_data = {
'domain_search': [],
'nameserver': [],
'nameservers_dhcp_interfaces': {},
+ 'snmpd_restart_reqired': False,
'static_host_mapping': {}
}
@@ -52,6 +54,10 @@ def get_config(config=None):
hosts['hostname'] = conf.return_value(['system', 'host-name'])
+ base = ['system']
+ if leaf_node_changed(conf, base + ['host-name']) or leaf_node_changed(conf, base + ['domain-name']):
+ hosts['snmpd_restart_reqired'] = True
+
# This may happen if the config is not loaded yet,
# e.g. if run by cloud-init
if not hosts['hostname']:
@@ -171,7 +177,7 @@ def apply(config):
call("systemctl restart rsyslog.service")
# If SNMP is running, restart it too
- if process_named_running('snmpd'):
+ if process_named_running('snmpd') and config['snmpd_restart_reqired']:
call('systemctl restart snmpd.service')
return None
diff --git a/src/conf_mode/system_ip.py b/src/conf_mode/system_ip.py
index b945b51f2..2a0bda91a 100755
--- a/src/conf_mode/system_ip.py
+++ b/src/conf_mode/system_ip.py
@@ -81,11 +81,6 @@ def apply(opt):
value = '0' if (tmp != None) else '1'
write_file('/proc/sys/net/ipv4/conf/all/forwarding', value)
- # enable/disable IPv4 directed broadcast forwarding
- tmp = dict_search('disable_directed_broadcast', opt)
- value = '0' if (tmp != None) else '1'
- write_file('/proc/sys/net/ipv4/conf/all/bc_forwarding', value)
-
# configure multipath
tmp = dict_search('multipath.ignore_unreachable_nexthops', opt)
value = '1' if (tmp != None) else '0'
diff --git a/src/conf_mode/vrf.py b/src/conf_mode/vrf.py
index 1fc813189..8d8c234c0 100755
--- a/src/conf_mode/vrf.py
+++ b/src/conf_mode/vrf.py
@@ -130,11 +130,6 @@ def get_config(config=None):
tmp = {'policy' : {'route-map' : conf.get_config_dict(['policy', 'route-map'],
get_first_key=True)}}
- # L3VNI setup is done via vrf_vni.py as it must be de-configured (on node
- # deletetion prior to the BGP process. Tell the Jinja2 template no VNI
- # setup is needed
- vrf.update({'no_vni' : ''})
-
# Merge policy dict into "regular" config dict
vrf = dict_merge(tmp, vrf)
return vrf
@@ -315,6 +310,20 @@ def apply(vrf):
for chain, rule in nftables_rules.items():
cmd(f'nft flush chain inet vrf_zones {chain}')
+ # Return default ip rule values
+ if 'name' not in vrf:
+ for afi in ['-4', '-6']:
+ # move lookup local to pref 0 (from 32765)
+ if not has_rule(afi, 0, 'local'):
+ call(f'ip {afi} rule add pref 0 from all lookup local')
+ if has_rule(afi, 32765, 'local'):
+ call(f'ip {afi} rule del pref 32765 table local')
+
+ if has_rule(afi, 1000, 'l3mdev'):
+ call(f'ip {afi} rule del pref 1000 l3mdev protocol kernel')
+ if has_rule(afi, 2000, 'l3mdev'):
+ call(f'ip {afi} rule del pref 2000 l3mdev unreachable')
+
# Apply FRR filters
zebra_daemon = 'zebra'
# Save original configuration prior to starting any commit actions
diff --git a/src/conf_mode/vrf_vni.py b/src/conf_mode/vrf_vni.py
deleted file mode 100644
index 8dab164d7..000000000
--- a/src/conf_mode/vrf_vni.py
+++ /dev/null
@@ -1,103 +0,0 @@
-#!/usr/bin/env python3
-#
-# 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
-# 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/>.
-
-from sys import argv
-from sys import exit
-
-from vyos.config import Config
-from vyos.template import render_to_string
-from vyos import ConfigError
-from vyos import frr
-from vyos import airbag
-airbag.enable()
-
-def get_config(config=None):
- if config:
- conf = config
- else:
- conf = Config()
-
- vrf_name = None
- if len(argv) > 1:
- vrf_name = argv[1]
- else:
- return None
-
- # Using duplicate L3VNIs makes no sense - it's also forbidden in FRR,
- # thus VyOS CLI must deny this, too. Instead of getting only the dict for
- # the requested VRF and den comparing it with depenent VRfs to not have any
- # duplicate we will just grad ALL VRFs by default but only render/apply
- # the configuration for the requested VRF - that makes the code easier and
- # hopefully less error prone
- vrf = conf.get_config_dict(['vrf'], key_mangling=('-', '_'),
- no_tag_node_value_mangle=True,
- get_first_key=True)
-
- # Store name of VRF we are interested in for FRR config rendering
- vrf.update({'only_vrf' : vrf_name})
-
- return vrf
-
-def verify(vrf):
- if not vrf:
- return
-
- if len(argv) < 2:
- raise ConfigError('VRF parameter not specified when valling vrf_vni.py')
-
- if 'name' in vrf:
- vni_ids = []
- for name, vrf_config in vrf['name'].items():
- # VRF VNI (Virtual Network Identifier) must be unique on the system
- if 'vni' in vrf_config:
- if vrf_config['vni'] in vni_ids:
- raise ConfigError(f'VRF "{name}" VNI is not unique!')
- vni_ids.append(vrf_config['vni'])
-
- return None
-
-def generate(vrf):
- if not vrf:
- return
-
- vrf['new_frr_config'] = render_to_string('frr/zebra.vrf.route-map.frr.j2', vrf)
- return None
-
-def apply(vrf):
- frr_daemon = 'zebra'
-
- # add configuration to FRR
- frr_cfg = frr.FRRConfig()
- frr_cfg.load_configuration(frr_daemon)
- # There is only one VRF inside the dict as we read only one in get_config()
- if vrf and 'only_vrf' in vrf:
- vrf_name = vrf['only_vrf']
- frr_cfg.modify_section(f'^vrf {vrf_name}', stop_pattern='^exit-vrf', remove_stop_mark=True)
- if vrf and 'new_frr_config' in vrf:
- frr_cfg.add_before(frr.default_add_before, vrf['new_frr_config'])
- frr_cfg.commit_configuration(frr_daemon)
-
- return None
-
-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/etc/rsyslog.conf b/src/etc/rsyslog.conf
index 9781f0835..b3f41acb6 100644
--- a/src/etc/rsyslog.conf
+++ b/src/etc/rsyslog.conf
@@ -15,21 +15,6 @@ $KLogPath /proc/kmsg
#### GLOBAL DIRECTIVES ####
###########################
-# The lines below cause all listed daemons/processes to be logged into
-# /var/log/auth.log, then drops the message so it does not also go to the
-# regular syslog so that messages are not duplicated
-
-$outchannel auth_log,/var/log/auth.log
-if $programname == 'CRON' or
- $programname == 'sudo' or
- $programname == 'su'
- then :omfile:$auth_log
-
-if $programname == 'CRON' or
- $programname == 'sudo' or
- $programname == 'su'
- then stop
-
# Use traditional timestamp format.
# To enable high precision timestamps, comment out the following line.
# A modern-style logfile format similar to TraditionalFileFormat, buth with high-precision timestamps and timezone information
@@ -60,6 +45,21 @@ $Umask 0022
#
$IncludeConfig /etc/rsyslog.d/*.conf
+# The lines below cause all listed daemons/processes to be logged into
+# /var/log/auth.log, then drops the message so it does not also go to the
+# regular syslog so that messages are not duplicated
+
+$outchannel auth_log,/var/log/auth.log
+if $programname == 'CRON' or
+ $programname == 'sudo' or
+ $programname == 'su'
+ then :omfile:$auth_log
+
+if $programname == 'CRON' or
+ $programname == 'sudo' or
+ $programname == 'su'
+ then stop
+
###############
#### RULES ####
###############
diff --git a/src/helpers/vyos-failover.py b/src/helpers/vyos-failover.py
index f34c18916..348974364 100755
--- a/src/helpers/vyos-failover.py
+++ b/src/helpers/vyos-failover.py
@@ -197,6 +197,7 @@ if __name__ == '__main__':
proto = nexthop_config.get('check').get('type')
target = nexthop_config.get('check').get('target')
timeout = nexthop_config.get('check').get('timeout')
+ onlink = 'onlink' if 'onlink' in nexthop_config else ''
# Route not found in the current routing table
if not is_route_exists(route, next_hop, conf_iface, conf_metric):
@@ -206,14 +207,14 @@ if __name__ == '__main__':
if debug: print(f' [ ADD ] -- ip route add {route} via {next_hop} dev {conf_iface} '
f'metric {conf_metric} proto failover\n###')
rc, command = rc_cmd(f'ip route add {route} via {next_hop} dev {conf_iface} '
- f'metric {conf_metric} proto failover')
+ f'{onlink} metric {conf_metric} proto failover')
# If something is wrong and gateway not added
# Example: Error: Next-hop has invalid gateway.
if rc !=0:
if debug: print(f'{command} -- return-code [RC: {rc}] {next_hop} dev {conf_iface}')
else:
journal.send(f'ip route add {route} via {next_hop} dev {conf_iface} '
- f'metric {conf_metric} proto failover', SYSLOG_IDENTIFIER=my_name)
+ f'{onlink} metric {conf_metric} proto failover', SYSLOG_IDENTIFIER=my_name)
else:
if debug: print(f' [ TARGET_FAIL ] target checks fails for [{target}], do nothing')
journal.send(f'Check fail for route {route} target {target} proto {proto} '
diff --git a/src/helpers/vyos-vrrp-conntracksync.sh b/src/helpers/vyos-vrrp-conntracksync.sh
index 0cc718938..90fa77f23 100755
--- a/src/helpers/vyos-vrrp-conntracksync.sh
+++ b/src/helpers/vyos-vrrp-conntracksync.sh
@@ -25,7 +25,7 @@ LOGCMD="logger -t $TAG -p $FACILITY.$LEVEL"
VRRP_GRP="VRRP sync-group [$2]"
FAILOVER_STATE="/var/run/vyatta-conntrackd-failover-state"
-$LOGCMD "vyatta-vrrp-conntracksync invoked at `date`"
+$LOGCMD "vyos-vrrp-conntracksync invoked at `date`"
if ! systemctl is-active --quiet conntrackd.service; then
echo "conntrackd service not running"
@@ -148,7 +148,7 @@ case "$1" in
*)
echo UNKNOWN at `date` > $FAILOVER_STATE
$LOGCMD "ERROR: `uname -n` unknown state transition for $VRRP_GRP"
- echo "Usage: vyatta-vrrp-conntracksync.sh {master|backup|fault}"
+ echo "Usage: vyos-vrrp-conntracksync.sh {master|backup|fault}"
exit 1
;;
esac
diff --git a/src/helpers/vyos_config_sync.py b/src/helpers/vyos_config_sync.py
index 0604b2837..9d9aec376 100755
--- a/src/helpers/vyos_config_sync.py
+++ b/src/helpers/vyos_config_sync.py
@@ -93,7 +93,8 @@ def set_remote_config(
key: str,
op: str,
mask: Dict[str, Any],
- config: Dict[str, Any]) -> Optional[Dict[str, Any]]:
+ config: Dict[str, Any],
+ port: int) -> Optional[Dict[str, Any]]:
"""Loads the VyOS configuration in JSON format to a remote host.
Args:
@@ -102,6 +103,7 @@ def set_remote_config(
op (str): The operation to perform (set or load).
mask (dict): The dict of paths in sections.
config (dict): The dict of masked config data.
+ port (int): The remote API port
Returns:
Optional[Dict[str, Any]]: The response from the remote host as a
@@ -113,7 +115,7 @@ def set_remote_config(
# Disable the InsecureRequestWarning
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
- url = f'https://{address}/configure-section'
+ url = f'https://{address}:{port}/configure-section'
data = json.dumps({
'op': op,
'mask': mask,
@@ -138,7 +140,8 @@ def is_section_revised(section: List[str]) -> bool:
def config_sync(secondary_address: str,
secondary_key: str,
sections: List[list[str]],
- mode: str):
+ mode: str,
+ secondary_port: int):
"""Retrieve a config section from primary router in JSON format and send it to
secondary router
"""
@@ -158,7 +161,8 @@ def config_sync(secondary_address: str,
key=secondary_key,
op=mode,
mask=mask_dict,
- config=config_dict)
+ config=config_dict,
+ port=secondary_port)
logger.debug(f"Set config for sections '{sections}': {set_config}")
@@ -178,14 +182,12 @@ if __name__ == '__main__':
secondary_address = config.get('secondary', {}).get('address')
secondary_address = bracketize_ipv6(secondary_address)
secondary_key = config.get('secondary', {}).get('key')
+ secondary_port = int(config.get('secondary', {}).get('port', 443))
sections = config.get('section')
timeout = int(config.get('secondary', {}).get('timeout'))
- if not all([
- mode, secondary_address, secondary_key, sections
- ]):
- logger.error(
- "Missing required configuration data for config synchronization.")
+ if not all([mode, secondary_address, secondary_key, sections]):
+ logger.error("Missing required configuration data for config synchronization.")
exit(0)
# Generate list_sections of sections/subsections
@@ -200,5 +202,4 @@ if __name__ == '__main__':
else:
list_sections.append([section])
- config_sync(secondary_address, secondary_key,
- list_sections, mode)
+ config_sync(secondary_address, secondary_key, list_sections, mode, secondary_port)
diff --git a/src/init/vyos-router b/src/init/vyos-router
index 06fea140d..15e37df07 100755
--- a/src/init/vyos-router
+++ b/src/init/vyos-router
@@ -451,6 +451,7 @@ start ()
touch /tmp/vyos.ifconfig.debug
touch /tmp/vyos.frr.debug
touch /tmp/vyos.container.debug
+ touch /tmp/vyos.smoketest.debug
fi
log_action_begin_msg "Mounting VyOS Config"
diff --git a/src/migration-scripts/firewall/14-to-15 b/src/migration-scripts/firewall/14-to-15
new file mode 100755
index 000000000..735839365
--- /dev/null
+++ b/src/migration-scripts/firewall/14-to-15
@@ -0,0 +1,46 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2022-2024 VyOS maintainers and contributors
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 or later as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+# T5535: Migrate <set system ip disable-directed-broadcast> to <set firewall global-options directed-broadcas [enable|disable]
+
+from sys import argv
+from sys import exit
+
+from vyos.configtree import ConfigTree
+
+if len(argv) < 2:
+ print("Must specify file name!")
+ exit(1)
+
+file_name = argv[1]
+
+with open(file_name, 'r') as f:
+ config_file = f.read()
+
+config = ConfigTree(config_file)
+
+base = ['firewall']
+
+if config.exists(['system', 'ip', 'disable-directed-broadcast']):
+ config.set(['firewall', 'global-options', 'directed-broadcast'], value='disable')
+ config.delete(['system', 'ip', 'disable-directed-broadcast'])
+
+try:
+ with open(file_name, 'w') as f:
+ f.write(config.to_string())
+except OSError as e:
+ print("Failed to save the modified config: {}".format(e))
+ exit(1) \ No newline at end of file
diff --git a/src/migration-scripts/openconnect/2-to-3 b/src/migration-scripts/openconnect/2-to-3
new file mode 100755
index 000000000..e78fc8a91
--- /dev/null
+++ b/src/migration-scripts/openconnect/2-to-3
@@ -0,0 +1,50 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2024 VyOS maintainers and contributors
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 or later as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+# 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')
+
+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/pppoe-server/9-to-10 b/src/migration-scripts/pppoe-server/9-to-10
new file mode 100755
index 000000000..e0c782f04
--- /dev/null
+++ b/src/migration-scripts/pppoe-server/9-to-10
@@ -0,0 +1,56 @@
+#!/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/>.
+
+# 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
+
+# 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]
+
+# Rename last delay -> disable
+tmp = base + [last_delay]
+if config.exists(tmp):
+ config.rename(tmp, '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)
diff --git a/src/op_mode/connect_disconnect.py b/src/op_mode/connect_disconnect.py
index bd02dc6ea..373f9e953 100755
--- a/src/op_mode/connect_disconnect.py
+++ b/src/op_mode/connect_disconnect.py
@@ -48,7 +48,7 @@ def connect(interface):
if os.path.isdir(f'/sys/class/net/{interface}'):
print(f'Interface {interface}: already connected!')
elif check_ppp_running(interface):
- print(f'Interface {interface}: connection is beeing established!')
+ print(f'Interface {interface}: connection is being established!')
else:
print(f'Interface {interface}: connecting...')
call(f'systemctl restart ppp@{interface}.service')
@@ -58,7 +58,7 @@ def connect(interface):
else:
call(f'VYOS_TAGNODE_VALUE={interface} /usr/libexec/vyos/conf_mode/interfaces_wwan.py')
else:
- print(f'Unknown interface {interface}, can not connect. Aborting!')
+ print(f'Unknown interface {interface}, cannot connect. Aborting!')
# Reaply QoS configuration
config = ConfigTreeQuery()
@@ -90,7 +90,7 @@ def disconnect(interface):
modem = interface.lstrip('wwan')
call(f'mmcli --modem {modem} --simple-disconnect', stdout=DEVNULL)
else:
- print(f'Unknown interface {interface}, can not disconnect. Aborting!')
+ print(f'Unknown interface {interface}, cannot disconnect. Aborting!')
def main():
parser = argparse.ArgumentParser()
diff --git a/src/op_mode/firewall.py b/src/op_mode/firewall.py
index 25554b781..442c186cc 100755
--- a/src/op_mode/firewall.py
+++ b/src/op_mode/firewall.py
@@ -16,6 +16,7 @@
import argparse
import ipaddress
+import json
import re
import tabulate
import textwrap
@@ -89,10 +90,38 @@ def get_nftables_details(family, hook, priority):
out[rule_id] = rule
return out
-def output_firewall_vertical(rules, headers):
+def get_nftables_group_members(family, table, name):
+ prefix = 'ip6' if family == 'ipv6' else 'ip'
+ out = []
+
+ try:
+ results_str = cmd(f'sudo nft -j list set {prefix} {table} {name}')
+ results = json.loads(results_str)
+ except:
+ return out
+
+ if 'nftables' not in results:
+ return out
+
+ for obj in results['nftables']:
+ if 'set' not in obj:
+ continue
+
+ set_obj = obj['set']
+
+ if 'elem' in set_obj:
+ for elem in set_obj['elem']:
+ if isinstance(elem, str):
+ out.append(elem)
+ elif isinstance(elem, dict) and 'elem' in elem:
+ out.append(elem['elem'])
+
+ return out
+
+def output_firewall_vertical(rules, headers, adjust=True):
for rule in rules:
- adjusted_rule = rule + [""] * (len(headers) - len(rule)) # account for different header length, like default-action
- transformed_rule = [[header, textwrap.fill(adjusted_rule[i].replace('\n', ' '), 65)] for i, header in enumerate(headers)] # create key-pair list from headers and rules lists; wrap at 100 char
+ adjusted_rule = rule + [""] * (len(headers) - len(rule)) if adjust else rule # account for different header length, like default-action
+ transformed_rule = [[header, textwrap.fill(adjusted_rule[i].replace('\n', ' '), 65)] for i, header in enumerate(headers) if i < len(adjusted_rule)] # create key-pair list from headers and rules lists; wrap at 100 char
print(tabulate.tabulate(transformed_rule, tablefmt="presto"))
print()
@@ -453,6 +482,7 @@ def show_firewall_group(name=None):
return out
rows = []
+ header_tail = []
for group_type, group_type_conf in firewall['group'].items():
##
@@ -479,21 +509,53 @@ def show_firewall_group(name=None):
rows.append(row)
else:
+ if not args.detail:
+ header_tail = ['Timeout', 'Expires']
+
for dynamic_type in ['address_group', 'ipv6_address_group']:
+ family = 'ipv4' if dynamic_type == 'address_group' else 'ipv6'
+ prefix = 'DA_' if dynamic_type == 'address_group' else 'DA6_'
if dynamic_type in firewall['group']['dynamic_group']:
for dynamic_name, dynamic_conf in firewall['group']['dynamic_group'][dynamic_type].items():
references = find_references(dynamic_type, dynamic_name)
row = [dynamic_name, textwrap.fill(dynamic_conf.get('description') or '', 50), dynamic_type + '(dynamic)', '\n'.join(references) or 'N/D']
- row.append('N/D')
- rows.append(row)
+
+ members = get_nftables_group_members(family, 'vyos_filter', f'{prefix}{dynamic_name}')
+
+ if not members:
+ if args.detail:
+ row.append('N/D')
+ else:
+ row += ["N/D"] * 3
+ rows.append(row)
+ continue
+
+ for idx, member in enumerate(members):
+ val = member.get('val', 'N/D')
+ timeout = str(member.get('timeout', 'N/D'))
+ expires = str(member.get('expires', 'N/D'))
+
+ if args.detail:
+ row.append(f'{val} (timeout: {timeout}, expires: {expires})')
+ continue
+
+ if idx > 0:
+ row = [""] * 4
+
+ row += [val, timeout, expires]
+ rows.append(row)
+
+ if args.detail:
+ header_tail += [""] * (len(members) - 1)
+ rows.append(row)
if rows:
print('Firewall Groups\n')
if args.detail:
- header = ['Name', 'Description','Type', 'References', 'Members']
- output_firewall_vertical(rows, header)
+ header = ['Name', 'Description', 'Type', 'References', 'Members'] + header_tail
+ output_firewall_vertical(rows, header, adjust=False)
else:
- header = ['Name', 'Type', 'References', 'Members']
+ header = ['Name', 'Type', 'References', 'Members'] + header_tail
for i in rows:
rows[rows.index(i)].pop(1)
print(tabulate.tabulate(rows, header))
diff --git a/src/op_mode/image_installer.py b/src/op_mode/image_installer.py
index 9f6949fb3..ba0e3b6db 100755
--- a/src/op_mode/image_installer.py
+++ b/src/op_mode/image_installer.py
@@ -26,6 +26,7 @@ from os import environ
from typing import Union
from urllib.parse import urlparse
from passlib.hosts import linux_context
+from errno import ENOSPC
from psutil import disk_partitions
@@ -60,7 +61,8 @@ MSG_INPUT_CONFIG_CHOICE: str = 'The following config files are available for boo
MSG_INPUT_CONFIG_CHOOSE: str = 'Which file would you like as boot config?'
MSG_INPUT_IMAGE_NAME: str = 'What would you like to name this image?'
MSG_INPUT_IMAGE_DEFAULT: str = 'Would you like to set the new image as the default one for boot?'
-MSG_INPUT_PASSWORD: str = 'Please enter a password for the "vyos" user'
+MSG_INPUT_PASSWORD: str = 'Please enter a password for the "vyos" user:'
+MSG_INPUT_PASSWORD_CONFIRM: str = 'Please confirm password for the "vyos" user:'
MSG_INPUT_ROOT_SIZE_ALL: str = 'Would you like to use all the free space on the drive?'
MSG_INPUT_ROOT_SIZE_SET: str = 'Please specify the size (in GB) of the root partition (min is 1.5 GB)?'
MSG_INPUT_CONSOLE_TYPE: str = 'What console should be used by default? (K: KVM, S: Serial, U: USB-Serial)?'
@@ -74,6 +76,7 @@ MSG_WARN_ROOT_SIZE_TOOBIG: str = 'The size is too big. Try again.'
MSG_WARN_ROOT_SIZE_TOOSMALL: str = 'The size is too small. Try again'
MSG_WARN_IMAGE_NAME_WRONG: str = 'The suggested name is unsupported!\n'\
'It must be between 1 and 64 characters long and contains only the next characters: .+-_ a-z A-Z 0-9'
+MSG_WARN_PASSWORD_CONFIRM: str = 'The entered values did not match. Try again'
CONST_MIN_DISK_SIZE: int = 2147483648 # 2 GB
CONST_MIN_ROOT_SIZE: int = 1610612736 # 1.5 GB
# a reserved space: 2MB for header, 1 MB for BIOS partition, 256 MB for EFI
@@ -695,8 +698,14 @@ def install_image() -> None:
print(MSG_WARN_IMAGE_NAME_WRONG)
# ask for password
- user_password: str = ask_input(MSG_INPUT_PASSWORD, default='vyos',
- no_echo=True)
+ while True:
+ user_password: str = ask_input(MSG_INPUT_PASSWORD, no_echo=True,
+ non_empty=True)
+ confirm: str = ask_input(MSG_INPUT_PASSWORD_CONFIRM, no_echo=True,
+ non_empty=True)
+ if user_password == confirm:
+ break
+ print(MSG_WARN_PASSWORD_CONFIRM)
# ask for default console
console_type: str = ask_input(MSG_INPUT_CONSOLE_TYPE,
@@ -931,6 +940,16 @@ def add_image(image_path: str, vrf: str = None, username: str = '',
if set_as_default:
grub.set_default(image_name, root_dir)
+ except OSError as e:
+ # if no space error, remove image dir and cleanup
+ if e.errno == ENOSPC:
+ cleanup(mounts=[str(iso_path)],
+ remove_items=[f'{root_dir}/boot/{image_name}'])
+ else:
+ # unmount an ISO and cleanup
+ cleanup([str(iso_path)])
+ exit(f'Error: {e}')
+
except Exception as err:
# unmount an ISO and cleanup
cleanup([str(iso_path)])
diff --git a/src/op_mode/openvpn.py b/src/op_mode/openvpn.py
index d54a67199..092873909 100755
--- a/src/op_mode/openvpn.py
+++ b/src/op_mode/openvpn.py
@@ -48,9 +48,12 @@ def _get_tunnel_address(peer_host, peer_port, status_file):
# 10.10.2.0/25,client1,...
lst = [l for l in lst[1:] if '/' not in l.split(',')[0]]
- tunnel_ip = lst[0].split(',')[0]
+ if lst:
+ tunnel_ip = lst[0].split(',')[0]
- return tunnel_ip
+ return tunnel_ip
+
+ return 'n/a'
def _get_interface_status(mode: str, interface: str) -> dict:
status_file = f'/run/openvpn/{interface}.status'
diff --git a/src/op_mode/pki.py b/src/op_mode/pki.py
index ad2c1ada0..b1ca6ee29 100755
--- a/src/op_mode/pki.py
+++ b/src/op_mode/pki.py
@@ -306,7 +306,7 @@ def parse_san_string(san_string):
output.append(ipaddress.IPv4Address(value))
elif tag == 'ipv6':
output.append(ipaddress.IPv6Address(value))
- elif tag == 'dns':
+ elif tag == 'dns' or tag == 'rfc822':
output.append(value)
return output
@@ -324,7 +324,7 @@ def generate_certificate_request(private_key=None, key_type=None, return_request
subject_alt_names = None
if ask_san and ask_yes_no('Do you want to configure Subject Alternative Names?'):
- print("Enter alternative names in a comma separate list, example: ipv4:1.1.1.1,ipv6:fe80::1,dns:vyos.net")
+ print("Enter alternative names in a comma separate list, example: ipv4:1.1.1.1,ipv6:fe80::1,dns:vyos.net,rfc822:user@vyos.net")
san_string = ask_input('Enter Subject Alternative Names:')
subject_alt_names = parse_san_string(san_string)
diff --git a/src/op_mode/uptime.py b/src/op_mode/uptime.py
index d6adf6f4d..059a4c3f6 100755
--- a/src/op_mode/uptime.py
+++ b/src/op_mode/uptime.py
@@ -49,7 +49,7 @@ def _get_raw_data():
res = {}
res["uptime_seconds"] = _get_uptime_seconds()
- res["uptime"] = seconds_to_human(_get_uptime_seconds())
+ res["uptime"] = seconds_to_human(_get_uptime_seconds(), separator=' ')
res["load_average"] = _get_load_averages()
return res
diff --git a/src/services/vyos-configd b/src/services/vyos-configd
index 648a017d5..c89c486e5 100755
--- a/src/services/vyos-configd
+++ b/src/services/vyos-configd
@@ -236,7 +236,7 @@ def process_node_data(config, data, last: bool = False) -> int:
with stdout_redirected(session_out, session_mode):
result = run_script(conf_mode_scripts[script_name], config, args)
- if last:
+ if last and result == R_SUCCESS:
call_dependents(dependent_func=config.dependent_func)
return result