summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--data/configd-include.json1
-rw-r--r--data/templates/accel-ppp/pppoe.config.tmpl18
-rw-r--r--data/templates/dns-forwarding/recursor.conf.tmpl3
-rw-r--r--data/templates/dns-forwarding/recursor.vyos-hostsd.conf.lua.tmpl6
-rw-r--r--data/templates/dns-forwarding/recursor.zone.conf.tmpl7
-rw-r--r--data/templates/frr/bfdd.frr.tmpl3
-rw-r--r--data/templates/frr/bgpd.frr.tmpl39
-rw-r--r--data/templates/frr/isisd.frr.tmpl3
-rw-r--r--data/templates/frr/ldpd.frr.tmpl99
-rw-r--r--data/templates/frr/ospf6d.frr.tmpl9
-rw-r--r--data/templates/frr/ospfd.frr.tmpl3
-rw-r--r--data/templates/https/nginx.default.tmpl4
-rw-r--r--data/templates/https/override.conf.tmpl15
-rw-r--r--data/templates/https/vyos-http-api.service.tmpl (renamed from src/systemd/vyos-http-api.service)3
-rw-r--r--data/templates/ipsec/swanctl.conf.tmpl2
-rw-r--r--data/templates/ipsec/swanctl/peer.tmpl5
-rw-r--r--data/templates/logs/logrotate/vyos-atop.tmpl20
-rw-r--r--data/templates/logs/logrotate/vyos-rsyslog.tmpl13
-rw-r--r--data/templates/netflow/uacctd.conf.tmpl114
-rw-r--r--data/templates/snmp/etc.snmp.conf.tmpl2
-rw-r--r--data/templates/snmp/etc.snmpd.conf.tmpl151
-rw-r--r--data/templates/snmp/override.conf.tmpl2
-rw-r--r--data/templates/snmp/usr.snmpd.conf.tmpl8
-rw-r--r--data/templates/snmp/var.snmpd.conf.tmpl20
-rw-r--r--data/templates/squid/squid.conf.tmpl2
-rw-r--r--data/templates/syslog/rsyslog.conf.tmpl12
-rw-r--r--data/templates/vrrp/keepalived.conf.tmpl18
-rw-r--r--debian/control1
-rw-r--r--interface-definitions/dns-forwarding.xml.in452
-rw-r--r--interface-definitions/firewall.xml.in20
-rw-r--r--interface-definitions/flow-accounting-conf.xml.in107
-rw-r--r--interface-definitions/https.xml.in20
-rw-r--r--interface-definitions/include/bfd.xml.i8
-rw-r--r--interface-definitions/include/bfd/bfd.xml.i10
-rw-r--r--interface-definitions/include/bfd/common.xml.i (renamed from interface-definitions/include/bfd-common.xml.i)2
-rw-r--r--interface-definitions/include/bfd/profile.xml.i14
-rw-r--r--interface-definitions/include/bgp/neighbor-afi-ipv4-ipv6-common.xml.i (renamed from interface-definitions/include/bgp/afi-common.xml.i)57
-rw-r--r--interface-definitions/include/bgp/neighbor-afi-ipv4-labeled-unicast.xml.i2
-rw-r--r--interface-definitions/include/bgp/neighbor-afi-ipv4-multicast.xml.i2
-rw-r--r--interface-definitions/include/bgp/neighbor-afi-ipv4-unicast.xml.i2
-rw-r--r--interface-definitions/include/bgp/neighbor-afi-ipv4-vpn.xml.i2
-rw-r--r--interface-definitions/include/bgp/neighbor-afi-ipv6-labeled-unicast.xml.i2
-rw-r--r--interface-definitions/include/bgp/neighbor-afi-ipv6-multicast.xml.i2
-rw-r--r--interface-definitions/include/bgp/neighbor-afi-ipv6-unicast.xml.i2
-rw-r--r--interface-definitions/include/bgp/neighbor-afi-ipv6-vpn.xml.i2
-rw-r--r--interface-definitions/include/bgp/neighbor-bfd.xml.i1
-rw-r--r--interface-definitions/include/bgp/neighbor-shutdown.xml.i2
-rw-r--r--interface-definitions/include/bgp/protocol-common-config.xml.i58
-rw-r--r--interface-definitions/include/dhcp/ntp-server.xml.i26
-rw-r--r--interface-definitions/include/dns/time-to-live.xml.i15
-rw-r--r--interface-definitions/include/interface/netns.xml.i14
-rw-r--r--interface-definitions/include/interface/vrf.xml.i2
-rw-r--r--interface-definitions/include/isis/protocol-common-config.xml.i2
-rw-r--r--interface-definitions/include/listen-address-ipv4.xml.i4
-rw-r--r--interface-definitions/include/listen-address-vrf.xml.i4
-rw-r--r--interface-definitions/include/listen-address.xml.i4
-rw-r--r--interface-definitions/include/ospf/auto-cost.xml.i22
-rw-r--r--interface-definitions/include/ospf/default-information.xml.i25
-rw-r--r--interface-definitions/include/ospf/interface-common.xml.i2
-rw-r--r--interface-definitions/include/ospf/protocol-common-config.xml.i45
-rw-r--r--interface-definitions/include/ospfv3/no-summary.xml.i8
-rw-r--r--interface-definitions/include/ospfv3/protocol-common-config.xml.i19
-rw-r--r--interface-definitions/include/snmp/access-mode.xml.i23
-rw-r--r--interface-definitions/include/snmp/authentication-type.xml.i22
-rw-r--r--interface-definitions/include/snmp/privacy-type.xml.i22
-rw-r--r--interface-definitions/include/snmp/protocol.xml.i22
-rw-r--r--interface-definitions/interfaces-dummy.xml.in1
-rw-r--r--interface-definitions/interfaces-tunnel.xml.in52
-rw-r--r--interface-definitions/interfaces-vxlan.xml.in12
-rw-r--r--interface-definitions/nat.xml.in1
-rw-r--r--interface-definitions/netns.xml.in23
-rw-r--r--interface-definitions/protocols-bfd.xml.in17
-rw-r--r--interface-definitions/service_pppoe-server.xml.in18
-rw-r--r--interface-definitions/snmp.xml.in236
-rw-r--r--interface-definitions/system-login-banner.xml.in4
-rw-r--r--interface-definitions/system-logs.xml.in92
-rw-r--r--interface-definitions/vpn_ipsec.xml.in124
-rw-r--r--op-mode-definitions/conntrack-sync.xml.in12
-rw-r--r--op-mode-definitions/include/bfd-common.xml.i54
-rw-r--r--op-mode-definitions/include/bgp/afi-common.xml.i1
-rw-r--r--op-mode-definitions/include/bgp/afi-ipv4-ipv6-common.xml.i1
-rw-r--r--op-mode-definitions/include/ospfv3/border-routers.xml.i20
-rw-r--r--op-mode-definitions/include/ospfv3/database.xml.i238
-rw-r--r--op-mode-definitions/include/ospfv3/interface.xml.i75
-rw-r--r--op-mode-definitions/include/ospfv3/linkstate.xml.i38
-rw-r--r--op-mode-definitions/include/ospfv3/neighbor.xml.i17
-rw-r--r--op-mode-definitions/include/ospfv3/redistribute.xml.i8
-rw-r--r--op-mode-definitions/include/ospfv3/route.xml.i79
-rw-r--r--op-mode-definitions/include/show-route-bgp.xml.i8
-rw-r--r--op-mode-definitions/include/show-route-connected.xml.i8
-rw-r--r--op-mode-definitions/include/show-route-isis.xml.i8
-rw-r--r--op-mode-definitions/include/show-route-kernel.xml.i8
-rw-r--r--op-mode-definitions/include/show-route-ospf.xml.i8
-rw-r--r--op-mode-definitions/include/show-route-ospfv3.xml.i8
-rw-r--r--op-mode-definitions/include/show-route-rip.xml.i8
-rw-r--r--op-mode-definitions/include/show-route-ripng.xml.i8
-rw-r--r--op-mode-definitions/include/show-route-static.xml.i8
-rw-r--r--op-mode-definitions/include/show-route-summary.xml.i8
-rw-r--r--op-mode-definitions/include/show-route-supernets-only.xml.i8
-rw-r--r--op-mode-definitions/include/show-route-table.xml.i17
-rw-r--r--op-mode-definitions/include/show-route-tag.xml.i16
-rw-r--r--op-mode-definitions/restart-frr.xml.in6
-rw-r--r--op-mode-definitions/show-bfd.xml.in50
-rw-r--r--op-mode-definitions/show-ip-route.xml.in109
-rw-r--r--op-mode-definitions/show-ipv6-ospfv3.xml.in469
-rw-r--r--op-mode-definitions/show-ipv6-route.xml.in97
-rw-r--r--op-mode-definitions/show-netns.xml.in13
-rw-r--r--op-mode-definitions/show-protocols.xml.in1
-rw-r--r--python/vyos/base.py9
-rw-r--r--python/vyos/configdict.py17
-rw-r--r--python/vyos/configquery.py46
-rw-r--r--python/vyos/configsource.py3
-rw-r--r--python/vyos/defaults.py7
-rw-r--r--python/vyos/hostsd_client.py12
-rwxr-xr-xpython/vyos/ifconfig/interface.py47
-rw-r--r--python/vyos/ifconfig/vxlan.py11
-rw-r--r--python/vyos/range_regex.py142
-rw-r--r--python/vyos/remote.py26
-rw-r--r--python/vyos/template.py24
-rw-r--r--python/vyos/util.py27
-rw-r--r--smoketest/configs/bgp-big-as-cloud6
-rw-r--r--smoketest/scripts/cli/base_interfaces_test.py39
-rwxr-xr-xsmoketest/scripts/cli/test_configd_init.py38
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_netns.py83
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_vxlan.py35
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_bfd.py64
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_bgp.py135
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_isis.py6
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_mpls.py117
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_ospf.py28
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_ospfv3.py77
-rwxr-xr-xsmoketest/scripts/cli/test_service_snmp.py102
-rwxr-xr-xsmoketest/scripts/cli/test_system_flow-accounting.py154
-rwxr-xr-xsmoketest/scripts/cli/test_system_logs.py117
-rwxr-xr-xsmoketest/scripts/cli/test_vpn_ipsec.py32
-rwxr-xr-xsrc/conf_mode/dns_forwarding.py207
-rwxr-xr-xsrc/conf_mode/flow_accounting_conf.py359
-rwxr-xr-xsrc/conf_mode/http-api.py54
-rwxr-xr-xsrc/conf_mode/https.py16
-rwxr-xr-xsrc/conf_mode/interfaces-vxlan.py31
-rwxr-xr-xsrc/conf_mode/interfaces-wwan.py49
-rwxr-xr-xsrc/conf_mode/netns.py118
-rwxr-xr-xsrc/conf_mode/protocols_bfd.py12
-rwxr-xr-xsrc/conf_mode/protocols_bgp.py22
-rwxr-xr-xsrc/conf_mode/protocols_mpls.py38
-rwxr-xr-xsrc/conf_mode/protocols_ospfv3.py36
-rwxr-xr-xsrc/conf_mode/service_pppoe-server.py15
-rwxr-xr-xsrc/conf_mode/snmp.py635
-rwxr-xr-xsrc/conf_mode/system-login-banner.py35
-rwxr-xr-xsrc/conf_mode/system-logs.py83
-rw-r--r--src/etc/dhcp/dhclient-enter-hooks.d/04-vyos-resolvconf82
-rw-r--r--src/etc/dhcp/dhclient-exit-hooks.d/01-vyos-cleanup31
-rw-r--r--src/etc/systemd/system/uacctd.service.d/override.conf14
-rwxr-xr-xsrc/helpers/vyos-boot-config-loader.py4
-rwxr-xr-xsrc/helpers/vyos_net_name10
-rwxr-xr-xsrc/migration-scripts/flow-accounting/0-to-169
-rwxr-xr-xsrc/op_mode/conntrack_sync.py45
-rwxr-xr-xsrc/op_mode/restart_frr.py2
-rwxr-xr-xsrc/op_mode/show_nat_rules.py22
-rw-r--r--src/services/api/graphql/README.graphql61
-rw-r--r--src/services/api/graphql/bindings.py18
-rw-r--r--src/services/api/graphql/graphql/directives.py16
-rw-r--r--src/services/api/graphql/graphql/mutations.py52
-rw-r--r--src/services/api/graphql/graphql/queries.py89
-rw-r--r--src/services/api/graphql/graphql/schema/firewall_group.graphql48
-rw-r--r--src/services/api/graphql/graphql/schema/schema.graphql14
-rw-r--r--src/services/api/graphql/recipes/remove_firewall_address_group_members.py14
-rw-r--r--src/services/api/graphql/recipes/session.py15
-rw-r--r--src/services/api/graphql/recipes/templates/create_firewall_address_ipv_6_group.tmpl4
-rw-r--r--src/services/api/graphql/recipes/templates/remove_firewall_address_ipv_6_group_members.tmpl3
-rw-r--r--src/services/api/graphql/recipes/templates/update_firewall_address_ipv_6_group_members.tmpl3
-rwxr-xr-xsrc/services/vyos-configd3
-rwxr-xr-xsrc/services/vyos-hostsd36
-rwxr-xr-xsrc/services/vyos-http-api-server32
-rw-r--r--src/systemd/vyos-hostsd.service2
-rwxr-xr-xsrc/validators/ipv4-multicast2
-rwxr-xr-xsrc/validators/ipv6-multicast2
-rwxr-xr-xsrc/validators/range56
-rwxr-xr-xsrc/validators/script4
179 files changed, 5166 insertions, 2321 deletions
diff --git a/data/configd-include.json b/data/configd-include.json
index 6893aaa86..ee4cb0d42 100644
--- a/data/configd-include.json
+++ b/data/configd-include.json
@@ -6,6 +6,7 @@
"dhcpv6_relay.py",
"dns_forwarding.py",
"dynamic_dns.py",
+"flow_accounting_conf.py",
"host_name.py",
"https.py",
"igmp_proxy.py",
diff --git a/data/templates/accel-ppp/pppoe.config.tmpl b/data/templates/accel-ppp/pppoe.config.tmpl
index 238e7ee15..0a8e0079b 100644
--- a/data/templates/accel-ppp/pppoe.config.tmpl
+++ b/data/templates/accel-ppp/pppoe.config.tmpl
@@ -108,19 +108,17 @@ ac-name={{ access_concentrator }}
{% if iface_config.vlan_id is not defined and iface_config.vlan_range is not defined %}
interface={{ iface }}
{% endif %}
-{% if iface_config.vlan_id is defined and iface_config.vlan_range is not defined %}
-{% for vlan in iface_config.vlan_id %}
-interface={{ iface }}.{{ vlan }}
-vlan-mon={{ iface }},{{ vlan }}
+{% if iface_config.vlan_range is defined %}
+{% for regex in iface_config.regex %}
+interface=re:^{{ iface | replace('.', '\\.') }}\.({{ regex }})$
{% endfor %}
-{% endif %}
-{% if iface_config.vlan_range is defined and iface_config.vlan_id is not defined %}
vlan-mon={{ iface }},{{ iface_config.vlan_range | join(',') }}
-interface=re:{{ iface | replace('.', '\\.') }}\.\d+
{% endif %}
-{% if iface_config.vlan_id is defined and iface_config.vlan_range is defined %}
-vlan-mon={{ iface }},{{ iface_config.vlan_id | join(',') }},{{ iface_config.vlan_range | join(',') }}
-interface=re:{{ iface | replace('.', '\\.') }}\.\d+
+{% if iface_config.vlan_id is defined %}
+{% for vlan in iface_config.vlan_id %}
+vlan-mon={{ iface }},{{ vlan }}
+interface=re:^{{ iface | replace('.', '\\.') }}\.{{ vlan }}$
+{% endfor %}
{% endif %}
{% endfor %}
{% endif %}
diff --git a/data/templates/dns-forwarding/recursor.conf.tmpl b/data/templates/dns-forwarding/recursor.conf.tmpl
index d44f756e8..02efe903b 100644
--- a/data/templates/dns-forwarding/recursor.conf.tmpl
+++ b/data/templates/dns-forwarding/recursor.conf.tmpl
@@ -31,5 +31,8 @@ dnssec={{ dnssec }}
# serve rfc1918 records
serve-rfc1918={{ 'no' if no_serve_rfc1918 is defined else 'yes' }}
+# zones
+auth-zones={% for z in authoritative_zones %}{{ z.name }}={{ z.file }}{{- "," if not loop.last -}}{% endfor %}
+
forward-zones-file=recursor.forward-zones.conf
diff --git a/data/templates/dns-forwarding/recursor.vyos-hostsd.conf.lua.tmpl b/data/templates/dns-forwarding/recursor.vyos-hostsd.conf.lua.tmpl
index 784d5c360..7f29c387e 100644
--- a/data/templates/dns-forwarding/recursor.vyos-hostsd.conf.lua.tmpl
+++ b/data/templates/dns-forwarding/recursor.vyos-hostsd.conf.lua.tmpl
@@ -22,3 +22,9 @@ addNTA("{{ zone }}", "static")
{% endfor %}
{% endif %}
+{% if authoritative_zones is defined %}
+-- from 'service dns forwarding authoritative-domain'
+{% for zone in authoritative_zones %}
+addNTA("{{ zone }}", "static")
+{% endfor %}
+{% endif %}
diff --git a/data/templates/dns-forwarding/recursor.zone.conf.tmpl b/data/templates/dns-forwarding/recursor.zone.conf.tmpl
new file mode 100644
index 000000000..758871bef
--- /dev/null
+++ b/data/templates/dns-forwarding/recursor.zone.conf.tmpl
@@ -0,0 +1,7 @@
+;
+; Autogenerated by dns_forwarding.py
+;
+;
+{% for r in records %}
+{{ r.name }} {{ r.ttl }} {{ r.type }} {{ r.value }}
+{% endfor %}
diff --git a/data/templates/frr/bfdd.frr.tmpl b/data/templates/frr/bfdd.frr.tmpl
index e0e94c24d..439f79d67 100644
--- a/data/templates/frr/bfdd.frr.tmpl
+++ b/data/templates/frr/bfdd.frr.tmpl
@@ -41,6 +41,9 @@ bfd
{% if peer_config.passive is defined %}
passive-mode
{% endif %}
+{% if peer_config.profile is defined and peer_config.profile is not none %}
+ profile {{ peer_config.profile }}
+{% endif %}
{% if peer_config.shutdown is defined %}
shutdown
{% else %}
diff --git a/data/templates/frr/bgpd.frr.tmpl b/data/templates/frr/bgpd.frr.tmpl
index fbdbafd6e..45e0544b7 100644
--- a/data/templates/frr/bgpd.frr.tmpl
+++ b/data/templates/frr/bgpd.frr.tmpl
@@ -17,6 +17,12 @@
{% endif %}
{% if config.bfd is defined %}
neighbor {{ neighbor }} bfd
+{% if config.bfd.check_control_plane_failure is defined %}
+ neighbor {{ neighbor }} bfd check-control-plane-failure
+{% endif %}
+{% if config.bfd.profile is defined and config.bfd.profile is not none %}
+ neighbor {{ neighbor }} bfd profile {{ config.bfd.profile }}
+{% endif %}
{% endif %}
{% if config.capability is defined and config.capability is not none %}
{% if config.capability.dynamic is defined %}
@@ -140,6 +146,17 @@
{% if afi_config.as_override is defined %}
neighbor {{ neighbor }} as-override
{% endif %}
+{% if afi_config.conditionally_advertise is defined and afi_config.conditionally_advertise is not none %}
+{% if afi_config.conditionally_advertise.advertise_map is defined and afi_config.conditionally_advertise.advertise_map is not none %}
+{% set exist_non_exist_map = 'exist-map' %}
+{% if afi_config.conditionally_advertise.exist_map is defined and afi_config.conditionally_advertise.exist_map is not none %}
+{% set exist_non_exist_map = 'exist-map ' ~ afi_config.conditionally_advertise.exist_map %}
+{% elif afi_config.conditionally_advertise.non_exist_map is defined and afi_config.conditionally_advertise.non_exist_map is not none %}
+{% set exist_non_exist_map = 'non-exist-map ' ~ afi_config.conditionally_advertise.non_exist_map %}
+{% endif %}
+ neighbor {{ neighbor }} advertise-map {{ afi_config.conditionally_advertise.advertise_map }} {{ exist_non_exist_map }}
+{% endif %}
+{% endif %}
{% if afi_config.remove_private_as is defined %}
neighbor {{ neighbor }} remove-private-AS
{% endif %}
@@ -469,6 +486,11 @@ router bgp {{ local_as }} {{ 'vrf ' ~ vrf if vrf is defined and vrf is not none
{% if parameters.cluster_id is defined and parameters.cluster_id is not none %}
bgp cluster-id {{ parameters.cluster_id }}
{% endif %}
+{% if parameters.conditional_advertisement is defined and parameters.conditional_advertisement is not none %}
+{% if parameters.conditional_advertisement.timer is defined and parameters.conditional_advertisement.timer is not none %}
+ bgp conditional-advertisement timer {{ parameters.conditional_advertisement.timer }}
+{% endif %}
+{% endif %}
{% if parameters.confederation is defined and parameters.confederation is not none %}
{% if parameters.confederation.identifier is defined and parameters.confederation.identifier is not none %}
bgp confederation identifier {{ parameters.confederation.identifier }}
@@ -499,6 +521,9 @@ router bgp {{ local_as }} {{ 'vrf ' ~ vrf if vrf is defined and vrf is not none
{% endfor %}
{% endif %}
{% endif %}
+{% if parameters.fast_convergence is defined %}
+ bgp fast-convergence
+{% endif %}
{% if parameters.graceful_restart is defined %}
bgp graceful-restart {{ 'stalepath-time ' ~ parameters.graceful_restart.stalepath_time if parameters.graceful_restart.stalepath_time is defined }}
{% endif %}
@@ -508,6 +533,9 @@ router bgp {{ local_as }} {{ 'vrf ' ~ vrf if vrf is defined and vrf is not none
{% if parameters.log_neighbor_changes is defined %}
bgp log-neighbor-changes
{% endif %}
+{% if parameters.minimum_holdtime is defined and parameters.minimum_holdtime is not none %}
+ bgp minimum-holdtime {{ parameters.minimum_holdtime }}
+{% endif %}
{% if parameters.network_import_check is defined %}
bgp network import-check
{% endif %}
@@ -517,11 +545,20 @@ router bgp {{ local_as }} {{ 'vrf ' ~ vrf if vrf is defined and vrf is not none
{% if parameters.no_fast_external_failover is defined %}
no bgp fast-external-failover
{% endif %}
+{% if parameters.reject_as_sets is defined %}
+ bgp reject-as-sets
+{% endif %}
{% if parameters.router_id is defined and parameters.router_id is not none %}
bgp router-id {{ parameters.router_id }}
{% endif %}
+{% if parameters.shutdown is defined %}
+ bgp shutdown
+{% endif %}
+{% if parameters.suppress_fib_pending is defined %}
+ bgp suppress-fib-pending
+{% endif %}
{% endif %}
{% if timers is defined and timers.keepalive is defined and timers.holdtime is defined %}
timers bgp {{ timers.keepalive }} {{ timers.holdtime }}
{% endif %}
-exit \ No newline at end of file
+exit
diff --git a/data/templates/frr/isisd.frr.tmpl b/data/templates/frr/isisd.frr.tmpl
index fc0799e02..b1e3f825b 100644
--- a/data/templates/frr/isisd.frr.tmpl
+++ b/data/templates/frr/isisd.frr.tmpl
@@ -6,6 +6,9 @@ interface {{ iface }} {{ 'vrf ' + vrf if vrf is defined and vrf is not none }}
ipv6 router isis VyOS
{% if iface_config.bfd is defined %}
isis bfd
+{% if iface_config.bfd.profile is defined and iface_config.bfd.profile is not none %}
+ isis bfd profile {{ iface_config.bfd.profile }}
+{% endif %}
{% endif %}
{% if iface_config.network is defined and iface_config.network.point_to_point is defined %}
isis network point-to-point
diff --git a/data/templates/frr/ldpd.frr.tmpl b/data/templates/frr/ldpd.frr.tmpl
index 0a5411552..537ea4025 100644
--- a/data/templates/frr/ldpd.frr.tmpl
+++ b/data/templates/frr/ldpd.frr.tmpl
@@ -2,69 +2,69 @@
{% if ldp is defined %}
mpls ldp
{% if ldp.router_id is defined %}
-router-id {{ ldp.router_id }}
+ router-id {{ ldp.router_id }}
{% endif %}
{% if ldp.parameters is defined %}
{% if ldp.parameters.cisco_interop_tlv is defined %}
-dual-stack cisco-interop
+ dual-stack cisco-interop
{% endif %}
{% if ldp.parameters.transport_prefer_ipv4 is defined%}
-dual-stack transport-connection prefer ipv4
+ dual-stack transport-connection prefer ipv4
{% endif %}
{% if ldp.parameters.ordered_control is defined%}
-ordered-control
+ ordered-control
{% endif %}
{% endif %}
{% if ldp.neighbor is defined %}
{% for neighbors in ldp.neighbor %}
{% if ldp.neighbor[neighbors].password is defined %}
-neighbor {{neighbors}} password {{ldp.neighbor[neighbors].password}}
+ neighbor {{ neighbors }} password {{ ldp.neighbor[neighbors].password }}
{% endif %}
{% if ldp.neighbor[neighbors].ttl_security is defined %}
{% if 'disable' in ldp.neighbor[neighbors].ttl_security %}
-neighbor {{neighbors}} ttl-security disable
+ neighbor {{ neighbors }} ttl-security disable
{% else %}
-neighbor {{neighbors}} ttl-security hops {{ldp.neighbor[neighbors].ttl_security}}
+ neighbor {{ neighbors }} ttl-security hops {{ ldp.neighbor[neighbors].ttl_security }}
{% endif %}
{% endif %}
{% if ldp.neighbor[neighbors].session_holdtime is defined %}
-neighbor {{neighbors}} session holdtime {{ldp.neighbor[neighbors].session_holdtime}}
+ neighbor {{ neighbors }} session holdtime {{ ldp.neighbor[neighbors].session_holdtime }}
{% endif %}
{% endfor %}
{% endif %}
-!
+ !
{% if ldp.discovery is defined %}
{% if ldp.discovery.transport_ipv4_address is defined %}
-address-family ipv4
+ address-family ipv4
{% if ldp.allocation is defined %}
{% if ldp.allocation.ipv4 is defined %}
{% if ldp.allocation.ipv4.access_list is defined %}
-label local allocate for {{ ldp.allocation.ipv4.access_list }}
+ label local allocate for {{ ldp.allocation.ipv4.access_list }}
{% endif %}
{% endif %}
{% else %}
-label local allocate host-routes
+ label local allocate host-routes
{% endif %}
{% if ldp.discovery.transport_ipv4_address is defined %}
-discovery transport-address {{ ldp.discovery.transport_ipv4_address }}
+ discovery transport-address {{ ldp.discovery.transport_ipv4_address }}
{% endif %}
{% if ldp.discovery.hello_ipv4_holdtime is defined %}
-discovery hello holdtime {{ ldp.discovery.hello_ipv4_holdtime }}
+ discovery hello holdtime {{ ldp.discovery.hello_ipv4_holdtime }}
{% endif %}
{% if ldp.discovery.hello_ipv4_interval is defined %}
-discovery hello interval {{ ldp.discovery.hello_ipv4_interval }}
+ discovery hello interval {{ ldp.discovery.hello_ipv4_interval }}
{% endif %}
{% if ldp.discovery.session_ipv4_holdtime is defined %}
-session holdtime {{ ldp.discovery.session_ipv4_holdtime }}
+ session holdtime {{ ldp.discovery.session_ipv4_holdtime }}
{% endif %}
{% if ldp.import is defined %}
{% if ldp.import.ipv4 is defined %}
{% if ldp.import.ipv4.import_filter is defined %}
{% if ldp.import.ipv4.import_filter.filter_access_list is defined %}
{% if ldp.import.ipv4.import_filter.neighbor_access_list is defined %}
-label remote accept for {{ ldp.import.ipv4.import_filter.filter_access_list }} from {{ ldp.import.ipv4.import_filter.neighbor_access_list }}
+ label remote accept for {{ ldp.import.ipv4.import_filter.filter_access_list }} from {{ ldp.import.ipv4.import_filter.neighbor_access_list }}
{% else %}
-label remote accept for {{ ldp.import.ipv4.import_filter.filter_access_list }}
+ label remote accept for {{ ldp.import.ipv4.import_filter.filter_access_list }}
{% endif %}
{% endif %}
{% endif %}
@@ -73,14 +73,14 @@ label remote accept for {{ ldp.import.ipv4.import_filter.filter_access_list }}
{% if ldp.export is defined %}
{% if ldp.export.ipv4 is defined %}
{% if ldp.export.ipv4.explicit_null is defined %}
-label local advertise explicit-null
+ label local advertise explicit-null
{% endif %}
{% if ldp.export.ipv4.export_filter is defined %}
{% if ldp.export.ipv4.export_filter.filter_access_list is defined %}
{% if ldp.export.ipv4.export_filter.neighbor_access_list is defined %}
-label local advertise for {{ ldp.export.ipv4.export_filter.filter_access_list }} to {{ ldp.export.ipv4.export_filter.neighbor_access_list }}
+ label local advertise for {{ ldp.export.ipv4.export_filter.filter_access_list }} to {{ ldp.export.ipv4.export_filter.neighbor_access_list }}
{% else %}
-label local advertise for {{ ldp.export.ipv4.export_filter.filter_access_list }}
+ label local advertise for {{ ldp.export.ipv4.export_filter.filter_access_list }}
{% endif %}
{% endif %}
{% endif %}
@@ -88,59 +88,59 @@ label local advertise for {{ ldp.export.ipv4.export_filter.filter_access_list }}
{% endif %}
{% if ldp.targeted_neighbor is defined %}
{% if ldp.targeted_neighbor.ipv4.enable is defined %}
-discovery targeted-hello accept
+ discovery targeted-hello accept
{% endif %}
{% if ldp.targeted_neighbor.ipv4.hello_holdtime is defined %}
-discovery targeted-hello holdtime {{ ldp.targeted_neighbor.ipv4.hello_holdtime }}
+ discovery targeted-hello holdtime {{ ldp.targeted_neighbor.ipv4.hello_holdtime }}
{% endif %}
{% if ldp.targeted_neighbor.ipv4.hello_interval is defined %}
-discovery targeted-hello interval {{ ldp.targeted_neighbor.ipv4.hello_interval }}
+ discovery targeted-hello interval {{ ldp.targeted_neighbor.ipv4.hello_interval }}
{% endif %}
{% for addresses in ldp.targeted_neighbor.ipv4.address %}
-neighbor {{addresses}} targeted
+ neighbor {{addresses}} targeted
{% endfor %}
{% endif %}
{% for interfaces in ldp.interface %}
-interface {{interfaces}}
+ interface {{interfaces}}
{% endfor %}
-exit-address-family
+ exit-address-family
{% else %}
-no address-family ipv4
+ no address-family ipv4
{% endif %}
{% endif %}
-!
+ !
{% if ldp.discovery is defined %}
{% if ldp.discovery.transport_ipv6_address is defined %}
-address-family ipv6
+ address-family ipv6
{% if ldp.allocation is defined %}
{% if ldp.allocation.ipv6 is defined %}
{% if ldp.allocation.ipv6.access_list6 is defined %}
-label local allocate for {{ ldp.allocation.ipv6.access_list6 }}
+ label local allocate for {{ ldp.allocation.ipv6.access_list6 }}
{% endif %}
{% endif %}
{% else %}
-label local allocate host-routes
+ label local allocate host-routes
{% endif %}
{% if ldp.discovery.transport_ipv6_address is defined %}
-discovery transport-address {{ ldp.discovery.transport_ipv6_address }}
+ discovery transport-address {{ ldp.discovery.transport_ipv6_address }}
{% endif %}
{% if ldp.discovery.hello_ipv6_holdtime is defined %}
-discovery hello holdtime {{ ldp.discovery.hello_ipv6_holdtime }}
+ discovery hello holdtime {{ ldp.discovery.hello_ipv6_holdtime }}
{% endif %}
{% if ldp.discovery.hello_ipv6_interval is defined %}
-discovery hello interval {{ ldp.discovery.hello_ipv6_interval }}
+ discovery hello interval {{ ldp.discovery.hello_ipv6_interval }}
{% endif %}
{% if ldp.discovery.session_ipv6_holdtime is defined %}
-session holdtime {{ ldp.discovery.session_ipv6_holdtime }}
+ session holdtime {{ ldp.discovery.session_ipv6_holdtime }}
{% endif %}
{% if ldp.import is defined %}
{% if ldp.import.ipv6 is defined %}
{% if ldp.import.ipv6.import_filter is defined %}
{% if ldp.import.ipv6.import_filter.filter_access_list6 is defined %}
{% if ldp.import.ipv6.import_filter.neighbor_access_list6 is defined %}
-label remote accept for {{ ldp.import.ipv6.import_filter.filter_access_list6 }} from {{ ldp.import.ipv6.import_filter.neighbor_access_list6 }}
+ label remote accept for {{ ldp.import.ipv6.import_filter.filter_access_list6 }} from {{ ldp.import.ipv6.import_filter.neighbor_access_list6 }}
{% else %}
-label remote accept for {{ ldp.import.ipv6.import_filter.filter_access_list6 }}
+ label remote accept for {{ ldp.import.ipv6.import_filter.filter_access_list6 }}
{% endif %}
{% endif %}
{% endif %}
@@ -149,14 +149,14 @@ label remote accept for {{ ldp.import.ipv6.import_filter.filter_access_list6 }}
{% if ldp.export is defined %}
{% if ldp.export.ipv6 is defined %}
{% if ldp.export.ipv6.explicit_null is defined %}
-label local advertise explicit-null
+ label local advertise explicit-null
{% endif %}
{% if ldp.export.ipv6.export_filter is defined %}
{% if ldp.export.ipv6.export_filter.filter_access_list6 is defined %}
{% if ldp.export.ipv6.export_filter.neighbor_access_list6 is defined %}
-label local advertise for {{ ldp.export.ipv6.export_filter.filter_access_list6 }} to {{ ldp.export.ipv6.export_filter.neighbor_access_list6 }}
+ label local advertise for {{ ldp.export.ipv6.export_filter.filter_access_list6 }} to {{ ldp.export.ipv6.export_filter.neighbor_access_list6 }}
{% else %}
-label local advertise for {{ ldp.export.ipv6.export_filter.filter_access_list6 }}
+ label local advertise for {{ ldp.export.ipv6.export_filter.filter_access_list6 }}
{% endif %}
{% endif %}
{% endif %}
@@ -164,24 +164,27 @@ label local advertise for {{ ldp.export.ipv6.export_filter.filter_access_list6 }
{% endif %}
{% if ldp.targeted_neighbor is defined %}
{% if ldp.targeted_neighbor.ipv6.enable is defined %}
-discovery targeted-hello accept
+ discovery targeted-hello accept
{% endif %}
{% if ldp.targeted_neighbor.ipv6.hello_holdtime is defined %}
-discovery targeted-hello holdtime {{ ldp.targeted_neighbor.ipv6.hello_holdtime }}
+ discovery targeted-hello holdtime {{ ldp.targeted_neighbor.ipv6.hello_holdtime }}
{% endif %}
{% if ldp.targeted_neighbor.ipv6.hello_interval is defined %}
-discovery targeted-hello interval {{ ldp.targeted_neighbor.ipv6.hello_interval }}
+ discovery targeted-hello interval {{ ldp.targeted_neighbor.ipv6.hello_interval }}
{% endif %}
{% for addresses in ldp.targeted_neighbor.ipv6.address %}
-neighbor {{addresses}} targeted
+ neighbor {{addresses}} targeted
{% endfor %}
{% endif %}
{% for interfaces in ldp.interface %}
-interface {{interfaces}}
+ interface {{interfaces}}
{% endfor %}
-exit-address-family
+ exit-address-family
{% else %}
-no address-family ipv6
+ no address-family ipv6
{% endif %}
+ !
{% endif %}
+exit
{% endif %}
+!
diff --git a/data/templates/frr/ospf6d.frr.tmpl b/data/templates/frr/ospf6d.frr.tmpl
index 10a6d9b4b..8279e5abb 100644
--- a/data/templates/frr/ospf6d.frr.tmpl
+++ b/data/templates/frr/ospf6d.frr.tmpl
@@ -25,6 +25,9 @@ interface {{ iface }} {{ 'vrf ' + vrf if vrf is defined and vrf is not none }}
{% endif %}
{% if iface_config.bfd is defined %}
ipv6 ospf6 bfd
+{% if iface_config.bfd.profile is defined and iface_config.bfd.profile is not none %}
+ ipv6 ospf6 bfd profile {{ iface_config.bfd.profile }}
+{% endif %}
{% endif %}
{% if iface_config.mtu_ignore is defined %}
ipv6 ospf6 mtu-ignore
@@ -51,7 +54,7 @@ router ospf6 {{ 'vrf ' + vrf if vrf is defined and vrf is not none }}
{% for area_id, area_config in area.items() %}
{% if area_config.area_type is defined and area_config.area_type is not none %}
{% for type, type_config in area_config.area_type.items() %}
- area {{ area_id }} {{ type }} {{ 'no-summary' if type_config.no_summary is defined }}
+ area {{ area_id }} {{ type }} {{ 'default-information-originate' if type_config.default_information_originate is defined }} {{ 'no-summary' if type_config.no_summary is defined }}
{% endfor %}
{% endif %}
{% if area_config.range is defined and area_config.range is not none %}
@@ -67,6 +70,10 @@ router ospf6 {{ 'vrf ' + vrf if vrf is defined and vrf is not none }}
{% endif %}
{% endfor %}
{% endif %}
+ auto-cost reference-bandwidth {{ auto_cost.reference_bandwidth }}
+{% if default_information is defined and default_information.originate is defined and default_information.originate is not none %}
+ default-information originate {{ 'always' if default_information.originate.always is defined }} {{ 'metric ' + default_information.originate.metric if default_information.originate.metric is defined }} {{ 'metric-type ' + default_information.originate.metric_type if default_information.originate.metric_type is defined }} {{ 'route-map ' + default_information.originate.route_map if default_information.originate.route_map is defined }}
+{% endif %}
{% if distance is defined and distance is not none %}
{% if distance.global is defined and distance.global is not none %}
distance {{ distance.global }}
diff --git a/data/templates/frr/ospfd.frr.tmpl b/data/templates/frr/ospfd.frr.tmpl
index a7b770f07..af66baf53 100644
--- a/data/templates/frr/ospfd.frr.tmpl
+++ b/data/templates/frr/ospfd.frr.tmpl
@@ -42,6 +42,9 @@ interface {{ iface }} {{ 'vrf ' + vrf if vrf is defined and vrf is not none }}
{% endif %}
{% if iface_config.bfd is defined %}
ip ospf bfd
+{% if iface_config.bfd.profile is defined and iface_config.bfd.profile is not none %}
+ ip ospf bfd profile {{ iface_config.bfd.profile }}
+{% endif %}
{% endif %}
{% if iface_config.mtu_ignore is defined %}
ip ospf mtu-ignore
diff --git a/data/templates/https/nginx.default.tmpl b/data/templates/https/nginx.default.tmpl
index 9d73baeee..ac9203e83 100644
--- a/data/templates/https/nginx.default.tmpl
+++ b/data/templates/https/nginx.default.tmpl
@@ -44,7 +44,11 @@ server {
# proxy settings for HTTP API, if enabled; 503, if not
location ~ /(retrieve|configure|config-file|image|generate|show|docs|openapi.json|redoc|graphql) {
{% if server.api %}
+{% if server.api.socket %}
+ proxy_pass http://unix:/run/api.sock;
+{% else %}
proxy_pass http://localhost:{{ server.api.port }};
+{% endif %}
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 600;
diff --git a/data/templates/https/override.conf.tmpl b/data/templates/https/override.conf.tmpl
new file mode 100644
index 000000000..824b1ba3b
--- /dev/null
+++ b/data/templates/https/override.conf.tmpl
@@ -0,0 +1,15 @@
+{% set vrf_command = 'ip vrf exec ' + vrf + ' ' if vrf is defined else '' %}
+[Unit]
+StartLimitIntervalSec=0
+After=vyos-router.service
+
+[Service]
+ExecStartPre=
+ExecStartPre={{vrf_command}}/usr/sbin/nginx -t -q -g 'daemon on; master_process on;'
+ExecStart=
+ExecStart={{vrf_command}}/usr/sbin/nginx -g 'daemon on; master_process on;'
+ExecReload=
+ExecReload={{vrf_command}}/usr/sbin/nginx -g 'daemon on; master_process on;' -s reload
+Restart=always
+RestartPreventExitStatus=
+RestartSec=10
diff --git a/src/systemd/vyos-http-api.service b/data/templates/https/vyos-http-api.service.tmpl
index 55370b356..15bd80d65 100644
--- a/src/systemd/vyos-http-api.service
+++ b/data/templates/https/vyos-http-api.service.tmpl
@@ -1,10 +1,11 @@
+{% set vrf_command = 'ip vrf exec ' + vrf + ' ' if vrf is defined else '' %}
[Unit]
Description=VyOS HTTP API service
After=vyos-router.service
Requires=vyos-router.service
[Service]
-ExecStart=/usr/libexec/vyos/services/vyos-http-api-server
+ExecStart={{vrf_command}}/usr/libexec/vyos/services/vyos-http-api-server
Type=idle
SyslogIdentifier=vyos-http-api
diff --git a/data/templates/ipsec/swanctl.conf.tmpl b/data/templates/ipsec/swanctl.conf.tmpl
index 161f19f95..68b108365 100644
--- a/data/templates/ipsec/swanctl.conf.tmpl
+++ b/data/templates/ipsec/swanctl.conf.tmpl
@@ -57,7 +57,7 @@ secrets {
{% endif %}
{% if site_to_site is defined and site_to_site.peer is defined %}
{% for peer, peer_conf in site_to_site.peer.items() if peer not in dhcp_no_address and peer_conf.disable is not defined %}
-{% set peer_name = peer.replace(".", "-").replace("@", "") %}
+{% set peer_name = peer.replace("@", "") | dot_colon_to_dash %}
{% if peer_conf.authentication.mode == 'pre-shared-secret' %}
ike_{{ peer_name }} {
{% if peer_conf.local_address is defined %}
diff --git a/data/templates/ipsec/swanctl/peer.tmpl b/data/templates/ipsec/swanctl/peer.tmpl
index 8c3776bf1..c6b71f2a1 100644
--- a/data/templates/ipsec/swanctl/peer.tmpl
+++ b/data/templates/ipsec/swanctl/peer.tmpl
@@ -1,5 +1,5 @@
{% macro conn(peer, peer_conf, ike_group, esp_group) %}
-{% set name = peer.replace(".", "-").replace("@", "") %}
+{% set name = peer.replace("@", "") | dot_colon_to_dash %}
{# peer needs to reference the global IKE configuration for certain values #}
{% set ike = ike_group[peer_conf.ike_group] %}
peer_{{ name }} {
@@ -101,6 +101,9 @@
{% set remote_prefix = tunnel_conf.remote.prefix if 'any' not in tunnel_conf.remote.prefix else ['0.0.0.0/0', '::/0'] %}
remote_ts = {{ remote_prefix | join(remote_suffix + ",") }}{{ remote_suffix }}
{% endif %}
+{% if tunnel_conf.priority is defined and tunnel_conf.priority is not none %}
+ priority = {{ tunnel_conf.priority }}
+{% endif %}
{% elif tunnel_esp.mode == 'transport' %}
local_ts = {{ peer_conf.local_address }}{{ local_suffix }}
remote_ts = {{ peer }}{{ remote_suffix }}
diff --git a/data/templates/logs/logrotate/vyos-atop.tmpl b/data/templates/logs/logrotate/vyos-atop.tmpl
new file mode 100644
index 000000000..2d078f379
--- /dev/null
+++ b/data/templates/logs/logrotate/vyos-atop.tmpl
@@ -0,0 +1,20 @@
+/var/log/atop/atop.log {
+ daily
+ dateext
+ dateformat _%Y-%m-%d_%H-%M-%S
+ maxsize {{ max_size }}M
+ missingok
+ nocompress
+ nocreate
+ nomail
+ rotate {{ rotate }}
+ prerotate
+ # stop the service
+ systemctl stop atop.service
+ endscript
+ postrotate
+ # start atop service again
+ systemctl start atop.service
+ endscript
+}
+
diff --git a/data/templates/logs/logrotate/vyos-rsyslog.tmpl b/data/templates/logs/logrotate/vyos-rsyslog.tmpl
new file mode 100644
index 000000000..f2e4d2ab2
--- /dev/null
+++ b/data/templates/logs/logrotate/vyos-rsyslog.tmpl
@@ -0,0 +1,13 @@
+/var/log/messages {
+ create
+ missingok
+ nomail
+ notifempty
+ rotate {{ rotate }}
+ size {{ max_size }}M
+ postrotate
+ # inform rsyslog service about rotation
+ /usr/lib/rsyslog/rsyslog-rotate
+ endscript
+}
+
diff --git a/data/templates/netflow/uacctd.conf.tmpl b/data/templates/netflow/uacctd.conf.tmpl
index 1c183bb20..f81002dc1 100644
--- a/data/templates/netflow/uacctd.conf.tmpl
+++ b/data/templates/netflow/uacctd.conf.tmpl
@@ -1,72 +1,74 @@
# Genereated from VyOS configuration
daemonize: true
promisc: false
-pidfile: /var/run/uacctd.pid
+pidfile: /run/pmacct/uacctd.pid
uacctd_group: 2
uacctd_nl_size: 2097152
-snaplen: {{ snaplen }}
-{% if templatecfg['enable-egress'] != none %}
-aggregate: in_iface,out_iface,src_mac,dst_mac,vlan,src_host,dst_host,src_port,dst_port,proto,tos,flows
-{% else %}
-aggregate: in_iface,src_mac,dst_mac,vlan,src_host,dst_host,src_port,dst_port,proto,tos,flows
+snaplen: {{ packet_length }}
+aggregate: in_iface{{ ',out_iface' if enable_egress is defined }},src_mac,dst_mac,vlan,src_host,dst_host,src_port,dst_port,proto,tos,flows
+{% set pipe_size = buffer_size | int *1024 *1024 %}
+plugin_pipe_size: {{ pipe_size }}
+{# We need an integer division (//) without any remainder or fraction #}
+plugin_buffer_size: {{ pipe_size // 1000 }}
+{% if syslog_facility is defined and syslog_facility is not none %}
+syslog: {{ syslog_facility }}
{% endif %}
-plugin_pipe_size: {{ templatecfg['plugin_pipe_size'] }}
-plugin_buffer_size: {{ templatecfg['plugin_buffer_size'] }}
-{% if templatecfg['syslog-facility'] != none %}
-syslog: {{ templatecfg['syslog-facility'] }}
-{% endif %}
-{% if templatecfg['disable-imt'] == none %}
+{% if disable_imt is not defined %}
imt_path: /tmp/uacctd.pipe
imt_mem_pools_number: 169
{% endif %}
-plugins: {% if templatecfg['netflow']['servers'] != none %}
-{% for server in templatecfg['netflow']['servers'] %}
-{% if loop.last %}nfprobe[nf_{{ server['address'] }}]{% else %}nfprobe[nf_{{ server['address'] }}],{% endif %}
-{% endfor %}
-{% set plugins_presented = true %}
-{% endif %}
-{% if templatecfg['sflow']['servers'] != none %}
-{% if plugins_presented %}
-{% for server in templatecfg['sflow']['servers'] %},sfprobe[sf_{{ server['address'] }}]{% endfor %}
-{% else %}
-{% for server in templatecfg['sflow']['servers'] %}
-{% if loop.last %}sfprobe[sf_{{ server['address'] }}]{% else %}sfprobe[sf_{{ server['address'] }}],{% endif %}
-{% endfor %}
-{% endif %}
-{% set plugins_presented = true %}
-{% endif %}
-{% if templatecfg['disable-imt'] == none %}
-{% if plugins_presented %},memory{% else %}memory{% endif %}
-{% endif %}
-{% if templatecfg['netflow']['servers'] != none %}
-{% for server in templatecfg['netflow']['servers'] %}
-nfprobe_receiver[nf_{{ server['address'] }}]: {{ server['address'] }}:{{ server['port'] }}
-nfprobe_version[nf_{{ server['address'] }}]: {{ templatecfg['netflow']['version'] }}
-{% if templatecfg['netflow']['engine-id'] != none %}
-nfprobe_engine[nf_{{ server['address'] }}]: {{ templatecfg['netflow']['engine-id'] }}
-{% endif %}
-{% if templatecfg['netflow']['max-flows'] != none %}
-nfprobe_maxflows[nf_{{ server['address'] }}]: {{ templatecfg['netflow']['max-flows'] }}
-{% endif %}
-{% if templatecfg['netflow']['sampling-rate'] != none %}
-sampling_rate[nf_{{ server['address'] }}]: {{ templatecfg['netflow']['sampling-rate'] }}
-{% endif %}
-{% if templatecfg['netflow']['source-ip'] != none %}
-nfprobe_source_ip[nf_{{ server['address'] }}]: {{ templatecfg['netflow']['source-ip'] }}
+{% set plugin = [] %}
+{% if disable_imt is not defined %}
+{% set plugin = ['memory'] %}
{% endif %}
-{% if templatecfg['netflow']['timeout_string'] != '' %}
-nfprobe_timeouts[nf_{{ server['address'] }}]: {{ templatecfg['netflow']['timeout_string'] }}
+{% if netflow is defined and netflow.server is defined and netflow.server is not none %}
+{% for server in netflow.server %}
+{% set plugin = plugin.append('nfprobe[nf_' ~ server ~ ']') %}
+{% endfor %}
{% endif %}
-{% endfor %}
+{% if sflow is defined and sflow.server is defined and sflow.server is not none %}
+{% for server in sflow.server %}
+{% set plugin = plugin.append('sfprobe[sf_' ~ server ~ ']') %}
+{% endfor %}
{% endif %}
+plugins: {{ plugin | join(',') }}
-{% if templatecfg['sflow']['servers'] != none %}
-{% for server in templatecfg['sflow']['servers'] %}
-sfprobe_receiver[sf_{{ server['address'] }}]: {{ server['address'] }}:{{ server['port'] }}
-sfprobe_agentip[sf_{{ server['address'] }}]: {{ templatecfg['sflow']['agent-address'] }}
-{% if templatecfg['sflow']['sampling-rate'] != none %}
-sampling_rate[sf_{{ server['address'] }}]: {{ templatecfg['sflow']['sampling-rate'] }}
+{% if netflow is defined and netflow.server is defined and netflow.server is not none %}
+# NetFlow servers
+{% for server, server_config in netflow.server.items() %}
+nfprobe_receiver[nf_{{ server }}]: {{ server }}:{{ server_config.port }}
+nfprobe_version[nf_{{ server }}]: {{ netflow.version }}
+{% if netflow.engine_id is defined and netflow.engine_id is not none %}
+nfprobe_engine[nf_{{ server }}]: {{ netflow.engine_id }}
+{% endif %}
+{% if netflow.max_flows is defined and netflow.max_flows is not none %}
+nfprobe_maxflows[nf_{{ server }}]: {{ netflow.max_flows }}
+{% endif %}
+{% if netflow.sampling_rate is defined and netflow.sampling_rate is not none %}
+sampling_rate[nf_{{ server }}]: {{ netflow.sampling_rate }}
+{% endif %}
+{% if netflow.source_address is defined and netflow.source_address is not none %}
+nfprobe_source_ip[nf_{{ server }}]: {{ netflow.source_address }}
+{% endif %}
+{% if netflow.timeout is defined and netflow.timeout is not none %}
+nfprobe_timeouts[nf_{{ server }}]: expint={{ netflow.timeout.expiry_interval }}:general={{ netflow.timeout.flow_generic }}:icmp={{ netflow.timeout.icmp }}:maxlife={{ netflow.timeout.max_active_life }}:tcp.fin={{ netflow.timeout.tcp_fin }}:tcp={{ netflow.timeout.tcp_generic }}:tcp.rst={{ netflow.timeout.tcp_rst }}:udp={{ netflow.timeout.udp }}
+{% endif %}
+
+{% endfor %}
{% endif %}
-{% endfor %}
+
+{% if sflow is defined and sflow.server is defined and sflow.server is not none %}
+# sFlow servers
+{% for server, server_config in sflow.server.items() %}
+sfprobe_receiver[sf_{{ server }}]: {{ server }}:{{ server_config.port }}
+sfprobe_agentip[sf_{{ server }}]: {{ sflow.agent_address }}
+{% if sflow.sampling_rate is defined and sflow.sampling_rate is not none %}
+sampling_rate[sf_{{ server }}]: {{ sflow.sampling_rate }}
+{% endif %}
+{% if sflow.source_address is defined and sflow.source_address is not none %}
+sfprobe_source_ip[sf_{{ server }}]: {{ sflow.source_address }}
+{% endif %}
+
+{% endfor %}
{% endif %}
diff --git a/data/templates/snmp/etc.snmp.conf.tmpl b/data/templates/snmp/etc.snmp.conf.tmpl
index 6e4c6f063..f7d9a3c17 100644
--- a/data/templates/snmp/etc.snmp.conf.tmpl
+++ b/data/templates/snmp/etc.snmp.conf.tmpl
@@ -1,4 +1,4 @@
### Autogenerated by snmp.py ###
-{% if trap_source %}
+{% if trap_source is defined and trap_source is not none %}
clientaddr {{ trap_source }}
{% endif %}
diff --git a/data/templates/snmp/etc.snmpd.conf.tmpl b/data/templates/snmp/etc.snmpd.conf.tmpl
index 30806ce8a..befea0122 100644
--- a/data/templates/snmp/etc.snmpd.conf.tmpl
+++ b/data/templates/snmp/etc.snmpd.conf.tmpl
@@ -33,87 +33,152 @@ interface_replace_old yes
# Default system description is VyOS version
sysDescr VyOS {{ version }}
-{% if description %}
+{% if description is defined and description is not none %}
# Description
SysDescr {{ description }}
{% endif %}
# Listen
-agentaddress unix:/run/snmpd.socket{% if listen_on %}{% for li in listen_on %},{{ li }}{% endfor %}{% else %},{{protocol}}:161{% if ipv6_enabled %},{{protocol}}6:161{% endif %}{% endif %}
+{% set options = [] %}
+{% if listen_address is defined and listen_address is not none %}
+{% for address, address_options in listen_address.items() %}
+{% if address | is_ipv6 %}
+{% set protocol = protocol ~ '6' %}
+{% endif %}
+{% set _ = options.append(protocol ~ ':' ~ address | bracketize_ipv6 ~ ':' ~ address_options.port) %}
+{% endfor %}
+{% else %}
+{% set _ = options.append(protocol ~ ':161') %}
+{% if ipv6_disabled is not defined %}
+{% set _ = options.append(protocol ~ '6:161') %}
+{% endif %}
+{% endif %}
+agentaddress unix:/run/snmpd.socket{{ ',' ~ options | join(',') if options is defined and options is not none }}
# SNMP communities
-{% for c in communities %}
-{% if c.network_v4 %}
-{% for network in c.network_v4 %}
-{{ c.authorization }}community {{ c.name }} {{ network }}
-{% endfor %}
-{% elif not c.has_source %}
-{{ c.authorization }}community {{ c.name }}
-{% endif %}
-{% if c.network_v6 %}
-{% for network in c.network_v6 %}
-{{ c.authorization }}community6 {{ c.name }} {{ network }}
-{% endfor %}
-{% elif not c.has_source %}
-{{ c.authorization }}community6 {{ c.name }}
-{% endif %}
-{% endfor %}
+{% if community is defined and community is not none %}
+{% for comm, comm_config in community.items() %}
+{% if comm_config.client is defined and comm_config.client is not none %}
+{% for client in comm_config.client %}
+{% if client | is_ipv4 %}
+{{ comm_config.authorization }}community {{ comm }} {{ client }}
+{% elif client | is_ipv6 %}
+{{ comm_config.authorization }}community6 {{ comm }} {{ client }}
+{% endif %}
+{% endfor %}
+{% endif %}
+{% if comm_config.network is defined and comm_config.network is not none %}
+{% for network in comm_config.network %}
+{% if network | is_ipv4 %}
+{{ comm_config.authorization }}community {{ comm }} {{ network }}
+{% elif client | is_ipv6 %}
+{{ comm_config.authorization }}community6 {{ comm }} {{ network }}
+{% endif %}
+{% endfor %}
+{% endif %}
+{% if comm_config.client is not defined and comm_config.network is not defined %}
+{{ comm_config.authorization }}community {{ comm }}
+{% endif %}
+{% endfor %}
+{% endif %}
-{% if contact %}
+{% if contact is defined and contact is not none %}
# system contact information
SysContact {{ contact }}
{% endif %}
-{% if location %}
+{% if location is defined and location is not none %}
# system location information
SysLocation {{ location }}
{% endif %}
-{% if smux_peers %}
+{% if smux_peer is defined and smux_peer is not none %}
# additional smux peers
-{% for sp in smux_peers %}
-smuxpeer {{ sp }}
+{% for peer in smux_peer %}
+smuxpeer {{ peer }}
{% endfor %}
{% endif %}
-{% if trap_targets %}
+{% if trap_target is defined and trap_target is not none %}
# if there is a problem - tell someone!
-{% for trap in trap_targets %}
-trap2sink {{ trap.target }}{{ ":" + trap.port if trap.port is defined }} {{ trap.community }}
+{% for trap, trap_config in trap_target.items() %}
+trap2sink {{ trap }}:{{ trap_config.port }} {{ trap_config.community }}
{% endfor %}
{% endif %}
-{% if v3_enabled %}
+{% if v3 is defined and v3 is not none %}
#
# SNMPv3 stuff goes here
#
+{% if v3.view is defined and v3.view is not none %}
# views
-{% for view in v3_views %}
-{% for oid in view.oids %}
-view {{ view.name }} included .{{ oid.oid }}
+{% for view, view_config in v3.view.items() %}
+{% if view_config.oid is defined and view_config.oid is not none %}
+{% for oid in view_config.oid %}
+view {{ view }} included .{{ oid }}
+{% endfor %}
+{% endif %}
{% endfor %}
-{% endfor %}
+{% endif %}
# access
+{% if v3.group is defined and v3.group is not none %}
# context sec.model sec.level match read write notif
-{% for group in v3_groups %}
-access {{ group.name }} "" usm {{ group.seclevel }} exact {{ group.view }} {% if group.mode == 'ro' %}none{% else %}{{ group.view }}{% endif %} none
-{% endfor %}
+{% for group, group_config in v3.group.items() %}
+access {{ group }} "" usm {{ group_config.seclevel }} exact {{ group_config.view }} {% if group_config.mode == 'ro' %}none{% else %}{{ group_config.view }}{% endif %} none
+{% endfor %}
+{% endif %}
# trap-target
-{% for t in v3_traps %}
-trapsess -v 3 {{ '-Ci' if t.type == 'inform' }} -e {{ v3_engineid }} -u {{ t.secName }} -l {{ t.secLevel }} -a {{ t.authProtocol }} {% if t.authPassword %}-A {{ t.authPassword }}{% elif t.authMasterKey %}-3m {{ t.authMasterKey }}{% endif %} -x {{ t.privProtocol }} {% if t.privPassword %}-X {{ t.privPassword }}{% elif t.privMasterKey %}-3M {{ t.privMasterKey }}{% endif %} {{ t.ipProto }}:{{ t.ipAddr }}:{{ t.ipPort }}
-{% endfor %}
+{% if v3.trap_target is defined and v3.trap_target is not none %}
+{% for trap, trap_config in v3.trap_target.items() %}
+{% set options = '' %}
+{% if trap_config.type == 'inform' %}
+{% set options = options ~ ' -Ci' %}
+{% endif %}
+{% if v3.engineid is defined and v3.engineid is not none %}
+{% set options = options ~ ' -e "' ~ v3.engineid ~ '"' %}
+{% endif %}
+{% if trap_config.user is defined and trap_config.user is not none %}
+{% set options = options ~ ' -u ' ~ trap_config.user %}
+{% endif %}
+{% if trap_config.auth is defined and trap_config.auth.plaintext_password is defined or trap_config.auth.encrypted_password is defined %}
+{% set options = options ~ ' -a ' ~ trap_config.auth.type %}
+{% if trap_config.auth.plaintext_password is defined and trap_config.auth.plaintext_password is not none %}
+{% set options = options ~ ' -A ' ~ trap_config.auth.plaintext_password %}
+{% elif trap_config.auth.encrypted_password is defined and trap_config.auth.encrypted_password is not none %}
+{% set options = options ~ ' -3m ' ~ trap_config.auth.encrypted_password %}
+{% endif %}
+{% if trap_config.privacy is defined and trap_config.privacy.plaintext_password is defined or trap_config.privacy.encrypted_password is defined %}
+{% set options = options ~ ' -x ' ~ trap_config.privacy.type %}
+{% if trap_config.privacy.plaintext_password is defined and trap_config.privacy.plaintext_password is not none %}
+{% set options = options ~ ' -X ' ~ trap_config.privacy.plaintext_password %}
+{% elif trap_config.privacy.encrypted_password is defined and trap_config.privacy.encrypted_password is not none %}
+{% set options = options ~ ' -3M ' ~ trap_config.privacy.encrypted_password %}
+{% endif %}
+{% set options = options ~ ' -l authPriv' %}
+{% else %}
+{% set options = options ~ ' -l authNoPriv' %}
+{% endif %}
+{% else %}
+{% set options = options ~ ' -l noAuthNoPriv' %}
+{% endif %}
+trapsess -v 3 {{ options }} {{ trap }}:{{ trap_config.protocol }}:{{ trap_config.port }}
+{% endfor %}
+{% endif %}
# group
-{% for u in v3_users %}
-group {{ u.group }} usm {{ u.name }}
-{% endfor %}
+{% if v3.user is defined and v3.user is not none %}
+{% for user, user_config in v3.user.items() %}
+group {{ user_config.group }} usm {{ user }}
+{% endfor %}
+{% endif %}
+{# SNMPv3 end #}
{% endif %}
-{% if script_ext %}
+{% if script_extensions is defined and script_extensions.extension_name is defined and script_extensions.extension_name is not none %}
# extension scripts
-{% for ext in script_ext|sort(attribute='name') %}
-extend {{ ext.name }} {{ ext.script }}
+{% for script, script_config in script_extensions.extension_name.items() | sort(attribute=script) %}
+extend {{ script }} {{ script_config.script }}
{% endfor %}
{% endif %}
diff --git a/data/templates/snmp/override.conf.tmpl b/data/templates/snmp/override.conf.tmpl
index 2ac45a89f..3b00aab83 100644
--- a/data/templates/snmp/override.conf.tmpl
+++ b/data/templates/snmp/override.conf.tmpl
@@ -1,5 +1,5 @@
{% set vrf_command = 'ip vrf exec ' + vrf + ' ' if vrf is defined else '' %}
-{% set oid_route_table = ' ' if route_table is sameas true else '-I -ipCidrRouteTable,inetCidrRouteTable' %}
+{% set oid_route_table = ' ' if oid_enable is defined and oid_enable == 'route-table' else '-I -ipCidrRouteTable,inetCidrRouteTable' %}
[Unit]
StartLimitIntervalSec=0
After=vyos-router.service
diff --git a/data/templates/snmp/usr.snmpd.conf.tmpl b/data/templates/snmp/usr.snmpd.conf.tmpl
index e2c5ec102..1c688a61e 100644
--- a/data/templates/snmp/usr.snmpd.conf.tmpl
+++ b/data/templates/snmp/usr.snmpd.conf.tmpl
@@ -1,6 +1,8 @@
### Autogenerated by snmp.py ###
-{% for u in v3_users %}
-{{ u.mode }}user {{ u.name }}
-{% endfor %}
+{% if v3 is defined and v3.user is defined and v3.user is not none %}
+{% for user, user_config in v3.user.items() %}
+{{ user_config.mode }}user {{ user }}
+{% endfor %}
+{% endif %}
rwuser {{ vyos_user }}
diff --git a/data/templates/snmp/var.snmpd.conf.tmpl b/data/templates/snmp/var.snmpd.conf.tmpl
index c779587df..5871a8234 100644
--- a/data/templates/snmp/var.snmpd.conf.tmpl
+++ b/data/templates/snmp/var.snmpd.conf.tmpl
@@ -1,14 +1,16 @@
### Autogenerated by snmp.py ###
# user
-{% for u in v3_users %}
-{% if u.authOID == 'none' %}
-createUser {{ u.name }}
-{% else %}
-usmUser 1 3 0x{{ v3_engineid }} "{{ u.name }}" "{{ u.name }}" NULL {{ u.authOID }} 0x{{ u.authMasterKey }} {{ u.privOID }} 0x{{ u.privMasterKey }} 0x
-{% endif %}
-{% endfor %}
+{% if v3 is defined and v3 is not none %}
+{% if v3.user is defined and v3.user is not none %}
+{% for user, user_config in v3.user.items() %}
+usmUser 1 3 0x{{ v3.engineid }} "{{ user }}" "{{ user }}" NULL {{ user_config.auth.type | snmp_auth_oid }} 0x{{ user_config.auth.encrypted_password }} {{ user_config.privacy.type | snmp_auth_oid }} 0x{{ user_config.privacy.encrypted_password }} 0x
+{% endfor %}
+{% endif %}
+# VyOS default user
createUser {{ vyos_user }} MD5 "{{ vyos_user_pass }}" DES
-{% if v3_engineid %}
-oldEngineID 0x{{ v3_engineid }}
+
+{% if v3.engineid is defined and v3.engineid is not none %}
+oldEngineID 0x{{ v3.engineid }}
+{% endif %}
{% endif %}
diff --git a/data/templates/squid/squid.conf.tmpl b/data/templates/squid/squid.conf.tmpl
index 80826fc75..26aff90bf 100644
--- a/data/templates/squid/squid.conf.tmpl
+++ b/data/templates/squid/squid.conf.tmpl
@@ -88,7 +88,7 @@ tcp_outgoing_address {{ outgoing_address }}
{% if listen_address is defined and listen_address is not none %}
{% for address, config in listen_address.items() %}
-http_port {{ address }}:{{ config.port if config.port is defined else default_port }} {{ 'intercept' if config.disable_transparent is not defined }}
+http_port {{ address | bracketize_ipv6 }}:{{ config.port if config.port is defined else default_port }} {{ 'intercept' if config.disable_transparent is not defined }}
{% endfor %}
{% endif %}
http_port 127.0.0.1:{{ default_port }}
diff --git a/data/templates/syslog/rsyslog.conf.tmpl b/data/templates/syslog/rsyslog.conf.tmpl
index e25ef48d4..2fb621760 100644
--- a/data/templates/syslog/rsyslog.conf.tmpl
+++ b/data/templates/syslog/rsyslog.conf.tmpl
@@ -25,12 +25,18 @@ $outchannel {{ file }},{{ file_options['log-file'] }},{{ file_options['max-size'
{% if host_options.proto == 'tcp' %}
{% if host_options.port is defined %}
{% if host_options.oct_count is defined %}
-{{ host_options.selectors }} @@(o){{ host }}:{{ host_options.port }};RSYSLOG_SyslogProtocol23Format
+{{ host_options.selectors }} @@(o){{ host | bracketize_ipv6 }}:{{ host_options.port }};RSYSLOG_SyslogProtocol23Format
{% else %}
-{{ host_options.selectors }} @@{{ host }}:{{ host_options.port }}
+{{ host_options.selectors }} @@{{ host | bracketize_ipv6 }}:{{ host_options.port }}
{% endif %}
{% else %}
-{{ host_options.selectors }} @@{{ host }}
+{{ 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'] %}
diff --git a/data/templates/vrrp/keepalived.conf.tmpl b/data/templates/vrrp/keepalived.conf.tmpl
index b4824a994..6585fc60b 100644
--- a/data/templates/vrrp/keepalived.conf.tmpl
+++ b/data/templates/vrrp/keepalived.conf.tmpl
@@ -5,9 +5,6 @@
global_defs {
dynamic_interfaces
script_user root
- # Don't run scripts configured to be run as root if any part of the path
- # is writable by a non-root user.
- enable_script_security
notify_fifo /run/keepalived/keepalived_notify_fifo
notify_fifo_script /usr/libexec/vyos/system/keepalived-fifo.py
}
@@ -86,15 +83,24 @@ vrrp_instance {{ name }} {
{% endif %}
{% if sync_group is defined and sync_group is not none %}
-{% for name, group_config in sync_group.items() if group_config.disable is not defined %}
+{% for name, sync_group_config in sync_group.items() if sync_group_config.disable is not defined %}
vrrp_sync_group {{ name }} {
group {
-{% if group_config.member is defined and group_config.member is not none %}
-{% for member in group_config.member %}
+{% if sync_group_config.member is defined and sync_group_config.member is not none %}
+{% for member in sync_group_config.member %}
{{ member }}
{% endfor %}
{% endif %}
}
+
+{# Health-check scripts should be in section sync-group if member is part of the sync-group T4081 #}
+{% for name, group_config in group.items() if group_config.disable is not defined %}
+{% if group_config.health_check is defined and group_config.health_check.script is defined and group_config.health_check.script is not none and name in sync_group_config.member %}
+ track_script {
+ healthcheck_{{ name }}
+ }
+{% endif %}
+{% endfor %}
{% if conntrack_sync_group is defined and conntrack_sync_group == name %}
{% set vyos_helper = "/usr/libexec/vyos/vyos-vrrp-conntracksync.sh" %}
notify_master "{{ vyos_helper }} master {{ name }}"
diff --git a/debian/control b/debian/control
index 08c04b439..ef124679b 100644
--- a/debian/control
+++ b/debian/control
@@ -183,5 +183,6 @@ Description: VyOS configuration scripts and data for VMware
Package: vyos-1x-smoketest
Architecture: all
Depends:
+ snmp,
vyos-1x
Description: VyOS build sanity checking toolkit
diff --git a/interface-definitions/dns-forwarding.xml.in b/interface-definitions/dns-forwarding.xml.in
index 5b0c87597..4faf604ad 100644
--- a/interface-definitions/dns-forwarding.xml.in
+++ b/interface-definitions/dns-forwarding.xml.in
@@ -105,6 +105,456 @@
</leafNode>
</children>
</tagNode>
+ <tagNode name="authoritative-domain">
+ <properties>
+ <help>Domain to host authoritative records for</help>
+ <valueHelp>
+ <format>text</format>
+ <description>An absolute DNS name</description>
+ </valueHelp>
+ <constraint>
+ <regex>^[-_a-zA-Z0-9.]{1,63}$</regex>
+ </constraint>
+ </properties>
+ <children>
+ <node name="records">
+ <properties>
+ <help>DNS zone records</help>
+ </properties>
+ <children>
+ <tagNode name="a">
+ <properties>
+ <help>"A" record</help>
+ <valueHelp>
+ <format>text</format>
+ <description>A DNS name relative to the root record</description>
+ </valueHelp>
+ <valueHelp>
+ <format>@</format>
+ <description>Root record</description>
+ </valueHelp>
+ <constraint>
+ <regex>^([-_a-zA-Z0-9.]{1,63}|@)(?&lt;!\.)$</regex>
+ </constraint>
+ </properties>
+ <children>
+ <leafNode name="address">
+ <properties>
+ <help>IPv4 address [REQUIRED]</help>
+ <valueHelp>
+ <format>ipv4</format>
+ <description>IPv4 address</description>
+ </valueHelp>
+ <multi/>
+ <constraint>
+ <validator name="ipv4-address"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ #include <include/dns/time-to-live.xml.i>
+ #include <include/generic-disable-node.xml.i>
+ </children>
+ </tagNode>
+ <tagNode name="aaaa">
+ <properties>
+ <help>"AAAA" record</help>
+ <valueHelp>
+ <format>text</format>
+ <description>A DNS name relative to the root record</description>
+ </valueHelp>
+ <valueHelp>
+ <format>@</format>
+ <description>Root record</description>
+ </valueHelp>
+ <constraint>
+ <regex>^([-_a-zA-Z0-9.]{1,63}|@)(?&lt;!\.)$</regex>
+ </constraint>
+ </properties>
+ <children>
+ <leafNode name="address">
+ <properties>
+ <help>IPv6 address [REQUIRED]</help>
+ <valueHelp>
+ <format>ipv6</format>
+ <description>IPv6 address</description>
+ </valueHelp>
+ <multi/>
+ <constraint>
+ <validator name="ipv6-address"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ #include <include/dns/time-to-live.xml.i>
+ #include <include/generic-disable-node.xml.i>
+ </children>
+ </tagNode>
+ <tagNode name="cname">
+ <properties>
+ <help>"CNAME" record</help>
+ <valueHelp>
+ <format>text</format>
+ <description>A DNS name relative to the root record</description>
+ </valueHelp>
+ <valueHelp>
+ <format>@</format>
+ <description>Root record</description>
+ </valueHelp>
+ <constraint>
+ <regex>^([-_a-zA-Z0-9.]{1,63}|@)(?&lt;!\.)$</regex>
+ </constraint>
+ </properties>
+ <children>
+ <leafNode name="target">
+ <properties>
+ <help>Target DNS name [REQUIRED]</help>
+ <valueHelp>
+ <format>name.example.com</format>
+ <description>An absolute DNS name</description>
+ </valueHelp>
+ <constraint>
+ <regex>^[-_a-zA-Z0-9.]{1,63}(?&lt;!\.)$</regex>
+ </constraint>
+ </properties>
+ </leafNode>
+ #include <include/dns/time-to-live.xml.i>
+ #include <include/generic-disable-node.xml.i>
+ </children>
+ </tagNode>
+ <tagNode name="mx">
+ <properties>
+ <help>"MX" record</help>
+ <valueHelp>
+ <format>text</format>
+ <description>A DNS name relative to the root record</description>
+ </valueHelp>
+ <valueHelp>
+ <format>@</format>
+ <description>Root record</description>
+ </valueHelp>
+ <constraint>
+ <regex>^([-_a-zA-Z0-9.]{1,63}|@)(?&lt;!\.)$</regex>
+ </constraint>
+ </properties>
+ <children>
+ <tagNode name="server">
+ <properties>
+ <help>Mail server [REQUIRED]</help>
+ <valueHelp>
+ <format>name.example.com</format>
+ <description>An absolute DNS name</description>
+ </valueHelp>
+ <constraint>
+ <regex>^[-_a-zA-Z0-9.]{1,63}(?&lt;!\.)$</regex>
+ </constraint>
+ </properties>
+ <children>
+ <leafNode name="priority">
+ <properties>
+ <help>Server priority</help>
+ <valueHelp>
+ <format>u32:1-999</format>
+ <description>Server priority (lower numbers are higher priority)</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-999"/>
+ </constraint>
+ </properties>
+ <defaultValue>10</defaultValue>
+ </leafNode>
+ </children>
+ </tagNode>
+ #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>
+ <valueHelp>
+ <format>text</format>
+ <description>A DNS name relative to the root record</description>
+ </valueHelp>
+ <valueHelp>
+ <format>@</format>
+ <description>Root record</description>
+ </valueHelp>
+ <constraint>
+ <regex>^([-_a-zA-Z0-9.]{1,63}|@)(?&lt;!\.)$</regex>
+ </constraint>
+ </properties>
+ <children>
+ <leafNode name="target">
+ <properties>
+ <help>Target DNS name [REQUIRED]</help>
+ <valueHelp>
+ <format>name.example.com</format>
+ <description>An absolute DNS name</description>
+ </valueHelp>
+ <constraint>
+ <regex>^[-_a-zA-Z0-9.]{1,63}(?&lt;!\.)$</regex>
+ </constraint>
+ </properties>
+ </leafNode>
+ #include <include/dns/time-to-live.xml.i>
+ #include <include/generic-disable-node.xml.i>
+ </children>
+ </tagNode>
+ <tagNode name="txt">
+ <properties>
+ <help>"TXT" record</help>
+ <valueHelp>
+ <format>text</format>
+ <description>A DNS name relative to the root record</description>
+ </valueHelp>
+ <valueHelp>
+ <format>@</format>
+ <description>Root record</description>
+ </valueHelp>
+ <constraint>
+ <regex>^([-_a-zA-Z0-9.]{1,63}|@)(?&lt;!\.)$</regex>
+ </constraint>
+ </properties>
+ <children>
+ <leafNode name="value">
+ <properties>
+ <help>Record contents [REQUIRED]</help>
+ <valueHelp>
+ <format>text</format>
+ <description>Record contents</description>
+ </valueHelp>
+ <multi/>
+ </properties>
+ </leafNode>
+ #include <include/dns/time-to-live.xml.i>
+ #include <include/generic-disable-node.xml.i>
+ </children>
+ </tagNode>
+ <tagNode name="spf">
+ <properties>
+ <help>"SPF" record (type=SPF)</help>
+ <valueHelp>
+ <format>text</format>
+ <description>A DNS name relative to the root record</description>
+ </valueHelp>
+ <valueHelp>
+ <format>@</format>
+ <description>Root record</description>
+ </valueHelp>
+ <constraint>
+ <regex>^([-_a-zA-Z0-9.]{1,63}|@)(?&lt;!\.)$</regex>
+ </constraint>
+ </properties>
+ <children>
+ <leafNode name="value">
+ <properties>
+ <help>Record contents [REQUIRED]</help>
+ <valueHelp>
+ <format>text</format>
+ <description>Record contents</description>
+ </valueHelp>
+ </properties>
+ </leafNode>
+ #include <include/dns/time-to-live.xml.i>
+ #include <include/generic-disable-node.xml.i>
+ </children>
+ </tagNode>
+ <tagNode name="srv">
+ <properties>
+ <help>"SRV" record</help>
+ <valueHelp>
+ <format>text</format>
+ <description>A DNS name relative to the root record</description>
+ </valueHelp>
+ <valueHelp>
+ <format>@</format>
+ <description>Root record</description>
+ </valueHelp>
+ <constraint>
+ <regex>^([-_a-zA-Z0-9.]{1,63}|@)(?&lt;!\.)$</regex>
+ </constraint>
+ </properties>
+ <children>
+ <tagNode name="entry">
+ <properties>
+ <help>Service entry [REQUIRED]</help>
+ <valueHelp>
+ <format>u32:0-65535</format>
+ <description>Entry number</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 0-65535"/>
+ </constraint>
+ </properties>
+ <children>
+ <leafNode name="hostname">
+ <properties>
+ <help>Server hostname [REQUIRED]</help>
+ <valueHelp>
+ <format>name.example.com</format>
+ <description>An absolute DNS name</description>
+ </valueHelp>
+ <constraint>
+ <regex>^[-_a-zA-Z0-9.]{1,63}(?&lt;!\.)$</regex>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="port">
+ <properties>
+ <help>Port number [REQUIRED]</help>
+ <valueHelp>
+ <format>u32:0-65535</format>
+ <description>TCP/UDP port number</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 0-65536"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="priority">
+ <properties>
+ <help>Entry priority</help>
+ <valueHelp>
+ <format>u32:0-65535</format>
+ <description>Entry priority (lower numbers are higher priority)</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 0-65535"/>
+ </constraint>
+ </properties>
+ <defaultValue>10</defaultValue>
+ </leafNode>
+ <leafNode name="weight">
+ <properties>
+ <help>Entry weight</help>
+ <valueHelp>
+ <format>u32:0-65535</format>
+ <description>Entry weight</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 0-65535"/>
+ </constraint>
+ </properties>
+ <defaultValue>0</defaultValue>
+ </leafNode>
+ </children>
+ </tagNode>
+ #include <include/dns/time-to-live.xml.i>
+ #include <include/generic-disable-node.xml.i>
+ </children>
+ </tagNode>
+ <tagNode name="naptr">
+ <properties>
+ <help>"NAPTR" record</help>
+ <valueHelp>
+ <format>text</format>
+ <description>A DNS name relative to the root record</description>
+ </valueHelp>
+ <valueHelp>
+ <format>@</format>
+ <description>Root record</description>
+ </valueHelp>
+ <constraint>
+ <regex>^([-_a-zA-Z0-9.]{1,63}|@)(?&lt;!\.)$</regex>
+ </constraint>
+ </properties>
+ <children>
+ <tagNode name="rule">
+ <properties>
+ <help>NAPTR rule [REQUIRED]</help>
+ <valueHelp>
+ <format>u32:0-65535</format>
+ <description>Rule number</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 0-65535"/>
+ </constraint>
+ </properties>
+ <children>
+ <leafNode name="order">
+ <properties>
+ <help>Rule order</help>
+ <valueHelp>
+ <format>u32:0-65535</format>
+ <description>Rule order (lower order is evaluated first)</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 0-65535"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="preference">
+ <properties>
+ <help>Rule preference</help>
+ <valueHelp>
+ <format>u32:0-65535</format>
+ <description>Rule preference</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 0-65535"/>
+ </constraint>
+ </properties>
+ <defaultValue>0</defaultValue>
+ </leafNode>
+ <leafNode name="lookup-srv">
+ <properties>
+ <help>"S" flag</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="lookup-a">
+ <properties>
+ <help>"A" flag</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="resolve-uri">
+ <properties>
+ <help>"U" flag</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="protocol-specific">
+ <properties>
+ <help>"P" flag</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="service">
+ <properties>
+ <help>Service type</help>
+ <constraint>
+ <regex>^[a-zA-Z][a-zA-Z0-9]{0,31}(\+[a-zA-Z][a-zA-Z0-9]{0,31})?$</regex>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="regexp">
+ <properties>
+ <help>Regular expression</help>
+ </properties>
+ </leafNode>
+ <leafNode name="replacement">
+ <properties>
+ <help>Replacement DNS name</help>
+ <valueHelp>
+ <format>name.example.com</format>
+ <description>An absolute DNS name</description>
+ </valueHelp>
+ <constraint>
+ <regex>^[-_a-zA-Z0-9.]{1,63}(?&lt;!\.)$</regex>
+ </constraint>
+ </properties>
+ </leafNode>
+ </children>
+ </tagNode>
+ #include <include/dns/time-to-live.xml.i>
+ #include <include/generic-disable-node.xml.i>
+ </children>
+ </tagNode>
+ </children>
+ </node>
+ #include <include/generic-disable-node.xml.i>
+ </children>
+ </tagNode>
<leafNode name="ignore-hosts-file">
<properties>
<help>Do not use local /etc/hosts file in name resolution</help>
@@ -114,7 +564,7 @@
<leafNode name="no-serve-rfc1918">
<properties>
<help>Makes the server authoritatively not aware of RFC1918 addresses</help>
- <valueless/>
+ <valueless/>
</properties>
</leafNode>
<leafNode name="allow-from">
diff --git a/interface-definitions/firewall.xml.in b/interface-definitions/firewall.xml.in
index 1bb7fb4a1..78a48a522 100644
--- a/interface-definitions/firewall.xml.in
+++ b/interface-definitions/firewall.xml.in
@@ -218,7 +218,15 @@
#include <include/generic-description.xml.i>
<tagNode name="rule">
<properties>
- <help>Rule number (1-9999)</help>
+ <help>Firewall rule number (IPv6)</help>
+ <valueHelp>
+ <format>u32:1-999999</format>
+ <description>Number for this Firewall rule</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-999999"/>
+ </constraint>
+ <constraintErrorMessage>Firewall rule number must be between 1 and 999999</constraintErrorMessage>
</properties>
<children>
#include <include/firewall/action.xml.i>
@@ -544,7 +552,15 @@
#include <include/generic-description.xml.i>
<tagNode name="rule">
<properties>
- <help>Rule number (1-9999)</help>
+ <help>Firewall rule number (IPv4)</help>
+ <valueHelp>
+ <format>u32:1-999999</format>
+ <description>Number for this Firewall rule</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-999999"/>
+ </constraint>
+ <constraintErrorMessage>Firewall rule number must be between 1 and 999999</constraintErrorMessage>
</properties>
<children>
#include <include/firewall/action.xml.i>
diff --git a/interface-definitions/flow-accounting-conf.xml.in b/interface-definitions/flow-accounting-conf.xml.in
index 113c1d849..1b57d706c 100644
--- a/interface-definitions/flow-accounting-conf.xml.in
+++ b/interface-definitions/flow-accounting-conf.xml.in
@@ -14,23 +14,37 @@
<help>Buffer size</help>
<valueHelp>
<format>u32</format>
- <description>Buffer size in MiB</description>
+ <description>Buffer size in MiB (default: 10)</description>
</valueHelp>
<constraint>
- <validator name="numeric" argument="--range 0-4294967295" />
+ <validator name="numeric" argument="--range 0-4294967295"/>
</constraint>
</properties>
+ <defaultValue>10</defaultValue>
+ </leafNode>
+ <leafNode name="packet-length">
+ <properties>
+ <help>Specifies the maximum number of bytes to capture for each packet</help>
+ <valueHelp>
+ <format>u32:128-750</format>
+ <description>Packet length in bytes (default: 128)</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 128-750"/>
+ </constraint>
+ </properties>
+ <defaultValue>128</defaultValue>
</leafNode>
<leafNode name="enable-egress">
<properties>
<help>Enable egress flow accounting</help>
- <valueless />
+ <valueless/>
</properties>
</leafNode>
<leafNode name="disable-imt">
<properties>
<help>Disable in memory table plugin</help>
- <valueless />
+ <valueless/>
</properties>
</leafNode>
<leafNode name="syslog-facility">
@@ -166,7 +180,7 @@
<description>NetFlow maximum flows</description>
</valueHelp>
<constraint>
- <validator name="numeric" argument="--range 0-4294967295" />
+ <validator name="numeric" argument="--range 0-4294967295"/>
</constraint>
</properties>
</leafNode>
@@ -178,27 +192,11 @@
<description>Sampling rate (1 in N packets)</description>
</valueHelp>
<constraint>
- <validator name="numeric" argument="--range 0-4294967295" />
- </constraint>
- </properties>
- </leafNode>
- <leafNode name="source-ip">
- <properties>
- <help>IPv4 or IPv6 source address of NetFlow packets</help>
- <valueHelp>
- <format>ipv4</format>
- <description>IPv4 source address of NetFlow packets</description>
- </valueHelp>
- <valueHelp>
- <format>ipv6</format>
- <description>IPv6 source address of NetFlow packets</description>
- </valueHelp>
- <constraint>
- <validator name="ipv4-address"/>
- <validator name="ipv6-address"/>
+ <validator name="numeric" argument="--range 0-4294967295"/>
</constraint>
</properties>
</leafNode>
+ #include <include/source-address-ipv4-ipv6.xml.i>
<leafNode name="version">
<properties>
<help>NetFlow version to export</help>
@@ -218,6 +216,7 @@
<description>Internet Protocol Flow Information Export (IPFIX)</description>
</valueHelp>
</properties>
+ <defaultValue>9</defaultValue>
</leafNode>
<tagNode name="server">
<properties>
@@ -241,12 +240,13 @@
<help>NetFlow port number</help>
<valueHelp>
<format>u32:1025-65535</format>
- <description>NetFlow port number (default 2055)</description>
+ <description>NetFlow port number (default: 2055)</description>
</valueHelp>
<constraint>
- <validator name="numeric" argument="--range 1025-65535" />
+ <validator name="numeric" argument="--range 1025-65535"/>
</constraint>
</properties>
+ <defaultValue>2055</defaultValue>
</leafNode>
</children>
</tagNode>
@@ -260,96 +260,104 @@
<help>Expiry scan interval</help>
<valueHelp>
<format>u32:0-2147483647</format>
- <description>Expiry scan interval (default 60)</description>
+ <description>Expiry scan interval (default: 60)</description>
</valueHelp>
<constraint>
- <validator name="numeric" argument="--range 0-2147483647" />
+ <validator name="numeric" argument="--range 0-2147483647"/>
</constraint>
</properties>
+ <defaultValue>60</defaultValue>
</leafNode>
<leafNode name="flow-generic">
<properties>
<help>Generic flow timeout value</help>
<valueHelp>
<format>u32:0-2147483647</format>
- <description>Generic flow timeout in seconds (default 3600)</description>
+ <description>Generic flow timeout in seconds (default: 3600)</description>
</valueHelp>
<constraint>
- <validator name="numeric" argument="--range 0-2147483647" />
+ <validator name="numeric" argument="--range 0-2147483647"/>
</constraint>
</properties>
+ <defaultValue>3600</defaultValue>
</leafNode>
<leafNode name="icmp">
<properties>
<help>ICMP timeout value</help>
<valueHelp>
<format>u32:0-2147483647</format>
- <description>ICMP timeout in seconds (default 300)</description>
+ <description>ICMP timeout in seconds (default: 300)</description>
</valueHelp>
<constraint>
- <validator name="numeric" argument="--range 0-2147483647" />
+ <validator name="numeric" argument="--range 0-2147483647"/>
</constraint>
</properties>
+ <defaultValue>300</defaultValue>
</leafNode>
<leafNode name="max-active-life">
<properties>
<help>Max active timeout value</help>
<valueHelp>
<format>u32:0-2147483647</format>
- <description>Max active timeout in seconds (default 604800)</description>
+ <description>Max active timeout in seconds (default: 604800)</description>
</valueHelp>
<constraint>
- <validator name="numeric" argument="--range 0-2147483647" />
+ <validator name="numeric" argument="--range 0-2147483647"/>
</constraint>
</properties>
+ <defaultValue>604800</defaultValue>
</leafNode>
<leafNode name="tcp-fin">
<properties>
<help>TCP finish timeout value</help>
<valueHelp>
<format>u32:0-2147483647</format>
- <description>TCP FIN timeout in seconds (default 300)</description>
+ <description>TCP FIN timeout in seconds (default: 300)</description>
</valueHelp>
<constraint>
- <validator name="numeric" argument="--range 0-2147483647" />
+ <validator name="numeric" argument="--range 0-2147483647"/>
</constraint>
</properties>
+ <defaultValue>300</defaultValue>
</leafNode>
<leafNode name="tcp-generic">
<properties>
<help>TCP generic timeout value</help>
<valueHelp>
<format>u32:0-2147483647</format>
- <description>TCP generic timeout in seconds (default 3600)</description>
+ <description>TCP generic timeout in seconds (default: 3600)</description>
</valueHelp>
<constraint>
- <validator name="numeric" argument="--range 0-2147483647" />
+ <validator name="numeric" argument="--range 0-2147483647"/>
</constraint>
</properties>
+ <defaultValue>3600</defaultValue>
</leafNode>
<leafNode name="tcp-rst">
<properties>
<help>TCP reset timeout value</help>
<valueHelp>
<format>u32:0-2147483647</format>
- <description>TCP RST timeout in seconds (default 120)</description>
+ <description>TCP RST timeout in seconds (default: 120)</description>
</valueHelp>
<constraint>
- <validator name="numeric" argument="--range 0-2147483647" />
+ <validator name="numeric" argument="--range 0-2147483647"/>
</constraint>
</properties>
+ <defaultValue>120</defaultValue>
</leafNode>
<leafNode name="udp">
<properties>
<help>UDP timeout value</help>
<valueHelp>
<format>u32:0-2147483647</format>
- <description>UDP timeout in seconds (default 300)</description>
+ <description>UDP timeout in seconds (default: 300)</description>
</valueHelp>
<constraint>
- <validator name="numeric" argument="--range 0-2147483647" />
+ <validator name="numeric" argument="--range 0-2147483647"/>
</constraint>
</properties>
+ <defaultValue>300</defaultValue>
</leafNode>
</children>
</node>
@@ -363,17 +371,16 @@
<leafNode name="agent-address">
<properties>
<help>sFlow agent IPv4 address</help>
- <valueHelp>
- <format>auto</format>
- <description>auto select sFlow agent-address (default)</description>
- </valueHelp>
+ <completionHelp>
+ <list>auto</list>
+ <script>${vyos_completion_dir}/list_local_ips.sh --ipv4</script>
+ </completionHelp>
<valueHelp>
<format>ipv4</format>
<description>sFlow IPv4 agent address</description>
</valueHelp>
<constraint>
<validator name="ipv4-address"/>
- <regex>^auto$</regex>
</constraint>
</properties>
</leafNode>
@@ -385,7 +392,7 @@
<description>Sampling rate (1 in N packets)</description>
</valueHelp>
<constraint>
- <validator name="numeric" argument="--range 0-4294967295" />
+ <validator name="numeric" argument="--range 0-4294967295"/>
</constraint>
</properties>
</leafNode>
@@ -411,15 +418,17 @@
<help>sFlow port number</help>
<valueHelp>
<format>u32:1025-65535</format>
- <description>sFlow port number (default 6343)</description>
+ <description>sFlow port number (default: 6343)</description>
</valueHelp>
<constraint>
- <validator name="numeric" argument="--range 1025-65535" />
+ <validator name="numeric" argument="--range 1025-65535"/>
</constraint>
</properties>
+ <defaultValue>6343</defaultValue>
</leafNode>
</children>
</tagNode>
+ #include <include/source-address-ipv4-ipv6.xml.i>
</children>
</node>
</children>
diff --git a/interface-definitions/https.xml.in b/interface-definitions/https.xml.in
index f60df7c34..6fea2f1f6 100644
--- a/interface-definitions/https.xml.in
+++ b/interface-definitions/https.xml.in
@@ -101,6 +101,25 @@
<hidden/>
</properties>
</leafNode>
+ <leafNode name="socket">
+ <properties>
+ <help>Run server on Unix domain socket</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <node name="cors">
+ <properties>
+ <help>Set CORS options</help>
+ </properties>
+ <children>
+ <leafNode name="allow-origin">
+ <properties>
+ <help>Allow resource request from origin</help>
+ <multi/>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
</children>
</node>
<node name="api-restrict">
@@ -143,6 +162,7 @@
</node>
</children>
</node>
+ #include <include/interface/vrf.xml.i>
</children>
</node>
</children>
diff --git a/interface-definitions/include/bfd.xml.i b/interface-definitions/include/bfd.xml.i
deleted file mode 100644
index 2bc3664e1..000000000
--- a/interface-definitions/include/bfd.xml.i
+++ /dev/null
@@ -1,8 +0,0 @@
-<!-- include start from bfd.xml.i -->
-<leafNode name="bfd">
- <properties>
- <help>Enable Bidirectional Forwarding Detection (BFD)</help>
- <valueless/>
- </properties>
-</leafNode>
-<!-- include end -->
diff --git a/interface-definitions/include/bfd/bfd.xml.i b/interface-definitions/include/bfd/bfd.xml.i
new file mode 100644
index 000000000..022956d98
--- /dev/null
+++ b/interface-definitions/include/bfd/bfd.xml.i
@@ -0,0 +1,10 @@
+<!-- include start from bfd/bfd.xml.i -->
+<node name="bfd">
+ <properties>
+ <help>Enable Bidirectional Forwarding Detection (BFD)</help>
+ </properties>
+ <children>
+ #include <include/bfd/profile.xml.i>
+ </children>
+</node>
+<!-- include end -->
diff --git a/interface-definitions/include/bfd-common.xml.i b/interface-definitions/include/bfd/common.xml.i
index 8379784f7..e52221441 100644
--- a/interface-definitions/include/bfd-common.xml.i
+++ b/interface-definitions/include/bfd/common.xml.i
@@ -1,4 +1,4 @@
-<!-- include start from bfd-common.xml.i -->
+<!-- include start from bfd/common.xml.i -->
<leafNode name="echo-mode">
<properties>
<help>Enables the echo transmission mode</help>
diff --git a/interface-definitions/include/bfd/profile.xml.i b/interface-definitions/include/bfd/profile.xml.i
new file mode 100644
index 000000000..5ff057286
--- /dev/null
+++ b/interface-definitions/include/bfd/profile.xml.i
@@ -0,0 +1,14 @@
+<!-- include start from bfd/profile.xml.i -->
+<leafNode name="profile">
+ <properties>
+ <help>Use settings from BFD profile</help>
+ <completionHelp>
+ <path>protocols bfd profile</path>
+ </completionHelp>
+ <valueHelp>
+ <format>txt</format>
+ <description>BFD profile name</description>
+ </valueHelp>
+ </properties>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/include/bgp/afi-common.xml.i b/interface-definitions/include/bgp/neighbor-afi-ipv4-ipv6-common.xml.i
index 62beff40c..f3fc4444c 100644
--- a/interface-definitions/include/bgp/afi-common.xml.i
+++ b/interface-definitions/include/bgp/neighbor-afi-ipv4-ipv6-common.xml.i
@@ -1,4 +1,4 @@
-<!-- include start from bgp/afi-common.xml.i -->
+<!-- include start from bgp/neighbor-afi-ipv4-ipv6-common.xml.i -->
<leafNode name="addpath-tx-all">
<properties>
<help>Use addpath to advertise all paths to a neighbor</help>
@@ -11,6 +11,61 @@
<valueless/>
</properties>
</leafNode>
+<node name="conditionally-advertise">
+ <properties>
+ <help>Use route-map to conditionally advertise routes</help>
+ </properties>
+ <children>
+ <leafNode name="advertise-map">
+ <properties>
+ <help>Route-map to conditionally advertise routes</help>
+ <completionHelp>
+ <path>policy route-map</path>
+ </completionHelp>
+ <valueHelp>
+ <format>txt</format>
+ <description>Route map name</description>
+ </valueHelp>
+ <constraint>
+ <regex>^[-_a-zA-Z0-9.]+$</regex>
+ </constraint>
+ <constraintErrorMessage>Name of route-map can only contain alpha-numeric letters, hyphen and underscores</constraintErrorMessage>
+ </properties>
+ </leafNode>
+ <leafNode name="exist-map">
+ <properties>
+ <help>Advertise routes only if prefixes in exist-map are installed in BGP table</help>
+ <completionHelp>
+ <path>policy route-map</path>
+ </completionHelp>
+ <valueHelp>
+ <format>txt</format>
+ <description>Route map name</description>
+ </valueHelp>
+ <constraint>
+ <regex>^[-_a-zA-Z0-9.]+$</regex>
+ </constraint>
+ <constraintErrorMessage>Name of route-map can only contain alpha-numeric letters, hyphen and underscores</constraintErrorMessage>
+ </properties>
+ </leafNode>
+ <leafNode name="non-exist-map">
+ <properties>
+ <help>Advertise routes only if prefixes in non-exist-map are not installed in BGP table</help>
+ <completionHelp>
+ <path>policy route-map</path>
+ </completionHelp>
+ <valueHelp>
+ <format>txt</format>
+ <description>Route map name</description>
+ </valueHelp>
+ <constraint>
+ <regex>^[-_a-zA-Z0-9.]+$</regex>
+ </constraint>
+ <constraintErrorMessage>Name of route-map can only contain alpha-numeric letters, hyphen and underscores</constraintErrorMessage>
+ </properties>
+ </leafNode>
+ </children>
+</node>
#include <include/bgp/afi-allowas-in.xml.i>
<leafNode name="as-override">
<properties>
diff --git a/interface-definitions/include/bgp/neighbor-afi-ipv4-labeled-unicast.xml.i b/interface-definitions/include/bgp/neighbor-afi-ipv4-labeled-unicast.xml.i
index 45a440fd8..0eae29f5e 100644
--- a/interface-definitions/include/bgp/neighbor-afi-ipv4-labeled-unicast.xml.i
+++ b/interface-definitions/include/bgp/neighbor-afi-ipv4-labeled-unicast.xml.i
@@ -13,7 +13,7 @@
</children>
</node>
#include <include/bgp/afi-ipv4-prefix-list.xml.i>
- #include <include/bgp/afi-common.xml.i>
+ #include <include/bgp/neighbor-afi-ipv4-ipv6-common.xml.i>
#include <include/bgp/afi-default-originate.xml.i>
</children>
</node>
diff --git a/interface-definitions/include/bgp/neighbor-afi-ipv4-multicast.xml.i b/interface-definitions/include/bgp/neighbor-afi-ipv4-multicast.xml.i
index 6526169ca..4bb6df7c3 100644
--- a/interface-definitions/include/bgp/neighbor-afi-ipv4-multicast.xml.i
+++ b/interface-definitions/include/bgp/neighbor-afi-ipv4-multicast.xml.i
@@ -13,7 +13,7 @@
</children>
</node>
#include <include/bgp/afi-ipv4-prefix-list.xml.i>
- #include <include/bgp/afi-common.xml.i>
+ #include <include/bgp/neighbor-afi-ipv4-ipv6-common.xml.i>
#include <include/bgp/afi-default-originate.xml.i>
</children>
</node>
diff --git a/interface-definitions/include/bgp/neighbor-afi-ipv4-unicast.xml.i b/interface-definitions/include/bgp/neighbor-afi-ipv4-unicast.xml.i
index b7b7ca5b5..0094ce874 100644
--- a/interface-definitions/include/bgp/neighbor-afi-ipv4-unicast.xml.i
+++ b/interface-definitions/include/bgp/neighbor-afi-ipv4-unicast.xml.i
@@ -13,7 +13,7 @@
</children>
</node>
#include <include/bgp/afi-ipv4-prefix-list.xml.i>
- #include <include/bgp/afi-common.xml.i>
+ #include <include/bgp/neighbor-afi-ipv4-ipv6-common.xml.i>
#include <include/bgp/afi-default-originate.xml.i>
</children>
</node>
diff --git a/interface-definitions/include/bgp/neighbor-afi-ipv4-vpn.xml.i b/interface-definitions/include/bgp/neighbor-afi-ipv4-vpn.xml.i
index 838327bc9..220f22fe3 100644
--- a/interface-definitions/include/bgp/neighbor-afi-ipv4-vpn.xml.i
+++ b/interface-definitions/include/bgp/neighbor-afi-ipv4-vpn.xml.i
@@ -5,7 +5,7 @@
</properties>
<children>
#include <include/bgp/afi-ipv4-prefix-list.xml.i>
- #include <include/bgp/afi-common.xml.i>
+ #include <include/bgp/neighbor-afi-ipv4-ipv6-common.xml.i>
</children>
</node>
<!-- include end -->
diff --git a/interface-definitions/include/bgp/neighbor-afi-ipv6-labeled-unicast.xml.i b/interface-definitions/include/bgp/neighbor-afi-ipv6-labeled-unicast.xml.i
index f680b7357..995183571 100644
--- a/interface-definitions/include/bgp/neighbor-afi-ipv6-labeled-unicast.xml.i
+++ b/interface-definitions/include/bgp/neighbor-afi-ipv6-labeled-unicast.xml.i
@@ -14,7 +14,7 @@
</node>
#include <include/bgp/afi-ipv6-nexthop-local.xml.i>
#include <include/bgp/afi-ipv6-prefix-list.xml.i>
- #include <include/bgp/afi-common.xml.i>
+ #include <include/bgp/neighbor-afi-ipv4-ipv6-common.xml.i>
#include <include/bgp/afi-default-originate.xml.i>
</children>
</node>
diff --git a/interface-definitions/include/bgp/neighbor-afi-ipv6-multicast.xml.i b/interface-definitions/include/bgp/neighbor-afi-ipv6-multicast.xml.i
index 1f8db8361..bb713c313 100644
--- a/interface-definitions/include/bgp/neighbor-afi-ipv6-multicast.xml.i
+++ b/interface-definitions/include/bgp/neighbor-afi-ipv6-multicast.xml.i
@@ -6,7 +6,7 @@
<children>
#include <include/bgp/afi-ipv6-nexthop-local.xml.i>
#include <include/bgp/afi-ipv6-prefix-list.xml.i>
- #include <include/bgp/afi-common.xml.i>
+ #include <include/bgp/neighbor-afi-ipv4-ipv6-common.xml.i>
#include <include/bgp/afi-default-originate.xml.i>
</children>
</node>
diff --git a/interface-definitions/include/bgp/neighbor-afi-ipv6-unicast.xml.i b/interface-definitions/include/bgp/neighbor-afi-ipv6-unicast.xml.i
index f6b812c28..26a5e7090 100644
--- a/interface-definitions/include/bgp/neighbor-afi-ipv6-unicast.xml.i
+++ b/interface-definitions/include/bgp/neighbor-afi-ipv6-unicast.xml.i
@@ -14,7 +14,7 @@
</node>
#include <include/bgp/afi-ipv6-nexthop-local.xml.i>
#include <include/bgp/afi-ipv6-prefix-list.xml.i>
- #include <include/bgp/afi-common.xml.i>
+ #include <include/bgp/neighbor-afi-ipv4-ipv6-common.xml.i>
#include <include/bgp/afi-default-originate.xml.i>
</children>
</node>
diff --git a/interface-definitions/include/bgp/neighbor-afi-ipv6-vpn.xml.i b/interface-definitions/include/bgp/neighbor-afi-ipv6-vpn.xml.i
index c0df71cf3..5c6811986 100644
--- a/interface-definitions/include/bgp/neighbor-afi-ipv6-vpn.xml.i
+++ b/interface-definitions/include/bgp/neighbor-afi-ipv6-vpn.xml.i
@@ -6,7 +6,7 @@
<children>
#include <include/bgp/afi-ipv6-nexthop-local.xml.i>
#include <include/bgp/afi-ipv6-prefix-list.xml.i>
- #include <include/bgp/afi-common.xml.i>
+ #include <include/bgp/neighbor-afi-ipv4-ipv6-common.xml.i>
</children>
</node>
<!-- include end -->
diff --git a/interface-definitions/include/bgp/neighbor-bfd.xml.i b/interface-definitions/include/bgp/neighbor-bfd.xml.i
index d486bdd8a..fac2a1166 100644
--- a/interface-definitions/include/bgp/neighbor-bfd.xml.i
+++ b/interface-definitions/include/bgp/neighbor-bfd.xml.i
@@ -4,6 +4,7 @@
<help>Enable Bidirectional Forwarding Detection (BFD) support</help>
</properties>
<children>
+ #include <include/bfd/profile.xml.i>
<leafNode name="check-control-plane-failure">
<properties>
<help>Allow to write CBIT independence in BFD outgoing packets and read both C-BIT value of BFD and lookup BGP peer status</help>
diff --git a/interface-definitions/include/bgp/neighbor-shutdown.xml.i b/interface-definitions/include/bgp/neighbor-shutdown.xml.i
index 6d15899a6..acc7bc5a9 100644
--- a/interface-definitions/include/bgp/neighbor-shutdown.xml.i
+++ b/interface-definitions/include/bgp/neighbor-shutdown.xml.i
@@ -1,7 +1,7 @@
<!-- include start from bgp/neighbor-shutdown.xml.i -->
<leafNode name="shutdown">
<properties>
- <help>Administratively shut down this neighbor</help>
+ <help>Administratively shutdown this neighbor</help>
<valueless/>
</properties>
</leafNode>
diff --git a/interface-definitions/include/bgp/protocol-common-config.xml.i b/interface-definitions/include/bgp/protocol-common-config.xml.i
index 2dfae517e..8214d0779 100644
--- a/interface-definitions/include/bgp/protocol-common-config.xml.i
+++ b/interface-definitions/include/bgp/protocol-common-config.xml.i
@@ -1181,6 +1181,26 @@
</leafNode>
</children>
</node>
+ <node name="conditional-advertisement">
+ <properties>
+ <help>Conditional advertisement settings</help>
+ </properties>
+ <children>
+ <leafNode name="timer">
+ <properties>
+ <help>Set period to rescan BGP table to check if condition is met</help>
+ <valueHelp>
+ <format>u32:5-240</format>
+ <description>Period to rerun the conditional advertisement scanner process (default: 60)</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 5-240"/>
+ </constraint>
+ </properties>
+ <defaultValue>60</defaultValue>
+ </leafNode>
+ </children>
+ </node>
<node name="dampening">
<properties>
<help>Enable route-flap dampening</help>
@@ -1343,6 +1363,12 @@
<valueless/>
</properties>
</leafNode>
+ <leafNode name="fast-convergence">
+ <properties>
+ <help>Teardown sessions immediately whenever peer becomes unreachable</help>
+ <valueless/>
+ </properties>
+ </leafNode>
<node name="graceful-restart">
<properties>
<help>Graceful restart capability parameters</help>
@@ -1374,6 +1400,18 @@
<valueless/>
</properties>
</leafNode>
+ <leafNode name="minimum-holdtime">
+ <properties>
+ <help>BGP minimum holdtime</help>
+ <valueHelp>
+ <format>u32:1-65535</format>
+ <description>Minimum holdtime in seconds</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-65535"/>
+ </constraint>
+ </properties>
+ </leafNode>
<leafNode name="network-import-check">
<properties>
<help>Enable IGP route check for network statements</help>
@@ -1392,6 +1430,24 @@
<valueless/>
</properties>
</leafNode>
+ <leafNode name="reject-as-sets">
+ <properties>
+ <help>Reject routes with AS_SET or AS_CONFED_SET flag</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="shutdown">
+ <properties>
+ <help>Administrative shutdown of the BGP instance</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="suppress-fib-pending">
+ <properties>
+ <help>Advertise only routes that are programmed in kernel to peers</help>
+ <valueless/>
+ </properties>
+ </leafNode>
#include <include/router-id.xml.i>
</children>
</node>
@@ -1441,4 +1497,4 @@
#include <include/bgp/timers-keepalive.xml.i>
</children>
</node>
-<!-- include end --> \ No newline at end of file
+<!-- include end -->
diff --git a/interface-definitions/include/dhcp/ntp-server.xml.i b/interface-definitions/include/dhcp/ntp-server.xml.i
index 32d8207e5..4d7235aa1 100644
--- a/interface-definitions/include/dhcp/ntp-server.xml.i
+++ b/interface-definitions/include/dhcp/ntp-server.xml.i
@@ -1,15 +1,15 @@
<!-- include start from dhcp/ntp-server.xml.i -->
- <leafNode name="ntp-server">
- <properties>
- <help>IP address of NTP server</help>
- <valueHelp>
- <format>ipv4</format>
- <description>NTP server IPv4 address</description>
- </valueHelp>
- <constraint>
- <validator name="ipv4-address"/>
- </constraint>
- <multi/>
- </properties>
- </leafNode>
+<leafNode name="ntp-server">
+ <properties>
+ <help>IP address of NTP server</help>
+ <valueHelp>
+ <format>ipv4</format>
+ <description>NTP server IPv4 address</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ipv4-address"/>
+ </constraint>
+ <multi/>
+ </properties>
+</leafNode>
<!-- include end -->
diff --git a/interface-definitions/include/dns/time-to-live.xml.i b/interface-definitions/include/dns/time-to-live.xml.i
new file mode 100644
index 000000000..5c1a1472d
--- /dev/null
+++ b/interface-definitions/include/dns/time-to-live.xml.i
@@ -0,0 +1,15 @@
+<!-- include start from dns/time-to-live.xml.i -->
+<leafNode name="ttl">
+ <properties>
+ <help>Time-to-live (TTL)</help>
+ <valueHelp>
+ <format>u32:0-2147483647</format>
+ <description>TTL in seconds</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 0-2147483647"/>
+ </constraint>
+ </properties>
+ <defaultValue>300</defaultValue>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/include/interface/netns.xml.i b/interface-definitions/include/interface/netns.xml.i
new file mode 100644
index 000000000..39f9118fa
--- /dev/null
+++ b/interface-definitions/include/interface/netns.xml.i
@@ -0,0 +1,14 @@
+<!-- include start from interface/netns.xml.i -->
+<leafNode name="netns">
+ <properties>
+ <help>Network namespace name</help>
+ <valueHelp>
+ <format>text</format>
+ <description>Network namespace name</description>
+ </valueHelp>
+ <completionHelp>
+ <path>netns name</path>
+ </completionHelp>
+ </properties>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/include/interface/vrf.xml.i b/interface-definitions/include/interface/vrf.xml.i
index 5ad978a27..8605f56e8 100644
--- a/interface-definitions/include/interface/vrf.xml.i
+++ b/interface-definitions/include/interface/vrf.xml.i
@@ -3,7 +3,7 @@
<properties>
<help>VRF instance name</help>
<valueHelp>
- <format>text</format>
+ <format>txt</format>
<description>VRF instance name</description>
</valueHelp>
<completionHelp>
diff --git a/interface-definitions/include/isis/protocol-common-config.xml.i b/interface-definitions/include/isis/protocol-common-config.xml.i
index 84e2f7bb2..8ffa14a19 100644
--- a/interface-definitions/include/isis/protocol-common-config.xml.i
+++ b/interface-definitions/include/isis/protocol-common-config.xml.i
@@ -648,7 +648,7 @@
</completionHelp>
</properties>
<children>
- #include <include/bfd.xml.i>
+ #include <include/bfd/bfd.xml.i>
<leafNode name="circuit-type">
<properties>
<help>Configure circuit type for interface</help>
diff --git a/interface-definitions/include/listen-address-ipv4.xml.i b/interface-definitions/include/listen-address-ipv4.xml.i
index ee52cebe8..9cca297a0 100644
--- a/interface-definitions/include/listen-address-ipv4.xml.i
+++ b/interface-definitions/include/listen-address-ipv4.xml.i
@@ -1,13 +1,13 @@
<!-- include start from listen-address-ipv4.xml.i -->
<leafNode name="listen-address">
<properties>
- <help>Local IPv4 addresses for service to listen on</help>
+ <help>Local IPv4 addresses to listen on</help>
<completionHelp>
<script>${vyos_completion_dir}/list_local_ips.sh --ipv4</script>
</completionHelp>
<valueHelp>
<format>ipv4</format>
- <description>IP address to listen for incoming connections</description>
+ <description>IPv4 address to listen for incoming connections</description>
</valueHelp>
<multi/>
<constraint>
diff --git a/interface-definitions/include/listen-address-vrf.xml.i b/interface-definitions/include/listen-address-vrf.xml.i
index 7ec9eace4..8c2bdce70 100644
--- a/interface-definitions/include/listen-address-vrf.xml.i
+++ b/interface-definitions/include/listen-address-vrf.xml.i
@@ -1,13 +1,13 @@
<!-- include start from listen-address-vrf.xml.i -->
<tagNode name="listen-address">
<properties>
- <help>Local IP addresses for service to listen on</help>
+ <help>Local IP addresses to listen on</help>
<completionHelp>
<script>${vyos_completion_dir}/list_local_ips.sh --both</script>
</completionHelp>
<valueHelp>
<format>ipv4</format>
- <description>IP address to listen for incoming connections</description>
+ <description>IPv4 address to listen for incoming connections</description>
</valueHelp>
<valueHelp>
<format>ipv6</format>
diff --git a/interface-definitions/include/listen-address.xml.i b/interface-definitions/include/listen-address.xml.i
index 9b86851c7..19ddf0bb0 100644
--- a/interface-definitions/include/listen-address.xml.i
+++ b/interface-definitions/include/listen-address.xml.i
@@ -1,13 +1,13 @@
<!-- include start from listen-address.xml.i -->
<leafNode name="listen-address">
<properties>
- <help>Local IP addresses for service to listen on</help>
+ <help>Local IP addresses to listen on</help>
<completionHelp>
<script>${vyos_completion_dir}/list_local_ips.sh --both</script>
</completionHelp>
<valueHelp>
<format>ipv4</format>
- <description>IP address to listen for incoming connections</description>
+ <description>IPv4 address to listen for incoming connections</description>
</valueHelp>
<valueHelp>
<format>ipv6</format>
diff --git a/interface-definitions/include/ospf/auto-cost.xml.i b/interface-definitions/include/ospf/auto-cost.xml.i
new file mode 100644
index 000000000..3e6cc8232
--- /dev/null
+++ b/interface-definitions/include/ospf/auto-cost.xml.i
@@ -0,0 +1,22 @@
+<!-- include start from ospf/auto-cost.xml.i -->
+<node name="auto-cost">
+ <properties>
+ <help>Calculate interface cost according to bandwidth</help>
+ </properties>
+ <children>
+ <leafNode name="reference-bandwidth">
+ <properties>
+ <help>Reference bandwidth method to assign cost (default: 100)</help>
+ <valueHelp>
+ <format>u32:1-4294967</format>
+ <description>Reference bandwidth cost in Mbits/sec</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-4294967"/>
+ </constraint>
+ </properties>
+ <defaultValue>100</defaultValue>
+ </leafNode>
+ </children>
+</node>
+<!-- include end -->
diff --git a/interface-definitions/include/ospf/default-information.xml.i b/interface-definitions/include/ospf/default-information.xml.i
new file mode 100644
index 000000000..50cda54a4
--- /dev/null
+++ b/interface-definitions/include/ospf/default-information.xml.i
@@ -0,0 +1,25 @@
+<!-- include start from ospf/intervals.xml.i -->
+<node name="default-information">
+ <properties>
+ <help>Default route advertisment settings</help>
+ </properties>
+ <children>
+ <node name="originate">
+ <properties>
+ <help>Distribute a default route</help>
+ </properties>
+ <children>
+ <leafNode name="always">
+ <properties>
+ <help>Always advertise a default route</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ #include <include/ospf/metric.xml.i>
+ #include <include/ospf/metric-type.xml.i>
+ #include <include/route-map.xml.i>
+ </children>
+ </node>
+ </children>
+</node>
+<!-- include end -->
diff --git a/interface-definitions/include/ospf/interface-common.xml.i b/interface-definitions/include/ospf/interface-common.xml.i
index 4b0aef380..738651594 100644
--- a/interface-definitions/include/ospf/interface-common.xml.i
+++ b/interface-definitions/include/ospf/interface-common.xml.i
@@ -1,5 +1,5 @@
<!-- include start from ospf/interface-common.xml.i -->
-#include <include/bfd.xml.i>
+#include <include/bfd/bfd.xml.i>
<leafNode name="cost">
<properties>
<help>Interface cost</help>
diff --git a/interface-definitions/include/ospf/protocol-common-config.xml.i b/interface-definitions/include/ospf/protocol-common-config.xml.i
index ac165a157..688e78034 100644
--- a/interface-definitions/include/ospf/protocol-common-config.xml.i
+++ b/interface-definitions/include/ospf/protocol-common-config.xml.i
@@ -275,49 +275,8 @@
</tagNode>
</children>
</tagNode>
-<node name="auto-cost">
- <properties>
- <help>Calculate OSPF interface cost according to bandwidth (default: 100)</help>
- </properties>
- <children>
- <leafNode name="reference-bandwidth">
- <properties>
- <help>Reference bandwidth method to assign OSPF cost</help>
- <valueHelp>
- <format>u32:1-4294967</format>
- <description>Reference bandwidth cost in Mbits/sec</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--range 1-4294967"/>
- </constraint>
- </properties>
- <defaultValue>100</defaultValue>
- </leafNode>
- </children>
-</node>
-<node name="default-information">
- <properties>
- <help>Default route advertisment settings</help>
- </properties>
- <children>
- <node name="originate">
- <properties>
- <help>Distribute a default route</help>
- </properties>
- <children>
- <leafNode name="always">
- <properties>
- <help>Always advertise a default route</help>
- <valueless/>
- </properties>
- </leafNode>
- #include <include/ospf/metric.xml.i>
- #include <include/ospf/metric-type.xml.i>
- #include <include/route-map.xml.i>
- </children>
- </node>
- </children>
-</node>
+#include <include/ospf/auto-cost.xml.i>
+#include <include/ospf/default-information.xml.i>
<leafNode name="default-metric">
<properties>
<help>Metric of redistributed routes</help>
diff --git a/interface-definitions/include/ospfv3/no-summary.xml.i b/interface-definitions/include/ospfv3/no-summary.xml.i
new file mode 100644
index 000000000..a6afda3e0
--- /dev/null
+++ b/interface-definitions/include/ospfv3/no-summary.xml.i
@@ -0,0 +1,8 @@
+<!-- include start from ospfv3/no-summary.xml.i -->
+<leafNode name="no-summary">
+ <properties>
+ <help>Do not inject inter-area routes into the stub</help>
+ <valueless/>
+ </properties>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/include/ospfv3/protocol-common-config.xml.i b/interface-definitions/include/ospfv3/protocol-common-config.xml.i
index a93939a34..5d08debda 100644
--- a/interface-definitions/include/ospfv3/protocol-common-config.xml.i
+++ b/interface-definitions/include/ospfv3/protocol-common-config.xml.i
@@ -21,17 +21,26 @@
<help>OSPFv3 Area type</help>
</properties>
<children>
- <node name="stub">
+ <node name="nssa">
<properties>
- <help>Stub OSPFv3 area</help>
+ <help>NSSA OSPFv3 area</help>
</properties>
<children>
- <leafNode name="no-summary">
+ <leafNode name="default-information-originate">
<properties>
- <help>Do not inject inter-area routes into the stub</help>
+ <help>Originate Type 7 default into NSSA area</help>
<valueless/>
</properties>
</leafNode>
+ #include <include/ospfv3/no-summary.xml.i>
+ </children>
+ </node>
+ <node name="stub">
+ <properties>
+ <help>Stub OSPFv3 area</help>
+ </properties>
+ <children>
+ #include <include/ospfv3/no-summary.xml.i>
</children>
</node>
</children>
@@ -80,6 +89,8 @@
</tagNode>
</children>
</tagNode>
+#include <include/ospf/auto-cost.xml.i>
+#include <include/ospf/default-information.xml.i>
<node name="distance">
<properties>
<help>Administrative distance</help>
diff --git a/interface-definitions/include/snmp/access-mode.xml.i b/interface-definitions/include/snmp/access-mode.xml.i
new file mode 100644
index 000000000..1fce2364e
--- /dev/null
+++ b/interface-definitions/include/snmp/access-mode.xml.i
@@ -0,0 +1,23 @@
+<!-- include start from snmp/access-mode.xml.i -->
+<leafNode name="mode">
+ <properties>
+ <help>Define access permission</help>
+ <completionHelp>
+ <list>ro rw</list>
+ </completionHelp>
+ <valueHelp>
+ <format>ro</format>
+ <description>Read-Only (default)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>rw</format>
+ <description>read write</description>
+ </valueHelp>
+ <constraint>
+ <regex>^(ro|rw)$</regex>
+ </constraint>
+ <constraintErrorMessage>Authorization type must be either 'rw' or 'ro'</constraintErrorMessage>
+ </properties>
+ <defaultValue>ro</defaultValue>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/include/snmp/authentication-type.xml.i b/interface-definitions/include/snmp/authentication-type.xml.i
new file mode 100644
index 000000000..2a545864a
--- /dev/null
+++ b/interface-definitions/include/snmp/authentication-type.xml.i
@@ -0,0 +1,22 @@
+<!-- include start from snmp/authentication-type.xml.i -->
+<leafNode name="type">
+ <properties>
+ <help>Define used protocol</help>
+ <completionHelp>
+ <list>md5 sha</list>
+ </completionHelp>
+ <valueHelp>
+ <format>md5</format>
+ <description>Message Digest 5 (default)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>sha</format>
+ <description>Secure Hash Algorithm</description>
+ </valueHelp>
+ <constraint>
+ <regex>^(md5|sha)$</regex>
+ </constraint>
+ </properties>
+ <defaultValue>md5</defaultValue>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/include/snmp/privacy-type.xml.i b/interface-definitions/include/snmp/privacy-type.xml.i
new file mode 100644
index 000000000..47a1e632e
--- /dev/null
+++ b/interface-definitions/include/snmp/privacy-type.xml.i
@@ -0,0 +1,22 @@
+<!-- include start from snmp/privacy-type.xml.i -->
+<leafNode name="type">
+ <properties>
+ <help>Defines the protocol for privacy</help>
+ <completionHelp>
+ <list>des aes</list>
+ </completionHelp>
+ <valueHelp>
+ <format>des</format>
+ <description>Data Encryption Standard (default)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>aes</format>
+ <description>Advanced Encryption Standard</description>
+ </valueHelp>
+ <constraint>
+ <regex>^(des|aes)$</regex>
+ </constraint>
+ </properties>
+ <defaultValue>des</defaultValue>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/include/snmp/protocol.xml.i b/interface-definitions/include/snmp/protocol.xml.i
new file mode 100644
index 000000000..335736724
--- /dev/null
+++ b/interface-definitions/include/snmp/protocol.xml.i
@@ -0,0 +1,22 @@
+<!-- include start from snmp/protocol.xml.i -->
+<leafNode name="protocol">
+ <properties>
+ <help>Protocol to be used (TCP/UDP)</help>
+ <completionHelp>
+ <list>udp tcp</list>
+ </completionHelp>
+ <valueHelp>
+ <format>udp</format>
+ <description>Listen protocol UDP (default)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>tcp</format>
+ <description>Listen protocol TCP</description>
+ </valueHelp>
+ <constraint>
+ <regex>^(udp|tcp)$</regex>
+ </constraint>
+ </properties>
+ <defaultValue>udp</defaultValue>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/interfaces-dummy.xml.in b/interface-definitions/interfaces-dummy.xml.in
index 1231b1492..3bca8b950 100644
--- a/interface-definitions/interfaces-dummy.xml.in
+++ b/interface-definitions/interfaces-dummy.xml.in
@@ -29,6 +29,7 @@
#include <include/interface/source-validation.xml.i>
</children>
</node>
+ #include <include/interface/netns.xml.i>
#include <include/interface/vrf.xml.i>
</children>
</tagNode>
diff --git a/interface-definitions/interfaces-tunnel.xml.in b/interface-definitions/interfaces-tunnel.xml.in
index e08b2fdab..fd69fd177 100644
--- a/interface-definitions/interfaces-tunnel.xml.in
+++ b/interface-definitions/interfaces-tunnel.xml.in
@@ -68,39 +68,39 @@
</valueHelp>
<valueHelp>
<format>gre</format>
- <description>Generic Routing Encapsulation</description>
+ <description>Generic Routing Encapsulation (network layer)</description>
</valueHelp>
<valueHelp>
<format>gretap</format>
- <description>Generic Routing Encapsulation (virtual L2 tunnel)</description>
+ <description>Generic Routing Encapsulation (datalink layer)</description>
</valueHelp>
<valueHelp>
<format>ip6erspan</format>
- <description>Encapsulated Remote Switched Port Analyzer over IPv6 network</description>
+ <description>Encapsulated Remote Switched Port Analyzer over IPv6</description>
</valueHelp>
<valueHelp>
<format>ip6gre</format>
- <description>GRE over IPv6 network</description>
+ <description>GRE over IPv6 (network layer)</description>
</valueHelp>
<valueHelp>
<format>ip6gretap</format>
- <description>Generic Routing Encapsulation over IPv6 (virtual L2 tunnel)</description>
+ <description>GRE over IPv6 (datalink layer)</description>
</valueHelp>
<valueHelp>
<format>ip6ip6</format>
- <description>IP6 in IP6 encapsulation</description>
+ <description>IPv6 in IPv6 encapsulation</description>
</valueHelp>
<valueHelp>
<format>ipip</format>
- <description>IP in IP encapsulation</description>
+ <description>IPv4 in IPv4 encapsulation</description>
</valueHelp>
<valueHelp>
<format>ipip6</format>
- <description>IP in IP6 encapsulation</description>
+ <description>IPv4 in IP6 encapsulation</description>
</valueHelp>
<valueHelp>
<format>sit</format>
- <description>Simple Internet Transition encapsulation</description>
+ <description>Simple Internet Transition (IPv6 in IPv4)</description>
</valueHelp>
<constraint>
<regex>^(erspan|gre|gretap|ip6erspan|ip6gre|ip6gretap|ip6ip6|ipip|ipip6|sit)$</regex>
@@ -116,11 +116,11 @@
</completionHelp>
<valueHelp>
<format>enable</format>
- <description>Enable Multicast</description>
+ <description>Enable multicast</description>
</valueHelp>
<valueHelp>
<format>disable</format>
- <description>Disable Multicast (default)</description>
+ <description>Disable multicast (default)</description>
</valueHelp>
<constraint>
<regex>^(enable|disable)$</regex>
@@ -135,22 +135,22 @@
<children>
<node name="erspan">
<properties>
- <help>ERSPAN Tunnel parameters</help>
+ <help>ERSPAN tunnel parameters</help>
</properties>
<children>
<leafNode name="direction">
<properties>
- <help>Specifies mirrored traffic direction</help>
+ <help>Mirrored traffic direction</help>
<completionHelp>
<list>ingress egress</list>
</completionHelp>
<valueHelp>
<format>ingress</format>
- <description>Mirror ingress direction</description>
+ <description>Mirror ingress traffic</description>
</valueHelp>
<valueHelp>
<format>egress</format>
- <description>Mirror egress direction</description>
+ <description>Mirror egress traffic</description>
</valueHelp>
<constraint>
<regex>^(ingress|egress)$</regex>
@@ -159,10 +159,10 @@
</leafNode>
<leafNode name="hw-id">
<properties>
- <help>Unique identifier of ERSPAN engine within a system</help>
+ <help>Unique identifier of an ERSPAN engine within a system</help>
<valueHelp>
<format>u32:0-1048575</format>
- <description>Unique identifier of ERSPAN engine</description>
+ <description>Unique identifier of an ERSPAN engine</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 0-1048575"/>
@@ -171,7 +171,7 @@
</leafNode>
<leafNode name="index">
<properties>
- <help>Specifify ERSPAN version 1 index field</help>
+ <help>ERSPAN version 1 index field</help>
<valueHelp>
<format>u32:0-63</format>
<description>Platform-depedent field for specifying port number and direction</description>
@@ -205,18 +205,18 @@
</node>
<node name="ip">
<properties>
- <help>IPv4 specific tunnel parameters</help>
+ <help>IPv4-specific tunnel parameters</help>
</properties>
<children>
<leafNode name="no-pmtu-discovery">
<properties>
- <help>Disable Path MTU Discovery on this tunnel</help>
+ <help>Disable path MTU discovery</help>
<valueless/>
</properties>
</leafNode>
<leafNode name="ignore-df">
<properties>
- <help>Enable IPv4 DF suppression on this tunnel</help>
+ <help>Ignore the DF (don't fragment) bit</help>
<valueless/>
</properties>
</leafNode>
@@ -230,7 +230,7 @@
</node>
<node name="ipv6">
<properties>
- <help>IPv6 specific tunnel parameters</help>
+ <help>IPv6-specific tunnel parameters</help>
</properties>
<children>
<leafNode name="encaplimit">
@@ -241,11 +241,11 @@
</completionHelp>
<valueHelp>
<format>u32:0-255</format>
- <description>Encaplimit (default: 4)</description>
+ <description>Encapsulation limit (default: 4)</description>
</valueHelp>
<valueHelp>
<format>none</format>
- <description>Encaplimit disabled</description>
+ <description>Disable encapsulation limit</description>
</valueHelp>
<constraint>
<regex>^(none)$</regex>
@@ -261,12 +261,12 @@
<help>Hoplimit</help>
<valueHelp>
<format>u32:0-255</format>
- <description>Hoplimit (default 64)</description>
+ <description>Hop limit (default: 64)</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 0-255"/>
</constraint>
- <constraintErrorMessage>hoplimit must be between 0-255</constraintErrorMessage>
+ <constraintErrorMessage>hop limit must be between 0-255</constraintErrorMessage>
</properties>
<defaultValue>64</defaultValue>
</leafNode>
diff --git a/interface-definitions/interfaces-vxlan.xml.in b/interface-definitions/interfaces-vxlan.xml.in
index 0c13dd4d3..4c3c3ac71 100644
--- a/interface-definitions/interfaces-vxlan.xml.in
+++ b/interface-definitions/interfaces-vxlan.xml.in
@@ -19,6 +19,18 @@
#include <include/interface/address-ipv4-ipv6.xml.i>
#include <include/interface/description.xml.i>
#include <include/interface/disable.xml.i>
+ <leafNode name="external">
+ <properties>
+ <help>Use external control plane</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="gpe">
+ <properties>
+ <help>Enable Generic Protocol extension (VXLAN-GPE)</help>
+ <valueless/>
+ </properties>
+ </leafNode>
<leafNode name="group">
<properties>
<help>Multicast group address for VXLAN interface</help>
diff --git a/interface-definitions/nat.xml.in b/interface-definitions/nat.xml.in
index 3cf3ba6aa..f79680947 100644
--- a/interface-definitions/nat.xml.in
+++ b/interface-definitions/nat.xml.in
@@ -18,6 +18,7 @@
<properties>
<help>Inbound interface of NAT traffic</help>
<completionHelp>
+ <list>any</list>
<script>${vyos_completion_dir}/list_interfaces.py</script>
</completionHelp>
</properties>
diff --git a/interface-definitions/netns.xml.in b/interface-definitions/netns.xml.in
new file mode 100644
index 000000000..80de805fb
--- /dev/null
+++ b/interface-definitions/netns.xml.in
@@ -0,0 +1,23 @@
+<?xml version="1.0"?>
+<interfaceDefinition>
+ <node name="netns" owner="${vyos_conf_scripts_dir}/netns.py">
+ <properties>
+ <help>Network namespace</help>
+ <priority>299</priority>
+ </properties>
+ <children>
+ <tagNode name="name">
+ <properties>
+ <help>Network namespace name</help>
+ <constraint>
+ <regex>^[a-zA-Z0-9-_]{1,100}</regex>
+ </constraint>
+ <constraintErrorMessage>Netns name must be alphanumeric and can contain hyphens and underscores.</constraintErrorMessage>
+ </properties>
+ <children>
+ #include <include/interface/description.xml.i>
+ </children>
+ </tagNode>
+ </children>
+ </node>
+</interfaceDefinition>
diff --git a/interface-definitions/protocols-bfd.xml.in b/interface-definitions/protocols-bfd.xml.in
index d5a968001..a9957d884 100644
--- a/interface-definitions/protocols-bfd.xml.in
+++ b/interface-definitions/protocols-bfd.xml.in
@@ -26,18 +26,7 @@
</constraint>
</properties>
<children>
- <leafNode name="profile">
- <properties>
- <help>Use settings from BFD profile</help>
- <completionHelp>
- <path>protocols bfd profile</path>
- </completionHelp>
- <valueHelp>
- <format>txt</format>
- <description>BFD profile name</description>
- </valueHelp>
- </properties>
- </leafNode>
+ #include <include/bfd/profile.xml.i>
<node name="source">
<properties>
<help>Bind listener to specified interface/address, mandatory for IPv6</help>
@@ -66,7 +55,7 @@
</leafNode>
</children>
</node>
- #include <include/bfd-common.xml.i>
+ #include <include/bfd/common.xml.i>
<leafNode name="multihop">
<properties>
<help>Allow this BFD peer to not be directly connected</help>
@@ -88,7 +77,7 @@
</constraint>
</properties>
<children>
- #include <include/bfd-common.xml.i>
+ #include <include/bfd/common.xml.i>
</children>
</tagNode>
</children>
diff --git a/interface-definitions/service_pppoe-server.xml.in b/interface-definitions/service_pppoe-server.xml.in
index 188aed6c4..97952d882 100644
--- a/interface-definitions/service_pppoe-server.xml.in
+++ b/interface-definitions/service_pppoe-server.xml.in
@@ -70,19 +70,27 @@
<children>
<leafNode name="vlan-id">
<properties>
- <help>VLAN monitor for the automatic creation of vlans (user per vlan)</help>
+ <help>VLAN monitor for the automatic creation of single vlan</help>
+ <valueHelp>
+ <format>u32:1-4094</format>
+ <description>VLAN monitor for the automatic creation of single vlan</description>
+ </valueHelp>
<constraint>
- <validator name="numeric" argument="--range 1-4096"/>
+ <validator name="numeric" argument="--range 1-4094"/>
</constraint>
- <constraintErrorMessage>VLAN ID needs to be between 1 and 4096</constraintErrorMessage>
+ <constraintErrorMessage>VLAN ID needs to be between 1 and 4094</constraintErrorMessage>
<multi/>
</properties>
</leafNode>
<leafNode name="vlan-range">
<properties>
- <help>VLAN monitor for the automatic creation of vlans (user per vlan)</help>
+ <help>VLAN monitor for the automatic creation of vlans range</help>
+ <valueHelp>
+ <format>start-end</format>
+ <description>VLAN monitor range for the automatic creation of vlans (e.g. 1-4094)</description>
+ </valueHelp>
<constraint>
- <regex>(409[0-6]|40[0-8][0-9]|[1-3][0-9]{3}|[1-9][0-9]{0,2})-(409[0-6]|40[0-8][0-9]|[1-3][0-9]{3}|[1-9][0-9]{0,2})</regex>
+ <validator name="range" argument="--min=1 --max=4094"/>
</constraint>
<multi/>
</properties>
diff --git a/interface-definitions/snmp.xml.in b/interface-definitions/snmp.xml.in
index 949536fe7..67d3aef9a 100644
--- a/interface-definitions/snmp.xml.in
+++ b/interface-definitions/snmp.xml.in
@@ -20,23 +20,24 @@
<children>
<leafNode name="authorization">
<properties>
- <help>Authorization type (default: 'ro')</help>
+ <help>Authorization type</help>
<completionHelp>
<list>ro rw</list>
</completionHelp>
<valueHelp>
<format>ro</format>
- <description>read only</description>
+ <description>Read-Only (default)</description>
</valueHelp>
<valueHelp>
<format>rw</format>
- <description>read write</description>
+ <description>Read-Write</description>
</valueHelp>
<constraint>
<regex>^(ro|rw)$</regex>
</constraint>
<constraintErrorMessage>Authorization type must be either 'rw' or 'ro'</constraintErrorMessage>
</properties>
+ <defaultValue>ro</defaultValue>
</leafNode>
<leafNode name="client">
<properties>
@@ -105,18 +106,9 @@
</constraint>
</properties>
<children>
+ #include <include/port-number.xml.i>
<leafNode name="port">
- <properties>
- <help>Port for SNMP service (default: '161')</help>
- <valueHelp>
- <format>u32:1-65535</format>
- <description>Numeric IP port</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--range 1-65535"/>
- </constraint>
- <constraintErrorMessage>Port number must be in range 1 to 65535</constraintErrorMessage>
- </properties>
+ <defaultValue>161</defaultValue>
</leafNode>
</children>
</tagNode>
@@ -131,50 +123,27 @@
</leafNode>
<leafNode name="oid-enable">
<properties>
- <help>Enable specific oids</help>
- <valueHelp>
- <format>txt</format>
- <description>Enable specific oids</description>
- </valueHelp>
- <valueHelp>
- <format>route-table</format>
- <description>Enable route table oids (ipCidrRouteTable inetCidrRouteTable)</description>
- </valueHelp>
+ <help>Enable specific OIDs</help>
<completionHelp>
<list>route-table</list>
</completionHelp>
- <constraint>
- <regex>^(route-table)$</regex>
- </constraint>
- <constraintErrorMessage>Oid must be 'route-table'</constraintErrorMessage>
- </properties>
- </leafNode>
- <leafNode name="protocol">
- <properties>
- <help>Listen protocol for SNMP</help>
- <completionHelp>
- <list>udp tcp</list>
- </completionHelp>
<valueHelp>
- <format>udp</format>
- <description>Listen protocol UDP (default)</description>
- </valueHelp>
- <valueHelp>
- <format>tcp</format>
- <description>Listen protocol TCP</description>
+ <format>route-table</format>
+ <description>Enable routing table OIDs (ipCidrRouteTable inetCidrRouteTable)</description>
</valueHelp>
<constraint>
- <regex>^(udp|tcp)$</regex>
+ <regex>^(route-table)$</regex>
</constraint>
+ <constraintErrorMessage>OID must be 'route-table'</constraintErrorMessage>
</properties>
- <defaultValue>udp</defaultValue>
</leafNode>
+ #include <include/snmp/protocol.xml.i>
<leafNode name="smux-peer">
<properties>
<help>Register a subtree for SMUX-based processing</help>
<valueHelp>
- <format>oid</format>
- <description>Object Identifier</description>
+ <format>txt</format>
+ <description>SNMP Object Identifier</description>
</valueHelp>
<multi/>
</properties>
@@ -218,18 +187,9 @@
<help>Community used when sending trap information</help>
</properties>
</leafNode>
+ #include <include/port-number.xml.i>
<leafNode name="port">
- <properties>
- <help>Destination port used for trap notification</help>
- <valueHelp>
- <format>u32:1-65535</format>
- <description>Numeric IP port</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--range 1-65535"/>
- </constraint>
- <constraintErrorMessage>Port number must be in range 1 to 65535</constraintErrorMessage>
- </properties>
+ <defaultValue>162</defaultValue>
</leafNode>
</children>
</tagNode>
@@ -246,32 +206,14 @@
</constraint>
<constraintErrorMessage>ID must contain an even number (from 2 to 36) of hex digits</constraintErrorMessage>
</properties>
+ <defaultValue></defaultValue>
</leafNode>
<tagNode name="group">
<properties>
<help>Specifies the group with name groupname</help>
</properties>
<children>
- <leafNode name="mode">
- <properties>
- <help>Define group access permission (default: 'ro')</help>
- <completionHelp>
- <list>ro rw</list>
- </completionHelp>
- <valueHelp>
- <format>ro</format>
- <description>read only</description>
- </valueHelp>
- <valueHelp>
- <format>rw</format>
- <description>read write</description>
- </valueHelp>
- <constraint>
- <regex>^(ro|rw)$</regex>
- </constraint>
- <constraintErrorMessage>Authorization type must be either 'rw' or 'ro'</constraintErrorMessage>
- </properties>
- </leafNode>
+ #include <include/snmp/access-mode.xml.i>
<leafNode name="seclevel">
<properties>
<help>Security levels</help>
@@ -284,7 +226,7 @@
</valueHelp>
<valueHelp>
<format>auth</format>
- <description>Messages are authenticated but not encrypted (authNoPriv)</description>
+ <description>Messages are authenticated but not encrypted (authNoPriv, default)</description>
</valueHelp>
<valueHelp>
<format>priv</format>
@@ -294,6 +236,7 @@
<regex>^(noauth|auth|priv)$</regex>
</constraint>
</properties>
+ <defaultValue>auth</defaultValue>
</leafNode>
<leafNode name="view">
<properties>
@@ -345,39 +288,12 @@
<constraintErrorMessage>Key must contain 8 or more characters</constraintErrorMessage>
</properties>
</leafNode>
- <leafNode name="type">
- <properties>
- <help>Defines the protocol used for authentication (default: 'md5')</help>
- <completionHelp>
- <list>md5 sha</list>
- </completionHelp>
- <valueHelp>
- <format>md5</format>
- <description>Message Digest 5</description>
- </valueHelp>
- <valueHelp>
- <format>sha</format>
- <description>Secure Hash Algorithm</description>
- </valueHelp>
- <constraint>
- <regex>^(md5|sha)$</regex>
- </constraint>
- </properties>
- </leafNode>
+ #include <include/snmp/authentication-type.xml.i>
</children>
</node>
+ #include <include/port-number.xml.i>
<leafNode name="port">
- <properties>
- <help>Specifies TCP/UDP port of destination SNMP traps/informs (default: '162')</help>
- <valueHelp>
- <format>u32:1-65535</format>
- <description>Numeric IP port</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--range 1-65535"/>
- </constraint>
- <constraintErrorMessage>Port number must be in range 1 to 65535</constraintErrorMessage>
- </properties>
+ <defaultValue>162</defaultValue>
</leafNode>
<node name="privacy">
<properties>
@@ -402,54 +318,18 @@
<constraintErrorMessage>Key must contain 8 or more characters</constraintErrorMessage>
</properties>
</leafNode>
- <leafNode name="type">
- <properties>
- <help>Defines the protocol for privacy (default: 'des')</help>
- <completionHelp>
- <list>des aes</list>
- </completionHelp>
- <valueHelp>
- <format>des</format>
- <description>Data Encryption Standard</description>
- </valueHelp>
- <valueHelp>
- <format>aes</format>
- <description>Advanced Encryption Standard</description>
- </valueHelp>
- <constraint>
- <regex>^(des|aes)$</regex>
- </constraint>
- </properties>
- </leafNode>
+ #include <include/snmp/privacy-type.xml.i>
</children>
</node>
- <leafNode name="protocol">
- <properties>
- <help>Defines protocol for notification between TCP and UDP</help>
- <completionHelp>
- <list>tcp udp</list>
- </completionHelp>
- <valueHelp>
- <format>tcp</format>
- <description>Use Transmission Control Protocol for notifications</description>
- </valueHelp>
- <valueHelp>
- <format>udp</format>
- <description>Use User Datagram Protocol for notifications</description>
- </valueHelp>
- <constraint>
- <regex>^(tcp|udp)$</regex>
- </constraint>
- </properties>
- </leafNode>
+ #include <include/snmp/protocol.xml.i>
<leafNode name="type">
<properties>
- <help>Specifies the type of notification between inform and trap (default: 'inform')</help>
+ <help>Specifies the type of notification between inform and trap</help>
<completionHelp>
<list>inform trap</list>
</completionHelp>
<valueHelp>
- <format>inform</format>
+ <format>inform (default)</format>
<description>Use INFORM</description>
</valueHelp>
<valueHelp>
@@ -460,6 +340,7 @@
<regex>^(inform|trap)$</regex>
</constraint>
</properties>
+ <defaultValue>inform</defaultValue>
</leafNode>
<leafNode name="user">
<properties>
@@ -503,25 +384,7 @@
<constraintErrorMessage>Key must contain 8 or more characters</constraintErrorMessage>
</properties>
</leafNode>
- <leafNode name="type">
- <properties>
- <help>Defines the protocol used for authentication (default: 'md5')</help>
- <completionHelp>
- <list>md5 sha</list>
- </completionHelp>
- <valueHelp>
- <format>md5</format>
- <description>Message Digest 5</description>
- </valueHelp>
- <valueHelp>
- <format>sha</format>
- <description>Secure Hash Algorithm</description>
- </valueHelp>
- <constraint>
- <regex>^(md5|sha)$</regex>
- </constraint>
- </properties>
- </leafNode>
+ #include <include/snmp/authentication-type.xml.i>
</children>
</node>
<leafNode name="group">
@@ -532,26 +395,7 @@
</completionHelp>
</properties>
</leafNode>
- <leafNode name="mode">
- <properties>
- <help>Define users access permission (default: 'ro')</help>
- <completionHelp>
- <list>ro rw</list>
- </completionHelp>
- <valueHelp>
- <format>ro</format>
- <description>read only</description>
- </valueHelp>
- <valueHelp>
- <format>rw</format>
- <description>read write</description>
- </valueHelp>
- <constraint>
- <regex>^(ro|rw)$</regex>
- </constraint>
- <constraintErrorMessage>Authorization type must be either 'rw' or 'ro'</constraintErrorMessage>
- </properties>
- </leafNode>
+ #include <include/snmp/access-mode.xml.i>
<node name="privacy">
<properties>
<help>Defines the privacy</help>
@@ -575,25 +419,7 @@
<constraintErrorMessage>Key must contain 8 or more characters</constraintErrorMessage>
</properties>
</leafNode>
- <leafNode name="type">
- <properties>
- <help>Defines the protocol for privacy (default: 'des')</help>
- <completionHelp>
- <list>des aes</list>
- </completionHelp>
- <valueHelp>
- <format>des</format>
- <description>Data Encryption Standard</description>
- </valueHelp>
- <valueHelp>
- <format>aes</format>
- <description>Advanced Encryption Standard</description>
- </valueHelp>
- <constraint>
- <regex>^(des|aes)$</regex>
- </constraint>
- </properties>
- </leafNode>
+ #include <include/snmp/privacy-type.xml.i>
</children>
</node>
</children>
diff --git a/interface-definitions/system-login-banner.xml.in b/interface-definitions/system-login-banner.xml.in
index c4bb14bd6..bdd0ad96a 100644
--- a/interface-definitions/system-login-banner.xml.in
+++ b/interface-definitions/system-login-banner.xml.in
@@ -15,12 +15,12 @@
<children>
<leafNode name="post-login">
<properties>
- <help>System loging banner post-login</help>
+ <help>A system banner after the user logs in </help>
</properties>
</leafNode>
<leafNode name="pre-login">
<properties>
- <help>System loging banner pre-login</help>
+ <help>A system banner before the user logs in</help>
</properties>
</leafNode>
</children>
diff --git a/interface-definitions/system-logs.xml.in b/interface-definitions/system-logs.xml.in
new file mode 100644
index 000000000..8b6c7c399
--- /dev/null
+++ b/interface-definitions/system-logs.xml.in
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interfaceDefinition>
+ <node name="system">
+ <children>
+ <node name="logs" owner="${vyos_conf_scripts_dir}/system-logs.py">
+ <properties>
+ <help>Logging options</help>
+ <priority>9999</priority>
+ </properties>
+ <children>
+ <node name="logrotate">
+ <properties>
+ <help>Logrotate options</help>
+ </properties>
+ <children>
+ <node name="atop">
+ <properties>
+ <help>Atop logs options (system resources usage)</help>
+ </properties>
+ <children>
+ <leafNode name="max-size">
+ <properties>
+ <help>Size of a single log file that triggers rotation</help>
+ <valueHelp>
+ <format>u32:1-1024</format>
+ <description>Size in MB (default: 10)</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-1024" />
+ </constraint>
+ <constraintErrorMessage>The size must be between 1 and 1024 MB</constraintErrorMessage>
+ </properties>
+ <defaultValue>10</defaultValue>
+ </leafNode>
+ <leafNode name="rotate">
+ <properties>
+ <help>Count of rotations before old logs will be deleted</help>
+ <valueHelp>
+ <format>u32:1-100</format>
+ <description>Rotations (default: 10)</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-100" />
+ </constraint>
+ <constraintErrorMessage>The count must be between 1 and 100</constraintErrorMessage>
+ </properties>
+ <defaultValue>10</defaultValue>
+ </leafNode>
+ </children>
+ </node>
+ <node name="messages">
+ <properties>
+ <help>The /var/log/messages file rotation</help>
+ </properties>
+ <children>
+ <leafNode name="max-size">
+ <properties>
+ <help>Size of a single log file that triggers rotation</help>
+ <valueHelp>
+ <format>u32:1-1024</format>
+ <description>Size in MB (default: 1)</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-1024" />
+ </constraint>
+ <constraintErrorMessage>The size must be between 1 and 1024 MB</constraintErrorMessage>
+ </properties>
+ <defaultValue>1</defaultValue>
+ </leafNode>
+ <leafNode name="rotate">
+ <properties>
+ <help>Count of rotations before old logs will be deleted</help>
+ <valueHelp>
+ <format>u32:1-100</format>
+ <description>Rotations (default: 10)</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-100" />
+ </constraint>
+ <constraintErrorMessage>The count must be between 1 and 100</constraintErrorMessage>
+ </properties>
+ <defaultValue>10</defaultValue>
+ </leafNode>
+ </children>
+ </node>
+ </children>
+ </node>
+ </children>
+ </node>
+ </children>
+ </node>
+</interfaceDefinition>
diff --git a/interface-definitions/vpn_ipsec.xml.in b/interface-definitions/vpn_ipsec.xml.in
index e82249d44..0c2205410 100644
--- a/interface-definitions/vpn_ipsec.xml.in
+++ b/interface-definitions/vpn_ipsec.xml.in
@@ -13,13 +13,13 @@
<children>
<leafNode name="disable-uniqreqids">
<properties>
- <help>Option to disable requirement for unique IDs in the Security Database</help>
+ <help>Disable requirement for unique IDs in the Security Database</help>
<valueless/>
</properties>
</leafNode>
<tagNode name="esp-group">
<properties>
- <help>Name of Encapsulating Security Payload (ESP) group</help>
+ <help>Encapsulated Security Payload (ESP) group name</help>
</properties>
<children>
<leafNode name="compression">
@@ -47,7 +47,7 @@
<help>ESP lifetime</help>
<valueHelp>
<format>u32:30-86400</format>
- <description>ESP lifetime in seconds (default 3600)</description>
+ <description>ESP lifetime in seconds (default: 3600)</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 30-86400"/>
@@ -83,7 +83,7 @@
</completionHelp>
<valueHelp>
<format>enable</format>
- <description>Inherit Diffie-Hellman group from IKE group - default</description>
+ <description>Inherit Diffie-Hellman group from the IKE group (default)</description>
</valueHelp>
<valueHelp>
<format>dh-group1</format>
@@ -185,10 +185,10 @@
</leafNode>
<tagNode name="proposal">
<properties>
- <help>ESP-group proposal [REQUIRED]</help>
+ <help>ESP group proposal [REQUIRED]</help>
<valueHelp>
<format>u32:1-65535</format>
- <description>ESP-group proposal number</description>
+ <description>ESP group proposal number</description>
</valueHelp>
</properties>
<children>
@@ -200,30 +200,30 @@
</tagNode>
<tagNode name="ike-group">
<properties>
- <help>Name of Internet Key Exchange (IKE) group</help>
+ <help>Internet Key Exchange (IKE) group name</help>
</properties>
<children>
<leafNode name="close-action">
<properties>
- <help>close-action_help</help>
+ <help>Action to take if a child SA is unexpectedly closed</help>
<completionHelp>
<list>none hold clear restart</list>
</completionHelp>
<valueHelp>
<format>none</format>
- <description>Set action to none (default)</description>
+ <description>Do nothing (default)</description>
</valueHelp>
<valueHelp>
<format>hold</format>
- <description>Set action to hold</description>
+ <description>Attempt to re-negotiate when matching traffic is seen</description>
</valueHelp>
<valueHelp>
<format>clear</format>
- <description>Set action to clear</description>
+ <description>Remove the connection immediately</description>
</valueHelp>
<valueHelp>
<format>restart</format>
- <description>Set action to restart</description>
+ <description>Attempt to re-negotiate the connection immediately</description>
</valueHelp>
<constraint>
<regex>^(none|hold|clear|restart)$</regex>
@@ -243,15 +243,15 @@
</completionHelp>
<valueHelp>
<format>hold</format>
- <description>Set action to hold (default)</description>
+ <description>Attempt to re-negotiate the connection when matching traffic is seen (default)</description>
</valueHelp>
<valueHelp>
<format>clear</format>
- <description>Set action to clear</description>
+ <description>Remove the connection immediately</description>
</valueHelp>
<valueHelp>
<format>restart</format>
- <description>Set action to restart</description>
+ <description>Attempt to re-negotiate the connection immediately</description>
</valueHelp>
<constraint>
<regex>^(hold|clear|restart)$</regex>
@@ -263,7 +263,7 @@
<help>Keep-alive interval</help>
<valueHelp>
<format>u32:2-86400</format>
- <description>Keep-alive interval in seconds (default 30)</description>
+ <description>Keep-alive interval in seconds (default: 30)</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 2-86400"/>
@@ -272,7 +272,7 @@
</leafNode>
<leafNode name="timeout">
<properties>
- <help>Dead-Peer-Detection keep-alive timeout (IKEv1 only)</help>
+ <help>Dead Peer Detection keep-alive timeout (IKEv1 only)</help>
<valueHelp>
<format>u32:2-86400</format>
<description>Keep-alive timeout in seconds (default 120)</description>
@@ -296,7 +296,7 @@
</valueHelp>
<valueHelp>
<format>no</format>
- <description>Disable remote host re-authenticaton during an IKE rekey. (Default)</description>
+ <description>Disable remote host re-authenticaton during an IKE rekey. (default)</description>
</valueHelp>
<constraint>
<regex>^(yes|no)$</regex>
@@ -305,17 +305,17 @@
</leafNode>
<leafNode name="key-exchange">
<properties>
- <help>Key Exchange Version</help>
+ <help>IKE version</help>
<completionHelp>
<list>ikev1 ikev2</list>
</completionHelp>
<valueHelp>
<format>ikev1</format>
- <description>Use IKEv1 for Key Exchange [DEFAULT]</description>
+ <description>Use IKEv1 for key exchange [DEFAULT]</description>
</valueHelp>
<valueHelp>
<format>ikev2</format>
- <description>Use IKEv2 for Key Exchange</description>
+ <description>Use IKEv2 for key exchange</description>
</valueHelp>
<constraint>
<regex>^(ikev1|ikev2)$</regex>
@@ -327,7 +327,7 @@
<help>IKE lifetime</help>
<valueHelp>
<format>u32:30-86400</format>
- <description>IKE lifetime in seconds (default 28800)</description>
+ <description>IKE lifetime in seconds (default: 28800)</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 30-86400"/>
@@ -337,7 +337,7 @@
</leafNode>
<leafNode name="mobike">
<properties>
- <help>Enable MOBIKE Support. MOBIKE is only available for IKEv2.</help>
+ <help>Enable MOBIKE Support (IKEv2 only)</help>
<completionHelp>
<list>enable disable</list>
</completionHelp>
@@ -356,17 +356,17 @@
</leafNode>
<leafNode name="mode">
<properties>
- <help>IKEv1 Phase 1 Mode Selection</help>
+ <help>IKEv1 phase 1 mode selection</help>
<completionHelp>
<list>main aggressive</list>
</completionHelp>
<valueHelp>
<format>main</format>
- <description>Use Main mode for Key Exchanges in the IKEv1 Protocol (Recommended Default)</description>
+ <description>Use the main mode (recommended, default)</description>
</valueHelp>
<valueHelp>
<format>aggressive</format>
- <description>Use Aggressive mode for Key Exchanges in the IKEv1 protocol - We do not recommend users to use aggressive mode as it is much more insecure compared to Main mode.</description>
+ <description>Use the aggressive mode (insecure, not recommended)</description>
</valueHelp>
<constraint>
<regex>^(main|aggressive)$</regex>
@@ -375,10 +375,10 @@
</leafNode>
<tagNode name="proposal">
<properties>
- <help>proposal_help</help>
+ <help>IKE proposal</help>
<valueHelp>
<format>u32:1-65535</format>
- <description>IKE-group proposal</description>
+ <description>IKE group proposal</description>
</valueHelp>
</properties>
<children>
@@ -490,12 +490,12 @@
</tagNode>
<leafNode name="include-ipsec-conf">
<properties>
- <help>Sets to include an additional configuration directive file for strongSwan. Use an absolute path to specify the included file</help>
+ <help>Absolute path to specify a strongSwan config include file</help>
</properties>
</leafNode>
<leafNode name="include-ipsec-secrets">
<properties>
- <help>Sets to include an additional secrets file for strongSwan. Use an absolute path to specify the included file.</help>
+ <help>Absolute path to a strongSwan secrets include file</help>
</properties>
</leafNode>
#include <include/generic-interface-multi.xml.i>
@@ -506,7 +506,7 @@
<children>
<leafNode name="level">
<properties>
- <help>strongSwan Logger Level</help>
+ <help>strongSwan logging Level</help>
<valueHelp>
<format>0</format>
<description>Very basic auditing logs e.g. SA up/SA down (default)</description>
@@ -527,7 +527,7 @@
</leafNode>
<leafNode name="subsystem">
<properties>
- <help>Subsystem in the daemon the log comes from</help>
+ <help>Subsystem logging levels</help>
<completionHelp>
<list>dmn mgr ike chd job cfg knl net asn enc lib esp tls tnc imc imv pts any</list>
</completionHelp>
@@ -626,7 +626,7 @@
</node>
<tagNode name="profile">
<properties>
- <help>VPN IPSec Profile</help>
+ <help>VPN IPSec profile</help>
</properties>
<children>
#include <include/generic-disable-node.xml.i>
@@ -643,7 +643,7 @@
</completionHelp>
<valueHelp>
<format>pre-shared-secret</format>
- <description>Use pre shared secret key</description>
+ <description>Use a pre-shared secret key</description>
</valueHelp>
</properties>
</leafNode>
@@ -657,13 +657,13 @@
<children>
<leafNode name="tunnel">
<properties>
- <help>Tunnel interface associated with this configuration profile</help>
+ <help>Tunnel interface associated with this profile</help>
<completionHelp>
<path>interfaces tunnel</path>
</completionHelp>
<valueHelp>
<format>txt</format>
- <description>Associated interface to this configuration profile</description>
+ <description>Associated interface to this profile</description>
</valueHelp>
<multi/>
</properties>
@@ -699,15 +699,15 @@
</completionHelp>
<valueHelp>
<format>eap-tls</format>
- <description>Client uses EAP-TLS authentication</description>
+ <description>Use EAP-TLS authentication</description>
</valueHelp>
<valueHelp>
<format>eap-mschapv2</format>
- <description>Client uses EAP-MSCHAPv2 authentication</description>
+ <description>Use EAP-MSCHAPv2 authentication</description>
</valueHelp>
<valueHelp>
<format>eap-radius</format>
- <description>Client uses EAP-RADIUS authentication</description>
+ <description>Use EAP-RADIUS authentication</description>
</valueHelp>
<constraint>
<regex>^(eap-tls|eap-mschapv2|eap-radius)$</regex>
@@ -724,11 +724,11 @@
</completionHelp>
<valueHelp>
<format>pre-shared-secret</format>
- <description>Authentication pre-shared-secret</description>
+ <description>Use a pre-shared secret key</description>
</valueHelp>
<valueHelp>
<format>x509</format>
- <description>Authentication x509</description>
+ <description>Use x.509 certificate</description>
</valueHelp>
<constraint>
<regex>^(pre-shared-secret|x509)$</regex>
@@ -754,7 +754,7 @@
</valueHelp>
<valueHelp>
<format>u32:1-86400</format>
- <description>Timeout in seconds (default 28800)</description>
+ <description>Timeout in seconds (default: 28800)</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 0-86400"/>
@@ -764,14 +764,14 @@
</leafNode>
<leafNode name="pool">
<properties>
- <help>Pool name used for IP address assignments</help>
+ <help>IP address pool</help>
<completionHelp>
<path>vpn ipsec remote-access pool</path>
<list>dhcp radius</list>
</completionHelp>
<valueHelp>
<format>txt</format>
- <description>Name of predefined IP pool</description>
+ <description>Predefined IP pool name</description>
</valueHelp>
<valueHelp>
<format>dhcp</format>
@@ -786,17 +786,17 @@
</leafNode>
<leafNode name="unique">
<properties>
- <help>Connection uniqueness policy to enforce</help>
+ <help>Connection uniqueness enforcement policy</help>
<completionHelp>
<list>never keep replace</list>
</completionHelp>
<valueHelp>
<format>never</format>
- <description>Never enforce connection uniqueness policy</description>
+ <description>Never enforce connection uniqueness</description>
</valueHelp>
<valueHelp>
<format>keep</format>
- <description>Rejects new connection attempts if the same user already has an active connection</description>
+ <description>Reject new connection attempts if the same user already has an active connection</description>
</valueHelp>
<valueHelp>
<format>replace</format>
@@ -811,7 +811,7 @@
</tagNode>
<node name="dhcp">
<properties>
- <help>DHCP pool options for remote-access</help>
+ <help>DHCP pool options for remote access</help>
</properties>
<children>
#include <include/generic-interface.xml.i>
@@ -831,7 +831,7 @@
</node>
<tagNode name="pool">
<properties>
- <help>IP address pool for remote-access users</help>
+ <help>IP address pool for remote access users</help>
</properties>
<children>
<leafNode name="exclude">
@@ -936,7 +936,7 @@
</valueHelp>
<valueHelp>
<format>x509</format>
- <description>Use X.509 certificate</description>
+ <description>Use x.509 certificate</description>
</valueHelp>
<constraint>
<regex>^(pre-shared-secret|rsa|x509)$</regex>
@@ -992,17 +992,17 @@
#include <include/dhcp-interface.xml.i>
<leafNode name="force-encapsulation">
<properties>
- <help>Force UDP Encapsulation for ESP Payloads</help>
+ <help>Force UDP Encapsulation for ESP payloads</help>
<completionHelp>
<list>enable disable</list>
</completionHelp>
<valueHelp>
<format>enable</format>
- <description>This endpoint will force UDP encapsulation for this peer</description>
+ <description>Force UDP encapsulation</description>
</valueHelp>
<valueHelp>
<format>disable</format>
- <description>This endpoint will not force UDP encapsulation for this peer</description>
+ <description>Do not force UDP encapsulation</description>
</valueHelp>
<constraint>
<regex>^(enable|disable)$</regex>
@@ -1012,7 +1012,7 @@
#include <include/ipsec/ike-group.xml.i>
<leafNode name="ikev2-reauth">
<properties>
- <help>Re-authentication of the remote peer during an IKE re-key. IKEv2 option only</help>
+ <help>Re-authentication of the remote peer during an IKE re-key (IKEv2 only)</help>
<completionHelp>
<list>yes no inherit</list>
</completionHelp>
@@ -1026,7 +1026,7 @@
</valueHelp>
<valueHelp>
<format>inherit</format>
- <description>Inherit the reauth configuration form your IKE-group (Default)</description>
+ <description>Inherit the reauth configuration form your IKE-group (default)</description>
</valueHelp>
<constraint>
<regex>^(yes|no|inherit)$</regex>
@@ -1047,9 +1047,21 @@
#include <include/ipsec/esp-group.xml.i>
#include <include/ipsec/local-traffic-selector.xml.i>
#include <include/ip-protocol.xml.i>
+ <leafNode name="priority">
+ <properties>
+ <help>Priority for IPSec policy (lowest value more preferable)</help>
+ <valueHelp>
+ <format>u32:1-100</format>
+ <description>Priority for IPSec policy (lowest value more preferable)</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-100"/>
+ </constraint>
+ </properties>
+ </leafNode>
<node name="remote">
<properties>
- <help>Remote parameters for interesting traffic</help>
+ <help>Match remote addresses</help>
</properties>
<children>
#include <include/port-number.xml.i>
diff --git a/op-mode-definitions/conntrack-sync.xml.in b/op-mode-definitions/conntrack-sync.xml.in
index 41a71b04a..3e29ecd39 100644
--- a/op-mode-definitions/conntrack-sync.xml.in
+++ b/op-mode-definitions/conntrack-sync.xml.in
@@ -87,6 +87,18 @@
</node>
</children>
</node>
+ <leafNode name="statistics">
+ <properties>
+ <help>Show connection syncing statistics</help>
+ </properties>
+ <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>
+ </leafNode>
</children>
</node>
</children>
diff --git a/op-mode-definitions/include/bfd-common.xml.i b/op-mode-definitions/include/bfd-common.xml.i
deleted file mode 100644
index eebfbdad4..000000000
--- a/op-mode-definitions/include/bfd-common.xml.i
+++ /dev/null
@@ -1,54 +0,0 @@
-<!-- included start from bfd-common.xml.i -->
-<node name="bfd">
- <properties>
- <help>Show Bidirectional Forwarding Detection (BFD)</help>
- </properties>
- <children>
- <node name="peer">
- <properties>
- <help>Show all Bidirectional Forwarding Detection (BFD) peer status</help>
- </properties>
- <command>vtysh -c "show bfd peers"</command>
- <children>
- <leafNode name="counters">
- <properties>
- <help>Show Bidirectional Forwarding Detection (BFD) peer counters</help>
- </properties>
- <command>vtysh -c "show bfd peers counters"</command>
- </leafNode>
- </children>
- </node>
- <tagNode name="peer">
- <properties>
- <help>Show Bidirectional Forwarding Detection (BFD) peer status</help>
- <completionHelp>
- <script>vtysh -c "show bfd peers" | awk '/[:blank:]*peer/ { printf "%s\n", $2 }'</script>
- </completionHelp>
- </properties>
- <command>vtysh -c "show bfd peers" | awk -v BFD_PEER=$5 'BEGIN { regex = sprintf("(peer %s.*)vrf", BFD_PEER) } { if (match($0, regex, bfd_peer_value)) peer=bfd_peer_value[1] } END { if (peer) system("vtysh -c \"show bfd " peer "\"") }'</command>
- <children>
- <leafNode name="counters">
- <properties>
- <help>Show Bidirectional Forwarding Detection (BFD) peer counters</help>
- </properties>
- <command>vtysh -c "show bfd peers" | awk -v BFD_PEER=$5 'BEGIN { regex = sprintf("(peer %s.*)vrf", BFD_PEER) } { if (match($0, regex, bfd_peer_value)) peer=bfd_peer_value[1] } END { if (peer) system("vtysh -c \"show bfd " peer " counters\"") }'</command>
- </leafNode>
- </children>
- </tagNode>
- <node name="peers">
- <properties>
- <help>Show Bidirectional Forwarding Detection peers</help>
- </properties>
- <command>vtysh -c "show bfd peers"</command>
- <children>
- <leafNode name="brief">
- <properties>
- <help>Show Bidirectional Forwarding Detection (BFD) peers brief</help>
- </properties>
- <command>vtysh -c "show bfd peers brief"</command>
- </leafNode>
- </children>
- </node>
- </children>
-</node>
-<!-- included end -->
diff --git a/op-mode-definitions/include/bgp/afi-common.xml.i b/op-mode-definitions/include/bgp/afi-common.xml.i
index 4d5f56656..acf20d950 100644
--- a/op-mode-definitions/include/bgp/afi-common.xml.i
+++ b/op-mode-definitions/include/bgp/afi-common.xml.i
@@ -61,5 +61,4 @@
</leafNode>
</children>
</node>
-#include <include/vtysh-generic-wide.xml.i>
<!-- included end -->
diff --git a/op-mode-definitions/include/bgp/afi-ipv4-ipv6-common.xml.i b/op-mode-definitions/include/bgp/afi-ipv4-ipv6-common.xml.i
index a51595b7f..084f5da83 100644
--- a/op-mode-definitions/include/bgp/afi-ipv4-ipv6-common.xml.i
+++ b/op-mode-definitions/include/bgp/afi-ipv4-ipv6-common.xml.i
@@ -230,4 +230,5 @@
</properties>
<command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
</tagNode>
+#include <include/vtysh-generic-wide.xml.i>
<!-- included end -->
diff --git a/op-mode-definitions/include/ospfv3/border-routers.xml.i b/op-mode-definitions/include/ospfv3/border-routers.xml.i
new file mode 100644
index 000000000..b6fac6785
--- /dev/null
+++ b/op-mode-definitions/include/ospfv3/border-routers.xml.i
@@ -0,0 +1,20 @@
+<!-- included start from ospfv3/border-routers.xml.i -->
+<node name="border-routers">
+ <properties>
+ <help>Show OSPFv3 border-router (ABR and ASBR) information</help>
+ </properties>
+ <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
+ <children>
+ #include <include/ospfv3/detail.xml.i>
+ </children>
+</node>
+<tagNode name="border-routers">
+ <properties>
+ <help>Border router ID</help>
+ <completionHelp>
+ <list>&lt;x.x.x.x&gt;</list>
+ </completionHelp>
+ </properties>
+ <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
+</tagNode>
+<!-- included end -->
diff --git a/op-mode-definitions/include/ospfv3/database.xml.i b/op-mode-definitions/include/ospfv3/database.xml.i
new file mode 100644
index 000000000..e98f9e35b
--- /dev/null
+++ b/op-mode-definitions/include/ospfv3/database.xml.i
@@ -0,0 +1,238 @@
+<!-- included start from ospfv3/database.xml.i -->
+<node name="database">
+ <properties>
+ <help>Show OSPFv3 Link state database information</help>
+ </properties>
+ <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
+ <children>
+ <tagNode name="adv-router">
+ <properties>
+ <help>Search by Advertising Router ID</help>
+ <completionHelp>
+ <list>&lt;x.x.x.x&gt;</list>
+ </completionHelp>
+ </properties>
+ <children>
+ #include <include/ospfv3/linkstate-id.xml.i>
+ </children>
+ </tagNode>
+ <node name="any">
+ <properties>
+ <help>Search by Any Link state Type</help>
+ </properties>
+ <children>
+ <tagNode name="any">
+ <properties>
+ <help>Search by Link state ID</help>
+ <completionHelp>
+ <list>&lt;x.x.x.x&gt;</list>
+ </completionHelp>
+ </properties>
+ <children>
+ #include <include/ospfv3/detail.xml.i>
+ #include <include/ospfv3/dump.xml.i>
+ #include <include/ospfv3/internal.xml.i>
+ </children>
+ </tagNode>
+ </children>
+ </node>
+ <tagNode name="any">
+ <properties>
+ <help>Search by Link state ID</help>
+ <completionHelp>
+ <list>&lt;x.x.x.x&gt;</list>
+ </completionHelp>
+ </properties>
+ <command>vtysh -c "show ipv6 ospf6 database * $6"</command>
+ <children>
+ #include <include/ospfv3/detail.xml.i>
+ #include <include/ospfv3/dump.xml.i>
+ #include <include/ospfv3/internal.xml.i>
+ #include <include/ospfv3/adv-router-id-node-tag.xml.i>
+ </children>
+ </tagNode>
+ <node name="as-external">
+ <properties>
+ <help>Show AS-External LSAs</help>
+ </properties>
+ <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
+ <children>
+ #include <include/ospfv3/adv-router.xml.i>
+ <tagNode name="any">
+ <properties>
+ <help>Search by Advertising Router ID</help>
+ <completionHelp>
+ <list>&lt;x.x.x.x&gt;</list>
+ </completionHelp>
+ </properties>
+ <command>vtysh -c "show ipv6 ospf6 database as-external * $7"</command>
+ <children>
+ #include <include/ospfv3/detail.xml.i>
+ #include <include/ospfv3/dump.xml.i>
+ #include <include/ospfv3/internal.xml.i>
+ </children>
+ </tagNode>
+ #include <include/ospfv3/detail.xml.i>
+ #include <include/ospfv3/dump.xml.i>
+ #include <include/ospfv3/internal.xml.i>
+ #include <include/ospfv3/linkstate-id.xml.i>
+ #include <include/ospfv3/self-originated.xml.i>
+ </children>
+ </node>
+ <tagNode name="as-external">
+ <properties>
+ <help>Search by Advertising Router IDs</help>
+ <completionHelp>
+ <list>&lt;x.x.x.x&gt;</list>
+ </completionHelp>
+ </properties>
+ <children>
+ #include <include/ospfv3/detail.xml.i>
+ #include <include/ospfv3/dump.xml.i>
+ #include <include/ospfv3/internal.xml.i>
+ #include <include/ospfv3/self-originated.xml.i>
+ #include <include/ospfv3/adv-router-id-node-tag.xml.i>
+ </children>
+ </tagNode>
+ #include <include/ospfv3/detail.xml.i>
+ #include <include/ospfv3/internal.xml.i>
+ #include <include/ospfv3/linkstate-id.xml.i>
+ #include <include/ospfv3/self-originated.xml.i>
+ <node name="group-membership">
+ <properties>
+ <help>Show Group-Membership LSAs</help>
+ </properties>
+ <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
+ <children>
+ #include <include/ospfv3/adv-router.xml.i>
+ #include <include/ospfv3/detail.xml.i>
+ #include <include/ospfv3/dump.xml.i>
+ #include <include/ospfv3/internal.xml.i>
+ #include <include/ospfv3/linkstate-id.xml.i>
+ #include <include/ospfv3/linkstate-id-node-tag.xml.i>
+ #include <include/ospfv3/self-originated.xml.i>
+ </children>
+ </node>
+ <node name="inter-prefix">
+ <properties>
+ <help>Show Inter-Area-Prefix LSAs</help>
+ </properties>
+ <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
+ <children>
+ #include <include/ospfv3/adv-router.xml.i>
+ #include <include/ospfv3/detail.xml.i>
+ #include <include/ospfv3/dump.xml.i>
+ #include <include/ospfv3/internal.xml.i>
+ #include <include/ospfv3/linkstate-id.xml.i>
+ #include <include/ospfv3/linkstate-id-node-tag.xml.i>
+ #include <include/ospfv3/self-originated.xml.i>
+ </children>
+ </node>
+ <node name="inter-router">
+ <properties>
+ <help>Show Inter-Area-Router LSAs</help>
+ </properties>
+ <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
+ <children>
+ #include <include/ospfv3/adv-router.xml.i>
+ #include <include/ospfv3/detail.xml.i>
+ #include <include/ospfv3/dump.xml.i>
+ #include <include/ospfv3/internal.xml.i>
+ #include <include/ospfv3/linkstate-id.xml.i>
+ #include <include/ospfv3/linkstate-id-node-tag.xml.i>
+ #include <include/ospfv3/self-originated.xml.i>
+ </children>
+ </node>
+ <node name="intra-prefix">
+ <properties>
+ <help>Show Intra-Area-Prefix LSAs</help>
+ </properties>
+ <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
+ <children>
+ #include <include/ospfv3/adv-router.xml.i>
+ #include <include/ospfv3/detail.xml.i>
+ #include <include/ospfv3/dump.xml.i>
+ #include <include/ospfv3/internal.xml.i>
+ #include <include/ospfv3/linkstate-id.xml.i>
+ #include <include/ospfv3/linkstate-id-node-tag.xml.i>
+ #include <include/ospfv3/self-originated.xml.i>
+ </children>
+ </node>
+ <node name="link">
+ <properties>
+ <help>Show Link LSAs</help>
+ </properties>
+ <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
+ <children>
+ #include <include/ospfv3/adv-router.xml.i>
+ #include <include/ospfv3/detail.xml.i>
+ #include <include/ospfv3/dump.xml.i>
+ #include <include/ospfv3/internal.xml.i>
+ #include <include/ospfv3/linkstate-id.xml.i>
+ #include <include/ospfv3/linkstate-id-node-tag.xml.i>
+ #include <include/ospfv3/self-originated.xml.i>
+ </children>
+ </node>
+ <node name="network">
+ <properties>
+ <help>Show Network LSAs</help>
+ </properties>
+ <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
+ <children>
+ #include <include/ospfv3/adv-router.xml.i>
+ #include <include/ospfv3/detail.xml.i>
+ #include <include/ospfv3/dump.xml.i>
+ #include <include/ospfv3/internal.xml.i>
+ #include <include/ospfv3/linkstate-id.xml.i>
+ #include <include/ospfv3/linkstate-id-node-tag.xml.i>
+ #include <include/ospfv3/self-originated.xml.i>
+ </children>
+ </node>
+ <node name="node.tag">
+ <properties>
+ <help>Show LSAs</help>
+ </properties>
+ <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
+ <children>
+ #include <include/ospfv3/adv-router.xml.i>
+ #include <include/ospfv3/detail.xml.i>
+ #include <include/ospfv3/dump.xml.i>
+ #include <include/ospfv3/internal.xml.i>
+ #include <include/ospfv3/linkstate-id.xml.i>
+ #include <include/ospfv3/linkstate-id-node-tag.xml.i>
+ #include <include/ospfv3/self-originated.xml.i>
+ </children>
+ </node>
+ <node name="router">
+ <properties>
+ <help>Show router LSAs</help>
+ </properties>
+ <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
+ <children>
+ #include <include/ospfv3/adv-router.xml.i>
+ #include <include/ospfv3/detail.xml.i>
+ #include <include/ospfv3/dump.xml.i>
+ #include <include/ospfv3/internal.xml.i>
+ #include <include/ospfv3/linkstate-id.xml.i>
+ #include <include/ospfv3/linkstate-id-node-tag.xml.i>
+ #include <include/ospfv3/self-originated.xml.i>
+ </children>
+ </node>
+ <node name="type-7">
+ <properties>
+ <help>Show Type-7 LSAs</help>
+ </properties>
+ <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
+ <children>
+ #include <include/ospfv3/adv-router.xml.i>
+ #include <include/ospfv3/detail.xml.i>
+ #include <include/ospfv3/dump.xml.i>
+ #include <include/ospfv3/internal.xml.i>
+ #include <include/ospfv3/linkstate-id.xml.i>
+ #include <include/ospfv3/linkstate-id-node-tag.xml.i>
+ #include <include/ospfv3/self-originated.xml.i>
+ </children>
+ </node>
+ </children>
+</node>
+<!-- included end -->
diff --git a/op-mode-definitions/include/ospfv3/interface.xml.i b/op-mode-definitions/include/ospfv3/interface.xml.i
new file mode 100644
index 000000000..0fb66257d
--- /dev/null
+++ b/op-mode-definitions/include/ospfv3/interface.xml.i
@@ -0,0 +1,75 @@
+<!-- included start from ospfv3/interface.xml.i -->
+<node name="interface">
+ <properties>
+ <help>Show OSPFv3 interface information</help>
+ </properties>
+ <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
+ <children>
+ <node name="prefix">
+ <properties>
+ <help>Show connected prefixes to advertise</help>
+ </properties>
+ <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
+ <children>
+ #include <include/ospfv3/detail.xml.i>
+ </children>
+ </node>
+ <tagNode name="prefix">
+ <properties>
+ <help>Show interface prefix route specific information</help>
+ <completionHelp>
+ <list>&lt;h:h:h:h:h:h:h:h&gt; &lt;h:h:h:h:h:h:h:h/x&gt;</list>
+ </completionHelp>
+ </properties>
+ <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
+ <children>
+ #include <include/ospfv3/detail.xml.i>
+ <node name="match">
+ <properties>
+ <help>Matched interface prefix information</help>
+ </properties>
+ <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
+ </node>
+ </children>
+ </tagNode>
+ </children>
+</node>
+<tagNode name="interface">
+ <properties>
+ <help>Specific insterface to examine</help>
+ <completionHelp>
+ <script>${vyos_completion_dir}/list_interfaces.py</script>
+ </completionHelp>
+ </properties>
+ <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
+ <children>
+ <node name="prefix">
+ <properties>
+ <help>Show connected prefixes to advertise</help>
+ </properties>
+ <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
+ <children>
+ #include <include/ospfv3/detail.xml.i>
+ </children>
+ </node>
+ <tagNode name="prefix">
+ <properties>
+ <help>Show interface prefix route specific information</help>
+ <completionHelp>
+ <list>&lt;h:h:h:h:h:h:h:h&gt; &lt;h:h:h:h:h:h:h:h/x&gt;</list>
+ </completionHelp>
+ </properties>
+ <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
+ <children>
+ #include <include/ospfv3/detail.xml.i>
+ <node name="match">
+ <properties>
+ <help>Matched interface prefix information</help>
+ </properties>
+ <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
+ </node>
+ </children>
+ </tagNode>
+ </children>
+</tagNode>
+<!-- included end -->
diff --git a/op-mode-definitions/include/ospfv3/linkstate.xml.i b/op-mode-definitions/include/ospfv3/linkstate.xml.i
new file mode 100644
index 000000000..78ef3efa1
--- /dev/null
+++ b/op-mode-definitions/include/ospfv3/linkstate.xml.i
@@ -0,0 +1,38 @@
+<!-- included start from ospfv3/linkstate.xml.i -->
+<node name="linkstate">
+ <properties>
+ <help>Show OSPFv3 linkstate routing information</help>
+ </properties>
+ <children>
+ #include <include/ospfv3/detail.xml.i>
+ <tagNode name="network">
+ <properties>
+ <help>Show linkstate Network information</help>
+ <completionHelp>
+ <list>&lt;x.x.x.x&gt;</list>
+ </completionHelp>
+ </properties>
+ <children>
+ <node name="node.tag">
+ <properties>
+ <help>Specify Link state ID as IPv4 address notation</help>
+ <completionHelp>
+ <list>&lt;x.x.x.x&gt;</list>
+ </completionHelp>
+ </properties>
+ <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
+ </node>
+ </children>
+ </tagNode>
+ <tagNode name="router">
+ <properties>
+ <help>Show linkstate Router information</help>
+ <completionHelp>
+ <list>&lt;x.x.x.x&gt;</list>
+ </completionHelp>
+ </properties>
+ <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
+ </tagNode>
+ </children>
+</node>
+<!-- included end -->
diff --git a/op-mode-definitions/include/ospfv3/neighbor.xml.i b/op-mode-definitions/include/ospfv3/neighbor.xml.i
new file mode 100644
index 000000000..37859f815
--- /dev/null
+++ b/op-mode-definitions/include/ospfv3/neighbor.xml.i
@@ -0,0 +1,17 @@
+<!-- included start from ospfv3/neighbor.xml.i -->
+<node name="neighbor">
+ <properties>
+ <help>Show OSPFv3 neighbor information</help>
+ </properties>
+ <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
+ <children>
+ #include <include/ospfv3/detail.xml.i>
+ <node name="drchoice">
+ <properties>
+ <help>Show neighbor DR choice information</help>
+ </properties>
+ <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
+ </node>
+ </children>
+</node>
+<!-- included end -->
diff --git a/op-mode-definitions/include/ospfv3/redistribute.xml.i b/op-mode-definitions/include/ospfv3/redistribute.xml.i
new file mode 100644
index 000000000..1c2d6494f
--- /dev/null
+++ b/op-mode-definitions/include/ospfv3/redistribute.xml.i
@@ -0,0 +1,8 @@
+<!-- included start from ospfv3/redistribute.xml.i -->
+<node name="redistribute">
+ <properties>
+ <help>Show OSPFv3 redistribute external information</help>
+ </properties>
+ <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
+</node>
+<!-- included end -->
diff --git a/op-mode-definitions/include/ospfv3/route.xml.i b/op-mode-definitions/include/ospfv3/route.xml.i
new file mode 100644
index 000000000..9271c9c3a
--- /dev/null
+++ b/op-mode-definitions/include/ospfv3/route.xml.i
@@ -0,0 +1,79 @@
+<!-- included start from ospfv3/route.xml.i -->
+<node name="route">
+ <properties>
+ <help>Show OSPFv3 routing table information</help>
+ </properties>
+ <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
+ <children>
+ <node name="external-1">
+ <properties>
+ <help>Show Type-1 External route information</help>
+ </properties>
+ <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
+ <children>
+ #include <include/ospfv3/detail.xml.i>
+ </children>
+ </node>
+ <node name="external-2">
+ <properties>
+ <help>Show Type-2 External route information</help>
+ </properties>
+ <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
+ <children>
+ #include <include/ospfv3/detail.xml.i>
+ </children>
+ </node>
+ <node name="inter-area">
+ <properties>
+ <help>Show Inter-Area route information</help>
+ </properties>
+ <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
+ <children>
+ #include <include/ospfv3/detail.xml.i>
+ </children>
+ </node>
+ <node name="intra-area">
+ <properties>
+ <help>Show Intra-Area route information</help>
+ </properties>
+ <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
+ <children>
+ #include <include/ospfv3/detail.xml.i>
+ </children>
+ </node>
+ #include <include/ospfv3/detail.xml.i>
+ <node name="summary">
+ <properties>
+ <help>Show route table summary</help>
+ </properties>
+ <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
+ </node>
+ </children>
+</node>
+<tagNode name="route">
+ <properties>
+ <help>Show specified route/prefix information</help>
+ <completionHelp>
+ <list>&lt;h:h:h:h:h:h:h:h&gt; &lt;h:h:h:h:h:h:h:h/x&gt;</list>
+ </completionHelp>
+ </properties>
+ <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
+ <children>
+ <node name="longer">
+ <properties>
+ <help>Show routes longer than specified prefix</help>
+ </properties>
+ <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
+ </node>
+ <node name="match">
+ <properties>
+ <help>Show routes matching specified prefix</help>
+ </properties>
+ <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
+ <children>
+ #include <include/ospfv3/detail.xml.i>
+ </children>
+ </node>
+ </children>
+</tagNode>
+<!-- included end -->
diff --git a/op-mode-definitions/include/show-route-bgp.xml.i b/op-mode-definitions/include/show-route-bgp.xml.i
new file mode 100644
index 000000000..5c26bf43f
--- /dev/null
+++ b/op-mode-definitions/include/show-route-bgp.xml.i
@@ -0,0 +1,8 @@
+<!-- included start from show-route-bgp.xml.i -->
+<leafNode name="bgp">
+ <properties>
+ <help>Border Gateway Protocol (BGP)</help>
+ </properties>
+ <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
+</leafNode>
+<!-- included end -->
diff --git a/op-mode-definitions/include/show-route-connected.xml.i b/op-mode-definitions/include/show-route-connected.xml.i
new file mode 100644
index 000000000..37364de64
--- /dev/null
+++ b/op-mode-definitions/include/show-route-connected.xml.i
@@ -0,0 +1,8 @@
+<!-- included start from show-route-connected.xml.i -->
+<leafNode name="connected">
+ <properties>
+ <help>Connected routes (directly attached subnet or host)</help>
+ </properties>
+ <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
+</leafNode>
+<!-- included end -->
diff --git a/op-mode-definitions/include/show-route-isis.xml.i b/op-mode-definitions/include/show-route-isis.xml.i
new file mode 100644
index 000000000..9ff2ccdc5
--- /dev/null
+++ b/op-mode-definitions/include/show-route-isis.xml.i
@@ -0,0 +1,8 @@
+<!-- included start from show-route-isis.xml.i -->
+<leafNode name="isis">
+ <properties>
+ <help>Intermediate System to Intermediate System (IS-IS)</help>
+ </properties>
+ <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
+</leafNode>
+<!-- included end -->
diff --git a/op-mode-definitions/include/show-route-kernel.xml.i b/op-mode-definitions/include/show-route-kernel.xml.i
new file mode 100644
index 000000000..8c5ac414e
--- /dev/null
+++ b/op-mode-definitions/include/show-route-kernel.xml.i
@@ -0,0 +1,8 @@
+<!-- included start from show-route-kernel.xml.i -->
+<leafNode name="kernel">
+ <properties>
+ <help>Kernel routes (not installed via the zebra RIB)</help>
+ </properties>
+ <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
+</leafNode>
+<!-- included end -->
diff --git a/op-mode-definitions/include/show-route-ospf.xml.i b/op-mode-definitions/include/show-route-ospf.xml.i
new file mode 100644
index 000000000..1122aaba5
--- /dev/null
+++ b/op-mode-definitions/include/show-route-ospf.xml.i
@@ -0,0 +1,8 @@
+<!-- included start from show-route-ospf.xml.i -->
+<leafNode name="ospf">
+ <properties>
+ <help>Open Shortest Path First (OSPFv2)</help>
+ </properties>
+ <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
+</leafNode>
+<!-- included end -->
diff --git a/op-mode-definitions/include/show-route-ospfv3.xml.i b/op-mode-definitions/include/show-route-ospfv3.xml.i
new file mode 100644
index 000000000..c7a11b7ba
--- /dev/null
+++ b/op-mode-definitions/include/show-route-ospfv3.xml.i
@@ -0,0 +1,8 @@
+<!-- included start from show-route-ospfv3.xml.i -->
+<leafNode name="ospfv3">
+ <properties>
+ <help>Open Shortest Path First (IPv6) (OSPFv3)</help>
+ </properties>
+ <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
+</leafNode>
+<!-- included end -->
diff --git a/op-mode-definitions/include/show-route-rip.xml.i b/op-mode-definitions/include/show-route-rip.xml.i
new file mode 100644
index 000000000..3c2fede28
--- /dev/null
+++ b/op-mode-definitions/include/show-route-rip.xml.i
@@ -0,0 +1,8 @@
+<!-- included start from show-route-rip.xml.i -->
+<leafNode name="rip">
+ <properties>
+ <help>Routing Information Protocol (RIP)</help>
+ </properties>
+ <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
+</leafNode>
+<!-- included end -->
diff --git a/op-mode-definitions/include/show-route-ripng.xml.i b/op-mode-definitions/include/show-route-ripng.xml.i
new file mode 100644
index 000000000..6e59cb054
--- /dev/null
+++ b/op-mode-definitions/include/show-route-ripng.xml.i
@@ -0,0 +1,8 @@
+<!-- included start from show-route-ripng.xml.i -->
+<leafNode name="ripng">
+ <properties>
+ <help>Routing Information Protocol next-generation (IPv6) (RIPng)</help>
+ </properties>
+ <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
+</leafNode>
+<!-- included end -->
diff --git a/op-mode-definitions/include/show-route-static.xml.i b/op-mode-definitions/include/show-route-static.xml.i
new file mode 100644
index 000000000..c2e396763
--- /dev/null
+++ b/op-mode-definitions/include/show-route-static.xml.i
@@ -0,0 +1,8 @@
+<!-- included start from show-route-static.xml.i -->
+<leafNode name="static">
+ <properties>
+ <help>Statically configured routes</help>
+ </properties>
+ <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
+</leafNode>
+<!-- included end -->
diff --git a/op-mode-definitions/include/show-route-summary.xml.i b/op-mode-definitions/include/show-route-summary.xml.i
new file mode 100644
index 000000000..471124562
--- /dev/null
+++ b/op-mode-definitions/include/show-route-summary.xml.i
@@ -0,0 +1,8 @@
+<!-- included start from show-route-summary.xml.i -->
+<leafNode name="summary">
+ <properties>
+ <help>Summary of all routes</help>
+ </properties>
+ <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
+</leafNode>
+<!-- included end -->
diff --git a/op-mode-definitions/include/show-route-supernets-only.xml.i b/op-mode-definitions/include/show-route-supernets-only.xml.i
new file mode 100644
index 000000000..4d1e7c51f
--- /dev/null
+++ b/op-mode-definitions/include/show-route-supernets-only.xml.i
@@ -0,0 +1,8 @@
+<!-- included start from show-route-supernets-only.xml.i -->
+<leafNode name="supernets-only">
+ <properties>
+ <help>Show supernet entries only</help>
+ </properties>
+ <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
+</leafNode>
+<!-- included end -->
diff --git a/op-mode-definitions/include/show-route-table.xml.i b/op-mode-definitions/include/show-route-table.xml.i
new file mode 100644
index 000000000..c3cf82a86
--- /dev/null
+++ b/op-mode-definitions/include/show-route-table.xml.i
@@ -0,0 +1,17 @@
+<!-- included start from show-route-table.xml.i -->
+<node name="table">
+ <properties>
+ <help>Table to display</help>
+ </properties>
+</node>
+<tagNode name="table">
+ <properties>
+ <help>The table number to display</help>
+ <completionHelp>
+ <list>all</list>
+ <path>protocols static table</path>
+ </completionHelp>
+ </properties>
+ <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
+</tagNode>
+<!-- included end -->
diff --git a/op-mode-definitions/include/show-route-tag.xml.i b/op-mode-definitions/include/show-route-tag.xml.i
new file mode 100644
index 000000000..8bfa0ae4e
--- /dev/null
+++ b/op-mode-definitions/include/show-route-tag.xml.i
@@ -0,0 +1,16 @@
+<!-- included start from show-route-tag.xml.i -->
+<node name="tag">
+ <properties>
+ <help>Show only routes with tag</help>
+ </properties>
+</node>
+<tagNode name="tag">
+ <properties>
+ <help>Tag value</help>
+ <completionHelp>
+ <list>&lt;1-4294967295&gt;</list>
+ </completionHelp>
+ </properties>
+ <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
+</tagNode>
+<!-- included end -->
diff --git a/op-mode-definitions/restart-frr.xml.in b/op-mode-definitions/restart-frr.xml.in
index 475bd1ee8..4e2be1bf2 100644
--- a/op-mode-definitions/restart-frr.xml.in
+++ b/op-mode-definitions/restart-frr.xml.in
@@ -26,6 +26,12 @@
</properties>
<command>sudo ${vyos_op_scripts_dir}/restart_frr.py --action restart --daemon isisd</command>
</leafNode>
+ <leafNode name="ldp">
+ <properties>
+ <help>Restart the Label Distribution Protocol (LDP) daemon</help>
+ </properties>
+ <command>sudo ${vyos_op_scripts_dir}/restart_frr.py --action restart --daemon ldpd</command>
+ </leafNode>
<leafNode name="ospf">
<properties>
<help>Restart Open Shortest Path First (OSPF) routing daemon</help>
diff --git a/op-mode-definitions/show-bfd.xml.in b/op-mode-definitions/show-bfd.xml.in
index 7339c92a2..39e42e6ec 100644
--- a/op-mode-definitions/show-bfd.xml.in
+++ b/op-mode-definitions/show-bfd.xml.in
@@ -2,7 +2,55 @@
<interfaceDefinition>
<node name="show">
<children>
- #include <include/bfd-common.xml.i>
+ <node name="bfd">
+ <properties>
+ <help>Show Bidirectional Forwarding Detection (BFD)</help>
+ </properties>
+ <children>
+ <node name="peer">
+ <properties>
+ <help>Show all Bidirectional Forwarding Detection (BFD) peer status</help>
+ </properties>
+ </node>
+ <tagNode name="peer">
+ <properties>
+ <help>Show Bidirectional Forwarding Detection (BFD) peer status</help>
+ <completionHelp>
+ <script>vtysh -c "show bfd peers" | awk '/[:blank:]*peer/ { printf "%s\n", $2 }'</script>
+ </completionHelp>
+ </properties>
+ <command>vtysh -c "show bfd peers" | sed -n "/peer $4 /,/^$/p"</command>
+ <children>
+ <leafNode name="counters">
+ <properties>
+ <help>Show Bidirectional Forwarding Detection (BFD) peer counters</help>
+ </properties>
+ <command>vtysh -c "show bfd peers counters" | sed -n "/peer $4 /,/^$/p"</command>
+ </leafNode>
+ </children>
+ </tagNode>
+ <node name="peers">
+ <properties>
+ <help>Show Bidirectional Forwarding Detection peers</help>
+ </properties>
+ <command>vtysh -c "show bfd peers"</command>
+ <children>
+ <leafNode name="counters">
+ <properties>
+ <help>Show Bidirectional Forwarding Detection (BFD) peer counters</help>
+ </properties>
+ <command>vtysh -c "show bfd peers counters"</command>
+ </leafNode>
+ <leafNode name="brief">
+ <properties>
+ <help>Show Bidirectional Forwarding Detection (BFD) peers brief</help>
+ </properties>
+ <command>vtysh -c "show bfd peers brief"</command>
+ </leafNode>
+ </children>
+ </node>
+ </children>
+ </node>
</children>
</node>
</interfaceDefinition>
diff --git a/op-mode-definitions/show-ip-route.xml.in b/op-mode-definitions/show-ip-route.xml.in
index fdbb6859d..1e906672d 100644
--- a/op-mode-definitions/show-ip-route.xml.in
+++ b/op-mode-definitions/show-ip-route.xml.in
@@ -13,12 +13,7 @@
</properties>
<command>vtysh -c "show ip route"</command>
<children>
- <leafNode name="bgp">
- <properties>
- <help>Show IP BGP routes</help>
- </properties>
- <command>vtysh -c "show ip route bgp"</command>
- </leafNode>
+ #include <include/show-route-bgp.xml.i>
<node name="cache">
<properties>
<help>Show kernel route cache</help>
@@ -34,12 +29,7 @@
</properties>
<command>ip -s route list cache $5</command>
</tagNode>
- <leafNode name="connected">
- <properties>
- <help>Show IP connected routes</help>
- </properties>
- <command>vtysh -c "show ip route connected"</command>
- </leafNode>
+ #include <include/show-route-connected.xml.i>
<node name="forward">
<properties>
<help>Show kernel route table</help>
@@ -55,76 +45,15 @@
</properties>
<command>ip -s route list $5</command>
</tagNode>
- <leafNode name="isis">
- <properties>
- <help>Show IP IS-IS routes</help>
- </properties>
- <command>vtysh -c "show ip route isis"</command>
- </leafNode>
- <leafNode name="kernel">
- <properties>
- <help>Show IP kernel routes</help>
- </properties>
- <command>vtysh -c "show ip route kernel"</command>
- </leafNode>
- <leafNode name="ospf">
- <properties>
- <help>Show IP OSPF routes</help>
- </properties>
- <command>vtysh -c "show ip route ospf"</command>
- </leafNode>
- <leafNode name="rip">
- <properties>
- <help>Show IP RIP routes</help>
- </properties>
- <command>vtysh -c "show ip route rip"</command>
- </leafNode>
- <leafNode name="static">
- <properties>
- <help>Show IP static routes</help>
- </properties>
- <command>vtysh -c "show ip route static"</command>
- </leafNode>
- <leafNode name="summary">
- <properties>
- <help>Show IP routes summary</help>
- </properties>
- <command>vtysh -c "show ip route summary"</command>
- </leafNode>
- <leafNode name="supernets-only">
- <properties>
- <help>Show IP supernet routes</help>
- </properties>
- <command>vtysh -c "show ip route supernets-only"</command>
- </leafNode>
- <node name="table">
- <properties>
- <help>Show IP routes in policy table</help>
- </properties>
- </node>
- <tagNode name="table">
- <properties>
- <help>Show IP routes in policy table</help>
- <completionHelp>
- <list>&lt;1-200&gt;</list>
- </completionHelp>
- </properties>
- <command>vtysh -c "show ip route table $5"</command>
- </tagNode>
- <node name="tag">
- <properties>
- <help>Show only routes with tag</help>
- </properties>
- </node>
- <tagNode name="tag">
- <properties>
- <help>Tag value</help>
- <completionHelp>
- <list>&lt;1-4294967295&gt;</list>
- </completionHelp>
- </properties>
- <command>vtysh -c "show ip route tag $5"</command>
- </tagNode>
+ #include <include/show-route-isis.xml.i>
+ #include <include/show-route-kernel.xml.i>
+ #include <include/show-route-ospf.xml.i>
+ #include <include/show-route-rip.xml.i>
+ #include <include/show-route-static.xml.i>
+ #include <include/show-route-summary.xml.i>
+ #include <include/show-route-supernets-only.xml.i>
+ #include <include/show-route-table.xml.i>
+ #include <include/show-route-tag.xml.i>
<tagNode name="vrf">
<properties>
<help>Show IP routes in VRF</help>
@@ -133,7 +62,19 @@
<path>vrf name</path>
</completionHelp>
</properties>
- <command>vtysh -c "show ip route vrf $5"</command>
+ <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
+ <children>
+ #include <include/show-route-bgp.xml.i>
+ #include <include/show-route-connected.xml.i>
+ #include <include/show-route-isis.xml.i>
+ #include <include/show-route-kernel.xml.i>
+ #include <include/show-route-ospf.xml.i>
+ #include <include/show-route-rip.xml.i>
+ #include <include/show-route-static.xml.i>
+ #include <include/show-route-summary.xml.i>
+ #include <include/show-route-supernets-only.xml.i>
+ #include <include/show-route-tag.xml.i>
+ </children>
</tagNode>
</children>
</node>
@@ -144,7 +85,7 @@
<list>&lt;x.x.x.x&gt; &lt;x.x.x.x/x&gt;</list>
</completionHelp>
</properties>
- <command>vtysh -c "show ip route $4"</command>
+ <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
<children>
<leafNode name="longer-prefixes">
<properties>
diff --git a/op-mode-definitions/show-ipv6-ospfv3.xml.in b/op-mode-definitions/show-ipv6-ospfv3.xml.in
index e6c8a6700..a63465472 100644
--- a/op-mode-definitions/show-ipv6-ospfv3.xml.in
+++ b/op-mode-definitions/show-ipv6-ospfv3.xml.in
@@ -11,7 +11,7 @@
<properties>
<help>Show IPv6 Open Shortest Path First (OSPF)</help>
</properties>
- <command>vtysh -c "show ipv6 ospf6"</command>
+ <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
<children>
<node name="area">
<properties>
@@ -39,467 +39,74 @@
</tagNode>
</children>
</tagNode>
- <node name="border-routers">
+ #include <include/ospfv3/border-routers.xml.i>
+ #include <include/ospfv3/database.xml.i>
+ #include <include/ospfv3/interface.xml.i>
+ #include <include/ospfv3/linkstate.xml.i>
+ #include <include/ospfv3/neighbor.xml.i>
+ #include <include/ospfv3/redistribute.xml.i>
+ #include <include/ospfv3/route.xml.i>
+ <node name="vrf">
<properties>
- <help>Show OSPFv3 border-router (ABR and ASBR) information</help>
- </properties>
- <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
- <children>
- #include <include/ospfv3/detail.xml.i>
- </children>
- </node>
- <tagNode name="border-routers">
- <properties>
- <help>Border router ID</help>
+ <help>Specify the VRF</help>
<completionHelp>
- <list>&lt;x.x.x.x&gt;</list>
+ <list>all</list>
+ <path>vrf name</path>
</completionHelp>
</properties>
<command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
- </tagNode>
- <node name="database">
- <properties>
- <help>Show OSPFv3 Link state database information</help>
- </properties>
- <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
- <children>
- <tagNode name="adv-router">
- <properties>
- <help>Search by Advertising Router ID</help>
- <completionHelp>
- <list>&lt;x.x.x.x&gt;</list>
- </completionHelp>
- </properties>
- <children>
- #include <include/ospfv3/linkstate-id.xml.i>
- </children>
- </tagNode>
- <node name="any">
- <properties>
- <help>Search by Any Link state Type</help>
- </properties>
- <children>
- <tagNode name="any">
- <properties>
- <help>Search by Link state ID</help>
- <completionHelp>
- <list>&lt;x.x.x.x&gt;</list>
- </completionHelp>
- </properties>
- <children>
- #include <include/ospfv3/detail.xml.i>
- #include <include/ospfv3/dump.xml.i>
- #include <include/ospfv3/internal.xml.i>
- </children>
- </tagNode>
- </children>
- </node>
- <tagNode name="any">
- <properties>
- <help>Search by Link state ID</help>
- <completionHelp>
- <list>&lt;x.x.x.x&gt;</list>
- </completionHelp>
- </properties>
- <command>vtysh -c "show ipv6 ospf6 database * $6"</command>
- <children>
- #include <include/ospfv3/detail.xml.i>
- #include <include/ospfv3/dump.xml.i>
- #include <include/ospfv3/internal.xml.i>
- #include <include/ospfv3/adv-router-id-node-tag.xml.i>
- </children>
- </tagNode>
- <node name="as-external">
- <properties>
- <help>Show AS-External LSAs</help>
- </properties>
- <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
- <children>
- #include <include/ospfv3/adv-router.xml.i>
- <tagNode name="any">
- <properties>
- <help>Search by Advertising Router ID</help>
- <completionHelp>
- <list>&lt;x.x.x.x&gt;</list>
- </completionHelp>
- </properties>
- <command>vtysh -c "show ipv6 ospf6 database as-external * $7"</command>
- <children>
- #include <include/ospfv3/detail.xml.i>
- #include <include/ospfv3/dump.xml.i>
- #include <include/ospfv3/internal.xml.i>
- </children>
- </tagNode>
- #include <include/ospfv3/detail.xml.i>
- #include <include/ospfv3/dump.xml.i>
- #include <include/ospfv3/internal.xml.i>
- #include <include/ospfv3/linkstate-id.xml.i>
- #include <include/ospfv3/self-originated.xml.i>
- </children>
- </node>
- <tagNode name="as-external">
- <properties>
- <help>Search by Advertising Router IDs</help>
- <completionHelp>
- <list>&lt;x.x.x.x&gt;</list>
- </completionHelp>
- </properties>
- <children>
- #include <include/ospfv3/detail.xml.i>
- #include <include/ospfv3/dump.xml.i>
- #include <include/ospfv3/internal.xml.i>
- #include <include/ospfv3/self-originated.xml.i>
- #include <include/ospfv3/adv-router-id-node-tag.xml.i>
- </children>
- </tagNode>
- #include <include/ospfv3/detail.xml.i>
- #include <include/ospfv3/internal.xml.i>
- #include <include/ospfv3/linkstate-id.xml.i>
- #include <include/ospfv3/self-originated.xml.i>
- <node name="group-membership">
- <properties>
- <help>Show Group-Membership LSAs</help>
- </properties>
- <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
- <children>
- #include <include/ospfv3/adv-router.xml.i>
- #include <include/ospfv3/detail.xml.i>
- #include <include/ospfv3/dump.xml.i>
- #include <include/ospfv3/internal.xml.i>
- #include <include/ospfv3/linkstate-id.xml.i>
- #include <include/ospfv3/linkstate-id-node-tag.xml.i>
- #include <include/ospfv3/self-originated.xml.i>
- </children>
- </node>
- <node name="inter-prefix">
- <properties>
- <help>Show Inter-Area-Prefix LSAs</help>
- </properties>
- <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
- <children>
- #include <include/ospfv3/adv-router.xml.i>
- #include <include/ospfv3/detail.xml.i>
- #include <include/ospfv3/dump.xml.i>
- #include <include/ospfv3/internal.xml.i>
- #include <include/ospfv3/linkstate-id.xml.i>
- #include <include/ospfv3/linkstate-id-node-tag.xml.i>
- #include <include/ospfv3/self-originated.xml.i>
- </children>
- </node>
- <node name="inter-router">
- <properties>
- <help>Show Inter-Area-Router LSAs</help>
- </properties>
- <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
- <children>
- #include <include/ospfv3/adv-router.xml.i>
- #include <include/ospfv3/detail.xml.i>
- #include <include/ospfv3/dump.xml.i>
- #include <include/ospfv3/internal.xml.i>
- #include <include/ospfv3/linkstate-id.xml.i>
- #include <include/ospfv3/linkstate-id-node-tag.xml.i>
- #include <include/ospfv3/self-originated.xml.i>
- </children>
- </node>
- <node name="intra-prefix">
- <properties>
- <help>Show Intra-Area-Prefix LSAs</help>
- </properties>
- <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
- <children>
- #include <include/ospfv3/adv-router.xml.i>
- #include <include/ospfv3/detail.xml.i>
- #include <include/ospfv3/dump.xml.i>
- #include <include/ospfv3/internal.xml.i>
- #include <include/ospfv3/linkstate-id.xml.i>
- #include <include/ospfv3/linkstate-id-node-tag.xml.i>
- #include <include/ospfv3/self-originated.xml.i>
- </children>
- </node>
- <node name="link">
- <properties>
- <help>Show Link LSAs</help>
- </properties>
- <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
- <children>
- #include <include/ospfv3/adv-router.xml.i>
- #include <include/ospfv3/detail.xml.i>
- #include <include/ospfv3/dump.xml.i>
- #include <include/ospfv3/internal.xml.i>
- #include <include/ospfv3/linkstate-id.xml.i>
- #include <include/ospfv3/linkstate-id-node-tag.xml.i>
- #include <include/ospfv3/self-originated.xml.i>
- </children>
- </node>
- <node name="network">
- <properties>
- <help>Show Network LSAs</help>
- </properties>
- <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
- <children>
- #include <include/ospfv3/adv-router.xml.i>
- #include <include/ospfv3/detail.xml.i>
- #include <include/ospfv3/dump.xml.i>
- #include <include/ospfv3/internal.xml.i>
- #include <include/ospfv3/linkstate-id.xml.i>
- #include <include/ospfv3/linkstate-id-node-tag.xml.i>
- #include <include/ospfv3/self-originated.xml.i>
- </children>
- </node>
- <node name="node.tag">
- <properties>
- <help>Show LSAs</help>
- </properties>
- <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
- <children>
- #include <include/ospfv3/adv-router.xml.i>
- #include <include/ospfv3/detail.xml.i>
- #include <include/ospfv3/dump.xml.i>
- #include <include/ospfv3/internal.xml.i>
- #include <include/ospfv3/linkstate-id.xml.i>
- #include <include/ospfv3/linkstate-id-node-tag.xml.i>
- #include <include/ospfv3/self-originated.xml.i>
- </children>
- </node>
- <node name="router">
- <properties>
- <help>Show router LSAs</help>
- </properties>
- <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
- <children>
- #include <include/ospfv3/adv-router.xml.i>
- #include <include/ospfv3/detail.xml.i>
- #include <include/ospfv3/dump.xml.i>
- #include <include/ospfv3/internal.xml.i>
- #include <include/ospfv3/linkstate-id.xml.i>
- #include <include/ospfv3/linkstate-id-node-tag.xml.i>
- #include <include/ospfv3/self-originated.xml.i>
- </children>
- </node>
- <node name="type-7">
- <properties>
- <help>Show Type-7 LSAs</help>
- </properties>
- <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
- <children>
- #include <include/ospfv3/adv-router.xml.i>
- #include <include/ospfv3/detail.xml.i>
- #include <include/ospfv3/dump.xml.i>
- #include <include/ospfv3/internal.xml.i>
- #include <include/ospfv3/linkstate-id.xml.i>
- #include <include/ospfv3/linkstate-id-node-tag.xml.i>
- #include <include/ospfv3/self-originated.xml.i>
- </children>
- </node>
- </children>
</node>
- <node name="interface">
+ <tagNode name="vrf">
<properties>
- <help>Show OSPFv3 interface information</help>
- </properties>
- <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
- <children>
- <node name="prefix">
- <properties>
- <help>Show connected prefixes to advertise</help>
- </properties>
- <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
- <children>
- #include <include/ospfv3/detail.xml.i>
- </children>
- </node>
- <tagNode name="prefix">
- <properties>
- <help>Show interface prefix route specific information</help>
- <completionHelp>
- <list>&lt;h:h:h:h:h:h:h:h&gt; &lt;h:h:h:h:h:h:h:h/x&gt;</list>
- </completionHelp>
- </properties>
- <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
- <children>
- #include <include/ospfv3/detail.xml.i>
- <node name="match">
- <properties>
- <help>Matched interface prefix information</help>
- </properties>
- <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
- </node>
- </children>
- </tagNode>
- </children>
- </node>
- <tagNode name="interface">
- <properties>
- <help>Specific insterface to examine</help>
+ <help>VRF name</help>
<completionHelp>
- <script>${vyos_completion_dir}/list_interfaces.py</script>
+ <list>all</list>
+ <path>vrf name</path>
</completionHelp>
</properties>
<command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
<children>
- <node name="prefix">
+ <node name="area">
<properties>
- <help>Show connected prefixes to advertise</help>
+ <help>Show Shortest Path First tree information</help>
</properties>
- <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
- <children>
- #include <include/ospfv3/detail.xml.i>
- </children>
+ <command>vtysh -c "show ipv6 ospf6 vrf $5 spf tree"</command>
</node>
- <tagNode name="prefix">
+ <tagNode name="area">
<properties>
- <help>Show interface prefix route specific information</help>
+ <help>Area ID (as an IPv4 notation)</help>
<completionHelp>
- <list>&lt;h:h:h:h:h:h:h:h&gt; &lt;h:h:h:h:h:h:h:h/x&gt;</list>
+ <path>protocols ospfv3 area</path>
</completionHelp>
</properties>
- <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
+ <command>vtysh -c "show ipv6 ospf6 vrf $5 area $7 spf tree"</command>
<children>
- #include <include/ospfv3/detail.xml.i>
- <node name="match">
+ <tagNode name="router">
<properties>
- <help>Matched interface prefix information</help>
- </properties>
- <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
- </node>
- </children>
- </tagNode>
- </children>
- </tagNode>
- <node name="linkstate">
- <properties>
- <help>Show OSPFv3 linkstate routing information</help>
- </properties>
- <children>
- #include <include/ospfv3/detail.xml.i>
- <tagNode name="network">
- <properties>
- <help>Show linkstate Network information</help>
- <completionHelp>
- <list>&lt;x.x.x.x&gt;</list>
- </completionHelp>
- </properties>
- <children>
- <node name="node.tag">
- <properties>
- <help>Specify Link state ID as IPv4 address notation</help>
+ <help> Simulate view point (Router ID)</help>
<completionHelp>
<list>&lt;x.x.x.x&gt;</list>
</completionHelp>
</properties>
- <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
- </node>
+ <command>vtysh -c "show ipv6 ospf6 vrf $5 simulate spf-tree $9 $6 $7"</command>
+ </tagNode>
</children>
</tagNode>
- <tagNode name="router">
- <properties>
- <help>Show linkstate Router information</help>
- <completionHelp>
- <list>&lt;x.x.x.x&gt;</list>
- </completionHelp>
- </properties>
- <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
- </tagNode>
- </children>
- </node>
- <node name="neighbor">
- <properties>
- <help>Show OSPFv3 neighbor information</help>
- </properties>
- <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
- <children>
- #include <include/ospfv3/detail.xml.i>
- <node name="drchoice">
- <properties>
- <help>Show neighbor DR choice information</help>
- </properties>
- <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
- </node>
+ #include <include/ospfv3/border-routers.xml.i>
+ #include <include/ospfv3/database.xml.i>
+ #include <include/ospfv3/interface.xml.i>
+ #include <include/ospfv3/linkstate.xml.i>
+ #include <include/ospfv3/neighbor.xml.i>
+ #include <include/ospfv3/redistribute.xml.i>
+ #include <include/ospfv3/route.xml.i>
</children>
- </node>
- <node name="redistribute">
- <properties>
- <help>Show OSPFv3 redistribute external information</help>
- </properties>
- <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
- </node>
- <node name="route">
- <properties>
- <help>Show OSPFv3 routing table information</help>
- </properties>
- <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
- <children>
- <node name="external-1">
- <properties>
- <help>Show Type-1 External route information</help>
- </properties>
- <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
- <children>
- #include <include/ospfv3/detail.xml.i>
- </children>
- </node>
- <node name="external-2">
- <properties>
- <help>Show Type-2 External route information</help>
- </properties>
- <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
- <children>
- #include <include/ospfv3/detail.xml.i>
- </children>
- </node>
- <node name="inter-area">
- <properties>
- <help>Show Inter-Area route information</help>
- </properties>
- <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
- <children>
- #include <include/ospfv3/detail.xml.i>
- </children>
- </node>
- <node name="intra-area">
- <properties>
- <help>Show Intra-Area route information</help>
- </properties>
- <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
- <children>
- #include <include/ospfv3/detail.xml.i>
- </children>
- </node>
- #include <include/ospfv3/detail.xml.i>
- <node name="summary">
- <properties>
- <help>Show route table summary</help>
- </properties>
- <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
- </node>
- </children>
- </node>
- <tagNode name="route">
+ </tagNode>
+ <leafNode name="vrfs">
<properties>
- <help>Show specified route/prefix information</help>
- <completionHelp>
- <list>&lt;h:h:h:h:h:h:h:h&gt; &lt;h:h:h:h:h:h:h:h/x&gt;</list>
- </completionHelp>
+ <help>Show OSPFv3 VRFs</help>
</properties>
<command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
- <children>
- <node name="longer">
- <properties>
- <help>Show routes longer than specified prefix</help>
- </properties>
- <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
- </node>
- <node name="match">
- <properties>
- <help>Show routes matching specified prefix</help>
- </properties>
- <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
- <children>
- #include <include/ospfv3/detail.xml.i>
- </children>
- </node>
- </children>
- </tagNode>
+ </leafNode>
</children>
</node>
</children>
diff --git a/op-mode-definitions/show-ipv6-route.xml.in b/op-mode-definitions/show-ipv6-route.xml.in
index 8624574ac..2c5024991 100644
--- a/op-mode-definitions/show-ipv6-route.xml.in
+++ b/op-mode-definitions/show-ipv6-route.xml.in
@@ -13,12 +13,7 @@
</properties>
<command>vtysh -c "show ipv6 route"</command>
<children>
- <node name="bgp">
- <properties>
- <help>Show IPv6 BGP routes</help>
- </properties>
- <command>vtysh -c "show ipv6 route bgp"</command>
- </node>
+ #include <include/show-route-bgp.xml.i>
<node name="cache">
<properties>
<help>Show kernel IPv6 route cache</help>
@@ -34,12 +29,7 @@
</properties>
<command>ip -s -f inet6 route list cache $5</command>
</tagNode>
- <node name="connected">
- <properties>
- <help>Show IPv6 connected routes</help>
- </properties>
- <command>vtysh -c "show ipv6 route connected"</command>
- </node>
+ #include <include/show-route-connected.xml.i>
<node name="forward">
<properties>
<help>Show kernel IPv6 route table</help>
@@ -55,71 +45,36 @@
</properties>
<command>ip -s -f inet6 route list $5</command>
</tagNode>
- <node name="isis">
- <properties>
- <help>Show IPv6 IS-IS routes</help>
- </properties>
- <command>vtysh -c "show ipv6 route isis"</command>
- </node>
- <node name="kernel">
- <properties>
- <help>Show IPv6 Kernel routes</help>
- </properties>
- <command>vtysh -c "show ipv6 route kernel"</command>
- </node>
- <node name="ospfv3">
- <properties>
- <help>Show IPv6 OSPF routes</help>
- </properties>
- <command>vtysh -c "show ipv6 route ospf6"</command>
- </node>
- <node name="ripng">
- <properties>
- <help>Show IPv6 RIPNG routes</help>
- </properties>
- <command>vtysh -c "show ipv6 route ripng"</command>
- </node>
- <node name="static">
- <properties>
- <help>Show IPv6 static routes</help>
- </properties>
- <command>vtysh -c "show ipv6 route static"</command>
- </node>
- <node name="summary">
- <properties>
- <help>Show IPv6 routes summary</help>
- </properties>
- <command>vtysh -c "show ipv6 route summary"</command>
- </node>
- <node name="table">
- <properties>
- <help>Show IPv6 routes in policy tables</help>
- </properties>
- <command>vtysh -c "show ipv6 route table all"</command>
- </node>
- <tagNode name="table">
- <properties>
- <help>Show IPv6 routes in specific policy table</help>
- <completionHelp>
- <path>protocols static table</path>
- </completionHelp>
- </properties>
- <command>vtysh -c "show ipv6 route table $5"</command>
- </tagNode>
- <node name="vrf">
- <properties>
- <help>Show IPv6 routes in VRFs</help>
- </properties>
- <command>vtysh -c "show ipv6 route vrf all"</command>
- </node>
+ #include <include/show-route-isis.xml.i>
+ #include <include/show-route-kernel.xml.i>
+ #include <include/show-route-ospfv3.xml.i>
+ #include <include/show-route-ripng.xml.i>
+ #include <include/show-route-static.xml.i>
+ #include <include/show-route-summary.xml.i>
+ #include <include/show-route-table.xml.i>
+ #include <include/show-route-tag.xml.i>
<tagNode name="vrf">
<properties>
- <help>Show IPv6 routes in specific VRF</help>
+ <help>Show IPv6 routes in VRF</help>
<completionHelp>
+ <list>all</list>
<path>vrf name</path>
</completionHelp>
</properties>
- <command>vtysh -c "show ipv6 route vrf $5"</command>
+ <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
+ <children>
+ #include <include/show-route-bgp.xml.i>
+ #include <include/show-route-connected.xml.i>
+ #include <include/show-route-isis.xml.i>
+ #include <include/show-route-kernel.xml.i>
+ #include <include/show-route-ospfv3.xml.i>
+ #include <include/show-route-ripng.xml.i>
+ #include <include/show-route-static.xml.i>
+ #include <include/show-route-summary.xml.i>
+ #include <include/show-route-supernets-only.xml.i>
+ #include <include/show-route-table.xml.i>
+ #include <include/show-route-tag.xml.i>
+ </children>
</tagNode>
</children>
</node>
diff --git a/op-mode-definitions/show-netns.xml.in b/op-mode-definitions/show-netns.xml.in
new file mode 100644
index 000000000..8d5072d4e
--- /dev/null
+++ b/op-mode-definitions/show-netns.xml.in
@@ -0,0 +1,13 @@
+<?xml version="1.0"?>
+<interfaceDefinition>
+ <node name="show">
+ <children>
+ <node name="netns">
+ <properties>
+ <help>Show network namespace information</help>
+ </properties>
+ <command>ip netns ls</command>
+ </node>
+ </children>
+ </node>
+</interfaceDefinition>
diff --git a/op-mode-definitions/show-protocols.xml.in b/op-mode-definitions/show-protocols.xml.in
index 48bfa10dc..698001b76 100644
--- a/op-mode-definitions/show-protocols.xml.in
+++ b/op-mode-definitions/show-protocols.xml.in
@@ -7,7 +7,6 @@
<help>Show protocol specific information</help>
</properties>
<children>
- #include <include/bfd-common.xml.i>
<node name="static">
<properties>
<help>Show static protocol parameters</help>
diff --git a/python/vyos/base.py b/python/vyos/base.py
index 4e23714e5..c78045548 100644
--- a/python/vyos/base.py
+++ b/python/vyos/base.py
@@ -1,4 +1,4 @@
-# Copyright 2018 VyOS maintainers and contributors <maintainers@vyos.io>
+# Copyright 2018-2021 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
@@ -13,6 +13,11 @@
# 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/>.
+from textwrap import fill
class ConfigError(Exception):
- pass
+ def __init__(self, message):
+ # Reformat the message and trim it to 72 characters in length
+ message = fill(message, width=72)
+ # Call the base class constructor with the parameters it needs
+ super().__init__(message)
diff --git a/python/vyos/configdict.py b/python/vyos/configdict.py
index 425a2e416..d974a7565 100644
--- a/python/vyos/configdict.py
+++ b/python/vyos/configdict.py
@@ -459,7 +459,10 @@ def get_interface_dict(config, base, ifname=''):
# Only add defaults if interface is not about to be deleted - this is
# to keep a cleaner config dict.
if 'deleted' not in dict:
- dict['vif'][vif] = dict_merge(default_vif_values, vif_config)
+ address = leaf_node_changed(config, ['vif', vif, 'address'])
+ if address: dict['vif'][vif].update({'address_old' : address})
+
+ dict['vif'][vif] = dict_merge(default_vif_values, dict['vif'][vif])
# XXX: T2665: blend in proper DHCPv6-PD default values
dict['vif'][vif] = T2665_set_dhcpv6pd_defaults(dict['vif'][vif])
@@ -480,7 +483,11 @@ def get_interface_dict(config, base, ifname=''):
# Only add defaults if interface is not about to be deleted - this is
# to keep a cleaner config dict.
if 'deleted' not in dict:
- dict['vif_s'][vif_s] = dict_merge(default_vif_s_values, vif_s_config)
+ address = leaf_node_changed(config, ['vif-s', vif_s, 'address'])
+ if address: dict['vif_s'][vif_s].update({'address_old' : address})
+
+ dict['vif_s'][vif_s] = dict_merge(default_vif_s_values,
+ dict['vif_s'][vif_s])
# XXX: T2665: blend in proper DHCPv6-PD default values
dict['vif_s'][vif_s] = T2665_set_dhcpv6pd_defaults(dict['vif_s'][vif_s])
@@ -499,8 +506,12 @@ def get_interface_dict(config, base, ifname=''):
# Only add defaults if interface is not about to be deleted - this is
# to keep a cleaner config dict.
if 'deleted' not in dict:
+ address = leaf_node_changed(config, ['vif-s', vif_s, 'vif-c', vif_c, 'address'])
+ if address: dict['vif_s'][vif_s]['vif_c'][vif_c].update(
+ {'address_old' : address})
+
dict['vif_s'][vif_s]['vif_c'][vif_c] = dict_merge(
- default_vif_c_values, vif_c_config)
+ default_vif_c_values, dict['vif_s'][vif_s]['vif_c'][vif_c])
# XXX: T2665: blend in proper DHCPv6-PD default values
dict['vif_s'][vif_s]['vif_c'][vif_c] = T2665_set_dhcpv6pd_defaults(
dict['vif_s'][vif_s]['vif_c'][vif_c])
diff --git a/python/vyos/configquery.py b/python/vyos/configquery.py
index b981463e4..5b097b312 100644
--- a/python/vyos/configquery.py
+++ b/python/vyos/configquery.py
@@ -18,16 +18,15 @@ A small library that allows querying existence or value(s) of config
settings from op mode, and execution of arbitrary op mode commands.
'''
-import re
-import json
-from copy import deepcopy
+import os
from subprocess import STDOUT
-import vyos.util
-import vyos.xml
+from vyos.util import popen, boot_configuration_complete
from vyos.config import Config
-from vyos.configtree import ConfigTree
-from vyos.configsource import ConfigSourceSession
+from vyos.configsource import ConfigSourceSession, ConfigSourceString
+from vyos.defaults import directories
+
+config_file = os.path.join(directories['config'], 'config.boot')
class ConfigQueryError(Exception):
pass
@@ -58,21 +57,21 @@ class CliShellApiConfigQuery(GenericConfigQuery):
def exists(self, path: list):
cmd = ' '.join(path)
- (_, err) = vyos.util.popen(f'cli-shell-api existsActive {cmd}')
+ (_, err) = popen(f'cli-shell-api existsActive {cmd}')
if err:
return False
return True
def value(self, path: list):
cmd = ' '.join(path)
- (out, err) = vyos.util.popen(f'cli-shell-api returnActiveValue {cmd}')
+ (out, err) = popen(f'cli-shell-api returnActiveValue {cmd}')
if err:
raise ConfigQueryError('No value for given path')
return out
def values(self, path: list):
cmd = ' '.join(path)
- (out, err) = vyos.util.popen(f'cli-shell-api returnActiveValues {cmd}')
+ (out, err) = popen(f'cli-shell-api returnActiveValues {cmd}')
if err:
raise ConfigQueryError('No values for given path')
return out
@@ -81,25 +80,36 @@ class ConfigTreeQuery(GenericConfigQuery):
def __init__(self):
super().__init__()
- config_source = ConfigSourceSession()
- self.configtree = Config(config_source=config_source)
+ if boot_configuration_complete():
+ config_source = ConfigSourceSession()
+ self.config = Config(config_source=config_source)
+ else:
+ try:
+ with open(config_file) as f:
+ config_string = f.read()
+ except OSError as err:
+ raise ConfigQueryError('No config file available') from err
+
+ config_source = ConfigSourceString(running_config_text=config_string,
+ session_config_text=config_string)
+ self.config = Config(config_source=config_source)
def exists(self, path: list):
- return self.configtree.exists(path)
+ return self.config.exists(path)
def value(self, path: list):
- return self.configtree.return_value(path)
+ return self.config.return_value(path)
def values(self, path: list):
- return self.configtree.return_values(path)
+ return self.config.return_values(path)
def list_nodes(self, path: list):
- return self.configtree.list_nodes(path)
+ return self.config.list_nodes(path)
def get_config_dict(self, path=[], effective=False, key_mangling=None,
get_first_key=False, no_multi_convert=False,
no_tag_node_value_mangle=False):
- return self.configtree.get_config_dict(path, effective=effective,
+ return self.config.get_config_dict(path, effective=effective,
key_mangling=key_mangling, get_first_key=get_first_key,
no_multi_convert=no_multi_convert,
no_tag_node_value_mangle=no_tag_node_value_mangle)
@@ -110,7 +120,7 @@ class VbashOpRun(GenericOpRun):
def run(self, path: list, **kwargs):
cmd = ' '.join(path)
- (out, err) = vyos.util.popen(f'/opt/vyatta/bin/vyatta-op-cmd-wrapper {cmd}', stderr=STDOUT, **kwargs)
+ (out, err) = popen(f'/opt/vyatta/bin/vyatta-op-cmd-wrapper {cmd}', stderr=STDOUT, **kwargs)
if err:
raise ConfigQueryError(out)
return out
diff --git a/python/vyos/configsource.py b/python/vyos/configsource.py
index b0981d25e..a0f6a46b5 100644
--- a/python/vyos/configsource.py
+++ b/python/vyos/configsource.py
@@ -19,6 +19,7 @@ import re
import subprocess
from vyos.configtree import ConfigTree
+from vyos.util import boot_configuration_complete
class VyOSError(Exception):
"""
@@ -117,7 +118,7 @@ class ConfigSourceSession(ConfigSource):
# Running config can be obtained either from op or conf mode, it always succeeds
# once the config system is initialized during boot;
# before initialization, set to empty string
- if os.path.isfile('/tmp/vyos-config-status'):
+ if boot_configuration_complete():
try:
running_config_text = self._run([self._cli_shell_api, '--show-active-only', '--show-show-defaults', '--show-ignore-edit', 'showConfig'])
except VyOSError:
diff --git a/python/vyos/defaults.py b/python/vyos/defaults.py
index 00b14a985..c77b695bd 100644
--- a/python/vyos/defaults.py
+++ b/python/vyos/defaults.py
@@ -29,6 +29,8 @@ directories = {
"vyos_udev_dir": "/run/udev/vyos"
}
+config_status = '/tmp/vyos-config-status'
+
cfg_group = 'vyattacfg'
cfg_vintage = 'vyos'
@@ -44,8 +46,9 @@ https_data = {
api_data = {
'listen_address' : '127.0.0.1',
'port' : '8080',
- 'strict' : 'false',
- 'debug' : 'false',
+ 'socket' : False,
+ 'strict' : False,
+ 'debug' : False,
'api_keys' : [ {"id": "testapp", "key": "qwerty"} ]
}
diff --git a/python/vyos/hostsd_client.py b/python/vyos/hostsd_client.py
index 303b6ea47..f31ef51cf 100644
--- a/python/vyos/hostsd_client.py
+++ b/python/vyos/hostsd_client.py
@@ -79,6 +79,18 @@ class Client(object):
msg = {'type': 'forward_zones', 'op': 'get'}
return self._communicate(msg)
+ def add_authoritative_zones(self, data):
+ msg = {'type': 'authoritative_zones', 'op': 'add', 'data': data}
+ self._communicate(msg)
+
+ def delete_authoritative_zones(self, data):
+ msg = {'type': 'authoritative_zones', 'op': 'delete', 'data': data}
+ self._communicate(msg)
+
+ def get_authoritative_zones(self):
+ msg = {'type': 'authoritative_zones', 'op': 'get'}
+ return self._communicate(msg)
+
def add_search_domains(self, data):
msg = {'type': 'search_domains', 'op': 'add', 'data': data}
self._communicate(msg)
diff --git a/python/vyos/ifconfig/interface.py b/python/vyos/ifconfig/interface.py
index edc99d6f7..91c7f0c33 100755
--- a/python/vyos/ifconfig/interface.py
+++ b/python/vyos/ifconfig/interface.py
@@ -37,6 +37,7 @@ from vyos.util import mac2eui64
from vyos.util import dict_search
from vyos.util import read_file
from vyos.util import get_interface_config
+from vyos.util import get_interface_namespace
from vyos.util import is_systemd_service_active
from vyos.template import is_ipv4
from vyos.template import is_ipv6
@@ -135,6 +136,9 @@ class Interface(Control):
'validate': assert_mtu,
'shellcmd': 'ip link set dev {ifname} mtu {value}',
},
+ 'netns': {
+ 'shellcmd': 'ip link set dev {ifname} netns {value}',
+ },
'vrf': {
'convert': lambda v: f'master {v}' if v else 'nomaster',
'shellcmd': 'ip link set dev {ifname} {value}',
@@ -512,6 +516,35 @@ class Interface(Control):
if prev_state == 'up':
self.set_admin_state('up')
+ def del_netns(self, netns):
+ """
+ Remove interface from given NETNS.
+ """
+
+ # If NETNS does not exist then there is nothing to delete
+ if not os.path.exists(f'/run/netns/{netns}'):
+ return None
+
+ # As a PoC we only allow 'dummy' interfaces
+ if 'dum' not in self.ifname:
+ return None
+
+ # Check if interface realy exists in namespace
+ if get_interface_namespace(self.ifname) != None:
+ self._cmd(f'ip netns exec {get_interface_namespace(self.ifname)} ip link del dev {self.ifname}')
+ return
+
+ def set_netns(self, netns):
+ """
+ Add interface from given NETNS.
+
+ Example:
+ >>> from vyos.ifconfig import Interface
+ >>> Interface('dum0').set_netns('foo')
+ """
+
+ self.set_interface('netns', netns)
+
def set_vrf(self, vrf):
"""
Add/Remove interface from given VRF instance.
@@ -1264,8 +1297,8 @@ class Interface(Control):
source_if = next(iter(self._config['is_mirror_intf']))
config = self._config['is_mirror_intf'][source_if].get('mirror', None)
- # Check configuration stored by old perl code before delete T3782
- if not 'redirect' in self._config:
+ # Check configuration stored by old perl code before delete T3782/T4056
+ if not 'redirect' in self._config and not 'traffic_policy' in self._config:
# Please do not clear the 'set $? = 0 '. It's meant to force a return of 0
# Remove existing mirroring rules
delete_tc_cmd = f'tc qdisc del dev {source_if} handle ffff: ingress 2> /dev/null;'
@@ -1346,6 +1379,16 @@ class Interface(Control):
if mac:
self.set_mac(mac)
+ # If interface is connected to NETNS we don't have to check all other
+ # settings like MTU/IPv6/sysctl values, etc.
+ # Since the interface is pushed onto a separate logical stack
+ # Configure NETNS
+ if dict_search('netns', config) != None:
+ self.set_netns(config.get('netns', ''))
+ return
+ else:
+ self.del_netns(config.get('netns', ''))
+
# Update interface description
self.set_alias(config.get('description', ''))
diff --git a/python/vyos/ifconfig/vxlan.py b/python/vyos/ifconfig/vxlan.py
index d73fb47b8..0c5282db4 100644
--- a/python/vyos/ifconfig/vxlan.py
+++ b/python/vyos/ifconfig/vxlan.py
@@ -54,18 +54,21 @@ class VXLANIf(Interface):
# arguments used by iproute2. For more information please refer to:
# - https://man7.org/linux/man-pages/man8/ip-link.8.html
mapping = {
- 'source_address' : 'local',
- 'source_interface' : 'dev',
- 'remote' : 'remote',
'group' : 'group',
+ 'external' : 'external',
+ 'gpe' : 'gpe',
'parameters.ip.dont_fragment': 'df set',
'parameters.ip.tos' : 'tos',
'parameters.ip.ttl' : 'ttl',
'parameters.ipv6.flowlabel' : 'flowlabel',
'parameters.nolearning' : 'nolearning',
+ 'remote' : 'remote',
+ 'source_address' : 'local',
+ 'source_interface' : 'dev',
+ 'vni' : 'id',
}
- cmd = 'ip link add {ifname} type {type} id {vni} dstport {port}'
+ cmd = 'ip link add {ifname} type {type} dstport {port}'
for vyos_key, iproute2_key in mapping.items():
# dict_search will return an empty dict "{}" for valueless nodes like
# "parameters.nolearning" - thus we need to test the nodes existence
diff --git a/python/vyos/range_regex.py b/python/vyos/range_regex.py
new file mode 100644
index 000000000..a8190d140
--- /dev/null
+++ b/python/vyos/range_regex.py
@@ -0,0 +1,142 @@
+'''Copyright (c) 2013, Dmitry Voronin
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+this list of conditions and the following disclaimer in the documentation
+and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+'''
+import math
+
+# coding=utf8
+
+# Split range to ranges that has its unique pattern.
+# Example for 12-345:
+#
+# 12- 19: 1[2-9]
+# 20- 99: [2-9]\d
+# 100-299: [1-2]\d{2}
+# 300-339: 3[0-3]\d
+# 340-345: 34[0-5]
+
+def range_to_regex(inpt_range):
+ if isinstance(inpt_range, str):
+ range_list = inpt_range.split('-')
+ # Check input arguments
+ if len(range_list) == 2:
+ # The first element in range must be higher then the second
+ if int(range_list[0]) < int(range_list[1]):
+ return regex_for_range(int(range_list[0]), int(range_list[1]))
+
+ return None
+
+def bounded_regex_for_range(min_, max_):
+ return r'\b({})\b'.format(regex_for_range(min_, max_))
+
+def regex_for_range(min_, max_):
+ """
+ > regex_for_range(12, 345)
+ '1[2-9]|[2-9]\d|[1-2]\d{2}|3[0-3]\d|34[0-5]'
+ """
+ positive_subpatterns = []
+ negative_subpatterns = []
+
+ if min_ < 0:
+ min__ = 1
+ if max_ < 0:
+ min__ = abs(max_)
+ max__ = abs(min_)
+
+ negative_subpatterns = split_to_patterns(min__, max__)
+ min_ = 0
+
+ if max_ >= 0:
+ positive_subpatterns = split_to_patterns(min_, max_)
+
+ negative_only_subpatterns = ['-' + val for val in negative_subpatterns if val not in positive_subpatterns]
+ positive_only_subpatterns = [val for val in positive_subpatterns if val not in negative_subpatterns]
+ intersected_subpatterns = ['-?' + val for val in negative_subpatterns if val in positive_subpatterns]
+
+ subpatterns = negative_only_subpatterns + intersected_subpatterns + positive_only_subpatterns
+ return '|'.join(subpatterns)
+
+
+def split_to_patterns(min_, max_):
+ subpatterns = []
+
+ start = min_
+ for stop in split_to_ranges(min_, max_):
+ subpatterns.append(range_to_pattern(start, stop))
+ start = stop + 1
+
+ return subpatterns
+
+
+def split_to_ranges(min_, max_):
+ stops = {max_}
+
+ nines_count = 1
+ stop = fill_by_nines(min_, nines_count)
+ while min_ <= stop < max_:
+ stops.add(stop)
+
+ nines_count += 1
+ stop = fill_by_nines(min_, nines_count)
+
+ zeros_count = 1
+ stop = fill_by_zeros(max_ + 1, zeros_count) - 1
+ while min_ < stop <= max_:
+ stops.add(stop)
+
+ zeros_count += 1
+ stop = fill_by_zeros(max_ + 1, zeros_count) - 1
+
+ stops = list(stops)
+ stops.sort()
+
+ return stops
+
+
+def fill_by_nines(integer, nines_count):
+ return int(str(integer)[:-nines_count] + '9' * nines_count)
+
+
+def fill_by_zeros(integer, zeros_count):
+ return integer - integer % 10 ** zeros_count
+
+
+def range_to_pattern(start, stop):
+ pattern = ''
+ any_digit_count = 0
+
+ for start_digit, stop_digit in zip(str(start), str(stop)):
+ if start_digit == stop_digit:
+ pattern += start_digit
+ elif start_digit != '0' or stop_digit != '9':
+ pattern += '[{}-{}]'.format(start_digit, stop_digit)
+ else:
+ any_digit_count += 1
+
+ if any_digit_count:
+ pattern += r'\d'
+
+ if any_digit_count > 1:
+ pattern += '{{{}}}'.format(any_digit_count)
+
+ return pattern \ No newline at end of file
diff --git a/python/vyos/remote.py b/python/vyos/remote.py
index 732ef76b7..aa62ac60d 100644
--- a/python/vyos/remote.py
+++ b/python/vyos/remote.py
@@ -74,21 +74,6 @@ class SourceAdapter(HTTPAdapter):
num_pools=connections, maxsize=maxsize,
block=block, source_address=self._source_pair)
-class WrappedFile:
- def __init__(self, obj, size=None, chunk_size=CHUNK_SIZE):
- self._obj = obj
- self._progress = size and make_incremental_progressbar(chunk_size / size)
- def read(self, size=-1):
- if self._progress:
- next(self._progress)
- self._obj.read(size)
- def write(self, size=-1):
- if self._progress:
- next(self._progress)
- self._obj.write(size)
- def __getattr__(self, attr):
- return getattr(self._obj, attr)
-
def check_storage(path, size):
"""
@@ -241,11 +226,9 @@ class HttpC:
# Abort early if the destination is inaccessible.
r.raise_for_status()
# If the request got redirected, keep the last URL we ended up with.
+ final_urlstring = r.url
if r.history:
- final_urlstring = r.history[-1].url
print_error('Redirecting to ' + final_urlstring)
- else:
- final_urlstring = self.urlstring
# Check for the prospective file size.
try:
size = int(r.headers['Content-Length'])
@@ -266,10 +249,9 @@ class HttpC:
shutil.copyfileobj(r.raw, f)
def upload(self, location: str):
- size = os.path.getsize(location) if self.progressbar else None
- # Keep in mind that `data` can be a file-like or iterable object.
- with self._establish() as s, file(location, 'rb') as f:
- s.post(self.urlstring, data=WrappedFile(f, size), allow_redirects=True)
+ # Does not yet support progressbars.
+ with self._establish() as s, open(location, 'rb') as f:
+ s.post(self.urlstring, data=f, allow_redirects=True)
class TftpC:
diff --git a/python/vyos/template.py b/python/vyos/template.py
index e20890e25..2987fcd0e 100644
--- a/python/vyos/template.py
+++ b/python/vyos/template.py
@@ -152,6 +152,16 @@ def bracketize_ipv6(address):
return f'[{address}]'
return address
+@register_filter('dot_colon_to_dash')
+def dot_colon_to_dash(text):
+ """ Replace dot and colon to dash for string
+ Example:
+ 192.0.2.1 => 192-0-2-1, 2001:db8::1 => 2001-db8--1
+ """
+ text = text.replace(":", "-")
+ text = text.replace(".", "-")
+ return text
+
@register_filter('netmask_from_cidr')
def netmask_from_cidr(prefix):
""" Take CIDR prefix and convert the prefix length to a "subnet mask".
@@ -481,6 +491,20 @@ def get_openvpn_ncp_ciphers(ciphers):
out.append(cipher)
return ':'.join(out).upper()
+@register_filter('snmp_auth_oid')
+def snmp_auth_oid(type):
+ if type not in ['md5', 'sha', 'aes', 'des', 'none']:
+ raise ValueError()
+
+ OIDs = {
+ 'md5' : '.1.3.6.1.6.3.10.1.1.2',
+ 'sha' : '.1.3.6.1.6.3.10.1.1.3',
+ 'aes' : '.1.3.6.1.6.3.10.1.2.4',
+ 'des' : '.1.3.6.1.6.3.10.1.2.2',
+ 'none': '.1.3.6.1.6.3.10.1.2.1'
+ }
+ return OIDs[type]
+
@register_filter('nft_action')
def nft_action(vyos_action):
if vyos_action == 'accept':
diff --git a/python/vyos/util.py b/python/vyos/util.py
index d8e83ab8d..954c6670d 100644
--- a/python/vyos/util.py
+++ b/python/vyos/util.py
@@ -794,6 +794,24 @@ def get_interface_address(interface):
tmp = loads(cmd(f'ip -d -j addr show {interface}'))[0]
return tmp
+def get_interface_namespace(iface):
+ """
+ Returns wich netns the interface belongs to
+ """
+ from json import loads
+ # Check if netns exist
+ tmp = loads(cmd(f'ip --json netns ls'))
+ if len(tmp) == 0:
+ return None
+
+ for ns in tmp:
+ namespace = f'{ns["name"]}'
+ # Search interface in each netns
+ data = loads(cmd(f'ip netns exec {namespace} ip -j link show'))
+ for compare in data:
+ if iface == compare["ifname"]:
+ return namespace
+
def get_all_vrfs():
""" Return a dictionary of all system wide known VRF instances """
from json import loads
@@ -961,3 +979,12 @@ def is_wwan_connected(interface):
# return True/False if interface is in connected state
return dict_search('modem.generic.state', tmp) == 'connected'
+
+def boot_configuration_complete() -> bool:
+ """ Check if the boot config loader has completed
+ """
+ from vyos.defaults import config_status
+
+ if os.path.isfile(config_status):
+ return True
+ return False
diff --git a/smoketest/configs/bgp-big-as-cloud b/smoketest/configs/bgp-big-as-cloud
index 694243d1e..10660ec87 100644
--- a/smoketest/configs/bgp-big-as-cloud
+++ b/smoketest/configs/bgp-big-as-cloud
@@ -1819,6 +1819,12 @@ system {
}
version 9
}
+ sflow {
+ agent-address auto
+ server 1.2.3.4 {
+ port 1234
+ }
+ }
syslog-facility daemon
}
host-name vyos
diff --git a/smoketest/scripts/cli/base_interfaces_test.py b/smoketest/scripts/cli/base_interfaces_test.py
index 447c1db30..9de961249 100644
--- a/smoketest/scripts/cli/base_interfaces_test.py
+++ b/smoketest/scripts/cli/base_interfaces_test.py
@@ -171,10 +171,10 @@ class BasicInterfaceTest:
def test_add_multiple_ip_addresses(self):
# Add address
for intf in self._interfaces:
+ for option in self._options.get(intf, []):
+ self.cli_set(self._base_path + [intf] + option.split())
for addr in self._test_addr:
self.cli_set(self._base_path + [intf, 'address', addr])
- for option in self._options.get(intf, []):
- self.cli_set(self._base_path + [intf] + option.split())
self.cli_commit()
@@ -297,6 +297,23 @@ class BasicInterfaceTest:
self.assertEqual(Interface(vif).get_admin_state(), 'up')
+ # T4064: Delete interface addresses, keep VLAN interface
+ for interface in self._interfaces:
+ base = self._base_path + [interface]
+ for vlan in self._vlan_range:
+ base = self._base_path + [interface, 'vif', vlan]
+ self.cli_delete(base + ['address'])
+
+ self.cli_commit()
+
+ # Verify no IP address is assigned
+ for interface in self._interfaces:
+ for vlan in self._vlan_range:
+ vif = f'{intf}.{vlan}'
+ for address in self._test_addr:
+ self.assertFalse(is_intf_addr_assigned(vif, address))
+
+
def test_vif_8021q_mtu_limits(self):
# XXX: This testcase is not allowed to run as first testcase, reason
# is the Wireless test will first load the wifi kernel hwsim module
@@ -493,6 +510,24 @@ class BasicInterfaceTest:
tmp = get_interface_config(vif)
self.assertEqual(tmp['mtu'], int(self._mtu))
+
+ # T4064: Delete interface addresses, keep VLAN interface
+ for interface in self._interfaces:
+ base = self._base_path + [interface]
+ for vif_s in self._qinq_range:
+ for vif_c in self._vlan_range:
+ self.cli_delete(self._base_path + [interface, 'vif-s', vif_s, 'vif-c', vif_c, 'address'])
+
+ self.cli_commit()
+ # Verify no IP address is assigned
+ for interface in self._interfaces:
+ base = self._base_path + [interface]
+ for vif_s in self._qinq_range:
+ for vif_c in self._vlan_range:
+ vif = f'{interface}.{vif_s}.{vif_c}'
+ for address in self._test_addr:
+ self.assertFalse(is_intf_addr_assigned(vif, address))
+
# T3972: remove vif-c interfaces from vif-s
for interface in self._interfaces:
base = self._base_path + [interface]
diff --git a/smoketest/scripts/cli/test_configd_init.py b/smoketest/scripts/cli/test_configd_init.py
new file mode 100755
index 000000000..5dec89963
--- /dev/null
+++ b/smoketest/scripts/cli/test_configd_init.py
@@ -0,0 +1,38 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2021 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 unittest
+from time import sleep
+
+from vyos.util import cmd, is_systemd_service_running
+
+class TestConfigdInit(unittest.TestCase):
+ def setUp(self):
+ self.running_state = is_systemd_service_running('vyos-configd.service')
+
+ def test_configd_init(self):
+ if not self.running_state:
+ cmd('sudo systemctl start vyos-configd.service')
+ # allow time for init to succeed/fail
+ sleep(2)
+ self.assertTrue(is_systemd_service_running('vyos-configd.service'))
+
+ def tearDown(self):
+ if not self.running_state:
+ cmd('sudo systemctl stop vyos-configd.service')
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_interfaces_netns.py b/smoketest/scripts/cli/test_interfaces_netns.py
new file mode 100755
index 000000000..9975a6b09
--- /dev/null
+++ b/smoketest/scripts/cli/test_interfaces_netns.py
@@ -0,0 +1,83 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2021 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 re
+import os
+import json
+import unittest
+
+from netifaces import interfaces
+from base_vyostest_shim import VyOSUnitTestSHIM
+
+from vyos.configsession import ConfigSession
+from vyos.configsession import ConfigSessionError
+from vyos.ifconfig import Interface
+from vyos.ifconfig import Section
+from vyos.util import cmd
+
+base_path = ['netns']
+namespaces = ['mgmt', 'front', 'back', 'ams-ix']
+
+class NETNSTest(VyOSUnitTestSHIM.TestCase):
+
+ def setUp(self):
+ self._interfaces = ['dum10', 'dum12', 'dum50']
+
+ def test_create_netns(self):
+ for netns in namespaces:
+ base = base_path + ['name', netns]
+ self.cli_set(base)
+
+ # commit changes
+ self.cli_commit()
+
+ netns_list = cmd('ip netns ls')
+
+ # Verify NETNS configuration
+ for netns in namespaces:
+ self.assertTrue(netns in netns_list)
+
+
+ def test_netns_assign_interface(self):
+ netns = 'foo'
+ self.cli_set(['netns', 'name', netns])
+
+ # Set
+ for iface in self._interfaces:
+ self.cli_set(['interfaces', 'dummy', iface, 'netns', netns])
+
+ # commit changes
+ self.cli_commit()
+
+ netns_iface_list = cmd(f'sudo ip netns exec {netns} ip link show')
+
+ for iface in self._interfaces:
+ self.assertTrue(iface in netns_iface_list)
+
+ # Delete
+ for iface in self._interfaces:
+ self.cli_delete(['interfaces', 'dummy', iface, 'netns', netns])
+
+ # commit changes
+ self.cli_commit()
+
+ netns_iface_list = cmd(f'sudo ip netns exec {netns} ip link show')
+
+ for iface in self._interfaces:
+ self.assertNotIn(iface, netns_iface_list)
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_interfaces_vxlan.py b/smoketest/scripts/cli/test_interfaces_vxlan.py
index f63c850d8..9278adadd 100755
--- a/smoketest/scripts/cli/test_interfaces_vxlan.py
+++ b/smoketest/scripts/cli/test_interfaces_vxlan.py
@@ -16,6 +16,7 @@
import unittest
+from vyos.configsession import ConfigSessionError
from vyos.ifconfig import Interface
from vyos.util import get_interface_config
@@ -78,6 +79,9 @@ class VXLANInterfaceTest(BasicInterfaceTest.TestCase):
label = options['linkinfo']['info_data']['label']
self.assertIn(f'parameters ipv6 flowlabel {label}', self._options[interface])
+ if any('external' in s for s in self._options[interface]):
+ self.assertTrue(options['linkinfo']['info_data']['external'])
+
self.assertEqual('vxlan', options['linkinfo']['info_kind'])
self.assertEqual('set', options['linkinfo']['info_data']['df'])
self.assertEqual(f'0x{tos}', options['linkinfo']['info_data']['tos'])
@@ -85,5 +89,36 @@ class VXLANInterfaceTest(BasicInterfaceTest.TestCase):
self.assertEqual(Interface(interface).get_admin_state(), 'up')
ttl += 10
+ def test_vxlan_external(self):
+ interface = 'vxlan0'
+ source_address = '192.0.2.1'
+ self.cli_set(self._base_path + [interface, 'external'])
+ self.cli_set(self._base_path + [interface, 'source-address', source_address])
+
+ # Both 'VNI' and 'external' can not be specified at the same time.
+ self.cli_set(self._base_path + [interface, 'vni', '111'])
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+ self.cli_delete(self._base_path + [interface, 'vni'])
+
+ # Now add some more interfaces - this must fail and a CLI error needs
+ # to be generated as Linux can only handle one VXLAN tunnel when using
+ # external mode.
+ for intf in self._interfaces:
+ for option in self._options.get(intf, []):
+ self.cli_set(self._base_path + [intf] + option.split())
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+
+ # Remove those test interfaces again
+ for intf in self._interfaces:
+ self.cli_delete(self._base_path + [intf])
+
+ self.cli_commit()
+
+ options = get_interface_config(interface)
+ self.assertTrue(options['linkinfo']['info_data']['external'])
+ self.assertEqual('vxlan', options['linkinfo']['info_kind'])
+
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_protocols_bfd.py b/smoketest/scripts/cli/test_protocols_bfd.py
index d33a64301..fdc254a05 100755
--- a/smoketest/scripts/cli/test_protocols_bfd.py
+++ b/smoketest/scripts/cli/test_protocols_bfd.py
@@ -31,7 +31,8 @@ peers = {
'intv_tx' : '600',
'multihop' : '',
'source_addr': '192.0.2.254',
- },
+ 'profile' : 'foo-bar-baz',
+ },
'192.0.2.20' : {
'echo_mode' : '',
'intv_echo' : '100',
@@ -40,16 +41,18 @@ peers = {
'intv_tx' : '333',
'passive' : '',
'shutdown' : '',
+ 'profile' : 'foo',
'source_intf': dum_if,
- },
- '2001:db8::a' : {
+ },
+ '2001:db8::1000:1' : {
'source_addr': '2001:db8::1',
'vrf' : vrf_name,
- },
- '2001:db8::b' : {
+ },
+ '2001:db8::2000:1' : {
'source_addr': '2001:db8::1',
'multihop' : '',
- },
+ 'profile' : 'baz_foo',
+ },
}
profiles = {
@@ -61,7 +64,12 @@ profiles = {
'intv_tx' : '333',
'shutdown' : '',
},
- 'bar' : {
+ 'foo-bar-baz' : {
+ 'intv_mult' : '4',
+ 'intv_rx' : '400',
+ 'intv_tx' : '400',
+ },
+ 'baz_foo' : {
'intv_mult' : '102',
'intv_rx' : '444',
'passive' : '',
@@ -106,7 +114,7 @@ class TestProtocolsBFD(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify FRR bgpd configuration
- frrconfig = self.getFRRconfig('bfd')
+ frrconfig = self.getFRRconfig('bfd', daemon=PROCESS_NAME)
for peer, peer_config in peers.items():
tmp = f'peer {peer}'
if 'multihop' in peer_config:
@@ -119,7 +127,7 @@ class TestProtocolsBFD(VyOSUnitTestSHIM.TestCase):
tmp += f' vrf {peer_config["vrf"]}'
self.assertIn(tmp, frrconfig)
- peerconfig = self.getFRRconfig(f' peer {peer}', end='')
+ peerconfig = self.getFRRconfig(f' peer {peer}', end='', daemon=PROCESS_NAME)
if 'echo_mode' in peer_config:
self.assertIn(f'echo-mode', peerconfig)
@@ -142,8 +150,6 @@ class TestProtocolsBFD(VyOSUnitTestSHIM.TestCase):
self.cli_delete(['vrf', 'name', vrf_name])
def test_bfd_profile(self):
- peer = '192.0.2.10'
-
for profile, profile_config in profiles.items():
if 'echo_mode' in profile_config:
self.cli_set(base_path + ['profile', profile, 'echo-mode'])
@@ -160,7 +166,20 @@ class TestProtocolsBFD(VyOSUnitTestSHIM.TestCase):
if 'shutdown' in profile_config:
self.cli_set(base_path + ['profile', profile, 'shutdown'])
- self.cli_set(base_path + ['peer', peer, 'profile', list(profiles)[0]])
+ for peer, peer_config in peers.items():
+ if 'profile' in peer_config:
+ self.cli_set(base_path + ['peer', peer, 'profile', peer_config["profile"] + 'wrong'])
+ if 'source_addr' in peer_config:
+ self.cli_set(base_path + ['peer', peer, 'source', 'address', peer_config["source_addr"]])
+ if 'source_intf' in peer_config:
+ self.cli_set(base_path + ['peer', peer, 'source', 'interface', peer_config["source_intf"]])
+
+ # BFD profile does not exist!
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+ for peer, peer_config in peers.items():
+ if 'profile' in peer_config:
+ self.cli_set(base_path + ['peer', peer, 'profile', peer_config["profile"]])
# commit changes
self.cli_commit()
@@ -169,22 +188,27 @@ class TestProtocolsBFD(VyOSUnitTestSHIM.TestCase):
for profile, profile_config in profiles.items():
config = self.getFRRconfig(f' profile {profile}', endsection='^ !')
if 'echo_mode' in profile_config:
- self.assertIn(f'echo-mode', config)
+ self.assertIn(f' echo-mode', config)
if 'intv_echo' in profile_config:
- self.assertIn(f'echo receive-interval {profile_config["intv_echo"]}', config)
- self.assertIn(f'echo transmit-interval {profile_config["intv_echo"]}', config)
+ self.assertIn(f' echo receive-interval {profile_config["intv_echo"]}', config)
+ self.assertIn(f' echo transmit-interval {profile_config["intv_echo"]}', config)
if 'intv_mult' in profile_config:
- self.assertIn(f'detect-multiplier {profile_config["intv_mult"]}', config)
+ self.assertIn(f' detect-multiplier {profile_config["intv_mult"]}', config)
if 'intv_rx' in profile_config:
- self.assertIn(f'receive-interval {profile_config["intv_rx"]}', config)
+ self.assertIn(f' receive-interval {profile_config["intv_rx"]}', config)
if 'intv_tx' in profile_config:
- self.assertIn(f'transmit-interval {profile_config["intv_tx"]}', config)
+ self.assertIn(f' transmit-interval {profile_config["intv_tx"]}', config)
if 'passive' in profile_config:
- self.assertIn(f'passive-mode', config)
+ self.assertIn(f' passive-mode', config)
if 'shutdown' in profile_config:
- self.assertIn(f'shutdown', config)
+ self.assertIn(f' shutdown', config)
else:
self.assertNotIn(f'shutdown', config)
+ for peer, peer_config in peers.items():
+ peerconfig = self.getFRRconfig(f' peer {peer}', end='', daemon=PROCESS_NAME)
+ if 'profile' in peer_config:
+ self.assertIn(f' profile {peer_config["profile"]}', peerconfig)
+
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_protocols_bgp.py b/smoketest/scripts/cli/test_protocols_bgp.py
index 16284ed01..d7230baf4 100755
--- a/smoketest/scripts/cli/test_protocols_bgp.py
+++ b/smoketest/scripts/cli/test_protocols_bgp.py
@@ -32,9 +32,11 @@ prefix_list_in = 'pfx-foo-in'
prefix_list_out = 'pfx-foo-out'
prefix_list_in6 = 'pfx-foo-in6'
prefix_list_out6 = 'pfx-foo-out6'
+bfd_profile = 'foo-bar-baz'
neighbor_config = {
'192.0.2.1' : {
+ 'bfd' : '',
'cap_dynamic' : '',
'cap_ext_next' : '',
'remote_as' : '100',
@@ -51,23 +53,30 @@ neighbor_config = {
'addpath_all' : '',
},
'192.0.2.2' : {
+ 'bfd_profile' : bfd_profile,
'remote_as' : '200',
'shutdown' : '',
'no_cap_nego' : '',
'port' : '667',
'cap_strict' : '',
+ 'advertise_map': route_map_in,
+ 'non_exist_map': route_map_out,
'pfx_list_in' : prefix_list_in,
'pfx_list_out' : prefix_list_out,
'no_send_comm_std' : '',
},
'192.0.2.3' : {
+ 'advertise_map': route_map_in,
'description' : 'foo bar baz',
'remote_as' : '200',
'passive' : '',
'multi_hop' : '5',
'update_src' : 'lo',
+ 'peer_group' : 'foo',
},
'2001:db8::1' : {
+ 'advertise_map': route_map_in,
+ 'exist_map' : route_map_out,
'cap_dynamic' : '',
'cap_ext_next' : '',
'remote_as' : '123',
@@ -83,6 +92,7 @@ neighbor_config = {
'route_map_out': route_map_out,
'no_send_comm_std' : '',
'addpath_per_as' : '',
+ 'peer_group' : 'foo-bar',
},
'2001:db8::2' : {
'remote_as' : '456',
@@ -93,11 +103,15 @@ neighbor_config = {
'pfx_list_in' : prefix_list_in6,
'pfx_list_out' : prefix_list_out6,
'no_send_comm_ext' : '',
+ 'peer_group' : 'foo-bar_baz',
},
}
peer_group_config = {
'foo' : {
+ 'advertise_map': route_map_in,
+ 'exist_map' : route_map_out,
+ 'bfd' : '',
'remote_as' : '100',
'passive' : '',
'password' : 'VyOS-Secure123',
@@ -105,7 +119,8 @@ peer_group_config = {
'cap_over' : '',
'ttl_security': '5',
},
- 'bar' : {
+ 'foo-bar' : {
+ 'advertise_map': route_map_in,
'description' : 'foo peer bar group',
'remote_as' : '200',
'shutdown' : '',
@@ -115,7 +130,10 @@ peer_group_config = {
'pfx_list_out' : prefix_list_out,
'no_send_comm_ext' : '',
},
- 'baz' : {
+ 'foo-bar_baz' : {
+ 'advertise_map': route_map_in,
+ 'non_exist_map': route_map_out,
+ 'bfd_profile' : bfd_profile,
'cap_dynamic' : '',
'cap_ext_next' : '',
'remote_as' : '200',
@@ -128,23 +146,34 @@ peer_group_config = {
}
class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
- def setUp(self):
- self.cli_set(['policy', 'route-map', route_map_in, 'rule', '10', 'action', 'permit'])
- self.cli_set(['policy', 'route-map', route_map_out, 'rule', '10', 'action', 'permit'])
- self.cli_set(['policy', 'prefix-list', prefix_list_in, 'rule', '10', 'action', 'permit'])
- self.cli_set(['policy', 'prefix-list', prefix_list_in, 'rule', '10', 'prefix', '192.0.2.0/25'])
- self.cli_set(['policy', 'prefix-list', prefix_list_out, 'rule', '10', 'action', 'permit'])
- self.cli_set(['policy', 'prefix-list', prefix_list_out, 'rule', '10', 'prefix', '192.0.2.128/25'])
-
- self.cli_set(['policy', 'prefix-list6', prefix_list_in6, 'rule', '10', 'action', 'permit'])
- self.cli_set(['policy', 'prefix-list6', prefix_list_in6, 'rule', '10', 'prefix', '2001:db8:1000::/64'])
- self.cli_set(['policy', 'prefix-list6', prefix_list_out6, 'rule', '10', 'action', 'deny'])
- self.cli_set(['policy', 'prefix-list6', prefix_list_out6, 'rule', '10', 'prefix', '2001:db8:2000::/64'])
+ @classmethod
+ def setUpClass(cls):
+ super(cls, cls).setUpClass()
+
+ # ensure we can also run this test on a live system - so lets clean
+ # out the current configuration :)
+ cls.cli_delete(cls, base_path)
+
+ cls.cli_set(cls, ['policy', 'route-map', route_map_in, 'rule', '10', 'action', 'permit'])
+ cls.cli_set(cls, ['policy', 'route-map', route_map_out, 'rule', '10', 'action', 'permit'])
+ cls.cli_set(cls, ['policy', 'prefix-list', prefix_list_in, 'rule', '10', 'action', 'permit'])
+ cls.cli_set(cls, ['policy', 'prefix-list', prefix_list_in, 'rule', '10', 'prefix', '192.0.2.0/25'])
+ cls.cli_set(cls, ['policy', 'prefix-list', prefix_list_out, 'rule', '10', 'action', 'permit'])
+ cls.cli_set(cls, ['policy', 'prefix-list', prefix_list_out, 'rule', '10', 'prefix', '192.0.2.128/25'])
+
+ cls.cli_set(cls, ['policy', 'prefix-list6', prefix_list_in6, 'rule', '10', 'action', 'permit'])
+ cls.cli_set(cls, ['policy', 'prefix-list6', prefix_list_in6, 'rule', '10', 'prefix', '2001:db8:1000::/64'])
+ cls.cli_set(cls, ['policy', 'prefix-list6', prefix_list_out6, 'rule', '10', 'action', 'deny'])
+ cls.cli_set(cls, ['policy', 'prefix-list6', prefix_list_out6, 'rule', '10', 'prefix', '2001:db8:2000::/64'])
+
+ @classmethod
+ def tearDownClass(cls):
+ cls.cli_delete(cls, ['policy'])
+ def setUp(self):
self.cli_set(base_path + ['local-as', ASN])
def tearDown(self):
- self.cli_delete(['policy'])
self.cli_delete(['vrf'])
self.cli_delete(base_path)
self.cli_commit()
@@ -154,6 +183,11 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
def verify_frr_config(self, peer, peer_config, frrconfig):
# recurring patterns to verify for both a simple neighbor and a peer-group
+ if 'bfd' in peer_config:
+ self.assertIn(f' neighbor {peer} bfd', frrconfig)
+ if 'bfd_profile' in peer_config:
+ self.assertIn(f' neighbor {peer} bfd profile {peer_config["bfd_profile"]}', frrconfig)
+ self.assertIn(f' neighbor {peer} bfd check-control-plane-failure', frrconfig)
if 'cap_dynamic' in peer_config:
self.assertIn(f' neighbor {peer} capability dynamic', frrconfig)
if 'cap_ext_next' in peer_config:
@@ -198,7 +232,13 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
self.assertIn(f' neighbor {peer} addpath-tx-all-paths', frrconfig)
if 'addpath_per_as' in peer_config:
self.assertIn(f' neighbor {peer} addpath-tx-bestpath-per-AS', frrconfig)
-
+ if 'advertise_map' in peer_config:
+ base = f' neighbor {peer} advertise-map {peer_config["advertise_map"]}'
+ if 'exist_map' in peer_config:
+ base = f'{base} exist-map {peer_config["exist_map"]}'
+ if 'non_exist_map' in peer_config:
+ base = f'{base} non-exist-map {peer_config["non_exist_map"]}'
+ self.assertIn(base, frrconfig)
def test_bgp_01_simple(self):
router_id = '127.0.0.1'
@@ -208,6 +248,8 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
max_path_v4ibgp = '4'
max_path_v6 = '8'
max_path_v6ibgp = '16'
+ cond_adv_timer = '30'
+ min_hold_time = '2'
self.cli_set(base_path + ['parameters', 'router-id', router_id])
self.cli_set(base_path + ['parameters', 'log-neighbor-changes'])
@@ -229,6 +271,13 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
self.cli_set(base_path + ['parameters', 'bestpath', 'bandwidth', 'default-weight-for-missing'])
self.cli_set(base_path + ['parameters', 'bestpath', 'compare-routerid'])
+ self.cli_set(base_path + ['parameters', 'conditional-advertisement', 'timer', cond_adv_timer])
+ self.cli_set(base_path + ['parameters', 'fast-convergence'])
+ self.cli_set(base_path + ['parameters', 'minimum-holdtime', min_hold_time])
+ self.cli_set(base_path + ['parameters', 'reject-as-sets'])
+ self.cli_set(base_path + ['parameters', 'shutdown'])
+ self.cli_set(base_path + ['parameters', 'suppress-fib-pending'])
+
# AFI maximum path support
self.cli_set(base_path + ['address-family', 'ipv4-unicast', 'maximum-paths', 'ebgp', max_path_v4])
self.cli_set(base_path + ['address-family', 'ipv4-unicast', 'maximum-paths', 'ibgp', max_path_v4ibgp])
@@ -244,11 +293,17 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
self.assertIn(f' bgp router-id {router_id}', frrconfig)
self.assertIn(f' bgp log-neighbor-changes', frrconfig)
self.assertIn(f' bgp default local-preference {local_pref}', frrconfig)
+ self.assertIn(f' bgp conditional-advertisement timer {cond_adv_timer}', frrconfig)
+ self.assertIn(f' bgp fast-convergence', frrconfig)
self.assertIn(f' bgp graceful-restart stalepath-time {stalepath_time}', frrconfig)
self.assertIn(f' bgp graceful-shutdown', frrconfig)
self.assertIn(f' bgp bestpath as-path multipath-relax', frrconfig)
self.assertIn(f' bgp bestpath bandwidth default-weight-for-missing', frrconfig)
self.assertIn(f' bgp bestpath compare-routerid', frrconfig)
+ self.assertIn(f' bgp minimum-holdtime {min_hold_time}', frrconfig)
+ self.assertIn(f' bgp reject-as-sets', frrconfig)
+ self.assertIn(f' bgp shutdown', frrconfig)
+ self.assertIn(f' bgp suppress-fib-pending', frrconfig)
self.assertNotIn(f'bgp ebgp-requires-policy', frrconfig)
afiv4_config = self.getFRRconfig(' address-family ipv4 unicast')
@@ -270,6 +325,11 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
if 'adv_interv' in peer_config:
self.cli_set(base_path + ['neighbor', peer, 'advertisement-interval', peer_config["adv_interv"]])
+ if 'bfd' in peer_config:
+ self.cli_set(base_path + ['neighbor', peer, 'bfd'])
+ if 'bfd_profile' in peer_config:
+ self.cli_set(base_path + ['neighbor', peer, 'bfd', 'profile', peer_config["bfd_profile"]])
+ self.cli_set(base_path + ['neighbor', peer, 'bfd', 'check-control-plane-failure'])
if 'cap_dynamic' in peer_config:
self.cli_set(base_path + ['neighbor', peer, 'capability', 'dynamic'])
if 'cap_ext_next' in peer_config:
@@ -319,6 +379,20 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
if 'addpath_per_as' in peer_config:
self.cli_set(base_path + ['neighbor', peer, 'address-family', afi, 'addpath-tx-per-as'])
+ # Conditional advertisement
+ if 'advertise_map' in peer_config:
+ self.cli_set(base_path + ['neighbor', peer, 'address-family', afi, 'conditionally-advertise', 'advertise-map', peer_config["advertise_map"]])
+ # Either exist-map or non-exist-map needs to be specified
+ if 'exist_map' not in peer_config and 'non_exist_map' not in peer_config:
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+ self.cli_set(base_path + ['neighbor', peer, 'address-family', afi, 'conditionally-advertise', 'exist-map', route_map_in])
+
+ if 'exist_map' in peer_config:
+ self.cli_set(base_path + ['neighbor', peer, 'address-family', afi, 'conditionally-advertise', 'exist-map', peer_config["exist_map"]])
+ if 'non_exist_map' in peer_config:
+ self.cli_set(base_path + ['neighbor', peer, 'address-family', afi, 'conditionally-advertise', 'non-exist-map', peer_config["non_exist_map"]])
+
# commit changes
self.cli_commit()
@@ -339,6 +413,11 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
def test_bgp_03_peer_groups(self):
# Test out individual peer-group configuration items
for peer_group, config in peer_group_config.items():
+ if 'bfd' in config:
+ self.cli_set(base_path + ['peer-group', peer_group, 'bfd'])
+ if 'bfd_profile' in config:
+ self.cli_set(base_path + ['peer-group', peer_group, 'bfd', 'profile', config["bfd_profile"]])
+ self.cli_set(base_path + ['peer-group', peer_group, 'bfd', 'check-control-plane-failure'])
if 'cap_dynamic' in config:
self.cli_set(base_path + ['peer-group', peer_group, 'capability', 'dynamic'])
if 'cap_ext_next' in config:
@@ -382,6 +461,24 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
if 'addpath_per_as' in config:
self.cli_set(base_path + ['peer-group', peer_group, 'address-family', 'ipv4-unicast', 'addpath-tx-per-as'])
+ # Conditional advertisement
+ if 'advertise_map' in config:
+ self.cli_set(base_path + ['peer-group', peer_group, 'address-family', 'ipv4-unicast', 'conditionally-advertise', 'advertise-map', config["advertise_map"]])
+ # Either exist-map or non-exist-map needs to be specified
+ if 'exist_map' not in config and 'non_exist_map' not in config:
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+ self.cli_set(base_path + ['peer-group', peer_group, 'address-family', 'ipv4-unicast', 'conditionally-advertise', 'exist-map', route_map_in])
+
+ if 'exist_map' in config:
+ self.cli_set(base_path + ['peer-group', peer_group, 'address-family', 'ipv4-unicast', 'conditionally-advertise', 'exist-map', config["exist_map"]])
+ if 'non_exist_map' in config:
+ self.cli_set(base_path + ['peer-group', peer_group, 'address-family', 'ipv4-unicast', 'conditionally-advertise', 'non-exist-map', config["non_exist_map"]])
+
+ for peer, peer_config in neighbor_config.items():
+ if 'peer_group' in peer_config:
+ self.cli_set(base_path + ['neighbor', peer, 'peer-group', peer_config['peer_group']])
+
# commit changes
self.cli_commit()
@@ -393,6 +490,10 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
self.assertIn(f' neighbor {peer_group} peer-group', frrconfig)
self.verify_frr_config(peer, peer_config, frrconfig)
+ for peer, peer_config in neighbor_config.items():
+ if 'peer_group' in peer_config:
+ self.assertIn(f' neighbor {peer} peer-group {peer_config["peer_group"]}', frrconfig)
+
def test_bgp_04_afi_ipv4(self):
networks = {
@@ -753,4 +854,4 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
self.assertIn(f' exit-address-family', afi_config)
if __name__ == '__main__':
- unittest.main(verbosity=2) \ No newline at end of file
+ unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_protocols_isis.py b/smoketest/scripts/cli/test_protocols_isis.py
index e42040025..7f51c7178 100755
--- a/smoketest/scripts/cli/test_protocols_isis.py
+++ b/smoketest/scripts/cli/test_protocols_isis.py
@@ -198,17 +198,19 @@ class TestProtocolsISIS(VyOSUnitTestSHIM.TestCase):
self.assertIn(f' area-password clear {password}', tmp)
- def test_isis_06_spf_delay(self):
+ def test_isis_06_spf_delay_bfd(self):
network = 'point-to-point'
holddown = '10'
init_delay = '50'
long_delay = '200'
short_delay = '100'
time_to_learn = '75'
+ bfd_profile = 'isis-bfd'
self.cli_set(base_path + ['net', net])
for interface in self._interfaces:
self.cli_set(base_path + ['interface', interface, 'network', network])
+ self.cli_set(base_path + ['interface', interface, 'bfd', 'profile', bfd_profile])
self.cli_set(base_path + ['spf-delay-ietf', 'holddown', holddown])
# verify() - All types of spf-delay must be configured
@@ -244,6 +246,8 @@ class TestProtocolsISIS(VyOSUnitTestSHIM.TestCase):
self.assertIn(f' ip router isis {domain}', tmp)
self.assertIn(f' ipv6 router isis {domain}', tmp)
self.assertIn(f' isis network {network}', tmp)
+ self.assertIn(f' isis bfd', tmp)
+ self.assertIn(f' isis bfd profile {bfd_profile}', tmp)
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_protocols_mpls.py b/smoketest/scripts/cli/test_protocols_mpls.py
new file mode 100755
index 000000000..13d38d01b
--- /dev/null
+++ b/smoketest/scripts/cli/test_protocols_mpls.py
@@ -0,0 +1,117 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2021 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 unittest
+
+from base_vyostest_shim import VyOSUnitTestSHIM
+from vyos.configsession import ConfigSessionError
+from vyos.ifconfig import Section
+from vyos.util import process_named_running
+
+PROCESS_NAME = 'ldpd'
+base_path = ['protocols', 'mpls', 'ldp']
+
+peers = {
+ '192.0.2.10' : {
+ 'intv_rx' : '500',
+ 'intv_tx' : '600',
+ 'multihop' : '',
+ 'source_addr': '192.0.2.254',
+ },
+ '192.0.2.20' : {
+ 'echo_mode' : '',
+ 'intv_echo' : '100',
+ 'intv_mult' : '100',
+ 'intv_rx' : '222',
+ 'intv_tx' : '333',
+ 'passive' : '',
+ 'shutdown' : '',
+ },
+ '2001:db8::a' : {
+ 'source_addr': '2001:db8::1',
+ },
+ '2001:db8::b' : {
+ 'source_addr': '2001:db8::1',
+ 'multihop' : '',
+ },
+}
+
+profiles = {
+ 'foo' : {
+ 'echo_mode' : '',
+ 'intv_echo' : '100',
+ 'intv_mult' : '101',
+ 'intv_rx' : '222',
+ 'intv_tx' : '333',
+ 'shutdown' : '',
+ },
+ 'bar' : {
+ 'intv_mult' : '102',
+ 'intv_rx' : '444',
+ 'passive' : '',
+ },
+}
+
+class TestProtocolsMPLS(VyOSUnitTestSHIM.TestCase):
+ @classmethod
+ def setUpClass(cls):
+ super(cls, cls).setUpClass()
+
+ # ensure we can also run this test on a live system - so lets clean
+ # out the current configuration :)
+ cls.cli_delete(cls, base_path)
+
+ def tearDown(self):
+ self.cli_delete(base_path)
+ self.cli_commit()
+ # Check for running process
+ self.assertTrue(process_named_running(PROCESS_NAME))
+
+ def test_mpls_basic(self):
+ self.debug = True
+ router_id = '1.2.3.4'
+ transport_ipv4_addr = '5.6.7.8'
+ interfaces = Section.interfaces('ethernet')
+
+ self.cli_set(base_path + ['router-id', router_id])
+
+ # At least one LDP interface must be configured
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+ for interface in interfaces:
+ self.cli_set(base_path + ['interface', interface])
+
+ # LDP transport address missing
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+ self.cli_set(base_path + ['discovery', 'transport-ipv4-address', transport_ipv4_addr])
+
+ # Commit changes
+ self.cli_commit()
+
+ # Validate configuration
+ frrconfig = self.getFRRconfig('mpls ldp', daemon=PROCESS_NAME)
+ self.assertIn(f'mpls ldp', frrconfig)
+ self.assertIn(f' router-id {router_id}', frrconfig)
+
+ # Validate AFI IPv4
+ afiv4_config = self.getFRRconfig(' address-family ipv4', daemon=PROCESS_NAME)
+ self.assertIn(f' discovery transport-address {transport_ipv4_addr}', afiv4_config)
+ for interface in interfaces:
+ self.assertIn(f' interface {interface}', afiv4_config)
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2, failfast=True)
diff --git a/smoketest/scripts/cli/test_protocols_ospf.py b/smoketest/scripts/cli/test_protocols_ospf.py
index 04853c5fe..ee58b0fe2 100755
--- a/smoketest/scripts/cli/test_protocols_ospf.py
+++ b/smoketest/scripts/cli/test_protocols_ospf.py
@@ -33,14 +33,21 @@ route_map = 'foo-bar-baz10'
log = logging.getLogger('TestProtocolsOSPF')
class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase):
- def setUp(self):
- self.cli_set(['policy', 'route-map', route_map, 'rule', '10', 'action', 'permit'])
- self.cli_set(['policy', 'route-map', route_map, 'rule', '20', 'action', 'permit'])
+ @classmethod
+ def setUpClass(cls):
+ super(cls, cls).setUpClass()
+
+ cls.cli_set(cls, ['policy', 'route-map', route_map, 'rule', '10', 'action', 'permit'])
+ cls.cli_set(cls, ['policy', 'route-map', route_map, 'rule', '20', 'action', 'permit'])
+
+ @classmethod
+ def tearDownClass(cls):
+ cls.cli_delete(cls, ['policy', 'route-map', route_map])
+ super(cls, cls).tearDownClass()
def tearDown(self):
# Check for running process
self.assertTrue(process_named_running(PROCESS_NAME))
- self.cli_delete(['policy', 'route-map', route_map])
self.cli_delete(base_path)
self.cli_commit()
@@ -199,9 +206,15 @@ class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase):
self.cli_set(base_path + ['redistribute', protocol, 'route-map', route_map])
self.cli_set(base_path + ['redistribute', protocol, 'metric-type', metric_type])
+ # enable FRR debugging to find the root cause of failing testcases
+ cmd('touch /tmp/vyos.frr.debug')
+
# commit changes
self.cli_commit()
+ # disable FRR debugging
+ cmd('rm -f /tmp/vyos.frr.debug')
+
# Verify FRR ospfd configuration
frrconfig = self.getFRRconfig('router ospf')
try:
@@ -210,8 +223,7 @@ class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase):
self.assertIn(f' redistribute {protocol} metric {metric} metric-type {metric_type} route-map {route_map}', frrconfig)
except:
log.debug(frrconfig)
- log.debug(cmd('sudo cat /var/log/messages'))
- log.debug(cmd('vtysh -c "show run"'))
+ log.debug(cmd('sudo cat /tmp/vyos-configd-script-stdout'))
self.fail('Now we can hopefully see why OSPF fails!')
def test_ospf_08_virtual_link(self):
@@ -251,13 +263,14 @@ class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase):
cost = '150'
network = 'point-to-point'
priority = '200'
+ bfd_profile = 'vyos-test'
self.cli_set(base_path + ['passive-interface', 'default'])
for interface in interfaces:
base_interface = base_path + ['interface', interface]
self.cli_set(base_interface + ['authentication', 'plaintext-password', password])
self.cli_set(base_interface + ['bandwidth', bandwidth])
- self.cli_set(base_interface + ['bfd'])
+ self.cli_set(base_interface + ['bfd', 'profile', bfd_profile])
self.cli_set(base_interface + ['cost', cost])
self.cli_set(base_interface + ['mtu-ignore'])
self.cli_set(base_interface + ['network', network])
@@ -272,6 +285,7 @@ class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase):
self.assertIn(f'interface {interface}', config)
self.assertIn(f' ip ospf authentication-key {password}', config)
self.assertIn(f' ip ospf bfd', config)
+ self.assertIn(f' ip ospf bfd profile {bfd_profile}', config)
self.assertIn(f' ip ospf cost {cost}', config)
self.assertIn(f' ip ospf mtu-ignore', config)
self.assertIn(f' ip ospf network {network}', config)
diff --git a/smoketest/scripts/cli/test_protocols_ospfv3.py b/smoketest/scripts/cli/test_protocols_ospfv3.py
index f0557f640..1327fd910 100755
--- a/smoketest/scripts/cli/test_protocols_ospfv3.py
+++ b/smoketest/scripts/cli/test_protocols_ospfv3.py
@@ -18,16 +18,31 @@ import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
+from vyos.configsession import ConfigSessionError
from vyos.ifconfig import Section
from vyos.util import process_named_running
PROCESS_NAME = 'ospf6d'
base_path = ['protocols', 'ospfv3']
+route_map = 'foo-bar-baz-0815'
+
router_id = '192.0.2.1'
default_area = '0'
class TestProtocolsOSPFv3(VyOSUnitTestSHIM.TestCase):
+ @classmethod
+ def setUpClass(cls):
+ super(cls, cls).setUpClass()
+
+ cls.cli_set(cls, ['policy', 'route-map', route_map, 'rule', '10', 'action', 'permit'])
+ cls.cli_set(cls, ['policy', 'route-map', route_map, 'rule', '20', 'action', 'permit'])
+
+ @classmethod
+ def tearDownClass(cls):
+ cls.cli_delete(cls, ['policy', 'route-map', route_map])
+ super(cls, cls).tearDownClass()
+
def tearDown(self):
# Check for running process
self.assertTrue(process_named_running(PROCESS_NAME))
@@ -109,7 +124,9 @@ class TestProtocolsOSPFv3(VyOSUnitTestSHIM.TestCase):
for protocol in redistribute:
self.assertIn(f' redistribute {protocol} route-map {route_map}', frrconfig)
+
def test_ospfv3_04_interfaces(self):
+ bfd_profile = 'vyos-ipv6'
self.cli_set(base_path + ['parameters', 'router-id', router_id])
self.cli_set(base_path + ['area', default_area])
@@ -119,7 +136,7 @@ class TestProtocolsOSPFv3(VyOSUnitTestSHIM.TestCase):
interfaces = Section.interfaces('ethernet')
for interface in interfaces:
if_base = base_path + ['interface', interface]
- self.cli_set(if_base + ['bfd'])
+ self.cli_set(if_base + ['bfd', 'profile', bfd_profile])
self.cli_set(if_base + ['cost', cost])
self.cli_set(if_base + ['instance-id', '0'])
self.cli_set(if_base + ['mtu-ignore'])
@@ -142,6 +159,7 @@ class TestProtocolsOSPFv3(VyOSUnitTestSHIM.TestCase):
if_config = self.getFRRconfig(f'interface {interface}')
self.assertIn(f'interface {interface}', if_config)
self.assertIn(f' ipv6 ospf6 bfd', if_config)
+ self.assertIn(f' ipv6 ospf6 bfd profile {bfd_profile}', if_config)
self.assertIn(f' ipv6 ospf6 cost {cost}', if_config)
self.assertIn(f' ipv6 ospf6 mtu-ignore', if_config)
self.assertIn(f' ipv6 ospf6 network point-to-point', if_config)
@@ -168,7 +186,60 @@ class TestProtocolsOSPFv3(VyOSUnitTestSHIM.TestCase):
self.assertIn(f' area {area_stub_nosum} stub no-summary', frrconfig)
- def test_ospfv3_06_vrfs(self):
+ def test_ospfv3_06_area_nssa(self):
+ area_nssa = '1.1.1.1'
+ area_nssa_nosum = '2.2.2.2'
+ area_nssa_default = '3.3.3.3'
+
+ self.cli_set(base_path + ['area', area_nssa, 'area-type', 'nssa'])
+ self.cli_set(base_path + ['area', area_nssa, 'area-type', 'stub'])
+ # can only set one area-type per OSPFv3 area
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+ self.cli_delete(base_path + ['area', area_nssa, 'area-type', 'stub'])
+
+ self.cli_set(base_path + ['area', area_nssa_nosum, 'area-type', 'nssa', 'no-summary'])
+ self.cli_set(base_path + ['area', area_nssa_nosum, 'area-type', 'nssa', 'default-information-originate'])
+ self.cli_set(base_path + ['area', area_nssa_default, 'area-type', 'nssa', 'default-information-originate'])
+
+ # commit changes
+ self.cli_commit()
+
+ # Verify FRR ospfd configuration
+ frrconfig = self.getFRRconfig('router ospf6')
+ self.assertIn(f'router ospf6', frrconfig)
+ self.assertIn(f' area {area_nssa} nssa', frrconfig)
+ self.assertIn(f' area {area_nssa_nosum} nssa default-information-originate no-summary', frrconfig)
+ self.assertIn(f' area {area_nssa_default} nssa default-information-originate', frrconfig)
+
+
+ def test_ospfv3_07_default_originate(self):
+ seq = '100'
+ metric = '50'
+ metric_type = '1'
+
+ self.cli_set(base_path + ['default-information', 'originate', 'metric', metric])
+ self.cli_set(base_path + ['default-information', 'originate', 'metric-type', metric_type])
+ self.cli_set(base_path + ['default-information', 'originate', 'route-map', route_map])
+
+ # commit changes
+ self.cli_commit()
+
+ # Verify FRR ospfd configuration
+ frrconfig = self.getFRRconfig('router ospf6')
+ self.assertIn(f'router ospf6', frrconfig)
+ self.assertIn(f' default-information originate metric {metric} metric-type {metric_type} route-map {route_map}', frrconfig)
+
+ # Now set 'always'
+ self.cli_set(base_path + ['default-information', 'originate', 'always'])
+ self.cli_commit()
+
+ # Verify FRR ospfd configuration
+ frrconfig = self.getFRRconfig('router ospf6')
+ self.assertIn(f' default-information originate always metric {metric} metric-type {metric_type} route-map {route_map}', frrconfig)
+
+
+ def test_ospfv3_08_vrfs(self):
# It is safe to assume that when the basic VRF test works, all
# other OSPF related features work, as we entirely inherit the CLI
# templates and Jinja2 FRR template.
@@ -207,4 +278,4 @@ class TestProtocolsOSPFv3(VyOSUnitTestSHIM.TestCase):
self.cli_delete(['interfaces', 'ethernet', vrf_iface, 'vrf'])
if __name__ == '__main__':
- unittest.main(verbosity=2)
+ unittest.main(verbosity=2, failfast=True)
diff --git a/smoketest/scripts/cli/test_service_snmp.py b/smoketest/scripts/cli/test_service_snmp.py
index 058835c72..fc24fd54e 100755
--- a/smoketest/scripts/cli/test_service_snmp.py
+++ b/smoketest/scripts/cli/test_service_snmp.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2019-2020 VyOS maintainers and contributors
+# Copyright (C) 2019-2021 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
@@ -22,14 +22,25 @@ from base_vyostest_shim import VyOSUnitTestSHIM
from vyos.configsession import ConfigSessionError
from vyos.template import is_ipv4
from vyos.template import address_from_cidr
+from vyos.util import call
+from vyos.util import DEVNULL
from vyos.util import read_file
from vyos.util import process_named_running
+from vyos.version import get_version_data
PROCESS_NAME = 'snmpd'
SNMPD_CONF = '/etc/snmp/snmpd.conf'
base_path = ['service', 'snmp']
+snmpv3_group = 'default_group'
+snmpv3_view = 'default_view'
+snmpv3_view_oid = '1'
+snmpv3_user = 'vyos'
+snmpv3_auth_pw = 'vyos12345678'
+snmpv3_priv_pw = 'vyos87654321'
+snmpv3_engine_id = '000000000000000000000002'
+
def get_config_value(key):
tmp = read_file(SNMPD_CONF)
tmp = re.findall(r'\n?{}\s+(.*)'.format(key), tmp)
@@ -45,13 +56,22 @@ class TestSNMPService(VyOSUnitTestSHIM.TestCase):
cls.cli_delete(cls, base_path)
def tearDown(self):
+ # Check for running process
+ self.assertTrue(process_named_running(PROCESS_NAME))
+
# delete testing SNMP config
self.cli_delete(base_path)
self.cli_commit()
+ # Check for running process
+ self.assertFalse(process_named_running(PROCESS_NAME))
+
def test_snmp_basic(self):
dummy_if = 'dum7312'
dummy_addr = '100.64.0.1/32'
+ contact = 'maintainers@vyos.io'
+ location = 'QEMU'
+
self.cli_set(['interfaces', 'dummy', dummy_if, 'address', dummy_addr])
# Check if SNMP can be configured and service runs
@@ -71,8 +91,8 @@ class TestSNMPService(VyOSUnitTestSHIM.TestCase):
for addr in listen:
self.cli_set(base_path + ['listen-address', addr, 'port', port])
- self.cli_set(base_path + ['contact', 'maintainers@vyos.io'])
- self.cli_set(base_path + ['location', 'qemu'])
+ self.cli_set(base_path + ['contact', contact])
+ self.cli_set(base_path + ['location', location])
self.cli_commit()
@@ -82,7 +102,6 @@ class TestSNMPService(VyOSUnitTestSHIM.TestCase):
config = get_config_value('agentaddress')
expected = 'unix:/run/snmpd.socket'
self.assertIn(expected, config)
-
for addr in listen:
if is_ipv4(addr):
expected = f'udp:{addr}:{port}'
@@ -90,6 +109,16 @@ class TestSNMPService(VyOSUnitTestSHIM.TestCase):
expected = f'udp6:[{addr}]:{port}'
self.assertIn(expected, config)
+ config = get_config_value('sysDescr')
+ version_data = get_version_data()
+ self.assertEqual('VyOS ' + version_data['version'], config)
+
+ config = get_config_value('SysContact')
+ self.assertEqual(contact, config)
+
+ config = get_config_value('SysLocation')
+ self.assertEqual(location, config)
+
# Check for running process
self.assertTrue(process_named_running(PROCESS_NAME))
self.cli_delete(['interfaces', 'dummy', dummy_if])
@@ -98,8 +127,7 @@ class TestSNMPService(VyOSUnitTestSHIM.TestCase):
def test_snmpv3_sha(self):
# Check if SNMPv3 can be configured with SHA authentication
# and service runs
-
- self.cli_set(base_path + ['v3', 'engineid', '000000000000000000000002'])
+ self.cli_set(base_path + ['v3', 'engineid', snmpv3_engine_id])
self.cli_set(base_path + ['v3', 'group', 'default', 'mode', 'ro'])
# check validate() - a view must be created before this can be committed
with self.assertRaises(ConfigSessionError):
@@ -109,46 +137,52 @@ class TestSNMPService(VyOSUnitTestSHIM.TestCase):
self.cli_set(base_path + ['v3', 'group', 'default', 'view', 'default'])
# create user
- self.cli_set(base_path + ['v3', 'user', 'vyos', 'auth', 'plaintext-password', 'vyos12345678'])
- self.cli_set(base_path + ['v3', 'user', 'vyos', 'auth', 'type', 'sha'])
- self.cli_set(base_path + ['v3', 'user', 'vyos', 'privacy', 'plaintext-password', 'vyos12345678'])
- self.cli_set(base_path + ['v3', 'user', 'vyos', 'privacy', 'type', 'aes'])
- self.cli_set(base_path + ['v3', 'user', 'vyos', 'group', 'default'])
+ self.cli_set(base_path + ['v3', 'user', snmpv3_user, 'auth', 'plaintext-password', snmpv3_auth_pw])
+ self.cli_set(base_path + ['v3', 'user', snmpv3_user, 'auth', 'type', 'sha'])
+ self.cli_set(base_path + ['v3', 'user', snmpv3_user, 'privacy', 'plaintext-password', snmpv3_priv_pw])
+ self.cli_set(base_path + ['v3', 'user', snmpv3_user, 'privacy', 'type', 'aes'])
+ self.cli_set(base_path + ['v3', 'user', snmpv3_user, 'group', 'default'])
self.cli_commit()
# commit will alter the CLI values - check if they have been updated:
hashed_password = '4e52fe55fd011c9c51ae2c65f4b78ca93dcafdfe'
- tmp = self._session.show_config(base_path + ['v3', 'user', 'vyos', 'auth', 'encrypted-password']).split()[1]
+ tmp = self._session.show_config(base_path + ['v3', 'user', snmpv3_user, 'auth', 'encrypted-password']).split()[1]
self.assertEqual(tmp, hashed_password)
- tmp = self._session.show_config(base_path + ['v3', 'user', 'vyos', 'privacy', 'encrypted-password']).split()[1]
+ hashed_password = '54705c8de9e81fdf61ad7ac044fa8fe611ddff6b'
+ tmp = self._session.show_config(base_path + ['v3', 'user', snmpv3_user, 'privacy', 'encrypted-password']).split()[1]
self.assertEqual(tmp, hashed_password)
# TODO: read in config file and check values
- # Check for running process
- self.assertTrue(process_named_running(PROCESS_NAME))
+ # Try SNMPv3 connection
+ tmp = call(f'snmpwalk -v 3 -u {snmpv3_user} -a SHA -A {snmpv3_auth_pw} -x AES -X {snmpv3_priv_pw} -l authPriv 127.0.0.1', stdout=DEVNULL)
+ self.assertEqual(tmp, 0)
def test_snmpv3_md5(self):
# Check if SNMPv3 can be configured with MD5 authentication
# and service runs
+ self.cli_set(base_path + ['v3', 'engineid', snmpv3_engine_id])
- self.cli_set(base_path + ['v3', 'engineid', '000000000000000000000002'])
- self.cli_set(base_path + ['v3', 'group', 'default', 'mode', 'ro'])
- # check validate() - a view must be created before this can be comitted
+ # create user
+ self.cli_set(base_path + ['v3', 'user', snmpv3_user, 'auth', 'plaintext-password', snmpv3_auth_pw])
+ self.cli_set(base_path + ['v3', 'user', snmpv3_user, 'auth', 'type', 'md5'])
+ self.cli_set(base_path + ['v3', 'user', snmpv3_user, 'privacy', 'plaintext-password', snmpv3_priv_pw])
+ self.cli_set(base_path + ['v3', 'user', snmpv3_user, 'privacy', 'type', 'des'])
+
+ # check validate() - user requires a group to be created
with self.assertRaises(ConfigSessionError):
self.cli_commit()
+ self.cli_set(base_path + ['v3', 'user', 'vyos', 'group', snmpv3_group])
- self.cli_set(base_path + ['v3', 'view', 'default', 'oid', '1'])
- self.cli_set(base_path + ['v3', 'group', 'default', 'view', 'default'])
+ self.cli_set(base_path + ['v3', 'group', snmpv3_group, 'mode', 'ro'])
+ # check validate() - a view must be created before this can be comitted
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
- # create user
- self.cli_set(base_path + ['v3', 'user', 'vyos', 'auth', 'plaintext-password', 'vyos12345678'])
- self.cli_set(base_path + ['v3', 'user', 'vyos', 'auth', 'type', 'md5'])
- self.cli_set(base_path + ['v3', 'user', 'vyos', 'privacy', 'plaintext-password', 'vyos12345678'])
- self.cli_set(base_path + ['v3', 'user', 'vyos', 'privacy', 'type', 'des'])
- self.cli_set(base_path + ['v3', 'user', 'vyos', 'group', 'default'])
+ self.cli_set(base_path + ['v3', 'view', snmpv3_view, 'oid', snmpv3_view_oid])
+ self.cli_set(base_path + ['v3', 'group', snmpv3_group, 'view', snmpv3_view])
self.cli_commit()
@@ -157,13 +191,21 @@ class TestSNMPService(VyOSUnitTestSHIM.TestCase):
tmp = self._session.show_config(base_path + ['v3', 'user', 'vyos', 'auth', 'encrypted-password']).split()[1]
self.assertEqual(tmp, hashed_password)
+ hashed_password = 'e11c83f2c510540a3c4de84ee66de440'
tmp = self._session.show_config(base_path + ['v3', 'user', 'vyos', 'privacy', 'encrypted-password']).split()[1]
self.assertEqual(tmp, hashed_password)
- # TODO: read in config file and check values
-
- # Check for running process
- self.assertTrue(process_named_running(PROCESS_NAME))
+ tmp = read_file(SNMPD_CONF)
+ # views
+ self.assertIn(f'view {snmpv3_view} included .{snmpv3_view_oid}', tmp)
+ # group
+ self.assertIn(f'group {snmpv3_group} usm {snmpv3_user}', tmp)
+ # access
+ self.assertIn(f'access {snmpv3_group} "" usm auth exact {snmpv3_view} none none', tmp)
+
+ # Try SNMPv3 connection
+ tmp = call(f'snmpwalk -v 3 -u {snmpv3_user} -a MD5 -A {snmpv3_auth_pw} -x DES -X {snmpv3_priv_pw} -l authPriv 127.0.0.1', stdout=DEVNULL)
+ self.assertEqual(tmp, 0)
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_system_flow-accounting.py b/smoketest/scripts/cli/test_system_flow-accounting.py
index dfbfba94e..857df1be6 100755
--- a/smoketest/scripts/cli/test_system_flow-accounting.py
+++ b/smoketest/scripts/cli/test_system_flow-accounting.py
@@ -27,7 +27,7 @@ from vyos.util import read_file
PROCESS_NAME = 'uacctd'
base_path = ['system', 'flow-accounting']
-uacctd_conf = '/etc/pmacct/uacctd.conf'
+uacctd_conf = '/run/pmacct/uacctd.conf'
class TestSystemFlowAccounting(VyOSUnitTestSHIM.TestCase):
@classmethod
@@ -47,7 +47,10 @@ class TestSystemFlowAccounting(VyOSUnitTestSHIM.TestCase):
def test_basic(self):
buffer_size = '5' # MiB
+ syslog = 'all'
+
self.cli_set(base_path + ['buffer-size', buffer_size])
+ self.cli_set(base_path + ['syslog-facility', syslog])
# You need to configure at least one interface for flow-accounting
with self.assertRaises(ConfigSessionError):
@@ -84,8 +87,153 @@ class TestSystemFlowAccounting(VyOSUnitTestSHIM.TestCase):
tmp //= 1000
self.assertIn(f'plugin_buffer_size: {tmp}', uacctd)
- # Check for running process
- self.assertTrue(process_named_running(PROCESS_NAME))
+ # when 'disable-imt' is not configured on the CLI it must be present
+ self.assertIn(f'imt_path: /tmp/uacctd.pipe', uacctd)
+ self.assertIn(f'imt_mem_pools_number: 169', uacctd)
+ self.assertIn(f'syslog: {syslog}', uacctd)
+ self.assertIn(f'plugins: memory', uacctd)
+
+ def test_sflow(self):
+ sampling_rate = '4000'
+ source_address = '192.0.2.1'
+ dummy_if = 'dum3841'
+ agent_address = '192.0.2.2'
+
+ sflow_server = {
+ '1.2.3.4' : {
+ },
+ '5.6.7.8' : {
+ 'port' : '6000'
+ }
+ }
+
+ self.cli_set(['interfaces', 'dummy', dummy_if, 'address', agent_address + '/32'])
+ self.cli_set(base_path + ['disable-imt'])
+
+ # You need to configure at least one interface for flow-accounting
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+ for interface in Section.interfaces('ethernet'):
+ self.cli_set(base_path + ['interface', interface])
+
+
+ # You need to configure at least one sFlow or NetFlow protocol, or not
+ # set "disable-imt" for flow-accounting
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+
+ self.cli_set(base_path + ['sflow', 'agent-address', agent_address])
+ self.cli_set(base_path + ['sflow', 'sampling-rate', sampling_rate])
+ self.cli_set(base_path + ['sflow', 'source-address', source_address])
+ for server, server_config in sflow_server.items():
+ self.cli_set(base_path + ['sflow', 'server', server])
+ if 'port' in server_config:
+ self.cli_set(base_path + ['sflow', 'server', server, 'port', server_config['port']])
+
+ # commit changes
+ self.cli_commit()
+
+ uacctd = read_file(uacctd_conf)
+
+ # when 'disable-imt' is not configured on the CLI it must be present
+ self.assertNotIn(f'imt_path: /tmp/uacctd.pipe', uacctd)
+ self.assertNotIn(f'imt_mem_pools_number: 169', uacctd)
+ self.assertNotIn(f'plugins: memory', uacctd)
+
+ for server, server_config in sflow_server.items():
+ if 'port' in server_config:
+ self.assertIn(f'sfprobe_receiver[sf_{server}]: {server}', uacctd)
+ else:
+ self.assertIn(f'sfprobe_receiver[sf_{server}]: {server}:6343', uacctd)
+
+ self.assertIn(f'sfprobe_agentip[sf_{server}]: {agent_address}', uacctd)
+ self.assertIn(f'sampling_rate[sf_{server}]: {sampling_rate}', uacctd)
+ self.assertIn(f'sfprobe_source_ip[sf_{server}]: {source_address}', uacctd)
+
+ self.cli_delete(['interfaces', 'dummy', dummy_if])
+
+ def test_netflow(self):
+ engine_id = '33'
+ max_flows = '667'
+ sampling_rate = '100'
+ source_address = '192.0.2.1'
+ dummy_if = 'dum3842'
+ agent_address = '192.0.2.10'
+ version = '10'
+ tmo_expiry = '120'
+ tmo_flow = '1200'
+ tmo_icmp = '60'
+ tmo_max = '50000'
+ tmo_tcp_fin = '100'
+ tmo_tcp_generic = '120'
+ tmo_tcp_rst = '99'
+ tmo_udp = '10'
+
+ netflow_server = {
+ '11.22.33.44' : {
+ },
+ '55.66.77.88' : {
+ 'port' : '6000'
+ }
+ }
+
+ self.cli_set(['interfaces', 'dummy', dummy_if, 'address', agent_address + '/32'])
+
+ for interface in Section.interfaces('ethernet'):
+ self.cli_set(base_path + ['interface', interface])
+
+ self.cli_set(base_path + ['netflow', 'engine-id', engine_id])
+ self.cli_set(base_path + ['netflow', 'max-flows', max_flows])
+ self.cli_set(base_path + ['netflow', 'sampling-rate', sampling_rate])
+ self.cli_set(base_path + ['netflow', 'source-address', source_address])
+ self.cli_set(base_path + ['netflow', 'version', version])
+
+ # timeouts
+ self.cli_set(base_path + ['netflow', 'timeout', 'expiry-interval', tmo_expiry])
+ self.cli_set(base_path + ['netflow', 'timeout', 'flow-generic', tmo_flow])
+ self.cli_set(base_path + ['netflow', 'timeout', 'icmp', tmo_icmp])
+ self.cli_set(base_path + ['netflow', 'timeout', 'max-active-life', tmo_max])
+ self.cli_set(base_path + ['netflow', 'timeout', 'tcp-fin', tmo_tcp_fin])
+ self.cli_set(base_path + ['netflow', 'timeout', 'tcp-generic', tmo_tcp_generic])
+ self.cli_set(base_path + ['netflow', 'timeout', 'tcp-rst', tmo_tcp_rst])
+ self.cli_set(base_path + ['netflow', 'timeout', 'udp', tmo_udp])
+
+ # You need to configure at least one netflow server
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+
+ for server, server_config in netflow_server.items():
+ self.cli_set(base_path + ['netflow', 'server', server])
+ if 'port' in server_config:
+ self.cli_set(base_path + ['netflow', 'server', server, 'port', server_config['port']])
+
+ # commit changes
+ self.cli_commit()
+
+ uacctd = read_file(uacctd_conf)
+
+ tmp = []
+ tmp.append('memory')
+ for server, server_config in netflow_server.items():
+ tmp.append(f'nfprobe[nf_{server}]')
+ self.assertIn('plugins: ' + ','.join(tmp), uacctd)
+
+ for server, server_config in netflow_server.items():
+ self.assertIn(f'nfprobe_engine[nf_{server}]: {engine_id}', uacctd)
+ self.assertIn(f'nfprobe_maxflows[nf_{server}]: {max_flows}', uacctd)
+ self.assertIn(f'sampling_rate[nf_{server}]: {sampling_rate}', uacctd)
+ self.assertIn(f'nfprobe_source_ip[nf_{server}]: {source_address}', uacctd)
+ self.assertIn(f'nfprobe_version[nf_{server}]: {version}', uacctd)
+
+ if 'port' in server_config:
+ self.assertIn(f'nfprobe_receiver[nf_{server}]: {server}', uacctd)
+ else:
+ self.assertIn(f'nfprobe_receiver[nf_{server}]: {server}:2055', uacctd)
+
+ self.assertIn(f'nfprobe_timeouts[nf_{server}]: expint={tmo_expiry}:general={tmo_flow}:icmp={tmo_icmp}:maxlife={tmo_max}:tcp.fin={tmo_tcp_fin}:tcp={tmo_tcp_generic}:tcp.rst={tmo_tcp_rst}:udp={tmo_udp}', uacctd)
+
+
+ self.cli_delete(['interfaces', 'dummy', dummy_if])
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_system_logs.py b/smoketest/scripts/cli/test_system_logs.py
new file mode 100755
index 000000000..0c11c4663
--- /dev/null
+++ b/smoketest/scripts/cli/test_system_logs.py
@@ -0,0 +1,117 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2021 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 re
+import unittest
+from base_vyostest_shim import VyOSUnitTestSHIM
+from vyos.util import read_file
+
+# path to logrotate configs
+logrotate_atop_file = '/etc/logrotate.d/vyos-atop'
+logrotate_rsyslog_file = '/etc/logrotate.d/vyos-rsyslog'
+# default values
+default_atop_maxsize = '10M'
+default_atop_rotate = '10'
+default_rsyslog_size = '1M'
+default_rsyslog_rotate = '10'
+
+base_path = ['system', 'logs']
+
+
+def logrotate_config_parse(file_path):
+ # read the file
+ logrotate_config = read_file(file_path)
+ # create regex for parsing options
+ regex_options = re.compile(
+ r'(^\s+(?P<option_name_script>postrotate|prerotate|firstaction|lastaction|preremove)\n(?P<option_value_script>((?!endscript).)*)\n\s+endscript\n)|(^\s+(?P<option_name>[\S]+)([ \t]+(?P<option_value>\S+))*$)',
+ re.M | re.S)
+ # create empty dict for config
+ logrotate_config_dict = {}
+ # fill dictionary with actual config
+ for option in regex_options.finditer(logrotate_config):
+ option_name = option.group('option_name')
+ option_value = option.group('option_value')
+ option_name_script = option.group('option_name_script')
+ option_value_script = option.group('option_value_script')
+ if option_name:
+ logrotate_config_dict[option_name] = option_value
+ if option_name_script:
+ logrotate_config_dict[option_name_script] = option_value_script
+
+ # return config dictionary
+ return (logrotate_config_dict)
+
+
+class TestSystemLogs(VyOSUnitTestSHIM.TestCase):
+
+ def tearDown(self):
+ self.cli_delete(base_path)
+ self.cli_commit()
+
+ def test_logs_defaults(self):
+ # test with empty section for default values
+ self.cli_set(base_path)
+ self.cli_commit()
+
+ # read the config file and check content
+ logrotate_config_atop = logrotate_config_parse(logrotate_atop_file)
+ logrotate_config_rsyslog = logrotate_config_parse(
+ logrotate_rsyslog_file)
+ self.assertEqual(logrotate_config_atop['maxsize'], default_atop_maxsize)
+ self.assertEqual(logrotate_config_atop['rotate'], default_atop_rotate)
+ self.assertEqual(logrotate_config_rsyslog['size'], default_rsyslog_size)
+ self.assertEqual(logrotate_config_rsyslog['rotate'],
+ default_rsyslog_rotate)
+
+ def test_logs_atop_maxsize(self):
+ # test for maxsize option
+ self.cli_set(base_path + ['logrotate', 'atop', 'max-size', '50'])
+ self.cli_commit()
+
+ # read the config file and check content
+ logrotate_config = logrotate_config_parse(logrotate_atop_file)
+ self.assertEqual(logrotate_config['maxsize'], '50M')
+
+ def test_logs_atop_rotate(self):
+ # test for rotate option
+ self.cli_set(base_path + ['logrotate', 'atop', 'rotate', '50'])
+ self.cli_commit()
+
+ # read the config file and check content
+ logrotate_config = logrotate_config_parse(logrotate_atop_file)
+ self.assertEqual(logrotate_config['rotate'], '50')
+
+ def test_logs_rsyslog_size(self):
+ # test for size option
+ self.cli_set(base_path + ['logrotate', 'messages', 'max-size', '50'])
+ self.cli_commit()
+
+ # read the config file and check content
+ logrotate_config = logrotate_config_parse(logrotate_rsyslog_file)
+ self.assertEqual(logrotate_config['size'], '50M')
+
+ def test_logs_rsyslog_rotate(self):
+ # test for rotate option
+ self.cli_set(base_path + ['logrotate', 'messages', 'rotate', '50'])
+ self.cli_commit()
+
+ # read the config file and check content
+ logrotate_config = logrotate_config_parse(logrotate_rsyslog_file)
+ self.assertEqual(logrotate_config['rotate'], '50')
+
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2, failfast=True)
diff --git a/smoketest/scripts/cli/test_vpn_ipsec.py b/smoketest/scripts/cli/test_vpn_ipsec.py
index c710aec6e..1433c7329 100755
--- a/smoketest/scripts/cli/test_vpn_ipsec.py
+++ b/smoketest/scripts/cli/test_vpn_ipsec.py
@@ -111,9 +111,22 @@ rgiyCHemtMepq57Pl1Nmj49eEA==
"""
class TestVPNIPsec(VyOSUnitTestSHIM.TestCase):
- def setUp(self):
- self.cli_set(base_path + ['interface', f'{interface}.{vif}'])
+ @classmethod
+ def setUpClass(cls):
+ super(cls, cls).setUpClass()
+ # ensure we can also run this test on a live system - so lets clean
+ # out the current configuration :)
+ cls.cli_delete(cls, base_path)
+
+ cls.cli_set(cls, base_path + ['interface', f'{interface}.{vif}'])
+
+ @classmethod
+ def tearDownClass(cls):
+ super(cls, cls).tearDownClass()
+
+ cls.cli_delete(cls, base_path + ['interface', f'{interface}.{vif}'])
+ def setUp(self):
# Set IKE/ESP Groups
self.cli_set(base_path + ['esp-group', esp_group, 'proposal', '1', 'encryption', 'aes128'])
self.cli_set(base_path + ['esp-group', esp_group, 'proposal', '1', 'hash', 'sha1'])
@@ -127,7 +140,6 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase):
self.cli_delete(base_path)
self.cli_delete(tunnel_path)
- self.cli_delete(ethernet_path)
self.cli_commit()
# Check for no longer running process
@@ -158,6 +170,7 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase):
# Site to site
local_address = '192.0.2.10'
+ priority = '20'
peer_base_path = base_path + ['site-to-site', 'peer', peer_ip]
self.cli_set(peer_base_path + ['authentication', 'mode', 'pre-shared-secret'])
@@ -173,6 +186,10 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase):
self.cli_set(peer_base_path + ['tunnel', '1', 'remote', 'prefix', '172.17.11.0/24'])
self.cli_set(peer_base_path + ['tunnel', '1', 'remote', 'port', '443'])
+ self.cli_set(peer_base_path + ['tunnel', '2', 'local', 'prefix', '10.1.0.0/16'])
+ self.cli_set(peer_base_path + ['tunnel', '2', 'remote', 'prefix', '10.2.0.0/16'])
+ self.cli_set(peer_base_path + ['tunnel', '2', 'priority', priority])
+
self.cli_commit()
# Verify strongSwan configuration
@@ -187,8 +204,15 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase):
f'local_addrs = {local_address} # dhcp:no',
f'remote_addrs = {peer_ip}',
f'mode = tunnel',
+ f'peer_{peer_ip.replace(".","-")}_tunnel_1',
f'local_ts = 172.16.10.0/24[tcp/443],172.16.11.0/24[tcp/443]',
- f'remote_ts = 172.17.10.0/24[tcp/443],172.17.11.0/24[tcp/443]'
+ f'remote_ts = 172.17.10.0/24[tcp/443],172.17.11.0/24[tcp/443]',
+ f'mode = tunnel',
+ f'peer_{peer_ip.replace(".","-")}_tunnel_2',
+ f'local_ts = 10.1.0.0/16',
+ f'remote_ts = 10.2.0.0/16',
+ f'priority = {priority}',
+ f'mode = tunnel',
]
for line in swanctl_conf_lines:
self.assertIn(line, swanctl_conf)
diff --git a/src/conf_mode/dns_forwarding.py b/src/conf_mode/dns_forwarding.py
index 06366362a..23a16df63 100755
--- a/src/conf_mode/dns_forwarding.py
+++ b/src/conf_mode/dns_forwarding.py
@@ -17,6 +17,7 @@
import os
from sys import exit
+from glob import glob
from vyos.config import Config
from vyos.configdict import dict_merge
@@ -50,10 +51,12 @@ def get_config(config=None):
if not conf.exists(base):
return None
- dns = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True)
+ dns = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True, 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.
+ # options which we need to update into the dictionary retrieved.
default_values = defaults(base)
+ # T2665 due to how defaults under tag nodes work, we must clear these out before we merge
+ del default_values['authoritative_domain']
dns = dict_merge(default_values, dns)
# some additions to the default dictionary
@@ -66,6 +69,183 @@ def get_config(config=None):
if conf.exists(base_nameservers_dhcp):
dns.update({'system_name_server_dhcp': conf.return_values(base_nameservers_dhcp)})
+ if 'authoritative_domain' in dns:
+ dns['authoritative_zones'] = []
+ dns['authoritative_zone_errors'] = []
+ for node in dns['authoritative_domain']:
+ zonedata = dns['authoritative_domain'][node]
+ if ('disable' in zonedata) or (not 'records' in zonedata):
+ continue
+ zone = {
+ 'name': node,
+ 'file': "{}/zone.{}.conf".format(pdns_rec_run_dir, node),
+ 'records': [],
+ }
+
+ recorddata = zonedata['records']
+
+ for rtype in [ 'a', 'aaaa', 'cname', 'mx', 'ptr', 'txt', 'spf', 'srv', 'naptr' ]:
+ if rtype not in recorddata:
+ continue
+ for subnode in recorddata[rtype]:
+ if 'disable' in recorddata[rtype][subnode]:
+ continue
+
+ rdata = recorddata[rtype][subnode]
+
+ if rtype in [ 'a', 'aaaa' ]:
+ rdefaults = defaults(base + ['authoritative-domain', 'records', rtype]) # T2665
+ rdata = dict_merge(rdefaults, rdata)
+
+ if not 'address' in rdata:
+ dns['authoritative_zone_errors'].append('{}.{}: at least one address is required'.format(subnode, node))
+ continue
+
+ for address in rdata['address']:
+ zone['records'].append({
+ 'name': subnode,
+ 'type': rtype.upper(),
+ 'ttl': rdata['ttl'],
+ 'value': address
+ })
+ elif rtype in ['cname', 'ptr']:
+ 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))
+ continue
+
+ zone['records'].append({
+ 'name': subnode,
+ 'type': rtype.upper(),
+ 'ttl': rdata['ttl'],
+ 'value': '{}.'.format(rdata['target'])
+ })
+ elif rtype == 'mx':
+ rdefaults = defaults(base + ['authoritative-domain', 'records', rtype]) # T2665
+ del rdefaults['server']
+ rdata = dict_merge(rdefaults, rdata)
+
+ if not 'server' in rdata:
+ dns['authoritative_zone_errors'].append('{}.{}: at least one server is required'.format(subnode, node))
+ continue
+
+ for servername in rdata['server']:
+ serverdata = rdata['server'][servername]
+ serverdefaults = defaults(base + ['authoritative-domain', 'records', rtype, 'server']) # T2665
+ serverdata = dict_merge(serverdefaults, serverdata)
+ zone['records'].append({
+ 'name': subnode,
+ 'type': rtype.upper(),
+ 'ttl': rdata['ttl'],
+ 'value': '{} {}.'.format(serverdata['priority'], servername)
+ })
+ elif rtype == 'txt':
+ rdefaults = defaults(base + ['authoritative-domain', 'records', rtype]) # T2665
+ rdata = dict_merge(rdefaults, rdata)
+
+ if not 'value' in rdata:
+ dns['authoritative_zone_errors'].append('{}.{}: at least one value is required'.format(subnode, node))
+ continue
+
+ for value in rdata['value']:
+ zone['records'].append({
+ 'name': subnode,
+ 'type': rtype.upper(),
+ 'ttl': rdata['ttl'],
+ 'value': "\"{}\"".format(value.replace("\"", "\\\""))
+ })
+ elif rtype == 'spf':
+ rdefaults = defaults(base + ['authoritative-domain', 'records', rtype]) # T2665
+ rdata = dict_merge(rdefaults, rdata)
+
+ if not 'value' in rdata:
+ dns['authoritative_zone_errors'].append('{}.{}: value is required'.format(subnode, node))
+ continue
+
+ zone['records'].append({
+ 'name': subnode,
+ 'type': rtype.upper(),
+ 'ttl': rdata['ttl'],
+ 'value': '"{}"'.format(rdata['value'].replace("\"", "\\\""))
+ })
+ elif rtype == 'srv':
+ rdefaults = defaults(base + ['authoritative-domain', 'records', rtype]) # T2665
+ del rdefaults['entry']
+ rdata = dict_merge(rdefaults, rdata)
+
+ if not 'entry' in rdata:
+ dns['authoritative_zone_errors'].append('{}.{}: at least one entry is required'.format(subnode, node))
+ continue
+
+ for entryno in rdata['entry']:
+ entrydata = rdata['entry'][entryno]
+ entrydefaults = defaults(base + ['authoritative-domain', 'records', rtype, 'entry']) # T2665
+ entrydata = dict_merge(entrydefaults, entrydata)
+
+ if not 'hostname' in entrydata:
+ dns['authoritative_zone_errors'].append('{}.{}: hostname is required for entry {}'.format(subnode, node, entryno))
+ continue
+
+ if not 'port' in entrydata:
+ dns['authoritative_zone_errors'].append('{}.{}: port is required for entry {}'.format(subnode, node, entryno))
+ continue
+
+ zone['records'].append({
+ 'name': subnode,
+ 'type': rtype.upper(),
+ 'ttl': rdata['ttl'],
+ 'value': '{} {} {} {}.'.format(entrydata['priority'], entrydata['weight'], entrydata['port'], entrydata['hostname'])
+ })
+ elif rtype == 'naptr':
+ rdefaults = defaults(base + ['authoritative-domain', 'records', rtype]) # T2665
+ del rdefaults['rule']
+ rdata = dict_merge(rdefaults, rdata)
+
+
+ if not 'rule' in rdata:
+ dns['authoritative_zone_errors'].append('{}.{}: at least one rule is required'.format(subnode, node))
+ continue
+
+ for ruleno in rdata['rule']:
+ ruledata = rdata['rule'][ruleno]
+ ruledefaults = defaults(base + ['authoritative-domain', 'records', rtype, 'rule']) # T2665
+ ruledata = dict_merge(ruledefaults, ruledata)
+ flags = ""
+ if 'lookup-srv' in ruledata:
+ flags += "S"
+ if 'lookup-a' in ruledata:
+ flags += "A"
+ if 'resolve-uri' in ruledata:
+ flags += "U"
+ if 'protocol-specific' in ruledata:
+ flags += "P"
+
+ if 'order' in ruledata:
+ order = ruledata['order']
+ else:
+ order = ruleno
+
+ if 'regexp' in ruledata:
+ regexp= ruledata['regexp'].replace("\"", "\\\"")
+ else:
+ regexp = ''
+
+ if ruledata['replacement']:
+ replacement = '{}.'.format(ruledata['replacement'])
+ else:
+ replacement = ''
+
+ zone['records'].append({
+ 'name': subnode,
+ 'type': rtype.upper(),
+ 'ttl': rdata['ttl'],
+ 'value': '{} {} "{}" "{}" "{}" {}'.format(order, ruledata['preference'], flags, ruledata['service'], regexp, replacement)
+ })
+
+ dns['authoritative_zones'].append(zone)
+
return dns
def verify(dns):
@@ -86,6 +266,11 @@ def verify(dns):
if 'server' not in dns['domain'][domain]:
raise ConfigError(f'No server configured for domain {domain}!')
+ if ('authoritative_zone_errors' in dns) and dns['authoritative_zone_errors']:
+ for error in dns['authoritative_zone_errors']:
+ print(error)
+ raise ConfigError('Invalid authoritative records have been defined')
+
if 'system' in dns:
if not ('system_name_server' in dns or 'system_name_server_dhcp' in dns):
print("Warning: No 'system name-server' or 'system " \
@@ -104,6 +289,15 @@ def generate(dns):
render(pdns_rec_lua_conf_file, 'dns-forwarding/recursor.conf.lua.tmpl',
dns, user=pdns_rec_user, group=pdns_rec_group)
+ for zone_filename in glob(f'{pdns_rec_run_dir}/zone.*.conf'):
+ os.unlink(zone_filename)
+
+ if 'authoritative_zones' in dns:
+ for zone in dns['authoritative_zones']:
+ render(zone['file'], 'dns-forwarding/recursor.zone.conf.tmpl',
+ zone, user=pdns_rec_user, group=pdns_rec_group)
+
+
# if vyos-hostsd didn't create its files yet, create them (empty)
for file in [pdns_rec_hostsd_lua_conf_file, pdns_rec_hostsd_zones_file]:
with open(file, 'a'):
@@ -119,6 +313,9 @@ def apply(dns):
if os.path.isfile(pdns_rec_config_file):
os.unlink(pdns_rec_config_file)
+
+ for zone_filename in glob(f'{pdns_rec_run_dir}/zone.*.conf'):
+ os.unlink(zone_filename)
else:
### first apply vyos-hostsd config
hc = hostsd_client()
@@ -153,6 +350,12 @@ def apply(dns):
if 'domain' in dns:
hc.add_forward_zones(dns['domain'])
+ # hostsd generates NTAs for the authoritative zones
+ # the list and keys() are required as get returns a dict, not list
+ hc.delete_authoritative_zones(list(hc.get_authoritative_zones()))
+ if 'authoritative_zones' in dns:
+ hc.add_authoritative_zones(list(map(lambda zone: zone['name'], dns['authoritative_zones'])))
+
# call hostsd to generate forward-zones and its lua-config-file
hc.apply()
diff --git a/src/conf_mode/flow_accounting_conf.py b/src/conf_mode/flow_accounting_conf.py
index 1b036a53f..975f19acf 100755
--- a/src/conf_mode/flow_accounting_conf.py
+++ b/src/conf_mode/flow_accounting_conf.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2018-2020 VyOS maintainers and contributors
+# Copyright (C) 2018-2021 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
@@ -16,65 +16,30 @@
import os
import re
+
from sys import exit
import ipaddress
from ipaddress import ip_address
-from jinja2 import FileSystemLoader, Environment
+from vyos.config import Config
+from vyos.configdict import dict_merge
from vyos.ifconfig import Section
from vyos.ifconfig import Interface
-from vyos.config import Config
-from vyos import ConfigError
-from vyos.util import cmd
from vyos.template import render
-
+from vyos.util import cmd
+from vyos.validate import is_addr_assigned
+from vyos.xml import defaults
+from vyos import ConfigError
from vyos import airbag
airbag.enable()
-# default values
-default_sflow_server_port = 6343
-default_netflow_server_port = 2055
-default_plugin_pipe_size = 10
-default_captured_packet_size = 128
-default_netflow_version = '9'
-default_sflow_agentip = 'auto'
-uacctd_conf_path = '/etc/pmacct/uacctd.conf'
+uacctd_conf_path = '/run/pmacct/uacctd.conf'
nftables_nflog_table = 'raw'
nftables_nflog_chain = 'VYOS_CT_PREROUTING_HOOK'
egress_nftables_nflog_table = 'inet mangle'
egress_nftables_nflog_chain = 'FORWARD'
-# helper functions
-# check if node exists and return True if this is true
-def _node_exists(path):
- vyos_config = Config()
- if vyos_config.exists(path):
- return True
-
-# get sFlow agent-ip if agent-address is "auto" (default behaviour)
-def _sflow_default_agentip(config):
- # check if any of BGP, OSPF, OSPFv3 protocols are configured and use router-id from there
- if config.exists('protocols bgp'):
- bgp_router_id = config.return_value("protocols bgp {} parameters router-id".format(config.list_nodes('protocols bgp')[0]))
- if bgp_router_id:
- return bgp_router_id
- if config.return_value('protocols ospf parameters router-id'):
- return config.return_value('protocols ospf parameters router-id')
- if config.return_value('protocols ospfv3 parameters router-id'):
- return config.return_value('protocols ospfv3 parameters router-id')
-
- # if router-id was not found, use first available ip of any interface
- for iface in Section.interfaces():
- for address in Interface(iface).get_addr():
- # return an IP, if this is not loopback
- regex_filter = re.compile('^(?!(127)|(::1)|(fe80))(?P<ipaddr>[a-f\d\.:]+)/\d+$')
- if regex_filter.search(address):
- return regex_filter.search(address).group('ipaddr')
-
- # return nothing by default
- return None
-
# get nftables rule dict for chain in table
def _nftables_get_nflog(chain, table):
# define list with rules
@@ -87,7 +52,6 @@ def _nftables_get_nflog(chain, table):
# run nftables, save output and split it by lines
nftables_command = f'nft -a list chain {table} {chain}'
tmp = cmd(nftables_command, message='Failed to get flows list')
-
# parse each line and add information to list
for current_rule in tmp.splitlines():
if 'FLOW_ACCOUNTING_RULE' not in current_rule:
@@ -100,8 +64,7 @@ def _nftables_get_nflog(chain, table):
# return list with rules
return rules
-# modify nftables rules
-def _nftables_config(configured_ifaces, direction):
+def _nftables_config(configured_ifaces, direction, length=None):
# define list of nftables commands to modify settings
nftable_commands = []
nftables_chain = nftables_nflog_chain
@@ -141,7 +104,7 @@ def _nftables_config(configured_ifaces, direction):
for iface_extended in configured_ifaces_extended:
iface = iface_extended['iface']
iface_prefix = "o" if direction == "egress" else "i"
- rule_definition = f'{iface_prefix}ifname "{iface}" counter log group 2 snaplen {default_captured_packet_size} queue-threshold 100 comment "FLOW_ACCOUNTING_RULE"'
+ rule_definition = f'{iface_prefix}ifname "{iface}" counter log group 2 snaplen {length} queue-threshold 100 comment "FLOW_ACCOUNTING_RULE"'
nftable_commands.append(f'nft insert rule {nftables_table} {nftables_chain} {rule_definition}')
# change nftables
@@ -149,231 +112,157 @@ def _nftables_config(configured_ifaces, direction):
cmd(command, raising=ConfigError)
-def get_config():
- vc = Config()
- vc.set_level('')
- # Convert the VyOS config to an abstract internal representation
- flow_config = {
- 'flow-accounting-configured': vc.exists('system flow-accounting'),
- 'buffer-size': vc.return_value('system flow-accounting buffer-size'),
- 'enable-egress': _node_exists('system flow-accounting enable-egress'),
- 'disable-imt': _node_exists('system flow-accounting disable-imt'),
- 'syslog-facility': vc.return_value('system flow-accounting syslog-facility'),
- 'interfaces': None,
- 'sflow': {
- 'configured': vc.exists('system flow-accounting sflow'),
- 'agent-address': vc.return_value('system flow-accounting sflow agent-address'),
- 'sampling-rate': vc.return_value('system flow-accounting sflow sampling-rate'),
- 'servers': None
- },
- 'netflow': {
- 'configured': vc.exists('system flow-accounting netflow'),
- 'engine-id': vc.return_value('system flow-accounting netflow engine-id'),
- 'max-flows': vc.return_value('system flow-accounting netflow max-flows'),
- 'sampling-rate': vc.return_value('system flow-accounting netflow sampling-rate'),
- 'source-ip': vc.return_value('system flow-accounting netflow source-ip'),
- 'version': vc.return_value('system flow-accounting netflow version'),
- 'timeout': {
- 'expint': vc.return_value('system flow-accounting netflow timeout expiry-interval'),
- 'general': vc.return_value('system flow-accounting netflow timeout flow-generic'),
- 'icmp': vc.return_value('system flow-accounting netflow timeout icmp'),
- 'maxlife': vc.return_value('system flow-accounting netflow timeout max-active-life'),
- 'tcp.fin': vc.return_value('system flow-accounting netflow timeout tcp-fin'),
- 'tcp': vc.return_value('system flow-accounting netflow timeout tcp-generic'),
- 'tcp.rst': vc.return_value('system flow-accounting netflow timeout tcp-rst'),
- 'udp': vc.return_value('system flow-accounting netflow timeout udp')
- },
- 'servers': None
- }
- }
-
- # get interfaces list
- if vc.exists('system flow-accounting interface'):
- flow_config['interfaces'] = vc.return_values('system flow-accounting interface')
-
- # get sFlow collectors list
- if vc.exists('system flow-accounting sflow server'):
- flow_config['sflow']['servers'] = []
- sflow_collectors = vc.list_nodes('system flow-accounting sflow server')
- for collector in sflow_collectors:
- port = default_sflow_server_port
- if vc.return_value("system flow-accounting sflow server {} port".format(collector)):
- port = vc.return_value("system flow-accounting sflow server {} port".format(collector))
- flow_config['sflow']['servers'].append({ 'address': collector, 'port': port })
-
- # get NetFlow collectors list
- if vc.exists('system flow-accounting netflow server'):
- flow_config['netflow']['servers'] = []
- netflow_collectors = vc.list_nodes('system flow-accounting netflow server')
- for collector in netflow_collectors:
- port = default_netflow_server_port
- if vc.return_value("system flow-accounting netflow server {} port".format(collector)):
- port = vc.return_value("system flow-accounting netflow server {} port".format(collector))
- flow_config['netflow']['servers'].append({ 'address': collector, 'port': port })
-
- # get sflow agent-id
- if flow_config['sflow']['agent-address'] == None or flow_config['sflow']['agent-address'] == 'auto':
- flow_config['sflow']['agent-address'] = _sflow_default_agentip(vc)
-
- # get NetFlow version
- if not flow_config['netflow']['version']:
- flow_config['netflow']['version'] = default_netflow_version
-
- # convert NetFlow engine-id format, if this is necessary
- if flow_config['netflow']['engine-id'] and flow_config['netflow']['version'] == '5':
- regex_filter = re.compile('^\d+$')
- if regex_filter.search(flow_config['netflow']['engine-id']):
- flow_config['netflow']['engine-id'] = "{}:0".format(flow_config['netflow']['engine-id'])
-
- # return dict with flow-accounting configuration
- return flow_config
-
-def verify(config):
- # Verify that configuration is valid
- # skip all checks if flow-accounting was removed
- if not config['flow-accounting-configured']:
- return True
+def get_config(config=None):
+ if config:
+ conf = config
+ else:
+ conf = Config()
+ base = ['system', 'flow-accounting']
+ if not conf.exists(base):
+ return None
+
+ flow_accounting = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=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)
+
+ # delete individual flow type default - should only be added if user uses
+ # this feature
+ for flow_type in ['sflow', 'netflow']:
+ if flow_type in default_values:
+ del default_values[flow_type]
+ flow_accounting = dict_merge(default_values, flow_accounting)
+
+ for flow_type in ['sflow', 'netflow']:
+ if flow_type in flow_accounting:
+ default_values = defaults(base + [flow_type])
+ # we need to merge individual server configurations
+ if 'server' in default_values:
+ del default_values['server']
+ flow_accounting[flow_type] = dict_merge(default_values, flow_accounting[flow_type])
+
+ if 'server' in flow_accounting[flow_type]:
+ default_values = defaults(base + [flow_type, 'server'])
+ for server in flow_accounting[flow_type]['server']:
+ flow_accounting[flow_type]['server'][server] = dict_merge(
+ default_values,flow_accounting[flow_type]['server'][server])
+
+ return flow_accounting
+
+def verify(flow_config):
+ if not flow_config:
+ return None
# check if at least one collector is enabled
- if not (config['sflow']['configured'] or config['netflow']['configured'] or not config['disable-imt']):
- raise ConfigError("You need to configure at least one sFlow or NetFlow protocol, or not set \"disable-imt\" for flow-accounting")
+ if 'sflow' not in flow_config and 'netflow' not in flow_config and 'disable_imt' in flow_config:
+ raise ConfigError('You need to configure at least sFlow or NetFlow, ' \
+ 'or not set "disable-imt" for flow-accounting!')
# Check if at least one interface is configured
- if not config['interfaces']:
- raise ConfigError("You need to configure at least one interface for flow-accounting")
+ if 'interface' not in flow_config:
+ raise ConfigError('Flow accounting requires at least one interface to ' \
+ 'be configured!')
# check that all configured interfaces exists in the system
- for iface in config['interfaces']:
- if not iface in Section.interfaces():
- # chnged from error to warning to allow adding dynamic interfaces and interface templates
- # raise ConfigError("The {} interface is not presented in the system".format(iface))
- print("Warning: the {} interface is not presented in the system".format(iface))
+ for interface in flow_config['interface']:
+ if interface not in Section.interfaces():
+ # Changed from error to warning to allow adding dynamic interfaces
+ # and interface templates
+ print(f'Warning: Interface "{interface}" is not presented in the system')
# check sFlow configuration
- if config['sflow']['configured']:
- # check if at least one sFlow collector is configured if sFlow configuration is presented
- if not config['sflow']['servers']:
- raise ConfigError("You need to configure at least one sFlow server")
+ if 'sflow' in flow_config:
+ # check if at least one sFlow collector is configured
+ if 'server' not in flow_config['sflow']:
+ raise ConfigError('You need to configure at least one sFlow server!')
# check that all sFlow collectors use the same IP protocol version
sflow_collector_ipver = None
- for sflow_collector in config['sflow']['servers']:
+ for server in flow_config['sflow']['server']:
if sflow_collector_ipver:
- if sflow_collector_ipver != ip_address(sflow_collector['address']).version:
+ if sflow_collector_ipver != ip_address(server).version:
raise ConfigError("All sFlow servers must use the same IP protocol")
else:
- sflow_collector_ipver = ip_address(sflow_collector['address']).version
-
+ sflow_collector_ipver = ip_address(server).version
# check agent-id for sFlow: we should avoid mixing IPv4 agent-id with IPv6 collectors and vice-versa
- for sflow_collector in config['sflow']['servers']:
- if ip_address(sflow_collector['address']).version != ip_address(config['sflow']['agent-address']).version:
- raise ConfigError("Different IP address versions cannot be mixed in \"sflow agent-address\" and \"sflow server\". You need to set manually the same IP version for \"agent-address\" as for all sFlow servers")
-
- # check if configured sFlow agent-id exist in the system
- agent_id_presented = None
- for iface in Section.interfaces():
- for address in Interface(iface).get_addr():
- # check an IP, if this is not loopback
- regex_filter = re.compile('^(?!(127)|(::1)|(fe80))(?P<ipaddr>[a-f\d\.:]+)/\d+$')
- if regex_filter.search(address):
- if regex_filter.search(address).group('ipaddr') == config['sflow']['agent-address']:
- agent_id_presented = True
- break
- if not agent_id_presented:
- raise ConfigError("Your \"sflow agent-address\" does not exist in the system")
+ for server in flow_config['sflow']['server']:
+ if 'agent_address' in flow_config['sflow']:
+ if ip_address(server).version != ip_address(flow_config['sflow']['agent_address']).version:
+ raise ConfigError('IPv4 and IPv6 addresses can not be mixed in "sflow agent-address" and "sflow '\
+ 'server". You need to set the same IP version for both "agent-address" and '\
+ 'all sFlow servers')
+
+ if 'agent_address' in flow_config['sflow']:
+ tmp = flow_config['sflow']['agent_address']
+ if not is_addr_assigned(tmp):
+ print(f'Warning: Configured "sflow agent-address {tmp}" does not exist in the system!')
# check NetFlow configuration
- if config['netflow']['configured']:
+ if 'netflow' in flow_config:
# check if at least one NetFlow collector is configured if NetFlow configuration is presented
- if not config['netflow']['servers']:
- raise ConfigError("You need to configure at least one NetFlow server")
-
- # check if configured netflow source-ip exist in the system
- if config['netflow']['source-ip']:
- source_ip_presented = None
- for iface in Section.interfaces():
- for address in Interface(iface).get_addr():
- # check an IP
- regex_filter = re.compile('^(?!(127)|(::1)|(fe80))(?P<ipaddr>[a-f\d\.:]+)/\d+$')
- if regex_filter.search(address):
- if regex_filter.search(address).group('ipaddr') == config['netflow']['source-ip']:
- source_ip_presented = True
- break
- if not source_ip_presented:
- print("Warning: your \"netflow source-ip\" does not exist in the system")
-
- # check if engine-id compatible with selected protocol version
- if config['netflow']['engine-id']:
+ if 'server' not in flow_config['netflow']:
+ raise ConfigError('You need to configure at least one NetFlow server!')
+
+ # Check if configured netflow source-address exist in the system
+ if 'source_address' in flow_config['netflow']:
+ if not is_addr_assigned(flow_config['netflow']['source_address']):
+ tmp = flow_config['netflow']['source_address']
+ print(f'Warning: Configured "netflow source-address {tmp}" does not exist on the system!')
+
+ # Check if engine-id compatible with selected protocol version
+ if 'engine_id' in flow_config['netflow']:
v5_filter = '^(\d|[1-9]\d|1\d{2}|2[0-4]\d|25[0-5]):(\d|[1-9]\d|1\d{2}|2[0-4]\d|25[0-5])$'
v9v10_filter = '^(\d|[1-9]\d{1,8}|[1-3]\d{9}|4[01]\d{8}|42[0-8]\d{7}|429[0-3]\d{6}|4294[0-8]\d{5}|42949[0-5]\d{4}|429496[0-6]\d{3}|4294967[01]\d{2}|42949672[0-8]\d|429496729[0-5])$'
- if config['netflow']['version'] == '5':
+ engine_id = flow_config['netflow']['engine_id']
+ version = flow_config['netflow']['version']
+
+ if flow_config['netflow']['version'] == '5':
regex_filter = re.compile(v5_filter)
- if not regex_filter.search(config['netflow']['engine-id']):
- raise ConfigError("You cannot use NetFlow engine-id {} together with NetFlow protocol version {}".format(config['netflow']['engine-id'], config['netflow']['version']))
+ if not regex_filter.search(engine_id):
+ raise ConfigError(f'You cannot use NetFlow engine-id "{engine_id}" '\
+ f'together with NetFlow protocol version "{version}"!')
else:
regex_filter = re.compile(v9v10_filter)
- if not regex_filter.search(config['netflow']['engine-id']):
- raise ConfigError("You cannot use NetFlow engine-id {} together with NetFlow protocol version {}".format(config['netflow']['engine-id'], config['netflow']['version']))
+ if not regex_filter.search(flow_config['netflow']['engine_id']):
+ raise ConfigError(f'Can not use NetFlow engine-id "{engine_id}" together '\
+ f'with NetFlow protocol version "{version}"!')
# return True if all checks were passed
return True
-def generate(config):
- # skip all checks if flow-accounting was removed
- if not config['flow-accounting-configured']:
- return True
+def generate(flow_config):
+ if not flow_config:
+ return None
- # Calculate all necessary values
- if config['buffer-size']:
- # circular queue size
- config['plugin_pipe_size'] = int(config['buffer-size']) * 1024**2
- else:
- config['plugin_pipe_size'] = default_plugin_pipe_size * 1024**2
- # transfer buffer size
- # recommended value from pmacct developers 1/1000 of pipe size
- config['plugin_buffer_size'] = int(config['plugin_pipe_size'] / 1000)
-
- # Prepare a timeouts string
- timeout_string = ''
- for timeout_type, timeout_value in config['netflow']['timeout'].items():
- if timeout_value:
- if timeout_string == '':
- timeout_string = "{}{}={}".format(timeout_string, timeout_type, timeout_value)
- else:
- timeout_string = "{}:{}={}".format(timeout_string, timeout_type, timeout_value)
- config['netflow']['timeout_string'] = timeout_string
+ render(uacctd_conf_path, 'netflow/uacctd.conf.tmpl', flow_config)
- render(uacctd_conf_path, 'netflow/uacctd.conf.tmpl', {
- 'templatecfg': config,
- 'snaplen': default_captured_packet_size,
- })
-
-
-def apply(config):
- # define variables
- command = None
+def apply(flow_config):
+ action = 'restart'
# Check if flow-accounting was removed and define command
- if not config['flow-accounting-configured']:
- command = 'systemctl stop uacctd.service'
- else:
- command = 'systemctl restart uacctd.service'
+ if not flow_config:
+ _nftables_config([], 'ingress')
+ _nftables_config([], 'egress')
+
+ # Stop flow-accounting daemon and remove configuration file
+ cmd('systemctl stop uacctd.service')
+ if os.path.exists(uacctd_conf_path):
+ os.unlink(uacctd_conf_path)
+ return
- # run command to start or stop flow-accounting
- cmd(command, raising=ConfigError, message='Failed to start/stop flow-accounting')
+ # Start/reload flow-accounting daemon
+ cmd(f'systemctl restart uacctd.service')
# configure nftables rules for defined interfaces
- if config['interfaces']:
- _nftables_config(config['interfaces'], 'ingress')
+ if 'interface' in flow_config:
+ _nftables_config(flow_config['interface'], 'ingress', flow_config['packet_length'])
# configure egress the same way if configured otherwise remove it
- if config['enable-egress']:
- _nftables_config(config['interfaces'], 'egress')
+ if 'enable_egress' in flow_config:
+ _nftables_config(flow_config['interface'], 'egress', flow_config['packet_length'])
else:
_nftables_config([], 'egress')
- else:
- _nftables_config([], 'ingress')
- _nftables_config([], 'egress')
if __name__ == '__main__':
try:
diff --git a/src/conf_mode/http-api.py b/src/conf_mode/http-api.py
index 4bfcbeb47..b5f5e919f 100755
--- a/src/conf_mode/http-api.py
+++ b/src/conf_mode/http-api.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2019 VyOS maintainers and contributors
+# Copyright (C) 2019-2021 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
@@ -13,25 +13,26 @@
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-#
-#
import sys
import os
import json
-import time
+
+from time import sleep
from copy import deepcopy
import vyos.defaults
+
from vyos.config import Config
-from vyos import ConfigError
+from vyos.template import render
from vyos.util import cmd
from vyos.util import call
-
+from vyos import ConfigError
from vyos import airbag
airbag.enable()
-config_file = '/etc/vyos/http-api.conf'
+api_conf_file = '/etc/vyos/http-api.conf'
+systemd_service = '/run/systemd/system/vyos-http-api.service'
vyos_conf_scripts_dir=vyos.defaults.directories['conf_mode']
@@ -49,21 +50,35 @@ def get_config(config=None):
else:
conf = Config()
- if not conf.exists('service https api'):
+ base = ['service', 'https', 'api']
+ if not conf.exists(base):
return None
- else:
- conf.set_level('service https api')
+ # Do we run inside a VRF context?
+ vrf_path = ['service', 'https', 'vrf']
+ if conf.exists(vrf_path):
+ http_api['vrf'] = conf.return_value(vrf_path)
+
+ conf.set_level('service https api')
if conf.exists('strict'):
- http_api['strict'] = 'true'
+ http_api['strict'] = True
if conf.exists('debug'):
- http_api['debug'] = 'true'
+ http_api['debug'] = True
+
+ if conf.exists('socket'):
+ http_api['socket'] = True
if conf.exists('port'):
port = conf.return_value('port')
http_api['port'] = port
+ if conf.exists('cors'):
+ http_api['cors'] = {}
+ if conf.exists('cors allow-origin'):
+ origins = conf.return_values('cors allow-origin')
+ http_api['cors']['origins'] = origins[:]
+
if conf.exists('keys'):
for name in conf.list_nodes('keys id'):
if conf.exists('keys id {0} key'.format(name)):
@@ -83,24 +98,31 @@ def verify(http_api):
def generate(http_api):
if http_api is None:
+ if os.path.exists(systemd_service):
+ os.unlink(systemd_service)
return None
if not os.path.exists('/etc/vyos'):
os.mkdir('/etc/vyos')
- with open(config_file, 'w') as f:
+ with open(api_conf_file, 'w') as f:
json.dump(http_api, f, indent=2)
+ render(systemd_service, 'https/vyos-http-api.service.tmpl', http_api)
return None
def apply(http_api):
+ # Reload systemd manager configuration
+ call('systemctl daemon-reload')
+ service_name = 'vyos-http-api.service'
+
if http_api is not None:
- call('systemctl restart vyos-http-api.service')
+ call(f'systemctl restart {service_name}')
else:
- call('systemctl stop vyos-http-api.service')
+ call(f'systemctl stop {service_name}')
# Let uvicorn settle before restarting Nginx
- time.sleep(1)
+ sleep(1)
cmd(f'{vyos_conf_scripts_dir}/https.py', raising=ConfigError)
diff --git a/src/conf_mode/https.py b/src/conf_mode/https.py
index 92dc4a410..37fa36797 100755
--- a/src/conf_mode/https.py
+++ b/src/conf_mode/https.py
@@ -23,6 +23,7 @@ import vyos.defaults
import vyos.certbot_util
from vyos.config import Config
+from vyos.configverify import verify_vrf
from vyos import ConfigError
from vyos.pki import wrap_certificate
from vyos.pki import wrap_private_key
@@ -34,6 +35,7 @@ from vyos import airbag
airbag.enable()
config_file = '/etc/nginx/sites-available/default'
+systemd_override = r'/etc/systemd/system/nginx.service.d/override.conf'
cert_dir = '/etc/ssl/certs'
key_dir = '/etc/ssl/private'
certbot_dir = vyos.defaults.directories['certbot']
@@ -59,10 +61,11 @@ def get_config(config=None):
else:
conf = Config()
- if not conf.exists('service https'):
+ base = ['service', 'https']
+ if not conf.exists(base):
return None
- https = conf.get_config_dict('service https', get_first_key=True)
+ https = conf.get_config_dict(base, get_first_key=True)
if https:
https['pki'] = conf.get_config_dict(['pki'], key_mangling=('-', '_'),
@@ -103,6 +106,8 @@ def verify(https):
if not domains_found:
raise ConfigError("At least one 'virtual-host <id> server-name' "
"matching the 'certbot domain-name' is required.")
+
+ verify_vrf(https)
return None
def generate(https):
@@ -143,7 +148,6 @@ def generate(https):
server_cert = str(wrap_certificate(pki_cert['certificate']))
if 'ca-certificate' in cert_dict:
ca_cert = cert_dict['ca-certificate']
- print(ca_cert)
server_cert += '\n' + str(wrap_certificate(https['pki']['ca'][ca_cert]['certificate']))
write_file(cert_path, server_cert)
@@ -188,6 +192,8 @@ def generate(https):
vhosts = https.get('api-restrict', {}).get('virtual-host', [])
if vhosts:
api_data['vhost'] = vhosts[:]
+ if 'socket' in list(api_settings):
+ api_data['socket'] = True
if api_data:
vhost_list = api_data.get('vhost', [])
@@ -209,10 +215,12 @@ def generate(https):
}
render(config_file, 'https/nginx.default.tmpl', data)
-
+ render(systemd_override, 'https/override.conf.tmpl', https)
return None
def apply(https):
+ # Reload systemd manager configuration
+ call('systemctl daemon-reload')
if https is not None:
call('systemctl restart nginx.service')
else:
diff --git a/src/conf_mode/interfaces-vxlan.py b/src/conf_mode/interfaces-vxlan.py
index 804f2d14f..1f097c4e3 100755
--- a/src/conf_mode/interfaces-vxlan.py
+++ b/src/conf_mode/interfaces-vxlan.py
@@ -44,6 +44,20 @@ def get_config(config=None):
base = ['interfaces', 'vxlan']
vxlan = get_interface_dict(conf, base)
+ # We need to verify that no other VXLAN tunnel is configured when external
+ # mode is in use - Linux Kernel limitation
+ conf.set_level(base)
+ vxlan['other_tunnels'] = conf.get_config_dict([], key_mangling=('-', '_'),
+ get_first_key=True,
+ no_tag_node_value_mangle=True)
+
+ # This if-clause is just to be sure - it will always evaluate to true
+ ifname = vxlan['ifname']
+ if ifname in vxlan['other_tunnels']:
+ del vxlan['other_tunnels'][ifname]
+ if len(vxlan['other_tunnels']) == 0:
+ del vxlan['other_tunnels']
+
return vxlan
def verify(vxlan):
@@ -63,8 +77,21 @@ def verify(vxlan):
if not any(tmp in ['group', 'remote', 'source_address'] for tmp in vxlan):
raise ConfigError('Group, remote or source-address must be configured')
- if 'vni' not in vxlan:
- raise ConfigError('Must configure VNI for VXLAN')
+ if 'vni' not in vxlan and 'external' not in vxlan:
+ raise ConfigError(
+ 'Must either configure VXLAN "vni" or use "external" CLI option!')
+
+ if {'external', 'vni'} <= set(vxlan):
+ raise ConfigError('Can not specify both "external" and "VNI"!')
+
+ if {'external', 'other_tunnels'} <= set(vxlan):
+ other_tunnels = ', '.join(vxlan['other_tunnels'])
+ raise ConfigError(f'Only one VXLAN tunnel is supported when "external" '\
+ f'CLI option is used. Additional tunnels: {other_tunnels}')
+
+ if 'gpe' in vxlan and 'external' not in vxlan:
+ raise ConfigError(f'VXLAN-GPE is only supported when "external" '\
+ f'CLI option is used.')
if 'source_interface' in vxlan:
# VXLAN adds at least an overhead of 50 byte - we need to check the
diff --git a/src/conf_mode/interfaces-wwan.py b/src/conf_mode/interfaces-wwan.py
index f013e5411..a4b033374 100755
--- a/src/conf_mode/interfaces-wwan.py
+++ b/src/conf_mode/interfaces-wwan.py
@@ -17,6 +17,7 @@
import os
from sys import exit
+from time import sleep
from vyos.config import Config
from vyos.configdict import get_interface_dict
@@ -28,10 +29,15 @@ from vyos.util import cmd
from vyos.util import call
from vyos.util import dict_search
from vyos.util import DEVNULL
+from vyos.util import is_systemd_service_active
+from vyos.util import write_file
from vyos import ConfigError
from vyos import airbag
airbag.enable()
+service_name = 'ModemManager.service'
+cron_script = '/etc/cron.d/wwan'
+
def get_config(config=None):
"""
Retrive CLI config as dictionary. Dictionary can never be empty, as at least the
@@ -44,6 +50,20 @@ def get_config(config=None):
base = ['interfaces', 'wwan']
wwan = get_interface_dict(conf, base)
+ # We need to know the amount of other WWAN interfaces as ModemManager needs
+ # to be started or stopped.
+ conf.set_level(base)
+ wwan['other_interfaces'] = conf.get_config_dict([], key_mangling=('-', '_'),
+ get_first_key=True,
+ no_tag_node_value_mangle=True)
+
+ # This if-clause is just to be sure - it will always evaluate to true
+ ifname = wwan['ifname']
+ if ifname in wwan['other_interfaces']:
+ del wwan['other_interfaces'][ifname]
+ if len(wwan['other_interfaces']) == 0:
+ del wwan['other_interfaces']
+
return wwan
def verify(wwan):
@@ -61,9 +81,26 @@ def verify(wwan):
return None
def generate(wwan):
+ if 'deleted' in wwan:
+ return None
+
+ if not os.path.exists(cron_script):
+ write_file(cron_script, '*/5 * * * * root /usr/libexec/vyos/vyos-check-wwan.py')
return None
def apply(wwan):
+ if not is_systemd_service_active(service_name):
+ cmd(f'systemctl start {service_name}')
+
+ counter = 100
+ # Wait until a modem is detected and then we can continue
+ while counter > 0:
+ counter -= 1
+ tmp = cmd('mmcli -L')
+ if tmp != 'No modems were found':
+ break
+ sleep(0.250)
+
# we only need the modem number. wwan0 -> 0, wwan1 -> 1
modem = wwan['ifname'].lstrip('wwan')
base_cmd = f'mmcli --modem {modem}'
@@ -73,6 +110,15 @@ def apply(wwan):
w = WWANIf(wwan['ifname'])
if 'deleted' in wwan or 'disable' in wwan:
w.remove()
+
+ # There are no other WWAN interfaces - stop the daemon
+ if 'other_interfaces' not in wwan:
+ cmd(f'systemctl stop {service_name}')
+ # Clean CRON helper script which is used for to re-connect when
+ # RF signal is lost
+ if os.path.exists(cron_script):
+ os.unlink(cron_script)
+
return None
ip_type = 'ipv4'
@@ -93,6 +139,9 @@ def apply(wwan):
call(command, stdout=DEVNULL)
w.update(wwan)
+ if 'other_interfaces' not in wwan and 'deleted' in wwan:
+ cmd(f'systemctl start {service_name}')
+
return None
if __name__ == '__main__':
diff --git a/src/conf_mode/netns.py b/src/conf_mode/netns.py
new file mode 100755
index 000000000..0924eb616
--- /dev/null
+++ b/src/conf_mode/netns.py
@@ -0,0 +1,118 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2021 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
+
+from sys import exit
+from tempfile import NamedTemporaryFile
+
+from vyos.config import Config
+from vyos.configdict import node_changed
+from vyos.ifconfig import Interface
+from vyos.util import call
+from vyos.util import dict_search
+from vyos.util import get_interface_config
+from vyos import ConfigError
+from vyos import airbag
+airbag.enable()
+
+
+def netns_interfaces(c, match):
+ """
+ get NETNS bound interfaces
+ """
+ matched = []
+ old_level = c.get_level()
+ c.set_level(['interfaces'])
+ section = c.get_config_dict([], get_first_key=True)
+ for type in section:
+ interfaces = section[type]
+ for name in interfaces:
+ interface = interfaces[name]
+ if 'netns' in interface:
+ v = interface.get('netns', '')
+ if v == match:
+ matched.append(name)
+
+ c.set_level(old_level)
+ return matched
+
+def get_config(config=None):
+ if config:
+ conf = config
+ else:
+ conf = Config()
+
+ base = ['netns']
+ netns = conf.get_config_dict(base, get_first_key=True,
+ no_tag_node_value_mangle=True)
+
+ # determine which NETNS has been removed
+ for name in node_changed(conf, base + ['name']):
+ if 'netns_remove' not in netns:
+ netns.update({'netns_remove' : {}})
+
+ netns['netns_remove'][name] = {}
+ # get NETNS bound interfaces
+ interfaces = netns_interfaces(conf, name)
+ if interfaces: netns['netns_remove'][name]['interface'] = interfaces
+
+ return netns
+
+def verify(netns):
+ # ensure NETNS is not assigned to any interface
+ if 'netns_remove' in netns:
+ for name, config in netns['netns_remove'].items():
+ if 'interface' in config:
+ raise ConfigError(f'Can not remove NETNS "{name}", it still has '\
+ f'member interfaces!')
+
+ if 'name' in netns:
+ for name, config in netns['name'].items():
+ print(name)
+
+ return None
+
+
+def generate(netns):
+ if not netns:
+ return None
+
+ return None
+
+
+def apply(netns):
+
+ for tmp in (dict_search('netns_remove', netns) or []):
+ if os.path.isfile(f'/run/netns/{tmp}'):
+ call(f'ip netns del {tmp}')
+
+ if 'name' in netns:
+ for name, config in netns['name'].items():
+ if not os.path.isfile(f'/run/netns/{name}'):
+ call(f'ip netns add {name}')
+
+ return None
+
+if __name__ == '__main__':
+ try:
+ c = get_config()
+ verify(c)
+ generate(c)
+ apply(c)
+ except ConfigError as e:
+ print(e)
+ exit(1)
diff --git a/src/conf_mode/protocols_bfd.py b/src/conf_mode/protocols_bfd.py
index 6981d0db1..4ebc0989c 100755
--- a/src/conf_mode/protocols_bfd.py
+++ b/src/conf_mode/protocols_bfd.py
@@ -35,7 +35,8 @@ def get_config(config=None):
conf = Config()
base = ['protocols', 'bfd']
bfd = conf.get_config_dict(base, key_mangling=('-', '_'),
- get_first_key=True)
+ get_first_key=True,
+ no_tag_node_value_mangle=True)
# Bail out early if configuration tree does not exist
if not conf.exists(base):
return bfd
@@ -78,11 +79,16 @@ def verify(bfd):
# multihop and echo-mode cannot be used together
if 'echo_mode' in peer_config:
- raise ConfigError('Multihop and echo-mode cannot be used together')
+ raise ConfigError('BFD multihop and echo-mode cannot be used together')
# multihop doesn't accept interface names
if 'source' in peer_config and 'interface' in peer_config['source']:
- raise ConfigError('Multihop and source interface cannot be used together')
+ raise ConfigError('BFD multihop and source interface cannot be used together')
+
+ if 'profile' in peer_config:
+ profile_name = peer_config['profile']
+ if 'profile' not in bfd or profile_name not in bfd['profile']:
+ raise ConfigError(f'BFD profile "{profile_name}" does not exist!')
if 'vrf' in peer_config:
verify_vrf(peer_config)
diff --git a/src/conf_mode/protocols_bgp.py b/src/conf_mode/protocols_bgp.py
index 03fb17ba7..d8704727c 100755
--- a/src/conf_mode/protocols_bgp.py
+++ b/src/conf_mode/protocols_bgp.py
@@ -183,6 +183,28 @@ def verify(bgp):
raise ConfigError(f'Neighbor "{peer}" cannot have both ipv6-unicast and ipv6-labeled-unicast configured at the same time!')
afi_config = peer_config['address_family'][afi]
+
+ if 'conditionally_advertise' in afi_config:
+ if 'advertise_map' not in afi_config['conditionally_advertise']:
+ raise ConfigError('Must speficy advertise-map when conditionally-advertise is in use!')
+ # Verify advertise-map (which is a route-map) exists
+ verify_route_map(afi_config['conditionally_advertise']['advertise_map'], bgp)
+
+ if ('exist_map' not in afi_config['conditionally_advertise'] and
+ 'non_exist_map' not in afi_config['conditionally_advertise']):
+ raise ConfigError('Must either speficy exist-map or non-exist-map when ' \
+ 'conditionally-advertise is in use!')
+
+ if {'exist_map', 'non_exist_map'} <= set(afi_config['conditionally_advertise']):
+ raise ConfigError('Can not specify both exist-map and non-exist-map for ' \
+ 'conditionally-advertise!')
+
+ if 'exist_map' in afi_config['conditionally_advertise']:
+ verify_route_map(afi_config['conditionally_advertise']['exist_map'], bgp)
+
+ if 'non_exist_map' in afi_config['conditionally_advertise']:
+ verify_route_map(afi_config['conditionally_advertise']['non_exist_map'], bgp)
+
# Validate if configured Prefix list exists
if 'prefix_list' in afi_config:
for tmp in ['import', 'export']:
diff --git a/src/conf_mode/protocols_mpls.py b/src/conf_mode/protocols_mpls.py
index 3b27608da..0b0c7d07b 100755
--- a/src/conf_mode/protocols_mpls.py
+++ b/src/conf_mode/protocols_mpls.py
@@ -66,36 +66,24 @@ def verify(mpls):
def generate(mpls):
# If there's no MPLS config generated, create dictionary key with no value.
- if not mpls:
- mpls['new_frr_config'] = ''
+ if not mpls or 'deleted' in mpls:
return None
- mpls['new_frr_config'] = render_to_string('frr/ldpd.frr.tmpl', mpls)
+ mpls['frr_ldpd_config'] = render_to_string('frr/ldpd.frr.tmpl', mpls)
return None
def apply(mpls):
- # Define dictionary that will load FRR config
- frr_cfg = {}
+ ldpd_damon = 'ldpd'
+
# Save original configuration prior to starting any commit actions
- frr_cfg['original_config'] = frr.get_configuration(daemon='ldpd')
- frr_cfg['modified_config'] = frr.replace_section(frr_cfg['original_config'], mpls['new_frr_config'], from_re='mpls.*')
-
- # If FRR config is blank, rerun the blank commit three times due to frr-reload
- # behavior/bug not properly clearing out on one commit.
- if mpls['new_frr_config'] == '':
- for x in range(3):
- frr.reload_configuration(frr_cfg['modified_config'], daemon='ldpd')
- elif not 'ldp' in mpls:
- for x in range(3):
- frr.reload_configuration(frr_cfg['modified_config'], daemon='ldpd')
- else:
- # FRR mark configuration will test for syntax errors and throws an
- # exception if any syntax errors is detected
- frr.mark_configuration(frr_cfg['modified_config'])
+ frr_cfg = frr.FRRConfig()
+
+ frr_cfg.load_configuration(ldpd_damon)
+ frr_cfg.modify_section(f'^mpls ldp', stop_pattern='^exit', remove_stop_mark=True)
- # Commit resulting configuration to FRR, this will throw CommitError
- # on failure
- frr.reload_configuration(frr_cfg['modified_config'], daemon='ldpd')
+ if 'frr_ldpd_config' in mpls:
+ frr_cfg.add_before(frr.default_add_before, mpls['frr_ldpd_config'])
+ frr_cfg.commit_configuration(ldpd_damon)
# Set number of entries in the platform label tables
labels = '0'
@@ -122,7 +110,7 @@ def apply(mpls):
system_interfaces = []
# Populate system interfaces list with local MPLS capable interfaces
for interface in glob('/proc/sys/net/mpls/conf/*'):
- system_interfaces.append(os.path.basename(interface))
+ system_interfaces.append(os.path.basename(interface))
# This is where the comparison is done on if an interface needs to be enabled/disabled.
for system_interface in system_interfaces:
interface_state = read_file(f'/proc/sys/net/mpls/conf/{system_interface}/input')
@@ -138,7 +126,7 @@ def apply(mpls):
system_interfaces = []
# If MPLS interfaces are not configured, set MPLS processing disabled
for interface in glob('/proc/sys/net/mpls/conf/*'):
- system_interfaces.append(os.path.basename(interface))
+ system_interfaces.append(os.path.basename(interface))
for system_interface in system_interfaces:
system_interface = system_interface.replace('.', '/')
call(f'sysctl -wq net.mpls.conf.{system_interface}.input=0')
diff --git a/src/conf_mode/protocols_ospfv3.py b/src/conf_mode/protocols_ospfv3.py
index d0460b830..f8e733ba5 100755
--- a/src/conf_mode/protocols_ospfv3.py
+++ b/src/conf_mode/protocols_ospfv3.py
@@ -23,8 +23,11 @@ from vyos.config import Config
from vyos.configdict import dict_merge
from vyos.configdict import node_changed
from vyos.configverify import verify_common_route_maps
+from vyos.configverify import verify_route_map
+from vyos.configverify import verify_interface_exists
from vyos.template import render_to_string
from vyos.ifconfig import Interface
+from vyos.util import dict_search
from vyos.util import get_interface_config
from vyos.xml import defaults
from vyos import ConfigError
@@ -66,6 +69,28 @@ def get_config(config=None):
ospfv3.update({'deleted' : ''})
return ospfv3
+ # We have gathered the dict representation of the CLI, but there are default
+ # options which we need to update into the dictionary retrived.
+ # XXX: Note that we can not call defaults(base), as defaults does not work
+ # on an instance of a tag node. As we use the exact same CLI definition for
+ # both the non-vrf and vrf version this is absolutely safe!
+ default_values = defaults(base_path)
+
+ # We have to cleanup the default dict, as default values could enable features
+ # which are not explicitly enabled on the CLI. Example: default-information
+ # originate comes with a default metric-type of 2, which will enable the
+ # entire default-information originate tree, even when not set via CLI so we
+ # need to check this first and probably drop that key.
+ if dict_search('default_information.originate', ospfv3) is None:
+ del default_values['default_information']
+
+ # XXX: T2665: we currently have no nice way for defaults under tag nodes,
+ # clean them out and add them manually :(
+ del default_values['interface']
+
+ # merge in remaining default values
+ ospfv3 = dict_merge(default_values, ospfv3)
+
# We also need some additional information from the config, prefix-lists
# and route-maps for instance. They will be used in verify().
#
@@ -83,8 +108,19 @@ def verify(ospfv3):
verify_common_route_maps(ospfv3)
+ # As we can have a default-information route-map, we need to validate it!
+ route_map_name = dict_search('default_information.originate.route_map', ospfv3)
+ if route_map_name: verify_route_map(route_map_name, ospfv3)
+
+ if 'area' in ospfv3:
+ for area, area_config in ospfv3['area'].items():
+ if 'area_type' in area_config:
+ if len(area_config['area_type']) > 1:
+ raise ConfigError(f'Can only configure one area-type for OSPFv3 area "{area}"!')
+
if 'interface' in ospfv3:
for interface, interface_config in ospfv3['interface'].items():
+ verify_interface_exists(interface)
if 'ifmtu' in interface_config:
mtu = Interface(interface).get_mtu()
if int(interface_config['ifmtu']) > int(mtu):
diff --git a/src/conf_mode/service_pppoe-server.py b/src/conf_mode/service_pppoe-server.py
index 9fbd531da..1f31d132d 100755
--- a/src/conf_mode/service_pppoe-server.py
+++ b/src/conf_mode/service_pppoe-server.py
@@ -24,8 +24,11 @@ from vyos.configverify import verify_accel_ppp_base_service
from vyos.template import render
from vyos.util import call
from vyos.util import dict_search
+from vyos.util import get_interface_config
from vyos import ConfigError
from vyos import airbag
+from vyos.range_regex import range_to_regex
+
airbag.enable()
pppoe_conf = r'/run/accel-pppd/pppoe.conf'
@@ -56,6 +59,11 @@ def verify(pppoe):
if 'interface' not in pppoe:
raise ConfigError('At least one listen interface must be defined!')
+ # Check is interface exists in the system
+ for iface in pppoe['interface']:
+ if not get_interface_config(iface):
+ raise ConfigError(f'Interface {iface} does not exist!')
+
# local ippool and gateway settings config checks
if not (dict_search('client_ip_pool.subnet', pppoe) or
(dict_search('client_ip_pool.start', pppoe) and
@@ -73,6 +81,13 @@ def generate(pppoe):
if not pppoe:
return None
+ # Generate special regex for dynamic interfaces
+ for iface in pppoe['interface']:
+ if 'vlan_range' in pppoe['interface'][iface]:
+ pppoe['interface'][iface]['regex'] = []
+ for vlan_range in pppoe['interface'][iface]['vlan_range']:
+ pppoe['interface'][iface]['regex'].append(range_to_regex(vlan_range))
+
render(pppoe_conf, 'accel-ppp/pppoe.config.tmpl', pppoe)
if dict_search('authentication.mode', pppoe) == 'local':
diff --git a/src/conf_mode/snmp.py b/src/conf_mode/snmp.py
index e1852f2ce..8ce48780b 100755
--- a/src/conf_mode/snmp.py
+++ b/src/conf_mode/snmp.py
@@ -19,16 +19,18 @@ import os
from sys import exit
from vyos.config import Config
+from vyos.configdict import dict_merge
from vyos.configverify import verify_vrf
from vyos.snmpv3_hashgen import plaintext_to_md5
from vyos.snmpv3_hashgen import plaintext_to_sha1
from vyos.snmpv3_hashgen import random
from vyos.template import render
-from vyos.template import is_ipv4
from vyos.util import call
from vyos.util import chmod_755
+from vyos.util import dict_search
from vyos.validate import is_addr_assigned
from vyos.version import get_version_data
+from vyos.xml import defaults
from vyos import ConfigError
from vyos import airbag
airbag.enable()
@@ -37,57 +39,29 @@ config_file_client = r'/etc/snmp/snmp.conf'
config_file_daemon = r'/etc/snmp/snmpd.conf'
config_file_access = r'/usr/share/snmp/snmpd.conf'
config_file_user = r'/var/lib/snmp/snmpd.conf'
-default_script_dir = r'/config/user-data/'
systemd_override = r'/etc/systemd/system/snmpd.service.d/override.conf'
+systemd_service = 'snmpd.service'
-# SNMP OIDs used to mark auth/priv type
-OIDs = {
- 'md5' : '.1.3.6.1.6.3.10.1.1.2',
- 'sha' : '.1.3.6.1.6.3.10.1.1.3',
- 'aes' : '.1.3.6.1.6.3.10.1.2.4',
- 'des' : '.1.3.6.1.6.3.10.1.2.2',
- 'none': '.1.3.6.1.6.3.10.1.2.1'
-}
-
-default_config_data = {
- 'listen_on': [],
- 'listen_address': [],
- 'ipv6_enabled': 'True',
- 'communities': [],
- 'smux_peers': [],
- 'location' : '',
- 'protocol' : 'udp',
- 'description' : '',
- 'contact' : '',
- 'route_table': 'False',
- 'trap_source': '',
- 'trap_targets': [],
- 'vyos_user': '',
- 'vyos_user_pass': '',
- 'version': '',
- 'v3_enabled': 'False',
- 'v3_engineid': '',
- 'v3_groups': [],
- 'v3_traps': [],
- 'v3_users': [],
- 'v3_views': [],
- 'script_ext': []
-}
-
-def rmfile(file):
- if os.path.isfile(file):
- os.unlink(file)
-
-def get_config():
- snmp = default_config_data
- conf = Config()
- if not conf.exists('service snmp'):
- return None
+def get_config(config=None):
+ if config:
+ conf = config
else:
- if conf.exists('system ipv6 disable'):
- snmp['ipv6_enabled'] = False
+ conf = Config()
+ base = ['service', 'snmp']
+
+ snmp = conf.get_config_dict(base, key_mangling=('-', '_'),
+ get_first_key=True, no_tag_node_value_mangle=True)
+ if not conf.exists(base):
+ snmp.update({'deleted' : ''})
+
+ if conf.exists(['service', 'lldp', 'snmp', 'enable']):
+ snmp.update({'lldp_snmp' : ''})
- conf.set_level('service snmp')
+ if conf.exists(['system', 'ipv6', 'disable']):
+ snmp.update({'ipv6_disabled' : ''})
+
+ if 'deleted' in snmp:
+ return snmp
version_data = get_version_data()
snmp['version'] = version_data['version']
@@ -96,466 +70,207 @@ def get_config():
snmp['vyos_user'] = 'vyos' + random(8)
snmp['vyos_user_pass'] = random(16)
- if conf.exists('community'):
- for name in conf.list_nodes('community'):
- community = {
- 'name': name,
- 'authorization': 'ro',
- 'network_v4': [],
- 'network_v6': [],
- 'has_source' : False
- }
-
- if conf.exists('community {0} authorization'.format(name)):
- community['authorization'] = conf.return_value('community {0} authorization'.format(name))
-
- # Subnet of SNMP client(s) allowed to contact system
- if conf.exists('community {0} network'.format(name)):
- for addr in conf.return_values('community {0} network'.format(name)):
- if is_ipv4(addr):
- community['network_v4'].append(addr)
- else:
- community['network_v6'].append(addr)
-
- # IP address of SNMP client allowed to contact system
- if conf.exists('community {0} client'.format(name)):
- for addr in conf.return_values('community {0} client'.format(name)):
- if is_ipv4(addr):
- community['network_v4'].append(addr)
- else:
- community['network_v6'].append(addr)
-
- if (len(community['network_v4']) > 0) or (len(community['network_v6']) > 0):
- community['has_source'] = True
-
- snmp['communities'].append(community)
-
- if conf.exists('contact'):
- snmp['contact'] = conf.return_value('contact')
-
- if conf.exists('description'):
- snmp['description'] = conf.return_value('description')
-
- if conf.exists('listen-address'):
- for addr in conf.list_nodes('listen-address'):
- port = '161'
- if conf.exists('listen-address {0} port'.format(addr)):
- port = conf.return_value('listen-address {0} port'.format(addr))
-
- snmp['listen_address'].append((addr, port))
+ # 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)
+
+ # We can not merge defaults for tagNodes - those need to be blended in
+ # per tagNode instance
+ if 'listen_address' in default_values:
+ del default_values['listen_address']
+ if 'community' in default_values:
+ del default_values['community']
+ if 'trap_target' in default_values:
+ del default_values['trap_target']
+ if 'v3' in default_values:
+ del default_values['v3']
+ snmp = dict_merge(default_values, snmp)
+
+ if 'listen_address' in snmp:
+ default_values = defaults(base + ['listen-address'])
+ for address in snmp['listen_address']:
+ snmp['listen_address'][address] = dict_merge(
+ default_values, snmp['listen_address'][address])
# Always listen on localhost if an explicit address has been configured
# This is a safety measure to not end up with invalid listen addresses
# that are not configured on this system. See https://phabricator.vyos.net/T850
- if not '127.0.0.1' in conf.list_nodes('listen-address'):
- snmp['listen_address'].append(('127.0.0.1', '161'))
-
- if not '::1' in conf.list_nodes('listen-address'):
- snmp['listen_address'].append(('::1', '161'))
-
- if conf.exists('location'):
- snmp['location'] = conf.return_value('location')
-
- if conf.exists('protocol'):
- snmp['protocol'] = conf.return_value('protocol')
-
- if conf.exists('smux-peer'):
- snmp['smux_peers'] = conf.return_values('smux-peer')
-
- if conf.exists('trap-source'):
- snmp['trap_source'] = conf.return_value('trap-source')
-
- if conf.exists('trap-target'):
- for target in conf.list_nodes('trap-target'):
- trap_tgt = {
- 'target': target,
- 'community': '',
- 'port': ''
- }
-
- if conf.exists('trap-target {0} community'.format(target)):
- trap_tgt['community'] = conf.return_value('trap-target {0} community'.format(target))
-
- if conf.exists('trap-target {0} port'.format(target)):
- trap_tgt['port'] = conf.return_value('trap-target {0} port'.format(target))
-
- snmp['trap_targets'].append(trap_tgt)
-
- if conf.exists('script-extensions'):
- for extname in conf.list_nodes('script-extensions extension-name'):
- conf_script = conf.return_value('script-extensions extension-name {} script'.format(extname))
- # if script has not absolute path, use pre configured path
- if "/" not in conf_script:
- conf_script = default_script_dir + conf_script
-
- extension = {
- 'name': extname,
- 'script' : conf_script
- }
-
- snmp['script_ext'].append(extension)
-
- if conf.exists('oid-enable route-table'):
- snmp['route_table'] = True
-
- if conf.exists('vrf'):
- # Append key to dict but don't place it in the default dictionary.
- # This is required to make the override.conf.tmpl work until we
- # migrate to get_config_dict().
- snmp['vrf'] = conf.return_value('vrf')
-
-
- #########################################################################
- # ____ _ _ __ __ ____ _____ #
- # / ___|| \ | | \/ | _ \ __ _|___ / #
- # \___ \| \| | |\/| | |_) | \ \ / / |_ \ #
- # ___) | |\ | | | | __/ \ V / ___) | #
- # |____/|_| \_|_| |_|_| \_/ |____/ #
- # #
- # now take care about the fancy SNMP v3 stuff, or bail out eraly #
- #########################################################################
- if not conf.exists('v3'):
- return snmp
- else:
- snmp['v3_enabled'] = True
-
- # 'set service snmp v3 engineid'
- if conf.exists('v3 engineid'):
- snmp['v3_engineid'] = conf.return_value('v3 engineid')
-
- # 'set service snmp v3 group'
- if conf.exists('v3 group'):
- for group in conf.list_nodes('v3 group'):
- v3_group = {
- 'name': group,
- 'mode': 'ro',
- 'seclevel': 'auth',
- 'view': ''
- }
-
- if conf.exists('v3 group {0} mode'.format(group)):
- v3_group['mode'] = conf.return_value('v3 group {0} mode'.format(group))
-
- if conf.exists('v3 group {0} seclevel'.format(group)):
- v3_group['seclevel'] = conf.return_value('v3 group {0} seclevel'.format(group))
-
- if conf.exists('v3 group {0} view'.format(group)):
- v3_group['view'] = conf.return_value('v3 group {0} view'.format(group))
-
- snmp['v3_groups'].append(v3_group)
-
- # 'set service snmp v3 trap-target'
- if conf.exists('v3 trap-target'):
- for trap in conf.list_nodes('v3 trap-target'):
- trap_cfg = {
- 'ipAddr': trap,
- 'secName': '',
- 'authProtocol': 'md5',
- 'authPassword': '',
- 'authMasterKey': '',
- 'privProtocol': 'des',
- 'privPassword': '',
- 'privMasterKey': '',
- 'ipProto': 'udp',
- 'ipPort': '162',
- 'type': '',
- 'secLevel': 'noAuthNoPriv'
- }
-
- if conf.exists('v3 trap-target {0} user'.format(trap)):
- # Set the securityName used for authenticated SNMPv3 messages.
- trap_cfg['secName'] = conf.return_value('v3 trap-target {0} user'.format(trap))
-
- if conf.exists('v3 trap-target {0} auth type'.format(trap)):
- # Set the authentication protocol (MD5 or SHA) used for authenticated SNMPv3 messages
- # cmdline option '-a'
- trap_cfg['authProtocol'] = conf.return_value('v3 trap-target {0} auth type'.format(trap))
-
- if conf.exists('v3 trap-target {0} auth plaintext-password'.format(trap)):
- # Set the authentication pass phrase used for authenticated SNMPv3 messages.
- # cmdline option '-A'
- trap_cfg['authPassword'] = conf.return_value('v3 trap-target {0} auth plaintext-password'.format(trap))
-
- if conf.exists('v3 trap-target {0} auth encrypted-password'.format(trap)):
- # Sets the keys to be used for SNMPv3 transactions. These options allow you to set the master authentication keys.
- # cmdline option '-3m'
- trap_cfg['authMasterKey'] = conf.return_value('v3 trap-target {0} auth encrypted-password'.format(trap))
-
- if conf.exists('v3 trap-target {0} privacy type'.format(trap)):
- # Set the privacy protocol (DES or AES) used for encrypted SNMPv3 messages.
- # cmdline option '-x'
- trap_cfg['privProtocol'] = conf.return_value('v3 trap-target {0} privacy type'.format(trap))
-
- if conf.exists('v3 trap-target {0} privacy plaintext-password'.format(trap)):
- # Set the privacy pass phrase used for encrypted SNMPv3 messages.
- # cmdline option '-X'
- trap_cfg['privPassword'] = conf.return_value('v3 trap-target {0} privacy plaintext-password'.format(trap))
-
- if conf.exists('v3 trap-target {0} privacy encrypted-password'.format(trap)):
- # Sets the keys to be used for SNMPv3 transactions. These options allow you to set the master encryption keys.
- # cmdline option '-3M'
- trap_cfg['privMasterKey'] = conf.return_value('v3 trap-target {0} privacy encrypted-password'.format(trap))
-
- if conf.exists('v3 trap-target {0} protocol'.format(trap)):
- trap_cfg['ipProto'] = conf.return_value('v3 trap-target {0} protocol'.format(trap))
-
- if conf.exists('v3 trap-target {0} port'.format(trap)):
- trap_cfg['ipPort'] = conf.return_value('v3 trap-target {0} port'.format(trap))
-
- if conf.exists('v3 trap-target {0} type'.format(trap)):
- trap_cfg['type'] = conf.return_value('v3 trap-target {0} type'.format(trap))
-
- # Determine securityLevel used for SNMPv3 messages (noAuthNoPriv|authNoPriv|authPriv).
- # Appropriate pass phrase(s) must provided when using any level higher than noAuthNoPriv.
- if trap_cfg['authPassword'] or trap_cfg['authMasterKey']:
- if trap_cfg['privProtocol'] or trap_cfg['privPassword']:
- trap_cfg['secLevel'] = 'authPriv'
- else:
- trap_cfg['secLevel'] = 'authNoPriv'
-
- snmp['v3_traps'].append(trap_cfg)
-
- # 'set service snmp v3 user'
- if conf.exists('v3 user'):
- for user in conf.list_nodes('v3 user'):
- user_cfg = {
- 'name': user,
- 'authMasterKey': '',
- 'authPassword': '',
- 'authProtocol': 'md5',
- 'authOID': 'none',
- 'group': '',
- 'mode': 'ro',
- 'privMasterKey': '',
- 'privPassword': '',
- 'privOID': '',
- 'privProtocol': 'des'
- }
-
- # v3 user {0} auth
- if conf.exists('v3 user {0} auth encrypted-password'.format(user)):
- user_cfg['authMasterKey'] = conf.return_value('v3 user {0} auth encrypted-password'.format(user))
-
- if conf.exists('v3 user {0} auth plaintext-password'.format(user)):
- user_cfg['authPassword'] = conf.return_value('v3 user {0} auth plaintext-password'.format(user))
-
- # load default value
- type = user_cfg['authProtocol']
- if conf.exists('v3 user {0} auth type'.format(user)):
- type = conf.return_value('v3 user {0} auth type'.format(user))
-
- # (re-)update with either default value or value from CLI
- user_cfg['authProtocol'] = type
- user_cfg['authOID'] = OIDs[type]
-
- # v3 user {0} group
- if conf.exists('v3 user {0} group'.format(user)):
- user_cfg['group'] = conf.return_value('v3 user {0} group'.format(user))
-
- # v3 user {0} mode
- if conf.exists('v3 user {0} mode'.format(user)):
- user_cfg['mode'] = conf.return_value('v3 user {0} mode'.format(user))
-
- # v3 user {0} privacy
- if conf.exists('v3 user {0} privacy encrypted-password'.format(user)):
- user_cfg['privMasterKey'] = conf.return_value('v3 user {0} privacy encrypted-password'.format(user))
-
- if conf.exists('v3 user {0} privacy plaintext-password'.format(user)):
- user_cfg['privPassword'] = conf.return_value('v3 user {0} privacy plaintext-password'.format(user))
-
- # load default value
- type = user_cfg['privProtocol']
- if conf.exists('v3 user {0} privacy type'.format(user)):
- type = conf.return_value('v3 user {0} privacy type'.format(user))
-
- # (re-)update with either default value or value from CLI
- user_cfg['privProtocol'] = type
- user_cfg['privOID'] = OIDs[type]
-
- snmp['v3_users'].append(user_cfg)
-
- # 'set service snmp v3 view'
- if conf.exists('v3 view'):
- for view in conf.list_nodes('v3 view'):
- view_cfg = {
- 'name': view,
- 'oids': []
- }
-
- if conf.exists('v3 view {0} oid'.format(view)):
- for oid in conf.list_nodes('v3 view {0} oid'.format(view)):
- oid_cfg = {
- 'oid': oid
- }
- view_cfg['oids'].append(oid_cfg)
- snmp['v3_views'].append(view_cfg)
+ if '127.0.0.1' not in snmp['listen_address']:
+ tmp = {'127.0.0.1': {'port': '161'}}
+ snmp['listen_address'] = dict_merge(tmp, snmp['listen_address'])
+
+ if '::1' not in snmp['listen_address']:
+ if 'ipv6_disabled' not in snmp:
+ tmp = {'::1': {'port': '161'}}
+ snmp['listen_address'] = dict_merge(tmp, snmp['listen_address'])
+
+ if 'community' in snmp:
+ default_values = defaults(base + ['community'])
+ for community in snmp['community']:
+ snmp['community'][community] = dict_merge(
+ default_values, snmp['community'][community])
+
+ if 'trap_target' in snmp:
+ default_values = defaults(base + ['trap-target'])
+ for trap in snmp['trap_target']:
+ snmp['trap_target'][trap] = dict_merge(
+ default_values, snmp['trap_target'][trap])
+
+ if 'v3' in snmp:
+ default_values = defaults(base + ['v3'])
+ # tagNodes need to be merged in individually later on
+ for tmp in ['user', 'group', 'trap_target']:
+ del default_values[tmp]
+ snmp['v3'] = dict_merge(default_values, snmp['v3'])
+
+ for user_group in ['user', 'group']:
+ if user_group in snmp['v3']:
+ default_values = defaults(base + ['v3', user_group])
+ for tmp in snmp['v3'][user_group]:
+ snmp['v3'][user_group][tmp] = dict_merge(
+ default_values, snmp['v3'][user_group][tmp])
+
+ if 'trap_target' in snmp['v3']:
+ default_values = defaults(base + ['v3', 'trap-target'])
+ for trap in snmp['v3']['trap_target']:
+ snmp['v3']['trap_target'][trap] = dict_merge(
+ default_values, snmp['v3']['trap_target'][trap])
return snmp
def verify(snmp):
- if snmp is None:
- # we can not delete SNMP when LLDP is configured with SNMP
- conf = Config()
- if conf.exists('service lldp snmp enable'):
- raise ConfigError('Can not delete SNMP service, as LLDP still uses SNMP!')
-
+ if not snmp:
return None
+ if {'deleted', 'lldp_snmp'} <= set(snmp):
+ raise ConfigError('Can not delete SNMP service, as LLDP still uses SNMP!')
+
### check if the configured script actually exist
- if snmp['script_ext']:
- for ext in snmp['script_ext']:
- if not os.path.isfile(ext['script']):
- print ("WARNING: script: {} doesn't exist".format(ext['script']))
+ if 'script_extensions' in snmp and 'extension_name' in snmp['script_extensions']:
+ for extension, extension_opt in snmp['script_extensions']['extension_name'].items():
+ if 'script' not in extension_opt:
+ raise ConfigError(f'Script extension "{extension}" requires an actual script to be configured!')
+
+ tmp = extension_opt['script']
+ if not os.path.isfile(tmp):
+ print(f'WARNING: script "{tmp}" does not exist!')
else:
- chmod_755(ext['script'])
-
- for listen in snmp['listen_address']:
- addr = listen[0]
- port = listen[1]
- protocol = snmp['protocol']
-
- tmp = None
- if is_ipv4(addr):
- # example: udp:127.0.0.1:161
- tmp = f'{protocol}:{addr}:{port}'
- elif snmp['ipv6_enabled']:
- # example: udp6:[::1]:161
- tmp = f'{protocol}6:[{addr}]:{port}'
-
- # We only wan't to configure addresses that exist on the system.
- # Hint the user if they don't exist
- if is_addr_assigned(addr):
- if tmp: snmp['listen_on'].append(tmp)
- else:
- print(f'WARNING: SNMP listen address {addr} not configured!')
+ chmod_755(extension_opt['script'])
+
+ if 'listen_address' in snmp:
+ for address in snmp['listen_address']:
+ # We only wan't to configure addresses that exist on the system.
+ # Hint the user if they don't exist
+ if not is_addr_assigned(address):
+ print(f'WARNING: SNMP listen address "{address}" not configured!')
+
+ if 'trap_target' in snmp:
+ for trap, trap_config in snmp['trap_target'].items():
+ if 'community' not in trap_config:
+ raise ConfigError(f'Trap target "{trap}" requires a community to be set!')
verify_vrf(snmp)
# bail out early if SNMP v3 is not configured
- if not snmp['v3_enabled']:
+ if 'v3' not in snmp:
return None
- if 'v3_groups' in snmp.keys():
- for group in snmp['v3_groups']:
- #
- # A view must exist prior to mapping it into a group
- #
- if 'view' in group.keys():
- error = True
- if 'v3_views' in snmp.keys():
- for view in snmp['v3_views']:
- if view['name'] == group['view']:
- error = False
- if error:
- raise ConfigError('You must create view "{0}" first'.format(group['view']))
- else:
- raise ConfigError('"view" must be specified')
-
- if not 'mode' in group.keys():
- raise ConfigError('"mode" must be specified')
-
- if not 'seclevel' in group.keys():
- raise ConfigError('"seclevel" must be specified')
-
- if 'v3_traps' in snmp.keys():
- for trap in snmp['v3_traps']:
- if trap['authPassword'] and trap['authMasterKey']:
- raise ConfigError('Must specify only one of encrypted-password/plaintext-key for trap auth')
-
- if trap['authPassword'] == '' and trap['authMasterKey'] == '':
- raise ConfigError('Must specify encrypted-password or plaintext-key for trap auth')
-
- if trap['privPassword'] and trap['privMasterKey']:
- raise ConfigError('Must specify only one of encrypted-password/plaintext-key for trap privacy')
+ if 'user' in snmp['v3']:
+ for user, user_config in snmp['v3']['user'].items():
+ if 'group' not in user_config:
+ raise ConfigError(f'Group membership required for user "{user}"!')
- if trap['privPassword'] == '' and trap['privMasterKey'] == '':
- raise ConfigError('Must specify encrypted-password or plaintext-key for trap privacy')
+ if 'plaintext_password' not in user_config['auth'] and 'encrypted_password' not in user_config['auth']:
+ raise ConfigError(f'Must specify authentication encrypted-password or plaintext-password for user "{user}"!')
- if not 'type' in trap.keys():
- raise ConfigError('v3 trap: "type" must be specified')
+ if 'plaintext_password' not in user_config['privacy'] and 'encrypted_password' not in user_config['privacy']:
+ raise ConfigError(f'Must specify privacy encrypted-password or plaintext-password for user "{user}"!')
- if not 'authPassword' and 'authMasterKey' in trap.keys():
- raise ConfigError('v3 trap: "auth" must be specified')
+ if 'group' in snmp['v3']:
+ for group, group_config in snmp['v3']['group'].items():
+ if 'seclevel' not in group_config:
+ raise ConfigError(f'Must configure "seclevel" for group "{group}"!')
+ if 'view' not in group_config:
+ raise ConfigError(f'Must configure "view" for group "{group}"!')
- if not 'authProtocol' in trap.keys():
- raise ConfigError('v3 trap: "protocol" must be specified')
+ # Check if 'view' exists
+ view = group_config['view']
+ if 'view' not in snmp['v3'] or view not in snmp['v3']['view']:
+ raise ConfigError(f'You must create view "{view}" first!')
- if not 'privPassword' and 'privMasterKey' in trap.keys():
- raise ConfigError('v3 trap: "user" must be specified')
+ if 'view' in snmp['v3']:
+ for view, view_config in snmp['v3']['view'].items():
+ if 'oid' not in view_config:
+ raise ConfigError(f'Must configure an "oid" for view "{view}"!')
- if 'v3_users' in snmp.keys():
- for user in snmp['v3_users']:
- #
- # Group must exist prior to mapping it into a group
- # seclevel will be extracted from group
- #
- if user['group']:
- error = True
- if 'v3_groups' in snmp.keys():
- for group in snmp['v3_groups']:
- if group['name'] == user['group']:
- seclevel = group['seclevel']
- error = False
+ if 'trap_target' in snmp['v3']:
+ for trap, trap_config in snmp['v3']['trap_target'].items():
+ if 'plaintext_password' not in trap_config['auth'] and 'encrypted_password' not in trap_config['auth']:
+ raise ConfigError(f'Must specify one of authentication encrypted-password or plaintext-password for trap "{trap}"!')
- if error:
- raise ConfigError('You must create group "{0}" first'.format(user['group']))
+ if {'plaintext_password', 'encrypted_password'} <= set(trap_config['auth']):
+ raise ConfigError(f'Can not specify both authentication encrypted-password and plaintext-password for trap "{trap}"!')
- # Depending on the configured security level the user has to provide additional info
- if (not user['authPassword'] and not user['authMasterKey']):
- raise ConfigError('Must specify encrypted-password or plaintext-key for user auth')
+ if 'plaintext_password' not in trap_config['privacy'] and 'encrypted_password' not in trap_config['privacy']:
+ raise ConfigError(f'Must specify one of privacy encrypted-password or plaintext-password for trap "{trap}"!')
- if user['privPassword'] == '' and user['privMasterKey'] == '':
- raise ConfigError('Must specify encrypted-password or plaintext-key for user privacy')
+ if {'plaintext_password', 'encrypted_password'} <= set(trap_config['privacy']):
+ raise ConfigError(f'Can not specify both privacy encrypted-password and plaintext-password for trap "{trap}"!')
- if user['mode'] == '':
- raise ConfigError('Must specify user mode ro/rw')
-
- if 'v3_views' in snmp.keys():
- for view in snmp['v3_views']:
- if not view['oids']:
- raise ConfigError('Must configure an oid')
+ if 'type' not in trap_config:
+ raise ConfigError('SNMP v3 trap "type" must be specified!')
return None
def generate(snmp):
+
#
# As we are manipulating the snmpd user database we have to stop it first!
# This is even save if service is going to be removed
- call('systemctl stop snmpd.service')
- config_files = [config_file_client, config_file_daemon, config_file_access,
- config_file_user, systemd_override]
+ call(f'systemctl stop {systemd_service}')
+ # Clean config files
+ config_files = [config_file_client, config_file_daemon,
+ config_file_access, config_file_user, systemd_override]
for file in config_files:
- rmfile(file)
+ if os.path.isfile(file):
+ os.unlink(file)
if not snmp:
return None
- if 'v3_users' in snmp.keys():
+ if 'v3' in snmp:
# net-snmp is now regenerating the configuration file in the background
# thus we need to re-open and re-read the file as the content changed.
# After that we can no read the encrypted password from the config and
# replace the CLI plaintext password with its encrypted version.
- os.environ["vyos_libexec_dir"] = "/usr/libexec/vyos"
+ os.environ['vyos_libexec_dir'] = '/usr/libexec/vyos'
- for user in snmp['v3_users']:
- if user['authProtocol'] == 'sha':
- hash = plaintext_to_sha1
- else:
- hash = plaintext_to_md5
+ if 'user' in snmp['v3']:
+ for user, user_config in snmp['v3']['user'].items():
+ if dict_search('auth.type', user_config) == 'sha':
+ hash = plaintext_to_sha1
+ else:
+ hash = plaintext_to_md5
+
+ if dict_search('auth.plaintext_password', user_config) is not None:
+ tmp = hash(dict_search('auth.plaintext_password', user_config),
+ dict_search('v3.engineid', snmp))
+
+ snmp['v3']['user'][user]['auth']['encrypted_password'] = tmp
+ del snmp['v3']['user'][user]['auth']['plaintext_password']
- if user['authPassword']:
- user['authMasterKey'] = hash(user['authPassword'], snmp['v3_engineid'])
- user['authPassword'] = ''
+ call(f'/opt/vyatta/sbin/my_set service snmp v3 user "{user}" auth encrypted-password "{tmp}" > /dev/null')
+ call(f'/opt/vyatta/sbin/my_delete service snmp v3 user "{user}" auth plaintext-password > /dev/null')
- call('/opt/vyatta/sbin/my_set service snmp v3 user "{name}" auth encrypted-password "{authMasterKey}" > /dev/null'.format(**user))
- call('/opt/vyatta/sbin/my_delete service snmp v3 user "{name}" auth plaintext-password > /dev/null'.format(**user))
+ if dict_search('privacy.plaintext_password', user_config) is not None:
+ tmp = hash(dict_search('privacy.plaintext_password', user_config),
+ dict_search('v3.engineid', snmp))
- if user['privPassword']:
- user['privMasterKey'] = hash(user['privPassword'], snmp['v3_engineid'])
- user['privPassword'] = ''
+ snmp['v3']['user'][user]['privacy']['encrypted_password'] = tmp
+ del snmp['v3']['user'][user]['privacy']['plaintext_password']
- call('/opt/vyatta/sbin/my_set service snmp v3 user "{name}" privacy encrypted-password "{privMasterKey}" > /dev/null'.format(**user))
- call('/opt/vyatta/sbin/my_delete service snmp v3 user "{name}" privacy plaintext-password > /dev/null'.format(**user))
+ call(f'/opt/vyatta/sbin/my_set service snmp v3 user "{user}" privacy encrypted-password "{tmp}" > /dev/null')
+ call(f'/opt/vyatta/sbin/my_delete service snmp v3 user "{user}" privacy plaintext-password > /dev/null')
# Write client config file
render(config_file_client, 'snmp/etc.snmp.conf.tmpl', snmp)
@@ -578,7 +293,7 @@ def apply(snmp):
return None
# start SNMP daemon
- call('systemctl restart snmpd.service')
+ call(f'systemctl restart {systemd_service}')
# Enable AgentX in FRR
call('vtysh -c "configure terminal" -c "agentx" >/dev/null')
diff --git a/src/conf_mode/system-login-banner.py b/src/conf_mode/system-login-banner.py
index 2220d7b66..a521c9834 100755
--- a/src/conf_mode/system-login-banner.py
+++ b/src/conf_mode/system-login-banner.py
@@ -15,22 +15,20 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from sys import exit
+from copy import deepcopy
+
from vyos.config import Config
+from vyos.util import write_file
from vyos import ConfigError
-
from vyos import airbag
airbag.enable()
-motd="""
-Check out project news at https://blog.vyos.io
-and feel free to report bugs at https://phabricator.vyos.net
-
-You can change this banner using "set system login banner post-login" command.
-
-VyOS is a free software distribution that includes multiple components,
-you can check individual component licenses under /usr/share/doc/*/copyright
-
-"""
+try:
+ with open('/usr/share/vyos/default_motd') as f:
+ motd = f.read()
+except:
+ # Use an empty banner if the default banner file cannot be read
+ motd = "\n"
PRELOGIN_FILE = r'/etc/issue'
PRELOGIN_NET_FILE = r'/etc/issue.net'
@@ -38,12 +36,12 @@ POSTLOGIN_FILE = r'/etc/motd'
default_config_data = {
'issue': 'Welcome to VyOS - \\n \\l\n\n',
- 'issue_net': 'Welcome to VyOS\n',
+ 'issue_net': '',
'motd': motd
}
def get_config(config=None):
- banner = default_config_data
+ banner = deepcopy(default_config_data)
if config:
conf = config
else:
@@ -92,14 +90,9 @@ def generate(banner):
pass
def apply(banner):
- with open(PRELOGIN_FILE, 'w') as f:
- f.write(banner['issue'])
-
- with open(PRELOGIN_NET_FILE, 'w') as f:
- f.write(banner['issue_net'])
-
- with open(POSTLOGIN_FILE, 'w') as f:
- f.write(banner['motd'])
+ write_file(PRELOGIN_FILE, banner['issue'])
+ write_file(PRELOGIN_NET_FILE, banner['issue_net'])
+ write_file(POSTLOGIN_FILE, banner['motd'])
return None
diff --git a/src/conf_mode/system-logs.py b/src/conf_mode/system-logs.py
new file mode 100755
index 000000000..e6296656d
--- /dev/null
+++ b/src/conf_mode/system-logs.py
@@ -0,0 +1,83 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2021 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 exit
+
+from vyos import ConfigError
+from vyos import airbag
+from vyos.config import Config
+from vyos.configdict import dict_merge
+from vyos.logger import syslog
+from vyos.template import render
+from vyos.util import dict_search
+from vyos.xml import defaults
+airbag.enable()
+
+# path to logrotate configs
+logrotate_atop_file = '/etc/logrotate.d/vyos-atop'
+logrotate_rsyslog_file = '/etc/logrotate.d/vyos-rsyslog'
+
+
+def get_config(config=None):
+ if config:
+ conf = config
+ else:
+ conf = Config()
+
+ base = ['system', 'logs']
+ default_values = defaults(base)
+ logs_config = conf.get_config_dict(base,
+ key_mangling=('-', '_'),
+ get_first_key=True)
+ logs_config = dict_merge(default_values, logs_config)
+
+ return logs_config
+
+
+def verify(logs_config):
+ # Nothing to verify here
+ pass
+
+
+def generate(logs_config):
+ # get configuration for logrotate atop
+ logrotate_atop = dict_search('logrotate.atop', logs_config)
+ # generate new config file for atop
+ syslog.debug('Adding logrotate config for atop')
+ render(logrotate_atop_file, 'logs/logrotate/vyos-atop.tmpl', logrotate_atop)
+
+ # get configuration for logrotate rsyslog
+ logrotate_rsyslog = dict_search('logrotate.messages', logs_config)
+ # generate new config file for rsyslog
+ syslog.debug('Adding logrotate config for rsyslog')
+ render(logrotate_rsyslog_file, 'logs/logrotate/vyos-rsyslog.tmpl',
+ logrotate_rsyslog)
+
+
+def apply(logs_config):
+ # No further actions needed
+ pass
+
+
+if __name__ == '__main__':
+ try:
+ c = get_config()
+ verify(c)
+ generate(c)
+ apply(c)
+ except ConfigError as e:
+ print(e)
+ exit(1)
diff --git a/src/etc/dhcp/dhclient-enter-hooks.d/04-vyos-resolvconf b/src/etc/dhcp/dhclient-enter-hooks.d/04-vyos-resolvconf
index 24090e2a8..b1902b585 100644
--- a/src/etc/dhcp/dhclient-enter-hooks.d/04-vyos-resolvconf
+++ b/src/etc/dhcp/dhclient-enter-hooks.d/04-vyos-resolvconf
@@ -1,44 +1,48 @@
-# modified make_resolv_conf () for VyOS
-make_resolv_conf() {
- hostsd_client="/usr/bin/vyos-hostsd-client"
- hostsd_changes=
+# modified make_resolv_conf() for VyOS
+# should be used only if vyos-hostsd is running
- if [ -n "$new_domain_name" ]; then
- logmsg info "Deleting search domains with tag \"dhcp-$interface\" via vyos-hostsd-client"
- $hostsd_client --delete-search-domains --tag "dhcp-$interface"
- logmsg info "Adding domain name \"$new_domain_name\" as search domain with tag \"dhcp-$interface\" via vyos-hostsd-client"
- $hostsd_client --add-search-domains "$new_domain_name" --tag "dhcp-$interface"
- hostsd_changes=y
- fi
+if /usr/bin/systemctl -q is-active vyos-hostsd; then
+ make_resolv_conf() {
+ hostsd_client="/usr/bin/vyos-hostsd-client"
+ hostsd_changes=
- if [ -n "$new_dhcp6_domain_search" ]; then
- logmsg info "Deleting search domains with tag \"dhcpv6-$interface\" via vyos-hostsd-client"
- $hostsd_client --delete-search-domains --tag "dhcpv6-$interface"
- logmsg info "Adding search domain \"$new_dhcp6_domain_search\" with tag \"dhcpv6-$interface\" via vyos-hostsd-client"
- $hostsd_client --add-search-domains "$new_dhcp6_domain_search" --tag "dhcpv6-$interface"
- hostsd_changes=y
- fi
+ if [ -n "$new_domain_name" ]; then
+ logmsg info "Deleting search domains with tag \"dhcp-$interface\" via vyos-hostsd-client"
+ $hostsd_client --delete-search-domains --tag "dhcp-$interface"
+ logmsg info "Adding domain name \"$new_domain_name\" as search domain with tag \"dhcp-$interface\" via vyos-hostsd-client"
+ $hostsd_client --add-search-domains "$new_domain_name" --tag "dhcp-$interface"
+ hostsd_changes=y
+ fi
- if [ -n "$new_domain_name_servers" ]; then
- logmsg info "Deleting nameservers with tag \"dhcp-$interface\" via vyos-hostsd-client"
- $hostsd_client --delete-name-servers --tag "dhcp-$interface"
- logmsg info "Adding nameservers \"$new_domain_name_servers\" with tag \"dhcp-$interface\" via vyos-hostsd-client"
- $hostsd_client --add-name-servers $new_domain_name_servers --tag "dhcp-$interface"
- hostsd_changes=y
- fi
+ if [ -n "$new_dhcp6_domain_search" ]; then
+ logmsg info "Deleting search domains with tag \"dhcpv6-$interface\" via vyos-hostsd-client"
+ $hostsd_client --delete-search-domains --tag "dhcpv6-$interface"
+ logmsg info "Adding search domain \"$new_dhcp6_domain_search\" with tag \"dhcpv6-$interface\" via vyos-hostsd-client"
+ $hostsd_client --add-search-domains "$new_dhcp6_domain_search" --tag "dhcpv6-$interface"
+ hostsd_changes=y
+ fi
- if [ -n "$new_dhcp6_name_servers" ]; then
- logmsg info "Deleting nameservers with tag \"dhcpv6-$interface\" via vyos-hostsd-client"
- $hostsd_client --delete-name-servers --tag "dhcpv6-$interface"
- logmsg info "Adding nameservers \"$new_dhcpv6_name_servers\" with tag \"dhcpv6-$interface\" via vyos-hostsd-client"
- $hostsd_client --add-name-servers $new_dhcpv6_name_servers --tag "dhcpv6-$interface"
- hostsd_changes=y
- fi
+ if [ -n "$new_domain_name_servers" ]; then
+ logmsg info "Deleting nameservers with tag \"dhcp-$interface\" via vyos-hostsd-client"
+ $hostsd_client --delete-name-servers --tag "dhcp-$interface"
+ logmsg info "Adding nameservers \"$new_domain_name_servers\" with tag \"dhcp-$interface\" via vyos-hostsd-client"
+ $hostsd_client --add-name-servers $new_domain_name_servers --tag "dhcp-$interface"
+ hostsd_changes=y
+ fi
- if [ $hostsd_changes ]; then
- logmsg info "Applying changes via vyos-hostsd-client"
- $hostsd_client --apply
- else
- logmsg info "No changes to apply via vyos-hostsd-client"
- fi
-}
+ if [ -n "$new_dhcp6_name_servers" ]; then
+ logmsg info "Deleting nameservers with tag \"dhcpv6-$interface\" via vyos-hostsd-client"
+ $hostsd_client --delete-name-servers --tag "dhcpv6-$interface"
+ logmsg info "Adding nameservers \"$new_dhcpv6_name_servers\" with tag \"dhcpv6-$interface\" via vyos-hostsd-client"
+ $hostsd_client --add-name-servers $new_dhcpv6_name_servers --tag "dhcpv6-$interface"
+ hostsd_changes=y
+ fi
+
+ if [ $hostsd_changes ]; then
+ logmsg info "Applying changes via vyos-hostsd-client"
+ $hostsd_client --apply
+ else
+ logmsg info "No changes to apply via vyos-hostsd-client"
+ fi
+ }
+fi
diff --git a/src/etc/dhcp/dhclient-exit-hooks.d/01-vyos-cleanup b/src/etc/dhcp/dhclient-exit-hooks.d/01-vyos-cleanup
index fec792b64..ad6a1d5eb 100644
--- a/src/etc/dhcp/dhclient-exit-hooks.d/01-vyos-cleanup
+++ b/src/etc/dhcp/dhclient-exit-hooks.d/01-vyos-cleanup
@@ -4,14 +4,19 @@
# NOTE: here we use 'ip' wrapper, therefore a route will be actually deleted via /usr/sbin/ip or vtysh, according to the system state
hostsd_client="/usr/bin/vyos-hostsd-client"
hostsd_changes=
+# check vyos-hostsd status
+/usr/bin/systemctl -q is-active vyos-hostsd
+hostsd_status=$?
if [[ $reason =~ (EXPIRE|FAIL|RELEASE|STOP) ]]; then
- # delete search domains and nameservers via vyos-hostsd
- logmsg info "Deleting search domains with tag \"dhcp-$interface\" via vyos-hostsd-client"
- $hostsd_client --delete-search-domains --tag "dhcp-$interface"
- logmsg info "Deleting nameservers with tag \"dhcp-${interface}\" via vyos-hostsd-client"
- $hostsd_client --delete-name-servers --tag "dhcp-${interface}"
- hostsd_changes=y
+ if [[ $hostsd_status -eq 0 ]]; then
+ # delete search domains and nameservers via vyos-hostsd
+ logmsg info "Deleting search domains with tag \"dhcp-$interface\" via vyos-hostsd-client"
+ $hostsd_client --delete-search-domains --tag "dhcp-$interface"
+ logmsg info "Deleting nameservers with tag \"dhcp-${interface}\" via vyos-hostsd-client"
+ $hostsd_client --delete-name-servers --tag "dhcp-${interface}"
+ hostsd_changes=y
+ fi
if_metric="$IF_METRIC"
@@ -92,12 +97,14 @@ if [[ $reason =~ (EXPIRE|FAIL|RELEASE|STOP) ]]; then
fi
if [[ $reason =~ (EXPIRE6|RELEASE6|STOP6) ]]; then
- # delete search domains and nameservers via vyos-hostsd
- logmsg info "Deleting search domains with tag \"dhcpv6-$interface\" via vyos-hostsd-client"
- $hostsd_client --delete-search-domains --tag "dhcpv6-$interface"
- logmsg info "Deleting nameservers with tag \"dhcpv6-${interface}\" via vyos-hostsd-client"
- $hostsd_client --delete-name-servers --tag "dhcpv6-${interface}"
- hostsd_changes=y
+ if [[ $hostsd_status -eq 0 ]]; then
+ # delete search domains and nameservers via vyos-hostsd
+ logmsg info "Deleting search domains with tag \"dhcpv6-$interface\" via vyos-hostsd-client"
+ $hostsd_client --delete-search-domains --tag "dhcpv6-$interface"
+ logmsg info "Deleting nameservers with tag \"dhcpv6-${interface}\" via vyos-hostsd-client"
+ $hostsd_client --delete-name-servers --tag "dhcpv6-${interface}"
+ hostsd_changes=y
+ fi
fi
if [ $hostsd_changes ]; then
diff --git a/src/etc/systemd/system/uacctd.service.d/override.conf b/src/etc/systemd/system/uacctd.service.d/override.conf
new file mode 100644
index 000000000..38bcce515
--- /dev/null
+++ b/src/etc/systemd/system/uacctd.service.d/override.conf
@@ -0,0 +1,14 @@
+[Unit]
+After=
+After=vyos-router.service
+ConditionPathExists=
+ConditionPathExists=/run/pmacct/uacctd.conf
+
+[Service]
+EnvironmentFile=
+ExecStart=
+ExecStart=/usr/sbin/uacctd -f /run/pmacct/uacctd.conf
+WorkingDirectory=
+WorkingDirectory=/run/pmacct
+PIDFile=
+PIDFile=/run/pmacct/uacctd.pid
diff --git a/src/helpers/vyos-boot-config-loader.py b/src/helpers/vyos-boot-config-loader.py
index c5bf22f10..b9cc87bfa 100755
--- a/src/helpers/vyos-boot-config-loader.py
+++ b/src/helpers/vyos-boot-config-loader.py
@@ -23,12 +23,12 @@ import grp
import traceback
from datetime import datetime
-from vyos.defaults import directories
+from vyos.defaults import directories, config_status
from vyos.configsession import ConfigSession, ConfigSessionError
from vyos.configtree import ConfigTree
from vyos.util import cmd
-STATUS_FILE = '/tmp/vyos-config-status'
+STATUS_FILE = config_status
TRACE_FILE = '/tmp/boot-config-trace'
CFG_GROUP = 'vyattacfg'
diff --git a/src/helpers/vyos_net_name b/src/helpers/vyos_net_name
index e21d8c9ff..afeef8f2d 100755
--- a/src/helpers/vyos_net_name
+++ b/src/helpers/vyos_net_name
@@ -25,14 +25,13 @@ from sys import argv
from vyos.configtree import ConfigTree
from vyos.defaults import directories
-from vyos.util import cmd
+from vyos.util import cmd, boot_configuration_complete
vyos_udev_dir = directories['vyos_udev_dir']
vyos_log_dir = '/run/udev/log'
vyos_log_file = os.path.join(vyos_log_dir, 'vyos-net-name')
config_path = '/opt/vyatta/etc/config/config.boot'
-config_status = '/tmp/vyos-config-status'
lock = threading.Lock()
@@ -43,13 +42,6 @@ except FileExistsError:
logging.basicConfig(filename=vyos_log_file, level=logging.DEBUG)
-def boot_configuration_complete() -> bool:
- """ Check if vyos-router has completed, hence hotplug event
- """
- if os.path.isfile(config_status):
- return True
- return False
-
def is_available(intfs: dict, intf_name: str) -> bool:
""" Check if interface name is already assigned
"""
diff --git a/src/migration-scripts/flow-accounting/0-to-1 b/src/migration-scripts/flow-accounting/0-to-1
new file mode 100755
index 000000000..72cce77b0
--- /dev/null
+++ b/src/migration-scripts/flow-accounting/0-to-1
@@ -0,0 +1,69 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2021 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/>.
+
+# T4099: flow-accounting: sync "source-ip" and "source-address" between netflow
+# and sflow ion CLI
+# T4105: flow-accounting: drop "sflow agent-address auto"
+
+from sys import 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', 'flow-accounting']
+config = ConfigTree(config_file)
+
+if not config.exists(base):
+ # Nothing to do
+ exit(0)
+
+# T4099
+tmp = base + ['netflow', 'source-ip']
+if config.exists(tmp):
+ config.rename(tmp, 'source-address')
+
+# T4105
+tmp = base + ['sflow', 'agent-address']
+if config.exists(tmp):
+ value = config.return_value(tmp)
+ if value == 'auto':
+ # delete the "auto"
+ config.delete(tmp)
+
+ # 1) check if BGP router-id is set
+ # 2) check if OSPF router-id is set
+ # 3) check if OSPFv3 router-id is set
+ router_id = None
+ for protocol in ['bgp', 'ospf', 'ospfv3']:
+ if config.exists(['protocols', protocol, 'parameters', 'router-id']):
+ router_id = config.return_value(['protocols', protocol, 'parameters', 'router-id'])
+ break
+ if router_id:
+ config.set(tmp, value=router_id)
+
+try:
+ with open(file_name, 'w') as f:
+ f.write(config.to_string())
+except OSError as e:
+ print("Failed to save the modified config: {}".format(e))
+ exit(1)
diff --git a/src/op_mode/conntrack_sync.py b/src/op_mode/conntrack_sync.py
index 66ecf8439..89f6df4b9 100755
--- a/src/op_mode/conntrack_sync.py
+++ b/src/op_mode/conntrack_sync.py
@@ -20,12 +20,15 @@ import xmltodict
from argparse import ArgumentParser
from vyos.configquery import CliShellApiConfigQuery
+from vyos.configquery import ConfigTreeQuery
+from vyos.util import call
from vyos.util import cmd
from vyos.util import run
from vyos.template import render_to_string
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()
@@ -36,6 +39,8 @@ group.add_argument('--show-internal', help='Show internal (main) tracking cache'
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 """
@@ -131,6 +136,46 @@ if __name__ == '__main__':
out = cmd(f'sudo {conntrackd_bin} -C {conntrackd_config} {opt} -x')
xml_to_stdout(out)
+ elif args.show_statistics:
+ is_configured()
+ config = ConfigTreeQuery()
+ print('\nMain Table Statistics:\n')
+ call(f'sudo {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')
+ 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"]
+ expect_sync_protocols = ', '.join(expect_sync_protocols)
+
+ 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'ExpectationSync : {expect_sync_protocols}')
+
+ print(show_status)
+
else:
parser.print_help()
exit(1)
diff --git a/src/op_mode/restart_frr.py b/src/op_mode/restart_frr.py
index 109c8dd7b..e5014452f 100755
--- a/src/op_mode/restart_frr.py
+++ b/src/op_mode/restart_frr.py
@@ -138,7 +138,7 @@ def _reload_config(daemon):
# define program arguments
cmd_args_parser = argparse.ArgumentParser(description='restart frr daemons')
cmd_args_parser.add_argument('--action', choices=['restart'], required=True, help='action to frr daemons')
-cmd_args_parser.add_argument('--daemon', choices=['bfdd', 'bgpd', 'ospfd', 'ospf6d', 'isisd', 'ripd', 'ripngd', 'staticd', 'zebra'], required=False, nargs='*', help='select single or multiple daemons')
+cmd_args_parser.add_argument('--daemon', choices=['bfdd', 'bgpd', 'ldpd', 'ospfd', 'ospf6d', 'isisd', 'ripd', 'ripngd', 'staticd', 'zebra'], required=False, nargs='*', help='select single or multiple daemons')
# parse arguments
cmd_args = cmd_args_parser.parse_args()
diff --git a/src/op_mode/show_nat_rules.py b/src/op_mode/show_nat_rules.py
index d68def26a..98adb31dd 100755
--- a/src/op_mode/show_nat_rules.py
+++ b/src/op_mode/show_nat_rules.py
@@ -32,7 +32,7 @@ args = parser.parse_args()
if args.source or args.destination:
tmp = cmd('sudo nft -j list table ip nat')
tmp = json.loads(tmp)
-
+
format_nat_rule = '{0: <10} {1: <50} {2: <50} {3: <10}'
print(format_nat_rule.format("Rule", "Source" if args.source else "Destination", "Translation", "Outbound Interface" if args.source else "Inbound Interface"))
print(format_nat_rule.format("----", "------" if args.source else "-----------", "-----------", "------------------" if args.source else "-----------------"))
@@ -40,7 +40,7 @@ if args.source or args.destination:
data_json = jmespath.search('nftables[?rule].rule[?chain]', tmp)
for idx in range(0, len(data_json)):
data = data_json[idx]
-
+
# The following key values must exist
# When the rule JSON does not have some keys, this is not a rule we can work with
continue_rule = False
@@ -50,9 +50,9 @@ if args.source or args.destination:
continue
if continue_rule:
continue
-
+
comment = data['comment']
-
+
# Check the annotation to see if the annotation format is created by VYOS
continue_rule = True
for comment_prefix in ['SRC-NAT-', 'DST-NAT-']:
@@ -60,7 +60,7 @@ if args.source or args.destination:
continue_rule = False
if continue_rule:
continue
-
+
rule = int(''.join(list(filter(str.isdigit, comment))))
chain = data['chain']
if not ((args.source and chain == 'POSTROUTING') or (not args.source and chain == 'PREROUTING')):
@@ -88,7 +88,7 @@ if args.source or args.destination:
else:
port_range = srcdest_json['set'][0]['range']
srcdest += 'port ' + str(port_range[0]) + '-' + str(port_range[1]) + ' '
-
+
tran_addr_json = dict_search('snat' if args.source else 'dnat', data['expr'][i])
if tran_addr_json:
if isinstance(tran_addr_json['addr'],str):
@@ -98,10 +98,10 @@ if args.source or args.destination:
len_tmp = dict_search('snat.addr.prefix.len' if args.source else 'dnat.addr.prefix.len', data['expr'][3])
if addr_tmp and len_tmp:
tran_addr += addr_tmp + '/' + str(len_tmp) + ' '
-
+
if isinstance(tran_addr_json['port'],int):
- tran_addr += 'port ' + tran_addr_json['port']
-
+ tran_addr += 'port ' + str(tran_addr_json['port'])
+
else:
if 'masquerade' in data['expr'][i]:
tran_addr = 'masquerade'
@@ -112,10 +112,10 @@ if args.source or args.destination:
srcdests.append(srcdest)
srcdest = ''
print(format_nat_rule.format(rule, srcdests[0], tran_addr, interface))
-
+
for i in range(1, len(srcdests)):
print(format_nat_rule.format(' ', srcdests[i], ' ', ' '))
-
+
exit(0)
else:
parser.print_help()
diff --git a/src/services/api/graphql/README.graphql b/src/services/api/graphql/README.graphql
index a3c30b005..1133d79ed 100644
--- a/src/services/api/graphql/README.graphql
+++ b/src/services/api/graphql/README.graphql
@@ -1,7 +1,12 @@
+The following examples are in the form as entered in the GraphQL
+'playground', which is found at:
+
+https://{{ host_address }}/graphql
+
Example using GraphQL mutations to configure a DHCP server:
-This assumes that the http-api is running:
+All examples assume that the http-api is running:
'set service https api'
@@ -58,8 +63,8 @@ N.B. fileName can be empty (fileName: "") or data can be empty (data: {}) to
save to /config/config.boot; to save to an alternative path, specify
fileName.
-Similarly, using the same 'endpoint' (meaning the form of the request and
-resolver; the actual enpoint for all GraphQL requests is
+Similarly, using an analogous 'endpoint' (meaning the form of the request
+and resolver; the actual enpoint for all GraphQL requests is
https://hostname/graphql), one can load an arbitrary config file from a
path.
@@ -75,7 +80,7 @@ mutation {
Op-mode 'show' commands may be requested by path, e.g.:
-mutation {
+query {
Show (data: {path: ["interfaces", "ethernet", "detail"]}) {
success
errors
@@ -88,16 +93,58 @@ mutation {
N.B. to see the output the 'data' field 'result' must be present in the
request.
-The GraphQL playground will be found at:
+Mutations to manipulate firewall address groups:
-https://{{ host_address }}/graphql
+mutation {
+ CreateFirewallAddressGroup (data: {name: "ADDR-GRP", address: "10.0.0.1"}) {
+ success
+ errors
+ }
+}
+
+mutation {
+ UpdateFirewallAddressGroupMembers (data: {name: "ADDR-GRP",
+ address: ["10.0.0.1-10.0.0.8", "192.168.0.1"]}) {
+ success
+ errors
+ }
+}
+
+mutation {
+ RemoveFirewallAddressGroupMembers (data: {name: "ADDR-GRP",
+ address: "192.168.0.1"}) {
+ success
+ errors
+ }
+}
+
+N.B. The schema for the above specify that 'address' be of the form 'list of
+strings' (SDL type [String!]! for UpdateFirewallAddressGroupMembers, where
+the ! indicates that the input is required; SDL type [String] in
+CreateFirewallAddressGroup, since a group may be created without any
+addresses). However, notice that a single string may be passed without being
+a member of a list, in which case the specification allows for 'input
+coercion':
+
+http://spec.graphql.org/October2021/#sec-Scalars.Input-Coercion
+
+Similarly, IPv6 versions of the above:
-An equivalent curl command to the first example above would be:
+CreateFirewallAddressIpv6Group
+UpdateFirewallAddressIpv6GroupMembers
+RemoveFirewallAddressIpv6GroupMembers
+
+
+Instead of using the GraphQL playground, an equivalent curl command to the
+first example above would be:
curl -k 'https://192.168.100.168/graphql' -H 'Content-Type: application/json' --data-binary '{"query": "mutation {createInterfaceEthernet (data: {interface: \"eth1\", address: \"192.168.0.1/24\", description: \"BOB\"}) {success errors data {address}}}"}'
Note that the 'mutation' term is prefaced by 'query' in the curl command.
+Curl equivalents may be read from within the GraphQL playground at the 'copy
+curl' button.
+
What's here:
services
diff --git a/src/services/api/graphql/bindings.py b/src/services/api/graphql/bindings.py
index 1fbe13d0c..84d719fda 100644
--- a/src/services/api/graphql/bindings.py
+++ b/src/services/api/graphql/bindings.py
@@ -1,4 +1,20 @@
+# Copyright 2021 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 vyos.defaults
+from . graphql.queries import query
from . graphql.mutations import mutation
from . graphql.directives import directives_dict
from ariadne import make_executable_schema, load_schema_from_path, snake_case_fallback_resolvers
@@ -8,6 +24,6 @@ def generate_schema():
type_defs = load_schema_from_path(api_schema_dir)
- schema = make_executable_schema(type_defs, mutation, snake_case_fallback_resolvers, directives=directives_dict)
+ schema = make_executable_schema(type_defs, query, mutation, snake_case_fallback_resolvers, directives=directives_dict)
return schema
diff --git a/src/services/api/graphql/graphql/directives.py b/src/services/api/graphql/graphql/directives.py
index 10bc522db..0a9298f55 100644
--- a/src/services/api/graphql/graphql/directives.py
+++ b/src/services/api/graphql/graphql/directives.py
@@ -1,4 +1,20 @@
+# Copyright 2021 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/>.
+
from ariadne import SchemaDirectiveVisitor, ObjectType
+from . queries import *
from . mutations import *
def non(arg):
diff --git a/src/services/api/graphql/graphql/mutations.py b/src/services/api/graphql/graphql/mutations.py
index 8e5aab56d..0c3eb702a 100644
--- a/src/services/api/graphql/graphql/mutations.py
+++ b/src/services/api/graphql/graphql/mutations.py
@@ -1,3 +1,17 @@
+# Copyright 2021 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/>.
from importlib import import_module
from typing import Any, Dict
@@ -10,7 +24,7 @@ from api.graphql.recipes.session import Session
mutation = ObjectType("Mutation")
-def make_resolver(mutation_name, class_name, session_func):
+def make_mutation_resolver(mutation_name, class_name, session_func):
"""Dynamically generate a resolver for the mutation named in the
schema by 'mutation_name'.
@@ -66,34 +80,20 @@ def make_resolver(mutation_name, class_name, session_func):
return func_impl
-def make_configure_resolver(mutation_name):
- class_name = mutation_name
- return make_resolver(mutation_name, class_name, 'configure')
+def make_prefix_resolver(mutation_name, prefix=[]):
+ for pre in prefix:
+ Pre = pre.capitalize()
+ if Pre in mutation_name:
+ class_name = mutation_name.replace(Pre, '', 1)
+ return make_mutation_resolver(mutation_name, class_name, pre)
+ raise Exception
-def make_show_config_resolver(mutation_name):
+def make_configure_resolver(mutation_name):
class_name = mutation_name
- return make_resolver(mutation_name, class_name, 'show_config')
+ return make_mutation_resolver(mutation_name, class_name, 'configure')
def make_config_file_resolver(mutation_name):
- if 'Save' in mutation_name:
- class_name = mutation_name.replace('Save', '', 1)
- return make_resolver(mutation_name, class_name, 'save')
- elif 'Load' in mutation_name:
- class_name = mutation_name.replace('Load', '', 1)
- return make_resolver(mutation_name, class_name, 'load')
- else:
- raise Exception
-
-def make_show_resolver(mutation_name):
- class_name = mutation_name
- return make_resolver(mutation_name, class_name, 'show')
+ return make_prefix_resolver(mutation_name, prefix=['save', 'load'])
def make_image_resolver(mutation_name):
- if 'Add' in mutation_name:
- class_name = mutation_name.replace('Add', '', 1)
- return make_resolver(mutation_name, class_name, 'add')
- elif 'Delete' in mutation_name:
- class_name = mutation_name.replace('Delete', '', 1)
- return make_resolver(mutation_name, class_name, 'delete')
- else:
- raise Exception
+ return make_prefix_resolver(mutation_name, prefix=['add', 'delete'])
diff --git a/src/services/api/graphql/graphql/queries.py b/src/services/api/graphql/graphql/queries.py
new file mode 100644
index 000000000..e1868091e
--- /dev/null
+++ b/src/services/api/graphql/graphql/queries.py
@@ -0,0 +1,89 @@
+# Copyright 2021 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/>.
+
+from importlib import import_module
+from typing import Any, Dict
+from ariadne import ObjectType, convert_kwargs_to_snake_case, convert_camel_case_to_snake
+from graphql import GraphQLResolveInfo
+from makefun import with_signature
+
+from .. import state
+from api.graphql.recipes.session import Session
+
+query = ObjectType("Query")
+
+def make_query_resolver(query_name, class_name, session_func):
+ """Dynamically generate a resolver for the query named in the
+ schema by 'query_name'.
+
+ Dynamic generation is provided using the package 'makefun' (via the
+ decorator 'with_signature'), which provides signature-preserving
+ function wrappers; it provides several improvements over, say,
+ functools.wraps.
+
+ :raise Exception:
+ raising ConfigErrors, or internal errors
+ """
+
+ func_base_name = convert_camel_case_to_snake(class_name)
+ resolver_name = f'resolve_{func_base_name}'
+ func_sig = '(obj: Any, info: GraphQLResolveInfo, data: Dict)'
+
+ @query.field(query_name)
+ @convert_kwargs_to_snake_case
+ @with_signature(func_sig, func_name=resolver_name)
+ async def func_impl(*args, **kwargs):
+ try:
+ if 'data' not in kwargs:
+ return {
+ "success": False,
+ "errors": ['missing data']
+ }
+
+ data = kwargs['data']
+ session = state.settings['app'].state.vyos_session
+
+ # one may override the session functions with a local subclass
+ try:
+ mod = import_module(f'api.graphql.recipes.{func_base_name}')
+ klass = getattr(mod, class_name)
+ except ImportError:
+ # otherwise, dynamically generate subclass to invoke subclass
+ # name based templates
+ klass = type(class_name, (Session,), {})
+ k = klass(session, data)
+ method = getattr(k, session_func)
+ result = method()
+ data['result'] = result
+
+ return {
+ "success": True,
+ "data": data
+ }
+ except Exception as error:
+ return {
+ "success": False,
+ "errors": [str(error)]
+ }
+
+ return func_impl
+
+def make_show_config_resolver(query_name):
+ class_name = query_name
+ return make_query_resolver(query_name, class_name, 'show_config')
+
+def make_show_resolver(query_name):
+ class_name = query_name
+ return make_query_resolver(query_name, class_name, 'show')
diff --git a/src/services/api/graphql/graphql/schema/firewall_group.graphql b/src/services/api/graphql/graphql/schema/firewall_group.graphql
index efe7de632..d89904b9e 100644
--- a/src/services/api/graphql/graphql/schema/firewall_group.graphql
+++ b/src/services/api/graphql/graphql/schema/firewall_group.graphql
@@ -45,3 +45,51 @@ type RemoveFirewallAddressGroupMembersResult {
success: Boolean!
errors: [String]
}
+
+input CreateFirewallAddressIpv6GroupInput {
+ name: String!
+ address: [String]
+}
+
+type CreateFirewallAddressIpv6Group {
+ name: String!
+ address: [String]
+}
+
+type CreateFirewallAddressIpv6GroupResult {
+ data: CreateFirewallAddressIpv6Group
+ success: Boolean!
+ errors: [String]
+}
+
+input UpdateFirewallAddressIpv6GroupMembersInput {
+ name: String!
+ address: [String!]!
+}
+
+type UpdateFirewallAddressIpv6GroupMembers {
+ name: String!
+ address: [String!]!
+}
+
+type UpdateFirewallAddressIpv6GroupMembersResult {
+ data: UpdateFirewallAddressIpv6GroupMembers
+ success: Boolean!
+ errors: [String]
+}
+
+input RemoveFirewallAddressIpv6GroupMembersInput {
+ name: String!
+ address: [String!]!
+}
+
+type RemoveFirewallAddressIpv6GroupMembers {
+ name: String!
+ address: [String!]!
+}
+
+type RemoveFirewallAddressIpv6GroupMembersResult {
+ data: RemoveFirewallAddressIpv6GroupMembers
+ success: Boolean!
+ errors: [String]
+}
diff --git a/src/services/api/graphql/graphql/schema/schema.graphql b/src/services/api/graphql/graphql/schema/schema.graphql
index c6899bee6..952e46f34 100644
--- a/src/services/api/graphql/graphql/schema/schema.graphql
+++ b/src/services/api/graphql/graphql/schema/schema.graphql
@@ -3,26 +3,28 @@ schema {
mutation: Mutation
}
-type Query {
- _dummy: String
-}
-
directive @configure on FIELD_DEFINITION
directive @configfile on FIELD_DEFINITION
directive @show on FIELD_DEFINITION
directive @showconfig on FIELD_DEFINITION
directive @image on FIELD_DEFINITION
+type Query {
+ Show(data: ShowInput) : ShowResult @show
+ ShowConfig(data: ShowConfigInput) : ShowConfigResult @showconfig
+}
+
type Mutation {
CreateDhcpServer(data: DhcpServerConfigInput) : CreateDhcpServerResult @configure
CreateInterfaceEthernet(data: InterfaceEthernetConfigInput) : CreateInterfaceEthernetResult @configure
CreateFirewallAddressGroup(data: CreateFirewallAddressGroupInput) : CreateFirewallAddressGroupResult @configure
UpdateFirewallAddressGroupMembers(data: UpdateFirewallAddressGroupMembersInput) : UpdateFirewallAddressGroupMembersResult @configure
RemoveFirewallAddressGroupMembers(data: RemoveFirewallAddressGroupMembersInput) : RemoveFirewallAddressGroupMembersResult @configure
+ CreateFirewallAddressIpv6Group(data: CreateFirewallAddressIpv6GroupInput) : CreateFirewallAddressIpv6GroupResult @configure
+ UpdateFirewallAddressIpv6GroupMembers(data: UpdateFirewallAddressIpv6GroupMembersInput) : UpdateFirewallAddressIpv6GroupMembersResult @configure
+ RemoveFirewallAddressIpv6GroupMembers(data: RemoveFirewallAddressIpv6GroupMembersInput) : RemoveFirewallAddressIpv6GroupMembersResult @configure
SaveConfigFile(data: SaveConfigFileInput) : SaveConfigFileResult @configfile
LoadConfigFile(data: LoadConfigFileInput) : LoadConfigFileResult @configfile
- Show(data: ShowInput) : ShowResult @show
- ShowConfig(data: ShowConfigInput) : ShowConfigResult @showconfig
AddSystemImage(data: AddSystemImageInput) : AddSystemImageResult @image
DeleteSystemImage(data: DeleteSystemImageInput) : DeleteSystemImageResult @image
}
diff --git a/src/services/api/graphql/recipes/remove_firewall_address_group_members.py b/src/services/api/graphql/recipes/remove_firewall_address_group_members.py
index cde30c27a..b91932e14 100644
--- a/src/services/api/graphql/recipes/remove_firewall_address_group_members.py
+++ b/src/services/api/graphql/recipes/remove_firewall_address_group_members.py
@@ -1,3 +1,17 @@
+# Copyright 2021 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/>.
from . session import Session
diff --git a/src/services/api/graphql/recipes/session.py b/src/services/api/graphql/recipes/session.py
index 5ece78ee6..1f844ff70 100644
--- a/src/services/api/graphql/recipes/session.py
+++ b/src/services/api/graphql/recipes/session.py
@@ -1,3 +1,18 @@
+# Copyright 2021 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 json
from ariadne import convert_camel_case_to_snake
diff --git a/src/services/api/graphql/recipes/templates/create_firewall_address_ipv_6_group.tmpl b/src/services/api/graphql/recipes/templates/create_firewall_address_ipv_6_group.tmpl
new file mode 100644
index 000000000..e9b660722
--- /dev/null
+++ b/src/services/api/graphql/recipes/templates/create_firewall_address_ipv_6_group.tmpl
@@ -0,0 +1,4 @@
+set firewall group ipv6-address-group {{ name }}
+{% for add in address %}
+set firewall group ipv6-address-group {{ name }} address {{ add }}
+{% endfor %}
diff --git a/src/services/api/graphql/recipes/templates/remove_firewall_address_ipv_6_group_members.tmpl b/src/services/api/graphql/recipes/templates/remove_firewall_address_ipv_6_group_members.tmpl
new file mode 100644
index 000000000..0efa0b226
--- /dev/null
+++ b/src/services/api/graphql/recipes/templates/remove_firewall_address_ipv_6_group_members.tmpl
@@ -0,0 +1,3 @@
+{% for add in address %}
+delete firewall group ipv6-address-group {{ name }} address {{ add }}
+{% endfor %}
diff --git a/src/services/api/graphql/recipes/templates/update_firewall_address_ipv_6_group_members.tmpl b/src/services/api/graphql/recipes/templates/update_firewall_address_ipv_6_group_members.tmpl
new file mode 100644
index 000000000..f98a5517c
--- /dev/null
+++ b/src/services/api/graphql/recipes/templates/update_firewall_address_ipv_6_group_members.tmpl
@@ -0,0 +1,3 @@
+{% for add in address %}
+set firewall group ipv6-address-group {{ name }} address {{ add }}
+{% endfor %}
diff --git a/src/services/vyos-configd b/src/services/vyos-configd
index 670b6e66a..48c9135e2 100755
--- a/src/services/vyos-configd
+++ b/src/services/vyos-configd
@@ -28,6 +28,7 @@ import zmq
from contextlib import contextmanager
from vyos.defaults import directories
+from vyos.util import boot_configuration_complete
from vyos.configsource import ConfigSourceString, ConfigSourceError
from vyos.config import Config
from vyos import ConfigError
@@ -186,7 +187,7 @@ def initialization(socket):
session_out = None
# if not a 'live' session, for example on boot, write to file
- if not session_out or not os.path.isfile('/tmp/vyos-config-status'):
+ if not session_out or not boot_configuration_complete():
session_out = script_stdout_log
session_mode = 'a'
diff --git a/src/services/vyos-hostsd b/src/services/vyos-hostsd
index f4b1d0fc2..df9f18d2d 100755
--- a/src/services/vyos-hostsd
+++ b/src/services/vyos-hostsd
@@ -139,6 +139,27 @@
# }
#
#
+### authoritative_zones
+## Additional zones hosted authoritatively by pdns-recursor.
+## We add NTAs for these zones but do not do much else here.
+#
+# { 'type': 'authoritative_zones',
+# 'op': 'add',
+# 'data': ['<str zone>', ...]
+# }
+#
+# { 'type': 'authoritative_zones',
+# 'op': 'delete',
+# 'data': ['<str zone>', ...]
+# }
+#
+# { 'type': 'authoritative_zones',
+# 'op': 'get',
+# }
+# response:
+# { 'data': ['<str zone>', ...] }
+#
+#
### search_domains
#
# { 'type': 'search_domains',
@@ -255,6 +276,7 @@ STATE = {
"name_server_tags_recursor": [],
"name_server_tags_system": [],
"forward_zones": {},
+ "authoritative_zones": [],
"hosts": {},
"host_name": "vyos",
"domain_name": "",
@@ -267,7 +289,8 @@ base_schema = Schema({
Required('op'): Any('add', 'delete', 'set', 'get', 'apply'),
'type': Any('name_servers',
'name_server_tags_recursor', 'name_server_tags_system',
- 'forward_zones', 'search_domains', 'hosts', 'host_name'),
+ 'forward_zones', 'authoritative_zones', 'search_domains',
+ 'hosts', 'host_name'),
'data': Any(list, dict),
'tag': str,
'tag_regex': str
@@ -347,6 +370,11 @@ msg_schema_map = {
'delete': data_list_schema,
'get': op_type_schema
},
+ 'authoritative_zones': {
+ 'add': data_list_schema,
+ 'delete': data_list_schema,
+ 'get': op_type_schema
+ },
'search_domains': {
'add': data_dict_list_schema,
'delete': data_list_schema,
@@ -522,7 +550,7 @@ def handle_message(msg):
data = get_option(msg, 'data')
if _type in ['name_servers', 'forward_zones', 'search_domains', 'hosts']:
delete_items_from_dict(STATE[_type], data)
- elif _type in ['name_server_tags_recursor', 'name_server_tags_system']:
+ elif _type in ['name_server_tags_recursor', 'name_server_tags_system', 'authoritative_zones']:
delete_items_from_list(STATE[_type], data)
else:
raise ValueError(f'Operation "{op}" unknown data type "{_type}"')
@@ -534,7 +562,7 @@ def handle_message(msg):
elif _type in ['forward_zones', 'hosts']:
add_items_to_dict(STATE[_type], data)
# maybe we need to rec_control clear-nta each domain that was removed here?
- elif _type in ['name_server_tags_recursor', 'name_server_tags_system']:
+ elif _type in ['name_server_tags_recursor', 'name_server_tags_system', 'authoritative_zones']:
add_items_to_list(STATE[_type], data)
else:
raise ValueError(f'Operation "{op}" unknown data type "{_type}"')
@@ -550,7 +578,7 @@ def handle_message(msg):
if _type in ['name_servers', 'search_domains', 'hosts']:
tag_regex = get_option(msg, 'tag_regex')
result = get_items_from_dict_regex(STATE[_type], tag_regex)
- elif _type in ['name_server_tags_recursor', 'name_server_tags_system', 'forward_zones']:
+ elif _type in ['name_server_tags_recursor', 'name_server_tags_system', 'forward_zones', 'authoritative_zones']:
result = STATE[_type]
else:
raise ValueError(f'Operation "{op}" unknown data type "{_type}"')
diff --git a/src/services/vyos-http-api-server b/src/services/vyos-http-api-server
index aa7ac6708..06871f1d6 100755
--- a/src/services/vyos-http-api-server
+++ b/src/services/vyos-http-api-server
@@ -32,6 +32,7 @@ from fastapi.responses import HTMLResponse
from fastapi.exceptions import RequestValidationError
from fastapi.routing import APIRoute
from pydantic import BaseModel, StrictStr, validator
+from starlette.middleware.cors import CORSMiddleware
from starlette.datastructures import FormData
from starlette.formparsers import FormParser, MultiPartParser
from multipart.multipart import parse_options_header
@@ -610,13 +611,19 @@ def show_op(data: ShowModel):
# GraphQL integration
###
-from api.graphql.bindings import generate_schema
+def graphql_init(fast_api_app):
+ from api.graphql.bindings import generate_schema
-api.graphql.state.init()
+ api.graphql.state.init()
+ api.graphql.state.settings['app'] = app
-schema = generate_schema()
+ schema = generate_schema()
-app.add_route('/graphql', GraphQL(schema, debug=True))
+ if app.state.vyos_origins:
+ origins = app.state.vyos_origins
+ app.add_route('/graphql', CORSMiddleware(GraphQL(schema, debug=True), allow_origins=origins, allow_methods=("GET", "POST", "OPTIONS")))
+ else:
+ app.add_route('/graphql', GraphQL(schema, debug=True))
###
@@ -640,15 +647,20 @@ if __name__ == '__main__':
app.state.vyos_session = config_session
app.state.vyos_keys = server_config['api_keys']
- app.state.vyos_debug = bool(server_config['debug'] == 'true')
- app.state.vyos_strict = bool(server_config['strict'] == 'true')
+ app.state.vyos_debug = server_config['debug']
+ app.state.vyos_strict = server_config['strict']
+ app.state.vyos_origins = server_config.get('cors', {}).get('origins', [])
- api.graphql.state.settings['app'] = app
+ graphql_init(app)
try:
- uvicorn.run(app, host=server_config["listen_address"],
- port=int(server_config["port"]),
- proxy_headers=True)
+ if not server_config['socket']:
+ uvicorn.run(app, host=server_config["listen_address"],
+ port=int(server_config["port"]),
+ proxy_headers=True)
+ else:
+ uvicorn.run(app, uds="/run/api.sock",
+ proxy_headers=True)
except OSError as err:
logger.critical(f"OSError {err}")
sys.exit(1)
diff --git a/src/systemd/vyos-hostsd.service b/src/systemd/vyos-hostsd.service
index b77335778..4da55f518 100644
--- a/src/systemd/vyos-hostsd.service
+++ b/src/systemd/vyos-hostsd.service
@@ -7,7 +7,7 @@ DefaultDependencies=no
# Seemingly sensible way to say "as early as the system is ready"
# All vyos-hostsd needs is read/write mounted root
-After=systemd-remount-fs.service
+After=systemd-remount-fs.service cloud-init.service
[Service]
WorkingDirectory=/run/vyos-hostsd
diff --git a/src/validators/ipv4-multicast b/src/validators/ipv4-multicast
index e5cbc9532..5465c728d 100755
--- a/src/validators/ipv4-multicast
+++ b/src/validators/ipv4-multicast
@@ -1,3 +1,3 @@
#!/bin/sh
-ipaddrcheck --is-ipv4-multicast $1
+ipaddrcheck --is-ipv4-multicast $1 && ipaddrcheck --is-ipv4-single $1
diff --git a/src/validators/ipv6-multicast b/src/validators/ipv6-multicast
index 66cd90c9c..5afc437e5 100755
--- a/src/validators/ipv6-multicast
+++ b/src/validators/ipv6-multicast
@@ -1,3 +1,3 @@
#!/bin/sh
-ipaddrcheck --is-ipv6-multicast $1
+ipaddrcheck --is-ipv6-multicast $1 && ipaddrcheck --is-ipv6-single $1
diff --git a/src/validators/range b/src/validators/range
new file mode 100755
index 000000000..d4c25f3c4
--- /dev/null
+++ b/src/validators/range
@@ -0,0 +1,56 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2021 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 re
+import sys
+import argparse
+
+class MalformedRange(Exception):
+ pass
+
+def validate_range(value, min=None, max=None):
+ try:
+ lower, upper = re.match(r'^(\d+)-(\d+)$', value).groups()
+
+ lower, upper = int(lower), int(upper)
+
+ if int(lower) > int(upper):
+ raise MalformedRange("the lower bound exceeds the upper bound".format(value))
+
+ if min is not None:
+ if lower < min:
+ raise MalformedRange("the lower bound must not be less than {}".format(min))
+
+ if max is not None:
+ if upper > max:
+ raise MalformedRange("the upper bound must not be greater than {}".format(max))
+
+ except (AttributeError, ValueError):
+ raise MalformedRange("range syntax error")
+
+parser = argparse.ArgumentParser(description='Range validator.')
+parser.add_argument('--min', type=int, action='store')
+parser.add_argument('--max', type=int, action='store')
+parser.add_argument('value', action='store')
+
+if __name__ == '__main__':
+ args = parser.parse_args()
+
+ try:
+ validate_range(args.value, min=args.min, max=args.max)
+ except MalformedRange as e:
+ print("Incorrect range '{}': {}".format(args.value, e))
+ sys.exit(1)
diff --git a/src/validators/script b/src/validators/script
index 1d8a27e5c..4ffdeb2a0 100755
--- a/src/validators/script
+++ b/src/validators/script
@@ -36,7 +36,7 @@ if __name__ == '__main__':
# File outside the config dir is just a warning
if not vyos.util.file_is_persistent(script):
- sys.exit(
- f'Warning: file {path} is outside the / config directory\n'
+ sys.exit(0)(
+ f'Warning: file {script} is outside the "/config" directory\n'
'It will not be automatically migrated to a new image on system update'
)