diff options
139 files changed, 3973 insertions, 2319 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 1c843e9fa..456211caa 100644 --- a/data/configd-include.json +++ b/data/configd-include.json @@ -86,4 +86,5 @@ "vpn_pptp.py", "vpn_sstp.py", "vrf.py", +"vrf_vni.py" ] diff --git a/data/templates/accel-ppp/config_chap_secrets_radius.j2 b/data/templates/accel-ppp/config_chap_secrets_radius.j2 index bb820497b..a498d8186 100644 --- a/data/templates/accel-ppp/config_chap_secrets_radius.j2 +++ b/data/templates/accel-ppp/config_chap_secrets_radius.j2 @@ -7,6 +7,9 @@ verbose=1 {% for server, options in authentication.radius.server.items() if not options.disable is vyos_defined %} server={{ server }},{{ options.key }},auth-port={{ options.port }},acct-port={{ options.acct_port }},req-limit=0,fail-time={{ options.fail_time }} {% endfor %} +{% if authentication.radius.accounting_interim_interval is vyos_defined %} +acct-interim-interval={{ authentication.radius.accounting_interim_interval }} +{% endif %} {% if authentication.radius.acct_interim_jitter is vyos_defined %} acct-interim-jitter={{ authentication.radius.acct_interim_jitter }} {% endif %} diff --git a/data/templates/accel-ppp/l2tp.config.j2 b/data/templates/accel-ppp/l2tp.config.j2 index 5914fd375..a2f9c9fc7 100644 --- a/data/templates/accel-ppp/l2tp.config.j2 +++ b/data/templates/accel-ppp/l2tp.config.j2 @@ -91,6 +91,9 @@ server={{ r.server }},{{ r.key }},auth-port={{ r.port }},acct-port={{ r.acct_por {% if radius_dynamic_author.server is vyos_defined %} dae-server={{ radius_dynamic_author.server }}:{{ radius_dynamic_author.port }},{{ radius_dynamic_author.key }} {% endif %} +{% if radius_acct_interim_interval is vyos_defined %} +acct-interim-interval={{ radius_acct_interim_interval }} +{% endif %} {% if radius_acct_inter_jitter %} acct-interim-jitter={{ radius_acct_inter_jitter }} {% endif %} diff --git a/data/templates/accel-ppp/pptp.config.j2 b/data/templates/accel-ppp/pptp.config.j2 index 78a629d2d..0082e55bf 100644 --- a/data/templates/accel-ppp/pptp.config.j2 +++ b/data/templates/accel-ppp/pptp.config.j2 @@ -70,6 +70,9 @@ verbose=1 server={{ r.server }},{{ r.key }},auth-port={{ r.port }},acct-port={{ r.acct_port }},req-limit=0,fail-time={{ r.fail_time }} {% endfor %} +{% if radius_acct_interim_interval is vyos_defined %} +acct-interim-interval={{ radius_acct_interim_interval }} +{% endif %} {% if radius_acct_inter_jitter %} acct-interim-jitter={{ radius_acct_inter_jitter }} {% endif %} 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/high-availability/keepalived.conf.j2 b/data/templates/high-availability/keepalived.conf.j2 index 6ea5f91d0..85b89c70c 100644 --- a/data/templates/high-availability/keepalived.conf.j2 +++ b/data/templates/high-availability/keepalived.conf.j2 @@ -32,9 +32,13 @@ global_defs { {% if vrrp.group is vyos_defined %} {% for name, group_config in vrrp.group.items() if group_config.disable is not vyos_defined %} -{% if group_config.health_check.script is vyos_defined %} +{% if group_config.health_check is vyos_defined %} vrrp_script healthcheck_{{ name }} { +{% if group_config.health_check.script is vyos_defined %} script "{{ group_config.health_check.script }}" +{% elif group_config.health_check.ping is vyos_defined %} + script "/usr/bin/ping -c1 {{ group_config.health_check.ping }}" +{% endif %} interval {{ group_config.health_check.interval }} fall {{ group_config.health_check.failure_count }} rise 1 @@ -121,7 +125,7 @@ vrrp_instance {{ name }} { {% endfor %} } {% endif %} -{% if group_config.health_check.script is vyos_defined %} +{% if group_config.health_check is vyos_defined %} track_script { healthcheck_{{ name }} } 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/data/templates/rsyslog/logrotate.j2 b/data/templates/rsyslog/logrotate.j2 new file mode 100644 index 000000000..89d1a8a50 --- /dev/null +++ b/data/templates/rsyslog/logrotate.j2 @@ -0,0 +1,16 @@ +### Autogenerated by system-syslog.py ### +{% if file is vyos_defined %} +{% for file_name, file_options in file.items() %} +/var/log/user/{{ file_name }} { + missingok + notifempty + create + rotate {{ file_options.archive.file }} + size={{ file_options.archive.size | int // 1024 }}k + postrotate + invoke-rc.d rsyslog rotate > /dev/null + endscript +} + +{% endfor %} +{% endif %} diff --git a/data/templates/rsyslog/override.conf.j2 b/data/templates/rsyslog/override.conf.j2 new file mode 100644 index 000000000..5f6a87edf --- /dev/null +++ b/data/templates/rsyslog/override.conf.j2 @@ -0,0 +1,11 @@ +{% set vrf_command = 'ip vrf exec ' ~ vrf ~ ' ' if vrf is vyos_defined else '' %} +[Unit] +StartLimitIntervalSec=0 + +[Service] +ExecStart= +ExecStart={{ vrf_command }}/usr/sbin/rsyslogd -n -iNONE +Restart=always +RestartPreventExitStatus= +RestartSec=10 +RuntimeDirectoryPreserve=yes diff --git a/data/templates/rsyslog/rsyslog.conf.j2 b/data/templates/rsyslog/rsyslog.conf.j2 new file mode 100644 index 000000000..0460ae5f0 --- /dev/null +++ b/data/templates/rsyslog/rsyslog.conf.j2 @@ -0,0 +1,71 @@ +### Autogenerated by system-syslog.py ### + +{% if global.marker is vyos_defined %} +$ModLoad immark +{% if global.marker.interval is vyos_defined %} +$MarkMessagePeriod {{ global.marker.interval }} +{% endif %} +{% endif %} +{% if global.preserve_fqdn is vyos_defined %} +$PreserveFQDN on +{% endif %} + +# We always log to /var/log/messages +$outchannel global,/var/log/messages,262144,/usr/sbin/logrotate {{ logrotate }} +{% if global.facility is vyos_defined %} +{% set tmp = [] %} +{% for facility, facility_options in global.facility.items() %} +{% set _ = tmp.append(facility.replace('all', '*') + '.' + facility_options.level) %} +{% endfor %} +{{ tmp | join(';') }} :omfile:$global +{% endif %} + +{% if file is vyos_defined %} +# File based configuration section +{% for file_name, file_options in file.items() %} +$outchannel {{ file_name }},/var/log/user/{{ file_name }},{{ file_options.archive.size }},/usr/sbin/logrotate {{ logrotate }} +{% set tmp = [] %} +{% for facility, facility_options in file_options.facility.items() %} +{% set _ = tmp.append(facility.replace('all', '*') + '.' + facility_options.level) %} +{% endfor %} +{{ tmp | join(';') }} :omfile:${{ file }} +{% endfor %} +{% endif %} + +{% if console.facility is vyos_defined %} +# Console logging +{% set tmp = [] %} +{% for facility, facility_options in console.facility.items() %} +{% set _ = tmp.append(facility.replace('all', '*') + '.' + facility_options.level) %} +{% endfor %} +{{ tmp | join(';') }} /dev/console +{% endif %} + +{% if host is vyos_defined %} +# Remote logging +{% for host_name, host_options in host.items() %} +{% set tmp = [] %} +{% for facility, facility_options in host_options.facility.items() %} +{% set _ = tmp.append(facility.replace('all', '*') + '.' + facility_options.level) %} +{% endfor %} +{% if host_options.protocol is vyos_defined('tcp') %} +{% if host_options.oct_count is vyos_defined %} +{{ tmp | join(';') }} @@(o){{ host_name | bracketize_ipv6 }}:{{ host_options.port }};RSYSLOG_SyslogProtocol23Format +{% else %} +{{ tmp | join(';') }} @@{{ host_name | bracketize_ipv6 }}:{{ host_options.port }} +{% endif %} +{% else %} +{{ tmp | join(';') }} @{{ host_name | bracketize_ipv6 }}:{{ host_options.port }}{{ ';RSYSLOG_SyslogProtocol23Format' if host_options.format.octet_counted is vyos_defined }} +{% endif %} +{% endfor %} +{% endif %} + +{% if user is defined and user is not none %} +# Log to user terminal +{% for username, user_options in user.items() %} +{% for facility, facility_options in user_options.facility.items() %} +{% set _ = tmp.append(facility.replace('all', '*') + '.' + facility_options.level) %} +{% endfor %} +{{ tmp | join(';') }} :omusrmsg:{{ username }} +{% endfor %} +{% endif %} diff --git a/data/templates/syslog/logrotate.j2 b/data/templates/syslog/logrotate.j2 deleted file mode 100644 index c1b951e8b..000000000 --- a/data/templates/syslog/logrotate.j2 +++ /dev/null @@ -1,11 +0,0 @@ -{{ config_render['log-file'] }} { - missingok - notifempty - create - rotate {{ config_render['max-files'] }} - size={{ config_render['max-size'] // 1024 }}k - postrotate - invoke-rc.d rsyslog rotate > /dev/null - endscript -} - diff --git a/data/templates/syslog/rsyslog.conf.j2 b/data/templates/syslog/rsyslog.conf.j2 deleted file mode 100644 index abe880283..000000000 --- a/data/templates/syslog/rsyslog.conf.j2 +++ /dev/null @@ -1,58 +0,0 @@ -## generated by syslog.py ## -## file based logging -{% if files['global']['marker'] %} -$ModLoad immark -{% if files['global']['marker-interval'] %} -$MarkMessagePeriod {{ files['global']['marker-interval'] }} -{% endif %} -{% endif %} -{% if files['global']['preserver_fqdn'] %} -$PreserveFQDN on -{% endif %} -{% for file, file_options in files.items() %} -{% if file_options['max-size'] is vyos_defined %} -$outchannel {{ file }},{{ file_options['log-file'] }},{{ file_options['max-size'] }},{{ file_options['action-on-max-size'] }} -{% else %} -$outchannel {{ file }},{{ file_options['log-file'] }} -{% endif %} -{{ file_options['selectors'] }} :omfile:${{ file }} -{% endfor %} -{% if console is defined and console is not none %} -## console logging -{% for con, con_options in console.items() %} -{{ con_options['selectors'] }} /dev/console -{% endfor %} -{% endif %} -{% if hosts is defined and hosts is not none %} -## remote logging -{% for host, host_options in hosts.items() %} -{% if host_options.proto == 'tcp' %} -{% if host_options.port is defined %} -{% if host_options.oct_count is defined %} -{{ host_options.selectors }} @@(o){{ host | bracketize_ipv6 }}:{{ host_options.port }};RSYSLOG_SyslogProtocol23Format -{% else %} -{{ host_options.selectors }} @@{{ host | bracketize_ipv6 }}:{{ host_options.port }} -{% endif %} -{% else %} -{{ host_options.selectors }} @@{{ host | bracketize_ipv6 }} -{% endif %} -{% elif host_options.proto == 'udp' %} -{% if host_options.port is defined %} -{{ host_options.selectors }} @{{ host | bracketize_ipv6 }}:{{ host_options.port }}{{ ';RSYSLOG_SyslogProtocol23Format' if host_options.oct_count is sameas true }} -{% else %} -{{ host_options.selectors }} @{{ host | bracketize_ipv6 }} -{% endif %} -{% else %} -{% if host_options['port'] %} -{{ host_options.selectors }} @{{ host | bracketize_ipv6 }}:{{ host_options.port }} -{% else %} -{{ host_options.selectors }} @{{ host | bracketize_ipv6 }} -{% endif %} -{% endif %} -{% endfor %} -{% endif %} -{% if user is defined and user is not none %} -{% for username, user_options in user.items() %} -{{ user_options.selectors }} :omusrmsg:{{ username }} -{% endfor %} -{% endif %} diff --git a/data/templates/system/cloud_init_networking.j2 b/data/templates/system/cloud_init_networking.j2 new file mode 100644 index 000000000..52cce72f8 --- /dev/null +++ b/data/templates/system/cloud_init_networking.j2 @@ -0,0 +1,9 @@ +network: + version: 2 + ethernets: +{% for iface in ifaces_list %} + {{ iface['name'] }}: + dhcp4: true + match: + macaddress: "{{ iface['mac'] }}" +{% endfor %} diff --git a/debian/control b/debian/control index 856f57030..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,8 @@ Standards-Version: 3.9.6 Package: vyos-1x Architecture: amd64 arm64 Depends: - ${python3:Depends}, + ${python3:Depends} (>= 3.10), + aardvark-dns, accel-ppp, auditd, avahi-daemon, diff --git a/debian/vyos-1x.install b/debian/vyos-1x.install index 98d1bc0cd..2b04f173b 100644 --- a/debian/vyos-1x.install +++ b/debian/vyos-1x.install @@ -6,7 +6,7 @@ etc/netplug etc/opennhrp etc/modprobe.d etc/ppp -etc/rsyslog.d +etc/rsyslog.conf etc/securetty etc/security etc/sudoers.d diff --git a/debian/vyos-1x.postinst b/debian/vyos-1x.postinst index ddc189508..6653cd585 100644 --- a/debian/vyos-1x.postinst +++ b/debian/vyos-1x.postinst @@ -122,5 +122,8 @@ if test -f /etc/pam.d/frr; then fi fi +# Enable Cloud-init pre-configuration service +systemctl enable vyos-config-cloud-init.service + # Generate API GraphQL schema /usr/libexec/vyos/services/api/graphql/generate/generate_schema.py diff --git a/debian/vyos-1x.preinst b/debian/vyos-1x.preinst index 213a23d9e..58f24cb5a 100644 --- a/debian/vyos-1x.preinst +++ b/debian/vyos-1x.preinst @@ -3,3 +3,4 @@ dpkg-divert --package vyos-1x --add --rename /etc/security/capability.conf dpkg-divert --package vyos-1x --add --rename /lib/systemd/system/lcdproc.service dpkg-divert --package vyos-1x --add --rename /etc/logrotate.d/conntrackd dpkg-divert --package vyos-1x --add --rename /usr/share/pam-configs/radius +dpkg-divert --package vyos-1x --add --rename /etc/rsyslog.conf diff --git a/interface-definitions/dns-dynamic.xml.in b/interface-definitions/dns-dynamic.xml.in index 58dd48f9d..48c101d73 100644 --- a/interface-definitions/dns-dynamic.xml.in +++ b/interface-definitions/dns-dynamic.xml.in @@ -14,10 +14,17 @@ <children> <tagNode name="interface"> <properties> - <help>Interface to send DDNS updates for</help> + <help>Interface to send Dynamic DNS updates for</help> <completionHelp> <script>${vyos_completion_dir}/list_interfaces</script> </completionHelp> + <valueHelp> + <format>txt</format> + <description>Interface name</description> + </valueHelp> + <constraint> + #include <include/constraint/interface-name.xml.i> + </constraint> </properties> <children> <tagNode name="rfc2136"> @@ -127,144 +134,63 @@ <children> <leafNode name="host-name"> <properties> - <help>Hostname registered with DDNS service</help> + <help>Hostname to register with Dynamic DNS service</help> + <constraint> + #include <include/constraint/host-name.xml.i> + </constraint> + <constraintErrorMessage>Host-name must be alphanumeric and can contain hyphens</constraintErrorMessage> <multi/> </properties> </leafNode> <leafNode name="login"> <properties> - <help>Login for DDNS service</help> - </properties> - </leafNode> - <leafNode name="password"> - <properties> - <help>Password for DDNS service</help> + <help>Login/Username for Dynamic DNS service</help> </properties> </leafNode> + #include <include/generic-password.xml.i> <leafNode name="protocol"> <properties> - <help>ddclient protocol used for DDNS service</help> + <help>ddclient protocol used for Dynamic DNS service</help> <completionHelp> - <list>changeip cloudflare dnsmadeeasy dnspark dondominio dslreports1 dtdns duckdns dyndns2 easydns freedns freemyip googledomains hammernode1 namecheap nfsn noip sitelutions woima yandex zoneedit1</list> + <script>${vyos_completion_dir}/list_ddclient_protocols.sh</script> </completionHelp> - <valueHelp> - <format>changeip</format> - <description>ChangeIP protocol</description> - </valueHelp> - <valueHelp> - <format>cloudflare</format> - <description>Cloudflare protocol</description> - </valueHelp> - <valueHelp> - <format>dnsmadeeasy</format> - <description>DNS Made Easy protocol</description> - </valueHelp> - <valueHelp> - <format>dnspark</format> - <description>DNS Park protocol</description> - </valueHelp> - <valueHelp> - <format>dondominio</format> - <description>DonDominio protocol</description> - </valueHelp> - <valueHelp> - <format>dslreports1</format> - <description>DslReports protocol</description> - </valueHelp> - <valueHelp> - <format>dtdns</format> - <description>DtDNS protocol</description> - </valueHelp> - <valueHelp> - <format>duckdns</format> - <description>DuckDNS protocol</description> - </valueHelp> - <valueHelp> - <format>dyndns2</format> - <description>DynDNS protocol v2</description> - </valueHelp> - <valueHelp> - <format>easydns</format> - <description>easyDNS protocol</description> - </valueHelp> - <valueHelp> - <format>freedns</format> - <description>FreeDNS protocol</description> - </valueHelp> - <valueHelp> - <format>freemyip</format> - <description>freemyip protocol</description> - </valueHelp> - <valueHelp> - <format>googledomains</format> - <description>Google domains protocol</description> - </valueHelp> - <valueHelp> - <format>hammernode1</format> - <description>Hammernode protocol</description> - </valueHelp> - <valueHelp> - <format>namecheap</format> - <description>Namecheap protocol</description> - </valueHelp> - <valueHelp> - <format>nfsn</format> - <description>NearlyFreeSpeech DNS protocol</description> - </valueHelp> - <valueHelp> - <format>noip</format> - <description>No-IP protocol</description> - </valueHelp> - <valueHelp> - <format>sitelutions</format> - <description>Sitelutions protocol</description> - </valueHelp> - <valueHelp> - <format>woima</format> - <description>WOIMA protocol</description> - </valueHelp> - <valueHelp> - <format>yandex</format> - <description>Yandex.DNS protocol</description> - </valueHelp> - <valueHelp> - <format>zoneedit1</format> - <description>Zoneedit protocol</description> - </valueHelp> <constraint> - <regex>(changeip|cloudflare|dnsmadeeasy|dnspark|dondominio|dslreports1|dtdns|duckdns|dyndns2|easydns|freedns|freemyip|googledomains|hammernode1|namecheap|nfsn|noip|sitelutions|woima|yandex|zoneedit1)</regex> + <validator name="ddclient-protocol"/> </constraint> - <constraintErrorMessage>Please choose from the list of allowed protocols</constraintErrorMessage> </properties> </leafNode> #include <include/server-ipv4-fqdn.xml.i> <leafNode name="zone"> <properties> - <help>DNS zone to update (only available with CloudFlare)</help> + <help>DNS zone to update (not used by all protocols)</help> + <valueHelp> + <format>txt</format> + <description>Name of DNS zone</description> + </valueHelp> </properties> </leafNode> </children> </tagNode> <node name="use-web"> <properties> - <help>Web check used for obtaining the external IP address</help> + <help>Use HTTP(S) web request to obtain external IP address instead of the IP address associated with the interface</help> </properties> <children> <leafNode name="skip"> <properties> - <help>Skip everything before this on the given URL</help> - </properties> - </leafNode> - <leafNode name="url"> - <properties> - <help>URL to obtain the current external IP address</help> + <help>Pattern to skip from the respose</help> + <valueHelp> + <format>txt</format> + <description>Pattern to skip from the respose of the given URL to extract the external IP address</description> + </valueHelp> </properties> </leafNode> + #include <include/url.xml.i> </children> </node> <leafNode name="ipv6-enable"> <properties> - <help>Allow explicit IPv6 addresses for Dynamic DNS for this interface</help> + <help>Explicitly use IPv6 address instead of IPv4 address to update the Dynamic DNS IP address</help> <valueless/> </properties> </leafNode> 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/high-availability.xml.in b/interface-definitions/high-availability.xml.in index ce6603796..94253def3 100644 --- a/interface-definitions/high-availability.xml.in +++ b/interface-definitions/high-availability.xml.in @@ -6,6 +6,7 @@ <help>High availability settings</help> </properties> <children> + #include <include/generic-disable-node.xml.i> <node name="vrrp"> <properties> <help>Virtual Router Redundancy Protocol settings</help> @@ -95,7 +96,7 @@ #include <include/generic-disable-node.xml.i> <node name="health-check"> <properties> - <help>Health check script</help> + <help>Health check</help> </properties> <children> <leafNode name="failure-count"> @@ -116,6 +117,23 @@ </properties> <defaultValue>60</defaultValue> </leafNode> + <leafNode name="ping"> + <properties> + <help>ICMP ping health check</help> + <valueHelp> + <format>ipv4</format> + <description>IPv4 ping target address</description> + </valueHelp> + <valueHelp> + <format>ipv6</format> + <description>IPv6 ping target address</description> + </valueHelp> + <constraint> + <validator name="ipv4-address"/> + <validator name="ipv6-address"/> + </constraint> + </properties> + </leafNode> <leafNode name="script"> <properties> <help>Health check script file</help> diff --git a/interface-definitions/include/accel-ppp/radius-accounting-interim-interval.xml.i b/interface-definitions/include/accel-ppp/radius-accounting-interim-interval.xml.i new file mode 100644 index 000000000..311ef969c --- /dev/null +++ b/interface-definitions/include/accel-ppp/radius-accounting-interim-interval.xml.i @@ -0,0 +1,15 @@ +<!-- include start from accel-ppp/radius-accounting-interim-interval.xml.i --> +<leafNode name="accounting-interim-interval"> + <properties> + <help>Interval in seconds to send accounting information</help> + <valueHelp> + <format>u32:1-3600</format> + <description>Interval in seconds to send accounting information</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-3600"/> + </constraint> + <constraintErrorMessage>Interval value must be between 1 and 3600 seconds</constraintErrorMessage> + </properties> +</leafNode> +<!-- include end --> diff --git a/interface-definitions/include/accel-ppp/radius-additions.xml.i b/interface-definitions/include/accel-ppp/radius-additions.xml.i index 15ff5165f..cdd0bf300 100644 --- a/interface-definitions/include/accel-ppp/radius-additions.xml.i +++ b/interface-definitions/include/accel-ppp/radius-additions.xml.i @@ -1,6 +1,19 @@ <!-- include start from accel-ppp/radius-additions.xml.i --> <node name="radius"> <children> + <leafNode name="accounting-interim-interval"> + <properties> + <help>Interval in seconds to send accounting information</help> + <valueHelp> + <format>u32:1-3600</format> + <description>Interval in seconds to send accounting information</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-3600"/> + </constraint> + <constraintErrorMessage>Interval value must be between 1 and 3600 seconds</constraintErrorMessage> + </properties> + </leafNode> <leafNode name="acct-interim-jitter"> <properties> <help>Maximum jitter value in seconds to be applied to accounting information interval</help> 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/snmp/protocol.xml.i b/interface-definitions/include/protocol-tcp-udp.xml.i index d7e6752ad..d7e6752ad 100644 --- a/interface-definitions/include/snmp/protocol.xml.i +++ b/interface-definitions/include/protocol-tcp-udp.xml.i 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..e6138a122 --- /dev/null +++ b/interface-definitions/include/syslog-facility.xml.i @@ -0,0 +1,149 @@ +<!-- 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 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|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>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> + <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> + <constraint> + <regex>(emerg|alert|crit|err|warning|notice|info|debug|all)</regex> + </constraint> + <constraintErrorMessage>Invalid loglevel</constraintErrorMessage> + </properties> + <defaultValue>err</defaultValue> + </leafNode> + </children> +</tagNode> +<!-- include end --> diff --git a/interface-definitions/include/version/firewall-version.xml.i b/interface-definitions/include/version/firewall-version.xml.i index bc04f8d51..c32484542 100644 --- a/interface-definitions/include/version/firewall-version.xml.i +++ b/interface-definitions/include/version/firewall-version.xml.i @@ -1,3 +1,3 @@ <!-- include start from include/version/firewall-version.xml.i --> -<syntaxVersion component='firewall' version='9'></syntaxVersion> +<syntaxVersion component='firewall' version='10'></syntaxVersion> <!-- include end --> diff --git a/interface-definitions/include/version/system-version.xml.i b/interface-definitions/include/version/system-version.xml.i index b7650c782..73df8bd8e 100644 --- a/interface-definitions/include/version/system-version.xml.i +++ b/interface-definitions/include/version/system-version.xml.i @@ -1,3 +1,3 @@ <!-- include start from include/version/system-version.xml.i --> -<syntaxVersion component='system' version='25'></syntaxVersion> +<syntaxVersion component='system' version='26'></syntaxVersion> <!-- include end --> diff --git a/interface-definitions/interfaces-virtual-ethernet.xml.in b/interface-definitions/interfaces-virtual-ethernet.xml.in index 864f658da..a5702bfc0 100644 --- a/interface-definitions/interfaces-virtual-ethernet.xml.in +++ b/interface-definitions/interfaces-virtual-ethernet.xml.in @@ -22,6 +22,7 @@ #include <include/interface/dhcpv6-options.xml.i> #include <include/interface/disable.xml.i> #include <include/interface/vrf.xml.i> + #include <include/interface/netns.xml.i> <leafNode name="peer-name"> <properties> <help>Virtual ethernet peer interface name</help> 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-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/snmp.xml.in b/interface-definitions/snmp.xml.in index 559e09388..6527cabd6 100644 --- a/interface-definitions/snmp.xml.in +++ b/interface-definitions/snmp.xml.in @@ -143,7 +143,7 @@ <multi/> </properties> </leafNode> - #include <include/snmp/protocol.xml.i> + #include <include/protocol-tcp-udp.xml.i> <leafNode name="smux-peer"> <properties> <help>Register a subtree for SMUX-based processing</help> @@ -327,7 +327,7 @@ #include <include/snmp/privacy-type.xml.i> </children> </node> - #include <include/snmp/protocol.xml.i> + #include <include/protocol-tcp-udp.xml.i> <leafNode name="type"> <properties> <help>Specifies the type of notification between inform and trap</help> 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..cd5c514a8 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,21 @@ <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> + <leafNode name="port"> + <defaultValue>514</defaultValue> + </leafNode> + #include <include/protocol-tcp-udp.xml.i> + #include <include/syslog-facility.xml.i> <node name="format"> <properties> <help>Logging format</help> @@ -390,160 +75,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> @@ -551,18 +83,19 @@ <children> <leafNode name="interval"> <properties> - <help>time interval how often a mark message is being sent in seconds (default: 1200)</help> + <help>time interval how often a mark message is being sent in seconds</help> <constraint> <validator name="numeric" argument="--positive"/> </constraint> </properties> + <defaultValue>1200</defaultValue> </leafNode> </children> </node> - <leafNode name ="preserve-fqdn"> + <leafNode name="preserve-fqdn"> <properties> <help>uses FQDN for logging</help> - <valueless /> + <valueless/> </properties> </leafNode> </children> @@ -583,178 +116,27 @@ <children> <leafNode name="file"> <properties> - <help>Number of saved files (default is 5)</help> + <help>Number of saved files</help> <constraint> <regex>[0-9]+</regex> </constraint> <constraintErrorMessage>illegal characters in number of files</constraintErrorMessage> </properties> + <defaultValue>5</defaultValue> </leafNode> <leafNode name="size"> <properties> - <help>Size of log files (in kbytes, default is 256)</help> + <help>Size of log files in kbytes</help> <constraint> <regex>[0-9]+</regex> </constraint> <constraintErrorMessage>illegal characters in size</constraintErrorMessage> </properties> + <defaultValue>256</defaultValue> </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,162 +144,10 @@ <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> + #include <include/interface/vrf.xml.i> </children> </node> </children> diff --git a/interface-definitions/vpn-l2tp.xml.in b/interface-definitions/vpn-l2tp.xml.in index 6b64c5f5d..ec186cd23 100644 --- a/interface-definitions/vpn-l2tp.xml.in +++ b/interface-definitions/vpn-l2tp.xml.in @@ -177,6 +177,7 @@ #include <include/radius-auth-server-ipv4.xml.i> <node name="radius"> <children> + #include <include/accel-ppp/radius-accounting-interim-interval.xml.i> <tagNode name="server"> <children> #include <include/accel-ppp/radius-additions-disable-accounting.xml.i> 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/bgp/evpn-type-1.xml.i b/op-mode-definitions/include/bgp/evpn-type-1.xml.i new file mode 100644 index 000000000..b5097c8b1 --- /dev/null +++ b/op-mode-definitions/include/bgp/evpn-type-1.xml.i @@ -0,0 +1,8 @@ +<!-- included start from bgp/evpn-type-1.xml.i --> +<leafNode name="1"> + <properties> + <help>EAD (Type-1) route</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> +</leafNode> +<!-- included end --> diff --git a/op-mode-definitions/include/bgp/evpn-type-2.xml.i b/op-mode-definitions/include/bgp/evpn-type-2.xml.i new file mode 100644 index 000000000..827298d62 --- /dev/null +++ b/op-mode-definitions/include/bgp/evpn-type-2.xml.i @@ -0,0 +1,8 @@ +<!-- included start from bgp/evpn-type-2.xml.i --> +<leafNode name="2"> + <properties> + <help>MAC-IP (Type-2) route</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> +</leafNode> +<!-- included end --> diff --git a/op-mode-definitions/include/bgp/evpn-type-3.xml.i b/op-mode-definitions/include/bgp/evpn-type-3.xml.i new file mode 100644 index 000000000..ae90b2e5c --- /dev/null +++ b/op-mode-definitions/include/bgp/evpn-type-3.xml.i @@ -0,0 +1,8 @@ +<!-- included start from bgp/evpn-type-3.xml.i --> +<leafNode name="3"> + <properties> + <help>Multicast (Type-3) route</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> +</leafNode> +<!-- included end --> diff --git a/op-mode-definitions/include/bgp/evpn-type-4.xml.i b/op-mode-definitions/include/bgp/evpn-type-4.xml.i new file mode 100644 index 000000000..7248b4753 --- /dev/null +++ b/op-mode-definitions/include/bgp/evpn-type-4.xml.i @@ -0,0 +1,8 @@ +<!-- included start from bgp/evpn-type-4.xml.i --> +<leafNode name="4"> + <properties> + <help>Ethernet Segment (Type-4) route</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> +</leafNode> +<!-- included end --> diff --git a/op-mode-definitions/include/bgp/evpn-type-5.xml.i b/op-mode-definitions/include/bgp/evpn-type-5.xml.i new file mode 100644 index 000000000..e3a72168a --- /dev/null +++ b/op-mode-definitions/include/bgp/evpn-type-5.xml.i @@ -0,0 +1,8 @@ +<!-- included start from bgp/evpn-type-5.xml.i --> +<leafNode name="5"> + <properties> + <help>Prefix (Type-5) route</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> +</leafNode> +<!-- included end --> diff --git a/op-mode-definitions/include/bgp/evpn-type-ead.xml.i b/op-mode-definitions/include/bgp/evpn-type-ead.xml.i new file mode 100644 index 000000000..452de2f9a --- /dev/null +++ b/op-mode-definitions/include/bgp/evpn-type-ead.xml.i @@ -0,0 +1,8 @@ +<!-- included start from bgp/evpn-type-ead.xml.i --> +<leafNode name="ead"> + <properties> + <help>EAD (Type-1) route</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> +</leafNode> +<!-- included end --> diff --git a/op-mode-definitions/include/bgp/evpn-type-es.xml.i b/op-mode-definitions/include/bgp/evpn-type-es.xml.i new file mode 100644 index 000000000..50c40151a --- /dev/null +++ b/op-mode-definitions/include/bgp/evpn-type-es.xml.i @@ -0,0 +1,8 @@ +<!-- included start from bgp/evpn-type-es.xml.i --> +<leafNode name="es"> + <properties> + <help>Ethernet Segment (Type-4) route</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> +</leafNode> +<!-- included end --> diff --git a/op-mode-definitions/include/bgp/evpn-type-macip.xml.i b/op-mode-definitions/include/bgp/evpn-type-macip.xml.i new file mode 100644 index 000000000..6f601eb3f --- /dev/null +++ b/op-mode-definitions/include/bgp/evpn-type-macip.xml.i @@ -0,0 +1,8 @@ +<!-- included start from bgp/evpn-type-macip.xml.i --> +<leafNode name="macip"> + <properties> + <help>MAC-IP (Type-2) route</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> +</leafNode> +<!-- included end --> diff --git a/op-mode-definitions/include/bgp/evpn-type-multicast.xml.i b/op-mode-definitions/include/bgp/evpn-type-multicast.xml.i new file mode 100644 index 000000000..5194dbb56 --- /dev/null +++ b/op-mode-definitions/include/bgp/evpn-type-multicast.xml.i @@ -0,0 +1,8 @@ +<!-- included start from bgp/evpn-type-multicast.xml.i --> +<leafNode name="multicast"> + <properties> + <help>Multicast (Type-3) route</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> +</leafNode> +<!-- included end --> diff --git a/op-mode-definitions/include/bgp/evpn-type-prefix.xml.i b/op-mode-definitions/include/bgp/evpn-type-prefix.xml.i new file mode 100644 index 000000000..d5054d86b --- /dev/null +++ b/op-mode-definitions/include/bgp/evpn-type-prefix.xml.i @@ -0,0 +1,8 @@ +<!-- included start from bgp/evpn-type-prefix.xml.i --> +<leafNode name="prefix"> + <properties> + <help>Prefix (Type-5) route</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> +</leafNode> +<!-- included end --> diff --git a/op-mode-definitions/include/bgp/show-bgp-common.xml.i b/op-mode-definitions/include/bgp/show-bgp-common.xml.i index c9a112fca..de794a879 100644 --- a/op-mode-definitions/include/bgp/show-bgp-common.xml.i +++ b/op-mode-definitions/include/bgp/show-bgp-common.xml.i @@ -171,66 +171,16 @@ <help>Specify Route type</help> </properties> <children> - <leafNode name="1"> - <properties> - <help>EAD (Type-1) route</help> - </properties> - <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> - </leafNode> - <leafNode name="2"> - <properties> - <help>MAC-IP (Type-2) route</help> - </properties> - <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> - </leafNode> - <leafNode name="3"> - <properties> - <help>Multicast (Type-3) route</help> - </properties> - <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> - </leafNode> - <leafNode name="4"> - <properties> - <help>Ethernet Segment (Type-4) route</help> - </properties> - <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> - </leafNode> - <leafNode name="5"> - <properties> - <help>Prefix (Type-5) route</help> - </properties> - <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> - </leafNode> - <leafNode name="ead"> - <properties> - <help>EAD (Type-1) route</help> - </properties> - <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> - </leafNode> - <leafNode name="es"> - <properties> - <help>Ethernet Segment (Type-4) route</help> - </properties> - <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> - </leafNode> - <leafNode name="macip"> - <properties> - <help>MAC-IP (Type-2) route</help> - </properties> - <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> - </leafNode> - <leafNode name="multicast"> - <properties> - <help>Multicast (Type-3) route</help> - </properties> - <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> - </leafNode> - <leafNode name="prefix"> - <properties> - <help>Prefix (Type-5) route</help> - </properties> - <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> - </leafNode> + #include <include/bgp/evpn-type-1.xml.i> + #include <include/bgp/evpn-type-2.xml.i> + #include <include/bgp/evpn-type-3.xml.i> + #include <include/bgp/evpn-type-4.xml.i> + #include <include/bgp/evpn-type-5.xml.i> + #include <include/bgp/evpn-type-ead.xml.i> + #include <include/bgp/evpn-type-es.xml.i> + #include <include/bgp/evpn-type-macip.xml.i> + #include <include/bgp/evpn-type-multicast.xml.i> + #include <include/bgp/evpn-type-prefix.xml.i> </children> </node> #include <include/vni-tagnode-all.xml.i> 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-bgp.xml.in b/op-mode-definitions/show-bgp.xml.in index 974147621..3c212614c 100644 --- a/op-mode-definitions/show-bgp.xml.in +++ b/op-mode-definitions/show-bgp.xml.in @@ -51,6 +51,33 @@ </properties> <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> </leafNode> + #include <include/vni-tagnode-all.xml.i> + <tagNode name="vni"> + <children> + <tagNode name="vtep"> + <properties> + <help>Remote VTEP IP address</help> + <completionHelp> + <list><x.x.x.x></list> + </completionHelp> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </tagNode> + <node name="type"> + <properties> + <help>Display number of prefixes for all afi/safi</help> + </properties> + <children> + #include <include/bgp/evpn-type-1.xml.i> + #include <include/bgp/evpn-type-2.xml.i> + #include <include/bgp/evpn-type-3.xml.i> + #include <include/bgp/evpn-type-ead.xml.i> + #include <include/bgp/evpn-type-macip.xml.i> + #include <include/bgp/evpn-type-multicast.xml.i> + </children> + </node> + </children> + </tagNode> <leafNode name="vrf"> <properties> <help>Show BGP VRF information</help> 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/ifconfig/interface.py b/python/vyos/ifconfig/interface.py index fc33430eb..2f1d5eb96 100644 --- a/python/vyos/ifconfig/interface.py +++ b/python/vyos/ifconfig/interface.py @@ -532,7 +532,7 @@ class Interface(Control): return None # As a PoC we only allow 'dummy' interfaces - if 'dum' not in self.ifname: + if not ('dum' in self.ifname or 'veth' in self.ifname): return None # Check if interface realy exists in namespace @@ -1709,6 +1709,14 @@ class VLANIf(Interface): if self.exists(f'{self.ifname}'): return + # If source_interface or vlan_id was not explicitly defined (e.g. when + # calling VLANIf('eth0.1').remove() we can define source_interface and + # vlan_id here, as it's quiet obvious that it would be eth0 in that case. + if 'source_interface' not in self.config: + self.config['source_interface'] = '.'.join(self.ifname.split('.')[:-1]) + if 'vlan_id' not in self.config: + self.config['vlan_id'] = self.ifname.split('.')[-1] + cmd = 'ip link add link {source_interface} name {ifname} type vlan id {vlan_id}' if 'protocol' in self.config: cmd += ' protocol {protocol}' 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/convert.py b/python/vyos/utils/convert.py new file mode 100644 index 000000000..975c67e0a --- /dev/null +++ b/python/vyos/utils/convert.py @@ -0,0 +1,145 @@ +# 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 seconds_to_human(s, separator=""): + """ Converts number of seconds passed to a human-readable + interval such as 1w4d18h35m59s + """ + s = int(s) + + week = 60 * 60 * 24 * 7 + day = 60 * 60 * 24 + hour = 60 * 60 + + remainder = 0 + result = "" + + weeks = s // week + if weeks > 0: + result = "{0}w".format(weeks) + s = s % week + + days = s // day + if days > 0: + result = "{0}{1}{2}d".format(result, separator, days) + s = s % day + + hours = s // hour + if hours > 0: + result = "{0}{1}{2}h".format(result, separator, hours) + s = s % hour + + minutes = s // 60 + if minutes > 0: + result = "{0}{1}{2}m".format(result, separator, minutes) + s = s % 60 + + seconds = s + if seconds > 0: + result = "{0}{1}{2}s".format(result, separator, seconds) + + return result + +def bytes_to_human(bytes, initial_exponent=0, precision=2): + """ Converts a value in bytes to a human-readable size string like 640 KB + + The initial_exponent parameter is the exponent of 2, + e.g. 10 (1024) for kilobytes, 20 (1024 * 1024) for megabytes. + """ + + if bytes == 0: + return "0 B" + + from math import log2 + + bytes = bytes * (2**initial_exponent) + + # log2 is a float, while range checking requires an int + exponent = int(log2(bytes)) + + if exponent < 10: + value = bytes + suffix = "B" + elif exponent in range(10, 20): + value = bytes / 1024 + suffix = "KB" + elif exponent in range(20, 30): + value = bytes / 1024**2 + suffix = "MB" + elif exponent in range(30, 40): + value = bytes / 1024**3 + suffix = "GB" + else: + value = bytes / 1024**4 + suffix = "TB" + # Add a new case when the first machine with petabyte RAM + # hits the market. + + size_string = "{0:.{1}f} {2}".format(value, precision, suffix) + return size_string + +def human_to_bytes(value): + """ Converts a data amount with a unit suffix to bytes, like 2K to 2048 """ + + from re import match as re_match + + res = re_match(r'^\s*(\d+(?:\.\d+)?)\s*([a-zA-Z]+)\s*$', value) + + if not res: + raise ValueError(f"'{value}' is not a valid data amount") + else: + amount = float(res.group(1)) + unit = res.group(2).lower() + + if unit == 'b': + res = amount + elif (unit == 'k') or (unit == 'kb'): + res = amount * 1024 + elif (unit == 'm') or (unit == 'mb'): + res = amount * 1024**2 + elif (unit == 'g') or (unit == 'gb'): + res = amount * 1024**3 + elif (unit == 't') or (unit == 'tb'): + res = amount * 1024**4 + else: + raise ValueError(f"Unsupported data unit '{unit}'") + + # There cannot be fractional bytes, so we convert them to integer. + # However, truncating causes problems with conversion back to human unit, + # so we round instead -- that seems to work well enough. + return round(res) + +def mac_to_eui64(mac, prefix=None): + """ + Convert a MAC address to a EUI64 address or, with prefix provided, a full + IPv6 address. + Thankfully copied from https://gist.github.com/wido/f5e32576bb57b5cc6f934e177a37a0d3 + """ + import re + from ipaddress import ip_network + # http://tools.ietf.org/html/rfc4291#section-2.5.1 + eui64 = re.sub(r'[.:-]', '', mac).lower() + eui64 = eui64[0:6] + 'fffe' + eui64[6:] + eui64 = hex(int(eui64[0:2], 16) ^ 2)[2:].zfill(2) + eui64[2:] + + if prefix is None: + return ':'.join(re.findall(r'.{4}', eui64)) + else: + try: + net = ip_network(prefix, strict=False) + euil = int('0x{0}'.format(eui64), 16) + return str(net[euil]) + except: # pylint: disable=bare-except + return diff --git a/python/vyos/utils/dict.py b/python/vyos/utils/dict.py new file mode 100644 index 000000000..4afc9f54e --- /dev/null +++ b/python/vyos/utils/dict.py @@ -0,0 +1,256 @@ +# 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 + +def check_mutually_exclusive_options(d, keys, required=False): + """ Checks if a dict has at most one or only one of + mutually exclusive keys. + """ + present_keys = [] + + for k in d: + if k in keys: + present_keys.append(k) + + # Un-mangle the keys to make them match CLI option syntax + from re import sub + orig_keys = list(map(lambda s: sub(r'_', '-', s), keys)) + orig_present_keys = list(map(lambda s: sub(r'_', '-', s), present_keys)) + + if len(present_keys) > 1: + raise ValueError(f"Options {orig_keys} are mutually-exclusive but more than one of them is present: {orig_present_keys}") + + if required and (len(present_keys) < 1): + raise ValueError(f"At least one of the following options is required: {orig_present_keys}") diff --git a/python/vyos/utils/file.py b/python/vyos/utils/file.py new file mode 100644 index 000000000..2560a35be --- /dev/null +++ b/python/vyos/utils/file.py @@ -0,0 +1,171 @@ +# 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/>. + +import os + + +def read_file(fname, defaultonfailure=None): + """ + read the content of a file, stripping any end characters (space, newlines) + should defaultonfailure be not None, it is returned on failure to read + """ + try: + """ Read a file to string """ + with open(fname, 'r') as f: + data = f.read().strip() + return data + except Exception as e: + if defaultonfailure is not None: + return defaultonfailure + raise e + +def write_file(fname, data, defaultonfailure=None, user=None, group=None, mode=None, append=False): + """ + Write content of data to given fname, should defaultonfailure be not None, + it is returned on failure to read. + + If directory of file is not present, it is auto-created. + """ + dirname = os.path.dirname(fname) + if not os.path.isdir(dirname): + os.makedirs(dirname, mode=0o755, exist_ok=False) + chown(dirname, user, group) + + try: + """ Write a file to string """ + bytes = 0 + with open(fname, 'w' if not append else 'a') as f: + bytes = f.write(data) + chown(fname, user, group) + chmod(fname, mode) + return bytes + except Exception as e: + if defaultonfailure is not None: + return defaultonfailure + raise e + +def read_json(fname, defaultonfailure=None): + """ + read and json decode the content of a file + should defaultonfailure be not None, it is returned on failure to read + """ + import json + try: + with open(fname, 'r') as f: + data = json.load(f) + return data + except Exception as e: + if defaultonfailure is not None: + return defaultonfailure + raise e + +def chown(path, user, group): + """ change file/directory owner """ + from pwd import getpwnam + from grp import getgrnam + + if user is None or group is None: + return False + + # path may also be an open file descriptor + if not isinstance(path, int) and not os.path.exists(path): + return False + + uid = getpwnam(user).pw_uid + gid = getgrnam(group).gr_gid + os.chown(path, uid, gid) + return True + + +def chmod(path, bitmask): + # path may also be an open file descriptor + if not isinstance(path, int) and not os.path.exists(path): + return + if bitmask is None: + return + os.chmod(path, bitmask) + + +def chmod_600(path): + """ Make file only read/writable by owner """ + from stat import S_IRUSR, S_IWUSR + + bitmask = S_IRUSR | S_IWUSR + chmod(path, bitmask) + + +def chmod_750(path): + """ Make file/directory only executable to user and group """ + from stat import S_IRUSR, S_IWUSR, S_IXUSR, S_IRGRP, S_IXGRP + + bitmask = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP + chmod(path, bitmask) + + +def chmod_755(path): + """ Make file executable by all """ + from stat import S_IRUSR, S_IWUSR, S_IXUSR, S_IRGRP, S_IXGRP, S_IROTH, S_IXOTH + + bitmask = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | \ + S_IROTH | S_IXOTH + chmod(path, bitmask) + + +def makedir(path, user=None, group=None): + if os.path.exists(path): + return + os.makedirs(path, mode=0o755) + chown(path, user, group) + +def wait_for_inotify(file_path, pre_hook=None, event_type=None, timeout=None, sleep_interval=0.1): + """ Waits for an inotify event to occur """ + if not os.path.dirname(file_path): + raise ValueError( + "File path {} does not have a directory part (required for inotify watching)".format(file_path)) + if not os.path.basename(file_path): + raise ValueError( + "File path {} does not have a file part, do not know what to watch for".format(file_path)) + + from inotify.adapters import Inotify + from time import time + from time import sleep + + time_start = time() + + i = Inotify() + i.add_watch(os.path.dirname(file_path)) + + if pre_hook: + pre_hook() + + for event in i.event_gen(yield_nones=True): + if (timeout is not None) and ((time() - time_start) > timeout): + # If the function didn't return until this point, + # the file failed to have been written to and closed within the timeout + raise OSError("Waiting for file {} to be written has failed".format(file_path)) + + # Most such events don't take much time, so it's better to check right away + # and sleep later. + if event is not None: + (_, type_names, path, filename) = event + if filename == os.path.basename(file_path): + if event_type in type_names: + return + sleep(sleep_interval) + +def wait_for_file_write_complete(file_path, pre_hook=None, timeout=None, sleep_interval=0.1): + """ Waits for a process to close a file after opening it in write mode. """ + wait_for_inotify(file_path, + event_type='IN_CLOSE_WRITE', pre_hook=pre_hook, timeout=timeout, sleep_interval=sleep_interval) diff --git a/python/vyos/utils/io.py b/python/vyos/utils/io.py new file mode 100644 index 000000000..843494855 --- /dev/null +++ b/python/vyos/utils/io.py @@ -0,0 +1,103 @@ +# 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 print_error(str='', end='\n'): + """ + Print `str` to stderr, terminated with `end`. + Used for warnings and out-of-band messages to avoid mangling precious + stdout output. + """ + import sys + sys.stderr.write(str) + sys.stderr.write(end) + sys.stderr.flush() + +def make_progressbar(): + """ + Make a procedure that takes two arguments `done` and `total` and prints a + progressbar based on the ratio thereof, whose length is determined by the + width of the terminal. + """ + import shutil, math + col, _ = shutil.get_terminal_size() + col = max(col - 15, 20) + def print_progressbar(done, total): + if done <= total: + increment = total / col + length = math.ceil(done / increment) + percentage = str(math.ceil(100 * done / total)).rjust(3) + print_error(f'[{length * "#"}{(col - length) * "_"}] {percentage}%', '\r') + # Print a newline so that the subsequent prints don't overwrite the full bar. + if done == total: + print_error() + return print_progressbar + +def make_incremental_progressbar(increment: float): + """ + Make a generator that displays a progressbar that grows monotonically with + every iteration. + First call displays it at 0% and every subsequent iteration displays it + at `increment` increments where 0.0 < `increment` < 1.0. + Intended for FTP and HTTP transfers with stateless callbacks. + """ + print_progressbar = make_progressbar() + total = 0.0 + while total < 1.0: + print_progressbar(total, 1.0) + yield + total += increment + print_progressbar(1, 1) + # Ignore further calls. + while True: + yield + +def ask_input(question, default='', numeric_only=False, valid_responses=[]): + question_out = question + if default: + question_out += f' (Default: {default})' + response = '' + while True: + response = input(question_out + ' ').strip() + if not response and default: + return default + if numeric_only: + if not response.isnumeric(): + print("Invalid value, try again.") + continue + response = int(response) + if valid_responses and response not in valid_responses: + print("Invalid value, try again.") + continue + break + return response + +def ask_yes_no(question, default=False) -> bool: + """Ask a yes/no question via input() and return their answer.""" + from sys import stdout + default_msg = "[Y/n]" if default else "[y/N]" + while True: + try: + stdout.write("%s %s " % (question, default_msg)) + c = input().lower() + if c == '': + return default + elif c in ("y", "ye", "yes"): + return True + elif c in ("n", "no"): + return False + else: + stdout.write("Please respond with yes/y or no/n\n") + except EOFError: + stdout.write("\nPlease respond with yes/y or no/n\n") 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/basic-vyos b/smoketest/configs/basic-vyos index 23186b9b8..033c1a518 100644 --- a/smoketest/configs/basic-vyos +++ b/smoketest/configs/basic-vyos @@ -127,14 +127,40 @@ system { } name-server 192.168.0.1 syslog { - global { - archive { - file 5 - size 512 + console { + facility all { + level emerg + } + facility mail { + level info } + } + global { facility all { level info } + facility protocols { + level debug + } + facility security { + level info + } + preserve-fqdn + } + host syslog.vyos.net { + facility local7 { + level notice + } + facility protocols { + level alert + } + facility security { + level warning + } + format { + octet-counted + } + port 8000 } } time-zone Europe/Berlin 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_isis.py b/smoketest/scripts/cli/test_protocols_isis.py index 61e29c449..f1a030e77 100755 --- a/smoketest/scripts/cli/test_protocols_isis.py +++ b/smoketest/scripts/cli/test_protocols_isis.py @@ -119,39 +119,6 @@ class TestProtocolsISIS(VyOSUnitTestSHIM.TestCase): self.cli_delete(['vrf', 'name', vrf]) self.cli_delete(['interfaces', 'ethernet', vrf_iface, 'vrf']) - def test_isis_03_zebra_route_map(self): - # Implemented because of T3328 - route_map = 'foo-isis-in' - - self.cli_set(['policy', 'route-map', route_map, 'rule', '10', 'action', 'permit']) - - self.isis_base_config() - self.cli_set(base_path + ['redistribute', 'ipv4', 'connected', 'level-2', 'route-map', route_map]) - self.cli_set(base_path + ['route-map', route_map]) - self.cli_set(base_path + ['level', 'level-2']) - - # commit changes - self.cli_commit() - - # Verify FRR configuration - zebra_route_map = f'ip protocol isis route-map {route_map}' - frrconfig = self.getFRRconfig(zebra_route_map, daemon='zebra') - self.assertIn(zebra_route_map, frrconfig) - - tmp = self.getFRRconfig(f'router isis {domain}', daemon='isisd') - self.assertIn(' is-type level-2-only', tmp) - - # Remove the route-map again - self.cli_delete(base_path + ['route-map']) - # commit changes - self.cli_commit() - - # Verify FRR configuration - frrconfig = self.getFRRconfig(zebra_route_map, daemon='zebra') - self.assertNotIn(zebra_route_map, frrconfig) - - self.cli_delete(['policy', 'route-map', route_map]) - def test_isis_04_default_information(self): metric = '50' route_map = 'default-foo-' @@ -293,7 +260,7 @@ class TestProtocolsISIS(VyOSUnitTestSHIM.TestCase): self.cli_set(base_path + ['segment-routing', 'prefix', prefix_three, 'absolute', 'explicit-null']) self.cli_set(base_path + ['segment-routing', 'prefix', prefix_four, 'absolute', 'value', prefix_four_value]) self.cli_set(base_path + ['segment-routing', 'prefix', prefix_four, 'absolute', 'no-php-flag']) - + # Commit all changes self.cli_commit() @@ -315,16 +282,16 @@ class TestProtocolsISIS(VyOSUnitTestSHIM.TestCase): self.cli_set(base_path + ['net', net]) self.cli_set(base_path + ['interface', interface]) self.cli_set(base_path + ['ldp-sync', 'holddown', holddown]) - + # Commit main ISIS changes self.cli_commit() - + # Verify main ISIS changes tmp = self.getFRRconfig(f'router isis {domain}', daemon='isisd') self.assertIn(f' net {net}', tmp) self.assertIn(f' mpls ldp-sync', tmp) self.assertIn(f' mpls ldp-sync holddown {holddown}', tmp) - + for interface in self._interfaces: self.cli_set(base_path + ['interface', interface, 'ldp-sync', 'holddown', holddown]) @@ -337,13 +304,13 @@ class TestProtocolsISIS(VyOSUnitTestSHIM.TestCase): self.assertIn(f' ip router isis {domain}', tmp) self.assertIn(f' ipv6 router isis {domain}', tmp) self.assertIn(f' isis mpls ldp-sync holddown {holddown}', tmp) - + for interface in self._interfaces: self.cli_set(base_path + ['interface', interface, 'ldp-sync', 'disable']) - + # Commit interface changes for disable self.cli_commit() - + # Verify interface changes for disable tmp = self.getFRRconfig(f'interface {interface}', daemon='isisd') self.assertIn(f'interface {interface}', tmp) diff --git a/smoketest/scripts/cli/test_protocols_ospf.py b/smoketest/scripts/cli/test_protocols_ospf.py index d4c85f2b2..6fe6dd979 100755 --- a/smoketest/scripts/cli/test_protocols_ospf.py +++ b/smoketest/scripts/cli/test_protocols_ospf.py @@ -300,26 +300,6 @@ class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase): self.assertIn(f' no ip ospf passive', config) self.assertIn(f' bandwidth {bandwidth}', config) - def test_ospf_10_zebra_route_map(self): - # Implemented because of T3328 - self.cli_set(base_path + ['route-map', route_map]) - # commit changes - self.cli_commit() - - # Verify FRR configuration - zebra_route_map = f'ip protocol ospf route-map {route_map}' - frrconfig = self.getFRRconfig(zebra_route_map) - self.assertIn(zebra_route_map, frrconfig) - - # Remove the route-map again - self.cli_delete(base_path + ['route-map']) - # commit changes - self.cli_commit() - - # Verify FRR configuration - frrconfig = self.getFRRconfig(zebra_route_map) - self.assertNotIn(zebra_route_map, frrconfig) - def test_ospf_11_interface_area(self): area = '0' interfaces = Section.interfaces('ethernet') @@ -441,16 +421,16 @@ class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase): self.cli_set(base_path + ['interface', interface]) self.cli_set(base_path + ['ldp-sync', 'holddown', holddown]) - + # Commit main OSPF changes self.cli_commit() - + # Verify main OSPF changes frrconfig = self.getFRRconfig('router ospf') self.assertIn(f'router ospf', frrconfig) self.assertIn(f' timers throttle spf 200 1000 10000', frrconfig) self.assertIn(f' mpls ldp-sync holddown {holddown}', frrconfig) - + for interface in interfaces: self.cli_set(base_path + ['interface', interface, 'ldp-sync', 'holddown', holddown]) @@ -463,13 +443,13 @@ class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase): self.assertIn(f' ip ospf dead-interval 40', config) self.assertIn(f' ip ospf mpls ldp-sync', config) self.assertIn(f' ip ospf mpls ldp-sync holddown {holddown}', config) - + for interface in interfaces: self.cli_set(base_path + ['interface', interface, 'ldp-sync', 'disable']) - + # Commit interface changes for disable self.cli_commit() - + # Verify interface changes for disable config = self.getFRRconfig(f'interface {interface}') self.assertIn(f'interface {interface}', config) diff --git a/smoketest/scripts/cli/test_protocols_static.py b/smoketest/scripts/cli/test_protocols_static.py index 19efe7786..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) @@ -433,30 +447,5 @@ class TestProtocolsStatic(VyOSUnitTestSHIM.TestCase): self.assertIn(tmp, frrconfig) - def test_04_static_zebra_route_map(self): - # Implemented because of T3328 - route_map = 'foo-static-in' - self.cli_set(['policy', 'route-map', route_map, 'rule', '10', 'action', 'permit']) - - self.cli_set(base_path + ['route-map', route_map]) - # commit changes - self.cli_commit() - - # Verify FRR configuration - zebra_route_map = f'ip protocol static route-map {route_map}' - frrconfig = self.getFRRconfig(zebra_route_map) - self.assertIn(zebra_route_map, frrconfig) - - # Remove the route-map again - self.cli_delete(base_path + ['route-map']) - # commit changes - self.cli_commit() - - # Verify FRR configuration - frrconfig = self.getFRRconfig(zebra_route_map) - self.assertNotIn(zebra_route_map, frrconfig) - - self.cli_delete(['policy', 'route-map', route_map]) - if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/smoketest/scripts/cli/test_service_pppoe-server.py b/smoketest/scripts/cli/test_service_pppoe-server.py index 4f9181704..bb6a1c1cd 100755 --- a/smoketest/scripts/cli/test_service_pppoe-server.py +++ b/smoketest/scripts/cli/test_service_pppoe-server.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2022 VyOS maintainers and contributors +# Copyright (C) 2022-2023 VyOS maintainers and contributors # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 or later as @@ -243,9 +243,11 @@ class TestServicePPPoEServer(BasicAccelPPPTest.TestCase): def test_accel_radius_authentication(self): radius_called_sid = 'ifname:mac' radius_acct_interim_jitter = '9' + radius_acct_interim_interval = '60' self.set(['authentication', 'radius', 'called-sid-format', radius_called_sid]) self.set(['authentication', 'radius', 'acct-interim-jitter', radius_acct_interim_jitter]) + self.set(['authentication', 'radius', 'accounting-interim-interval', radius_acct_interim_interval]) # run common tests super().test_accel_radius_authentication() @@ -257,6 +259,7 @@ class TestServicePPPoEServer(BasicAccelPPPTest.TestCase): # Validate configuration self.assertEqual(conf['pppoe']['called-sid'], radius_called_sid) self.assertEqual(conf['radius']['acct-interim-jitter'], radius_acct_interim_jitter) + self.assertEqual(conf['radius']['acct-interim-interval'], radius_acct_interim_interval) def test_pppoe_server_vlan(self): 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/smoketest/scripts/system/test_kernel_options.py b/smoketest/scripts/system/test_kernel_options.py index 94be0483a..fe2a1c48a 100755 --- a/smoketest/scripts/system/test_kernel_options.py +++ b/smoketest/scripts/system/test_kernel_options.py @@ -63,6 +63,19 @@ class TestKernelModules(unittest.TestCase): self.assertIn(option, config_data, f"Option {option} is not present in /proc/config.gz") + def test_synproxy_enabled(self): + options_to_check = [ + 'CONFIG_NFT_SYNPROXY', + 'CONFIG_IP_NF_TARGET_SYNPROXY' + ] + if not os.path.isfile(CONFIG): + call('sudo modprobe configs') + with gzip.open(CONFIG, 'rt') as f: + config_data = f.read() + for option in options_to_check: + tmp = re.findall(f'{option}=(y|m)', config_data) + self.assertTrue(tmp) + def test_qemu_support(self): # The bond/lacp interface must be enabled in the OS Kernel for option in ['CONFIG_VIRTIO_BLK', 'CONFIG_SCSI_VIRTIO', diff --git a/src/completion/list_ddclient_protocols.sh b/src/completion/list_ddclient_protocols.sh new file mode 100755 index 000000000..75fb0cf44 --- /dev/null +++ b/src/completion/list_ddclient_protocols.sh @@ -0,0 +1,17 @@ +#!/bin/sh +# +# 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/>. + +echo -n $(ddclient -list-protocols) diff --git a/src/conf_mode/container.py b/src/conf_mode/container.py index 4b7ab3444..aceb27fb0 100755 --- a/src/conf_mode/container.py +++ b/src/conf_mode/container.py @@ -376,11 +376,11 @@ def generate(container): 'name': network, 'id' : sha256(f'{network}'.encode()).hexdigest(), 'driver': 'bridge', - 'network_interface': f'podman-{network}', + 'network_interface': f'pod-{network}', 'subnets': [], 'ipv6_enabled': False, 'internal': False, - 'dns_enabled': False, + 'dns_enabled': True, 'ipam_options': { 'driver': 'host-local' } @@ -479,7 +479,7 @@ def apply(container): # the network interface in advance if 'network' in container: for network, network_config in container['network'].items(): - network_name = f'podman-{network}' + network_name = f'pod-{network}' # T5147: Networks are started only as soon as there is a consumer. # If only a network is created in the first place, no need to assign # it to a VRF as there's no consumer, yet. 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/high-availability.py b/src/conf_mode/high-availability.py index 79e407efd..7a63f5b4b 100755 --- a/src/conf_mode/high-availability.py +++ b/src/conf_mode/high-availability.py @@ -86,7 +86,7 @@ def get_config(config=None): return ha def verify(ha): - if not ha: + if not ha or 'disable' in ha: return None used_vrid_if = [] @@ -106,6 +106,13 @@ def verify(ha): if not {'password', 'type'} <= set(group_config['authentication']): raise ConfigError(f'Authentication requires both type and passwortd to be set in VRRP group "{group}"') + if 'health_check' in group_config: + from vyos.utils.dict import check_mutually_exclusive_options + try: + check_mutually_exclusive_options(group_config["health_check"], ["script", "ping"], required=True) + except ValueError as e: + raise ConfigError(f'Health check config is incorrect in VRRP group "{group}": {e}') + # Keepalived doesn't allow mixing IPv4 and IPv6 in one group, so we mirror that restriction # We also need to make sure VRID is not used twice on the same interface with the # same address family. @@ -175,7 +182,7 @@ def verify(ha): def generate(ha): - if not ha: + if not ha or 'disable' in ha: return None render(VRRP.location['config'], 'high-availability/keepalived.conf.j2', ha) @@ -183,7 +190,7 @@ def generate(ha): def apply(ha): service_name = 'keepalived.service' - if not ha: + if not ha or 'disable' in ha: call(f'systemctl stop {service_name}') return None 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/service_pppoe-server.py b/src/conf_mode/service_pppoe-server.py index 600ba4e92..adeefaa37 100755 --- a/src/conf_mode/service_pppoe-server.py +++ b/src/conf_mode/service_pppoe-server.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2018-2022 VyOS maintainers and contributors +# Copyright (C) 2018-2023 VyOS maintainers and contributors # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 or later as @@ -71,8 +71,9 @@ def verify(pppoe): # local ippool and gateway settings config checks if not (dict_search('client_ip_pool.subnet', pppoe) or + (dict_search('client_ip_pool.name', pppoe) or (dict_search('client_ip_pool.start', pppoe) and - dict_search('client_ip_pool.stop', pppoe))): + dict_search('client_ip_pool.stop', pppoe)))): print('Warning: No PPPoE client pool defined') if dict_search('authentication.radius.dynamic_author.server', pppoe): diff --git a/src/conf_mode/system-syslog.py b/src/conf_mode/system-syslog.py index 20132456c..e646fb0ae 100755 --- a/src/conf_mode/system-syslog.py +++ b/src/conf_mode/system-syslog.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2018-2020 VyOS maintainers and contributors +# Copyright (C) 2018-2023 VyOS maintainers and contributors # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 or later as @@ -15,253 +15,129 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. import os -import re -from pathlib import Path from sys import exit from vyos.config import Config -from vyos import ConfigError -from vyos.util import run +from vyos.configdict import dict_merge +from vyos.configdict import is_node_changed +from vyos.configverify import verify_vrf +from vyos.util import call from vyos.template import render - +from vyos.xml import defaults +from vyos import ConfigError from vyos import airbag airbag.enable() +rsyslog_conf = '/etc/rsyslog.d/00-vyos.conf' +logrotate_conf = '/etc/logrotate.d/vyos-rsyslog' +systemd_override = r'/run/systemd/system/rsyslog.service.d/override.conf' + def get_config(config=None): if config: - c = config + conf = config else: - c = Config() - if not c.exists('system syslog'): + conf = Config() + base = ['system', 'syslog'] + if not conf.exists(base): return None - c.set_level('system syslog') - - config_data = { - 'files': {}, - 'console': {}, - 'hosts': {}, - 'user': {} - } - - # - # /etc/rsyslog.d/vyos-rsyslog.conf - # 'set system syslog global' - # - config_data['files'].update( - { - 'global': { - 'log-file': '/var/log/messages', - 'selectors': '*.notice;local7.debug', - 'max-files': '5', - 'preserver_fqdn': False - } - } - ) - - if c.exists('global marker'): - config_data['files']['global']['marker'] = True - if c.exists('global marker interval'): - config_data['files']['global'][ - 'marker-interval'] = c.return_value('global marker interval') - if c.exists('global facility'): - config_data['files']['global'][ - 'selectors'] = generate_selectors(c, 'global facility') - if c.exists('global archive size'): - config_data['files']['global']['max-size'] = int( - c.return_value('global archive size')) * 1024 - if c.exists('global archive file'): - config_data['files']['global'][ - 'max-files'] = c.return_value('global archive file') - if c.exists('global preserve-fqdn'): - config_data['files']['global']['preserver_fqdn'] = True - - # - # set system syslog file - # - - if c.exists('file'): - filenames = c.list_nodes('file') - for filename in filenames: - config_data['files'].update( - { - filename: { - 'log-file': '/var/log/user/' + filename, - 'max-files': '5', - 'action-on-max-size': '/usr/sbin/logrotate /etc/logrotate.d/vyos-rsyslog-generated-' + filename, - 'selectors': '*.err', - 'max-size': 262144 - } - } - ) - - if c.exists('file ' + filename + ' facility'): - config_data['files'][filename]['selectors'] = generate_selectors( - c, 'file ' + filename + ' facility') - if c.exists('file ' + filename + ' archive size'): - config_data['files'][filename]['max-size'] = int( - c.return_value('file ' + filename + ' archive size')) * 1024 - if c.exists('file ' + filename + ' archive files'): - config_data['files'][filename]['max-files'] = c.return_value( - 'file ' + filename + ' archive files') - - # set system syslog console - if c.exists('console'): - config_data['console'] = { - '/dev/console': { - 'selectors': '*.err' - } - } - - for f in c.list_nodes('console facility'): - if c.exists('console facility ' + f + ' level'): - config_data['console'] = { - '/dev/console': { - 'selectors': generate_selectors(c, 'console facility') - } - } - # set system syslog host - if c.exists('host'): - rhosts = c.list_nodes('host') - proto = 'udp' - for rhost in rhosts: - for fac in c.list_nodes('host ' + rhost + ' facility'): - if c.exists('host ' + rhost + ' facility ' + fac + ' protocol'): - proto = c.return_value( - 'host ' + rhost + ' facility ' + fac + ' protocol') - else: - proto = 'udp' - - config_data['hosts'].update( - { - rhost: { - 'selectors': generate_selectors(c, 'host ' + rhost + ' facility'), - 'proto': proto - } - } - ) - if c.exists('host ' + rhost + ' port'): - config_data['hosts'][rhost][ - 'port'] = c.return_value(['host', rhost, 'port']) - - # set system syslog host x.x.x.x format octet-counted - if c.exists('host ' + rhost + ' format octet-counted'): - config_data['hosts'][rhost]['oct_count'] = True - else: - config_data['hosts'][rhost]['oct_count'] = False - - # set system syslog user - if c.exists('user'): - usrs = c.list_nodes('user') - for usr in usrs: - config_data['user'].update( - { - usr: { - 'selectors': generate_selectors(c, 'user ' + usr + ' facility') - } - } - ) - - return config_data - - -def generate_selectors(c, config_node): -# protocols and security are being mapped here -# for backward compatibility with old configs -# security and protocol mappings can be removed later - nodes = c.list_nodes(config_node) - selectors = "" - for node in nodes: - lvl = c.return_value(config_node + ' ' + node + ' level') - if lvl == None: - lvl = "err" - if lvl == 'all': - lvl = '*' - if node == 'all' and node != nodes[-1]: - selectors += "*." + lvl + ";" - elif node == 'all': - selectors += "*." + lvl - elif node != nodes[-1]: - if node == 'protocols': - node = 'local7' - if node == 'security': - node = 'auth' - selectors += node + "." + lvl + ";" - else: - if node == 'protocols': - node = 'local7' - if node == 'security': - node = 'auth' - selectors += node + "." + lvl - return selectors - - -def generate(c): - if c == None: + syslog = conf.get_config_dict(base, key_mangling=('-', '_'), + get_first_key=True, no_tag_node_value_mangle=True) + + syslog.update({ 'logrotate' : logrotate_conf }) + tmp = is_node_changed(conf, base + ['vrf']) + if tmp: syslog.update({'restart_required': {}}) + + # 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) + # XXX: some syslog default values can not be merged here (originating from + # a tagNode - remove and add them later per individual tagNode instance + if 'console' in default_values: + del default_values['console'] + for entity in ['global', 'user', 'host', 'file']: + if entity in default_values: + del default_values[entity] + + syslog = dict_merge(default_values, syslog) + + # XXX: add defaults for "console" tree + if 'console' in syslog and 'facility' in syslog['console']: + default_values = defaults(base + ['console', 'facility']) + for facility in syslog['console']['facility']: + syslog['console']['facility'][facility] = dict_merge(default_values, + syslog['console']['facility'][facility]) + + # XXX: add defaults for "host" tree + if 'host' in syslog: + default_values_host = defaults(base + ['host']) + if 'facility' in default_values_host: + del default_values_host['facility'] + default_values_facility = defaults(base + ['host', 'facility']) + + for host, host_config in syslog['host'].items(): + syslog['host'][host] = dict_merge(default_values_host, syslog['host'][host]) + if 'facility' in host_config: + for facility in host_config['facility']: + syslog['host'][host]['facility'][facility] = dict_merge(default_values_facility, + syslog['host'][host]['facility'][facility]) + + # XXX: add defaults for "user" tree + if 'user' in syslog: + default_values = defaults(base + ['user', 'facility']) + for user, user_config in syslog['user'].items(): + if 'facility' in user_config: + for facility in user_config['facility']: + syslog['user'][user]['facility'][facility] = dict_merge(default_values, + syslog['user'][user]['facility'][facility]) + + # XXX: add defaults for "file" tree + if 'file' in syslog: + default_values = defaults(base + ['file']) + for file, file_config in syslog['file'].items(): + for facility in file_config['facility']: + syslog['file'][file]['facility'][facility] = dict_merge(default_values, + syslog['file'][file]['facility'][facility]) + + return syslog + +def verify(syslog): + if not syslog: return None - conf = '/etc/rsyslog.d/vyos-rsyslog.conf' - render(conf, 'syslog/rsyslog.conf.j2', c) - - # cleanup current logrotate config files - logrotate_files = Path('/etc/logrotate.d/').glob('vyos-rsyslog-generated-*') - for file in logrotate_files: - file.unlink() + verify_vrf(syslog) - # eventually write for each file its own logrotate file, since size is - # defined it shouldn't matter - for filename, fileconfig in c.get('files', {}).items(): - if fileconfig['log-file'].startswith('/var/log/user/'): - conf = '/etc/logrotate.d/vyos-rsyslog-generated-' + filename - render(conf, 'syslog/logrotate.j2', { 'config_render': fileconfig }) +def generate(syslog): + if not syslog: + if os.path.exists(rsyslog_conf): + os.path.unlink(rsyslog_conf) + if os.path.exists(logrotate_conf): + os.path.unlink(logrotate_conf) - -def verify(c): - if c == None: return None - # may be obsolete - # /etc/rsyslog.conf is generated somewhere and copied over the original (exists in /opt/vyatta/etc/rsyslog.conf) - # it interferes with the global logging, to make sure we are using a single base, template is enforced here - # - if not os.path.islink('/etc/rsyslog.conf'): - os.remove('/etc/rsyslog.conf') - os.symlink( - '/usr/share/vyos/templates/rsyslog/rsyslog.conf', '/etc/rsyslog.conf') + render(rsyslog_conf, 'rsyslog/rsyslog.conf.j2', syslog) + render(systemd_override, 'rsyslog/override.conf.j2', syslog) + render(logrotate_conf, 'rsyslog/logrotate.j2', syslog) - # /var/log/vyos-rsyslog were the old files, we may want to clean those up, but currently there - # is a chance that someone still needs it, so I don't automatically remove - # them - # + # Reload systemd manager configuration + call('systemctl daemon-reload') + return None - if c == None: +def apply(syslog): + systemd_service = 'syslog.service' + if not syslog: + call(f'systemctl stop {systemd_service}') return None - fac = [ - '*', 'auth', 'authpriv', 'cron', 'daemon', 'kern', 'lpr', 'mail', 'mark', 'news', 'protocols', 'security', - 'syslog', 'user', 'uucp', 'local0', 'local1', 'local2', 'local3', 'local4', 'local5', 'local6', 'local7'] - lvl = ['emerg', 'alert', 'crit', 'err', - 'warning', 'notice', 'info', 'debug', '*'] - - for conf in c: - if c[conf]: - for item in c[conf]: - for s in c[conf][item]['selectors'].split(";"): - f = re.sub("\..*$", "", s) - if f not in fac: - raise ConfigError( - 'Invalid facility ' + s + ' set in ' + conf + ' ' + item) - l = re.sub("^.+\.", "", s) - if l not in lvl: - raise ConfigError( - 'Invalid logging level ' + s + ' set in ' + conf + ' ' + item) - + # we need to restart the service if e.g. the VRF name changed + systemd_action = 'reload-or-restart' + if 'restart_required' in syslog: + systemd_action = 'restart' -def apply(c): - if not c: - return run('systemctl stop syslog.service') - return run('systemctl restart syslog.service') + call(f'systemctl {systemd_action} {systemd_service}') + return None if __name__ == '__main__': try: diff --git a/src/conf_mode/vpn_l2tp.py b/src/conf_mode/vpn_l2tp.py index 65623c2b1..ffac3b023 100755 --- a/src/conf_mode/vpn_l2tp.py +++ b/src/conf_mode/vpn_l2tp.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2019-2020 VyOS maintainers and contributors +# Copyright (C) 2019-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 @@ -63,6 +63,7 @@ default_config_data = { 'ppp_ipv6_peer_intf_id': None, 'radius_server': [], 'radius_acct_inter_jitter': '', + 'radius_acct_interim_interval': None, 'radius_acct_tmo': '3', 'radius_max_try': '3', 'radius_timeout': '3', @@ -190,6 +191,9 @@ def get_config(config=None): # advanced radius-setting conf.set_level(base_path + ['authentication', 'radius']) + if conf.exists(['accounting-interim-interval']): + l2tp['radius_acct_interim_interval'] = conf.return_value(['accounting-interim-interval']) + if conf.exists(['acct-interim-jitter']): l2tp['radius_acct_inter_jitter'] = conf.return_value(['acct-interim-jitter']) diff --git a/src/conf_mode/vpn_pptp.py b/src/conf_mode/vpn_pptp.py index 986a19972..b9d18110a 100755 --- a/src/conf_mode/vpn_pptp.py +++ b/src/conf_mode/vpn_pptp.py @@ -37,6 +37,7 @@ default_pptp = { 'local_users' : [], 'radius_server' : [], 'radius_acct_inter_jitter': '', + 'radius_acct_interim_interval': None, 'radius_acct_tmo' : '30', 'radius_max_try' : '3', 'radius_timeout' : '30', @@ -145,6 +146,9 @@ def get_config(config=None): # advanced radius-setting conf.set_level(base_path + ['authentication', 'radius']) + if conf.exists(['accounting-interim-interval']): + pptp['radius_acct_interim_interval'] = conf.return_value(['accounting-interim-interval']) + if conf.exists(['acct-interim-jitter']): pptp['radius_acct_inter_jitter'] = conf.return_value(['acct-interim-jitter']) 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/data/templates/rsyslog/rsyslog.conf b/src/etc/rsyslog.conf index ab60fc0f0..c28e9b537 100644 --- a/data/templates/rsyslog/rsyslog.conf +++ b/src/etc/rsyslog.conf @@ -1,6 +1,3 @@ -# /etc/rsyslog.conf Configuration file for rsyslog. -# - ################# #### MODULES #### ################# @@ -14,22 +11,30 @@ $SystemLogSocketName /run/systemd/journal/syslog $KLogPath /proc/kmsg -# provides UDP syslog reception -#$ModLoad imudp -#$UDPServerRun 514 - -# provides TCP syslog reception -#$ModLoad imtcp -#$InputTCPServerRun 514 - ########################### #### GLOBAL DIRECTIVES #### ########################### -# +# The lines below cause all listed daemons/processes to be logged into +# /var/log/auth.log, then drops the message so it does not also go to the +# regular syslog so that messages are not duplicated + +$outchannel auth_log,/var/log/auth.log +if $programname == 'CRON' or + $programname == 'sudo' or + $programname == 'su' + then :omfile:$auth_log + +if $programname == 'CRON' or + $programname == 'sudo' or + $programname == 'su' + then stop + # Use traditional timestamp format. # To enable high precision timestamps, comment out the following line. -# +# A modern-style logfile format similar to TraditionalFileFormat, buth with high-precision timestamps and timezone information +#$ActionFileDefaultTemplate RSYSLOG_FileFormat +# The "old style" default log file format with low-precision timestamps $ActionFileDefaultTemplate RSYSLOG_TraditionalFileFormat # Filter duplicated messages @@ -44,6 +49,11 @@ $FileCreateMode 0640 $DirCreateMode 0755 $Umask 0022 +# +# Stop excessive logging of sudo +# +:msg, contains, " pam_unix(sudo:session): session opened for user root(uid=0) by" ~ +:msg, contains, "pam_unix(sudo:session): session closed for user root" ~ # # Include all config files in /etc/rsyslog.d/ @@ -54,6 +64,4 @@ $IncludeConfig /etc/rsyslog.d/*.conf #### RULES #### ############### # Emergencies are sent to everybody logged in. - -*.emerg :omusrmsg:* - +*.emerg :omusrmsg:*
\ No newline at end of file diff --git a/src/etc/rsyslog.d/01-auth.conf b/src/etc/rsyslog.d/01-auth.conf deleted file mode 100644 index cc64099d6..000000000 --- a/src/etc/rsyslog.d/01-auth.conf +++ /dev/null @@ -1,14 +0,0 @@ -# The lines below cause all listed daemons/processes to be logged into -# /var/log/auth.log, then drops the message so it does not also go to the -# regular syslog so that messages are not duplicated - -$outchannel auth_log,/var/log/auth.log -if $programname == 'CRON' or - $programname == 'sudo' or - $programname == 'su' - then :omfile:$auth_log - -if $programname == 'CRON' or - $programname == 'sudo' or - $programname == 'su' - then stop 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/migration-scripts/system/25-to-26 b/src/migration-scripts/system/25-to-26 new file mode 100755 index 000000000..615274430 --- /dev/null +++ b/src/migration-scripts/system/25-to-26 @@ -0,0 +1,82 @@ +#!/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/>. +# +# syslog: migrate deprecated CLI options +# - protocols -> local7 +# - security -> auth + +from sys import exit, argv +from vyos.configtree import ConfigTree + +if (len(argv) < 1): + print("Must specify file name!") + exit(1) + +file_name = argv[1] +with open(file_name, 'r') as f: + config_file = f.read() + +base = ['system', 'syslog'] +config = ConfigTree(config_file) + +if not config.exists(base): + exit(0) + +def rename_facilities(config, base_tree, facility, facility_new) -> None: + if config.exists(base + [base_tree, 'facility', facility]): + # do not overwrite already existing replacement facility + if not config.exists(base + [base_tree, 'facility', facility_new]): + config.rename(base + [base_tree, 'facility', facility], facility_new) + else: + # delete old duplicate facility config + config.delete(base + [base_tree, 'facility', facility]) + +# +# Rename protocols and securityy facility to common ones +# +replace = { + 'protocols' : 'local7', + 'security' : 'auth' +} +for facility, facility_new in replace.items(): + rename_facilities(config, 'console', facility, facility_new) + rename_facilities(config, 'global', facility, facility_new) + + if config.exists(base + ['host']): + for host in config.list_nodes(base + ['host']): + rename_facilities(config, f'host {host}', facility, facility_new) + +# +# It makes no sense to configure udp/tcp transport per individual facility +# +if config.exists(base + ['host']): + for host in config.list_nodes(base + ['host']): + protocol = None + for facility in config.list_nodes(base + ['host', host, 'facility']): + tmp_path = base + ['host', host, 'facility', facility, 'protocol'] + if config.exists(tmp_path): + # We can only change the first one + if protocol == None: + protocol = config.return_value(tmp_path) + config.set(base + ['host', host, 'protocol'], value=protocol) + config.delete(tmp_path) + +try: + with open(file_name, 'w') as f: + f.write(config.to_string()) +except OSError as e: + print(f'Failed to save the modified config: {e}') + exit(1) 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/dhcp.py b/src/op_mode/dhcp.py index 41da14065..fe7f252ba 100755 --- a/src/op_mode/dhcp.py +++ b/src/op_mode/dhcp.py @@ -264,8 +264,10 @@ def show_pool_statistics(raw: bool, family: ArgFamily, pool: typing.Optional[str def show_server_leases(raw: bool, family: ArgFamily, pool: typing.Optional[str], sorted: typing.Optional[str], state: typing.Optional[ArgState]): # if dhcp server is down, inactive leases may still be shown as active, so warn the user. - if not is_systemd_service_running('isc-dhcp-server.service'): - Warning('DHCP server is configured but not started. Data may be stale.') + v = '6' if family == 'inet6' else '' + service_name = 'DHCPv6' if family == 'inet6' else 'DHCP' + if not is_systemd_service_running(f'isc-dhcp-server{v}.service'): + Warning(f'{service_name} server is configured but not started. Data may be stale.') v = 'v6' if family == 'inet6' else '' if pool and pool not in _get_dhcp_pools(family=family): 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/pki.py b/src/op_mode/pki.py index 1e78c3a03..b054690b0 100755 --- a/src/op_mode/pki.py +++ b/src/op_mode/pki.py @@ -87,6 +87,9 @@ def get_config_certificate(name=None): def get_certificate_ca(cert, ca_certs): # Find CA certificate for given certificate + if not ca_certs: + return None + for ca_name, ca_dict in ca_certs.items(): if 'certificate' not in ca_dict: continue diff --git a/src/op_mode/show_techsupport_report.py b/src/op_mode/show_techsupport_report.py index 13ed9a3c1..782004144 100644 --- a/src/op_mode/show_techsupport_report.py +++ b/src/op_mode/show_techsupport_report.py @@ -14,425 +14,290 @@ # 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 vyos.util import call import os +from typing import List +from vyos.util import rc_cmd +from vyos.ifconfig import Section +from vyos.ifconfig import Interface -def header(cmd): - print(16 * '-' + '\n' + cmd + '\n' + 16 * '-') - return - - -# get intefaces info -interfaces_list = os.popen('ls /sys/class/net/ | grep eth').read().split() -bridges_list = os.popen('ls /sys/class/net/ | grep br').read().split() - -###################### THE PART OF CONFIGURATION ###################### - -cmd_list_conf = [ - "VyOS Version and Package Changes%/opt/vyatta/bin/vyatta-op-cmd-wrapper show version all", - "Configuration File%cat /opt/vyatta/etc/config/config.boot", - "Running configuration%/opt/vyatta/bin/vyatta-op-cmd-wrapper show configuration", - "Package Repository Configuration File%cat /etc/apt/sources.list", - "User Startup Scripts%cat /etc/rc.local", - "Quagga Configuration%vtysh -c 'show run'" -] - - -def CONFIGURATION(cmd): - for command_line in cmd: - line = command_line.split('%') - head = line[0] - command = line[1] - header(head) - call(command) - return - - -###################### THE PART OF INTERFACES ###################### - -cmd_list_int = [ - "Interfaces%/opt/vyatta/bin/vyatta-op-cmd-wrapper show interfaces", - "Ethernet", - "Interface statistics%ip -s link show", - "Physical Interface statistics for%ethtool -S", - "Physical Interface Details for %/opt/vyatta/bin/vyatta-op-cmd-wrapper show interfaces ethernet%ethtool -k $eth", - "ARP Table (Total entries)%/opt/vyatta/bin/vyatta-op-cmd-wrapper show arp", - "Number of incomplete entries in ARP table%show arp | grep incomplete | wc -l", - "Bridges" -] - - -def INTERFACES(cmd): - for command_line in cmd: - line = command_line.split('%') - head = line[0] - if command_line.startswith("Ethernet"): - header(command_line) - elif command_line.startswith("Physical Interface statistics"): - for command_interface in interfaces_list: - header(f'{head} {command_interface}') - call(f'{line[1]} {command_interface}') - elif command_line.startswith("Physical Interface Details"): - for command_interface in interfaces_list: - header(f'{head} {command_interface}') - call(f'{line[1]} {command_interface} physical') - call(f'{line[2]} {command_interface}') - elif command_line.startswith("Bridges"): - header(command_line) - for command_interface in bridges_list: - header(f'Information for {command_interface}') - call(f'/sbin/brctl showstp {command_interface}') - call(f'/sbin/brctl showmacs {command_interface}') - else: - command = line[1] - header(head) - call(command) - return - - -###################### THE PART OF ROUTING ###################### - -cmd_list_route = [ - "show ip route bgp", - "show ip route cache", - "show ip route connected", - "show ip route forward", - "show ip route isis", - "show ip route kernel", - "show ip route ospf", - "show ip route rip", - "show ip route static", - "show ip route summary", - "show ip route supernets-only", - "show ip route table", - "show ip route tag", - "show ip route vrf", - "show ipv6 route bgp", - "show ipv6 route cache", - "show ipv6 route connected", - "show ipv6 route forward", - "show ipv6 route isis", - "show ipv6 route kernel", - "show ipv6 route ospf", - "show ipv6 route rip", - "show ipv6 route static", - "show ipv6 route summary", - "show ipv6 route supernets-only", - "show ipv6 route table", - "show ipv6 route tag", - "show ipv6 route vrf", -] - - -def ROUTING(cmd): - for command_line in cmd: - head = command_line - command = command_line - header(head) - call(f'/opt/vyatta/bin/vyatta-op-cmd-wrapper {command}') - return - - -###################### THE PART OF IPTABLES ###################### - -cmd_list_iptables = [ - "Filter Chain Details%sudo /sbin/iptables -L -vn", - "Nat Chain Details%sudo /sbin/iptables -t nat -L -vn", - "Mangle Chain Details%sudo /sbin/iptables -t mangle -L -vn", - "Raw Chain Details%sudo /sbin/iptables -t raw -L -vn", - "Save Iptables Rule-Set%sudo iptables-save -c" -] - - -def IPTABLES(cmd): - for command_line in cmd: - line = command_line.split('%') - head = line[0] - command = line[1] - header(head) - call(command) - return - - -###################### THE PART OF SYSTEM ###################### - -cmd_list_system = [ - "Show System Image Version%show system image version", - "Show System Image Storage%show system image storage", - "Current Time%date", - "Installed Packages%dpkg -l", - "Loaded Modules%cat /proc/modules", - "CPU", - "Installed CPU/s%lscpu", - "Cumulative CPU Time Used by Running Processes%top -n1 -b -S", - "Hardware Interrupt Counters%cat /proc/interrupts", - "Load Average%cat /proc/loadavg" -] - - -def SYSTEM(cmd): - for command_line in cmd: - line = command_line.split('%') - head = line[0] - if command_line.startswith("CPU"): - header(command_line) - elif line[1].startswith("show"): - header(head) - command = line[1] - call(f'/opt/vyatta/bin/vyatta-op-cmd-wrapper {command}') - else: - header(head) - command = line[1] - call(command) - return - - -###################### THE PART OF PROCESSES ###################### - -cmd_list_processes = [ - "Running Processes%ps -ef", - "Memory", - "Installed Memory%cat /proc/meminfo", - " Memory Usage%free", - "Storage", - "Devices%cat /proc/devices", - "Partitions%cat /proc/partitions", - "Partitioning for disks%fdisk -l /dev/" -] - - -def PROCESSES(cmd): - for command_line in cmd: - line = command_line.split('%') - head = line[0] - if command_line.startswith("Memory"): - header(command_line) - elif command_line.startswith("Storage"): - header(command_line) - elif command_line.startswith("Partitioning for disks"): - header(head) - disks = set() - with open('/proc/partitions') as partitions_file: - for line in partitions_file: - fields = line.strip().split() - if len(fields) == 4 and fields[3].isalpha() and fields[3] != 'name': - disks.add(fields[3]) - for disk in disks: - call(f'fdisk -l /dev/{disk}') - else: - header(head) - command = line[1] - call(command) - return - - -###################### THE PART OF CORE SECTION ###################### - -cmd_list_core = [ - "Mounts%cat /proc/mounts", - "Diskstats%cat /proc/diskstats", - "Hard Drive Usage%df -h -x squashfs", - # "General System", - "Boot Messages%cat /var/log/dmesg", - "Recent Kernel messages (dmesg)%dmesg", - "PCI Info%sudo lspci -vvx", - "PCI Vendor and Device Codes%sudo lspci -nn", - # "System Info%${vyatta_bindir}/vyatta-show-dmi", - "GRUB Command line%cat /proc/cmdline", - "Open Ports%sudo lsof -P -n -i", - "System Startup Files%ls -l /etc/rc?.d", - "Login History%last -ix", - "Recent Log Messages%tail -n 250 /var/log/messages", - "NTP%/opt/vyatta/bin/vyatta-op-cmd-wrapper show ntp", -] - - -def CORE(cmd): - for command_line in cmd: - line = command_line.split('%') - command = line[1] - header(line[0]) - call(command) - return - - -###################### THE PART OF VyOS INFORMATION ###################### - -cmd_list_vyos = [ - "BGP", - "header BGP Summary", - "show ip bgp summary", - "header BGP Neighbors", - "show ip bgp neighbors", - "header BGP Debugging Information", - "show monitoring protocols bgp", - "CLUSTERING", - "Cluster Status", - "show cluster status", - "DHCP Server", - "DHCP Leases", - "show dhcp server leases", - "DHCP Statistics", - "show dhcp server statistics", - "DHCP Client", - "DHCP Client Leases", - "show dhcp client leases", - "DHCPV6 Server", - "DHCPV6 Server Status", - "show dhcpv6 server status", - "DHCPV6 Server Leases", - "show dhcpv6 server leases", - "DHCPV6 Relay", - "DHCPV6 Relay Status", - "show dhcpv6 relay-agent status", - "DHCPV6 Client", - "DHCPV6 Client Leases", - "show dhcpv6 client leases", - "DNS", - "DNS Dynamic Status", - "show dns dynamic status", - "DNS Forwarding Statistics", - "show dns forwarding statistics", - "DNS Forwarding Nameservers", - "show dns forwarding nameservers", - "FIREWALL", - "Firewall Group", - "show firewall group", - "Firewall Summary", - "show firewall summary", - "Firewall Statistics", - "show firewall statistics", - "IPSec", - "IPSec Status", - "show vpn ipsec status", - "IPSec sa", - "show vpn ipsec sa", - "IPSec sa Detail", - "show vpn ipsec sa detail", - "IPSec sa Statistics", - "show vpn ipsec sa statistics", - "/etc/ipsec.conf", - "cat /etc/ipsec.conf", - "/etc/ipsec.secrets", - "cat /etc/ipsec.secrets", - "NAT", - "NAT Rules", - "show nat rules", - "NAT Statistics", - "show nat statistics", - "NAT Translations Detail", - "show nat translations detail", - "FlowAccounting", - "show flow-accounting", - "OPENVPN", - "OpenVPN Interfaces", - "show interfaces openvpn detail", - "OpenVPN Server Status", - "show openvpn status server", - "OSPF", - "OSPF Neighbor", - "show ip ospf neighbor", - "OSPF Route", - "show ip ospf route", - "OSPF Debugging Information", - "show monitoring protocols ospf", - "OSPFV3", - "OSPFV3 Debugging Information", - "show monitoring protocols ospfv3", - "Policy", - "IP Route Maps", - "show ip protocol", - "Route-Map", - "show route-map", - # header IP Access Lists - # show ip access-lists - "IP Community List", - "show ip community-list", - "Traffic Policy", - "Current Traffic Policies", - "show queueing", - "RIP", - "IP RIP", - "show ip rip", - "RIP Status", - "show ip rip status", - "RIP Debugging Information", - "show monitoring protocols rip", - "RIPNG", - "RIPNG Debugging Information", - "show monitoring protocols ripng", - "VPN-L2TP", - "VPN ike secrets", - "show vpn ike secrets", - "VPN rsa-keys", - "show vpn ike rsa-keys", - "VPN ike sa", - "show vpn ike sa", - "VPN ike Status", - "show vpn ike status", - "VPN Remote-Access", - "show vpn remote-access", - "VPN Debug Detail", - "show vpn debug detail", - "VPN-PPTP", - "VPN Remote-Access", - "show vpn remote-access", - "VRRP", - # XXX: not checking if configured, we'd have to walk all VIFs - "show vrrp detail", - "WAN LOAD BALANCING", - "Wan Load Balance", - "show wan-load-balance", - "Wan Load Balance Status", - "show wan-load-balance status", - "Wan Load Balance Connection", - "show wan-load-balance connection", - "WEBPROXY/URL-FILTERING", - "WebProxy Blacklist Categories", - "show webproxy blacklist categories", - "WebProxy Blacklist Domains", - "show webproxy blacklist domains", - "WebProxy Blacklist URLs", - "show webproxy blacklist urls", - "WebProxy Blacklist Log", - "show webproxy blacklist log summary", -] - - -def VyOS(cmd): - for command_line in cmd: - if command_line.startswith("show"): - call(f'/opt/vyatta/bin/vyatta-op-cmd-wrapper {command_line}') - elif command_line.startswith("cat"): - call(command_line) - else: - header(command_line) - return - - -###################### execute all the commands ###################### - -header('CONFIGURATION') -CONFIGURATION(cmd_list_conf) - -header('INTERFACES') -INTERFACES(cmd_list_int) - -header('ROUTING') -ROUTING(cmd_list_route) - -header('IPTABLES') -IPTABLES(cmd_list_iptables) - -header('SYSTEM') -SYSTEM(cmd_list_system) - -header('PROCESSES') -PROCESSES(cmd_list_processes) - -header('CORE') -CORE(cmd_list_core) - -header('VyOS Information') -VyOS(cmd_list_vyos) + +def print_header(command: str) -> None: + """Prints a command with headers '-'. + + Example: + + % print_header('Example command') + + --------------- + Example command + --------------- + """ + header_length = len(command) * '-' + print(f"\n{header_length}\n{command}\n{header_length}") + + +def execute_command(command: str, header_text: str) -> None: + """Executes a command and prints the output with a header. + + Example: + % execute_command('uptime', "Uptime of the system") + + -------------------- + Uptime of the system + -------------------- + 20:21:57 up 9:04, 5 users, load average: 0.00, 0.00, 0.0 + + """ + print_header(header_text) + try: + rc, output = rc_cmd(command) + print(output) + except Exception as e: + print(f"Error executing command: {command}") + print(f"Error message: {e}") + + +def op(cmd: str) -> str: + """Returns a command with the VyOS operational mode wrapper.""" + return f'/opt/vyatta/bin/vyatta-op-cmd-wrapper {cmd}' + + +def get_ethernet_interfaces() -> List[Interface]: + """Returns a list of Ethernet interfaces.""" + return Section.interfaces('ethernet') + + +def show_version() -> None: + """Prints the VyOS version and package changes.""" + execute_command(op('show version'), 'VyOS Version and Package Changes') + + +def show_config_file() -> None: + """Prints the contents of a configuration file with a header.""" + execute_command('cat /opt/vyatta/etc/config/config.boot', 'Configuration file') + + +def show_running_config() -> None: + """Prints the running configuration.""" + execute_command(op('show configuration'), 'Running configuration') + + +def show_package_repository_config() -> None: + """Prints the package repository configuration file.""" + execute_command('cat /etc/apt/sources.list', 'Package Repository Configuration File') + execute_command('ls -l /etc/apt/sources.list.d/', 'Repositories') + + +def show_user_startup_scripts() -> None: + """Prints the user startup scripts.""" + execute_command('cat /config/scripts/vyos-postconfig-bootup.script', 'User Startup Scripts') + + +def show_frr_config() -> None: + """Prints the FRR configuration.""" + execute_command('vtysh -c "show run"', 'FRR configuration') + + +def show_interfaces() -> None: + """Prints the interfaces.""" + execute_command(op('show interfaces'), 'Interfaces') + + +def show_interface_statistics() -> None: + """Prints the interface statistics.""" + execute_command('ip -s link show', 'Interface statistics') + + +def show_physical_interface_statistics() -> None: + """Prints the physical interface statistics.""" + execute_command('/usr/bin/true', 'Physical Interface statistics') + for iface in get_ethernet_interfaces(): + # Exclude vlans + if '.' in iface: + continue + execute_command(f'ethtool --driver {iface}', f'ethtool --driver {iface}') + execute_command(f'ethtool --statistics {iface}', f'ethtool --statistics {iface}') + execute_command(f'ethtool --show-ring {iface}', f'ethtool --show-ring {iface}') + execute_command(f'ethtool --show-coalesce {iface}', f'ethtool --show-coalesce {iface}') + execute_command(f'ethtool --pause {iface}', f'ethtool --pause {iface}') + execute_command(f'ethtool --show-features {iface}', f'ethtool --show-features {iface}') + execute_command(f'ethtool --phy-statistics {iface}', f'ethtool --phy-statistics {iface}') + execute_command('netstat --interfaces', 'netstat --interfaces') + execute_command('netstat --listening', 'netstat --listening') + execute_command('cat /proc/net/dev', 'cat /proc/net/dev') + + +def show_bridge() -> None: + """Show bridge interfaces.""" + execute_command(op('show bridge'), 'Show bridge') + + +def show_arp() -> None: + """Prints ARP entries.""" + execute_command(op('show arp'), 'ARP Table (Total entries)') + execute_command(op('show ipv6 neighbors'), 'show ipv6 neighbors') + + +def show_route() -> None: + """Prints routing information.""" + + cmd_list_route = [ + "show ip route bgp | head -108", + "show ip route cache", + "show ip route connected", + "show ip route forward", + "show ip route isis | head -108", + "show ip route kernel", + "show ip route ospf | head -108", + "show ip route rip", + "show ip route static", + "show ip route summary", + "show ip route supernets-only", + "show ip route table all", + "show ip route vrf all", + "show ipv6 route bgp | head 108", + "show ipv6 route cache", + "show ipv6 route connected", + "show ipv6 route forward", + "show ipv6 route isis", + "show ipv6 route kernel", + "show ipv6 route ospf", + "show ipv6 route rip", + "show ipv6 route static", + "show ipv6 route summary", + "show ipv6 route table all", + "show ipv6 route vrf all", + ] + for command in cmd_list_route: + execute_command(op(command), command) + + +def show_firewall() -> None: + """Prints firweall information.""" + execute_command('sudo nft list ruleset', 'nft list ruleset') + + +def show_system() -> None: + """Prints system parameters.""" + execute_command(op('show system image version'), 'Show System Image Version') + execute_command(op('show system image storage'), 'Show System Image Storage') + + +def show_date() -> None: + """Print the current date.""" + execute_command('date', 'Current Time') + + +def show_installed_packages() -> None: + """Prints installed packages.""" + execute_command('dpkg --list', 'Installed Packages') + + +def show_loaded_modules() -> None: + """Prints loaded modules /proc/modules""" + execute_command('cat /proc/modules', 'Loaded Modules') + + +def show_cpu_statistics() -> None: + """Prints CPU statistics.""" + execute_command('/usr/bin/true', 'CPU') + execute_command('lscpu', 'Installed CPU\'s') + execute_command('top --iterations 1 --batch-mode --accum-time-toggle', 'Cumulative CPU Time Used by Running Processes') + execute_command('cat /proc/loadavg', 'Load Average') + + +def show_system_interrupts() -> None: + """Prints system interrupts.""" + execute_command('cat /proc/interrupts', 'Hardware Interrupt Counters') + + +def show_soft_irqs() -> None: + """Prints soft IRQ's.""" + execute_command('cat /proc/softirqs', 'Soft IRQ\'s') + + +def show_softnet_statistics() -> None: + """Prints softnet statistics.""" + execute_command('cat /proc/net/softnet_stat', 'cat /proc/net/softnet_stat') + + +def show_running_processes() -> None: + """Prints current running processes""" + execute_command('ps -ef', 'Running Processes') + + +def show_memory_usage() -> None: + """Prints memory usage""" + execute_command('/usr/bin/true', 'Memory') + execute_command('cat /proc/meminfo', 'Installed Memory') + execute_command('free', 'Memory Usage') + + +def list_disks(): + disks = set() + with open('/proc/partitions') as partitions_file: + for line in partitions_file: + fields = line.strip().split() + if len(fields) == 4 and fields[3].isalpha() and fields[3] != 'name': + disks.add(fields[3]) + return disks + + +def show_storage() -> None: + """Prints storage information.""" + execute_command('cat /proc/devices', 'Devices') + execute_command('cat /proc/partitions', 'Partitions') + + for disk in list_disks(): + execute_command(f'fdisk --list /dev/{disk}', f'Partitioning for disk {disk}') + + +def main(): + # Configuration data + show_version() + show_config_file() + show_running_config() + show_package_repository_config() + show_user_startup_scripts() + show_frr_config() + + # Interfaces + show_interfaces() + show_interface_statistics() + show_physical_interface_statistics() + show_bridge() + show_arp() + + # Routing + show_route() + + # Firewall + show_firewall() + + # System + show_system() + show_date() + show_installed_packages() + show_loaded_modules() + + # CPU + show_cpu_statistics() + show_system_interrupts() + show_soft_irqs() + show_softnet_statistics() + + # Memory + show_memory_usage() + + # Storage + show_storage() + + # Processes + show_running_processes() + + # TODO: Get information from clouds + + +if __name__ == "__main__": + main() 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/system/vyos-config-cloud-init.py b/src/system/vyos-config-cloud-init.py new file mode 100755 index 000000000..0a6c1f9bc --- /dev/null +++ b/src/system/vyos-config-cloud-init.py @@ -0,0 +1,169 @@ +#!/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/>. + +import logging +from concurrent.futures import ProcessPoolExecutor +from pathlib import Path +from subprocess import run, TimeoutExpired +from sys import exit + +from psutil import net_if_addrs, AF_LINK +from systemd.journal import JournalHandler +from yaml import safe_load + +from vyos.template import render + +# define a path to the configuration file and template +config_file = '/etc/cloud/cloud.cfg.d/20_vyos_network.cfg' +template_file = 'system/cloud_init_networking.j2' + + +def check_interface_dhcp(iface_name: str) -> bool: + """Check DHCP client can work on an interface + + Args: + iface_name (str): interface name + + Returns: + bool: check result + """ + dhclient_command: list[str] = [ + 'dhclient', '-4', '-1', '-q', '--no-pid', '-sf', '/bin/true', iface_name + ] + check_result = False + # try to get an IP address + # we use dhclient behavior here to speedup detection + # if dhclient receives a configuration and configure an interface + # it switch to background + # If no - it will keep running in foreground + try: + run(['ip', 'l', 'set', iface_name, 'up']) + run(dhclient_command, timeout=5) + check_result = True + except TimeoutExpired: + pass + finally: + run(['ip', 'l', 'set', iface_name, 'down']) + + logger.info(f'DHCP server was found on {iface_name}: {check_result}') + return check_result + + +def dhclient_cleanup() -> None: + """Clean up after dhclients + """ + run(['killall', 'dhclient']) + leases_file: Path = Path('/var/lib/dhcp/dhclient.leases') + leases_file.unlink(missing_ok=True) + logger.debug('cleaned up after dhclients') + + +def dict_interfaces() -> dict[str, str]: + """Return list of available network interfaces except loopback + + Returns: + list[str]: a list of interfaces + """ + interfaces_dict: dict[str, str] = {} + ifaces = net_if_addrs() + for iface_name, iface_addresses in ifaces.items(): + # we do not need loopback interface + if iface_name == 'lo': + continue + # check other interfaces for MAC addresses + for iface_addr in iface_addresses: + if iface_addr.family == AF_LINK and iface_addr.address: + interfaces_dict[iface_name] = iface_addr.address + continue + + logger.debug(f'found interfaces: {interfaces_dict}') + return interfaces_dict + + +def need_to_check() -> bool: + """Check if we need to perform DHCP checks + + Returns: + bool: check result + """ + # if cloud-init config does not exist, we do not need to do anything + ci_config_vyos = Path('/etc/cloud/cloud.cfg.d/20_vyos_custom.cfg') + if not ci_config_vyos.exists(): + logger.info( + 'No need to check interfaces: Cloud-init config file was not found') + return False + + # load configuration file + try: + config = safe_load(ci_config_vyos.read_text()) + except: + logger.error('Cloud-init config file has a wrong format') + return False + + # check if we have in config configured option + # vyos_config_options: + # network_preconfigure: true + if not config.get('vyos_config_options', {}).get('network_preconfigure'): + logger.info( + 'No need to check interfaces: Cloud-init config option "network_preconfigure" is not set' + ) + return False + + return True + + +if __name__ == '__main__': + # prepare logger + logger = logging.getLogger(__name__) + logger.addHandler(JournalHandler(SYSLOG_IDENTIFIER=Path(__file__).name)) + logger.setLevel(logging.INFO) + + # we need to give udev some time to rename all interfaces + # this is placed before need_to_check() call, because we are not always + # need to preconfigure cloud-init, but udev always need to finish its work + # before cloud-init start + run(['udevadm', 'settle']) + logger.info('udev finished its work, we continue') + + # do not perform any checks if this is not required + if not need_to_check(): + exit() + + # get list of interfaces and check them + interfaces_dhcp: list[dict[str, str]] = [] + interfaces_dict: dict[str, str] = dict_interfaces() + + with ProcessPoolExecutor(max_workers=len(interfaces_dict)) as executor: + iface_check_results = [{ + 'dhcp': executor.submit(check_interface_dhcp, iface_name), + 'append': { + 'name': iface_name, + 'mac': iface_mac + } + } for iface_name, iface_mac in interfaces_dict.items()] + + dhclient_cleanup() + + for iface_check_result in iface_check_results: + if iface_check_result.get('dhcp').result(): + interfaces_dhcp.append(iface_check_result.get('append')) + + # render cloud-init config + if interfaces_dhcp: + logger.debug('rendering cloud-init network configuration') + render(config_file, template_file, {'ifaces_list': interfaces_dhcp}) + + exit() diff --git a/src/systemd/vyos-config-cloud-init.service b/src/systemd/vyos-config-cloud-init.service new file mode 100644 index 000000000..ba6f90e6d --- /dev/null +++ b/src/systemd/vyos-config-cloud-init.service @@ -0,0 +1,19 @@ +[Unit] +Description=Pre-configure Cloud-init +DefaultDependencies=no +Requires=systemd-remount-fs.service +Requires=systemd-udevd.service +Wants=network-pre.target +After=systemd-remount-fs.service +After=systemd-udevd.service +Before=cloud-init-local.service + +[Service] +Type=oneshot +ExecStart=/usr/libexec/vyos/system/vyos-config-cloud-init.py +TimeoutSec=120 +KillMode=process +StandardOutput=journal+console + +[Install] +WantedBy=cloud-init-local.service 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 diff --git a/src/validators/ddclient-protocol b/src/validators/ddclient-protocol new file mode 100755 index 000000000..6f927927b --- /dev/null +++ b/src/validators/ddclient-protocol @@ -0,0 +1,24 @@ +#!/bin/sh +# +# 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/>. + +ddclient -list-protocols | grep -qw $1 + +if [ $? -gt 0 ]; then + echo "Error: $1 is not a valid protocol, please choose from the supported list of protocols" + exit 1 +fi + +exit 0 |