diff options
| author | Daniil Baturin <daniil@vyos.io> | 2026-10-14 18:16:45 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-10-14 18:16:45 +0100 |
| commit | e96e646f030f4ee6835b1ebbdca0640f0440f0d0 (patch) | |
| tree | cc2e505b83991ec6e2652e46d60adc200650d2b4 | |
| parent | b75c60f0602723122f0f45a7da8d6b45c4765edf (diff) | |
| parent | 0b43a668ac1fdbc5149616f5065279bbce9a02c8 (diff) | |
| download | vyos-1x-e96e646f030f4ee6835b1ebbdca0640f0440f0d0.tar.gz vyos-1x-e96e646f030f4ee6835b1ebbdca0640f0440f0d0.zip | |
Merge pull request #5478 from jestabro/rdep-interface
T7886: add a general method for verifying interface reverse dependencies
54 files changed, 477 insertions, 38 deletions
diff --git a/interface-definitions/firewall.xml.in b/interface-definitions/firewall.xml.in index 53be2edfa..0ebe6cf69 100644 --- a/interface-definitions/firewall.xml.in +++ b/interface-definitions/firewall.xml.in @@ -16,7 +16,7 @@ </properties> <children> #include <include/generic-description.xml.i> - #include <include/generic-interface-multi.xml.i> + #include <include/dependent-error-generic-interface-multi.xml.i> <leafNode name="offload"> <properties> <help>Offloading method</help> diff --git a/interface-definitions/include/constraint/dependent-error-interface-name.xml.i b/interface-definitions/include/constraint/dependent-error-interface-name.xml.i new file mode 100644 index 000000000..ec4d524f4 --- /dev/null +++ b/interface-definitions/include/constraint/dependent-error-interface-name.xml.i @@ -0,0 +1,7 @@ +<!-- include start from constraint/dependent-error-interface-name.xml.i --> +<constraint> + <regex>(bond|br|dum|en|ersp|eth|gnv|ifb|ipoe|lan|l2tp|l2tpeth|macsec|peth|ppp|pppoe|pptp|sstp|sstpc|tun|veth|vpptap|vpptun|vti|vtun|vxlan|wg|wlan|wwan)[0-9]+(.\d+)?|pod-[-_a-zA-Z0-9]{1,11}|lo</regex> + <validator name="interface-exists"/> +</constraint> +<dependency kind="interface" alert="error"/> +<!-- include end --> diff --git a/interface-definitions/include/constraint/dependent-warning-interface-name.xml.i b/interface-definitions/include/constraint/dependent-warning-interface-name.xml.i new file mode 100644 index 000000000..bc646c3c9 --- /dev/null +++ b/interface-definitions/include/constraint/dependent-warning-interface-name.xml.i @@ -0,0 +1,7 @@ +<!-- include start from constraint/dependent-warning-interface-name.xml.i --> +<constraint> + <regex>(bond|br|dum|en|ersp|eth|gnv|ifb|ipoe|lan|l2tp|l2tpeth|macsec|peth|ppp|pppoe|pptp|sstp|sstpc|tun|veth|vpptap|vpptun|vti|vtun|vxlan|wg|wlan|wwan)[0-9]+(.\d+)?|pod-[-_a-zA-Z0-9]{1,11}|lo</regex> + <validator name="interface-exists"/> +</constraint> +<dependency kind="interface" alert="warning"/> +<!-- include end --> diff --git a/interface-definitions/include/dependent-error-generic-interface-multi.xml.i b/interface-definitions/include/dependent-error-generic-interface-multi.xml.i new file mode 100644 index 000000000..c5dd26267 --- /dev/null +++ b/interface-definitions/include/dependent-error-generic-interface-multi.xml.i @@ -0,0 +1,19 @@ +<!-- include start from dependent-error-generic-interface-multi.xml.i --> +<leafNode name="interface"> + <properties> + <help>Interface</help> + <completionHelp> + <script>${vyos_completion_dir}/list_interfaces</script> + </completionHelp> + <valueHelp> + <format>txt</format> + <description>Interface name</description> + </valueHelp> + <constraint> + #include <include/constraint/interface-name.xml.i> + </constraint> + <multi/> + <dependency kind="interface" alert="error"/> + </properties> +</leafNode> +<!-- include end --> diff --git a/interface-definitions/include/dependent-error-generic-interface.xml.i b/interface-definitions/include/dependent-error-generic-interface.xml.i new file mode 100644 index 000000000..c48e728c8 --- /dev/null +++ b/interface-definitions/include/dependent-error-generic-interface.xml.i @@ -0,0 +1,18 @@ +<!-- include start from dependent-error-generic-interface.xml.i --> +<leafNode name="interface"> + <properties> + <help>Interface</help> + <completionHelp> + <script>${vyos_completion_dir}/list_interfaces</script> + </completionHelp> + <valueHelp> + <format>txt</format> + <description>Interface name</description> + </valueHelp> + <constraint> + #include <include/constraint/interface-name.xml.i> + </constraint> + <dependency kind="interface" alert="error"/> + </properties> +</leafNode> +<!-- include end --> diff --git a/interface-definitions/include/dependent-error-source-interface.xml.i b/interface-definitions/include/dependent-error-source-interface.xml.i new file mode 100644 index 000000000..b3c23f64e --- /dev/null +++ b/interface-definitions/include/dependent-error-source-interface.xml.i @@ -0,0 +1,18 @@ +<!-- include start from dependent-error-source-interface.xml.i --> +<leafNode name="source-interface"> + <properties> + <help>Interface used to establish connection</help> + <valueHelp> + <format>interface</format> + <description>Interface name</description> + </valueHelp> + <completionHelp> + <script>${vyos_completion_dir}/list_interfaces</script> + </completionHelp> + <constraint> + #include <include/constraint/interface-name.xml.i> + </constraint> + <dependency kind="interface" alert="error"/> + </properties> +</leafNode> +<!-- include end --> diff --git a/interface-definitions/include/dependent-warning-generic-interface-multi-broadcast.xml.i b/interface-definitions/include/dependent-warning-generic-interface-multi-broadcast.xml.i new file mode 100644 index 000000000..22f6b6552 --- /dev/null +++ b/interface-definitions/include/dependent-warning-generic-interface-multi-broadcast.xml.i @@ -0,0 +1,19 @@ +<!-- include start from dependent-warning-generic-interface-multi-broadcast.xml.i --> +<leafNode name="interface"> + <properties> + <help>Interface</help> + <completionHelp> + <script>${vyos_completion_dir}/list_interfaces --broadcast</script> + </completionHelp> + <valueHelp> + <format>txt</format> + <description>Interface name</description> + </valueHelp> + <constraint> + #include <include/constraint/interface-name.xml.i> + </constraint> + <multi/> + <dependency kind="interface" alert="warning"/> + </properties> +</leafNode> +<!-- include end --> diff --git a/interface-definitions/include/dependent-warning-generic-interface-multi.xml.i b/interface-definitions/include/dependent-warning-generic-interface-multi.xml.i new file mode 100644 index 000000000..bd4b9ea93 --- /dev/null +++ b/interface-definitions/include/dependent-warning-generic-interface-multi.xml.i @@ -0,0 +1,19 @@ +<!-- include start from dependent-warning-generic-interface-multi.xml.i --> +<leafNode name="interface"> + <properties> + <help>Interface</help> + <completionHelp> + <script>${vyos_completion_dir}/list_interfaces</script> + </completionHelp> + <valueHelp> + <format>txt</format> + <description>Interface name</description> + </valueHelp> + <constraint> + #include <include/constraint/interface-name.xml.i> + </constraint> + <multi/> + <dependency kind="interface" alert="warning"/> + </properties> +</leafNode> +<!-- include end --> diff --git a/interface-definitions/include/dependent-warning-source-interface.xml.i b/interface-definitions/include/dependent-warning-source-interface.xml.i new file mode 100644 index 000000000..164135f2f --- /dev/null +++ b/interface-definitions/include/dependent-warning-source-interface.xml.i @@ -0,0 +1,18 @@ +<!-- include start from dependent-warning-source-interface.xml.i --> +<leafNode name="source-interface"> + <properties> + <help>Interface used to establish connection</help> + <valueHelp> + <format>interface</format> + <description>Interface name</description> + </valueHelp> + <completionHelp> + <script>${vyos_completion_dir}/list_interfaces</script> + </completionHelp> + <constraint> + #include <include/constraint/interface-name.xml.i> + </constraint> + <dependency kind="interface" alert="warning"/> + </properties> +</leafNode> +<!-- include end --> diff --git a/interface-definitions/include/interface/dependent-error-inbound-interface.xml.i b/interface-definitions/include/interface/dependent-error-inbound-interface.xml.i new file mode 100644 index 000000000..e1c79feb0 --- /dev/null +++ b/interface-definitions/include/interface/dependent-error-inbound-interface.xml.i @@ -0,0 +1,11 @@ +<!-- include start from interface/dependent-error-inbound-interface.xml.i --> +<leafNode name="inbound-interface"> + <properties> + <help>Inbound Interface</help> + <completionHelp> + <script>${vyos_completion_dir}/list_interfaces</script> + </completionHelp> + <dependency kind="interface" alert="error"/> + </properties> +</leafNode> +<!-- include end --> diff --git a/interface-definitions/include/interface/vif-s.xml.i b/interface-definitions/include/interface/vif-s.xml.i index 65ca10207..e07a9bf8a 100644 --- a/interface-definitions/include/interface/vif-s.xml.i +++ b/interface-definitions/include/interface/vif-s.xml.i @@ -10,6 +10,7 @@ <validator name="numeric" argument="--range 0-4094"/> </constraint> <constraintErrorMessage>VLAN ID must be between 0 and 4094</constraintErrorMessage> + <kind>interface</kind> </properties> <children> #include <include/generic-description.xml.i> @@ -32,6 +33,7 @@ <validator name="numeric" argument="--range 0-4094"/> </constraint> <constraintErrorMessage>VLAN ID must be between 0 and 4094</constraintErrorMessage> + <kind>interface</kind> </properties> <children> #include <include/generic-description.xml.i> diff --git a/interface-definitions/include/interface/vif.xml.i b/interface-definitions/include/interface/vif.xml.i index 87f91c5ce..e9b9385f1 100644 --- a/interface-definitions/include/interface/vif.xml.i +++ b/interface-definitions/include/interface/vif.xml.i @@ -10,6 +10,7 @@ <validator name="numeric" argument="--range 0-4094"/> </constraint> <constraintErrorMessage>VLAN ID must be between 0 and 4094</constraintErrorMessage> + <kind>interface</kind> </properties> <children> #include <include/generic-description.xml.i> diff --git a/interface-definitions/include/isis/protocol-common-config.xml.i b/interface-definitions/include/isis/protocol-common-config.xml.i index 6a7388c99..d0424ea4f 100644 --- a/interface-definitions/include/isis/protocol-common-config.xml.i +++ b/interface-definitions/include/isis/protocol-common-config.xml.i @@ -719,6 +719,7 @@ <completionHelp> <script>${vyos_completion_dir}/list_interfaces</script> </completionHelp> + <dependency kind="interface" alert="error"/> </properties> <children> #include <include/bfd/bfd.xml.i> diff --git a/interface-definitions/include/ospf/protocol-common-config.xml.i b/interface-definitions/include/ospf/protocol-common-config.xml.i index 93d718ed4..caef8abb4 100644 --- a/interface-definitions/include/ospf/protocol-common-config.xml.i +++ b/interface-definitions/include/ospf/protocol-common-config.xml.i @@ -406,9 +406,7 @@ <format>txt</format> <description>Interface name</description> </valueHelp> - <constraint> - #include <include/constraint/interface-name.xml.i> - </constraint> + #include <include/constraint/dependent-error-interface-name.xml.i> </properties> <children> <leafNode name="area"> diff --git a/interface-definitions/include/ospfv3/protocol-common-config.xml.i b/interface-definitions/include/ospfv3/protocol-common-config.xml.i index 72fb86d3d..e6d3b23b8 100644 --- a/interface-definitions/include/ospfv3/protocol-common-config.xml.i +++ b/interface-definitions/include/ospfv3/protocol-common-config.xml.i @@ -132,9 +132,7 @@ <format>txt</format> <description>Interface used for routing information exchange</description> </valueHelp> - <constraint> - #include <include/constraint/interface-name.xml.i> - </constraint> + #include <include/constraint/dependent-error-interface-name.xml.i> </properties> <children> <leafNode name="area"> diff --git a/interface-definitions/interfaces_bonding.xml.in b/interface-definitions/interfaces_bonding.xml.in index 9945fc15d..6eca96fb1 100644 --- a/interface-definitions/interfaces_bonding.xml.in +++ b/interface-definitions/interfaces_bonding.xml.in @@ -14,6 +14,7 @@ <format>bondN</format> <description>Bonding interface name</description> </valueHelp> + <kind>interface</kind> </properties> <children> #include <include/interface/address-ipv4-ipv6-dhcp.xml.i> diff --git a/interface-definitions/interfaces_bridge.xml.in b/interface-definitions/interfaces_bridge.xml.in index d4ee2a652..b2b08659a 100644 --- a/interface-definitions/interfaces_bridge.xml.in +++ b/interface-definitions/interfaces_bridge.xml.in @@ -14,6 +14,7 @@ <format>brN</format> <description>Bridge interface name</description> </valueHelp> + <kind>interface</kind> </properties> <children> #include <include/interface/address-ipv4-ipv6-dhcp.xml.i> diff --git a/interface-definitions/interfaces_dummy.xml.in b/interface-definitions/interfaces_dummy.xml.in index 993380ab8..9f4856845 100644 --- a/interface-definitions/interfaces_dummy.xml.in +++ b/interface-definitions/interfaces_dummy.xml.in @@ -14,6 +14,7 @@ <format>dumN</format> <description>Dummy interface name</description> </valueHelp> + <kind>interface</kind> </properties> <children> #include <include/interface/address-ipv4-ipv6.xml.i> diff --git a/interface-definitions/interfaces_ethernet.xml.in b/interface-definitions/interfaces_ethernet.xml.in index e110bd48f..466c0c8e4 100644 --- a/interface-definitions/interfaces_ethernet.xml.in +++ b/interface-definitions/interfaces_ethernet.xml.in @@ -17,6 +17,7 @@ <regex>((eth|lan)[0-9]+|(eno|ens|enp|enx).+)</regex> </constraint> <constraintErrorMessage>Invalid Ethernet interface name</constraintErrorMessage> + <kind>interface</kind> </properties> <children> #include <include/interface/address-ipv4-ipv6-dhcp.xml.i> diff --git a/interface-definitions/interfaces_geneve.xml.in b/interface-definitions/interfaces_geneve.xml.in index b85bd3b9e..385d15250 100644 --- a/interface-definitions/interfaces_geneve.xml.in +++ b/interface-definitions/interfaces_geneve.xml.in @@ -14,6 +14,7 @@ <format>gnvN</format> <description>GENEVE interface name</description> </valueHelp> + <kind>interface</kind> </properties> <children> #include <include/interface/address-ipv4-ipv6.xml.i> diff --git a/interface-definitions/interfaces_input.xml.in b/interface-definitions/interfaces_input.xml.in index 771c47e42..8ca45053a 100644 --- a/interface-definitions/interfaces_input.xml.in +++ b/interface-definitions/interfaces_input.xml.in @@ -15,6 +15,7 @@ <format>ifbN</format> <description>Input interface name</description> </valueHelp> + <kind>interface</kind> </properties> <children> #include <include/generic-description.xml.i> diff --git a/interface-definitions/interfaces_l2tpv3.xml.in b/interface-definitions/interfaces_l2tpv3.xml.in index 381e86bd0..3f6d77ad8 100644 --- a/interface-definitions/interfaces_l2tpv3.xml.in +++ b/interface-definitions/interfaces_l2tpv3.xml.in @@ -14,6 +14,7 @@ <format>l2tpethN</format> <description>L2TPv3 interface name</description> </valueHelp> + <kind>interface</kind> </properties> <children> #include <include/interface/address-ipv4-ipv6.xml.i> diff --git a/interface-definitions/interfaces_macsec.xml.in b/interface-definitions/interfaces_macsec.xml.in index 5279a9495..3ece4d777 100644 --- a/interface-definitions/interfaces_macsec.xml.in +++ b/interface-definitions/interfaces_macsec.xml.in @@ -14,6 +14,7 @@ <format>macsecN</format> <description>MACsec interface name</description> </valueHelp> + <kind>interface</kind> </properties> <children> #include <include/interface/address-ipv4-ipv6-dhcp.xml.i> diff --git a/interface-definitions/interfaces_openvpn.xml.in b/interface-definitions/interfaces_openvpn.xml.in index ff16a5610..f0bf1db9c 100644 --- a/interface-definitions/interfaces_openvpn.xml.in +++ b/interface-definitions/interfaces_openvpn.xml.in @@ -14,6 +14,7 @@ <format>vtunN</format> <description>OpenVPN interface name</description> </valueHelp> + <kind>interface</kind> </properties> <children> #include <include/interface/authentication.xml.i> diff --git a/interface-definitions/interfaces_pppoe.xml.in b/interface-definitions/interfaces_pppoe.xml.in index d38074b41..37067aefc 100644 --- a/interface-definitions/interfaces_pppoe.xml.in +++ b/interface-definitions/interfaces_pppoe.xml.in @@ -14,6 +14,7 @@ <format>pppoeN</format> <description>PPPoE dialer interface name</description> </valueHelp> + <kind>interface</kind> </properties> <children> <leafNode name="address"> diff --git a/interface-definitions/interfaces_pseudo-ethernet.xml.in b/interface-definitions/interfaces_pseudo-ethernet.xml.in index 5687e3556..72e0fcf80 100644 --- a/interface-definitions/interfaces_pseudo-ethernet.xml.in +++ b/interface-definitions/interfaces_pseudo-ethernet.xml.in @@ -14,6 +14,7 @@ <format>pethN</format> <description>Pseudo Ethernet interface name</description> </valueHelp> + <kind>interface</kind> </properties> <children> #include <include/interface/address-ipv4-ipv6-dhcp.xml.i> diff --git a/interface-definitions/interfaces_sstpc.xml.in b/interface-definitions/interfaces_sstpc.xml.in index b7c49446f..418901a66 100644 --- a/interface-definitions/interfaces_sstpc.xml.in +++ b/interface-definitions/interfaces_sstpc.xml.in @@ -14,6 +14,7 @@ <format>sstpcN</format> <description>Secure Socket Tunneling Protocol interface name</description> </valueHelp> + <kind>interface</kind> </properties> <children> #include <include/generic-description.xml.i> diff --git a/interface-definitions/interfaces_tunnel.xml.in b/interface-definitions/interfaces_tunnel.xml.in index 74849be5d..eb97e2b4c 100644 --- a/interface-definitions/interfaces_tunnel.xml.in +++ b/interface-definitions/interfaces_tunnel.xml.in @@ -14,6 +14,7 @@ <format>tunN</format> <description>Tunnel interface name</description> </valueHelp> + <kind>interface</kind> </properties> <children> #include <include/generic-description.xml.i> diff --git a/interface-definitions/interfaces_virtual-ethernet.xml.in b/interface-definitions/interfaces_virtual-ethernet.xml.in index 1559a6038..37631e312 100644 --- a/interface-definitions/interfaces_virtual-ethernet.xml.in +++ b/interface-definitions/interfaces_virtual-ethernet.xml.in @@ -14,6 +14,7 @@ <format>vethN</format> <description>Virtual Ethernet interface name</description> </valueHelp> + <kind>interface</kind> </properties> <children> #include <include/interface/address-ipv4-ipv6-dhcp.xml.i> diff --git a/interface-definitions/interfaces_vti.xml.in b/interface-definitions/interfaces_vti.xml.in index 39fb3131e..b472ac9b1 100644 --- a/interface-definitions/interfaces_vti.xml.in +++ b/interface-definitions/interfaces_vti.xml.in @@ -14,6 +14,7 @@ <format>vtiN</format> <description>VTI interface name</description> </valueHelp> + <kind>interface</kind> </properties> <children> #include <include/interface/address-ipv4-ipv6.xml.i> diff --git a/interface-definitions/interfaces_vxlan.xml.in b/interface-definitions/interfaces_vxlan.xml.in index 7f71dca34..55924edc0 100644 --- a/interface-definitions/interfaces_vxlan.xml.in +++ b/interface-definitions/interfaces_vxlan.xml.in @@ -14,6 +14,7 @@ <format>vxlanN</format> <description>VXLAN interface name</description> </valueHelp> + <kind>interface</kind> </properties> <children> #include <include/interface/address-ipv4-ipv6.xml.i> diff --git a/interface-definitions/interfaces_wireguard.xml.in b/interface-definitions/interfaces_wireguard.xml.in index 33cb5864a..d22230de4 100644 --- a/interface-definitions/interfaces_wireguard.xml.in +++ b/interface-definitions/interfaces_wireguard.xml.in @@ -14,6 +14,7 @@ <format>wgN</format> <description>WireGuard interface name</description> </valueHelp> + <kind>interface</kind> </properties> <children> #include <include/interface/address-ipv4-ipv6.xml.i> diff --git a/interface-definitions/interfaces_wireless.xml.in b/interface-definitions/interfaces_wireless.xml.in index 1b5356caa..ec1817585 100644 --- a/interface-definitions/interfaces_wireless.xml.in +++ b/interface-definitions/interfaces_wireless.xml.in @@ -17,6 +17,7 @@ <format>wlanN</format> <description>Wireless (WiFi/WLAN) interface name</description> </valueHelp> + <kind>interface</kind> </properties> <children> #include <include/interface/address-ipv4-ipv6-dhcp.xml.i> diff --git a/interface-definitions/interfaces_wwan.xml.in b/interface-definitions/interfaces_wwan.xml.in index 552806d4e..7763cc704 100644 --- a/interface-definitions/interfaces_wwan.xml.in +++ b/interface-definitions/interfaces_wwan.xml.in @@ -17,6 +17,7 @@ <format>wwanN</format> <description>Wireless Wide Area Network interface name</description> </valueHelp> + <kind>interface</kind> </properties> <children> #include <include/interface/address-ipv4-ipv6-dhcp.xml.i> diff --git a/interface-definitions/policy_local-route.xml.in b/interface-definitions/policy_local-route.xml.in index 9f6588db8..fed3a8edc 100644 --- a/interface-definitions/policy_local-route.xml.in +++ b/interface-definitions/policy_local-route.xml.in @@ -73,7 +73,7 @@ #include <include/port-number.xml.i> </children> </node> - #include <include/interface/inbound-interface.xml.i> + #include <include/interface/dependent-error-inbound-interface.xml.i> </children> </tagNode> </children> @@ -148,7 +148,7 @@ #include <include/port-number.xml.i> </children> </node> - #include <include/interface/inbound-interface.xml.i> + #include <include/interface/dependent-error-inbound-interface.xml.i> </children> </tagNode> </children> diff --git a/interface-definitions/protocols_igmp-proxy.xml.in b/interface-definitions/protocols_igmp-proxy.xml.in index a1deeb3fc..65a5cf3c7 100644 --- a/interface-definitions/protocols_igmp-proxy.xml.in +++ b/interface-definitions/protocols_igmp-proxy.xml.in @@ -22,6 +22,7 @@ <completionHelp> <script>${vyos_completion_dir}/list_interfaces</script> </completionHelp> + <dependency kind="interface" alert="error"/> </properties> <children> <leafNode name="alt-subnet"> diff --git a/interface-definitions/protocols_mpls.xml.in b/interface-definitions/protocols_mpls.xml.in index fc1864f38..b904a0740 100644 --- a/interface-definitions/protocols_mpls.xml.in +++ b/interface-definitions/protocols_mpls.xml.in @@ -574,7 +574,7 @@ </leafNode> </children> </node> - #include <include/generic-interface-multi.xml.i> + #include <include/dependent-error-generic-interface-multi.xml.i> </children> </node> </children> diff --git a/interface-definitions/protocols_pim.xml.in b/interface-definitions/protocols_pim.xml.in index 4a20c0d9b..3de986545 100644 --- a/interface-definitions/protocols_pim.xml.in +++ b/interface-definitions/protocols_pim.xml.in @@ -15,9 +15,7 @@ <completionHelp> <script>${vyos_completion_dir}/list_interfaces</script> </completionHelp> - <constraint> - #include <include/constraint/interface-name.xml.i> - </constraint> + #include <include/constraint/dependent-error-interface-name.xml.i> </properties> <children> #include <include/bfd/bfd.xml.i> diff --git a/interface-definitions/protocols_pim6.xml.in b/interface-definitions/protocols_pim6.xml.in index 8bd3f3fee..0d6d4d1b6 100644 --- a/interface-definitions/protocols_pim6.xml.in +++ b/interface-definitions/protocols_pim6.xml.in @@ -15,9 +15,7 @@ <completionHelp> <script>${vyos_completion_dir}/list_interfaces</script> </completionHelp> - <constraint> - #include <include/constraint/interface-name.xml.i> - </constraint> + #include <include/constraint/dependent-error-interface-name.xml.i> </properties> <children> #include <include/pim/bsm.xml.i> diff --git a/interface-definitions/qos.xml.in b/interface-definitions/qos.xml.in index aad1de629..5f3e76c2c 100644 --- a/interface-definitions/qos.xml.in +++ b/interface-definitions/qos.xml.in @@ -16,9 +16,7 @@ <format>txt</format> <description>Interface name</description> </valueHelp> - <constraint> - #include <include/constraint/interface-name.xml.i> - </constraint> + #include <include/constraint/dependent-warning-interface-name.xml.i> </properties> <children> <leafNode name="ingress"> diff --git a/interface-definitions/service_conntrack-sync.xml.in b/interface-definitions/service_conntrack-sync.xml.in index 9483c809b..56faa4a59 100644 --- a/interface-definitions/service_conntrack-sync.xml.in +++ b/interface-definitions/service_conntrack-sync.xml.in @@ -141,6 +141,7 @@ <completionHelp> <script>${vyos_completion_dir}/list_interfaces --bridgeable</script> </completionHelp> + <dependency kind="interface" alert="error"/> </properties> <children> <leafNode name="peer"> diff --git a/interface-definitions/service_dhcp-relay.xml.in b/interface-definitions/service_dhcp-relay.xml.in index 9fdd9581d..0fbb75598 100644 --- a/interface-definitions/service_dhcp-relay.xml.in +++ b/interface-definitions/service_dhcp-relay.xml.in @@ -10,7 +10,7 @@ </properties> <children> #include <include/generic-disable-node.xml.i> - #include <include/generic-interface-multi-broadcast.xml.i> + #include <include/dependent-warning-generic-interface-multi-broadcast.xml.i> <leafNode name="listen-interface"> <properties> <help>Interface for DHCP Relay Agent to listen for requests</help> @@ -21,9 +21,7 @@ <format>txt</format> <description>Interface name</description> </valueHelp> - <constraint> - #include <include/constraint/interface-name.xml.i> - </constraint> + #include <include/constraint/dependent-warning-interface-name.xml.i> <multi/> </properties> </leafNode> @@ -37,9 +35,7 @@ <format>txt</format> <description>Interface name</description> </valueHelp> - <constraint> - #include <include/constraint/interface-name.xml.i> - </constraint> + #include <include/constraint/dependent-warning-interface-name.xml.i> <multi/> </properties> </leafNode> diff --git a/interface-definitions/service_ipoe-server.xml.in b/interface-definitions/service_ipoe-server.xml.in index 27b163001..f3a7e1e3c 100644 --- a/interface-definitions/service_ipoe-server.xml.in +++ b/interface-definitions/service_ipoe-server.xml.in @@ -90,6 +90,7 @@ <completionHelp> <script>${vyos_completion_dir}/list_interfaces</script> </completionHelp> + <dependency kind="interface" alert="warning"/> </properties> <children> <leafNode name="mode"> diff --git a/interface-definitions/service_mdns_repeater.xml.in b/interface-definitions/service_mdns_repeater.xml.in index 9d626bf6a..0cdb45db3 100644 --- a/interface-definitions/service_mdns_repeater.xml.in +++ b/interface-definitions/service_mdns_repeater.xml.in @@ -14,7 +14,7 @@ </properties> <children> #include <include/generic-disable-node.xml.i> - #include <include/generic-interface-multi.xml.i> + #include <include/dependent-error-generic-interface-multi.xml.i> <leafNode name="ip-version"> <properties> <help>IP address version to use</help> diff --git a/interface-definitions/service_ndp-proxy.xml.in b/interface-definitions/service_ndp-proxy.xml.in index 327ce89d5..a35457991 100644 --- a/interface-definitions/service_ndp-proxy.xml.in +++ b/interface-definitions/service_ndp-proxy.xml.in @@ -28,9 +28,7 @@ <completionHelp> <script>${vyos_completion_dir}/list_interfaces</script> </completionHelp> - <constraint> - #include <include/constraint/interface-name.xml.i> - </constraint> + #include <include/constraint/dependent-error-interface-name.xml.i> </properties> <children> #include <include/generic-disable-node.xml.i> diff --git a/interface-definitions/service_ntp.xml.in b/interface-definitions/service_ntp.xml.in index 216722715..f85b5715b 100644 --- a/interface-definitions/service_ntp.xml.in +++ b/interface-definitions/service_ntp.xml.in @@ -10,10 +10,10 @@ </properties> <children> #include <include/allow-client.xml.i> - #include <include/generic-interface.xml.i> + #include <include/dependent-error-generic-interface.xml.i> #include <include/listen-address.xml.i> #include <include/source-address-ipv4-ipv6-multi.xml.i> - #include <include/source-interface.xml.i> + #include <include/dependent-error-source-interface.xml.i> #include <include/interface/vrf.xml.i> <node name="timestamp"> <properties> @@ -39,6 +39,7 @@ #include <include/constraint/interface-name.xml.i> <regex>all</regex> </constraint> + <dependency kind="interface" alert="error"/> </properties> <children> <leafNode name="receive-filter"> diff --git a/interface-definitions/service_pppoe-server.xml.in b/interface-definitions/service_pppoe-server.xml.in index 81a4a95e3..06f4225a6 100644 --- a/interface-definitions/service_pppoe-server.xml.in +++ b/interface-definitions/service_pppoe-server.xml.in @@ -61,6 +61,7 @@ <completionHelp> <script>${vyos_completion_dir}/list_interfaces</script> </completionHelp> + <dependency kind="interface" alert="warning"/> </properties> <children> <leafNode name="combined"> diff --git a/interface-definitions/system_flow-accounting.xml.in b/interface-definitions/system_flow-accounting.xml.in index 53436036e..c340fccd6 100644 --- a/interface-definitions/system_flow-accounting.xml.in +++ b/interface-definitions/system_flow-accounting.xml.in @@ -20,7 +20,7 @@ <help>NetFlow settings</help> </properties> <children> - #include <include/generic-interface-multi.xml.i> + #include <include/dependent-warning-generic-interface-multi.xml.i> <leafNode name="engine-id"> <properties> <help>NetFlow engine-id</help> @@ -112,7 +112,7 @@ <defaultValue>2055</defaultValue> </leafNode> #include <include/source-address-ipv4-ipv6.xml.i> - #include <include/source-interface.xml.i> + #include <include/dependent-warning-source-interface.xml.i> </children> </tagNode> <leafNode name="inactive-timeout"> diff --git a/interface-definitions/system_option.xml.in b/interface-definitions/system_option.xml.in index c295b2ebe..3bc58155c 100644 --- a/interface-definitions/system_option.xml.in +++ b/interface-definitions/system_option.xml.in @@ -381,7 +381,7 @@ <help>Global options used for HTTP client</help> </properties> <children> - #include <include/source-interface.xml.i> + #include <include/dependent-error-source-interface.xml.i> #include <include/source-address-ipv4-ipv6.xml.i> </children> </node> diff --git a/python/vyos/configmanager.py b/python/vyos/configmanager.py index 46bff08b0..69d40e5ef 100644 --- a/python/vyos/configmanager.py +++ b/python/vyos/configmanager.py @@ -13,7 +13,9 @@ # 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 + +import json # noqa: I001 +import logging from types import ModuleType from dataclasses import dataclass @@ -23,9 +25,26 @@ from pathlib import Path from vyos.config import Config from vyos.config import ConfigDict from vyos.configtree import ConfigTree +from vyos.configtree import DiffTree from vyos.referencetree import ReferenceTree +from vyos.dependentverify import removed_dependency_value_paths_of_kind +from vyos.dependentverify import dependent_value_paths_of_kind +from vyos.dependentverify import concatenate_vif + from vyos.utils.system import load_as_module from vyos.defaults import directories +from vyos.base import Warning as Warn +from vyos import ConfigError + +LOG = logging.getLogger(Path(__file__).stem) +handler = logging.StreamHandler() +formatter = logging.Formatter('(%(name)s): %(message)s') +handler.setFormatter(formatter) +LOG.addHandler(handler) + +debug = False + +_ = LOG.setLevel(logging.DEBUG) if debug else LOG.setLevel(logging.INFO) @dataclass @@ -38,6 +57,8 @@ class ConfigDictCache: class Component: module: ModuleType name: str + rdep_error: list = field(default_factory=list) + rdep_warning: list = field(default_factory=list) cache: ConfigDictCache = None @@ -54,6 +75,7 @@ class ConfigManager: self.config: Config = None self.running_config: ConfigTree = None self.session_config: ConfigTree = None + self.diff_tree: DiffTree = None self.components = self.init_components() @@ -92,14 +114,111 @@ class ConfigManager: if config is None: self.running_config = None self.session_config = None + self.diff_tree = None else: self.running_config = config.get_config_tree(effective=True) self.session_config = config.get_config_tree() - setattr(config, 'manager', self) + self.diff_tree = DiffTree(self.running_config, self.session_config) if self.cache_config: self.clear_cache() + self.clear_rdep() + + if config is None: + return + + self.init_rdep() + + def init_rdep(self): + # pylint: disable=too-many-locals,too-many-branches + """ + Schema definition for reverse dependency alert: + alertAttr = attribute alert + { + "error" | "warning" + } + + We are currently only considering reverse dependencies for type + (kind) 'interface'. Extension for other kinds may be added here. + + The reference tree utils return data of the form outlined below. In + all cases, the input path is a reference tree path, and consequently + may define a subtree of the config (due to intermediary tag node); + thus the return values are lists of (tuples including) actual config + paths. + + removed_dependency_value_paths: list[tuple[list(values), path]] + are the values for each path defined by the XML paths of kind + 'interface', which are to be removed in the current commit session. + + dependent_value_paths: dict[alert: list[tuple[list(values), path]]] + are the (values, path) defined by all XML dependency annotations + existing in the session tree, indexed by alert type. + + The dependent_value_paths are cross-referenced with the interface + values scheduled for deletion in removed_dependency_value_paths, and + the corresponding alert raised in the configmanager verify stage. + """ + removed_dependency_value_paths = removed_dependency_value_paths_of_kind( + 'interface', self.reference_tree, self.session_config, self.diff_tree + ) + + removed_dependency_value_paths, orig_value = concatenate_vif( + self.reference_tree, removed_dependency_value_paths + ) + + LOG.debug(f'removed_dependency_value_paths: {removed_dependency_value_paths}') + + if not removed_dependency_value_paths: + return + + dependent_value_paths = dependent_value_paths_of_kind( + 'interface', self.reference_tree, self.session_config + ) + + LOG.debug(f'dependent_value_paths: {dependent_value_paths}') + + if not dependent_value_paths: + return + + for vals, path in removed_dependency_value_paths: + rpath = self.reference_tree.reference_path_from_config_path(path) + owner = self.reference_tree.get_owner(rpath) + if owner is None: + LOG.error(f'Path {path} has no owner') + continue + component_name = Path(owner).stem + component = self.components.get(component_name) + LOG.debug(f'owner: {component_name}') + + errors = dependent_value_paths.get('error', []) + rdep_err = component.rdep_error + for dependent_vals, dependent_path in errors: + for v in set(vals) & set(dependent_vals): + ov = orig_value.get(v, v) + path_str = ' '.join(path + [ov]) + dpath_str = ' '.join(dependent_path) + rdep_err.append( + f'\nNode:\n "{path_str}"\nis required by:\n "{dpath_str}"\n' + ) + + warnings = dependent_value_paths.get('warning', []) + rdep_warn = component.rdep_warning + for dependent_vals, dependent_path in warnings: + for v in set(vals) & set(dependent_vals): + ov = orig_value.get(v, v) + path_str = ' '.join(path + [ov]) + dpath_str = ' '.join(dependent_path) + rdep_warn.append( + f'removed node:\n "{path_str}"\nis referenced by:\n "{dpath_str}"\n' + ) + + def clear_rdep(self): + for component in self.components.values(): + component.rdep_error = [] + component.rdep_warning = [] + def clear_cache(self): for component in self.components.values(): component.cache = None @@ -121,6 +240,11 @@ class ConfigManager: component = self.components[script_name] mod = component.module + if errors := component.rdep_error: + raise ConfigError('\n'.join(errors)) + if warnings := component.rdep_warning: + Warn('\n'.join(warnings)) + mod.verify(config_dict) def generate(self, script_name: str, config_dict: ConfigDict) -> None: diff --git a/python/vyos/dependentverify.py b/python/vyos/dependentverify.py new file mode 100644 index 000000000..1e22b65a0 --- /dev/null +++ b/python/vyos/dependentverify.py @@ -0,0 +1,115 @@ +# Copyright (C) VyOS Inc. +# +# 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 vyos.configtree import ConfigTree # noqa: I001 +from vyos.configtree import DiffTree +from vyos.configtree import subtree_values_of_path +from vyos.referencetree import ReferenceTree +from vyos.base import Warning as Warn + + +def removed_dependency_value_paths_of_kind( + kind: str, + reference_tree: ReferenceTree, + session_tree: ConfigTree, + diff_tree: DiffTree, +): + paths = reference_tree.get_nodes_of_kind(kind) + value_paths = [] + for path in paths: + path_vals = subtree_values_of_path(diff_tree.sub, path, reference_tree) + path_vals = [t for t in path_vals if t != ([], [])] + for vlst, p in path_vals: + vals = list( + filter( + lambda v, cur_p=p: not session_tree.exists(cur_p + [v]), + vlst, + ) + ) + if vals: + value_paths.append((vals, p)) + + return value_paths + + +def dependent_value_paths_of_kind( + kind: str, + reference_tree: ReferenceTree, + session_tree: ConfigTree, +): + dependent_data = reference_tree.get_rdeps_of_kind_data(kind) + + dependent_value_paths = {} + for p, a in dependent_data: + path_vals = subtree_values_of_path(session_tree, p, reference_tree) + path_vals = [t for t in path_vals if t != ([], [])] + if path_vals: + lst = dependent_value_paths.setdefault(a, []) + lst.extend(path_vals) + + return dependent_value_paths + + +vif_refpath = [ + ['interfaces', 'ethernet', 'vif'], + ['interfaces', 'ethernet', 'vif-s'], + ['interfaces', 'ethernet', 'vif-s', 'vif-c'], + ['interfaces', 'bonding', 'vif'], + ['interfaces', 'bonding', 'vif-s'], + ['interfaces', 'bonding', 'vif-s', 'vif-c'], + ['interfaces', 'bridge', 'vif'], + ['interfaces', 'pseudo-ethernet', 'vif'], + ['interfaces', 'pseudo-ethernet', 'vif-s'], + ['interfaces', 'pseudo-ethernet', 'vif-s', 'vif-c'], + ['interfaces', 'virtual-ethernet', 'vif'], + ['interfaces', 'virtual-ethernet', 'vif-s'], + ['interfaces', 'virtual-ethernet', 'vif-s', 'vif-c'], + ['interfaces', 'wireless', 'vif'], + ['interfaces', 'wireless', 'vif-s'], + ['interfaces', 'wireless', 'vif-s', 'vif-c'], +] + + +def check_vif_path(reference_tree: ReferenceTree): + for p in vif_refpath: + if not reference_tree.exists(p): + Warn(f'Out of date reference path: {p}') + + +def concatenate_vif(reference_tree: ReferenceTree, value_paths): + check_vif_path(reference_tree) + revised = [] + orig_value = {} + for vp in value_paths: + refpath = reference_tree.reference_path_from_config_path(vp[1]) + if refpath not in vif_refpath: + revised.append(vp) + else: + p = vp[1] + match p[-1]: + case 'vif': + rev_vals = [p[2] + '.' + v for v in vp[0]] + orig_value |= dict(zip(rev_vals, vp[0])) + revised.append((rev_vals, vp[1])) + case 'vif-s': + rev_vals = [p[2] + '.' + v for v in vp[0]] + orig_value |= dict(zip(rev_vals, vp[0])) + revised.append((rev_vals, vp[1])) + case 'vif-c': + rev_vals = [p[2] + '.' + p[4] + '.' + v for v in vp[0]] + orig_value |= dict(zip(rev_vals, vp[0])) + revised.append((rev_vals, vp[1])) + return revised, orig_value diff --git a/python/vyos/referencetree.py b/python/vyos/referencetree.py index 0cd969762..24e8da1ec 100644 --- a/python/vyos/referencetree.py +++ b/python/vyos/referencetree.py @@ -71,7 +71,14 @@ class ReferenceTree: return bool(res) def get_owner(self, path): - return self.__lib.get_owner(self.__pointer, path.encode()).decode() + check_path(path) + path_str = ' '.join(map(str, path)).encode() + + owner = self.__lib.get_owner(self.__pointer, path_str) + if not owner: + return None + + return owner.decode() def get_multi_nodes(self, tag_value_placeholder='', as_tuple=False): import json diff --git a/schema/interface_definition.rnc b/schema/interface_definition.rnc index 0c929ad2c..faa0c4f03 100644 --- a/schema/interface_definition.rnc +++ b/schema/interface_definition.rnc @@ -99,6 +99,9 @@ properties = element properties (element constraintSilenceOutput { empty })? & completionHelp* & + (element kind { text })* & + dependency? & + # "docs" is used to store documentation for a node in a structured format # It is used to generate documentation for the CLI and Web docs (element docs { @@ -197,3 +200,18 @@ completionHelp = element completionHelp (element path { text })* & (element script { text })* } + +dependency = element dependency +{ + kindAttr & alertAttr +} + +kindAttr = attribute kind +{ + text +} + +alertAttr = attribute alert +{ + "error" | "warning" +} diff --git a/schema/interface_definition.rng b/schema/interface_definition.rng index 2c46266d3..0398d749b 100644 --- a/schema/interface_definition.rng +++ b/schema/interface_definition.rng @@ -182,6 +182,14 @@ <zeroOrMore> <ref name="completionHelp"/> </zeroOrMore> + <zeroOrMore> + <element name="kind"> + <text/> + </element> + </zeroOrMore> + <optional> + <ref name="dependency"/> + </optional> <optional> <!-- "docs" is used to store documentation for a node in a structured format @@ -366,4 +374,23 @@ </interleave> </element> </define> + <define name="dependency"> + <element name="dependency"> + <interleave> + <ref name="kindAttr"/> + <ref name="alertAttr"/> + </interleave> + </element> + </define> + <define name="kindAttr"> + <attribute name="kind"/> + </define> + <define name="alertAttr"> + <attribute name="alert"> + <choice> + <value>error</value> + <value>warning</value> + </choice> + </attribute> + </define> </grammar> |
