diff options
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><interface></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><x.x.x.x> <h:h:h:h:h:h:h:h></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><x.x.x.x> <h:h:h:h:h:h:h:h></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' |