diff options
81 files changed, 2387 insertions, 1308 deletions
@@ -38,9 +38,6 @@ interface_definitions: $(config_xml_obj) # T2773 - EIGRP support for VRF rm -rf $(TMPL_DIR)/vrf/name/node.tag/protocols/eigrp - # T4518, T4470 Load-balancing wan - rm -rf $(TMPL_DIR)/load-balancing - # XXX: test if there are empty node.def files - this is not allowed as these # could mask help strings or mandatory priority statements find $(TMPL_DIR) -name node.def -type f -empty -exec false {} + || sh -c 'echo "There are empty node.def files! Check your interface definitions." && exit 1' diff --git a/data/configd-include.json b/data/configd-include.json index 2f1d39006..456211caa 100644 --- a/data/configd-include.json +++ b/data/configd-include.json @@ -85,5 +85,6 @@ "vpn_l2tp.py", "vpn_pptp.py", "vpn_sstp.py", -"vrf.py" +"vrf.py", +"vrf_vni.py" ] diff --git a/data/templates/frr/bgpd.frr.j2 b/data/templates/frr/bgpd.frr.j2 index b749be93f..7bd9efdce 100644 --- a/data/templates/frr/bgpd.frr.j2 +++ b/data/templates/frr/bgpd.frr.j2 @@ -419,16 +419,18 @@ router bgp {{ system_as }} {{ 'vrf ' ~ vrf if vrf is vyos_defined }} rd {{ vni_config.rd }} {% endif %} {% if vni_config.route_target.both is vyos_defined %} - route-target both {{ vni_config.route_target.both }} +{% for route_target in vni_config.route_target.both %} + route-target both {{ route_target }} +{% endfor %} {% endif %} {% if vni_config.route_target.export is vyos_defined %} {% for route_target in vni_config.route_target.export %} - route-target export {{ route_target }} + route-target export {{ route_target }} {% endfor %} {% endif %} {% if vni_config.route_target.import is vyos_defined %} {% for route_target in vni_config.route_target.import %} - route-target import {{ route_target }} + route-target import {{ route_target }} {% endfor %} {% endif %} exit-vni diff --git a/data/templates/frr/policy.frr.j2 b/data/templates/frr/policy.frr.j2 index 9b5e80aed..ed5876ae9 100644 --- a/data/templates/frr/policy.frr.j2 +++ b/data/templates/frr/policy.frr.j2 @@ -245,6 +245,10 @@ route-map {{ route_map }} {{ rule_config.action }} {{ rule }} {% if rule_config.match.peer is vyos_defined %} match peer {{ rule_config.match.peer }} {% endif %} +{% if rule_config.match.protocol is vyos_defined %} +{% set source_protocol = 'ospf6' if rule_config.match.protocol == 'ospfv3' else rule_config.match.protocol %} + match source-protocol {{ source_protocol }} +{% endif %} {% if rule_config.match.rpki is vyos_defined %} match rpki {{ rule_config.match.rpki }} {% endif %} diff --git a/data/templates/frr/static_routes_macro.j2 b/data/templates/frr/static_routes_macro.j2 index 1c64ac58b..8afd4a68a 100644 --- a/data/templates/frr/static_routes_macro.j2 +++ b/data/templates/frr/static_routes_macro.j2 @@ -18,7 +18,12 @@ {% endif %} {% if prefix_config.next_hop is vyos_defined and prefix_config.next_hop is not none %} {% for next_hop, next_hop_config in prefix_config.next_hop.items() if next_hop_config.disable is not defined %} -{{ ip_ipv6 }} route {{ prefix }} {{ next_hop }} {{ next_hop_config.interface if next_hop_config.interface is vyos_defined }} {{ next_hop_config.distance if next_hop_config.distance is vyos_defined }} {{ 'nexthop-vrf ' ~ next_hop_config.vrf if next_hop_config.vrf is vyos_defined }} {{ 'table ' ~ table if table is vyos_defined }} +{{ ip_ipv6 }} route {{ prefix }} {{ next_hop }} {{ next_hop_config.interface if next_hop_config.interface is vyos_defined }} {{ next_hop_config.distance if next_hop_config.distance is vyos_defined }} {{ 'nexthop-vrf ' ~ next_hop_config.vrf if next_hop_config.vrf is vyos_defined }} {{ 'bfd profile ' ~ next_hop_config.bfd.profile if next_hop_config.bfd.profile is vyos_defined }} {{ 'table ' ~ table if table is vyos_defined }} +{% if next_hop_config.bfd.multi_hop.source is vyos_defined %} +{% for source, source_config in next_hop_config.bfd.multi_hop.source.items() %} +{{ ip_ipv6 }} route {{ prefix }} {{ next_hop }} bfd multi-hop source {{ source }} profile {{ source_config.profile }} +{% endfor %} +{% endif %} {% endfor %} {% endif %} {% endmacro %} diff --git a/data/templates/frr/staticd.frr.j2 b/data/templates/frr/staticd.frr.j2 index 55c05ceb7..992a0435c 100644 --- a/data/templates/frr/staticd.frr.j2 +++ b/data/templates/frr/staticd.frr.j2 @@ -37,7 +37,7 @@ vrf {{ vrf }} {% endfor %} {% endif %} {% if vrf is vyos_defined %} - exit-vrf +exit-vrf {% endif %} ! {# Policy route tables #} diff --git a/data/templates/frr/zebra.vrf.route-map.frr.j2 b/data/templates/frr/zebra.vrf.route-map.frr.j2 index eb6abd8e7..4e1206374 100644 --- a/data/templates/frr/zebra.vrf.route-map.frr.j2 +++ b/data/templates/frr/zebra.vrf.route-map.frr.j2 @@ -1,6 +1,10 @@ ! {% if name is vyos_defined %} {% for vrf, vrf_config in name.items() %} +{# code path required for vrf_vni.py as we will only render the required VR configuration and not all of them #} +{% if only_vrf is vyos_defined and vrf is not vyos_defined(only_vrf) %} +{% continue %} +{% endif %} vrf {{ vrf }} {% if vrf_config.ip.protocol is vyos_defined %} {% for protocol_name, protocol_config in vrf_config.ip.protocol.items() %} @@ -15,10 +19,10 @@ vrf {{ vrf }} ipv6 protocol {{ protocol_name }} route-map {{ protocol_config.route_map }} {% endfor %} {% endif %} -{% if vrf_config.vni is vyos_defined %} +{% if vrf_config.vni is vyos_defined and no_vni is not vyos_defined %} vni {{ vrf_config.vni }} {% endif %} +exit-vrf {% endfor %} - exit-vrf ! {% endif %} diff --git a/data/templates/load-balancing/wlb.conf.j2 b/data/templates/load-balancing/wlb.conf.j2 new file mode 100644 index 000000000..d3326b6b8 --- /dev/null +++ b/data/templates/load-balancing/wlb.conf.j2 @@ -0,0 +1,130 @@ +# Generated by /usr/libexec/vyos/conf_mode/load-balancing-wan.py + +{% if disable_source_nat is vyos_defined %} +disable-source-nat +{% endif %} +{% if enable_local_traffic is vyos_defined %} +enable-local-traffic +{% endif %} +{% if sticky_connections is vyos_defined %} +sticky-connections inbound +{% endif %} +{% if flush_connections is vyos_defined %} +flush-conntrack +{% endif %} +{% if hook is vyos_defined %} +hook "{{ hook }}" +{% endif %} +{% if interface_health is vyos_defined %} +health { +{% for interface, interface_config in interface_health.items() %} + interface {{ interface }} { +{% if interface_config.failure_count is vyos_defined %} + failure-ct {{ interface_config.failure_count }} +{% endif %} +{% if interface_config.success_count is vyos_defined %} + success-ct {{ interface_config.success_count }} +{% endif %} +{% if interface_config.nexthop is vyos_defined %} + nexthop {{ interface_config.nexthop }} +{% endif %} +{% if interface_config.test is vyos_defined %} +{% for test_rule, test_config in interface_config.test.items() %} + rule {{ test_rule }} { +{% if test_config.type is vyos_defined %} +{% set type_translate = {'ping': 'ping', 'ttl': 'udp', 'user-defined': 'user-defined'} %} + type {{ type_translate[test_config.type] }} { +{% if test_config.ttl_limit is vyos_defined and test_config.type == 'ttl' %} + ttl {{ test_config.ttl_limit }} +{% endif %} +{% if test_config.test_script is vyos_defined and test_config.type == 'user-defined' %} + test-script {{ test_config.test_script }} +{% endif %} +{% if test_config.target is vyos_defined %} + target {{ test_config.target }} +{% endif %} + resp-time {{ test_config.resp_time | int * 1000 }} + } +{% endif %} + } +{% endfor %} +{% endif %} + } +{% endfor %} +} +{% endif %} + +{% if rule is vyos_defined %} +{% for rule, rule_config in rule.items() %} +rule {{ rule }} { +{% if rule_config.exclude is vyos_defined %} + exclude +{% endif %} +{% if rule_config.failover is vyos_defined %} + failover +{% endif %} +{% if rule_config.limit is vyos_defined %} + limit { +{% if rule_config.limit.burst is vyos_defined %} + burst {{ rule_config.limit.burst }} +{% endif %} +{% if rule_config.limit.rate is vyos_defined %} + rate {{ rule_config.limit.rate }} +{% endif %} +{% if rule_config.limit.period is vyos_defined %} + period {{ rule_config.limit.period }} +{% endif %} +{% if rule_config.limit.threshold is vyos_defined %} + thresh {{ rule_config.limit.threshold }} +{% endif %} + } +{% endif %} +{% if rule_config.per_packet_balancing is vyos_defined %} + per-packet-balancing +{% endif %} +{% if rule_config.protocol is vyos_defined %} + protocol {{ rule_config.protocol }} +{% endif %} +{% if rule_config.destination is vyos_defined %} + destination { +{% if rule_config.destination.address is vyos_defined %} + address "{{ rule_config.destination.address }}" +{% endif %} +{% if rule_config.destination.port is vyos_defined %} +{% if '-' in rule_config.destination.port %} + port-ipt "-m multiport --dports {{ rule_config.destination.port | replace('-', ':') }}" +{% else %} + port-ipt " --dport {{ rule_config.destination.port }}" +{% endif %} +{% endif %} + } +{% endif %} +{% if rule_config.source is vyos_defined %} + source { +{% if rule_config.source.address is vyos_defined %} + address "{{ rule_config.source.address }}" +{% endif %} +{% if rule_config.source.port is vyos_defined %} +{% if '-' in rule_config.source.port %} + port-ipt "-m multiport --sports {{ rule_config.source.port | replace('-', ':') }}" +{% else %} + port.ipt " --sport {{ rule_config.source.port }}" +{% endif %} +{% endif %} + } +{% endif %} +{% if rule_config.inbound_interface is vyos_defined %} + inbound-interface {{ rule_config.inbound_interface }} +{% endif %} +{% if rule_config.interface is vyos_defined %} +{% for interface, interface_config in rule_config.interface.items() %} + interface {{ interface }} { +{% if interface_config.weight is vyos_defined %} + weight {{ interface_config.weight }} +{% endif %} + } +{% endfor %} +{% endif %} +} +{% endfor %} +{% endif %} diff --git a/data/templates/openvpn/server.conf.j2 b/data/templates/openvpn/server.conf.j2 index af866f2a6..6332ed9c2 100644 --- a/data/templates/openvpn/server.conf.j2 +++ b/data/templates/openvpn/server.conf.j2 @@ -98,7 +98,7 @@ server-ipv6 {{ subnet }} {% endif %} {% if server.client_ip_pool is vyos_defined and server.client_ip_pool.disable is not vyos_defined %} -ifconfig-pool {{ server.client_ip_pool.start }} {{ server.client_ip_pool.stop }}{{ server.client_ip_pool.subnet_mask if server.client_ip_pool.subnet_mask is vyos_defined }} +ifconfig-pool {{ server.client_ip_pool.start }} {{ server.client_ip_pool.stop }} {{ server.client_ip_pool.subnet_mask if server.client_ip_pool.subnet_mask is vyos_defined }} {% endif %} {% if server.max_connections is vyos_defined %} max-clients {{ server.max_connections }} diff --git a/debian/control b/debian/control index 3126e6ad9..4a2706fc3 100644 --- a/debian/control +++ b/debian/control @@ -16,7 +16,7 @@ Build-Depends: build-essential, libvyosconfig0 (>= 0.0.7), libzmq3-dev, - python3, + python3 (>= 3.10), python3-coverage, python3-lxml, python3-netifaces, @@ -33,7 +33,7 @@ Standards-Version: 3.9.6 Package: vyos-1x Architecture: amd64 arm64 Depends: - ${python3:Depends}, + ${python3:Depends} (>= 3.10), aardvark-dns, accel-ppp, auditd, diff --git a/interface-definitions/dns-forwarding.xml.in b/interface-definitions/dns-forwarding.xml.in index 6b7344b1d..de6991e06 100644 --- a/interface-definitions/dns-forwarding.xml.in +++ b/interface-definitions/dns-forwarding.xml.in @@ -126,7 +126,7 @@ <children> <tagNode name="a"> <properties> - <help>"A" record</help> + <help>A record</help> <valueHelp> <format>txt</format> <description>A DNS name relative to the root record</description> @@ -163,7 +163,7 @@ </tagNode> <tagNode name="aaaa"> <properties> - <help>"AAAA" record</help> + <help>AAAA record</help> <valueHelp> <format>txt</format> <description>A DNS name relative to the root record</description> @@ -200,7 +200,7 @@ </tagNode> <tagNode name="cname"> <properties> - <help>"CNAME" record</help> + <help>CNAME record</help> <valueHelp> <format>txt</format> <description>A DNS name relative to the root record</description> @@ -219,7 +219,7 @@ <help>Target DNS name</help> <valueHelp> <format>name.example.com</format> - <description>An absolute DNS name</description> + <description>Absolute DNS name</description> </valueHelp> <constraint> <regex>[-_a-zA-Z0-9.]{1,63}(?<!\.)</regex> @@ -232,7 +232,7 @@ </tagNode> <tagNode name="mx"> <properties> - <help>"MX" record</help> + <help>MX record</help> <valueHelp> <format>txt</format> <description>A DNS name relative to the root record</description> @@ -251,7 +251,7 @@ <help>Mail server</help> <valueHelp> <format>name.example.com</format> - <description>An absolute DNS name</description> + <description>Absolute DNS name</description> </valueHelp> <constraint> <regex>[-_a-zA-Z0-9.]{1,63}(?<!\.)</regex> @@ -277,9 +277,37 @@ #include <include/generic-disable-node.xml.i> </children> </tagNode> + <tagNode name="ns"> + <properties> + <help>NS record</help> + <valueHelp> + <format>txt</format> + <description>A DNS name relative to the root record</description> + </valueHelp> + <constraint> + <regex>([-_a-zA-Z0-9.]{1,63}|@)(?<!\.)</regex> + </constraint> + </properties> + <children> + <leafNode name="target"> + <properties> + <help>Target DNS server authoritative for subdomain</help> + <valueHelp> + <format>nsXX.example.com</format> + <description>Absolute DNS name</description> + </valueHelp> + <constraint> + <regex>[-_a-zA-Z0-9.]{1,63}(?<!\.)</regex> + </constraint> + </properties> + </leafNode> + #include <include/dns/time-to-live.xml.i> + #include <include/generic-disable-node.xml.i> + </children> + </tagNode> <tagNode name="ptr"> <properties> - <help>"PTR" record</help> + <help>PTR record</help> <valueHelp> <format>txt</format> <description>A DNS name relative to the root record</description> @@ -298,7 +326,7 @@ <help>Target DNS name</help> <valueHelp> <format>name.example.com</format> - <description>An absolute DNS name</description> + <description>Absolute DNS name</description> </valueHelp> <constraint> <regex>[-_a-zA-Z0-9.]{1,63}(?<!\.)</regex> @@ -311,7 +339,7 @@ </tagNode> <tagNode name="txt"> <properties> - <help>"TXT" record</help> + <help>TXT record</help> <valueHelp> <format>txt</format> <description>A DNS name relative to the root record</description> @@ -341,7 +369,7 @@ </tagNode> <tagNode name="spf"> <properties> - <help>"SPF" record (type=SPF)</help> + <help>SPF record</help> <valueHelp> <format>txt</format> <description>A DNS name relative to the root record</description> @@ -370,7 +398,7 @@ </tagNode> <tagNode name="srv"> <properties> - <help>"SRV" record</help> + <help>SRV record</help> <valueHelp> <format>txt</format> <description>A DNS name relative to the root record</description> @@ -401,7 +429,7 @@ <help>Server hostname</help> <valueHelp> <format>name.example.com</format> - <description>An absolute DNS name</description> + <description>Absolute DNS name</description> </valueHelp> <constraint> <regex>[-_a-zA-Z0-9.]{1,63}(?<!\.)</regex> @@ -454,7 +482,7 @@ </tagNode> <tagNode name="naptr"> <properties> - <help>"NAPTR" record</help> + <help>NAPTR record</help> <valueHelp> <format>txt</format> <description>A DNS name relative to the root record</description> @@ -507,25 +535,25 @@ </leafNode> <leafNode name="lookup-srv"> <properties> - <help>"S" flag</help> + <help>S flag</help> <valueless/> </properties> </leafNode> <leafNode name="lookup-a"> <properties> - <help>"A" flag</help> + <help>A flag</help> <valueless/> </properties> </leafNode> <leafNode name="resolve-uri"> <properties> - <help>"U" flag</help> + <help>U flag</help> <valueless/> </properties> </leafNode> <leafNode name="protocol-specific"> <properties> - <help>"P" flag</help> + <help>P flag</help> <valueless/> </properties> </leafNode> @@ -547,7 +575,7 @@ <help>Replacement DNS name</help> <valueHelp> <format>name.example.com</format> - <description>An absolute DNS name</description> + <description>Absolute DNS name</description> </valueHelp> <constraint> <regex>[-_a-zA-Z0-9.]{1,63}(?<!\.)</regex> diff --git a/interface-definitions/include/bgp/protocol-common-config.xml.i b/interface-definitions/include/bgp/protocol-common-config.xml.i index bcc131f83..527eaf991 100644 --- a/interface-definitions/include/bgp/protocol-common-config.xml.i +++ b/interface-definitions/include/bgp/protocol-common-config.xml.i @@ -810,6 +810,12 @@ </node> </children> </node> + <leafNode name="advertise-all-vni"> + <properties> + <help>Advertise All local VNIs</help> + <valueless/> + </properties> + </leafNode> #include <include/bgp/afi-l2vpn-common.xml.i> <leafNode name="advertise-pip"> <properties> diff --git a/interface-definitions/include/constraint/login-username.xml.i b/interface-definitions/include/constraint/login-username.xml.i new file mode 100644 index 000000000..09a68b796 --- /dev/null +++ b/interface-definitions/include/constraint/login-username.xml.i @@ -0,0 +1,3 @@ +<!-- include start from constraint/login-username.xml.i --> +<regex>[-_a-zA-Z0-9.]{1,100}</regex> +<!-- include end --> diff --git a/interface-definitions/include/static/static-route-bfd.xml.i b/interface-definitions/include/static/static-route-bfd.xml.i new file mode 100644 index 000000000..a05a08d12 --- /dev/null +++ b/interface-definitions/include/static/static-route-bfd.xml.i @@ -0,0 +1,37 @@ +<!-- include start from static/static-route-bfd.xml.i --> +<node name="bfd"> + <properties> + <help>BFD monitoring</help> + </properties> + <children> + #include <include/bfd/profile.xml.i> + <node name="multi-hop"> + <properties> + <help>Use BFD multi hop session</help> + </properties> + <children> + <tagNode name="source"> + <properties> + <help>Use source for BFD session</help> + <valueHelp> + <format>ipv4</format> + <description>IPv4 source address</description> + </valueHelp> + <valueHelp> + <format>ipv6</format> + <description>IPv6 source address</description> + </valueHelp> + <constraint> + <validator name="ipv4-address"/> + <validator name="ipv6-address"/> + </constraint> + </properties> + <children> + #include <include/bfd/profile.xml.i> + </children> + </tagNode> + </children> + </node> + </children> +</node> +<!-- include end -->
\ No newline at end of file diff --git a/interface-definitions/include/static/static-route.xml.i b/interface-definitions/include/static/static-route.xml.i index 268cfa005..29921a731 100644 --- a/interface-definitions/include/static/static-route.xml.i +++ b/interface-definitions/include/static/static-route.xml.i @@ -51,6 +51,7 @@ #include <include/static/static-route-distance.xml.i> #include <include/static/static-route-interface.xml.i> #include <include/static/static-route-vrf.xml.i> + #include <include/static/static-route-bfd.xml.i> </children> </tagNode> </children> diff --git a/interface-definitions/include/static/static-route6.xml.i b/interface-definitions/include/static/static-route6.xml.i index 1f8d54108..a83cc230b 100644 --- a/interface-definitions/include/static/static-route6.xml.i +++ b/interface-definitions/include/static/static-route6.xml.i @@ -50,6 +50,7 @@ #include <include/static/static-route-distance.xml.i> #include <include/static/static-route-interface.xml.i> #include <include/static/static-route-vrf.xml.i> + #include <include/static/static-route-bfd.xml.i> </children> </tagNode> </children> diff --git a/interface-definitions/include/syslog-facility.xml.i b/interface-definitions/include/syslog-facility.xml.i new file mode 100644 index 000000000..57067ece2 --- /dev/null +++ b/interface-definitions/include/syslog-facility.xml.i @@ -0,0 +1,156 @@ +<!-- include start from syslog-facility.xml.i --> +<tagNode name="facility"> + <properties> + <help>Facility for logging</help> + <completionHelp> + <list>auth authpriv cron daemon kern lpr mail mark news protocols security syslog user uucp local0 local1 local2 local3 local4 local5 local6 local7 all</list> + </completionHelp> + <constraint> + <regex>(auth|authpriv|cron|daemon|kern|lpr|mail|mark|news|protocols|security|syslog|user|uucp|local0|local1|local2|local3|local4|local5|local6|local7|all)</regex> + </constraint> + <constraintErrorMessage>Invalid facility type</constraintErrorMessage> + <valueHelp> + <format>all</format> + <description>All facilities excluding "mark"</description> + </valueHelp> + <valueHelp> + <format>auth</format> + <description>Authentication and authorization</description> + </valueHelp> + <valueHelp> + <format>authpriv</format> + <description>Non-system authorization</description> + </valueHelp> + <valueHelp> + <format>cron</format> + <description>Cron daemon</description> + </valueHelp> + <valueHelp> + <format>daemon</format> + <description>System daemons</description> + </valueHelp> + <valueHelp> + <format>kern</format> + <description>Kernel</description> + </valueHelp> + <valueHelp> + <format>lpr</format> + <description>Line printer spooler</description> + </valueHelp> + <valueHelp> + <format>mail</format> + <description>Mail subsystem</description> + </valueHelp> + <valueHelp> + <format>mark</format> + <description>Timestamp</description> + </valueHelp> + <valueHelp> + <format>news</format> + <description>USENET subsystem</description> + </valueHelp> + <valueHelp> + <format>protocols</format> + <description>depricated will be set to local7</description> + </valueHelp> + <valueHelp> + <format>security</format> + <description>depricated will be set to auth</description> + </valueHelp> + <valueHelp> + <format>syslog</format> + <description>Authentication and authorization</description> + </valueHelp> + <valueHelp> + <format>user</format> + <description>Application processes</description> + </valueHelp> + <valueHelp> + <format>uucp</format> + <description>UUCP subsystem</description> + </valueHelp> + <valueHelp> + <format>local0</format> + <description>Local facility 0</description> + </valueHelp> + <valueHelp> + <format>local1</format> + <description>Local facility 1</description> + </valueHelp> + <valueHelp> + <format>local2</format> + <description>Local facility 2</description> + </valueHelp> + <valueHelp> + <format>local3</format> + <description>Local facility 3</description> + </valueHelp> + <valueHelp> + <format>local4</format> + <description>Local facility 4</description> + </valueHelp> + <valueHelp> + <format>local5</format> + <description>Local facility 5</description> + </valueHelp> + <valueHelp> + <format>local6</format> + <description>Local facility 6</description> + </valueHelp> + <valueHelp> + <format>local7</format> + <description>Local facility 7</description> + </valueHelp> + </properties> + <children> + <leafNode name="level"> + <properties> + <help>Logging level</help> + <completionHelp> + <list>emerg alert crit err warning notice info debug all</list> + </completionHelp> + <constraint> + <regex>(emerg|alert|crit|err|warning|notice|info|debug|all)</regex> + </constraint> + <constraintErrorMessage>Invalid loglevel</constraintErrorMessage> + <valueHelp> + <format>emerg</format> + <description>Emergency messages</description> + </valueHelp> + <valueHelp> + <format>alert</format> + <description>Urgent messages</description> + </valueHelp> + <valueHelp> + <format>crit</format> + <description>Critical messages</description> + </valueHelp> + <valueHelp> + <format>err</format> + <description>Error messages</description> + </valueHelp> + <valueHelp> + <format>warning</format> + <description>Warning messages</description> + </valueHelp> + <valueHelp> + <format>notice</format> + <description>Messages for further investigation</description> + </valueHelp> + <valueHelp> + <format>info</format> + <description>Informational messages</description> + </valueHelp> + <valueHelp> + <format>debug</format> + <description>Debug messages</description> + </valueHelp> + <valueHelp> + <format>all</format> + <description>Log everything</description> + </valueHelp> + </properties> + </leafNode> + </children> +</tagNode> +<!-- include end --> diff --git a/interface-definitions/lldp.xml.in b/interface-definitions/lldp.xml.in index b9ffe234c..738bb11c1 100644 --- a/interface-definitions/lldp.xml.in +++ b/interface-definitions/lldp.xml.in @@ -20,7 +20,7 @@ <description>Location data for a specific interface</description> </valueHelp> <completionHelp> - <script>${vyatta_sbindir}/vyatta-interfaces.pl --show all</script> + <script>${vyos_completion_dir}/list_interfaces</script> <list>all</list> </completionHelp> </properties> diff --git a/interface-definitions/load-balancing-wan.xml.in b/interface-definitions/load-balancing-wan.xml.in index c1d7e2c67..3a2c111ac 100644 --- a/interface-definitions/load-balancing-wan.xml.in +++ b/interface-definitions/load-balancing-wan.xml.in @@ -3,6 +3,7 @@ <node name="load-balancing"> <properties> <help>Configure load-balancing</help> + <priority>900</priority> </properties> <children> <node name="wan" owner="${vyos_conf_scripts_dir}/load-balancing-wan.py"> @@ -59,6 +60,7 @@ <validator name="numeric" argument="--range 1-10"/> </constraint> </properties> + <defaultValue>1</defaultValue> </leafNode> <leafNode name="nexthop"> <properties> @@ -91,6 +93,7 @@ <validator name="numeric" argument="--range 1-10"/> </constraint> </properties> + <defaultValue>1</defaultValue> </leafNode> <tagNode name="test"> <properties> @@ -115,6 +118,7 @@ <validator name="numeric" argument="--range 1-30"/> </constraint> </properties> + <defaultValue>5</defaultValue> </leafNode> <leafNode name="target"> <properties> @@ -151,6 +155,7 @@ <validator name="numeric" argument="--range 1-254"/> </constraint> </properties> + <defaultValue>1</defaultValue> </leafNode> <leafNode name="type"> <properties> @@ -242,6 +247,7 @@ </constraint> <constraintErrorMessage>Weight must be between 1 and 255</constraintErrorMessage> </properties> + <defaultValue>1</defaultValue> </leafNode> </children> </tagNode> @@ -261,6 +267,7 @@ <validator name="numeric" argument="--range 0-4294967295"/> </constraint> </properties> + <defaultValue>5</defaultValue> </leafNode> <leafNode name="period"> <properties> @@ -284,6 +291,7 @@ <regex>(hour|minute|second)</regex> </constraint> </properties> + <defaultValue>second</defaultValue> </leafNode> <leafNode name="rate"> <properties> @@ -296,6 +304,7 @@ <validator name="numeric" argument="--range 0-4294967295"/> </constraint> </properties> + <defaultValue>5</defaultValue> </leafNode> <leafNode name="threshold"> <properties> @@ -315,6 +324,7 @@ <regex>(above|below)</regex> </constraint> </properties> + <defaultValue>below</defaultValue> </leafNode> </children> </node> @@ -355,6 +365,7 @@ <validator name="ip-protocol"/> </constraint> </properties> + <defaultValue>all</defaultValue> </leafNode> <node name="source"> <properties> diff --git a/interface-definitions/policy.xml.in b/interface-definitions/policy.xml.in index 7d5fe79ef..02828c4f6 100644 --- a/interface-definitions/policy.xml.in +++ b/interface-definitions/policy.xml.in @@ -971,6 +971,65 @@ </constraint> </properties> </leafNode> + <leafNode name="protocol"> + <properties> + <help>Match protocol via which the route was learnt</help> + <completionHelp> + <list>babel bgp connected isis kernel ospf ospfv3 rip ripng static table vnc</list> + </completionHelp> + <valueHelp> + <format>babel</format> + <description>Babel routing protocol (Babel)</description> + </valueHelp> + <valueHelp> + <format>bgp</format> + <description>Border Gateway Protocol (BGP)</description> + </valueHelp> + <valueHelp> + <format>connected</format> + <description>Connected routes (directly attached subnet or host)</description> + </valueHelp> + <valueHelp> + <format>isis</format> + <description>Intermediate System to Intermediate System (IS-IS)</description> + </valueHelp> + <valueHelp> + <format>kernel</format> + <description>Kernel routes</description> + </valueHelp> + <valueHelp> + <format>ospf</format> + <description>Open Shortest Path First (OSPFv2)</description> + </valueHelp> + <valueHelp> + <format>ospfv3</format> + <description>Open Shortest Path First (IPv6) (OSPFv3)</description> + </valueHelp> + <valueHelp> + <format>rip</format> + <description>Routing Information Protocol (RIP)</description> + </valueHelp> + <valueHelp> + <format>ripng</format> + <description>Routing Information Protocol next-generation (IPv6) (RIPng)</description> + </valueHelp> + <valueHelp> + <format>static</format> + <description>Statically configured routes</description> + </valueHelp> + <valueHelp> + <format>table</format> + <description>Non-main Kernel Routing Table</description> + </valueHelp> + <valueHelp> + <format>vnc</format> + <description>Virtual Network Control (VNC)</description> + </valueHelp> + <constraint> + <regex>(babel|bgp|connected|isis|kernel|ospf|ospfv3|rip|ripng|static|table|vnc)</regex> + </constraint> + </properties> + </leafNode> <leafNode name="rpki"> <properties> <help>Match RPKI validation result</help> diff --git a/interface-definitions/protocols-bgp.xml.in b/interface-definitions/protocols-bgp.xml.in index 78b461f9d..e1a822999 100644 --- a/interface-definitions/protocols-bgp.xml.in +++ b/interface-definitions/protocols-bgp.xml.in @@ -9,20 +9,6 @@ </properties> <children> #include <include/bgp/protocol-common-config.xml.i> - <node name="address-family"> - <children> - <node name="l2vpn-evpn"> - <children> - <leafNode name="advertise-all-vni"> - <properties> - <help>Advertise all local VNIs</help> - <valueless/> - </properties> - </leafNode> - </children> - </node> - </children> - </node> </children> </node> </children> diff --git a/interface-definitions/protocols-failover.xml.in b/interface-definitions/protocols-failover.xml.in index a8c5c717f..c0caec68e 100644 --- a/interface-definitions/protocols-failover.xml.in +++ b/interface-definitions/protocols-failover.xml.in @@ -37,6 +37,26 @@ <help>Check target options</help> </properties> <children> + <leafNode name="policy"> + <properties> + <help>Policy for check targets</help> + <completionHelp> + <list>any-available all-available</list> + </completionHelp> + <valueHelp> + <format>all-available</format> + <description>All targets must be alive</description> + </valueHelp> + <valueHelp> + <format>any-available</format> + <description>Any target must be alive</description> + </valueHelp> + <constraint> + <regex>(all-available|any-available)</regex> + </constraint> + </properties> + <defaultValue>any-available</defaultValue> + </leafNode> #include <include/port-number.xml.i> <leafNode name="target"> <properties> diff --git a/interface-definitions/system-login.xml.in b/interface-definitions/system-login.xml.in index 258913929..be4f53c3b 100644 --- a/interface-definitions/system-login.xml.in +++ b/interface-definitions/system-login.xml.in @@ -12,7 +12,7 @@ <properties> <help>Local user account information</help> <constraint> - <regex>[-_a-zA-Z0-9.]{1,100}</regex> + #include <include/constraint/login-username.xml.i> </constraint> <constraintErrorMessage>Username contains illegal characters or\nexceeds 100 character limitation.</constraintErrorMessage> </properties> diff --git a/interface-definitions/system-syslog.xml.in b/interface-definitions/system-syslog.xml.in index 90c3de5c1..4a2adfd5f 100644 --- a/interface-definitions/system-syslog.xml.in +++ b/interface-definitions/system-syslog.xml.in @@ -11,175 +11,25 @@ <tagNode name="user"> <properties> <help>Logging to specific terminal of given user</help> + <completionHelp> + <path>system login user</path> + </completionHelp> + <valueHelp> + <format>txt</format> + <description>Local user account</description> + </valueHelp> <constraint> - <regex>[a-z_][a-z0-9_-]{1,31}[$]?</regex> + #include <include/constraint/login-username.xml.i> </constraint> <constraintErrorMessage>illegal characters in user</constraintErrorMessage> - <valueHelp> - <format>username</format> - <description>user login name</description> - </valueHelp> </properties> <children> - <tagNode name="facility"> - <properties> - <help>Facility for logging</help> - <completionHelp> - <list>auth authpriv cron daemon kern lpr mail mark news protocols security syslog user uucp local0 local1 local2 local3 local4 local5 local6 local7 all</list> - </completionHelp> - <constraint> - <regex>(auth|authpriv|cron|daemon|kern|lpr|mail|mark|news|protocols|security|syslog|user|uucp|local0|local1|local2|local3|local4|local5|local6|local7|all)</regex> - </constraint> - <constraintErrorMessage>Invalid facility type</constraintErrorMessage> - <valueHelp> - <format>all</format> - <description>All facilities excluding "mark"</description> - </valueHelp> - <valueHelp> - <format>auth</format> - <description>Authentication and authorization</description> - </valueHelp> - <valueHelp> - <format>authpriv</format> - <description>Non-system authorization</description> - </valueHelp> - <valueHelp> - <format>cron</format> - <description>Cron daemon</description> - </valueHelp> - <valueHelp> - <format>daemon</format> - <description>System daemons</description> - </valueHelp> - <valueHelp> - <format>kern</format> - <description>Kernel</description> - </valueHelp> - <valueHelp> - <format>lpr</format> - <description>Line printer spooler</description> - </valueHelp> - <valueHelp> - <format>mail</format> - <description>Mail subsystem</description> - </valueHelp> - <valueHelp> - <format>mark</format> - <description>Timestamp</description> - </valueHelp> - <valueHelp> - <format>news</format> - <description>USENET subsystem</description> - </valueHelp> - <valueHelp> - <format>protocols</format> - <description>depricated will be set to local7</description> - </valueHelp> - <valueHelp> - <format>security</format> - <description>depricated will be set to auth</description> - </valueHelp> - <valueHelp> - <format>syslog</format> - <description>Authentication and authorization</description> - </valueHelp> - <valueHelp> - <format>user</format> - <description>Application processes</description> - </valueHelp> - <valueHelp> - <format>uucp</format> - <description>UUCP subsystem</description> - </valueHelp> - <valueHelp> - <format>local0</format> - <description>Local facility 0</description> - </valueHelp> - <valueHelp> - <format>local1</format> - <description>Local facility 1</description> - </valueHelp> - <valueHelp> - <format>local2</format> - <description>Local facility 2</description> - </valueHelp> - <valueHelp> - <format>local3</format> - <description>Local facility 3</description> - </valueHelp> - <valueHelp> - <format>local4</format> - <description>Local facility 4</description> - </valueHelp> - <valueHelp> - <format>local5</format> - <description>Local facility 5</description> - </valueHelp> - <valueHelp> - <format>local6</format> - <description>Local facility 6</description> - </valueHelp> - <valueHelp> - <format>local7</format> - <description>Local facility 7</description> - </valueHelp> - </properties> - <children> - <leafNode name="level"> - <properties> - <help>Logging level</help> - <completionHelp> - <list>emerg alert crit err warning notice info debug all</list> - </completionHelp> - <constraint> - <regex>(emerg|alert|crit|err|warning|notice|info|debug|all)</regex> - </constraint> - <constraintErrorMessage>Invalid loglevel</constraintErrorMessage> - <valueHelp> - <format>emerg</format> - <description>Emergency messages</description> - </valueHelp> - <valueHelp> - <format>alert</format> - <description>Urgent messages</description> - </valueHelp> - <valueHelp> - <format>crit</format> - <description>Critical messages</description> - </valueHelp> - <valueHelp> - <format>err</format> - <description>Error messages</description> - </valueHelp> - <valueHelp> - <format>warning</format> - <description>Warning messages</description> - </valueHelp> - <valueHelp> - <format>notice</format> - <description>Messages for further investigation</description> - </valueHelp> - <valueHelp> - <format>info</format> - <description>Informational messages</description> - </valueHelp> - <valueHelp> - <format>debug</format> - <description>Debug messages</description> - </valueHelp> - <valueHelp> - <format>all</format> - <description>Log everything</description> - </valueHelp> - </properties> - </leafNode> - </children> - </tagNode> + #include <include/syslog-facility.xml.i> </children> </tagNode> <tagNode name="host"> <properties> - <help>Logging to a remote host</help> + <help>Logging to remote host</help> <constraint> <validator name="ip-address"/> <validator name="fqdn"/> @@ -190,186 +40,17 @@ <description>Remote syslog server IPv4 address</description> </valueHelp> <valueHelp> + <format>ipv6</format> + <description>Remote syslog server IPv6 address</description> + </valueHelp> + <valueHelp> <format>hostname</format> <description>Remote syslog server FQDN</description> </valueHelp> </properties> <children> #include <include/port-number.xml.i> - <tagNode name="facility"> - <properties> - <help>Facility for logging</help> - <completionHelp> - <list>auth authpriv cron daemon kern lpr mail mark news protocols security syslog user uucp local0 local1 local2 local3 local4 local5 local6 local7 all</list> - </completionHelp> - <constraint> - <regex>(auth|authpriv|cron|daemon|kern|lpr|mail|mark|news|protocols|security|syslog|user|uucp|local0|local1|local2|local3|local4|local5|local6|local7|all)</regex> - </constraint> - <constraintErrorMessage>Invalid facility type</constraintErrorMessage> - <valueHelp> - <format>all</format> - <description>All facilities excluding "mark"</description> - </valueHelp> - <valueHelp> - <format>auth</format> - <description>Authentication and authorization</description> - </valueHelp> - <valueHelp> - <format>authpriv</format> - <description>Non-system authorization</description> - </valueHelp> - <valueHelp> - <format>cron</format> - <description>Cron daemon</description> - </valueHelp> - <valueHelp> - <format>daemon</format> - <description>System daemons</description> - </valueHelp> - <valueHelp> - <format>kern</format> - <description>Kernel</description> - </valueHelp> - <valueHelp> - <format>lpr</format> - <description>Line printer spooler</description> - </valueHelp> - <valueHelp> - <format>mail</format> - <description>Mail subsystem</description> - </valueHelp> - <valueHelp> - <format>mark</format> - <description>Timestamp</description> - </valueHelp> - <valueHelp> - <format>news</format> - <description>USENET subsystem</description> - </valueHelp> - <valueHelp> - <format>protocols</format> - <description>depricated will be set to local7</description> - </valueHelp> - <valueHelp> - <format>security</format> - <description>depricated will be set to auth</description> - </valueHelp> - <valueHelp> - <format>syslog</format> - <description>Authentication and authorization</description> - </valueHelp> - <valueHelp> - <format>user</format> - <description>Application processes</description> - </valueHelp> - <valueHelp> - <format>uucp</format> - <description>UUCP subsystem</description> - </valueHelp> - <valueHelp> - <format>local0</format> - <description>Local facility 0</description> - </valueHelp> - <valueHelp> - <format>local1</format> - <description>Local facility 1</description> - </valueHelp> - <valueHelp> - <format>local2</format> - <description>Local facility 2</description> - </valueHelp> - <valueHelp> - <format>local3</format> - <description>Local facility 3</description> - </valueHelp> - <valueHelp> - <format>local4</format> - <description>Local facility 4</description> - </valueHelp> - <valueHelp> - <format>local5</format> - <description>Local facility 5</description> - </valueHelp> - <valueHelp> - <format>local6</format> - <description>Local facility 6</description> - </valueHelp> - <valueHelp> - <format>local7</format> - <description>Local facility 7</description> - </valueHelp> - </properties> - <children> - <leafNode name="protocol"> - <properties> - <help>syslog communication protocol</help> - <valueHelp> - <format>udp</format> - <description>send log messages to remote syslog server over udp</description> - </valueHelp> - <valueHelp> - <format>tcp</format> - <description>send log messages to remote syslog server over tcp</description> - </valueHelp> - <completionHelp> - <list>udp tcp</list> - </completionHelp> - <constraint> - <regex>(udp|tcp)</regex> - </constraint> - <constraintErrorMessage>invalid protocol name</constraintErrorMessage> - </properties> - </leafNode> - <leafNode name="level"> - <properties> - <help>Logging level</help> - <completionHelp> - <list>emerg alert crit err warning notice info debug all</list> - </completionHelp> - <constraint> - <regex>(emerg|alert|crit|err|warning|notice|info|debug|all)</regex> - </constraint> - <constraintErrorMessage>Invalid loglevel</constraintErrorMessage> - <valueHelp> - <format>emerg</format> - <description>Emergency messages</description> - </valueHelp> - <valueHelp> - <format>alert</format> - <description>Urgent messages</description> - </valueHelp> - <valueHelp> - <format>crit</format> - <description>Critical messages</description> - </valueHelp> - <valueHelp> - <format>err</format> - <description>Error messages</description> - </valueHelp> - <valueHelp> - <format>warning</format> - <description>Warning messages</description> - </valueHelp> - <valueHelp> - <format>notice</format> - <description>Messages for further investigation</description> - </valueHelp> - <valueHelp> - <format>info</format> - <description>Informational messages</description> - </valueHelp> - <valueHelp> - <format>debug</format> - <description>Debug messages</description> - </valueHelp> - <valueHelp> - <format>all</format> - <description>Log everything</description> - </valueHelp> - </properties> - </leafNode> - </children> - </tagNode> + #include <include/syslog-facility.xml.i> <node name="format"> <properties> <help>Logging format</help> @@ -390,160 +71,7 @@ <help>Logging to system standard location</help> </properties> <children> - <tagNode name="facility"> - <properties> - <help>Facility for logging</help> - <completionHelp> - <list>auth authpriv cron daemon kern lpr mail mark news protocols security syslog user uucp local0 local1 local2 local3 local4 local5 local6 local7 all</list> - </completionHelp> - <constraint> - <regex>(auth|authpriv|cron|daemon|kern|lpr|mail|mark|news|protocols|security|syslog|user|uucp|local0|local1|local2|local3|local4|local5|local6|local7|all)</regex> - </constraint> - <constraintErrorMessage>Invalid facility type</constraintErrorMessage> - <valueHelp> - <format>all</format> - <description>All facilities excluding "mark"</description> - </valueHelp> - <valueHelp> - <format>auth</format> - <description>Authentication and authorization</description> - </valueHelp> - <valueHelp> - <format>authpriv</format> - <description>Non-system authorization</description> - </valueHelp> - <valueHelp> - <format>cron</format> - <description>Cron daemon</description> - </valueHelp> - <valueHelp> - <format>daemon</format> - <description>System daemons</description> - </valueHelp> - <valueHelp> - <format>kern</format> - <description>Kernel</description> - </valueHelp> - <valueHelp> - <format>lpr</format> - <description>Line printer spooler</description> - </valueHelp> - <valueHelp> - <format>mail</format> - <description>Mail subsystem</description> - </valueHelp> - <valueHelp> - <format>mark</format> - <description>Timestamp</description> - </valueHelp> - <valueHelp> - <format>news</format> - <description>USENET subsystem</description> - </valueHelp> - <valueHelp> - <format>protocols</format> - <description>depricated will be set to local7</description> - </valueHelp> - <valueHelp> - <format>security</format> - <description>depricated will be set to auth</description> - </valueHelp> - <valueHelp> - <format>syslog</format> - <description>Authentication and authorization</description> - </valueHelp> - <valueHelp> - <format>user</format> - <description>Application processes</description> - </valueHelp> - <valueHelp> - <format>uucp</format> - <description>UUCP subsystem</description> - </valueHelp> - <valueHelp> - <format>local0</format> - <description>Local facility 0</description> - </valueHelp> - <valueHelp> - <format>local1</format> - <description>Local facility 1</description> - </valueHelp> - <valueHelp> - <format>local2</format> - <description>Local facility 2</description> - </valueHelp> - <valueHelp> - <format>local3</format> - <description>Local facility 3</description> - </valueHelp> - <valueHelp> - <format>local4</format> - <description>Local facility 4</description> - </valueHelp> - <valueHelp> - <format>local5</format> - <description>Local facility 5</description> - </valueHelp> - <valueHelp> - <format>local6</format> - <description>Local facility 6</description> - </valueHelp> - <valueHelp> - <format>local7</format> - <description>Local facility 7</description> - </valueHelp> - </properties> - <children> - <leafNode name="level"> - <properties> - <help>Logging level</help> - <completionHelp> - <list>emerg alert crit err warning notice info debug all</list> - </completionHelp> - <constraint> - <regex>(emerg|alert|crit|err|warning|notice|info|debug|all)</regex> - </constraint> - <constraintErrorMessage>Invalid loglevel</constraintErrorMessage> - <valueHelp> - <format>emerg</format> - <description>Emergency messages</description> - </valueHelp> - <valueHelp> - <format>alert</format> - <description>Urgent messages</description> - </valueHelp> - <valueHelp> - <format>crit</format> - <description>Critical messages</description> - </valueHelp> - <valueHelp> - <format>err</format> - <description>Error messages</description> - </valueHelp> - <valueHelp> - <format>warning</format> - <description>Warning messages</description> - </valueHelp> - <valueHelp> - <format>notice</format> - <description>Messages for further investigation</description> - </valueHelp> - <valueHelp> - <format>info</format> - <description>Informational messages</description> - </valueHelp> - <valueHelp> - <format>debug</format> - <description>Debug messages</description> - </valueHelp> - <valueHelp> - <format>all</format> - <description>Log everything</description> - </valueHelp> - </properties> - </leafNode> - </children> - </tagNode> + #include <include/syslog-facility.xml.i> <node name="marker"> <properties> <help>mark messages sent to syslog</help> @@ -559,10 +87,10 @@ </leafNode> </children> </node> - <leafNode name ="preserve-fqdn"> + <leafNode name="preserve-fqdn"> <properties> <help>uses FQDN for logging</help> - <valueless /> + <valueless/> </properties> </leafNode> </children> @@ -601,160 +129,7 @@ </leafNode> </children> </node> - <tagNode name="facility"> - <properties> - <help>Facility for logging</help> - <completionHelp> - <list>auth authpriv cron daemon kern lpr mail mark news protocols security syslog user uucp local0 local1 local2 local3 local4 local5 local6 local7 all</list> - </completionHelp> - <constraint> - <regex>(auth|authpriv|cron|daemon|kern|lpr|mail|mark|news|protocols|security|syslog|user|uucp|local0|local1|local2|local3|local4|local5|local6|local7|all)</regex> - </constraint> - <constraintErrorMessage>Invalid facility type</constraintErrorMessage> - <valueHelp> - <format>all</format> - <description>All facilities excluding "mark"</description> - </valueHelp> - <valueHelp> - <format>auth</format> - <description>Authentication and authorization</description> - </valueHelp> - <valueHelp> - <format>authpriv</format> - <description>Non-system authorization</description> - </valueHelp> - <valueHelp> - <format>cron</format> - <description>Cron daemon</description> - </valueHelp> - <valueHelp> - <format>daemon</format> - <description>System daemons</description> - </valueHelp> - <valueHelp> - <format>kern</format> - <description>Kernel</description> - </valueHelp> - <valueHelp> - <format>lpr</format> - <description>Line printer spooler</description> - </valueHelp> - <valueHelp> - <format>mail</format> - <description>Mail subsystem</description> - </valueHelp> - <valueHelp> - <format>mark</format> - <description>Timestamp</description> - </valueHelp> - <valueHelp> - <format>news</format> - <description>USENET subsystem</description> - </valueHelp> - <valueHelp> - <format>protocols</format> - <description>depricated will be set to local7</description> - </valueHelp> - <valueHelp> - <format>security</format> - <description>depricated will be set to auth</description> - </valueHelp> - <valueHelp> - <format>syslog</format> - <description>Authentication and authorization</description> - </valueHelp> - <valueHelp> - <format>user</format> - <description>Application processes</description> - </valueHelp> - <valueHelp> - <format>uucp</format> - <description>UUCP subsystem</description> - </valueHelp> - <valueHelp> - <format>local0</format> - <description>Local facility 0</description> - </valueHelp> - <valueHelp> - <format>local1</format> - <description>Local facility 1</description> - </valueHelp> - <valueHelp> - <format>local2</format> - <description>Local facility 2</description> - </valueHelp> - <valueHelp> - <format>local3</format> - <description>Local facility 3</description> - </valueHelp> - <valueHelp> - <format>local4</format> - <description>Local facility 4</description> - </valueHelp> - <valueHelp> - <format>local5</format> - <description>Local facility 5</description> - </valueHelp> - <valueHelp> - <format>local6</format> - <description>Local facility 6</description> - </valueHelp> - <valueHelp> - <format>local7</format> - <description>Local facility 7</description> - </valueHelp> - </properties> - <children> - <leafNode name="level"> - <properties> - <help>Logging level</help> - <completionHelp> - <list>emerg alert crit err warning notice info debug all</list> - </completionHelp> - <constraint> - <regex>(emerg|alert|crit|err|warning|notice|info|debug|all)</regex> - </constraint> - <constraintErrorMessage>Invalid loglevel</constraintErrorMessage> - <valueHelp> - <format>emerg</format> - <description>Emergency messages</description> - </valueHelp> - <valueHelp> - <format>alert</format> - <description>Urgent messages</description> - </valueHelp> - <valueHelp> - <format>crit</format> - <description>Critical messages</description> - </valueHelp> - <valueHelp> - <format>err</format> - <description>Error messages</description> - </valueHelp> - <valueHelp> - <format>warning</format> - <description>Warning messages</description> - </valueHelp> - <valueHelp> - <format>notice</format> - <description>Messages for further investigation</description> - </valueHelp> - <valueHelp> - <format>info</format> - <description>Informational messages</description> - </valueHelp> - <valueHelp> - <format>debug</format> - <description>Debug messages</description> - </valueHelp> - <valueHelp> - <format>all</format> - <description>Log everything</description> - </valueHelp> - </properties> - </leafNode> - </children> - </tagNode> + #include <include/syslog-facility.xml.i> </children> </tagNode> <node name="console"> @@ -762,160 +137,7 @@ <help>logging to serial console</help> </properties> <children> - <tagNode name="facility"> - <properties> - <help>Facility for logging</help> - <completionHelp> - <list>auth authpriv cron daemon kern lpr mail mark news protocols security syslog user uucp local0 local1 local2 local3 local4 local5 local6 local7 all</list> - </completionHelp> - <constraint> - <regex>(auth|authpriv|cron|daemon|kern|lpr|mail|mark|news|protocols|security|syslog|user|uucp|local0|local1|local2|local3|local4|local5|local6|local7|all)</regex> - </constraint> - <constraintErrorMessage>Invalid facility type</constraintErrorMessage> - <valueHelp> - <format>all</format> - <description>All facilities excluding "mark"</description> - </valueHelp> - <valueHelp> - <format>auth</format> - <description>Authentication and authorization</description> - </valueHelp> - <valueHelp> - <format>authpriv</format> - <description>Non-system authorization</description> - </valueHelp> - <valueHelp> - <format>cron</format> - <description>Cron daemon</description> - </valueHelp> - <valueHelp> - <format>daemon</format> - <description>System daemons</description> - </valueHelp> - <valueHelp> - <format>kern</format> - <description>Kernel</description> - </valueHelp> - <valueHelp> - <format>lpr</format> - <description>Line printer spooler</description> - </valueHelp> - <valueHelp> - <format>mail</format> - <description>Mail subsystem</description> - </valueHelp> - <valueHelp> - <format>mark</format> - <description>Timestamp</description> - </valueHelp> - <valueHelp> - <format>news</format> - <description>USENET subsystem</description> - </valueHelp> - <valueHelp> - <format>protocols</format> - <description>depricated will be set to local7</description> - </valueHelp> - <valueHelp> - <format>security</format> - <description>depricated will be set to auth</description> - </valueHelp> - <valueHelp> - <format>syslog</format> - <description>Authentication and authorization</description> - </valueHelp> - <valueHelp> - <format>user</format> - <description>Application processes</description> - </valueHelp> - <valueHelp> - <format>uucp</format> - <description>UUCP subsystem</description> - </valueHelp> - <valueHelp> - <format>local0</format> - <description>Local facility 0</description> - </valueHelp> - <valueHelp> - <format>local1</format> - <description>Local facility 1</description> - </valueHelp> - <valueHelp> - <format>local2</format> - <description>Local facility 2</description> - </valueHelp> - <valueHelp> - <format>local3</format> - <description>Local facility 3</description> - </valueHelp> - <valueHelp> - <format>local4</format> - <description>Local facility 4</description> - </valueHelp> - <valueHelp> - <format>local5</format> - <description>Local facility 5</description> - </valueHelp> - <valueHelp> - <format>local6</format> - <description>Local facility 6</description> - </valueHelp> - <valueHelp> - <format>local7</format> - <description>Local facility 7</description> - </valueHelp> - </properties> - <children> - <leafNode name="level"> - <properties> - <help>Logging level</help> - <completionHelp> - <list>emerg alert crit err warning notice info debug all</list> - </completionHelp> - <constraint> - <regex>(emerg|alert|crit|err|warning|notice|info|debug|all)</regex> - </constraint> - <constraintErrorMessage>Invalid loglevel</constraintErrorMessage> - <valueHelp> - <format>emerg</format> - <description>Emergency messages</description> - </valueHelp> - <valueHelp> - <format>alert</format> - <description>Urgent messages</description> - </valueHelp> - <valueHelp> - <format>crit</format> - <description>Critical messages</description> - </valueHelp> - <valueHelp> - <format>err</format> - <description>Error messages</description> - </valueHelp> - <valueHelp> - <format>warning</format> - <description>Warning messages</description> - </valueHelp> - <valueHelp> - <format>notice</format> - <description>Messages for further investigation</description> - </valueHelp> - <valueHelp> - <format>info</format> - <description>Informational messages</description> - </valueHelp> - <valueHelp> - <format>debug</format> - <description>Debug messages</description> - </valueHelp> - <valueHelp> - <format>all</format> - <description>Log everything</description> - </valueHelp> - </properties> - </leafNode> - </children> - </tagNode> + #include <include/syslog-facility.xml.i> </children> </node> </children> diff --git a/interface-definitions/vrf.xml.in b/interface-definitions/vrf.xml.in index a7efe146a..3783785ce 100644 --- a/interface-definitions/vrf.xml.in +++ b/interface-definitions/vrf.xml.in @@ -121,7 +121,20 @@ <constraintErrorMessage>VRF routing table must be in range from 100 to 65535</constraintErrorMessage> </properties> </leafNode> - #include <include/vni.xml.i> + <leafNode name="vni" owner="${vyos_conf_scripts_dir}/vrf_vni.py $VAR(../@)"> + <properties> + <help>Virtual Network Identifier</help> + <!-- must be after BGP to keep correct order when removing L3VNIs in FRR --> + <priority>822</priority> + <valueHelp> + <format>u32:0-16777214</format> + <description>VXLAN virtual network identifier</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 0-16777214"/> + </constraint> + </properties> + </leafNode> </children> </tagNode> </children> diff --git a/op-mode-definitions/conntrack-sync.xml.in b/op-mode-definitions/conntrack-sync.xml.in index 3e29ecd39..a66331f27 100644 --- a/op-mode-definitions/conntrack-sync.xml.in +++ b/op-mode-definitions/conntrack-sync.xml.in @@ -11,13 +11,13 @@ <properties> <help>Reset external cache and request resync with other systems</help> </properties> - <command>sudo ${vyos_op_scripts_dir}/conntrack_sync.py --reset-cache-external</command> + <command>sudo ${vyos_op_scripts_dir}/conntrack_sync.py reset_external_cache</command> </leafNode> <leafNode name="internal-cache"> <properties> <help>Reset internal cache and request resync with other systems</help> </properties> - <command>sudo ${vyos_op_scripts_dir}/conntrack_sync.py --reset-cache-internal</command> + <command>sudo ${vyos_op_scripts_dir}/conntrack_sync.py reset_internal_cache</command> </leafNode> </children> </node> @@ -27,9 +27,9 @@ <children> <leafNode name="conntrack-sync"> <properties> - <help>Restart connection tracking synchronization service</help> + <help>Restart the connection tracking synchronization service</help> </properties> - <command>sudo ${vyos_op_scripts_dir}/conntrack_sync.py --restart</command> + <command>sudo ${vyos_op_scripts_dir}/conntrack_sync.py restart</command> </leafNode> </children> </node> @@ -49,19 +49,19 @@ <properties> <help>Show external connection tracking cache entries</help> </properties> - <command>sudo ${vyos_op_scripts_dir}/conntrack_sync.py --show-external; ${vyos_op_scripts_dir}/conntrack_sync.py --show-external-expect</command> + <command>sudo ${vyos_op_scripts_dir}/conntrack_sync.py show_external_cache</command> <children> <leafNode name="main"> <properties> <help>Show external main connection tracking cache entries</help> </properties> - <command>sudo ${vyos_op_scripts_dir}/conntrack_sync.py --show-external</command> + <command>sudo ${vyos_op_scripts_dir}/conntrack_sync.py show_external_cache</command> </leafNode> <leafNode name="expect"> <properties> <help>Show external expect connection tracking cache entries</help> </properties> - <command>sudo ${vyos_op_scripts_dir}/conntrack_sync.py --show-external-expect</command> + <command>sudo ${vyos_op_scripts_dir}/conntrack_sync.py show_external_expect</command> </leafNode> </children> </node> @@ -69,19 +69,19 @@ <properties> <help>Show internal connection tracking cache entries</help> </properties> - <command>sudo ${vyos_op_scripts_dir}/conntrack_sync.py --show-internal; ${vyos_op_scripts_dir}/conntrack_sync.py --show-internal-expect</command> + <command>sudo ${vyos_op_scripts_dir}/conntrack_sync.py show_internal_cache</command> <children> <leafNode name="main"> <properties> <help>Show internal main connection tracking cache entries</help> </properties> - <command>sudo ${vyos_op_scripts_dir}/conntrack_sync.py --show-internal</command> + <command>sudo ${vyos_op_scripts_dir}/conntrack_sync.py show_internal_cache</command> </leafNode> <leafNode name="expect"> <properties> <help>Show internal expect connection tracking cache entries</help> </properties> - <command>sudo ${vyos_op_scripts_dir}/conntrack_sync.py --show-internal-expect</command> + <command>sudo ${vyos_op_scripts_dir}/conntrack_sync.py show_internal_expect</command> </leafNode> </children> </node> @@ -91,13 +91,13 @@ <properties> <help>Show connection syncing statistics</help> </properties> - <command>sudo ${vyos_op_scripts_dir}/conntrack_sync.py --show-statistics</command> + <command>sudo ${vyos_op_scripts_dir}/conntrack_sync.py show_statistics</command> </leafNode> <leafNode name="status"> <properties> <help>Show conntrack-sync status</help> </properties> - <command>sudo ${vyos_op_scripts_dir}/conntrack_sync.py --show-status</command> + <command>sudo ${vyos_op_scripts_dir}/conntrack_sync.py show_status</command> </leafNode> </children> </node> diff --git a/op-mode-definitions/counters.xml.in b/op-mode-definitions/counters.xml.in index 4bf08d201..f563cb9a0 100644 --- a/op-mode-definitions/counters.xml.in +++ b/op-mode-definitions/counters.xml.in @@ -19,7 +19,7 @@ <properties> <help>Clear all bonding interface counters</help> </properties> - <command>sudo ${vyos_op_scripts_dir}/interfaces.py clear_counters --intf_type "$3"</command> + <command>sudo ${vyos_op_scripts_dir}/interfaces.py clear_counters --intf-type "$3"</command> </node> </children> </node> @@ -35,7 +35,7 @@ <properties> <help>Clear interface counters for a given bonding interface</help> </properties> - <command>sudo ${vyos_op_scripts_dir}/interfaces.py clear_counters --intf_name "$4"</command> + <command>sudo ${vyos_op_scripts_dir}/interfaces.py clear_counters --intf-name "$4"</command> </leafNode> </children> </tagNode> @@ -48,7 +48,7 @@ <properties> <help>Clear all bridge interface counters</help> </properties> - <command>sudo ${vyos_op_scripts_dir}/interfaces.py clear_counters --intf_type "$3"</command> + <command>sudo ${vyos_op_scripts_dir}/interfaces.py clear_counters --intf-type "$3"</command> </node> </children> </node> @@ -64,7 +64,7 @@ <properties> <help>Clear interface counters for a given bridge interface</help> </properties> - <command>sudo ${vyos_op_scripts_dir}/interfaces.py clear_counters --intf_name "$4"</command> + <command>sudo ${vyos_op_scripts_dir}/interfaces.py clear_counters --intf-name "$4"</command> </leafNode> </children> </tagNode> @@ -77,7 +77,7 @@ <properties> <help>Clear all dummy interface counters</help> </properties> - <command>sudo ${vyos_op_scripts_dir}/interfaces.py clear_counters --intf_type "$3"</command> + <command>sudo ${vyos_op_scripts_dir}/interfaces.py clear_counters --intf-type "$3"</command> </node> </children> </node> @@ -93,7 +93,7 @@ <properties> <help>Clear interface counters for a given dummy interface</help> </properties> - <command>sudo ${vyos_op_scripts_dir}/interfaces.py clear_counters --intf_name "$4"</command> + <command>sudo ${vyos_op_scripts_dir}/interfaces.py clear_counters --intf-name "$4"</command> </leafNode> </children> </tagNode> @@ -106,7 +106,7 @@ <properties> <help>Clear all ethernet interface counters</help> </properties> - <command>sudo ${vyos_op_scripts_dir}/interfaces.py clear_counters --intf_type "$3"</command> + <command>sudo ${vyos_op_scripts_dir}/interfaces.py clear_counters --intf-type "$3"</command> </node> </children> </node> @@ -122,7 +122,7 @@ <properties> <help>Clear interface counters for a given ethernet interface</help> </properties> - <command>sudo ${vyos_op_scripts_dir}/interfaces.py clear_counters --intf_name "$4"</command> + <command>sudo ${vyos_op_scripts_dir}/interfaces.py clear_counters --intf-name "$4"</command> </leafNode> </children> </tagNode> @@ -135,7 +135,7 @@ <properties> <help>Clear all GENEVE interface counters</help> </properties> - <command>sudo ${vyos_op_scripts_dir}/interfaces.py clear_counters --intf_type "$3"</command> + <command>sudo ${vyos_op_scripts_dir}/interfaces.py clear_counters --intf-type "$3"</command> </node> </children> </node> @@ -151,7 +151,7 @@ <properties> <help>Clear interface counters for a given GENEVE interface</help> </properties> - <command>sudo ${vyos_op_scripts_dir}/interfaces.py clear_counters --intf_name "$4"</command> + <command>sudo ${vyos_op_scripts_dir}/interfaces.py clear_counters --intf-name "$4"</command> </leafNode> </children> </tagNode> @@ -164,7 +164,7 @@ <properties> <help>Clear all Input interface counters</help> </properties> - <command>sudo ${vyos_op_scripts_dir}/interfaces.py clear_counters --intf_type "$3"</command> + <command>sudo ${vyos_op_scripts_dir}/interfaces.py clear_counters --intf-type "$3"</command> </node> </children> </node> @@ -180,7 +180,7 @@ <properties> <help>Clear interface counters for a given Input interface</help> </properties> - <command>sudo ${vyos_op_scripts_dir}/interfaces.py clear_counters --intf_name "$4"</command> + <command>sudo ${vyos_op_scripts_dir}/interfaces.py clear_counters --intf-name "$4"</command> </leafNode> </children> </tagNode> @@ -193,7 +193,7 @@ <properties> <help>Clear all L2TPv3 interface counters</help> </properties> - <command>sudo ${vyos_op_scripts_dir}/interfaces.py clear_counters --intf_type "$3"</command> + <command>sudo ${vyos_op_scripts_dir}/interfaces.py clear_counters --intf-type "$3"</command> </node> </children> </node> @@ -209,7 +209,7 @@ <properties> <help>Clear interface counters for a given L2TPv3 interface</help> </properties> - <command>sudo ${vyos_op_scripts_dir}/interfaces.py clear_counters --intf_name "$4"</command> + <command>sudo ${vyos_op_scripts_dir}/interfaces.py clear_counters --intf-name "$4"</command> </leafNode> </children> </tagNode> @@ -222,7 +222,7 @@ <properties> <help>Clear all loopback interface counters</help> </properties> - <command>sudo ${vyos_op_scripts_dir}/interfaces.py clear_counters --intf_type "$3"</command> + <command>sudo ${vyos_op_scripts_dir}/interfaces.py clear_counters --intf-type "$3"</command> </node> </children> </node> @@ -238,7 +238,7 @@ <properties> <help>Clear interface counters for a given loopback interface</help> </properties> - <command>sudo ${vyos_op_scripts_dir}/interfaces.py clear_counters --intf_name "$4"</command> + <command>sudo ${vyos_op_scripts_dir}/interfaces.py clear_counters --intf-name "$4"</command> </leafNode> </children> </tagNode> @@ -251,7 +251,7 @@ <properties> <help>Clear all MACsec interface counters</help> </properties> - <command>sudo ${vyos_op_scripts_dir}/interfaces.py clear_counters --intf_type "$3"</command> + <command>sudo ${vyos_op_scripts_dir}/interfaces.py clear_counters --intf-type "$3"</command> </node> </children> </node> @@ -267,7 +267,7 @@ <properties> <help>Clear interface counters for a given MACsec interface</help> </properties> - <command>sudo ${vyos_op_scripts_dir}/interfaces.py clear_counters --intf_name "$4"</command> + <command>sudo ${vyos_op_scripts_dir}/interfaces.py clear_counters --intf-name "$4"</command> </leafNode> </children> </tagNode> @@ -280,7 +280,7 @@ <properties> <help>Clear all OpenVPN interface counters</help> </properties> - <command>sudo ${vyos_op_scripts_dir}/interfaces.py clear_counters --intf_type "$3"</command> + <command>sudo ${vyos_op_scripts_dir}/interfaces.py clear_counters --intf-type "$3"</command> </node> </children> </node> @@ -296,7 +296,7 @@ <properties> <help>Clear interface counters for a given OpenVPN interface</help> </properties> - <command>sudo ${vyos_op_scripts_dir}/interfaces.py clear_counters --intf_name "$4"</command> + <command>sudo ${vyos_op_scripts_dir}/interfaces.py clear_counters --intf-name "$4"</command> </leafNode> </children> </tagNode> @@ -309,7 +309,7 @@ <properties> <help>Clear all PPPoE interface counters</help> </properties> - <command>sudo ${vyos_op_scripts_dir}/interfaces.py clear_counters --intf_type "$3"</command> + <command>sudo ${vyos_op_scripts_dir}/interfaces.py clear_counters --intf-type "$3"</command> </node> </children> </node> @@ -325,7 +325,7 @@ <properties> <help>Clear interface counters for a given PPPoE interface</help> </properties> - <command>sudo ${vyos_op_scripts_dir}/interfaces.py clear_counters --intf_name "$4"</command> + <command>sudo ${vyos_op_scripts_dir}/interfaces.py clear_counters --intf-name "$4"</command> </leafNode> </children> </tagNode> @@ -338,7 +338,7 @@ <properties> <help>Clear all Pseudo-Ethernet interface counters</help> </properties> - <command>sudo ${vyos_op_scripts_dir}/interfaces.py clear_counters --intf_type "$3"</command> + <command>sudo ${vyos_op_scripts_dir}/interfaces.py clear_counters --intf-type "$3"</command> </node> </children> </node> @@ -354,7 +354,7 @@ <properties> <help>Clear interface counters for a given Pseudo-Ethernet interface</help> </properties> - <command>sudo ${vyos_op_scripts_dir}/interfaces.py clear_counters --intf_name "$4"</command> + <command>sudo ${vyos_op_scripts_dir}/interfaces.py clear_counters --intf-name "$4"</command> </leafNode> </children> </tagNode> @@ -367,7 +367,7 @@ <properties> <help>Clear all SSTP interface counters</help> </properties> - <command>sudo ${vyos_op_scripts_dir}/interfaces.py clear_counters --intf_type "$3"</command> + <command>sudo ${vyos_op_scripts_dir}/interfaces.py clear_counters --intf-type "$3"</command> </node> </children> </node> @@ -383,7 +383,7 @@ <properties> <help>Clear interface counters for a given SSTP interface</help> </properties> - <command>sudo ${vyos_op_scripts_dir}/interfaces.py clear_counters --intf_name "$4"</command> + <command>sudo ${vyos_op_scripts_dir}/interfaces.py clear_counters --intf-name "$4"</command> </leafNode> </children> </tagNode> @@ -396,7 +396,7 @@ <properties> <help>Clear all tunnel interface counters</help> </properties> - <command>sudo ${vyos_op_scripts_dir}/interfaces.py clear_counters --intf_type "$3"</command> + <command>sudo ${vyos_op_scripts_dir}/interfaces.py clear_counters --intf-type "$3"</command> </node> </children> </node> @@ -412,7 +412,7 @@ <properties> <help>Clear interface counters for a given tunnel interface</help> </properties> - <command>sudo ${vyos_op_scripts_dir}/interfaces.py clear_counters --intf_name "$4"</command> + <command>sudo ${vyos_op_scripts_dir}/interfaces.py clear_counters --intf-name "$4"</command> </leafNode> </children> </tagNode> @@ -425,7 +425,7 @@ <properties> <help>Clear all virtual-ethernet interface counters</help> </properties> - <command>sudo ${vyos_op_scripts_dir}/interfaces.py clear_counters --intf_type "$3"</command> + <command>sudo ${vyos_op_scripts_dir}/interfaces.py clear_counters --intf-type "$3"</command> </node> </children> </node> @@ -441,7 +441,7 @@ <properties> <help>Clear interface counters for a given virtual-ethernet interface</help> </properties> - <command>sudo ${vyos_op_scripts_dir}/interfaces.py clear_counters --intf_name "$4"</command> + <command>sudo ${vyos_op_scripts_dir}/interfaces.py clear_counters --intf-name "$4"</command> </leafNode> </children> </tagNode> @@ -454,7 +454,7 @@ <properties> <help>Clear all VTI interface counters</help> </properties> - <command>sudo ${vyos_op_scripts_dir}/interfaces.py clear_counters --intf_type "$3"</command> + <command>sudo ${vyos_op_scripts_dir}/interfaces.py clear_counters --intf-type "$3"</command> </node> </children> </node> @@ -470,7 +470,7 @@ <properties> <help>Clear interface counters for a given VTI interface</help> </properties> - <command>sudo ${vyos_op_scripts_dir}/interfaces.py clear_counters --intf_name "$4"</command> + <command>sudo ${vyos_op_scripts_dir}/interfaces.py clear_counters --intf-name "$4"</command> </leafNode> </children> </tagNode> @@ -483,7 +483,7 @@ <properties> <help>Clear all VXLAN interface counters</help> </properties> - <command>sudo ${vyos_op_scripts_dir}/interfaces.py clear_counters --intf_type "$3"</command> + <command>sudo ${vyos_op_scripts_dir}/interfaces.py clear_counters --intf-type "$3"</command> </node> </children> </node> @@ -499,7 +499,7 @@ <properties> <help>Clear interface counters for a given VXLAN interface</help> </properties> - <command>sudo ${vyos_op_scripts_dir}/interfaces.py clear_counters --intf_name "$4"</command> + <command>sudo ${vyos_op_scripts_dir}/interfaces.py clear_counters --intf-name "$4"</command> </leafNode> </children> </tagNode> @@ -512,7 +512,7 @@ <properties> <help>Clear all Wireguard interface counters</help> </properties> - <command>sudo ${vyos_op_scripts_dir}/interfaces.py clear_counters --intf_type "$3"</command> + <command>sudo ${vyos_op_scripts_dir}/interfaces.py clear_counters --intf-type "$3"</command> </node> </children> </node> @@ -528,7 +528,7 @@ <properties> <help>Clear interface counters for a given Wireguard interface</help> </properties> - <command>sudo ${vyos_op_scripts_dir}/interfaces.py clear_counters --intf_name "$4"</command> + <command>sudo ${vyos_op_scripts_dir}/interfaces.py clear_counters --intf-name "$4"</command> </leafNode> </children> </tagNode> @@ -541,7 +541,7 @@ <properties> <help>Clear all wireless interface counters</help> </properties> - <command>sudo ${vyos_op_scripts_dir}/interfaces.py clear_counters --intf_type "$3"</command> + <command>sudo ${vyos_op_scripts_dir}/interfaces.py clear_counters --intf-type "$3"</command> </leafNode> </children> </node> @@ -557,7 +557,7 @@ <properties> <help>Clear counters for a given wireless interface</help> </properties> - <command>sudo ${vyos_op_scripts_dir}/interfaces.py clear_counters --intf_name "$4"</command> + <command>sudo ${vyos_op_scripts_dir}/interfaces.py clear_counters --intf-name "$4"</command> </leafNode> </children> </tagNode> @@ -570,7 +570,7 @@ <properties> <help>Clear all WWAN interface counters</help> </properties> - <command>sudo ${vyos_op_scripts_dir}/interfaces.py clear_counters --intf_type "$3"</command> + <command>sudo ${vyos_op_scripts_dir}/interfaces.py clear_counters --intf-type "$3"</command> </leafNode> </children> </node> @@ -586,7 +586,7 @@ <properties> <help>Clear counters for a given WWAN interface</help> </properties> - <command>sudo ${vyos_op_scripts_dir}/interfaces.py clear_counters --intf_name "$4"</command> + <command>sudo ${vyos_op_scripts_dir}/interfaces.py clear_counters --intf-name "$4"</command> </leafNode> </children> </tagNode> diff --git a/op-mode-definitions/generate-system-login-user.xml.in b/op-mode-definitions/generate-system-login-user.xml.in index d0519b6bd..237a13610 100755 --- a/op-mode-definitions/generate-system-login-user.xml.in +++ b/op-mode-definitions/generate-system-login-user.xml.in @@ -35,19 +35,19 @@ <properties>
<help>Duration of single time interval</help>
</properties>
- <command>sudo ${vyos_op_scripts_dir}/generate_system_login_user.py --username "$5" --rate_limit "$9"</command>
+ <command>sudo ${vyos_op_scripts_dir}/generate_system_login_user.py --username "$5" --rate-limit "$9"</command>
<children>
<tagNode name="rate-time">
<properties>
<help>The number of digits in the one-time password</help>
</properties>
- <command>sudo ${vyos_op_scripts_dir}/generate_system_login_user.py --username "$5" --rate_limit "$9" --rate_time "${11}" </command>
+ <command>sudo ${vyos_op_scripts_dir}/generate_system_login_user.py --username "$5" --rate-limit "$9" --rate-time "${11}" </command>
<children>
<tagNode name="window-size">
<properties>
<help>The number of digits in the one-time password</help>
</properties>
- <command>sudo ${vyos_op_scripts_dir}/generate_system_login_user.py --username "$5" --rate_limit "$9" --rate_time "${11}" --window_size "${13}"</command>
+ <command>sudo ${vyos_op_scripts_dir}/generate_system_login_user.py --username "$5" --rate-limit "$9" --rate-time "${11}" --window-size "${13}"</command>
</tagNode>
</children>
</tagNode>
@@ -57,19 +57,19 @@ <properties>
<help>The number of digits in the one-time password</help>
</properties>
- <command>sudo ${vyos_op_scripts_dir}/generate_system_login_user.py --username "$5" --window_size "${9}"</command>
+ <command>sudo ${vyos_op_scripts_dir}/generate_system_login_user.py --username "$5" --window-size "${9}"</command>
<children>
<tagNode name="rate-limit">
<properties>
<help>Duration of single time interval</help>
</properties>
- <command>sudo ${vyos_op_scripts_dir}/generate_system_login_user.py --username "$5" --rate_limit "${11}" --window_size "${9}"</command>
+ <command>sudo ${vyos_op_scripts_dir}/generate_system_login_user.py --username "$5" --rate-limit "${11}" --window-size "${9}"</command>
<children>
<tagNode name="rate-time">
<properties>
<help>Duration of single time interval</help>
</properties>
- <command>sudo ${vyos_op_scripts_dir}/generate_system_login_user.py --username "$5" --rate_limit "${11}" --rate_time "${13}" --window_size "${9}"</command>
+ <command>sudo ${vyos_op_scripts_dir}/generate_system_login_user.py --username "$5" --rate-limit "${11}" --rate-time "${13}" --window-size "${9}"</command>
</tagNode>
</children>
</tagNode>
diff --git a/op-mode-definitions/include/ospf-common.xml.i b/op-mode-definitions/include/ospf-common.xml.i index aebbae5ff..979ffb07e 100644 --- a/op-mode-definitions/include/ospf-common.xml.i +++ b/op-mode-definitions/include/ospf-common.xml.i @@ -541,10 +541,19 @@ </properties> <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> </tagNode> -<leafNode name="route"> +<node name="route"> <properties> <help>Show IPv4 OSPF route information</help> </properties> <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> -</leafNode> -<!-- included end -->
\ No newline at end of file + <children> + <leafNode name="detail"> + <properties> + <help>Show detailed IPv4 OSPF route information</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </leafNode> + </children> +</node> +<!-- included end --> + diff --git a/op-mode-definitions/openvpn.xml.in b/op-mode-definitions/openvpn.xml.in index 94647af02..f205b0026 100644 --- a/op-mode-definitions/openvpn.xml.in +++ b/op-mode-definitions/openvpn.xml.in @@ -37,13 +37,13 @@ <properties> <help>Show OpenVPN interface information</help> </properties> - <command>${vyos_op_scripts_dir}/interfaces.py show_summary --intf_type=openvpn</command> + <command>${vyos_op_scripts_dir}/interfaces.py show_summary --intf-type=openvpn</command> <children> <leafNode name="detail"> <properties> <help>Show detailed OpenVPN interface information</help> </properties> - <command>${vyos_op_scripts_dir}/interfaces.py show --intf_type=openvpn</command> + <command>${vyos_op_scripts_dir}/interfaces.py show --intf-type=openvpn</command> </leafNode> </children> </node> @@ -54,7 +54,7 @@ <script>sudo ${vyos_completion_dir}/list_interfaces --type openvpn</script> </completionHelp> </properties> - <command>${vyos_op_scripts_dir}/interfaces.py show --intf_name=$4</command> + <command>${vyos_op_scripts_dir}/interfaces.py show --intf-name=$4</command> <children> <tagNode name="user"> <properties> @@ -95,7 +95,7 @@ <properties> <help>Show summary of specified OpenVPN interface information</help> </properties> - <command>${vyos_op_scripts_dir}/interfaces.py show_summary --intf_name="$4"</command> + <command>${vyos_op_scripts_dir}/interfaces.py show_summary --intf-name="$4"</command> </leafNode> </children> </tagNode> diff --git a/op-mode-definitions/reboot.xml.in b/op-mode-definitions/reboot.xml.in index 6414742d9..d5a71f561 100644 --- a/op-mode-definitions/reboot.xml.in +++ b/op-mode-definitions/reboot.xml.in @@ -25,7 +25,7 @@ <list><Minutes></list> </completionHelp> </properties> - <command>sudo ${vyos_op_scripts_dir}/powerctrl.py --yes --reboot_in $3 $4</command> + <command>sudo ${vyos_op_scripts_dir}/powerctrl.py --yes --reboot-in $3 $4</command> </tagNode> <tagNode name="at"> <properties> diff --git a/op-mode-definitions/show-acceleration.xml.in b/op-mode-definitions/show-acceleration.xml.in index 6fd3babf5..fccfba5e3 100644 --- a/op-mode-definitions/show-acceleration.xml.in +++ b/op-mode-definitions/show-acceleration.xml.in @@ -21,7 +21,7 @@ <properties> <help>Show QAT information for a given acceleration device</help> <completionHelp> - <script>${vyos_op_scripts_dir}/show_acceleration.py --dev_list</script> + <script>${vyos_op_scripts_dir}/show_acceleration.py --dev-list</script> </completionHelp> </properties> <children> diff --git a/op-mode-definitions/show-bfd.xml.in b/op-mode-definitions/show-bfd.xml.in index 39e42e6ec..87d672e04 100644 --- a/op-mode-definitions/show-bfd.xml.in +++ b/op-mode-definitions/show-bfd.xml.in @@ -49,6 +49,19 @@ </leafNode> </children> </node> + <node name="static"> + <properties> + <help>Show route Routing Table</help> + </properties> + <children> + <leafNode name="routes"> + <properties> + <help>Showing BFD monitored static routes</help> + </properties> + <command>vtysh -c "show bfd static route"</command> + </leafNode> + </children> + </node> </children> </node> </children> diff --git a/op-mode-definitions/show-interfaces-bonding.xml.in b/op-mode-definitions/show-interfaces-bonding.xml.in index c41e7bd5f..aa224e6cf 100644 --- a/op-mode-definitions/show-interfaces-bonding.xml.in +++ b/op-mode-definitions/show-interfaces-bonding.xml.in @@ -11,13 +11,13 @@ <path>interfaces bonding</path> </completionHelp> </properties> - <command>${vyos_op_scripts_dir}/interfaces.py show --intf_name="$4" --intf_type=bonding</command> + <command>${vyos_op_scripts_dir}/interfaces.py show --intf-name="$4" --intf-type=bonding</command> <children> <leafNode name="brief"> <properties> <help>Show summary of the specified bonding interface information</help> </properties> - <command>${vyos_op_scripts_dir}/interfaces.py show_summary --intf_name="$4" --intf_type=bonding</command> + <command>${vyos_op_scripts_dir}/interfaces.py show_summary --intf-name="$4" --intf-type=bonding</command> </leafNode> <leafNode name="detail"> <properties> @@ -38,13 +38,13 @@ <path>interfaces bonding ${COMP_WORDS[3]} vif</path> </completionHelp> </properties> - <command>${vyos_op_scripts_dir}/interfaces.py show --intf_name="$4.$6" --intf_type=bonding</command> + <command>${vyos_op_scripts_dir}/interfaces.py show --intf-name="$4.$6" --intf-type=bonding</command> <children> <leafNode name="brief"> <properties> <help>Show summary of specified virtual network interface (vif) information</help> </properties> - <command>${vyos_op_scripts_dir}/interfaces.py show_summary --intf_name="$4.$6" --intf_type=bonding</command> + <command>${vyos_op_scripts_dir}/interfaces.py show_summary --intf-name="$4.$6" --intf-type=bonding</command> </leafNode> </children> </tagNode> @@ -60,13 +60,13 @@ <properties> <help>Show Bonding interface information</help> </properties> - <command>${vyos_op_scripts_dir}/interfaces.py show_summary --intf_type=bonding</command> + <command>${vyos_op_scripts_dir}/interfaces.py show_summary --intf-type=bonding</command> <children> <leafNode name="detail"> <properties> <help>Show detailed bonding interface information</help> </properties> - <command>${vyos_op_scripts_dir}/interfaces.py show --intf_type=bonding</command> + <command>${vyos_op_scripts_dir}/interfaces.py show --intf-type=bonding</command> </leafNode> <leafNode name="slaves"> <properties> diff --git a/op-mode-definitions/show-interfaces-bridge.xml.in b/op-mode-definitions/show-interfaces-bridge.xml.in index 22cd3ee67..dc813682d 100644 --- a/op-mode-definitions/show-interfaces-bridge.xml.in +++ b/op-mode-definitions/show-interfaces-bridge.xml.in @@ -11,13 +11,13 @@ <path>interfaces bridge</path> </completionHelp> </properties> - <command>${vyos_op_scripts_dir}/interfaces.py show --intf_name="$4" --intf_type=bridge</command> + <command>${vyos_op_scripts_dir}/interfaces.py show --intf-name="$4" --intf-type=bridge</command> <children> <leafNode name="brief"> <properties> <help>Show summary of the specified bridge interface information</help> </properties> - <command>${vyos_op_scripts_dir}/interfaces.py show_summary --intf_name="$4" --intf_type=bridge</command> + <command>${vyos_op_scripts_dir}/interfaces.py show_summary --intf-name="$4" --intf-type=bridge</command> </leafNode> </children> </tagNode> @@ -25,13 +25,13 @@ <properties> <help>Show Bridge interface information</help> </properties> - <command>${vyos_op_scripts_dir}/interfaces.py show_summary --intf_type=bridge</command> + <command>${vyos_op_scripts_dir}/interfaces.py show_summary --intf-type=bridge</command> <children> <leafNode name="detail"> <properties> <help>Show detailed bridge interface information</help> </properties> - <command>${vyos_op_scripts_dir}/interfaces.py show --intf_type=bridge</command> + <command>${vyos_op_scripts_dir}/interfaces.py show --intf-type=bridge</command> </leafNode> </children> </node> diff --git a/op-mode-definitions/show-interfaces-dummy.xml.in b/op-mode-definitions/show-interfaces-dummy.xml.in index 958d3483d..b8ec7da91 100644 --- a/op-mode-definitions/show-interfaces-dummy.xml.in +++ b/op-mode-definitions/show-interfaces-dummy.xml.in @@ -11,13 +11,13 @@ <path>interfaces dummy</path> </completionHelp> </properties> - <command>${vyos_op_scripts_dir}/interfaces.py show --intf_name="$4" --intf_type=dummy</command> + <command>${vyos_op_scripts_dir}/interfaces.py show --intf-name="$4" --intf-type=dummy</command> <children> <leafNode name="brief"> <properties> <help>Show summary of the specified dummy interface information</help> </properties> - <command>${vyos_op_scripts_dir}/interfaces.py show_summary --intf_name="$4" --intf_type=dummy</command> + <command>${vyos_op_scripts_dir}/interfaces.py show_summary --intf-name="$4" --intf-type=dummy</command> </leafNode> </children> </tagNode> @@ -25,13 +25,13 @@ <properties> <help>Show Dummy interface information</help> </properties> - <command>${vyos_op_scripts_dir}/interfaces.py show_summary --intf_type=dummy</command> + <command>${vyos_op_scripts_dir}/interfaces.py show_summary --intf-type=dummy</command> <children> <leafNode name="detail"> <properties> <help>Show detailed dummy interface information</help> </properties> - <command>${vyos_op_scripts_dir}/interfaces.py show --intf_type=dummy</command> + <command>${vyos_op_scripts_dir}/interfaces.py show --intf-type=dummy</command> </leafNode> </children> </node> diff --git a/op-mode-definitions/show-interfaces-ethernet.xml.in b/op-mode-definitions/show-interfaces-ethernet.xml.in index 81759c2b6..7c12d6084 100644 --- a/op-mode-definitions/show-interfaces-ethernet.xml.in +++ b/op-mode-definitions/show-interfaces-ethernet.xml.in @@ -11,13 +11,13 @@ <path>interfaces ethernet</path> </completionHelp> </properties> - <command>${vyos_op_scripts_dir}/interfaces.py show --intf_name="$4" --intf_type=ethernet</command> + <command>${vyos_op_scripts_dir}/interfaces.py show --intf-name="$4" --intf-type=ethernet</command> <children> <leafNode name="brief"> <properties> <help>Show summary of the specified ethernet interface information</help> </properties> - <command>${vyos_op_scripts_dir}/interfaces.py show_summary --intf_name="$4" --intf_type=ethernet</command> + <command>${vyos_op_scripts_dir}/interfaces.py show_summary --intf-name="$4" --intf-type=ethernet</command> </leafNode> <leafNode name="identify"> <properties> @@ -58,13 +58,13 @@ <path>interfaces ethernet ${COMP_WORDS[3]} vif</path> </completionHelp> </properties> - <command>${vyos_op_scripts_dir}/interfaces.py show --intf_name="$4.$6" --intf_type=ethernet</command> + <command>${vyos_op_scripts_dir}/interfaces.py show --intf-name="$4.$6" --intf-type=ethernet</command> <children> <leafNode name="brief"> <properties> <help>Show summary of specified virtual network interface (vif) information</help> </properties> - <command>${vyos_op_scripts_dir}/interfaces.py show_summary --intf_name="$4.$6" --intf_type=ethernet</command> + <command>${vyos_op_scripts_dir}/interfaces.py show_summary --intf-name="$4.$6" --intf-type=ethernet</command> </leafNode> </children> </tagNode> @@ -80,13 +80,13 @@ <properties> <help>Show Ethernet interface information</help> </properties> - <command>${vyos_op_scripts_dir}/interfaces.py show_summary --intf_type=ethernet</command> + <command>${vyos_op_scripts_dir}/interfaces.py show_summary --intf-type=ethernet</command> <children> <leafNode name="detail"> <properties> <help>Show detailed ethernet interface information</help> </properties> - <command>${vyos_op_scripts_dir}/interfaces.py show --intf_type=ethernet</command> + <command>${vyos_op_scripts_dir}/interfaces.py show --intf-type=ethernet</command> </leafNode> </children> </node> diff --git a/op-mode-definitions/show-interfaces-geneve.xml.in b/op-mode-definitions/show-interfaces-geneve.xml.in index 3cf45878d..d3d188031 100644 --- a/op-mode-definitions/show-interfaces-geneve.xml.in +++ b/op-mode-definitions/show-interfaces-geneve.xml.in @@ -11,13 +11,13 @@ <path>interfaces geneve</path> </completionHelp> </properties> - <command>${vyos_op_scripts_dir}/interfaces.py show --intf_name="$4" --intf_type=geneve</command> + <command>${vyos_op_scripts_dir}/interfaces.py show --intf-name="$4" --intf-type=geneve</command> <children> <leafNode name="brief"> <properties> <help>Show summary of the specified GENEVE interface information</help> </properties> - <command>${vyos_op_scripts_dir}/interfaces.py show_summary --intf_name="$4" --intf_type=geneve</command> + <command>${vyos_op_scripts_dir}/interfaces.py show_summary --intf-name="$4" --intf-type=geneve</command> </leafNode> </children> </tagNode> @@ -25,13 +25,13 @@ <properties> <help>Show GENEVE interface information</help> </properties> - <command>${vyos_op_scripts_dir}/interfaces.py show_summary --intf_type=geneve</command> + <command>${vyos_op_scripts_dir}/interfaces.py show_summary --intf-type=geneve</command> <children> <leafNode name="detail"> <properties> <help>Show detailed GENEVE interface information</help> </properties> - <command>${vyos_op_scripts_dir}/interfaces.py show --intf_type=geneve</command> + <command>${vyos_op_scripts_dir}/interfaces.py show --intf-type=geneve</command> </leafNode> </children> </node> diff --git a/op-mode-definitions/show-interfaces-input.xml.in b/op-mode-definitions/show-interfaces-input.xml.in index 5d93dcee6..e5d420056 100644 --- a/op-mode-definitions/show-interfaces-input.xml.in +++ b/op-mode-definitions/show-interfaces-input.xml.in @@ -11,13 +11,13 @@ <path>interfaces input</path> </completionHelp> </properties> - <command>${vyos_op_scripts_dir}/interfaces.py show --intf_name="$4" --intf_type=input</command> + <command>${vyos_op_scripts_dir}/interfaces.py show --intf-name="$4" --intf-type=input</command> <children> <leafNode name="brief"> <properties> <help>Show summary of the specified input interface information</help> </properties> - <command>${vyos_op_scripts_dir}/interfaces.py show_summary --intf_name="$4" --intf_type=input</command> + <command>${vyos_op_scripts_dir}/interfaces.py show_summary --intf-name="$4" --intf-type=input</command> </leafNode> </children> </tagNode> @@ -25,13 +25,13 @@ <properties> <help>Show Input (ifb) interface information</help> </properties> - <command>${vyos_op_scripts_dir}/interfaces.py show_summary --intf_type=input</command> + <command>${vyos_op_scripts_dir}/interfaces.py show_summary --intf-type=input</command> <children> <leafNode name="detail"> <properties> <help>Show detailed input interface information</help> </properties> - <command>${vyos_op_scripts_dir}/interfaces.py show --intf_type=input</command> + <command>${vyos_op_scripts_dir}/interfaces.py show --intf-type=input</command> </leafNode> </children> </node> diff --git a/op-mode-definitions/show-interfaces-l2tpv3.xml.in b/op-mode-definitions/show-interfaces-l2tpv3.xml.in index 713e36dac..2d165171c 100644 --- a/op-mode-definitions/show-interfaces-l2tpv3.xml.in +++ b/op-mode-definitions/show-interfaces-l2tpv3.xml.in @@ -11,13 +11,13 @@ <path>interfaces l2tpv3</path> </completionHelp> </properties> - <command>${vyos_op_scripts_dir}/interfaces.py show --intf_name="$4" --intf_type=l2tpv3</command> + <command>${vyos_op_scripts_dir}/interfaces.py show --intf-name="$4" --intf-type=l2tpv3</command> <children> <leafNode name="brief"> <properties> <help>Show summary of the specified L2TPv3 interface information</help> </properties> - <command>${vyos_op_scripts_dir}/interfaces.py show_summary --intf_name="$4" --intf_type=l2tpv3</command> + <command>${vyos_op_scripts_dir}/interfaces.py show_summary --intf-name="$4" --intf-type=l2tpv3</command> </leafNode> </children> </tagNode> @@ -25,13 +25,13 @@ <properties> <help>Show L2TPv3 interface information</help> </properties> - <command>${vyos_op_scripts_dir}/interfaces.py show_summary --intf_type=l2tpv3</command> + <command>${vyos_op_scripts_dir}/interfaces.py show_summary --intf-type=l2tpv3</command> <children> <leafNode name="detail"> <properties> <help>Show detailed L2TPv3 interface information</help> </properties> - <command>${vyos_op_scripts_dir}/interfaces.py show --intf_type=l2tpv3</command> + <command>${vyos_op_scripts_dir}/interfaces.py show --intf-type=l2tpv3</command> </leafNode> </children> </node> diff --git a/op-mode-definitions/show-interfaces-loopback.xml.in b/op-mode-definitions/show-interfaces-loopback.xml.in index a24151cc3..d341a6359 100644 --- a/op-mode-definitions/show-interfaces-loopback.xml.in +++ b/op-mode-definitions/show-interfaces-loopback.xml.in @@ -11,13 +11,13 @@ <path>interfaces loopback</path> </completionHelp> </properties> - <command>${vyos_op_scripts_dir}/interfaces.py show --intf_name="$4" --intf_type=loopback</command> + <command>${vyos_op_scripts_dir}/interfaces.py show --intf-name="$4" --intf-type=loopback</command> <children> <leafNode name="brief"> <properties> <help>Show summary of the specified Loopback interface information</help> </properties> - <command>${vyos_op_scripts_dir}/interfaces.py show_summary --intf_name="$4" --intf_type=loopback</command> + <command>${vyos_op_scripts_dir}/interfaces.py show_summary --intf-name="$4" --intf-type=loopback</command> </leafNode> </children> </tagNode> @@ -25,13 +25,13 @@ <properties> <help>Show Loopback interface information</help> </properties> - <command>${vyos_op_scripts_dir}/interfaces.py show_summary --intf_type=loopback</command> + <command>${vyos_op_scripts_dir}/interfaces.py show_summary --intf-type=loopback</command> <children> <leafNode name="detail"> <properties> <help>Show detailed Loopback interface information</help> </properties> - <command>${vyos_op_scripts_dir}/interfaces.py show --intf_type=loopback</command> + <command>${vyos_op_scripts_dir}/interfaces.py show --intf-type=loopback</command> </leafNode> </children> </node> diff --git a/op-mode-definitions/show-interfaces-pppoe.xml.in b/op-mode-definitions/show-interfaces-pppoe.xml.in index a34473148..1c6e0b83e 100644 --- a/op-mode-definitions/show-interfaces-pppoe.xml.in +++ b/op-mode-definitions/show-interfaces-pppoe.xml.in @@ -11,7 +11,7 @@ <path>interfaces pppoe</path> </completionHelp> </properties> - <command>${vyos_op_scripts_dir}/interfaces.py show --intf_name="$4" --intf_type=pppoe</command> + <command>${vyos_op_scripts_dir}/interfaces.py show --intf-name="$4" --intf-type=pppoe</command> <children> <leafNode name="log"> <properties> @@ -34,13 +34,13 @@ <properties> <help>Show PPPoE interface information</help> </properties> - <command>${vyos_op_scripts_dir}/interfaces.py show_summary --intf_type=pppoe</command> + <command>${vyos_op_scripts_dir}/interfaces.py show_summary --intf-type=pppoe</command> <children> <leafNode name="detail"> <properties> <help>Show detailed PPPoE interface information</help> </properties> - <command>${vyos_op_scripts_dir}/interfaces.py show --intf_type=pppoe</command> + <command>${vyos_op_scripts_dir}/interfaces.py show --intf-type=pppoe</command> </leafNode> </children> </node> diff --git a/op-mode-definitions/show-interfaces-pseudo-ethernet.xml.in b/op-mode-definitions/show-interfaces-pseudo-ethernet.xml.in index cb62639ee..4ab2a5fbb 100644 --- a/op-mode-definitions/show-interfaces-pseudo-ethernet.xml.in +++ b/op-mode-definitions/show-interfaces-pseudo-ethernet.xml.in @@ -11,13 +11,13 @@ <path>interfaces pseudo-ethernet</path> </completionHelp> </properties> - <command>${vyos_op_scripts_dir}/interfaces.py show --intf_name="$4" --intf_type=pseudo-ethernet</command> + <command>${vyos_op_scripts_dir}/interfaces.py show --intf-name="$4" --intf-type=pseudo-ethernet</command> <children> <leafNode name="brief"> <properties> <help>Show summary of the specified pseudo-ethernet/MACvlan interface information</help> </properties> - <command>${vyos_op_scripts_dir}/interfaces.py show_summary --intf_name="$4" --intf_type=pseudo-ethernet</command> + <command>${vyos_op_scripts_dir}/interfaces.py show_summary --intf-name="$4" --intf-type=pseudo-ethernet</command> </leafNode> </children> </tagNode> @@ -25,13 +25,13 @@ <properties> <help>Show Pseudo-Ethernet/MACvlan interface information</help> </properties> - <command>${vyos_op_scripts_dir}/interfaces.py show_summary --intf_type=pseudo-ethernet</command> + <command>${vyos_op_scripts_dir}/interfaces.py show_summary --intf-type=pseudo-ethernet</command> <children> <leafNode name="detail"> <properties> <help>Show detailed pseudo-ethernet/MACvlan interface information</help> </properties> - <command>${vyos_op_scripts_dir}/interfaces.py show --intf_type=pseudo-ethernet</command> + <command>${vyos_op_scripts_dir}/interfaces.py show --intf-type=pseudo-ethernet</command> </leafNode> </children> </node> diff --git a/op-mode-definitions/show-interfaces-sstpc.xml.in b/op-mode-definitions/show-interfaces-sstpc.xml.in index a619a9fd2..307276f72 100644 --- a/op-mode-definitions/show-interfaces-sstpc.xml.in +++ b/op-mode-definitions/show-interfaces-sstpc.xml.in @@ -11,7 +11,7 @@ <path>interfaces sstpc</path> </completionHelp> </properties> - <command>${vyos_op_scripts_dir}/interfaces.py show --intf_name="$4" --intf_type=sstpc</command> + <command>${vyos_op_scripts_dir}/interfaces.py show --intf-name="$4" --intf-type=sstpc</command> <children> <leafNode name="log"> <properties> @@ -34,13 +34,13 @@ <properties> <help>Show SSTP client interface information</help> </properties> - <command>${vyos_op_scripts_dir}/interfaces.py show_summary --intf_type=sstpc</command> + <command>${vyos_op_scripts_dir}/interfaces.py show_summary --intf-type=sstpc</command> <children> <leafNode name="detail"> <properties> <help>Show detailed SSTP client interface information</help> </properties> - <command>${vyos_op_scripts_dir}/interfaces.py show --intf_type=sstpc</command> + <command>${vyos_op_scripts_dir}/interfaces.py show --intf-type=sstpc</command> </leafNode> </children> </node> diff --git a/op-mode-definitions/show-interfaces-tunnel.xml.in b/op-mode-definitions/show-interfaces-tunnel.xml.in index 10e10e655..b99b0cbb2 100644 --- a/op-mode-definitions/show-interfaces-tunnel.xml.in +++ b/op-mode-definitions/show-interfaces-tunnel.xml.in @@ -11,13 +11,13 @@ <path>interfaces tunnel</path> </completionHelp> </properties> - <command>${vyos_op_scripts_dir}/interfaces.py show --intf_name="$4" --intf_type=tunnel</command> + <command>${vyos_op_scripts_dir}/interfaces.py show --intf-name="$4" --intf-type=tunnel</command> <children> <leafNode name="brief"> <properties> <help>Show summary of the specified tunnel interface information</help> </properties> - <command>${vyos_op_scripts_dir}/interfaces.py show_summary --intf_name="$4" --intf_type=tunnel</command> + <command>${vyos_op_scripts_dir}/interfaces.py show_summary --intf-name="$4" --intf-type=tunnel</command> </leafNode> </children> </tagNode> @@ -25,13 +25,13 @@ <properties> <help>Show Tunnel interface information</help> </properties> - <command>${vyos_op_scripts_dir}/interfaces.py show_summary --intf_type=tunnel</command> + <command>${vyos_op_scripts_dir}/interfaces.py show_summary --intf-type=tunnel</command> <children> <leafNode name="detail"> <properties> <help>Show detailed tunnel interface information</help> </properties> - <command>${vyos_op_scripts_dir}/interfaces.py show --intf_type=tunnel</command> + <command>${vyos_op_scripts_dir}/interfaces.py show --intf-type=tunnel</command> </leafNode> </children> </node> diff --git a/op-mode-definitions/show-interfaces-virtual-ethernet.xml.in b/op-mode-definitions/show-interfaces-virtual-ethernet.xml.in index c743492fb..18ae806b7 100644 --- a/op-mode-definitions/show-interfaces-virtual-ethernet.xml.in +++ b/op-mode-definitions/show-interfaces-virtual-ethernet.xml.in @@ -11,13 +11,13 @@ <path>interfaces virtual-ethernet</path> </completionHelp> </properties> - <command>${vyos_op_scripts_dir}/interfaces.py show --intf_name="$4" --intf_type=virtual-ethernet</command> + <command>${vyos_op_scripts_dir}/interfaces.py show --intf-name="$4" --intf-type=virtual-ethernet</command> <children> <leafNode name="brief"> <properties> <help>Show summary of the specified virtual-ethernet interface information</help> </properties> - <command>${vyos_op_scripts_dir}/interfaces.py show_summary --intf_name="$4" --intf_type=virtual-ethernet</command> + <command>${vyos_op_scripts_dir}/interfaces.py show_summary --intf-name="$4" --intf-type=virtual-ethernet</command> </leafNode> </children> </tagNode> @@ -25,13 +25,13 @@ <properties> <help>Show virtual-ethernet interface information</help> </properties> - <command>${vyos_op_scripts_dir}/interfaces.py show_summary --intf_type=virtual-ethernet</command> + <command>${vyos_op_scripts_dir}/interfaces.py show_summary --intf-type=virtual-ethernet</command> <children> <leafNode name="detail"> <properties> <help>Show detailed virtual-ethernet interface information</help> </properties> - <command>${vyos_op_scripts_dir}/interfaces.py show --intf_type=virtual-ethernet</command> + <command>${vyos_op_scripts_dir}/interfaces.py show --intf-type=virtual-ethernet</command> </leafNode> </children> </node> diff --git a/op-mode-definitions/show-interfaces-vti.xml.in b/op-mode-definitions/show-interfaces-vti.xml.in index d532894b7..ae5cfeb9c 100644 --- a/op-mode-definitions/show-interfaces-vti.xml.in +++ b/op-mode-definitions/show-interfaces-vti.xml.in @@ -11,13 +11,13 @@ <path>interfaces vti</path> </completionHelp> </properties> - <command>${vyos_op_scripts_dir}/interfaces.py show --intf_name="$4" --intf_type=vti</command> + <command>${vyos_op_scripts_dir}/interfaces.py show --intf-name="$4" --intf-type=vti</command> <children> <leafNode name="brief"> <properties> <help>Show summary of the specified vti interface information</help> </properties> - <command>${vyos_op_scripts_dir}/interfaces.py show_summary --intf_name="$4" --intf_type=vti</command> + <command>${vyos_op_scripts_dir}/interfaces.py show_summary --intf-name="$4" --intf-type=vti</command> </leafNode> </children> </tagNode> @@ -25,13 +25,13 @@ <properties> <help>Show VTI interface information</help> </properties> - <command>${vyos_op_scripts_dir}/interfaces.py show_summary --intf_type=vti</command> + <command>${vyos_op_scripts_dir}/interfaces.py show_summary --intf-type=vti</command> <children> <leafNode name="detail"> <properties> <help>Show detailed vti interface information</help> </properties> - <command>${vyos_op_scripts_dir}/interfaces.py show --intf_type=vti</command> + <command>${vyos_op_scripts_dir}/interfaces.py show --intf-type=vti</command> </leafNode> </children> </node> diff --git a/op-mode-definitions/show-interfaces-vxlan.xml.in b/op-mode-definitions/show-interfaces-vxlan.xml.in index fde832551..fd729b986 100644 --- a/op-mode-definitions/show-interfaces-vxlan.xml.in +++ b/op-mode-definitions/show-interfaces-vxlan.xml.in @@ -11,13 +11,13 @@ <path>interfaces vxlan</path> </completionHelp> </properties> - <command>${vyos_op_scripts_dir}/interfaces.py show --intf_name="$4" --intf_type=vxlan</command> + <command>${vyos_op_scripts_dir}/interfaces.py show --intf-name="$4" --intf-type=vxlan</command> <children> <leafNode name="brief"> <properties> <help>Show summary of the specified VXLAN interface information</help> </properties> - <command>${vyos_op_scripts_dir}/interfaces.py show_summary --intf_name="$4" --intf_type=vxlan</command> + <command>${vyos_op_scripts_dir}/interfaces.py show_summary --intf-name="$4" --intf-type=vxlan</command> </leafNode> </children> </tagNode> @@ -25,13 +25,13 @@ <properties> <help>Show VXLAN interface information</help> </properties> - <command>${vyos_op_scripts_dir}/interfaces.py show_summary --intf_type=vxlan</command> + <command>${vyos_op_scripts_dir}/interfaces.py show_summary --intf-type=vxlan</command> <children> <leafNode name="detail"> <properties> <help>Show detailed VXLAN interface information</help> </properties> - <command>${vyos_op_scripts_dir}/interfaces.py show --intf_type=vxlan</command> + <command>${vyos_op_scripts_dir}/interfaces.py show --intf-type=vxlan</command> </leafNode> </children> </node> diff --git a/op-mode-definitions/show-interfaces-wireguard.xml.in b/op-mode-definitions/show-interfaces-wireguard.xml.in index d045beafc..bab7f19c8 100644 --- a/op-mode-definitions/show-interfaces-wireguard.xml.in +++ b/op-mode-definitions/show-interfaces-wireguard.xml.in @@ -11,7 +11,7 @@ <script>${vyos_completion_dir}/list_interfaces --type wireguard</script> </completionHelp> </properties> - <command>${vyos_op_scripts_dir}/interfaces.py show --intf_name="$4" --intf_type=wireguard</command> + <command>${vyos_op_scripts_dir}/interfaces.py show --intf-name="$4" --intf-type=wireguard</command> <children> <leafNode name="allowed-ips"> <properties> @@ -49,13 +49,13 @@ <properties> <help>Show WireGuard interface information</help> </properties> - <command>${vyos_op_scripts_dir}/interfaces.py show_summary --intf_type=wireguard</command> + <command>${vyos_op_scripts_dir}/interfaces.py show_summary --intf-type=wireguard</command> <children> <leafNode name="detail"> <properties> <help>Show detailed Wireguard interface information</help> </properties> - <command>${vyos_op_scripts_dir}/interfaces.py show --intf_type=wireguard</command> + <command>${vyos_op_scripts_dir}/interfaces.py show --intf-type=wireguard</command> </leafNode> </children> </node> diff --git a/op-mode-definitions/show-interfaces-wireless.xml.in b/op-mode-definitions/show-interfaces-wireless.xml.in index f39d402f1..27c0f43db 100644 --- a/op-mode-definitions/show-interfaces-wireless.xml.in +++ b/op-mode-definitions/show-interfaces-wireless.xml.in @@ -8,13 +8,13 @@ <properties> <help>Show Wireless (WLAN) interface information</help> </properties> - <command>${vyos_op_scripts_dir}/interfaces.py show_summary --intf_type=wireless</command> + <command>${vyos_op_scripts_dir}/interfaces.py show_summary --intf-type=wireless</command> <children> <leafNode name="detail"> <properties> <help>Show detailed wireless interface information</help> </properties> - <command>${vyos_op_scripts_dir}/interfaces.py show --intf_type=wireless</command> + <command>${vyos_op_scripts_dir}/interfaces.py show --intf-type=wireless</command> </leafNode> <leafNode name="info"> <properties> @@ -31,13 +31,13 @@ <script>${vyos_completion_dir}/list_interfaces --type wireless</script> </completionHelp> </properties> - <command>${vyos_op_scripts_dir}/interfaces.py show --intf_name="$4" --intf_type=wireless</command> + <command>${vyos_op_scripts_dir}/interfaces.py show --intf-name="$4" --intf-type=wireless</command> <children> <leafNode name="brief"> <properties> <help>Show summary of the specified wireless interface information</help> </properties> - <command>${vyos_op_scripts_dir}/interfaces.py show_summary --intf_name="$4" --intf_type=wireless</command> + <command>${vyos_op_scripts_dir}/interfaces.py show_summary --intf-name="$4" --intf-type=wireless</command> </leafNode> <node name="scan"> <properties> @@ -63,13 +63,13 @@ <properties> <help>Show specified virtual network interface (vif) information</help> </properties> - <command>${vyos_op_scripts_dir}/interfaces.py show --intf_name="$4.$6" --intf_type=wireless</command> + <command>${vyos_op_scripts_dir}/interfaces.py show --intf-name="$4.$6" --intf-type=wireless</command> <children> <leafNode name="brief"> <properties> <help>Show summary of specified virtual network interface (vif) information</help> </properties> - <command>${vyos_op_scripts_dir}/interfaces.py show_summary --intf_name="$4.$6" --intf_type=wireless</command> + <command>${vyos_op_scripts_dir}/interfaces.py show_summary --intf-name="$4.$6" --intf-type=wireless</command> </leafNode> </children> </tagNode> diff --git a/op-mode-definitions/show-interfaces-wwan.xml.in b/op-mode-definitions/show-interfaces-wwan.xml.in index 17d4111a9..45558115b 100644 --- a/op-mode-definitions/show-interfaces-wwan.xml.in +++ b/op-mode-definitions/show-interfaces-wwan.xml.in @@ -12,7 +12,7 @@ <script>cd /sys/class/net; ls -d wwan*</script> </completionHelp> </properties> - <command>${vyos_op_scripts_dir}/interfaces.py show --intf_name="$4" --intf_type=wirelessmodem</command> + <command>${vyos_op_scripts_dir}/interfaces.py show --intf-name="$4" --intf-type=wirelessmodem</command> <children> <leafNode name="capabilities"> <properties> @@ -72,7 +72,7 @@ <properties> <help>Show WWAN module detailed information summary</help> </properties> - <command>mmcli --modem ${4#wwan}</command> + <command>if cli-shell-api existsActive interfaces wwan $4; then mmcli --modem ${4#wwan}; else echo "Interface \"$4\" unconfigured!"; fi</command> </leafNode> <leafNode name="log"> <properties> @@ -86,13 +86,13 @@ <properties> <help>Show Wireless Modem (WWAN) interface information</help> </properties> - <command>${vyos_op_scripts_dir}/interfaces.py show_summary --intf_type=wirelessmodem</command> + <command>${vyos_op_scripts_dir}/interfaces.py show_summary --intf-type=wirelessmodem</command> <children> <leafNode name="detail"> <properties> <help>Show detailed Wireless Modem (WWAN( interface information</help> </properties> - <command>${vyos_op_scripts_dir}/interfaces.py show --intf_type=wirelessmodem</command> + <command>${vyos_op_scripts_dir}/interfaces.py show --intf-type=wirelessmodem</command> </leafNode> </children> </node> diff --git a/op-mode-definitions/vpn-ipsec.xml.in b/op-mode-definitions/vpn-ipsec.xml.in index 5a7e6dd63..c7ba780a3 100644 --- a/op-mode-definitions/vpn-ipsec.xml.in +++ b/op-mode-definitions/vpn-ipsec.xml.in @@ -35,7 +35,7 @@ <list><x.x.x.x> <h:h:h:h:h:h:h:h></list> </completionHelp> </properties> - <command>sudo ${vyos_op_scripts_dir}/ipsec.py reset_profile_dst --profile="$5" --tunnel="$7" --nbma_dst="$9"</command> + <command>sudo ${vyos_op_scripts_dir}/ipsec.py reset_profile_dst --profile="$5" --tunnel="$7" --nbma-dst="$9"</command> </tagNode> </children> <command>sudo ${vyos_op_scripts_dir}/ipsec.py reset_profile_all --profile="$5" --tunnel="$7"</command> @@ -204,12 +204,37 @@ </properties> <command>sudo ip xfrm policy list</command> </node> - <leafNode name="remote-access"> - <properties> - <help>Show active VPN server sessions</help> - </properties> - <command>${vyos_op_scripts_dir}/show_vpn_ra.py</command> - </leafNode> + <node name="remote-access"> + <properties> + <help>Show active VPN server sessions</help> + </properties> + <children> + <node name="detail"> + <properties> + <help>Show detail active IKEv2 RA sessions</help> + </properties> + <command>if systemctl is-active --quiet strongswan ; then sudo ${vyos_op_scripts_dir}/ipsec.py show_ra_detail; else echo "IPsec process not running" ; fi</command> + </node> + <tagNode name="connection-id"> + <properties> + <help>Show detail active IKEv2 RA sessions by connection-id</help> + </properties> + <command>if systemctl is-active --quiet strongswan ; then sudo ${vyos_op_scripts_dir}/ipsec.py show_ra_detail --conn-id="$6"; else echo "IPsec process not running" ; fi</command> + </tagNode> + <node name="summary"> + <properties> + <help>Show active IKEv2 RA sessions summary</help> + </properties> + <command>if systemctl is-active --quiet strongswan ; then sudo ${vyos_op_scripts_dir}/ipsec.py show_ra_summary; else echo "IPsec process not running" ; fi</command> + </node> + <tagNode name="username"> + <properties> + <help>Show detail active IKEv2 RA sessions by username</help> + </properties> + <command>if systemctl is-active --quiet strongswan ; then sudo ${vyos_op_scripts_dir}/ipsec.py show_ra_detail --username="$6"; else echo "IPsec process not running" ; fi</command> + </tagNode> + </children> + </node> <node name="sa"> <properties> <help>Show all active IPsec Security Associations (SA)</help> @@ -241,11 +266,11 @@ <command></command> </tagNode> --> - <node name="verbose"> + <node name="detail"> <properties> <help>Show Verbose Detail on all active IPsec Security Associations (SA)</help> </properties> - <command>if systemctl is-active --quiet strongswan ; then sudo /usr/sbin/ipsec statusall ; else echo "IPsec process not running" ; fi</command> + <command>if systemctl is-active --quiet strongswan ; then sudo ${vyos_op_scripts_dir}/ipsec.py show_sa_detail ; else echo "IPsec process not running" ; fi</command> </node> </children> <command>if systemctl is-active --quiet strongswan ; then sudo ${vyos_op_scripts_dir}/ipsec.py show_sa ; else echo "IPsec process not running" ; fi</command> diff --git a/python/vyos/base.py b/python/vyos/base.py index 9b93cb2f2..c1acfd060 100644 --- a/python/vyos/base.py +++ b/python/vyos/base.py @@ -1,4 +1,4 @@ -# Copyright 2018-2022 VyOS maintainers and contributors <maintainers@vyos.io> +# Copyright 2018-2023 VyOS maintainers and contributors <maintainers@vyos.io> # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -41,7 +41,6 @@ class BaseWarning: isfirstmessage = False initial_indent = self.standardindent print(f'{mes}') - print('') class Warning(): diff --git a/python/vyos/ethtool.py b/python/vyos/ethtool.py index bc3402059..1b1e54dfb 100644 --- a/python/vyos/ethtool.py +++ b/python/vyos/ethtool.py @@ -51,6 +51,7 @@ class Ethtool: _ring_buffers_max = { } _driver_name = None _auto_negotiation = False + _auto_negotiation_supported = None _flow_control = False _flow_control_enabled = None @@ -80,7 +81,13 @@ class Ethtool: self._speed_duplex.update({ speed : {}}) if duplex not in self._speed_duplex[speed]: self._speed_duplex[speed].update({ duplex : ''}) - if 'Auto-negotiation:' in line: + if 'Supports auto-negotiation:' in line: + # Split the following string: Auto-negotiation: off + # we are only interested in off or on + tmp = line.split()[-1] + self._auto_negotiation_supported = bool(tmp == 'Yes') + # Only read in if Auto-negotiation is supported + if self._auto_negotiation_supported and 'Auto-negotiation:' in line: # Split the following string: Auto-negotiation: off # we are only interested in off or on tmp = line.split()[-1] @@ -132,8 +139,12 @@ class Ethtool: # ['Autonegotiate:', 'on'] self._flow_control_enabled = out.splitlines()[1].split()[-1] + def check_auto_negotiation_supported(self): + """ Check if the NIC supports changing auto-negotiation """ + return self._auto_negotiation_supported + def get_auto_negotiation(self): - return self._auto_negotiation + return self._auto_negotiation_supported and self._auto_negotiation def get_driver_name(self): return self._driver_name diff --git a/python/vyos/ifconfig/ethernet.py b/python/vyos/ifconfig/ethernet.py index 5080144ff..6a49c022a 100644 --- a/python/vyos/ifconfig/ethernet.py +++ b/python/vyos/ifconfig/ethernet.py @@ -1,4 +1,4 @@ -# Copyright 2019-2021 VyOS maintainers and contributors <maintainers@vyos.io> +# Copyright 2019-2023 VyOS maintainers and contributors <maintainers@vyos.io> # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -14,9 +14,10 @@ # License along with this library. If not, see <http://www.gnu.org/licenses/>. import os -import re from glob import glob + +from vyos.base import Warning from vyos.ethtool import Ethtool from vyos.ifconfig.interface import Interface from vyos.util import run @@ -118,7 +119,7 @@ class EthernetIf(Interface): cmd = f'ethtool --pause {ifname} autoneg {enable} tx {enable} rx {enable}' output, code = self._popen(cmd) if code: - print(f'Could not set flowcontrol for {ifname}') + Warning(f'could not change "{ifname}" flow control setting!') return output return None @@ -134,6 +135,7 @@ class EthernetIf(Interface): >>> i = EthernetIf('eth0') >>> i.set_speed_duplex('auto', 'auto') """ + ifname = self.config['ifname'] if speed not in ['auto', '10', '100', '1000', '2500', '5000', '10000', '25000', '40000', '50000', '100000', '400000']: @@ -143,7 +145,11 @@ class EthernetIf(Interface): raise ValueError("Value out of range (duplex)") if not self.ethtool.check_speed_duplex(speed, duplex): - self._debug_msg(f'NIC driver does not support changing speed/duplex settings!') + Warning(f'changing speed/duplex setting on "{ifname}" is unsupported!') + return + + if not self.ethtool.check_auto_negotiation_supported(): + Warning(f'changing auto-negotiation setting on "{ifname}" is unsupported!') return # Get current speed and duplex settings: diff --git a/python/vyos/opmode.py b/python/vyos/opmode.py index d7172a0b5..230a85541 100644 --- a/python/vyos/opmode.py +++ b/python/vyos/opmode.py @@ -209,6 +209,11 @@ def run(module): for opt in type_hints: th = type_hints[opt] + # Function argument names use underscores as separators + # but command-line options should use hyphens + # Without this, we'd get options like "--foo_bar" + opt = re.sub(r'_', '-', opt) + if _get_arg_type(th) == bool: subparser.add_argument(f"--{opt}", action='store_true') else: diff --git a/python/vyos/template.py b/python/vyos/template.py index 06a292706..254a15e3a 100644 --- a/python/vyos/template.py +++ b/python/vyos/template.py @@ -44,6 +44,7 @@ def _get_environment(location=None): loader=loc_loader, trim_blocks=True, undefined=ChainableUndefined, + extensions=['jinja2.ext.loopcontrols'] ) env.filters.update(_FILTERS) env.tests.update(_TESTS) diff --git a/python/vyos/utils/__init__.py b/python/vyos/utils/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/vyos/utils/__init__.py diff --git a/python/vyos/utils/dict.py b/python/vyos/utils/dict.py new file mode 100644 index 000000000..66b40d92b --- /dev/null +++ b/python/vyos/utils/dict.py @@ -0,0 +1,235 @@ +# Copyright 2023 VyOS maintainers and contributors <maintainers@vyos.io> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library. If not, see <http://www.gnu.org/licenses/>. + + +def colon_separated_to_dict(data_string, uniquekeys=False): + """ Converts a string containing newline-separated entries + of colon-separated key-value pairs into a dict. + + Such files are common in Linux /proc filesystem + + Args: + data_string (str): data string + uniquekeys (bool): whether to insist that keys are unique or not + + Returns: dict + + Raises: + ValueError: if uniquekeys=True and the data string has + duplicate keys. + + Note: + If uniquekeys=True, then dict entries are always strings, + otherwise they are always lists of strings. + """ + import re + key_value_re = re.compile('([^:]+)\s*\:\s*(.*)') + + data_raw = re.split('\n', data_string) + + data = {} + + for l in data_raw: + l = l.strip() + if l: + match = re.match(key_value_re, l) + if match and (len(match.groups()) == 2): + key = match.groups()[0].strip() + value = match.groups()[1].strip() + else: + raise ValueError(f"""Line "{l}" could not be parsed a colon-separated pair """, l) + if key in data.keys(): + if uniquekeys: + raise ValueError("Data string has duplicate keys: {0}".format(key)) + else: + data[key].append(value) + else: + if uniquekeys: + data[key] = value + else: + data[key] = [value] + else: + pass + + return data + +def _mangle_dict_keys(data, regex, replacement, abs_path=[], no_tag_node_value_mangle=False, mod=0): + """ Mangles dict keys according to a regex and replacement character. + Some libraries like Jinja2 do not like certain characters in dict keys. + This function can be used for replacing all offending characters + with something acceptable. + + Args: + data (dict): Original dict to mangle + + Returns: dict + """ + from vyos.xml import is_tag + + new_dict = {} + + for key in data.keys(): + save_mod = mod + save_path = abs_path[:] + + abs_path.append(key) + + if not is_tag(abs_path): + new_key = re.sub(regex, replacement, key) + else: + if mod%2: + new_key = key + else: + new_key = re.sub(regex, replacement, key) + if no_tag_node_value_mangle: + mod += 1 + + value = data[key] + + if isinstance(value, dict): + new_dict[new_key] = _mangle_dict_keys(value, regex, replacement, abs_path=abs_path, mod=mod, no_tag_node_value_mangle=no_tag_node_value_mangle) + else: + new_dict[new_key] = value + + mod = save_mod + abs_path = save_path[:] + + return new_dict + +def mangle_dict_keys(data, regex, replacement, abs_path=[], no_tag_node_value_mangle=False): + return _mangle_dict_keys(data, regex, replacement, abs_path=abs_path, no_tag_node_value_mangle=no_tag_node_value_mangle, mod=0) + +def _get_sub_dict(d, lpath): + k = lpath[0] + if k not in d.keys(): + return {} + c = {k: d[k]} + lpath = lpath[1:] + if not lpath: + return c + elif not isinstance(c[k], dict): + return {} + return _get_sub_dict(c[k], lpath) + +def get_sub_dict(source, lpath, get_first_key=False): + """ Returns the sub-dict of a nested dict, defined by path of keys. + + Args: + source (dict): Source dict to extract from + lpath (list[str]): sequence of keys + + Returns: source, if lpath is empty, else + {key : source[..]..[key]} for key the last element of lpath, if exists + {} otherwise + """ + if not isinstance(source, dict): + raise TypeError("source must be of type dict") + if not isinstance(lpath, list): + raise TypeError("path must be of type list") + if not lpath: + return source + + ret = _get_sub_dict(source, lpath) + + if get_first_key and lpath and ret: + tmp = next(iter(ret.values())) + if not isinstance(tmp, dict): + raise TypeError("Data under node is not of type dict") + ret = tmp + + return ret + +def dict_search(path, dict_object): + """ Traverse Python dictionary (dict_object) delimited by dot (.). + Return value of key if found, None otherwise. + + This is faster implementation then jmespath.search('foo.bar', dict_object)""" + if not isinstance(dict_object, dict) or not path: + return None + + parts = path.split('.') + inside = parts[:-1] + if not inside: + if path not in dict_object: + return None + return dict_object[path] + c = dict_object + for p in parts[:-1]: + c = c.get(p, {}) + return c.get(parts[-1], None) + +def dict_search_args(dict_object, *path): + # Traverse dictionary using variable arguments + # Added due to above function not allowing for '.' in the key names + # Example: dict_search_args(some_dict, 'key', 'subkey', 'subsubkey', ...) + if not isinstance(dict_object, dict) or not path: + return None + + for item in path: + if item not in dict_object: + return None + dict_object = dict_object[item] + return dict_object + +def dict_search_recursive(dict_object, key, path=[]): + """ Traverse a dictionary recurisvely and return the value of the key + we are looking for. + + Thankfully copied from https://stackoverflow.com/a/19871956 + + Modified to yield optional path to found keys + """ + if isinstance(dict_object, list): + for i in dict_object: + new_path = path + [i] + for x in dict_search_recursive(i, key, new_path): + yield x + elif isinstance(dict_object, dict): + if key in dict_object: + new_path = path + [key] + yield dict_object[key], new_path + for k, j in dict_object.items(): + new_path = path + [k] + for x in dict_search_recursive(j, key, new_path): + yield x + +def dict_to_list(d, save_key_to=None): + """ Convert a dict to a list of dicts. + + Optionally, save the original key of the dict inside + dicts stores in that list. + """ + def save_key(i, k): + if isinstance(i, dict): + i[save_key_to] = k + return + elif isinstance(i, list): + for _i in i: + save_key(_i, k) + else: + raise ValueError(f"Cannot save the key: the item is {type(i)}, not a dict") + + collect = [] + + for k,_ in d.items(): + item = d[k] + if save_key_to is not None: + save_key(item, k) + if isinstance(item, list): + collect += item + else: + collect.append(item) + + return collect diff --git a/python/vyos/xml/load.py b/python/vyos/xml/load.py index c3022f3d6..f842ff9ce 100644 --- a/python/vyos/xml/load.py +++ b/python/vyos/xml/load.py @@ -71,16 +71,12 @@ def _merge(dict1, dict2): continue if isinstance(dict1[k], dict) and isinstance(dict2[k], dict): dict1[k] = _merge(dict1[k], dict2[k]) - elif isinstance(dict1[k], dict) and isinstance(dict2[k], dict): + elif isinstance(dict1[k], list) and isinstance(dict2[k], list): dict1[k].extend(dict2[k]) elif dict1[k] == dict2[k]: - # A definition shared between multiple files - if k in (kw.valueless, kw.multi, kw.hidden, kw.node, kw.summary, kw.owner, kw.priority): - continue - _fatal() - raise RuntimeError('parsing issue - undefined leaf?') + continue else: - raise RuntimeError('parsing issue - we messed up?') + dict1[k] = dict2[k] return dict1 @@ -131,7 +127,7 @@ def _format_nodes(inside, conf, xml): name = node.pop('@name') into = inside + [name] if name in r: - r[name].update(_format_node(into, node, xml)) + _merge(r[name], _format_node(into, node, xml)) else: r[name] = _format_node(into, node, xml) r[name][kw.node] = nodename @@ -141,7 +137,7 @@ def _format_nodes(inside, conf, xml): name = node.pop('@name') into = inside + [name] if name in r: - r[name].update(_format_node(inside + [name], node, xml)) + _merge(r[name], _format_node(inside + [name], node, xml)) else: r[name] = _format_node(inside + [name], node, xml) r[name][kw.node] = nodename @@ -180,10 +176,10 @@ def _format_node(inside, conf, xml): if isinstance(conf, list): for child in children: - r = _safe_update(r, _format_nodes(inside, child, xml)) + _merge(r, _format_nodes(inside, child, xml)) else: child = children - r = _safe_update(r, _format_nodes(inside, child, xml)) + _merge(r, _format_nodes(inside, child, xml)) elif 'properties' in keys: properties = conf.pop('properties') diff --git a/smoketest/configs/vrf-bgp-pppoe-underlay b/smoketest/configs/vrf-bgp-pppoe-underlay new file mode 100644 index 000000000..cba35eab1 --- /dev/null +++ b/smoketest/configs/vrf-bgp-pppoe-underlay @@ -0,0 +1,473 @@ +interfaces { + bridge br50 { + address 192.168.0.1/24 + member { + interface eth0.50 { + } + interface eth2 { + } + interface eth3 { + } + } + } + dummy dum0 { + address 100.64.51.252/32 + address 2001:db8:200:ffff::1/128 + vrf vyos-test-01 + } + ethernet eth0 { + offload { + gro + gso + rps + sg + tso + } + ring-buffer { + rx 256 + tx 256 + } + vif 5 { + address 2001:db8:200:f0::114/64 + address 100.64.50.121/28 + vrf vyos-test-01 + } + vif 10 { + address 2001:db8:200:10::ffff/64 + address 2001:db8:200::ffff/64 + address 100.64.50.62/26 + vrf vyos-test-01 + } + vif 15 { + address 100.64.50.78/28 + address 2001:db8:200:15::ffff/64 + vrf vyos-test-01 + } + vif 50 { + description "Member of bridge br50" + } + vif 110 { + address 100.64.51.190/27 + address 100.64.51.158/28 + address 2001:db8:200:101::ffff/64 + vrf vyos-test-01 + } + vif 410 { + address 100.64.51.206/28 + address 2001:db8:200:104::ffff/64 + vrf vyos-test-01 + } + vif 500 { + address 100.64.51.238/28 + address 2001:db8:200:50::ffff/64 + vrf vyos-test-01 + } + vif 520 { + address 100.64.50.190/28 + address 2001:db8:200:520::ffff/64 + vrf vyos-test-01 + } + vif 666 { + address 2001:db8:200:ff::101:1/112 + address 100.64.51.223/31 + vrf vyos-test-01 + } + vif 800 { + address 2001:db8:200:ff::104:1/112 + address 100.64.51.212/31 + vrf vyos-test-01 + } + vif 810 { + address 100.64.51.30/27 + address 2001:db8:200:102::ffff/64 + vrf vyos-test-01 + } + } + ethernet eth1 { + offload { + gro + gso + rps + sg + tso + } + ring-buffer { + rx 256 + tx 256 + } + } + ethernet eth2 { + offload { + gro + gso + sg + tso + } + } + ethernet eth3 { + offload { + gro + gso + sg + tso + } + } + loopback lo { + } + pppoe pppoe7 { + authentication { + password vyos + username vyos + } + dhcpv6-options { + pd 0 { + interface br50 { + address 1 + } + length 56 + } + } + ip { + adjust-mss 1452 + } + ipv6 { + address { + autoconf + } + adjust-mss 1432 + } + mtu 1492 + no-peer-dns + source-interface eth1 + } + virtual-ethernet veth0 { + address 100.64.51.220/31 + address 2001:db8:200:ff::105:1/112 + description "Core: connect vyos-test-01 and default VRF" + peer-name veth1 + } + virtual-ethernet veth1 { + address 100.64.51.221/31 + address 2001:db8:200:ff::105:2/112 + description "Core: connect vyos-test-01 and default VRF" + peer-name veth0 + vrf vyos-test-01 + } + wireguard wg500 { + address 100.64.51.209/31 + mtu 1500 + peer A { + address 192.0.2.1 + allowed-ips 0.0.0.0/0 + port 5500 + public-key KGSXF4QckzGe7f7CT+r6VZ5brOD/pVYk8yvrxOQ+X0Y= + } + port 5500 + private-key iLJh6Me6AdPJtNv3dgGhUbtyFxExxmNU4v0Fs6YE2Xc= + vrf vyos-test-01 + } + wireguard wg501 { + address 2001:db8:200:ff::102:2/112 + mtu 1500 + peer A { + address 2001:db8:300::1 + allowed-ips ::/0 + port 5501 + public-key OF+1OJ+VfQ0Yw1mgVtQ2ion4CnAdy8Bvx7yEiO4+Pn8= + } + port 5501 + private-key 0MP5X0PW58O4q2LDpuIXgZ0ySyAoWH8/kdpvQccCbUU= + vrf vyos-test-01 + } + wireguard wg666 { + address 172.29.0.0/31 + mtu 1500 + peer B { + allowed-ips 0.0.0.0/0 + public-key 2HT+RfwcqJMYNYzdmtmpem8Ht0dL37o31APHVwmh024= + } + port 50666 + private-key zvPnp2MLAoX7SotuHLFLDyy4sdlD7ttbD1xNEqA3mkU= + } +} +nat { + source { + rule 100 { + outbound-interface pppoe7 + source { + address 192.168.0.0/24 + } + translation { + address masquerade + } + } + } +} +policy { + prefix-list AS100-origin-v4 { + rule 10 { + action permit + prefix 100.64.0.0/12 + } + rule 100 { + action permit + prefix 0.0.0.0/0 + } + } + prefix-list AS200-origin-v4 { + rule 10 { + action permit + prefix 10.0.0.0/8 + } + rule 20 { + action permit + prefix 172.16.0.0/12 + } + + } + prefix-list6 AS100-origin-v6 { + rule 10 { + action permit + prefix 2001:db8:200::/40 + } + } + prefix-list6 AS200-origin-v6 { + rule 10 { + action permit + prefix 2001:db8:100::/40 + } + } +} +protocols { + static { + route 192.0.2.255/32 { + interface pppoe7 { + } + } + route 100.64.50.0/23 { + next-hop 100.64.51.221 { + } + } + route6 2001:db8:ffff:ffff:ffff:ffff:ffff:ffff/128 { + interface pppoe7 { + } + } + } +} +qos { + interface pppoe7 { + egress isp-out + } + policy { + shaper isp-out { + bandwidth 38mbit + default { + bandwidth 100% + burst 15k + queue-limit 1000 + queue-type fq-codel + } + } + } +} +service { + router-advert { + interface br50 { + prefix ::/64 { + preferred-lifetime 2700 + valid-lifetime 5400 + } + } + interface eth0.500 { + default-preference high + name-server 2001:db8:200::1 + name-server 2001:db8:200::2 + prefix 2001:db8:200:50::/64 { + valid-lifetime infinity + } + } + interface eth0.520 { + default-preference high + name-server 2001:db8:200::1 + name-server 2001:db8:200::2 + prefix 2001:db8:200:520::/64 { + valid-lifetime infinity + } + } + } + ssh { + disable-host-validation + dynamic-protection { + allow-from 100.64.0.0/10 + allow-from 2001:db8:200::/40 + } + } +} +system { + config-management { + commit-revisions 100 + } + conntrack { + modules { + ftp + h323 + nfs + pptp + sip + sqlnet + tftp + } + } + console { + device ttyS0 { + speed 115200 + } + } + domain-name vyos.net + host-name vyos + login { + user vyos { + authentication { + encrypted-password $6$O5gJRlDYQpj$MtrCV9lxMnZPMbcxlU7.FI793MImNHznxGoMFgm3Q6QP3vfKJyOSRCt3Ka/GzFQyW1yZS4NS616NLHaIPPFHc0 + plaintext-password "" + } + } + } + name-server 192.168.0.1 + syslog { + global { + facility all { + level info + } + facility protocols { + level debug + } + } + } + time-zone Europe/Berlin +} +vrf { + bind-to-all + name vyos-test-01 { + protocols { + bgp { + address-family { + ipv4-unicast { + network 100.64.50.0/23 { + } + } + ipv6-unicast { + network 2001:db8:200:ffff::1/128 { + } + } + } + neighbor 100.64.51.208 { + peer-group AS100v4 + } + neighbor 100.64.51.222 { + address-family { + ipv4-unicast { + default-originate { + } + maximum-prefix 10 + prefix-list { + export AS100-origin-v4 + import AS200-origin-v4 + } + soft-reconfiguration { + inbound + } + } + } + capability { + dynamic + } + remote-as 200 + } + neighbor 100.64.51.251 { + peer-group AS100v4 + shutdown + } + neighbor 100.64.51.254 { + peer-group AS100v4 + shutdown + } + neighbor 2001:db8:200:ffff::2 { + peer-group AS100v6 + shutdown + } + neighbor 2001:db8:200:ffff::a { + peer-group AS100v6 + } + neighbor 2001:db8:200:ff::101:2 { + address-family { + ipv6-unicast { + maximum-prefix 10 + prefix-list { + export AS100-origin-v6 + import AS200-origin-v6 + } + soft-reconfiguration { + inbound + } + } + } + capability { + dynamic + } + remote-as 200 + } + peer-group AS100v4 { + address-family { + ipv4-unicast { + nexthop-self { + } + } + } + capability { + dynamic + } + remote-as internal + update-source dum0 + } + peer-group AS100v6 { + address-family { + ipv6-unicast { + nexthop-self { + } + } + } + capability { + dynamic + } + remote-as internal + update-source dum0 + } + system-as 100 + } + static { + route 192.168.0.0/24 { + next-hop 100.64.51.220 { + } + } + route 100.64.50.0/23 { + blackhole { + } + } + route 100.64.51.32/27 { + next-hop 100.64.51.5 { + } + } + route6 2001:db8:2fe:ffff::/64 { + next-hop 2001:db8:200:102::5 { + } + } + } + } + table 1000 + } +} + +// Warning: Do not remove the following line. +// vyos-config-version: "bgp@3:broadcast-relay@1:cluster@1:config-management@1:conntrack@3:conntrack-sync@2:container@1:dhcp-relay@2:dhcp-server@6:dhcpv6-server@1:dns-forwarding@3:firewall@9:flow-accounting@1:https@4:ids@1:interfaces@28:ipoe-server@1:ipsec@12:isis@2:l2tp@4:lldp@1:mdns@1:monitoring@1:nat@5:nat66@1:ntp@2:openconnect@2:ospf@1:policy@5:pppoe-server@6:pptp@2:qos@2:quagga@10:rpki@1:salt@1:snmp@3:ssh@2:sstp@4:system@25:vrf@3:vrrp@3:vyos-accel-ppp@2:wanloadbalance@3:webproxy@2" +// Release version: 1.4-rolling-202303160317 diff --git a/smoketest/scripts/cli/test_policy.py b/smoketest/scripts/cli/test_policy.py index 3a4ef666a..f35cdaa4c 100755 --- a/smoketest/scripts/cli/test_policy.py +++ b/smoketest/scripts/cli/test_policy.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2021-2022 VyOS maintainers and contributors +# Copyright (C) 2021-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 @@ -1071,6 +1071,22 @@ class TestPolicy(VyOSUnitTestSHIM.TestCase): }, }, }, + 'match-protocol' : { + 'rule' : { + '10' : { + 'action' : 'permit', + 'match' : { + 'protocol' : 'static', + }, + }, + '20' : { + 'action' : 'permit', + 'match' : { + 'protocol' : 'bgp', + }, + }, + }, + }, 'relative-metric' : { 'rule' : { '10' : { @@ -1202,6 +1218,8 @@ class TestPolicy(VyOSUnitTestSHIM.TestCase): self.cli_set(path + ['rule', rule, 'match', 'rpki', 'notfound']) if 'rpki-valid' in rule_config['match']: self.cli_set(path + ['rule', rule, 'match', 'rpki', 'valid']) + if 'protocol' in rule_config['match']: + self.cli_set(path + ['rule', rule, 'match', 'protocol', rule_config['match']['protocol']]) if 'tag' in rule_config['match']: self.cli_set(path + ['rule', rule, 'match', 'tag', rule_config['match']['tag']]) @@ -1368,6 +1386,9 @@ class TestPolicy(VyOSUnitTestSHIM.TestCase): if 'peer' in rule_config['match']: tmp = f'match peer {rule_config["match"]["peer"]}' self.assertIn(tmp, config) + if 'protocol' in rule_config['match']: + tmp = f'match source-protocol {rule_config["match"]["protocol"]}' + self.assertIn(tmp, config) if 'rpki-invalid' in rule_config['match']: tmp = f'match rpki invalid' self.assertIn(tmp, config) diff --git a/smoketest/scripts/cli/test_protocols_static.py b/smoketest/scripts/cli/test_protocols_static.py index bc023f3f2..275f1a1df 100755 --- a/smoketest/scripts/cli/test_protocols_static.py +++ b/smoketest/scripts/cli/test_protocols_static.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2021-2022 VyOS maintainers and contributors +# Copyright (C) 2021-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 @@ -31,6 +31,8 @@ routes = { '192.0.2.100' : { 'distance' : '100' }, '192.0.2.110' : { 'distance' : '110', 'interface' : 'eth0' }, '192.0.2.120' : { 'distance' : '120', 'disable' : '' }, + '192.0.2.130' : { 'bfd' : '' }, + '192.0.2.140' : { 'bfd_source' : '192.0.2.10' }, }, 'interface' : { 'eth0' : { 'distance' : '130' }, @@ -67,6 +69,8 @@ routes = { '2001:db8::1' : { 'distance' : '10' }, '2001:db8::2' : { 'distance' : '20', 'interface' : 'eth0' }, '2001:db8::3' : { 'distance' : '30', 'disable' : '' }, + '2001:db8::4' : { 'bfd' : '' }, + '2001:db8::5' : { 'bfd_source' : '2001:db8::ffff' }, }, 'interface' : { 'eth0' : { 'distance' : '40', 'vrf' : 'black' }, @@ -95,6 +99,7 @@ class TestProtocolsStatic(VyOSUnitTestSHIM.TestCase): @classmethod def setUpClass(cls): super(TestProtocolsStatic, cls).setUpClass() + cls.cli_delete(cls, ['vrf']) cls.cli_set(cls, ['vrf', 'name', 'black', 'table', '43210']) @classmethod @@ -116,6 +121,7 @@ class TestProtocolsStatic(VyOSUnitTestSHIM.TestCase): self.cli_commit() def test_01_static(self): + bfd_profile = 'vyos-test' for route, route_config in routes.items(): route_type = 'route' if is_ipv6(route): @@ -132,6 +138,10 @@ class TestProtocolsStatic(VyOSUnitTestSHIM.TestCase): self.cli_set(base + ['next-hop', next_hop, 'interface', next_hop_config['interface']]) if 'vrf' in next_hop_config: self.cli_set(base + ['next-hop', next_hop, 'vrf', next_hop_config['vrf']]) + if 'bfd' in next_hop_config: + self.cli_set(base + ['next-hop', next_hop, 'bfd', 'profile', bfd_profile ]) + if 'bfd_source' in next_hop_config: + self.cli_set(base + ['next-hop', next_hop, 'bfd', 'multi-hop', 'source', next_hop_config['bfd_source'], 'profile', bfd_profile]) if 'interface' in route_config: @@ -186,6 +196,10 @@ class TestProtocolsStatic(VyOSUnitTestSHIM.TestCase): tmp += ' ' + next_hop_config['distance'] if 'vrf' in next_hop_config: tmp += ' nexthop-vrf ' + next_hop_config['vrf'] + if 'bfd' in next_hop_config: + tmp += ' bfd profile ' + bfd_profile + if 'bfd_source' in next_hop_config: + tmp += ' bfd multi-hop source ' + next_hop_config['bfd_source'] + ' profile ' + bfd_profile if 'disable' in next_hop_config: self.assertNotIn(tmp, frrconfig) diff --git a/smoketest/scripts/cli/test_vrf.py b/smoketest/scripts/cli/test_vrf.py index 8016c0105..926616727 100755 --- a/smoketest/scripts/cli/test_vrf.py +++ b/smoketest/scripts/cli/test_vrf.py @@ -61,7 +61,8 @@ class VRFTest(VyOSUnitTestSHIM.TestCase): self.assertNotIn(vrf, interfaces()) def test_vrf_vni_and_table_id(self): - table = '1000' + base_table = '1000' + table = base_table for vrf in vrfs: base = base_path + ['name', vrf] description = f'VyOS-VRF-{vrf}' @@ -82,7 +83,7 @@ class VRFTest(VyOSUnitTestSHIM.TestCase): self.cli_commit() # Verify VRF configuration - table = '1000' + table = base_table iproute2_config = read_file('/etc/iproute2/rt_tables.d/vyos-vrf.conf') for vrf in vrfs: description = f'VyOS-VRF-{vrf}' @@ -196,7 +197,8 @@ class VRFTest(VyOSUnitTestSHIM.TestCase): self.cli_delete(['interfaces', section, interface, 'vrf']) def test_vrf_static_route(self): - table = '100' + base_table = '100' + table = base_table for vrf in vrfs: next_hop = f'192.0.{table}.1' prefix = f'10.0.{table}.0/24' @@ -217,13 +219,12 @@ class VRFTest(VyOSUnitTestSHIM.TestCase): self.cli_commit() # Verify VRF configuration - table = '100' + table = base_table for vrf in vrfs: next_hop = f'192.0.{table}.1' prefix = f'10.0.{table}.0/24' self.assertTrue(vrf in interfaces()) - vrf_if = Interface(vrf) frrconfig = self.getFRRconfig(f'vrf {vrf}') self.assertIn(f' vni {table}', frrconfig) @@ -369,5 +370,98 @@ class VRFTest(VyOSUnitTestSHIM.TestCase): route_map = f'route-map-{vrf}-{protocol}' self.assertIn(f' ipv6 protocol {protocol} route-map {route_map}', frrconfig) + def test_vrf_vni_duplicates(self): + base_table = '6300' + table = base_table + for vrf in vrfs: + base = base_path + ['name', vrf] + self.cli_set(base + ['table', str(table)]) + self.cli_set(base + ['vni', '100']) + table = str(int(table) + 1) + + # L3VNIs can only be used once + with self.assertRaises(ConfigSessionError): + self.cli_commit() + + table = base_table + for vrf in vrfs: + base = base_path + ['name', vrf] + self.cli_set(base + ['vni', str(table)]) + table = str(int(table) + 1) + + # commit changes + self.cli_commit() + + # Verify VRF configuration + table = base_table + for vrf in vrfs: + self.assertTrue(vrf in interfaces()) + + frrconfig = self.getFRRconfig(f'vrf {vrf}') + self.assertIn(f' vni {table}', frrconfig) + # Increment table ID for the next run + table = str(int(table) + 1) + + def test_vrf_vni_add_change_remove(self): + base_table = '6300' + table = base_table + for vrf in vrfs: + base = base_path + ['name', vrf] + self.cli_set(base + ['table', str(table)]) + self.cli_set(base + ['vni', str(table)]) + table = str(int(table) + 1) + + # commit changes + self.cli_commit() + + # Verify VRF configuration + table = base_table + for vrf in vrfs: + self.assertTrue(vrf in interfaces()) + + frrconfig = self.getFRRconfig(f'vrf {vrf}') + self.assertIn(f' vni {table}', frrconfig) + # Increment table ID for the next run + table = str(int(table) + 1) + + # Now change all L3VNIs (increment 2) + # We must also change the base_table number as we probably could get + # duplicate VNI's during the test as VNIs are applied 1:1 to FRR + base_table = '5000' + table = base_table + for vrf in vrfs: + base = base_path + ['name', vrf] + self.cli_set(base + ['vni', str(table)]) + table = str(int(table) + 2) + + # commit changes + self.cli_commit() + + # Verify VRF configuration + table = base_table + for vrf in vrfs: + self.assertTrue(vrf in interfaces()) + + frrconfig = self.getFRRconfig(f'vrf {vrf}') + self.assertIn(f' vni {table}', frrconfig) + # Increment table ID for the next run + table = str(int(table) + 2) + + # Now delete all the VNIs + for vrf in vrfs: + base = base_path + ['name', vrf] + self.cli_delete(base + ['vni']) + + # commit changes + self.cli_commit() + + # Verify no VNI is defined + for vrf in vrfs: + self.assertTrue(vrf in interfaces()) + + frrconfig = self.getFRRconfig(f'vrf {vrf}') + self.assertNotIn('vni', frrconfig) + + if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/src/conf_mode/dns_forwarding.py b/src/conf_mode/dns_forwarding.py index 36c1098fe..0d86c6a52 100755 --- a/src/conf_mode/dns_forwarding.py +++ b/src/conf_mode/dns_forwarding.py @@ -99,7 +99,7 @@ def get_config(config=None): recorddata = zonedata['records'] - for rtype in [ 'a', 'aaaa', 'cname', 'mx', 'ptr', 'txt', 'spf', 'srv', 'naptr' ]: + for rtype in [ 'a', 'aaaa', 'cname', 'mx', 'ns', 'ptr', 'txt', 'spf', 'srv', 'naptr' ]: if rtype not in recorddata: continue for subnode in recorddata[rtype]: @@ -113,7 +113,7 @@ def get_config(config=None): rdata = dict_merge(rdefaults, rdata) if not 'address' in rdata: - dns['authoritative_zone_errors'].append('{}.{}: at least one address is required'.format(subnode, node)) + dns['authoritative_zone_errors'].append(f'{subnode}.{node}: at least one address is required') continue if subnode == 'any': @@ -126,12 +126,12 @@ def get_config(config=None): 'ttl': rdata['ttl'], 'value': address }) - elif rtype in ['cname', 'ptr']: + elif rtype in ['cname', 'ptr', 'ns']: rdefaults = defaults(base + ['authoritative-domain', 'records', rtype]) # T2665 rdata = dict_merge(rdefaults, rdata) if not 'target' in rdata: - dns['authoritative_zone_errors'].append('{}.{}: target is required'.format(subnode, node)) + dns['authoritative_zone_errors'].append(f'{subnode}.{node}: target is required') continue zone['records'].append({ @@ -146,7 +146,7 @@ def get_config(config=None): rdata = dict_merge(rdefaults, rdata) if not 'server' in rdata: - dns['authoritative_zone_errors'].append('{}.{}: at least one server is required'.format(subnode, node)) + dns['authoritative_zone_errors'].append(f'{subnode}.{node}: at least one server is required') continue for servername in rdata['server']: @@ -164,7 +164,7 @@ def get_config(config=None): rdata = dict_merge(rdefaults, rdata) if not 'value' in rdata: - dns['authoritative_zone_errors'].append('{}.{}: at least one value is required'.format(subnode, node)) + dns['authoritative_zone_errors'].append(f'{subnode}.{node}: at least one value is required') continue for value in rdata['value']: @@ -179,7 +179,7 @@ def get_config(config=None): rdata = dict_merge(rdefaults, rdata) if not 'value' in rdata: - dns['authoritative_zone_errors'].append('{}.{}: value is required'.format(subnode, node)) + dns['authoritative_zone_errors'].append(f'{subnode}.{node}: value is required') continue zone['records'].append({ @@ -194,7 +194,7 @@ def get_config(config=None): rdata = dict_merge(rdefaults, rdata) if not 'entry' in rdata: - dns['authoritative_zone_errors'].append('{}.{}: at least one entry is required'.format(subnode, node)) + dns['authoritative_zone_errors'].append(f'{subnode}.{node}: at least one entry is required') continue for entryno in rdata['entry']: @@ -203,11 +203,11 @@ def get_config(config=None): entrydata = dict_merge(entrydefaults, entrydata) if not 'hostname' in entrydata: - dns['authoritative_zone_errors'].append('{}.{}: hostname is required for entry {}'.format(subnode, node, entryno)) + dns['authoritative_zone_errors'].append(f'{subnode}.{node}: hostname is required for entry {entryno}') continue if not 'port' in entrydata: - dns['authoritative_zone_errors'].append('{}.{}: port is required for entry {}'.format(subnode, node, entryno)) + dns['authoritative_zone_errors'].append(f'{subnode}.{node}: port is required for entry {entryno}') continue zone['records'].append({ @@ -223,7 +223,7 @@ def get_config(config=None): if not 'rule' in rdata: - dns['authoritative_zone_errors'].append('{}.{}: at least one rule is required'.format(subnode, node)) + dns['authoritative_zone_errors'].append(f'{subnode}.{node}: at least one rule is required') continue for ruleno in rdata['rule']: diff --git a/src/conf_mode/load-balancing-wan.py b/src/conf_mode/load-balancing-wan.py index 11840249f..7086aaf8b 100755 --- a/src/conf_mode/load-balancing-wan.py +++ b/src/conf_mode/load-balancing-wan.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2022 VyOS maintainers and contributors +# Copyright (C) 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 @@ -14,17 +14,25 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. +import os from sys import exit +from shutil import rmtree +from vyos.base import Warning from vyos.config import Config -from vyos.configdict import node_changed -from vyos.util import call +from vyos.configdict import dict_merge +from vyos.util import cmd +from vyos.template import render +from vyos.xml import defaults from vyos import ConfigError -from pprint import pprint from vyos import airbag airbag.enable() +load_balancing_dir = '/run/load-balance' +load_balancing_conf_file = f'{load_balancing_dir}/wlb.conf' +systemd_service = 'vyos-wan-load-balance.service' + def get_config(config=None): if config: @@ -33,27 +41,135 @@ def get_config(config=None): conf = Config() base = ['load-balancing', 'wan'] - lb = conf.get_config_dict(base, get_first_key=True, - no_tag_node_value_mangle=True) + lb = conf.get_config_dict(base, + get_first_key=True, + key_mangling=('-', '_'), + no_tag_node_value_mangle=True) + + # We have gathered the dict representation of the CLI, but there are default + # options which we need to update into the dictionary retrived. + default_values = defaults(base) + # lb base default values can not be merged here - remove and add them later + if 'interface_health' in default_values: + del default_values['interface_health'] + if 'rule' in default_values: + del default_values['rule'] + lb = dict_merge(default_values, lb) + + if 'interface_health' in lb: + for iface in lb.get('interface_health'): + default_values_iface = defaults(base + ['interface-health']) + if 'test' in default_values_iface: + del default_values_iface['test'] + lb['interface_health'][iface] = dict_merge( + default_values_iface, lb['interface_health'][iface]) + if 'test' in lb['interface_health'][iface]: + for node_test in lb['interface_health'][iface]['test']: + default_values_test = defaults(base + + ['interface-health', 'test']) + lb['interface_health'][iface]['test'][node_test] = dict_merge( + default_values_test, + lb['interface_health'][iface]['test'][node_test]) + + if 'rule' in lb: + for rule in lb.get('rule'): + default_values_rule = defaults(base + ['rule']) + if 'interface' in default_values_rule: + del default_values_rule['interface'] + lb['rule'][rule] = dict_merge(default_values_rule, lb['rule'][rule]) + if not conf.exists(base + ['rule', rule, 'limit']): + del lb['rule'][rule]['limit'] + if 'interface' in lb['rule'][rule]: + for iface in lb['rule'][rule]['interface']: + default_values_rule_iface = defaults(base + ['rule', 'interface']) + lb['rule'][rule]['interface'][iface] = dict_merge(default_values_rule_iface, lb['rule'][rule]['interface'][iface]) - pprint(lb) return lb + def verify(lb): - return None + if not lb: + return None + + if 'interface_health' not in lb: + raise ConfigError( + 'A valid WAN load-balance configuration requires an interface with a nexthop!' + ) + + for interface, interface_config in lb['interface_health'].items(): + if 'nexthop' not in interface_config: + raise ConfigError( + f'interface-health {interface} nexthop must be specified!') + + if 'test' in interface_config: + for test_rule, test_config in interface_config['test'].items(): + if 'type' in test_config: + if test_config['type'] == 'user-defined' and 'test_script' not in test_config: + raise ConfigError( + f'test {test_rule} script must be defined for test-script!' + ) + + if 'rule' not in lb: + Warning( + 'At least one rule with an (outbound) interface must be defined for WAN load balancing to be active!' + ) + else: + for rule, rule_config in lb['rule'].items(): + if 'inbound_interface' not in rule_config: + raise ConfigError(f'rule {rule} inbound-interface must be specified!') + if {'failover', 'exclude'} <= set(rule_config): + raise ConfigError(f'rule {rule} failover cannot be configured with exclude!') + if {'limit', 'exclude'} <= set(rule_config): + raise ConfigError(f'rule {rule} limit cannot be used with exclude!') + if 'interface' not in rule_config: + if 'exclude' not in rule_config: + Warning( + f'rule {rule} will be inactive because no (outbound) interfaces have been defined for this rule' + ) + for direction in {'source', 'destination'}: + if direction in rule_config: + if 'protocol' in rule_config and 'port' in rule_config[ + direction]: + if rule_config['protocol'] not in {'tcp', 'udp'}: + raise ConfigError('ports can only be specified when protocol is "tcp" or "udp"') def generate(lb): if not lb: + # Delete /run/load-balance/wlb.conf + if os.path.isfile(load_balancing_conf_file): + os.unlink(load_balancing_conf_file) + # Delete old directories + if os.path.isdir(load_balancing_dir): + rmtree(load_balancing_dir, ignore_errors=True) + if os.path.exists('/var/run/load-balance/wlb.out'): + os.unlink('/var/run/load-balance/wlb.out') + return None + # Create load-balance dir + if not os.path.isdir(load_balancing_dir): + os.mkdir(load_balancing_dir) + + render(load_balancing_conf_file, 'load-balancing/wlb.conf.j2', lb) + return None def apply(lb): + if not lb: + try: + cmd(f'systemctl stop {systemd_service}') + except Exception as e: + print(f"Error message: {e}") + + else: + cmd('sudo sysctl -w net.netfilter.nf_conntrack_acct=1') + cmd(f'systemctl restart {systemd_service}') return None + if __name__ == '__main__': try: c = get_config() diff --git a/src/conf_mode/protocols_bgp.py b/src/conf_mode/protocols_bgp.py index 66505e58d..b23584bdb 100755 --- a/src/conf_mode/protocols_bgp.py +++ b/src/conf_mode/protocols_bgp.py @@ -50,16 +50,24 @@ def get_config(config=None): bgp = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True, no_tag_node_value_mangle=True) - # Assign the name of our VRF context. This MUST be done before the return - # statement below, else on deletion we will delete the default instance - # instead of the VRF instance. - if vrf: bgp.update({'vrf' : vrf}) - bgp['dependent_vrfs'] = conf.get_config_dict(['vrf', 'name'], key_mangling=('-', '_'), get_first_key=True, no_tag_node_value_mangle=True) + # Assign the name of our VRF context. This MUST be done before the return + # statement below, else on deletion we will delete the default instance + # instead of the VRF instance. + if vrf: + bgp.update({'vrf' : vrf}) + # We can not delete the BGP VRF instance if there is a L3VNI configured + tmp = ['vrf', 'name', vrf, 'vni'] + if conf.exists(tmp): + bgp.update({'vni' : conf.return_value(tmp)}) + # We can safely delete ourself from the dependent vrf list + if vrf in bgp['dependent_vrfs']: + del bgp['dependent_vrfs'][vrf] + bgp['dependent_vrfs'].update({'default': {'protocols': { 'bgp': conf.get_config_dict(base_path, key_mangling=('-', '_'), get_first_key=True, @@ -202,9 +210,13 @@ def verify(bgp): if 'vrf' in bgp: # Cannot delete vrf if it exists in import vrf list in other vrfs for tmp_afi in ['ipv4_unicast', 'ipv6_unicast']: - if verify_vrf_as_import(bgp['vrf'],tmp_afi,bgp['dependent_vrfs']): - raise ConfigError(f'Cannot delete vrf {bgp["vrf"]} instance, ' \ - 'Please unconfigure import vrf commands!') + if verify_vrf_as_import(bgp['vrf'], tmp_afi, bgp['dependent_vrfs']): + raise ConfigError(f'Cannot delete VRF instance "{bgp["vrf"]}", ' \ + 'unconfigure "import vrf" commands!') + # We can not delete the BGP instance if a L3VNI instance exists + if 'vni' in bgp: + raise ConfigError(f'Cannot delete VRF instance "{bgp["vrf"]}", ' \ + f'unconfigure VNI "{bgp["vni"]}" first!') else: # We are running in the default VRF context, thus we can not delete # our main BGP instance if there are dependent BGP VRF instances. @@ -429,7 +441,6 @@ def verify(bgp): f'{afi} administrative distance {key}!') if afi in ['ipv4_unicast', 'ipv6_unicast']: - vrf_name = bgp['vrf'] if dict_search('vrf', bgp) else 'default' # Verify if currant VRF contains rd and route-target options # and does not exist in import list in other VRFs @@ -478,6 +489,15 @@ def verify(bgp): tmp = dict_search(f'route_map.vpn.{export_import}', afi_config) if tmp: verify_route_map(tmp, bgp) + # Checks only required for L2VPN EVPN + if afi in ['l2vpn_evpn']: + if 'vni' in afi_config: + for vni, vni_config in afi_config['vni'].items(): + if 'rd' in vni_config and 'advertise_all_vni' not in afi_config: + raise ConfigError('BGP EVPN "rd" requires "advertise-all-vni" to be set!') + if 'route_target' in vni_config and 'advertise_all_vni' not in afi_config: + raise ConfigError('BGP EVPN "route-target" requires "advertise-all-vni" to be set!') + return None def generate(bgp): diff --git a/src/conf_mode/protocols_isis.py b/src/conf_mode/protocols_isis.py index af2937db8..ecca87db0 100755 --- a/src/conf_mode/protocols_isis.py +++ b/src/conf_mode/protocols_isis.py @@ -129,7 +129,7 @@ def verify(isis): vrf = isis['vrf'] tmp = get_interface_config(interface) if 'master' not in tmp or tmp['master'] != vrf: - raise ConfigError(f'Interface {interface} is not a member of VRF {vrf}!') + raise ConfigError(f'Interface "{interface}" is not a member of VRF "{vrf}"!') # If md5 and plaintext-password set at the same time for password in ['area_password', 'domain_password']: diff --git a/src/conf_mode/protocols_ospf.py b/src/conf_mode/protocols_ospf.py index fbb876123..b73483470 100755 --- a/src/conf_mode/protocols_ospf.py +++ b/src/conf_mode/protocols_ospf.py @@ -196,7 +196,7 @@ def verify(ospf): vrf = ospf['vrf'] tmp = get_interface_config(interface) if 'master' not in tmp or tmp['master'] != vrf: - raise ConfigError(f'Interface {interface} is not a member of VRF {vrf}!') + raise ConfigError(f'Interface "{interface}" is not a member of VRF "{vrf}"!') # Segment routing checks if dict_search('segment_routing.global_block', ospf): diff --git a/src/conf_mode/protocols_ospfv3.py b/src/conf_mode/protocols_ospfv3.py index ee1fdd399..cb21bd83c 100755 --- a/src/conf_mode/protocols_ospfv3.py +++ b/src/conf_mode/protocols_ospfv3.py @@ -138,7 +138,7 @@ def verify(ospfv3): vrf = ospfv3['vrf'] tmp = get_interface_config(interface) if 'master' not in tmp or tmp['master'] != vrf: - raise ConfigError(f'Interface {interface} is not a member of VRF {vrf}!') + raise ConfigError(f'Interface "{interface}" is not a member of VRF "{vrf}"!') return None diff --git a/src/conf_mode/vrf.py b/src/conf_mode/vrf.py index a7ef4cb5c..0b983293e 100755 --- a/src/conf_mode/vrf.py +++ b/src/conf_mode/vrf.py @@ -108,6 +108,12 @@ def get_config(config=None): # vyos.configverify.verify_common_route_maps() for more information. tmp = {'policy' : {'route-map' : conf.get_config_dict(['policy', 'route-map'], get_first_key=True)}} + + # L3VNI setup is done via vrf_vni.py as it must be de-configured (on node + # deletetion prior to the BGP process. Tell the Jinja2 template no VNI + # setup is needed + vrf.update({'no_vni' : ''}) + # Merge policy dict into "regular" config dict vrf = dict_merge(tmp, vrf) return vrf @@ -124,8 +130,8 @@ def verify(vrf): f'static routes installed!') if 'name' in vrf: - reserved_names = ["add", "all", "broadcast", "default", "delete", "dev", "get", "inet", "mtu", "link", "type", - "vrf"] + reserved_names = ["add", "all", "broadcast", "default", "delete", "dev", + "get", "inet", "mtu", "link", "type", "vrf"] table_ids = [] for name, vrf_config in vrf['name'].items(): # Reserved VRF names @@ -142,8 +148,8 @@ def verify(vrf): if tmp and tmp != vrf_config['table']: raise ConfigError(f'VRF "{name}" table id modification not possible!') - # VRf routing table ID must be unique on the system - if vrf_config['table'] in table_ids: + # VRF routing table ID must be unique on the system + if 'table' in vrf_config and vrf_config['table'] in table_ids: raise ConfigError(f'VRF "{name}" table id is not unique!') table_ids.append(vrf_config['table']) diff --git a/src/conf_mode/vrf_vni.py b/src/conf_mode/vrf_vni.py new file mode 100644 index 000000000..9f33536e5 --- /dev/null +++ b/src/conf_mode/vrf_vni.py @@ -0,0 +1,104 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 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 +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +from sys import argv +from sys import exit + +from vyos.config import Config +from vyos.template import render_to_string +from vyos.util import dict_search +from vyos import ConfigError +from vyos import frr +from vyos import airbag +airbag.enable() + +def get_config(config=None): + if config: + conf = config + else: + conf = Config() + + vrf_name = None + if len(argv) > 1: + vrf_name = argv[1] + else: + return None + + # Using duplicate L3VNIs makes no sense - it's also forbidden in FRR, + # thus VyOS CLI must deny this, too. Instead of getting only the dict for + # the requested VRF and den comparing it with depenent VRfs to not have any + # duplicate we will just grad ALL VRFs by default but only render/apply + # the configuration for the requested VRF - that makes the code easier and + # hopefully less error prone + vrf = conf.get_config_dict(['vrf'], key_mangling=('-', '_'), + no_tag_node_value_mangle=True, + get_first_key=True) + + # Store name of VRF we are interested in for FRR config rendering + vrf.update({'only_vrf' : vrf_name}) + + return vrf + +def verify(vrf): + if not vrf: + return + + if len(argv) < 2: + raise ConfigError('VRF parameter not specified when valling vrf_vni.py') + + if 'name' in vrf: + vni_ids = [] + for name, vrf_config in vrf['name'].items(): + # VRF VNI (Virtual Network Identifier) must be unique on the system + if 'vni' in vrf_config: + if vrf_config['vni'] in vni_ids: + raise ConfigError(f'VRF "{name}" VNI is not unique!') + vni_ids.append(vrf_config['vni']) + + return None + +def generate(vrf): + if not vrf: + return + + vrf['new_frr_config'] = render_to_string('frr/zebra.vrf.route-map.frr.j2', vrf) + return None + +def apply(vrf): + frr_daemon = 'zebra' + + # add configuration to FRR + frr_cfg = frr.FRRConfig() + frr_cfg.load_configuration(frr_daemon) + # There is only one VRF inside the dict as we read only one in get_config() + if vrf and 'only_vrf' in vrf: + vrf_name = vrf['only_vrf'] + frr_cfg.modify_section(f'^vrf {vrf_name}', stop_pattern='^exit-vrf', remove_stop_mark=True) + if vrf and 'new_frr_config' in vrf: + frr_cfg.add_before(frr.default_add_before, vrf['new_frr_config']) + frr_cfg.commit_configuration(frr_daemon) + + return None + +if __name__ == '__main__': + try: + c = get_config() + verify(c) + generate(c) + apply(c) + except ConfigError as e: + print(e) + exit(1) diff --git a/src/helpers/vyos-failover.py b/src/helpers/vyos-failover.py index 03fb42f57..ce4cf8fa4 100755 --- a/src/helpers/vyos-failover.py +++ b/src/helpers/vyos-failover.py @@ -93,7 +93,12 @@ def is_port_open(ip, port): s.close() -def is_target_alive(target_list=None, iface='', proto='icmp', port=None, debug=False): +def is_target_alive(target_list=None, + iface='', + proto='icmp', + port=None, + debug=False, + policy='any-available') -> bool: """Check the availability of each target in the target_list using the specified protocol ICMP, ARP, TCP @@ -103,17 +108,19 @@ def is_target_alive(target_list=None, iface='', proto='icmp', port=None, debug=F proto (str): The protocol to use for the check. Options are 'icmp', 'arp', or 'tcp'. port (int): The port number to use for the TCP check. Only applicable if proto is 'tcp'. debug (bool): If True, print debug information during the check. + policy (str): The policy to use for the check. Options are 'any-available' or 'all-available'. Returns: - bool: True if all targets are reachable, False otherwise. + bool: True if all targets are reachable according to the policy, False otherwise. Example: - % is_target_alive(['192.0.2.1', '192.0.2.5'], 'eth1', proto='arp') + % is_target_alive(['192.0.2.1', '192.0.2.5'], 'eth1', proto='arp', policy='all-available') True """ if iface != '': iface = f'-I {iface}' + num_reachable_targets = 0 for target in target_list: match proto: case 'icmp': @@ -121,25 +128,34 @@ def is_target_alive(target_list=None, iface='', proto='icmp', port=None, debug=F rc, response = rc_cmd(command) if debug: print(f' [ CHECK-TARGET ]: [{command}] -- return-code [RC: {rc}]') - if rc != 0: - return False + if rc == 0: + num_reachable_targets += 1 + if policy == 'any-available': + return True case 'arp': command = f'/usr/bin/arping -b -c 2 -f -w 1 -i 1 {iface} {target}' rc, response = rc_cmd(command) if debug: print(f' [ CHECK-TARGET ]: [{command}] -- return-code [RC: {rc}]') - if rc != 0: - return False + if rc == 0: + num_reachable_targets += 1 + if policy == 'any-available': + return True case _ if proto == 'tcp' and port is not None: - if not is_port_open(target, port): - return False + if is_port_open(target, port): + num_reachable_targets += 1 + if policy == 'any-available': + return True case _: return False - return True + if policy == 'all-available' and num_reachable_targets == len(target_list): + return True + + return False if __name__ == '__main__': @@ -178,6 +194,7 @@ if __name__ == '__main__': conf_metric = int(nexthop_config.get('metric')) port = nexthop_config.get('check').get('port') port_opt = f'port {port}' if port else '' + policy = nexthop_config.get('check').get('policy') proto = nexthop_config.get('check').get('type') target = nexthop_config.get('check').get('target') timeout = nexthop_config.get('check').get('timeout') @@ -186,7 +203,7 @@ if __name__ == '__main__': if not is_route_exists(route, next_hop, conf_iface, conf_metric): if debug: print(f" [NEW_ROUTE_DETECTED] route: [{route}]") # Add route if check-target alive - if is_target_alive(target, conf_iface, proto, port, debug=debug): + if is_target_alive(target, conf_iface, proto, port, debug=debug, policy=policy): if debug: print(f' [ ADD ] -- ip route add {route} via {next_hop} dev {conf_iface} ' f'metric {conf_metric} proto failover\n###') rc, command = rc_cmd(f'ip route add {route} via {next_hop} dev {conf_iface} ' @@ -205,7 +222,7 @@ if __name__ == '__main__': # Route was added, check if the target is alive # We should delete route if check fails only if route exists in the routing table - if not is_target_alive(target, conf_iface, proto, port, debug=debug) and \ + if not is_target_alive(target, conf_iface, proto, port, debug=debug, policy=policy) and \ is_route_exists(route, next_hop, conf_iface, conf_metric): if debug: print(f'Nexh_hop {next_hop} fail, target not response') diff --git a/src/op_mode/bgp.py b/src/op_mode/bgp.py index 3f6d45dd7..af9ea788b 100755 --- a/src/op_mode/bgp.py +++ b/src/op_mode/bgp.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2022 VyOS maintainers and contributors +# Copyright (C) 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 @@ -15,101 +15,133 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. # # Purpose: -# Displays bgp neighbors information. -# Used by the "show bgp (vrf <tag>) ipv4|ipv6 neighbors" commands. +# Displays BGP neighbors and tables information. import re import sys import typing -import jmespath from jinja2 import Template -from humps import decamelize - -from vyos.configquery import ConfigTreeQuery import vyos.opmode -ArgFamily = typing.Literal['inet', 'inet6'] - frr_command_template = Template(""" -{% if family %} - show bgp - {{ 'vrf ' ~ vrf if vrf else '' }} - {{ 'ipv6' if family == 'inet6' else 'ipv4'}} - {{ 'neighbor ' ~ peer if peer else 'summary' }} +show bgp + +{## VRF and family modifiers that may precede any options ##} + +{% if vrf %} + vrf {{vrf}} +{% endif %} + +{% if family == "inet" %} + ipv4 +{% elif family == "inet6" %} + ipv6 +{% elif family == "l2vpn" %} + l2vpn evpn +{% endif %} + +{% if family_modifier == "unicast" %} + unicast +{% elif family_modifier == "multicast" %} + multicast +{% elif family_modifier == "flowspec" %} + flowspec +{% elif family_modifier == "vpn" %} + vpn +{% endif %} + +{## Mutually exclusive query parameters ##} + +{# Network prefix #} +{% if prefix %} + {{prefix}} + + {% if longer_prefixes %} + longer-prefixes + {% elif best_path %} + bestpath + {% endif %} {% endif %} +{# Regex #} +{% if regex %} + regex {{regex}} +{% endif %} + +{## Raw modifier ##} + {% if raw %} json {% endif %} """) +ArgFamily = typing.Literal['inet', 'inet6', 'l2vpn'] +ArgFamilyModifier = typing.Literal['unicast', 'labeled_unicast', 'multicast', 'vpn', 'flowspec'] + +def show_summary(raw: bool): + from vyos.util import cmd + + if raw: + from json import loads + + output = cmd(f"vtysh -c 'show bgp summary json'").strip() -def _verify(func): - """Decorator checks if BGP config exists - BGP configuration can be present under vrf <tag> - If we do npt get arg 'peer' then it can be 'bgp summary' - """ - from functools import wraps - - @wraps(func) - def _wrapper(*args, **kwargs): - config = ConfigTreeQuery() - afi = 'ipv6' if kwargs.get('family') == 'inet6' else 'ipv4' - global_vrfs = ['all', 'default'] - peer = kwargs.get('peer') - vrf = kwargs.get('vrf') - unconf_message = f'BGP or neighbor is not configured' - # Add option to check the specific neighbor if we have arg 'peer' - peer_opt = f'neighbor {peer} address-family {afi}-unicast' if peer else '' - vrf_opt = '' - if vrf and vrf not in global_vrfs: - vrf_opt = f'vrf name {vrf}' - # Check if config does not exist - if not config.exists(f'{vrf_opt} protocols bgp {peer_opt}'): - raise vyos.opmode.UnconfiguredSubsystem(unconf_message) - return func(*args, **kwargs) - - return _wrapper - - -@_verify -def show_neighbors(raw: bool, - family: ArgFamily, - peer: typing.Optional[str], - vrf: typing.Optional[str]): - kwargs = dict(locals()) - frr_command = frr_command_template.render(kwargs) - frr_command = re.sub(r'\s+', ' ', frr_command) + # FRR 8.5 correctly returns an empty object when BGP is not running, + # we don't need to do anything special here + return loads(output) + else: + output = cmd(f"vtysh -c 'show bgp summary'") + return output +def show_neighbors(raw: bool): from vyos.util import cmd - output = cmd(f"vtysh -c '{frr_command}'") + from vyos.utils.dict import dict_to_list if raw: from json import loads - data = loads(output) - # Get list of the peers - peers = jmespath.search('*.peers | [0]', data) - if peers: - # Create new dict, delete old key 'peers' - # add key 'peers' neighbors to the list - list_peers = [] - new_dict = jmespath.search('* | [0]', data) - if 'peers' in new_dict: - new_dict.pop('peers') - - for neighbor, neighbor_options in peers.items(): - neighbor_options['neighbor'] = neighbor - list_peers.append(neighbor_options) - new_dict['peers'] = list_peers - return decamelize(new_dict) - data = jmespath.search('* | [0]', data) - return decamelize(data) + output = cmd(f"vtysh -c 'show bgp neighbors json'").strip() + d = loads(output) + return dict_to_list(d, save_key_to="neighbor") else: + output = cmd(f"vtysh -c 'show bgp neighbors'") return output +def show(raw: bool, + family: ArgFamily, + family_modifier: ArgFamilyModifier, + prefix: typing.Optional[str], + longer_prefixes: typing.Optional[bool], + best_path: typing.Optional[bool], + regex: typing.Optional[str], + vrf: typing.Optional[str]): + from vyos.utils.dict import dict_to_list + + if (longer_prefixes or best_path) and (prefix is None): + raise ValueError("longer_prefixes and best_path can only be used when prefix is given") + elif (family == "l2vpn") and (family_modifier is not None): + raise ValueError("l2vpn family does not accept any modifiers") + else: + kwargs = dict(locals()) + + frr_command = frr_command_template.render(kwargs) + frr_command = re.sub(r'\s+', ' ', frr_command) + + from vyos.util import cmd + output = cmd(f"vtysh -c '{frr_command}'") + + if raw: + from json import loads + d = loads(output) + if not ("routes" in d): + raise vyos.opmode.InternalError("FRR returned a BGP table with no routes field") + d = d["routes"] + routes = dict_to_list(d, save_key_to="route_key") + return routes + else: + return output if __name__ == '__main__': try: diff --git a/src/op_mode/conntrack_sync.py b/src/op_mode/conntrack_sync.py index 54ecd6d0e..c3345a936 100755 --- a/src/op_mode/conntrack_sync.py +++ b/src/op_mode/conntrack_sync.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2021 VyOS maintainers and contributors +# Copyright (C) 2022 VyOS maintainers and contributors # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 or later as @@ -15,9 +15,12 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. import os +import sys import syslog import xmltodict +import vyos.opmode + from argparse import ArgumentParser from vyos.configquery import CliShellApiConfigQuery from vyos.configquery import ConfigTreeQuery @@ -31,36 +34,23 @@ conntrackd_bin = '/usr/sbin/conntrackd' conntrackd_config = '/run/conntrackd/conntrackd.conf' failover_state_file = '/var/run/vyatta-conntrackd-failover-state' -parser = ArgumentParser(description='Conntrack Sync') -group = parser.add_mutually_exclusive_group() -group.add_argument('--restart', help='Restart connection tracking synchronization service', action='store_true') -group.add_argument('--reset-cache-internal', help='Reset internal cache', action='store_true') -group.add_argument('--reset-cache-external', help='Reset external cache', action='store_true') -group.add_argument('--show-internal', help='Show internal (main) tracking cache', action='store_true') -group.add_argument('--show-external', help='Show external (main) tracking cache', action='store_true') -group.add_argument('--show-internal-expect', help='Show internal (expect) tracking cache', action='store_true') -group.add_argument('--show-external-expect', help='Show external (expect) tracking cache', action='store_true') -group.add_argument('--show-statistics', help='Show connection syncing statistics', action='store_true') -group.add_argument('--show-status', help='Show conntrack-sync status', action='store_true') - def is_configured(): """ Check if conntrack-sync service is configured """ config = CliShellApiConfigQuery() if not config.exists(['service', 'conntrack-sync']): - print('Service conntrackd-sync not configured!') - exit(1) + raise vyos.opmode.UnconfiguredSubsystem("conntrack-sync is not configured!") def send_bulk_update(): """ send bulk update of internal-cache to other systems """ tmp = run(f'{conntrackd_bin} -C {conntrackd_config} -B') if tmp > 0: - print('ERROR: failed to send bulk update to other conntrack-sync systems') + raise vyos.opmode.Error('Failed to send bulk update to other conntrack-sync systems') def request_sync(): """ request resynchronization with other systems """ tmp = run(f'{conntrackd_bin} -C {conntrackd_config} -n') if tmp > 0: - print('ERROR: failed to request resynchronization of external cache') + raise vyos.opmode.Error('Failed to request resynchronization of external cache') def flush_cache(direction): """ flush conntrackd cache (internal or external) """ @@ -68,9 +58,9 @@ def flush_cache(direction): raise ValueError() tmp = run(f'{conntrackd_bin} -C {conntrackd_config} -f {direction}') if tmp > 0: - print('ERROR: failed to clear {direction} cache') + raise vyos.opmode.Error('Failed to clear {direction} cache') -def xml_to_stdout(xml): +def from_xml(raw, xml): out = [] for line in xml.splitlines(): if line == '\n': @@ -78,108 +68,131 @@ def xml_to_stdout(xml): parsed = xmltodict.parse(line) out.append(parsed) - print(render_to_string('conntrackd/conntrackd.op-mode.j2', {'data' : out})) - -if __name__ == '__main__': - args = parser.parse_args() - syslog.openlog(ident='conntrack-tools', logoption=syslog.LOG_PID, - facility=syslog.LOG_INFO) + if raw: + return out + else: + return render_to_string('conntrackd/conntrackd.op-mode.j2', {'data' : out}) + +def restart(): + is_configured() + if commit_in_progress(): + raise vyos.opmode.CommitInProgress('Cannot restart conntrackd while a commit is in progress') + + syslog.syslog('Restarting conntrack sync service...') + cmd('systemctl restart conntrackd.service') + # request resynchronization with other systems + request_sync() + # send bulk update of internal-cache to other systems + send_bulk_update() + +def reset_external_cache(): + is_configured() + syslog.syslog('Resetting external cache of conntrack sync service...') + + # flush the external cache + flush_cache('external') + # request resynchronization with other systems + request_sync() + +def reset_internal_cache(): + is_configured() + syslog.syslog('Resetting internal cache of conntrack sync service...') + # flush the internal cache + flush_cache('internal') + + # request resynchronization of internal cache with kernel conntrack table + tmp = run(f'{conntrackd_bin} -C {conntrackd_config} -R') + if tmp > 0: + print('ERROR: failed to resynchronize internal cache with kernel conntrack table') - if args.restart: - is_configured() - if commit_in_progress(): - print('Cannot restart conntrackd while a commit is in progress') - exit(1) - - syslog.syslog('Restarting conntrack sync service...') - cmd('systemctl restart conntrackd.service') - # request resynchronization with other systems - request_sync() - # send bulk update of internal-cache to other systems - send_bulk_update() - - elif args.reset_cache_external: - is_configured() - syslog.syslog('Resetting external cache of conntrack sync service...') + # send bulk update of internal-cache to other systems + send_bulk_update() - # flush the external cache - flush_cache('external') - # request resynchronization with other systems - request_sync() +def _show_cache(raw, opts): + is_configured() + out = cmd(f'{conntrackd_bin} -C {conntrackd_config} {opts} -x') + return from_xml(raw, out) - elif args.reset_cache_internal: - is_configured() - syslog.syslog('Resetting internal cache of conntrack sync service...') - # flush the internal cache - flush_cache('internal') +def show_external_cache(raw: bool): + opts = '-e ct' + return _show_cache(raw, opts) - # request resynchronization of internal cache with kernel conntrack table - tmp = run(f'{conntrackd_bin} -C {conntrackd_config} -R') - if tmp > 0: - print('ERROR: failed to resynchronize internal cache with kernel conntrack table') +def show_external_expect(raw: bool): + opts = '-e expect' + return _show_cache(raw, opts) - # send bulk update of internal-cache to other systems - send_bulk_update() +def show_internal_cache(raw: bool): + opts = '-i ct' + return _show_cache(raw, opts) - elif args.show_external or args.show_internal or args.show_external_expect or args.show_internal_expect: - is_configured() - opt = '' - if args.show_external: - opt = '-e ct' - elif args.show_external_expect: - opt = '-e expect' - elif args.show_internal: - opt = '-i ct' - elif args.show_internal_expect: - opt = '-i expect' - - if args.show_external or args.show_internal: - print('Main Table Entries:') - else: - print('Expect Table Entries:') - out = cmd(f'sudo {conntrackd_bin} -C {conntrackd_config} {opt} -x') - xml_to_stdout(out) +def show_internal_expect(raw: bool): + opts = '-i expect' + return _show_cache(raw, opts) - elif args.show_statistics: +def show_statistics(raw: bool): + if raw: + raise vyos.opmode.UnsupportedOperation("Machine-readable conntrack-sync statistics are not available yet") + else: is_configured() config = ConfigTreeQuery() print('\nMain Table Statistics:\n') - call(f'sudo {conntrackd_bin} -C {conntrackd_config} -s') + call(f'{conntrackd_bin} -C {conntrackd_config} -s') print() if config.exists(['service', 'conntrack-sync', 'expect-sync']): print('\nExpect Table Statistics:\n') - call(f'sudo {conntrackd_bin} -C {conntrackd_config} -s exp') + call(f'{conntrackd_bin} -C {conntrackd_config} -s exp') print() - elif args.show_status: - is_configured() - config = ConfigTreeQuery() - ct_sync_intf = config.list_nodes(['service', 'conntrack-sync', 'interface']) - ct_sync_intf = ', '.join(ct_sync_intf) - failover_state = "no transition yet!" - expect_sync_protocols = "disabled" - - if config.exists(['service', 'conntrack-sync', 'failover-mechanism', 'vrrp']): - failover_mechanism = "vrrp" - vrrp_sync_grp = config.value(['service', 'conntrack-sync', 'failover-mechanism', 'vrrp', 'sync-group']) - - if os.path.isfile(failover_state_file): - with open(failover_state_file, "r") as f: - failover_state = f.readline() - - if config.exists(['service', 'conntrack-sync', 'expect-sync']): - expect_sync_protocols = config.values(['service', 'conntrack-sync', 'expect-sync']) - if 'all' in expect_sync_protocols: - expect_sync_protocols = ["ftp", "sip", "h323", "nfs", "sqlnet"] +def show_status(raw: bool): + is_configured() + config = ConfigTreeQuery() + ct_sync_intf = config.list_nodes(['service', 'conntrack-sync', 'interface']) + ct_sync_intf = ', '.join(ct_sync_intf) + failover_state = "no transition yet!" + expect_sync_protocols = [] + + if config.exists(['service', 'conntrack-sync', 'failover-mechanism', 'vrrp']): + failover_mechanism = "vrrp" + vrrp_sync_grp = config.value(['service', 'conntrack-sync', 'failover-mechanism', 'vrrp', 'sync-group']) + + if os.path.isfile(failover_state_file): + with open(failover_state_file, "r") as f: + failover_state = f.readline() + + if config.exists(['service', 'conntrack-sync', 'expect-sync']): + expect_sync_protocols = config.values(['service', 'conntrack-sync', 'expect-sync']) + if 'all' in expect_sync_protocols: + expect_sync_protocols = ["ftp", "sip", "h323", "nfs", "sqlnet"] + + if raw: + status_data = { + "sync_interface": ct_sync_intf, + "failover_mechanism": failover_mechanism, + "sync_group": vrrp_sync_grp, + "last_transition": failover_state, + "sync_protocols": expect_sync_protocols + } + + return status_data + else: + if expect_sync_protocols: expect_sync_protocols = ', '.join(expect_sync_protocols) - + else: + expect_sync_protocols = "disabled" show_status = (f'\nsync-interface : {ct_sync_intf}\n' f'failover-mechanism : {failover_mechanism} [sync-group {vrrp_sync_grp}]\n' - f'last state transition : {failover_state}' + f'last state transition : {failover_state}\n' f'ExpectationSync : {expect_sync_protocols}') - print(show_status) + return show_status - else: - parser.print_help() - exit(1) +if __name__ == '__main__': + syslog.openlog(ident='conntrack-tools', logoption=syslog.LOG_PID, facility=syslog.LOG_INFO) + + try: + res = vyos.opmode.run(sys.modules[__name__]) + if res: + print(res) + except (ValueError, vyos.opmode.Error) as e: + print(e) + sys.exit(1) diff --git a/src/op_mode/dynamic_dns.py b/src/op_mode/dynamic_dns.py index 2cba33cc8..d41a74db3 100755 --- a/src/op_mode/dynamic_dns.py +++ b/src/op_mode/dynamic_dns.py @@ -21,6 +21,7 @@ import time from tabulate import tabulate from vyos.config import Config +from vyos.template import is_ipv4, is_ipv6 from vyos.util import call cache_file = r'/run/ddclient/ddclient.cache' @@ -46,7 +47,7 @@ def _get_formatted_host_records(host_data): def show_status(): - # A ddclient status file must not always exist + # A ddclient status file might not always exist if not os.path.exists(cache_file): sys.exit(0) @@ -62,9 +63,20 @@ def show_status(): # we pick up the ones we are interested in for kvraw in line.split(' ')[0].split(','): k, v = kvraw.split('=') - if k in columns.keys(): + if k in list(columns.keys()) + ['ip', 'status']: # ip and status are legacy keys props[k] = v + # Extract IPv4 and IPv6 address and status from legacy keys + # Dual-stack isn't supported in legacy format, 'ip' and 'status' are for one of IPv4 or IPv6 + if 'ip' in props: + if is_ipv4(props['ip']): + props['ipv4'] = props['ip'] + props['status-ipv4'] = props['status'] + elif is_ipv6(props['ip']): + props['ipv6'] = props['ip'] + props['status-ipv6'] = props['status'] + del props['ip'] + # Convert mtime to human readable format if 'mtime' in props: props['mtime'] = time.strftime( diff --git a/src/op_mode/ipsec.py b/src/op_mode/ipsec.py index 7f4fb72e5..db4948d7a 100755 --- a/src/op_mode/ipsec.py +++ b/src/op_mode/ipsec.py @@ -13,7 +13,6 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. - import re import sys import typing @@ -24,6 +23,7 @@ from tabulate import tabulate from vyos.util import convert_data from vyos.util import seconds_to_human +from vyos.util import cmd from vyos.configquery import ConfigTreeQuery import vyos.opmode @@ -46,6 +46,25 @@ def _get_raw_data_sas(): except (vyos.ipsec.ViciInitiateError) as err: raise vyos.opmode.UnconfiguredSubsystem(err) + +def _get_output_swanctl_sas_from_list(ra_output_list: list) -> str: + """ + Template for output for VICI + Inserts \n after each IKE SA + :param ra_output_list: IKE SAs list + :type ra_output_list: list + :return: formatted string + :rtype: str + """ + output = ''; + for sa_val in ra_output_list: + for sa in sa_val.values(): + swanctl_output: str = cmd( + f'sudo swanctl -l --ike-id {sa["uniqueid"]}') + output = f'{output}{swanctl_output}\n\n' + return output + + def _get_formatted_output_sas(sas): sa_data = [] for sa in sas: @@ -444,6 +463,7 @@ def reset_peer(peer: str, tunnel: typing.Optional[str] = None): except (vyos.ipsec.ViciCommandError) as err: raise vyos.opmode.IncorrectValue(err) + def reset_all_peers(): sitetosite_list = _get_all_sitetosite_peers_name_list() if sitetosite_list: @@ -457,6 +477,7 @@ def reset_all_peers(): raise vyos.opmode.UnconfiguredSubsystem( 'VPN IPSec site-to-site is not configured, aborting') + def _get_ra_session_list_by_username(username: typing.Optional[str] = None): """ Return list of remote-access IKE_SAs uniqueids @@ -466,15 +487,15 @@ def _get_ra_session_list_by_username(username: typing.Optional[str] = None): :rtype: """ list_sa_id = [] - sa_list = vyos.ipsec.get_vici_sas() + sa_list = _get_raw_data_sas() for sa_val in sa_list: for sa in sa_val.values(): if 'remote-eap-id' in sa: if username: - if username == sa['remote-eap-id'].decode(): - list_sa_id.append(sa['uniqueid'].decode()) + if username == sa['remote-eap-id']: + list_sa_id.append(sa['uniqueid']) else: - list_sa_id.append(sa['uniqueid'].decode()) + list_sa_id.append(sa['uniqueid']) return list_sa_id @@ -556,6 +577,24 @@ def show_sa(raw: bool): return _get_formatted_output_sas(sa_data) +def _get_output_sas_detail(ra_output_list: list) -> str: + """ + Formate all IKE SAs detail output + :param ra_output_list: IKE SAs list + :type ra_output_list: list + :return: formatted RA IKE SAs detail output + :rtype: str + """ + return _get_output_swanctl_sas_from_list(ra_output_list) + + +def show_sa_detail(raw: bool): + sa_data = _get_raw_data_sas() + if raw: + return sa_data + return _get_output_sas_detail(sa_data) + + def show_connections(raw: bool): list_conns = _get_convert_data_connections() list_sas = _get_raw_data_sas() @@ -573,6 +612,173 @@ def show_connections_summary(raw: bool): return _get_raw_connections_summary(list_conns, list_sas) +def _get_ra_sessions(username: typing.Optional[str] = None) -> list: + """ + Return list of remote-access IKE_SAs from VICI by username. + If username unspecified, return all remote-access IKE_SAs + :param username: Username of RA connection + :type username: str + :return: list of ra remote-access IKE_SAs + :rtype: list + """ + list_sa = [] + sa_list = _get_raw_data_sas() + for conn in sa_list: + for sa in conn.values(): + if 'remote-eap-id' in sa: + if username: + if username == sa['remote-eap-id']: + list_sa.append(conn) + else: + list_sa.append(conn) + return list_sa + + +def _filter_ikesas(list_sa: list, filter_key: str, filter_value: str) -> list: + """ + Filter IKE SAs by specifice key + :param list_sa: list of IKE SAs + :type list_sa: list + :param filter_key: Filter Key + :type filter_key: str + :param filter_value: Filter Value + :type filter_value: str + :return: Filtered list of IKE SAs + :rtype: list + """ + filtered_sa_list = [] + for conn in list_sa: + for sa in conn.values(): + if sa[filter_key] and sa[filter_key] == filter_value: + filtered_sa_list.append(conn) + return filtered_sa_list + + +def _get_last_installed_childsa(sa: dict) -> str: + """ + Return name of last installed active Child SA + :param sa: Dictionary with Child SAs + :type sa: dict + :return: Name of the Last installed active Child SA + :rtype: str + """ + child_sa_name = None + child_sa_id = 0 + for sa_name, child_sa in sa['child-sas'].items(): + if child_sa['state'] == 'INSTALLED': + if child_sa_id == 0 or int(child_sa['uniqueid']) > child_sa_id: + child_sa_id = int(child_sa['uniqueid']) + child_sa_name = sa_name + return child_sa_name + + +def _get_formatted_ike_proposal(sa: dict) -> str: + """ + Return IKE proposal string in format + EncrALG-EncrKeySize/PFR/HASH/DH-GROUP + :param sa: IKE SA + :type sa: dict + :return: IKE proposal string + :rtype: str + """ + proposal = '' + proposal = f'{proposal}{sa["encr-alg"]}' if 'encr-alg' in sa else proposal + proposal = f'{proposal}-{sa["encr-keysize"]}' if 'encr-keysize' in sa else proposal + proposal = f'{proposal}/{sa["prf-alg"]}' if 'prf-alg' in sa else proposal + proposal = f'{proposal}/{sa["integ-alg"]}' if 'integ-alg' in sa else proposal + proposal = f'{proposal}/{sa["dh-group"]}' if 'dh-group' in sa else proposal + return proposal + + +def _get_formatted_ipsec_proposal(sa: dict) -> str: + """ + Return IPSec proposal string in format + Protocol: EncrALG-EncrKeySize/HASH/PFS + :param sa: Child SA + :type sa: dict + :return: IPSec proposal string + :rtype: str + """ + proposal = '' + proposal = f'{proposal}{sa["protocol"]}' if 'protocol' in sa else proposal + proposal = f'{proposal}:{sa["encr-alg"]}' if 'encr-alg' in sa else proposal + proposal = f'{proposal}-{sa["encr-keysize"]}' if 'encr-keysize' in sa else proposal + proposal = f'{proposal}/{sa["integ-alg"]}' if 'integ-alg' in sa else proposal + proposal = f'{proposal}/{sa["dh-group"]}' if 'dh-group' in sa else proposal + return proposal + + +def _get_output_ra_sas_detail(ra_output_list: list) -> str: + """ + Formate RA IKE SAs detail output + :param ra_output_list: IKE SAs list + :type ra_output_list: list + :return: formatted RA IKE SAs detail output + :rtype: str + """ + return _get_output_swanctl_sas_from_list(ra_output_list) + + +def _get_formatted_output_ra_summary(ra_output_list: list): + sa_data = [] + for conn in ra_output_list: + for sa in conn.values(): + sa_id = sa['uniqueid'] if 'uniqueid' in sa else '' + sa_username = sa['remote-eap-id'] if 'remote-eap-id' in sa else '' + sa_protocol = f'IKEv{sa["version"]}' if 'version' in sa else '' + sa_remotehost = sa['remote-host'] if 'remote-host' in sa else '' + sa_remoteid = sa['remote-id'] if 'remote-id' in sa else '' + sa_ike_proposal = _get_formatted_ike_proposal(sa) + sa_tunnel_ip = sa['remote-vips'] + child_sa_key = _get_last_installed_childsa(sa) + if child_sa_key: + child_sa = sa['child-sas'][child_sa_key] + sa_ipsec_proposal = _get_formatted_ipsec_proposal(child_sa) + sa_state = "UP" + sa_uptime = seconds_to_human(sa['established']) + else: + sa_ipsec_proposal = '' + sa_state = "DOWN" + sa_uptime = '' + sa_data.append( + [sa_id, sa_username, sa_protocol, sa_state, sa_uptime, + sa_tunnel_ip, + sa_remotehost, sa_remoteid, sa_ike_proposal, + sa_ipsec_proposal]) + + headers = ["Connection ID", "Username", "Protocol", "State", "Uptime", + "Tunnel IP", "Remote Host", "Remote ID", "IKE Proposal", + "IPSec Proposal"] + sa_data = sorted(sa_data, key=_alphanum_key) + output = tabulate(sa_data, headers) + return output + + +def show_ra_detail(raw: bool, username: typing.Optional[str] = None, + conn_id: typing.Optional[str] = None): + list_sa: list = _get_ra_sessions() + if username: + list_sa = _filter_ikesas(list_sa, 'remote-eap-id', username) + elif conn_id: + list_sa = _filter_ikesas(list_sa, 'uniqueid', conn_id) + if not list_sa: + raise vyos.opmode.IncorrectValue( + f'No active connections found, aborting') + if raw: + return list_sa + return _get_output_ra_sas_detail(list_sa) + + +def show_ra_summary(raw: bool): + list_sa: list = _get_ra_sessions() + if not list_sa: + raise vyos.opmode.IncorrectValue( + f'No active connections found, aborting') + if raw: + return list_sa + return _get_formatted_output_ra_summary(list_sa) + + if __name__ == '__main__': try: res = vyos.opmode.run(sys.modules[__name__]) diff --git a/src/op_mode/show_vpn_ra.py b/src/op_mode/show_vpn_ra.py deleted file mode 100755 index 73688c4ea..000000000 --- a/src/op_mode/show_vpn_ra.py +++ /dev/null @@ -1,56 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright (C) 2019 VyOS maintainers and contributors -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 2 or later as -# published by the Free Software Foundation. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see <http://www.gnu.org/licenses/>. - -import os -import sys -import re - -from vyos.util import popen - -# chech connection to pptp and l2tp daemon -def get_sessions(): - absent_pptp = False - absent_l2tp = False - pptp_cmd = "accel-cmd -p 2003 show sessions" - l2tp_cmd = "accel-cmd -p 2004 show sessions" - err_pattern = "^Connection.+failed$" - # This value for chack only output header without sessions. - len_def_header = 170 - - # Check pptp - output, err = popen(pptp_cmd, decode='utf-8') - if not err and len(output) > len_def_header and not re.search(err_pattern, output): - print(output) - else: - absent_pptp = True - - # Check l2tp - output, err = popen(l2tp_cmd, decode='utf-8') - if not err and len(output) > len_def_header and not re.search(err_pattern, output): - print(output) - else: - absent_l2tp = True - - if absent_l2tp and absent_pptp: - print("No active remote access VPN sessions") - - -def main(): - get_sessions() - - -if __name__ == '__main__': - main() diff --git a/src/op_mode/show_wwan.py b/src/op_mode/show_wwan.py index 529b5bd0f..eb601a456 100755 --- a/src/op_mode/show_wwan.py +++ b/src/op_mode/show_wwan.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2021 VyOS maintainers and contributors +# Copyright (C) 2021-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 @@ -17,6 +17,7 @@ import argparse from sys import exit +from vyos.configquery import ConfigTreeQuery from vyos.util import cmd parser = argparse.ArgumentParser() @@ -49,6 +50,11 @@ def qmi_cmd(device, command, silent=False): if __name__ == '__main__': args = parser.parse_args() + tmp = ConfigTreeQuery() + if not tmp.exists(['interfaces', 'wwan', args.interface]): + print(f'Interface "{args.interface}" unconfigured!') + exit(1) + # remove the WWAN prefix from the interface, required for the CDC interface if_num = args.interface.replace('wwan','') cdc = f'/dev/cdc-wdm{if_num}' diff --git a/src/services/vyos-http-api-server b/src/services/vyos-http-api-server index cd73f38ec..acaa383b4 100755 --- a/src/services/vyos-http-api-server +++ b/src/services/vyos-http-api-server @@ -283,7 +283,7 @@ class MultipartRequest(Request): return self._headers async def form(self) -> FormData: - if not hasattr(self, "_form"): + if self._form is None: assert ( parse_options_header is not None ), "The `python-multipart` library must be installed to use form parsing." diff --git a/src/systemd/vyos-wan-load-balance.service b/src/systemd/vyos-wan-load-balance.service new file mode 100644 index 000000000..7d62a2ff6 --- /dev/null +++ b/src/systemd/vyos-wan-load-balance.service @@ -0,0 +1,15 @@ +[Unit] +Description=VyOS WAN load-balancing service +After=vyos-router.service + +[Service] +ExecStart=/opt/vyatta/sbin/wan_lb -f /run/load-balance/wlb.conf -d -i /var/run/vyatta/wlb.pid +ExecReload=/bin/kill -s SIGTERM $MAINPID && sleep 5 && /opt/vyatta/sbin/wan_lb -f /run/load-balance/wlb.conf -d -i /var/run/vyatta/wlb.pid +ExecStop=/bin/kill -s SIGTERM $MAINPID +PIDFile=/var/run/vyatta/wlb.pid +KillMode=process +Restart=on-failure +RestartSec=5s + +[Install] +WantedBy=multi-user.target |