summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--data/config-mode-dependencies.json20
-rw-r--r--data/templates/accel-ppp/ipoe.config.j214
-rw-r--r--data/templates/container/registries.conf.j26
-rw-r--r--interface-definitions/container.xml.in9
-rw-r--r--interface-definitions/dhcp-relay.xml.in4
-rw-r--r--interface-definitions/dhcpv6-relay.xml.in4
-rw-r--r--interface-definitions/dns-domain-name.xml.in2
-rw-r--r--interface-definitions/dns-dynamic.xml.in2
-rw-r--r--interface-definitions/dns-forwarding.xml.in2
-rw-r--r--interface-definitions/firewall.xml.in6
-rw-r--r--interface-definitions/high-availability.xml.in2
-rw-r--r--interface-definitions/igmp-proxy.xml.in2
-rw-r--r--interface-definitions/include/babel/interface.xml.i2
-rw-r--r--interface-definitions/include/bgp/neighbor-update-source.xml.i2
-rw-r--r--interface-definitions/include/bgp/protocol-common-config.xml.i4
-rw-r--r--interface-definitions/include/dhcp-interface-multi.xml.i2
-rw-r--r--interface-definitions/include/dhcp-interface.xml.i2
-rw-r--r--interface-definitions/include/eigrp/protocol-common-config.xml.i2
-rw-r--r--interface-definitions/include/firewall/match-interface.xml.i2
-rw-r--r--interface-definitions/include/generic-interface-broadcast.xml.i2
-rw-r--r--interface-definitions/include/generic-interface-multi-broadcast.xml.i2
-rw-r--r--interface-definitions/include/generic-interface-multi.xml.i2
-rw-r--r--interface-definitions/include/generic-interface.xml.i2
-rw-r--r--interface-definitions/include/generic-password.xml.i15
-rw-r--r--interface-definitions/include/generic-username.xml.i15
-rw-r--r--interface-definitions/include/inbound-interface.xml.i2
-rw-r--r--interface-definitions/include/interface/authentication.xml.i28
-rw-r--r--interface-definitions/include/interface/dhcpv6-options.xml.i2
-rw-r--r--interface-definitions/include/interface/inbound-interface.xml.i2
-rw-r--r--interface-definitions/include/interface/mirror.xml.i4
-rw-r--r--interface-definitions/include/interface/redirect.xml.i2
-rw-r--r--interface-definitions/include/isis/protocol-common-config.xml.i2
-rw-r--r--interface-definitions/include/nat-interface.xml.i2
-rw-r--r--interface-definitions/include/ospf/protocol-common-config.xml.i2
-rw-r--r--interface-definitions/include/ospfv3/protocol-common-config.xml.i2
-rw-r--r--interface-definitions/include/rip/interface.xml.i2
-rw-r--r--interface-definitions/include/routing-passive-interface.xml.i2
-rw-r--r--interface-definitions/include/source-interface-ethernet.xml.i2
-rw-r--r--interface-definitions/include/source-interface.xml.i2
-rw-r--r--interface-definitions/include/static/static-route-interface.xml.i2
-rw-r--r--interface-definitions/include/static/static-route.xml.i2
-rw-r--r--interface-definitions/include/static/static-route6.xml.i2
-rw-r--r--interface-definitions/interfaces-bonding.xml.in4
-rw-r--r--interface-definitions/interfaces-bridge.xml.in2
-rw-r--r--interface-definitions/load-balancing-wan.xml.in6
-rw-r--r--interface-definitions/nat66.xml.in4
-rw-r--r--interface-definitions/protocols-babel.xml.in4
-rw-r--r--interface-definitions/protocols-igmp.xml.in2
-rw-r--r--interface-definitions/protocols-multicast.xml.in2
-rw-r--r--interface-definitions/protocols-pim.xml.in2
-rw-r--r--interface-definitions/protocols-rip.xml.in2
-rw-r--r--interface-definitions/protocols-ripng.xml.in4
-rw-r--r--interface-definitions/protocols-static-arp.xml.in2
-rw-r--r--interface-definitions/qos.xml.in2
-rw-r--r--interface-definitions/service-conntrack-sync.xml.in2
-rw-r--r--interface-definitions/service-ids-ddos-protection.xml.in2
-rw-r--r--interface-definitions/service-ipoe-server.xml.in4
-rw-r--r--interface-definitions/service-pppoe-server.xml.in2
-rw-r--r--interface-definitions/service-router-advert.xml.in2
-rw-r--r--interface-definitions/service-upnp.xml.in4
-rw-r--r--interface-definitions/system-conntrack.xml.in4
-rw-r--r--op-mode-definitions/include/bgp/martian-next-hop.xml.i15
-rw-r--r--op-mode-definitions/include/bgp/next-hop.xml.i23
-rw-r--r--op-mode-definitions/openvpn.xml.in2
-rw-r--r--op-mode-definitions/show-babel.xml.in2
-rw-r--r--op-mode-definitions/show-bgp.xml.in38
-rw-r--r--python/vyos/configdict.py13
-rw-r--r--python/vyos/defaults.py1
-rw-r--r--python/vyos/opmode.py35
-rw-r--r--python/vyos/template.py17
-rw-r--r--python/vyos/util.py6
-rw-r--r--schema/interface_definition.rnc2
-rw-r--r--schema/interface_definition.rng15
-rwxr-xr-xsrc/completion/list_interfaces.py54
-rwxr-xr-xsrc/conf_mode/container.py37
-rwxr-xr-xsrc/conf_mode/interfaces-tunnel.py2
-rwxr-xr-xsrc/conf_mode/qos.py16
-rwxr-xr-xsrc/conf_mode/service_ipoe-server.py7
-rw-r--r--src/etc/systemd/system/frr.service.d/override.conf2
-rwxr-xr-xsrc/op_mode/bgp.py3
-rwxr-xr-xsrc/op_mode/conntrack.py4
-rwxr-xr-xsrc/op_mode/dhcp.py32
-rwxr-xr-xsrc/op_mode/nat.py10
-rwxr-xr-xsrc/op_mode/neighbor.py8
-rwxr-xr-xsrc/op_mode/openvpn.py7
-rwxr-xr-xsrc/op_mode/route.py6
-rwxr-xr-xsrc/services/api/graphql/generate/schema_from_op_mode.py126
-rw-r--r--src/services/api/graphql/libs/op_mode.py17
88 files changed, 496 insertions, 249 deletions
diff --git a/data/config-mode-dependencies.json b/data/config-mode-dependencies.json
index 9e943ba2c..ccee359d1 100644
--- a/data/config-mode-dependencies.json
+++ b/data/config-mode-dependencies.json
@@ -8,5 +8,25 @@
"ipsec": ["vpn_ipsec"],
"openconnect": ["vpn_openconnect"],
"sstp": ["vpn_sstp"]
+ },
+ "qos": {
+ "bonding": ["interfaces-bonding"],
+ "bridge": ["interfaces-bridge"],
+ "dummy": ["interfaces-dummy"],
+ "ethernet": ["interfaces-ethernet"],
+ "geneve": ["interfaces-geneve"],
+ "input": ["interfaces-input"],
+ "l2tpv3": ["interfaces-l2tpv3"],
+ "loopback": ["interfaces-loopback"],
+ "macsec": ["interfaces-macsec"],
+ "openvpn": ["interfaces-openvpn"],
+ "pppoe": ["interfaces-pppoe"],
+ "pseudo-ethernet": ["interfaces-pseudo-ethernet"],
+ "tunnel": ["interfaces-tunnel"],
+ "vti": ["interfaces-vti"],
+ "vxlan": ["interfaces-vxlan"],
+ "wireguard": ["interfaces-wireguard"],
+ "wireless": ["interfaces-wireless"],
+ "wwan": ["interfaces-wwan"]
}
}
diff --git a/data/templates/accel-ppp/ipoe.config.j2 b/data/templates/accel-ppp/ipoe.config.j2
index 179f2277b..27621bc9b 100644
--- a/data/templates/accel-ppp/ipoe.config.j2
+++ b/data/templates/accel-ppp/ipoe.config.j2
@@ -25,7 +25,7 @@ verbose=1
{% for iface, iface_config in interface.items() %}
{% set tmp = 'interface=' %}
{% if iface_config.vlan is vyos_defined %}
-{% set tmp = tmp ~ 're:' ~ iface ~ '\.\d+' %}
+{% set tmp = tmp ~ 're:^' ~ iface ~ '\.' ~ iface_config.vlan | range_to_regex ~ '$' %}
{% else %}
{% set tmp = tmp ~ iface %}
{% endif %}
@@ -35,7 +35,11 @@ verbose=1
{% elif iface_config.network is vyos_defined('vlan') %}
{% set shared = 'shared=0,' %}
{% endif %}
-{{ tmp }},{{ shared }}mode={{ iface_config.mode | upper }},ifcfg=1,range={{ iface_config.client_subnet }},start=dhcpv4,ipv6=1
+{% set range = 'range=' ~ iface_config.client_subnet ~ ',' if iface_config.client_subnet is vyos_defined else '' %}
+{{ tmp }},{{ shared }}mode={{ iface_config.mode | upper }},ifcfg=1,{{ range }}start=dhcpv4,ipv6=1
+{% if iface_config.vlan is vyos_defined %}
+vlan-mon={{ iface }},{{ iface_config.vlan | join(',') }}
+{% endif %}
{% endfor %}
{% endif %}
{% if authentication.mode is vyos_defined('noauth') %}
@@ -54,12 +58,6 @@ password=csid
{% endif %}
proxy-arp=1
-{% for iface, iface_options in interface.items() %}
-{% if iface_options.network is vyos_defined('vlan') %}
-vlan-mon={{ iface }},{{ iface_options.vlan | join(',') }}
-{% endif %}
-{% endfor %}
-
{% if client_ip_pool.name is vyos_defined %}
[ip-pool]
{% for pool, pool_options in client_ip_pool.name.items() %}
diff --git a/data/templates/container/registries.conf.j2 b/data/templates/container/registries.conf.j2
index 2e86466a1..eb7ff8775 100644
--- a/data/templates/container/registries.conf.j2
+++ b/data/templates/container/registries.conf.j2
@@ -23,5 +23,9 @@
# unqualified-search-registries = ["example.com"]
{% if registry is vyos_defined %}
-unqualified-search-registries = {{ registry }}
+{% set registry_list = [] %}
+{% for r, r_options in registry.items() if r_options.disable is not vyos_defined %}
+{% set _ = registry_list.append(r) %}
+{% endfor %}
+unqualified-search-registries = {{ registry_list }}
{% endif %}
diff --git a/interface-definitions/container.xml.in b/interface-definitions/container.xml.in
index a5940ae17..6947ed500 100644
--- a/interface-definitions/container.xml.in
+++ b/interface-definitions/container.xml.in
@@ -368,13 +368,16 @@
</leafNode>
</children>
</tagNode>
- <leafNode name="registry">
+ <tagNode name="registry">
<properties>
<help>Registry Name</help>
- <multi/>
</properties>
<defaultValue>docker.io quay.io</defaultValue>
- </leafNode>
+ <children>
+ #include <include/interface/authentication.xml.i>
+ #include <include/generic-disable-node.xml.i>
+ </children>
+ </tagNode>
</children>
</node>
</interfaceDefinition>
diff --git a/interface-definitions/dhcp-relay.xml.in b/interface-definitions/dhcp-relay.xml.in
index df2821881..79ad2c01c 100644
--- a/interface-definitions/dhcp-relay.xml.in
+++ b/interface-definitions/dhcp-relay.xml.in
@@ -14,7 +14,7 @@
<properties>
<help>Interface for DHCP Relay Agent to listen for requests</help>
<completionHelp>
- <script>${vyos_completion_dir}/list_interfaces.py</script>
+ <script>${vyos_completion_dir}/list_interfaces</script>
</completionHelp>
<valueHelp>
<format>txt</format>
@@ -30,7 +30,7 @@
<properties>
<help>Interface for DHCP Relay Agent forward requests out</help>
<completionHelp>
- <script>${vyos_completion_dir}/list_interfaces.py</script>
+ <script>${vyos_completion_dir}/list_interfaces</script>
</completionHelp>
<valueHelp>
<format>txt</format>
diff --git a/interface-definitions/dhcpv6-relay.xml.in b/interface-definitions/dhcpv6-relay.xml.in
index 5abcbe804..947adef75 100644
--- a/interface-definitions/dhcpv6-relay.xml.in
+++ b/interface-definitions/dhcpv6-relay.xml.in
@@ -13,7 +13,7 @@
<properties>
<help>Interface for DHCPv6 Relay Agent to listen for requests</help>
<completionHelp>
- <script>${vyos_completion_dir}/list_interfaces.py</script>
+ <script>${vyos_completion_dir}/list_interfaces</script>
</completionHelp>
</properties>
<children>
@@ -49,7 +49,7 @@
<properties>
<help>Interface for DHCPv6 Relay Agent forward requests out</help>
<completionHelp>
- <script>${vyos_completion_dir}/list_interfaces.py</script>
+ <script>${vyos_completion_dir}/list_interfaces</script>
</completionHelp>
</properties>
<children>
diff --git a/interface-definitions/dns-domain-name.xml.in b/interface-definitions/dns-domain-name.xml.in
index e4d8c0f90..c0ac16a80 100644
--- a/interface-definitions/dns-domain-name.xml.in
+++ b/interface-definitions/dns-domain-name.xml.in
@@ -7,7 +7,7 @@
<help>System Domain Name Servers (DNS)</help>
<priority>400</priority>
<completionHelp>
- <script>${vyos_completion_dir}/list_interfaces.py</script>
+ <script>${vyos_completion_dir}/list_interfaces</script>
</completionHelp>
<valueHelp>
<format>ipv4</format>
diff --git a/interface-definitions/dns-dynamic.xml.in b/interface-definitions/dns-dynamic.xml.in
index a39e412b2..58dd48f9d 100644
--- a/interface-definitions/dns-dynamic.xml.in
+++ b/interface-definitions/dns-dynamic.xml.in
@@ -16,7 +16,7 @@
<properties>
<help>Interface to send DDNS updates for</help>
<completionHelp>
- <script>${vyos_completion_dir}/list_interfaces.py</script>
+ <script>${vyos_completion_dir}/list_interfaces</script>
</completionHelp>
</properties>
<children>
diff --git a/interface-definitions/dns-forwarding.xml.in b/interface-definitions/dns-forwarding.xml.in
index 409028572..371f198c6 100644
--- a/interface-definitions/dns-forwarding.xml.in
+++ b/interface-definitions/dns-forwarding.xml.in
@@ -31,7 +31,7 @@
<properties>
<help>Interfaces whose DHCP client nameservers to forward requests to</help>
<completionHelp>
- <script>${vyos_completion_dir}/list_interfaces.py</script>
+ <script>${vyos_completion_dir}/list_interfaces</script>
</completionHelp>
<multi/>
</properties>
diff --git a/interface-definitions/firewall.xml.in b/interface-definitions/firewall.xml.in
index f8eed2ce0..624d61759 100644
--- a/interface-definitions/firewall.xml.in
+++ b/interface-definitions/firewall.xml.in
@@ -146,7 +146,7 @@
<properties>
<help>Interface-group member</help>
<completionHelp>
- <script>${vyos_completion_dir}/list_interfaces.py</script>
+ <script>${vyos_completion_dir}/list_interfaces</script>
</completionHelp>
<multi/>
</properties>
@@ -347,7 +347,7 @@
<properties>
<help>Interface name to apply firewall configuration</help>
<completionHelp>
- <script>${vyos_completion_dir}/list_interfaces.py</script>
+ <script>${vyos_completion_dir}/list_interfaces</script>
</completionHelp>
</properties>
<children>
@@ -912,7 +912,7 @@
<description>Interface associated with zone</description>
</valueHelp>
<completionHelp>
- <script>${vyos_completion_dir}/list_interfaces.py</script>
+ <script>${vyos_completion_dir}/list_interfaces</script>
</completionHelp>
<multi/>
</properties>
diff --git a/interface-definitions/high-availability.xml.in b/interface-definitions/high-availability.xml.in
index 6cb40247a..1fa051df9 100644
--- a/interface-definitions/high-availability.xml.in
+++ b/interface-definitions/high-availability.xml.in
@@ -213,7 +213,7 @@
<properties>
<help>Interface name state check</help>
<completionHelp>
- <script>${vyos_completion_dir}/list_interfaces.py --broadcast</script>
+ <script>${vyos_completion_dir}/list_interfaces --broadcast</script>
</completionHelp>
<valueHelp>
<format>txt</format>
diff --git a/interface-definitions/igmp-proxy.xml.in b/interface-definitions/igmp-proxy.xml.in
index 50cb33a93..0eea85060 100644
--- a/interface-definitions/igmp-proxy.xml.in
+++ b/interface-definitions/igmp-proxy.xml.in
@@ -20,7 +20,7 @@
<properties>
<help>Interface for IGMP proxy</help>
<completionHelp>
- <script>${vyos_completion_dir}/list_interfaces.py</script>
+ <script>${vyos_completion_dir}/list_interfaces</script>
</completionHelp>
</properties>
<children>
diff --git a/interface-definitions/include/babel/interface.xml.i b/interface-definitions/include/babel/interface.xml.i
index 549e4909d..586eca7a5 100644
--- a/interface-definitions/include/babel/interface.xml.i
+++ b/interface-definitions/include/babel/interface.xml.i
@@ -3,7 +3,7 @@
<properties>
<help>Interface name</help>
<completionHelp>
- <script>${vyos_completion_dir}/list_interfaces.py</script>
+ <script>${vyos_completion_dir}/list_interfaces</script>
</completionHelp>
<valueHelp>
<format>txt</format>
diff --git a/interface-definitions/include/bgp/neighbor-update-source.xml.i b/interface-definitions/include/bgp/neighbor-update-source.xml.i
index 60c127e8f..0acec4126 100644
--- a/interface-definitions/include/bgp/neighbor-update-source.xml.i
+++ b/interface-definitions/include/bgp/neighbor-update-source.xml.i
@@ -5,7 +5,7 @@
<help>Source IP of routing updates</help>
<completionHelp>
<script>${vyos_completion_dir}/list_local_ips.sh --both</script>
- <script>${vyos_completion_dir}/list_interfaces.py</script>
+ <script>${vyos_completion_dir}/list_interfaces</script>
</completionHelp>
<valueHelp>
<format>ipv4</format>
diff --git a/interface-definitions/include/bgp/protocol-common-config.xml.i b/interface-definitions/include/bgp/protocol-common-config.xml.i
index 0d88c7b25..7a3617044 100644
--- a/interface-definitions/include/bgp/protocol-common-config.xml.i
+++ b/interface-definitions/include/bgp/protocol-common-config.xml.i
@@ -359,7 +359,7 @@
<properties>
<help>Interface</help>
<completionHelp>
- <script>${vyos_completion_dir}/list_interfaces.py</script>
+ <script>${vyos_completion_dir}/list_interfaces</script>
</completionHelp>
<multi/>
</properties>
@@ -739,7 +739,7 @@
<properties>
<help>Interface</help>
<completionHelp>
- <script>${vyos_completion_dir}/list_interfaces.py</script>
+ <script>${vyos_completion_dir}/list_interfaces</script>
</completionHelp>
<multi/>
</properties>
diff --git a/interface-definitions/include/dhcp-interface-multi.xml.i b/interface-definitions/include/dhcp-interface-multi.xml.i
index c74751a19..e10341037 100644
--- a/interface-definitions/include/dhcp-interface-multi.xml.i
+++ b/interface-definitions/include/dhcp-interface-multi.xml.i
@@ -3,7 +3,7 @@
<properties>
<help>DHCP interface supplying next-hop IP address</help>
<completionHelp>
- <script>${vyos_completion_dir}/list_interfaces.py</script>
+ <script>${vyos_completion_dir}/list_interfaces</script>
</completionHelp>
<valueHelp>
<format>txt</format>
diff --git a/interface-definitions/include/dhcp-interface.xml.i b/interface-definitions/include/dhcp-interface.xml.i
index f5107ba2b..24edbbd15 100644
--- a/interface-definitions/include/dhcp-interface.xml.i
+++ b/interface-definitions/include/dhcp-interface.xml.i
@@ -2,7 +2,7 @@
<properties>
<help>DHCP interface supplying next-hop IP address</help>
<completionHelp>
- <script>${vyos_completion_dir}/list_interfaces.py</script>
+ <script>${vyos_completion_dir}/list_interfaces</script>
</completionHelp>
<valueHelp>
<format>txt</format>
diff --git a/interface-definitions/include/eigrp/protocol-common-config.xml.i b/interface-definitions/include/eigrp/protocol-common-config.xml.i
index 30ddc5d11..88365187a 100644
--- a/interface-definitions/include/eigrp/protocol-common-config.xml.i
+++ b/interface-definitions/include/eigrp/protocol-common-config.xml.i
@@ -59,7 +59,7 @@
<properties>
<help>Suppress routing updates on an interface</help>
<completionHelp>
- <script>${vyos_completion_dir}/list_interfaces.py</script>
+ <script>${vyos_completion_dir}/list_interfaces</script>
</completionHelp>
</properties>
</leafNode>
diff --git a/interface-definitions/include/firewall/match-interface.xml.i b/interface-definitions/include/firewall/match-interface.xml.i
index 675a87574..3e52422cf 100644
--- a/interface-definitions/include/firewall/match-interface.xml.i
+++ b/interface-definitions/include/firewall/match-interface.xml.i
@@ -3,7 +3,7 @@
<properties>
<help>Match interface</help>
<completionHelp>
- <script>${vyos_completion_dir}/list_interfaces.py</script>
+ <script>${vyos_completion_dir}/list_interfaces</script>
</completionHelp>
</properties>
</leafNode>
diff --git a/interface-definitions/include/generic-interface-broadcast.xml.i b/interface-definitions/include/generic-interface-broadcast.xml.i
index af35a888b..82bfc139b 100644
--- a/interface-definitions/include/generic-interface-broadcast.xml.i
+++ b/interface-definitions/include/generic-interface-broadcast.xml.i
@@ -3,7 +3,7 @@
<properties>
<help>Interface Name to use</help>
<completionHelp>
- <script>${vyos_completion_dir}/list_interfaces.py --broadcast</script>
+ <script>${vyos_completion_dir}/list_interfaces --broadcast</script>
</completionHelp>
<valueHelp>
<format>txt</format>
diff --git a/interface-definitions/include/generic-interface-multi-broadcast.xml.i b/interface-definitions/include/generic-interface-multi-broadcast.xml.i
index 1ae38fb43..8160f816d 100644
--- a/interface-definitions/include/generic-interface-multi-broadcast.xml.i
+++ b/interface-definitions/include/generic-interface-multi-broadcast.xml.i
@@ -3,7 +3,7 @@
<properties>
<help>Interface Name to use</help>
<completionHelp>
- <script>${vyos_completion_dir}/list_interfaces.py --broadcast</script>
+ <script>${vyos_completion_dir}/list_interfaces --broadcast</script>
</completionHelp>
<valueHelp>
<format>txt</format>
diff --git a/interface-definitions/include/generic-interface-multi.xml.i b/interface-definitions/include/generic-interface-multi.xml.i
index 16916ff54..1b8dc102b 100644
--- a/interface-definitions/include/generic-interface-multi.xml.i
+++ b/interface-definitions/include/generic-interface-multi.xml.i
@@ -3,7 +3,7 @@
<properties>
<help>Interface to use</help>
<completionHelp>
- <script>${vyos_completion_dir}/list_interfaces.py</script>
+ <script>${vyos_completion_dir}/list_interfaces</script>
</completionHelp>
<valueHelp>
<format>txt</format>
diff --git a/interface-definitions/include/generic-interface.xml.i b/interface-definitions/include/generic-interface.xml.i
index 36ddee417..9417f9ef0 100644
--- a/interface-definitions/include/generic-interface.xml.i
+++ b/interface-definitions/include/generic-interface.xml.i
@@ -3,7 +3,7 @@
<properties>
<help>Interface to use</help>
<completionHelp>
- <script>${vyos_completion_dir}/list_interfaces.py</script>
+ <script>${vyos_completion_dir}/list_interfaces</script>
</completionHelp>
<valueHelp>
<format>txt</format>
diff --git a/interface-definitions/include/generic-password.xml.i b/interface-definitions/include/generic-password.xml.i
new file mode 100644
index 000000000..76d5f12d8
--- /dev/null
+++ b/interface-definitions/include/generic-password.xml.i
@@ -0,0 +1,15 @@
+<!-- include start from generic-password.xml.i -->
+<leafNode name="password">
+ <properties>
+ <help>Password used for authentication</help>
+ <valueHelp>
+ <format>txt</format>
+ <description>Password</description>
+ </valueHelp>
+ <constraint>
+ <regex>[[:ascii:]]{1,128}</regex>
+ </constraint>
+ <constraintErrorMessage>Password is limited to ASCII characters only, with a total length of 128</constraintErrorMessage>
+ </properties>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/include/generic-username.xml.i b/interface-definitions/include/generic-username.xml.i
new file mode 100644
index 000000000..678f30ddf
--- /dev/null
+++ b/interface-definitions/include/generic-username.xml.i
@@ -0,0 +1,15 @@
+<!-- include start from generic-username.xml.i -->
+<leafNode name="username">
+ <properties>
+ <help>Username used for authentication</help>
+ <valueHelp>
+ <format>txt</format>
+ <description>Username</description>
+ </valueHelp>
+ <constraint>
+ <regex>[[:ascii:]]{1,128}</regex>
+ </constraint>
+ <constraintErrorMessage>Username is limited to ASCII characters only, with a total length of 128</constraintErrorMessage>
+ </properties>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/include/inbound-interface.xml.i b/interface-definitions/include/inbound-interface.xml.i
index 3289bbf8f..422f9de75 100644
--- a/interface-definitions/include/inbound-interface.xml.i
+++ b/interface-definitions/include/inbound-interface.xml.i
@@ -4,7 +4,7 @@
<help>Inbound interface of NAT traffic</help>
<completionHelp>
<list>any</list>
- <script>${vyos_completion_dir}/list_interfaces.py</script>
+ <script>${vyos_completion_dir}/list_interfaces</script>
</completionHelp>
</properties>
</leafNode>
diff --git a/interface-definitions/include/interface/authentication.xml.i b/interface-definitions/include/interface/authentication.xml.i
index ac06faef5..0bd792209 100644
--- a/interface-definitions/include/interface/authentication.xml.i
+++ b/interface-definitions/include/interface/authentication.xml.i
@@ -4,32 +4,8 @@
<help>Authentication settings</help>
</properties>
<children>
- <leafNode name="username">
- <properties>
- <help>Username used for authentication</help>
- <valueHelp>
- <format>txt</format>
- <description>Username</description>
- </valueHelp>
- <constraint>
- <regex>[[:ascii:]]{1,128}</regex>
- </constraint>
- <constraintErrorMessage>Username is limited to ASCII characters only, with a total length of 128</constraintErrorMessage>
- </properties>
- </leafNode>
- <leafNode name="password">
- <properties>
- <help>Password used for authentication</help>
- <valueHelp>
- <format>txt</format>
- <description>Password</description>
- </valueHelp>
- <constraint>
- <regex>[[:ascii:]]{1,128}</regex>
- </constraint>
- <constraintErrorMessage>Password is limited to ASCII characters only, with a total length of 128</constraintErrorMessage>
- </properties>
- </leafNode>
+ #include <include/generic-username.xml.i>
+ #include <include/generic-password.xml.i>
</children>
</node>
<!-- include end -->
diff --git a/interface-definitions/include/interface/dhcpv6-options.xml.i b/interface-definitions/include/interface/dhcpv6-options.xml.i
index c705af7c2..609af1a2b 100644
--- a/interface-definitions/include/interface/dhcpv6-options.xml.i
+++ b/interface-definitions/include/interface/dhcpv6-options.xml.i
@@ -51,7 +51,7 @@
<properties>
<help>Delegate IPv6 prefix from provider to this interface</help>
<completionHelp>
- <script>${vyos_completion_dir}/list_interfaces.py --broadcast</script>
+ <script>${vyos_completion_dir}/list_interfaces --broadcast</script>
</completionHelp>
</properties>
<children>
diff --git a/interface-definitions/include/interface/inbound-interface.xml.i b/interface-definitions/include/interface/inbound-interface.xml.i
index 5a8d47280..96ade331d 100644
--- a/interface-definitions/include/interface/inbound-interface.xml.i
+++ b/interface-definitions/include/interface/inbound-interface.xml.i
@@ -3,7 +3,7 @@
<properties>
<help>Inbound Interface</help>
<completionHelp>
- <script>${vyos_completion_dir}/list_interfaces.py</script>
+ <script>${vyos_completion_dir}/list_interfaces</script>
</completionHelp>
</properties>
</leafNode>
diff --git a/interface-definitions/include/interface/mirror.xml.i b/interface-definitions/include/interface/mirror.xml.i
index 74a172b50..903c62777 100644
--- a/interface-definitions/include/interface/mirror.xml.i
+++ b/interface-definitions/include/interface/mirror.xml.i
@@ -8,7 +8,7 @@
<properties>
<help>Mirror ingress traffic to destination interface</help>
<completionHelp>
- <script>${vyos_completion_dir}/list_interfaces.py</script>
+ <script>${vyos_completion_dir}/list_interfaces</script>
</completionHelp>
<valueHelp>
<format>txt</format>
@@ -20,7 +20,7 @@
<properties>
<help>Mirror egress traffic to destination interface</help>
<completionHelp>
- <script>${vyos_completion_dir}/list_interfaces.py</script>
+ <script>${vyos_completion_dir}/list_interfaces</script>
</completionHelp>
<valueHelp>
<format>txt</format>
diff --git a/interface-definitions/include/interface/redirect.xml.i b/interface-definitions/include/interface/redirect.xml.i
index b01e486ce..0421f4074 100644
--- a/interface-definitions/include/interface/redirect.xml.i
+++ b/interface-definitions/include/interface/redirect.xml.i
@@ -3,7 +3,7 @@
<properties>
<help>Redirect incoming packet to destination</help>
<completionHelp>
- <script>${vyos_completion_dir}/list_interfaces.py</script>
+ <script>${vyos_completion_dir}/list_interfaces</script>
</completionHelp>
<valueHelp>
<format>txt</format>
diff --git a/interface-definitions/include/isis/protocol-common-config.xml.i b/interface-definitions/include/isis/protocol-common-config.xml.i
index c44939528..0e6f19480 100644
--- a/interface-definitions/include/isis/protocol-common-config.xml.i
+++ b/interface-definitions/include/isis/protocol-common-config.xml.i
@@ -572,7 +572,7 @@
<properties>
<help>Interface params</help>
<completionHelp>
- <script>${vyos_completion_dir}/list_interfaces.py</script>
+ <script>${vyos_completion_dir}/list_interfaces</script>
</completionHelp>
</properties>
<children>
diff --git a/interface-definitions/include/nat-interface.xml.i b/interface-definitions/include/nat-interface.xml.i
index 68969472f..ef1ffc1ba 100644
--- a/interface-definitions/include/nat-interface.xml.i
+++ b/interface-definitions/include/nat-interface.xml.i
@@ -4,7 +4,7 @@
<help>Outbound interface of NAT traffic</help>
<completionHelp>
<list>any</list>
- <script>${vyos_completion_dir}/list_interfaces.py</script>
+ <script>${vyos_completion_dir}/list_interfaces</script>
</completionHelp>
</properties>
</leafNode>
diff --git a/interface-definitions/include/ospf/protocol-common-config.xml.i b/interface-definitions/include/ospf/protocol-common-config.xml.i
index 16b346131..25b54b181 100644
--- a/interface-definitions/include/ospf/protocol-common-config.xml.i
+++ b/interface-definitions/include/ospf/protocol-common-config.xml.i
@@ -351,7 +351,7 @@
<properties>
<help>Interface configuration</help>
<completionHelp>
- <script>${vyos_completion_dir}/list_interfaces.py</script>
+ <script>${vyos_completion_dir}/list_interfaces</script>
</completionHelp>
<valueHelp>
<format>txt</format>
diff --git a/interface-definitions/include/ospfv3/protocol-common-config.xml.i b/interface-definitions/include/ospfv3/protocol-common-config.xml.i
index fd00af95e..014bf9e49 100644
--- a/interface-definitions/include/ospfv3/protocol-common-config.xml.i
+++ b/interface-definitions/include/ospfv3/protocol-common-config.xml.i
@@ -111,7 +111,7 @@
<properties>
<help>Enable routing on an IPv6 interface</help>
<completionHelp>
- <script>${vyos_completion_dir}/list_interfaces.py</script>
+ <script>${vyos_completion_dir}/list_interfaces</script>
</completionHelp>
<valueHelp>
<format>txt</format>
diff --git a/interface-definitions/include/rip/interface.xml.i b/interface-definitions/include/rip/interface.xml.i
index e0792cdc1..0a89f4d92 100644
--- a/interface-definitions/include/rip/interface.xml.i
+++ b/interface-definitions/include/rip/interface.xml.i
@@ -3,7 +3,7 @@
<properties>
<help>Interface name</help>
<completionHelp>
- <script>${vyos_completion_dir}/list_interfaces.py</script>
+ <script>${vyos_completion_dir}/list_interfaces</script>
</completionHelp>
<valueHelp>
<format>txt</format>
diff --git a/interface-definitions/include/routing-passive-interface.xml.i b/interface-definitions/include/routing-passive-interface.xml.i
index fe229aebe..715468e59 100644
--- a/interface-definitions/include/routing-passive-interface.xml.i
+++ b/interface-definitions/include/routing-passive-interface.xml.i
@@ -4,7 +4,7 @@
<help>Suppress routing updates on an interface</help>
<completionHelp>
<list>default</list>
- <script>${vyos_completion_dir}/list_interfaces.py</script>
+ <script>${vyos_completion_dir}/list_interfaces</script>
</completionHelp>
<valueHelp>
<format>txt</format>
diff --git a/interface-definitions/include/source-interface-ethernet.xml.i b/interface-definitions/include/source-interface-ethernet.xml.i
index ee04f2cd5..e06e47d6c 100644
--- a/interface-definitions/include/source-interface-ethernet.xml.i
+++ b/interface-definitions/include/source-interface-ethernet.xml.i
@@ -7,7 +7,7 @@
<description>Physical interface used for traffic forwarding</description>
</valueHelp>
<completionHelp>
- <script>${vyos_completion_dir}/list_interfaces.py -t ethernet</script>
+ <script>${vyos_completion_dir}/list_interfaces --type ethernet</script>
</completionHelp>
</properties>
</leafNode>
diff --git a/interface-definitions/include/source-interface.xml.i b/interface-definitions/include/source-interface.xml.i
index 4c1fddb57..c25a6a6d0 100644
--- a/interface-definitions/include/source-interface.xml.i
+++ b/interface-definitions/include/source-interface.xml.i
@@ -7,7 +7,7 @@
<description>Interface name</description>
</valueHelp>
<completionHelp>
- <script>${vyos_completion_dir}/list_interfaces.py</script>
+ <script>${vyos_completion_dir}/list_interfaces</script>
</completionHelp>
<constraint>
#include <include/constraint/interface-name.xml.in>
diff --git a/interface-definitions/include/static/static-route-interface.xml.i b/interface-definitions/include/static/static-route-interface.xml.i
index cc7a92612..db2f0baa6 100644
--- a/interface-definitions/include/static/static-route-interface.xml.i
+++ b/interface-definitions/include/static/static-route-interface.xml.i
@@ -3,7 +3,7 @@
<properties>
<help>Gateway interface name</help>
<completionHelp>
- <script>${vyos_completion_dir}/list_interfaces.py</script>
+ <script>${vyos_completion_dir}/list_interfaces</script>
</completionHelp>
<valueHelp>
<format>txt</format>
diff --git a/interface-definitions/include/static/static-route.xml.i b/interface-definitions/include/static/static-route.xml.i
index aeb2044c9..34e36f5a7 100644
--- a/interface-definitions/include/static/static-route.xml.i
+++ b/interface-definitions/include/static/static-route.xml.i
@@ -19,7 +19,7 @@
<properties>
<help>Next-hop IPv4 router interface</help>
<completionHelp>
- <script>${vyos_completion_dir}/list_interfaces.py</script>
+ <script>${vyos_completion_dir}/list_interfaces</script>
</completionHelp>
<valueHelp>
<format>txt</format>
diff --git a/interface-definitions/include/static/static-route6.xml.i b/interface-definitions/include/static/static-route6.xml.i
index d5e7a25bc..aac02062f 100644
--- a/interface-definitions/include/static/static-route6.xml.i
+++ b/interface-definitions/include/static/static-route6.xml.i
@@ -18,7 +18,7 @@
<properties>
<help>IPv6 gateway interface name</help>
<completionHelp>
- <script>${vyos_completion_dir}/list_interfaces.py</script>
+ <script>${vyos_completion_dir}/list_interfaces</script>
</completionHelp>
<valueHelp>
<format>txt</format>
diff --git a/interface-definitions/interfaces-bonding.xml.in b/interface-definitions/interfaces-bonding.xml.in
index 6e8c5283a..f5f1eb1b6 100644
--- a/interface-definitions/interfaces-bonding.xml.in
+++ b/interface-definitions/interfaces-bonding.xml.in
@@ -192,7 +192,7 @@
<properties>
<help>Member interface name</help>
<completionHelp>
- <script>${vyos_completion_dir}/list_interfaces.py --bondable</script>
+ <script>${vyos_completion_dir}/list_interfaces --bondable</script>
</completionHelp>
<valueHelp>
<format>txt</format>
@@ -211,7 +211,7 @@
<properties>
<help>Primary device interface</help>
<completionHelp>
- <script>${vyos_completion_dir}/list_interfaces.py --bondable</script>
+ <script>${vyos_completion_dir}/list_interfaces --bondable</script>
</completionHelp>
<valueHelp>
<format>txt</format>
diff --git a/interface-definitions/interfaces-bridge.xml.in b/interface-definitions/interfaces-bridge.xml.in
index 1636411ec..fcfb8686c 100644
--- a/interface-definitions/interfaces-bridge.xml.in
+++ b/interface-definitions/interfaces-bridge.xml.in
@@ -121,7 +121,7 @@
<properties>
<help>Member interface name</help>
<completionHelp>
- <script>${vyos_completion_dir}/list_interfaces.py --bridgeable</script>
+ <script>${vyos_completion_dir}/list_interfaces --bridgeable</script>
</completionHelp>
</properties>
<children>
diff --git a/interface-definitions/load-balancing-wan.xml.in b/interface-definitions/load-balancing-wan.xml.in
index c2b6316ae..2b812eb4d 100644
--- a/interface-definitions/load-balancing-wan.xml.in
+++ b/interface-definitions/load-balancing-wan.xml.in
@@ -44,7 +44,7 @@
<properties>
<help>Interface name</help>
<completionHelp>
- <script>${vyos_completion_dir}/list_interfaces.py</script>
+ <script>${vyos_completion_dir}/list_interfaces</script>
</completionHelp>
</properties>
<children>
@@ -226,7 +226,7 @@
<help>Inbound interface name (e.g., "eth0") [REQUIRED]</help>
<completionHelp>
<list>any</list>
- <script>${vyos_completion_dir}/list_interfaces.py</script>
+ <script>${vyos_completion_dir}/list_interfaces</script>
</completionHelp>
</properties>
</leafNode>
@@ -234,7 +234,7 @@
<properties>
<help>Interface name [REQUIRED]</help>
<completionHelp>
- <script>${vyos_completion_dir}/list_interfaces.py</script>
+ <script>${vyos_completion_dir}/list_interfaces</script>
</completionHelp>
</properties>
<children>
diff --git a/interface-definitions/nat66.xml.in b/interface-definitions/nat66.xml.in
index dab4543e0..6ea611789 100644
--- a/interface-definitions/nat66.xml.in
+++ b/interface-definitions/nat66.xml.in
@@ -46,7 +46,7 @@
<properties>
<help>Outbound interface of NAT66 traffic</help>
<completionHelp>
- <script>${vyos_completion_dir}/list_interfaces.py</script>
+ <script>${vyos_completion_dir}/list_interfaces</script>
</completionHelp>
</properties>
</leafNode>
@@ -179,7 +179,7 @@
<help>Inbound interface of NAT66 traffic</help>
<completionHelp>
<list>any</list>
- <script>${vyos_completion_dir}/list_interfaces.py</script>
+ <script>${vyos_completion_dir}/list_interfaces</script>
</completionHelp>
</properties>
</leafNode>
diff --git a/interface-definitions/protocols-babel.xml.in b/interface-definitions/protocols-babel.xml.in
index 0ef833077..b3377aac1 100644
--- a/interface-definitions/protocols-babel.xml.in
+++ b/interface-definitions/protocols-babel.xml.in
@@ -203,7 +203,7 @@
<description>Apply filtering to an interface</description>
</valueHelp>
<completionHelp>
- <script>${vyos_completion_dir}/list_interfaces.py</script>
+ <script>${vyos_completion_dir}/list_interfaces</script>
</completionHelp>
<constraint>
#include <include/constraint/interface-name.xml.in>
@@ -231,7 +231,7 @@
<description>Apply filtering to an interface</description>
</valueHelp>
<completionHelp>
- <script>${vyos_completion_dir}/list_interfaces.py</script>
+ <script>${vyos_completion_dir}/list_interfaces</script>
</completionHelp>
<constraint>
#include <include/constraint/interface-name.xml.in>
diff --git a/interface-definitions/protocols-igmp.xml.in b/interface-definitions/protocols-igmp.xml.in
index e10340512..a055db71e 100644
--- a/interface-definitions/protocols-igmp.xml.in
+++ b/interface-definitions/protocols-igmp.xml.in
@@ -12,7 +12,7 @@
<properties>
<help>IGMP interface</help>
<completionHelp>
- <script>${vyos_completion_dir}/list_interfaces.py</script>
+ <script>${vyos_completion_dir}/list_interfaces</script>
</completionHelp>
</properties>
<children>
diff --git a/interface-definitions/protocols-multicast.xml.in b/interface-definitions/protocols-multicast.xml.in
index b1791c471..c8e28ed35 100644
--- a/interface-definitions/protocols-multicast.xml.in
+++ b/interface-definitions/protocols-multicast.xml.in
@@ -65,7 +65,7 @@
<properties>
<help>Next-hop interface</help>
<completionHelp>
- <script>${vyos_completion_dir}/list_interfaces.py</script>
+ <script>${vyos_completion_dir}/list_interfaces</script>
</completionHelp>
</properties>
<children>
diff --git a/interface-definitions/protocols-pim.xml.in b/interface-definitions/protocols-pim.xml.in
index bb5cc797b..e9475930c 100644
--- a/interface-definitions/protocols-pim.xml.in
+++ b/interface-definitions/protocols-pim.xml.in
@@ -13,7 +13,7 @@
<properties>
<help>PIM interface</help>
<completionHelp>
- <script>${vyos_completion_dir}/list_interfaces.py</script>
+ <script>${vyos_completion_dir}/list_interfaces</script>
</completionHelp>
</properties>
<children>
diff --git a/interface-definitions/protocols-rip.xml.in b/interface-definitions/protocols-rip.xml.in
index b5d48090a..68d2b64ca 100644
--- a/interface-definitions/protocols-rip.xml.in
+++ b/interface-definitions/protocols-rip.xml.in
@@ -36,7 +36,7 @@
<description>Apply filtering to an interface</description>
</valueHelp>
<completionHelp>
- <script>${vyos_completion_dir}/list_interfaces.py</script>
+ <script>${vyos_completion_dir}/list_interfaces</script>
</completionHelp>
<constraint>
#include <include/constraint/interface-name.xml.in>
diff --git a/interface-definitions/protocols-ripng.xml.in b/interface-definitions/protocols-ripng.xml.in
index cf000b824..be643896f 100644
--- a/interface-definitions/protocols-ripng.xml.in
+++ b/interface-definitions/protocols-ripng.xml.in
@@ -37,7 +37,7 @@
<description>Apply filtering to an interface</description>
</valueHelp>
<completionHelp>
- <script>${vyos_completion_dir}/list_interfaces.py</script>
+ <script>${vyos_completion_dir}/list_interfaces</script>
</completionHelp>
<constraint>
#include <include/constraint/interface-name.xml.in>
@@ -73,7 +73,7 @@
<description>Suppress routing updates on interface</description>
</valueHelp>
<completionHelp>
- <script>${vyos_completion_dir}/list_interfaces.py</script>
+ <script>${vyos_completion_dir}/list_interfaces</script>
</completionHelp>
<multi/>
</properties>
diff --git a/interface-definitions/protocols-static-arp.xml.in b/interface-definitions/protocols-static-arp.xml.in
index 52caf435a..63f450bd8 100644
--- a/interface-definitions/protocols-static-arp.xml.in
+++ b/interface-definitions/protocols-static-arp.xml.in
@@ -13,7 +13,7 @@
<properties>
<help>Interface configuration</help>
<completionHelp>
- <script>${vyos_completion_dir}/list_interfaces.py</script>
+ <script>${vyos_completion_dir}/list_interfaces</script>
</completionHelp>
<valueHelp>
<format>txt</format>
diff --git a/interface-definitions/qos.xml.in b/interface-definitions/qos.xml.in
index 757c1f856..9b1430ea0 100644
--- a/interface-definitions/qos.xml.in
+++ b/interface-definitions/qos.xml.in
@@ -10,7 +10,7 @@
<properties>
<help>Interface to apply QoS policy</help>
<completionHelp>
- <script>${vyos_completion_dir}/list_interfaces.py</script>
+ <script>${vyos_completion_dir}/list_interfaces</script>
</completionHelp>
<valueHelp>
<format>txt</format>
diff --git a/interface-definitions/service-conntrack-sync.xml.in b/interface-definitions/service-conntrack-sync.xml.in
index 6fa6fc5f9..50a4bf62f 100644
--- a/interface-definitions/service-conntrack-sync.xml.in
+++ b/interface-definitions/service-conntrack-sync.xml.in
@@ -127,7 +127,7 @@
<properties>
<help>Interface to use for syncing conntrack entries</help>
<completionHelp>
- <script>${vyos_completion_dir}/list_interfaces.py --bridgeable</script>
+ <script>${vyos_completion_dir}/list_interfaces --bridgeable</script>
</completionHelp>
</properties>
<children>
diff --git a/interface-definitions/service-ids-ddos-protection.xml.in b/interface-definitions/service-ids-ddos-protection.xml.in
index a661b845d..bb06189bc 100644
--- a/interface-definitions/service-ids-ddos-protection.xml.in
+++ b/interface-definitions/service-ids-ddos-protection.xml.in
@@ -65,7 +65,7 @@
<properties>
<help>Listen interface for mirroring traffic</help>
<completionHelp>
- <script>${vyos_completion_dir}/list_interfaces.py</script>
+ <script>${vyos_completion_dir}/list_interfaces</script>
</completionHelp>
<multi/>
</properties>
diff --git a/interface-definitions/service-ipoe-server.xml.in b/interface-definitions/service-ipoe-server.xml.in
index ebe99d3aa..b6e6503d3 100644
--- a/interface-definitions/service-ipoe-server.xml.in
+++ b/interface-definitions/service-ipoe-server.xml.in
@@ -12,7 +12,7 @@
<properties>
<help>Interface to listen dhcp or unclassified packets</help>
<completionHelp>
- <script>${vyos_completion_dir}/list_interfaces.py</script>
+ <script>${vyos_completion_dir}/list_interfaces</script>
</completionHelp>
</properties>
<children>
@@ -122,7 +122,7 @@
<properties>
<help>Network interface for client MAC addresses</help>
<completionHelp>
- <script>${vyos_completion_dir}/list_interfaces.py</script>
+ <script>${vyos_completion_dir}/list_interfaces</script>
</completionHelp>
</properties>
<children>
diff --git a/interface-definitions/service-pppoe-server.xml.in b/interface-definitions/service-pppoe-server.xml.in
index 3fde07019..022ac2885 100644
--- a/interface-definitions/service-pppoe-server.xml.in
+++ b/interface-definitions/service-pppoe-server.xml.in
@@ -65,7 +65,7 @@
<properties>
<help>interface(s) to listen on</help>
<completionHelp>
- <script>${vyos_completion_dir}/list_interfaces.py</script>
+ <script>${vyos_completion_dir}/list_interfaces</script>
</completionHelp>
</properties>
<children>
diff --git a/interface-definitions/service-router-advert.xml.in b/interface-definitions/service-router-advert.xml.in
index 8b7364a8c..16c29022d 100644
--- a/interface-definitions/service-router-advert.xml.in
+++ b/interface-definitions/service-router-advert.xml.in
@@ -12,7 +12,7 @@
<properties>
<help>Interface to send RA on</help>
<completionHelp>
- <script>${vyos_completion_dir}/list_interfaces.py</script>
+ <script>${vyos_completion_dir}/list_interfaces</script>
</completionHelp>
</properties>
<children>
diff --git a/interface-definitions/service-upnp.xml.in b/interface-definitions/service-upnp.xml.in
index 79d8ae42e..9e222d29a 100644
--- a/interface-definitions/service-upnp.xml.in
+++ b/interface-definitions/service-upnp.xml.in
@@ -21,7 +21,7 @@
<properties>
<help>WAN network interface</help>
<completionHelp>
- <script>${vyos_completion_dir}/list_interfaces.py</script>
+ <script>${vyos_completion_dir}/list_interfaces</script>
</completionHelp>
<constraint>
#include <include/constraint/interface-name.xml.in>
@@ -95,7 +95,7 @@
<help>Local IP addresses for service to listen on</help>
<completionHelp>
<script>${vyos_completion_dir}/list_local_ips.sh --both</script>
- <script>${vyos_completion_dir}/list_interfaces.py</script>
+ <script>${vyos_completion_dir}/list_interfaces</script>
</completionHelp>
<valueHelp>
<format>&lt;interface&gt;</format>
diff --git a/interface-definitions/system-conntrack.xml.in b/interface-definitions/system-conntrack.xml.in
index 5810a97c6..8dad048b8 100644
--- a/interface-definitions/system-conntrack.xml.in
+++ b/interface-definitions/system-conntrack.xml.in
@@ -68,7 +68,7 @@
<help>Interface to ignore connections tracking on</help>
<completionHelp>
<list>any</list>
- <script>${vyos_completion_dir}/list_interfaces.py</script>
+ <script>${vyos_completion_dir}/list_interfaces</script>
</completionHelp>
</properties>
</leafNode>
@@ -310,7 +310,7 @@
<help>Interface to ignore connections tracking on</help>
<completionHelp>
<list>any</list>
- <script>${vyos_completion_dir}/list_interfaces.py</script>
+ <script>${vyos_completion_dir}/list_interfaces</script>
</completionHelp>
</properties>
</leafNode>
diff --git a/op-mode-definitions/include/bgp/martian-next-hop.xml.i b/op-mode-definitions/include/bgp/martian-next-hop.xml.i
new file mode 100644
index 000000000..938d4ffa2
--- /dev/null
+++ b/op-mode-definitions/include/bgp/martian-next-hop.xml.i
@@ -0,0 +1,15 @@
+<!-- included start from bgp/martian-next-hop.xml.i -->
+<node name="martian">
+ <properties>
+ <help>Martian next-hops</help>
+ </properties>
+ <children>
+ <leafNode name="next-hop">
+ <properties>
+ <help>Martian next-hop database</help>
+ </properties>
+ <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
+ </leafNode>
+ </children>
+</node>
+<!-- included end -->
diff --git a/op-mode-definitions/include/bgp/next-hop.xml.i b/op-mode-definitions/include/bgp/next-hop.xml.i
new file mode 100644
index 000000000..517a44888
--- /dev/null
+++ b/op-mode-definitions/include/bgp/next-hop.xml.i
@@ -0,0 +1,23 @@
+<!-- included start from bgp/next-hop.xml.i -->
+<node name="nexthop">
+ <properties>
+ <help>Show BGP nexthop table</help>
+ </properties>
+ <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
+ <children>
+ #include <include/vtysh-generic-detail.xml.i>
+ </children>
+</node>
+<tagNode name="nexthop">
+ <properties>
+ <help>IPv4/IPv6 nexthop address</help>
+ <completionHelp>
+ <list>&lt;x.x.x.x&gt; &lt;h:h:h:h:h:h:h:h&gt;</list>
+ </completionHelp>
+ </properties>
+ <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
+ <children>
+ #include <include/vtysh-generic-detail.xml.i>
+ </children>
+</tagNode>
+<!-- included end -->
diff --git a/op-mode-definitions/openvpn.xml.in b/op-mode-definitions/openvpn.xml.in
index 0aa9c3209..94647af02 100644
--- a/op-mode-definitions/openvpn.xml.in
+++ b/op-mode-definitions/openvpn.xml.in
@@ -122,7 +122,7 @@
<properties>
<help>Show tunnel status for OpenVPN site-to-site interfaces</help>
</properties>
- <command>sudo ${vyos_op_scripts_dir}/openvpn.py show --mode site-to-site</command>
+ <command>sudo ${vyos_op_scripts_dir}/openvpn.py show --mode site_to_site</command>
</leafNode>
</children>
</node>
diff --git a/op-mode-definitions/show-babel.xml.in b/op-mode-definitions/show-babel.xml.in
index 3aac3764e..0a1f1b262 100644
--- a/op-mode-definitions/show-babel.xml.in
+++ b/op-mode-definitions/show-babel.xml.in
@@ -23,7 +23,7 @@
<properties>
<help>Show Babel neighbor information for specified interface</help>
<completionHelp>
- <script>${vyos_completion_dir}/list_interfaces.py</script>
+ <script>${vyos_completion_dir}/list_interfaces</script>
</completionHelp>
</properties>
<command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
diff --git a/op-mode-definitions/show-bgp.xml.in b/op-mode-definitions/show-bgp.xml.in
index c33a9dacf..974147621 100644
--- a/op-mode-definitions/show-bgp.xml.in
+++ b/op-mode-definitions/show-bgp.xml.in
@@ -31,46 +31,14 @@
</leafNode>
</children>
</node>
- <node name="martian">
- <properties>
- <help>martian next-hops</help>
- </properties>
- <children>
- <leafNode name="next-hop">
- <properties>
- <help>martian next-hop database</help>
- </properties>
- <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
- </leafNode>
- </children>
- </node>
+ #include <include/bgp/martian-next-hop.xml.i>
<leafNode name="memory">
<properties>
<help>Global BGP memory statistics</help>
</properties>
<command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
</leafNode>
- <node name="nexthop">
- <properties>
- <help>Show BGP nexthop table</help>
- </properties>
- <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
- <children>
- #include <include/vtysh-generic-detail.xml.i>
- </children>
- </node>
- <tagNode name="nexthop">
- <properties>
- <help>IPv4/IPv6 nexthop address</help>
- <completionHelp>
- <list>&lt;x.x.x.x&gt; &lt;h:h:h:h:h:h:h:h&gt;</list>
- </completionHelp>
- </properties>
- <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
- <children>
- #include <include/vtysh-generic-detail.xml.i>
- </children>
- </tagNode>
+ #include <include/bgp/next-hop.xml.i>
<leafNode name="statistics">
<properties>
<help>BGP RIB advertisement statistics</help>
@@ -100,6 +68,8 @@
<command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
<children>
#include <include/bgp/show-bgp-common.xml.i>
+ #include <include/bgp/martian-next-hop.xml.i>
+ #include <include/bgp/next-hop.xml.i>
</children>
</tagNode>
#include <include/vtysh-generic-wide.xml.i>
diff --git a/python/vyos/configdict.py b/python/vyos/configdict.py
index 53decfbf5..434ff99d7 100644
--- a/python/vyos/configdict.py
+++ b/python/vyos/configdict.py
@@ -427,6 +427,10 @@ def get_interface_dict(config, base, ifname=''):
# Add interface instance name into dictionary
dict.update({'ifname': ifname})
+ # Check if QoS policy applied on this interface - See ifconfig.interface.set_mirror_redirect()
+ if config.exists(['qos', 'interface', ifname]):
+ dict.update({'traffic_policy': {}})
+
# XXX: T2665: When there is no DHCPv6-PD configuration given, we can safely
# remove the default values from the dict.
if 'dhcpv6_options' not in dict:
@@ -498,6 +502,9 @@ def get_interface_dict(config, base, ifname=''):
# Add subinterface name to dictionary
dict['vif'][vif].update({'ifname' : f'{ifname}.{vif}'})
+ if config.exists(['qos', 'interface', f'{ifname}.{vif}']):
+ dict['vif'][vif].update({'traffic_policy': {}})
+
default_vif_values = defaults(base + ['vif'])
# XXX: T2665: When there is no DHCPv6-PD configuration given, we can safely
# remove the default values from the dict.
@@ -532,6 +539,9 @@ def get_interface_dict(config, base, ifname=''):
# Add subinterface name to dictionary
dict['vif_s'][vif_s].update({'ifname' : f'{ifname}.{vif_s}'})
+ if config.exists(['qos', 'interface', f'{ifname}.{vif_s}']):
+ dict['vif_s'][vif_s].update({'traffic_policy': {}})
+
default_vif_s_values = defaults(base + ['vif-s'])
# XXX: T2665: we only wan't the vif-s defaults - do not care about vif-c
if 'vif_c' in default_vif_s_values: del default_vif_s_values['vif_c']
@@ -571,6 +581,9 @@ def get_interface_dict(config, base, ifname=''):
# Add subinterface name to dictionary
dict['vif_s'][vif_s]['vif_c'][vif_c].update({'ifname' : f'{ifname}.{vif_s}.{vif_c}'})
+ if config.exists(['qos', 'interface', f'{ifname}.{vif_s}.{vif_c}']):
+ dict['vif_s'][vif_s]['vif_c'][vif_c].update({'traffic_policy': {}})
+
default_vif_c_values = defaults(base + ['vif-s', 'vif-c'])
# XXX: T2665: When there is no DHCPv6-PD configuration given, we can safely
diff --git a/python/vyos/defaults.py b/python/vyos/defaults.py
index 060a0ba03..d4ffc249e 100644
--- a/python/vyos/defaults.py
+++ b/python/vyos/defaults.py
@@ -30,6 +30,7 @@ directories = {
'templates' : '/usr/share/vyos/templates/',
'certbot' : '/config/auth/letsencrypt',
'api_schema': f'{base_dir}/services/api/graphql/graphql/schema/',
+ 'api_client_op': f'{base_dir}/services/api/graphql/graphql/client_op/',
'api_templates': f'{base_dir}/services/api/graphql/session/templates/',
'vyos_udev_dir' : '/run/udev/vyos'
}
diff --git a/python/vyos/opmode.py b/python/vyos/opmode.py
index d02ad4de6..d7172a0b5 100644
--- a/python/vyos/opmode.py
+++ b/python/vyos/opmode.py
@@ -128,6 +128,25 @@ def _get_arg_type(t):
else:
return t
+def _is_literal_type(t):
+ if _is_optional_type(t):
+ t = _get_arg_type(t)
+
+ if typing.get_origin(t) == typing.Literal:
+ return True
+
+ return False
+
+def _get_literal_values(t):
+ """ Returns the tuple of allowed values for a Literal type
+ """
+ if not _is_literal_type(t):
+ return tuple()
+ if _is_optional_type(t):
+ t = _get_arg_type(t)
+
+ return typing.get_args(t)
+
def _normalize_field_name(name):
# Convert the name to string if it is not
# (in some cases they may be numbers)
@@ -194,9 +213,21 @@ def run(module):
subparser.add_argument(f"--{opt}", action='store_true')
else:
if _is_optional_type(th):
- subparser.add_argument(f"--{opt}", type=_get_arg_type(th), default=None)
+ if _is_literal_type(th):
+ subparser.add_argument(f"--{opt}",
+ choices=list(_get_literal_values(th)),
+ default=None)
+ else:
+ subparser.add_argument(f"--{opt}",
+ type=_get_arg_type(th), default=None)
else:
- subparser.add_argument(f"--{opt}", type=_get_arg_type(th), required=True)
+ if _is_literal_type(th):
+ subparser.add_argument(f"--{opt}",
+ choices=list(_get_literal_values(th)),
+ required=True)
+ else:
+ subparser.add_argument(f"--{opt}",
+ type=_get_arg_type(th), required=True)
# Get options as a dict rather than a namespace,
# so that we can modify it and pack for passing to functions
diff --git a/python/vyos/template.py b/python/vyos/template.py
index 6367f51e5..06a292706 100644
--- a/python/vyos/template.py
+++ b/python/vyos/template.py
@@ -663,7 +663,24 @@ def nat_static_rule(rule_conf, rule_id, nat_type):
@register_filter('range_to_regex')
def range_to_regex(num_range):
+ """Convert range of numbers or list of ranges
+ to regex
+
+ % range_to_regex('11-12')
+ '(1[1-2])'
+ % range_to_regex(['11-12', '14-15'])
+ '(1[1-2]|1[4-5])'
+ """
from vyos.range_regex import range_to_regex
+ if isinstance(num_range, list):
+ data = []
+ for entry in num_range:
+ if '-' not in entry:
+ data.append(entry)
+ else:
+ data.append(range_to_regex(entry))
+ return f'({"|".join(data)})'
+
if '-' not in num_range:
return num_range
diff --git a/python/vyos/util.py b/python/vyos/util.py
index 66ded464d..0593184cc 100644
--- a/python/vyos/util.py
+++ b/python/vyos/util.py
@@ -874,12 +874,16 @@ def convert_data(data):
Returns:
str | list | dict: converted data
"""
+ from base64 import b64encode
from collections import OrderedDict
if isinstance(data, str):
return data
if isinstance(data, bytes):
- return data.decode()
+ try:
+ return data.decode()
+ except UnicodeDecodeError:
+ return b64encode(data).decode()
if isinstance(data, list):
list_tmp = []
for item in data:
diff --git a/schema/interface_definition.rnc b/schema/interface_definition.rnc
index d7fc4966c..758d9ce1c 100644
--- a/schema/interface_definition.rnc
+++ b/schema/interface_definition.rnc
@@ -50,7 +50,7 @@ node = element node
tagNode = element tagNode
{
(ownerAttr? & nodeNameAttr),
- (properties? & children )
+ (defaultValue? & properties? & children )
}
# Leaf nodes are terminal configuration nodes that can't have children,
diff --git a/schema/interface_definition.rng b/schema/interface_definition.rng
index 3ff60cf18..94a828c3b 100644
--- a/schema/interface_definition.rng
+++ b/schema/interface_definition.rng
@@ -2,19 +2,19 @@
<grammar xmlns="http://relaxng.org/ns/structure/1.0">
<!--
interface_definition.rnc: VyConf reference tree XML grammar
-
+
Copyright (C) 2014. 2017 VyOS maintainers and contributors <maintainers@vyos.net>
-
+
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
-
+
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
-
+
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
@@ -84,6 +84,9 @@
</interleave>
<interleave>
<optional>
+ <ref name="defaultValue"/>
+ </optional>
+ <optional>
<ref name="properties"/>
</optional>
<ref name="children"/>
@@ -139,7 +142,7 @@
Nodes may have properties
For simplicity, any property is allowed in any node,
but whether they are used or not is implementation-defined
-
+
Leaf nodes may differ in number of values that can be
associated with them.
By default, a leaf node can have only one value.
@@ -147,7 +150,7 @@
"valueless" means it can have no values at all.
"hidden" means node visibility can be toggled, eg 'dangerous' commands,
"secret" allows a node to hide its value from unprivileged users.
-
+
"priority" is used to influence node processing order for nodes
with exact same dependencies and in compatibility modes.
-->
diff --git a/src/completion/list_interfaces.py b/src/completion/list_interfaces.py
deleted file mode 100755
index b19b90156..000000000
--- a/src/completion/list_interfaces.py
+++ /dev/null
@@ -1,54 +0,0 @@
-#!/usr/bin/env python3
-#
-# Copyright (C) 2019-2020 VyOS maintainers and contributors
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License version 2 or later as
-# published by the Free Software Foundation.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-import sys
-import argparse
-from vyos.ifconfig import Section
-
-def matching(feature):
- for section in Section.feature(feature):
- for intf in Section.interfaces(section):
- yield intf
-
-parser = argparse.ArgumentParser()
-group = parser.add_mutually_exclusive_group()
-group.add_argument("-t", "--type", type=str, help="List interfaces of specific type")
-group.add_argument("-b", "--broadcast", action="store_true", help="List all broadcast interfaces")
-group.add_argument("-br", "--bridgeable", action="store_true", help="List all bridgeable interfaces")
-group.add_argument("-bo", "--bondable", action="store_true", help="List all bondable interfaces")
-
-args = parser.parse_args()
-
-if args.type:
- try:
- interfaces = Section.interfaces(args.type)
- print(" ".join(interfaces))
- except ValueError as e:
- print(e, file=sys.stderr)
- print("")
-
-elif args.broadcast:
- print(" ".join(matching("broadcast")))
-
-elif args.bridgeable:
- print(" ".join(matching("bridgeable")))
-
-elif args.bondable:
- # we need to filter out VLAN interfaces identified by a dot (.) in their name
- print(" ".join([intf for intf in matching("bondable") if '.' not in intf]))
-
-else:
- print(" ".join(Section.interfaces()))
diff --git a/src/conf_mode/container.py b/src/conf_mode/container.py
index 10e9e9213..68070ea5b 100755
--- a/src/conf_mode/container.py
+++ b/src/conf_mode/container.py
@@ -18,7 +18,6 @@ import os
from ipaddress import ip_address
from ipaddress import ip_network
-from time import sleep
from json import dumps as json_write
from vyos.base import Warning
@@ -28,6 +27,7 @@ from vyos.configdict import node_changed
from vyos.util import call
from vyos.util import cmd
from vyos.util import run
+from vyos.util import rc_cmd
from vyos.util import write_file
from vyos.template import inc_ip
from vyos.template import is_ipv4
@@ -68,6 +68,9 @@ def get_config(config=None):
# container base default values can not be merged here - remove and add them later
if 'name' in default_values:
del default_values['name']
+ # registry will be handled below
+ if 'registry' in default_values:
+ del default_values['registry']
container = dict_merge(default_values, container)
# Merge per-container default values
@@ -95,6 +98,15 @@ def get_config(config=None):
container['name'][name]['volume'][volume] = dict_merge(
default_values_volume, container['name'][name]['volume'][volume])
+ # registry is a tagNode with default values - merge the list from
+ # default_values['registry'] into the tagNode variables
+ if 'registry' not in container:
+ container.update({'registry' : {}})
+ default_values = defaults(base)
+ for registry in default_values['registry'].split():
+ tmp = {registry : {}}
+ container['registry'] = dict_merge(tmp, container['registry'])
+
# Delete container network, delete containers
tmp = node_changed(conf, base + ['network'])
if tmp: container.update({'network_remove' : tmp})
@@ -226,6 +238,11 @@ def verify(container):
if 'network' in container_config and network in container_config['network']:
raise ConfigError(f'Can not remove network "{network}", used by container "{container}"!')
+ if 'registry' in container and 'authentication' in container['registry']:
+ for registry, registry_config in container['registry']['authentication'].items():
+ if not {'username', 'password'} <= set(registry_config):
+ raise ConfigError('If registry username or or password is defined, so must be the other!')
+
return None
def generate_run_arguments(name, container_config):
@@ -355,6 +372,24 @@ def generate(container):
write_file(f'/etc/cni/net.d/{network}.conflist', json_write(tmp, indent=2))
+ if 'registry' in container:
+ cmd = f'podman logout --all'
+ rc, out = rc_cmd(cmd)
+ if rc != 0:
+ raise ConfigError(out)
+
+ for registry, registry_config in container['registry'].items():
+ if 'disable' in registry_config:
+ continue
+ if 'authentication' in registry_config:
+ if {'username', 'password'} <= set(registry_config['authentication']):
+ username = registry_config['authentication']['username']
+ password = registry_config['authentication']['password']
+ cmd = f'podman login --username {username} --password {password} {registry}'
+ rc, out = rc_cmd(cmd)
+ if rc != 0:
+ raise ConfigError(out)
+
render(config_containers_registry, 'container/registries.conf.j2', container)
render(config_containers_storage, 'container/storage.conf.j2', container)
diff --git a/src/conf_mode/interfaces-tunnel.py b/src/conf_mode/interfaces-tunnel.py
index e2701d9d3..0a3726e94 100755
--- a/src/conf_mode/interfaces-tunnel.py
+++ b/src/conf_mode/interfaces-tunnel.py
@@ -136,7 +136,7 @@ def verify(tunnel):
if our_key != None:
if their_address == our_address and their_key == our_key:
raise ConfigError(f'Key "{our_key}" for source-address "{our_address}" ' \
- f'is already used for tunnel "{tunnel_if}"!')
+ f'is already used for tunnel "{o_tunnel}"!')
else:
our_source_if = dict_search('source_interface', tunnel)
their_source_if = dict_search('source_interface', o_tunnel_conf)
diff --git a/src/conf_mode/qos.py b/src/conf_mode/qos.py
index dca713283..1be2c283f 100755
--- a/src/conf_mode/qos.py
+++ b/src/conf_mode/qos.py
@@ -21,7 +21,9 @@ from netifaces import interfaces
from vyos.base import Warning
from vyos.config import Config
+from vyos.configdep import set_dependents, call_dependents
from vyos.configdict import dict_merge
+from vyos.ifconfig import Section
from vyos.qos import CAKE
from vyos.qos import DropTail
from vyos.qos import FairQueue
@@ -83,6 +85,18 @@ def get_config(config=None):
get_first_key=True,
no_tag_node_value_mangle=True)
+ if 'interface' in qos:
+ for ifname, if_conf in qos['interface'].items():
+ if_node = Section.get_config_path(ifname)
+
+ if not if_node:
+ continue
+
+ path = f'interfaces {if_node}'
+ if conf.exists(f'{path} mirror') or conf.exists(f'{path} redirect'):
+ type_node = path.split(" ")[1] # return only interface type node
+ set_dependents(type_node, conf, ifname)
+
if 'policy' in qos:
for policy in qos['policy']:
# when calling defaults() we need to use the real CLI node, thus we
@@ -245,6 +259,8 @@ def apply(qos):
tmp = shaper_type(interface)
tmp.update(shaper_config, direction)
+ call_dependents()
+
return None
if __name__ == '__main__':
diff --git a/src/conf_mode/service_ipoe-server.py b/src/conf_mode/service_ipoe-server.py
index e9afd6a55..9cdfa08ef 100755
--- a/src/conf_mode/service_ipoe-server.py
+++ b/src/conf_mode/service_ipoe-server.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2018-2022 VyOS maintainers and contributors
+# Copyright (C) 2018-2023 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
@@ -53,8 +53,11 @@ def verify(ipoe):
if 'interface' not in ipoe:
raise ConfigError('No IPoE interface configured')
- for interface in ipoe['interface']:
+ for interface, iface_config in ipoe['interface'].items():
verify_interface_exists(interface)
+ if 'client_subnet' in iface_config and 'vlan' in iface_config:
+ raise ConfigError('Option "client-subnet" incompatible with "vlan"!'
+ 'Use "ipoe client-ip-pool" instead.')
#verify_accel_ppp_base_service(ipoe, local_users=False)
diff --git a/src/etc/systemd/system/frr.service.d/override.conf b/src/etc/systemd/system/frr.service.d/override.conf
index 69eb1a86a..2e2f67f70 100644
--- a/src/etc/systemd/system/frr.service.d/override.conf
+++ b/src/etc/systemd/system/frr.service.d/override.conf
@@ -3,6 +3,8 @@ Before=
Before=vyos-router.service
[Service]
+LimitNOFILE=4096
+LimitNOFILESoft=4096
ExecStartPre=/bin/bash -c 'mkdir -p /run/frr/config; \
echo "log syslog" > /run/frr/config/frr.conf; \
echo "log facility local7" >> /run/frr/config/frr.conf; \
diff --git a/src/op_mode/bgp.py b/src/op_mode/bgp.py
index 23001a9d7..3f6d45dd7 100755
--- a/src/op_mode/bgp.py
+++ b/src/op_mode/bgp.py
@@ -30,6 +30,7 @@ from vyos.configquery import ConfigTreeQuery
import vyos.opmode
+ArgFamily = typing.Literal['inet', 'inet6']
frr_command_template = Template("""
{% if family %}
@@ -75,7 +76,7 @@ def _verify(func):
@_verify
def show_neighbors(raw: bool,
- family: str,
+ family: ArgFamily,
peer: typing.Optional[str],
vrf: typing.Optional[str]):
kwargs = dict(locals())
diff --git a/src/op_mode/conntrack.py b/src/op_mode/conntrack.py
index df213cc5a..ea7c4c208 100755
--- a/src/op_mode/conntrack.py
+++ b/src/op_mode/conntrack.py
@@ -15,6 +15,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import sys
+import typing
import xmltodict
from tabulate import tabulate
@@ -23,6 +24,7 @@ from vyos.util import run
import vyos.opmode
+ArgFamily = typing.Literal['inet', 'inet6']
def _get_xml_data(family):
"""
@@ -126,7 +128,7 @@ def get_formatted_output(dict_data):
return output
-def show(raw: bool, family: str):
+def show(raw: bool, family: ArgFamily):
family = 'ipv6' if family == 'inet6' else 'ipv4'
conntrack_data = _get_raw_data(family)
if raw:
diff --git a/src/op_mode/dhcp.py b/src/op_mode/dhcp.py
index b9e6e7bc9..41da14065 100755
--- a/src/op_mode/dhcp.py
+++ b/src/op_mode/dhcp.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2022 VyOS maintainers and contributors
+# Copyright (C) 2022-2023 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
@@ -36,6 +36,9 @@ lease_valid_states = ['all', 'active', 'free', 'expired', 'released', 'abandoned
sort_valid_inet = ['end', 'mac', 'hostname', 'ip', 'pool', 'remaining', 'start', 'state']
sort_valid_inet6 = ['end', 'iaid_duid', 'ip', 'last_communication', 'pool', 'remaining', 'state', 'type']
+ArgFamily = typing.Literal['inet', 'inet6']
+ArgState = typing.Literal['all', 'active', 'free', 'expired', 'released', 'abandoned', 'reset', 'backup']
+
def _utc_to_local(utc_dt):
return datetime.fromtimestamp((datetime.fromtimestamp(utc_dt) - datetime(1970, 1, 1)).total_seconds())
@@ -82,7 +85,7 @@ def _get_raw_server_leases(family='inet', pool=None, sorted=None, state=[]) -> l
data_lease['ip'] = lease.ip
data_lease['state'] = lease.binding_state
data_lease['pool'] = lease.sets.get('shared-networkname', '')
- data_lease['end'] = lease.end.timestamp()
+ data_lease['end'] = lease.end.timestamp() if lease.end else None
if family == 'inet':
data_lease['mac'] = lease.ethernet
@@ -95,17 +98,18 @@ def _get_raw_server_leases(family='inet', pool=None, sorted=None, state=[]) -> l
lease_types_long = {'na': 'non-temporary', 'ta': 'temporary', 'pd': 'prefix delegation'}
data_lease['type'] = lease_types_long[lease.type]
- data_lease['remaining'] = lease.end - datetime.utcnow()
+ data_lease['remaining'] = '-'
- if data_lease['remaining'].days >= 0:
- # substraction gives us a timedelta object which can't be formatted with strftime
- # so we use str(), split gets rid of the microseconds
- data_lease['remaining'] = str(data_lease["remaining"]).split('.')[0]
- else:
- data_lease['remaining'] = ''
+ if lease.end:
+ data_lease['remaining'] = lease.end - datetime.utcnow()
+
+ if data_lease['remaining'].days >= 0:
+ # substraction gives us a timedelta object which can't be formatted with strftime
+ # so we use str(), split gets rid of the microseconds
+ data_lease['remaining'] = str(data_lease["remaining"]).split('.')[0]
# Do not add old leases
- if data_lease['remaining'] != '' and data_lease['pool'] in pool:
+ if data_lease['remaining'] != '' and data_lease['pool'] in pool and data_lease['state'] != 'free':
if not state or data_lease['state'] in state:
data.append(data_lease)
@@ -137,7 +141,7 @@ def _get_formatted_server_leases(raw_data, family='inet'):
start = lease.get('start')
start = _utc_to_local(start).strftime('%Y/%m/%d %H:%M:%S')
end = lease.get('end')
- end = _utc_to_local(end).strftime('%Y/%m/%d %H:%M:%S')
+ end = _utc_to_local(end).strftime('%Y/%m/%d %H:%M:%S') if end else '-'
remain = lease.get('remaining')
pool = lease.get('pool')
hostname = lease.get('hostname')
@@ -248,7 +252,7 @@ def _verify(func):
@_verify
-def show_pool_statistics(raw: bool, family: str, pool: typing.Optional[str]):
+def show_pool_statistics(raw: bool, family: ArgFamily, pool: typing.Optional[str]):
pool_data = _get_raw_pool_statistics(family=family, pool=pool)
if raw:
return pool_data
@@ -257,8 +261,8 @@ def show_pool_statistics(raw: bool, family: str, pool: typing.Optional[str]):
@_verify
-def show_server_leases(raw: bool, family: str, pool: typing.Optional[str],
- sorted: typing.Optional[str], state: typing.Optional[str]):
+def show_server_leases(raw: bool, family: ArgFamily, pool: typing.Optional[str],
+ sorted: typing.Optional[str], state: typing.Optional[ArgState]):
# if dhcp server is down, inactive leases may still be shown as active, so warn the user.
if not is_systemd_service_running('isc-dhcp-server.service'):
Warning('DHCP server is configured but not started. Data may be stale.')
diff --git a/src/op_mode/nat.py b/src/op_mode/nat.py
index cf06de0e9..c92795745 100755
--- a/src/op_mode/nat.py
+++ b/src/op_mode/nat.py
@@ -31,6 +31,8 @@ from vyos.util import dict_search
base = 'nat'
unconf_message = 'NAT is not configured'
+ArgDirection = typing.Literal['source', 'destination']
+ArgFamily = typing.Literal['inet', 'inet6']
def _get_xml_translation(direction, family, address=None):
"""
@@ -298,7 +300,7 @@ def _verify(func):
@_verify
-def show_rules(raw: bool, direction: str, family: str):
+def show_rules(raw: bool, direction: ArgDirection, family: ArgFamily):
nat_rules = _get_raw_data_rules(direction, family)
if raw:
return nat_rules
@@ -307,7 +309,7 @@ def show_rules(raw: bool, direction: str, family: str):
@_verify
-def show_statistics(raw: bool, direction: str, family: str):
+def show_statistics(raw: bool, direction: ArgDirection, family: ArgFamily):
nat_statistics = _get_raw_data_rules(direction, family)
if raw:
return nat_statistics
@@ -316,8 +318,8 @@ def show_statistics(raw: bool, direction: str, family: str):
@_verify
-def show_translations(raw: bool, direction:
- str, family: str,
+def show_translations(raw: bool, direction: ArgDirection,
+ family: ArgFamily,
address: typing.Optional[str],
verbose: typing.Optional[bool]):
family = 'ipv6' if family == 'inet6' else 'ipv4'
diff --git a/src/op_mode/neighbor.py b/src/op_mode/neighbor.py
index 264dbdc72..b329ea280 100755
--- a/src/op_mode/neighbor.py
+++ b/src/op_mode/neighbor.py
@@ -32,6 +32,9 @@ import typing
import vyos.opmode
+ArgFamily = typing.Literal['inet', 'inet6']
+ArgState = typing.Literal['reachable', 'stale', 'failed', 'permanent']
+
def interface_exists(interface):
import os
return os.path.exists(f'/sys/class/net/{interface}')
@@ -88,7 +91,8 @@ def format_neighbors(neighs, interface=None):
headers = ["Address", "Interface", "Link layer address", "State"]
return tabulate(neighs, headers)
-def show(raw: bool, family: str, interface: typing.Optional[str], state: typing.Optional[str]):
+def show(raw: bool, family: ArgFamily, interface: typing.Optional[str],
+ state: typing.Optional[ArgState]):
""" Display neighbor table contents """
data = get_raw_data(family, interface, state=state)
@@ -97,7 +101,7 @@ def show(raw: bool, family: str, interface: typing.Optional[str], state: typing.
else:
return format_neighbors(data, interface)
-def reset(family: str, interface: typing.Optional[str], address: typing.Optional[str]):
+def reset(family: ArgFamily, interface: typing.Optional[str], address: typing.Optional[str]):
from vyos.util import run
if address and interface:
diff --git a/src/op_mode/openvpn.py b/src/op_mode/openvpn.py
index 79130c7c0..8f88ab422 100755
--- a/src/op_mode/openvpn.py
+++ b/src/op_mode/openvpn.py
@@ -18,6 +18,7 @@
import os
import sys
+import typing
from tabulate import tabulate
import vyos.opmode
@@ -26,6 +27,8 @@ from vyos.util import commit_in_progress
from vyos.util import call
from vyos.config import Config
+ArgMode = typing.Literal['client', 'server', 'site_to_site']
+
def _get_tunnel_address(peer_host, peer_port, status_file):
peer = peer_host + ':' + peer_port
lst = []
@@ -155,7 +158,7 @@ def _get_raw_data(mode: str) -> dict:
d['local_port'] = conf_dict[intf].get('local-port', '')
if conf.exists(f'interfaces openvpn {intf} server client'):
d['configured_clients'] = conf.list_nodes(f'interfaces openvpn {intf} server client')
- if mode in ['client', 'site-to-site']:
+ if mode in ['client', 'site_to_site']:
for client in d['clients']:
if 'shared-secret-key-file' in list(conf_dict[intf]):
client['name'] = 'None (PSK)'
@@ -198,7 +201,7 @@ def _format_openvpn(data: dict) -> str:
return out
-def show(raw: bool, mode: str) -> str:
+def show(raw: bool, mode: ArgMode) -> str:
openvpn_data = _get_raw_data(mode)
if raw:
diff --git a/src/op_mode/route.py b/src/op_mode/route.py
index 7f0f9cbac..d6d6b7d6f 100755
--- a/src/op_mode/route.py
+++ b/src/op_mode/route.py
@@ -54,7 +54,9 @@ frr_command_template = Template("""
{% endif %}
""")
-def show_summary(raw: bool, family: str, table: typing.Optional[int], vrf: typing.Optional[str]):
+ArgFamily = typing.Literal['inet', 'inet6']
+
+def show_summary(raw: bool, family: ArgFamily, table: typing.Optional[int], vrf: typing.Optional[str]):
from vyos.util import cmd
if family == 'inet':
@@ -94,7 +96,7 @@ def show_summary(raw: bool, family: str, table: typing.Optional[int], vrf: typin
return output
def show(raw: bool,
- family: str,
+ family: ArgFamily,
net: typing.Optional[str],
table: typing.Optional[int],
protocol: typing.Optional[str],
diff --git a/src/services/api/graphql/generate/schema_from_op_mode.py b/src/services/api/graphql/generate/schema_from_op_mode.py
index 98b2ad7b7..cb7b0fd21 100755
--- a/src/services/api/graphql/generate/schema_from_op_mode.py
+++ b/src/services/api/graphql/generate/schema_from_op_mode.py
@@ -26,6 +26,7 @@ from jinja2 import Template
from vyos.defaults import directories
from vyos.opmode import _is_op_mode_function_name as is_op_mode_function_name
+from vyos.opmode import _get_literal_values as get_literal_values
from vyos.util import load_as_module
if __package__ is None or __package__ == '':
sys.path.append(os.path.join(directories['services'], 'api'))
@@ -38,8 +39,10 @@ else:
OP_MODE_PATH = directories['op_mode']
SCHEMA_PATH = directories['api_schema']
+CLIENT_OP_PATH = directories['api_client_op']
DATA_DIR = directories['data']
+
op_mode_include_file = os.path.join(DATA_DIR, 'op-mode-standardized.json')
op_mode_error_schema = 'op_mode_error.graphql'
@@ -94,6 +97,14 @@ extend type Mutation {
}
"""
+enum_template = """
+enum {{ enum_name }} {
+ {%- for field_entry in enum_fields %}
+ {{ field_entry }}
+ {%- endfor %}
+}
+"""
+
error_template = """
interface OpModeError {
name: String!
@@ -109,12 +120,52 @@ type {{ name }} implements OpModeError {
{%- endfor %}
"""
-def create_schema(func_name: str, base_name: str, func: callable) -> str:
+op_query_template = """
+query {{ op_name }} ({{ op_sig }}) {
+ {{ op_name }} (data: { {{ op_arg }} }) {
+ success
+ errors
+ op_mode_error {
+ name
+ message
+ vyos_code
+ }
+ data {
+ result
+ }
+ }
+}
+"""
+
+op_mutation_template = """
+mutation {{ op_name }} ({{ op_sig }}) {
+ {{ op_name }} (data: { {{ op_arg }} }) {
+ success
+ errors
+ op_mode_error {
+ name
+ message
+ vyos_code
+ }
+ data {
+ result
+ }
+ }
+}
+"""
+
+def create_schema(func_name: str, base_name: str, func: callable,
+ enums: dict) -> str:
sig = signature(func)
+ for k in sig.parameters:
+ t = get_literal_values(sig.parameters[k].annotation)
+ if t:
+ enums[t] = snake_to_pascal_case(sig.parameters[k].name + '_' + base_name)
+
field_dict = {}
for k in sig.parameters:
- field_dict[sig.parameters[k].name] = map_type_name(sig.parameters[k].annotation)
+ field_dict[sig.parameters[k].name] = map_type_name(sig.parameters[k].annotation, enums)
# It is assumed that if one is generating a schema for a 'show_*'
# function, that 'get_raw_data' is present and 'raw' is desired.
@@ -137,6 +188,58 @@ def create_schema(func_name: str, base_name: str, func: callable) -> str:
return res
+def create_client_op(func_name: str, base_name: str, func: callable,
+ enums: dict) -> str:
+ sig = signature(func)
+
+ for k in sig.parameters:
+ t = get_literal_values(sig.parameters[k].annotation)
+ if t:
+ enums[t] = snake_to_pascal_case(sig.parameters[k].name + '_' + base_name)
+
+ field_dict = {}
+ for k in sig.parameters:
+ field_dict[sig.parameters[k].name] = map_type_name(sig.parameters[k].annotation, enums)
+
+ # It is assumed that if one is generating a schema for a 'show_*'
+ # function, that 'get_raw_data' is present and 'raw' is desired.
+ if 'raw' in list(field_dict):
+ del field_dict['raw']
+
+ op_sig = ['$key: String']
+ op_arg = ['key: $key']
+ for k,v in field_dict.items():
+ op_sig.append('$'+k+': '+v)
+ op_arg.append(k+': $'+k)
+
+ op_data = {}
+ op_data['op_name'] = snake_to_pascal_case(func_name + '_' + base_name)
+ op_data['op_sig'] = ', '.join(op_sig)
+ op_data['op_arg'] = ', '.join(op_arg)
+
+ if is_show_function_name(func_name):
+ j2_template = Template(op_query_template)
+ else:
+ j2_template = Template(op_mutation_template)
+
+ res = j2_template.render(op_data)
+
+ return res
+
+def create_enums(enums: dict) -> str:
+ enum_data = []
+ for k, v in enums.items():
+ enum = {'enum_name': v, 'enum_fields': list(k)}
+ enum_data.append(enum)
+
+ out = ''
+ j2_template = Template(enum_template)
+ for el in enum_data:
+ out += j2_template.render(el)
+ out += '\n'
+
+ return out
+
def create_error_schema():
from vyos import opmode
@@ -157,6 +260,8 @@ def create_error_schema():
return res
def generate_op_mode_definitions():
+ os.makedirs(CLIENT_OP_PATH, exist_ok=True)
+
out = create_error_schema()
with open(f'{SCHEMA_PATH}/{op_mode_error_schema}', 'w') as f:
f.write(out)
@@ -175,14 +280,23 @@ def generate_op_mode_definitions():
for (name, thunk) in funcs:
funcs_dict[name] = thunk
- results = []
+ schema = []
+ client_op = []
+ enums = {} # gather enums from function Literal type args
for name,func in funcs_dict.items():
- res = create_schema(name, basename, func)
- results.append(res)
+ res = create_schema(name, basename, func, enums)
+ schema.append(res)
+ res = create_client_op(name, basename, func, enums)
+ client_op.append(res)
- out = '\n'.join(results)
+ out = create_enums(enums)
+ out += '\n'.join(schema)
with open(f'{SCHEMA_PATH}/{basename}.graphql', 'w') as f:
f.write(out)
+ out = '\n'.join(client_op)
+ with open(f'{CLIENT_OP_PATH}/{basename}.graphql', 'w') as f:
+ f.write(out)
+
if __name__ == '__main__':
generate_op_mode_definitions()
diff --git a/src/services/api/graphql/libs/op_mode.py b/src/services/api/graphql/libs/op_mode.py
index c553bbd67..e91d8bd0f 100644
--- a/src/services/api/graphql/libs/op_mode.py
+++ b/src/services/api/graphql/libs/op_mode.py
@@ -16,13 +16,13 @@
import os
import re
import typing
-import importlib.util
-from typing import Union
+from typing import Union, Tuple, Optional
from humps import decamelize
from vyos.defaults import directories
from vyos.util import load_as_module
from vyos.opmode import _normalize_field_names
+from vyos.opmode import _is_literal_type, _get_literal_values
def load_op_mode_as_module(name: str):
path = os.path.join(directories['op_mode'], name)
@@ -73,7 +73,7 @@ def snake_to_pascal_case(name: str) -> str:
res = ''.join(map(str.title, name.split('_')))
return res
-def map_type_name(type_name: type, optional: bool = False) -> str:
+def map_type_name(type_name: type, enums: Optional[dict] = None, optional: bool = False) -> str:
if type_name == str:
return 'String!' if not optional else 'String = null'
if type_name == int:
@@ -82,12 +82,17 @@ def map_type_name(type_name: type, optional: bool = False) -> str:
return 'Boolean = false'
if typing.get_origin(type_name) == list:
if not optional:
- return f'[{map_type_name(typing.get_args(type_name)[0])}]!'
- return f'[{map_type_name(typing.get_args(type_name)[0])}]'
+ return f'[{map_type_name(typing.get_args(type_name)[0], enums=enums)}]!'
+ return f'[{map_type_name(typing.get_args(type_name)[0], enums=enums)}]'
+ if _is_literal_type(type_name):
+ mapped = enums.get(_get_literal_values(type_name), '')
+ if not mapped:
+ raise ValueError(typing.get_args(type_name))
+ return f'{mapped}!' if not optional else mapped
# typing.Optional is typing.Union[_, NoneType]
if (typing.get_origin(type_name) is typing.Union and
typing.get_args(type_name)[1] == type(None)):
- return f'{map_type_name(typing.get_args(type_name)[0], optional=True)}'
+ return f'{map_type_name(typing.get_args(type_name)[0], enums=enums, optional=True)}'
# scalar 'Generic' is defined in schema.graphql
return 'Generic'