diff options
22 files changed, 281 insertions, 72 deletions
diff --git a/.github/workflows/pull-request-labels.yml b/.github/workflows/pull-request-labels.yml index 778daae30..3398af5b0 100644 --- a/.github/workflows/pull-request-labels.yml +++ b/.github/workflows/pull-request-labels.yml @@ -17,4 +17,4 @@ jobs: contents: read pull-requests: write steps: - - uses: actions/labeler@v5.0.0-alpha.1 + - uses: actions/labeler@v5.0.0 diff --git a/data/templates/firewall/nftables-vrf-zones.j2 b/data/templates/firewall/nftables-vrf-zones.j2 deleted file mode 100644 index 3bce7312d..000000000 --- a/data/templates/firewall/nftables-vrf-zones.j2 +++ /dev/null @@ -1,17 +0,0 @@ -table inet vrf_zones { - # Map of interfaces and connections tracking zones - map ct_iface_map { - typeof iifname : ct zone - } - # Assign unique zones for each VRF - # Chain for inbound traffic - chain vrf_zones_ct_in { - type filter hook prerouting priority raw; policy accept; - counter ct original zone set iifname map @ct_iface_map - } - # Chain for locally-generated traffic - chain vrf_zones_ct_out { - type filter hook output priority raw; policy accept; - counter ct original zone set oifname map @ct_iface_map - } -} diff --git a/data/vyos-firewall-init.conf b/data/vyos-firewall-init.conf index cd7d5011f..5a4e03015 100644 --- a/data/vyos-firewall-init.conf +++ b/data/vyos-firewall-init.conf @@ -54,3 +54,22 @@ table ip6 raw { type filter hook prerouting priority -300; policy accept; } } + +# Required by VRF +table inet vrf_zones { + # Map of interfaces and connections tracking zones + map ct_iface_map { + typeof iifname : ct zone + } + # Assign unique zones for each VRF + # Chain for inbound traffic + chain vrf_zones_ct_in { + type filter hook prerouting priority raw; policy accept; + counter ct original zone set iifname map @ct_iface_map + } + # Chain for locally-generated traffic + chain vrf_zones_ct_out { + type filter hook output priority raw; policy accept; + counter ct original zone set oifname map @ct_iface_map + } +} diff --git a/interface-definitions/include/firewall/common-rule-inet.xml.i b/interface-definitions/include/firewall/common-rule-inet.xml.i index 6f56ecc85..85189d975 100644 --- a/interface-definitions/include/firewall/common-rule-inet.xml.i +++ b/interface-definitions/include/firewall/common-rule-inet.xml.i @@ -32,25 +32,6 @@ </leafNode> </children> </node> -<node name="ipsec"> - <properties> - <help>Inbound IPsec packets</help> - </properties> - <children> - <leafNode name="match-ipsec"> - <properties> - <help>Inbound IPsec packets</help> - <valueless/> - </properties> - </leafNode> - <leafNode name="match-none"> - <properties> - <help>Inbound non-IPsec packets</help> - <valueless/> - </properties> - </leafNode> - </children> -</node> <node name="limit"> <properties> <help>Rate limit using a token bucket filter</help> diff --git a/interface-definitions/include/firewall/ipv4-custom-name.xml.i b/interface-definitions/include/firewall/ipv4-custom-name.xml.i index 8199d15fe..8046b2d6c 100644 --- a/interface-definitions/include/firewall/ipv4-custom-name.xml.i +++ b/interface-definitions/include/firewall/ipv4-custom-name.xml.i @@ -33,6 +33,7 @@ <children> #include <include/firewall/common-rule-ipv4.xml.i> #include <include/firewall/inbound-interface.xml.i> + #include <include/firewall/match-ipsec.xml.i> #include <include/firewall/offload-target.xml.i> #include <include/firewall/outbound-interface.xml.i> </children> diff --git a/interface-definitions/include/firewall/ipv4-hook-forward.xml.i b/interface-definitions/include/firewall/ipv4-hook-forward.xml.i index de2c70482..b0e240a03 100644 --- a/interface-definitions/include/firewall/ipv4-hook-forward.xml.i +++ b/interface-definitions/include/firewall/ipv4-hook-forward.xml.i @@ -28,6 +28,7 @@ #include <include/firewall/action-forward.xml.i> #include <include/firewall/common-rule-ipv4.xml.i> #include <include/firewall/inbound-interface.xml.i> + #include <include/firewall/match-ipsec.xml.i> #include <include/firewall/offload-target.xml.i> #include <include/firewall/outbound-interface.xml.i> </children> diff --git a/interface-definitions/include/firewall/ipv4-hook-input.xml.i b/interface-definitions/include/firewall/ipv4-hook-input.xml.i index 5d32657ea..cefb1ffa7 100644 --- a/interface-definitions/include/firewall/ipv4-hook-input.xml.i +++ b/interface-definitions/include/firewall/ipv4-hook-input.xml.i @@ -27,6 +27,7 @@ <children> #include <include/firewall/common-rule-ipv4.xml.i> #include <include/firewall/inbound-interface.xml.i> + #include <include/firewall/match-ipsec.xml.i> </children> </tagNode> </children> diff --git a/interface-definitions/include/firewall/ipv6-custom-name.xml.i b/interface-definitions/include/firewall/ipv6-custom-name.xml.i index 5748b3927..fb8740c38 100644 --- a/interface-definitions/include/firewall/ipv6-custom-name.xml.i +++ b/interface-definitions/include/firewall/ipv6-custom-name.xml.i @@ -33,6 +33,7 @@ <children> #include <include/firewall/common-rule-ipv6.xml.i> #include <include/firewall/inbound-interface.xml.i> + #include <include/firewall/match-ipsec.xml.i> #include <include/firewall/offload-target.xml.i> #include <include/firewall/outbound-interface.xml.i> </children> diff --git a/interface-definitions/include/firewall/ipv6-hook-forward.xml.i b/interface-definitions/include/firewall/ipv6-hook-forward.xml.i index b53f09f59..7efc2614e 100644 --- a/interface-definitions/include/firewall/ipv6-hook-forward.xml.i +++ b/interface-definitions/include/firewall/ipv6-hook-forward.xml.i @@ -28,6 +28,7 @@ #include <include/firewall/action-forward.xml.i> #include <include/firewall/common-rule-ipv6.xml.i> #include <include/firewall/inbound-interface.xml.i> + #include <include/firewall/match-ipsec.xml.i> #include <include/firewall/offload-target.xml.i> #include <include/firewall/outbound-interface.xml.i> </children> diff --git a/interface-definitions/include/firewall/ipv6-hook-input.xml.i b/interface-definitions/include/firewall/ipv6-hook-input.xml.i index 493611fb1..e1f41e64c 100644 --- a/interface-definitions/include/firewall/ipv6-hook-input.xml.i +++ b/interface-definitions/include/firewall/ipv6-hook-input.xml.i @@ -27,6 +27,7 @@ <children> #include <include/firewall/common-rule-ipv6.xml.i> #include <include/firewall/inbound-interface.xml.i> + #include <include/firewall/match-ipsec.xml.i> </children> </tagNode> </children> diff --git a/interface-definitions/include/firewall/match-ipsec.xml.i b/interface-definitions/include/firewall/match-ipsec.xml.i new file mode 100644 index 000000000..82c2b324d --- /dev/null +++ b/interface-definitions/include/firewall/match-ipsec.xml.i @@ -0,0 +1,21 @@ +<!-- include start from firewall/match-ipsec.xml.i --> +<node name="ipsec"> + <properties> + <help>Inbound IPsec packets</help> + </properties> + <children> + <leafNode name="match-ipsec"> + <properties> + <help>Inbound IPsec packets</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="match-none"> + <properties> + <help>Inbound non-IPsec packets</help> + <valueless/> + </properties> + </leafNode> + </children> +</node> +<!-- include end -->
\ No newline at end of file diff --git a/interface-definitions/include/haproxy/rule-backend.xml.i b/interface-definitions/include/haproxy/rule-backend.xml.i index a6832d693..b2be4fde4 100644 --- a/interface-definitions/include/haproxy/rule-backend.xml.i +++ b/interface-definitions/include/haproxy/rule-backend.xml.i @@ -118,7 +118,7 @@ <description>Exactly URL</description> </valueHelp> <constraint> - <regex>^\/[\w\-.\/]+$</regex> + <regex>^\/[\w\-.\/]*$</regex> </constraint> <constraintErrorMessage>Incorrect URL format</constraintErrorMessage> <multi/> diff --git a/interface-definitions/include/version/bgp-version.xml.i b/interface-definitions/include/version/bgp-version.xml.i index 1386ea9bc..6bed7189f 100644 --- a/interface-definitions/include/version/bgp-version.xml.i +++ b/interface-definitions/include/version/bgp-version.xml.i @@ -1,3 +1,3 @@ <!-- include start from include/version/bgp-version.xml.i --> -<syntaxVersion component='bgp' version='4'></syntaxVersion> +<syntaxVersion component='bgp' version='5'></syntaxVersion> <!-- include end --> diff --git a/op-mode-definitions/rpki.xml.in b/op-mode-definitions/rpki.xml.in index 72d378b88..9e0f83e20 100644 --- a/op-mode-definitions/rpki.xml.in +++ b/op-mode-definitions/rpki.xml.in @@ -7,6 +7,15 @@ <help>Show RPKI (Resource Public Key Infrastructure) information</help> </properties> <children> + <tagNode name="as-number"> + <properties> + <help>Lookup by ASN in prefix table</help> + <completionHelp> + <list><ASNUM></list> + </completionHelp> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </tagNode> <leafNode name="cache-connection"> <properties> <help>Show RPKI cache connections</help> @@ -19,6 +28,26 @@ </properties> <command>vtysh -c "show rpki cache-server"</command> </leafNode> + <tagNode name="prefix"> + <properties> + <help>Lookup IP prefix and optionally ASN in prefix table</help> + <completionHelp> + <list><x.x.x.x/x> <h:h:h:h:h:h:h:h/x></list> + </completionHelp> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + <children> + <tagNode name="as-number"> + <properties> + <help>AS Number</help> + <completionHelp> + <list><ASNUM></list> + </completionHelp> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $(echo $@ | sed -e "s/as-number //g")</command> + </tagNode> + </children> + </tagNode> <leafNode name="prefix-table"> <properties> <help>Show RPKI-validated prefixes</help> diff --git a/python/vyos/qos/trafficshaper.py b/python/vyos/qos/trafficshaper.py index 1f3b03680..d6705cc77 100644 --- a/python/vyos/qos/trafficshaper.py +++ b/python/vyos/qos/trafficshaper.py @@ -99,7 +99,11 @@ class TrafficShaper(QoSBase): self._cmd(tmp) if 'default' in config: - rate = self._rate_convert(config['default']['bandwidth']) + if config['default']['bandwidth'].endswith('%'): + percent = config['default']['bandwidth'].rstrip('%') + rate = self._rate_convert(config['bandwidth']) * int(percent) // 100 + else: + rate = self._rate_convert(config['default']['bandwidth']) burst = config['default']['burst'] quantum = config['default']['codel_quantum'] tmp = f'tc class replace dev {self._interface} parent {self._parent:x}:1 classid {self._parent:x}:{default_minor_id:x} htb rate {rate} burst {burst} quantum {quantum}' @@ -107,7 +111,11 @@ class TrafficShaper(QoSBase): priority = config['default']['priority'] tmp += f' prio {priority}' if 'ceiling' in config['default']: - f_ceil = self._rate_convert(config['default']['ceiling']) + if config['default']['ceiling'].endswith('%'): + percent = config['default']['ceiling'].rstrip('%') + f_ceil = self._rate_convert(config['bandwidth']) * int(percent) // 100 + else: + f_ceil = self._rate_convert(config['default']['ceiling']) tmp += f' ceil {f_ceil}' self._cmd(tmp) diff --git a/src/conf_mode/protocols_bgp.py b/src/conf_mode/protocols_bgp.py index f6f3370c3..d90dfe45b 100755 --- a/src/conf_mode/protocols_bgp.py +++ b/src/conf_mode/protocols_bgp.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2020-2023 VyOS maintainers and contributors +# Copyright (C) 2020-2024 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 @@ -509,6 +509,14 @@ def verify(bgp): if verify_vrf_as_import(vrf_name, afi, bgp['dependent_vrfs']): raise ConfigError( 'Command "import vrf" conflicts with "route-target vpn both" command!') + if dict_search('route_target.vpn.export', afi_config): + raise ConfigError( + 'Command "route-target vpn export" conflicts '\ + 'with "route-target vpn both" command!') + if dict_search('route_target.vpn.import', afi_config): + raise ConfigError( + 'Command "route-target vpn import" conflicts '\ + 'with "route-target vpn both" command!') if dict_search('route_target.vpn.import', afi_config): if verify_vrf_as_import(vrf_name, afi, bgp['dependent_vrfs']): diff --git a/src/conf_mode/vrf.py b/src/conf_mode/vrf.py index 9b1b6355f..f2c544aa6 100755 --- a/src/conf_mode/vrf.py +++ b/src/conf_mode/vrf.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2020-2023 VyOS maintainers and contributors +# Copyright (C) 2020-2024 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 @@ -27,13 +27,12 @@ from vyos.ifconfig import Interface from vyos.template import render from vyos.template import render_to_string from vyos.utils.dict import dict_search +from vyos.utils.kernel import check_kmod from vyos.utils.network import get_interface_config from vyos.utils.network import get_vrf_members from vyos.utils.network import interface_exists from vyos.utils.process import call from vyos.utils.process import cmd -from vyos.utils.process import popen -from vyos.utils.process import run from vyos.utils.system import sysctl_write from vyos import ConfigError from vyos import frr @@ -41,17 +40,29 @@ from vyos import airbag airbag.enable() config_file = '/etc/iproute2/rt_tables.d/vyos-vrf.conf' -nft_vrf_config = '/tmp/nftables-vrf-zones' - -def has_rule(af : str, priority : int, table : str): - """ Check if a given ip rule exists """ +k_mod = ['vrf'] + +def has_rule(af : str, priority : int, table : str=None): + """ + Check if a given ip rule exists + $ ip --json -4 rule show + [{'l3mdev': None, 'priority': 1000, 'src': 'all'}, + {'action': 'unreachable', 'l3mdev': None, 'priority': 2000, 'src': 'all'}, + {'priority': 32765, 'src': 'all', 'table': 'local'}, + {'priority': 32766, 'src': 'all', 'table': 'main'}, + {'priority': 32767, 'src': 'all', 'table': 'default'}] + """ if af not in ['-4', '-6']: raise ValueError() - command = f'ip -j {af} rule show' + command = f'ip --detail --json {af} rule show' for tmp in loads(cmd(command)): - if {'priority', 'table'} <= set(tmp): + if 'priority' in tmp and 'table' in tmp: if tmp['priority'] == priority and tmp['table'] == table: return True + elif 'priority' in tmp and table in tmp: + # l3mdev table has a different layout + if tmp['priority'] == priority: + return True return False def vrf_interfaces(c, match): @@ -173,8 +184,6 @@ def verify(vrf): def generate(vrf): # Render iproute2 VR helper names render(config_file, 'iproute2/vrf.conf.j2', vrf) - # Render nftables zones config - render(nft_vrf_config, 'firewall/nftables-vrf-zones.j2', vrf) # Render VRF Kernel/Zebra route-map filters vrf['frr_zebra_config'] = render_to_string('frr/zebra.vrf.route-map.frr.j2', vrf) @@ -227,14 +236,6 @@ def apply(vrf): sysctl_write('net.vrf.strict_mode', strict_mode) if 'name' in vrf: - # Separate VRFs in conntrack table - # check if table already exists - _, err = popen('nft list table inet vrf_zones') - # If not, create a table - if err and os.path.exists(nft_vrf_config): - cmd(f'nft -f {nft_vrf_config}') - os.unlink(nft_vrf_config) - # Linux routing uses rules to find tables - routing targets are then # looked up in those tables. If the lookup got a matching route, the # process ends. @@ -318,17 +319,11 @@ def apply(vrf): frr_cfg.add_before(frr.default_add_before, vrf['frr_zebra_config']) frr_cfg.commit_configuration(zebra_daemon) - # return to default lookup preference when no VRF is configured - if 'name' not in vrf: - # Remove VRF zones table from nftables - tmp = run('nft list table inet vrf_zones') - if tmp == 0: - cmd('nft delete table inet vrf_zones') - return None if __name__ == '__main__': try: + check_kmod(k_mod) c = get_config() verify(c) generate(c) diff --git a/src/migration-scripts/bgp/4-to-5 b/src/migration-scripts/bgp/4-to-5 new file mode 100755 index 000000000..c4eb9ec72 --- /dev/null +++ b/src/migration-scripts/bgp/4-to-5 @@ -0,0 +1,67 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2024 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/>. + +# Delete 'protocols bgp address-family ipv6-unicast route-target vpn +# import/export', if 'protocols bgp address-family ipv6-unicast +# route-target vpn both' exists + +from sys import argv +from sys import exit + +from vyos.configtree import ConfigTree + +if len(argv) < 2: + print("Must specify file name!") + exit(1) + +file_name = argv[1] + +with open(file_name, 'r') as f: + config_file = f.read() + +config = ConfigTree(config_file) + +bgp_base = ['protocols', 'bgp'] +# Delete 'import/export' in default vrf if 'both' exists +if config.exists(bgp_base): + for address_family in ['ipv4-unicast', 'ipv6-unicast']: + rt_path = bgp_base + ['address-family', address_family, 'route-target', + 'vpn'] + if config.exists(rt_path + ['both']): + if config.exists(rt_path + ['import']): + config.delete(rt_path + ['import']) + if config.exists(rt_path + ['export']): + config.delete(rt_path + ['export']) + +# Delete import/export in vrfs if both exists +if config.exists(['vrf', 'name']): + for vrf in config.list_nodes(['vrf', 'name']): + vrf_base = ['vrf', 'name', vrf] + for address_family in ['ipv4-unicast', 'ipv6-unicast']: + rt_path = vrf_base + bgp_base + ['address-family', address_family, + 'route-target', 'vpn'] + if config.exists(rt_path + ['both']): + if config.exists(rt_path + ['import']): + config.delete(rt_path + ['import']) + if config.exists(rt_path + ['export']): + config.delete(rt_path + ['export']) + +try: + with open(file_name, 'w') as f: + f.write(config.to_string()) +except OSError as e: + print(f'Failed to save the modified config: {e}') + exit(1) diff --git a/src/migration-scripts/https/5-to-6 b/src/migration-scripts/https/5-to-6 index 6d6efd32c..0090adccb 100755 --- a/src/migration-scripts/https/5-to-6 +++ b/src/migration-scripts/https/5-to-6 @@ -43,11 +43,11 @@ if not config.exists(base): # Nothing to do sys.exit(0) -if config.exists(base + ['certificates']): +if config.exists(base + ['certificates', 'certbot']): # both domain-name and email must be set on CLI - ensured by previous verify() domain_names = config.return_values(base + ['certificates', 'certbot', 'domain-name']) email = config.return_value(base + ['certificates', 'certbot', 'email']) - config.delete(base + ['certificates']) + config.delete(base + ['certificates', 'certbot']) # Set default certname based on domain-name cert_name = 'https-' + domain_names[0].split('.')[0] diff --git a/src/migration-scripts/policy/4-to-5 b/src/migration-scripts/policy/4-to-5 index f6f889c35..5b8fee17e 100755 --- a/src/migration-scripts/policy/4-to-5 +++ b/src/migration-scripts/policy/4-to-5 @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2022 VyOS maintainers and contributors +# Copyright (C) 2022-2024 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 @@ -37,7 +37,53 @@ base4 = ['policy', 'route'] base6 = ['policy', 'route6'] config = ConfigTree(config_file) + +def delete_orphaned_interface_policy(config, iftype, ifname, vif=None, vifs=None, vifc=None): + """Delete unexpected policy on interfaces in cases when + policy does not exist but inreface has a policy configuration + Example T5941: + set interfaces bonding bond0 vif 995 policy + """ + if_path = ['interfaces', iftype, ifname] + + if vif: + if_path += ['vif', vif] + elif vifs: + if_path += ['vif-s', vifs] + if vifc: + if_path += ['vif-c', vifc] + + if not config.exists(if_path + ['policy']): + return + + config.delete(if_path + ['policy']) + + if not config.exists(base4) and not config.exists(base6): + # Delete orphaned nodes on interfaces T5941 + for iftype in config.list_nodes(['interfaces']): + for ifname in config.list_nodes(['interfaces', iftype]): + delete_orphaned_interface_policy(config, iftype, ifname) + + if config.exists(['interfaces', iftype, ifname, 'vif']): + for vif in config.list_nodes(['interfaces', iftype, ifname, 'vif']): + delete_orphaned_interface_policy(config, iftype, ifname, vif=vif) + + if config.exists(['interfaces', iftype, ifname, 'vif-s']): + for vifs in config.list_nodes(['interfaces', iftype, ifname, 'vif-s']): + delete_orphaned_interface_policy(config, iftype, ifname, vifs=vifs) + + if config.exists(['interfaces', iftype, ifname, 'vif-s', vifs, 'vif-c']): + for vifc in config.list_nodes(['interfaces', iftype, ifname, 'vif-s', vifs, 'vif-c']): + delete_orphaned_interface_policy(config, iftype, ifname, vifs=vifs, vifc=vifc) + + 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) + # Nothing to do exit(0) diff --git a/src/migration-scripts/qos/1-to-2 b/src/migration-scripts/qos/1-to-2 index cca32d06e..666811e5a 100755 --- a/src/migration-scripts/qos/1-to-2 +++ b/src/migration-scripts/qos/1-to-2 @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2022 VyOS maintainers and contributors +# Copyright (C) 2022-2024 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 @@ -40,7 +40,53 @@ with open(file_name, 'r') as f: base = ['traffic-policy'] config = ConfigTree(config_file) + +def delete_orphaned_interface_policy(config, iftype, ifname, vif=None, vifs=None, vifc=None): + """Delete unexpected traffic-policy on interfaces in cases when + policy does not exist but inreface has a policy configuration + Example T5941: + set interfaces bonding bond0 vif 995 traffic-policy + """ + if_path = ['interfaces', iftype, ifname] + + if vif: + if_path += ['vif', vif] + elif vifs: + if_path += ['vif-s', vifs] + if vifc: + if_path += ['vif-c', vifc] + + if not config.exists(if_path + ['traffic-policy']): + return + + config.delete(if_path + ['traffic-policy']) + + if not config.exists(base): + # Delete orphaned nodes on interfaces T5941 + for iftype in config.list_nodes(['interfaces']): + for ifname in config.list_nodes(['interfaces', iftype]): + delete_orphaned_interface_policy(config, iftype, ifname) + + if config.exists(['interfaces', iftype, ifname, 'vif']): + for vif in config.list_nodes(['interfaces', iftype, ifname, 'vif']): + delete_orphaned_interface_policy(config, iftype, ifname, vif=vif) + + if config.exists(['interfaces', iftype, ifname, 'vif-s']): + for vifs in config.list_nodes(['interfaces', iftype, ifname, 'vif-s']): + delete_orphaned_interface_policy(config, iftype, ifname, vifs=vifs) + + if config.exists(['interfaces', iftype, ifname, 'vif-s', vifs, 'vif-c']): + for vifc in config.list_nodes(['interfaces', iftype, ifname, 'vif-s', vifs, 'vif-c']): + delete_orphaned_interface_policy(config, iftype, ifname, vifs=vifs, vifc=vifc) + + 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) + # Nothing to do exit(0) |