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 | 
