From e15ced69a67b3a02220ce2cb60ddadb238b1e485 Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Wed, 30 Oct 2024 21:38:59 +0100 Subject: [PATCH 001/132] addons: address: get default mac address from policy Default MAC addresses can now be defined as iface_default policy $ cat /var/lib/ifupdown2/policy.d/mac.json { "address": { "iface_defaults": { "swp1": {"hwaddress": "7a:43:9e:c0:e0:42"} } } } The usecase being, how to revert to a base mac once the hwaddress attribute is removed from ENI. Signed-off-by: Julien Fortin --- ifupdown2/addons/address.py | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/ifupdown2/addons/address.py b/ifupdown2/addons/address.py index 20f88669..4fa896d1 100644 --- a/ifupdown2/addons/address.py +++ b/ifupdown2/addons/address.py @@ -4,6 +4,7 @@ # Author: Roopa Prabhu, roopa@cumulusnetworks.com # +import re import socket import json import time @@ -269,6 +270,9 @@ def __init__(self, *args, **kargs): self.logger.debug(f"policy: default_loopback_scope set to {self.default_loopback_scope}") self.valid_scopes = self.get_mod_subattr("scope", "validvals") + self.mac_regex = re.compile(r"^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$") + + def __policy_get_default_mtu(self): default_mtu = policymanager.policymanager_api.get_attr_default( module_name=self.__class__.__name__, @@ -1216,6 +1220,24 @@ def _up(self, ifaceobj, ifaceobj_getfunc=None): return self._settle_dad(ifaceobj, [ip for ip, _ in user_addrs_list if ip.version == 6]) + def validate_mac(self, mac): + if not mac: + return False + if not bool(self.mac_regex.match(mac)): + raise Exception("Invalid MAC address from policy: %s" % mac) + return True + + def process_hwaddress_reset_to_default(self, ifaceobj): + iface_defaults = policymanager.policymanager_api.get_iface_defaults("address") + + if iface_defaults: + interface_mac_default = iface_defaults.get(ifaceobj.name, {}).get("hwaddress") + + if self.validate_mac(interface_mac_default): + return interface_mac_default + + return None + def process_hwaddress(self, ifaceobj): hwaddress = self._get_hwaddress(ifaceobj) @@ -1230,7 +1252,9 @@ def process_hwaddress(self, ifaceobj): if not hwaddress: return None, None else: - return None, None + hwaddress = self.process_hwaddress_reset_to_default(ifaceobj) + if not hwaddress: + return None, None if not ifupdownflags.flags.PERFMODE: # system is clean running_hwaddress = self.cache.get_link_address(ifaceobj.name) From c8fb057a03654fb311783bd1f327c668a0604913 Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Mon, 30 May 2022 22:07:34 +0200 Subject: [PATCH 002/132] SONAR: addons: address: Merge if statement with the enclosing one. Signed-off-by: Julien Fortin --- ifupdown2/addons/address.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/ifupdown2/addons/address.py b/ifupdown2/addons/address.py index 4fa896d1..388f48eb 100644 --- a/ifupdown2/addons/address.py +++ b/ifupdown2/addons/address.py @@ -461,11 +461,10 @@ def _process_bridge(self, ifaceobj, up, hwaddress, old_mac_addr=None): arp_accept = utils.boolean_support_binary(arp_accept) is_vlan_dev_on_vlan_aware_bridge = False is_bridge = self.cache.get_link_kind(ifaceobj.name) == 'bridge' - if not is_bridge: - if ifaceobj.link_kind & ifaceLinkKind.VLAN: - bridgename = ifaceobj.lowerifaces[0] - vlan = self._get_vlan_id(ifaceobj) - is_vlan_dev_on_vlan_aware_bridge = self.cache.bridge_is_vlan_aware(bridgename) + if not is_bridge and ifaceobj.link_kind & ifaceLinkKind.VLAN: + bridgename = ifaceobj.lowerifaces[0] + vlan = self._get_vlan_id(ifaceobj) + is_vlan_dev_on_vlan_aware_bridge = self.cache.bridge_is_vlan_aware(bridgename) if ((is_bridge and not self.cache.bridge_is_vlan_aware(ifaceobj.name)) or is_vlan_dev_on_vlan_aware_bridge): if self._address_valid(addrs): From f0639a9ede13fb7c0b94e313051bb64a900c1e93 Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Wed, 30 Oct 2024 21:46:42 +0100 Subject: [PATCH 003/132] addons: address: special diff mode handling for stale fdb entries In diff mode SVI won't be processed if their config hasn't changed. We need to do a specific fdb check during bridge processing and purge stale macs. This commit includes an incremental fix by Scott Laffer: Fix logic in bridge stale perm fdb handling not to delete VRR vlan entries In the scenario where only a bridge interface is up'd (i.e. it's the only thing in the run queue in a diff-based apply), permanent fdb entries for VRR interfaces were being incorrectly marked as stale and deleted from the fdb. This patch updates the logic to not only match the bridge's own MAC address but any permanent entry on the bridge device itself. Signed-off-by: Julien Fortin --- ifupdown2/addons/address.py | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/ifupdown2/addons/address.py b/ifupdown2/addons/address.py index 388f48eb..d0882c91 100644 --- a/ifupdown2/addons/address.py +++ b/ifupdown2/addons/address.py @@ -502,6 +502,33 @@ def _process_bridge(self, ifaceobj, up, hwaddress, old_mac_addr=None): else: self.iproute2.bridge_fdb_del(bridgename, hwaddress, vlan) + if is_bridge: + # Get the link hwaddress of bridge if we cannot find it in defaults + if not hwaddress: + hwaddress = self.cache.get_link_address(ifaceobj.name) + + # we need to do an fdb check during bridge processing and purge stale macs + fdbs = self._get_bridge_fdbs(ifaceobj.name) + + # Save the permanent MACs for comparison too, as this can be used to preserve + # perm entries for VRR interfaces. + valid_macs = set([utils.mac_str_to_int(i) for i in fdbs.get('permanent', [])]) + # Add the actual bridge MAC to this set too. + valid_macs.add(utils.mac_str_to_int(hwaddress)) + + # Now iterate and purge if it's not a valid mac. + for vlan, macs in fdbs.items(): + for mac in macs: + if utils.mac_str_to_int(mac) not in valid_macs: + self.logger.info(f"{ifaceobj.name}: stale fdb entry ({mac}) detected on vlan {vlan}") + try: + if vlan == 'permanent': + self.iproute2.bridge_fdb_del(ifaceobj.name, mac) + else: + self.iproute2.bridge_fdb_del(ifaceobj.name, mac, vlan) + except Exception as e: + self.logger.debug(f"{ifaceobj.name}: bridge_fdb_del failed: {str(e)}") + def __get_ip_addr_with_attributes(self, ifaceobj_list, ifname): user_config_ip_addrs_list = [] @@ -1341,14 +1368,14 @@ def _down(self, ifaceobj, ifaceobj_getfunc=None): self.logger.debug('%s : %s' %(ifaceobj.name, str(e))) pass - def _get_bridge_fdbs(self, bridgename, vlan): + def _get_bridge_fdbs(self, bridgename, vlan=None): fdbs = self._bridge_fdb_query_cache.get(bridgename) if not fdbs: fdbs = self.iproute2.bridge_fdb_show_dev(bridgename) if not fdbs: return self._bridge_fdb_query_cache[bridgename] = fdbs - return fdbs.get(vlan) + return fdbs.get(vlan) if vlan else fdbs def _check_addresses_in_bridge(self, ifaceobj, hwaddress): """ If the device is a bridge, make sure the addresses From d3d6e32c297dfde569464c45f89872ccb8877ab7 Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Fri, 12 Jul 2024 23:43:15 +0200 Subject: [PATCH 004/132] addons: address: dont reset swp's mac if part of a bond When a switch port is enslaved to a bond we don't manage its mac address anymore - all port have the same mac. Signed-off-by: Julien Fortin --- ifupdown2/addons/address.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ifupdown2/addons/address.py b/ifupdown2/addons/address.py index d0882c91..a1c6d387 100644 --- a/ifupdown2/addons/address.py +++ b/ifupdown2/addons/address.py @@ -1254,6 +1254,10 @@ def validate_mac(self, mac): return True def process_hwaddress_reset_to_default(self, ifaceobj): + if not ifaceobj.link_kind and ifaceobj.link_privflags & ifaceLinkPrivFlags.BOND_SLAVE: + # if the switch port is part of a bond we shouldn't revert the mac address + return None + iface_defaults = policymanager.policymanager_api.get_iface_defaults("address") if iface_defaults: From 05461a026321ca4c59495386b8cc2538d02377a7 Mon Sep 17 00:00:00 2001 From: Riddhi Rex Antonyrex Date: Tue, 30 Jul 2024 15:35:22 -0700 Subject: [PATCH 005/132] addons: address: reset mac address on switch port Signed-off-by: Julien Fortin --- ifupdown2/addons/address.py | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/ifupdown2/addons/address.py b/ifupdown2/addons/address.py index a1c6d387..7074a6bc 100644 --- a/ifupdown2/addons/address.py +++ b/ifupdown2/addons/address.py @@ -1360,13 +1360,21 @@ def _down(self, ifaceobj, ifaceobj_getfunc=None): if alias: self.sysfs.link_set_alias(ifaceobj.name, None) # None to reset alias. - # XXX hwaddress reset cannot happen because we dont know last - # address. + hwaddress = self.process_hwaddress_reset_to_default(ifaceobj) + if hwaddress != None and not ifaceobj.link_kind: + hwaddress_int = utils.mac_str_to_int(hwaddress) + self.netlink.link_set_address( + ifaceobj.name, + hwaddress, + hwaddress_int, + keep_link_down=ifaceobj.link_privflags & ifaceLinkPrivFlags.KEEP_LINK_DOWN + ) + else: + # Handle special things on a bridge + hwaddress = self._get_hwaddress(ifaceobj) + if not hwaddress: + hwaddress = self.cache.get_link_address(ifaceobj.name) - # Handle special things on a bridge - hwaddress = self._get_hwaddress(ifaceobj) - if not hwaddress: - hwaddress = self.cache.get_link_address(ifaceobj.name) self._process_bridge(ifaceobj, False, hwaddress, None) except Exception as e: self.logger.debug('%s : %s' %(ifaceobj.name, str(e))) From 0599c2550970e78fa531ad77e792c4943c3134fb Mon Sep 17 00:00:00 2001 From: Riddhi Rex Antonyrex Date: Mon, 22 Jul 2024 15:13:31 -0700 Subject: [PATCH 006/132] addons: address: Changing bond member port mac addr should not get applied when it is part of a bond Changing bond member port mac addr should not get applied when it is part of a bond Problem description: Changing the hwaddress of a bond slave is not updating the bond interface's hwaddress. Fix description: Block changing the hwaddress of a bond slave Testcases covered: 1. change the hwaddress of an existing bond slave through nvue 2. Edit the eni file to add the port in bridge, see the hwaddress set through ifquery and 'ip link show', remove the port from bridge and add it to bond interface and change the hwaddress. do 'ifreload -a' and ifquery displays the changed hwaddress but 'ip link show' displays the older hwaddress and so does the bond intf. Signed-off-by: Julien Fortin --- ifupdown2/addons/address.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ifupdown2/addons/address.py b/ifupdown2/addons/address.py index 7074a6bc..4408a1d5 100644 --- a/ifupdown2/addons/address.py +++ b/ifupdown2/addons/address.py @@ -1303,6 +1303,11 @@ def process_hwaddress(self, ifaceobj): self.netlink.link_down(l) slave_down = True try: + if ifaceobj.link_privflags & ifaceLinkPrivFlags.BOND_SLAVE and ifaceobj.get_attr_value("hwaddress"): + master_ifname = self.cache.get_master(ifaceobj.name) + if master_ifname: + self.log_error("%s: setting hwaddress is not permitted on an existing bond slave" % ifaceobj.name, ifaceobj=ifaceobj) + self.netlink.link_set_address( ifaceobj.name, hwaddress, From 245fe815bd77f1c9c2d41ccfe13d1dfa160ccfc0 Mon Sep 17 00:00:00 2001 From: Riddhi Rex Antonyrex Date: Tue, 18 Jun 2024 11:49:56 -0700 Subject: [PATCH 007/132] addons: addressvirtual: vrr macvlan is created even if address-virtual in e/n/i stanza is rendered with values like "none" Signed-off-by: Julien Fortin --- ifupdown2/addons/addressvirtual.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/ifupdown2/addons/addressvirtual.py b/ifupdown2/addons/addressvirtual.py index d52115eb..18a0d6d9 100644 --- a/ifupdown2/addons/addressvirtual.py +++ b/ifupdown2/addons/addressvirtual.py @@ -8,6 +8,7 @@ import glob import ipaddress import subprocess +import re from collections import deque @@ -328,14 +329,19 @@ def _remove_address_config(self, ifaceobj, address_virtual_list=None): def check_mac_address(self, ifaceobj, mac): if mac == 'none': + self.logger.info("%s: The virtual mac address is set as none" %ifaceobj.name) return True try: if int(mac.split(":")[0], 16) & 1 : - self.log_error("%s: Multicast bit is set in the virtual mac address '%s'" - % (ifaceobj.name, mac), ifaceobj=ifaceobj) - return False + raise Exception("Multicast bit is set in the virtual mac address '%s'" + % mac) + mac_regex = re.compile(r'^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$') + if not mac_regex.match(mac): + raise Exception("'%s'" % mac) return True - except ValueError: + + except Exception as e: + self.logger.error("%s: Invalid virtual mac address: %s" % (ifaceobj.name, str(e))) return False def _fixup_vrf_enslavements(self, ifaceobj, ifaceobj_getfunc=None): From 13372350fe48ceede6fbb5516eec4a5150e4067c Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Thu, 19 Sep 2024 15:13:01 +0200 Subject: [PATCH 008/132] addons: addressvirtual: fix regex to allow single digit segments Previous commit 4cfd51f broke test_bridge7_macvlans which is using mac address with single digit segments: 42:38:39:FF:0:1 The regex is also compiled and moved to init to only do it once. Signed-off-by: Julien Fortin --- ifupdown2/addons/addressvirtual.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/ifupdown2/addons/addressvirtual.py b/ifupdown2/addons/addressvirtual.py index 18a0d6d9..c85013fa 100644 --- a/ifupdown2/addons/addressvirtual.py +++ b/ifupdown2/addons/addressvirtual.py @@ -91,7 +91,7 @@ def __init__(self, *args, **kargs): ), default=True ) - + self.mac_regex = re.compile(r"^([0-9A-Fa-f]{1,2}[:-]){5}([0-9A-Fa-f]{1,2})$") self.address_virtual_ipv6_addrgen_value_dict = {'on': 0, 'yes': 0, '0': 0, 'off': 1, 'no': 1, '1': 1} if addressvirtual.ADDR_METRIC_SUPPORT is None: @@ -335,8 +335,7 @@ def check_mac_address(self, ifaceobj, mac): if int(mac.split(":")[0], 16) & 1 : raise Exception("Multicast bit is set in the virtual mac address '%s'" % mac) - mac_regex = re.compile(r'^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$') - if not mac_regex.match(mac): + if not self.mac_regex.match(mac): raise Exception("'%s'" % mac) return True From cd28db9584cb96e8679e900fb03c15af5f7467b4 Mon Sep 17 00:00:00 2001 From: Andy Roulin Date: Wed, 17 Apr 2024 10:46:44 -0700 Subject: [PATCH 009/132] addressvirtual: set accept_dad=0 before link up DAD must be disabled on macvlan/VRR interfaces as they share the same MAC (and thus link-local IPv6 address). The sysctl accept_dad must be set to 0 before setting the link UP otherwise DAD might trigger before we reset the sysctl to 0. Signed-off-by: Andy Roulin Signed-off-by: Julien Fortin --- ifupdown2/addons/addressvirtual.py | 36 +++++++++++++++--------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/ifupdown2/addons/addressvirtual.py b/ifupdown2/addons/addressvirtual.py index c85013fa..d7f7c706 100644 --- a/ifupdown2/addons/addressvirtual.py +++ b/ifupdown2/addons/addressvirtual.py @@ -472,6 +472,24 @@ def create_macvlan_and_apply_config(self, ifaceobj, intf_config_list, vrrp=False self.sync_macvlan_forwarding_state(ifname, macvlan_ifname) link_created = True + # Disable IPv6 duplicate address detection on VRR interfaces + sysctl_prefix = "net.ipv6.conf.%s" % macvlan_ifname + + try: + syskey = "%s.%s" % (sysctl_prefix, "enhanced_dad") + if self.sysctl_get(syskey) != "0": + self.sysctl_set(syskey, "0") + except Exception as e: + self.logger.info("sysctl failure: operation not supported: %s" % str(e)) + + for key, sysval in { + "accept_dad": "0", + "dad_transmits": "0" + }.items(): + syskey = "%s.%s" % (sysctl_prefix, key) + if self.sysctl_get(syskey) != sysval: + self.sysctl_set(syskey, sysval) + # first thing we need to handle vrf enslavement if ifaceobj.link_privflags & ifaceLinkPrivFlags.VRF_SLAVE: vrf_ifname = self.cache.get_master(ifaceobj.name) @@ -558,24 +576,6 @@ def create_macvlan_and_apply_config(self, ifaceobj, intf_config_list, vrrp=False except Exception as e: self.logger.debug('fix_vrf_slave_ipv6_route_metric: failed: %s' % e) - # Disable IPv6 duplicate address detection on VRR interfaces - sysctl_prefix = "net.ipv6.conf.%s" % macvlan_ifname - - try: - syskey = "%s.%s" % (sysctl_prefix, "enhanced_dad") - if self.sysctl_get(syskey) != "0": - self.sysctl_set(syskey, "0") - except Exception as e: - self.logger.info("sysctl failure: operation not supported: %s" % str(e)) - - for key, sysval in { - "accept_dad": "0", - "dad_transmits": "0" - }.items(): - syskey = "%s.%s" % (sysctl_prefix, key) - if self.sysctl_get(syskey) != sysval: - self.sysctl_set(syskey, sysval) - self.iproute2.batch_commit() return hw_address_list From c1ae42cd62c2f119405d67d14e390b0900018b73 Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Sat, 27 Apr 2024 00:46:01 +0200 Subject: [PATCH 010/132] addons: bond: don't speed check downed slave if the interface is down we won't be able to get the speed skip the speed check Signed-off-by: Julien Fortin --- ifupdown2/addons/bond.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/ifupdown2/addons/bond.py b/ifupdown2/addons/bond.py index f2d38de5..4e77baaa 100644 --- a/ifupdown2/addons/bond.py +++ b/ifupdown2/addons/bond.py @@ -363,15 +363,24 @@ def compare_bond_and_slave_speed(self, bond_ifaceobj, slave_ifname, slave_speed) if self.current_bond_speed != slave_speed: self.log_error( "%s: ignoring device due to device's speed (%s) mismatching bond (%s) speed (%s)" - % (slave_ifname, slave_speed, bond_ifaceobj.name, self.current_bond_speed), - ifaceobj=bond_ifaceobj + % (slave_ifname, slave_speed, bond_ifaceobj.name, self.current_bond_speed) ) - def valid_slave_speed(self, ifaceobj, bond_slaves, slave): + def valid_slave_speed(self, ifaceobj, bond_slaves, slave, ifaceobj_getfunc): if not slave.startswith("swp"): # lazy optimization: only check "swp" interfaces return True + try: + if ifaceobj_getfunc(slave)[0].link_privflags & ifaceLinkPrivFlags.KEEP_LINK_DOWN: + return True + except: + pass + + if not self.sysfs.link_is_up(slave): + self.logger.debug(f"{slave}: bond-slave is down - skipping speed validation") + return True + if self.current_bond_speed < 0: self.current_bond_speed = self.get_bond_speed(bond_slaves) @@ -447,7 +456,7 @@ def _add_slaves(self, ifaceobj, runningslaves, ifaceobj_getfunc=None): try: # making sure the slave-to-be has the right speed - if not self.valid_slave_speed(ifaceobj, runningslaves, slave): + if not self.valid_slave_speed(ifaceobj, runningslaves, slave, ifaceobj_getfunc): continue except Exception as e: self.logger.debug("%s: bond-slave (%s) speed validation failed: %s" % (ifaceobj.name, slave, str(e))) From 621f53933b40acfd08e3c8ff92b5311d0cc3a8e3 Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Thu, 19 Sep 2024 13:03:47 +0200 Subject: [PATCH 011/132] addons: bond: removing bond-slave speed check The bond slave speed check has been causing issues over the past few months It is not critical to have it so I prefer to remove it for now. Signed-off-by: Julien Fortin --- ifupdown2/addons/bond.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/ifupdown2/addons/bond.py b/ifupdown2/addons/bond.py index 4e77baaa..deb88def 100644 --- a/ifupdown2/addons/bond.py +++ b/ifupdown2/addons/bond.py @@ -454,13 +454,6 @@ def _add_slaves(self, ifaceobj, runningslaves, ifaceobj_getfunc=None): raise_error=False) continue - try: - # making sure the slave-to-be has the right speed - if not self.valid_slave_speed(ifaceobj, runningslaves, slave, ifaceobj_getfunc): - continue - except Exception as e: - self.logger.debug("%s: bond-slave (%s) speed validation failed: %s" % (ifaceobj.name, slave, str(e))) - if not self.slave_has_no_subinterface(ifaceobj, slave, ifaceobj_getfunc): continue From 29807929cce23e0c11875c9efddea45b187e3c86 Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Tue, 28 Nov 2023 23:17:18 +0100 Subject: [PATCH 012/132] addons: bond: change bond mac inheritance code (any slave mac is fine) Signed-off-by: Julien Fortin --- ifupdown2/addons/bond.py | 90 +++++++++++++++++++++------------------- 1 file changed, 48 insertions(+), 42 deletions(-) diff --git a/ifupdown2/addons/bond.py b/ifupdown2/addons/bond.py index deb88def..b691ef44 100644 --- a/ifupdown2/addons/bond.py +++ b/ifupdown2/addons/bond.py @@ -901,51 +901,57 @@ def _up(self, ifaceobj, ifaceobj_getfunc=None): bond_slaves, ifaceobj_getfunc, ) - - if not self.bond_mac_mgmt or not link_exists or ifaceobj.get_attr_value_first("hwaddress"): - return - - # check if the bond mac address is correctly inherited from it's - # first slave. There's a case where that might not be happening: - # $ ip link show swp1 | grep ether - # link/ether 08:00:27:04:d8:01 brd ff:ff:ff:ff:ff:ff - # $ ip link show swp2 | grep ether - # link/ether 08:00:27:04:d8:02 brd ff:ff:ff:ff:ff:ff - # $ ip link add dev bond0 type bond - # $ ip link set dev swp1 master bond0 - # $ ip link set dev swp2 master bond0 - # $ ip link show bond0 | grep ether - # link/ether 08:00:27:04:d8:01 brd ff:ff:ff:ff:ff:ff - # $ ip link add dev bond1 type bond - # $ ip link set dev swp1 master bond1 - # $ ip link show swp1 | grep ether - # link/ether 08:00:27:04:d8:01 brd ff:ff:ff:ff:ff:ff - # $ ip link show swp2 | grep ether - # link/ether 08:00:27:04:d8:01 brd ff:ff:ff:ff:ff:ff - # $ ip link show bond0 | grep ether - # link/ether 08:00:27:04:d8:01 brd ff:ff:ff:ff:ff:ff - # $ ip link show bond1 | grep ether - # link/ether 08:00:27:04:d8:01 brd ff:ff:ff:ff:ff:ff - # $ - # ifupdown2 will automatically correct and fix this unexpected behavior - bond_mac = self.cache.get_link_address(ifaceobj.name) - - if bond_slaves: - first_slave_ifname = bond_slaves[0] - first_slave_mac = self.cache.get_link_info_slave_data_attribute( - first_slave_ifname, - Link.IFLA_BOND_SLAVE_PERM_HWADDR - ) - - if first_slave_mac and bond_mac != first_slave_mac: - self.logger.info( - "%s: invalid bond mac detected - resetting to %s's mac (%s)" - % (ifaceobj.name, first_slave_ifname, first_slave_mac) - ) - self.netlink.link_set_address(ifaceobj.name, first_slave_mac, utils.mac_str_to_int(first_slave_mac)) + self.set_bond_mac(link_exists, ifaceobj, bond_slaves) except Exception as e: self.log_error(str(e), ifaceobj) + def set_bond_mac(self, link_exists, ifaceobj, bond_slaves): + if not self.bond_mac_mgmt or not link_exists or ifaceobj.get_attr_value_first("hwaddress"): + return + + # check if the bond mac address is correctly inherited from it's + # first slave. There's a case where that might not be happening: + # $ ip link show swp1 | grep ether + # link/ether 08:00:27:04:d8:01 brd ff:ff:ff:ff:ff:ff + # $ ip link show swp2 | grep ether + # link/ether 08:00:27:04:d8:02 brd ff:ff:ff:ff:ff:ff + # $ ip link add dev bond0 type bond + # $ ip link set dev swp1 master bond0 + # $ ip link set dev swp2 master bond0 + # $ ip link show bond0 | grep ether + # link/ether 08:00:27:04:d8:01 brd ff:ff:ff:ff:ff:ff + # $ ip link add dev bond1 type bond + # $ ip link set dev swp1 master bond1 + # $ ip link show swp1 | grep ether + # link/ether 08:00:27:04:d8:01 brd ff:ff:ff:ff:ff:ff + # $ ip link show swp2 | grep ether + # link/ether 08:00:27:04:d8:01 brd ff:ff:ff:ff:ff:ff + # $ ip link show bond0 | grep ether + # link/ether 08:00:27:04:d8:01 brd ff:ff:ff:ff:ff:ff + # $ ip link show bond1 | grep ether + # link/ether 08:00:27:04:d8:01 brd ff:ff:ff:ff:ff:ff + # $ + # ifupdown2 will automatically correct and fix this unexpected behavior + # Although if the bond's mac belongs to any of its slave we won't update it + bond_mac = self.cache.get_link_address(ifaceobj.name) + + # Get the list of slave macs + bond_slave_macs = map( + lambda slave_ifname: self.cache.get_link_info_slave_data_attribute(slave_ifname, Link.IFLA_BOND_SLAVE_PERM_HWADDR), + bond_slaves + ) + + if bond_slaves and bond_mac not in bond_slave_macs: + first_slave_ifname = bond_slaves[0] + first_slave_mac = list(bond_slave_macs)[0] + + if first_slave_mac and bond_mac != first_slave_mac: + self.logger.info( + "%s: invalid bond mac detected - resetting to %s's mac (%s)" + % (ifaceobj.name, first_slave_ifname, first_slave_mac) + ) + self.netlink.link_set_address(ifaceobj.name, first_slave_mac, utils.mac_str_to_int(first_slave_mac)) + def _down(self, ifaceobj, ifaceobj_getfunc=None): bond_slaves = self.cache.get_slaves(ifaceobj.name) From 97fb31ce1af89079834d441b59a3d7be865158c4 Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Fri, 5 Jan 2024 15:57:26 +0100 Subject: [PATCH 013/132] addons: bond: fix 'list index out of range' error when removing first bond slave Signed-off-by: Julien Fortin --- ifupdown2/addons/bond.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/ifupdown2/addons/bond.py b/ifupdown2/addons/bond.py index b691ef44..2e6edf2d 100644 --- a/ifupdown2/addons/bond.py +++ b/ifupdown2/addons/bond.py @@ -936,14 +936,18 @@ def set_bond_mac(self, link_exists, ifaceobj, bond_slaves): bond_mac = self.cache.get_link_address(ifaceobj.name) # Get the list of slave macs - bond_slave_macs = map( - lambda slave_ifname: self.cache.get_link_info_slave_data_attribute(slave_ifname, Link.IFLA_BOND_SLAVE_PERM_HWADDR), + bond_slave_macs = list(map( + lambda slave_ifname: self.cache.get_link_info_slave_data_attribute( + slave_ifname, + Link.IFLA_BOND_SLAVE_PERM_HWADDR, + default=list() + ), bond_slaves - ) + )) - if bond_slaves and bond_mac not in bond_slave_macs: + if bond_slaves and bond_slave_macs and bond_mac not in bond_slave_macs: first_slave_ifname = bond_slaves[0] - first_slave_mac = list(bond_slave_macs)[0] + first_slave_mac = bond_slave_macs[0] if first_slave_mac and bond_mac != first_slave_mac: self.logger.info( From aae38cdaebba9106f63b94109509e4c8d865594d Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Tue, 31 May 2022 00:51:49 +0200 Subject: [PATCH 014/132] SONAR: bridge: Rename class "bridgeFlags" to match the regular expression ^_?([A-Z_][a-zA-Z0-9]*|[a-z_][a-z0-9_]*)$. Signed-off-by: Julien Fortin --- ifupdown2/addons/bridge.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/ifupdown2/addons/bridge.py b/ifupdown2/addons/bridge.py index 4d6c091c..be8aac21 100644 --- a/ifupdown2/addons/bridge.py +++ b/ifupdown2/addons/bridge.py @@ -40,7 +40,7 @@ from ifupdownaddons.modulebase import moduleBase -class bridgeFlags: +class BridgeFlags: PORT_PROCESSED = 0x1 PORT_PROCESSED_OVERRIDE = 0x2 @@ -1635,7 +1635,7 @@ def up_apply_bridge_settings(self, ifaceobj, link_just_created, bridge_vlan_awar self.logger.info('%s: stp state reset, reapplying port settings' % ifname) ifaceobj.module_flags[ifaceobj.name] = \ ifaceobj.module_flags.setdefault(self.name, 0) | \ - bridgeFlags.PORT_PROCESSED_OVERRIDE + BridgeFlags.PORT_PROCESSED_OVERRIDE else: # If stp not specified and running stp state on, set it to off if self._is_running_stp_state_on(ifname): @@ -1955,7 +1955,7 @@ def _apply_bridge_port_settings_all(self, ifaceobj, ifaceobj_getfunc, bridge_vla bridge_pvid = None if (ifaceobj.module_flags.get(self.name, 0x0) & - bridgeFlags.PORT_PROCESSED_OVERRIDE): + BridgeFlags.PORT_PROCESSED_OVERRIDE): port_processed_override = True else: port_processed_override = False @@ -1982,7 +1982,7 @@ def _apply_bridge_port_settings_all(self, ifaceobj, ifaceobj_getfunc, bridge_vla # and there is no override on port_processed if (not port_processed_override and (bportifaceobj.module_flags.get(self.name,0x0) & - bridgeFlags.PORT_PROCESSED)): + BridgeFlags.PORT_PROCESSED)): continue try: # Add attributes specific to the vlan aware bridge @@ -2063,7 +2063,7 @@ def up_bridge_port(self, ifaceobj, ifaceobj_getfunc): ifaceobj_getfunc=ifaceobj_getfunc, bridge_vlan_aware=vlan_aware_bridge) - ifaceobj.module_flags[self.name] = ifaceobj.module_flags.setdefault(self.name, 0) | bridgeFlags.PORT_PROCESSED + ifaceobj.module_flags[self.name] = ifaceobj.module_flags.setdefault(self.name, 0) | BridgeFlags.PORT_PROCESSED def up_check_bridge_vlan_aware(self, ifaceobj, ifaceobj_getfunc, link_just_created): if ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_VLAN_AWARE: @@ -2071,7 +2071,7 @@ def up_check_bridge_vlan_aware(self, ifaceobj, ifaceobj_getfunc, link_just_creat return False if not link_just_created and not self.cache.bridge_is_vlan_aware(ifaceobj.name): # if bridge-vlan-aware was added on a existing old-bridge, we need to reprocess all ports - ifaceobj.module_flags[self.name] = ifaceobj.module_flags.setdefault(self.name, 0) | bridgeFlags.PORT_PROCESSED_OVERRIDE + ifaceobj.module_flags[self.name] = ifaceobj.module_flags.setdefault(self.name, 0) | BridgeFlags.PORT_PROCESSED_OVERRIDE return True return False @@ -2171,7 +2171,7 @@ def up_apply_brports_attributes(self, ifaceobj, ifaceobj_getfunc, bridge_vlan_aw cache_brports_ifla_info_slave_data = {} - port_processed_override = ifaceobj.module_flags.get(self.name, 0x0) & bridgeFlags.PORT_PROCESSED_OVERRIDE + port_processed_override = ifaceobj.module_flags.get(self.name, 0x0) & BridgeFlags.PORT_PROCESSED_OVERRIDE running_brports = self.cache.get_slaves(ifname) @@ -2198,7 +2198,7 @@ def up_apply_brports_attributes(self, ifaceobj, ifaceobj_getfunc, bridge_vlan_aw if port not in newly_enslaved_ports: # check if brport was already processed for brportifaceobj in brport_list: - if not port_processed_override and brportifaceobj.module_flags.get(self.name, 0x0) & bridgeFlags.PORT_PROCESSED: + if not port_processed_override and brportifaceobj.module_flags.get(self.name, 0x0) & BridgeFlags.PORT_PROCESSED: # skip port if already processed (probably by `up_bridge_port`) port_already_processed = True self.logger.info("%s: port %s: already processed" % (ifname, port)) From 67944c570c7ff813d85bb54fc0f94a3b050252ee Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Tue, 23 Jan 2024 13:17:44 +0100 Subject: [PATCH 015/132] addons: bridge: remove deprecated bridge-hashel default value IFLA_BR_MCAST_HASH_ELASTICITY: Set multicast database hash elasticity, It is the maximum chain length in the multicast hash table. This attribute is deprecated and the value is always 16. Signed-off-by: Julien Fortin --- ifupdown2/addons/bridge.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/ifupdown2/addons/bridge.py b/ifupdown2/addons/bridge.py index be8aac21..f3ccf0d1 100644 --- a/ifupdown2/addons/bridge.py +++ b/ifupdown2/addons/bridge.py @@ -179,10 +179,12 @@ class bridge(Bridge, moduleBase): "example": ["bridge-mcquerier no"] }, "bridge-hashel": { - "help": "set hash elasticity", - "validrange": ["0", "4096"], - "default": "4", - "example": ["bridge-hashel 4096"] + "help": "Set multicast database hash elasticity, It is the maximum chain length in the " + "multicast hash table. This attribute is deprecated and the value is always 16.", + "validval": ["16"], + "default": "16", + "example": ["bridge-hashel 16"], + "deprecated": True }, "bridge-hashmax": { "help": "set hash max", From d0ca06206753dd5ca0b03055390dfeef80419c12 Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Tue, 31 May 2022 01:10:44 +0200 Subject: [PATCH 016/132] SONAR: bridge: Merge if statements with the enclosing ones Signed-off-by: Julien Fortin --- ifupdown2/addons/bridge.py | 117 ++++++++++++++++++------------------- 1 file changed, 56 insertions(+), 61 deletions(-) diff --git a/ifupdown2/addons/bridge.py b/ifupdown2/addons/bridge.py index f3ccf0d1..9cdd734a 100644 --- a/ifupdown2/addons/bridge.py +++ b/ifupdown2/addons/bridge.py @@ -891,9 +891,8 @@ def _query_check_l2protocol_tunnel_all(ifla_brport_group_mask, ifla_brport_group def syntax_check(self, ifaceobj, ifaceobj_getfunc): retval = self.check_bridge_vlan_aware_port(ifaceobj, ifaceobj_getfunc) - if ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT: - if not self.check_bridge_port_vid_attrs(ifaceobj): - retval = False + if ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT and not self.check_bridge_port_vid_attrs(ifaceobj): + retval = False c1 = self.syntax_check_vxlan_in_vlan_aware_br(ifaceobj, ifaceobj_getfunc) c2 = self.syntax_check_bridge_allow_multiple_vlans(ifaceobj, ifaceobj_getfunc) c3 = self.syntax_check_learning_l2_vni_evpn(ifaceobj) @@ -975,9 +974,8 @@ def syntax_check_bridge_arp_vni_vlan(self, ifaceobj, ifaceobj_getfunc): for obj in ifaceobj_getfunc(ifaceobj.upperifaces[0]) or []: for upper_ifname in obj.upperifaces or []: for upper_obj in ifaceobj_getfunc(upper_ifname) or []: - if upper_obj.link_kind & ifaceLinkKind.VLAN: - if str(self._get_vlan_id(upper_obj)) == bridge_access: - return True + if upper_obj.link_kind & ifaceLinkKind.VLAN and str(self._get_vlan_id(upper_obj)) == bridge_access: + return True self.logger.warning( "%s: ARP suppression configured on %s and associated vlan %s not configured. " @@ -990,15 +988,17 @@ def syntax_check_bridge_arp_vni_vlan(self, ifaceobj, ifaceobj_getfunc): def syntax_check_learning_l2_vni_evpn(self, ifaceobj): result = True - if ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT and ifaceobj.link_kind & ifaceLinkKind.VXLAN: - if utils.get_boolean_from_string(ifaceobj.get_attr_value_first("bridge-learning")) and \ - (not ifaceobj.get_attr_value_first("vxlan-remoteip") and not ifaceobj.get_attr_value_first("vxlan-remoteip-map")): - self.logger.warning( - "%s: possible mis-configuration detected: l2-vni configured with bridge-learning ON " - "while EVPN is also configured - these two parameters conflict with each other." - % ifaceobj.name - ) - result = False + if ( + ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT and ifaceobj.link_kind & ifaceLinkKind.VXLAN + and utils.get_boolean_from_string(ifaceobj.get_attr_value_first("bridge-learning")) + and not ifaceobj.get_attr_value_first("vxlan-remoteip") and not ifaceobj.get_attr_value_first("vxlan-remoteip-map") + ): + self.logger.warning( + "%s: possible mis-configuration detected: l2-vni configured with bridge-learning ON " + "while EVPN is also configured - these two parameters conflict with each other." + % ifaceobj.name + ) + result = False return result def syntax_check_bridge_allow_multiple_vlans(self, ifaceobj, ifaceobj_getfunc): @@ -1225,11 +1225,12 @@ def _pretty_print_add_ports_error(self, errstr, bridgeifaceobj, bridgeports): vlanid = None for bport in bridgeports: currvlanid = self._get_vlan_id_from_ifacename(bport) - if vlanid: - if currvlanid != vlanid: - self.log_error('%s: ' %bridgeifaceobj.name + - 'net.bridge.bridge-allow-multiple-vlans not set, multiple vlans not allowed', bridgeifaceobj) - break + if vlanid and currvlanid != vlanid: + self.log_error( + "%s: net.bridge.bridge-allow-multiple-vlans not set, multiple vlans not allowed" + % bridgeifaceobj.name, bridgeifaceobj + ) + break if currvlanid: vlanid = currvlanid except Exception as e: @@ -1490,9 +1491,8 @@ def get_bridge_mcsnoop_value(self, ifaceobj): if mcsnoop: return mcsnoop - if ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_VXLAN: - if self._vxlan_bridge_default_igmp_snooping is not None: - return self._vxlan_bridge_default_igmp_snooping + if ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_VXLAN and self._vxlan_bridge_default_igmp_snooping is not None: + return self._vxlan_bridge_default_igmp_snooping return self.get_attr_default_value("bridge-mcsnoop") @@ -1769,9 +1769,8 @@ def _apply_bridge_vids_and_pvid(self, bportifaceobj, ifaceobj_getfunc, vids, pvi (vids_to_del, vids_to_add) = \ utils.diff_ids(vids_to_add, running_vids) - if running_pvid: - if running_pvid != pvid_int and running_pvid != 0: - pvid_to_del = running_pvid + if running_pvid and running_pvid != pvid_int and running_pvid != 0: + pvid_to_del = running_pvid if (pvid_to_del and (pvid_to_del in vids_int) and (pvid_to_del not in vids_to_add)): @@ -2248,8 +2247,8 @@ def up_apply_brports_attributes(self, ifaceobj, ifaceobj_getfunc, bridge_vlan_aw attr=attr_name ) - if br_config: - #if bridge_vlan_aware: + if br_config and "=" in br_config: + # if bridge_vlan_aware: # self.logger.info('%s: is a vlan-aware bridge, "%s %s" ' # 'should be configured under the ports' # % (ifname, attr_name, br_config)) @@ -2259,12 +2258,11 @@ def up_apply_brports_attributes(self, ifaceobj, ifaceobj_getfunc, bridge_vlan_aw # example: # bridge-portprios swp1=5 swp2=32 # swp1: { bridge-portprios: 5 } swp2: { bridge-portprios: 32} - if '=' in br_config: - try: - br_config = self.parse_interface_list_value(br_config) - except Exception: - self.log_error('error while parsing \'%s %s\'' % (attr_name, br_config)) - continue + try: + br_config = self.parse_interface_list_value(br_config) + except Exception: + self.log_error("error while parsing '%s %s'" % (attr_name, br_config)) + continue for brport_ifaceobj in list(brport_ifaceobj_dict.values()): brport_config = brport_ifaceobj.get_attr_value_first(attr_name) @@ -2380,21 +2378,21 @@ def up_apply_brports_attributes(self, ifaceobj, ifaceobj_getfunc, bridge_vlan_aw except Exception as e: self.logger.debug('%s: %s: peerlink check: %s' % (ifname, brport_name, str(e))) - if nl_attr == Link.IFLA_BRPORT_MULTICAST_ROUTER: - - if (brport_ifaceobj.link_kind & ifaceLinkKind.VXLAN - and brport_ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT) \ - and ( - ( - self.vxlan_bridge_igmp_snooping_enable_port_mcrouter and utils.get_boolean_from_string( - self.get_bridge_mcsnoop_value(ifaceobj)) - ) or cached_bridge_mcsnoop - ): - # if policy "vxlan_bridge_igmp_snooping_enable_port_mcrouter" is on and mcsnoop is - # on (or mcsnoop is already enabled on the bridge, keep 'bridge-portmcrouter 2' - # on vxlan ports (if not set by the user) - if cached_value == 2: - continue + if ( + nl_attr == Link.IFLA_BRPORT_MULTICAST_ROUTER + and cached_value == 2 + and brport_ifaceobj.link_kind & ifaceLinkKind.VXLAN + and brport_ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT) \ + and ( + ( + self.vxlan_bridge_igmp_snooping_enable_port_mcrouter and utils.get_boolean_from_string( + self.get_bridge_mcsnoop_value(ifaceobj)) + ) or cached_bridge_mcsnoop + ): + # if policy "vxlan_bridge_igmp_snooping_enable_port_mcrouter" is on and mcsnoop is + # on (or mcsnoop is already enabled on the bridge, keep 'bridge-portmcrouter 2' + # on vxlan ports (if not set by the user) + continue if default_netlink != cached_value: self.logger.info('%s: %s: %s: no configuration detected, resetting to default %s' @@ -3996,10 +3994,11 @@ def _query_check_l2protocol_tunnel(self, brport_name, user_config_l2protocol_tun for protocol in re.split(',|\s*', user_config_l2protocol_tunnel): callback = self.query_check_l2protocol_tunnel_callback.get(protocol) - if callable(callback): - if not callback(cached_ifla_brport_group_mask, cached_ifla_brport_group_maskhi): - raise Exception('%s: bridge-l2protocol-tunnel: protocol \'%s\' not present (cached value: %d | %d)' - % (brport_name, protocol, cached_ifla_brport_group_mask, cached_ifla_brport_group_maskhi)) + if callable(callback) and not callback(cached_ifla_brport_group_mask, cached_ifla_brport_group_maskhi): + raise Exception( + "%s: bridge-l2protocol-tunnel: protocol '%s' not present (cached value: %d | %d)" + % (brport_name, protocol, cached_ifla_brport_group_mask, cached_ifla_brport_group_maskhi) + ) def _query_running_bridge_l2protocol_tunnel(self, brport_name, brport_ifaceobj=None, bridge_ifaceobj=None): cached_ifla_brport_group_maskhi = self.cache.get_link_info_slave_data_attribute(brport_name, Link.IFLA_BRPORT_GROUP_FWD_MASKHI) @@ -4090,14 +4089,10 @@ def _query_running_bridge_port(self, ifaceobjrunning, ifaceobjrunning.update_config('bridge-access', bridge_port_pvid) else: - if bridge_port_vids: - if (not bridge_vids or bridge_port_vids != bridge_vids): - ifaceobjrunning.update_config('bridge-vids', - ' '.join(bridge_port_vids)) - if bridge_port_pvid and bridge_port_pvid != '1': - if (not bridge_pvid or (bridge_port_pvid != bridge_pvid)): - ifaceobjrunning.update_config('bridge-pvid', - bridge_port_pvid) + if bridge_port_vids and (not bridge_vids or bridge_port_vids != bridge_vids): + ifaceobjrunning.update_config("bridge-vids", " ".join(bridge_port_vids)) + if bridge_port_pvid and bridge_port_pvid != "1" and (not bridge_pvid or (bridge_port_pvid != bridge_pvid)): + ifaceobjrunning.update_config("bridge-pvid", bridge_port_pvid) v = utils.get_onff_from_onezero(self.cache.get_brport_learning(ifaceobjrunning.name)) if v and v != self.get_mod_subattr('bridge-learning', 'default'): From 353dcfbe34f290f4bc784f12b65f433e8536f1cf Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Tue, 31 May 2022 01:24:07 +0200 Subject: [PATCH 017/132] SONAR: dhcp: rename DHCLIENT_RETRY_ON_FAILURE constant to avoid naming conflicts Signed-off-by: Julien Fortin --- ifupdown2/addons/dhcp.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ifupdown2/addons/dhcp.py b/ifupdown2/addons/dhcp.py index a5bf8605..2ffb28f4 100644 --- a/ifupdown2/addons/dhcp.py +++ b/ifupdown2/addons/dhcp.py @@ -41,7 +41,7 @@ class dhcp(Addon, moduleBase): # by default we won't perform any dhcp retry # this can be changed by setting the module global # policy: dhclient_retry_on_failure - DHCLIENT_RETRY_ON_FAILURE = 0 + DHCLIENT_DEFAULT_RETRY_ON_FAILURE = 0 def __init__(self, *args, **kargs): Addon.__init__(self) @@ -62,7 +62,7 @@ def __init__(self, *args, **kargs): ) ) except Exception: - self.dhclient_retry_on_failure = self.DHCLIENT_RETRY_ON_FAILURE + self.dhclient_retry_on_failure = self.DHCLIENT_DEFAULT_RETRY_ON_FAILURE if self.dhclient_retry_on_failure < 0: self.dhclient_retry_on_failure = 0 From 08650b1319932e525fa166103a426c4382a64217 Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Thu, 16 Jun 2022 18:01:26 +0200 Subject: [PATCH 018/132] addons: dhcp: new policy: dhclient_no_wait_on_reload If the policy is enabled (true/false) dhclient wont wait (-nw) (except in case of networking restart or reboot) Signed-off-by: Julien Fortin --- ifupdown2/addons/dhcp.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/ifupdown2/addons/dhcp.py b/ifupdown2/addons/dhcp.py index 2ffb28f4..b1fb62f1 100644 --- a/ifupdown2/addons/dhcp.py +++ b/ifupdown2/addons/dhcp.py @@ -69,6 +69,13 @@ def __init__(self, *args, **kargs): self.logger.debug("dhclient: dhclient_retry_on_failure set to %s" % self.dhclient_retry_on_failure) + self.dhclient_no_wait_on_reload = utils.get_boolean_from_string( + policymanager.policymanager_api.get_module_globals( + module_name=self.__class__.__name__, + attr="dhclient_no_wait_on_reload" + ), + ) + def syntax_check(self, ifaceobj, ifaceobj_getfunc): return self.is_dhcp_allowed_on(ifaceobj, syntax_check=True) @@ -168,6 +175,10 @@ def _up(self, ifaceobj): dhclient_cmd_prefix = '%s %s' %(self.vrf_exec_cmd_prefix, 'default') self.logger.info('detected mgmt vrf context starting dhclient in default vrf context') + if not ifupdownflags.flags.PERFMODE and self.dhclient_no_wait_on_reload: + self.logger.info("%s: dhclient won't wait (-nw): policy dhclient_no_wait_on_reload=true" % (ifaceobj.name)) + wait = False + if 'inet' in ifaceobj.addr_family: if dhclient4_running: self.logger.info('dhclient4 already running on %s. ' From b174e209ddbd526939b1d05ed781b11179e8b9a2 Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Tue, 31 May 2022 01:26:32 +0200 Subject: [PATCH 019/132] SONAR: dhcp: Rename this parameter "dhclientX_running" to match the regular expression ^[_a-z][a-z0-9_]*$. Signed-off-by: Julien Fortin --- ifupdown2/addons/dhcp.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ifupdown2/addons/dhcp.py b/ifupdown2/addons/dhcp.py index b1fb62f1..ef3120cf 100644 --- a/ifupdown2/addons/dhcp.py +++ b/ifupdown2/addons/dhcp.py @@ -238,10 +238,10 @@ def _up(self, ifaceobj): self.logger.error("%s: %s" % (ifaceobj.name, str(e))) ifaceobj.set_status(ifaceStatus.ERROR) - def _down_stale_dhcp_config(self, ifaceobj, family, dhclientX_running): + def _down_stale_dhcp_config(self, ifaceobj, family, dhclient_running): addr_family = ifaceobj.addr_family try: - if not family in ifaceobj.addr_family and dhclientX_running: + if not family in ifaceobj.addr_family and dhclient_running: ifaceobj.addr_family = [family] self._dhcp_down(ifaceobj) except Exception: From d8b9c2f937bd3f7acdc438af31066709c8fdf1c3 Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Wed, 15 Feb 2023 14:02:29 +0100 Subject: [PATCH 020/132] sonarlink: use the opposite operation 'not in' Issue: 3364956 Signed-off-by: Julien Fortin --- ifupdown2/addons/bridge.py | 6 +++--- ifupdown2/addons/dhcp.py | 2 +- ifupdown2/ifupdown/ifupdownmain.py | 4 ++-- ifupdown2/ifupdownaddons/modulebase.py | 2 +- ifupdown2/ifupdownaddons/mstpctlutil.py | 2 +- ifupdown2/lib/nlcache.py | 2 +- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/ifupdown2/addons/bridge.py b/ifupdown2/addons/bridge.py index 9cdd734a..797995a7 100644 --- a/ifupdown2/addons/bridge.py +++ b/ifupdown2/addons/bridge.py @@ -929,12 +929,12 @@ def err(): err() return False else: - if not bridge_name in self.bridge_vni_per_svi: + if bridge_name not in self.bridge_vni_per_svi: self.bridge_vni_per_svi[bridge_name] = { svi: vni_name } - elif not svi in self.bridge_vni_per_svi[bridge_name]: + elif svi not in self.bridge_vni_per_svi[bridge_name]: self.bridge_vni_per_svi[bridge_name][svi] = vni_name else: @@ -3478,7 +3478,7 @@ def _query_check_brport_attributes_on_bridge(self, ifname, ifaceobj, ifaceobjcur for port_config in self.parse_port_list(ifname, ifaceobj.get_attr_value_first(attr)) or []: port, config = port_config.split("=") - if not port in brports_info_slave_data: + if port not in brports_info_slave_data: info_slave_data = brports_info_slave_data[port] = self.cache.get_link_info_slave_data(port) else: info_slave_data = brports_info_slave_data[port] diff --git a/ifupdown2/addons/dhcp.py b/ifupdown2/addons/dhcp.py index ef3120cf..f02203cd 100644 --- a/ifupdown2/addons/dhcp.py +++ b/ifupdown2/addons/dhcp.py @@ -241,7 +241,7 @@ def _up(self, ifaceobj): def _down_stale_dhcp_config(self, ifaceobj, family, dhclient_running): addr_family = ifaceobj.addr_family try: - if not family in ifaceobj.addr_family and dhclient_running: + if family not in ifaceobj.addr_family and dhclient_running: ifaceobj.addr_family = [family] self._dhcp_down(ifaceobj) except Exception: diff --git a/ifupdown2/ifupdown/ifupdownmain.py b/ifupdown2/ifupdown/ifupdownmain.py index 51f54609..8f0e3803 100644 --- a/ifupdown2/ifupdown/ifupdownmain.py +++ b/ifupdown2/ifupdown/ifupdownmain.py @@ -1039,7 +1039,7 @@ def _keyword_interface_list_with_value(self, value, validvals): if iface_value[0] == 'glob' or iface_value[0] == 'regex': continue return False - if not iface_value[1] in validvals: + if iface_value[1] not in validvals: return False return True except Exception as e: @@ -1425,7 +1425,7 @@ def load_addon_modules(self, modules_dir_list): self._load_addon_modules_config() for modules_dir in modules_dir_list: - if not modules_dir in sys.path: + if modules_dir not in sys.path: sys.path.insert(1, modules_dir) try: for op, mlist in list(self.module_ops.items()): diff --git a/ifupdown2/ifupdownaddons/modulebase.py b/ifupdown2/ifupdownaddons/modulebase.py index 2ec8b7e0..3f2b94d7 100644 --- a/ifupdown2/ifupdownaddons/modulebase.py +++ b/ifupdown2/ifupdownaddons/modulebase.py @@ -69,7 +69,7 @@ def merge_modinfo_with_policy_files(self): # first check module_defaults for key, value in list(policymanager.policymanager_api.get_module_defaults(self.modulename).items()): - if not key in attrs: + if key not in attrs: self.logger.warning('%s: %s: %s' % (self.modulename, key, error_msg)) continue attrs[key]['default'] = value diff --git a/ifupdown2/ifupdownaddons/mstpctlutil.py b/ifupdown2/ifupdownaddons/mstpctlutil.py index 0e0026bd..14142583 100644 --- a/ifupdown2/ifupdownaddons/mstpctlutil.py +++ b/ifupdown2/ifupdownaddons/mstpctlutil.py @@ -192,7 +192,7 @@ def update_bridge_port_cache(self, bridgename, portname, attrname, value): attrs = self.get_bridge_ports_attrs(bridgename) if not attrs: attrs = {} - if not portname in attrs: + if portname not in attrs: attrs[portname] = {} attrs[portname][attrname] = value MSTPAttrsCache.set(bridgename, attrs) diff --git a/ifupdown2/lib/nlcache.py b/ifupdown2/lib/nlcache.py index bbd2a8bc..5a547e1b 100644 --- a/ifupdown2/lib/nlcache.py +++ b/ifupdown2/lib/nlcache.py @@ -1388,7 +1388,7 @@ def add_bridge_vlan(self, msg): # those notifications should be ignored. ifla_master = msg.get_attribute_value(Link.IFLA_MASTER) - if not ifla_master or not ifla_master in self._ifname_by_ifindex: + if not ifla_master or ifla_master not in self._ifname_by_ifindex: return except Exception: pass From a518621efb4a8392bfec7848ee314ae168e14d93 Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Wed, 18 Sep 2024 19:13:16 +0200 Subject: [PATCH 021/132] addons: dhcp: bring dhcp config down if link-down yes is set Default route is missing after the dhcp interface is cycled through link-down yes/no This requires a full dhcp down-up cycle as well. Signed-off-by: Julien Fortin --- ifupdown2/addons/dhcp.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ifupdown2/addons/dhcp.py b/ifupdown2/addons/dhcp.py index f02203cd..c2589f5f 100644 --- a/ifupdown2/addons/dhcp.py +++ b/ifupdown2/addons/dhcp.py @@ -150,7 +150,8 @@ def _up(self, ifaceobj): self._down_stale_dhcp_config(ifaceobj, 'inet6', dhclient6_running) if ifaceobj.link_privflags & ifaceLinkPrivFlags.KEEP_LINK_DOWN: - self.logger.info("%s: skipping dhcp configuration: link-down yes" % ifaceobj.name) + self.logger.info("%s: bringing dhcp configuration down due to: link-down yes" % ifaceobj.name) + self._dhcp_down(ifaceobj) return try: From 49945ff14bc7bf15b54573bb7bd3834a1ceb3e77 Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Mon, 30 May 2022 21:38:57 +0200 Subject: [PATCH 022/132] SONAR: Remove redundant Exception class; it derives from another which is already caught. ImportError is the parent classs of ModuleNotFoundError Signed-off-by: Julien Fortin --- ifupdown2/addons/address.py | 2 +- ifupdown2/addons/addressvirtual.py | 2 +- ifupdown2/addons/bond.py | 2 +- ifupdown2/addons/bridge.py | 2 +- ifupdown2/addons/bridgevlan.py | 2 +- ifupdown2/addons/dhcp.py | 2 +- ifupdown2/addons/ethtool.py | 2 +- ifupdown2/addons/link.py | 2 +- ifupdown2/addons/mstpctl.py | 2 +- ifupdown2/addons/tunnel.py | 2 +- ifupdown2/addons/usercmds.py | 2 +- ifupdown2/addons/vlan.py | 2 +- ifupdown2/addons/vrf.py | 2 +- ifupdown2/addons/vrrpd.py | 2 +- ifupdown2/addons/vxlan.py | 2 +- ifupdown2/ifupdown/argv.py | 2 +- ifupdown2/ifupdown/client.py | 2 +- ifupdown2/ifupdown/graph.py | 2 +- ifupdown2/ifupdown/ifupdownmain.py | 2 +- ifupdown2/ifupdown/main.py | 2 +- ifupdown2/ifupdown/networkinterfaces.py | 2 +- ifupdown2/ifupdown/scheduler.py | 2 +- ifupdown2/ifupdown/statemanager.py | 2 +- ifupdown2/ifupdown/template.py | 2 +- ifupdown2/ifupdown/utils.py | 2 +- ifupdown2/ifupdownaddons/dhclient.py | 2 +- ifupdown2/ifupdownaddons/modulebase.py | 2 +- ifupdown2/ifupdownaddons/mstpctlutil.py | 2 +- ifupdown2/ifupdownaddons/systemutils.py | 2 +- ifupdown2/ifupdownaddons/utilsbase.py | 2 +- ifupdown2/lib/addon.py | 2 +- ifupdown2/lib/base_objects.py | 2 +- ifupdown2/lib/io.py | 2 +- ifupdown2/lib/iproute2.py | 2 +- ifupdown2/lib/nlcache.py | 2 +- ifupdown2/lib/sysfs.py | 2 +- 36 files changed, 36 insertions(+), 36 deletions(-) diff --git a/ifupdown2/addons/address.py b/ifupdown2/addons/address.py index 4408a1d5..30085447 100644 --- a/ifupdown2/addons/address.py +++ b/ifupdown2/addons/address.py @@ -28,7 +28,7 @@ import ifupdown2.ifupdown.policymanager as policymanager import ifupdown2.ifupdown.ifupdownflags as ifupdownflags import ifupdown2.ifupdown.ifupdownconfig as ifupdownconfig -except (ImportError, ModuleNotFoundError): +except ImportError: from lib.addon import AddonWithIpBlackList from nlmanager.nlmanager import Link diff --git a/ifupdown2/addons/addressvirtual.py b/ifupdown2/addons/addressvirtual.py index d7f7c706..0afe9dfa 100644 --- a/ifupdown2/addons/addressvirtual.py +++ b/ifupdown2/addons/addressvirtual.py @@ -27,7 +27,7 @@ import ifupdown2.ifupdown.policymanager as policymanager import ifupdown2.ifupdown.ifupdownflags as ifupdownflags import ifupdown2.ifupdown.ifupdownconfig as ifupdownconfig -except (ImportError, ModuleNotFoundError): +except ImportError: from lib.addon import AddonWithIpBlackList from ifupdown.iface import ifaceType, ifaceLinkKind, ifaceLinkPrivFlags, ifaceStatus from ifupdown.utils import utils diff --git a/ifupdown2/addons/bond.py b/ifupdown2/addons/bond.py index 2e6edf2d..4b96fd77 100644 --- a/ifupdown2/addons/bond.py +++ b/ifupdown2/addons/bond.py @@ -25,7 +25,7 @@ import ifupdown2.ifupdown.ifupdownflags as ifupdownflags from ifupdown2.ifupdownaddons.modulebase import moduleBase -except (ImportError, ModuleNotFoundError): +except ImportError: from nlmanager.ipnetwork import IPv4Address from lib.addon import Addon from nlmanager.nlmanager import Link diff --git a/ifupdown2/addons/bridge.py b/ifupdown2/addons/bridge.py index 797995a7..6468afae 100644 --- a/ifupdown2/addons/bridge.py +++ b/ifupdown2/addons/bridge.py @@ -24,7 +24,7 @@ from ifupdown2.ifupdownaddons.cache import * from ifupdown2.ifupdownaddons.modulebase import moduleBase -except (ImportError, ModuleNotFoundError): +except ImportError: from lib.addon import Bridge import ifupdown.exceptions as exceptions diff --git a/ifupdown2/addons/bridgevlan.py b/ifupdown2/addons/bridgevlan.py index 6b75dc38..c2af5609 100644 --- a/ifupdown2/addons/bridgevlan.py +++ b/ifupdown2/addons/bridgevlan.py @@ -12,7 +12,7 @@ from ifupdown2.ifupdownaddons.modulebase import moduleBase import ifupdown2.ifupdown.ifupdownflags as ifupdownflags -except (ImportError, ModuleNotFoundError): +except ImportError: from lib.addon import Addon from ifupdown.iface import ifaceType, ifaceLinkKind, ifaceStatus diff --git a/ifupdown2/addons/dhcp.py b/ifupdown2/addons/dhcp.py index c2589f5f..dc539d36 100644 --- a/ifupdown2/addons/dhcp.py +++ b/ifupdown2/addons/dhcp.py @@ -21,7 +21,7 @@ from ifupdown2.ifupdownaddons.dhclient import dhclient from ifupdown2.ifupdownaddons.modulebase import moduleBase -except (ImportError, ModuleNotFoundError): +except ImportError: from lib.addon import Addon from lib.log import LogManager diff --git a/ifupdown2/addons/ethtool.py b/ifupdown2/addons/ethtool.py index 42118893..a2e5596a 100644 --- a/ifupdown2/addons/ethtool.py +++ b/ifupdown2/addons/ethtool.py @@ -19,7 +19,7 @@ from ifupdown2.ifupdownaddons.utilsbase import * from ifupdown2.ifupdownaddons.modulebase import moduleBase -except (ImportError, ModuleNotFoundError): +except ImportError: from lib.addon import Addon import ifupdown.ifupdownflags as ifupdownflags diff --git a/ifupdown2/addons/link.py b/ifupdown2/addons/link.py index da2a3cc0..36aeaaa2 100644 --- a/ifupdown2/addons/link.py +++ b/ifupdown2/addons/link.py @@ -18,7 +18,7 @@ import ifupdown2.ifupdown.ifupdownflags as ifupdownflags import ifupdown2.ifupdown.policymanager as policymanager -except (ImportError, ModuleNotFoundError): +except ImportError: from lib.addon import Addon from ifupdown.iface import ifaceLinkKind, ifaceLinkPrivFlags, ifaceStatus from ifupdown.utils import utils diff --git a/ifupdown2/addons/mstpctl.py b/ifupdown2/addons/mstpctl.py index ff44eab8..29005466 100644 --- a/ifupdown2/addons/mstpctl.py +++ b/ifupdown2/addons/mstpctl.py @@ -21,7 +21,7 @@ from ifupdown2.ifupdownaddons.mstpctlutil import mstpctlutil from ifupdown2.ifupdownaddons.systemutils import systemUtils from ifupdown2.ifupdown.exceptions import moduleNotSupported -except (ImportError, ModuleNotFoundError): +except ImportError: from lib.addon import Addon from ifupdown.iface import ifaceType, ifaceLinkKind, ifaceLinkPrivFlags, ifaceStatus diff --git a/ifupdown2/addons/tunnel.py b/ifupdown2/addons/tunnel.py index 3ebcf487..9fd2637c 100644 --- a/ifupdown2/addons/tunnel.py +++ b/ifupdown2/addons/tunnel.py @@ -13,7 +13,7 @@ import ifupdown2.ifupdown.ifupdownflags as ifupdownflags import ifupdown2.nlmanager.ipnetwork as ipnetwork -except (ImportError, ModuleNotFoundError): +except ImportError: from lib.addon import Addon from nlmanager.nlmanager import Link diff --git a/ifupdown2/addons/usercmds.py b/ifupdown2/addons/usercmds.py index 420f13fc..2dc5ca1d 100644 --- a/ifupdown2/addons/usercmds.py +++ b/ifupdown2/addons/usercmds.py @@ -10,7 +10,7 @@ from ifupdown2.ifupdown.utils import utils from ifupdown2.ifupdownaddons.modulebase import moduleBase -except (ImportError, ModuleNotFoundError): +except ImportError: from ifupdown.utils import utils from ifupdownaddons.modulebase import moduleBase diff --git a/ifupdown2/addons/vlan.py b/ifupdown2/addons/vlan.py index 4380dd8a..95fe457a 100644 --- a/ifupdown2/addons/vlan.py +++ b/ifupdown2/addons/vlan.py @@ -13,7 +13,7 @@ from ifupdown2.lib.exceptions import RetryCMD import ifupdown2.ifupdown.ifupdownflags as ifupdownflags import ifupdown2.ifupdown.policymanager as policymanager -except (ImportError, ModuleNotFoundError): +except ImportError: from lib.addon import Addon from ifupdown.iface import ifaceType, ifaceLinkKind, ifaceStatus from nlmanager.nlmanager import Link diff --git a/ifupdown2/addons/vrf.py b/ifupdown2/addons/vrf.py index f0ee5034..03289fd5 100644 --- a/ifupdown2/addons/vrf.py +++ b/ifupdown2/addons/vrf.py @@ -25,7 +25,7 @@ from ifupdown2.ifupdownaddons.dhclient import dhclient from ifupdown2.ifupdownaddons.utilsbase import * from ifupdown2.ifupdownaddons.modulebase import moduleBase -except (ImportError, ModuleNotFoundError): +except ImportError: from lib.addon import Addon import ifupdown.policymanager as policymanager diff --git a/ifupdown2/addons/vrrpd.py b/ifupdown2/addons/vrrpd.py index c343eafe..f4b79c52 100644 --- a/ifupdown2/addons/vrrpd.py +++ b/ifupdown2/addons/vrrpd.py @@ -16,7 +16,7 @@ from ifupdown2.ifupdown.iface import * from ifupdown2.ifupdown.utils import utils -except (ImportError, ModuleNotFoundError): +except ImportError: import ifupdown.ifupdownflags as ifupdownflags from ifupdownaddons.modulebase import moduleBase diff --git a/ifupdown2/addons/vxlan.py b/ifupdown2/addons/vxlan.py index cc8d3b3b..892af114 100644 --- a/ifupdown2/addons/vxlan.py +++ b/ifupdown2/addons/vxlan.py @@ -21,7 +21,7 @@ from ifupdown2.ifupdownaddons.cache import * from ifupdown2.ifupdownaddons.modulebase import moduleBase -except (ImportError, ModuleNotFoundError): +except ImportError: import nlmanager.ipnetwork as ipnetwork import ifupdown.policymanager as policymanager import ifupdown.ifupdownflags as ifupdownflags diff --git a/ifupdown2/ifupdown/argv.py b/ifupdown2/ifupdown/argv.py index f8fbb885..ae905099 100644 --- a/ifupdown2/ifupdown/argv.py +++ b/ifupdown2/ifupdown/argv.py @@ -12,7 +12,7 @@ try: from ifupdown2.ifupdown.utils import utils from ifupdown2.ifupdown.exceptions import ArgvParseError, ArgvParseHelp -except (ImportError, ModuleNotFoundError): +except ImportError: from ifupdown.utils import utils from ifupdown.exceptions import ArgvParseError, ArgvParseHelp diff --git a/ifupdown2/ifupdown/client.py b/ifupdown2/ifupdown/client.py index f63bdcb3..cbf51d88 100644 --- a/ifupdown2/ifupdown/client.py +++ b/ifupdown2/ifupdown/client.py @@ -44,7 +44,7 @@ from ifupdown2.lib.exceptions import ExitWithStatus, ExitWithStatusAndError from ifupdown2.ifupdown.argv import Parse -except (ImportError, ModuleNotFoundError): +except ImportError: from lib.status import Status from lib.io import SocketIO from lib.log import LogManager, root_logger diff --git a/ifupdown2/ifupdown/graph.py b/ifupdown2/ifupdown/graph.py index c26035a9..7e70342a 100644 --- a/ifupdown2/ifupdown/graph.py +++ b/ifupdown2/ifupdown/graph.py @@ -15,7 +15,7 @@ try: from ifupdown2.lib.gvgen import GvGen -except (ImportError, ModuleNotFoundError): +except ImportError: from lib.gvgen import GvGen diff --git a/ifupdown2/ifupdown/ifupdownmain.py b/ifupdown2/ifupdown/ifupdownmain.py index 8f0e3803..13ad142e 100644 --- a/ifupdown2/ifupdown/ifupdownmain.py +++ b/ifupdown2/ifupdown/ifupdownmain.py @@ -34,7 +34,7 @@ from ifupdown2.ifupdown.exceptions import * from ifupdown2.ifupdown.networkinterfaces import * from ifupdown2.ifupdown.config import ADDON_MODULES_DIR, ADDONS_CONF_PATH, IFUPDOWN2_ADDON_DROPIN_FOLDER -except (ImportError, ModuleNotFoundError): +except ImportError: import lib.nlcache as nlcache import ifupdownaddons.mstpctlutil diff --git a/ifupdown2/ifupdown/main.py b/ifupdown2/ifupdown/main.py index de6579ea..74b41653 100644 --- a/ifupdown2/ifupdown/main.py +++ b/ifupdown2/ifupdown/main.py @@ -23,7 +23,7 @@ from ifupdown2.lib.dry_run import DryRunManager from ifupdown2.lib.status import Status -except (ImportError, ModuleNotFoundError): +except ImportError: from ifupdown.argv import Parse from ifupdown.utils import utils from ifupdown.config import IFUPDOWN2_CONF_PATH diff --git a/ifupdown2/ifupdown/networkinterfaces.py b/ifupdown2/ifupdown/networkinterfaces.py index 55f93fae..26830079 100644 --- a/ifupdown2/ifupdown/networkinterfaces.py +++ b/ifupdown2/ifupdown/networkinterfaces.py @@ -18,7 +18,7 @@ from ifupdown2.ifupdown.iface import ifaceType, ifaceJsonDecoder, iface from ifupdown2.ifupdown.utils import utils from ifupdown2.ifupdown.template import templateEngine -except (ImportError, ModuleNotFoundError): +except ImportError: from ifupdown.iface import ifaceType, ifaceJsonDecoder, iface from ifupdown.utils import utils from ifupdown.template import templateEngine diff --git a/ifupdown2/ifupdown/scheduler.py b/ifupdown2/ifupdown/scheduler.py index e4d579fb..ea51be34 100644 --- a/ifupdown2/ifupdown/scheduler.py +++ b/ifupdown2/ifupdown/scheduler.py @@ -22,7 +22,7 @@ import ifupdown2.ifupdown.policymanager as policymanager import ifupdown2.ifupdown.ifupdownflags as ifupdownflags -except (ImportError, ModuleNotFoundError): +except ImportError: from ifupdown.graph import * from ifupdown.iface import ifaceType, ifaceLinkKind, ifaceStatus, ifaceState from ifupdown.utils import utils diff --git a/ifupdown2/ifupdown/statemanager.py b/ifupdown2/ifupdown/statemanager.py index 0cd58722..60494499 100644 --- a/ifupdown2/ifupdown/statemanager.py +++ b/ifupdown2/ifupdown/statemanager.py @@ -16,7 +16,7 @@ import ifupdown2.ifupdown.exceptions as exceptions import ifupdown2.ifupdown.ifupdownconfig as ifupdownConfig -except (ImportError, ModuleNotFoundError): +except ImportError: from ifupdown.iface import * import ifupdown.exceptions as exceptions diff --git a/ifupdown2/ifupdown/template.py b/ifupdown2/ifupdown/template.py index c011407c..d1844f2d 100644 --- a/ifupdown2/ifupdown/template.py +++ b/ifupdown2/ifupdown/template.py @@ -9,7 +9,7 @@ try: from ifupdown2.ifupdown.utils import * -except (ImportError, ModuleNotFoundError): +except ImportError: from ifupdown.utils import * diff --git a/ifupdown2/ifupdown/utils.py b/ifupdown2/ifupdown/utils.py index 0d0732b3..4465ce48 100644 --- a/ifupdown2/ifupdown/utils.py +++ b/ifupdown2/ifupdown/utils.py @@ -24,7 +24,7 @@ import ifupdown2.ifupdown.policymanager as policymanager import ifupdown2.ifupdown.ifupdownflags as ifupdownflags -except (ImportError, ModuleNotFoundError): +except ImportError: from ifupdown.iface import ifaceRole, ifaceLinkKind, ifaceLinkPrivFlags import ifupdown.policymanager as policymanager diff --git a/ifupdown2/ifupdownaddons/dhclient.py b/ifupdown2/ifupdownaddons/dhclient.py index c10db653..f967a8e6 100644 --- a/ifupdown2/ifupdownaddons/dhclient.py +++ b/ifupdown2/ifupdownaddons/dhclient.py @@ -10,7 +10,7 @@ try: from ifupdown2.ifupdown.utils import utils from ifupdown2.ifupdownaddons.utilsbase import * -except (ImportError, ModuleNotFoundError): +except ImportError: from ifupdown.utils import utils from ifupdownaddons.utilsbase import * diff --git a/ifupdown2/ifupdownaddons/modulebase.py b/ifupdown2/ifupdownaddons/modulebase.py index 3f2b94d7..36c7ff5d 100644 --- a/ifupdown2/ifupdownaddons/modulebase.py +++ b/ifupdown2/ifupdownaddons/modulebase.py @@ -17,7 +17,7 @@ import ifupdown2.ifupdown.exceptions as exceptions import ifupdown2.ifupdown.policymanager as policymanager import ifupdown2.ifupdown.ifupdownflags as ifupdownflags -except (ImportError, ModuleNotFoundError): +except ImportError: from ifupdown.iface import ifaceStatus from ifupdown.utils import utils diff --git a/ifupdown2/ifupdownaddons/mstpctlutil.py b/ifupdown2/ifupdownaddons/mstpctlutil.py index 14142583..5c27ff73 100644 --- a/ifupdown2/ifupdownaddons/mstpctlutil.py +++ b/ifupdown2/ifupdownaddons/mstpctlutil.py @@ -10,7 +10,7 @@ from ifupdown2.ifupdownaddons.cache import * from ifupdown2.ifupdownaddons.utilsbase import * -except (ImportError, ModuleNotFoundError): +except ImportError: from ifupdown.iface import * from ifupdown.utils import utils diff --git a/ifupdown2/ifupdownaddons/systemutils.py b/ifupdown2/ifupdownaddons/systemutils.py index 13369eda..ceced4dd 100644 --- a/ifupdown2/ifupdownaddons/systemutils.py +++ b/ifupdown2/ifupdownaddons/systemutils.py @@ -9,7 +9,7 @@ try: from ifupdown2.ifupdown.utils import utils from ifupdown2.ifupdownaddons.utilsbase import * -except (ImportError, ModuleNotFoundError): +except ImportError: from ifupdown.utils import utils from ifupdownaddons.utilsbase import * diff --git a/ifupdown2/ifupdownaddons/utilsbase.py b/ifupdown2/ifupdownaddons/utilsbase.py index 2467b89e..8ac3c4a8 100644 --- a/ifupdown2/ifupdownaddons/utilsbase.py +++ b/ifupdown2/ifupdownaddons/utilsbase.py @@ -13,7 +13,7 @@ from ifupdown2.ifupdownaddons.cache import * import ifupdown2.ifupdown.ifupdownflags as ifupdownflags -except (ImportError, ModuleNotFoundError): +except ImportError: from ifupdown.iface import * from ifupdown.utils import utils from ifupdownaddons.cache import * diff --git a/ifupdown2/lib/addon.py b/ifupdown2/lib/addon.py index 47e42c76..3af7f8ce 100644 --- a/ifupdown2/lib/addon.py +++ b/ifupdown2/lib/addon.py @@ -35,7 +35,7 @@ import ifupdown2.ifupdown.policymanager as policymanager import ifupdown2.nlmanager.ipnetwork as ipnetwork -except (ImportError, ModuleNotFoundError): +except ImportError: from lib.io import IO from lib.sysfs import Sysfs from lib.iproute2 import IPRoute2 diff --git a/ifupdown2/lib/base_objects.py b/ifupdown2/lib/base_objects.py index 0c8313ee..786edc52 100644 --- a/ifupdown2/lib/base_objects.py +++ b/ifupdown2/lib/base_objects.py @@ -28,7 +28,7 @@ try: from ifupdown2.lib.dry_run import DryRun from ifupdown2.ifupdown.utils import utils -except (ImportError, ModuleNotFoundError): +except ImportError: from lib.dry_run import DryRun from ifupdown.utils import utils diff --git a/ifupdown2/lib/io.py b/ifupdown2/lib/io.py index 7c1823fb..d489f1c1 100644 --- a/ifupdown2/lib/io.py +++ b/ifupdown2/lib/io.py @@ -29,7 +29,7 @@ try: from ifupdown2.lib.base_objects import BaseObject -except (ImportError, ModuleNotFoundError): +except ImportError: from lib.base_objects import BaseObject diff --git a/ifupdown2/lib/iproute2.py b/ifupdown2/lib/iproute2.py index 543082e3..c9b12cc2 100644 --- a/ifupdown2/lib/iproute2.py +++ b/ifupdown2/lib/iproute2.py @@ -38,7 +38,7 @@ from ifupdown2.ifupdown.utils import utils from ifupdown2.ifupdown.iface import ifaceLinkPrivFlags from ifupdown2.nlmanager.nlpacket import Link -except (ImportError, ModuleNotFoundError): +except ImportError: from lib.sysfs import Sysfs from lib.base_objects import Cache, Requirements diff --git a/ifupdown2/lib/nlcache.py b/ifupdown2/lib/nlcache.py index 5a547e1b..60a4a54b 100644 --- a/ifupdown2/lib/nlcache.py +++ b/ifupdown2/lib/nlcache.py @@ -68,7 +68,7 @@ import ifupdown2.nlmanager.nllistener as nllistener import ifupdown2.nlmanager.nlmanager as nlmanager import ifupdown2.ifupdown.statemanager as statemanager -except (ImportError, ModuleNotFoundError): +except ImportError: from lib.sysfs import Sysfs from lib.base_objects import BaseObject from lib.exceptions import RetryCMD diff --git a/ifupdown2/lib/sysfs.py b/ifupdown2/lib/sysfs.py index dd9f3616..42577807 100644 --- a/ifupdown2/lib/sysfs.py +++ b/ifupdown2/lib/sysfs.py @@ -32,7 +32,7 @@ from ifupdown2.ifupdown.utils import utils from ifupdown2.nlmanager.nlpacket import Link -except (ImportError, ModuleNotFoundError): +except ImportError: from lib.io import IO from lib.base_objects import Requirements From a71a614aed2f741a5f3d73a2605516eddf6e6907 Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Tue, 31 May 2022 01:25:04 +0200 Subject: [PATCH 023/132] SONAR: dhcp: remove uneeded pass statement Signed-off-by: Julien Fortin --- ifupdown2/addons/dhcp.py | 1 - 1 file changed, 1 deletion(-) diff --git a/ifupdown2/addons/dhcp.py b/ifupdown2/addons/dhcp.py index dc539d36..9b2f5f9b 100644 --- a/ifupdown2/addons/dhcp.py +++ b/ifupdown2/addons/dhcp.py @@ -165,7 +165,6 @@ def _up(self, ifaceobj): timeout = int(dhcp6_ll_wait)+1 except Exception: timeout = 10 - pass dhcp6_duid = policymanager.policymanager_api.get_iface_default(module_name=self.__class__.__name__, \ ifname=ifaceobj.name, attr='dhcp6-duid') vrf = ifaceobj.get_attr_value_first('vrf') From ae4f53ac56e2815935920953e3c7605dc449678c Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Mon, 30 May 2022 22:21:35 +0200 Subject: [PATCH 024/132] SONAR: addons: address: Remove redundant return and pass statement Signed-off-by: Julien Fortin --- ifupdown2/addons/address.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/ifupdown2/addons/address.py b/ifupdown2/addons/address.py index 30085447..cb0d3832 100644 --- a/ifupdown2/addons/address.py +++ b/ifupdown2/addons/address.py @@ -1383,7 +1383,6 @@ def _down(self, ifaceobj, ifaceobj_getfunc=None): self._process_bridge(ifaceobj, False, hwaddress, None) except Exception as e: self.logger.debug('%s : %s' %(ifaceobj.name, str(e))) - pass def _get_bridge_fdbs(self, bridgename, vlan=None): fdbs = self._bridge_fdb_query_cache.get(bridgename) @@ -1448,7 +1447,6 @@ def _query_sysctl(self, ifaceobj, ifaceobjcurr): ifaceobjcurr.update_config_with_status('mpls-enable', running_mpls_enable, mpls_enable != running_mpls_enable) - return def query_check_ipv6_addrgen(self, ifaceobj, ifaceobjcurr): ipv6_addrgen = ifaceobj.get_attr_value_first('ipv6-addrgen') From 2230ff17d842b63d114d40a7ed69ac923b8b0c7a Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Mon, 30 May 2022 22:45:43 +0200 Subject: [PATCH 025/132] SONAR: addons: bond: removed uneeded "pass" statement Signed-off-by: Julien Fortin --- ifupdown2/addons/bond.py | 1 - 1 file changed, 1 deletion(-) diff --git a/ifupdown2/addons/bond.py b/ifupdown2/addons/bond.py index 4b96fd77..2ca77c47 100644 --- a/ifupdown2/addons/bond.py +++ b/ifupdown2/addons/bond.py @@ -488,7 +488,6 @@ def _add_slaves(self, ifaceobj, runningslaves, ifaceobj_getfunc=None): self.netlink.link_up_force(slave) except Exception as e: self.logger.debug('%s: %s' % (ifaceobj.name, str(e))) - pass if runningslaves: removed_slave = [] From b6f91cb7e83e4ea1c35adc5d8ad17bc8ab81c218 Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Mon, 30 May 2022 22:37:02 +0200 Subject: [PATCH 026/132] SONAR: addressvirtual: Remove unneeded "pass". Signed-off-by: Julien Fortin --- ifupdown2/addons/addressvirtual.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/ifupdown2/addons/addressvirtual.py b/ifupdown2/addons/addressvirtual.py index 0afe9dfa..d11a1e08 100644 --- a/ifupdown2/addons/addressvirtual.py +++ b/ifupdown2/addons/addressvirtual.py @@ -148,14 +148,12 @@ def _remove_addresses_from_bridge(self, ifaceobj, hwaddress): self.iproute2.bridge_fdb_del(bridgename, addr, vlan) except Exception as e: self.logger.debug("%s: %s" %(ifaceobj.name, str(e))) - pass elif self.cache.link_is_bridge(ifaceobj.name): for addr in hwaddress: try: self.iproute2.bridge_fdb_del(ifaceobj.name, addr) except Exception as e: self.logger.debug("%s: %s" %(ifaceobj.name, str(e))) - pass def _get_bridge_fdbs(self, bridgename, vlan): fdbs = self._bridge_fdb_query_cache.get(bridgename) @@ -228,7 +226,6 @@ def _fix_connected_route(self, ifaceobj, vifacename, addr): except Exception as e: self.logger.debug('%s: fixing route entry failed (%s)' % (ifaceobj.name, str(e))) - pass def _get_macs_from_old_config(self, ifaceobj=None): """ This method returns a list of the mac addresses From 5db042139497eece924880d547052b14a10e6d77 Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Tue, 31 May 2022 01:13:48 +0200 Subject: [PATCH 027/132] SONAR: bridge: remove uneeded pass stateme Signed-off-by: Julien Fortin --- ifupdown2/addons/bridge.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/ifupdown2/addons/bridge.py b/ifupdown2/addons/bridge.py index 6468afae..58d2279b 100644 --- a/ifupdown2/addons/bridge.py +++ b/ifupdown2/addons/bridge.py @@ -1188,7 +1188,6 @@ def _process_bridge_waitport(self, ifaceobj, portlist): except IndexError as e: # ignore error and use all bridge ports waitportlist = portlist - pass if not waitportlist: return self.logger.info('%s: waiting for ports %s to exist ...' %(ifaceobj.name, str(waitportlist))) @@ -1315,7 +1314,6 @@ def _add_ports(self, ifaceobj, ifaceobj_getfunc): self.iproute2.addr_flush(bridgeport) except Exception as e: self.logger.error(str(e)) - pass self.iproute2.batch_commit() self.cache.force_add_slave_list(ifaceobj.name, newly_enslaved_ports) @@ -1740,7 +1738,6 @@ def _apply_bridge_vids_and_pvid(self, bportifaceobj, ifaceobj_getfunc, vids, pvi self.logger.warning('%s: unable to parse pvid \'%s\'' %(bportifaceobj.name, pvid)) pvid_int = 0 - pass vids_to_del = [] vids_to_add = vids_int @@ -1997,7 +1994,6 @@ def _apply_bridge_port_settings_all(self, ifaceobj, ifaceobj_getfunc, bridge_vla except Exception as e: err = True self.logger.warning('%s: %s' %(ifaceobj.name, str(e))) - pass self.iproute2.batch_commit() if err: raise Exception('%s: errors applying port settings' %ifaceobj.name) From 387d447f9c8134cc923d2e5fd8fe502b299b4dd5 Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Tue, 31 May 2022 15:40:05 +0200 Subject: [PATCH 028/132] SONAR: mstpctl: remove uneeded pass statement Signed-off-by: Julien Fortin --- ifupdown2/addons/mstpctl.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/ifupdown2/addons/mstpctl.py b/ifupdown2/addons/mstpctl.py index 29005466..b661173b 100644 --- a/ifupdown2/addons/mstpctl.py +++ b/ifupdown2/addons/mstpctl.py @@ -381,7 +381,6 @@ def _ports_enable_disable_ipv6(self, ports, enable='1'): '/disable_ipv6', enable) except Exception as e: self.logger.info(str(e)) - pass def _add_ports(self, ifaceobj): bridgeports = self._get_bridge_port_list(ifaceobj) @@ -448,7 +447,6 @@ def _apply_bridge_settings(self, ifaceobj, ifaceobj_getfunc): dstattrname, config_val, check) except Exception as e: self.logger.warning('%s' %str(e)) - pass if self.cache.bridge_is_vlan_aware(ifaceobj.name): return @@ -515,7 +513,6 @@ def _apply_bridge_settings(self, ifaceobj, ifaceobj_getfunc): ifaceobj, raise_error=False) except Exception as e: self.log_warn(str(e)) - pass def _get_default_val(self, attr, ifaceobj, bridgeifaceobj): if (self.set_default_mstp_vxlan_bridge_config @@ -669,7 +666,6 @@ def _apply_bridge_port_settings_all(self, ifaceobj, self._apply_bridge_port_settings(bportifaceobj, bvlan_aware, ifaceobj.name, ifaceobj) except Exception as e: - pass self.log_warn(str(e)) def _is_running_userspace_stp_state_on(self, bridgename): @@ -719,7 +715,6 @@ def _up(self, ifaceobj, ifaceobj_getfunc=None): except Exception as e: porterr = True porterrstr = str(e) - pass running_ports = self.cache.get_slaves(ifaceobj.name) if running_ports: @@ -973,7 +968,6 @@ def _query_check_bridge(self, ifaceobj, ifaceobjcurr, status = 1 except Exception as e: self.log_warn(str(e)) - pass ifaceobjcurr.update_config_with_status(k, currstr, status) elif not rv: ifaceobjcurr.update_config_with_status(k, '', 1) @@ -1226,7 +1220,6 @@ def _query_bridge_port(self, ifaceobj, ifaceobj_getfunc=None): return except Exception as e: self.logger.info("%s: %s" %(ifaceobj.name, str(e))) - pass def _query(self, ifaceobj, ifaceobj_getfunc=None, **kwargs): """ add default policy attributes supported by the module """ From 59d76d9f6066e453ac3a3424d241af0bb82206e9 Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Tue, 31 May 2022 15:46:35 +0200 Subject: [PATCH 029/132] SONAR: usercmds: remove unneeded pass statement Signed-off-by: Julien Fortin --- ifupdown2/addons/usercmds.py | 1 - 1 file changed, 1 deletion(-) diff --git a/ifupdown2/addons/usercmds.py b/ifupdown2/addons/usercmds.py index 2dc5ca1d..2f8510b3 100644 --- a/ifupdown2/addons/usercmds.py +++ b/ifupdown2/addons/usercmds.py @@ -57,7 +57,6 @@ def _run_command(self, ifaceobj, op): if not self.ignore_error(str(e)): self.logger.warning('%s: %s %s' % (ifaceobj.name, op, str(e).strip('\n'))) - pass def _query_check(self, ifaceobj, ifaceobjcurr): if ifaceobj.config: From ba18a639326a2e2dee77e2146091c6537ecf6755 Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Tue, 31 May 2022 15:49:21 +0200 Subject: [PATCH 030/132] SONAR: vrf: remove unneeded pass statement and fix exception handler unused var Signed-off-by: Julien Fortin --- ifupdown2/addons/vrf.py | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/ifupdown2/addons/vrf.py b/ifupdown2/addons/vrf.py index 03289fd5..87c4ddd6 100644 --- a/ifupdown2/addons/vrf.py +++ b/ifupdown2/addons/vrf.py @@ -227,7 +227,6 @@ def _iproute2_vrf_map_initialize(self, writetodisk=True): self.iproute2_vrf_map[int(table)] = vrf_name except Exception as e: self.logger.info('vrf: iproute2_vrf_map: unable to parse %s (%s)' %(l, str(e))) - pass running_vrf_map = self.cache.get_vrf_table_map() @@ -287,7 +286,6 @@ def _iproute2_vrf_map_sync_to_disk(self): f.flush() except Exception as e: self._iproute2_map_warn(str(e)) - pass def _iproute2_vrf_map_open(self, sync_vrfs=False, append=False): self.logger.info('vrf: syncing table map to %s' @@ -383,7 +381,6 @@ def _iproute2_vrf_table_entry_del(self, table_id): except Exception as e: self.logger.info('vrf: iproute2 vrf map del failed for %s (%s)' %(table_id, str(e))) - pass def _is_vrf_dev(self, ifacename): # Look at iproute2 map for now. @@ -598,7 +595,6 @@ def _add_vrf_rules(self, vrf_dev_name, vrf_table): %utils.ip_cmd) except Exception as e: self.logger.info('%s: %s' % (vrf_dev_name, str(e))) - pass if rule in self.ip6_rule_cache: try: utils.exec_command('%s -6 rule del pref 0' @@ -607,7 +603,6 @@ def _add_vrf_rules(self, vrf_dev_name, vrf_table): %utils.ip_cmd) except Exception as e: self.logger.info('%s: %s' % (vrf_dev_name, str(e))) - pass if not self.l3mdev_checked: self._rule_cache_fill() @@ -710,7 +705,6 @@ def _add_vrf_slaves(self, ifaceobj, ifaceobj_getfunc=None): self.netlink.link_up(s) except Exception as e: self.logger.debug("%s: %s" % (s, str(e))) - pass def _set_vrf_dev_processed_flag(self, ifaceobj): ifaceobj.module_flags[self.name] = \ @@ -885,7 +879,7 @@ def _kill_ssh_connections(self, ifacename, ifaceobj): %(ifacename, str(proc))) os.kill(int(pid), signal.SIGINT) return - except OSError as e: + except OSError: return except Exception as e: self.logger.info('%s: %s' %(ifacename, str(e))) @@ -949,7 +943,6 @@ def _close_sockets(self, ifacename): except Exception as e: self.logger.info('%s: closing socks using ss' ' failed (%s)' %(ifacename, str(e))) - pass def _down_vrf_dev(self, ifaceobj, vrf_table, ifaceobj_getfunc=None): @@ -970,25 +963,21 @@ def _down_vrf_dev(self, ifaceobj, vrf_table, ifaceobj_getfunc=None): ifaceobj.name) except Exception as e: self.logger.info('%s: %s' %(ifaceobj.name, str(e))) - pass try: self.netlink.addr_flush(s) self.netlink.link_down(s) except Exception as e: self.logger.info('%s: %s' %(s, str(e))) - pass try: self._down_vrf_helper(ifaceobj, vrf_table) except Exception as e: self.logger.warning('%s: %s' %(ifaceobj.name, str(e))) - pass try: self._del_vrf_rules(ifaceobj.name, vrf_table) except Exception as e: self.logger.info('%s: %s' %(ifaceobj.name, str(e))) - pass self._close_sockets(ifaceobj.name) @@ -996,13 +985,11 @@ def _down_vrf_dev(self, ifaceobj, vrf_table, ifaceobj_getfunc=None): self.netlink.link_del(ifaceobj.name) except Exception as e: self.logger.info('%s: %s' %(ifaceobj.name, str(e))) - pass try: self._iproute2_vrf_table_entry_del(vrf_table) except Exception as e: self.logger.info('%s: %s' %(ifaceobj.name, str(e))) - pass def _down_vrf_slave(self, ifacename, ifaceobj=None, vrfname=None): @@ -1062,13 +1049,12 @@ def _query_check_vrf_dev(self, ifaceobj, ifaceobjcurr, vrf_table): %(self.vrf_helper, ifaceobj.name, config_table), 0) - except Exception as e: + except Exception: ifaceobjcurr.update_config_with_status('vrf-helper', '%s create %s %s' %(self.vrf_helper, ifaceobj.name, config_table), 1) - pass except Exception as e: self.log_warn(str(e)) From ee6e5c5167eeb1cc4c7811b212d1fbe07075ec3a Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Tue, 31 May 2022 15:58:00 +0200 Subject: [PATCH 031/132] SONAR: vrrpd: remove unneeded pass statement Signed-off-by: Julien Fortin --- ifupdown2/addons/vrrpd.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/ifupdown2/addons/vrrpd.py b/ifupdown2/addons/vrrpd.py index f4b79c52..ac8cff37 100644 --- a/ifupdown2/addons/vrrpd.py +++ b/ifupdown2/addons/vrrpd.py @@ -130,7 +130,6 @@ def _down(self, ifaceobj): except Exception as e: self.logger.debug('%s: ifplugd down error (%s)' %(ifaceobj.name, str(e))) - pass for pidfile in glob.glob('/var/run/vrrpd_%s_*.pid' %ifaceobj.name): try: @@ -138,7 +137,6 @@ def _down(self, ifaceobj): except Exception as e: self.logger.debug('%s: vrrpd down error (%s)' %(ifaceobj.name, str(e))) - pass def _query_check(self, ifaceobj, ifaceobjcurr): # XXX From 687a35bcedbc56a267980035ff34b8e8151c1a8d Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Tue, 31 May 2022 01:31:10 +0200 Subject: [PATCH 032/132] SONAR: link: Merge this if statement with the enclosing one Signed-off-by: Julien Fortin --- ifupdown2/addons/link.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/ifupdown2/addons/link.py b/ifupdown2/addons/link.py index 36aeaaa2..e73b6c56 100644 --- a/ifupdown2/addons/link.py +++ b/ifupdown2/addons/link.py @@ -64,10 +64,9 @@ def __init__(self, *args, **kargs): ) def syntax_check(self, ifaceobj, ifaceobj_getfunc): - if self.check_physical_port_existance: - if not ifaceobj.link_kind and not self.cache.link_exists(ifaceobj.name): - self.logger.warning('%s: interface does not exist' % ifaceobj.name) - return False + if self.check_physical_port_existance and not ifaceobj.link_kind and not self.cache.link_exists(ifaceobj.name): + self.logger.warning('%s: interface does not exist' % ifaceobj.name) + return False return True @staticmethod From e5ae0fcb557f200471fef4679ed5ebdafea25687 Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Tue, 31 May 2022 15:52:10 +0200 Subject: [PATCH 033/132] SONAR: vrf: Merge if statement with the enclosing one Signed-off-by: Julien Fortin --- ifupdown2/addons/vrf.py | 32 ++++++++++++++------------------ 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/ifupdown2/addons/vrf.py b/ifupdown2/addons/vrf.py index 87c4ddd6..da4cbbd5 100644 --- a/ifupdown2/addons/vrf.py +++ b/ifupdown2/addons/vrf.py @@ -94,9 +94,12 @@ def __init__(self, *args, **kargs): self.user_reserved_vrf_table = [] - if (ifupdownflags.flags.PERFMODE and - not (self.vrf_mgmt_devname and os.path.exists('/sys/class/net/%s' - %self.vrf_mgmt_devname))): + if ( + ifupdownflags.flags.PERFMODE + and not self.vrf_mgmt_devname + and os.path.exists(self.iproute2_vrf_filename) + and os.path.exists("/sys/class/net/%s" % self.vrf_mgmt_devname) + ): # if perf mode is set (PERFMODE is set at boot), and this is the first # time we are calling ifup at boot (check for mgmt vrf existance at # boot, make sure this is really the first invocation at boot. @@ -104,14 +107,11 @@ def __init__(self, *args, **kargs): # and the second time with all auto interfaces). We want to delete # the map file only the first time. This is to avoid accidently # deleting map file with a valid mgmt vrf entry - if os.path.exists(self.iproute2_vrf_filename): - try: - self.logger.info('vrf: removing file %s' - %self.iproute2_vrf_filename) - os.remove(self.iproute2_vrf_filename) - except Exception as e: - self.logger.debug('vrf: removing file failed (%s)' - %str(e)) + try: + self.logger.info("vrf: removing file %s" % self.iproute2_vrf_filename) + os.remove(self.iproute2_vrf_filename) + except Exception as e: + self.logger.debug("vrf: removing file failed (%s)" % str(e)) try: ip_rules = utils.exec_command('%s rule show' %utils.ip_cmd).splitlines() @@ -651,10 +651,7 @@ def _is_address_virtual_slaves(self, vrfobj, config_vrfslaves, # format -v created by the # address virtual module vrfslave_lowers = self.sysfs.link_get_lowers(vrfslave) - if vrfslave_lowers: - if vrfslave_lowers[0] in config_vrfslaves: - return True - return False + return vrfslave_lowers and vrfslave_lowers[0] in config_vrfslaves def _add_vrf_slaves(self, ifaceobj, ifaceobj_getfunc=None): running_slaves = self.sysfs.link_get_lowers(ifaceobj.name) @@ -829,9 +826,8 @@ def _kill_ssh_connections(self, ifacename, ifaceobj): addr = citems[3].split(':')[0] if not addr: continue - if addr in iplist: - if len(citems) == 6: - proc.append(citems[5].split(',')[1].split('=')[1]) + if addr in iplist and len(citems) == 6: + proc.append(citems[5].split(',')[1].split('=')[1]) if not proc: return From b6721ea95ade74b0836549f069aed5f0bbb08943 Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Tue, 31 May 2022 15:54:35 +0200 Subject: [PATCH 034/132] SONAR: vrf: Remove commented out code Signed-off-by: Julien Fortin --- ifupdown2/addons/vrf.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/ifupdown2/addons/vrf.py b/ifupdown2/addons/vrf.py index da4cbbd5..f0beb801 100644 --- a/ifupdown2/addons/vrf.py +++ b/ifupdown2/addons/vrf.py @@ -128,12 +128,6 @@ def __init__(self, *args, **kargs): self.ip6_rule_cache = [] self.logger.warning('vrf: cache v6: %s' % str(e)) - #self.logger.debug("vrf: ip rule cache") - #self.logger.info(self.ip_rule_cache) - - #self.logger.info("vrf: ip -6 rule cache") - #self.logger.info(self.ip6_rule_cache) - self.l3mdev_checked = False self.l3mdev4_rule = False if self._l3mdev_rule(self.ip_rule_cache): From af52e664620ad5e611c98f1cc1998cb0482f908b Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Tue, 31 May 2022 15:50:10 +0200 Subject: [PATCH 035/132] SONAR: vrf: Rename class "vrfPrivFlags" to match the regular expression ^_?([A-Z_][a-zA-Z0-9]*|[a-z_][a-z0-9_]*)$ Signed-off-by: Julien Fortin --- ifupdown2/addons/vrf.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ifupdown2/addons/vrf.py b/ifupdown2/addons/vrf.py index f0beb801..71b3475c 100644 --- a/ifupdown2/addons/vrf.py +++ b/ifupdown2/addons/vrf.py @@ -42,7 +42,7 @@ from ifupdownaddons.modulebase import moduleBase -class vrfPrivFlags: +class VrfPrivFlags: PROCESSED = 0x1 @@ -700,10 +700,10 @@ def _add_vrf_slaves(self, ifaceobj, ifaceobj_getfunc=None): def _set_vrf_dev_processed_flag(self, ifaceobj): ifaceobj.module_flags[self.name] = \ ifaceobj.module_flags.setdefault(self.name, 0) | \ - vrfPrivFlags.PROCESSED + VrfPrivFlags.PROCESSED def _check_vrf_dev_processed_flag(self, ifaceobj): - if (ifaceobj.module_flags.get(self.name, 0) & vrfPrivFlags.PROCESSED): + if (ifaceobj.module_flags.get(self.name, 0) & VrfPrivFlags.PROCESSED): return True return False From 8c82fe283c94108183dd2d83e4280720bce2ca72 Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Tue, 31 May 2022 15:56:56 +0200 Subject: [PATCH 036/132] SONAR: vrf: remove unneeded return statement Signed-off-by: Julien Fortin --- ifupdown2/addons/vrf.py | 1 - 1 file changed, 1 deletion(-) diff --git a/ifupdown2/addons/vrf.py b/ifupdown2/addons/vrf.py index 71b3475c..d46786af 100644 --- a/ifupdown2/addons/vrf.py +++ b/ifupdown2/addons/vrf.py @@ -434,7 +434,6 @@ def _up_vrf_slave_without_master(self, ifacename, vrfname, ifaceobj, vrf_master_ self._handle_existing_connections(ifaceobj, vrfname) self.enable_ipv6_if_prev_brport(ifacename) self.netlink.link_set_master(ifacename, vrfname) - return def enable_ipv6_if_prev_brport(self, ifname): """ From 375a838d44a6a21b06f73325063d145f752abf00 Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Tue, 31 May 2022 15:56:02 +0200 Subject: [PATCH 037/132] SONAR: vrf: remove unneeded try/except Signed-off-by: Julien Fortin --- ifupdown2/addons/vrf.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/ifupdown2/addons/vrf.py b/ifupdown2/addons/vrf.py index d46786af..5c7b868d 100644 --- a/ifupdown2/addons/vrf.py +++ b/ifupdown2/addons/vrf.py @@ -426,10 +426,7 @@ def _up_vrf_slave_without_master(self, ifacename, vrfname, ifaceobj, vrf_master_ %mobj.name, ifaceobj) self.logger.info('%s: table id auto: selected table id %s' %(mobj.name, vrf_table)) - try: - self._up_vrf_dev(mobj, vrf_table, False) - except Exception: - raise + self._up_vrf_dev(mobj, vrf_table, False) break self._handle_existing_connections(ifaceobj, vrfname) self.enable_ipv6_if_prev_brport(ifacename) From fe73211563cf44d524898c55ea88494826b9804b Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Thu, 18 Apr 2024 20:31:32 +0200 Subject: [PATCH 038/132] addons: vrf: unslave macvlans if vrf is removed from lower interface This was previously handled by the _up ops on the VRF itself, but in the context of ifreload-diff we might not process the vrf and only the vrf-slave. Signed-off-by: Julien Fortin --- ifupdown2/addons/vrf.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/ifupdown2/addons/vrf.py b/ifupdown2/addons/vrf.py index 5c7b868d..dd719bf1 100644 --- a/ifupdown2/addons/vrf.py +++ b/ifupdown2/addons/vrf.py @@ -871,6 +871,7 @@ def _kill_ssh_connections(self, ifacename, ifaceobj): self.logger.info('%s: %s' %(ifacename, str(e))) def _up(self, ifaceobj, ifaceobj_getfunc=None): + ifname = ifaceobj.name try: vrf_table = ifaceobj.get_attr_value_first('vrf-table') if vrf_table: @@ -896,9 +897,24 @@ def _up(self, ifaceobj, ifaceobj_getfunc=None): if self._is_vrf_dev(master): self._down_vrf_slave(ifaceobj.name, ifaceobj, master) + + if ifaceobj.get_attr_value_first("address-virtual") or ifaceobj.get_attr_value_first("vrrp"): + # macvlans were created on this interface - we also need to removed them from the vrf + # (ifreload used to take care of that in the ifaceobj:vrf path, but we should in fact + # do this here as ifreload-diff might not process the vrf ifaceobj + [ + self._down_vrf_slave(macvlan, vrfname=master) + for macvlan in self.sysfs.link_get_uppers(ifname) if self.has_macvlan_prefix(ifname, macvlan) + ] + except Exception as e: self.log_error(str(e), ifaceobj) + @staticmethod + def has_macvlan_prefix(ifname, dev): + # Look for any of the ifupdown2 macvlan prefixes + return any(dev.startswith(prefix) for prefix in (f"{ifname}-v", "vrrp4", "vrrp6")) + def _down_vrf_helper(self, ifaceobj, vrf_table): mode = "" if ifupdownflags.flags.PERFMODE: From d46afbba826d876ca0f3c55794db1ef89fb94190 Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Tue, 31 May 2022 01:22:47 +0200 Subject: [PATCH 039/132] SONAR: bridgevlan: remove this redundant return statement Signed-off-by: Julien Fortin --- ifupdown2/addons/bridgevlan.py | 1 - 1 file changed, 1 deletion(-) diff --git a/ifupdown2/addons/bridgevlan.py b/ifupdown2/addons/bridgevlan.py index c2af5609..1d465a08 100644 --- a/ifupdown2/addons/bridgevlan.py +++ b/ifupdown2/addons/bridgevlan.py @@ -136,7 +136,6 @@ def _query_check(self, ifaceobj, ifaceobjcurr): ifaceobjcurr.update_config_with_status( 'bridge-igmp-querier-src', attrval, 0) ifaceobjcurr.status = ifaceStatus.SUCCESS - return def syntax_check(self, ifaceobj, ifaceobj_getfunc): ret = True From e4f18c2ec988df90ef6d76287fbc4ded84ab322f Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Wed, 30 Nov 2022 21:35:17 +0100 Subject: [PATCH 040/132] addons: vxlan: vxlan_tos_str will never be None/Null Signed-off-by: Julien Fortin --- ifupdown2/addons/vxlan.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ifupdown2/addons/vxlan.py b/ifupdown2/addons/vxlan.py index 892af114..63ddcae2 100644 --- a/ifupdown2/addons/vxlan.py +++ b/ifupdown2/addons/vxlan.py @@ -460,9 +460,9 @@ def __config_vxlan_tos(self, ifname, ifaceobj, user_request_vxlan_info_data, cac if vxlan_tos != cached_ifla_vxlan_tos: if cached_ifla_vxlan_tos is not None: - self.logger.info("%s: set vxlan-tos %s (cache %s)" % (ifname, vxlan_tos_str if vxlan_tos_str else vxlan_tos, cached_ifla_vxlan_tos)) + self.logger.info("%s: set vxlan-tos %s (cache %s)" % (ifname, vxlan_tos_str, cached_ifla_vxlan_tos)) else: - self.logger.info("%s: set vxlan-tos %s" % (ifname, vxlan_tos_str if vxlan_tos_str else vxlan_tos)) + self.logger.info("%s: set vxlan-tos %s" % (ifname, vxlan_tos_str)) user_request_vxlan_info_data[Link.IFLA_VXLAN_TOS] = vxlan_tos except Exception: From 4b286f82065470b588f71bbe9bb5823fc1640849 Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Tue, 31 May 2022 16:01:33 +0200 Subject: [PATCH 041/132] SONAR: vxlan: Rename map variable; it shadows a builtin Signed-off-by: Julien Fortin --- ifupdown2/addons/vxlan.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/ifupdown2/addons/vxlan.py b/ifupdown2/addons/vxlan.py index 63ddcae2..7264546a 100644 --- a/ifupdown2/addons/vxlan.py +++ b/ifupdown2/addons/vxlan.py @@ -634,8 +634,7 @@ def __get_vxlan_mcastgrp_map(self, ifaceobj): parsed_maps = {} for m_line in maps: # Cover single-line multi-entry case - map = m_line.split() - for m in map: + for m in m_line.split(): m_parts = m.split('=') if len(m_parts) != 2: self.log_error('%s: vxlan-mcastgrp-map %s format is invalid' % (ifaceobj.name, m)) @@ -997,8 +996,7 @@ def __get_vxlan_remote_ip_map(self, ifaceobj): parsed_maps = {} for m_line in maps: # Cover single-line multi-entry case - map = m_line.split() - for m in map: + for m in m_line.split(): m_parts = m.split('=') if len(m_parts) != 2: self.log_error('%s: %s %s format is invalid' % (ifaceobj.name, attr_name, m)) From 59da367084084de3416092aec2fcb0dfb09f331d Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Thu, 10 Aug 2023 16:20:53 +0200 Subject: [PATCH 042/132] addons: vxlan: svd: reset local and group ip when removed from user config Signed-off-by: Julien Fortin --- ifupdown2/addons/vxlan.py | 15 +++++++++++++-- ifupdown2/lib/iproute2.py | 6 ++++-- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/ifupdown2/addons/vxlan.py b/ifupdown2/addons/vxlan.py index 7264546a..757e5d05 100644 --- a/ifupdown2/addons/vxlan.py +++ b/ifupdown2/addons/vxlan.py @@ -1163,11 +1163,22 @@ def _up(self, ifaceobj): self.logger.info('%s: vxlan already exists - no change detected' % ifname) else: if ifaceobj.link_privflags & ifaceLinkPrivFlags.SINGLE_VXLAN: + + if Link.IFLA_VXLAN_LOCAL in user_request_vxlan_info_data and not user_request_vxlan_info_data[Link.IFLA_VXLAN_LOCAL]: + local_str = "0" + else: + local_str = local.ip if local else None + + if Link.IFLA_VXLAN_GROUP in user_request_vxlan_info_data and not user_request_vxlan_info_data[Link.IFLA_VXLAN_GROUP]: + group_str = "0" + else: + group_str = group.ip if group else None + self.iproute2.link_add_single_vxlan( link_exists, ifname, - local.ip if local else None, - group.ip if group else None, + local_str, + group_str, vxlan_physdev, user_request_vxlan_info_data.get(Link.IFLA_VXLAN_PORT), vxlan_vnifilter, diff --git a/ifupdown2/lib/iproute2.py b/ifupdown2/lib/iproute2.py index c9b12cc2..b2dbb01b 100644 --- a/ifupdown2/lib/iproute2.py +++ b/ifupdown2/lib/iproute2.py @@ -281,15 +281,17 @@ def link_add_veth(self, ifname, peer_name): ### def link_add_single_vxlan(self, link_exists, ifname, ip, group, physdev, port, vnifilter="off", ttl=None): - self.logger.info("creating single vxlan device: %s" % ifname) - if link_exists: + self.logger.info("updating single vxlan device: %s" % ifname) + # When updating an SVD we need to use `ip link set` and we have to # drop the external keyword: # $ ip link set dev vxlan0 type vxlan external local 27.0.0.242 dev ipmr-lo # Error: vxlan: cannot change COLLECT_METADATA flag. cmd = ["link set dev %s type vxlan" % ifname] else: + self.logger.info("creating single vxlan device: %s" % ifname) + cmd = ["link add dev %s type vxlan external" % ifname] # when changing local ip, if we specify vnifilter we get: From 43dd5a3c2fa3625d27e5d9dcc6c67a0a43dba802 Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Fri, 12 Apr 2024 17:56:29 +0200 Subject: [PATCH 043/132] addons: vxlan: set remoteip as an empty list if removed The piece of code in charge of deleting stale remote ips was relying on remote-ips being a list and not None. So when the attribute was actually removed from ENI we wouldn't clear the last fdb entry Signed-off-by: Julien Fortin --- ifupdown2/addons/vxlan.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ifupdown2/addons/vxlan.py b/ifupdown2/addons/vxlan.py index 757e5d05..fd85e52d 100644 --- a/ifupdown2/addons/vxlan.py +++ b/ifupdown2/addons/vxlan.py @@ -1247,6 +1247,8 @@ def _up(self, ifaceobj): ipnetwork.IPv4Address(remoteip) except Exception as e: self.log_error('%s: vxlan-remoteip: %s' % (ifaceobj.name, str(e))) + else: + remoteips = [] # get old remote ips to compare with new user config value and # purge any removed remote ip From d565eadf6ffa5327ff4374c36da60855f476294e Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Tue, 31 May 2022 16:04:09 +0200 Subject: [PATCH 044/132] SONAR: vxlan: Merge if statements with the enclosing ones Signed-off-by: Julien Fortin --- ifupdown2/addons/vxlan.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/ifupdown2/addons/vxlan.py b/ifupdown2/addons/vxlan.py index fd85e52d..a9b31f19 100644 --- a/ifupdown2/addons/vxlan.py +++ b/ifupdown2/addons/vxlan.py @@ -1234,9 +1234,8 @@ def _up(self, ifaceobj): except Exception as e: self.logger.warning("%s: l3 vxlan vni failure: %s" % (ifname, e)) - if ifaceobj.link_privflags & ifaceLinkPrivFlags.SINGLE_VXLAN: - if vxlan_vnifilter and utils.get_boolean_from_string(vxlan_vnifilter): - self.single_vxlan_device_vni_filter(ifaceobj, vxlan_mcast_grp_map) + if ifaceobj.link_privflags & ifaceLinkPrivFlags.SINGLE_VXLAN and vxlan_vnifilter and utils.get_boolean_from_string(vxlan_vnifilter): + self.single_vxlan_device_vni_filter(ifaceobj, vxlan_mcast_grp_map) vxlan_purge_remotes = self.__get_vlxan_purge_remotes(ifaceobj) @@ -1756,8 +1755,8 @@ def run(self, ifaceobj, operation, query_ifaceobj=None, **extra_args): if not self._is_vxlan_device(ifaceobj): return - if "query" not in operation: - if not self.vxlan_mcastgrp_ref \ + if "query" not in operation and \ + not self.vxlan_mcastgrp_ref \ and self.vxlan_physdev_mcast \ and self.cache.link_exists(self.vxlan_physdev_mcast): self.netlink.link_del(self.vxlan_physdev_mcast) From 1ff0d8f997aaa899e8edb174c6dd2c5f7b1573b3 Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Wed, 17 Apr 2024 23:52:49 +0200 Subject: [PATCH 045/132] addons: vxlan: cleaning up duplicated fdb vnifilter code This used to be handled by ifupdown2 but the logic was moved to the kernel thus removing the function call. For some reason the function was duplicated. We are cleaning this up but keep the original code and a commented out func call just in case we need it in the future. Signed-off-by: Julien Fortin --- ifupdown2/addons/vxlan.py | 51 ++++----------------------------------- 1 file changed, 5 insertions(+), 46 deletions(-) diff --git a/ifupdown2/addons/vxlan.py b/ifupdown2/addons/vxlan.py index a9b31f19..e034ac4b 100644 --- a/ifupdown2/addons/vxlan.py +++ b/ifupdown2/addons/vxlan.py @@ -1234,8 +1234,10 @@ def _up(self, ifaceobj): except Exception as e: self.logger.warning("%s: l3 vxlan vni failure: %s" % (ifname, e)) - if ifaceobj.link_privflags & ifaceLinkPrivFlags.SINGLE_VXLAN and vxlan_vnifilter and utils.get_boolean_from_string(vxlan_vnifilter): - self.single_vxlan_device_vni_filter(ifaceobj, vxlan_mcast_grp_map) + if ifaceobj.link_privflags & ifaceLinkPrivFlags.SINGLE_VXLAN: + if vxlan_vnifilter and utils.get_boolean_from_string(vxlan_vnifilter): + self.single_vxlan_device_vni_filter(ifaceobj, vxlan_mcast_grp_map) + #self.single_vxlan_device_mcast_grp_map_fdb_vnifilter(ifaceobj, ifname, vxlan_mcast_grp_map) vxlan_purge_remotes = self.__get_vlxan_purge_remotes(ifaceobj) @@ -1360,7 +1362,7 @@ def get_svd_running_fdb(ifname): return current_fdb - def single_vxlan_device_mcast_grp_map_fdb(self, ifaceobj, ifname, vxlan_mcast_grp_map): + def single_vxlan_device_mcast_grp_map_fdb_vnifilter(self, ifaceobj, ifname, vxlan_mcast_grp_map): # in this piece of code we won't be checking the running state of the fdb table # dumping all fdb entries would cause scalability issues in certain cases. @@ -1399,49 +1401,6 @@ def single_vxlan_device_mcast_grp_map_fdb(self, ifaceobj, ifname, vxlan_mcast_gr % (ifname, src_vni, dst_ip, str(e)), raise_error=False ) - def single_vxlan_device_mcast_grp_map_vnifilter(self, ifaceobj, ifname, vxlan_mcast_grp_map): - # in this piece of code we won't be checking the running state of the fdb table - # dumping all fdb entries would cause scalability issues in certain cases. - - # pulling old mcastgrp-map configuration - old_user_config_fdb = [] - - for old_ifaceobj in statemanager.get_ifaceobjs(ifname) or []: - old_user_config_fdb += self.get_vxlan_fdb_src_vni(self.__get_vxlan_mcastgrp_map(old_ifaceobj)) - - # new user configuration - user_config_fdb = self.get_vxlan_fdb_src_vni(vxlan_mcast_grp_map) - - # compare old and new config to know if we should remove any stale fdb entries. - fdb_entries_to_remove = set(old_user_config_fdb) - set(user_config_fdb) - - self.logger.info(old_user_config_fdb) - self.logger.info(user_config_fdb) - self.logger.info(fdb_entries_to_remove) - - if fdb_entries_to_remove: - for mac, src_vni, dst_ip in fdb_entries_to_remove: - try: - self.iproute2.bridge_fdb_del_src_vni(ifname, mac, src_vni) - except Exception as e: - if "no such file or directory" not in str(e).lower(): - self.logger.warning("%s: removing stale fdb entries failed: %s" % (ifname, str(e))) - - if not user_config_fdb: - # if vxlan-mcastgrp-map wasn't configure return - return - - for mac, src_vni, dst_ip in user_config_fdb: - try: - self.iproute2.bridge_fdb_add_src_vni(ifname, src_vni, dst_ip) - except Exception as e: - if "file exists" not in str(e).lower(): - ifaceobj.set_status(ifaceStatus.ERROR) - self.log_error( - "%s: vxlan-mcastgrp-map: %s=%s: %s" - % (ifname, src_vni, dst_ip, str(e)), raise_error=False - ) - def _down(self, ifaceobj): try: self.netlink.link_del(ifaceobj.name) From 5eff9ba0f033d128dc7ee48659c341ef1623d834 Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Thu, 16 Jun 2022 14:03:02 +0200 Subject: [PATCH 046/132] addons: vxlan: fix: name 'json' is not defined Signed-off-by: Julien Fortin --- ifupdown2/addons/vxlan.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ifupdown2/addons/vxlan.py b/ifupdown2/addons/vxlan.py index e034ac4b..fec11135 100644 --- a/ifupdown2/addons/vxlan.py +++ b/ifupdown2/addons/vxlan.py @@ -4,6 +4,8 @@ # Author: Roopa Prabhu, roopa@cumulusnetworks.com # +import json + from ipaddress import IPv4Network, IPv4Address, AddressValueError, ip_address try: import ifupdown2.nlmanager.ipnetwork as ipnetwork From 340edabc81e8fca11bef46c3f0effcf4a8fcec08 Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Tue, 31 May 2022 16:06:15 +0200 Subject: [PATCH 047/132] SONAR: argv: Remove the unused local variable "e" Signed-off-by: Julien Fortin --- ifupdown2/ifupdown/argv.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ifupdown2/ifupdown/argv.py b/ifupdown2/ifupdown/argv.py index ae905099..042c27cd 100644 --- a/ifupdown2/ifupdown/argv.py +++ b/ifupdown2/ifupdown/argv.py @@ -79,7 +79,7 @@ def __init__(self, argv): try: self.args = argparser.parse_args(self.argv) - except SystemExit as e: + except SystemExit: # on "--help" parse_args will raise SystemExit. # We need to catch this behavior and raise a custom # exception to return 0 properly From a574d8cf7a79f240f9ffc087b2e3fd2ac17b81fd Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Wed, 15 Feb 2023 14:20:02 +0100 Subject: [PATCH 048/132] sonarlink: remove unused function parameter Signed-off-by: Julien Fortin --- ifupdown2/ifupdown/iface.py | 2 +- ifupdown2/ifupdown/ifupdownmain.py | 17 ++++++++--------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/ifupdown2/ifupdown/iface.py b/ifupdown2/ifupdown/iface.py index 07bd067a..7c697363 100644 --- a/ifupdown2/ifupdown/iface.py +++ b/ifupdown2/ifupdown/iface.py @@ -700,7 +700,7 @@ def __setstate__(self, dict): self.blacklisted = False self.__dict__.update(dict) - def dump_raw(self, logger): + def dump_raw(self): indent = ' ' if self.auto: print('auto %s' %self.name) diff --git a/ifupdown2/ifupdown/ifupdownmain.py b/ifupdown2/ifupdown/ifupdownmain.py index 13ad142e..64e53536 100644 --- a/ifupdown2/ifupdown/ifupdownmain.py +++ b/ifupdown2/ifupdown/ifupdownmain.py @@ -649,7 +649,7 @@ def dump_iface_dependency_info(self): str(iobj.upperifaces) if iobj.upperifaces else [])) - def preprocess_dependency_list(self, upperifaceobj, dlist, ops): + def preprocess_dependency_list(self, upperifaceobj, dlist): """ We go through the dependency list and delete or add interfaces from the interfaces dict by applying the following rules: @@ -689,7 +689,7 @@ def preprocess_dependency_list(self, upperifaceobj, dlist, ops): for d in del_list: dlist.remove(d) - def preprocess_upperiface(self, lowerifaceobj, ulist, ops): + def preprocess_upperiface(self, lowerifaceobj, ulist): for u in ulist: if (lowerifaceobj.upperifaces and u in lowerifaceobj.upperifaces): @@ -724,7 +724,7 @@ def query_lowerifaces(self, ifaceobj, ops, ifacenames, old_ifaceobjs): if dlist: ret_dlist.extend(dlist) return list(set(ret_dlist)) - def query_upperifaces(self, ifaceobj, ops, ifacenames, type=None): + def query_upperifaces(self, ifaceobj, ops, ifacenames): """ Gets iface upperifaces by calling into respective modules """ ret_ulist = [] @@ -803,13 +803,13 @@ def populate_dependency_info(self, ops, ifacenames=None, old_ifaceobjs=False): if dlist: break if ulist: - self.preprocess_upperiface(ifaceobj, ulist, ops) + self.preprocess_upperiface(ifaceobj, ulist) if dependents_processed: continue if dlist: self._remove_circular_veth_dependencies(ifaceobj, dlist) self.preprocess_dependency_list(ifaceobj, - dlist, ops) + dlist) ifaceobj.lowerifaces = dlist [iqueue.append(d) for d in dlist] #if not self.dependency_graph.get(i): @@ -1248,7 +1248,7 @@ def _is_keyword(self, value): ' a valid keyword see `ifquery -s`' % value) return keyword_found - def _check_validvals_value(self, attrname, value, validvals, validrange): + def _check_validvals_value(self, value, validvals, validrange): if validvals and value not in validvals: is_valid = False for keyword in validvals: @@ -1292,8 +1292,7 @@ def _check_validvals(self, ifacename, module_name, attrs): validvals = attrname_dict.get('validvals', []) validrange = attrname_dict.get('validrange', []) for value in attrvalue: - res = self._check_validvals_value(attrname, - value, + res = self._check_validvals_value(value, validvals, validrange) if not res['result']: @@ -2483,7 +2482,7 @@ def print_ifaceobjs_raw(self, ifacenames, format=None): for ifaceobj in self.get_ifaceobjs(i): if self.is_ifaceobj_builtin(ifaceobj): continue - ifaceobj.dump_raw(self.logger) + ifaceobj.dump_raw() if (ifupdownflags.flags.WITH_DEPENDS and not ifupdownflags.flags.ALL): dlist = ifaceobj.lowerifaces From 879e284434da2d01b6482ac66fa20cfc123402ae Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Thu, 31 Oct 2024 16:15:05 +0100 Subject: [PATCH 049/132] ifupdown: main: fix code indentation Signed-off-by: Julien Fortin --- ifupdown2/ifupdown/main.py | 73 +++++++++++++++++++------------------- 1 file changed, 36 insertions(+), 37 deletions(-) diff --git a/ifupdown2/ifupdown/main.py b/ifupdown2/ifupdown/main.py index 74b41653..92a3d072 100644 --- a/ifupdown2/ifupdown/main.py +++ b/ifupdown2/ifupdown/main.py @@ -32,7 +32,6 @@ from lib.dry_run import DryRunManager - log = logging.getLogger() configmap_g = None @@ -82,8 +81,8 @@ def main(self, stdin_buffer=None): # else: if log: log.error('main exception: ' + str(e)) - #import traceback - #traceback.print_exc() + # import traceback + # traceback.print_exc() else: print(str(e)) # if args and not args.debug: @@ -113,7 +112,7 @@ def init(self, stdin_buffer): # Check to see if -i option is allowed by config file # But for ifquery, we will not check this if (self.op != 'query' and - configmap_g.get('disable_cli_interfacesfile', '0') == '1'): + configmap_g.get('disable_cli_interfacesfile', '0') == '1'): log.error('disable_cli_interfacesfile is set so users ' 'not allowed to specify interfaces file on cli.') raise Exception("") @@ -171,17 +170,17 @@ def run_up(self, args): cachearg = (False if (iflist or args.nocache or args.noact) else True) ifupdown_handle = ifupdownMain(daemon=self.daemon, args=args, - config=configmap_g, - force=args.force, - withdepends=args.withdepends, - perfmode=args.perfmode, - dryrun=args.noact, - cache=cachearg, - addons_enable=not args.noaddons, - statemanager_enable=not args.noaddons, - interfacesfile=self.interfaces_filename, - interfacesfileiobuf=self.interfaces_file_iobuf, - interfacesfileformat=args.interfacesfileformat) + config=configmap_g, + force=args.force, + withdepends=args.withdepends, + perfmode=args.perfmode, + dryrun=args.noact, + cache=cachearg, + addons_enable=not args.noaddons, + statemanager_enable=not args.noaddons, + interfacesfile=self.interfaces_filename, + interfacesfileiobuf=self.interfaces_file_iobuf, + interfacesfileformat=args.interfacesfileformat) if args.noaddons: ifupdown_handle.up(['up'], args.all, args.CLASS, iflist, excludepats=args.excludepats, @@ -205,15 +204,15 @@ def run_down(self, args): iflist = args.iflist log.debug('creating ifupdown object ..') ifupdown_handle = ifupdownMain(daemon=self.daemon, args=args, - config=configmap_g, force=args.force, - withdepends=args.withdepends, - perfmode=args.perfmode, - dryrun=args.noact, - addons_enable=not args.noaddons, - statemanager_enable=not args.noaddons, - interfacesfile=self.interfaces_filename, - interfacesfileiobuf=self.interfaces_file_iobuf, - interfacesfileformat=args.interfacesfileformat) + config=configmap_g, force=args.force, + withdepends=args.withdepends, + perfmode=args.perfmode, + dryrun=args.noact, + addons_enable=not args.noaddons, + statemanager_enable=not args.noaddons, + interfacesfile=self.interfaces_filename, + interfacesfileiobuf=self.interfaces_file_iobuf, + interfacesfileformat=args.interfacesfileformat) ifupdown_handle.down(['pre-down', 'down', 'post-down'], args.all, args.CLASS, iflist, @@ -251,14 +250,14 @@ def run_query(self, args): if os.path.isdir('/sys/class/net/%s' % i)] log.debug('creating ifupdown object ..') ifupdown_handle = ifupdownMain(daemon=self.daemon, args=args, - config=configmap_g, - withdepends=args.withdepends, - perfmode=args.perfmode, - cache=cachearg, - interfacesfile=self.interfaces_filename, - interfacesfileiobuf=self.interfaces_file_iobuf, - interfacesfileformat=args.interfacesfileformat, - withdefaults=args.withdefaults) + config=configmap_g, + withdepends=args.withdepends, + perfmode=args.perfmode, + cache=cachearg, + interfacesfile=self.interfaces_filename, + interfacesfileiobuf=self.interfaces_file_iobuf, + interfacesfileformat=args.interfacesfileformat, + withdefaults=args.withdefaults) # list implies all auto interfaces (this is how ifupdown behaves) if args.list: args.all = True @@ -275,11 +274,11 @@ def run_reload(self, args): try: log.debug('creating ifupdown object ..') ifupdown_handle = ifupdownMain(daemon=self.daemon, args=args, - config=configmap_g, - interfacesfile=self.interfaces_filename, - withdepends=args.withdepends, - perfmode=args.perfmode, - dryrun=args.noact) + config=configmap_g, + interfacesfile=self.interfaces_filename, + withdepends=args.withdepends, + perfmode=args.perfmode, + dryrun=args.noact) ifupdown_handle.reload(['pre-up', 'up', 'post-up'], ['pre-down', 'down', 'post-down'], auto=args.all, allow=args.CLASS, ifacenames=None, From ebe50762fbf202d110e7324f3db671ccb0b40e49 Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Thu, 31 Oct 2024 16:17:28 +0100 Subject: [PATCH 050/132] ifupdown: main: remove unnecessary try/except Signed-off-by: Julien Fortin --- ifupdown2/ifupdown/main.py | 216 ++++++++++++++++++------------------- 1 file changed, 102 insertions(+), 114 deletions(-) diff --git a/ifupdown2/ifupdown/main.py b/ifupdown2/ifupdown/main.py index 92a3d072..0ff7e256 100644 --- a/ifupdown2/ifupdown/main.py +++ b/ifupdown2/ifupdown/main.py @@ -162,129 +162,117 @@ def read_config(self): def run_up(self, args): log.debug('args = %s' % str(args)) - try: - iflist = args.iflist - if len(args.iflist) == 0: - iflist = None - log.debug('creating ifupdown object ..') - cachearg = (False if (iflist or args.nocache or args.noact) - else True) - ifupdown_handle = ifupdownMain(daemon=self.daemon, args=args, - config=configmap_g, - force=args.force, - withdepends=args.withdepends, - perfmode=args.perfmode, - dryrun=args.noact, - cache=cachearg, - addons_enable=not args.noaddons, - statemanager_enable=not args.noaddons, - interfacesfile=self.interfaces_filename, - interfacesfileiobuf=self.interfaces_file_iobuf, - interfacesfileformat=args.interfacesfileformat) - if args.noaddons: - ifupdown_handle.up(['up'], args.all, args.CLASS, iflist, - excludepats=args.excludepats, - printdependency=args.printdependency, - syntaxcheck=args.syntaxcheck, type=args.type, - skipupperifaces=args.skipupperifaces) - else: - ifupdown_handle.up(['pre-up', 'up', 'post-up'], - args.all, args.CLASS, iflist, - excludepats=args.excludepats, - printdependency=args.printdependency, - syntaxcheck=args.syntaxcheck, type=args.type, - skipupperifaces=args.skipupperifaces) - except Exception: - raise + iflist = args.iflist + if len(args.iflist) == 0: + iflist = None + log.debug('creating ifupdown object ..') + cachearg = (False if (iflist or args.nocache or args.noact) + else True) + ifupdown_handle = ifupdownMain(daemon=self.daemon, args=args, + config=configmap_g, + force=args.force, + withdepends=args.withdepends, + perfmode=args.perfmode, + dryrun=args.noact, + cache=cachearg, + addons_enable=not args.noaddons, + statemanager_enable=not args.noaddons, + interfacesfile=self.interfaces_filename, + interfacesfileiobuf=self.interfaces_file_iobuf, + interfacesfileformat=args.interfacesfileformat) + if args.noaddons: + ifupdown_handle.up(['up'], args.all, args.CLASS, iflist, + excludepats=args.excludepats, + printdependency=args.printdependency, + syntaxcheck=args.syntaxcheck, type=args.type, + skipupperifaces=args.skipupperifaces) + else: + ifupdown_handle.up(['pre-up', 'up', 'post-up'], + args.all, args.CLASS, iflist, + excludepats=args.excludepats, + printdependency=args.printdependency, + syntaxcheck=args.syntaxcheck, type=args.type, + skipupperifaces=args.skipupperifaces) def run_down(self, args): log.debug('args = %s' % str(args)) - try: - iflist = args.iflist - log.debug('creating ifupdown object ..') - ifupdown_handle = ifupdownMain(daemon=self.daemon, args=args, - config=configmap_g, force=args.force, - withdepends=args.withdepends, - perfmode=args.perfmode, - dryrun=args.noact, - addons_enable=not args.noaddons, - statemanager_enable=not args.noaddons, - interfacesfile=self.interfaces_filename, - interfacesfileiobuf=self.interfaces_file_iobuf, - interfacesfileformat=args.interfacesfileformat) - - ifupdown_handle.down(['pre-down', 'down', 'post-down'], - args.all, args.CLASS, iflist, - excludepats=args.excludepats, - printdependency=args.printdependency, - usecurrentconfig=args.usecurrentconfig, - type=args.type) - except Exception: - raise + iflist = args.iflist + log.debug('creating ifupdown object ..') + ifupdown_handle = ifupdownMain(daemon=self.daemon, args=args, + config=configmap_g, force=args.force, + withdepends=args.withdepends, + perfmode=args.perfmode, + dryrun=args.noact, + addons_enable=not args.noaddons, + statemanager_enable=not args.noaddons, + interfacesfile=self.interfaces_filename, + interfacesfileiobuf=self.interfaces_file_iobuf, + interfacesfileformat=args.interfacesfileformat) + + ifupdown_handle.down(['pre-down', 'down', 'post-down'], + args.all, args.CLASS, iflist, + excludepats=args.excludepats, + printdependency=args.printdependency, + usecurrentconfig=args.usecurrentconfig, + type=args.type) def run_query(self, args): log.debug('args = %s' % str(args)) - try: - iflist = args.iflist - if args.checkcurr: - qop = 'query-checkcurr' - elif args.running: - qop = 'query-running' - elif args.raw: - qop = 'query-raw' - elif args.syntaxhelp: - qop = 'query-syntax' - elif args.printdependency: - qop = 'query-dependency' - elif args.printsavedstate: - qop = 'query-savedstate' - else: - qop = 'query' - cachearg = (False if (iflist or args.nocache or args.syntaxhelp or - (qop != 'query-checkcurr' and - qop != 'query-running')) else True) - if not iflist and qop == 'query-running': - iflist = [i for i in os.listdir('/sys/class/net/') - if os.path.isdir('/sys/class/net/%s' % i)] - log.debug('creating ifupdown object ..') - ifupdown_handle = ifupdownMain(daemon=self.daemon, args=args, - config=configmap_g, - withdepends=args.withdepends, - perfmode=args.perfmode, - cache=cachearg, - interfacesfile=self.interfaces_filename, - interfacesfileiobuf=self.interfaces_file_iobuf, - interfacesfileformat=args.interfacesfileformat, - withdefaults=args.withdefaults) - # list implies all auto interfaces (this is how ifupdown behaves) - if args.list: - args.all = True - ifupdown_handle.query([qop], args.all, args.list, args.CLASS, iflist, - excludepats=args.excludepats, - printdependency=args.printdependency, - format=args.format, type=args.type) - except Exception: - raise + iflist = args.iflist + if args.checkcurr: + qop = 'query-checkcurr' + elif args.running: + qop = 'query-running' + elif args.raw: + qop = 'query-raw' + elif args.syntaxhelp: + qop = 'query-syntax' + elif args.printdependency: + qop = 'query-dependency' + elif args.printsavedstate: + qop = 'query-savedstate' + else: + qop = 'query' + cachearg = (False if (iflist or args.nocache or args.syntaxhelp or + (qop != 'query-checkcurr' and + qop != 'query-running')) else True) + if not iflist and qop == 'query-running': + iflist = [i for i in os.listdir('/sys/class/net/') + if os.path.isdir('/sys/class/net/%s' % i)] + log.debug('creating ifupdown object ..') + ifupdown_handle = ifupdownMain(daemon=self.daemon, args=args, + config=configmap_g, + withdepends=args.withdepends, + perfmode=args.perfmode, + cache=cachearg, + interfacesfile=self.interfaces_filename, + interfacesfileiobuf=self.interfaces_file_iobuf, + interfacesfileformat=args.interfacesfileformat, + withdefaults=args.withdefaults) + # list implies all auto interfaces (this is how ifupdown behaves) + if args.list: + args.all = True + ifupdown_handle.query([qop], args.all, args.list, args.CLASS, iflist, + excludepats=args.excludepats, + printdependency=args.printdependency, + format=args.format, type=args.type) def run_reload(self, args): log.debug('args = %s' % str(args)) - try: - log.debug('creating ifupdown object ..') - ifupdown_handle = ifupdownMain(daemon=self.daemon, args=args, - config=configmap_g, - interfacesfile=self.interfaces_filename, - withdepends=args.withdepends, - perfmode=args.perfmode, - dryrun=args.noact) - ifupdown_handle.reload(['pre-up', 'up', 'post-up'], - ['pre-down', 'down', 'post-down'], - auto=args.all, allow=args.CLASS, ifacenames=None, - excludepats=args.excludepats, - usecurrentconfig=args.usecurrentconfig, - syntaxcheck=args.syntaxcheck, - currentlyup=args.currentlyup) - except Exception: - raise + log.debug('creating ifupdown object ..') + ifupdown_handle = ifupdownMain(daemon=self.daemon, args=args, + config=configmap_g, + interfacesfile=self.interfaces_filename, + withdepends=args.withdepends, + perfmode=args.perfmode, + dryrun=args.noact) + ifupdown_handle.reload(['pre-up', 'up', 'post-up'], + ['pre-down', 'down', 'post-down'], + auto=args.all, allow=args.CLASS, ifacenames=None, + excludepats=args.excludepats, + usecurrentconfig=args.usecurrentconfig, + syntaxcheck=args.syntaxcheck, + currentlyup=args.currentlyup) From 0d25d6a97fce537d3c38c7498308dcb6e5ec04b6 Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Wed, 15 Feb 2023 14:22:52 +0100 Subject: [PATCH 051/132] sonarlink: merge if statements with enclosing ones Signed-off-by: Julien Fortin --- ifupdown2/ifupdown/iface.py | 5 ++--- ifupdown2/ifupdown/ifupdownmain.py | 25 +++++++++---------------- ifupdown2/lib/gvgen.py | 6 ++---- 3 files changed, 13 insertions(+), 23 deletions(-) diff --git a/ifupdown2/ifupdown/iface.py b/ifupdown2/ifupdown/iface.py index 7c697363..c759322a 100644 --- a/ifupdown2/ifupdown/iface.py +++ b/ifupdown2/ifupdown/iface.py @@ -745,9 +745,8 @@ def dump(self, logger): logger.info("%slink_privflags: %s" % (indent, ifaceLinkPrivFlags.get_str(self.link_privflags))) - if self.priv_flags: - if self.priv_flags.BUILTIN: - logger.info("%spriv_flags: BUILTIN" % indent) + if self.priv_flags and self.priv_flags.BUILTIN: + logger.info("%spriv_flags: BUILTIN" % indent) logger.info(indent + 'config: ') config = self.config diff --git a/ifupdown2/ifupdown/ifupdownmain.py b/ifupdown2/ifupdown/ifupdownmain.py index 64e53536..f101239e 100644 --- a/ifupdown2/ifupdown/ifupdownmain.py +++ b/ifupdown2/ifupdown/ifupdownmain.py @@ -451,10 +451,7 @@ def link_master_slave_ignore_error(self, errorstr): # bringing up a vlan on a bond interface and the bond # is a LINK_SLAVE of a bridge (in other words the bond is # part of a bridge) which is not up yet - if self._link_master_slave: - if 'Network is down' in errorstr: - return True - return False + return self._link_master_slave and 'Network is down' in errorstr def get_ifaceobjs(self, ifacename, all=False): if all: @@ -1018,9 +1015,8 @@ def _keyword_ipv4_vrf_text(self, value, validrange=None): if size > 1: if values[1] != 'vrf': return False - if size > 2: - if not self._keyword_text(values[2]): - return False + if size > 2 and not self._keyword_text(values[2]): + return False return True except Exception as e: self.logger.debug('keyword: ipv4 vrf text: %s' % str(e)) @@ -1029,9 +1025,8 @@ def _keyword_ipv4_vrf_text(self, value, validrange=None): def _keyword_interface_list_with_value(self, value, validvals): values = value.split() try: - if len(values) == 1: - if values[0] in validvals: - return True + if len(values) == 1 and values[0] in validvals: + return True for v in values: iface_value = v.split('=') size = len(iface_value) @@ -1128,9 +1123,8 @@ def _keyword_interface_range_list(self, value, validrange, multiple=None): % (values[0], '-'.join(validrange))) - if multiple is not None: - if n % multiple != 0: - raise invalidValueError('invalid value %s: must be a multiple of %s' % (n, multiple)) + if multiple is not None and n % multiple != 0: + raise invalidValueError('invalid value %s: must be a multiple of %s' % (n, multiple)) return True except invalidValueError as e: @@ -1154,9 +1148,8 @@ def _keyword_interface_range_list(self, value, validrange, multiple=None): iface_value[0], '-'.join(validrange))) - if multiple is not None: - if number % multiple != 0: - raise invalidValueError('invalid value %s: must be a multiple of %s' % (number, multiple)) + if multiple is not None and number % multiple != 0: + raise invalidValueError('invalid value %s: must be a multiple of %s' % (number, multiple)) return True except invalidValueError as e: diff --git a/ifupdown2/lib/gvgen.py b/ifupdown2/lib/gvgen.py index bb1b00d2..f39ba199 100644 --- a/ifupdown2/lib/gvgen.py +++ b/ifupdown2/lib/gvgen.py @@ -236,10 +236,8 @@ def collectUnlockedLeaves(self, parent): """ cl = [] for e in self.__nodes: - if e['parent'] == parent: - if not e['lock']: - cl.append(e) - + if e['parent'] == parent and not e['lock']: + cl.append(e) return cl def lockNode(self, node): From c6160b18912e49c120ba05d5d99f95471853d0a2 Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Wed, 15 Feb 2023 17:47:45 +0100 Subject: [PATCH 052/132] ifupdownmain: rename variable to not shadow python built-in Signed-off-by: Julien Fortin --- ifupdown2/ifupdown/ifupdownmain.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/ifupdown2/ifupdown/ifupdownmain.py b/ifupdown2/ifupdown/ifupdownmain.py index f101239e..5cb1af78 100644 --- a/ifupdown2/ifupdown/ifupdownmain.py +++ b/ifupdown2/ifupdown/ifupdownmain.py @@ -530,14 +530,14 @@ def get_ifaceobjcurr(self, ifacename, idx=0): def get_iface_refcnt(self, ifacename): """ Return iface ref count """ - max = 0 + max_ref = 0 ifaceobjs = self.get_ifaceobjs(ifacename) if not ifaceobjs: return 0 for i in ifaceobjs: - if i.refcnt > max: - max = i.refcnt - return max + if i.refcnt > max_ref: + max_ref = i.refcnt + return max_ref def is_iface_builtin_byname(self, ifacename): """ Returns true if iface name is a builtin interface. @@ -1185,9 +1185,9 @@ def _keyword_number_range_list(self, value, validrange=None): i = 0 while i < len(number_list): if '-' in number_list[i]: - range = number_list[i].split('-') - a = int(range[0]) - b = int(range[1]) + r = number_list[i].split('-') + a = int(r[0]) + b = int(r[1]) if a > b: return False else: From 29e7843b784fe4f6af5d4878cc1363e4fa3758f5 Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Thu, 31 Oct 2024 16:29:55 +0100 Subject: [PATCH 053/132] ifupdownmain: remove unnecessary try/except Signed-off-by: Julien Fortin --- ifupdown2/ifupdown/ifupdownmain.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/ifupdown2/ifupdown/ifupdownmain.py b/ifupdown2/ifupdown/ifupdownmain.py index 5cb1af78..c305c569 100644 --- a/ifupdown2/ifupdown/ifupdownmain.py +++ b/ifupdown2/ifupdown/ifupdownmain.py @@ -1784,10 +1784,8 @@ def up(self, ops, auto=False, allow_classes=None, ifacenames=None, if auto: ifupdownflags.flags.ALL = True ifupdownflags.flags.WITH_DEPENDS = True - try: - iface_read_ret = self.read_iface_config() - except Exception: - raise + + iface_read_ret = self.read_iface_config() if excludepats: excludepats = self._preprocess_excludepats(excludepats) From c416953ca9650956f8e223553caae3d38192a322 Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Wed, 30 Nov 2022 21:42:33 +0100 Subject: [PATCH 054/132] Bad use of null-like value (FORWARD_NULL) 6. invalid_operation: Invalid operation on null-like value Signed-off-by: Julien Fortin --- ifupdown2/lib/nlcache.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ifupdown2/lib/nlcache.py b/ifupdown2/lib/nlcache.py index 60a4a54b..711aeb6a 100644 --- a/ifupdown2/lib/nlcache.py +++ b/ifupdown2/lib/nlcache.py @@ -1052,7 +1052,7 @@ def get_pvid_and_vids(self, ifname): vlans = [] try: range_begin_vlan_id = None - range_flag = None + range_flag = 0 with self._cache_lock: bridge_vlans_tuples = self._bridge_vlan_cache.get(ifname) @@ -1079,7 +1079,7 @@ def get_pvid_and_vids(self, ifname): vlans.append(x) range_begin_vlan_id = None - range_flag = None + range_flag = 0 else: vlans.append(vlan_id) From 209899c34ee0b82fa00d158486529cc12a3772e3 Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Wed, 30 Nov 2022 21:47:57 +0100 Subject: [PATCH 055/132] nlmanager: Bad use of null-like value (FORWARD_NULL) 4. invalid_operation: Invalid operation on null-like value range_flag. Signed-off-by: Julien Fortin --- ifupdown2/nlmanager/nlmanager.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ifupdown2/nlmanager/nlmanager.py b/ifupdown2/nlmanager/nlmanager.py index 5e0cd8e2..0cc67c05 100644 --- a/ifupdown2/nlmanager/nlmanager.py +++ b/ifupdown2/nlmanager/nlmanager.py @@ -823,7 +823,7 @@ def vlan_flag_to_string(vlan_flag): iface_vlans = self.vlan_get(filter_ifindex, filter_vlanid, compress_vlans) log.debug("iface_vlans:\n%s\n" % pformat(iface_vlans)) range_begin_vlan_id = None - range_flag = None + range_flag = 0 print(" Interface VLAN Flags") print(" ========== ==== =====") @@ -847,7 +847,7 @@ def vlan_flag_to_string(vlan_flag): ifname = '' range_begin_vlan_id = None - range_flag = None + range_flag = 0 else: print(" %10s %4d %s" % (ifname, vlan_id, vlan_flag_to_string(vlan_flag))) From c8e19a1b46a1dd124ca0d6e3b82ecfce85620ac5 Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Mon, 15 Apr 2024 21:15:17 +0200 Subject: [PATCH 056/132] lib: log: disable persistent debug log for ifquery Signed-off-by: Julien Fortin --- ifupdown2/lib/log.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ifupdown2/lib/log.py b/ifupdown2/lib/log.py index 6a6f3e61..c270b89b 100644 --- a/ifupdown2/lib/log.py +++ b/ifupdown2/lib/log.py @@ -156,6 +156,11 @@ def __init_debug_logging(self): # check if enable_persistent_debug_logging is enabled user_config_limit = self.__get_enable_persistent_debug_logging() + # disable debug logging for ifquery + for s in sys.argv: + if "ifquery" in s: + return + if not user_config_limit: # user has disabled the feature return From e9cebfd17a943c8079056c7220795d61f2d97333 Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Thu, 31 Oct 2024 16:48:07 +0100 Subject: [PATCH 057/132] ifupdownmain: remove unnecessary try/except Signed-off-by: Julien Fortin --- ifupdown2/ifupdown/ifupdownmain.py | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/ifupdown2/ifupdown/ifupdownmain.py b/ifupdown2/ifupdown/ifupdownmain.py index c305c569..2c9c2759 100644 --- a/ifupdown2/ifupdown/ifupdownmain.py +++ b/ifupdown2/ifupdown/ifupdownmain.py @@ -2003,10 +2003,7 @@ def query(self, ops, auto=False, format_list=False, allow_classes=None, for i in ifacenames: self.create_n_save_ifaceobj(i, ifacePrivFlags(False, True)) else: - try: - iface_read_ret = self.read_iface_config(raw=ops[0] == "query-raw") - except Exception: - raise + iface_read_ret = self.read_iface_config(raw=ops[0] == "query-raw") if ifacenames and ops[0] != 'query-running': # If iface list is given, always check if iface is present @@ -2073,10 +2070,8 @@ def _reload_currentlyup(self, upops, downops, auto=False, allow=None, self.logger.info('reloading interfaces that are currently up ..') - try: - iface_read_ret = self.read_iface_config() - except Exception: - raise + iface_read_ret = self.read_iface_config() + if not self.ifaceobjdict: self.logger.warning("nothing to reload ..exiting.") return @@ -2167,10 +2162,8 @@ def _reload_default(self, upops, downops, auto=False, allow=None, """ reload interface config """ new_ifaceobjdict = {} - try: - iface_read_ret = self.read_iface_config() - except Exception: - raise + + iface_read_ret = self.read_iface_config() if not self.ifaceobjdict: self.logger.warning("nothing to reload ..exiting.") From 956bf604f8a58f1f6849827d44a7e648d64aaa9a Mon Sep 17 00:00:00 2001 From: Scott Laffer Date: Mon, 25 Jul 2022 11:41:22 -0700 Subject: [PATCH 058/132] json not imported in networkinterfaces.py The json library was previously imported into networkinterfaces.py through `from ifupdown.iface import *`, where json was explicitly imported. The above commit changed the iface import to only import specific objects. This change imports the correct libraries into the file itself. It also improves error handling around reading JSON formatted interface files. Signed-off-by: Julien Fortin --- ifupdown2/ifupdown/networkinterfaces.py | 28 +++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/ifupdown2/ifupdown/networkinterfaces.py b/ifupdown2/ifupdown/networkinterfaces.py index 26830079..be1e840e 100644 --- a/ifupdown2/ifupdown/networkinterfaces.py +++ b/ifupdown2/ifupdown/networkinterfaces.py @@ -14,6 +14,8 @@ import os import re +from json import loads, JSONDecodeError + try: from ifupdown2.ifupdown.iface import ifaceType, ifaceJsonDecoder, iface from ifupdown2.ifupdown.utils import utils @@ -490,16 +492,30 @@ def read_file(self, filename, fileiobuf=None): def read_file_json(self, filename, fileiobuf=None): if fileiobuf: - ifacedicts = json.loads(fileiobuf, encoding="utf-8") + ifacedicts = loads(fileiobuf, encoding="utf-8") #object_hook=ifaceJsonDecoder.json_object_hook) elif filename: - self.logger.info('processing interfaces file %s' %filename) - with open(filename) as fp: - ifacedicts = json.load(fp) - #object_hook=ifaceJsonDecoder.json_object_hook) + self.logger.info('processing JSON formatted interfaces file %s' % filename) + + # Check we can open and read the file + try: + with open(filename) as f: + filedata = f.read() + except Exception as e: + self.logger.warning('error processing file %s (%s)', + filename, str(e)) + return + + # Check we can decode JSON data from the file + try: + ifacedicts = loads(filedata) + except JSONDecodeError as e: + self.logger.warning('error loading JSON content from file %s (%s)', + filename, str(e)) + return # we need to handle both lists and non lists formats (e.g. {{}}) - if not isinstance(ifacedicts,list): + if not isinstance(ifacedicts, list): ifacedicts = [ifacedicts] errors = 0 From fae326cef0b0982a02cf653eaf77a1540c9768de Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Thu, 25 Jul 2024 18:13:21 +0200 Subject: [PATCH 059/132] policymanager: ignore "README" elements Signed-off-by: Julien Fortin --- ifupdown2/ifupdown/policymanager.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ifupdown2/ifupdown/policymanager.py b/ifupdown2/ifupdown/policymanager.py index 9825ff1c..d5c05d23 100644 --- a/ifupdown2/ifupdown/policymanager.py +++ b/ifupdown2/ifupdown/policymanager.py @@ -92,6 +92,8 @@ def __init__(self): self.logger.warning(' exception is %s' % str(e)) # customer added module attributes for module in list(user_array.keys()): + if module == "README": + continue if module in self.system_policy_array: # warn user that we are overriding the system module setting self.logger.debug('warning: overwriting system with user module %s from file %s' \ From e8c2bd1a8a36f99a80bf5191abcbae81debb80b4 Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Thu, 31 Oct 2024 16:56:52 +0100 Subject: [PATCH 060/132] statemanager: remove uncessary try/except Signed-off-by: Julien Fortin --- ifupdown2/ifupdown/statemanager.py | 37 ++++++++++++------------------ 1 file changed, 15 insertions(+), 22 deletions(-) diff --git a/ifupdown2/ifupdown/statemanager.py b/ifupdown2/ifupdown/statemanager.py index 60494499..69a54756 100644 --- a/ifupdown2/ifupdown/statemanager.py +++ b/ifupdown2/ifupdown/statemanager.py @@ -29,20 +29,16 @@ class pickling(): @classmethod def save(cls, filename, list_of_objects): """ pickle a list of iface objects """ - try: - with open(filename, 'wb') as f: - for obj in list_of_objects: - pickle.dump(obj, f, pickle.HIGHEST_PROTOCOL) - except Exception: - raise + with open(filename, 'wb') as f: + for obj in list_of_objects: + pickle.dump(obj, f, pickle.HIGHEST_PROTOCOL) + @classmethod def save_obj(cls, f, obj): """ pickle iface object """ - try: - pickle.dump(obj, f, pickle.HIGHEST_PROTOCOL) - except Exception: - raise + pickle.dump(obj, f, pickle.HIGHEST_PROTOCOL) + @classmethod def load(cls, filename): @@ -51,7 +47,6 @@ def load(cls, filename): while True: try: yield pickle.load(f) except EOFError: break - except Exception: raise class stateManager(): """ state manager for managing ifupdown iface obj state @@ -190,17 +185,15 @@ def ifaceobj_sync(self, ifaceobj, op): def save_state(self): """ saves state (ifaceobjects) to persistent state file """ - try: - with open(self.state_file, 'wb') as f: - if not len(self.ifaceobjdict): - f.truncate(0) - return - self.logger.debug('saving state ..') - for ifaceobjs in list(self.ifaceobjdict.values()): - [pickling.save_obj(f, i) for i in ifaceobjs] - open('%s/%s' %(self.state_rundir, self.state_runlockfile), 'w').close() - except Exception: - raise + with open(self.state_file, 'wb') as f: + if not len(self.ifaceobjdict): + f.truncate(0) + return + self.logger.debug('saving state ..') + for ifaceobjs in list(self.ifaceobjdict.values()): + [pickling.save_obj(f, i) for i in ifaceobjs] + open('%s/%s' % (self.state_rundir, self.state_runlockfile), 'w').close() + def dump_pretty(self, ifacenames, format='native'): if not ifacenames: From 84029915c9c814432b756ca1b66a0b3315f7204c Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Thu, 22 Dec 2022 00:55:13 +0100 Subject: [PATCH 061/132] log: dump sys.argv when log.debug is used Signed-off-by: Julien Fortin --- ifupdown2/lib/log.py | 1 + 1 file changed, 1 insertion(+) diff --git a/ifupdown2/lib/log.py b/ifupdown2/lib/log.py index c270b89b..7d03d265 100644 --- a/ifupdown2/lib/log.py +++ b/ifupdown2/lib/log.py @@ -199,6 +199,7 @@ def __init_debug_logging(self): self.__root_logger.setLevel(logging.DEBUG) self.__root_logger.debug("persistent debugging is initialized") + self.__root_logger.debug("argv: %s" % sys.argv) # cp ENI and ENI.d in the log directory shutil.copy2("/etc/network/interfaces", self.new_dir_path) From 7688f249db2a5b834464ab22e472f30428673280 Mon Sep 17 00:00:00 2001 From: Andy Roulin Date: Fri, 12 Apr 2024 09:48:19 -0700 Subject: [PATCH 062/132] iproute2: bridge vni only accepts "delete" not "del" Upstream iproute2 doesn't accept matches() for command arguments anymore so "del" will not be understood. The "delete" keyword must be used for "bridge vni". See [1] for reference. [1] https://lore.kernel.org/all/20220508045340.120653-4-roopa@nvidia.com/T/ [PATCH iproute2 net-next v2 0/3] support for vxlan vni filtering Signed-off-by: Andy Roulin Signed-off-by: Julien Fortin --- ifupdown2/lib/iproute2.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ifupdown2/lib/iproute2.py b/ifupdown2/lib/iproute2.py index b2dbb01b..f2d00452 100644 --- a/ifupdown2/lib/iproute2.py +++ b/ifupdown2/lib/iproute2.py @@ -980,15 +980,15 @@ def bridge_vni_add(self, vxlan_device, vni): ) def bridge_vni_int_set_del(self, vxlan_device, vni): - # bridge vni del understands ranges: - # bridge vni del dev vx0 vni 10,11,20-30 + # bridge vni delete understands ranges: + # bridge vni delete dev vx0 vni 10,11,20-30 self.__execute_or_batch( utils.bridge_cmd, - "vni del dev %s vni %s" % (vxlan_device, ','.join([str(x) for x in vni])) + "vni delete dev %s vni %s" % (vxlan_device, ','.join([str(x) for x in vni])) ) def bridge_vni_del_list(self, vxlandev, vnis): - cmd_args = "vni del dev %s vni %s" % (vxlandev, ','.join(vnis)) + cmd_args = "vni delete dev %s vni %s" % (vxlandev, ','.join(vnis)) self.__execute_or_batch(utils.bridge_cmd, cmd_args) def compress_vnifilter_into_ranges(self, vnis_ints, vnisd): From fff2ef5ce40b5322fb5f991237c3cf908486a7d2 Mon Sep 17 00:00:00 2001 From: Quentin Zilberberg Date: Mon, 5 Aug 2024 20:28:33 -0700 Subject: [PATCH 063/132] addons: address: fix traceback bridge: 'NoneType' object has no attribute 'items' ``` File "/usr/share/ifupdown2/addons/address.py", line 1167, in _pre_up self._process_bridge(ifaceobj, True, hwaddress, old_mac_addr) File "/usr/share/ifupdown2/addons/address.py", line 508, in _process_bridge for vlan, macs in fdbs.items(): ^^^^^^^^^^ AttributeError: 'NoneType' object has no attribute 'items' ``` This patch makes sure that fdbs is at least an empty dict Signed-off-by: Julien Fortin --- ifupdown2/addons/address.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ifupdown2/addons/address.py b/ifupdown2/addons/address.py index cb0d3832..c23dcf9c 100644 --- a/ifupdown2/addons/address.py +++ b/ifupdown2/addons/address.py @@ -1389,7 +1389,7 @@ def _get_bridge_fdbs(self, bridgename, vlan=None): if not fdbs: fdbs = self.iproute2.bridge_fdb_show_dev(bridgename) if not fdbs: - return + return {} self._bridge_fdb_query_cache[bridgename] = fdbs return fdbs.get(vlan) if vlan else fdbs From 93aebdf2caea9d6966b620689a886937b2b3894d Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Wed, 15 Feb 2023 23:30:42 +0100 Subject: [PATCH 064/132] sonarlink: replace generic exception with a more specific one Signed-off-by: Julien Fortin --- ifupdown2/addons/address.py | 10 ++--- ifupdown2/addons/addressvirtual.py | 1 - ifupdown2/addons/bond.py | 4 +- ifupdown2/addons/bridge.py | 18 ++++----- ifupdown2/addons/mstpctl.py | 6 +-- ifupdown2/addons/vlan.py | 20 +++++----- ifupdown2/addons/vrf.py | 6 +-- ifupdown2/addons/vxlan.py | 14 +++---- ifupdown2/ifupdown/argv.py | 10 +++-- ifupdown2/ifupdown/client.py | 6 ++- ifupdown2/ifupdown/graph.py | 6 ++- ifupdown2/ifupdown/ifupdownmain.py | 49 ++++++++++++++----------- ifupdown2/ifupdown/main.py | 8 +++- ifupdown2/ifupdown/networkinterfaces.py | 3 ++ ifupdown2/ifupdown/scheduler.py | 7 +++- ifupdown2/ifupdown/statemanager.py | 6 ++- ifupdown2/ifupdown/utils.py | 18 +++++---- ifupdown2/ifupdownaddons/modulebase.py | 15 +++++--- ifupdown2/lib/addon.py | 6 ++- ifupdown2/lib/io.py | 8 +++- ifupdown2/lib/iproute2.py | 6 ++- ifupdown2/lib/nlcache.py | 16 ++++---- 22 files changed, 147 insertions(+), 96 deletions(-) diff --git a/ifupdown2/addons/address.py b/ifupdown2/addons/address.py index c23dcf9c..92e27d93 100644 --- a/ifupdown2/addons/address.py +++ b/ifupdown2/addons/address.py @@ -13,7 +13,7 @@ from setuptools.dist import strtobool try: - from ifupdown2.lib.addon import AddonWithIpBlackList + from ifupdown2.lib.addon import AddonWithIpBlackList, AddonException from ifupdown2.nlmanager.nlmanager import Link from ifupdown2.ifupdown.iface import ifaceType, ifaceLinkKind, ifaceLinkPrivFlags, ifaceStatus, iface @@ -29,7 +29,7 @@ import ifupdown2.ifupdown.ifupdownflags as ifupdownflags import ifupdown2.ifupdown.ifupdownconfig as ifupdownconfig except ImportError: - from lib.addon import AddonWithIpBlackList + from lib.addon import AddonWithIpBlackList, AddonException from nlmanager.nlmanager import Link from ifupdown.iface import ifaceType, ifaceLinkKind, ifaceLinkPrivFlags, ifaceStatus, iface @@ -377,12 +377,12 @@ def syntax_check_enable_l3_iface_forwardings(self, ifaceobj, ifaceobj_getfunc, s if vlan_addr and vlan_ipforward_off: if syntax_check: - raise Exception( + raise AddonException( 'configuring ip-forward off and ip address(es) (%s) is not compatible' % (', '.join(vlan_addr)) ) else: - raise Exception( + raise AddonException( '%s: configuring ip-forward off and ip address(es) (%s) is not compatible' % (ifname, ', '.join(vlan_addr)) ) @@ -423,7 +423,7 @@ def syntax_check_addr_allowed_on(self, ifaceobj, syntax_check=False): def _syntax_check_multiple_gateway(self, family, found, addr, version): if ipnetwork.IPNetwork(addr).version == version: if found: - raise Exception('%s: multiple gateways for %s family' + raise AddonException('%s: multiple gateways for %s family' % (addr, family)) return True return False diff --git a/ifupdown2/addons/addressvirtual.py b/ifupdown2/addons/addressvirtual.py index d11a1e08..421f099c 100644 --- a/ifupdown2/addons/addressvirtual.py +++ b/ifupdown2/addons/addressvirtual.py @@ -411,7 +411,6 @@ def sync_macvlan_forwarding_state(self, ifname, macvlan_ifname): self.logger.info("%s: syncing macvlan forwarding with lower device forwarding state failed: %s" % (ifname, str(e))) def create_macvlan_and_apply_config(self, ifaceobj, intf_config_list, vrrp=False): - """ intf_config_list = [ { diff --git a/ifupdown2/addons/bond.py b/ifupdown2/addons/bond.py index 2ca77c47..dcccf5ab 100644 --- a/ifupdown2/addons/bond.py +++ b/ifupdown2/addons/bond.py @@ -390,7 +390,7 @@ def valid_slave_speed(self, ifaceobj, bond_slaves, slave, ifaceobj_getfunc): try: self.compare_bond_and_slave_speed(ifaceobj, slave, int(self.read_file_oneline(f"/sys/class/net/{slave}/speed"))) - except: + except Exception: try: match = self.speed_pattern.search(utils.exec_commandl(["/usr/sbin/ethtool", f"{slave}"])) if match: @@ -409,7 +409,7 @@ def get_bond_speed(self, runningslaves): continue try: slave_speed = int(self.read_file_oneline(f"/sys/class/net/{slave}/speed")) - except: + except Exception: slave_speed = -1 if bond_speed < 0: diff --git a/ifupdown2/addons/bridge.py b/ifupdown2/addons/bridge.py index 58d2279b..227b17c3 100644 --- a/ifupdown2/addons/bridge.py +++ b/ifupdown2/addons/bridge.py @@ -11,7 +11,7 @@ from collections import Counter try: - from ifupdown2.lib.addon import Bridge + from ifupdown2.lib.addon import Bridge, AddonException import ifupdown2.ifupdown.exceptions as exceptions import ifupdown2.ifupdown.policymanager as policymanager @@ -25,7 +25,7 @@ from ifupdown2.ifupdownaddons.cache import * from ifupdown2.ifupdownaddons.modulebase import moduleBase except ImportError: - from lib.addon import Bridge + from lib.addon import Bridge, AddonException import ifupdown.exceptions as exceptions import ifupdown.policymanager as policymanager @@ -1706,7 +1706,7 @@ def config_check_bridge_vni_svi_limit(self, vxlan_brport_obj, ifaceobj_getfunc, access = brport_obj.get_attr_value_first("bridge-access") if access == pvid: - raise Exception( + raise AddonException( "%s: misconfiguration detected: vlan \"%s\" added to two or more VXLANS (%s, %s)" % ( ifname, access, @@ -1996,7 +1996,7 @@ def _apply_bridge_port_settings_all(self, ifaceobj, ifaceobj_getfunc, bridge_vla self.logger.warning('%s: %s' %(ifaceobj.name, str(e))) self.iproute2.batch_commit() if err: - raise Exception('%s: errors applying port settings' %ifaceobj.name) + raise AddonException('%s: errors applying port settings' %ifaceobj.name) def _check_untagged_bridge(self, bridgename, bridgeportifaceobj, ifaceobj_getfunc): if bridgeportifaceobj.link_kind & ifaceLinkKind.VLAN: @@ -3669,7 +3669,7 @@ def _query_check_bridge_port_vidinfo(self, ifname, bridge_name, ifaceobj, ifaceo vid_int = int(brport_vid_access_user_config) except ValueError as e: ifaceobjcurr.update_config_with_status("bridge-access", brport_vid_access_user_config, 1) - raise Exception("%s: bridge-access invalid value: %s" % (ifname, str(e))) + raise AddonException("%s: bridge-access invalid value: %s" % (ifname, str(e))) ifaceobjcurr.update_config_with_status( "bridge-access", @@ -3688,7 +3688,7 @@ def _query_check_bridge_port_vidinfo(self, ifname, bridge_name, ifaceobj, ifaceo pvid = int(brport_pvid_user_config) except ValueError as e: ifaceobjcurr.update_config_with_status("bridge-pvid", brport_pvid_user_config, 1) - raise Exception("%s: bridge-pvid invalid value: %s" % (ifname, str(e))) + raise AddonException("%s: bridge-pvid invalid value: %s" % (ifname, str(e))) ifaceobjcurr.update_config_with_status( "bridge-pvid", @@ -3869,7 +3869,7 @@ def _query_check_bridge_port(self, ifaceobj, ifaceobjcurr, ) except ValueError as e: ifaceobjcurr.update_config_with_status(attr_name, str(cached_value), 1) - raise Exception("%s: %s invalid value: %s" % (ifname, attr_name, str(e))) + raise AddonException("%s: %s invalid value: %s" % (ifname, attr_name, str(e))) self._query_check_l2protocol_tunnel_on_port(ifaceobj, ifaceobjcurr) @@ -3991,7 +3991,7 @@ def _query_check_l2protocol_tunnel(self, brport_name, user_config_l2protocol_tun callback = self.query_check_l2protocol_tunnel_callback.get(protocol) if callable(callback) and not callback(cached_ifla_brport_group_mask, cached_ifla_brport_group_maskhi): - raise Exception( + raise AddonException( "%s: bridge-l2protocol-tunnel: protocol '%s' not present (cached value: %d | %d)" % (brport_name, protocol, cached_ifla_brport_group_mask, cached_ifla_brport_group_maskhi) ) @@ -4148,7 +4148,7 @@ def _query_running(self, ifaceobjrunning, ifaceobj_getfunc=None): elif self.cache.link_is_bridge_port(ifaceobjrunning.name): self._query_running_bridge_port(ifaceobjrunning, ifaceobj_getfunc) except Exception as e: - raise Exception('%s: %s' % (ifaceobjrunning.name, str(e))) + raise AddonException('%s: %s' % (ifaceobjrunning.name, str(e))) def _query(self, ifaceobj, **kwargs): """ add default policy attributes supported by the module """ diff --git a/ifupdown2/addons/mstpctl.py b/ifupdown2/addons/mstpctl.py index b661173b..75dcee76 100644 --- a/ifupdown2/addons/mstpctl.py +++ b/ifupdown2/addons/mstpctl.py @@ -9,7 +9,7 @@ from collections import OrderedDict try: - from ifupdown2.lib.addon import Addon + from ifupdown2.lib.addon import Addon, AddonException from ifupdown2.ifupdown.iface import ifaceType, ifaceLinkKind, ifaceLinkPrivFlags, ifaceStatus from ifupdown2.ifupdown.utils import utils @@ -22,7 +22,7 @@ from ifupdown2.ifupdownaddons.systemutils import systemUtils from ifupdown2.ifupdown.exceptions import moduleNotSupported except ImportError: - from lib.addon import Addon + from lib.addon import Addon, AddonException from ifupdown.iface import ifaceType, ifaceLinkKind, ifaceLinkPrivFlags, ifaceStatus from ifupdown.utils import utils @@ -742,7 +742,7 @@ def _up(self, ifaceobj, ifaceobj_getfunc=None): except Exception as e: self.log_error(str(e), ifaceobj) if porterr: - raise Exception(porterrstr) + raise AddonException(porterrstr) def _down(self, ifaceobj, ifaceobj_getfunc=None): if not self._is_bridge(ifaceobj): diff --git a/ifupdown2/addons/vlan.py b/ifupdown2/addons/vlan.py index 95fe457a..8b05b225 100644 --- a/ifupdown2/addons/vlan.py +++ b/ifupdown2/addons/vlan.py @@ -5,7 +5,7 @@ # try: - from ifupdown2.lib.addon import Addon + from ifupdown2.lib.addon import Addon, AddonException from ifupdown2.ifupdown.iface import ifaceType, ifaceLinkKind, ifaceStatus from ifupdown2.nlmanager.nlmanager import Link from ifupdown2.ifupdownaddons.modulebase import moduleBase @@ -14,7 +14,7 @@ import ifupdown2.ifupdown.ifupdownflags as ifupdownflags import ifupdown2.ifupdown.policymanager as policymanager except ImportError: - from lib.addon import Addon + from lib.addon import Addon, AddonException from ifupdown.iface import ifaceType, ifaceLinkKind, ifaceStatus from nlmanager.nlmanager import Link from ifupdownaddons.modulebase import moduleBase @@ -130,10 +130,10 @@ def _bridge_vid_check(self, ifaceobjcurr, bridgename, vlanid): def _up(self, ifaceobj): vlanid = self._get_vlan_id(ifaceobj) if vlanid == -1: - raise Exception('could not determine vlanid') + raise AddonException('could not determine vlanid') vlanrawdevice = self._get_vlan_raw_device(ifaceobj) if not vlanrawdevice: - raise Exception('could not determine vlan raw device') + raise AddonException('could not determine vlan raw device') ifname = ifaceobj.name @@ -159,14 +159,14 @@ def _up(self, ifaceobj): vlan_protocol = self.get_attr_default_value('vlan-protocol') if cached_vlan_protocol and vlan_protocol.lower() != cached_vlan_protocol.lower(): - raise Exception('%s: cannot change vlan-protocol to %s: operation not supported. ' + raise AddonException('%s: cannot change vlan-protocol to %s: operation not supported. ' 'Please delete the device with \'ifdown %s\' and recreate it to ' 'apply the change.' % (ifaceobj.name, vlan_protocol, ifaceobj.name)) cached_vlan_id = cached_vlan_ifla_info_data.get(Link.IFLA_VLAN_ID) if cached_vlan_id is not None and vlanid != cached_vlan_id: - raise Exception('%s: cannot change vlan-id to %s: operation not supported. ' + raise AddonException('%s: cannot change vlan-id to %s: operation not supported. ' 'Please delete the device with \'ifdown %s\' and recreate it to ' 'apply the change.' % (ifaceobj.name, vlanid, ifaceobj.name)) @@ -180,7 +180,7 @@ def _up(self, ifaceobj): cached_vlan_raw_device = self.cache.get_lower_device_ifname(ifname) if cached_vlan_raw_device and user_vlan_raw_device and cached_vlan_raw_device != user_vlan_raw_device: - raise Exception('%s: cannot change vlan-raw-device from %s to %s: operation not supported. ' + raise AddonException('%s: cannot change vlan-raw-device from %s to %s: operation not supported. ' 'Please delete the device with \'ifdown %s\' and recreate it to apply the change.' % (ifaceobj.name, cached_vlan_raw_device, user_vlan_raw_device, ifaceobj.name)) @@ -188,7 +188,7 @@ def _up(self, ifaceobj): if ifupdownflags.flags.DRYRUN: return else: - raise Exception('rawdevice %s not present' % vlanrawdevice) + raise AddonException('rawdevice %s not present' % vlanrawdevice) if vlan_exists: # vlan-bridge-binding has changed we need to update it @@ -209,10 +209,10 @@ def _up(self, ifaceobj): def _down(self, ifaceobj): vlanid = self._get_vlan_id(ifaceobj) if vlanid == -1: - raise Exception('could not determine vlanid') + raise AddonException('could not determine vlanid') vlanrawdevice = self._get_vlan_raw_device(ifaceobj) if not vlanrawdevice: - raise Exception('could not determine vlan raw device') + raise AddonException('could not determine vlan raw device') if not ifupdownflags.flags.PERFMODE and not self.cache.link_exists(ifaceobj.name): return try: diff --git a/ifupdown2/addons/vrf.py b/ifupdown2/addons/vrf.py index dd719bf1..712c5fd0 100644 --- a/ifupdown2/addons/vrf.py +++ b/ifupdown2/addons/vrf.py @@ -11,7 +11,7 @@ import signal try: - from ifupdown2.lib.addon import Addon + from ifupdown2.lib.addon import Addon, AddonException import ifupdown2.ifupdown.policymanager as policymanager import ifupdown2.ifupdown.ifupdownflags as ifupdownflags @@ -26,7 +26,7 @@ from ifupdown2.ifupdownaddons.utilsbase import * from ifupdown2.ifupdownaddons.modulebase import moduleBase except ImportError: - from lib.addon import Addon + from lib.addon import Addon, AddonException import ifupdown.policymanager as policymanager import ifupdown.ifupdownflags as ifupdownflags @@ -688,7 +688,7 @@ def _add_vrf_slaves(self, ifaceobj, ifaceobj_getfunc=None): if slave_ifaceobj.link_privflags & ifaceLinkPrivFlags.KEEP_LINK_DOWN or self.check_link_down_on_vlan_lower_dev( slave_ifaceobj, ifaceobj_getfunc ): - raise Exception("link-down yes: keeping VRF slave down") + raise AddonException("link-down yes: keeping VRF slave down") self.netlink.link_up(s) except Exception as e: self.logger.debug("%s: %s" % (s, str(e))) diff --git a/ifupdown2/addons/vxlan.py b/ifupdown2/addons/vxlan.py index fec11135..a638d293 100644 --- a/ifupdown2/addons/vxlan.py +++ b/ifupdown2/addons/vxlan.py @@ -12,7 +12,7 @@ import ifupdown2.ifupdown.policymanager as policymanager import ifupdown2.ifupdown.ifupdownflags as ifupdownflags - from ifupdown2.lib.addon import Vxlan + from ifupdown2.lib.addon import Vxlan, AddonException from ifupdown2.lib.nlcache import NetlinkCacheIfnameNotFoundError from ifupdown2.nlmanager.nlmanager import Link @@ -28,7 +28,7 @@ import ifupdown.policymanager as policymanager import ifupdown.ifupdownflags as ifupdownflags - from lib.addon import Vxlan + from lib.addon import Vxlan, AddonException from lib.nlcache import NetlinkCacheIfnameNotFoundError from nlmanager.nlmanager import Link @@ -555,7 +555,7 @@ def __config_vxlan_local_tunnelip(self, ifname, ifaceobj, link_exists, user_requ self.logger.warning("%s: vxlan-local-tunnelip %s: netmask ignored" % (ifname, local)) except Exception as e: - raise Exception("%s: invalid vxlan-local-tunnelip %s: %s" % (ifname, local, str(e))) + raise AddonException("%s: invalid vxlan-local-tunnelip %s: %s" % (ifname, local, str(e))) if local: @@ -720,7 +720,7 @@ def __config_vxlan_group(self, ifname, ifaceobj, link_exists, mcast_grp, group, self.logger.warning("%s: vxlan-svcnodeip %s: netmask ignored" % (ifname, group)) except Exception as e: - raise Exception("%s: invalid vxlan-svcnodeip %s: %s" % (ifname, group, str(e))) + raise AddonException("%s: invalid vxlan-svcnodeip %s: %s" % (ifname, group, str(e))) if group.ip.is_multicast: self.logger.warning("%s: vxlan-svcnodeip %s: invalid group address, " @@ -739,7 +739,7 @@ def __config_vxlan_group(self, ifname, ifaceobj, link_exists, mcast_grp, group, self.logger.warning("%s: vxlan-mcastgrp %s: netmask ignored" % (ifname, mcast_grp)) except Exception as e: - raise Exception("%s: invalid vxlan-mcastgrp %s: %s" % (ifname, mcast_grp, str(e))) + raise AddonException("%s: invalid vxlan-mcastgrp %s: %s" % (ifname, mcast_grp, str(e))) if not mcast_grp.ip.is_multicast: self.logger.warning("%s: vxlan-mcastgrp %s: invalid group address, " @@ -820,7 +820,7 @@ def __config_vxlan_group6(self, ifname, ifaceobj, link_exists, mcast_grp, group, self.logger.warning("%s: vxlan-svcnodeip6 %s: netmask ignored" % (ifname, group)) group = group_ip except Exception: - raise Exception("%s: invalid vxlan-svcnodeip6 %s: must be in ipv4 format" % (ifname, group)) + raise AddonException("%s: invalid vxlan-svcnodeip6 %s: must be in ipv4 format" % (ifname, group)) if group.is_multicast: self.logger.warning("%s: vxlan-svcnodeip6 %s: invalid group address, " @@ -840,7 +840,7 @@ def __config_vxlan_group6(self, ifname, ifaceobj, link_exists, mcast_grp, group, self.logger.warning("%s: vxlan-mcastgrp6 %s: netmask ignored" % (ifname, mcast_grp)) mcast_grp = group_ip except Exception: - raise Exception("%s: invalid vxlan-mcastgrp6 %s: must be in ipv4 format" % (ifname, mcast_grp)) + raise AddonException("%s: invalid vxlan-mcastgrp6 %s: must be in ipv4 format" % (ifname, mcast_grp)) if not mcast_grp.is_multicast: self.logger.warning("%s: vxlan-mcastgrp6 %s: invalid group address, " diff --git a/ifupdown2/ifupdown/argv.py b/ifupdown2/ifupdown/argv.py index 042c27cd..317d31b7 100644 --- a/ifupdown2/ifupdown/argv.py +++ b/ifupdown2/ifupdown/argv.py @@ -17,6 +17,10 @@ from ifupdown.exceptions import ArgvParseError, ArgvParseHelp +class ArgvException(Exception): + pass + + class VersionAction(argparse.Action): def __call__(self, parser, namespace, values, option_string=None): @@ -24,12 +28,12 @@ def __call__(self, parser, namespace, values, option_string=None): dpkg = utils.exec_commandl([utils.dpkg_cmd, '-l', 'ifupdown2']) if not dpkg: - raise Exception('dpkg -l ifupdown2 returns without output') + raise ArgvException('dpkg -l ifupdown2 returns without output') dpkg = dpkg.split('\n') if not dpkg: - raise Exception('dpkg -l ifupdown2 returns without output') + raise ArgvException('dpkg -l ifupdown2 returns without output') for line in dpkg: if 'ifupdown2' in line: @@ -38,7 +42,7 @@ def __call__(self, parser, namespace, values, option_string=None): sys.stdout.write('ifupdown2:%s\n' % (info[2])) sys.exit(0) - raise Exception('ifupdown2 package not found using dpkg -l') + raise ArgvException('ifupdown2 package not found using dpkg -l') except Exception as e: sys.stderr.write('error: cannot get current version using dpkg: %s\n' % str(e)) diff --git a/ifupdown2/ifupdown/client.py b/ifupdown2/ifupdown/client.py index cbf51d88..63556dd3 100644 --- a/ifupdown2/ifupdown/client.py +++ b/ifupdown2/ifupdown/client.py @@ -97,6 +97,10 @@ def __init__( socketserver.TCPServer.__init__(self, (host, port), handler) +class ClientException(Exception): + pass + + class Client(SocketIO): def __init__(self, argv): SocketIO.__init__(self) @@ -163,7 +167,7 @@ def __init__(self, argv): self.uds.setsockopt(socket.SOL_SOCKET, self.SO_PASSCRED, 1) except Exception as e: self.__shutdown() - raise Exception("setsockopt: %s" % str(e)) + raise ClientException("setsockopt: %s" % str(e)) self.daemon_pid, _, _ = self.get_socket_peer_cred(self.uds) diff --git a/ifupdown2/ifupdown/graph.py b/ifupdown2/ifupdown/graph.py index 7e70342a..40467c40 100644 --- a/ifupdown2/ifupdown/graph.py +++ b/ifupdown2/ifupdown/graph.py @@ -19,6 +19,10 @@ from lib.gvgen import GvGen +class GraphException(Exception): + pass + + class graph(): """ graph functions to sort and print interface graph """ @@ -66,7 +70,7 @@ def topological_sort_graphs_all(cls, dependency_graphs, indegrees_arg): for ifname,indegree in list(indegrees.items()): if indegree != 0: - raise Exception('cycle found involving iface %s' %ifname + + raise GraphException('cycle found involving iface %s' %ifname + ' (indegree %d)' %indegree) return S diff --git a/ifupdown2/ifupdown/ifupdownmain.py b/ifupdown2/ifupdown/ifupdownmain.py index 2c9c2759..ab08e79b 100644 --- a/ifupdown2/ifupdown/ifupdownmain.py +++ b/ifupdown2/ifupdown/ifupdownmain.py @@ -68,6 +68,11 @@ _success_sym = '(%s)' %_tickmark _error_sym = '(%s)' %_crossmark + +class MainException(Exception): + pass + + class ifupdownMainFlags(): COMPAT_EXEC_SCRIPTS = False STATEMANAGER_ENABLE = True @@ -1237,7 +1242,7 @@ def _is_keyword(self, value): return True keyword_found = value in self.validate_keywords if value.startswith('<') and value.endswith('>') and not keyword_found: - raise Exception('%s: invalid keyword, please make sure to use' + raise MainException('%s: invalid keyword, please make sure to use' ' a valid keyword see `ifquery -s`' % value) return keyword_found @@ -1262,7 +1267,7 @@ def _check_validvals_value(self, value, validvals, validrange): pass elif validrange: if len(validrange) != 2: - raise Exception('%s: invalid range in addon configuration' + raise MainException('%s: invalid range in addon configuration' % '-'.join(validrange)) _value = int(value) if _value < int(validrange[0]) or _value > int(validrange[1]): @@ -1601,7 +1606,7 @@ def _preprocess_ifacenames(self, ifacenames): else: new_ifacenames.append(i) if err_iface: - raise Exception('cannot find interfaces:%s' %err_iface) + raise MainException('cannot find interfaces:%s' %err_iface) return new_ifacenames def _iface_whitelisted(self, auto, allow_classes, excludepats, ifacename): @@ -1807,7 +1812,7 @@ def up(self, ops, auto=False, allow_classes=None, ifacenames=None, excludepats, i)] if not filtered_ifacenames: - raise Exception('no ifaces found matching given allow lists') + raise MainException('no ifaces found matching given allow lists') if printdependency: self.populate_dependency_info(ops, filtered_ifacenames) @@ -1821,11 +1826,11 @@ def up(self, ops, auto=False, allow_classes=None, ifacenames=None, # errors above are caught and reported. if syntaxcheck: if not self._module_syntax_check(filtered_ifacenames): - raise Exception() + raise MainException() if not iface_read_ret: - raise Exception() + raise MainException() elif self._any_iface_errors(filtered_ifacenames): - raise Exception() + raise MainException() return ret = None @@ -1841,7 +1846,7 @@ def up(self, ops, auto=False, allow_classes=None, ifacenames=None, self._save_state() if not iface_read_ret or not ret: - raise Exception() + raise MainException() self.check_running_configuration(filtered_ifacenames) @@ -1920,7 +1925,7 @@ def down(self, ops, auto=False, allow_classes=None, ifacenames=None, try: self.read_iface_config() except Exception as e: - raise Exception('error reading iface config (%s)' %str(e)) + raise MainException('error reading iface config (%s)' %str(e)) if excludepats: excludepats = self._preprocess_excludepats(excludepats) @@ -1936,7 +1941,7 @@ def down(self, ops, auto=False, allow_classes=None, ifacenames=None, filtered_ifacenames = self._get_filtered_ifacenames_with_classes(auto, allow_classes, excludepats, ifacenames) except Exception as e: - raise Exception('%s' %str(e) + + raise MainException('%s' %str(e) + ' (interface was probably never up ?)') @@ -1950,7 +1955,7 @@ def down(self, ops, auto=False, allow_classes=None, ifacenames=None, excludepats, i)] if not filtered_ifacenames: - raise Exception('no ifaces found matching given allow lists ' + + raise MainException('no ifaces found matching given allow lists ' + '(or interfaces were probably never up ?)') if printdependency: @@ -2029,7 +2034,7 @@ def query(self, ops, auto=False, format_list=False, allow_classes=None, ] if not filtered_ifacenames: - raise Exception('no ifaces found matching ' + + raise MainException('no ifaces found matching ' + 'given allow lists') self.populate_dependency_info(ops) @@ -2054,13 +2059,13 @@ def query(self, ops, auto=False, format_list=False, allow_classes=None, elif ops[0] == 'query-checkcurr': if self.print_ifaceobjscurr_pretty(filtered_ifacenames, format): # if any of the object has an error, signal that silently - raise Exception('') + raise MainException('') elif ops[0] == 'query-running': self.print_ifaceobjsrunning_pretty(filtered_ifacenames, format) return if not iface_read_ret or not ret: - raise Exception() + raise MainException() def _reload_currentlyup(self, upops, downops, auto=False, allow=None, ifacenames=None, excludepats=None, usecurrentconfig=False, @@ -2099,11 +2104,11 @@ def _reload_currentlyup(self, upops, downops, auto=False, allow=None, # errors above are caught and reported. if syntaxcheck: if not self._module_syntax_check(interfaces_to_up): - raise Exception() + raise MainException() if not iface_read_ret: - raise Exception() + raise MainException() elif self._any_iface_errors(interfaces_to_up): - raise Exception() + raise MainException() return if (already_up_ifacenames_not_present and @@ -2154,7 +2159,7 @@ def _reload_currentlyup(self, upops, downops, auto=False, allow=None, self._save_state() if not iface_read_ret or not ret: - raise Exception() + raise MainException() def _reload_default(self, upops, downops, auto=False, allow=None, ifacenames=None, excludepats=None, usecurrentconfig=False, @@ -2181,11 +2186,11 @@ def _reload_default(self, upops, downops, auto=False, allow=None, # errors above are caught and reported. if syntaxcheck: if not self._module_syntax_check(new_filtered_ifacenames): - raise Exception() + raise MainException() if not iface_read_ret: - raise Exception() + raise MainException() elif self._any_iface_errors(new_filtered_ifacenames): - raise Exception() + raise MainException() return if (not usecurrentconfig and self.flags.STATEMANAGER_ENABLE @@ -2409,7 +2414,7 @@ def _reload_default(self, upops, downops, auto=False, allow=None, self._save_state() if not iface_read_ret or not ret: - raise Exception() + raise MainException() def reload(self, *args, **kargs): """ reload interface config """ diff --git a/ifupdown2/ifupdown/main.py b/ifupdown2/ifupdown/main.py index 0ff7e256..222bfc46 100644 --- a/ifupdown2/ifupdown/main.py +++ b/ifupdown2/ifupdown/main.py @@ -36,6 +36,10 @@ configmap_g = None +class MainException(Exception): + pass + + class Ifupdown2: def __init__(self, daemon, uid): self.daemon = daemon @@ -62,7 +66,7 @@ def parse_argv(self, argv): def main(self, stdin_buffer=None): if self.op != 'query' and self.uid != 0: - raise Exception('must be root to run this command') + raise MainException('must be root to run this command') try: self.read_config() @@ -115,7 +119,7 @@ def init(self, stdin_buffer): configmap_g.get('disable_cli_interfacesfile', '0') == '1'): log.error('disable_cli_interfacesfile is set so users ' 'not allowed to specify interfaces file on cli.') - raise Exception("") + raise MainException("") if self.args.interfacesfile == '-': # If interfaces file is stdin, read if self.daemon: diff --git a/ifupdown2/ifupdown/networkinterfaces.py b/ifupdown2/ifupdown/networkinterfaces.py index be1e840e..c0c8ad96 100644 --- a/ifupdown2/ifupdown/networkinterfaces.py +++ b/ifupdown2/ifupdown/networkinterfaces.py @@ -28,6 +28,9 @@ whitespaces = '\n\t\r ' +class ENIException(Exception): + pass + class networkInterfaces(): """ debian ifupdown /etc/network/interfaces file parser """ diff --git a/ifupdown2/ifupdown/scheduler.py b/ifupdown2/ifupdown/scheduler.py index ea51be34..e71f7355 100644 --- a/ifupdown2/ifupdown/scheduler.py +++ b/ifupdown2/ifupdown/scheduler.py @@ -32,6 +32,9 @@ import ifupdown.policymanager as policymanager +class SchedulerException(Exception): + pass + class ifaceSchedulerFlags(): """ Enumerates scheduler flags """ @@ -254,7 +257,7 @@ def run_iface_graph(cls, ifupdownobj, ifacename, ops, parent=None, # Each ifacename can have a list of iface objects ifaceobjs = ifupdownobj.get_ifaceobjs(ifacename) if not ifaceobjs: - raise Exception('%s: not found' %ifacename) + raise SchedulerException('%s: not found' %ifacename) # Check state of the dependent. If it is already brought up, return if (cls._STATE_CHECK and @@ -344,7 +347,7 @@ def run_iface_graph_upper(cls, ifupdownobj, ifacename, ops, parent=None, # Each ifacename can have a list of iface objects ifaceobjs = ifupdownobj.get_ifaceobjs(ifacename) if not ifaceobjs: - raise Exception('%s: not found' %ifacename) + raise SchedulerException('%s: not found' %ifacename) if (cls._STATE_CHECK and (ifaceobjs[0].state == ifaceState.from_str(ops[-1]))): diff --git a/ifupdown2/ifupdown/statemanager.py b/ifupdown2/ifupdown/statemanager.py index 69a54756..19d63e64 100644 --- a/ifupdown2/ifupdown/statemanager.py +++ b/ifupdown2/ifupdown/statemanager.py @@ -23,6 +23,10 @@ import ifupdown.ifupdownconfig as ifupdownConfig +class StateManagerException(Exception): + pass + + class pickling(): """ class with helper methods for pickling/unpickling iface objects """ @@ -100,7 +104,7 @@ def init(self): try: self._init_makedirs_state_dir() except Exception as e: - raise Exception("statemanager: unable to create required directory: %s" % str(e)) + raise StateManagerException("statemanager: unable to create required directory: %s" % str(e)) if not os.path.exists(self.state_rundir): os.makedirs(self.state_rundir) diff --git a/ifupdown2/ifupdown/utils.py b/ifupdown2/ifupdown/utils.py index 4465ce48..53d0fb1a 100644 --- a/ifupdown2/ifupdown/utils.py +++ b/ifupdown2/ifupdown/utils.py @@ -37,6 +37,11 @@ def signal_handler_f(ps, sig, frame): if sig == signal.SIGINT: raise KeyboardInterrupt + +class UtilsException(Exception): + pass + + class utils(): logger = logging.getLogger('ifupdown') DEVNULL = open(os.devnull, 'w') @@ -376,14 +381,14 @@ def _execute_subprocess(cls, cmd, cmd_output = ch.communicate(input=stdin.encode() if stdin else stdin)[0] cmd_returncode = ch.wait() except Exception as e: - raise Exception('cmd \'%s\' failed (%s)' % (' '.join(cmd), str(e))) + raise UtilsException('cmd \'%s\' failed (%s)' % (' '.join(cmd), str(e))) finally: utils.disable_subprocess_signal_forwarding(signal.SIGINT) cmd_output_string = cmd_output.decode() if cmd_output is not None else cmd_output if cmd_returncode != 0: - raise Exception(cls._format_error(cmd, + raise UtilsException(cls._format_error(cmd, cmd_returncode, cmd_output_string, stdin)) @@ -505,7 +510,7 @@ def get_vlan_vni_in_map_entry(cls, vlan_vni_map_entry): vni = vni.split('+', 1)[1] vint = int(vni) if vint < 0: - raise Exception("invalid auto vni suffix %d" % (vint)) + raise UtilsException("invalid auto vni suffix %d" % (vint)) if '-' in vlan: (vstart, vend) = vlan.split('-', 1) vnistart = int(vstart) + vint @@ -516,7 +521,7 @@ def get_vlan_vni_in_map_entry(cls, vlan_vni_map_entry): vni = vni.split('-', 1)[1] vint = int(vni) if vint < 0: - raise Exception("invalid auto vni suffix %d" % (vint)) + raise UtilsException("invalid auto vni suffix %d" % (vint)) if '-' in vlan: (vstart, vend) = vlan.split('-', 1) vnistart = int(vstart) - vint @@ -525,14 +530,13 @@ def get_vlan_vni_in_map_entry(cls, vlan_vni_map_entry): vnistart = int(vlan) - vint if (vnistart <= 0 or (vniend > 0 and (vniend < vnistart)) or (vnistart > cls.vni_max) or (vniend > cls.vni_max)): - raise Exception("invalid vni - unable to derive auto vni %s" % (vni)) + raise UtilsException("invalid vni - unable to derive auto vni %s" % (vni)) if vniend > 0: vni = '%d-%d' % (vnistart, vniend) else: vni = '%d' % (vnistart) except Exception as e: - raise Exception(str(e)) - return + raise UtilsException(str(e)) return (vlan, vni) @classmethod diff --git a/ifupdown2/ifupdownaddons/modulebase.py b/ifupdown2/ifupdownaddons/modulebase.py index 36c7ff5d..3bfc1e0f 100644 --- a/ifupdown2/ifupdownaddons/modulebase.py +++ b/ifupdown2/ifupdownaddons/modulebase.py @@ -29,6 +29,11 @@ class NotSupported(Exception): pass + +class ModuleBaseException(Exception): + pass + + class moduleBase(object): """ Base class for ifupdown addon modules @@ -125,7 +130,7 @@ def log_error(self, msg, ifaceobj=None, raise_error=True): self.logger.debug("%s" % format[:-1]) if raise_error: - raise Exception(msg) + raise ModuleBaseException(msg) else: self.logger.error(msg) else: @@ -162,7 +167,7 @@ def parse_regex(self, ifacename, expr, ifacenames=None): if re.search(expr + '$', proc_ifacename): yield proc_ifacename except Exception as e: - raise Exception('%s: error searching regex \'%s\' in %s (%s)' + raise ModuleBaseException('%s: error searching regex \'%s\' in %s (%s)' %(ifacename, expr, proc_ifacename, str(e))) if not ifacenames: return @@ -171,7 +176,7 @@ def parse_regex(self, ifacename, expr, ifacenames=None): if re.search(expr + '$', ifacename): yield ifacename except Exception as e: - raise Exception('%s: error searching regex \'%s\' in %s (%s)' + raise ModuleBaseException('%s: error searching regex \'%s\' in %s (%s)' %(ifacename, expr, ifacename, str(e))) def ifname_is_glob(self, ifname): @@ -203,7 +208,7 @@ def parse_glob(self, ifacename, expr): mlist = m.groups() if len(mlist) < 7: # we have problems and should not continue - raise Exception('%s: error: unhandled glob expression %s\n%s' % (ifacename, expr,errmsg)) + raise ModuleBaseException('%s: error: unhandled glob expression %s\n%s' % (ifacename, expr,errmsg)) prefix = mlist[0] suffix = mlist[6] @@ -227,7 +232,7 @@ def parse_glob(self, ifacename, expr): m = regexs[2].match(expr) mlist = m.groups() if len(mlist) != 4: - raise Exception('%s: ' %ifacename + errmsg + '(unexpected len)') + raise ModuleBaseException('%s: ' %ifacename + errmsg + '(unexpected len)') prefix = mlist[0] suffix = mlist[3] start_index = int(mlist[1]) diff --git a/ifupdown2/lib/addon.py b/ifupdown2/lib/addon.py index 3af7f8ce..77208862 100644 --- a/ifupdown2/lib/addon.py +++ b/ifupdown2/lib/addon.py @@ -47,6 +47,10 @@ import nlmanager.ipnetwork as ipnetwork +class AddonException(Exception): + pass + + class Addon(Netlink, Cache): """ Base class for ifupdown2 addon modules @@ -224,4 +228,4 @@ def ip_blacklist_check(self, ifname, ip): :return: """ if ip.ip in AddonWithIpBlackList.ip_blacklist: - raise Exception("%s: blacklisted ip address in use: %s" % (ifname, ip.ip)) + raise AddonException("%s: blacklisted ip address in use: %s" % (ifname, ip.ip)) diff --git a/ifupdown2/lib/io.py b/ifupdown2/lib/io.py index d489f1c1..eb372e5d 100644 --- a/ifupdown2/lib/io.py +++ b/ifupdown2/lib/io.py @@ -33,6 +33,10 @@ from lib.base_objects import BaseObject +class IOException(Exception): + pass + + class IO(BaseObject): def __init__(self): BaseObject.__init__(self) @@ -107,9 +111,9 @@ def rx_json_packet(_socket): header_data = _socket.recv(4) if not header_data: - raise Exception("rx_json_packet: socket closed") + raise IOException("rx_json_packet: socket closed") if len(header_data) < 4: - raise Exception("rx_json_packet: invalid data received") + raise IOException("rx_json_packet: invalid data received") data_len = struct.unpack("=I", header_data)[0] data = _socket.recv(data_len) diff --git a/ifupdown2/lib/iproute2.py b/ifupdown2/lib/iproute2.py index f2d00452..c79dacf7 100644 --- a/ifupdown2/lib/iproute2.py +++ b/ifupdown2/lib/iproute2.py @@ -59,6 +59,10 @@ ################################################################################ +class IProute2Exception(Exception): + pass + + class IPRoute2(Cache, Requirements): VXLAN_UDP_PORT = 4789 @@ -354,7 +358,7 @@ def link_add_l3vxi(self, link_exists, ifname, ip, group, physdev, port, ttl=None def link_create_vxlan(self, name, vxlanid, localtunnelip=None, svcnodeip=None, remoteips=None, learning='on', ageing=None, ttl=None, physdev=None, udp_csum='on', tos = None): if svcnodeip and remoteips: - raise Exception("svcnodeip and remoteip are mutually exclusive") + raise IProute2Exception("svcnodeip and remoteip are mutually exclusive") if self.cache.link_exists(name): cmd = [ diff --git a/ifupdown2/lib/nlcache.py b/ifupdown2/lib/nlcache.py index 711aeb6a..d36280d5 100644 --- a/ifupdown2/lib/nlcache.py +++ b/ifupdown2/lib/nlcache.py @@ -2171,7 +2171,7 @@ def main(self): elif event == self.WORKQ_SERVICE_ERROR: self.logger.error('NetlinkListenerWithCache: WORKQ_SERVICE_ERROR') else: - raise Exception("Unsupported workq event %s" % event) + raise NetlinkCacheError("Unsupported workq event %s" % event) except Exception: raise finally: @@ -2276,7 +2276,7 @@ def tx_nlpacket_get_response_with_error(self, nl_packet): except ValueError: error_str = "operation failed with code %s" % error_code - raise Exception(error_str) + raise NetlinkCacheError(error_str) def tx_nlpacket_get_response_with_error_and_cache_on_ack(self, packet, ifname=None): """ @@ -2500,7 +2500,7 @@ def link_add_with_attributes(self, ifname, kind, ifla): link.build_message(next(self.sequence), self.pid) return self.tx_nlpacket_get_response_with_error_and_cache_on_ack(link) except Exception: - raise Exception("%s: cannot create link %s type %s" % (ifname, ifname, kind)) + raise NetlinkCacheError("%s: cannot create link %s type %s" % (ifname, ifname, kind)) def link_add_with_attributes_dry_run(self, ifname, kind, ifla): self.log_info_ifname_dry_run(ifname, "netlink: ip link add dev %s type %s" % (ifname, kind)) @@ -2780,7 +2780,7 @@ def link_add_macvlan(self, ifname, macvlan_ifname, macvlan_mode=None): return self.tx_nlpacket_get_response_with_error_and_cache_on_ack(link) except Exception as e: - raise Exception( + raise NetlinkCacheError( "netlink: %s: cannot create macvlan %s: %s" % (ifname, macvlan_ifname, str(e)) ) @@ -2867,7 +2867,7 @@ def link_set_bridge_info_data(self, ifname, ifla_info_data): return result except Exception as e: - raise Exception("%s: netlink: cannot create bridge or set attributes: %s" % (ifname, str(e))) + raise NetlinkCacheError("%s: netlink: cannot create bridge or set attributes: %s" % (ifname, str(e))) def link_set_bridge_info_data_dry_run(self, ifname, ifla_info_data): self.log_info_ifname_dry_run(ifname, "netlink: ip link add dev %s type bridge (with attributes)" % ifname) @@ -2937,7 +2937,7 @@ def link_add_vlan(self, vlan_raw_device, ifname, vlan_id, vlan_protocol=None, br ifname_vlanid = int(ifname.split(".")[-1]) if ifname_vlanid != vlan_id: - raise Exception( + raise NetlinkCacheError( "Interface %s must belong to VLAN %d (VLAN %d was requested)" % (ifname, ifname_vlanid, vlan_id) ) @@ -3091,7 +3091,7 @@ def link_add_bond_with_info_data(self, ifname, ifla_master, ifla_info_data): link.build_message(next(self.sequence), self.pid) return self.tx_nlpacket_get_response_with_error_and_cache_on_ack(link, ifname=ifname) except Exception as e: - raise Exception("%s: netlink: cannot create bond with attributes: %s" % (ifname, str(e))) + raise NetlinkCacheError("%s: netlink: cannot create bond with attributes: %s" % (ifname, str(e))) def link_add_bond_with_info_data_dry_run(self, ifname, ifla_info_data): self.log_info_ifname_dry_run(ifname, "netlink: ip link add dev %s type bond (with attributes)" % ifname) @@ -3133,7 +3133,7 @@ def link_set_brport_with_info_slave_data(self, ifname, kind, ifla_info_data, ifl # wait for the new notification to be cached. return self.tx_nlpacket_get_response_with_error(link) except Exception as e: - raise Exception("netlink: %s: cannot set %s (bridge slave) with options: %s" % (kind, ifname, str(e))) + raise NetlinkCacheError("netlink: %s: cannot set %s (bridge slave) with options: %s" % (kind, ifname, str(e))) def link_set_brport_with_info_slave_data_dry_run(self, ifname, kind, ifla_info_data, ifla_info_slave_data): self.log_info_ifname_dry_run(ifname, "netlink: ip link set dev %s: bridge port attributes" % ifname) From c6b9376900d78b91e633a29080895b3e81af0b8a Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Mon, 30 May 2022 22:22:31 +0200 Subject: [PATCH 065/132] SONAR: addons: address: remove unused local variables Signed-off-by: Julien Fortin --- ifupdown2/addons/address.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/ifupdown2/addons/address.py b/ifupdown2/addons/address.py index 92e27d93..5dea9ef7 100644 --- a/ifupdown2/addons/address.py +++ b/ifupdown2/addons/address.py @@ -1482,7 +1482,6 @@ def _query_check(self, ifaceobj, ifaceobjcurr, ifaceobj_getfunc=None): """ TODO: Check broadcast address, scope, etc """ - runningaddrsdict = None if not self.cache.link_exists(ifaceobj.name): self.logger.debug('iface %s not found' %ifaceobj.name) return @@ -1490,7 +1489,6 @@ def _query_check(self, ifaceobj, ifaceobjcurr, ifaceobj_getfunc=None): self.query_check_disable_ipv6(ifaceobj, ifaceobjcurr) self.query_check_ipv6_addrgen(ifaceobj, ifaceobjcurr) - addr_method = ifaceobj.addr_method self.query_n_update_ifaceobjcurr_attr(ifaceobj, ifaceobjcurr, 'mtu', self.cache.get_link_mtu_str) hwaddress = self._get_hwaddress(ifaceobj) From 82432a4bd679870be4745b71150297b1f3a722a1 Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Mon, 19 Jun 2023 18:27:14 +0200 Subject: [PATCH 066/132] addons: bridge: fix TypeError & AttributeError when no bridge-ports is given TypeError: 'NoneType' object is not iterable AttributeError: 'NoneType' object has no attribute 'append' Signed-off-by: Julien Fortin --- ifupdown2/addons/bridge.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/ifupdown2/addons/bridge.py b/ifupdown2/addons/bridge.py index 227b17c3..a41186e9 100644 --- a/ifupdown2/addons/bridge.py +++ b/ifupdown2/addons/bridge.py @@ -1237,7 +1237,7 @@ def _pretty_print_add_ports_error(self, errstr, bridgeifaceobj, bridgeports): self.log_error(bridgeifaceobj.name + ': ' + errstr, bridgeifaceobj) def _add_ports(self, ifaceobj, ifaceobj_getfunc): - bridgeports = self._get_bridge_port_list(ifaceobj) + bridgeports = self._get_bridge_port_list(ifaceobj) or [] bridgeportscondoneregex = self._get_bridge_port_condone_regex(ifaceobj) runningbridgeports = [] @@ -1278,7 +1278,7 @@ def _add_ports(self, ifaceobj, ifaceobj_getfunc): newly_enslaved_ports = [] newbridgeports_ordered = [] - for br_port in self._get_bridge_port_list_user_ordered(ifaceobj): + for br_port in self._get_bridge_port_list_user_ordered(ifaceobj) or []: if br_port in newbridgeports: newbridgeports_ordered.append(br_port) @@ -4208,7 +4208,6 @@ def run(self, ifaceobj, operation, query_ifaceobj=None, ifaceobj_getfunc=None): # make sure BRIDGE_VXLAN is set if we have a vxlan port self._re_evaluate_bridge_vxlan(ifaceobj, ifaceobj_getfunc) - if operation == 'query-checkcurr': op_handler(self, ifaceobj, query_ifaceobj, ifaceobj_getfunc=ifaceobj_getfunc) From 38871093fc0b38cc6063065fd30ce61451d9b520 Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Tue, 31 Oct 2023 00:38:47 +0100 Subject: [PATCH 067/132] addons: ethtool: re-applying link-speed to reset link-lanes to default when removed from eni Signed-off-by: Julien Fortin --- ifupdown2/addons/ethtool.py | 60 +++++++++++++++++++++++-------------- 1 file changed, 38 insertions(+), 22 deletions(-) diff --git a/ifupdown2/addons/ethtool.py b/ifupdown2/addons/ethtool.py index a2e5596a..84661bd1 100644 --- a/ifupdown2/addons/ethtool.py +++ b/ifupdown2/addons/ethtool.py @@ -247,7 +247,7 @@ def do_lanes_settings(self, ifaceobj): if not default_val and not config_val: # there is no point in checking the running config # if we have no default and the user did not have settings - return + return False, None # use only lowercase values running_val = str(self.get_running_attr('lanes', ifaceobj)).lower() @@ -259,11 +259,11 @@ def do_lanes_settings(self, ifaceobj): # check running values if config_val and config_val == running_val: - return + return False, None if not config_val and default_val and default_val == running_val: # nothing configured but the default is running - return + return False, None # if we got this far, we need to change it if config_val and (config_val != running_val): @@ -273,14 +273,7 @@ def do_lanes_settings(self, ifaceobj): # or if it has a default not equal to running value, set it lanescmd = default_val - if lanescmd: - try: - lanescmd = ('%s -s %s lanes %s' % - (utils.ethtool_cmd, ifaceobj.name, lanescmd)) - utils.exec_command(lanescmd) - except Exception as e: - if not self.ethtool_ignore_errors: - self.log_error('%s: %s' %(ifaceobj.name, str(e)), ifaceobj) + return (True, f"lanes {lanescmd}") if lanescmd else (False, None) def do_fec_settings(self, ifaceobj): feccmd = '' @@ -332,8 +325,19 @@ def do_fec_settings(self, ifaceobj): else: pass - def do_speed_settings(self, ifaceobj, down=False): - cmd = '' + def do_speed_lane_duplex_autoneg_settings(self, ifaceobj, down=False): + + force_speed_apply = False + lanes_config_applied, lane_cmd_str = self.do_lanes_settings(ifaceobj) + + cmd_list = [] + + if not down and not lanes_config_applied: + # if link-lanes is removed from /e/n/i we need to reset it + # to do so we reapply the speed + for old_ifaceobj in statemanager.statemanager_api.get_ifaceobjs(ifaceobj.name) or []: + if old_ifaceobj.get_attr_value_first("link-lanes"): + force_speed_apply = True autoneg_to_configure = None speed_to_configure = None @@ -399,13 +403,22 @@ def do_speed_settings(self, ifaceobj, down=False): running_val = self.get_running_attr('autoneg', ifaceobj) if autoneg_to_configure != running_val: # if the configured value is not set, set it - cmd += ' autoneg %s' % autoneg_to_configure + cmd_list.append(f"autoneg {autoneg_to_configure}") + + if autoneg_to_configure: + force_speed_apply = False else: force_set = False + + if force_speed_apply: + if not speed_to_configure: + speed_to_configure = default_speed + force_set = True + if speed_to_configure: # check running values if utils.get_boolean_from_string(self.get_running_attr('autoneg', ifaceobj) or 'off'): - cmd = 'autoneg off' + cmd_list.append("autoneg off") # if we are transitioning from autoneg 'on' to 'off' # don't check running speed force_set = True @@ -413,19 +426,21 @@ def do_speed_settings(self, ifaceobj, down=False): running_val = self.get_running_attr('speed', ifaceobj) if force_set or (speed_to_configure != running_val): # if the configured value is not set, set it - cmd += ' speed %s' % speed_to_configure + cmd_list.append(f"speed {speed_to_configure}") if duplex_to_configure: # check running values running_val = self.get_running_attr('duplex', ifaceobj) if force_set or (duplex_to_configure != running_val): # if the configured value is not set, set it - cmd += ' duplex %s' % duplex_to_configure + cmd_list.append(f"duplex {duplex_to_configure}") - if cmd: + if lane_cmd_str: + cmd_list.append(lane_cmd_str) + + if cmd_list: try: - cmd = ('%s -s %s %s' % (utils.ethtool_cmd, ifaceobj.name, cmd)) - utils.exec_command(cmd) + utils.exec_command(f"{utils.ethtool_cmd} -s {ifaceobj.name} {' '.join(cmd_list)}") except Exception as e: if not self.ethtool_ignore_errors: self.log_error('%s: %s' % (ifaceobj.name, str(e)), ifaceobj) @@ -438,7 +453,7 @@ def _pre_up(self, ifaceobj, operation='post_up'): if not self.cache.link_exists(ifaceobj.name): return - self.do_speed_settings(ifaceobj) + self.do_speed_lane_duplex_autoneg_settings(ifaceobj) self.do_fec_settings(ifaceobj) self.do_lanes_settings(ifaceobj) self.do_ring_settings(ifaceobj, 'ring-rx', 'rx') @@ -455,7 +470,7 @@ def _pre_up(self, ifaceobj, operation='post_up'): def _pre_down(self, ifaceobj): if not self.cache.link_exists(ifaceobj.name) or not ifaceobj.name.startswith("swp"): return - self.do_speed_settings(ifaceobj, down=True) + self.do_speed_lane_duplex_autoneg_settings(ifaceobj, down=True) def _query_check(self, ifaceobj, ifaceobjcurr): """ @@ -725,6 +740,7 @@ def run(self, ifaceobj, operation, query_ifaceobj=None, **extra_args): op_handler = self._run_ops.get(operation) if not op_handler: return + if operation == 'query-checkcurr': op_handler(self, ifaceobj, query_ifaceobj) else: From f928878e2f76e41b88325f702091e55e0c605aa3 Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Tue, 30 May 2023 23:59:08 +0200 Subject: [PATCH 068/132] nlcache: fix dry-run function prototype (missing arguments) The dry-run wrapper haven't been kept up to date and are missing some arguments thus causing dry-runs to fail. Signed-off-by: Julien Fortin --- ifupdown2/lib/nlcache.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ifupdown2/lib/nlcache.py b/ifupdown2/lib/nlcache.py index d36280d5..145b86cb 100644 --- a/ifupdown2/lib/nlcache.py +++ b/ifupdown2/lib/nlcache.py @@ -2839,7 +2839,7 @@ def link_add_bridge(self, ifname, mtu=None): link.build_message(next(self.sequence), self.pid) return self.tx_nlpacket_get_response_with_error_and_cache_on_ack(link) - def link_add_bridge_dry_run(self, ifname): + def link_add_bridge_dry_run(self, ifname, mtu=None): self.log_info_ifname_dry_run(ifname, "netlink: ip link add dev %s type bridge" % ifname) return True @@ -2964,7 +2964,7 @@ def link_add_vlan(self, vlan_raw_device, ifname, vlan_id, vlan_protocol=None, br else: raise NetlinkError(e, "cannot create vlan %s %s" % (ifname, vlan_id), ifname=ifname) - def link_add_vlan_dry_run(self, vlan_raw_device, ifname, vlan_id, vlan_protocol=None): + def link_add_vlan_dry_run(self, vlan_raw_device, ifname, vlan_id, vlan_protocol=None, bridge_binding=None): """ ifindex is the index of the parent interface that this sub-interface is being added to @@ -3093,7 +3093,7 @@ def link_add_bond_with_info_data(self, ifname, ifla_master, ifla_info_data): except Exception as e: raise NetlinkCacheError("%s: netlink: cannot create bond with attributes: %s" % (ifname, str(e))) - def link_add_bond_with_info_data_dry_run(self, ifname, ifla_info_data): + def link_add_bond_with_info_data_dry_run(self, ifname, ifla_master, ifla_info_data): self.log_info_ifname_dry_run(ifname, "netlink: ip link add dev %s type bond (with attributes)" % ifname) self.logger.debug("attributes: %s" % ifla_info_data) @@ -3143,7 +3143,7 @@ def link_set_brport_with_info_slave_data_dry_run(self, ifname, kind, ifla_info_d # ADDRESS ############################################################################ - def addr_add_dry_run(self, ifname, addr, broadcast=None, peer=None, scope=None, preferred_lifetime=None, metric=None): + def addr_add_dry_run(self, ifname, addr, broadcast=None, peer=None, scope=None, preferred_lifetime=None, metric=None, nodad=False): log_msg = ["netlink: ip addr add %s dev %s" % (addr, ifname)] if scope: From 775d918f26282dc4dbadef52deb5c722c3e9d066 Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Wed, 6 Nov 2024 19:04:45 +0100 Subject: [PATCH 069/132] lib: nlcache: remove unnecessary try/except Signed-off-by: Julien Fortin --- ifupdown2/lib/nlcache.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/ifupdown2/lib/nlcache.py b/ifupdown2/lib/nlcache.py index 145b86cb..e2fd5681 100644 --- a/ifupdown2/lib/nlcache.py +++ b/ifupdown2/lib/nlcache.py @@ -2172,8 +2172,6 @@ def main(self): self.logger.error('NetlinkListenerWithCache: WORKQ_SERVICE_ERROR') else: raise NetlinkCacheError("Unsupported workq event %s" % event) - except Exception: - raise finally: # il faut surement mettre un try/except autour de la boucle au dessus # car s'il y a une exception on ne quitte pas le listener thread From 7f9791d899755ccf3567003e6d1c2edf79dbbb32 Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Wed, 6 Nov 2024 19:50:02 +0100 Subject: [PATCH 070/132] vlan: add vxlan hopping filter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit prevents decapping Vxlan packets on SVIs and macvlan interfaces. It adds a tc filter to drop VxLAN packets before they are received locally. vxlan VLAN10 ┌───────┐ VLAN20 ┌──►│ leaf1 │◄─┐ │ └───────┘ │ │ │ ┌─┴──────┐ ┌─────┴──┐ │ host1 │ │ host2 │ └────────┘ └────────┘ Assume this topology where host1 is connected via an access port for VLAN10 to leaf1. The same way, host2 for VLAN20. The leaf1 switch is has a VxLAN device which maps vids to vnis 1-1. There is also an SVI interface on leaf1 named vlan10. If host1 sends the following packet to leaf1 (outer packet first): UDP ( dmac: vlan-mac, dip: vlan10-link-local-ipv6, VXLAN ( vni: 20, ARP ( dmac: broadcast))) Then the vlan10 SVI will locally receive the packet, which will be decapped by the vxlan interface and sent to VLAN20, breaking VLAN/VNI isolation. Host2 will see the packet. This attack will work also for different inner packet types, not just ARP. Signed-off-by: Andy Roulin Signed-off-by: Julien Fortin --- ifupdown2/addons/addressvirtual.py | 11 +++- ifupdown2/addons/bridge.py | 41 +++++++++++- ifupdown2/addons/vlan.py | 69 ++++++++++++++++++-- ifupdown2/ifupdown/utils.py | 53 +++++++++++++++ ifupdown2/lib/iproute2.py | 100 +++++++++++++++++++++++++++++ 5 files changed, 264 insertions(+), 10 deletions(-) diff --git a/ifupdown2/addons/addressvirtual.py b/ifupdown2/addons/addressvirtual.py index 421f099c..edcb02f7 100644 --- a/ifupdown2/addons/addressvirtual.py +++ b/ifupdown2/addons/addressvirtual.py @@ -14,6 +14,7 @@ try: from ifupdown2.lib.addon import AddonWithIpBlackList + from ifupdown2.lib.iproute2 import IPRoute2 from ifupdown2.ifupdown.iface import ifaceType, ifaceLinkKind, ifaceLinkPrivFlags, ifaceStatus from ifupdown2.ifupdown.utils import utils @@ -29,6 +30,7 @@ import ifupdown2.ifupdown.ifupdownconfig as ifupdownconfig except ImportError: from lib.addon import AddonWithIpBlackList + from lib.iproute2 import IPRoute2 from ifupdown.iface import ifaceType, ifaceLinkKind, ifaceLinkPrivFlags, ifaceStatus from ifupdown.utils import utils @@ -83,6 +85,7 @@ class addressvirtual(AddonWithIpBlackList, moduleBase): def __init__(self, *args, **kargs): AddonWithIpBlackList.__init__(self) moduleBase.__init__(self, *args, **kargs) + self.iproute2 = IPRoute2() self._bridge_fdb_query_cache = {} self.addressvirtual_with_route_metric = utils.get_boolean_from_string( policymanager.policymanager_api.get_module_globals( @@ -410,7 +413,7 @@ def sync_macvlan_forwarding_state(self, ifname, macvlan_ifname): except Exception as e: self.logger.info("%s: syncing macvlan forwarding with lower device forwarding state failed: %s" % (ifname, str(e))) - def create_macvlan_and_apply_config(self, ifaceobj, intf_config_list, vrrp=False): + def create_macvlan_and_apply_config(self, ifaceobj, intf_config_list, vrrp=False, ifaceobj_getfunc=None): """ intf_config_list = [ { @@ -602,7 +605,8 @@ def _up(self, ifaceobj, ifaceobj_getfunc=None): self.translate_addrvirtual_user_config_to_list( ifaceobj, address_virtual_list - ) + ), + ifaceobj_getfunc=ifaceobj_getfunc ) vrr_macs = self.create_macvlan_and_apply_config( @@ -611,7 +615,8 @@ def _up(self, ifaceobj, ifaceobj_getfunc=None): ifaceobj, vrr_config_list ), - vrrp=True + vrrp=True, + ifaceobj_getfunc=ifaceobj_getfunc ) hw_address_list = addr_virtual_macs + vrr_macs diff --git a/ifupdown2/addons/bridge.py b/ifupdown2/addons/bridge.py index a41186e9..979ea9c9 100644 --- a/ifupdown2/addons/bridge.py +++ b/ifupdown2/addons/bridge.py @@ -16,13 +16,13 @@ import ifupdown2.ifupdown.exceptions as exceptions import ifupdown2.ifupdown.policymanager as policymanager import ifupdown2.ifupdown.ifupdownflags as ifupdownflags + from ifupdown2.ifupdown.statemanager import statemanager_api as statemanager from ifupdown2.nlmanager.nlmanager import Link from ifupdown2.ifupdown.iface import ifaceRole, ifaceLinkKind, ifaceLinkPrivFlags, ifaceLinkType, ifaceDependencyType, ifaceStatus, iface from ifupdown2.ifupdown.utils import utils - from ifupdown2.ifupdownaddons.cache import * from ifupdown2.ifupdownaddons.modulebase import moduleBase except ImportError: from lib.addon import Bridge, AddonException @@ -36,7 +36,6 @@ from ifupdown.iface import ifaceRole, ifaceLinkKind, ifaceLinkPrivFlags, ifaceLinkType, ifaceDependencyType, ifaceStatus, iface from ifupdown.utils import utils - from ifupdownaddons.cache import * from ifupdownaddons.modulebase import moduleBase @@ -2764,6 +2763,43 @@ def get_bridge_mtu(self, ifaceobj): self.logger.warning("%s: invalid bridge mtu %s: %s" % (ifaceobj.name, user_config_mtu, str(e))) return None + def vxlan_hopping_filter(self, ifaceobj , ifaceobj_getfunc): + bridge_ports = [ + port + for ports in self._get_bridge_port_list(ifaceobj) for port in (ifaceobj_getfunc(ports) or []) + ] + + vxlan_devs = list(filter(lambda p: p.link_kind == ifaceLinkKind.VXLAN, bridge_ports)) + bridge_is_vxlan = len(vxlan_devs) > 0 + + vxlan_ports = set() + if bridge_is_vxlan: + vxlan_ports = set([self.netlink.VXLAN_UDP_PORT]) + vxlan_ports = vxlan_ports.union(map(lambda vx: vx.get_attr_value_first("vxlan-port"), vxlan_devs)) + vxlan_ports = set([p for p in vxlan_ports if p is not None ]) + + desired_filters = [ (vxlan_port, None, 'drop') for vxlan_port in vxlan_ports ] + else: + desired_filters = [] + + filters_to_add, filters_to_delete = self.iproute2.check_tc_filters(ifaceobj.name, desired_filters) + + try: + self.iproute2.batch_start() + + for (vxlan_port, vid, _) in filters_to_delete: + if vid == None: + self.iproute2.del_vxlan_hopping_tc_filter(ifaceobj.name, vxlan_port) + + for (vxlan_port, _, _) in filters_to_add: + self.iproute2.add_vxlan_hopping_tc_filter(ifaceobj.name, vxlan_port) + + self.iproute2.batch_commit() + except Exception as e: + if "Unterminated quoted string" not in str(e): + raise + self.logger.debug(f"tc quote failure: {str(e)}") + def up_bridge(self, ifaceobj, ifaceobj_getfunc): ifname = ifaceobj.name @@ -2804,6 +2840,7 @@ def up_bridge(self, ifaceobj, ifaceobj_getfunc): self._apply_bridge_port_settings_all(ifaceobj, ifaceobj_getfunc=ifaceobj_getfunc, bridge_vlan_aware=bridge_vlan_aware) + self.vxlan_hopping_filter(ifaceobj, ifaceobj_getfunc) except exceptions.ReservedVlanException as e: raise e except Exception as e: diff --git a/ifupdown2/addons/vlan.py b/ifupdown2/addons/vlan.py index 8b05b225..900e70b2 100644 --- a/ifupdown2/addons/vlan.py +++ b/ifupdown2/addons/vlan.py @@ -4,24 +4,33 @@ # Author: Roopa Prabhu, roopa@cumulusnetworks.com # +import subprocess +import re + try: from ifupdown2.lib.addon import Addon, AddonException + from ifupdown2.lib.iproute2 import IPRoute2 from ifupdown2.ifupdown.iface import ifaceType, ifaceLinkKind, ifaceStatus + from ifupdown2.ifupdown.statemanager import statemanager_api as statemanager from ifupdown2.nlmanager.nlmanager import Link from ifupdown2.ifupdownaddons.modulebase import moduleBase from ifupdown2.ifupdown.utils import utils from ifupdown2.lib.exceptions import RetryCMD import ifupdown2.ifupdown.ifupdownflags as ifupdownflags import ifupdown2.ifupdown.policymanager as policymanager + import ifupdown2.ifupdown.ifupdownconfig as ifupdownconfig except ImportError: from lib.addon import Addon, AddonException + from lib.iproute2 import IPRoute2 from ifupdown.iface import ifaceType, ifaceLinkKind, ifaceStatus + from ifupdown.statemanager import statemanager_api as statemanager from nlmanager.nlmanager import Link from ifupdownaddons.modulebase import moduleBase from ifupdown.utils import utils from lib.exceptions import RetryCMD import ifupdown.ifupdownflags as ifupdownflags import ifupdown.policymanager as policymanager + import ifupdown.ifupdownconfig as ifupdownconfig class vlan(Addon, moduleBase): @@ -63,6 +72,7 @@ class vlan(Addon, moduleBase): def __init__(self, *args, **kargs): Addon.__init__(self) moduleBase.__init__(self, *args, **kargs) + self.iproute2 = IPRoute2() def _is_vlan_device(self, ifaceobj): vlan_raw_device = ifaceobj.get_attr_value_first('vlan-raw-device') @@ -127,7 +137,54 @@ def _bridge_vid_check(self, ifaceobjcurr, bridgename, vlanid): ifaceobjcurr.status = ifaceStatus.ERROR ifaceobjcurr.status_str = 'bridge vid error' - def _up(self, ifaceobj): + def _is_peerlink(self, ifaceobj): + return ifaceobj.get_attr_value_first('clagd-backup-ip') + + def vxlan_hopping_filter_bypass(self, ifaceobj, vlanrawdevice, vid, ifaceobj_getfunc): + vlan_is_peerlink = self._is_peerlink(ifaceobj) + old_ifaceobjs = statemanager.get_ifaceobjs(ifaceobj.name) or [] + vlan_was_peerlink = any(map(self._is_peerlink, old_ifaceobjs)) + + if not vlan_is_peerlink and not vlan_was_peerlink: + return + + bridge_ifaceobjs = ifaceobj_getfunc(vlanrawdevice) + if any(map(lambda b: b.link_kind == ifaceLinkKind.BRIDGE, bridge_ifaceobjs)): + bridge_ports = [ port for b in bridge_ifaceobjs for ports in (self._get_bridge_port_list(b) or []) for port in (ifaceobj_getfunc(ports) or []) ] + bridge_is_vxlan = any(map(lambda p: p.link_kind == ifaceLinkKind.VXLAN, bridge_ports)) + else: + bridge_is_vxlan = False + + vxlan_port = self.netlink.VXLAN_UDP_PORT + if bridge_is_vxlan: + vxlan_dev = next(p for p in bridge_ports if p.link_kind == ifaceLinkKind.VXLAN) + vxlan_port_str = vxlan_dev.get_attr_value_first("vxlan-port") + if vxlan_port_str: + vxlan_port = vxlan_port_str + + if vlan_is_peerlink: + desired_filters = [(vxlan_port, vid, 'pass')] + else: + desired_filters = [] + filters_to_add, filters_to_delete = self.iproute2.check_tc_filters(bridge_ifaceobjs[0].name, desired_filters) + + try: + self.iproute2.batch_start() + + for (vxlan_port, vlan_id, _) in filters_to_delete: + if vlan_id == vid: + self.iproute2.del_vxlan_hopping_tc_filter_bypass(bridge_ifaceobjs[0].name, vlan_id, vxlan_port) + + for (vxlan_port, vid, _) in filters_to_add: + self.iproute2.add_vxlan_hopping_tc_filter_bypass(bridge_ifaceobjs[0].name, vid, vxlan_port) + + self.iproute2.batch_commit() + except Exception as e: + if "Unterminated quoted string" not in str(e): + raise + self.logger.debug(f"tc quote failure: {str(e)}") + + def _up(self, ifaceobj, ifaceobj_getfunc=None): vlanid = self._get_vlan_id(ifaceobj) if vlanid == -1: raise AddonException('could not determine vlanid') @@ -197,16 +254,18 @@ def _up(self, ifaceobj): self.netlink.link_add_vlan(vlanrawdevice, ifaceobj.name, vlanid, vlan_protocol, bool_vlan_bridge_binding) self._bridge_vid_add_del(vlanrawdevice, vlanid) + self.vxlan_hopping_filter_bypass(ifaceobj, vlanrawdevice, vlanid, ifaceobj_getfunc) return try: self.netlink.link_add_vlan(vlanrawdevice, ifaceobj.name, vlanid, vlan_protocol, bool_vlan_bridge_binding if vlan_bridge_binding is not None else None) + self.vxlan_hopping_filter_bypass(ifaceobj, vlanrawdevice, vlanid, ifaceobj_getfunc) except RetryCMD as e: self.logger.info("%s: attempting to create vlan without bridge_binding (capability not detected on the system)" % ifaceobj.name) utils.exec_command(e.cmd) self._bridge_vid_add_del(vlanrawdevice, vlanid) - def _down(self, ifaceobj): + def _down(self, ifaceobj, ifaceobj_getfunc=None): vlanid = self._get_vlan_id(ifaceobj) if vlanid == -1: raise AddonException('could not determine vlanid') @@ -221,7 +280,7 @@ def _down(self, ifaceobj): except Exception as e: self.log_warn(str(e)) - def _query_check(self, ifaceobj, ifaceobjcurr): + def _query_check(self, ifaceobj, ifaceobjcurr, ifaceobj_getfunc=None): if not self.cache.link_exists(ifaceobj.name): return @@ -291,7 +350,7 @@ def _query_check(self, ifaceobj, ifaceobjcurr): self._bridge_vid_check(ifaceobjcurr, cached_vlan_raw_device, cached_vlan_id) - def _query_running(self, ifaceobjrunning): + def _query_running(self, ifaceobjrunning, ifaceobj_getfunc=None): ifname = ifaceobjrunning.name if not self.cache.link_exists(ifname): @@ -329,7 +388,7 @@ def get_ops(self): """ returns list of ops supported by this module """ return list(self._run_ops.keys()) - def run(self, ifaceobj, operation, query_ifaceobj=None, **extra_args): + def run(self, ifaceobj, operation, query_ifaceobj=None, ifaceobj_getfunc=None): """ run vlan configuration on the interface object passed as argument Args: diff --git a/ifupdown2/ifupdown/utils.py b/ifupdown2/ifupdown/utils.py index 53d0fb1a..2123cc31 100644 --- a/ifupdown2/ifupdown/utils.py +++ b/ifupdown2/ifupdown/utils.py @@ -573,4 +573,57 @@ def get_vni_mcastgrp_in_map(cls, vni_mcastgrp_map): raise return vnid + @classmethod + def _get_ifaceobj_bridge_ports(cls, ifaceobj, as_list=False): + bridge_ports = [] + + for brport in ifaceobj.get_attr_value('bridge-ports') or []: + if brport != 'none': + bridge_ports.extend(brport.split()) + + if as_list: + return bridge_ports + + return ' '.join(bridge_ports) + + @classmethod + def parse_port_list(cls, ifacename, port_expr, ifacenames=None): + """ parse port list containing glob and regex + + Args: + port_expr (str): expression + ifacenames (list): list of interface names. This needs to be specified if the expression has a regular expression + """ + regex = 0 + glob = 0 + portlist = [] + + if not port_expr: + return None + exprs = re.split(r'[\s\t]\s*', port_expr) + for expr in exprs: + if expr == 'noregex': + regex = 0 + elif expr == 'noglob': + glob = 0 + elif expr == 'regex': + regex = 1 + elif expr == 'glob': + glob = 1 + elif regex: + for port in self.parse_regex(ifacename, expr, ifacenames): + if port not in portlist: + portlist.append(port) + regex = 0 + elif glob: + for port in self.parse_glob(ifacename, expr): + portlist.append(port) + glob = 0 + else: + portlist.append(expr) + if not portlist: + return None + return portlist + + fcntl.fcntl(utils.DEVNULL, fcntl.F_SETFD, fcntl.FD_CLOEXEC) diff --git a/ifupdown2/lib/iproute2.py b/ifupdown2/lib/iproute2.py index c79dacf7..5cff8e6e 100644 --- a/ifupdown2/lib/iproute2.py +++ b/ifupdown2/lib/iproute2.py @@ -28,6 +28,7 @@ import ipaddress import subprocess import json +from functools import lru_cache try: from ifupdown2.lib.sysfs import Sysfs @@ -1091,3 +1092,102 @@ def bridge_link_update_vni_filter(self, vxlandev, vnisd): except Exception as e: self.logger.error("bridge vni show failed .. %s" % str(e)) return None + + def add_clsact_qdisc(self, ifname): + existing_qdiscs_cmd = ['tc', 'qdisc', 'show', 'dev', ifname] + existing_qdiscs = subprocess.Popen(existing_qdiscs_cmd, stdout=subprocess.PIPE) + check_for_qdisc_cmd = ['grep', '--quiet', 'clsact'] + qdisc_found = subprocess.call(check_for_qdisc_cmd, stdin=existing_qdiscs.stdout) == 0 + + if not qdisc_found: + add_qdisc_cmd = 'tc qdisc add dev %s clsact' % (ifname) + utils.exec_command(add_qdisc_cmd) + + def _vxlan_hopping_tc_filter(self, ifname, add, vxlan_port): + if add: + action = 'add' + self.add_clsact_qdisc(ifname) + else: + action = 'del' + + add_filter_ipv4 = 'filter %s dev %s ingress prio %s proto 802.1q flower vlan_ethtype ipv4 ip_proto udp dst_port %s action drop' % (action, ifname, 10000 + 2 * int(vxlan_port), vxlan_port) + self.__execute_or_batch('tc', add_filter_ipv4) + add_filter_ipv6 = 'filter %s dev %s ingress prio %s proto 802.1q flower vlan_ethtype ipv6 ip_proto udp dst_port %s action drop' % (action, ifname, 10000 + 2 * int(vxlan_port) + 1, vxlan_port) + self.__execute_or_batch('tc', add_filter_ipv6) + + def add_vxlan_hopping_tc_filter(self, ifname, vxlan_port): + self._vxlan_hopping_tc_filter(ifname, True, vxlan_port) + + def del_vxlan_hopping_tc_filter(self, ifname, vxlan_port): + self._vxlan_hopping_tc_filter(ifname, False, vxlan_port) + + def _vxlan_hopping_tc_filter_bypass(self, ifname, add, vid, vxlan_port): + if add: + action = 'add' + self.add_clsact_qdisc(ifname) + else: + action = 'del' + + add_filter_ipv4 = 'filter %s dev %s ingress prio %s proto 802.1q flower vlan_id %s vlan_ethtype ipv4 ip_proto udp dst_port %s action pass' % (action, ifname, 2*int(vid), vid, vxlan_port) + self.__execute_or_batch('tc', add_filter_ipv4) + add_filter_ipv6 = 'filter %s dev %s ingress prio %s proto 802.1q flower vlan_id %s vlan_ethtype ipv6 ip_proto udp dst_port %s action pass' % (action, ifname, 2*int(vid)+1, vid, vxlan_port) + self.__execute_or_batch('tc', add_filter_ipv6) + + def add_vxlan_hopping_tc_filter_bypass(self, ifname, vid, vxlan_port): + self._vxlan_hopping_tc_filter_bypass(ifname, True, vid, vxlan_port) + + def del_vxlan_hopping_tc_filter_bypass(self, ifname, vid, vxlan_port): + self._vxlan_hopping_tc_filter_bypass(ifname, False, vid, vxlan_port) + + @lru_cache(maxsize=128) + def fetch_tc_filters(self, br_ifname): + cmd = 'tc -j filter show dev %s ingress' % br_ifname + try: + result = utils.exec_command(cmd) + return json.loads(result) + except subprocess.CalledProcessError as e: + self.logger.error("Error running command: %s" % cmd) + return [] + + def check_tc_filter_exists(self, br_ifname, dst_port, vlan_id=None, eth_type='ipv4', ip_proto='udp', action='drop'): + filters = self.fetch_tc_filters(br_ifname) + + for filter_entry in filters: + options = filter_entry.get('options', {}) + keys = options.get('keys', {}) + actions = options.get('actions', []) + + if (keys.get('eth_type') == eth_type + and keys.get('ip_proto') == ip_proto + and keys.get('dst_port') == dst_port + and keys.get('vlan_id') == vlan_id): + for act in actions: + if act.get('kind') == 'gact' and act.get('control_action', {}).get('type') == action: + return True + return False + + def check_tc_filters(self, br_ifname, desired_filters): + filters_to_add = [] + filters_to_delete = [] + + # Checking filters to add + for filter in desired_filters: + dst_port, vlan_id, action = filter + if not self.check_tc_filter_exists(br_ifname, dst_port, vlan_id, action=action): + filters_to_add.append(filter) + + # Checking filters to delete + system_filters = self.fetch_tc_filters(br_ifname) + for filter_entry in system_filters: + options = filter_entry.get('options', {}) + keys = options.get('keys', {}) + actions = options.get('actions', []) + for act in actions: + port = keys.get('dst_port') + vlan = keys.get('vlan_id') + if act.get('kind') == 'gact': + action = act.get('control_action', {}).get('type') + if (port, vlan, action) not in desired_filters: + filters_to_delete.append((port, vlan, action)) + + return set(filters_to_add), set(filters_to_delete) From 6e9c9894c70292988ce8ce387e8cbec8c6a2e2c9 Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Thu, 25 Jan 2024 23:03:06 +0100 Subject: [PATCH 071/132] lib: addon: removing duplicated get_bridge_port_list method Lots of code duplicate and checks for empty list and such. Trying to tyding this up. Signed-off-by: Julien Fortin --- ifupdown2/addons/bridge.py | 16 +--------------- ifupdown2/addons/mstpctl.py | 5 ++--- ifupdown2/addons/vlan.py | 2 +- ifupdown2/ifupdown/utils.py | 13 +++++++++++++ ifupdown2/lib/addon.py | 7 +++---- 5 files changed, 20 insertions(+), 23 deletions(-) diff --git a/ifupdown2/addons/bridge.py b/ifupdown2/addons/bridge.py index 979ea9c9..f2e8d92a 100644 --- a/ifupdown2/addons/bridge.py +++ b/ifupdown2/addons/bridge.py @@ -1134,20 +1134,6 @@ def get_dependent_ifacenames_running(self, ifaceobj): return None return self.cache.get_slaves(ifaceobj.name) - def _get_bridge_port_list(self, ifaceobj): - - # port list is also available in the previously - # parsed dependent list. Use that if available, instead - # of parsing port expr again - port_list = ifaceobj.lowerifaces - if port_list: - return port_list - ports = self._get_ifaceobj_bridge_ports(ifaceobj) - if ports: - return self.parse_port_list(ifaceobj.name, ports) - else: - return None - def _get_bridge_port_list_user_ordered(self, ifaceobj): # When enslaving bridge-ports we need to return the exact user # configured bridge ports list (bridge will inherit the mac of the @@ -3621,7 +3607,7 @@ def query_check_bridge_ports(self, ifaceobj, ifaceobjcurr, running_port_list, if bridge_all_ports = [] for obj in ifaceobj_getfunc(ifaceobj.name) or []: - bridge_all_ports.extend(self._get_bridge_port_list(obj) or []) + bridge_all_ports.extend(self._get_bridge_port_list(obj)) if not running_port_list and not bridge_all_ports: return diff --git a/ifupdown2/addons/mstpctl.py b/ifupdown2/addons/mstpctl.py index 75dcee76..c9c19241 100644 --- a/ifupdown2/addons/mstpctl.py +++ b/ifupdown2/addons/mstpctl.py @@ -370,9 +370,8 @@ def _get_bridge_port_list(self, ifaceobj): return port_list ports = ifaceobj.get_attr_value_first('mstpctl-ports') if ports: - return self.parse_port_list(ifaceobj.name, ports) - else: - return None + ports = self.parse_port_list(ifaceobj.name, ports) + return ports or [] def _ports_enable_disable_ipv6(self, ports, enable='1'): for p in ports: diff --git a/ifupdown2/addons/vlan.py b/ifupdown2/addons/vlan.py index 900e70b2..a906f704 100644 --- a/ifupdown2/addons/vlan.py +++ b/ifupdown2/addons/vlan.py @@ -150,7 +150,7 @@ def vxlan_hopping_filter_bypass(self, ifaceobj, vlanrawdevice, vid, ifaceobj_get bridge_ifaceobjs = ifaceobj_getfunc(vlanrawdevice) if any(map(lambda b: b.link_kind == ifaceLinkKind.BRIDGE, bridge_ifaceobjs)): - bridge_ports = [ port for b in bridge_ifaceobjs for ports in (self._get_bridge_port_list(b) or []) for port in (ifaceobj_getfunc(ports) or []) ] + bridge_ports = [ port for b in bridge_ifaceobjs for ports in (utils._get_bridge_port_list(b) or []) for port in ifaceobj_getfunc(ports) ] bridge_is_vxlan = any(map(lambda p: p.link_kind == ifaceLinkKind.VXLAN, bridge_ports)) else: bridge_is_vxlan = False diff --git a/ifupdown2/ifupdown/utils.py b/ifupdown2/ifupdown/utils.py index 2123cc31..b631a68f 100644 --- a/ifupdown2/ifupdown/utils.py +++ b/ifupdown2/ifupdown/utils.py @@ -625,5 +625,18 @@ def parse_port_list(cls, ifacename, port_expr, ifacenames=None): return None return portlist + @classmethod + def _get_bridge_port_list(cls, ifaceobj): + # port list is also available in the previously + # parsed dependent list. Use that if available, instead + # of parsing port expr again + port_list = ifaceobj.lowerifaces + if port_list: + return port_list + ports = utils._get_ifaceobj_bridge_ports(ifaceobj) + if ports: + return utils.parse_port_list(ifaceobj.name, ports) + else: + return None fcntl.fcntl(utils.DEVNULL, fcntl.F_SETFD, fcntl.FD_CLOEXEC) diff --git a/ifupdown2/lib/addon.py b/ifupdown2/lib/addon.py index 77208862..d729ad20 100644 --- a/ifupdown2/lib/addon.py +++ b/ifupdown2/lib/addon.py @@ -147,7 +147,7 @@ def _re_evaluate_bridge_vxlan(self, ifaceobj, ifaceobj_getfunc=None): if ifaceobj.link_kind & ifaceLinkKind.BRIDGE and not ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_VXLAN: for port in self._get_bridge_port_list(ifaceobj) or []: - for brport_ifaceobj in ifaceobj_getfunc(port): + for brport_ifaceobj in ifaceobj_getfunc(port) or []: if brport_ifaceobj.link_kind & ifaceLinkKind.VXLAN: ifaceobj.link_privflags |= ifaceLinkPrivFlags.BRIDGE_VXLAN self.__check_l3vni_bridge(ifaceobj) @@ -192,9 +192,8 @@ def _get_bridge_port_list(self, ifaceobj): return port_list ports = self._get_ifaceobj_bridge_ports(ifaceobj) if ports: - return self.parse_port_list(ifaceobj.name, ports) - else: - return None + ports = self.parse_port_list(ifaceobj.name, ports) + return ports or [] class AddonWithIpBlackList(Addon): From 281a4b113f46c1818b94cfe1daf567f7b82e2fae Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Thu, 10 Oct 2024 22:11:45 +0200 Subject: [PATCH 072/132] addons: vxlan: bridge port cycling on VNI-VLAN mapping changes With Single Vxlan Device (SVD), after changing the VLAN ID which maps to a specific VNI and ifreloading, old extern_learn bridge FDB entries with the old VLAN ID still exist in the bridge. The issue is only seen on HBN, not on CL. This commit adds a new method cycle_vxlan_brport_on_vni_change() to handle cases where the VNI-to-VLAN mapping changes for a VXLAN bridge port. The function does the following: - Checks if the interface is a single VXLAN bridge port - Compares the old and new VNI-VLAN mappings - If any VNIs are reused with different VLANs, it cycles the VXLAN port by: - Removing it from the bridge - Re-adding it to the bridge This will clean all FDB entries associated with the vxlan, including also those whose mapping have not changed. FRR will also have to re-add all external entries. This fix is for non-CL systems. Signed-off-by: Julien Fortin --- ifupdown2/addons/bridge.py | 54 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/ifupdown2/addons/bridge.py b/ifupdown2/addons/bridge.py index f2e8d92a..ffa33f31 100644 --- a/ifupdown2/addons/bridge.py +++ b/ifupdown2/addons/bridge.py @@ -819,6 +819,12 @@ def __init__(self, *args, **kargs): except Exception: self.bridge_vni_per_svi_limit = -1 + # Cumulus-check + try: + self.cumulus = "cumulus" in utils.exec_commandl(["lsb_release", "-a"]).lower() + except: + self.cumulus = False + @staticmethod def _l2protocol_tunnel_set_pvst(ifla_brport_group_mask, ifla_brport_group_maskhi): if not ifla_brport_group_maskhi: @@ -2025,6 +2031,9 @@ def up_bridge_port(self, ifaceobj, ifaceobj_getfunc): # bridge doesn't exist return + if not should_enslave_port and not self.cumulus: + self.cycle_vxlan_brport_on_vni_change(bridge_name, ifaceobj) + # check for bridge-learning on l2 vni in evpn setup self.syntax_check_learning_l2_vni_evpn(ifaceobj) @@ -2786,6 +2795,51 @@ def vxlan_hopping_filter(self, ifaceobj , ifaceobj_getfunc): raise self.logger.debug(f"tc quote failure: {str(e)}") + def cycle_vxlan_brport_on_vni_change(self, bridge_name: str, ifaceobj: iface): + """ + Cycle VXLAN bridge port if VNI-to-VLAN mapping has changed. + + This function checks if any VNIs are reused with different VLANs in the new configuration. + If so, it cycles the VXLAN port (removes it from the bridge and re-adds it) to ensure + proper updating of the VNI-VLAN mappings. + + Args: + bridge_name (str): Name of the bridge + ifaceobj (object): Interface object containing new configuration + Returns: + None + """ + if not ifaceobj.link_privflags & ifaceLinkPrivFlags.SINGLE_VXLAN: + return + + ifname: str = ifaceobj.name + old_vlan_vni_map: dict = {} + new_vlan_vni_map: dict = {} + + # Get old vlan-vni map from statemanager + for old_obj in statemanager.get_ifaceobjs(ifname) or []: + for mapping in old_obj.get_attr_value("bridge-vlan-vni-map") or []: + for entry in mapping.split(): + vlan, vni = entry.split("=") + old_vlan_vni_map[vni.strip()] = vlan.strip() + + # Get new vlan-vni map from current ifaceobj + for mapping in ifaceobj.get_attr_value("bridge-vlan-vni-map") or []: + for entry in mapping.split(): + vlan, vni = entry.split("=") + new_vlan_vni_map[vni.strip()] = vlan.strip() + + # Find VNIs that are reused with different VLANs + reused_vnis: list = [] + for vni, new_vlan in new_vlan_vni_map.items(): + if vni in old_vlan_vni_map and old_vlan_vni_map[vni] != new_vlan: + reused_vnis.append(vni) + + if reused_vnis: + self.logger.info(f"{ifname}: cycling VXLAN port from bridge '{bridge_name}' due to VNI reuse ({', '.join(reused_vnis)})") + self.netlink.link_set_nomaster(ifname) + self.netlink.link_set_master(ifname, bridge_name) + def up_bridge(self, ifaceobj, ifaceobj_getfunc): ifname = ifaceobj.name From 128939cddf90f310f683f2bf5ce43308e1c91cb4 Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Tue, 12 Dec 2023 00:19:45 +0100 Subject: [PATCH 073/132] addons: bridge: avoid checking reserved vlan range on l3vni bridge In multi-vlan-aware-bridge context we don't need to check the reserved vlan range on the l3vni bridge. Signed-off-by: Julien Fortin --- ifupdown2/addons/bridge.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/ifupdown2/addons/bridge.py b/ifupdown2/addons/bridge.py index ffa33f31..30fc3c51 100644 --- a/ifupdown2/addons/bridge.py +++ b/ifupdown2/addons/bridge.py @@ -2509,7 +2509,7 @@ def up_apply_brports_attributes(self, ifaceobj, ifaceobj_getfunc, bridge_vlan_aw self.log_error(str(e), ifaceobj) if single_vxlan_device_ifaceobj: - self.apply_bridge_port_vlan_vni_map(single_vxlan_device_ifaceobj) + self.apply_bridge_port_vlan_vni_map(ifaceobj, single_vxlan_device_ifaceobj) @staticmethod def range_to_string(range_start, range_end): @@ -2590,11 +2590,14 @@ def list_to_range(vlan_list, vni_list, range_dict): list_to_range(current_vlan_range, current_vni_range, vlan_vni_ranges) return vlan_vni_ranges - def check_bridge_vlan_vni_map_reserved(self, ifaceobj, vlan_to_add): + def check_bridge_vlan_vni_map_reserved(self, bridge_ifaceobj, ifaceobj, vlan_to_add): + if bridge_ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_l3VNI or ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_l3VNI: + # No need to check for vlan in the reserved range for l3vni bridge + return for vlan in sorted(vlan_to_add): self._handle_reserved_vlan(vlan, ifaceobj.name) - def apply_bridge_port_vlan_vni_map(self, ifaceobj): + def apply_bridge_port_vlan_vni_map(self, bridge_ifaceobj, ifaceobj): """ bridge vlan add vid dev vxlan0 bridge vlan add dev vxlan0 vid tunnel_info id @@ -2638,7 +2641,7 @@ def apply_bridge_port_vlan_vni_map(self, ifaceobj): self.check_duplicate_vnis(ifaceobj, vlan_vni_to_add) # check reserved vlans - self.check_bridge_vlan_vni_map_reserved(ifaceobj, vlan_vni_to_add.keys()) + self.check_bridge_vlan_vni_map_reserved(bridge_ifaceobj, ifaceobj, vlan_vni_to_add.keys()) vlan_vni_ranges_to_add = self.get_vlan_vni_ranges_from_dict(ifaceobj.name, vlan_vni_to_add) From 2d3f57efcfdd1f46ae53e607a075f5383a9de24c Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Thu, 11 Jan 2024 22:29:43 +0100 Subject: [PATCH 074/132] ifreload: new --diff cli argument: only reload delta between /e/n/i ifreload checks the delta between the user config (/e/n/i) and the config state of the device. It's spends of lot of time checking if the running state is valid or not. Introducing --diff, a cli argument to only reload an interface if the /e/n/i config was changed. Signed-off-by: Julien Fortin --- ifupdown2/ifupdown/argv.py | 1 + ifupdown2/ifupdown/iface.py | 9 ++++++ ifupdown2/ifupdown/ifupdownconfig.py | 1 + ifupdown2/ifupdown/ifupdownmain.py | 46 ++++++++++++++++++++++++++-- ifupdown2/ifupdown/main.py | 3 +- 5 files changed, 56 insertions(+), 4 deletions(-) diff --git a/ifupdown2/ifupdown/argv.py b/ifupdown2/ifupdown/argv.py index 317d31b7..14fe755b 100644 --- a/ifupdown2/ifupdown/argv.py +++ b/ifupdown2/ifupdown/argv.py @@ -215,6 +215,7 @@ def update_ifreload_argparser(self, argparser): 'currently up regardless of whether an interface has ' '"auto " configuration within the /etc/network/interfaces file.') group.add_argument('--allow', dest='CLASS', action='append', help='ignore non-"allow-CLASS" interfaces') + argparser.add_argument("--diff", action="store_true", help="diff based approach - only up/down what is really necessary") argparser.add_argument('iflist', metavar='IFACE', nargs='*', help=argparse.SUPPRESS) argparser.add_argument('-n', '--no-act', dest='noact', action='store_true', help='print out what would happen, but don\'t do it') diff --git a/ifupdown2/ifupdown/iface.py b/ifupdown2/ifupdown/iface.py index c759322a..e18b8151 100644 --- a/ifupdown2/ifupdown/iface.py +++ b/ifupdown2/ifupdown/iface.py @@ -454,6 +454,15 @@ def __init__(self, attrsdict={}): self.dependency_type = ifaceDependencyType.UNKNOWN self.blacklisted = False + def __eq__(self, other): + return ( + isinstance(other, iface) and + self.name == other.name and + self.config == other.config and + self.addr_family == other.addr_family and + self.addr_method == other.addr_method + ) + def _set_attrs_from_dict(self, attrdict): self.auto = attrdict.get('auto', False) self.name = attrdict.get('name') diff --git a/ifupdown2/ifupdown/ifupdownconfig.py b/ifupdown2/ifupdown/ifupdownconfig.py index b7d16d72..bcb860b8 100644 --- a/ifupdown2/ifupdown/ifupdownconfig.py +++ b/ifupdown2/ifupdown/ifupdownconfig.py @@ -8,6 +8,7 @@ class ifupdownConfig(): def __init__(self): + self.diff_mode = False self.conf = {} config = ifupdownConfig() diff --git a/ifupdown2/ifupdown/ifupdownmain.py b/ifupdown2/ifupdown/ifupdownmain.py index ab08e79b..45ba132f 100644 --- a/ifupdown2/ifupdown/ifupdownmain.py +++ b/ifupdown2/ifupdown/ifupdownmain.py @@ -116,7 +116,12 @@ def run_up(self, ifaceobj): # But do allow user to change state of the link if the interface # is already with its link master (hence the master check). if ifaceobj.link_type == ifaceLinkType.LINK_SLAVE: - return + if self.diff_based: + self.logger.debug( + f"{ifaceobj.name}: diff based approach will try to link-up this slave device" + ) + else: + return if not self.link_exists(ifaceobj.name): return if self._keep_link_down(ifaceobj): @@ -184,7 +189,10 @@ def run_down(self, ifaceobj): def run_sched_ifaceobj_posthook(self, ifaceobj, op): if (ifaceobj.priv_flags and (ifaceobj.priv_flags.BUILTIN or ifaceobj.priv_flags.NOCONFIG)): - return + if not self.diff_based: + return + else: + self.logger.debug(f"{ifaceobj.name}: diff based approach will save this empty ifaceobj") if self.flags.STATEMANAGER_UPDATE: self.statemanager.ifaceobj_sync(ifaceobj, op) @@ -428,6 +436,8 @@ def __init__(self, config={}, args=None, '': self._keyword_interface_l2protocol_tunnel_list } + self.diff_based = False + def _get_mgmt_iface_default_prefix(self): mgmt_iface_default_prefix = None try: @@ -2163,8 +2173,11 @@ def _reload_currentlyup(self, upops, downops, auto=False, allow=None, def _reload_default(self, upops, downops, auto=False, allow=None, ifacenames=None, excludepats=None, usecurrentconfig=False, - syntaxcheck=False, **extra_args): + syntaxcheck=False, diff=False, **extra_args): """ reload interface config """ + + ifupdownConfig.diff_mode = self.diff_based = diff + new_ifaceobjdict = {} @@ -2396,6 +2409,19 @@ def _reload_default(self, upops, downops, auto=False, allow=None, self.ifaceobjdict = new_ifaceobjdict self.dependency_graph = new_dependency_graph + if diff: + new_filtered_ifacenames = list( + self.get_diff_ifaceobjs(new_ifaceobjdict) + ) + + if not new_filtered_ifacenames: + self.logger.info( + "nothing changed since the last reload, exiting." + if self.diff_based else + "nothing to reload - exiting." + ) + return 0 + self.logger.info('reload: scheduling up on interfaces: %s' %str(new_filtered_ifacenames)) ifupdownflags.flags.CACHE = True @@ -2416,6 +2442,20 @@ def _reload_default(self, upops, downops, auto=False, allow=None, if not iface_read_ret or not ret: raise MainException() + def get_diff_ifaceobjs(self, ifaceobj_dict): + diff_ifname = set() + for ifname, ifaceobjs in ifaceobj_dict.items(): + old_ifaceobjs = statemanager_api.ifaceobjdict.get(ifname) + + if not old_ifaceobjs: + diff_ifname.add(ifname) + continue + + for new, old in itertools.zip_longest(ifaceobjs, old_ifaceobjs): + if new != old: + diff_ifname.add(ifname) + return diff_ifname + def reload(self, *args, **kargs): """ reload interface config """ self.logger.debug('reloading interface config ..') diff --git a/ifupdown2/ifupdown/main.py b/ifupdown2/ifupdown/main.py index 222bfc46..21c90b69 100644 --- a/ifupdown2/ifupdown/main.py +++ b/ifupdown2/ifupdown/main.py @@ -279,4 +279,5 @@ def run_reload(self, args): excludepats=args.excludepats, usecurrentconfig=args.usecurrentconfig, syntaxcheck=args.syntaxcheck, - currentlyup=args.currentlyup) + currentlyup=args.currentlyup, + diff=args.diff) From b5dda061c220941dc1b92930a60a9febe02f9b39 Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Tue, 23 Apr 2024 18:41:26 +0200 Subject: [PATCH 075/132] ifreload-diff: propagate mtu from physical interface to upper devs ifreload-diff isn't processing upper devs when only the mtu is changed/removed from the physical port. Logical devices like bridges and vlan devices rely on mtu from their lower devices. ie mtu travels from lower devices to upper devices. For bonds mtu travels from upper to lower devices. running mtu depends on upper and lower device mtu. With all this implicit mtu config by the kernel in play, we try to be cautious here on which devices we want to reset mtu to default. essentially only physical interfaces which are not bond slaves Signed-off-by: Julien Fortin --- ifupdown2/addons/address.py | 27 +++++++++++++++------------ ifupdown2/ifupdown/ifupdownconfig.py | 2 +- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/ifupdown2/addons/address.py b/ifupdown2/addons/address.py index 5dea9ef7..3d961032 100644 --- a/ifupdown2/addons/address.py +++ b/ifupdown2/addons/address.py @@ -839,6 +839,16 @@ def _check_mtu_config(self, ifaceobj, mtu_str, mtu_int, ifaceobj_getfunc, syntax return retval def _propagate_mtu_to_upper_devs(self, ifaceobj, mtu_str, mtu_int, ifaceobj_getfunc): + if not ( + (not ifupdownflags.flags.ALL or ifupdownconfig.diff_mode) and + not ifaceobj.link_kind and + ifupdownconfig.config.get('adjust_logical_dev_mtu', '1') != '0' + ): + # This is additional cost to us, so do it only when + # ifupdown2 is called on a particular interface and + # it is a physical interface (or diff mode) + return + if (not ifaceobj.upperifaces or (ifaceobj.link_privflags & ifaceLinkPrivFlags.BOND_SLAVE) or (ifaceobj.link_privflags & ifaceLinkPrivFlags.VRF_SLAVE) or @@ -862,18 +872,9 @@ def _process_mtu_config_mtu_valid(self, ifaceobj, ifaceobj_getfunc, mtu_str, mtu if mtu_int != self.cache.get_link_mtu(ifaceobj.name): self.sysfs.link_set_mtu(ifaceobj.name, mtu_str=mtu_str, mtu_int=mtu_int) + self._propagate_mtu_to_upper_devs(ifaceobj, mtu_str, mtu_int, ifaceobj_getfunc) - if (not ifupdownflags.flags.ALL and - not ifaceobj.link_kind and - ifupdownconfig.config.get('adjust_logical_dev_mtu', '1') != '0'): - # This is additional cost to us, so do it only when - # ifupdown2 is called on a particular interface and - # it is a physical interface - self._propagate_mtu_to_upper_devs(ifaceobj, mtu_str, mtu_int, ifaceobj_getfunc) - return - - def _process_mtu_config_mtu_none(self, ifaceobj): - + def _process_mtu_config_mtu_none(self, ifaceobj, ifaceobj_getfunc): if (ifaceobj.link_privflags & ifaceLinkPrivFlags.MGMT_INTF): return @@ -918,6 +919,8 @@ def _process_mtu_config_mtu_none(self, ifaceobj): # on which devices we want to reset mtu to default. # essentially only physical interfaces which are not bond slaves self.sysfs.link_set_mtu(ifaceobj.name, mtu_str=self.default_mtu, mtu_int=self.default_mtu_int) + if ifupdownconfig.diff_mode: + self._propagate_mtu_to_upper_devs(ifaceobj, self.default_mtu, self.default_mtu_int, ifaceobj_getfunc) def _set_bridge_forwarding(self, ifaceobj): """ set ip forwarding to 0 if bridge interface does not have a @@ -1062,7 +1065,7 @@ def process_mtu(self, ifaceobj, ifaceobj_getfunc): self._process_mtu_config_mtu_valid(ifaceobj, ifaceobj_getfunc, mtu_str, mtu_int) else: - self._process_mtu_config_mtu_none(ifaceobj) + self._process_mtu_config_mtu_none(ifaceobj, ifaceobj_getfunc) def up_ipv6_addrgen(self, ifaceobj): user_configured_ipv6_addrgen = ifaceobj.get_attr_value_first('ipv6-addrgen') diff --git a/ifupdown2/ifupdown/ifupdownconfig.py b/ifupdown2/ifupdown/ifupdownconfig.py index bcb860b8..945b6791 100644 --- a/ifupdown2/ifupdown/ifupdownconfig.py +++ b/ifupdown2/ifupdown/ifupdownconfig.py @@ -8,10 +8,10 @@ class ifupdownConfig(): def __init__(self): - self.diff_mode = False self.conf = {} config = ifupdownConfig() +diff_mode = False def reset(): global config From c82c84c52825100f150a865d95501d4a57364076 Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Thu, 25 Jan 2024 23:03:06 +0100 Subject: [PATCH 076/132] lib: addon: removing duplicated get_bridge_port_list method Lots of code duplicate and checks for empty list and such. Trying to tyding this up. Issue: 3736212 Signed-off-by: Julien Fortin --- ifupdown2/addons/bridge.py | 6 +++--- ifupdown2/addons/vlan.py | 2 +- ifupdown2/ifupdown/utils.py | 13 ------------- 3 files changed, 4 insertions(+), 17 deletions(-) diff --git a/ifupdown2/addons/bridge.py b/ifupdown2/addons/bridge.py index 30fc3c51..21cfb345 100644 --- a/ifupdown2/addons/bridge.py +++ b/ifupdown2/addons/bridge.py @@ -1228,7 +1228,7 @@ def _pretty_print_add_ports_error(self, errstr, bridgeifaceobj, bridgeports): self.log_error(bridgeifaceobj.name + ': ' + errstr, bridgeifaceobj) def _add_ports(self, ifaceobj, ifaceobj_getfunc): - bridgeports = self._get_bridge_port_list(ifaceobj) or [] + bridgeports = self._get_bridge_port_list(ifaceobj) bridgeportscondoneregex = self._get_bridge_port_condone_regex(ifaceobj) runningbridgeports = [] @@ -3718,14 +3718,14 @@ def get_ifaceobj_bridge_vids_value(self, ifaceobj): return self.get_ifaceobj_bridge_vids(ifaceobj)[1] def _get_bridge_vids(self, bridgename, ifaceobj_getfunc): - ifaceobjs = ifaceobj_getfunc(bridgename) + ifaceobjs = ifaceobj_getfunc(bridgename) or [] for ifaceobj in ifaceobjs: vids = self.get_ifaceobj_bridge_vids_value(ifaceobj) if vids: return re.split(r'[\s\t,]\s*', vids) return None def _get_bridge_pvid(self, bridgename, ifaceobj_getfunc): - ifaceobjs = ifaceobj_getfunc(bridgename) + ifaceobjs = ifaceobj_getfunc(bridgename) or [] pvid = None for ifaceobj in ifaceobjs: pvid = ifaceobj.get_attr_value_first('bridge-pvid') diff --git a/ifupdown2/addons/vlan.py b/ifupdown2/addons/vlan.py index a906f704..900e70b2 100644 --- a/ifupdown2/addons/vlan.py +++ b/ifupdown2/addons/vlan.py @@ -150,7 +150,7 @@ def vxlan_hopping_filter_bypass(self, ifaceobj, vlanrawdevice, vid, ifaceobj_get bridge_ifaceobjs = ifaceobj_getfunc(vlanrawdevice) if any(map(lambda b: b.link_kind == ifaceLinkKind.BRIDGE, bridge_ifaceobjs)): - bridge_ports = [ port for b in bridge_ifaceobjs for ports in (utils._get_bridge_port_list(b) or []) for port in ifaceobj_getfunc(ports) ] + bridge_ports = [ port for b in bridge_ifaceobjs for ports in (self._get_bridge_port_list(b) or []) for port in (ifaceobj_getfunc(ports) or []) ] bridge_is_vxlan = any(map(lambda p: p.link_kind == ifaceLinkKind.VXLAN, bridge_ports)) else: bridge_is_vxlan = False diff --git a/ifupdown2/ifupdown/utils.py b/ifupdown2/ifupdown/utils.py index b631a68f..2123cc31 100644 --- a/ifupdown2/ifupdown/utils.py +++ b/ifupdown2/ifupdown/utils.py @@ -625,18 +625,5 @@ def parse_port_list(cls, ifacename, port_expr, ifacenames=None): return None return portlist - @classmethod - def _get_bridge_port_list(cls, ifaceobj): - # port list is also available in the previously - # parsed dependent list. Use that if available, instead - # of parsing port expr again - port_list = ifaceobj.lowerifaces - if port_list: - return port_list - ports = utils._get_ifaceobj_bridge_ports(ifaceobj) - if ports: - return utils.parse_port_list(ifaceobj.name, ports) - else: - return None fcntl.fcntl(utils.DEVNULL, fcntl.F_SETFD, fcntl.FD_CLOEXEC) From 9dee032bdc68dce2e4c2c86f055eff591960e43e Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Thu, 28 Nov 2024 15:46:43 -0500 Subject: [PATCH 077/132] addons: bridge: add missing statemanager import Signed-off-by: Julien Fortin --- ifupdown2/addons/bridge.py | 1 + 1 file changed, 1 insertion(+) diff --git a/ifupdown2/addons/bridge.py b/ifupdown2/addons/bridge.py index 21cfb345..70e40a0b 100644 --- a/ifupdown2/addons/bridge.py +++ b/ifupdown2/addons/bridge.py @@ -30,6 +30,7 @@ import ifupdown.exceptions as exceptions import ifupdown.policymanager as policymanager import ifupdown.ifupdownflags as ifupdownflags + from ifupdown.statemanager import statemanager_api as statemanager from nlmanager.nlmanager import Link From 6ea3050ee28db4ef105a30454fdca27888e8d967 Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Thu, 28 Nov 2024 15:48:32 -0500 Subject: [PATCH 078/132] vlan: add ifaceobj_getfunc to vlan Signed-off-by: Andy Roulin Signed-off-by: Julien Fortin --- ifupdown2/addons/vlan.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ifupdown2/addons/vlan.py b/ifupdown2/addons/vlan.py index 900e70b2..61d5cae4 100644 --- a/ifupdown2/addons/vlan.py +++ b/ifupdown2/addons/vlan.py @@ -413,6 +413,6 @@ def run(self, ifaceobj, operation, query_ifaceobj=None, ifaceobj_getfunc=None): not self._is_vlan_device(ifaceobj)): return if operation == 'query-checkcurr': - op_handler(self, ifaceobj, query_ifaceobj) + op_handler(self, ifaceobj, query_ifaceobj, ifaceobj_getfunc=ifaceobj_getfunc) else: - op_handler(self, ifaceobj) + op_handler(self, ifaceobj, ifaceobj_getfunc=ifaceobj_getfunc) From 7a929a48069f0dda20cb46009c31dae221717882 Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Thu, 16 Feb 2023 00:55:21 +0100 Subject: [PATCH 079/132] sonarlink: various fixes Signed-off-by: Julien Fortin --- ifupdown2/ifupdown/ifupdownmain.py | 19 ++++++------------- ifupdown2/ifupdown/networkinterfaces.py | 3 +-- ifupdown2/ifupdown/scheduler.py | 8 ++------ ifupdown2/ifupdown/utils.py | 3 +-- ifupdown2/ifupdownaddons/modulebase.py | 10 ++++------ ifupdown2/ifupdownaddons/systemutils.py | 4 ++-- ifupdown2/lib/iproute2.py | 16 ++++------------ 7 files changed, 20 insertions(+), 43 deletions(-) diff --git a/ifupdown2/ifupdown/ifupdownmain.py b/ifupdown2/ifupdown/ifupdownmain.py index 45ba132f..e294ffd6 100644 --- a/ifupdown2/ifupdown/ifupdownmain.py +++ b/ifupdown2/ifupdown/ifupdownmain.py @@ -129,9 +129,7 @@ def run_up(self, ifaceobj): try: self.netlink.link_up(ifaceobj.name) except Exception: - if ifaceobj.addr_method == 'manual': - pass - else: + if ifaceobj.addr_method != 'manual': raise def _keep_link_down(self, ifaceobj): @@ -177,9 +175,7 @@ def run_down(self, ifaceobj): else: self.logger.info("%s: ifupdown2 cannot bring loopback interface down" % ifaceobj.name) except Exception: - if ifaceobj.addr_method == 'manual': - pass - else: + if ifaceobj.addr_method != 'manual': raise # ifupdown object interface operation handlers @@ -1826,7 +1822,7 @@ def up(self, ops, auto=False, allow_classes=None, ifacenames=None, if printdependency: self.populate_dependency_info(ops, filtered_ifacenames) - self.print_dependency(filtered_ifacenames, printdependency) + self.print_dependency(printdependency) return else: self.populate_dependency_info(ops) @@ -1970,7 +1966,7 @@ def down(self, ops, auto=False, allow_classes=None, ifacenames=None, if printdependency: self.populate_dependency_info(ops, filtered_ifacenames) - self.print_dependency(filtered_ifacenames, printdependency) + self.print_dependency(printdependency) return else: self.populate_dependency_info(ops) @@ -2049,7 +2045,7 @@ def query(self, ops, auto=False, format_list=False, allow_classes=None, self.populate_dependency_info(ops) if ops[0] == 'query-dependency' and printdependency: - self.print_dependency(filtered_ifacenames, printdependency) + self.print_dependency(printdependency) return if format_list and (ops[0] == 'query' or ops[0] == 'query-raw'): @@ -2482,11 +2478,8 @@ def _pretty_print_ordered_dict(self, prefix, argdict): outbuf += '\t%s : %s\n' %(k, str(vlist)) self.logger.debug(outbuf + '}') - def print_dependency(self, ifacenames, format): + def print_dependency(self, format): """ prints iface dependency information """ - - if not ifacenames: - ifacenames = list(self.ifaceobjdict.keys()) if format == 'list': for k,v in list(self.dependency_graph.items()): print('%s : %s' %(k, str(v))) diff --git a/ifupdown2/ifupdown/networkinterfaces.py b/ifupdown2/ifupdown/networkinterfaces.py index c0c8ad96..0222749f 100644 --- a/ifupdown2/ifupdown/networkinterfaces.py +++ b/ifupdown2/ifupdown/networkinterfaces.py @@ -189,13 +189,12 @@ def process_source(self, lines, cur_idx, lineno): if '*' not in sourced_file: self._parse_warn(self._currentfile, lineno, 'cannot find source file %s' %sourced_file) - return 0 + return for f in filenames: self.read_file(f) else: self._parse_error(self._currentfile, lineno, 'unable to read source line') - return 0 def process_source_directory(self, lines, cur_idx, lineno): self.logger.debug('processing source-directory line ..\'%s\'' % lines[cur_idx]) diff --git a/ifupdown2/ifupdown/scheduler.py b/ifupdown2/ifupdown/scheduler.py index e71f7355..b4302224 100644 --- a/ifupdown2/ifupdown/scheduler.py +++ b/ifupdown2/ifupdown/scheduler.py @@ -307,9 +307,7 @@ def run_iface_graph(cls, ifupdownobj, ifacename, ops, parent=None, followdependents, continueonfailure=False) except Exception as e: - if (ifupdownobj.ignore_error(str(e))): - pass - else: + if not ifupdownobj.ignore_error(str(e)): # Dont bring the iface up if children did not come up ifaceobj.set_state_n_status(ifaceState.NEW, ifaceStatus.ERROR) @@ -369,9 +367,7 @@ def run_iface_graph_upper(cls, ifupdownobj, ifacename, ops, parent=None, followdependents, continueonfailure=True) except Exception as e: - if (ifupdownobj.ignore_error(str(e))): - pass - else: + if not ifupdownobj.ignore_error(str(e)): raise @classmethod diff --git a/ifupdown2/ifupdown/utils.py b/ifupdown2/ifupdown/utils.py index 2123cc31..f5dc910e 100644 --- a/ifupdown2/ifupdown/utils.py +++ b/ifupdown2/ifupdown/utils.py @@ -194,8 +194,7 @@ def support_yesno_attrs(attrsdict, attrslist, ifaceobj=None): value = ifaceobj.get_attr_value_first(attr) if value and not utils.is_binary_bool(value): if attr in attrsdict: - bool = utils.get_boolean_from_string(attrsdict[attr]) - attrsdict[attr] = utils.get_yesno_boolean(bool) + attrsdict[attr] = utils.get_yesno_boolean(utils.get_boolean_from_string(attrsdict[attr])) else: for attr in attrslist: if attr in attrsdict: diff --git a/ifupdown2/ifupdownaddons/modulebase.py b/ifupdown2/ifupdownaddons/modulebase.py index 3bfc1e0f..350b84c5 100644 --- a/ifupdown2/ifupdownaddons/modulebase.py +++ b/ifupdown2/ifupdownaddons/modulebase.py @@ -106,10 +106,10 @@ def log_warn(self, str, ifaceobj=None): # the root logger has level NOTSET, and each logging handler logs # at different level. stack = traceback.format_stack() - format = traceback.format_exc() + f = traceback.format_exc() self.logger.debug("%s" % " ".join(stack)[:-1]) - self.logger.debug("%s" % format[:-1]) + self.logger.debug("%s" % f[:-1]) self.logger.warning(str) @@ -124,17 +124,15 @@ def log_error(self, msg, ifaceobj=None, raise_error=True): # we have the root logger has level NOTSET, and each logging handler # logs at different level. stack = traceback.format_stack() - format = traceback.format_exc() + f = traceback.format_exc() self.logger.debug("%s" % " ".join(stack)[:-1]) - self.logger.debug("%s" % format[:-1]) + self.logger.debug("%s" % f[:-1]) if raise_error: raise ModuleBaseException(msg) else: self.logger.error(msg) - else: - pass def is_process_running(self, procName): try: diff --git a/ifupdown2/ifupdownaddons/systemutils.py b/ifupdown2/ifupdownaddons/systemutils.py index ceced4dd..c1daa3ee 100644 --- a/ifupdown2/ifupdownaddons/systemutils.py +++ b/ifupdown2/ifupdownaddons/systemutils.py @@ -50,8 +50,8 @@ def check_service_status(cls, servicename=None): return False return True - @classmethod - def is_process_running(self, processname): + @staticmethod + def is_process_running(processname): if not processname: return False try: diff --git a/ifupdown2/lib/iproute2.py b/ifupdown2/lib/iproute2.py index 5cff8e6e..97de4aa5 100644 --- a/ifupdown2/lib/iproute2.py +++ b/ifupdown2/lib/iproute2.py @@ -814,14 +814,6 @@ def bridge_vlan_add_vid_list_self(self, ifname, vids, is_bridge=True): "vlan add vid %s dev %s %s" % (v, ifname, target) ) - def bridge_vlan_del_vid_list_self(self, ifname, vids, is_bridge=True): - target = "self" if is_bridge else "" - for v in vids: - self.__execute_or_batch( - utils.bridge_cmd, - "vlan del vid %s dev %s %s" % (v, ifname, target) - ) - def bridge_vlan_del_pvid(self, ifname, pvid): self.__execute_or_batch( utils.bridge_cmd, @@ -1013,8 +1005,8 @@ def compress_vnifilter_into_ranges(self, vnis_ints, vnisd): continue else: if vend > vbegin: - range = '%d-%d' %(vbegin, vend) - vnisd_ranges[range] = lastg + r = '%d-%d' %(vbegin, vend) + vnisd_ranges[r] = lastg else: vnisd_ranges['%s' %vbegin] = lastg vbegin = v @@ -1023,8 +1015,8 @@ def compress_vnifilter_into_ranges(self, vnis_ints, vnisd): if vbegin: if vend > vbegin: - range = '%d-%d' %(vbegin, vend) - vnisd_ranges[range] = lastg + r = '%d-%d' %(vbegin, vend) + vnisd_ranges[r] = lastg else: vnisd_ranges['%s' %vbegin] = lastg return vnisd_ranges From 7b06712b0c5492fc9ae41a164263fac64384f790 Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Thu, 28 Nov 2024 15:54:50 -0500 Subject: [PATCH 080/132] addons: vxlan: remove ifupdownaddons.cache import Signed-off-by: Julien Fortin --- ifupdown2/addons/vxlan.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/ifupdown2/addons/vxlan.py b/ifupdown2/addons/vxlan.py index a638d293..4cab0332 100644 --- a/ifupdown2/addons/vxlan.py +++ b/ifupdown2/addons/vxlan.py @@ -20,7 +20,6 @@ from ifupdown2.ifupdown.iface import ifaceLinkKind, ifaceLinkPrivFlags, ifaceStatus, iface from ifupdown2.ifupdown.utils import utils from ifupdown2.ifupdown.statemanager import statemanager_api as statemanager - from ifupdown2.ifupdownaddons.cache import * from ifupdown2.ifupdownaddons.modulebase import moduleBase except ImportError: @@ -37,7 +36,6 @@ from ifupdown.utils import utils from ifupdown.statemanager import statemanager_api as statemanager - from ifupdownaddons.cache import * from ifupdownaddons.modulebase import moduleBase From 1db40e09f05942e916bc2edc1ee68ce123959875 Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Thu, 28 Nov 2024 15:55:10 -0500 Subject: [PATCH 081/132] utilsbase: remove ifupdownaddons.cache import Signed-off-by: Julien Fortin --- ifupdown2/ifupdownaddons/utilsbase.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/ifupdown2/ifupdownaddons/utilsbase.py b/ifupdown2/ifupdownaddons/utilsbase.py index 8ac3c4a8..5ffa5a75 100644 --- a/ifupdown2/ifupdownaddons/utilsbase.py +++ b/ifupdown2/ifupdownaddons/utilsbase.py @@ -10,13 +10,11 @@ try: from ifupdown2.ifupdown.iface import * from ifupdown2.ifupdown.utils import utils - from ifupdown2.ifupdownaddons.cache import * import ifupdown2.ifupdown.ifupdownflags as ifupdownflags except ImportError: from ifupdown.iface import * from ifupdown.utils import utils - from ifupdownaddons.cache import * import ifupdown.ifupdownflags as ifupdownflags From 773be9058f03fd92de2624539fa2351517b2bc4f Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Tue, 26 Mar 2024 22:22:14 +0100 Subject: [PATCH 082/132] scheduler: run_queue: process child interface first In diff mode followdependents=False, we want to process 'child interfaces' before processing parents aka the interfaces with a positive reference count (get_iface_refcnt) Signed-off-by: Julien Fortin --- ifupdown2/ifupdown/ifupdownmain.py | 15 ++++++++------- ifupdown2/ifupdown/scheduler.py | 15 ++++++++++++++- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/ifupdown2/ifupdown/ifupdownmain.py b/ifupdown2/ifupdown/ifupdownmain.py index e294ffd6..bd61059a 100644 --- a/ifupdown2/ifupdown/ifupdownmain.py +++ b/ifupdown2/ifupdown/ifupdownmain.py @@ -1561,7 +1561,7 @@ def _schedule_addon_translate(self): pass def _sched_ifaces(self, ifacenames, ops, skipupperifaces=False, - followdependents=True, sort=False): + followdependents=True, sort=False, diff_mode=False): self.logger.debug('scheduling \'%s\' for %s' %(str(ops), str(ifacenames))) self._pretty_print_ordered_dict('dependency graph', @@ -1573,7 +1573,7 @@ def _sched_ifaces(self, ifacenames, ops, skipupperifaces=False, else ifaceSchedulerFlags.POSTORDER, followdependents=followdependents, skipupperifaces=skipupperifaces, - sort=True if (sort or ifupdownflags.flags.CLASS) else False) + sort=True if (sort or ifupdownflags.flags.CLASS) else False, diff_mode=diff_mode) return ifaceScheduler.get_sched_status() def _render_ifacename(self, ifacename): @@ -2425,7 +2425,7 @@ def _reload_default(self, upops, downops, auto=False, allow=None, ret = self._sched_ifaces(new_filtered_ifacenames, upops, followdependents=True if ifupdownflags.flags.WITH_DEPENDS - else False) + else False, diff_mode=self.diff_based) except Exception as e: ret = None self.logger.error(str(e)) @@ -2439,17 +2439,18 @@ def _reload_default(self, upops, downops, auto=False, allow=None, raise MainException() def get_diff_ifaceobjs(self, ifaceobj_dict): - diff_ifname = set() + diff_ifname = [] for ifname, ifaceobjs in ifaceobj_dict.items(): old_ifaceobjs = statemanager_api.ifaceobjdict.get(ifname) if not old_ifaceobjs: - diff_ifname.add(ifname) + if ifname not in diff_ifname: + diff_ifname.append(ifname) continue for new, old in itertools.zip_longest(ifaceobjs, old_ifaceobjs): - if new != old: - diff_ifname.add(ifname) + if new != old and ifname not in diff_ifname: + diff_ifname.append(ifname) return diff_ifname def reload(self, *args, **kargs): diff --git a/ifupdown2/ifupdown/scheduler.py b/ifupdown2/ifupdown/scheduler.py index b4302224..e76b10e6 100644 --- a/ifupdown2/ifupdown/scheduler.py +++ b/ifupdown2/ifupdown/scheduler.py @@ -499,7 +499,7 @@ def get_sorted_iface_list(cls, ifupdownobj, ifacenames, ops, def sched_ifaces(cls, ifupdownobj, ifacenames, ops, dependency_graph=None, indegrees=None, order=ifaceSchedulerFlags.POSTORDER, - followdependents=True, skipupperifaces=False, sort=False): + followdependents=True, skipupperifaces=False, sort=False, diff_mode=False): """ runs interface configuration modules on interfaces passed as argument. Runs topological sort on interface dependency graph. @@ -560,6 +560,19 @@ def sched_ifaces(cls, ifupdownobj, ifacenames, ops, ops, dependency_graph, indegrees) if run_queue and 'up' in ops[0]: run_queue.reverse() + elif diff_mode: + # In diff mode followdependents=False, we want to process + # 'child interfaces' before processing parents aka the interfaces + # with a positive reference count (get_iface_refcnt) + for ifacename in ifacenames: + if indegrees.get(ifacename): + run_queue.insert(0, ifacename) + else: + run_queue.append(ifacename) + + ifupdownobj.logger.debug("diff ref_count=%s" % indegrees) + ifupdownobj.logger.debug("diff run_queue=%s" % run_queue) + else: # if -a is set, we pick the interfaces # that have no parents and use a sorted list of those From c5c623ef1952e6d969b1b17833afb5b982e42bca Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Thu, 4 Apr 2024 22:35:54 +0200 Subject: [PATCH 083/132] ifreload-diff: process interface if not present on the system If for some reason the statemanager has ifaceobjs for an interface but the device doesn't exists on the system (not present in the cache) then we should add that device to the reload list.. Signed-off-by: Julien Fortin --- ifupdown2/ifupdown/ifupdownmain.py | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/ifupdown2/ifupdown/ifupdownmain.py b/ifupdown2/ifupdown/ifupdownmain.py index bd61059a..c77deab2 100644 --- a/ifupdown2/ifupdown/ifupdownmain.py +++ b/ifupdown2/ifupdown/ifupdownmain.py @@ -2440,17 +2440,27 @@ def _reload_default(self, upops, downops, auto=False, allow=None, def get_diff_ifaceobjs(self, ifaceobj_dict): diff_ifname = [] + for ifname, ifaceobjs in ifaceobj_dict.items(): + if ifname in diff_ifname: + continue + old_ifaceobjs = statemanager_api.ifaceobjdict.get(ifname) if not old_ifaceobjs: - if ifname not in diff_ifname: + diff_ifname.append(ifname) + else: + # If for some reason the statemanager has ifaceobjs but the device + # doesn't exist in the system (is not cached), then we should no + # matter what bring it back up + if not self.netlink.cache.link_exists(ifname): diff_ifname.append(ifname) - continue + continue + + for new, old in itertools.zip_longest(ifaceobjs, old_ifaceobjs): + if new != old and ifname not in diff_ifname: + diff_ifname.append(ifname) - for new, old in itertools.zip_longest(ifaceobjs, old_ifaceobjs): - if new != old and ifname not in diff_ifname: - diff_ifname.append(ifname) return diff_ifname def reload(self, *args, **kargs): From d473aec90917df78d5df01c196da99b4e2d3f1ad Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Tue, 16 Apr 2024 00:44:07 +0200 Subject: [PATCH 084/132] diff: add lower dev of removed interfaces to the run queue $ cat $BEFORE [...] auto hostbond_4 iface hostbond_4 mtu 9100 bond-slaves swp8 bond-mode 802.3ad bond-lacp-bypass-allow yes bridge-pvid 1002 auto swp8 iface swp8 link-speed 10000 link-autoneg off auto br_default iface br_default bridge-ports swp5 hostbond_4 hwaddress 44:38:39:ff:ff:13 bridge-vlan-aware yes bridge-vids 1000 1002 1004 1006 1008 bridge-pvid 1 bridge-stp yes bridge-mcsnoop no mstpctl-forcevers rstp $ $ $ $ cat $AFTER [...] auto swp8 iface swp8 link-speed 10000 link-autoneg off auto swp8.4000 iface swp8.4000 vrf vrf1 auto vrf1 iface vrf1 vrf-table auto auto br_default iface br_default bridge-ports swp5 hwaddress 44:38:39:ff:ff:13 bridge-vlan-aware yes bridge-vids 1000 1002 1004 1006 1008 bridge-pvid 1 bridge-stp yes bridge-mcsnoop no mstpctl-forcevers rstp $ $ ifdown -a -X eth0 -X mgmt ; cp $BEFORE /etc/network/interfaces ; ifreload -a ; cp $AFTER /etc/network/interfaces ; ifreload -a --diff; error: swp8.4000: netlink: swp8.4000: ip link set dev swp8.4000 up: operation failed with 'Network is down' (100) $ In that case swp8 goes down when hostbond_4 is removed, swp8 /e/n/i config hasn't changed though, so it's not picked up by --diff therefore its not reloaded and stays down which is a problem. The fix is to add lower devices of removed interfaces to the run queue. Signed-off-by: Julien Fortin --- ifupdown2/ifupdown/ifupdownmain.py | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/ifupdown2/ifupdown/ifupdownmain.py b/ifupdown2/ifupdown/ifupdownmain.py index c77deab2..dfbb78eb 100644 --- a/ifupdown2/ifupdown/ifupdownmain.py +++ b/ifupdown2/ifupdown/ifupdownmain.py @@ -2175,8 +2175,7 @@ def _reload_default(self, upops, downops, auto=False, allow=None, ifupdownConfig.diff_mode = self.diff_based = diff new_ifaceobjdict = {} - - + ifacedownlist = [] iface_read_ret = self.read_iface_config() if not self.ifaceobjdict: @@ -2256,7 +2255,6 @@ def _reload_default(self, upops, downops, auto=False, allow=None, # present in the new config # - interfaces that were changed between the last and current # config - ifacedownlist = [] for ifname in list(self.ifaceobjdict.keys()): lastifaceobjlist = self.ifaceobjdict.get(ifname) if not self.is_ifaceobj_builtin(lastifaceobjlist[0]): @@ -2402,14 +2400,15 @@ def _reload_default(self, upops, downops, auto=False, allow=None, ifupdownflags.flags.ALL = True ifupdownflags.flags.WITH_DEPENDS = True # and now, we are back to the current config in ifaceobjdict - self.ifaceobjdict = new_ifaceobjdict - self.dependency_graph = new_dependency_graph if diff: new_filtered_ifacenames = list( - self.get_diff_ifaceobjs(new_ifaceobjdict) + self.get_diff_ifaceobjs(new_ifaceobjdict, ifacedownlist, self.dependency_graph) ) + self.ifaceobjdict = new_ifaceobjdict + self.dependency_graph = new_dependency_graph + if not new_filtered_ifacenames: self.logger.info( "nothing changed since the last reload, exiting." @@ -2438,7 +2437,7 @@ def _reload_default(self, upops, downops, auto=False, allow=None, if not iface_read_ret or not ret: raise MainException() - def get_diff_ifaceobjs(self, ifaceobj_dict): + def get_diff_ifaceobjs(self, ifaceobj_dict, ifacedownlist, down_dependency_graph): diff_ifname = [] for ifname, ifaceobjs in ifaceobj_dict.items(): @@ -2461,6 +2460,13 @@ def get_diff_ifaceobjs(self, ifaceobj_dict): if new != old and ifname not in diff_ifname: diff_ifname.append(ifname) + if ifacedownlist and down_dependency_graph: + # If an interface is removed / downed we might need to reload lower_interfaces + for down_ifname in ifacedownlist: + for lower_ifname in down_dependency_graph.get(down_ifname, []): + if lower_ifname in ifaceobj_dict and lower_ifname not in diff_ifname: + diff_ifname.append(lower_ifname) + return diff_ifname def reload(self, *args, **kargs): From 849f6ec645f5b1976aec9c513c4fc540fab41765 Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Tue, 30 Apr 2024 22:17:49 +0200 Subject: [PATCH 085/132] diff-mode: reload all clagd interface if clagd change is detected The clagd module internally caches clagd-attribute/values and only apply/reload the config if all interfaces are processed. This is not compatible with the diff-mode. We need to force reload all clagd interfaces if we detect a change. The fix checks for a clagd change and populates the run queue with clagd interface as needed. Signed-off-by: Julien Fortin --- ifupdown2/ifupdown/ifupdownmain.py | 36 +++++++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/ifupdown2/ifupdown/ifupdownmain.py b/ifupdown2/ifupdown/ifupdownmain.py index dfbb78eb..ed72d550 100644 --- a/ifupdown2/ifupdown/ifupdownmain.py +++ b/ifupdown2/ifupdown/ifupdownmain.py @@ -433,6 +433,7 @@ def __init__(self, config={}, args=None, } self.diff_based = False + self.clagd_change_detected = False def _get_mgmt_iface_default_prefix(self): mgmt_iface_default_prefix = None @@ -2437,28 +2438,57 @@ def _reload_default(self, upops, downops, auto=False, allow=None, if not iface_read_ret or not ret: raise MainException() + def is_clag_interface(self, ifaceobjs): + for ifaceobj in ifaceobjs or []: + for attribute in ifaceobj.config.keys(): + if attribute.startswith("clagd-"): + return True + return False + + def add_diff_interface_with_clag_check(self, diff_list: list, ifname: str, current_clag_interface: bool): + diff_list.append(ifname) + if current_clag_interface: + self.clagd_change_detected = True + def get_diff_ifaceobjs(self, ifaceobj_dict, ifacedownlist, down_dependency_graph): diff_ifname = [] + # any changes in clagd attributes will result in all clagd interfaces + # to be added to the run queue + self.clagd_change_detected = False + clag_interfaces = [] + for ifname, ifaceobjs in ifaceobj_dict.items(): + current_clagd_interface = False + if ifname in diff_ifname: continue + if self.is_clag_interface(ifaceobjs): + clag_interfaces.append(ifname) + current_clagd_interface = True + old_ifaceobjs = statemanager_api.ifaceobjdict.get(ifname) if not old_ifaceobjs: - diff_ifname.append(ifname) + self.add_diff_interface_with_clag_check(diff_ifname, ifname, current_clagd_interface) else: # If for some reason the statemanager has ifaceobjs but the device # doesn't exist in the system (is not cached), then we should no # matter what bring it back up if not self.netlink.cache.link_exists(ifname): - diff_ifname.append(ifname) + self.add_diff_interface_with_clag_check(diff_ifname, ifname, current_clagd_interface) continue for new, old in itertools.zip_longest(ifaceobjs, old_ifaceobjs): if new != old and ifname not in diff_ifname: - diff_ifname.append(ifname) + self.add_diff_interface_with_clag_check(diff_ifname, ifname, current_clagd_interface) + + if self.clagd_change_detected: + self.logger.error(f"diff-mode: clagd changes detected, updating run queue with: {clag_interfaces}") + for ifname in clag_interfaces: + if ifname not in diff_ifname: + diff_ifname.append(ifname) if ifacedownlist and down_dependency_graph: # If an interface is removed / downed we might need to reload lower_interfaces From 218df68fc5885d326257f1aec869d78848519482 Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Tue, 7 May 2024 16:52:48 +0200 Subject: [PATCH 086/132] diff-mode: check interface admin state during diff-check Signed-off-by: Julien Fortin --- ifupdown2/ifupdown/ifupdownmain.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/ifupdown2/ifupdown/ifupdownmain.py b/ifupdown2/ifupdown/ifupdownmain.py index ed72d550..0e8e898b 100644 --- a/ifupdown2/ifupdown/ifupdownmain.py +++ b/ifupdown2/ifupdown/ifupdownmain.py @@ -2450,6 +2450,10 @@ def add_diff_interface_with_clag_check(self, diff_list: list, ifname: str, curre if current_clag_interface: self.clagd_change_detected = True + def interface_should_be_up(self, ifname, ifaceobjs): + # Return True if the link is down and should be up, check for link-down yes on any ifaceobjs + return not any([ifaceobj.link_privflags & ifaceLinkPrivFlags.KEEP_LINK_DOWN for ifaceobj in ifaceobjs]) and not self.netlink.cache.link_is_up(ifname) + def get_diff_ifaceobjs(self, ifaceobj_dict, ifacedownlist, down_dependency_graph): diff_ifname = [] @@ -2476,7 +2480,8 @@ def get_diff_ifaceobjs(self, ifaceobj_dict, ifacedownlist, down_dependency_graph # If for some reason the statemanager has ifaceobjs but the device # doesn't exist in the system (is not cached), then we should no # matter what bring it back up - if not self.netlink.cache.link_exists(ifname): + # We also do a quick check to see if the interface is right admin state + if not self.netlink.cache.link_exists(ifname) or self.interface_should_be_up(ifname, ifaceobjs): self.add_diff_interface_with_clag_check(diff_ifname, ifname, current_clagd_interface) continue From 8ee517d29de120fc9af8ee11c1779cd245fc8b33 Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Wed, 24 Jul 2024 12:58:59 +0200 Subject: [PATCH 087/132] ifupdownmain: update log level for clagd change detected log Signed-off-by: Julien Fortin --- ifupdown2/ifupdown/ifupdownmain.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ifupdown2/ifupdown/ifupdownmain.py b/ifupdown2/ifupdown/ifupdownmain.py index 0e8e898b..7cfeac79 100644 --- a/ifupdown2/ifupdown/ifupdownmain.py +++ b/ifupdown2/ifupdown/ifupdownmain.py @@ -2490,7 +2490,7 @@ def get_diff_ifaceobjs(self, ifaceobj_dict, ifacedownlist, down_dependency_graph self.add_diff_interface_with_clag_check(diff_ifname, ifname, current_clagd_interface) if self.clagd_change_detected: - self.logger.error(f"diff-mode: clagd changes detected, updating run queue with: {clag_interfaces}") + self.logger.info(f"diff-mode: clagd changes detected, updating run queue with: {clag_interfaces}") for ifname in clag_interfaces: if ifname not in diff_ifname: diff_ifname.append(ifname) From a7fcaaa75f582d2ee7bc9bda22f8bedb84f27cbb Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Tue, 16 Apr 2024 19:45:39 +0200 Subject: [PATCH 088/132] ifupdownmain: diff-mode: save state even if there's no diff if interfaces are downed but nothing else changes then we need to save the state otherwise the next ifreload won't know about the changes Signed-off-by: Julien Fortin --- ifupdown2/ifupdown/ifupdownmain.py | 1 + 1 file changed, 1 insertion(+) diff --git a/ifupdown2/ifupdown/ifupdownmain.py b/ifupdown2/ifupdown/ifupdownmain.py index 7cfeac79..dadf541d 100644 --- a/ifupdown2/ifupdown/ifupdownmain.py +++ b/ifupdown2/ifupdown/ifupdownmain.py @@ -2416,6 +2416,7 @@ def _reload_default(self, upops, downops, auto=False, allow=None, if self.diff_based else "nothing to reload - exiting." ) + self._save_state() return 0 self.logger.info('reload: scheduling up on interfaces: %s' From cc14664d2b265432d90c22943f314a79c9ec8487 Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Wed, 15 Feb 2023 14:05:53 +0100 Subject: [PATCH 089/132] sonarlink: remove uneeded 'pass' Signed-off-by: Julien Fortin --- ifupdown2/ifupdown/graph.py | 1 - ifupdown2/ifupdown/ifupdownmain.py | 8 -------- ifupdown2/ifupdown/scheduler.py | 9 +-------- ifupdown2/ifupdown/template.py | 1 - ifupdown2/ifupdown/utils.py | 2 -- ifupdown2/ifupdownaddons/modulebase.py | 2 -- ifupdown2/ifupdownaddons/mstpctlutil.py | 1 - 7 files changed, 1 insertion(+), 23 deletions(-) diff --git a/ifupdown2/ifupdown/graph.py b/ifupdown2/ifupdown/graph.py index 40467c40..1207afbd 100644 --- a/ifupdown2/ifupdown/graph.py +++ b/ifupdown2/ifupdown/graph.py @@ -62,7 +62,6 @@ def topological_sort_graphs_all(cls, dependency_graphs, indegrees_arg): except Exception: cls.logger.debug('topological_sort_graphs_all: did not find %s' %y) indegrees[y] = 0 - pass if indegrees.get(y) == 0: Q.append(y) diff --git a/ifupdown2/ifupdown/ifupdownmain.py b/ifupdown2/ifupdown/ifupdownmain.py index dadf541d..a8682d1c 100644 --- a/ifupdown2/ifupdown/ifupdownmain.py +++ b/ifupdown2/ifupdown/ifupdownmain.py @@ -228,13 +228,10 @@ def log_warn(self, str): traceback.print_stack() traceback.print_exc() self.logger.warning(str) - pass def log_error(self, str): if self.ignore_error(str) == False: raise Exception(str) - else: - pass def link_exists(self, ifacename): return os.path.exists('/sys/class/net/%s' %ifacename) @@ -753,7 +750,6 @@ def query_upperifaces(self, ifaceobj, ops, ifacenames): self.logger.warning('%s: error getting upper interfaces (%s)' %(ifaceobj.name, str(e))) ulist = None - pass if ulist: ret_ulist.extend(ulist) return list(set(ret_ulist)) @@ -864,7 +860,6 @@ def populate_dependency_info(self, ops, ifacenames=None, old_ifaceobjs=False): d.upperifaces.remove(i) except Exception: self.logger.debug('error removing %s from %s upperifaces' %(i, d)) - pass self.logger.debug("populate_dependency_info: deleting blacklisted interface %s" %i) del self.dependency_graph[i] continue @@ -1737,7 +1732,6 @@ def _process_delay_admin_state_queue(self, op): func(i) except Exception as e: self.logger.warning(str(e)) - pass def _get_iface_exclude_companion(self, ifacename): try: @@ -2243,7 +2237,6 @@ def _reload_default(self, upops, downops, auto=False, allow=None, except Exception as e: self.logger.info("error generating dependency graph for " "saved interfaces (%s)" %str(e)) - pass # make sure we pick up built-in interfaces # if config file had 'ifreload_down_changed' variable @@ -2384,7 +2377,6 @@ def _reload_default(self, upops, downops, auto=False, allow=None, sort=True) except Exception as e: self.logger.error(str(e)) - pass finally: self.flags.SCHED_SKIP_CHECK_UPPERIFACES = False self._process_delay_admin_state_queue('down') diff --git a/ifupdown2/ifupdown/scheduler.py b/ifupdown2/ifupdown/scheduler.py index e76b10e6..a70810fb 100644 --- a/ifupdown2/ifupdown/scheduler.py +++ b/ifupdown2/ifupdown/scheduler.py @@ -116,7 +116,6 @@ def run_iface_op(cls, ifupdownobj, ifaceobj, op, cenv=None): ifupdownobj.logger.error(str(e)) # Continue with rest of the modules - pass finally: if err or ifaceobj.status == ifaceStatus.ERROR: ifaceobj.set_state_n_status(ifaceState.from_str(op), @@ -186,7 +185,6 @@ def run_iface_list_ops(cls, ifupdownobj, ifaceobjs, ops): if not ifupdownobj.link_master_slave_ignore_error(str(e)): ifupdownobj.logger.warning('%s: %s' %(ifaceobjs[0].name, str(e))) - pass for ifaceobj in ifaceobjs: cls.run_iface_op(ifupdownobj, ifaceobj, op, cenv=ifupdownobj.generate_running_env(ifaceobj, op) @@ -199,7 +197,6 @@ def run_iface_list_ops(cls, ifupdownobj, ifaceobjs, ops): for ifaceobj in ifaceobjs] except Exception as e: ifupdownobj.logger.warning('%s' %str(e)) - pass @classmethod def _check_upperifaces(cls, ifupdownobj, ifaceobj, ops, parent, @@ -330,11 +327,8 @@ def run_iface_list(cls, ifupdownobj, ifacenames, if ifupdownobj.logger.isEnabledFor(logging.DEBUG): traceback.print_tb(sys.exc_info()[2]) ifupdownobj.logger.error('%s : %s' %(ifacename, str(e))) - pass else: - if (ifupdownobj.ignore_error(str(e))): - pass - else: + if not (ifupdownobj.ignore_error(str(e))): raise Exception('%s : (%s)' %(ifacename, str(e))) @classmethod @@ -384,7 +378,6 @@ def run_iface_list_upper(cls, ifupdownobj, ifacenames, if ifupdownobj.logger.isEnabledFor(logging.DEBUG): traceback.print_tb(sys.exc_info()[2]) ifupdownobj.logger.warning('%s : %s' %(ifacename, str(e))) - pass @classmethod def _get_valid_upperifaces(cls, ifupdownobj, ifacenames, diff --git a/ifupdown2/ifupdown/template.py b/ifupdown2/ifupdown/template.py index d1844f2d..90c39706 100644 --- a/ifupdown2/ifupdown/template.py +++ b/ifupdown2/ifupdown/template.py @@ -31,7 +31,6 @@ def __init__(self, template_engine, template_enable='0', except Exception as e: self.logger.warning('unable to load template engine %s (%s)' %(template_engine, str(e))) - pass if template_lookuppath: try: self.logger.debug('setting template lookuppath to %s' diff --git a/ifupdown2/ifupdown/utils.py b/ifupdown2/ifupdown/utils.py index f5dc910e..312967c8 100644 --- a/ifupdown2/ifupdown/utils.py +++ b/ifupdown2/ifupdown/utils.py @@ -147,7 +147,6 @@ class utils(): def mac_str_to_int(cls, hw_address): mac = 0 if hw_address: - pass for i in hw_address.translate(cls.mac_translate_tab).split(): mac = mac << 8 mac += int(i, 16) @@ -450,7 +449,6 @@ def ranges_to_ints(cls, rangelist): result.append(a) except Exception: cls.logger.warning('unable to parse vids \'%s\'' %''.join(rangelist)) - pass return result @classmethod diff --git a/ifupdown2/ifupdownaddons/modulebase.py b/ifupdown2/ifupdownaddons/modulebase.py index 350b84c5..5e2fd194 100644 --- a/ifupdown2/ifupdownaddons/modulebase.py +++ b/ifupdown2/ifupdownaddons/modulebase.py @@ -444,7 +444,6 @@ def _get_reserved_vlan_range(self): except Exception as e: self.logger.debug('%s failed (%s)' %(get_resvvlan, str(e))) # ignore errors - pass return (start, end) def _get_vrf_context(self): @@ -455,7 +454,6 @@ def _get_vrf_context(self): self.logger.debug('failed to get vrf id (%s)' %str(e)) # ignore errors vrfid = None - pass return vrfid def _handle_reserved_vlan(self, vlanid, logprefix='', end=-1): diff --git a/ifupdown2/ifupdownaddons/mstpctlutil.py b/ifupdown2/ifupdownaddons/mstpctlutil.py index 5c27ff73..e0953910 100644 --- a/ifupdown2/ifupdownaddons/mstpctlutil.py +++ b/ifupdown2/ifupdownaddons/mstpctlutil.py @@ -223,7 +223,6 @@ def get_bridge_attrs(self, bridgename): except Exception as e: self.logger.debug(bridgeattrs) self.logger.debug(str(e)) - pass return bridgeattrs def get_bridge_attr(self, bridgename, attrname): From 3c3910fe46a3e56af8efa275c0ddbb48df5451f1 Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Thu, 28 Nov 2024 16:39:24 -0500 Subject: [PATCH 090/132] ifupdownmain: cleanups Signed-off-by: Julien Fortin --- ifupdown2/ifupdown/ifupdownmain.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/ifupdown2/ifupdown/ifupdownmain.py b/ifupdown2/ifupdown/ifupdownmain.py index a8682d1c..a89aff8f 100644 --- a/ifupdown2/ifupdown/ifupdownmain.py +++ b/ifupdown2/ifupdown/ifupdownmain.py @@ -231,7 +231,7 @@ def log_warn(self, str): def log_error(self, str): if self.ignore_error(str) == False: - raise Exception(str) + raise MainException(str) def link_exists(self, ifacename): return os.path.exists('/sys/class/net/%s' %ifacename) @@ -862,7 +862,6 @@ def populate_dependency_info(self, ops, ifacenames=None, old_ifaceobjs=False): self.logger.debug('error removing %s from %s upperifaces' %(i, d)) self.logger.debug("populate_dependency_info: deleting blacklisted interface %s" %i) del self.dependency_graph[i] - continue def _check_config_no_repeats(self, ifaceobj): """ check if object has an attribute that is @@ -1362,7 +1361,6 @@ def _ifaceobj_syntax_checker(self, ifaceobj): ret = False self.logger.warning('%s: unsupported attribute \'%s\'' \ % (ifaceobj.name, attrname)) - continue return ret def read_iface_config(self, raw=False): @@ -2350,7 +2348,6 @@ def _reload_default(self, upops, downops, auto=False, allow=None, newobj = newifaceobjlist[objidx] if not newobj.compare(oldobj): ifacedownlist.append(ifname) - continue if ifacedownlist: self.logger.info('reload: scheduling down on interfaces: %s' From b4c3ea67008d7a663d46d7dba8c8c18ae1f82be3 Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Wed, 15 Feb 2023 17:52:37 +0100 Subject: [PATCH 091/132] sonarlink: eliminate except clauses that are not required Signed-off-by: Julien Fortin --- ifupdown2/ifupdown/ifupdownmain.py | 55 +++++++++++-------------- ifupdown2/ifupdownaddons/modulebase.py | 3 -- ifupdown2/ifupdownaddons/mstpctlutil.py | 2 - ifupdown2/lib/iproute2.py | 2 - 4 files changed, 25 insertions(+), 37 deletions(-) diff --git a/ifupdown2/ifupdown/ifupdownmain.py b/ifupdown2/ifupdown/ifupdownmain.py index a89aff8f..d02f7810 100644 --- a/ifupdown2/ifupdown/ifupdownmain.py +++ b/ifupdown2/ifupdown/ifupdownmain.py @@ -1424,37 +1424,32 @@ def load_addon_modules(self, modules_dir_list): for modules_dir in modules_dir_list: if modules_dir not in sys.path: sys.path.insert(1, modules_dir) - try: - for op, mlist in list(self.module_ops.items()): - for mname in mlist: - if self.modules.get(mname): + for op, mlist in list(self.module_ops.items()): + for mname in mlist: + if self.modules.get(mname): + continue + mpath = modules_dir + '/' + mname + '.py' + if os.path.exists(mpath) and mpath not in failed_import: + try: + m = __import__(mname) + mclass = getattr(m, mname) + except Exception as e: + self.logger.warning('cannot load "%s" module: %s' % (mname, str(e))) + failed_import.append(mpath) continue - mpath = modules_dir + '/' + mname + '.py' - if os.path.exists(mpath) and mpath not in failed_import: - try: - m = __import__(mname) - mclass = getattr(m, mname) - except Exception as e: - self.logger.warning('cannot load "%s" module: %s' % (mname, str(e))) - failed_import.append(mpath) - continue - try: - minstance = mclass() - script_override = minstance.get_overrides_ifupdown_scripts() - self.overridden_ifupdown_scripts.extend(script_override) - except moduleNotSupported as e: - self.logger.info('module %s not loaded (%s)' - %(mname, str(e))) - continue - except Exception: - raise - self.modules[mname] = minstance - try: - self.module_attrs[mname] = minstance.get_modinfo() - except Exception: - pass - except Exception: - raise + try: + minstance = mclass() + script_override = minstance.get_overrides_ifupdown_scripts() + self.overridden_ifupdown_scripts.extend(script_override) + except moduleNotSupported as e: + self.logger.info('module %s not loaded (%s)' + % (mname, str(e))) + continue + self.modules[mname] = minstance + try: + self.module_attrs[mname] = minstance.get_modinfo() + except Exception: + pass # Assign all modules to query operations self.module_ops['query-checkcurr'] = list(self.modules.keys()) diff --git a/ifupdown2/ifupdownaddons/modulebase.py b/ifupdown2/ifupdownaddons/modulebase.py index 5e2fd194..79107834 100644 --- a/ifupdown2/ifupdownaddons/modulebase.py +++ b/ifupdown2/ifupdownaddons/modulebase.py @@ -146,12 +146,9 @@ def is_process_running(self, procName): def get_ifaces_from_proc(self): ifacenames = [] with open('/proc/net/dev') as f: - try: lines = f.readlines() for line in lines[2:]: ifacenames.append(line.split()[0].strip(': ')) - except Exception: - raise return ifacenames def parse_regex(self, ifacename, expr, ifacenames=None): diff --git a/ifupdown2/ifupdownaddons/mstpctlutil.py b/ifupdown2/ifupdownaddons/mstpctlutil.py index e0953910..c3755ca7 100644 --- a/ifupdown2/ifupdownaddons/mstpctlutil.py +++ b/ifupdown2/ifupdownaddons/mstpctlutil.py @@ -96,8 +96,6 @@ def batch_commit(self): "%s batch -" % utils.mstpctl_cmd, stdin="\n".join(self.__batch) ) - except Exception: - raise finally: self.__batch_mode = False del self.__batch diff --git a/ifupdown2/lib/iproute2.py b/ifupdown2/lib/iproute2.py index 97de4aa5..67246bab 100644 --- a/ifupdown2/lib/iproute2.py +++ b/ifupdown2/lib/iproute2.py @@ -173,8 +173,6 @@ def batch_commit(self): "%s -force -batch -" % prefix, stdin="\n".join(commands) ) - except Exception: - raise finally: self.__batch_mode = False del self.__batch From 8ef3babd71d299c202aae2567d8205b45c25ee51 Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Fri, 5 Apr 2024 16:01:58 +0200 Subject: [PATCH 092/132] scheduler: diff-mode: filter out interface based on the initial filtered run queue Signed-off-by: Julien Fortin --- ifupdown2/ifupdown/scheduler.py | 38 +++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/ifupdown2/ifupdown/scheduler.py b/ifupdown2/ifupdown/scheduler.py index a70810fb..d96c1d3b 100644 --- a/ifupdown2/ifupdown/scheduler.py +++ b/ifupdown2/ifupdown/scheduler.py @@ -53,6 +53,9 @@ class ifaceScheduler(): _SCHED_STATUS = True + _DIFF_MODE = False + _RUN_QUEUE = [] + VRF_MGMT_DEVNAME = policymanager.policymanager_api.get_module_globals( module_name="vrf", attr="vrf-mgmt-devname" @@ -319,6 +322,11 @@ def run_iface_list(cls, ifupdownobj, ifacenames, """ Runs interface list """ for ifacename in ifacenames: + + if cls._DIFF_MODE and ifacename not in cls._RUN_QUEUE: + ifupdownobj.logger.debug(f"diff mode: skipping interface {ifacename} - not present in run queue") + continue + try: cls.run_iface_graph(ifupdownobj, ifacename, ops, parent, order, followdependents) @@ -435,6 +443,12 @@ def run_upperifaces(cls, ifupdownobj, ifacenames, ops, # dump valid upperifaces ifupdownobj.logger.debug(upperifaces) for u in upperifaces: + + if cls._DIFF_MODE and u not in cls._RUN_QUEUE: + ifupdownobj.logger.debug(f"diff mode: upperifaces: skipping interface {u} - not present in run queue") + continue + + try: ifaceobjs = ifupdownobj.get_ifaceobjs(u) if not ifaceobjs: @@ -527,6 +541,12 @@ def sched_ifaces(cls, ifupdownobj, ifacenames, ops, # # Run any upperifaces if available # + + cls._DIFF_MODE = diff_mode + cls._RUN_QUEUE = list(ifacenames) + + ifupdownobj.logger.debug(f"full run queue: {cls._RUN_QUEUE}") + followupperifaces = False run_queue = [] skip_ifacesort = int(ifupdownobj.config.get('skip_ifacesort', '0')) @@ -553,19 +573,6 @@ def sched_ifaces(cls, ifupdownobj, ifacenames, ops, ops, dependency_graph, indegrees) if run_queue and 'up' in ops[0]: run_queue.reverse() - elif diff_mode: - # In diff mode followdependents=False, we want to process - # 'child interfaces' before processing parents aka the interfaces - # with a positive reference count (get_iface_refcnt) - for ifacename in ifacenames: - if indegrees.get(ifacename): - run_queue.insert(0, ifacename) - else: - run_queue.append(ifacename) - - ifupdownobj.logger.debug("diff ref_count=%s" % indegrees) - ifupdownobj.logger.debug("diff run_queue=%s" % run_queue) - else: # if -a is set, we pick the interfaces # that have no parents and use a sorted list of those @@ -603,9 +610,12 @@ def sched_ifaces(cls, ifupdownobj, ifacenames, ops, if not cls.get_sched_status(): return + + + if (not skipupperifaces and ifupdownobj.config.get('skip_upperifaces', '0') == '0' and - ((not ifupdownflags.flags.ALL and followdependents) or + ((not ifupdownflags.flags.ALL and (followdependents or cls._DIFF_MODE)) or followupperifaces) and 'up' in ops[0]): # If user had given a set of interfaces to bring up From 9709d27d0c9db41b580bd299da9d0965152176e7 Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Thu, 1 Aug 2024 01:19:35 +0200 Subject: [PATCH 093/132] addons: mstpctl: add diff-mode check before brport configuration For some reason ifupdown2 tries to repush the mstp configuration probably due to a stale cache or wrong default values. However in diff mode we shouldn't even enter that function and simply skip it if the port (eni config) weren't changed. Signed-off-by: Julien Fortin --- ifupdown2/addons/mstpctl.py | 12 ++++++++++++ ifupdown2/ifupdown/scheduler.py | 3 +++ ifupdown2/lib/addon.py | 7 +++++++ 3 files changed, 22 insertions(+) diff --git a/ifupdown2/addons/mstpctl.py b/ifupdown2/addons/mstpctl.py index c9c19241..52d29de7 100644 --- a/ifupdown2/addons/mstpctl.py +++ b/ifupdown2/addons/mstpctl.py @@ -653,10 +653,22 @@ def _apply_bridge_port_settings_all(self, ifaceobj, continue if not os.path.exists('/sys/class/net/%s/brport' %bport): continue + + if self._diff_mode and bport not in self._runqueue: + self.logger.info(f"{bport}: diff-mode: skipping stp configuration on bridge port") + flag_skip = True + else: + flag_skip = False + bportifaceobjlist = ifaceobj_getfunc(bport) if not bportifaceobjlist: continue for bportifaceobj in bportifaceobjlist: + if flag_skip: + # We need to flag all ifaceobj as processed if flag_skip is True + bportifaceobj.module_flags[self.name] = bportifaceobj.module_flags.setdefault(self.name, 0) | MstpctlFlags.PORT_PROCESSED + continue + # Dont process bridge port if it already has been processed if (bportifaceobj.module_flags.get(self.name,0x0) & \ mstpctlFlags.PORT_PROCESSED): diff --git a/ifupdown2/ifupdown/scheduler.py b/ifupdown2/ifupdown/scheduler.py index d96c1d3b..022b5e8e 100644 --- a/ifupdown2/ifupdown/scheduler.py +++ b/ifupdown2/ifupdown/scheduler.py @@ -96,6 +96,9 @@ def run_iface_op(cls, ifupdownobj, ifaceobj, op, cenv=None): m = ifupdownobj.modules.get(mname) err = 0 try: + if cls._DIFF_MODE and hasattr(m, "set_runqueue"): + m.set_runqueue(list(cls._RUN_QUEUE)) + if hasattr(m, 'run'): msg = ('%s: %s : running module %s' %(ifacename, op, mname)) if op == 'query-checkcurr': diff --git a/ifupdown2/lib/addon.py b/ifupdown2/lib/addon.py index d729ad20..8d51c13e 100644 --- a/ifupdown2/lib/addon.py +++ b/ifupdown2/lib/addon.py @@ -74,6 +74,13 @@ def __init__(self): for alias in attribute_object.get("aliases", []): self.__alias_to_attribute[alias] = attribute_name + self._runqueue = [] + self._diff_mode = False + + def set_runqueue(self, runqueue): + self._runqueue = runqueue + self._diff_mode = True + def __get_modinfo(self) -> dict: try: return self._modinfo From 5796ff9143897b3e2dc34fa5cf449f1243af7eb4 Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Mon, 8 Apr 2024 22:54:46 +0200 Subject: [PATCH 094/132] scheduler: diff-mode: set run queue as diff-list Signed-off-by: Julien Fortin --- ifupdown2/ifupdown/scheduler.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/ifupdown2/ifupdown/scheduler.py b/ifupdown2/ifupdown/scheduler.py index 022b5e8e..efb54844 100644 --- a/ifupdown2/ifupdown/scheduler.py +++ b/ifupdown2/ifupdown/scheduler.py @@ -576,6 +576,8 @@ def sched_ifaces(cls, ifupdownobj, ifacenames, ops, ops, dependency_graph, indegrees) if run_queue and 'up' in ops[0]: run_queue.reverse() + elif cls._DIFF_MODE: + run_queue = cls._RUN_QUEUE else: # if -a is set, we pick the interfaces # that have no parents and use a sorted list of those @@ -613,12 +615,9 @@ def sched_ifaces(cls, ifupdownobj, ifacenames, ops, if not cls.get_sched_status(): return - - - - if (not skipupperifaces and + if (not cls._DIFF_MODE and not skipupperifaces and ifupdownobj.config.get('skip_upperifaces', '0') == '0' and - ((not ifupdownflags.flags.ALL and (followdependents or cls._DIFF_MODE)) or + ((not ifupdownflags.flags.ALL and followdependents) or followupperifaces) and 'up' in ops[0]): # If user had given a set of interfaces to bring up From ea5d2f9872c4846e2f3d2283dd9333560b9fe9c6 Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Thu, 28 Nov 2024 17:02:39 -0500 Subject: [PATCH 095/132] scheduler: use SchedulerException instead of broad Exception Signed-off-by: Julien Fortin --- ifupdown2/ifupdown/scheduler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ifupdown2/ifupdown/scheduler.py b/ifupdown2/ifupdown/scheduler.py index efb54844..3d8ba7a1 100644 --- a/ifupdown2/ifupdown/scheduler.py +++ b/ifupdown2/ifupdown/scheduler.py @@ -340,7 +340,7 @@ def run_iface_list(cls, ifupdownobj, ifacenames, ifupdownobj.logger.error('%s : %s' %(ifacename, str(e))) else: if not (ifupdownobj.ignore_error(str(e))): - raise Exception('%s : (%s)' %(ifacename, str(e))) + raise SchedulerException('%s : (%s)' %(ifacename, str(e))) @classmethod def run_iface_graph_upper(cls, ifupdownobj, ifacename, ops, parent=None, From d771a543e4c68c2de6da250e67607f6d62f6c7be Mon Sep 17 00:00:00 2001 From: Yong Wang Date: Tue, 3 Sep 2024 10:56:39 -0700 Subject: [PATCH 096/132] enable per vlan snooping when config mcqv4src Testing Done: verified with local test When we specifiy igmp query source IP address for one particular vlan, it makes sense to enable per vlan snooping and igmp snooping on the specified vlan, to align with the implementation in kernel side. Signed-off-by: Julien Fortin --- ifupdown2/lib/iproute2.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/ifupdown2/lib/iproute2.py b/ifupdown2/lib/iproute2.py index 67246bab..5f1d6006 100644 --- a/ifupdown2/lib/iproute2.py +++ b/ifupdown2/lib/iproute2.py @@ -831,8 +831,16 @@ def bridge_del_mcqv4src(self, bridge, vlan): self.logger.info("%s: del mcqv4src vlan: invalid parameter %s: %s" % (bridge, vlan, str(e))) return + utils.exec_command("%s delmcqv4src %s %d" % (utils.brctl_cmd, bridge, vlan)) + # Disable igmp snooping on specified vlan with {VID} + utils.exec_command("%s vlan global set vid %d dev %s mcast_snooping 0" % (utils.bridge_cmd, vlan, bridge)) + + # Disable per vlan snooping + utils.exec_command( "%s link set %s type bridge mcast_vlan_snooping 0" % (utils.ip_cmd, bridge)) + + def bridge_set_mcqv4src(self, bridge, vlan, mcquerier): try: vlan = int(vlan) @@ -854,6 +862,13 @@ def bridge_set_mcqv4src(self, bridge, vlan, mcquerier): utils.exec_command("%s setmcqv4src %s %d %s" % (utils.brctl_cmd, bridge, vlan, mcquerier)) + # Enable per vlan snooping + utils.exec_command( "%s link set %s type bridge mcast_vlan_snooping 1" % (utils.ip_cmd, bridge)) + + # Enable igmp snooping on specified vlan with {VID} + utils.exec_command("%s vlan global set vid %d dev %s mcast_snooping 1 mcast_querier 1" % (utils.bridge_cmd, vlan, bridge)) + + ############################################################################ # ROUTE ############################################################################ From 8aab90e1ada0fa6a8de9829086c03d633296e4e0 Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Mon, 14 Aug 2023 23:19:55 +0200 Subject: [PATCH 097/132] use exist_ok=True when trying to create /run/network/ directory Signed-off-by: Julien Fortin --- ifupdown2/ifupdown/main.py | 9 ++++++--- ifupdown2/ifupdown/statemanager.py | 4 ++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/ifupdown2/ifupdown/main.py b/ifupdown2/ifupdown/main.py index 21c90b69..b6a8143d 100644 --- a/ifupdown2/ifupdown/main.py +++ b/ifupdown2/ifupdown/main.py @@ -72,9 +72,12 @@ def main(self, stdin_buffer=None): self.read_config() self.init(stdin_buffer) - if self.op != 'query' and not utils.lockFile(self.args.lockfile): - log.error("Another instance of this program is already running.") - return Status.Client.STATUS_ALREADY_RUNNING + try: + if self.op != 'query' and not utils.lockFile(self.args.lockfile): + log.error("Another instance of this program is already running.") + return Status.Client.STATUS_ALREADY_RUNNING + except FileExistsError as e: + log.info("%s: lockfile error: %s" % (self.args.lockfile, str(e))) self.handlers.get(self.op)(self.args) except Exception as e: diff --git a/ifupdown2/ifupdown/statemanager.py b/ifupdown2/ifupdown/statemanager.py index 19d63e64..0c292453 100644 --- a/ifupdown2/ifupdown/statemanager.py +++ b/ifupdown2/ifupdown/statemanager.py @@ -107,13 +107,13 @@ def init(self): raise StateManagerException("statemanager: unable to create required directory: %s" % str(e)) if not os.path.exists(self.state_rundir): - os.makedirs(self.state_rundir) + os.makedirs(self.state_rundir, exist_ok=True) self.state_file = "%s/%s" % (self.state_dir, self.state_filename) def _init_makedirs_state_dir(self): if not os.path.exists(self.state_dir): - os.makedirs(self.state_dir) + os.makedirs(self.state_dir, exist_ok=True) def save_ifaceobj(self, ifaceobj): From 7a3766d1779eb88d1c38a5cf7320b75c58ba61f5 Mon Sep 17 00:00:00 2001 From: Andy Roulin Date: Wed, 8 Mar 2023 13:48:55 -0800 Subject: [PATCH 098/132] networkinterfaces.py: fix missing return statement Without the return statement, ifreload -a will fail to process files under /etc/network/interfaces.d/ The failure is: unsupported operand type(s) for +=: 'int' and 'NoneType' Fixes: 82a22438cfdc ("sonarlink: various fixes") Signed-off-by: Andy Roulin Signed-off-by: Julien Fortin --- ifupdown2/ifupdown/networkinterfaces.py | 1 + 1 file changed, 1 insertion(+) diff --git a/ifupdown2/ifupdown/networkinterfaces.py b/ifupdown2/ifupdown/networkinterfaces.py index 0222749f..575ef06d 100644 --- a/ifupdown2/ifupdown/networkinterfaces.py +++ b/ifupdown2/ifupdown/networkinterfaces.py @@ -195,6 +195,7 @@ def process_source(self, lines, cur_idx, lineno): else: self._parse_error(self._currentfile, lineno, 'unable to read source line') + return 0 def process_source_directory(self, lines, cur_idx, lineno): self.logger.debug('processing source-directory line ..\'%s\'' % lines[cur_idx]) From 8185847c9b7d0f4807bc4aff057c324e92727745 Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Mon, 20 Feb 2023 13:57:09 +0100 Subject: [PATCH 099/132] fix: unsupported operand type(s) for +=: 'int' and 'NoneType' Signed-off-by: Julien Fortin --- ifupdown2/ifupdown/networkinterfaces.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ifupdown2/ifupdown/networkinterfaces.py b/ifupdown2/ifupdown/networkinterfaces.py index 575ef06d..c0c8ad96 100644 --- a/ifupdown2/ifupdown/networkinterfaces.py +++ b/ifupdown2/ifupdown/networkinterfaces.py @@ -189,7 +189,7 @@ def process_source(self, lines, cur_idx, lineno): if '*' not in sourced_file: self._parse_warn(self._currentfile, lineno, 'cannot find source file %s' %sourced_file) - return + return 0 for f in filenames: self.read_file(f) else: From 57b16e6fac9a8217a7b31df2e796e8a04484cd13 Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Wed, 15 Feb 2023 14:15:07 +0100 Subject: [PATCH 100/132] sonarlink: remove uneeded 'continue' and 'return' statement Signed-off-by: Julien Fortin --- ifupdown2/ifupdown/policymanager.py | 1 - ifupdown2/ifupdown/scheduler.py | 1 - 2 files changed, 2 deletions(-) diff --git a/ifupdown2/ifupdown/policymanager.py b/ifupdown2/ifupdown/policymanager.py index d5c05d23..33dd6ea3 100644 --- a/ifupdown2/ifupdown/policymanager.py +++ b/ifupdown2/ifupdown/policymanager.py @@ -103,7 +103,6 @@ def __init__(self): self.user_policy_array[module].update(user_array[module]) else: self.user_policy_array[module] = user_array[module] - return def get_iface_default(self,module_name=None,ifname=None,attr=None): ''' diff --git a/ifupdown2/ifupdown/scheduler.py b/ifupdown2/ifupdown/scheduler.py index 3d8ba7a1..63b57130 100644 --- a/ifupdown2/ifupdown/scheduler.py +++ b/ifupdown2/ifupdown/scheduler.py @@ -432,7 +432,6 @@ def _get_valid_upperifaces(cls, ifupdownobj, ifacenames, if upperifacenames: cls._get_valid_upperifaces(ifupdownobj, upperifacenames, allupperifacenames) - return @classmethod def run_upperifaces(cls, ifupdownobj, ifacenames, ops, From b2476ce75f0d3ac74655988e8e72c09a421e3f1b Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Thu, 28 Nov 2024 17:24:01 -0500 Subject: [PATCH 101/132] statemanager: cleanups: remove empty lines Signed-off-by: Julien Fortin --- ifupdown2/ifupdown/statemanager.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/ifupdown2/ifupdown/statemanager.py b/ifupdown2/ifupdown/statemanager.py index 0c292453..16fa14ba 100644 --- a/ifupdown2/ifupdown/statemanager.py +++ b/ifupdown2/ifupdown/statemanager.py @@ -37,13 +37,11 @@ def save(cls, filename, list_of_objects): for obj in list_of_objects: pickle.dump(obj, f, pickle.HIGHEST_PROTOCOL) - @classmethod def save_obj(cls, f, obj): """ pickle iface object """ pickle.dump(obj, f, pickle.HIGHEST_PROTOCOL) - @classmethod def load(cls, filename): """ load picked iface object """ @@ -188,7 +186,6 @@ def ifaceobj_sync(self, ifaceobj, op): def save_state(self): """ saves state (ifaceobjects) to persistent state file """ - with open(self.state_file, 'wb') as f: if not len(self.ifaceobjdict): f.truncate(0) @@ -198,7 +195,6 @@ def save_state(self): [pickling.save_obj(f, i) for i in ifaceobjs] open('%s/%s' % (self.state_rundir, self.state_runlockfile), 'w').close() - def dump_pretty(self, ifacenames, format='native'): if not ifacenames: ifacenames = list(self.ifaceobjdict.keys()) From eb2be19f8295ecfba1666c444e5c62a9a8a2d3f8 Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Thu, 28 Nov 2024 17:25:16 -0500 Subject: [PATCH 102/132] __main__: use ModuleNotFounderror instead of Exception Signed-off-by: Julien Fortin --- ifupdown2/__main__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ifupdown2/__main__.py b/ifupdown2/__main__.py index ef7eb7db..4171fc88 100755 --- a/ifupdown2/__main__.py +++ b/ifupdown2/__main__.py @@ -29,7 +29,7 @@ try: from ifupdown2.lib.log import LogManager, root_logger from ifupdown2.lib.status import Status -except Exception: +except ModuleNotFoundError: from lib.log import LogManager, root_logger from lib.status import Status @@ -47,7 +47,7 @@ from ifupdown2.ifupdown.client import Client from ifupdown2.ifupdown.exceptions import ArgvParseHelp, ArgvParseError -except Exception: +except ModuleNotFoundError: import ifupdown.config as config config.__version__ = __import__("__init__").__version__ From 11226cfe417e5620077c3675a32dda026b9d936b Mon Sep 17 00:00:00 2001 From: Scott Laffer Date: Wed, 24 Jul 2024 22:39:19 -0700 Subject: [PATCH 103/132] Flush DHCP client lease files on boot up If dhclient starts and... * we still have a valid lease file, and * we see no responses to a series of DHCP Requests for the IP in that lease, and * no responses to a series DHCP Discovers. ... then we try and use our old address from the last valid lease. The address is added back to the interface, however if the router defined in the lease is not reachable through a ping check, it removes the address again. If there is no router, or the ping succeeds, the address remains. The dhclient application does then not try and do a Discovery again until the lease is due to expire. In the event of the ping failure of the router address and the valid lease is a long one, we may not be able to access the switch for an equally long time. This flow however only happens when the switch is unexpectedly power-cycled. In the case of a reboot, we delete the lease files on the way down so that we start fresh on next boot. This patch here provides parity between the expected reboot and non-expected power-cycle cases by also deleting the files on boot up before we bring up mgmt class interfaces. Signed-off-by: Julien Fortin --- ifupdown2/sbin/start-networking | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/ifupdown2/sbin/start-networking b/ifupdown2/sbin/start-networking index 8f790a09..efe674da 100755 --- a/ifupdown2/sbin/start-networking +++ b/ifupdown2/sbin/start-networking @@ -78,6 +78,11 @@ check_network_swap() { done < /proc/swaps } +delete_dhclient_leases() { + echo ${NAME}':' "Removing dhclient service files (if any)" + find /var/lib/dhcp/ -name "dhclient*leases" -delete +} + ifup_hotplug () { if [ -d /sys/class/net ] then @@ -125,6 +130,7 @@ start) set -f exclusions=$(process_exclusions) perfoptions=$(perf_options) + delete_dhclient_leases echo ${NAME}':' "Configuring network interfaces" ifup_mgmt ifup -a $EXTRA_ARGS $exclusions $perfoptions --systemd @@ -136,8 +142,7 @@ stop) systemctl list-jobs | egrep -q '(shutdown|reboot|halt|poweroff)\.target' [ $? -eq 0 ] && SYSRESET=1 if [ $SYSRESET -eq 1 ]; then - echo ${NAME}':' "Removing dhclient service files (if any)" - find /var/lib/dhcp/ -name "dhclient*leases" -delete + delete_dhclient_leases echo ${NAME}':' "Skipping deconfiguring the rest of the network interfaces" exit 0 fi From 7bc0519e88cbf2bbf004da36428905a59d575187 Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Fri, 29 Nov 2024 13:34:50 -0500 Subject: [PATCH 104/132] nlpacket: AttributeIFLA_PROTINFO: loop over range to add padding Signed-off-by: Julien Fortin --- ifupdown2/nlmanager/nlpacket.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ifupdown2/nlmanager/nlpacket.py b/ifupdown2/nlmanager/nlpacket.py index 8972c761..a2e2b677 100644 --- a/ifupdown2/nlmanager/nlpacket.py +++ b/ifupdown2/nlmanager/nlpacket.py @@ -3319,7 +3319,8 @@ def encode(self): sub_attr_payload[sub_attr_length_index] = sub_attr_length # add padding - sub_attr_pack_layout[-1] = "%s%s" % (sub_attr_pack_layout[-1], "x" * self.pad_bytes_needed(sub_attr_length)) + for x in range(self.pad_bytes_needed(sub_attr_length)): + sub_attr_pack_layout.append('x') # The [1:] is to remove the leading = so that when we do the ''.join() later # we do not end up with an = in the middle of the pack layout string. There From d63cfc3bf52007245e60397ef97ca7e702c9cf54 Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Tue, 31 May 2022 01:28:53 +0200 Subject: [PATCH 105/132] SONAR: ethtool: Merge this if statement with the enclosing one Signed-off-by: Julien Fortin --- ifupdown2/addons/ethtool.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/ifupdown2/addons/ethtool.py b/ifupdown2/addons/ethtool.py index 84661bd1..51143b1f 100644 --- a/ifupdown2/addons/ethtool.py +++ b/ifupdown2/addons/ethtool.py @@ -485,9 +485,8 @@ def _query_check(self, ifaceobj, ifaceobjcurr): for attr in ['speed', 'duplex', 'autoneg', 'fec']: configured = ifaceobj.get_attr_value_first('link-%s'%attr) # if there is nothing configured, do not check - if not configured: - if not ifupdownflags.flags.WITHDEFAULTS: - continue + if not configured and not ifupdownflags.flags.WITHDEFAULTS: + continue default = policymanager.policymanager_api.get_iface_default( module_name='ethtool', ifname=ifaceobj.name, From d5b8fe27e2ed4491732e82a46272a2088653f5de Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Wed, 30 Nov 2022 21:46:46 +0100 Subject: [PATCH 106/132] dead_error_line: Execution cannot reach the expression configured inside this statement: if (default || configured) Signed-off-by: Julien Fortin --- ifupdown2/addons/ethtool.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ifupdown2/addons/ethtool.py b/ifupdown2/addons/ethtool.py index 51143b1f..1fb92bb3 100644 --- a/ifupdown2/addons/ethtool.py +++ b/ifupdown2/addons/ethtool.py @@ -523,7 +523,7 @@ def _query_check(self, ifaceobj, ifaceobjcurr): # PASS since running is default ifaceobjcurr.update_config_with_status('link-%s'%attr, running_attr, 0) - elif (default or configured): + elif configured: # We show a FAIL since it is not the configured or default ifaceobjcurr.update_config_with_status('link-%s'%attr, running_attr, 1) From 44eb5257db0e2985cae4886c9db17223e9fb59ee Mon Sep 17 00:00:00 2001 From: Alok Kumar Date: Mon, 15 Apr 2024 07:34:17 -0700 Subject: [PATCH 107/132] addons: ethtool: update check for back and forward compatibility ethtool output changed, we need to be able to supports the old and new syntax. original commit title: Configuring some random port + apply causing flapping of other working ports Signed-off-by: Julien Fortin --- ifupdown2/addons/ethtool.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ifupdown2/addons/ethtool.py b/ifupdown2/addons/ethtool.py index 1fb92bb3..cc3fd87a 100644 --- a/ifupdown2/addons/ethtool.py +++ b/ifupdown2/addons/ethtool.py @@ -547,7 +547,8 @@ def get_fec_encoding(self,ethtool_output=None): """ try: for attr in ethtool_output.splitlines(): - if attr.startswith('Configured FEC encodings:'): + # ethtool syntax changed. using 'in' check supports both. + if "Configured FEC encodings:" in attr: fec_attrs = attr.split() return(fec_attrs[fec_attrs.index('encodings:')+1]) except Exception as e: From 8a7fcdc0eb236ee1be717f77134c8bf1c6423788 Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Fri, 29 Nov 2024 13:52:19 -0500 Subject: [PATCH 108/132] addons: ethtool: cleanups & sync with internal repo Signed-off-by: Julien Fortin --- ifupdown2/addons/ethtool.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/ifupdown2/addons/ethtool.py b/ifupdown2/addons/ethtool.py index cc3fd87a..3aace393 100644 --- a/ifupdown2/addons/ethtool.py +++ b/ifupdown2/addons/ethtool.py @@ -198,6 +198,7 @@ def do_ring_settings(self, ifaceobj, attr_name, option): except Exception as e: self.log_error('%s: %s' %(ifaceobj.name, str(e)), ifaceobj) + def do_offload_settings(self, ifaceobj, attr_name, eth_name): default = 'default_' + eth_name config_val = ifaceobj.get_attr_value_first(attr_name) @@ -322,8 +323,6 @@ def do_fec_settings(self, ifaceobj): except Exception as e: if not self.ethtool_ignore_errors: self.log_error('%s: %s' %(ifaceobj.name, str(e)), ifaceobj) - else: - pass def do_speed_lane_duplex_autoneg_settings(self, ifaceobj, down=False): @@ -455,7 +454,6 @@ def _pre_up(self, ifaceobj, operation='post_up'): self.do_speed_lane_duplex_autoneg_settings(ifaceobj) self.do_fec_settings(ifaceobj) - self.do_lanes_settings(ifaceobj) self.do_ring_settings(ifaceobj, 'ring-rx', 'rx') self.do_ring_settings(ifaceobj, 'ring-tx', 'tx') self.do_offload_settings(ifaceobj, 'gro-offload', 'gro') @@ -527,7 +525,6 @@ def _query_check(self, ifaceobj, ifaceobjcurr): # We show a FAIL since it is not the configured or default ifaceobjcurr.update_config_with_status('link-%s'%attr, running_attr, 1) - return def get_autoneg(self,ethtool_output=None): """ @@ -692,8 +689,6 @@ def _query_running(self, ifaceobj, ifaceobj_getfunc=None): if running_attr: ifaceobj.update_config('link-%s'%attr, running_attr) - return - def _query(self, ifaceobj, **kwargs): """ add default policy attributes supported by the module """ for attr in ['speed', 'duplex', 'autoneg', 'fec']: From 7d527e1c5b617ccdb469e316edf8d1b3d5e5a1eb Mon Sep 17 00:00:00 2001 From: Manohar M Date: Wed, 15 Sep 2021 14:15:57 -0700 Subject: [PATCH 109/132] start-networking: add callable hooks Useful in warmboot context. Signed-off-by: Julien Fortin --- ifupdown2/sbin/start-networking | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ifupdown2/sbin/start-networking b/ifupdown2/sbin/start-networking index efe674da..180a1fe5 100755 --- a/ifupdown2/sbin/start-networking +++ b/ifupdown2/sbin/start-networking @@ -135,7 +135,12 @@ start) ifup_mgmt ifup -a $EXTRA_ARGS $exclusions $perfoptions --systemd ifup_hotplug $HOTPLUG_ARGS $EXTRA_ARGS $exclusions + + if test -f /var/lib/ifupdown2/hooks.d/start-networking-post-init-hook.sh; then + /var/lib/ifupdown2/hooks.d/start-networking-post-init-hook.sh + fi ;; + stop) if [ "$SKIP_DOWN_AT_SYSRESET" = "yes" ]; then SYSRESET=0 From d432a2ea0513ce8b1363a07a79113cf5a3ea8472 Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Tue, 6 Feb 2024 22:05:44 +0100 Subject: [PATCH 110/132] Pytest: add initial support for pytest with code coverage tests/: pytest related files and tests, test are converted from the ifupdown2-qa repo __main__: necessary path injection (can probably be removed or adjusted in the future) test_l2.py for l2 tests test_l3.py for l3 tests test_coverage.py for code coverage and static analysis (flake8, mypy but disabled at the moment due to the huge amount of errors) Usage: $ export PYTEST_REMOTE_HOST=$DEV; $ export PYTEST_REMOTE_USER=$USER; $ export PYTEST_REMOTE_PASSWORD=$PW $ pytest === short test summary info === FAILED tests/test_coverage.py::test_coverage - AssertionError: Coverage less than 75% - total coverage: 54.5492335671603 - details in ./tests/results/coverage_data.tar.gz === 1 failed, 24 passed, 2 skipped, 9 warnings in 333.02s (0:05:33) === We can lower the coverage threshold until we had more test or clean up the dead code in ifupdown2 Right now all test run with coverage enabled on, and it's not possible to disable it, that will come in a different MR. Signed-off-by: Julien Fortin --- .gitignore | 2 + ifupdown2/__main__.py | 3 + requirements.txt | 7 + tests/conftest.py | 321 +++++++++++++ tests/eni/address.eni | 20 + tests/eni/address_gateway.eni | 22 + tests/eni/bond.eni | 67 +++ tests/eni/bond_lacp.eni | 38 ++ tests/eni/bridge1.eni | 18 + tests/eni/bridge2.eni | 19 + tests/eni/bridge3.eni | 27 ++ tests/eni/bridge4.eni | 28 ++ tests/eni/bridge5.eni | 24 + .../bridge6_multiple_bridge_ports_lines.eni | 18 + tests/eni/bridge7_macvlans.eni | 146 ++++++ tests/eni/bridge8_reserved_vlans.eni | 18 + tests/eni/bridge_access.eni | 34 ++ tests/eni/bridge_attr_back_to_default.eni | 53 +++ tests/eni/bridge_igmp_version.eni | 28 ++ tests/eni/bridge_l2protocol_tunnel.eni | 28 ++ tests/eni/bridge_new_attribute.eni | 14 + tests/eni/cm_11485_vlan_device_name_vlan.eni | 37 ++ ...b_clag_riot_flood_sup_off_config_tors2.eni | 72 +++ tests/eni/interfaces_link_state.eni | 14 + tests/eni/interfaces_vrr_vrf.eni | 38 ++ tests/eni/mac1.eni | 18 + tests/eni/vxlandev_sanity.eni | 51 +++ ...bClagRiotFloodSupOffConfig.ifquery.ac.json | 170 +++++++ tests/output/address.ifquery.ac.json | 62 +++ ...ddress_gateway.empty_addrs.ifquery.ac.json | 59 +++ tests/output/address_gateway.ifquery.ac.json | 63 +++ .../bond.flipped.values.ifquery.ac.json | 165 +++++++ tests/output/bond.ifquery.ac.json | 167 +++++++ .../bond_lacp.flipped.values.ifquery.ac.json | 103 +++++ tests/output/bond_lacp.ifquery.ac.json | 103 +++++ tests/output/bridge1.ifquery.ac.json | 50 +++ tests/output/bridge1.vlan.show.json | 48 ++ tests/output/bridge2.ifquery.ac.json | 52 +++ tests/output/bridge2.vlan.show.json | 54 +++ tests/output/bridge3.ifquery.ac.json | 74 +++ tests/output/bridge3.vlan.show.json | 41 ++ tests/output/bridge4.ifquery.ac.json | 76 ++++ tests/output/bridge4.vlan.show.json | 51 +++ tests/output/bridge5.ifquery.ac.json | 64 +++ tests/output/bridge5.vlan.show.json | 48 ++ ...ultiple_bridge_ports_lines.ifquery.ac.json | 50 +++ tests/output/bridge7_macvlans.ifquery.ac.json | 406 +++++++++++++++++ tests/output/bridge_access.ifquery.ac.json | 78 ++++ tests/output/bridge_access.vlan.show.json | 38 ++ ...attr_back_to_default.after.ifquery.ac.json | 58 +++ ...ridge_attr_back_to_default.ifquery.ac.json | 127 ++++++ .../bridge_igmp_version.ifquery.ac.json | 78 ++++ .../bridge_l2protocol_tunnel.ifquery.ac.json | 83 ++++ ...w_attribute_igmp_mld.after.ifquery.ac.json | 59 +++ ..._attribute_igmp_mld.before.ifquery.ac.json | 67 +++ ...lan_protocol_mcstats.after.ifquery.ac.json | 88 ++++ ...an_protocol_mcstats.before.ifquery.ac.json | 100 +++++ ...ning_arp_nd_suppress.after.ifquery.ac.json | 91 ++++ ...ing_arp_nd_suppress.before.ifquery.ac.json | 99 ++++ ...te_ucast_mcast_flood.after.ifquery.ac.json | 76 ++++ ...e_ucast_mcast_flood.before.ifquery.ac.json | 76 ++++ ..._vlan_protocol_stats.after.ifquery.ac.json | 63 +++ ...vlan_protocol_stats.before.ifquery.ac.json | 67 +++ ...1485_vlan_device_name_vlan.ifquery.ac.json | 106 +++++ .../interfaces.vrr_vrf.ifquery.ac.1.json | 109 +++++ .../interfaces.vrr_vrf.ifquery.ac.2.json | 105 +++++ .../interfaces.vrr_vrf.ifquery.ac.3.json | 108 +++++ .../interfaces.vrr_vrf.ifquery.ac.4.json | 105 +++++ .../interfaces.vrr_vrf.ifquery.ac.5.json | 101 +++++ .../interfaces.vrr_vrf.ifquery.ac.6.json | 93 ++++ .../interfaces.vrr_vrf.ifquery.ac.7.json | 101 +++++ .../interfaces.vrr_vrf.ifquery.ac.8.json | 97 ++++ ...nterfaces_link_state.after.ifquery.ac.json | 109 +++++ ...terfaces_link_state.before.ifquery.ac.json | 105 +++++ tests/output/mac1.ifquery.ac.json | 57 +++ tests/output/vxlan_sanity.ifquery.ac.json | 127 ++++++ tests/scp/bond.default.eni | 38 ++ .../scp/bridge_attr_back_to_default.after.eni | 19 + .../bridge_new_attribute_igmp_mld.after.eni | 8 + .../bridge_new_attribute_igmp_mld.before.eni | 12 + ..._ipforward_vlan_protocol_mcstats.after.eni | 20 + ...ipforward_vlan_protocol_mcstats.before.eni | 24 + ...tribute_learning_arp_nd_suppress.after.eni | 22 + ...ribute_learning_arp_nd_suppress.before.eni | 26 ++ ..._new_attribute_ucast_mcast_flood.after.eni | 15 + ...new_attribute_ucast_mcast_flood.before.eni | 15 + ...ew_attribute_vlan_protocol_stats.after.eni | 10 + ...w_attribute_vlan_protocol_stats.before.eni | 12 + tests/scp/interfaces_link_state.after.eni | 32 ++ tests/scp/interfaces_link_state.before.eni | 31 ++ tests/test_coverage.py | 91 ++++ tests/test_l2.py | 423 ++++++++++++++++++ tests/test_l3.py | 138 ++++++ 93 files changed, 6578 insertions(+) create mode 100644 requirements.txt create mode 100644 tests/conftest.py create mode 100644 tests/eni/address.eni create mode 100644 tests/eni/address_gateway.eni create mode 100644 tests/eni/bond.eni create mode 100644 tests/eni/bond_lacp.eni create mode 100644 tests/eni/bridge1.eni create mode 100644 tests/eni/bridge2.eni create mode 100644 tests/eni/bridge3.eni create mode 100644 tests/eni/bridge4.eni create mode 100644 tests/eni/bridge5.eni create mode 100644 tests/eni/bridge6_multiple_bridge_ports_lines.eni create mode 100644 tests/eni/bridge7_macvlans.eni create mode 100644 tests/eni/bridge8_reserved_vlans.eni create mode 100644 tests/eni/bridge_access.eni create mode 100644 tests/eni/bridge_attr_back_to_default.eni create mode 100644 tests/eni/bridge_igmp_version.eni create mode 100644 tests/eni/bridge_l2protocol_tunnel.eni create mode 100644 tests/eni/bridge_new_attribute.eni create mode 100644 tests/eni/cm_11485_vlan_device_name_vlan.eni create mode 100644 tests/eni/evpn_vab_clag_riot_flood_sup_off_config_tors2.eni create mode 100644 tests/eni/interfaces_link_state.eni create mode 100644 tests/eni/interfaces_vrr_vrf.eni create mode 100644 tests/eni/mac1.eni create mode 100644 tests/eni/vxlandev_sanity.eni create mode 100644 tests/output/EvpnVabClagRiotFloodSupOffConfig.ifquery.ac.json create mode 100644 tests/output/address.ifquery.ac.json create mode 100644 tests/output/address_gateway.empty_addrs.ifquery.ac.json create mode 100644 tests/output/address_gateway.ifquery.ac.json create mode 100644 tests/output/bond.flipped.values.ifquery.ac.json create mode 100644 tests/output/bond.ifquery.ac.json create mode 100644 tests/output/bond_lacp.flipped.values.ifquery.ac.json create mode 100644 tests/output/bond_lacp.ifquery.ac.json create mode 100644 tests/output/bridge1.ifquery.ac.json create mode 100644 tests/output/bridge1.vlan.show.json create mode 100644 tests/output/bridge2.ifquery.ac.json create mode 100644 tests/output/bridge2.vlan.show.json create mode 100644 tests/output/bridge3.ifquery.ac.json create mode 100644 tests/output/bridge3.vlan.show.json create mode 100644 tests/output/bridge4.ifquery.ac.json create mode 100644 tests/output/bridge4.vlan.show.json create mode 100644 tests/output/bridge5.ifquery.ac.json create mode 100644 tests/output/bridge5.vlan.show.json create mode 100644 tests/output/bridge6_multiple_bridge_ports_lines.ifquery.ac.json create mode 100644 tests/output/bridge7_macvlans.ifquery.ac.json create mode 100644 tests/output/bridge_access.ifquery.ac.json create mode 100644 tests/output/bridge_access.vlan.show.json create mode 100644 tests/output/bridge_attr_back_to_default.after.ifquery.ac.json create mode 100644 tests/output/bridge_attr_back_to_default.ifquery.ac.json create mode 100644 tests/output/bridge_igmp_version.ifquery.ac.json create mode 100644 tests/output/bridge_l2protocol_tunnel.ifquery.ac.json create mode 100644 tests/output/bridge_new_attribute_igmp_mld.after.ifquery.ac.json create mode 100644 tests/output/bridge_new_attribute_igmp_mld.before.ifquery.ac.json create mode 100644 tests/output/bridge_new_attribute_ipforward_vlan_protocol_mcstats.after.ifquery.ac.json create mode 100644 tests/output/bridge_new_attribute_ipforward_vlan_protocol_mcstats.before.ifquery.ac.json create mode 100644 tests/output/bridge_new_attribute_learning_arp_nd_suppress.after.ifquery.ac.json create mode 100644 tests/output/bridge_new_attribute_learning_arp_nd_suppress.before.ifquery.ac.json create mode 100644 tests/output/bridge_new_attribute_ucast_mcast_flood.after.ifquery.ac.json create mode 100644 tests/output/bridge_new_attribute_ucast_mcast_flood.before.ifquery.ac.json create mode 100644 tests/output/bridge_new_attribute_vlan_protocol_stats.after.ifquery.ac.json create mode 100644 tests/output/bridge_new_attribute_vlan_protocol_stats.before.ifquery.ac.json create mode 100644 tests/output/cm_11485_vlan_device_name_vlan.ifquery.ac.json create mode 100644 tests/output/interfaces.vrr_vrf.ifquery.ac.1.json create mode 100644 tests/output/interfaces.vrr_vrf.ifquery.ac.2.json create mode 100644 tests/output/interfaces.vrr_vrf.ifquery.ac.3.json create mode 100644 tests/output/interfaces.vrr_vrf.ifquery.ac.4.json create mode 100644 tests/output/interfaces.vrr_vrf.ifquery.ac.5.json create mode 100644 tests/output/interfaces.vrr_vrf.ifquery.ac.6.json create mode 100644 tests/output/interfaces.vrr_vrf.ifquery.ac.7.json create mode 100644 tests/output/interfaces.vrr_vrf.ifquery.ac.8.json create mode 100644 tests/output/interfaces_link_state.after.ifquery.ac.json create mode 100644 tests/output/interfaces_link_state.before.ifquery.ac.json create mode 100644 tests/output/mac1.ifquery.ac.json create mode 100644 tests/output/vxlan_sanity.ifquery.ac.json create mode 100644 tests/scp/bond.default.eni create mode 100644 tests/scp/bridge_attr_back_to_default.after.eni create mode 100644 tests/scp/bridge_new_attribute_igmp_mld.after.eni create mode 100644 tests/scp/bridge_new_attribute_igmp_mld.before.eni create mode 100644 tests/scp/bridge_new_attribute_ipforward_vlan_protocol_mcstats.after.eni create mode 100644 tests/scp/bridge_new_attribute_ipforward_vlan_protocol_mcstats.before.eni create mode 100644 tests/scp/bridge_new_attribute_learning_arp_nd_suppress.after.eni create mode 100644 tests/scp/bridge_new_attribute_learning_arp_nd_suppress.before.eni create mode 100644 tests/scp/bridge_new_attribute_ucast_mcast_flood.after.eni create mode 100644 tests/scp/bridge_new_attribute_ucast_mcast_flood.before.eni create mode 100644 tests/scp/bridge_new_attribute_vlan_protocol_stats.after.eni create mode 100644 tests/scp/bridge_new_attribute_vlan_protocol_stats.before.eni create mode 100644 tests/scp/interfaces_link_state.after.eni create mode 100644 tests/scp/interfaces_link_state.before.eni create mode 100644 tests/test_coverage.py create mode 100644 tests/test_l2.py create mode 100644 tests/test_l3.py diff --git a/.gitignore b/.gitignore index 60a10f6c..23bc3fa4 100644 --- a/.gitignore +++ b/.gitignore @@ -115,3 +115,5 @@ ifupdown2/ifdown ifupdown2/ifquery ifupdown2/ifreload ifupdown2/ifup + +tests/results diff --git a/ifupdown2/__main__.py b/ifupdown2/__main__.py index 4171fc88..c63dbd5d 100755 --- a/ifupdown2/__main__.py +++ b/ifupdown2/__main__.py @@ -26,6 +26,9 @@ import os import sys +sys.path.insert(0, "/root/ifupdown2/ifupdown2") +#sys.path.append("/usr/share/ifupdown2/") + try: from ifupdown2.lib.log import LogManager, root_logger from ifupdown2.lib.status import Status diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 00000000..d42cf070 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,7 @@ +pytest~=8.0.0 +pytest-order +paramiko~=3.4.0 +scp~=0.14.5 +setuptools~=58.1.0 +six~=1.16.0 +deepdiff diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 00000000..818ac21a --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,321 @@ +import os +import re +import time +import json +import pytest +import logging +import paramiko + +from pathlib import Path +from scp import SCPClient +from deepdiff import DeepDiff + +# Global to track file used by tests +registered_files = set() + +# Global variable to track test failures +test_failed = False + +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger(__name__) + +USER = os.environ.get("USER") +ENI = "/etc/network/interfaces" +ENI_D = f"{ENI}.d" + +if os.path.exists(f"/tmp/{USER}/"): + LOCAL_DIR_FILE_TRANSLATE = f"/tmp/{USER}/" +else: + LOCAL_DIR_FILE_TRANSLATE = "/tmp/.pytest_ifupdown2/" + +os.system(f"mkdir -p {LOCAL_DIR_FILE_TRANSLATE}") + + +def pytest_runtest_makereport(item, call): + global test_failed + if call.when == "call" and call.excinfo is not None: + test_failed = True + + +def assert_identical_json(json1, json2): + """ + Compares two JSON objects using deepdiff. + + :param json1: First JSON object to compare. + :param json2: Second JSON object to compare. + :return: True if JSON objects are identical, False otherwise. + """ + if diff := DeepDiff(json1, json2, ignore_order=True): + logger.error(f"JSON objects are not identical - deepdiff: {json.dumps(diff, indent=4)}") + assert json1 == json2 + + +class NotEnoughPhysDevException(Exception): + pass + + +class SSH(paramiko.SSHClient): + LOCAL_COVERAGE_PATH = "tests/results/" + REMOTE_COVERAGE_DATA_DIR = f"/tmp/.{int(time.time())}/coverage_data/" + REMOTE_COVERAGE_DATA_PATH = f"{REMOTE_COVERAGE_DATA_DIR}.coverage" + SWP_REGEX = re.compile("(swp_[A-Z]{2}_)", re.VERBOSE) + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.swp_translated_dict = {} + self.swp_available = [] + self.coverage_enabled = True + + def translate_swp_xx(self, content: str, with_update: bool = False): + update = False + + for match in self.SWP_REGEX.findall(content): + if not (swp := self.swp_translated_dict.get(match)): + if not self.swp_available: + raise NotEnoughPhysDevException( + f"Device does not have enough physical ports (swp) for this test - {self.swp_translated_dict}" + ) + swp = self.swp_available[0] + del self.swp_available[0] + self.swp_translated_dict[match] = swp + + content = content.replace(match, swp) + update = True + + if with_update: + return update, content + else: + return content + + def __get_tmp_translated_file(self, path: str) -> str: + with open(path, "r") as f: + content = f.read() + + update, content = self.translate_swp_xx(content, with_update=True) + + if update: + tmp_path = Path(f"{LOCAL_DIR_FILE_TRANSLATE}/{Path(path).name}") + + with open(tmp_path, "w") as f: + f.write(content) + return str(tmp_path) + else: + return path + + def mkdir_coverage(self): + self.run(f"mkdir -p {self.REMOTE_COVERAGE_DATA_DIR}") + + def scp(self, source: str, destination: str): + tmp_file = self.__get_tmp_translated_file(source) + + logger.info(f"scp {source} {destination}") + + assert os.path.exists(tmp_file), f"{tmp_file} does not exists" + assert os.access(tmp_file, os.R_OK), f"{tmp_file} cannot be read" + + with SCPClient(self.get_transport()) as session: + session.put(tmp_file, destination) + registered_files.add(source) + + def run(self, cmd: str): + translated_cmd = self.translate_swp_xx(cmd) + logger.debug(f"[ssh] {translated_cmd}") + + stdin, stdout, stderr = self.exec_command(translated_cmd) + exit_status = stdout.channel.recv_exit_status() + logger.info(f"[ssh][exit {exit_status}] {translated_cmd}") + + return stdin, stdout, stderr, exit_status + + def run_assert_success(self, cmd: str): + _, stdout, stderr, exit_status = self.run(cmd) + + assert exit_status == 0 + assert not stderr.read().decode("utf-8"), f"{cmd} has stderr" + + return stdout.read().decode("utf-8") + + def __ifupdown2( + self, + op: str, + args: str, + expected_status: int = 0, + return_stderr: bool = False, + return_stdout: bool = False, + ignore_stdout: bool = False, + ignore_stderr: bool = False, + ): + + assert op in ["ifup", "ifdown", "ifreload", "ifquery"] + assert args, f"{op} has been called without arguments" + + if self.coverage_enabled: + _, stdout, stderr, exit_status = self.run( + f"python3 -m coverage run --data-file={self.REMOTE_COVERAGE_DATA_PATH} -a /usr/sbin/{op} {args}" + ) + else: + _, stdout, stderr, exit_status = self.run(f"{op} {args}") + + stdout_str: str = stdout.read().decode("utf-8") + stderr_str: str = stderr.read().decode("utf-8") + + assert exit_status == expected_status, f"{op} exited {exit_status} (expected {expected_status}) (stdout: {repr(stdout_str)}, stderr: {repr(stderr_str)})" + + if return_stdout: + if not ignore_stderr: + assert not stderr_str, f"{op} has stderr ({repr(stderr_str)})" + return stdout_str + + if not ignore_stdout: + assert not stdout_str, f"{op} has stdout ({repr(stdout_str)})" + + if return_stderr: + return stderr_str + + if not ignore_stderr: + assert not stderr_str, f"{op} has stderr ({repr(stderr_str)})" + + def ifdown(self, args: str, **kwargs) -> str: + assert args, "ifdown has been called without arguments" + return self.__ifupdown2("ifdown", args, **kwargs) + + def ifdown_x_eth0_x_mgmt(self, **kwargs) -> str: + return self.__ifupdown2("ifdown", "-a -X eth0 -X mgmt", **kwargs) + + def ifup(self, args: str, **kwargs) -> str: + assert args, "ifup has been called without arguments" + return self.__ifupdown2("ifup", args, **kwargs) + + def ifup_a(self, **kwargs) -> str: + return self.__ifupdown2("ifup", "-a", **kwargs) + + def ifreload_a(self, **kwargs) -> str: + return self.__ifupdown2("ifreload", "-a", **kwargs) + + def ifreload_av(self, **kwargs) -> str: + """ + Run ifreload -av and return stderr + """ + return self.__ifupdown2("ifreload", "-av", return_stderr=True, **kwargs) + + def ifquery(self, args: str, **kwargs) -> str: + assert args, "ifquery has been called without arguments" + return self.__ifupdown2("ifquery", args, **kwargs) + + def ifquery_c(self, args: str, **kwargs) -> str: + return self.ifquery(f"{args} -c", return_stdout=True, **kwargs) + + def ifquery_ac(self, **kwargs) -> str: + return self.ifquery_c("-a", **kwargs) + + def ifquery_ac_json(self, **kwargs) -> str: + """ + Run ifquery -ac -o json and return json object + """ + return json.loads( + self.__ifupdown2("ifquery", "-ac -o json", return_stdout=True, **kwargs, ) + ) + + def bridge_vlan_show_json(self): + return json.loads(self.run_assert_success("bridge -c -j vlan show")) + + def download_coverage(self): + self.run( + f"cd {self.REMOTE_COVERAGE_DATA_DIR} && " + f"coverage html -d . && " + f"cd .. && " + f"tar -czvf coverage_data.tar.gz coverage_data/" + ) + + remote_path = f"{self.REMOTE_COVERAGE_DATA_DIR}/../coverage_data.tar.gz" + local_path = self.LOCAL_COVERAGE_PATH + + logger.info(f"[coverage] scp host:{remote_path} {local_path}") + + with SCPClient(self.get_transport()) as session: + session.get(remote_path=remote_path, local_path=local_path) + + def load_swps(self): + if self.swp_available: + return + _, stdout, _, _ = self.run( + 'python -c "import os;' + 'print \',\'.join(sorted([dev for dev in os.listdir(\'/sys/class/net/\') ' + 'if dev.startswith(\'swp\') and \'.\' not in dev]));"' + ) + for swp in stdout.read().decode("utf-8").strip("\r\n").split(","): + if swp: + self.swp_available.append(swp) + + +@pytest.fixture(scope="session") +def ssh(): + remote_host = os.environ.get("PYTEST_REMOTE_HOST") + remote_user = os.environ.get("PYTEST_REMOTE_USER") + remote_pw = os.environ.get("PYTEST_REMOTE_PASSWORD") + + if not remote_host: + pytest.fail("Missing required PYTEST_REMOTE_HOST in env") + + if not remote_user: + pytest.fail("Missing required PYTEST_REMOTE_USER in env") + + if not remote_pw: + pytest.fail("Missing required PYTEST_REMOTE_PASSWORD in env") + + # Setup SSH client using paramiko + client = SSH() + client.load_system_host_keys() + client.connect(remote_host, username=remote_user, password=remote_pw) + client.load_swps() + client.mkdir_coverage() + # todo: install necessary packages (i.e. coverage) + yield client + client.close() + + +@pytest.fixture(scope="function") +def setup(request, ssh): + ssh.ifdown_x_eth0_x_mgmt() + file_name = request.node.name.replace("test_", "") + ssh.scp(os.path.join("tests/eni", f"{file_name}.eni"), ENI) + ssh.run(f"rm -f {ENI_D}/*") + + +@pytest.fixture +def get_json(ssh): + def _get_translated_json(file_name): + file_path = f"tests/output/{file_name}" + + logger.info(f"get_json: {file_path}") + + with open(file_path, "r") as f: + content = f.read() + + registered_files.add(file_path) + return json.loads(ssh.translate_swp_xx(content)) + + yield _get_translated_json + + +@pytest.fixture +def get_file(ssh): + def _get_translated_file(file_name): + file_path = f"tests/output/{file_name}" + + logger.info(f"get_file: {file_path}") + + with open(file_path, "r") as f: + content = f.read() + + registered_files.add(file_path) + return ssh.translate_swp_xx(content) + + yield _get_translated_file + + +@pytest.fixture +def skip_if_any_test_failed(request): + global test_failed + if test_failed: + pytest.skip("skipping this test because a previous test failed") diff --git a/tests/eni/address.eni b/tests/eni/address.eni new file mode 100644 index 00000000..1b450309 --- /dev/null +++ b/tests/eni/address.eni @@ -0,0 +1,20 @@ +auto lo +iface lo inet loopback + +auto eth0 +iface eth0 inet dhcp + vrf mgmt + +auto mgmt +iface mgmt + vrf-table auto + +auto swp_AA_ +iface swp_AA_ + address 2001:db8::1/128 + address 192.0.2.1/32 + address 2001:db8::42/128 + address 192.0.2.42/32 + address 2042:db8::1/128 + address 192.0.42.1/32 + address 192.42.2.42/32 diff --git a/tests/eni/address_gateway.eni b/tests/eni/address_gateway.eni new file mode 100644 index 00000000..af18018c --- /dev/null +++ b/tests/eni/address_gateway.eni @@ -0,0 +1,22 @@ +auto lo +iface lo inet loopback + +auto eth0 +iface eth0 inet dhcp + vrf mgmt + +auto mgmt +iface mgmt + vrf-table auto + +auto swp_AA_ +iface swp_AA_ + address 10.0.14.3/14 + gateway 10.1.14.3 + link-down yes + +auto br1 +iface br1 + bridge-ports swp_AA_.3000 + address 10.8.22.2/23 + address-virtual 44:39:39:FF:30:00 10.8.22.1/23 2001:0388:6080:0340::1/64 diff --git a/tests/eni/bond.eni b/tests/eni/bond.eni new file mode 100644 index 00000000..ca7f812a --- /dev/null +++ b/tests/eni/bond.eni @@ -0,0 +1,67 @@ +auto lo +iface lo inet loopback + +auto eth0 +iface eth0 inet dhcp + vrf mgmt + +auto mgmt +iface mgmt + vrf-table auto + +auto bond0 +iface bond0 + bond-slaves swp_AA_ + bond-use-carrier yes + bond-num-grat-arp 0 + bond-num-unsol-na 0 + bond-xmit-hash-policy layer2 + bond-miimon 0 + bond-min-links 0 + bond-mode balance-rr + bond-updelay 0 + +auto bond1 +iface bond1 + bond-slaves swp_BB_ + bond-use-carrier no + bond-num-grat-arp 255 + bond-num-unsol-na 255 + bond-xmit-hash-policy layer3+4 + bond-miimon 255 + bond-min-links 255 + bond-mode active-backup + bond-updelay 65535 + +auto bond2 +iface bond2 + bond-slaves swp_CC_ + bond-use-carrier 0 + bond-num-grat-arp 1 + bond-num-unsol-na 1 + bond-xmit-hash-policy layer2+3 + bond-miimon 1 + bond-min-links 1 + bond-mode balance-xor + bond-updelay 1 + +auto bond3 +iface bond3 + bond-slaves swp_DD_ + bond-use-carrier 1 + bond-num-grat-arp 42 + bond-num-unsol-na 42 + bond-miimon 42 + bond-min-links 42 + bond-mode broadcast + bond-updelay 42 + +auto bond5 +iface bond5 + bond-slaves swp_FF_ + bond-mode balance-tlb + +auto bond6 +iface bond6 + bond-slaves swp_GG_ + bond-mode balance-alb diff --git a/tests/eni/bond_lacp.eni b/tests/eni/bond_lacp.eni new file mode 100644 index 00000000..abeaf821 --- /dev/null +++ b/tests/eni/bond_lacp.eni @@ -0,0 +1,38 @@ +auto lo +iface lo inet loopback + +auto eth0 +iface eth0 inet dhcp + vrf mgmt + +auto mgmt +iface mgmt + vrf-table auto + +auto bond4 +iface bond4 + bond-slaves swp_EE_ + bond-mode 802.3ad + bond-lacp-rate slow + bond-lacp-bypass-allow yes + +auto bond7 +iface bond7 + bond-slaves swp_HH_ + bond-mode 802.3ad + bond-lacp-rate fast + bond-lacp-bypass-allow no + +auto bond8 +iface bond8 + bond-slaves swp_II_ + bond-mode 802.3ad + bond-lacp-rate 0 + bond-lacp-bypass-allow 0 + +auto bond9 +iface bond9 + bond-slaves swp_JJ_ + bond-mode 802.3ad + bond-lacp-rate 1 + bond-lacp-bypass-allow 1 diff --git a/tests/eni/bridge1.eni b/tests/eni/bridge1.eni new file mode 100644 index 00000000..c55b778a --- /dev/null +++ b/tests/eni/bridge1.eni @@ -0,0 +1,18 @@ +# The loopback network interface +auto lo +iface lo inet loopback + +# The primary network interface +auto eth0 +iface eth0 inet dhcp + vrf mgmt + +auto mgmt +iface mgmt + vrf-table auto + +auto bridge +iface bridge + bridge-vlan-aware yes + bridge-ports swp_AA_ swp_BB_ + bridge-trunk 1-20 diff --git a/tests/eni/bridge2.eni b/tests/eni/bridge2.eni new file mode 100644 index 00000000..aa56fac9 --- /dev/null +++ b/tests/eni/bridge2.eni @@ -0,0 +1,19 @@ +# The loopback network interface +auto lo +iface lo inet loopback + +# The primary network interface +auto eth0 +iface eth0 inet dhcp + vrf mgmt + +auto mgmt +iface mgmt + vrf-table auto + +auto bridge +iface bridge + bridge-vlan-aware yes + bridge-ports swp_AA_ swp_BB_ + bridge-pvid 2 + bridge-vids 1-20 diff --git a/tests/eni/bridge3.eni b/tests/eni/bridge3.eni new file mode 100644 index 00000000..8d56f190 --- /dev/null +++ b/tests/eni/bridge3.eni @@ -0,0 +1,27 @@ +# The loopback network interface +auto lo +iface lo inet loopback + +# The primary network interface +auto eth0 +iface eth0 inet dhcp + vrf mgmt + +auto mgmt +iface mgmt + vrf-table auto + +auto swp_AA_ +iface swp_AA_ + bridge-access 100 + +auto swp_BB_ +iface swp_BB_ + bridge-vids 200 + +auto bridge +iface bridge + bridge-vlan-aware yes + bridge-ports swp_AA_ swp_BB_ + bridge-pvid 2 + bridge-vids 1-20 diff --git a/tests/eni/bridge4.eni b/tests/eni/bridge4.eni new file mode 100644 index 00000000..a71941f4 --- /dev/null +++ b/tests/eni/bridge4.eni @@ -0,0 +1,28 @@ +# The loopback network interface +auto lo +iface lo inet loopback + +# The primary network interface +auto eth0 +iface eth0 inet dhcp + vrf mgmt + +auto mgmt +iface mgmt + vrf-table auto + +auto swp_AA_ +iface swp_AA_ + bridge-pvid 4 + +auto swp_BB_ +iface swp_BB_ + bridge-pvid 3 + bridge-trunk 40 + +auto bridge +iface bridge + bridge-vlan-aware yes + bridge-ports swp_AA_ swp_BB_ + bridge-pvid 2 + bridge-trunk 1-20 diff --git a/tests/eni/bridge5.eni b/tests/eni/bridge5.eni new file mode 100644 index 00000000..ecb7cc82 --- /dev/null +++ b/tests/eni/bridge5.eni @@ -0,0 +1,24 @@ +# The loopback network interface +auto lo +iface lo inet loopback + +# The primary network interface +auto eth0 +iface eth0 inet dhcp + vrf mgmt + +auto mgmt +iface mgmt + vrf-table auto + +auto swp_AA_ +iface swp_AA_ + +auto swp_BB_ +iface swp_BB_ + +auto bridge +iface bridge + bridge-ports swp_AA_ swp_BB_ + bridge-vids 1-200 + bridge-vlan-aware yes diff --git a/tests/eni/bridge6_multiple_bridge_ports_lines.eni b/tests/eni/bridge6_multiple_bridge_ports_lines.eni new file mode 100644 index 00000000..729a7c00 --- /dev/null +++ b/tests/eni/bridge6_multiple_bridge_ports_lines.eni @@ -0,0 +1,18 @@ +auto lo +iface lo inet loopback + +# The primary network interface +auto eth0 +iface eth0 inet dhcp + vrf mgmt + +auto mgmt +iface mgmt + vrf-table auto + +auto bridge +iface bridge + bridge-vlan-aware yes + bridge-stp on + bridge-ports swp_AA_ swp_BB_ + bridge-ports swp_CC_ swp_DD_ diff --git a/tests/eni/bridge7_macvlans.eni b/tests/eni/bridge7_macvlans.eni new file mode 100644 index 00000000..f6155bf2 --- /dev/null +++ b/tests/eni/bridge7_macvlans.eni @@ -0,0 +1,146 @@ +auto lo +iface lo inet loopback + +auto eth0 +iface eth0 inet dhcp + vrf mgmt + +auto mgmt +iface mgmt + vrf-table auto + +auto bridge +iface bridge + bridge-vlan-aware yes + bridge-ports swp_AA_ + bridge-stp on + bridge-vids 1-33 + bridge-pvid 192 + +auto bridge.1 +iface bridge.1 + address-virtual 42:38:39:FF:00:1 10.7.192.1/1 + +auto bridge.2 +iface bridge.2 + address-virtual 42:38:39:ff:00:2 10.7.192.2/2 + +auto bridge.3 +iface bridge.3 + address-virtual 42:38:39:ff:00:3 10.7.192.3/3 + +auto bridge.4 +iface bridge.4 + address-virtual 42:38:39:ff:00:4 10.7.192.4/4 + +auto bridge.5 +iface bridge.5 + address-virtual 42:38:39:ff:00:5 10.7.192.5/5 + +auto bridge.6 +iface bridge.6 + address-virtual 42:38:39:ff:00:6 10.7.192.6/6 + +auto bridge.7 +iface bridge.7 + address-virtual 42:38:39:ff:00:7 10.7.192.7/7 + +auto bridge.8 +iface bridge.8 + address-virtual 42:38:39:ff:00:8 10.7.192.8/8 + +auto bridge.9 +iface bridge.9 + address-virtual 42:38:39:ff:00:9 10.7.192.9/9 + +auto bridge.10 +iface bridge.10 + address-virtual 42:38:39:ff:00:10 10.7.192.10/10 + +auto bridge.11 +iface bridge.11 + address-virtual 42:38:39:ff:00:11 10.7.192.11/11 + +auto bridge.12 +iface bridge.12 + address-virtual 42:38:39:ff:00:12 10.7.192.12/12 + +auto bridge.13 +iface bridge.13 + address-virtual 42:38:39:ff:00:13 10.7.192.13/13 + +auto bridge.14 +iface bridge.14 + address-virtual 42:38:39:ff:00:14 10.7.192.14/14 + +auto bridge.15 +iface bridge.15 + address-virtual 42:38:39:ff:00:15 10.7.192.15/15 + +auto bridge.16 +iface bridge.16 + address-virtual 42:38:39:ff:00:16 10.7.192.16/16 + +auto bridge.17 +iface bridge.17 + address-virtual 42:38:39:ff:00:17 10.7.192.17/17 + +auto bridge.18 +iface bridge.18 + address-virtual 42:38:39:ff:00:18 10.7.192.18/18 + +auto bridge.19 +iface bridge.19 + address-virtual 42:38:39:ff:00:19 10.7.192.19/19 + +auto bridge.20 +iface bridge.20 + address-virtual 42:38:39:ff:00:20 10.7.192.20/20 + +auto bridge.21 +iface bridge.21 + address-virtual 42:38:39:ff:00:21 10.7.192.21/21 + +auto bridge.22 +iface bridge.22 + address-virtual 42:38:39:ff:00:22 10.7.192.22/22 + +auto bridge.23 +iface bridge.23 + address-virtual 42:38:39:ff:00:23 10.7.192.23/23 + +auto bridge.24 +iface bridge.24 + address-virtual 42:38:39:ff:00:24 10.7.192.24/24 + +auto bridge.25 +iface bridge.25 + address-virtual 42:38:39:ff:00:25 10.7.192.25/25 + +auto bridge.26 +iface bridge.26 + address-virtual 42:38:39:ff:00:26 10.7.192.26/26 + +auto bridge.27 +iface bridge.27 + address-virtual 42:38:39:ff:00:27 10.7.192.27/27 + +auto bridge.28 +iface bridge.28 + address-virtual 42:38:39:ff:00:28 10.7.192.28/28 + +auto bridge.29 +iface bridge.29 + address-virtual 42:38:39:ff:00:29 10.7.192.29/29 + +auto bridge.30 +iface bridge.30 + address-virtual 42:38:39:ff:00:30 10.7.192.30/30 + +auto bridge.31 +iface bridge.31 + address-virtual 42:38:39:ff:00:31 10.7.192.31/31 + +auto bridge.32 +iface bridge.32 + address-virtual 42:38:39:ff:00:32 10.7.192.32/32 diff --git a/tests/eni/bridge8_reserved_vlans.eni b/tests/eni/bridge8_reserved_vlans.eni new file mode 100644 index 00000000..2263e8c9 --- /dev/null +++ b/tests/eni/bridge8_reserved_vlans.eni @@ -0,0 +1,18 @@ +# The loopback network interface +auto lo +iface lo inet loopback + +# The primary network interface +auto eth0 +iface eth0 inet dhcp + vrf mgmt + +auto mgmt +iface mgmt + vrf-table auto + +auto bridge +iface bridge + bridge-vlan-aware yes + bridge-ports swp_AA_ swp_BB_ + bridge-trunk 2999-4000 diff --git a/tests/eni/bridge_access.eni b/tests/eni/bridge_access.eni new file mode 100644 index 00000000..066f2947 --- /dev/null +++ b/tests/eni/bridge_access.eni @@ -0,0 +1,34 @@ +# This file describes the network interfaces available on your system +# and how to activate them. For more information, see interfaces(5). + +source /etc/network/interfaces.d/*.intf + +# The loopback network interface +auto lo +iface lo inet loopback + +# The primary network interface +auto eth0 +iface eth0 inet dhcp + vrf mgmt + +auto mgmt +iface mgmt + vrf-table auto + +auto vxlan1 +iface vxlan1 + vxlan-id 1 + bridge-access 1 + vxlan-local-tunnelip 27.0.0.11 + +auto vxlan500 +iface vxlan500 + vxlan-id 500 + bridge-access 500 + vxlan-local-tunnelip 27.0.0.11 + +auto br0 +iface br0 + bridge-vlan-aware yes + bridge-ports vxlan1 vxlan500 diff --git a/tests/eni/bridge_attr_back_to_default.eni b/tests/eni/bridge_attr_back_to_default.eni new file mode 100644 index 00000000..0a820275 --- /dev/null +++ b/tests/eni/bridge_attr_back_to_default.eni @@ -0,0 +1,53 @@ +auto lo +iface lo inet loopback + +auto eth0 +iface eth0 inet dhcp + vrf mgmt + +auto mgmt +iface mgmt + vrf-table auto + +auto br0 +iface br0 + bridge-ports vx42 + bridge-vlan-aware yes + bridge-stp off + bridge-bridgeprio 42 + bridge-ageing 42 + bridge-fd 29 + bridge-hello 10 + bridge-maxage 10 + bridge-pathcosts vx42=42 + bridge-portprios vx42=10 + bridge-mclmc 42 + bridge-mcrouter no + bridge-mcsnoop no + bridge-mcsqc 42 + bridge-mcqifaddr yes + bridge-mcquerier yes + bridge-hashmax 1024 + bridge-mclmi 42 + bridge-mcmi 42 + bridge-mcqpi 42 + bridge-mcqi 42 + bridge-mcqri 42 + bridge-mcsqi 42 + bridge-mcqv4src 100=172.16.100.1 200=10.10.10.1 + bridge-portmcfl vx42=yes + bridge-learning vx42=off + bridge-igmp-version 3 + bridge-mld-version 2 + bridge-unicast-flood vx42=off + bridge-multicast-flood vx42=off + bridge-vlan-protocol 802.1ad + bridge-vlan-stats off + bridge-arp-nd-suppress vx42=on + bridge-mcstats off + bridge-l2protocol-tunnel all + +auto vx42 +iface vx42 + vxlan-id 42 + bridge-access 42 diff --git a/tests/eni/bridge_igmp_version.eni b/tests/eni/bridge_igmp_version.eni new file mode 100644 index 00000000..884a2e1d --- /dev/null +++ b/tests/eni/bridge_igmp_version.eni @@ -0,0 +1,28 @@ +auto lo +iface lo inet loopback + +# The primary network interface +auto eth0 +iface eth0 inet dhcp + vrf mgmt + +auto mgmt +iface mgmt + vrf-table auto + +auto br0 +iface br0 + bridge-vlan-aware yes + bridge-ports swp_AA_ + bridge-igmp-version 3 + bridge-mld-version 2 + +auto br1 +iface br1 + bridge-ports swp_BB_ + bridge-igmp-version 3 + bridge-mld-version 2 + +auto br2 +iface br2 + bridge-ports swp_CC_ diff --git a/tests/eni/bridge_l2protocol_tunnel.eni b/tests/eni/bridge_l2protocol_tunnel.eni new file mode 100644 index 00000000..47e63cfd --- /dev/null +++ b/tests/eni/bridge_l2protocol_tunnel.eni @@ -0,0 +1,28 @@ +auto lo +iface lo inet loopback + +auto eth0 +iface eth0 inet dhcp + vrf mgmt + +auto mgmt +iface mgmt + vrf-table auto + +auto br_vlan_unaware +iface br_vlan_unaware + bridge-ports swp_AA_ swp_BB_ swp_CC_ swp_DD_ swp_EE_ + bridge-l2protocol-tunnel swp_AA_=lacp,pvst swp_BB_=stp,cdp swp_CC_=all swp_DD_=lldp + +auto br_vlan_aware +iface br_vlan_aware + bridge-ports swp_FF_ swp_GG_ + bridge-vlan-aware yes + +auto swp_GG_ +iface swp_GG_ + bridge-l2protocol-tunnel all + +auto swp_FF_ +iface swp_FF_ + bridge-l2protocol-tunnel stp lacp lldp,pvst cdp diff --git a/tests/eni/bridge_new_attribute.eni b/tests/eni/bridge_new_attribute.eni new file mode 100644 index 00000000..aa948705 --- /dev/null +++ b/tests/eni/bridge_new_attribute.eni @@ -0,0 +1,14 @@ +# The loopback network interface +auto lo +iface lo inet loopback + +# The primary network interface +auto eth0 +iface eth0 inet dhcp + vrf mgmt + +auto mgmt +iface mgmt + vrf-table auto + +source /etc/network/interfaces.d/*.eni diff --git a/tests/eni/cm_11485_vlan_device_name_vlan.eni b/tests/eni/cm_11485_vlan_device_name_vlan.eni new file mode 100644 index 00000000..90fd66f3 --- /dev/null +++ b/tests/eni/cm_11485_vlan_device_name_vlan.eni @@ -0,0 +1,37 @@ +auto eth0 +iface eth0 inet dhcp + vrf mgmt + +auto mgmt +iface mgmt + vrf-table auto + +auto lo +iface lo inet loopback + +auto swp_AA_ +iface swp_AA_ inet static + address 172.30.1.3 + netmask 24 + +auto swp_BB_.1005 +iface swp_BB_.1005 + address 10.132.253.11/31 + +auto bond0 +iface bond0 inet manual + bond-slaves swp_BB_ swp_CC_ + bond-mode 802.3ad + bond-min-links 1 + bond-xmit-hash-policy layer2+3 + +auto vlan1007 +iface vlan1007 + vlan-raw-device bond0 + vlan-id 1007 + address 10.132.253.15/31 + +auto dummy0 +iface dummy0 + link-type dummy + address fdca:ffee:ff12:fe06::1/64 diff --git a/tests/eni/evpn_vab_clag_riot_flood_sup_off_config_tors2.eni b/tests/eni/evpn_vab_clag_riot_flood_sup_off_config_tors2.eni new file mode 100644 index 00000000..8fcbc7f6 --- /dev/null +++ b/tests/eni/evpn_vab_clag_riot_flood_sup_off_config_tors2.eni @@ -0,0 +1,72 @@ +auto eth0 +iface eth0 inet dhcp + vrf mgmt + +auto mgmt +iface mgmt + vrf-table auto + +auto lo +iface lo + address 27.0.0.16/32 + alias BGP un-numbered Use for Vxlan Src Tunnel + +auto uplink +iface uplink + bond-slaves swp_AA_ swp_BB_ + bond-mode 802.3ad + bond-min-links 1 + bond-lacp-rate 1 + mtu 9202 + +auto hostbond3 +iface hostbond3 + bond-slaves swp_CC_ swp_DD_ + bond-mode 802.3ad + bond-min-links 1 + bond-lacp-rate 1 + mtu 9152 + bridge-pvid 1000 + + +auto vx-1000 +iface vx-1000 + vxlan-id 1000 + bridge-access 1000 + vxlan-local-tunnelip 27.0.0.16 + bridge-learning off + mstpctl-portbpdufilter yes + mstpctl-bpduguard yes + mtu 9152 + +auto vx-1001 +iface vx-1001 + vxlan-id 1001 + bridge-access 1001 + vxlan-local-tunnelip 27.0.0.16 + bridge-learning off + mstpctl-portbpdufilter yes + mstpctl-bpduguard yes + mtu 9152 + +auto bridge +iface bridge + bridge-vlan-aware yes + bridge-ports vx-1000 vx-1001 hostbond3 + bridge-stp on + bridge-vids 1000-1004 + bridge-pvid 1 + +auto vlan1000 +iface vlan1000 + address 45.0.0.2/24 + vlan-id 1000 + vlan-raw-device bridge + address-virtual 00:00:5e:00:01:01 45.0.0.1/24 2001:fee1::1/64 + +auto vlan1001 +iface vlan1001 + address 45.0.1.2/24 + vlan-id 1001 + vlan-raw-device bridge + address-virtual 00:00:5e:00:01:01 45.0.1.1/24 2001:fee1:0:1::1/64 diff --git a/tests/eni/interfaces_link_state.eni b/tests/eni/interfaces_link_state.eni new file mode 100644 index 00000000..aa948705 --- /dev/null +++ b/tests/eni/interfaces_link_state.eni @@ -0,0 +1,14 @@ +# The loopback network interface +auto lo +iface lo inet loopback + +# The primary network interface +auto eth0 +iface eth0 inet dhcp + vrf mgmt + +auto mgmt +iface mgmt + vrf-table auto + +source /etc/network/interfaces.d/*.eni diff --git a/tests/eni/interfaces_vrr_vrf.eni b/tests/eni/interfaces_vrr_vrf.eni new file mode 100644 index 00000000..4e40831c --- /dev/null +++ b/tests/eni/interfaces_vrr_vrf.eni @@ -0,0 +1,38 @@ +auto lo +iface lo inet loopback + +auto eth0 +iface eth0 inet dhcp + vrf mgmt + +auto mgmt +iface mgmt + vrf-table auto + +auto peerlink +iface peerlink + bond-slaves swp_AA_ swp_BB_ + +auto peerlink.4094 +iface peerlink.4094 + address 169.254.1.1/30 + +auto myvrf +iface myvrf + vrf-table auto + +auto bridge +iface bridge + bridge-vlan-aware yes + bridge-ports peerlink bond0 + bridge-stp on + +auto bridge.901 +iface bridge.901 + address 10.1.63.2/28 + address-virtual 00:00:5e:00:01:81 10.1.63.1/28 + vrf myvrf + +auto bond0 +iface bond0 + bond-slaves swp_CC_ diff --git a/tests/eni/mac1.eni b/tests/eni/mac1.eni new file mode 100644 index 00000000..11112653 --- /dev/null +++ b/tests/eni/mac1.eni @@ -0,0 +1,18 @@ +auto lo +iface lo inet loopback + +auto eth0 +iface eth0 inet dhcp + vrf mgmt + +auto mgmt +iface mgmt + vrf-table auto + +auto swp_AA_ +iface swp_AA_ + hwaddress ether 90:e2:ba:2c:b1:96 + +auto swp_BB_ +iface swp_BB_ + hwaddress 90:e2:ba:2c:b2:95 diff --git a/tests/eni/vxlandev_sanity.eni b/tests/eni/vxlandev_sanity.eni new file mode 100644 index 00000000..b09cb63e --- /dev/null +++ b/tests/eni/vxlandev_sanity.eni @@ -0,0 +1,51 @@ +auto lo +iface lo inet loopback + +auto eth0 +iface eth0 inet dhcp + vrf mgmt + +auto mgmt +iface mgmt + vrf-table auto + +auto bridge +iface bridge + bridge-vlan-aware yes + bridge-ports swp_AA_ swp_BB_ vxlan1000 vxlan10200 + bridge-stp on + bridge-vids 100 200 + bridge-pvid 1 + +auto vxlan1000 +iface vxlan1000 + vxlan-id 1000 + bridge-access 100 + vxlan-local-tunnelip 27.0.0.11 + mstpctl-portbpdufilter yes + mstpctl-bpduguard yes + +auto vxlan10200 +iface vxlan10200 + vxlan-id 10200 + vxlan-local-tunnelip 6.0.0.15 + vxlan-remoteip 6.0.0.16 + bridge-learning on + bridge-access 200 + +auto bridge.100 +iface bridge.100 + address 45.0.0.16/26 + +auto bridge.200 +iface bridge.200 + +auto vx0 +iface vx0 + vxlan-id 42 + vxlan-ageing 30 + vxlan-local-tunnelip 172.16.20.105 + vxlan-svcnodeip 172.16.22.126 + vxlan-remoteip 172.16.22.128 + vxlan-learning yes + vxlan-purge-remotes yes diff --git a/tests/output/EvpnVabClagRiotFloodSupOffConfig.ifquery.ac.json b/tests/output/EvpnVabClagRiotFloodSupOffConfig.ifquery.ac.json new file mode 100644 index 00000000..1bdd1c40 --- /dev/null +++ b/tests/output/EvpnVabClagRiotFloodSupOffConfig.ifquery.ac.json @@ -0,0 +1,170 @@ +[ + { + "name": "eth0", + "addr_method": "dhcp", + "addr_family": "inet", + "auto": true, + "config": { + "vrf": "mgmt" + }, + "config_status": { + "vrf": "pass" + }, + "status": "pass" + }, + { + "name": "mgmt", + "auto": true, + "config": { + "vrf-table": "1001" + }, + "config_status": { + "vrf-table": "pass" + }, + "status": "pass" + }, + { + "name": "lo", + "auto": true, + "config": { + "alias": "BGP un-numbered Use for Vxlan Src Tunnel", + "address": "27.0.0.16/32" + }, + "config_status": { + "alias": "pass", + "address": "pass" + }, + "status": "pass" + }, + { + "name": "uplink", + "auto": true, + "config": { + "bond-lacp-rate": "1", + "mtu": "9202", + "bond-mode": "802.3ad", + "bond-min-links": "1", + "bond-slaves": "swp_AA_ swp_BB_" + }, + "config_status": { + "bond-lacp-rate": "pass", + "mtu": "pass", + "bond-mode": "pass", + "bond-min-links": "pass", + "bond-slaves": "pass" + }, + "status": "pass" + }, + { + "name": "hostbond3", + "auto": true, + "config": { + "bond-lacp-rate": "1", + "bond-min-links": "1", + "mtu": "9152", + "bond-mode": "802.3ad", + "bridge-pvid": "1000", + "bond-slaves": "swp_CC_ swp_DD_" + }, + "config_status": { + "bond-lacp-rate": "pass", + "bond-min-links": "pass", + "mtu": "pass", + "bond-mode": "pass", + "bridge-pvid": "pass", + "bond-slaves": "pass" + }, + "status": "pass" + }, + { + "name": "vx-1000", + "auto": true, + "config": { + "bridge-learning": "off", + "vxlan-id": "1000", + "bridge-access": "1000", + "mtu": "9152", + "vxlan-local-tunnelip": "27.0.0.16" + }, + "config_status": { + "bridge-learning": "pass", + "vxlan-id": "pass", + "bridge-access": "pass", + "mtu": "pass", + "vxlan-local-tunnelip": "pass" + }, + "status": "pass" + }, + { + "name": "vx-1001", + "auto": true, + "config": { + "bridge-learning": "off", + "vxlan-id": "1001", + "bridge-access": "1001", + "mtu": "9152", + "vxlan-local-tunnelip": "27.0.0.16" + }, + "config_status": { + "bridge-learning": "pass", + "vxlan-id": "pass", + "bridge-access": "pass", + "mtu": "pass", + "vxlan-local-tunnelip": "pass" + }, + "status": "pass" + }, + { + "name": "bridge", + "auto": true, + "config": { + "bridge-vlan-aware": "yes", + "bridge-ports": "vx-1000 vx-1001 hostbond3", + "bridge-stp": "yes", + "bridge-pvid": "1", + "bridge-vids": "1000-1004" + }, + "config_status": { + "bridge-vlan-aware": "pass", + "bridge-ports": "pass", + "bridge-stp": "pass", + "bridge-pvid": "", + "bridge-vids": "" + }, + "status": "pass" + }, + { + "name": "vlan1000", + "auto": true, + "config": { + "vlan-id": "1000", + "address-virtual": "00:00:5e:00:01:01 45.0.0.1/24 2001:fee1::1/64", + "vlan-raw-device": "bridge", + "address": "45.0.0.2/24" + }, + "config_status": { + "vlan-id": "pass", + "address-virtual": "pass", + "vlan-raw-device": "pass", + "address": "pass" + }, + "status": "pass" + }, + { + "name": "vlan1001", + "auto": true, + "config": { + "vlan-id": "1001", + "address-virtual": "00:00:5e:00:01:01 45.0.1.1/24 2001:fee1:0:1::1/64", + "vlan-raw-device": "bridge", + "address": "45.0.1.2/24" + }, + "config_status": { + "vlan-id": "pass", + "address-virtual": "pass", + "vlan-raw-device": "pass", + "address": "pass" + }, + "status": "pass" + } +] diff --git a/tests/output/address.ifquery.ac.json b/tests/output/address.ifquery.ac.json new file mode 100644 index 00000000..414727ac --- /dev/null +++ b/tests/output/address.ifquery.ac.json @@ -0,0 +1,62 @@ +[ + { + "name": "lo", + "addr_method": "loopback", + "addr_family": "inet", + "auto": true, + "config": {}, + "config_status": {}, + "status": "pass" + }, + { + "name": "eth0", + "addr_method": "dhcp", + "addr_family": "inet", + "auto": true, + "config": { + "vrf": "mgmt" + }, + "config_status": { + "vrf": "pass" + }, + "status": "pass" + }, + { + "name": "mgmt", + "auto": true, + "config": { + "vrf-table": "1001" + }, + "config_status": { + "vrf-table": "pass" + }, + "status": "pass" + }, + { + "name": "swp_AA_", + "auto": true, + "config": { + "address": [ + "192.0.2.1/32", + "192.0.2.42/32", + "192.0.42.1/32", + "192.42.2.42/32", + "2001:db8::1/128", + "2001:db8::42/128", + "2042:db8::1/128" + ] + }, + "config_status": { + "address": [ + "pass", + "pass", + "pass", + "pass", + "pass", + "pass", + "pass" + ] + }, + "status": "pass" + } +] diff --git a/tests/output/address_gateway.empty_addrs.ifquery.ac.json b/tests/output/address_gateway.empty_addrs.ifquery.ac.json new file mode 100644 index 00000000..50f21fd7 --- /dev/null +++ b/tests/output/address_gateway.empty_addrs.ifquery.ac.json @@ -0,0 +1,59 @@ +[ + { + "name": "lo", + "addr_method": "loopback", + "addr_family": "inet", + "auto": true, + "config": {}, + "config_status": {}, + "status": "pass" + }, + { + "name": "eth0", + "addr_method": "dhcp", + "addr_family": "inet", + "auto": true, + "config": { + "vrf": "mgmt" + }, + "config_status": { + "vrf": "pass" + }, + "status": "pass" + }, + { + "name": "mgmt", + "auto": true, + "config": { + "vrf-table": "1001" + }, + "config_status": { + "vrf-table": "pass" + }, + "status": "pass" + }, + { + "name": "swp_AA_", + "auto": true, + "config": { + "link-down": "yes" + }, + "config_status": { + "link-down": "pass" + }, + "status": "pass" + }, + { + "name": "br1", + "auto": true, + "config": { + "address-virtual": "44:39:39:FF:30:00 10.8.22.1/23 2001:0388:6080:0340::1/64", + "bridge-ports": "swp_AA_.3000" + }, + "config_status": { + "address-virtual": "pass", + "bridge-ports": "pass" + }, + "status": "pass" + } +] diff --git a/tests/output/address_gateway.ifquery.ac.json b/tests/output/address_gateway.ifquery.ac.json new file mode 100644 index 00000000..4655050e --- /dev/null +++ b/tests/output/address_gateway.ifquery.ac.json @@ -0,0 +1,63 @@ +[ + { + "name": "lo", + "addr_method": "loopback", + "addr_family": "inet", + "auto": true, + "config": {}, + "config_status": {}, + "status": "pass" + }, + { + "name": "eth0", + "addr_method": "dhcp", + "addr_family": "inet", + "auto": true, + "config": { + "vrf": "mgmt" + }, + "config_status": { + "vrf": "pass" + }, + "status": "pass" + }, + { + "name": "mgmt", + "auto": true, + "config": { + "vrf-table": "1001" + }, + "config_status": { + "vrf-table": "pass" + }, + "status": "pass" + }, + { + "name": "swp_AA_", + "auto": true, + "config": { + "link-down": "yes", + "address": "10.0.14.3/14" + }, + "config_status": { + "link-down": "pass", + "address": "pass" + }, + "status": "pass" + }, + { + "name": "br1", + "auto": true, + "config": { + "address-virtual": "44:39:39:FF:30:00 10.8.22.1/23 2001:0388:6080:0340::1/64", + "bridge-ports": "swp_AA_.3000", + "address": "10.8.22.2/23" + }, + "config_status": { + "address-virtual": "pass", + "bridge-ports": "pass", + "address": "pass" + }, + "status": "pass" + } +] diff --git a/tests/output/bond.flipped.values.ifquery.ac.json b/tests/output/bond.flipped.values.ifquery.ac.json new file mode 100644 index 00000000..53bc0353 --- /dev/null +++ b/tests/output/bond.flipped.values.ifquery.ac.json @@ -0,0 +1,165 @@ +[ + { + "name": "lo", + "addr_method": "loopback", + "addr_family": "inet", + "auto": true, + "config": {}, + "config_status": {}, + "status": "pass" + }, + { + "name": "eth0", + "addr_method": "dhcp", + "addr_family": "inet", + "auto": true, + "config": { + "vrf": "mgmt" + }, + "config_status": { + "vrf": "pass" + }, + "status": "pass" + }, + { + "name": "mgmt", + "auto": true, + "config": { + "vrf-table": "1001" + }, + "config_status": { + "vrf-table": "pass" + }, + "status": "pass" + }, + { + "name": "bond0", + "auto": true, + "config": { + "bond-use-carrier": "no", + "bond-miimon": "1", + "bond-min-links": "1", + "bond-slaves": "swp_AA_", + "bond-mode": "0", + "bond-num-unsol-na": "1", + "bond-updelay": "1", + "bond-xmit-hash-policy": "layer3+4", + "bond-num-grat-arp": "1" + }, + "config_status": { + "bond-use-carrier": "pass", + "bond-miimon": "pass", + "bond-min-links": "pass", + "bond-slaves": "pass", + "bond-mode": "pass", + "bond-num-unsol-na": "pass", + "bond-updelay": "pass", + "bond-xmit-hash-policy": "pass", + "bond-num-grat-arp": "pass" + }, + "status": "pass" + }, + { + "name": "bond1", + "auto": true, + "config": { + "bond-use-carrier": "yes", + "bond-miimon": "0", + "bond-min-links": "0", + "bond-slaves": "swp_BB_", + "bond-mode": "1", + "bond-num-unsol-na": "0", + "bond-xmit-hash-policy": "layer2", + "bond-num-grat-arp": "0" + }, + "config_status": { + "bond-use-carrier": "pass", + "bond-miimon": "pass", + "bond-min-links": "pass", + "bond-slaves": "pass", + "bond-mode": "pass", + "bond-num-unsol-na": "pass", + "bond-xmit-hash-policy": "pass", + "bond-num-grat-arp": "pass" + }, + "status": "pass" + }, + { + "name": "bond2", + "auto": true, + "config": { + "bond-use-carrier": "1", + "bond-miimon": "0", + "bond-min-links": "0", + "bond-slaves": "swp_CC_", + "bond-mode": "2", + "bond-num-unsol-na": "0", + "bond-updelay": "0", + "bond-xmit-hash-policy": "layer2+3", + "bond-num-grat-arp": "0" + }, + "config_status": { + "bond-use-carrier": "pass", + "bond-miimon": "pass", + "bond-min-links": "pass", + "bond-slaves": "pass", + "bond-mode": "pass", + "bond-num-unsol-na": "pass", + "bond-updelay": "pass", + "bond-xmit-hash-policy": "pass", + "bond-num-grat-arp": "pass" + }, + "status": "pass" + }, + { + "name": "bond3", + "auto": true, + "config": { + "bond-use-carrier": "0", + "bond-miimon": "42", + "bond-min-links": "42", + "bond-slaves": "swp_DD_", + "bond-mode": "3", + "bond-num-unsol-na": "42", + "bond-updelay": "42", + "bond-num-grat-arp": "42" + }, + "config_status": { + "bond-use-carrier": "pass", + "bond-miimon": "pass", + "bond-min-links": "pass", + "bond-slaves": "pass", + "bond-mode": "pass", + "bond-num-unsol-na": "pass", + "bond-updelay": "pass", + "bond-num-grat-arp": "pass" + }, + "status": "pass" + }, + { + "name": "bond5", + "auto": true, + "config": { + "bond-mode": "5", + "bond-slaves": "swp_FF_" + }, + "config_status": { + "bond-mode": "pass", + "bond-slaves": "pass" + }, + "status": "pass" + }, + { + "name": "bond6", + "auto": true, + "config": { + "bond-mode": "6", + "bond-slaves": "swp_GG_" + }, + "config_status": { + "bond-mode": "pass", + "bond-slaves": "pass" + }, + "status": "pass" + } +] diff --git a/tests/output/bond.ifquery.ac.json b/tests/output/bond.ifquery.ac.json new file mode 100644 index 00000000..1edda1f2 --- /dev/null +++ b/tests/output/bond.ifquery.ac.json @@ -0,0 +1,167 @@ +[ + { + "name": "lo", + "addr_method": "loopback", + "addr_family": "inet", + "auto": true, + "config": {}, + "config_status": {}, + "status": "pass" + }, + { + "name": "eth0", + "addr_method": "dhcp", + "addr_family": "inet", + "auto": true, + "config": { + "vrf": "mgmt" + }, + "config_status": { + "vrf": "pass" + }, + "status": "pass" + }, + { + "name": "mgmt", + "auto": true, + "config": { + "vrf-table": "1001" + }, + "config_status": { + "vrf-table": "pass" + }, + "status": "pass" + }, + { + "name": "bond0", + "auto": true, + "config": { + "bond-use-carrier": "yes", + "bond-miimon": "0", + "bond-min-links": "0", + "bond-slaves": "swp_AA_", + "bond-mode": "balance-rr", + "bond-num-unsol-na": "0", + "bond-updelay": "0", + "bond-xmit-hash-policy": "layer2", + "bond-num-grat-arp": "0" + }, + "config_status": { + "bond-use-carrier": "pass", + "bond-miimon": "pass", + "bond-min-links": "pass", + "bond-slaves": "pass", + "bond-mode": "pass", + "bond-num-unsol-na": "pass", + "bond-updelay": "pass", + "bond-xmit-hash-policy": "pass", + "bond-num-grat-arp": "pass" + }, + "status": "pass" + }, + { + "name": "bond1", + "auto": true, + "config": { + "bond-use-carrier": "no", + "bond-miimon": "255", + "bond-min-links": "255", + "bond-slaves": "swp_BB_", + "bond-mode": "active-backup", + "bond-num-unsol-na": "255", + "bond-updelay": "65535", + "bond-xmit-hash-policy": "layer3+4", + "bond-num-grat-arp": "255" + }, + "config_status": { + "bond-use-carrier": "pass", + "bond-miimon": "pass", + "bond-min-links": "pass", + "bond-slaves": "pass", + "bond-mode": "pass", + "bond-num-unsol-na": "pass", + "bond-updelay": "pass", + "bond-xmit-hash-policy": "pass", + "bond-num-grat-arp": "pass" + }, + "status": "pass" + }, + { + "name": "bond2", + "auto": true, + "config": { + "bond-use-carrier": "0", + "bond-miimon": "1", + "bond-min-links": "1", + "bond-slaves": "swp_CC_", + "bond-mode": "balance-xor", + "bond-num-unsol-na": "1", + "bond-updelay": "1", + "bond-xmit-hash-policy": "layer2+3", + "bond-num-grat-arp": "1" + }, + "config_status": { + "bond-use-carrier": "pass", + "bond-miimon": "pass", + "bond-min-links": "pass", + "bond-slaves": "pass", + "bond-mode": "pass", + "bond-num-unsol-na": "pass", + "bond-updelay": "pass", + "bond-xmit-hash-policy": "pass", + "bond-num-grat-arp": "pass" + }, + "status": "pass" + }, + { + "name": "bond3", + "auto": true, + "config": { + "bond-use-carrier": "1", + "bond-miimon": "42", + "bond-min-links": "42", + "bond-slaves": "swp_DD_", + "bond-mode": "broadcast", + "bond-num-unsol-na": "42", + "bond-updelay": "42", + "bond-num-grat-arp": "42" + }, + "config_status": { + "bond-use-carrier": "pass", + "bond-miimon": "pass", + "bond-min-links": "pass", + "bond-slaves": "pass", + "bond-mode": "pass", + "bond-num-unsol-na": "pass", + "bond-updelay": "pass", + "bond-num-grat-arp": "pass" + }, + "status": "pass" + }, + { + "name": "bond5", + "auto": true, + "config": { + "bond-mode": "balance-tlb", + "bond-slaves": "swp_FF_" + }, + "config_status": { + "bond-mode": "pass", + "bond-slaves": "pass" + }, + "status": "pass" + }, + { + "name": "bond6", + "auto": true, + "config": { + "bond-mode": "balance-alb", + "bond-slaves": "swp_GG_" + }, + "config_status": { + "bond-mode": "pass", + "bond-slaves": "pass" + }, + "status": "pass" + } +] diff --git a/tests/output/bond_lacp.flipped.values.ifquery.ac.json b/tests/output/bond_lacp.flipped.values.ifquery.ac.json new file mode 100644 index 00000000..79e60b0a --- /dev/null +++ b/tests/output/bond_lacp.flipped.values.ifquery.ac.json @@ -0,0 +1,103 @@ +[ + { + "name": "lo", + "addr_method": "loopback", + "addr_family": "inet", + "auto": true, + "config": {}, + "config_status": {}, + "status": "pass" + }, + { + "name": "eth0", + "addr_method": "dhcp", + "addr_family": "inet", + "auto": true, + "config": { + "vrf": "mgmt" + }, + "config_status": { + "vrf": "pass" + }, + "status": "pass" + }, + { + "name": "mgmt", + "auto": true, + "config": { + "vrf-table": "1001" + }, + "config_status": { + "vrf-table": "pass" + }, + "status": "pass" + }, + { + "name": "bond4", + "auto": true, + "config": { + "bond-lacp-rate": "slow", + "bond-lacp-bypass-allow": "no", + "bond-mode": "4", + "bond-slaves": "swp_EE_" + }, + "config_status": { + "bond-lacp-rate": "pass", + "bond-lacp-bypass-allow": "pass", + "bond-mode": "pass", + "bond-slaves": "pass" + }, + "status": "pass" + }, + { + "name": "bond7", + "auto": true, + "config": { + "bond-lacp-rate": "fast", + "bond-lacp-bypass-allow": "yes", + "bond-mode": "4", + "bond-slaves": "swp_HH_" + }, + "config_status": { + "bond-lacp-rate": "pass", + "bond-lacp-bypass-allow": "pass", + "bond-mode": "pass", + "bond-slaves": "pass" + }, + "status": "pass" + }, + { + "name": "bond8", + "auto": true, + "config": { + "bond-lacp-rate": "1", + "bond-lacp-bypass-allow": "1", + "bond-mode": "4", + "bond-slaves": "swp_II_" + }, + "config_status": { + "bond-lacp-rate": "pass", + "bond-lacp-bypass-allow": "pass", + "bond-mode": "pass", + "bond-slaves": "pass" + }, + "status": "pass" + }, + { + "name": "bond9", + "auto": true, + "config": { + "bond-lacp-rate": "0", + "bond-lacp-bypass-allow": "0", + "bond-mode": "4", + "bond-slaves": "swp_JJ_" + }, + "config_status": { + "bond-lacp-rate": "pass", + "bond-lacp-bypass-allow": "pass", + "bond-mode": "pass", + "bond-slaves": "pass" + }, + "status": "pass" + } +] diff --git a/tests/output/bond_lacp.ifquery.ac.json b/tests/output/bond_lacp.ifquery.ac.json new file mode 100644 index 00000000..86c838df --- /dev/null +++ b/tests/output/bond_lacp.ifquery.ac.json @@ -0,0 +1,103 @@ +[ + { + "name": "lo", + "addr_method": "loopback", + "addr_family": "inet", + "auto": true, + "config": {}, + "config_status": {}, + "status": "pass" + }, + { + "name": "eth0", + "addr_method": "dhcp", + "addr_family": "inet", + "auto": true, + "config": { + "vrf": "mgmt" + }, + "config_status": { + "vrf": "pass" + }, + "status": "pass" + }, + { + "name": "mgmt", + "auto": true, + "config": { + "vrf-table": "1001" + }, + "config_status": { + "vrf-table": "pass" + }, + "status": "pass" + }, + { + "name": "bond4", + "auto": true, + "config": { + "bond-lacp-rate": "slow", + "bond-lacp-bypass-allow": "yes", + "bond-mode": "802.3ad", + "bond-slaves": "swp_EE_" + }, + "config_status": { + "bond-lacp-rate": "pass", + "bond-lacp-bypass-allow": "pass", + "bond-mode": "pass", + "bond-slaves": "pass" + }, + "status": "pass" + }, + { + "name": "bond7", + "auto": true, + "config": { + "bond-lacp-rate": "fast", + "bond-lacp-bypass-allow": "no", + "bond-mode": "802.3ad", + "bond-slaves": "swp_HH_" + }, + "config_status": { + "bond-lacp-rate": "pass", + "bond-lacp-bypass-allow": "pass", + "bond-mode": "pass", + "bond-slaves": "pass" + }, + "status": "pass" + }, + { + "name": "bond8", + "auto": true, + "config": { + "bond-lacp-rate": "0", + "bond-lacp-bypass-allow": "0", + "bond-mode": "802.3ad", + "bond-slaves": "swp_II_" + }, + "config_status": { + "bond-lacp-rate": "pass", + "bond-lacp-bypass-allow": "pass", + "bond-mode": "pass", + "bond-slaves": "pass" + }, + "status": "pass" + }, + { + "name": "bond9", + "auto": true, + "config": { + "bond-lacp-rate": "1", + "bond-lacp-bypass-allow": "1", + "bond-mode": "802.3ad", + "bond-slaves": "swp_JJ_" + }, + "config_status": { + "bond-lacp-rate": "pass", + "bond-lacp-bypass-allow": "pass", + "bond-mode": "pass", + "bond-slaves": "pass" + }, + "status": "pass" + } +] diff --git a/tests/output/bridge1.ifquery.ac.json b/tests/output/bridge1.ifquery.ac.json new file mode 100644 index 00000000..71f40cba --- /dev/null +++ b/tests/output/bridge1.ifquery.ac.json @@ -0,0 +1,50 @@ +[ + { + "name": "lo", + "addr_method": "loopback", + "addr_family": "inet", + "auto": true, + "config": {}, + "config_status": {}, + "status": "pass" + }, + { + "name": "eth0", + "addr_method": "dhcp", + "addr_family": "inet", + "auto": true, + "config": { + "vrf": "mgmt" + }, + "config_status": { + "vrf": "pass" + }, + "status": "pass" + }, + { + "name": "mgmt", + "auto": true, + "config": { + "vrf-table": "1001" + }, + "config_status": { + "vrf-table": "pass" + }, + "status": "pass" + }, + { + "name": "bridge", + "auto": true, + "config": { + "bridge-vlan-aware": "yes", + "bridge-ports": "swp_AA_ swp_BB_", + "bridge-vids": "1-20" + }, + "config_status": { + "bridge-vlan-aware": "pass", + "bridge-ports": "pass", + "bridge-vids": "" + }, + "status": "pass" + } +] diff --git a/tests/output/bridge1.vlan.show.json b/tests/output/bridge1.vlan.show.json new file mode 100644 index 00000000..948288a1 --- /dev/null +++ b/tests/output/bridge1.vlan.show.json @@ -0,0 +1,48 @@ +[ + { + "ifname": "swp_AA_", + "vlans": [ + { + "vlan": 1, + "flags": [ + "PVID", + "Egress Untagged" + ] + }, + { + "vlan": 2, + "vlanEnd": 20, + "flags": [] + } + ] + }, + { + "ifname": "swp_BB_", + "vlans": [ + { + "vlan": 1, + "flags": [ + "PVID", + "Egress Untagged" + ] + }, + { + "vlan": 2, + "vlanEnd": 20, + "flags": [] + } + ] + }, + { + "ifname": "bridge", + "vlans": [ + { + "vlan": 1, + "flags": [ + "PVID", + "Egress Untagged" + ] + } + ] + } +] diff --git a/tests/output/bridge2.ifquery.ac.json b/tests/output/bridge2.ifquery.ac.json new file mode 100644 index 00000000..03c54af6 --- /dev/null +++ b/tests/output/bridge2.ifquery.ac.json @@ -0,0 +1,52 @@ +[ + { + "name": "lo", + "addr_method": "loopback", + "addr_family": "inet", + "auto": true, + "config": {}, + "config_status": {}, + "status": "pass" + }, + { + "name": "eth0", + "addr_method": "dhcp", + "addr_family": "inet", + "auto": true, + "config": { + "vrf": "mgmt" + }, + "config_status": { + "vrf": "pass" + }, + "status": "pass" + }, + { + "name": "mgmt", + "auto": true, + "config": { + "vrf-table": "1001" + }, + "config_status": { + "vrf-table": "pass" + }, + "status": "pass" + }, + { + "name": "bridge", + "auto": true, + "config": { + "bridge-vlan-aware": "yes", + "bridge-ports": "swp_AA_ swp_BB_", + "bridge-pvid": "2", + "bridge-vids": "1-20" + }, + "config_status": { + "bridge-vlan-aware": "pass", + "bridge-ports": "pass", + "bridge-pvid": "", + "bridge-vids": "" + }, + "status": "pass" + } +] diff --git a/tests/output/bridge2.vlan.show.json b/tests/output/bridge2.vlan.show.json new file mode 100644 index 00000000..bb2ccc7f --- /dev/null +++ b/tests/output/bridge2.vlan.show.json @@ -0,0 +1,54 @@ +[ + { + "ifname": "swp_AA_", + "vlans": [ + { + "vlan": 1 + }, + { + "vlan": 2, + "flags": [ + "PVID", + "Egress Untagged" + ] + }, + { + "vlan": 3, + "vlanEnd": 20, + "flags": [] + } + ] + }, + { + "ifname": "swp_BB_", + "vlans": [ + { + "vlan": 1 + }, + { + "vlan": 2, + "flags": [ + "PVID", + "Egress Untagged" + ] + }, + { + "vlan": 3, + "vlanEnd": 20, + "flags": [] + } + ] + }, + { + "ifname": "bridge", + "vlans": [ + { + "vlan": 1, + "flags": [ + "PVID", + "Egress Untagged" + ] + } + ] + } +] diff --git a/tests/output/bridge3.ifquery.ac.json b/tests/output/bridge3.ifquery.ac.json new file mode 100644 index 00000000..48436f49 --- /dev/null +++ b/tests/output/bridge3.ifquery.ac.json @@ -0,0 +1,74 @@ +[ + { + "name": "lo", + "addr_method": "loopback", + "addr_family": "inet", + "auto": true, + "config": {}, + "config_status": {}, + "status": "pass" + }, + { + "name": "eth0", + "addr_method": "dhcp", + "addr_family": "inet", + "auto": true, + "config": { + "vrf": "mgmt" + }, + "config_status": { + "vrf": "pass" + }, + "status": "pass" + }, + { + "name": "mgmt", + "auto": true, + "config": { + "vrf-table": "1001" + }, + "config_status": { + "vrf-table": "pass" + }, + "status": "pass" + }, + { + "name": "swp_AA_", + "auto": true, + "config": { + "bridge-access": "100" + }, + "config_status": { + "bridge-access": "pass" + }, + "status": "pass" + }, + { + "name": "swp_BB_", + "auto": true, + "config": { + "bridge-vids": "200" + }, + "config_status": { + "bridge-vids": "pass" + }, + "status": "pass" + }, + { + "name": "bridge", + "auto": true, + "config": { + "bridge-vlan-aware": "yes", + "bridge-ports": "swp_AA_ swp_BB_", + "bridge-pvid": "2", + "bridge-vids": "1-20" + }, + "config_status": { + "bridge-vlan-aware": "pass", + "bridge-ports": "pass", + "bridge-pvid": "", + "bridge-vids": "" + }, + "status": "pass" + } +] diff --git a/tests/output/bridge3.vlan.show.json b/tests/output/bridge3.vlan.show.json new file mode 100644 index 00000000..77c9a9a9 --- /dev/null +++ b/tests/output/bridge3.vlan.show.json @@ -0,0 +1,41 @@ +[ + { + "ifname": "swp_AA_", + "vlans": [ + { + "vlan": 100, + "flags": [ + "PVID", + "Egress Untagged" + ] + } + ] + }, + { + "ifname": "swp_BB_", + "vlans": [ + { + "vlan": 2, + "flags": [ + "PVID", + "Egress Untagged" + ] + }, + { + "vlan": 200 + } + ] + }, + { + "ifname": "bridge", + "vlans": [ + { + "vlan": 1, + "flags": [ + "PVID", + "Egress Untagged" + ] + } + ] + } +] diff --git a/tests/output/bridge4.ifquery.ac.json b/tests/output/bridge4.ifquery.ac.json new file mode 100644 index 00000000..216853c7 --- /dev/null +++ b/tests/output/bridge4.ifquery.ac.json @@ -0,0 +1,76 @@ +[ + { + "name": "lo", + "addr_method": "loopback", + "addr_family": "inet", + "auto": true, + "config": {}, + "config_status": {}, + "status": "pass" + }, + { + "name": "eth0", + "addr_method": "dhcp", + "addr_family": "inet", + "auto": true, + "config": { + "vrf": "mgmt" + }, + "config_status": { + "vrf": "pass" + }, + "status": "pass" + }, + { + "name": "mgmt", + "auto": true, + "config": { + "vrf-table": "1001" + }, + "config_status": { + "vrf-table": "pass" + }, + "status": "pass" + }, + { + "name": "swp_AA_", + "auto": true, + "config": { + "bridge-pvid": "4" + }, + "config_status": { + "bridge-pvid": "pass" + }, + "status": "pass" + }, + { + "name": "swp_BB_", + "auto": true, + "config": { + "bridge-vids": "40", + "bridge-pvid": "3" + }, + "config_status": { + "bridge-vids": "pass", + "bridge-pvid": "pass" + }, + "status": "pass" + }, + { + "name": "bridge", + "auto": true, + "config": { + "bridge-vlan-aware": "yes", + "bridge-ports": "swp_AA_ swp_BB_", + "bridge-vids": "1-20", + "bridge-pvid": "2" + }, + "config_status": { + "bridge-vlan-aware": "pass", + "bridge-ports": "pass", + "bridge-vids": "", + "bridge-pvid": "" + }, + "status": "pass" + } +] diff --git a/tests/output/bridge4.vlan.show.json b/tests/output/bridge4.vlan.show.json new file mode 100644 index 00000000..f32f3c2b --- /dev/null +++ b/tests/output/bridge4.vlan.show.json @@ -0,0 +1,51 @@ +[ + { + "ifname": "swp_AA_", + "vlans": [ + { + "vlan": 1, + "vlanEnd": 3, + "flags": [] + }, + { + "vlan": 4, + "flags": [ + "PVID", + "Egress Untagged" + ] + }, + { + "vlan": 5, + "vlanEnd": 20, + "flags": [] + } + ] + }, + { + "ifname": "swp_BB_", + "vlans": [ + { + "vlan": 3, + "flags": [ + "PVID", + "Egress Untagged" + ] + }, + { + "vlan": 40 + } + ] + }, + { + "ifname": "bridge", + "vlans": [ + { + "vlan": 1, + "flags": [ + "PVID", + "Egress Untagged" + ] + } + ] + } +] diff --git a/tests/output/bridge5.ifquery.ac.json b/tests/output/bridge5.ifquery.ac.json new file mode 100644 index 00000000..27b56463 --- /dev/null +++ b/tests/output/bridge5.ifquery.ac.json @@ -0,0 +1,64 @@ +[ + { + "name": "lo", + "addr_method": "loopback", + "addr_family": "inet", + "auto": true, + "config": {}, + "config_status": {}, + "status": "pass" + }, + { + "name": "eth0", + "addr_method": "dhcp", + "addr_family": "inet", + "auto": true, + "config": { + "vrf": "mgmt" + }, + "config_status": { + "vrf": "pass" + }, + "status": "pass" + }, + { + "name": "mgmt", + "auto": true, + "config": { + "vrf-table": "1001" + }, + "config_status": { + "vrf-table": "pass" + }, + "status": "pass" + }, + { + "name": "swp_AA_", + "auto": true, + "config": {}, + "config_status": {}, + "status": "pass" + }, + { + "name": "swp_BB_", + "auto": true, + "config": {}, + "config_status": {}, + "status": "pass" + }, + { + "name": "bridge", + "auto": true, + "config": { + "bridge-vlan-aware": "yes", + "bridge-ports": "swp_AA_ swp_BB_", + "bridge-vids": "1-200" + }, + "config_status": { + "bridge-vlan-aware": "pass", + "bridge-ports": "pass", + "bridge-vids": "" + }, + "status": "pass" + } +] \ No newline at end of file diff --git a/tests/output/bridge5.vlan.show.json b/tests/output/bridge5.vlan.show.json new file mode 100644 index 00000000..a5be1b4a --- /dev/null +++ b/tests/output/bridge5.vlan.show.json @@ -0,0 +1,48 @@ +[ + { + "ifname": "swp_AA_", + "vlans": [ + { + "vlan": 1, + "flags": [ + "PVID", + "Egress Untagged" + ] + }, + { + "vlan": 2, + "vlanEnd": 200, + "flags": [] + } + ] + }, + { + "ifname": "swp_BB_", + "vlans": [ + { + "vlan": 1, + "flags": [ + "PVID", + "Egress Untagged" + ] + }, + { + "vlan": 2, + "vlanEnd": 200, + "flags": [] + } + ] + }, + { + "ifname": "bridge", + "vlans": [ + { + "vlan": 1, + "flags": [ + "PVID", + "Egress Untagged" + ] + } + ] + } +] diff --git a/tests/output/bridge6_multiple_bridge_ports_lines.ifquery.ac.json b/tests/output/bridge6_multiple_bridge_ports_lines.ifquery.ac.json new file mode 100644 index 00000000..17625a16 --- /dev/null +++ b/tests/output/bridge6_multiple_bridge_ports_lines.ifquery.ac.json @@ -0,0 +1,50 @@ +[ + { + "name": "lo", + "addr_method": "loopback", + "addr_family": "inet", + "auto": true, + "config": {}, + "config_status": {}, + "status": "pass" + }, + { + "name": "eth0", + "addr_method": "dhcp", + "addr_family": "inet", + "auto": true, + "config": { + "vrf": "mgmt" + }, + "config_status": { + "vrf": "pass" + }, + "status": "pass" + }, + { + "name": "mgmt", + "auto": true, + "config": { + "vrf-table": "1001" + }, + "config_status": { + "vrf-table": "pass" + }, + "status": "pass" + }, + { + "name": "bridge", + "auto": true, + "config": { + "bridge-vlan-aware": "yes", + "bridge-ports": "swp_AA_ swp_BB_ swp_CC_ swp_DD_", + "bridge-stp": "yes" + }, + "config_status": { + "bridge-vlan-aware": "pass", + "bridge-ports": "pass", + "bridge-stp": "pass" + }, + "status": "pass" + } +] diff --git a/tests/output/bridge7_macvlans.ifquery.ac.json b/tests/output/bridge7_macvlans.ifquery.ac.json new file mode 100644 index 00000000..678fe5e7 --- /dev/null +++ b/tests/output/bridge7_macvlans.ifquery.ac.json @@ -0,0 +1,406 @@ +[ + { + "name": "lo", + "addr_method": "loopback", + "addr_family": "inet", + "auto": true, + "config": {}, + "config_status": {}, + "status": "pass" + }, + { + "name": "eth0", + "addr_method": "dhcp", + "addr_family": "inet", + "auto": true, + "config": { + "vrf": "mgmt" + }, + "config_status": { + "vrf": "pass" + }, + "status": "pass" + }, + { + "name": "mgmt", + "auto": true, + "config": { + "vrf-table": "1001" + }, + "config_status": { + "vrf-table": "pass" + }, + "status": "pass" + }, + { + "name": "bridge", + "auto": true, + "config": { + "bridge-vlan-aware": "yes", + "bridge-vids": "1-33", + "bridge-stp": "yes", + "bridge-pvid": "192", + "bridge-ports": "swp_AA_" + }, + "config_status": { + "bridge-vlan-aware": "pass", + "bridge-vids": "", + "bridge-stp": "pass", + "bridge-pvid": "", + "bridge-ports": "pass" + }, + "status": "pass" + }, + { + "name": "bridge.1", + "auto": true, + "config": { + "address-virtual": "42:38:39:FF:00:1 10.7.192.1/1" + }, + "config_status": { + "address-virtual": "pass" + }, + "status": "pass" + }, + { + "name": "bridge.2", + "auto": true, + "config": { + "address-virtual": "42:38:39:ff:00:2 10.7.192.2/2" + }, + "config_status": { + "address-virtual": "pass" + }, + "status": "pass" + }, + { + "name": "bridge.3", + "auto": true, + "config": { + "address-virtual": "42:38:39:ff:00:3 10.7.192.3/3" + }, + "config_status": { + "address-virtual": "pass" + }, + "status": "pass" + }, + { + "name": "bridge.4", + "auto": true, + "config": { + "address-virtual": "42:38:39:ff:00:4 10.7.192.4/4" + }, + "config_status": { + "address-virtual": "pass" + }, + "status": "pass" + }, + { + "name": "bridge.5", + "auto": true, + "config": { + "address-virtual": "42:38:39:ff:00:5 10.7.192.5/5" + }, + "config_status": { + "address-virtual": "pass" + }, + "status": "pass" + }, + { + "name": "bridge.6", + "auto": true, + "config": { + "address-virtual": "42:38:39:ff:00:6 10.7.192.6/6" + }, + "config_status": { + "address-virtual": "pass" + }, + "status": "pass" + }, + { + "name": "bridge.7", + "auto": true, + "config": { + "address-virtual": "42:38:39:ff:00:7 10.7.192.7/7" + }, + "config_status": { + "address-virtual": "pass" + }, + "status": "pass" + }, + { + "name": "bridge.8", + "auto": true, + "config": { + "address-virtual": "42:38:39:ff:00:8 10.7.192.8/8" + }, + "config_status": { + "address-virtual": "pass" + }, + "status": "pass" + }, + { + "name": "bridge.9", + "auto": true, + "config": { + "address-virtual": "42:38:39:ff:00:9 10.7.192.9/9" + }, + "config_status": { + "address-virtual": "pass" + }, + "status": "pass" + }, + { + "name": "bridge.10", + "auto": true, + "config": { + "address-virtual": "42:38:39:ff:00:10 10.7.192.10/10" + }, + "config_status": { + "address-virtual": "pass" + }, + "status": "pass" + }, + { + "name": "bridge.11", + "auto": true, + "config": { + "address-virtual": "42:38:39:ff:00:11 10.7.192.11/11" + }, + "config_status": { + "address-virtual": "pass" + }, + "status": "pass" + }, + { + "name": "bridge.12", + "auto": true, + "config": { + "address-virtual": "42:38:39:ff:00:12 10.7.192.12/12" + }, + "config_status": { + "address-virtual": "pass" + }, + "status": "pass" + }, + { + "name": "bridge.13", + "auto": true, + "config": { + "address-virtual": "42:38:39:ff:00:13 10.7.192.13/13" + }, + "config_status": { + "address-virtual": "pass" + }, + "status": "pass" + }, + { + "name": "bridge.14", + "auto": true, + "config": { + "address-virtual": "42:38:39:ff:00:14 10.7.192.14/14" + }, + "config_status": { + "address-virtual": "pass" + }, + "status": "pass" + }, + { + "name": "bridge.15", + "auto": true, + "config": { + "address-virtual": "42:38:39:ff:00:15 10.7.192.15/15" + }, + "config_status": { + "address-virtual": "pass" + }, + "status": "pass" + }, + { + "name": "bridge.16", + "auto": true, + "config": { + "address-virtual": "42:38:39:ff:00:16 10.7.192.16/16" + }, + "config_status": { + "address-virtual": "pass" + }, + "status": "pass" + }, + { + "name": "bridge.17", + "auto": true, + "config": { + "address-virtual": "42:38:39:ff:00:17 10.7.192.17/17" + }, + "config_status": { + "address-virtual": "pass" + }, + "status": "pass" + }, + { + "name": "bridge.18", + "auto": true, + "config": { + "address-virtual": "42:38:39:ff:00:18 10.7.192.18/18" + }, + "config_status": { + "address-virtual": "pass" + }, + "status": "pass" + }, + { + "name": "bridge.19", + "auto": true, + "config": { + "address-virtual": "42:38:39:ff:00:19 10.7.192.19/19" + }, + "config_status": { + "address-virtual": "pass" + }, + "status": "pass" + }, + { + "name": "bridge.20", + "auto": true, + "config": { + "address-virtual": "42:38:39:ff:00:20 10.7.192.20/20" + }, + "config_status": { + "address-virtual": "pass" + }, + "status": "pass" + }, + { + "name": "bridge.21", + "auto": true, + "config": { + "address-virtual": "42:38:39:ff:00:21 10.7.192.21/21" + }, + "config_status": { + "address-virtual": "pass" + }, + "status": "pass" + }, + { + "name": "bridge.22", + "auto": true, + "config": { + "address-virtual": "42:38:39:ff:00:22 10.7.192.22/22" + }, + "config_status": { + "address-virtual": "pass" + }, + "status": "pass" + }, + { + "name": "bridge.23", + "auto": true, + "config": { + "address-virtual": "42:38:39:ff:00:23 10.7.192.23/23" + }, + "config_status": { + "address-virtual": "pass" + }, + "status": "pass" + }, + { + "name": "bridge.24", + "auto": true, + "config": { + "address-virtual": "42:38:39:ff:00:24 10.7.192.24/24" + }, + "config_status": { + "address-virtual": "pass" + }, + "status": "pass" + }, + { + "name": "bridge.25", + "auto": true, + "config": { + "address-virtual": "42:38:39:ff:00:25 10.7.192.25/25" + }, + "config_status": { + "address-virtual": "pass" + }, + "status": "pass" + }, + { + "name": "bridge.26", + "auto": true, + "config": { + "address-virtual": "42:38:39:ff:00:26 10.7.192.26/26" + }, + "config_status": { + "address-virtual": "pass" + }, + "status": "pass" + }, + { + "name": "bridge.27", + "auto": true, + "config": { + "address-virtual": "42:38:39:ff:00:27 10.7.192.27/27" + }, + "config_status": { + "address-virtual": "pass" + }, + "status": "pass" + }, + { + "name": "bridge.28", + "auto": true, + "config": { + "address-virtual": "42:38:39:ff:00:28 10.7.192.28/28" + }, + "config_status": { + "address-virtual": "pass" + }, + "status": "pass" + }, + { + "name": "bridge.29", + "auto": true, + "config": { + "address-virtual": "42:38:39:ff:00:29 10.7.192.29/29" + }, + "config_status": { + "address-virtual": "pass" + }, + "status": "pass" + }, + { + "name": "bridge.30", + "auto": true, + "config": { + "address-virtual": "42:38:39:ff:00:30 10.7.192.30/30" + }, + "config_status": { + "address-virtual": "pass" + }, + "status": "pass" + }, + { + "name": "bridge.31", + "auto": true, + "config": { + "address-virtual": "42:38:39:ff:00:31 10.7.192.31/31" + }, + "config_status": { + "address-virtual": "pass" + }, + "status": "pass" + }, + { + "name": "bridge.32", + "auto": true, + "config": { + "address-virtual": "42:38:39:ff:00:32 10.7.192.32/32" + }, + "config_status": { + "address-virtual": "pass" + }, + "status": "pass" + } +] diff --git a/tests/output/bridge_access.ifquery.ac.json b/tests/output/bridge_access.ifquery.ac.json new file mode 100644 index 00000000..4625b9ce --- /dev/null +++ b/tests/output/bridge_access.ifquery.ac.json @@ -0,0 +1,78 @@ +[ + { + "name": "lo", + "addr_method": "loopback", + "addr_family": "inet", + "auto": true, + "config": {}, + "config_status": {}, + "status": "pass" + }, + { + "name": "eth0", + "addr_method": "dhcp", + "addr_family": "inet", + "auto": true, + "config": { + "vrf": "mgmt" + }, + "config_status": { + "vrf": "pass" + }, + "status": "pass" + }, + { + "name": "mgmt", + "auto": true, + "config": { + "vrf-table": "1001" + }, + "config_status": { + "vrf-table": "pass" + }, + "status": "pass" + }, + { + "name": "vxlan1", + "auto": true, + "config": { + "vxlan-id": "1", + "bridge-access": "1", + "vxlan-local-tunnelip": "27.0.0.11" + }, + "config_status": { + "vxlan-id": "pass", + "bridge-access": "pass", + "vxlan-local-tunnelip": "pass" + }, + "status": "pass" + }, + { + "name": "vxlan500", + "auto": true, + "config": { + "vxlan-id": "500", + "bridge-access": "500", + "vxlan-local-tunnelip": "27.0.0.11" + }, + "config_status": { + "vxlan-id": "pass", + "bridge-access": "pass", + "vxlan-local-tunnelip": "pass" + }, + "status": "pass" + }, + { + "name": "br0", + "auto": true, + "config": { + "bridge-vlan-aware": "yes", + "bridge-ports": "vxlan1 vxlan500" + }, + "config_status": { + "bridge-vlan-aware": "pass", + "bridge-ports": "pass" + }, + "status": "pass" + } +] diff --git a/tests/output/bridge_access.vlan.show.json b/tests/output/bridge_access.vlan.show.json new file mode 100644 index 00000000..95932ce6 --- /dev/null +++ b/tests/output/bridge_access.vlan.show.json @@ -0,0 +1,38 @@ +[ + { + "ifname": "vxlan1", + "vlans": [ + { + "vlan": 1, + "flags": [ + "PVID", + "Egress Untagged" + ] + } + ] + }, + { + "ifname": "vxlan500", + "vlans": [ + { + "vlan": 500, + "flags": [ + "PVID", + "Egress Untagged" + ] + } + ] + }, + { + "ifname": "br0", + "vlans": [ + { + "vlan": 1, + "flags": [ + "PVID", + "Egress Untagged" + ] + } + ] + } +] diff --git a/tests/output/bridge_attr_back_to_default.after.ifquery.ac.json b/tests/output/bridge_attr_back_to_default.after.ifquery.ac.json new file mode 100644 index 00000000..6a464c67 --- /dev/null +++ b/tests/output/bridge_attr_back_to_default.after.ifquery.ac.json @@ -0,0 +1,58 @@ +[ + { + "name": "lo", + "addr_method": "loopback", + "addr_family": "inet", + "auto": true, + "config": {}, + "config_status": {}, + "status": "pass" + }, + { + "name": "eth0", + "addr_method": "dhcp", + "addr_family": "inet", + "auto": true, + "config": { + "vrf": "mgmt" + }, + "config_status": { + "vrf": "pass" + }, + "status": "pass" + }, + { + "name": "mgmt", + "auto": true, + "config": { + "vrf-table": "1001" + }, + "config_status": { + "vrf-table": "pass" + }, + "status": "pass" + }, + { + "name": "br0", + "auto": true, + "config": { + "bridge-ports": "vx42" + }, + "config_status": { + "bridge-ports": "pass" + }, + "status": "pass" + }, + { + "name": "vx42", + "auto": true, + "config": { + "vxlan-id": "42" + }, + "config_status": { + "vxlan-id": "pass" + }, + "status": "pass" + } +] + diff --git a/tests/output/bridge_attr_back_to_default.ifquery.ac.json b/tests/output/bridge_attr_back_to_default.ifquery.ac.json new file mode 100644 index 00000000..fa79f451 --- /dev/null +++ b/tests/output/bridge_attr_back_to_default.ifquery.ac.json @@ -0,0 +1,127 @@ +[ + { + "name": "lo", + "addr_method": "loopback", + "addr_family": "inet", + "auto": true, + "config": {}, + "config_status": {}, + "status": "pass" + }, + { + "name": "eth0", + "addr_method": "dhcp", + "addr_family": "inet", + "auto": true, + "config": { + "vrf": "mgmt" + }, + "config_status": { + "vrf": "pass" + }, + "status": "pass" + }, + { + "name": "mgmt", + "auto": true, + "config": { + "vrf-table": "1001" + }, + "config_status": { + "vrf-table": "pass" + }, + "status": "pass" + }, + { + "name": "br0", + "auto": true, + "config": { + "bridge-mcqifaddr": "yes", + "bridge-vlan-protocol": "802.1ad", + "bridge-mcsqc": "42", + "bridge-stp": "no", + "bridge-mcsqi": "42", + "bridge-arp-nd-suppress": "vx42=on", + "bridge-mcmi": "42", + "bridge-ports": "vx42", + "bridge-l2protocol-tunnel": "all", + "bridge-unicast-flood": "vx42=off", + "bridge-mcsnoop": "no", + "bridge-vlan-aware": "yes", + "bridge-pathcosts": "vx42=42", + "bridge-portprios": "vx42=10", + "bridge-fd": "29", + "bridge-ageing": "42", + "bridge-multicast-flood": "vx42=off", + "bridge-hello": "10", + "bridge-mcquerier": "yes", + "bridge-mclmc": "42", + "bridge-mcrouter": "no", + "bridge-vlan-stats": "off", + "bridge-mclmi": "42", + "bridge-hashmax": "1024", + "bridge-mcqri": "42", + "bridge-mcqpi": "42", + "bridge-bridgeprio": "42", + "bridge-maxage": "10", + "bridge-igmp-version": "3", + "bridge-mld-version": "2", + "bridge-mcqv4src": "100=172.16.100.1 200=10.10.10.1", + "bridge-learning": "vx42=off", + "bridge-mcstats": "off", + "bridge-portmcfl": "vx42=yes", + "bridge-mcqi": "42" + }, + "config_status": { + "bridge-mcqifaddr": "pass", + "bridge-vlan-protocol": "pass", + "bridge-mcsqc": "pass", + "bridge-stp": "pass", + "bridge-mcsqi": "pass", + "bridge-arp-nd-suppress": "pass", + "bridge-mcmi": "pass", + "bridge-ports": "pass", + "bridge-l2protocol-tunnel": "pass", + "bridge-unicast-flood": "pass", + "bridge-mcsnoop": "pass", + "bridge-vlan-aware": "pass", + "bridge-pathcosts": "pass", + "bridge-portprios": "pass", + "bridge-fd": "pass", + "bridge-ageing": "pass", + "bridge-multicast-flood": "pass", + "bridge-hello": "pass", + "bridge-mcquerier": "pass", + "bridge-mclmc": "pass", + "bridge-mcrouter": "pass", + "bridge-vlan-stats": "pass", + "bridge-mclmi": "pass", + "bridge-hashmax": "pass", + "bridge-mcqri": "pass", + "bridge-mcqpi": "pass", + "bridge-bridgeprio": "pass", + "bridge-maxage": "pass", + "bridge-igmp-version": "pass", + "bridge-mld-version": "pass", + "bridge-mcqv4src": "pass", + "bridge-learning": "pass", + "bridge-mcstats": "pass", + "bridge-portmcfl": "pass", + "bridge-mcqi": "pass" + }, + "status": "pass" + }, + { + "name": "vx42", + "auto": true, + "config": { + "vxlan-id": "42", + "bridge-access": "42" + }, + "config_status": { + "vxlan-id": "pass", + "bridge-access": "pass" + }, + "status": "pass" + } +] diff --git a/tests/output/bridge_igmp_version.ifquery.ac.json b/tests/output/bridge_igmp_version.ifquery.ac.json new file mode 100644 index 00000000..9e1a68b2 --- /dev/null +++ b/tests/output/bridge_igmp_version.ifquery.ac.json @@ -0,0 +1,78 @@ +[ + { + "name": "lo", + "addr_method": "loopback", + "addr_family": "inet", + "auto": true, + "config": {}, + "config_status": {}, + "status": "pass" + }, + { + "name": "eth0", + "addr_method": "dhcp", + "addr_family": "inet", + "auto": true, + "config": { + "vrf": "mgmt" + }, + "config_status": { + "vrf": "pass" + }, + "status": "pass" + }, + { + "name": "mgmt", + "auto": true, + "config": { + "vrf-table": "1001" + }, + "config_status": { + "vrf-table": "pass" + }, + "status": "pass" + }, + { + "name": "br0", + "auto": true, + "config": { + "bridge-igmp-version": "3", + "bridge-ports": "swp_AA_", + "bridge-vlan-aware": "yes", + "bridge-mld-version": "2" + }, + "config_status": { + "bridge-igmp-version": "pass", + "bridge-ports": "pass", + "bridge-vlan-aware": "pass", + "bridge-mld-version": "pass" + }, + "status": "pass" + }, + { + "name": "br1", + "auto": true, + "config": { + "bridge-mld-version": "2", + "bridge-igmp-version": "3", + "bridge-ports": "swp_BB_" + }, + "config_status": { + "bridge-mld-version": "pass", + "bridge-igmp-version": "pass", + "bridge-ports": "pass" + }, + "status": "pass" + }, + { + "name": "br2", + "auto": true, + "config": { + "bridge-ports": "swp_CC_" + }, + "config_status": { + "bridge-ports": "pass" + }, + "status": "pass" + } +] diff --git a/tests/output/bridge_l2protocol_tunnel.ifquery.ac.json b/tests/output/bridge_l2protocol_tunnel.ifquery.ac.json new file mode 100644 index 00000000..ad4b7142 --- /dev/null +++ b/tests/output/bridge_l2protocol_tunnel.ifquery.ac.json @@ -0,0 +1,83 @@ +[ + { + "name": "lo", + "addr_method": "loopback", + "addr_family": "inet", + "auto": true, + "config": {}, + "config_status": {}, + "status": "pass" + }, + { + "name": "eth0", + "addr_method": "dhcp", + "addr_family": "inet", + "auto": true, + "config": { + "vrf": "mgmt" + }, + "config_status": { + "vrf": "pass" + }, + "status": "pass" + }, + { + "name": "mgmt", + "auto": true, + "config": { + "vrf-table": "1001" + }, + "config_status": { + "vrf-table": "pass" + }, + "status": "pass" + }, + { + "name": "br_vlan_unaware", + "auto": true, + "config": { + "bridge-ports": "swp_AA_ swp_BB_ swp_CC_ swp_DD_ swp_EE_", + "bridge-l2protocol-tunnel": "swp_AA_=lacp,pvst swp_BB_=stp,cdp swp_CC_=all swp_DD_=lldp" + }, + "config_status": { + "bridge-ports": "pass", + "bridge-l2protocol-tunnel": "pass" + }, + "status": "pass" + }, + { + "name": "br_vlan_aware", + "auto": true, + "config": { + "bridge-vlan-aware": "yes", + "bridge-ports": "swp_FF_ swp_GG_" + }, + "config_status": { + "bridge-vlan-aware": "pass", + "bridge-ports": "pass" + }, + "status": "pass" + }, + { + "name": "swp_GG_", + "auto": true, + "config": { + "bridge-l2protocol-tunnel": "all" + }, + "config_status": { + "bridge-l2protocol-tunnel": "pass" + }, + "status": "pass" + }, + { + "name": "swp_FF_", + "auto": true, + "config": { + "bridge-l2protocol-tunnel": "stp lacp lldp,pvst cdp" + }, + "config_status": { + "bridge-l2protocol-tunnel": "pass" + }, + "status": "pass" + } +] diff --git a/tests/output/bridge_new_attribute_igmp_mld.after.ifquery.ac.json b/tests/output/bridge_new_attribute_igmp_mld.after.ifquery.ac.json new file mode 100644 index 00000000..b562b8e9 --- /dev/null +++ b/tests/output/bridge_new_attribute_igmp_mld.after.ifquery.ac.json @@ -0,0 +1,59 @@ +[ + { + "name": "lo", + "addr_method": "loopback", + "addr_family": "inet", + "auto": true, + "config": {}, + "config_status": {}, + "status": "pass" + }, + { + "name": "eth0", + "addr_method": "dhcp", + "addr_family": "inet", + "auto": true, + "config": { + "vrf": "mgmt" + }, + "config_status": { + "vrf": "pass" + }, + "status": "pass" + }, + { + "name": "mgmt", + "auto": true, + "config": { + "vrf-table": "1001" + }, + "config_status": { + "vrf-table": "pass" + }, + "status": "pass" + }, + { + "name": "br0", + "auto": true, + "config": { + "bridge-vlan-aware": "yes", + "bridge-ports": "swp_AA_" + }, + "config_status": { + "bridge-vlan-aware": "pass", + "bridge-ports": "pass" + }, + "status": "pass" + }, + { + "name": "br1", + "auto": true, + "config": { + "bridge-ports": "swp_BB_" + }, + "config_status": { + "bridge-ports": "pass" + }, + "status": "pass" + } +] diff --git a/tests/output/bridge_new_attribute_igmp_mld.before.ifquery.ac.json b/tests/output/bridge_new_attribute_igmp_mld.before.ifquery.ac.json new file mode 100644 index 00000000..65d12c88 --- /dev/null +++ b/tests/output/bridge_new_attribute_igmp_mld.before.ifquery.ac.json @@ -0,0 +1,67 @@ +[ + { + "name": "lo", + "addr_method": "loopback", + "addr_family": "inet", + "auto": true, + "config": {}, + "config_status": {}, + "status": "pass" + }, + { + "name": "eth0", + "addr_method": "dhcp", + "addr_family": "inet", + "auto": true, + "config": { + "vrf": "mgmt" + }, + "config_status": { + "vrf": "pass" + }, + "status": "pass" + }, + { + "name": "mgmt", + "auto": true, + "config": { + "vrf-table": "1001" + }, + "config_status": { + "vrf-table": "pass" + }, + "status": "pass" + }, + { + "name": "br0", + "auto": true, + "config": { + "bridge-igmp-version": "3", + "bridge-ports": "swp_AA_", + "bridge-vlan-aware": "yes", + "bridge-mld-version": "2" + }, + "config_status": { + "bridge-igmp-version": "pass", + "bridge-ports": "pass", + "bridge-vlan-aware": "pass", + "bridge-mld-version": "pass" + }, + "status": "pass" + }, + { + "name": "br1", + "auto": true, + "config": { + "bridge-mld-version": "2", + "bridge-igmp-version": "3", + "bridge-ports": "swp_BB_" + }, + "config_status": { + "bridge-mld-version": "pass", + "bridge-igmp-version": "pass", + "bridge-ports": "pass" + }, + "status": "pass" + } +] diff --git a/tests/output/bridge_new_attribute_ipforward_vlan_protocol_mcstats.after.ifquery.ac.json b/tests/output/bridge_new_attribute_ipforward_vlan_protocol_mcstats.after.ifquery.ac.json new file mode 100644 index 00000000..611a5497 --- /dev/null +++ b/tests/output/bridge_new_attribute_ipforward_vlan_protocol_mcstats.after.ifquery.ac.json @@ -0,0 +1,88 @@ +[ + { + "name": "lo", + "addr_method": "loopback", + "addr_family": "inet", + "auto": true, + "config": {}, + "config_status": {}, + "status": "pass" + }, + { + "name": "eth0", + "addr_method": "dhcp", + "addr_family": "inet", + "auto": true, + "config": { + "vrf": "mgmt" + }, + "config_status": { + "vrf": "pass" + }, + "status": "pass" + }, + { + "name": "mgmt", + "auto": true, + "config": { + "vrf-table": "1001" + }, + "config_status": { + "vrf-table": "pass" + }, + "status": "pass" + }, + { + "name": "swp_AA_", + "auto": true, + "config": {}, + "config_status": {}, + "status": "pass" + }, + { + "name": "swp_BB_", + "auto": true, + "config": {}, + "config_status": {}, + "status": "pass" + }, + { + "name": "br2", + "auto": true, + "config": { + "ip-forward": "on", + "bridge-ports": "swp_DD_ swp_EE_", + "address": "6.0.0.2/32" + }, + "config_status": { + "ip-forward": "pass", + "bridge-ports": "pass", + "address": "pass" + }, + "status": "pass" + }, + { + "name": "vlan100", + "auto": true, + "config": { + "vlan-id": "100", + "vlan-raw-device": "swp_CC_" + }, + "config_status": { + "vlan-id": "pass", + "vlan-raw-device": "pass" + }, + "status": "pass" + }, + { + "name": "br3", + "auto": true, + "config": { + "bridge-ports": "swp_FF_" + }, + "config_status": { + "bridge-ports": "pass" + }, + "status": "pass" + } +] diff --git a/tests/output/bridge_new_attribute_ipforward_vlan_protocol_mcstats.before.ifquery.ac.json b/tests/output/bridge_new_attribute_ipforward_vlan_protocol_mcstats.before.ifquery.ac.json new file mode 100644 index 00000000..f863be00 --- /dev/null +++ b/tests/output/bridge_new_attribute_ipforward_vlan_protocol_mcstats.before.ifquery.ac.json @@ -0,0 +1,100 @@ +[ + { + "name": "lo", + "addr_method": "loopback", + "addr_family": "inet", + "auto": true, + "config": {}, + "config_status": {}, + "status": "pass" + }, + { + "name": "eth0", + "addr_method": "dhcp", + "addr_family": "inet", + "auto": true, + "config": { + "vrf": "mgmt" + }, + "config_status": { + "vrf": "pass" + }, + "status": "pass" + }, + { + "name": "mgmt", + "auto": true, + "config": { + "vrf-table": "1001" + }, + "config_status": { + "vrf-table": "pass" + }, + "status": "pass" + }, + { + "name": "swp_AA_", + "auto": true, + "config": { + "ip-forward": "off" + }, + "config_status": { + "ip-forward": "pass" + }, + "status": "pass" + }, + { + "name": "swp_BB_", + "auto": true, + "config": { + "ip6-forward": "off" + }, + "config_status": { + "ip6-forward": "pass" + }, + "status": "pass" + }, + { + "name": "br2", + "auto": true, + "config": { + "bridge-ports": "swp_DD_ swp_EE_", + "bridge-mcstats": "off", + "ip-forward": "on" + }, + "config_status": { + "bridge-ports": "pass", + "bridge-mcstats": "pass", + "ip-forward": "pass" + }, + "status": "pass" + }, + { + "name": "vlan100", + "auto": true, + "config": { + "vlan-raw-device": "swp_CC_", + "vlan-id": "100", + "vlan-protocol": "802.1ad" + }, + "config_status": { + "vlan-raw-device": "pass", + "vlan-id": "pass", + "vlan-protocol": "pass" + }, + "status": "pass" + }, + { + "name": "br3", + "auto": true, + "config": { + "bridge-ports": "swp_FF_", + "bridge-mcstats": "off" + }, + "config_status": { + "bridge-ports": "pass", + "bridge-mcstats": "pass" + }, + "status": "pass" + } +] diff --git a/tests/output/bridge_new_attribute_learning_arp_nd_suppress.after.ifquery.ac.json b/tests/output/bridge_new_attribute_learning_arp_nd_suppress.after.ifquery.ac.json new file mode 100644 index 00000000..a1e36c10 --- /dev/null +++ b/tests/output/bridge_new_attribute_learning_arp_nd_suppress.after.ifquery.ac.json @@ -0,0 +1,91 @@ +[ + { + "name": "lo", + "addr_method": "loopback", + "addr_family": "inet", + "auto": true, + "config": {}, + "config_status": {}, + "status": "pass" + }, + { + "name": "eth0", + "addr_method": "dhcp", + "addr_family": "inet", + "auto": true, + "config": { + "vrf": "mgmt" + }, + "config_status": { + "vrf": "pass" + }, + "status": "pass" + }, + { + "name": "mgmt", + "auto": true, + "config": { + "vrf-table": "1001" + }, + "config_status": { + "vrf-table": "pass" + }, + "status": "pass" + }, + { + "name": "vxlan10101", + "auto": true, + "config": { + "vxlan-id": "10101", + "bridge-access": "101", + "vxlan-local-tunnelip": "6.0.0.17" + }, + "config_status": { + "vxlan-id": "pass", + "bridge-access": "pass", + "vxlan-local-tunnelip": "pass" + }, + "status": "pass" + }, + { + "name": "br1", + "auto": true, + "config": { + "bridge-vlan-aware": "yes", + "bridge-ports": "vxlan10101", + "bridge-vids": "101" + }, + "config_status": { + "bridge-vlan-aware": "pass", + "bridge-ports": "pass", + "bridge-vids": "" + }, + "status": "pass" + }, + { + "name": "vxlan10102", + "auto": true, + "config": { + "vxlan-id": "10102", + "vxlan-local-tunnelip": "6.0.0.17" + }, + "config_status": { + "vxlan-id": "pass", + "vxlan-local-tunnelip": "pass" + }, + "status": "pass" + }, + { + "name": "br2", + "auto": true, + "config": { + "bridge-ports": "vxlan10102", + "bridge-vids": "101" + }, + "config_status": { + "bridge-ports": "pass", + "bridge-vids": "" + }, + "status": "pass" + } +] diff --git a/tests/output/bridge_new_attribute_learning_arp_nd_suppress.before.ifquery.ac.json b/tests/output/bridge_new_attribute_learning_arp_nd_suppress.before.ifquery.ac.json new file mode 100644 index 00000000..874bb0e9 --- /dev/null +++ b/tests/output/bridge_new_attribute_learning_arp_nd_suppress.before.ifquery.ac.json @@ -0,0 +1,99 @@ +[ + { + "name": "lo", + "addr_method": "loopback", + "addr_family": "inet", + "auto": true, + "config": {}, + "config_status": {}, + "status": "pass" + }, + { + "name": "eth0", + "addr_method": "dhcp", + "addr_family": "inet", + "auto": true, + "config": { + "vrf": "mgmt" + }, + "config_status": { + "vrf": "pass" + }, + "status": "pass" + }, + { + "name": "mgmt", + "auto": true, + "config": { + "vrf-table": "1001" + }, + "config_status": { + "vrf-table": "pass" + }, + "status": "pass" + }, + { + "name": "vxlan10101", + "auto": true, + "config": { + "bridge-arp-nd-suppress": "on", + "bridge-learning": "off", + "vxlan-id": "10101", + "bridge-access": "101", + "vxlan-local-tunnelip": "6.0.0.17" + }, + "config_status": { + "bridge-arp-nd-suppress": "pass", + "bridge-learning": "pass", + "vxlan-id": "pass", + "bridge-access": "pass", + "vxlan-local-tunnelip": "pass" + }, + "status": "pass" + }, + { + "name": "br1", + "auto": true, + "config": { + "bridge-vlan-aware": "yes", + "bridge-ports": "vxlan10101", + "bridge-vids": "101" + }, + "config_status": { + "bridge-vlan-aware": "pass", + "bridge-ports": "pass", + "bridge-vids": "" + }, + "status": "pass" + }, + { + "name": "vxlan10102", + "auto": true, + "config": { + "vxlan-id": "10102", + "vxlan-local-tunnelip": "6.0.0.17" + }, + "config_status": { + "vxlan-id": "pass", + "vxlan-local-tunnelip": "pass" + }, + "status": "pass" + }, + { + "name": "br2", + "auto": true, + "config": { + "bridge-arp-nd-suppress": "vxlan10102=on", + "bridge-learning": "vxlan10102=off", + "bridge-ports": "vxlan10102", + "bridge-vids": "101" + }, + "config_status": { + "bridge-arp-nd-suppress": "pass", + "bridge-learning": "pass", + "bridge-ports": "pass", + "bridge-vids": "" + }, + "status": "pass" + } +] diff --git a/tests/output/bridge_new_attribute_ucast_mcast_flood.after.ifquery.ac.json b/tests/output/bridge_new_attribute_ucast_mcast_flood.after.ifquery.ac.json new file mode 100644 index 00000000..b9ce84b5 --- /dev/null +++ b/tests/output/bridge_new_attribute_ucast_mcast_flood.after.ifquery.ac.json @@ -0,0 +1,76 @@ +[ + { + "name": "lo", + "addr_method": "loopback", + "addr_family": "inet", + "auto": true, + "config": {}, + "config_status": {}, + "status": "pass" + }, + { + "name": "eth0", + "addr_method": "dhcp", + "addr_family": "inet", + "auto": true, + "config": { + "vrf": "mgmt" + }, + "config_status": { + "vrf": "pass" + }, + "status": "pass" + }, + { + "name": "mgmt", + "auto": true, + "config": { + "vrf-table": "1001" + }, + "config_status": { + "vrf-table": "pass" + }, + "status": "pass" + }, + { + "name": "swp_AA_", + "auto": true, + "config": { + "bridge-unicast-flood": "off", + "bridge-multicast-flood": "off" + }, + "config_status": { + "bridge-unicast-flood": "pass", + "bridge-multicast-flood": "pass" + }, + "status": "pass" + }, + { + "name": "br0", + "auto": true, + "config": { + "bridge-vlan-aware": "yes", + "bridge-ports": "swp_AA_" + }, + "config_status": { + "bridge-vlan-aware": "pass", + "bridge-ports": "pass" + }, + "status": "pass" + }, + { + "name": "br1", + "auto": true, + "config": { + "bridge-unicast-flood": "swp_BB_=off", + "bridge-multicast-flood": "swp_BB_=off", + "bridge-ports": "swp_BB_" + }, + "config_status": { + "bridge-unicast-flood": "pass", + "bridge-multicast-flood": "pass", + "bridge-ports": "pass" + }, + "status": "pass" + } +] diff --git a/tests/output/bridge_new_attribute_ucast_mcast_flood.before.ifquery.ac.json b/tests/output/bridge_new_attribute_ucast_mcast_flood.before.ifquery.ac.json new file mode 100644 index 00000000..7165f799 --- /dev/null +++ b/tests/output/bridge_new_attribute_ucast_mcast_flood.before.ifquery.ac.json @@ -0,0 +1,76 @@ +[ + { + "name": "lo", + "addr_method": "loopback", + "addr_family": "inet", + "auto": true, + "config": {}, + "config_status": {}, + "status": "pass" + }, + { + "name": "eth0", + "addr_method": "dhcp", + "addr_family": "inet", + "auto": true, + "config": { + "vrf": "mgmt" + }, + "config_status": { + "vrf": "pass" + }, + "status": "pass" + }, + { + "name": "mgmt", + "auto": true, + "config": { + "vrf-table": "1001" + }, + "config_status": { + "vrf-table": "pass" + }, + "status": "pass" + }, + { + "name": "swp_AA_", + "auto": true, + "config": { + "bridge-unicast-flood": "on", + "bridge-multicast-flood": "on" + }, + "config_status": { + "bridge-unicast-flood": "pass", + "bridge-multicast-flood": "pass" + }, + "status": "pass" + }, + { + "name": "br0", + "auto": true, + "config": { + "bridge-vlan-aware": "yes", + "bridge-ports": "swp_AA_" + }, + "config_status": { + "bridge-vlan-aware": "pass", + "bridge-ports": "pass" + }, + "status": "pass" + }, + { + "name": "br1", + "auto": true, + "config": { + "bridge-unicast-flood": "swp_BB_=on", + "bridge-multicast-flood": "swp_BB_=on", + "bridge-ports": "swp_BB_" + }, + "config_status": { + "bridge-unicast-flood": "pass", + "bridge-multicast-flood": "pass", + "bridge-ports": "pass" + }, + "status": "pass" + } +] diff --git a/tests/output/bridge_new_attribute_vlan_protocol_stats.after.ifquery.ac.json b/tests/output/bridge_new_attribute_vlan_protocol_stats.after.ifquery.ac.json new file mode 100644 index 00000000..a95fa20e --- /dev/null +++ b/tests/output/bridge_new_attribute_vlan_protocol_stats.after.ifquery.ac.json @@ -0,0 +1,63 @@ +[ + { + "name": "lo", + "addr_method": "loopback", + "addr_family": "inet", + "auto": true, + "config": {}, + "config_status": {}, + "status": "pass" + }, + { + "name": "eth0", + "addr_method": "dhcp", + "addr_family": "inet", + "auto": true, + "config": { + "vrf": "mgmt" + }, + "config_status": { + "vrf": "pass" + }, + "status": "pass" + }, + { + "name": "mgmt", + "auto": true, + "config": { + "vrf-table": "1001" + }, + "config_status": { + "vrf-table": "pass" + }, + "status": "pass" + }, + { + "name": "br0", + "auto": true, + "config": { + "bridge-vlan-protocol": "802.1ad", + "bridge-vlan-aware": "yes", + "bridge-ports": "swp_AA_" + }, + "config_status": { + "bridge-vlan-protocol": "pass", + "bridge-vlan-aware": "pass", + "bridge-ports": "pass" + }, + "status": "pass" + }, + { + "name": "br1", + "auto": true, + "config": { + "bridge-vlan-protocol": "802.1ad", + "bridge-ports": "swp_BB_" + }, + "config_status": { + "bridge-vlan-protocol": "pass", + "bridge-ports": "pass" + }, + "status": "pass" + } +] diff --git a/tests/output/bridge_new_attribute_vlan_protocol_stats.before.ifquery.ac.json b/tests/output/bridge_new_attribute_vlan_protocol_stats.before.ifquery.ac.json new file mode 100644 index 00000000..f28383bd --- /dev/null +++ b/tests/output/bridge_new_attribute_vlan_protocol_stats.before.ifquery.ac.json @@ -0,0 +1,67 @@ +[ + { + "name": "lo", + "addr_method": "loopback", + "addr_family": "inet", + "auto": true, + "config": {}, + "config_status": {}, + "status": "pass" + }, + { + "name": "eth0", + "addr_method": "dhcp", + "addr_family": "inet", + "auto": true, + "config": { + "vrf": "mgmt" + }, + "config_status": { + "vrf": "pass" + }, + "status": "pass" + }, + { + "name": "mgmt", + "auto": true, + "config": { + "vrf-table": "1001" + }, + "config_status": { + "vrf-table": "pass" + }, + "status": "pass" + }, + { + "name": "br0", + "auto": true, + "config": { + "bridge-vlan-protocol": "802.1ad", + "bridge-vlan-stats": "on", + "bridge-vlan-aware": "yes", + "bridge-ports": "swp_AA_" + }, + "config_status": { + "bridge-vlan-protocol": "pass", + "bridge-vlan-stats": "pass", + "bridge-vlan-aware": "pass", + "bridge-ports": "pass" + }, + "status": "pass" + }, + { + "name": "br1", + "auto": true, + "config": { + "bridge-vlan-protocol": "802.1ad", + "bridge-vlan-stats": "off", + "bridge-ports": "swp_BB_" + }, + "config_status": { + "bridge-vlan-protocol": "pass", + "bridge-vlan-stats": "pass", + "bridge-ports": "pass" + }, + "status": "pass" + } +] diff --git a/tests/output/cm_11485_vlan_device_name_vlan.ifquery.ac.json b/tests/output/cm_11485_vlan_device_name_vlan.ifquery.ac.json new file mode 100644 index 00000000..1f71fced --- /dev/null +++ b/tests/output/cm_11485_vlan_device_name_vlan.ifquery.ac.json @@ -0,0 +1,106 @@ +[ + { + "name": "eth0", + "addr_method": "dhcp", + "addr_family": "inet", + "auto": true, + "config": { + "vrf": "mgmt" + }, + "config_status": { + "vrf": "pass" + }, + "status": "pass" + }, + { + "name": "mgmt", + "auto": true, + "config": { + "vrf-table": "1001" + }, + "config_status": { + "vrf-table": "pass" + }, + "status": "pass" + }, + { + "name": "lo", + "addr_method": "loopback", + "addr_family": "inet", + "auto": true, + "config": {}, + "config_status": {}, + "status": "pass" + }, + { + "name": "swp_AA_", + "addr_method": "static", + "addr_family": "inet", + "auto": true, + "config": { + "address": "172.30.1.3/24" + }, + "config_status": { + "address": "pass" + }, + "status": "pass" + }, + { + "name": "swp_BB_.1005", + "auto": true, + "config": { + "address": "10.132.253.11/31" + }, + "config_status": { + "address": "pass" + }, + "status": "fail" + }, + { + "name": "bond0", + "addr_method": "manual", + "addr_family": "inet", + "auto": true, + "config": { + "bond-slaves": "None", + "bond-mode": "802.3ad", + "bond-min-links": "1", + "bond-xmit-hash-policy": "layer2+3" + }, + "config_status": { + "bond-slaves": "fail", + "bond-mode": "pass", + "bond-min-links": "pass", + "bond-xmit-hash-policy": "pass" + }, + "status": "fail" + }, + { + "name": "vlan1007", + "auto": true, + "config": { + "vlan-raw-device": "bond0", + "vlan-id": "1007", + "address": "10.132.253.15/31" + }, + "config_status": { + "vlan-raw-device": "pass", + "vlan-id": "pass", + "address": "pass" + }, + "status": "pass" + }, + { + "name": "dummy0", + "auto": true, + "config": { + "link-type": "dummy", + "address": "fdca:ffee:ff12:fe06::1/64" + }, + "config_status": { + "link-type": "pass", + "address": "pass" + }, + "status": "pass" + } +] diff --git a/tests/output/interfaces.vrr_vrf.ifquery.ac.1.json b/tests/output/interfaces.vrr_vrf.ifquery.ac.1.json new file mode 100644 index 00000000..995b1a0d --- /dev/null +++ b/tests/output/interfaces.vrr_vrf.ifquery.ac.1.json @@ -0,0 +1,109 @@ +[ + { + "name": "lo", + "addr_method": "loopback", + "addr_family": "inet", + "auto": true, + "config": {}, + "config_status": {}, + "status": "pass" + }, + { + "name": "eth0", + "addr_method": "dhcp", + "addr_family": "inet", + "auto": true, + "config": { + "vrf": "mgmt" + }, + "config_status": { + "vrf": "pass" + }, + "status": "pass" + }, + { + "name": "mgmt", + "auto": true, + "config": { + "vrf-table": "1001" + }, + "config_status": { + "vrf-table": "pass" + }, + "status": "pass" + }, + { + "name": "peerlink", + "auto": true, + "config": { + "bond-slaves": "swp_AA_ swp_BB_" + }, + "config_status": { + "bond-slaves": "pass" + }, + "status": "pass" + }, + { + "name": "peerlink.4094", + "auto": true, + "config": { + "address": "169.254.1.1/30" + }, + "config_status": { + "address": "pass" + }, + "status": "pass" + }, + { + "name": "myvrf", + "auto": true, + "config": { + "vrf-table": "1002" + }, + "config_status": { + "vrf-table": "pass" + }, + "status": "pass" + }, + { + "name": "bridge", + "auto": true, + "config": { + "bridge-vlan-aware": "yes", + "bridge-ports": "peerlink bond0", + "bridge-stp": "yes" + }, + "config_status": { + "bridge-vlan-aware": "pass", + "bridge-ports": "pass", + "bridge-stp": "pass" + }, + "status": "pass" + }, + { + "name": "bridge.901", + "auto": true, + "config": { + "address-virtual": "00:00:5e:00:01:81 10.1.63.1/28", + "vrf": "myvrf", + "address": "10.1.63.2/28" + }, + "config_status": { + "address-virtual": "pass", + "vrf": "pass", + "address": "pass" + }, + "status": "pass" + }, + { + "name": "bond0", + "auto": true, + "config": { + "bond-slaves": "swp_CC_" + }, + "config_status": { + "bond-slaves": "pass" + }, + "status": "pass" + } +] diff --git a/tests/output/interfaces.vrr_vrf.ifquery.ac.2.json b/tests/output/interfaces.vrr_vrf.ifquery.ac.2.json new file mode 100644 index 00000000..a98832a5 --- /dev/null +++ b/tests/output/interfaces.vrr_vrf.ifquery.ac.2.json @@ -0,0 +1,105 @@ +[ + { + "name": "lo", + "addr_method": "loopback", + "addr_family": "inet", + "auto": true, + "config": {}, + "config_status": {}, + "status": "pass" + }, + { + "name": "eth0", + "addr_method": "dhcp", + "addr_family": "inet", + "auto": true, + "config": { + "vrf": "mgmt" + }, + "config_status": { + "vrf": "pass" + }, + "status": "pass" + }, + { + "name": "mgmt", + "auto": true, + "config": { + "vrf-table": "1001" + }, + "config_status": { + "vrf-table": "pass" + }, + "status": "pass" + }, + { + "name": "peerlink", + "auto": true, + "config": { + "bond-slaves": "swp_AA_ swp_BB_" + }, + "config_status": { + "bond-slaves": "pass" + }, + "status": "pass" + }, + { + "name": "peerlink.4094", + "auto": true, + "config": { + "address": "169.254.1.1/30" + }, + "config_status": { + "address": "pass" + }, + "status": "pass" + }, + { + "name": "myvrf", + "auto": true, + "config": { + "vrf-table": "1002" + }, + "config_status": { + "vrf-table": "pass" + }, + "status": "pass" + }, + { + "name": "bridge", + "auto": true, + "config": { + "bridge-vlan-aware": "yes", + "bridge-ports": "peerlink", + "bridge-stp": "yes" + }, + "config_status": { + "bridge-vlan-aware": "pass", + "bridge-ports": "fail", + "bridge-stp": "pass" + }, + "status": "fail" + }, + { + "name": "bridge.901", + "auto": true, + "config": { + "address-virtual": "00:00:5e:00:01:81 10.1.63.1/28", + "vrf": "myvrf", + "address": "10.1.63.2/28" + }, + "config_status": { + "address-virtual": "pass", + "vrf": "pass", + "address": "pass" + }, + "status": "pass" + }, + { + "name": "bond0", + "auto": true, + "config": {}, + "config_status": {}, + "status": "fail" + } +] diff --git a/tests/output/interfaces.vrr_vrf.ifquery.ac.3.json b/tests/output/interfaces.vrr_vrf.ifquery.ac.3.json new file mode 100644 index 00000000..948e5762 --- /dev/null +++ b/tests/output/interfaces.vrr_vrf.ifquery.ac.3.json @@ -0,0 +1,108 @@ +[ + { + "name": "lo", + "addr_method": "loopback", + "addr_family": "inet", + "auto": true, + "config": {}, + "config_status": {}, + "status": "pass" + }, + { + "name": "eth0", + "addr_method": "dhcp", + "addr_family": "inet", + "auto": true, + "config": { + "vrf": "mgmt" + }, + "config_status": { + "vrf": "pass" + }, + "status": "pass" + }, + { + "name": "mgmt", + "auto": true, + "config": { + "vrf-table": "1001" + }, + "config_status": { + "vrf-table": "pass" + }, + "status": "pass" + }, + { + "name": "peerlink", + "auto": true, + "config": {}, + "config_status": {}, + "status": "fail" + }, + { + "name": "peerlink", + "auto": true, + "config": {}, + "config_status": {}, + "status": "fail" + }, + { + "name": "peerlink.4094", + "auto": true, + "config": {}, + "config_status": {}, + "status": "fail" + }, + { + "name": "myvrf", + "auto": true, + "config": { + "vrf-table": "1002" + }, + "config_status": { + "vrf-table": "pass" + }, + "status": "pass" + }, + { + "name": "bridge", + "auto": true, + "config": { + "bridge-vlan-aware": "yes", + "bridge-ports": "bond0", + "bridge-stp": "yes" + }, + "config_status": { + "bridge-vlan-aware": "pass", + "bridge-ports": "fail", + "bridge-stp": "pass" + }, + "status": "fail" + }, + { + "name": "bridge.901", + "auto": true, + "config": { + "address-virtual": "00:00:5e:00:01:81 10.1.63.1/28", + "vrf": "myvrf", + "address": "10.1.63.2/28" + }, + "config_status": { + "address-virtual": "pass", + "vrf": "pass", + "address": "pass" + }, + "status": "pass" + }, + { + "name": "bond0", + "auto": true, + "config": { + "bond-slaves": "swp_CC_" + }, + "config_status": { + "bond-slaves": "pass" + }, + "status": "pass" + } +] diff --git a/tests/output/interfaces.vrr_vrf.ifquery.ac.4.json b/tests/output/interfaces.vrr_vrf.ifquery.ac.4.json new file mode 100644 index 00000000..254449b3 --- /dev/null +++ b/tests/output/interfaces.vrr_vrf.ifquery.ac.4.json @@ -0,0 +1,105 @@ +[ + { + "name": "lo", + "addr_method": "loopback", + "addr_family": "inet", + "auto": true, + "config": {}, + "config_status": {}, + "status": "pass" + }, + { + "name": "eth0", + "addr_method": "dhcp", + "addr_family": "inet", + "auto": true, + "config": { + "vrf": "mgmt" + }, + "config_status": { + "vrf": "pass" + }, + "status": "pass" + }, + { + "name": "mgmt", + "auto": true, + "config": { + "vrf-table": "1001" + }, + "config_status": { + "vrf-table": "pass" + }, + "status": "pass" + }, + { + "name": "peerlink", + "auto": true, + "config": { + "bond-slaves": "swp_AA_ swp_BB_" + }, + "config_status": { + "bond-slaves": "pass" + }, + "status": "pass" + }, + { + "name": "peerlink.4094", + "auto": true, + "config": { + "address": "169.254.1.1/30" + }, + "config_status": { + "address": "pass" + }, + "status": "pass" + }, + { + "name": "myvrf", + "auto": true, + "config": {}, + "config_status": {}, + "status": "fail" + }, + { + "name": "bridge", + "auto": true, + "config": { + "bridge-vlan-aware": "yes", + "bridge-ports": "peerlink bond0", + "bridge-stp": "yes" + }, + "config_status": { + "bridge-vlan-aware": "pass", + "bridge-ports": "pass", + "bridge-stp": "pass" + }, + "status": "pass" + }, + { + "name": "bridge.901", + "auto": true, + "config": { + "address-virtual": "00:00:5e:00:01:81", + "vrf": "None", + "address": "10.1.63.2/28" + }, + "config_status": { + "address-virtual": "fail", + "vrf": "fail", + "address": "fail" + }, + "status": "fail" + }, + { + "name": "bond0", + "auto": true, + "config": { + "bond-slaves": "swp_CC_" + }, + "config_status": { + "bond-slaves": "pass" + }, + "status": "pass" + } +] diff --git a/tests/output/interfaces.vrr_vrf.ifquery.ac.5.json b/tests/output/interfaces.vrr_vrf.ifquery.ac.5.json new file mode 100644 index 00000000..ceb5c0fe --- /dev/null +++ b/tests/output/interfaces.vrr_vrf.ifquery.ac.5.json @@ -0,0 +1,101 @@ +[ + { + "name": "lo", + "addr_method": "loopback", + "addr_family": "inet", + "auto": true, + "config": {}, + "config_status": {}, + "status": "pass" + }, + { + "name": "eth0", + "addr_method": "dhcp", + "addr_family": "inet", + "auto": true, + "config": { + "vrf": "mgmt" + }, + "config_status": { + "vrf": "pass" + }, + "status": "pass" + }, + { + "name": "mgmt", + "auto": true, + "config": { + "vrf-table": "1001" + }, + "config_status": { + "vrf-table": "pass" + }, + "status": "pass" + }, + { + "name": "peerlink", + "auto": true, + "config": { + "bond-slaves": "swp_AA_ swp_BB_" + }, + "config_status": { + "bond-slaves": "pass" + }, + "status": "pass" + }, + { + "name": "peerlink.4094", + "auto": true, + "config": { + "address": "169.254.1.1/30" + }, + "config_status": { + "address": "pass" + }, + "status": "pass" + }, + { + "name": "myvrf", + "auto": true, + "config": {}, + "config_status": {}, + "status": "fail" + }, + { + "name": "bridge", + "auto": true, + "config": { + "bridge-vlan-aware": "yes", + "bridge-ports": "peerlink", + "bridge-stp": "yes" + }, + "config_status": { + "bridge-vlan-aware": "pass", + "bridge-ports": "fail", + "bridge-stp": "pass" + }, + "status": "fail" + }, + { + "name": "bridge.901", + "auto": true, + "config": { + "address-virtual": "00:00:5e:00:01:81", + "vrf": "None", + "address": "10.1.63.2/28" + }, + "config_status": { + "address-virtual": "fail", + "vrf": "fail", + "address": "fail" + }, + "status": "fail" + }, + { + "name": "bond0", + "auto": true, + "config": {}, + "config_status": {}, + "status": "fail" + } +] diff --git a/tests/output/interfaces.vrr_vrf.ifquery.ac.6.json b/tests/output/interfaces.vrr_vrf.ifquery.ac.6.json new file mode 100644 index 00000000..752b29b8 --- /dev/null +++ b/tests/output/interfaces.vrr_vrf.ifquery.ac.6.json @@ -0,0 +1,93 @@ +[ + { + "name": "lo", + "addr_method": "loopback", + "addr_family": "inet", + "auto": true, + "config": {}, + "config_status": {}, + "status": "pass" + }, + { + "name": "eth0", + "addr_method": "dhcp", + "addr_family": "inet", + "auto": true, + "config": { + "vrf": "mgmt" + }, + "config_status": { + "vrf": "pass" + }, + "status": "pass" + }, + { + "name": "mgmt", + "auto": true, + "config": { + "vrf-table": "1001" + }, + "config_status": { + "vrf-table": "pass" + }, + "status": "pass" + }, + { + "name": "peerlink", + "auto": true, + "config": { + "bond-slaves": "swp_AA_ swp_BB_" + }, + "config_status": { + "bond-slaves": "pass" + }, + "status": "pass" + }, + { + "name": "peerlink.4094", + "auto": true, + "config": { + "address": "169.254.1.1/30" + }, + "config_status": { + "address": "pass" + }, + "status": "pass" + }, + { + "name": "myvrf", + "auto": true, + "config": { + "vrf-table": "1002" + }, + "config_status": { + "vrf-table": "pass" + }, + "status": "pass" + }, + { + "name": "bridge", + "auto": true, + "config": {}, + "config_status": {}, + "status": "fail" + }, + { + "name": "bridge.901", + "auto": true, + "config": {}, + "config_status": {}, + "status": "fail" + }, + { + "name": "bond0", + "auto": true, + "config": { + "bond-slaves": "swp_CC_" + }, + "config_status": { + "bond-slaves": "pass" + }, + "status": "pass" + } +] diff --git a/tests/output/interfaces.vrr_vrf.ifquery.ac.7.json b/tests/output/interfaces.vrr_vrf.ifquery.ac.7.json new file mode 100644 index 00000000..97dca23d --- /dev/null +++ b/tests/output/interfaces.vrr_vrf.ifquery.ac.7.json @@ -0,0 +1,101 @@ +[ + { + "name": "lo", + "addr_method": "loopback", + "addr_family": "inet", + "auto": true, + "config": {}, + "config_status": {}, + "status": "pass" + }, + { + "name": "eth0", + "addr_method": "dhcp", + "addr_family": "inet", + "auto": true, + "config": { + "vrf": "mgmt" + }, + "config_status": { + "vrf": "pass" + }, + "status": "pass" + }, + { + "name": "mgmt", + "auto": true, + "config": { + "vrf-table": "1001" + }, + "config_status": { + "vrf-table": "pass" + }, + "status": "pass" + }, + { + "name": "peerlink", + "auto": true, + "config": { + "bond-slaves": "swp_AA_ swp_BB_" + }, + "config_status": { + "bond-slaves": "pass" + }, + "status": "pass" + }, + { + "name": "peerlink.4094", + "auto": true, + "config": { + "address": "169.254.1.1/30" + }, + "config_status": { + "address": "pass" + }, + "status": "pass" + }, + { + "name": "myvrf", + "auto": true, + "config": { + "vrf-table": "1002" + }, + "config_status": { + "vrf-table": "pass" + }, + "status": "pass" + }, + { + "name": "bridge", + "auto": true, + "config": { + "bridge-vlan-aware": "yes", + "bridge-ports": "peerlink bond0", + "bridge-stp": "yes" + }, + "config_status": { + "bridge-vlan-aware": "pass", + "bridge-ports": "pass", + "bridge-stp": "pass" + }, + "status": "pass" + }, + { + "name": "bridge.901", + "auto": true, + "config": {}, + "config_status": {}, + "status": "fail" + }, + { + "name": "bond0", + "auto": true, + "config": { + "bond-slaves": "swp_CC_" + }, + "config_status": { + "bond-slaves": "pass" + }, + "status": "pass" + } +] diff --git a/tests/output/interfaces.vrr_vrf.ifquery.ac.8.json b/tests/output/interfaces.vrr_vrf.ifquery.ac.8.json new file mode 100644 index 00000000..1fd9b07f --- /dev/null +++ b/tests/output/interfaces.vrr_vrf.ifquery.ac.8.json @@ -0,0 +1,97 @@ +[ + { + "name": "lo", + "addr_method": "loopback", + "addr_family": "inet", + "auto": true, + "config": {}, + "config_status": {}, + "status": "pass" + }, + { + "name": "eth0", + "addr_method": "dhcp", + "addr_family": "inet", + "auto": true, + "config": { + "vrf": "mgmt" + }, + "config_status": { + "vrf": "pass" + }, + "status": "pass" + }, + { + "name": "mgmt", + "auto": true, + "config": { + "vrf-table": "1001" + }, + "config_status": { + "vrf-table": "pass" + }, + "status": "pass" + }, + { + "name": "peerlink", + "auto": true, + "config": { + "bond-slaves": "swp_AA_ swp_BB_" + }, + "config_status": { + "bond-slaves": "pass" + }, + "status": "pass" + }, + { + "name": "peerlink.4094", + "auto": true, + "config": { + "address": "169.254.1.1/30" + }, + "config_status": { + "address": "pass" + }, + "status": "pass" + }, + { + "name": "myvrf", + "auto": true, + "config": { + "vrf-table": "1002" + }, + "config_status": { + "vrf-table": "pass" + }, + "status": "pass" + }, + { + "name": "bridge", + "auto": true, + "config": { + "bridge-vlan-aware": "yes", + "bridge-ports": "peerlink", + "bridge-stp": "yes" + }, + "config_status": { + "bridge-vlan-aware": "pass", + "bridge-ports": "fail", + "bridge-stp": "pass" + }, + "status": "fail" + }, + { + "name": "bridge.901", + "auto": true, + "config": {}, + "config_status": {}, + "status": "fail" + }, + { + "name": "bond0", + "auto": true, + "config": {}, + "config_status": {}, + "status": "fail" + } +] diff --git a/tests/output/interfaces_link_state.after.ifquery.ac.json b/tests/output/interfaces_link_state.after.ifquery.ac.json new file mode 100644 index 00000000..95ea2d2b --- /dev/null +++ b/tests/output/interfaces_link_state.after.ifquery.ac.json @@ -0,0 +1,109 @@ +[ + { + "name": "lo", + "addr_method": "loopback", + "addr_family": "inet", + "auto": true, + "config": {}, + "config_status": {}, + "status": "pass" + }, + { + "name": "eth0", + "addr_method": "dhcp", + "addr_family": "inet", + "auto": true, + "config": { + "vrf": "mgmt" + }, + "config_status": { + "vrf": "pass" + }, + "status": "pass" + }, + { + "name": "mgmt", + "auto": true, + "config": { + "vrf-table": "1001" + }, + "config_status": { + "vrf-table": "pass" + }, + "status": "pass" + }, + { + "name": "swp1", + "addr_method": "manual", + "addr_family": "inet", + "auto": true, + "config": {}, + "config_status": {}, + "status": "pass" + }, + { + "name": "swp10", + "auto": true, + "config": { + "link-down": "yes" + }, + "config_status": { + "link-down": "pass" + }, + "status": "pass" + }, + { + "name": "bridge1", + "auto": true, + "config": { + "bridge-ports": "swp_CC_" + }, + "config_status": { + "bridge-ports": "pass" + }, + "status": "pass" + }, + { + "name": "bridge2", + "addr_method": "manual", + "addr_family": "inet", + "auto": true, + "config": { + "bridge-ports": "swp_DD_" + }, + "config_status": { + "bridge-ports": "pass" + }, + "status": "pass" + }, + { + "name": "bridge3", + "addr_method": "manual", + "addr_family": "inet", + "auto": true, + "config": { + "link-down": "no", + "bridge-ports": "swp_EE_" + }, + "config_status": { + "link-down": "pass", + "bridge-ports": "pass" + }, + "status": "pass" + }, + { + "name": "bridge4", + "addr_method": "manual", + "addr_family": "inet", + "auto": true, + "config": { + "link-down": "yes", + "bridge-ports": "swp_FF_" + }, + "config_status": { + "link-down": "pass", + "bridge-ports": "pass" + }, + "status": "pass" + } +] diff --git a/tests/output/interfaces_link_state.before.ifquery.ac.json b/tests/output/interfaces_link_state.before.ifquery.ac.json new file mode 100644 index 00000000..8752d973 --- /dev/null +++ b/tests/output/interfaces_link_state.before.ifquery.ac.json @@ -0,0 +1,105 @@ +[ + { + "name": "lo", + "addr_method": "loopback", + "addr_family": "inet", + "auto": true, + "config": {}, + "config_status": {}, + "status": "pass" + }, + { + "name": "eth0", + "addr_method": "dhcp", + "addr_family": "inet", + "auto": true, + "config": { + "vrf": "mgmt" + }, + "config_status": { + "vrf": "pass" + }, + "status": "pass" + }, + { + "name": "mgmt", + "auto": true, + "config": { + "vrf-table": "1001" + }, + "config_status": { + "vrf-table": "pass" + }, + "status": "pass" + }, + { + "name": "swp_AA_", + "addr_method": "manual", + "addr_family": "inet", + "auto": true, + "config": {}, + "config_status": {}, + "status": "pass" + }, + { + "name": "swp_BB_", + "auto": true, + "config": {}, + "config_status": {}, + "status": "pass" + }, + { + "name": "bridge1", + "auto": true, + "config": { + "bridge-ports": "swp_CC_" + }, + "config_status": { + "bridge-ports": "pass" + }, + "status": "pass" + }, + { + "name": "bridge2", + "addr_method": "manual", + "addr_family": "inet", + "auto": true, + "config": { + "bridge-ports": "swp_DD_" + }, + "config_status": { + "bridge-ports": "pass" + }, + "status": "pass" + }, + { + "name": "bridge3", + "addr_method": "manual", + "addr_family": "inet", + "auto": true, + "config": { + "link-down": "yes", + "bridge-ports": "swp_EE_" + }, + "config_status": { + "link-down": "pass", + "bridge-ports": "pass" + }, + "status": "pass" + }, + { + "name": "bridge4", + "addr_method": "manual", + "addr_family": "inet", + "auto": true, + "config": { + "link-down": "no", + "bridge-ports": "swp_FF_" + }, + "config_status": { + "link-down": "pass", + "bridge-ports": "pass" + }, + "status": "pass" + } +] diff --git a/tests/output/mac1.ifquery.ac.json b/tests/output/mac1.ifquery.ac.json new file mode 100644 index 00000000..499a1d18 --- /dev/null +++ b/tests/output/mac1.ifquery.ac.json @@ -0,0 +1,57 @@ +[ + { + "name": "lo", + "addr_method": "loopback", + "addr_family": "inet", + "auto": true, + "config": {}, + "config_status": {}, + "status": "pass" + }, + { + "name": "eth0", + "addr_method": "dhcp", + "addr_family": "inet", + "auto": true, + "config": { + "vrf": "mgmt" + }, + "config_status": { + "vrf": "pass" + }, + "status": "pass" + }, + { + "name": "mgmt", + "auto": true, + "config": { + "vrf-table": "1001" + }, + "config_status": { + "vrf-table": "pass" + }, + "status": "pass" + }, + { + "name": "swp_AA_", + "auto": true, + "config": { + "hwaddress": "90:e2:ba:2c:b1:96" + }, + "config_status": { + "hwaddress": "pass" + }, + "status": "pass" + }, + { + "name": "swp_BB_", + "auto": true, + "config": { + "hwaddress": "90:e2:ba:2c:b2:95" + }, + "config_status": { + "hwaddress": "pass" + }, + "status": "pass" + } +] diff --git a/tests/output/vxlan_sanity.ifquery.ac.json b/tests/output/vxlan_sanity.ifquery.ac.json new file mode 100644 index 00000000..516dab78 --- /dev/null +++ b/tests/output/vxlan_sanity.ifquery.ac.json @@ -0,0 +1,127 @@ +[ + { + "name": "lo", + "addr_method": "loopback", + "addr_family": "inet", + "auto": true, + "config": {}, + "config_status": {}, + "status": "pass" + }, + { + "name": "eth0", + "addr_method": "dhcp", + "addr_family": "inet", + "auto": true, + "config": { + "vrf": "mgmt" + }, + "config_status": { + "vrf": "pass" + }, + "status": "pass" + }, + { + "name": "mgmt", + "auto": true, + "config": { + "vrf-table": "1001" + }, + "config_status": { + "vrf-table": "pass" + }, + "status": "pass" + }, + { + "name": "bridge", + "auto": true, + "config": { + "bridge-vlan-aware": "yes", + "bridge-ports": "swp_AA_ swp_BB_ vxlan1000 vxlan10200", + "bridge-stp": "yes", + "bridge-pvid": "1", + "bridge-vids": "100 200" + }, + "config_status": { + "bridge-vlan-aware": "pass", + "bridge-ports": "pass", + "bridge-stp": "pass", + "bridge-pvid": "", + "bridge-vids": "" + }, + "status": "pass" + }, + { + "name": "vxlan1000", + "auto": true, + "config": { + "vxlan-id": "1000", + "bridge-access": "100", + "vxlan-local-tunnelip": "27.0.0.11" + }, + "config_status": { + "vxlan-id": "pass", + "bridge-access": "pass", + "vxlan-local-tunnelip": "pass" + }, + "status": "pass" + }, + { + "name": "vxlan10200", + "auto": true, + "config": { + "bridge-learning": "on", + "vxlan-id": "10200", + "vxlan-remoteip": "6.0.0.16", + "bridge-access": "200", + "vxlan-local-tunnelip": "6.0.0.15" + }, + "config_status": { + "bridge-learning": "pass", + "vxlan-id": "pass", + "vxlan-remoteip": "pass", + "bridge-access": "pass", + "vxlan-local-tunnelip": "pass" + }, + "status": "pass" + }, + { + "name": "bridge.100", + "auto": true, + "config": { + "address": "45.0.0.16/26" + }, + "config_status": { + "address": "pass" + }, + "status": "pass" + }, + { + "name": "bridge.200", + "auto": true, + "config": {}, + "config_status": {}, + "status": "pass" + }, + { + "name": "vx0", + "auto": true, + "config": { + "vxlan-ageing": "30", + "vxlan-local-tunnelip": "172.16.20.105", + "vxlan-svcnodeip": "172.16.22.126", + "vxlan-id": "42", + "vxlan-learning": "yes", + "vxlan-remoteip": "172.16.22.128" + }, + "config_status": { + "vxlan-ageing": "pass", + "vxlan-local-tunnelip": "pass", + "vxlan-svcnodeip": "pass", + "vxlan-id": "pass", + "vxlan-learning": "pass", + "vxlan-remoteip": "pass" + }, + "status": "pass" + } +] diff --git a/tests/scp/bond.default.eni b/tests/scp/bond.default.eni new file mode 100644 index 00000000..00195ebb --- /dev/null +++ b/tests/scp/bond.default.eni @@ -0,0 +1,38 @@ +auto lo +iface lo inet loopback + +auto eth0 +iface eth0 inet dhcp + vrf mgmt + +auto mgmt +iface mgmt + vrf-table auto + +auto bond0 +iface bond0 + bond-slaves swp_AA_ + +auto bond1 +iface bond1 + bond-slaves swp_BB_ + +auto bond2 +iface bond2 + bond-slaves swp_CC_ + +auto bond3 +iface bond3 + bond-slaves swp_DD_ + +auto bond4 +iface bond4 + bond-slaves swp_EE_ + +auto bond5 +iface bond5 + bond-slaves swp_FF_ + +auto bond6 +iface bond6 + bond-slaves swp_GG_ diff --git a/tests/scp/bridge_attr_back_to_default.after.eni b/tests/scp/bridge_attr_back_to_default.after.eni new file mode 100644 index 00000000..3977d0eb --- /dev/null +++ b/tests/scp/bridge_attr_back_to_default.after.eni @@ -0,0 +1,19 @@ +auto lo +iface lo inet loopback + +auto eth0 +iface eth0 inet dhcp + vrf mgmt + +auto mgmt +iface mgmt + vrf-table auto + +auto br0 +iface br0 + bridge-ports vx42 + +auto vx42 +iface vx42 + vxlan-id 42 + bridge-access 42 diff --git a/tests/scp/bridge_new_attribute_igmp_mld.after.eni b/tests/scp/bridge_new_attribute_igmp_mld.after.eni new file mode 100644 index 00000000..fb15554d --- /dev/null +++ b/tests/scp/bridge_new_attribute_igmp_mld.after.eni @@ -0,0 +1,8 @@ +auto br0 +iface br0 + bridge-vlan-aware yes + bridge-ports swp_AA_ + +auto br1 +iface br1 + bridge-ports swp_BB_ diff --git a/tests/scp/bridge_new_attribute_igmp_mld.before.eni b/tests/scp/bridge_new_attribute_igmp_mld.before.eni new file mode 100644 index 00000000..417565b4 --- /dev/null +++ b/tests/scp/bridge_new_attribute_igmp_mld.before.eni @@ -0,0 +1,12 @@ +auto br0 +iface br0 + bridge-vlan-aware yes + bridge-ports swp_AA_ + bridge-igmp-version 3 + bridge-mld-version 2 + +auto br1 +iface br1 + bridge-ports swp_BB_ + bridge-igmp-version 3 + bridge-mld-version 2 diff --git a/tests/scp/bridge_new_attribute_ipforward_vlan_protocol_mcstats.after.eni b/tests/scp/bridge_new_attribute_ipforward_vlan_protocol_mcstats.after.eni new file mode 100644 index 00000000..0e29a7a3 --- /dev/null +++ b/tests/scp/bridge_new_attribute_ipforward_vlan_protocol_mcstats.after.eni @@ -0,0 +1,20 @@ +auto swp_AA_ +iface swp_AA_ + +auto swp_BB_ +iface swp_BB_ + +auto br2 +iface br2 + address 6.0.0.2/32 + bridge-ports swp_DD_ swp_EE_ + ip-forward on + +auto vlan100 +iface vlan100 + vlan-raw-device swp_CC_ + vlan-id 100 + +auto br3 +iface br3 + bridge-ports swp_FF_ diff --git a/tests/scp/bridge_new_attribute_ipforward_vlan_protocol_mcstats.before.eni b/tests/scp/bridge_new_attribute_ipforward_vlan_protocol_mcstats.before.eni new file mode 100644 index 00000000..b07d6415 --- /dev/null +++ b/tests/scp/bridge_new_attribute_ipforward_vlan_protocol_mcstats.before.eni @@ -0,0 +1,24 @@ +auto swp_AA_ +iface swp_AA_ + ip-forward off + +auto swp_BB_ +iface swp_BB_ + ip6-forward off + +auto br2 +iface br2 + bridge-ports swp_DD_ swp_EE_ + ip-forward on + bridge-mcstats off + +auto vlan100 +iface vlan100 + vlan-raw-device swp_CC_ + vlan-id 100 + vlan-protocol 802.1ad + +auto br3 +iface br3 + bridge-ports swp_FF_ + bridge-mcstats off diff --git a/tests/scp/bridge_new_attribute_learning_arp_nd_suppress.after.eni b/tests/scp/bridge_new_attribute_learning_arp_nd_suppress.after.eni new file mode 100644 index 00000000..b4ea12d7 --- /dev/null +++ b/tests/scp/bridge_new_attribute_learning_arp_nd_suppress.after.eni @@ -0,0 +1,22 @@ +auto vxlan10101 +iface vxlan10101 + vxlan-id 10101 + vxlan-local-tunnelip 6.0.0.17 + bridge-access 101 + +auto br1 +iface br1 + bridge-ports vxlan10101 + bridge-vlan-aware yes + bridge-vids 101 + +auto vxlan10102 +iface vxlan10102 + vxlan-id 10102 + vxlan-local-tunnelip 6.0.0.17 + bridge-access 102 + +auto br2 +iface br2 + bridge-ports vxlan10102 + bridge-vids 101 diff --git a/tests/scp/bridge_new_attribute_learning_arp_nd_suppress.before.eni b/tests/scp/bridge_new_attribute_learning_arp_nd_suppress.before.eni new file mode 100644 index 00000000..60610580 --- /dev/null +++ b/tests/scp/bridge_new_attribute_learning_arp_nd_suppress.before.eni @@ -0,0 +1,26 @@ +auto vxlan10101 +iface vxlan10101 + vxlan-id 10101 + vxlan-local-tunnelip 6.0.0.17 + bridge-learning off + bridge-arp-nd-suppress on + bridge-access 101 + +auto br1 +iface br1 + bridge-ports vxlan10101 + bridge-vlan-aware yes + bridge-vids 101 + +auto vxlan10102 +iface vxlan10102 + vxlan-id 10102 + vxlan-local-tunnelip 6.0.0.17 + bridge-access 102 + +auto br2 +iface br2 + bridge-ports vxlan10102 + bridge-vids 101 + bridge-learning vxlan10102=off + bridge-arp-nd-suppress vxlan10102=on diff --git a/tests/scp/bridge_new_attribute_ucast_mcast_flood.after.eni b/tests/scp/bridge_new_attribute_ucast_mcast_flood.after.eni new file mode 100644 index 00000000..522291f0 --- /dev/null +++ b/tests/scp/bridge_new_attribute_ucast_mcast_flood.after.eni @@ -0,0 +1,15 @@ +auto swp_AA_ +iface swp_AA_ + bridge-unicast-flood off + bridge-multicast-flood off + +auto br0 +iface br0 + bridge-vlan-aware yes + bridge-ports swp_AA_ + +auto br1 +iface br1 + bridge-ports swp_BB_ + bridge-unicast-flood swp_BB_=off + bridge-multicast-flood swp_BB_=off diff --git a/tests/scp/bridge_new_attribute_ucast_mcast_flood.before.eni b/tests/scp/bridge_new_attribute_ucast_mcast_flood.before.eni new file mode 100644 index 00000000..fcccd70a --- /dev/null +++ b/tests/scp/bridge_new_attribute_ucast_mcast_flood.before.eni @@ -0,0 +1,15 @@ +auto swp_AA_ +iface swp_AA_ + bridge-unicast-flood on + bridge-multicast-flood on + +auto br0 +iface br0 + bridge-vlan-aware yes + bridge-ports swp_AA_ + +auto br1 +iface br1 + bridge-ports swp_BB_ + bridge-unicast-flood swp_BB_=on + bridge-multicast-flood swp_BB_=on diff --git a/tests/scp/bridge_new_attribute_vlan_protocol_stats.after.eni b/tests/scp/bridge_new_attribute_vlan_protocol_stats.after.eni new file mode 100644 index 00000000..2809ba2e --- /dev/null +++ b/tests/scp/bridge_new_attribute_vlan_protocol_stats.after.eni @@ -0,0 +1,10 @@ +auto br0 +iface br0 + bridge-vlan-aware yes + bridge-ports swp_AA_ + bridge-vlan-protocol 802.1ad + +auto br1 +iface br1 + bridge-ports swp_BB_ + bridge-vlan-protocol 802.1ad diff --git a/tests/scp/bridge_new_attribute_vlan_protocol_stats.before.eni b/tests/scp/bridge_new_attribute_vlan_protocol_stats.before.eni new file mode 100644 index 00000000..c403c568 --- /dev/null +++ b/tests/scp/bridge_new_attribute_vlan_protocol_stats.before.eni @@ -0,0 +1,12 @@ +auto br0 +iface br0 + bridge-vlan-aware yes + bridge-ports swp_AA_ + bridge-vlan-protocol 802.1ad + bridge-vlan-stats on + +auto br1 +iface br1 + bridge-ports swp_BB_ + bridge-vlan-protocol 802.1ad + bridge-vlan-stats off diff --git a/tests/scp/interfaces_link_state.after.eni b/tests/scp/interfaces_link_state.after.eni new file mode 100644 index 00000000..0cfb4db8 --- /dev/null +++ b/tests/scp/interfaces_link_state.after.eni @@ -0,0 +1,32 @@ +# this link should be down +auto swp_AA_ +iface swp_AA_ inet manual + +# link should be down +auto swp_BB_ +iface swp_BB_ + link-down yes + +# link should be up +# interface +auto bridge1 +iface bridge1 + bridge-ports swp_CC_ + +# link should be up, since it is a logical +# interface. manual is ignored. +auto bridge2 +iface bridge2 inet manual + bridge-ports swp_DD_ + +# link should be up, since 'link-down no' is set. +auto bridge3 +iface bridge3 inet manual + bridge-ports swp_EE_ + link-down no + +# link should be down, since 'link-down yes' is set. +auto bridge4 +iface bridge4 inet manual + bridge-ports swp_FF_ + link-down yes diff --git a/tests/scp/interfaces_link_state.before.eni b/tests/scp/interfaces_link_state.before.eni new file mode 100644 index 00000000..be5291bf --- /dev/null +++ b/tests/scp/interfaces_link_state.before.eni @@ -0,0 +1,31 @@ +# this link should down +auto swp_AA_ +iface swp_AA_ inet manual + +# link should be up +auto swp_BB_ +iface swp_BB_ + +# link should be up +# interface +auto bridge1 +iface bridge1 + bridge-ports swp_CC_ + +# link should be up, since it is a logical +# interface. manual is ignored. +auto bridge2 +iface bridge2 inet manual + bridge-ports swp_DD_ + +# link should be down, since 'down yes' is set. +auto bridge3 +iface bridge3 inet manual + bridge-ports swp_EE_ + link-down yes + +# link should be up, since 'link-down no' is set. +auto bridge4 +iface bridge4 inet manual + bridge-ports swp_FF_ + link-down no diff --git a/tests/test_coverage.py b/tests/test_coverage.py new file mode 100644 index 00000000..235e8221 --- /dev/null +++ b/tests/test_coverage.py @@ -0,0 +1,91 @@ +from typing import List +from pathlib import Path + +import pytest +import logging +import tarfile +import coverage +import subprocess + +from .conftest import registered_files + +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger(__name__) + + +def __doesnt_start_with(file_path: Path, exclude_list: List[str]) -> bool: + for exclude in exclude_list: + if str(file_path).startswith(f"tests/{exclude}"): + return False + return True + + +@pytest.mark.order(-2) +def test_orphan_files(skip_if_any_test_failed): + """ + Check if we have orphaned files in tests/ + Orphaned files might have been used by older test but not used anymore + This test ensure that all files are used and not stale + """ + exclude_list = [ + "__pycache__", + "results", + "conftest.py", + "test_l2.py", + "test_l3.py", + "test_coverage.py", + "__init__.py" + ] + files = set(str(path) for path in Path("tests/").rglob("*") if path.is_file() and __doesnt_start_with(path, exclude_list)) + + if orphan_files := files - registered_files: + pytest.fail(f"Found orphan files: {orphan_files}") + + +def extract_tar_gz(archive_path, extract_path): + """ + Extracts a .tar.gz archive to the specified directory. + """ + with tarfile.open(archive_path, "r:gz") as archive: + archive.extractall(path=extract_path) + logger.info(f"Archive '{archive_path}' successfully extracted to '{extract_path}'.") + + +@pytest.mark.order(-1) +def test_coverage(ssh): + if ssh.coverage_enabled: + # todo move some of those things to a fixture? + ssh.download_coverage() + extract_tar_gz("tests/results/coverage_data.tar.gz", "tests/results/") + cov = coverage.Coverage(data_file="tests/results/coverage_data/.coverage") + cov.load() + total_coverage = cov.report(ignore_errors=True) + assert total_coverage >= 75, f"Coverage less than 75% - total coverage: {total_coverage} - details in ./tests/results/coverage_data.tar.gz" + else: + pytest.skip("coverage is disabled") + + +@pytest.mark.skip(reason="This test is currently disabled because flake8 is currently reporting 3638 errors and polluting the output") +def test_flake8(): + result = subprocess.run(["flake8", "."], text=True, capture_output=True) + + if result.returncode != 0: + error_count = len(result.stdout.strip().split("\n")) + pytest.fail( + f"flake8 reported {error_count} error{'s' if error_count > 1 else ''} (see logs)\n" + f"{result.stdout}\n" + f"{result.stderr}\n" + ) + + +@pytest.mark.skip(reason="This test is currently disabled due to import-not-found errors") +def test_mypy(): + result = subprocess.run(["mypy", "."], text=True, capture_output=True) + + if result.returncode != 0: + msg = result.stdout.strip().split("\n")[-1] + pytest.fail( + f"Mypy {msg}\n" + f"{result.stdout}\n" + f"{result.stderr}\n" + ) diff --git a/tests/test_l2.py b/tests/test_l2.py new file mode 100644 index 00000000..bad1b6ec --- /dev/null +++ b/tests/test_l2.py @@ -0,0 +1,423 @@ +import logging + +from .conftest import assert_identical_json, ENI, ENI_D + +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger(__name__) + + +def test_bond(ssh, setup, get_json): + bond_ifquery_ac_json = get_json("bond.ifquery.ac.json") + + ssh.ifreload_a() + assert ssh.ifquery_ac_json() == bond_ifquery_ac_json + + ifreload_output = ssh.ifreload_av() + assert ssh.ifquery_ac_json() == bond_ifquery_ac_json + + # Check that bonds are not flapped when there's no config change. + for string in [ + "bond0 down", + "bond1 down", + "bond2 down", + "bond3 down", + "bond5 down", + "bond6 down", + ]: + assert string not in ifreload_output + + ssh.run_assert_success( + "cp /etc/network/interfaces /tmp/.interfaces ; " + "sed -E 's/\\s+0/ _ZERO_/' /tmp/.interfaces | " + "sed -E 's/\\s+1/ 0/' | sed -E 's/_ZERO_/ 1/' > /etc/network/interfaces") + ssh.run_assert_success( + "cp /etc/network/interfaces /tmp/.interfaces ; " + "sed -E 's/\\s+yes/ _YES_/' /tmp/.interfaces | " + "sed -E 's/\\s+no/ yes/' | sed -E 's/_YES_/ no/' > /etc/network/interfaces" + ) + ssh.run_assert_success("sed -i.back -E 's/\\s+255/ 0/' /etc/network/interfaces") + ssh.run_assert_success("sed -i.back -E 's/\\s+balance-rr/ 0/' /etc/network/interfaces") + ssh.run_assert_success("sed -i.back -E 's/\\s+active-backup/ 1/' /etc/network/interfaces") + ssh.run_assert_success("sed -i.back -E 's/\\s+balance-xor/ 2/' /etc/network/interfaces") + ssh.run_assert_success("sed -i.back -E 's/\\s+broadcast/ 3/' /etc/network/interfaces") + ssh.run_assert_success("sed -i.back -E 's/\\s+802.3ad/ 4/' /etc/network/interfaces") + ssh.run_assert_success("sed -i.back -E 's/\\s+balance-tlb/ 5/' /etc/network/interfaces") + ssh.run_assert_success("sed -i.back -E 's/\\s+balance-alb/ 6/' /etc/network/interfaces") + ssh.run_assert_success( + "cp /etc/network/interfaces /tmp/.interfaces ; " + "sed -E 's/\\s+layer2\\s*$/ _LAYER2_/' /tmp/.interfaces | " + "sed -E 's/\\s+layer3\\+4/ layer2/' | sed -E 's/_LAYER2_/ layer3\\+4/' " + "> /etc/network/interfaces") + ssh.run_assert_success("sed -i.back -E 's/bond-updelay 65535//' /etc/network/interfaces") + + ifreload_output = ssh.ifreload_av() + assert ssh.ifquery_ac_json() == get_json("bond.flipped.values.ifquery.ac.json") + + for string in [ + "bond3 down", + "bond5 down", + "bond6 down", + ]: + assert string not in ifreload_output + + ssh.scp("tests/scp/bond.default.eni", ENI) + + ssh.ifreload_a() + ssh.ifquery_ac() + + +def test_bond_lacp(ssh, setup, get_json): + bond_lacp_ifquery_ac_json = get_json("bond_lacp.ifquery.ac.json") + bond_lacp_flipped_ifquery_ac_json = get_json("bond_lacp.flipped.values.ifquery.ac.json") + + ssh.ifreload_a() + assert ssh.ifquery_ac_json() == bond_lacp_ifquery_ac_json + + ifreload_output = ssh.ifreload_av() + assert ssh.ifquery_ac_json() == bond_lacp_ifquery_ac_json + + # Check that bonds are not flapped when there's no config change. + for string in [ + "bond4 down", + "bond7 down", + "bond8 down", + "bond9 down", + ]: + assert string not in ifreload_output + + ssh.run_assert_success( + r'cp /etc/network/interfaces /tmp/.interfaces ; ' + r'sed -E "s/\s+0/ _ZERO_/" /tmp/.interfaces | sed -E "s/\s+1/ 0/" | sed -E "s/_ZERO_/ 1/" > /etc/network/interfaces' + ) + ssh.run_assert_success( + r'cp /etc/network/interfaces /tmp/.interfaces ; ' + r'sed -E "s/\s+yes/ _YES_/" /tmp/.interfaces | sed -E "s/\s+no/ yes/" | sed -E "s/_YES_/ no/" > /etc/network/interfaces' + ) + ssh.run_assert_success(r'sed -i.back -E "s/\s+802.3ad/ 4/" /etc/network/interfaces') + + ssh.ifreload_a() + assert ssh.ifquery_ac_json() == bond_lacp_flipped_ifquery_ac_json + + ifreload_output = ssh.ifreload_av() + assert ssh.ifquery_ac_json() == bond_lacp_flipped_ifquery_ac_json + + # Check that bonds are not flapped when there's no config change. + for string in [ + "bond4 down", + "bond7 down", + "bond8 down", + "bond9 down", + ]: + assert string not in ifreload_output + + +def test_bridge1(ssh, setup, get_json): + ssh.ifreload_a() + assert ssh.ifquery_ac_json() == get_json("bridge1.ifquery.ac.json") + assert ssh.bridge_vlan_show_json() == get_json("bridge1.vlan.show.json") + + +def test_bridge2(ssh, setup, get_json): + ssh.ifreload_a() + assert ssh.ifquery_ac_json() == get_json("bridge2.ifquery.ac.json") + assert ssh.bridge_vlan_show_json() == get_json("bridge2.vlan.show.json") + + +def test_bridge3(ssh, setup, get_json): + ssh.ifreload_a() + assert ssh.ifquery_ac_json() == get_json("bridge3.ifquery.ac.json") + assert ssh.bridge_vlan_show_json() == get_json("bridge3.vlan.show.json") + + +def test_bridge4(ssh, setup, get_json): + ssh.ifreload_a() + assert ssh.ifquery_ac_json() == get_json("bridge4.ifquery.ac.json") + assert ssh.bridge_vlan_show_json() == get_json("bridge4.vlan.show.json") + + +def test_bridge5(ssh, setup, get_json): + ssh.ifreload_a() + assert ssh.ifquery_ac_json() == get_json("bridge5.ifquery.ac.json") + assert ssh.bridge_vlan_show_json() == get_json("bridge5.vlan.show.json") + + +def test_bridge6_multiple_bridge_ports_lines(ssh, setup, get_json): + ssh.ifreload_a() + assert ssh.ifquery_ac_json() == get_json("bridge6_multiple_bridge_ports_lines.ifquery.ac.json") + + +def test_bridge7_macvlans(ssh, setup, get_json): + ssh.ifreload_a() + assert ssh.ifquery_ac_json() == get_json("bridge7_macvlans.ifquery.ac.json") + + +def test_bridge8_reserved_vlans(ssh, setup, get_file): + assert "reserved vlan 3725 being used (reserved vlan range 3725-3999)" in ssh.ifreload_a(return_stderr=True, expected_status=1) + + +def test_bridge_access(ssh, setup, get_json): + bridge_ifquery_ac_json = get_json("bridge_access.ifquery.ac.json") + bridge_vlan_show_json = get_json("bridge_access.vlan.show.json") + + ssh.ifreload_a() + assert_identical_json(ssh.ifquery_ac_json(), bridge_ifquery_ac_json) + assert_identical_json(ssh.bridge_vlan_show_json(), bridge_vlan_show_json) + + ssh.ifdown("vxlan1") + + ssh.ifreload_a() + assert_identical_json(ssh.ifquery_ac_json(), bridge_ifquery_ac_json) + assert_identical_json(ssh.bridge_vlan_show_json(), bridge_vlan_show_json) + + +def test_bridge_attr_back_to_default(ssh, setup, get_json): + ssh.ifreload_a() + assert_identical_json(ssh.ifquery_ac_json(), get_json("bridge_attr_back_to_default.ifquery.ac.json")) + + ssh.scp("tests/scp/bridge_attr_back_to_default.after.eni", ENI) + + ssh.ifreload_a() + assert_identical_json(ssh.ifquery_ac_json(), get_json("bridge_attr_back_to_default.after.ifquery.ac.json")) + + ssh.run_assert_success("cat /sys/class/net/br0/bridge/vlan_filtering | grep ^0$") + ssh.run_assert_success("cat /sys/class/net/br0/bridge/stp_state | grep ^2$") + ssh.run_assert_success("cat /sys/class/net/br0/bridge/priority | grep ^32768$") + ssh.run_assert_success("cat /sys/class/net/br0/bridge/ageing_time | grep ^180000$") + ssh.run_assert_success("cat /sys/class/net/br0/bridge/forward_delay | grep ^1500$") + ssh.run_assert_success("cat /sys/class/net/br0/bridge/hello_time | grep ^200$") + ssh.run_assert_success("cat /sys/class/net/br0/bridge/max_age | grep ^2000$") + ssh.run_assert_success("cat /sys/class/net/vx42/brport/path_cost | grep ^100$") + ssh.run_assert_success("cat /sys/class/net/vx42/brport/priority | grep ^8$") + ssh.run_assert_success("cat /sys/class/net/br0/bridge/multicast_last_member_count | grep ^2$") + ssh.run_assert_success("cat /sys/class/net/br0/bridge/multicast_router | grep ^1$") + ssh.run_assert_success("cat /sys/class/net/br0/bridge/multicast_snooping | grep ^0$") + ssh.run_assert_success("cat /sys/class/net/br0/bridge/multicast_startup_query_count | grep ^2$") + ssh.run_assert_success("cat /sys/class/net/br0/bridge/multicast_query_use_ifaddr | grep ^0$") + ssh.run_assert_success("cat /sys/class/net/br0/bridge/multicast_querier | grep ^0$") + ssh.run_assert_success("cat /sys/class/net/br0/bridge/hash_elasticity | grep ^16$") + ssh.run_assert_success("cat /sys/class/net/br0/bridge/hash_max | grep ^4096$") + ssh.run_assert_success("cat /sys/class/net/br0/bridge/multicast_last_member_interval | grep ^100$") + ssh.run_assert_success("cat /sys/class/net/br0/bridge/multicast_membership_interval | grep ^26000$") + ssh.run_assert_success("cat /sys/class/net/br0/bridge/multicast_querier_interval | grep ^25500$") + ssh.run_assert_success("cat /sys/class/net/br0/bridge/multicast_query_interval | grep ^12500$") + ssh.run_assert_success("cat /sys/class/net/br0/bridge/multicast_query_response_interval | grep ^1000$") + ssh.run_assert_success("cat /sys/class/net/br0/bridge/multicast_startup_query_interval | grep ^31") + ssh.run_assert_success("cat /sys/class/net/vx42/brport/multicast_fast_leave | grep ^0$") + ssh.run_assert_success("cat /sys/class/net/vx42/brport/learning | grep ^0$") + ssh.run_assert_success("cat /sys/class/net/br0/bridge/multicast_igmp_version | grep ^2$") + ssh.run_assert_success("cat /sys/class/net/br0/bridge/multicast_mld_version | grep ^1$") + ssh.run_assert_success("cat /sys/class/net/vx42/brport/unicast_flood | grep ^1$") + ssh.run_assert_success("cat /sys/class/net/vx42/brport/multicast_flood | grep ^1$") + ssh.run_assert_success("cat /sys/class/net/br0/bridge/vlan_protocol | grep ^0x8100$") + ssh.run_assert_success("cat /sys/class/net/br0/bridge/vlan_stats_enabled | grep ^0$") + ssh.run_assert_success("cat /sys/class/net/br0/bridge/multicast_stats_enabled | grep ^1$") + ssh.run_assert_success("cat /sys/class/net/br0/bridge/group_fwd_mask | grep ^0x0$") + + +def test_bridge_igmp_version(ssh, setup, get_json): + ssh.ifreload_a() + ssh.run_assert_success("cat /sys/class/net/br2/bridge/multicast_igmp_version | grep 2") + ssh.run_assert_success("cat /sys/class/net/br2/bridge/multicast_mld_version | grep 1") + assert_identical_json(ssh.ifquery_ac_json(), get_json("bridge_igmp_version.ifquery.ac.json")) + + +def test_bridge_l2protocol_tunnel(ssh, setup, get_json): + ssh.ifreload_a() + assert_identical_json(ssh.ifquery_ac_json(), get_json("bridge_l2protocol_tunnel.ifquery.ac.json")) + + +def test_bridge_new_attribute(ssh, setup, get_json): + ssh.scp("tests/scp/bridge_new_attribute_learning_arp_nd_suppress.before.eni", ENI_D) + ssh.ifreload_a() + assert_identical_json( + ssh.ifquery_ac_json(), + get_json("bridge_new_attribute_learning_arp_nd_suppress.before.ifquery.ac.json") + ) + ssh.run_assert_success("ip -d -o link show dev vxlan10101 | grep ' learning off '") + ssh.run_assert_success("cat /sys/class/net/vxlan10101/brport/learning | grep '0'") + ssh.run_assert_success("ip -d -o link show dev vxlan10101 | grep ' neigh_suppress on '") + ssh.run_assert_success("ip -d -o link show dev vxlan10102 | grep ' learning off '") + ssh.run_assert_success("cat /sys/class/net/vxlan10102/brport/learning | grep '0'") + ssh.run_assert_success("ip -d -o link show dev vxlan10102 | grep ' neigh_suppress on '") + ssh.run_assert_success("rm /etc/network/interfaces.d/bridge_new_attribute_learning_arp_nd_suppress.before.eni") + + ssh.scp("tests/scp/bridge_new_attribute_learning_arp_nd_suppress.after.eni", ENI_D) + ssh.ifreload_a() + assert_identical_json( + ssh.ifquery_ac_json(), + get_json("bridge_new_attribute_learning_arp_nd_suppress.after.ifquery.ac.json") + ) + ssh.run_assert_success("ip -d -o link show dev vxlan10101 | grep ' nolearning '") + ssh.run_assert_success("cat /sys/class/net/vxlan10101/brport/learning | grep '0'") + ssh.run_assert_success("ip -d -o link show dev vxlan10101 | grep ' neigh_suppress on '") + ssh.run_assert_success("ip -d -o link show dev vxlan10102 | grep ' nolearning '") + ssh.run_assert_success("cat /sys/class/net/vxlan10102/brport/learning | grep '0'") + ssh.run_assert_success("ip -d -o link show dev vxlan10102 | grep ' neigh_suppress on '") + ssh.run_assert_success("rm /etc/network/interfaces.d/bridge_new_attribute_learning_arp_nd_suppress.after.eni") + ssh.ifdown_x_eth0_x_mgmt() + + ssh.scp("tests/scp/bridge_new_attribute_ipforward_vlan_protocol_mcstats.before.eni", ENI_D) + ssh.ifreload_a() + assert_identical_json( + ssh.ifquery_ac_json(), + get_json("bridge_new_attribute_ipforward_vlan_protocol_mcstats.before.ifquery.ac.json") + ) + ssh.run_assert_success("sysctl net.ipv4.conf.swp_AA_.forwarding | grep 'net.ipv4.conf.swp_AA_.forwarding = 0'") + ssh.run_assert_success("sysctl net.ipv6.conf.swp_AA_.forwarding | grep 'net.ipv6.conf.swp_AA_.forwarding = 1'") + ssh.run_assert_success("sysctl net.ipv4.conf.swp_BB_.forwarding | grep 'net.ipv4.conf.swp_BB_.forwarding = 1'") + ssh.run_assert_success("sysctl net.ipv6.conf.swp_BB_.forwarding | grep 'net.ipv6.conf.swp_BB_.forwarding = 0'") + ssh.run_assert_success("sysctl net.ipv4.conf.br2.forwarding | grep 'net.ipv4.conf.br2.forwarding = 1'") + ssh.run_assert_success("sysctl net.ipv6.conf.br2.forwarding | grep 'net.ipv6.conf.br2.forwarding = 1'") + ssh.run_assert_success("sysctl net.ipv4.conf.swp_DD_.forwarding | grep 'net.ipv4.conf.swp_DD_.forwarding = 1'") + ssh.run_assert_success("sysctl net.ipv6.conf.swp_DD_.forwarding | grep 'net.ipv6.conf.swp_DD_.forwarding = 1'") + ssh.run_assert_success("sysctl net.ipv4.conf.swp_EE_.forwarding | grep 'net.ipv4.conf.swp_EE_.forwarding = 1'") + ssh.run_assert_success("sysctl net.ipv6.conf.swp_EE_.forwarding | grep 'net.ipv6.conf.swp_EE_.forwarding = 1'") + ssh.run_assert_success("ip -o -d link show dev vlan100 | grep ' vlan protocol 802.1ad '") + ssh.run_assert_success("ip -o -d link show dev br3 | grep ' mcast_stats_enabled 0 '") + ssh.run_assert_success("rm /etc/network/interfaces.d/bridge_new_attribute_ipforward_vlan_protocol_mcstats.before.eni") + ssh.ifdown_x_eth0_x_mgmt() + + ssh.scp("tests/scp/bridge_new_attribute_ipforward_vlan_protocol_mcstats.after.eni", ENI_D) + ssh.ifreload_a() + assert_identical_json( + ssh.ifquery_ac_json(), + get_json("bridge_new_attribute_ipforward_vlan_protocol_mcstats.after.ifquery.ac.json") + ) + ssh.run_assert_success("sysctl net.ipv4.conf.swp_AA_.forwarding | grep 'net.ipv4.conf.swp_AA_.forwarding = 1'") + ssh.run_assert_success("sysctl net.ipv6.conf.swp_AA_.forwarding | grep 'net.ipv6.conf.swp_AA_.forwarding = 1'") + ssh.run_assert_success("sysctl net.ipv4.conf.swp_BB_.forwarding | grep 'net.ipv4.conf.swp_BB_.forwarding = 1'") + ssh.run_assert_success("sysctl net.ipv6.conf.swp_BB_.forwarding | grep 'net.ipv6.conf.swp_BB_.forwarding = 1'") + ssh.run_assert_success("sysctl net.ipv4.conf.br2.forwarding | grep 'net.ipv4.conf.br2.forwarding = 1'") + ssh.run_assert_success("sysctl net.ipv6.conf.br2.forwarding | grep 'net.ipv6.conf.br2.forwarding = 1'") + ssh.run_assert_success("sysctl net.ipv4.conf.swp_DD_.forwarding | grep 'net.ipv4.conf.swp_DD_.forwarding = 1'") + ssh.run_assert_success("sysctl net.ipv6.conf.swp_DD_.forwarding | grep 'net.ipv6.conf.swp_DD_.forwarding = 1'") + ssh.run_assert_success("sysctl net.ipv4.conf.swp_EE_.forwarding | grep 'net.ipv4.conf.swp_EE_.forwarding = 1'") + ssh.run_assert_success("sysctl net.ipv6.conf.swp_EE_.forwarding | grep 'net.ipv6.conf.swp_EE_.forwarding = 1'") + ssh.run_assert_success("ip -o -d link show dev vlan100 | grep ' vlan protocol 802.1Q '") + ssh.run_assert_success("ip -o -d link show dev br3 | grep ' mcast_stats_enabled 1 '") + ssh.run_assert_success("rm /etc/network/interfaces.d/bridge_new_attribute_ipforward_vlan_protocol_mcstats.after.eni") + ssh.ifdown_x_eth0_x_mgmt() + + ssh.scp("tests/scp/bridge_new_attribute_vlan_protocol_stats.before.eni", ENI_D) + ssh.ifreload_a() + assert_identical_json( + ssh.ifquery_ac_json(), + get_json("bridge_new_attribute_vlan_protocol_stats.before.ifquery.ac.json") + ) + ssh.run_assert_success("ip -d -o link show dev br0 | grep ' vlan_protocol 802.1ad '") + ssh.run_assert_success("ip -d -o link show dev br0 | grep ' vlan_stats_enabled 1 '") + ssh.run_assert_success("ip -d -o link show dev br1 | grep ' vlan_protocol 802.1ad '") + ssh.run_assert_success("ip -d -o link show dev br1 | grep ' vlan_stats_enabled 0 '") + ssh.run_assert_success("rm /etc/network/interfaces.d/bridge_new_attribute_vlan_protocol_stats.before.eni") + + ssh.scp("tests/scp/bridge_new_attribute_vlan_protocol_stats.after.eni", ENI_D) + ssh.ifreload_a() + assert_identical_json( + ssh.ifquery_ac_json(), + get_json("bridge_new_attribute_vlan_protocol_stats.after.ifquery.ac.json") + ) + ssh.run_assert_success("ip -d -o link show dev br0 | grep ' vlan_stats_enabled 1 '") + ssh.run_assert_success("ip -d -o link show dev br1 | grep ' vlan_stats_enabled 0 '") + ssh.ifdown_x_eth0_x_mgmt() + ssh.ifreload_a() + assert_identical_json( + ssh.ifquery_ac_json(), + get_json("bridge_new_attribute_vlan_protocol_stats.after.ifquery.ac.json") + ) + ssh.run_assert_success("ip -d -o link show dev br0 | grep ' vlan_stats_enabled 1 '") + ssh.run_assert_success("ip -d -o link show dev br1 | grep ' vlan_stats_enabled 0 '") + ssh.run_assert_success("rm /etc/network/interfaces.d/bridge_new_attribute_vlan_protocol_stats.after.eni") + ssh.ifdown_x_eth0_x_mgmt() + + ssh.scp("tests/scp/bridge_new_attribute_ucast_mcast_flood.before.eni", ENI_D) + ssh.ifreload_a() + assert_identical_json( + ssh.ifquery_ac_json(), + get_json("bridge_new_attribute_ucast_mcast_flood.before.ifquery.ac.json") + ) + ssh.run_assert_success("ip -d -o link show dev swp_AA_ | grep ' flood on '") + ssh.run_assert_success("ip -d -o link show dev swp_AA_ | grep ' mcast_flood on '") + ssh.run_assert_success("ip -d -o link show dev swp_BB_ | grep ' flood on '") + ssh.run_assert_success("ip -d -o link show dev swp_BB_ | grep ' mcast_flood on '") + ssh.run_assert_success("rm /etc/network/interfaces.d/bridge_new_attribute_ucast_mcast_flood.before.eni") + + ssh.scp("tests/scp/bridge_new_attribute_ucast_mcast_flood.after.eni", ENI_D) + ssh.ifreload_a() + assert_identical_json( + ssh.ifquery_ac_json(), + get_json("bridge_new_attribute_ucast_mcast_flood.after.ifquery.ac.json") + ) + ssh.run_assert_success("ip -d -o link show dev swp_AA_ | grep ' flood off '") + ssh.run_assert_success("ip -d -o link show dev swp_AA_ | grep ' mcast_flood off '") + ssh.run_assert_success("ip -d -o link show dev swp_BB_ | grep ' flood off '") + ssh.run_assert_success("ip -d -o link show dev swp_BB_ | grep ' mcast_flood off '") + ssh.run_assert_success("rm /etc/network/interfaces.d/bridge_new_attribute_ucast_mcast_flood.after.eni") + ssh.ifdown_x_eth0_x_mgmt() + + ssh.scp("tests/scp/bridge_new_attribute_igmp_mld.before.eni", ENI_D) + ssh.ifreload_a() + assert_identical_json( + ssh.ifquery_ac_json(), + get_json("bridge_new_attribute_igmp_mld.before.ifquery.ac.json") + ) + ssh.run_assert_success("ip -d -o link show dev br0 | grep ' mcast_igmp_version 3 '") + ssh.run_assert_success("ip -d -o link show dev br0 | grep ' mcast_mld_version 2 '") + ssh.run_assert_success("ip -d -o link show dev br1 | grep ' mcast_igmp_version 3 '") + ssh.run_assert_success("ip -d -o link show dev br1 | grep ' mcast_mld_version 2 '") + ssh.run_assert_success("rm /etc/network/interfaces.d/bridge_new_attribute_igmp_mld.before.eni") + + ssh.scp("tests/scp/bridge_new_attribute_igmp_mld.after.eni", ENI_D) + ssh.ifreload_a() + assert_identical_json( + ssh.ifquery_ac_json(), + get_json("bridge_new_attribute_igmp_mld.after.ifquery.ac.json") + ) + ssh.run_assert_success("ip -d -o link show dev br0 | grep ' mcast_igmp_version 2 '") + ssh.run_assert_success("ip -d -o link show dev br0 | grep ' mcast_mld_version 1 '") + ssh.run_assert_success("ip -d -o link show dev br1 | grep ' mcast_igmp_version 2 '") + ssh.run_assert_success("ip -d -o link show dev br1 | grep ' mcast_mld_version 1 '") + ssh.run_assert_success("rm /etc/network/interfaces.d/bridge_new_attribute_igmp_mld.after.eni") + + +def test_cm_11485_vlan_device_name_vlan(ssh, setup, get_json): + assert ssh.ifreload_a(expected_status=1, return_stderr=True) == ssh.translate_swp_xx( + "error: bond0: sub interfaces are not allowed on bond slave: swp_BB_ (swp_BB_.1005)\n" + ) + assert_identical_json( + ssh.ifquery_ac_json(expected_status=1), + get_json("cm_11485_vlan_device_name_vlan.ifquery.ac.json") + ) + + +def test_interfaces_link_state(ssh, setup, get_json): + ssh.scp("tests/scp/interfaces_link_state.before.eni", ENI_D) + ssh.ifreload_a() + assert_identical_json( + ssh.ifquery_ac_json(), + get_json("interfaces_link_state.before.ifquery.ac.json") + ) + ssh.run_assert_success("ip -br link show dev swp_AA_ | grep DOWN") + ssh.run_assert_success("ip -br link show dev swp_BB_ | grep UP") + ssh.run_assert_success("ip -br link show dev bridge1 | grep UP") + ssh.run_assert_success("ip -br link show dev bridge2 | grep UP") + ssh.run_assert_success("ip -br link show dev bridge3 | grep DOWN") + ssh.run_assert_success("ip -br link show dev bridge4 | grep UP") + ssh.run_assert_success("rm /etc/network/interfaces.d/interfaces_link_state.before.eni") + + ssh.scp("tests/scp/interfaces_link_state.after.eni", ENI_D) + ssh.ifreload_a() + assert_identical_json( + ssh.ifquery_ac_json(), + get_json("interfaces_link_state.after.ifquery.ac.json") + ) + ssh.run_assert_success("ip -br link show dev swp_BB_ | grep DOWN") + ssh.run_assert_success("ip -br link show dev bridge1 | grep UP") + ssh.run_assert_success("ip -br link show dev bridge2 | grep UP") + ssh.run_assert_success("ip -br link show dev bridge3 | grep UP") + ssh.run_assert_success("ip -br link show dev bridge4 | grep DOWN") + ssh.run_assert_success("rm /etc/network/interfaces.d/interfaces_link_state.after.eni") + + +def test_mac1(ssh, setup, get_json): + ssh.ifreload_a() + assert_identical_json(ssh.ifquery_ac_json(), get_json("mac1.ifquery.ac.json")) diff --git a/tests/test_l3.py b/tests/test_l3.py new file mode 100644 index 00000000..505abce9 --- /dev/null +++ b/tests/test_l3.py @@ -0,0 +1,138 @@ +from .conftest import ENI + +import logging + +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger(__name__) + + +def test_address(ssh, setup, get_json): + ssh.ifup_a() + assert ssh.ifquery_ac_json() == get_json("address.ifquery.ac.json") + + +def test_address_gateway(ssh, setup, get_json): + assert ssh.translate_swp_xx( + "error: swp_AA_: cmd '/bin/ip route replace default via 10.1.14.3 proto kernel dev swp1' failed: " + "returned 2 (Error: Nexthop has invalid gateway.\n)\nwarning: br1: untagged bridge not found. " + "Please configure a bridge with untagged bridge ports to avoid Spanning Tree Interoperability issue.\n" + ) == ssh.ifup_a(return_stderr=True, expected_status=1) + + assert ssh.ifquery_ac_json() == get_json("address_gateway.ifquery.ac.json") + + ssh.run(f"sed -i 's/address .*//' {ENI}") + + assert ssh.translate_swp_xx( + "info: executing /bin/ip route replace default via 10.1.14.3 proto kernel dev swp_AA_" + ) in ssh.ifreload_av(expected_status=1) + + assert ssh.ifquery_ac_json() == get_json("address_gateway.empty_addrs.ifquery.ac.json") + + +def test_evpn_vab_clag_riot_flood_sup_off_config_tors2(ssh, setup, get_json): + ssh.ifup_a() + + assert ssh.ifquery_ac_json() == get_json("EvpnVabClagRiotFloodSupOffConfig.ifquery.ac.json") + + ssh.run_assert_success("ip -d -o link show vx-1000 | grep nolearning | grep 'learning off'") + ssh.run_assert_success("ip -d -o link show vx-1001 | grep nolearning | grep 'learning off'") + + ssh.ifdown("vx-1000 vx-1001") + ssh.ifup("vx-1000 vx-1001") + + assert ssh.ifquery_ac_json() == get_json("EvpnVabClagRiotFloodSupOffConfig.ifquery.ac.json") + + ssh.run_assert_success("ip -d -o link show vx-1000 | grep nolearning | grep 'learning off'") + ssh.run_assert_success("ip -d -o link show vx-1001 | grep nolearning | grep 'learning off'") + + ssh.ifreload_a() + + assert ssh.ifquery_ac_json() == get_json("EvpnVabClagRiotFloodSupOffConfig.ifquery.ac.json") + + ssh.run_assert_success("ip -d -o link show vx-1000 | grep nolearning | grep 'learning off'") + ssh.run_assert_success("ip -d -o link show vx-1001 | grep nolearning | grep 'learning off'") + + ssh.ifup("uplink hostbond3 bridge vlan1000") + ssh.ifreload_a() + + assert ssh.ifquery_ac_json() == get_json("EvpnVabClagRiotFloodSupOffConfig.ifquery.ac.json") + + ssh.run_assert_success("ip -d -o link show vx-1000 | grep nolearning | grep 'learning off'") + ssh.run_assert_success("ip -d -o link show vx-1001 | grep nolearning | grep 'learning off'") + + ssh.run_assert_success("echo '1' > /sys/class/net/vx-1001/brport/learning") + ssh.run_assert_success("ip link set dev vx-1001 type vxlan learning") + ssh.ifup("vx-1001") + + assert ssh.ifquery_ac_json() == get_json("EvpnVabClagRiotFloodSupOffConfig.ifquery.ac.json") + + ssh.run_assert_success("ip -d -o link show vx-1001 | grep nolearning | grep 'learning off'") + + +def test_interfaces_vrr_vrf(ssh, setup, get_json): + ifquery_ac_1_json = get_json("interfaces.vrr_vrf.ifquery.ac.1.json") + ifquery_ac_2_json = get_json("interfaces.vrr_vrf.ifquery.ac.2.json") + + ssh.ifup_a() + assert ssh.ifquery_ac_json() == ifquery_ac_1_json + + ssh.ifdown("bond0") + assert ssh.ifquery_ac_json(expected_status=1) == ifquery_ac_2_json + ssh.ifup("bond0") + assert ssh.ifquery_ac_json() == ifquery_ac_1_json + ssh.ifreload_a() + assert ssh.ifquery_ac_json() == ifquery_ac_1_json + + ssh.ifdown("peerlink") + assert ssh.ifquery_ac_json(expected_status=1) == get_json("interfaces.vrr_vrf.ifquery.ac.3.json") + ssh.ifup("peerlink") + assert ssh.ifquery_ac_json() == ifquery_ac_1_json + ssh.ifreload_a() + assert ssh.ifquery_ac_json() == ifquery_ac_1_json + + ssh.ifdown("myvrf") + assert ssh.ifquery_ac_json(expected_status=1) == get_json("interfaces.vrr_vrf.ifquery.ac.4.json") + + ssh.ifdown("bond0") + assert ssh.ifquery_ac_json(expected_status=1) == get_json("interfaces.vrr_vrf.ifquery.ac.5.json") + + ssh.ifreload_a() + assert ssh.ifquery_ac_json() == ifquery_ac_1_json + + ssh.ifdown("bridge") + assert ssh.ifquery_ac_json(expected_status=1) == get_json("interfaces.vrr_vrf.ifquery.ac.6.json") + ssh.ifup("bridge") + assert ssh.ifquery_ac_json() == ifquery_ac_1_json + + ssh.ifdown("bond0") + assert ssh.ifquery_ac_json(expected_status=1) == ifquery_ac_2_json + ssh.ifup("bond0") + assert ssh.ifquery_ac_json() == ifquery_ac_1_json + ssh.ifreload_a() + assert ssh.ifquery_ac_json() == ifquery_ac_1_json + + ssh.ifdown("bridge.901") + assert ssh.ifquery_ac_json(expected_status=1) == get_json("interfaces.vrr_vrf.ifquery.ac.7.json") + ssh.ifdown("bond0") + assert ssh.ifquery_ac_json(expected_status=1) == get_json("interfaces.vrr_vrf.ifquery.ac.8.json") + + ssh.ifreload_a() + assert ssh.ifquery_ac_json() == ifquery_ac_1_json + + +def test_vxlandev_sanity(ssh, setup, get_json): + ssh.ifup_a() + assert ssh.ifquery_ac_json() == get_json("vxlan_sanity.ifquery.ac.json") + + ssh.run_assert_success("ip -d -o link show vxlan1000 | grep nolearning") + ssh.run_assert_success("ip -d -o link show vxlan10200 | grep 'learning on' | wc -l | grep '^1$'") + + ssh.run_assert_success(f"sed -i 's/vxlan-id 42/vxlan-id 43/' {ENI}") + assert ssh.ifup("vx0", expected_status=1, return_stderr=True) == "error: vx0: Cannot change running vxlan id (42): Operation not supported\n" + + ssh.run_assert_success(f"sed -i 's/vxlan-id 43/vxlan-id 42/' {ENI}") + ssh.ifquery_c("vx0") + + ssh.run_assert_success(f"sed -i 's/vxlan-remoteip 172.16.22.128/vxlan-remoteip 172.16.22.43/' {ENI}") + ssh.ifup("vx0") + ssh.ifquery_c("vx0") From ed811558e2f4eabc4bbc2a00124db90b4b27c54f Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Thu, 19 Sep 2024 17:13:56 +0200 Subject: [PATCH 111/132] pytest: conftest: use pprint to display json deepdiff Signed-off-by: Julien Fortin --- tests/conftest.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/conftest.py b/tests/conftest.py index 818ac21a..6f306706 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -2,6 +2,7 @@ import re import time import json +import pprint import pytest import logging import paramiko @@ -46,7 +47,11 @@ def assert_identical_json(json1, json2): :return: True if JSON objects are identical, False otherwise. """ if diff := DeepDiff(json1, json2, ignore_order=True): - logger.error(f"JSON objects are not identical - deepdiff: {json.dumps(diff, indent=4)}") + try: + logger.error(f"JSON objects are not identical - deepdiff: {json.dumps(diff, indent=4)}") + except: + logging.error(f"JSON objects are not identical - deepdiff: {pprint.pformat(diff)}") + assert json1 == json2 From 57edafb5ff88f29a09df7e77d49f08901e3eae3f Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Tue, 9 Apr 2024 12:25:44 +0200 Subject: [PATCH 112/132] pytest: add ifreload-diff support - enabled by default Signed-off-by: Julien Fortin --- tests/conftest.py | 17 ++++++++++++++--- tests/test_l3.py | 2 ++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 6f306706..c4037873 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -69,7 +69,8 @@ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.swp_translated_dict = {} self.swp_available = [] - self.coverage_enabled = True + self.coverage_enabled = False + self.ifreload_diff = True def translate_swp_xx(self, content: str, with_update: bool = False): update = False @@ -195,13 +196,23 @@ def ifup_a(self, **kwargs) -> str: return self.__ifupdown2("ifup", "-a", **kwargs) def ifreload_a(self, **kwargs) -> str: - return self.__ifupdown2("ifreload", "-a", **kwargs) + args = "-a" + + if self.ifreload_diff: + args += " --diff" + + return self.__ifupdown2("ifreload", args, **kwargs) def ifreload_av(self, **kwargs) -> str: """ Run ifreload -av and return stderr """ - return self.__ifupdown2("ifreload", "-av", return_stderr=True, **kwargs) + args = "-av" + + if self.ifreload_diff: + args += " --diff" + + return self.__ifupdown2("ifreload", args, return_stderr=True, **kwargs) def ifquery(self, args: str, **kwargs) -> str: assert args, "ifquery has been called without arguments" diff --git a/tests/test_l3.py b/tests/test_l3.py index 505abce9..55f35ff2 100644 --- a/tests/test_l3.py +++ b/tests/test_l3.py @@ -96,6 +96,7 @@ def test_interfaces_vrr_vrf(ssh, setup, get_json): ssh.ifdown("bond0") assert ssh.ifquery_ac_json(expected_status=1) == get_json("interfaces.vrr_vrf.ifquery.ac.5.json") + ssh.ifreload_diff = False ssh.ifreload_a() assert ssh.ifquery_ac_json() == ifquery_ac_1_json @@ -118,6 +119,7 @@ def test_interfaces_vrr_vrf(ssh, setup, get_json): ssh.ifreload_a() assert ssh.ifquery_ac_json() == ifquery_ac_1_json + ssh.ifreload_diff = True def test_vxlandev_sanity(ssh, setup, get_json): From 4ce87bdf5b9e2afbeed3eb7717ee60b4bdca1f50 Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Tue, 9 Apr 2024 12:26:19 +0200 Subject: [PATCH 113/132] pytest: conftest: fix python code loading swp names Signed-off-by: Julien Fortin --- tests/conftest.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index c4037873..db6d873c 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -256,8 +256,8 @@ def load_swps(self): return _, stdout, _, _ = self.run( 'python -c "import os;' - 'print \',\'.join(sorted([dev for dev in os.listdir(\'/sys/class/net/\') ' - 'if dev.startswith(\'swp\') and \'.\' not in dev]));"' + 'print(\',\'.join(sorted([dev for dev in os.listdir(\'/sys/class/net/\') ' + 'if dev.startswith(\'swp\') and \'.\' not in dev])));"' ) for swp in stdout.read().decode("utf-8").strip("\r\n").split(","): if swp: From 20cfb8ebe25a8aa18fe33f663dd0476b338b168f Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Tue, 9 Apr 2024 12:28:21 +0200 Subject: [PATCH 114/132] pytest: bridge vlan show: drop compressed vlan option the -c option in bridge -c vlan show is not working like it used to be -c used to be reserved for the compressed-vlan output but upstream changed it. We are using the fully expended output of the command now Signed-off-by: Julien Fortin --- tests/output/bridge1.vlan.show.json | 116 ++- tests/output/bridge2.vlan.show.json | 110 ++- tests/output/bridge4.vlan.show.json | 59 +- tests/output/bridge5.vlan.show.json | 1206 ++++++++++++++++++++++++++- 4 files changed, 1462 insertions(+), 29 deletions(-) diff --git a/tests/output/bridge1.vlan.show.json b/tests/output/bridge1.vlan.show.json index 948288a1..6cc5cbf1 100644 --- a/tests/output/bridge1.vlan.show.json +++ b/tests/output/bridge1.vlan.show.json @@ -10,9 +10,61 @@ ] }, { - "vlan": 2, - "vlanEnd": 20, - "flags": [] + "vlan": 2 + }, + { + "vlan": 3 + }, + { + "vlan": 4 + }, + { + "vlan": 5 + }, + { + "vlan": 6 + }, + { + "vlan": 7 + }, + { + "vlan": 8 + }, + { + "vlan": 9 + }, + { + "vlan": 10 + }, + { + "vlan": 11 + }, + { + "vlan": 12 + }, + { + "vlan": 13 + }, + { + "vlan": 14 + }, + { + "vlan": 15 + }, + { + "vlan": 16 + }, + { + "vlan": 17 + }, + { + "vlan": 18 + }, + { + "vlan": 19 + }, + { + "vlan": 20 } ] }, @@ -27,9 +79,61 @@ ] }, { - "vlan": 2, - "vlanEnd": 20, - "flags": [] + "vlan": 2 + }, + { + "vlan": 3 + }, + { + "vlan": 4 + }, + { + "vlan": 5 + }, + { + "vlan": 6 + }, + { + "vlan": 7 + }, + { + "vlan": 8 + }, + { + "vlan": 9 + }, + { + "vlan": 10 + }, + { + "vlan": 11 + }, + { + "vlan": 12 + }, + { + "vlan": 13 + }, + { + "vlan": 14 + }, + { + "vlan": 15 + }, + { + "vlan": 16 + }, + { + "vlan": 17 + }, + { + "vlan": 18 + }, + { + "vlan": 19 + }, + { + "vlan": 20 } ] }, diff --git a/tests/output/bridge2.vlan.show.json b/tests/output/bridge2.vlan.show.json index bb2ccc7f..c3c359ea 100644 --- a/tests/output/bridge2.vlan.show.json +++ b/tests/output/bridge2.vlan.show.json @@ -13,9 +13,58 @@ ] }, { - "vlan": 3, - "vlanEnd": 20, - "flags": [] + "vlan": 3 + }, + { + "vlan": 4 + }, + { + "vlan": 5 + }, + { + "vlan": 6 + }, + { + "vlan": 7 + }, + { + "vlan": 8 + }, + { + "vlan": 9 + }, + { + "vlan": 10 + }, + { + "vlan": 11 + }, + { + "vlan": 12 + }, + { + "vlan": 13 + }, + { + "vlan": 14 + }, + { + "vlan": 15 + }, + { + "vlan": 16 + }, + { + "vlan": 17 + }, + { + "vlan": 18 + }, + { + "vlan": 19 + }, + { + "vlan": 20 } ] }, @@ -33,9 +82,58 @@ ] }, { - "vlan": 3, - "vlanEnd": 20, - "flags": [] + "vlan": 3 + }, + { + "vlan": 4 + }, + { + "vlan": 5 + }, + { + "vlan": 6 + }, + { + "vlan": 7 + }, + { + "vlan": 8 + }, + { + "vlan": 9 + }, + { + "vlan": 10 + }, + { + "vlan": 11 + }, + { + "vlan": 12 + }, + { + "vlan": 13 + }, + { + "vlan": 14 + }, + { + "vlan": 15 + }, + { + "vlan": 16 + }, + { + "vlan": 17 + }, + { + "vlan": 18 + }, + { + "vlan": 19 + }, + { + "vlan": 20 } ] }, diff --git a/tests/output/bridge4.vlan.show.json b/tests/output/bridge4.vlan.show.json index f32f3c2b..5db2a482 100644 --- a/tests/output/bridge4.vlan.show.json +++ b/tests/output/bridge4.vlan.show.json @@ -3,9 +3,13 @@ "ifname": "swp_AA_", "vlans": [ { - "vlan": 1, - "vlanEnd": 3, - "flags": [] + "vlan": 1 + }, + { + "vlan": 2 + }, + { + "vlan": 3 }, { "vlan": 4, @@ -15,9 +19,52 @@ ] }, { - "vlan": 5, - "vlanEnd": 20, - "flags": [] + "vlan": 5 + }, + { + "vlan": 6 + }, + { + "vlan": 7 + }, + { + "vlan": 8 + }, + { + "vlan": 9 + }, + { + "vlan": 10 + }, + { + "vlan": 11 + }, + { + "vlan": 12 + }, + { + "vlan": 13 + }, + { + "vlan": 14 + }, + { + "vlan": 15 + }, + { + "vlan": 16 + }, + { + "vlan": 17 + }, + { + "vlan": 18 + }, + { + "vlan": 19 + }, + { + "vlan": 20 } ] }, diff --git a/tests/output/bridge5.vlan.show.json b/tests/output/bridge5.vlan.show.json index a5be1b4a..ad14392a 100644 --- a/tests/output/bridge5.vlan.show.json +++ b/tests/output/bridge5.vlan.show.json @@ -10,9 +10,601 @@ ] }, { - "vlan": 2, - "vlanEnd": 200, - "flags": [] + "vlan": 2 + }, + { + "vlan": 3 + }, + { + "vlan": 4 + }, + { + "vlan": 5 + }, + { + "vlan": 6 + }, + { + "vlan": 7 + }, + { + "vlan": 8 + }, + { + "vlan": 9 + }, + { + "vlan": 10 + }, + { + "vlan": 11 + }, + { + "vlan": 12 + }, + { + "vlan": 13 + }, + { + "vlan": 14 + }, + { + "vlan": 15 + }, + { + "vlan": 16 + }, + { + "vlan": 17 + }, + { + "vlan": 18 + }, + { + "vlan": 19 + }, + { + "vlan": 20 + }, + { + "vlan": 21 + }, + { + "vlan": 22 + }, + { + "vlan": 23 + }, + { + "vlan": 24 + }, + { + "vlan": 25 + }, + { + "vlan": 26 + }, + { + "vlan": 27 + }, + { + "vlan": 28 + }, + { + "vlan": 29 + }, + { + "vlan": 30 + }, + { + "vlan": 31 + }, + { + "vlan": 32 + }, + { + "vlan": 33 + }, + { + "vlan": 34 + }, + { + "vlan": 35 + }, + { + "vlan": 36 + }, + { + "vlan": 37 + }, + { + "vlan": 38 + }, + { + "vlan": 39 + }, + { + "vlan": 40 + }, + { + "vlan": 41 + }, + { + "vlan": 42 + }, + { + "vlan": 43 + }, + { + "vlan": 44 + }, + { + "vlan": 45 + }, + { + "vlan": 46 + }, + { + "vlan": 47 + }, + { + "vlan": 48 + }, + { + "vlan": 49 + }, + { + "vlan": 50 + }, + { + "vlan": 51 + }, + { + "vlan": 52 + }, + { + "vlan": 53 + }, + { + "vlan": 54 + }, + { + "vlan": 55 + }, + { + "vlan": 56 + }, + { + "vlan": 57 + }, + { + "vlan": 58 + }, + { + "vlan": 59 + }, + { + "vlan": 60 + }, + { + "vlan": 61 + }, + { + "vlan": 62 + }, + { + "vlan": 63 + }, + { + "vlan": 64 + }, + { + "vlan": 65 + }, + { + "vlan": 66 + }, + { + "vlan": 67 + }, + { + "vlan": 68 + }, + { + "vlan": 69 + }, + { + "vlan": 70 + }, + { + "vlan": 71 + }, + { + "vlan": 72 + }, + { + "vlan": 73 + }, + { + "vlan": 74 + }, + { + "vlan": 75 + }, + { + "vlan": 76 + }, + { + "vlan": 77 + }, + { + "vlan": 78 + }, + { + "vlan": 79 + }, + { + "vlan": 80 + }, + { + "vlan": 81 + }, + { + "vlan": 82 + }, + { + "vlan": 83 + }, + { + "vlan": 84 + }, + { + "vlan": 85 + }, + { + "vlan": 86 + }, + { + "vlan": 87 + }, + { + "vlan": 88 + }, + { + "vlan": 89 + }, + { + "vlan": 90 + }, + { + "vlan": 91 + }, + { + "vlan": 92 + }, + { + "vlan": 93 + }, + { + "vlan": 94 + }, + { + "vlan": 95 + }, + { + "vlan": 96 + }, + { + "vlan": 97 + }, + { + "vlan": 98 + }, + { + "vlan": 99 + }, + { + "vlan": 100 + }, + { + "vlan": 101 + }, + { + "vlan": 102 + }, + { + "vlan": 103 + }, + { + "vlan": 104 + }, + { + "vlan": 105 + }, + { + "vlan": 106 + }, + { + "vlan": 107 + }, + { + "vlan": 108 + }, + { + "vlan": 109 + }, + { + "vlan": 110 + }, + { + "vlan": 111 + }, + { + "vlan": 112 + }, + { + "vlan": 113 + }, + { + "vlan": 114 + }, + { + "vlan": 115 + }, + { + "vlan": 116 + }, + { + "vlan": 117 + }, + { + "vlan": 118 + }, + { + "vlan": 119 + }, + { + "vlan": 120 + }, + { + "vlan": 121 + }, + { + "vlan": 122 + }, + { + "vlan": 123 + }, + { + "vlan": 124 + }, + { + "vlan": 125 + }, + { + "vlan": 126 + }, + { + "vlan": 127 + }, + { + "vlan": 128 + }, + { + "vlan": 129 + }, + { + "vlan": 130 + }, + { + "vlan": 131 + }, + { + "vlan": 132 + }, + { + "vlan": 133 + }, + { + "vlan": 134 + }, + { + "vlan": 135 + }, + { + "vlan": 136 + }, + { + "vlan": 137 + }, + { + "vlan": 138 + }, + { + "vlan": 139 + }, + { + "vlan": 140 + }, + { + "vlan": 141 + }, + { + "vlan": 142 + }, + { + "vlan": 143 + }, + { + "vlan": 144 + }, + { + "vlan": 145 + }, + { + "vlan": 146 + }, + { + "vlan": 147 + }, + { + "vlan": 148 + }, + { + "vlan": 149 + }, + { + "vlan": 150 + }, + { + "vlan": 151 + }, + { + "vlan": 152 + }, + { + "vlan": 153 + }, + { + "vlan": 154 + }, + { + "vlan": 155 + }, + { + "vlan": 156 + }, + { + "vlan": 157 + }, + { + "vlan": 158 + }, + { + "vlan": 159 + }, + { + "vlan": 160 + }, + { + "vlan": 161 + }, + { + "vlan": 162 + }, + { + "vlan": 163 + }, + { + "vlan": 164 + }, + { + "vlan": 165 + }, + { + "vlan": 166 + }, + { + "vlan": 167 + }, + { + "vlan": 168 + }, + { + "vlan": 169 + }, + { + "vlan": 170 + }, + { + "vlan": 171 + }, + { + "vlan": 172 + }, + { + "vlan": 173 + }, + { + "vlan": 174 + }, + { + "vlan": 175 + }, + { + "vlan": 176 + }, + { + "vlan": 177 + }, + { + "vlan": 178 + }, + { + "vlan": 179 + }, + { + "vlan": 180 + }, + { + "vlan": 181 + }, + { + "vlan": 182 + }, + { + "vlan": 183 + }, + { + "vlan": 184 + }, + { + "vlan": 185 + }, + { + "vlan": 186 + }, + { + "vlan": 187 + }, + { + "vlan": 188 + }, + { + "vlan": 189 + }, + { + "vlan": 190 + }, + { + "vlan": 191 + }, + { + "vlan": 192 + }, + { + "vlan": 193 + }, + { + "vlan": 194 + }, + { + "vlan": 195 + }, + { + "vlan": 196 + }, + { + "vlan": 197 + }, + { + "vlan": 198 + }, + { + "vlan": 199 + }, + { + "vlan": 200 } ] }, @@ -20,16 +612,608 @@ "ifname": "swp_BB_", "vlans": [ { - "vlan": 1, - "flags": [ - "PVID", - "Egress Untagged" - ] + "vlan": 1, + "flags": [ + "PVID", + "Egress Untagged" + ] + }, + { + "vlan": 2 + }, + { + "vlan": 3 + }, + { + "vlan": 4 + }, + { + "vlan": 5 + }, + { + "vlan": 6 + }, + { + "vlan": 7 + }, + { + "vlan": 8 + }, + { + "vlan": 9 + }, + { + "vlan": 10 + }, + { + "vlan": 11 + }, + { + "vlan": 12 + }, + { + "vlan": 13 + }, + { + "vlan": 14 + }, + { + "vlan": 15 + }, + { + "vlan": 16 + }, + { + "vlan": 17 + }, + { + "vlan": 18 + }, + { + "vlan": 19 + }, + { + "vlan": 20 + }, + { + "vlan": 21 + }, + { + "vlan": 22 + }, + { + "vlan": 23 + }, + { + "vlan": 24 + }, + { + "vlan": 25 + }, + { + "vlan": 26 + }, + { + "vlan": 27 + }, + { + "vlan": 28 + }, + { + "vlan": 29 + }, + { + "vlan": 30 + }, + { + "vlan": 31 + }, + { + "vlan": 32 + }, + { + "vlan": 33 + }, + { + "vlan": 34 + }, + { + "vlan": 35 + }, + { + "vlan": 36 + }, + { + "vlan": 37 + }, + { + "vlan": 38 + }, + { + "vlan": 39 + }, + { + "vlan": 40 + }, + { + "vlan": 41 + }, + { + "vlan": 42 + }, + { + "vlan": 43 + }, + { + "vlan": 44 + }, + { + "vlan": 45 + }, + { + "vlan": 46 + }, + { + "vlan": 47 + }, + { + "vlan": 48 + }, + { + "vlan": 49 + }, + { + "vlan": 50 + }, + { + "vlan": 51 + }, + { + "vlan": 52 + }, + { + "vlan": 53 + }, + { + "vlan": 54 + }, + { + "vlan": 55 + }, + { + "vlan": 56 + }, + { + "vlan": 57 + }, + { + "vlan": 58 + }, + { + "vlan": 59 + }, + { + "vlan": 60 + }, + { + "vlan": 61 + }, + { + "vlan": 62 + }, + { + "vlan": 63 + }, + { + "vlan": 64 + }, + { + "vlan": 65 + }, + { + "vlan": 66 + }, + { + "vlan": 67 + }, + { + "vlan": 68 + }, + { + "vlan": 69 + }, + { + "vlan": 70 + }, + { + "vlan": 71 + }, + { + "vlan": 72 + }, + { + "vlan": 73 + }, + { + "vlan": 74 + }, + { + "vlan": 75 + }, + { + "vlan": 76 + }, + { + "vlan": 77 + }, + { + "vlan": 78 + }, + { + "vlan": 79 + }, + { + "vlan": 80 + }, + { + "vlan": 81 + }, + { + "vlan": 82 + }, + { + "vlan": 83 + }, + { + "vlan": 84 + }, + { + "vlan": 85 + }, + { + "vlan": 86 + }, + { + "vlan": 87 + }, + { + "vlan": 88 + }, + { + "vlan": 89 + }, + { + "vlan": 90 + }, + { + "vlan": 91 + }, + { + "vlan": 92 + }, + { + "vlan": 93 + }, + { + "vlan": 94 + }, + { + "vlan": 95 + }, + { + "vlan": 96 + }, + { + "vlan": 97 + }, + { + "vlan": 98 + }, + { + "vlan": 99 + }, + { + "vlan": 100 + }, + { + "vlan": 101 + }, + { + "vlan": 102 + }, + { + "vlan": 103 + }, + { + "vlan": 104 + }, + { + "vlan": 105 + }, + { + "vlan": 106 + }, + { + "vlan": 107 + }, + { + "vlan": 108 + }, + { + "vlan": 109 + }, + { + "vlan": 110 + }, + { + "vlan": 111 + }, + { + "vlan": 112 + }, + { + "vlan": 113 + }, + { + "vlan": 114 + }, + { + "vlan": 115 + }, + { + "vlan": 116 + }, + { + "vlan": 117 + }, + { + "vlan": 118 + }, + { + "vlan": 119 + }, + { + "vlan": 120 + }, + { + "vlan": 121 + }, + { + "vlan": 122 + }, + { + "vlan": 123 + }, + { + "vlan": 124 + }, + { + "vlan": 125 + }, + { + "vlan": 126 + }, + { + "vlan": 127 + }, + { + "vlan": 128 + }, + { + "vlan": 129 + }, + { + "vlan": 130 + }, + { + "vlan": 131 + }, + { + "vlan": 132 + }, + { + "vlan": 133 + }, + { + "vlan": 134 + }, + { + "vlan": 135 + }, + { + "vlan": 136 + }, + { + "vlan": 137 + }, + { + "vlan": 138 + }, + { + "vlan": 139 + }, + { + "vlan": 140 + }, + { + "vlan": 141 + }, + { + "vlan": 142 + }, + { + "vlan": 143 + }, + { + "vlan": 144 + }, + { + "vlan": 145 + }, + { + "vlan": 146 + }, + { + "vlan": 147 + }, + { + "vlan": 148 + }, + { + "vlan": 149 + }, + { + "vlan": 150 + }, + { + "vlan": 151 + }, + { + "vlan": 152 + }, + { + "vlan": 153 + }, + { + "vlan": 154 + }, + { + "vlan": 155 + }, + { + "vlan": 156 + }, + { + "vlan": 157 + }, + { + "vlan": 158 + }, + { + "vlan": 159 + }, + { + "vlan": 160 + }, + { + "vlan": 161 + }, + { + "vlan": 162 + }, + { + "vlan": 163 + }, + { + "vlan": 164 + }, + { + "vlan": 165 + }, + { + "vlan": 166 + }, + { + "vlan": 167 + }, + { + "vlan": 168 + }, + { + "vlan": 169 + }, + { + "vlan": 170 + }, + { + "vlan": 171 + }, + { + "vlan": 172 + }, + { + "vlan": 173 + }, + { + "vlan": 174 + }, + { + "vlan": 175 + }, + { + "vlan": 176 + }, + { + "vlan": 177 + }, + { + "vlan": 178 + }, + { + "vlan": 179 + }, + { + "vlan": 180 + }, + { + "vlan": 181 + }, + { + "vlan": 182 + }, + { + "vlan": 183 + }, + { + "vlan": 184 + }, + { + "vlan": 185 + }, + { + "vlan": 186 + }, + { + "vlan": 187 + }, + { + "vlan": 188 + }, + { + "vlan": 189 + }, + { + "vlan": 190 + }, + { + "vlan": 191 + }, + { + "vlan": 192 + }, + { + "vlan": 193 + }, + { + "vlan": 194 + }, + { + "vlan": 195 + }, + { + "vlan": 196 + }, + { + "vlan": 197 + }, + { + "vlan": 198 + }, + { + "vlan": 199 }, { - "vlan": 2, - "vlanEnd": 200, - "flags": [] + "vlan": 200 } ] }, From aa1e2295eef983df6084b51f650db541f26f6045 Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Tue, 9 Apr 2024 12:28:45 +0200 Subject: [PATCH 115/132] pytest: test_l3: fix hardcoded swp name to swp_XX_ format Signed-off-by: Julien Fortin --- tests/output/interfaces_link_state.after.ifquery.ac.json | 4 ++-- tests/test_l3.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/output/interfaces_link_state.after.ifquery.ac.json b/tests/output/interfaces_link_state.after.ifquery.ac.json index 95ea2d2b..10bc19c3 100644 --- a/tests/output/interfaces_link_state.after.ifquery.ac.json +++ b/tests/output/interfaces_link_state.after.ifquery.ac.json @@ -33,7 +33,7 @@ "status": "pass" }, { - "name": "swp1", + "name": "swp_AA_", "addr_method": "manual", "addr_family": "inet", "auto": true, @@ -42,7 +42,7 @@ "status": "pass" }, { - "name": "swp10", + "name": "swp_BB_", "auto": true, "config": { "link-down": "yes" diff --git a/tests/test_l3.py b/tests/test_l3.py index 55f35ff2..3e1b3447 100644 --- a/tests/test_l3.py +++ b/tests/test_l3.py @@ -13,7 +13,7 @@ def test_address(ssh, setup, get_json): def test_address_gateway(ssh, setup, get_json): assert ssh.translate_swp_xx( - "error: swp_AA_: cmd '/bin/ip route replace default via 10.1.14.3 proto kernel dev swp1' failed: " + "error: swp_AA_: cmd '/bin/ip route replace default via 10.1.14.3 proto kernel dev swp_AA_' failed: " "returned 2 (Error: Nexthop has invalid gateway.\n)\nwarning: br1: untagged bridge not found. " "Please configure a bridge with untagged bridge ports to avoid Spanning Tree Interoperability issue.\n" ) == ssh.ifup_a(return_stderr=True, expected_status=1) From 82ca8bbf9f8aa417bbb0ad2b181529eff1703a73 Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Wed, 17 Apr 2024 22:45:46 +0200 Subject: [PATCH 116/132] pytest: use assert_identical_json when checking json output Signed-off-by: Julien Fortin --- tests/test_l2.py | 34 ++++++++++++++--------------- tests/test_l3.py | 56 ++++++++++++++++++++++++------------------------ 2 files changed, 45 insertions(+), 45 deletions(-) diff --git a/tests/test_l2.py b/tests/test_l2.py index bad1b6ec..95f501fe 100644 --- a/tests/test_l2.py +++ b/tests/test_l2.py @@ -10,10 +10,10 @@ def test_bond(ssh, setup, get_json): bond_ifquery_ac_json = get_json("bond.ifquery.ac.json") ssh.ifreload_a() - assert ssh.ifquery_ac_json() == bond_ifquery_ac_json + assert_identical_json(ssh.ifquery_ac_json(), bond_ifquery_ac_json) ifreload_output = ssh.ifreload_av() - assert ssh.ifquery_ac_json() == bond_ifquery_ac_json + assert_identical_json(ssh.ifquery_ac_json(), bond_ifquery_ac_json) # Check that bonds are not flapped when there's no config change. for string in [ @@ -71,10 +71,10 @@ def test_bond_lacp(ssh, setup, get_json): bond_lacp_flipped_ifquery_ac_json = get_json("bond_lacp.flipped.values.ifquery.ac.json") ssh.ifreload_a() - assert ssh.ifquery_ac_json() == bond_lacp_ifquery_ac_json + assert_identical_json(ssh.ifquery_ac_json(), bond_lacp_ifquery_ac_json) ifreload_output = ssh.ifreload_av() - assert ssh.ifquery_ac_json() == bond_lacp_ifquery_ac_json + assert_identical_json(ssh.ifquery_ac_json(), bond_lacp_ifquery_ac_json) # Check that bonds are not flapped when there's no config change. for string in [ @@ -96,10 +96,10 @@ def test_bond_lacp(ssh, setup, get_json): ssh.run_assert_success(r'sed -i.back -E "s/\s+802.3ad/ 4/" /etc/network/interfaces') ssh.ifreload_a() - assert ssh.ifquery_ac_json() == bond_lacp_flipped_ifquery_ac_json + assert_identical_json(ssh.ifquery_ac_json(), bond_lacp_flipped_ifquery_ac_json) ifreload_output = ssh.ifreload_av() - assert ssh.ifquery_ac_json() == bond_lacp_flipped_ifquery_ac_json + assert_identical_json(ssh.ifquery_ac_json(), bond_lacp_flipped_ifquery_ac_json) # Check that bonds are not flapped when there's no config change. for string in [ @@ -113,32 +113,32 @@ def test_bond_lacp(ssh, setup, get_json): def test_bridge1(ssh, setup, get_json): ssh.ifreload_a() - assert ssh.ifquery_ac_json() == get_json("bridge1.ifquery.ac.json") - assert ssh.bridge_vlan_show_json() == get_json("bridge1.vlan.show.json") + assert_identical_json(ssh.ifquery_ac_json(), get_json("bridge1.ifquery.ac.json")) + assert_identical_json(ssh.bridge_vlan_show_json(), get_json("bridge1.vlan.show.json")) def test_bridge2(ssh, setup, get_json): ssh.ifreload_a() - assert ssh.ifquery_ac_json() == get_json("bridge2.ifquery.ac.json") - assert ssh.bridge_vlan_show_json() == get_json("bridge2.vlan.show.json") + assert_identical_json(ssh.ifquery_ac_json(), get_json("bridge2.ifquery.ac.json")) + assert_identical_json(ssh.bridge_vlan_show_json(), get_json("bridge2.vlan.show.json")) def test_bridge3(ssh, setup, get_json): ssh.ifreload_a() - assert ssh.ifquery_ac_json() == get_json("bridge3.ifquery.ac.json") - assert ssh.bridge_vlan_show_json() == get_json("bridge3.vlan.show.json") + assert_identical_json(ssh.ifquery_ac_json(), get_json("bridge3.ifquery.ac.json")) + assert_identical_json(ssh.bridge_vlan_show_json(), get_json("bridge3.vlan.show.json")) def test_bridge4(ssh, setup, get_json): ssh.ifreload_a() - assert ssh.ifquery_ac_json() == get_json("bridge4.ifquery.ac.json") - assert ssh.bridge_vlan_show_json() == get_json("bridge4.vlan.show.json") + assert_identical_json(ssh.ifquery_ac_json(), get_json("bridge4.ifquery.ac.json")) + assert_identical_json(ssh.bridge_vlan_show_json(), get_json("bridge4.vlan.show.json")) def test_bridge5(ssh, setup, get_json): ssh.ifreload_a() - assert ssh.ifquery_ac_json() == get_json("bridge5.ifquery.ac.json") - assert ssh.bridge_vlan_show_json() == get_json("bridge5.vlan.show.json") + assert_identical_json(ssh.ifquery_ac_json(), get_json("bridge5.ifquery.ac.json")) + assert_identical_json(ssh.bridge_vlan_show_json(), get_json("bridge5.vlan.show.json")) def test_bridge6_multiple_bridge_ports_lines(ssh, setup, get_json): @@ -148,7 +148,7 @@ def test_bridge6_multiple_bridge_ports_lines(ssh, setup, get_json): def test_bridge7_macvlans(ssh, setup, get_json): ssh.ifreload_a() - assert ssh.ifquery_ac_json() == get_json("bridge7_macvlans.ifquery.ac.json") + assert_identical_json(ssh.ifquery_ac_json(), get_json("bridge7_macvlans.ifquery.ac.json")) def test_bridge8_reserved_vlans(ssh, setup, get_file): diff --git a/tests/test_l3.py b/tests/test_l3.py index 3e1b3447..9bc242c9 100644 --- a/tests/test_l3.py +++ b/tests/test_l3.py @@ -1,4 +1,4 @@ -from .conftest import ENI +from .conftest import ENI, assert_identical_json import logging @@ -8,7 +8,7 @@ def test_address(ssh, setup, get_json): ssh.ifup_a() - assert ssh.ifquery_ac_json() == get_json("address.ifquery.ac.json") + assert_identical_json(ssh.ifquery_ac_json(), get_json("address.ifquery.ac.json")) def test_address_gateway(ssh, setup, get_json): @@ -18,7 +18,7 @@ def test_address_gateway(ssh, setup, get_json): "Please configure a bridge with untagged bridge ports to avoid Spanning Tree Interoperability issue.\n" ) == ssh.ifup_a(return_stderr=True, expected_status=1) - assert ssh.ifquery_ac_json() == get_json("address_gateway.ifquery.ac.json") + assert_identical_json(ssh.ifquery_ac_json(), get_json("address_gateway.ifquery.ac.json")) ssh.run(f"sed -i 's/address .*//' {ENI}") @@ -26,13 +26,13 @@ def test_address_gateway(ssh, setup, get_json): "info: executing /bin/ip route replace default via 10.1.14.3 proto kernel dev swp_AA_" ) in ssh.ifreload_av(expected_status=1) - assert ssh.ifquery_ac_json() == get_json("address_gateway.empty_addrs.ifquery.ac.json") + assert_identical_json(ssh.ifquery_ac_json(), get_json("address_gateway.empty_addrs.ifquery.ac.json")) def test_evpn_vab_clag_riot_flood_sup_off_config_tors2(ssh, setup, get_json): ssh.ifup_a() - assert ssh.ifquery_ac_json() == get_json("EvpnVabClagRiotFloodSupOffConfig.ifquery.ac.json") + assert_identical_json(ssh.ifquery_ac_json(), get_json("EvpnVabClagRiotFloodSupOffConfig.ifquery.ac.json")) ssh.run_assert_success("ip -d -o link show vx-1000 | grep nolearning | grep 'learning off'") ssh.run_assert_success("ip -d -o link show vx-1001 | grep nolearning | grep 'learning off'") @@ -40,14 +40,14 @@ def test_evpn_vab_clag_riot_flood_sup_off_config_tors2(ssh, setup, get_json): ssh.ifdown("vx-1000 vx-1001") ssh.ifup("vx-1000 vx-1001") - assert ssh.ifquery_ac_json() == get_json("EvpnVabClagRiotFloodSupOffConfig.ifquery.ac.json") + assert_identical_json(ssh.ifquery_ac_json(), get_json("EvpnVabClagRiotFloodSupOffConfig.ifquery.ac.json")) ssh.run_assert_success("ip -d -o link show vx-1000 | grep nolearning | grep 'learning off'") ssh.run_assert_success("ip -d -o link show vx-1001 | grep nolearning | grep 'learning off'") ssh.ifreload_a() - assert ssh.ifquery_ac_json() == get_json("EvpnVabClagRiotFloodSupOffConfig.ifquery.ac.json") + assert_identical_json(ssh.ifquery_ac_json(), get_json("EvpnVabClagRiotFloodSupOffConfig.ifquery.ac.json")) ssh.run_assert_success("ip -d -o link show vx-1000 | grep nolearning | grep 'learning off'") ssh.run_assert_success("ip -d -o link show vx-1001 | grep nolearning | grep 'learning off'") @@ -55,7 +55,7 @@ def test_evpn_vab_clag_riot_flood_sup_off_config_tors2(ssh, setup, get_json): ssh.ifup("uplink hostbond3 bridge vlan1000") ssh.ifreload_a() - assert ssh.ifquery_ac_json() == get_json("EvpnVabClagRiotFloodSupOffConfig.ifquery.ac.json") + assert_identical_json(ssh.ifquery_ac_json(), get_json("EvpnVabClagRiotFloodSupOffConfig.ifquery.ac.json")) ssh.run_assert_success("ip -d -o link show vx-1000 | grep nolearning | grep 'learning off'") ssh.run_assert_success("ip -d -o link show vx-1001 | grep nolearning | grep 'learning off'") @@ -64,7 +64,7 @@ def test_evpn_vab_clag_riot_flood_sup_off_config_tors2(ssh, setup, get_json): ssh.run_assert_success("ip link set dev vx-1001 type vxlan learning") ssh.ifup("vx-1001") - assert ssh.ifquery_ac_json() == get_json("EvpnVabClagRiotFloodSupOffConfig.ifquery.ac.json") + assert_identical_json(ssh.ifquery_ac_json(), get_json("EvpnVabClagRiotFloodSupOffConfig.ifquery.ac.json")) ssh.run_assert_success("ip -d -o link show vx-1001 | grep nolearning | grep 'learning off'") @@ -74,57 +74,57 @@ def test_interfaces_vrr_vrf(ssh, setup, get_json): ifquery_ac_2_json = get_json("interfaces.vrr_vrf.ifquery.ac.2.json") ssh.ifup_a() - assert ssh.ifquery_ac_json() == ifquery_ac_1_json + assert_identical_json(ssh.ifquery_ac_json(), ifquery_ac_1_json) ssh.ifdown("bond0") - assert ssh.ifquery_ac_json(expected_status=1) == ifquery_ac_2_json + assert_identical_json(ssh.ifquery_ac_json(expected_status=1), ifquery_ac_2_json) ssh.ifup("bond0") - assert ssh.ifquery_ac_json() == ifquery_ac_1_json + assert_identical_json(ssh.ifquery_ac_json(), ifquery_ac_1_json) ssh.ifreload_a() - assert ssh.ifquery_ac_json() == ifquery_ac_1_json + assert_identical_json(ssh.ifquery_ac_json(), ifquery_ac_1_json) ssh.ifdown("peerlink") - assert ssh.ifquery_ac_json(expected_status=1) == get_json("interfaces.vrr_vrf.ifquery.ac.3.json") + assert_identical_json(ssh.ifquery_ac_json(expected_status=1), get_json("interfaces.vrr_vrf.ifquery.ac.3.json")) ssh.ifup("peerlink") - assert ssh.ifquery_ac_json() == ifquery_ac_1_json + assert_identical_json(ssh.ifquery_ac_json(), ifquery_ac_1_json) ssh.ifreload_a() - assert ssh.ifquery_ac_json() == ifquery_ac_1_json + assert_identical_json(ssh.ifquery_ac_json(), ifquery_ac_1_json) ssh.ifdown("myvrf") - assert ssh.ifquery_ac_json(expected_status=1) == get_json("interfaces.vrr_vrf.ifquery.ac.4.json") + assert_identical_json(ssh.ifquery_ac_json(expected_status=1), get_json("interfaces.vrr_vrf.ifquery.ac.4.json")) ssh.ifdown("bond0") - assert ssh.ifquery_ac_json(expected_status=1) == get_json("interfaces.vrr_vrf.ifquery.ac.5.json") + assert_identical_json(ssh.ifquery_ac_json(expected_status=1), get_json("interfaces.vrr_vrf.ifquery.ac.5.json")) ssh.ifreload_diff = False ssh.ifreload_a() - assert ssh.ifquery_ac_json() == ifquery_ac_1_json + assert_identical_json(ssh.ifquery_ac_json(), ifquery_ac_1_json) ssh.ifdown("bridge") - assert ssh.ifquery_ac_json(expected_status=1) == get_json("interfaces.vrr_vrf.ifquery.ac.6.json") + assert_identical_json(ssh.ifquery_ac_json(expected_status=1), get_json("interfaces.vrr_vrf.ifquery.ac.6.json")) ssh.ifup("bridge") - assert ssh.ifquery_ac_json() == ifquery_ac_1_json + assert_identical_json(ssh.ifquery_ac_json(), ifquery_ac_1_json) ssh.ifdown("bond0") - assert ssh.ifquery_ac_json(expected_status=1) == ifquery_ac_2_json + assert_identical_json(ssh.ifquery_ac_json(expected_status=1), ifquery_ac_2_json) ssh.ifup("bond0") - assert ssh.ifquery_ac_json() == ifquery_ac_1_json + assert_identical_json(ssh.ifquery_ac_json(), ifquery_ac_1_json) ssh.ifreload_a() - assert ssh.ifquery_ac_json() == ifquery_ac_1_json + assert_identical_json(ssh.ifquery_ac_json(), ifquery_ac_1_json) ssh.ifdown("bridge.901") - assert ssh.ifquery_ac_json(expected_status=1) == get_json("interfaces.vrr_vrf.ifquery.ac.7.json") + assert_identical_json(ssh.ifquery_ac_json(expected_status=1), get_json("interfaces.vrr_vrf.ifquery.ac.7.json")) ssh.ifdown("bond0") - assert ssh.ifquery_ac_json(expected_status=1) == get_json("interfaces.vrr_vrf.ifquery.ac.8.json") + assert_identical_json(ssh.ifquery_ac_json(expected_status=1), get_json("interfaces.vrr_vrf.ifquery.ac.8.json")) ssh.ifreload_a() - assert ssh.ifquery_ac_json() == ifquery_ac_1_json + assert_identical_json(ssh.ifquery_ac_json(), ifquery_ac_1_json) ssh.ifreload_diff = True def test_vxlandev_sanity(ssh, setup, get_json): ssh.ifup_a() - assert ssh.ifquery_ac_json() == get_json("vxlan_sanity.ifquery.ac.json") + assert_identical_json(ssh.ifquery_ac_json(), get_json("vxlan_sanity.ifquery.ac.json")) ssh.run_assert_success("ip -d -o link show vxlan1000 | grep nolearning") ssh.run_assert_success("ip -d -o link show vxlan10200 | grep 'learning on' | wc -l | grep '^1$'") From cfde50b03d9ce1f3de65d90e70a665846e5ae180 Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Thu, 19 Sep 2024 17:14:45 +0200 Subject: [PATCH 117/132] tests: fix bridge_attr_back_to_default Signed-off-by: Julien Fortin --- tests/eni/bridge_attr_back_to_default.eni | 1 - tests/output/bridge_attr_back_to_default.ifquery.ac.json | 2 -- 2 files changed, 3 deletions(-) diff --git a/tests/eni/bridge_attr_back_to_default.eni b/tests/eni/bridge_attr_back_to_default.eni index 0a820275..16d35ccf 100644 --- a/tests/eni/bridge_attr_back_to_default.eni +++ b/tests/eni/bridge_attr_back_to_default.eni @@ -34,7 +34,6 @@ iface br0 bridge-mcqi 42 bridge-mcqri 42 bridge-mcsqi 42 - bridge-mcqv4src 100=172.16.100.1 200=10.10.10.1 bridge-portmcfl vx42=yes bridge-learning vx42=off bridge-igmp-version 3 diff --git a/tests/output/bridge_attr_back_to_default.ifquery.ac.json b/tests/output/bridge_attr_back_to_default.ifquery.ac.json index fa79f451..3a0c36c1 100644 --- a/tests/output/bridge_attr_back_to_default.ifquery.ac.json +++ b/tests/output/bridge_attr_back_to_default.ifquery.ac.json @@ -66,7 +66,6 @@ "bridge-maxage": "10", "bridge-igmp-version": "3", "bridge-mld-version": "2", - "bridge-mcqv4src": "100=172.16.100.1 200=10.10.10.1", "bridge-learning": "vx42=off", "bridge-mcstats": "off", "bridge-portmcfl": "vx42=yes", @@ -103,7 +102,6 @@ "bridge-maxage": "pass", "bridge-igmp-version": "pass", "bridge-mld-version": "pass", - "bridge-mcqv4src": "pass", "bridge-learning": "pass", "bridge-mcstats": "pass", "bridge-portmcfl": "pass", From 87ebc115c2a999a77cca11f514a6795eec39c2e3 Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Thu, 19 Sep 2024 17:44:02 +0200 Subject: [PATCH 118/132] pytest: fix test_cm_11485_vlan_device_name_vlan Signed-off-by: Julien Fortin --- tests/output/cm_11485_vlan_device_name_vlan.ifquery.ac.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/output/cm_11485_vlan_device_name_vlan.ifquery.ac.json b/tests/output/cm_11485_vlan_device_name_vlan.ifquery.ac.json index 1f71fced..ccce8b2a 100644 --- a/tests/output/cm_11485_vlan_device_name_vlan.ifquery.ac.json +++ b/tests/output/cm_11485_vlan_device_name_vlan.ifquery.ac.json @@ -54,7 +54,7 @@ "config_status": { "address": "pass" }, - "status": "fail" + "status": "pass" }, { "name": "bond0", From 5c9c88cdc825c87a1e09c5846bb1393ab108ec27 Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Mon, 24 Jul 2023 17:22:15 -0700 Subject: [PATCH 119/132] mstpctl: PVRST support New attributes: - mstpctl-pvrst-mode on/off Bridge attributes: - mstpctl-vlan-priority (aliased to: bridge-stp-vlan-priority) - mstpctl-vlan-hello (aliased to: bridge-stp-vlan-hello) - mstpctl-vlan-fdelay (aliased to: bridge-stp-vlan-fdelay) - mstpctl-vlan-maxage (aliased to: bridge-stp-vlan-maxage) Bridge port attribute: - mstpctl-port-vlan-path-cost - mstpctl-port-vlan-priority Format for both bridge and brport attributes: attribute-name vlan-range=VALUE Signed-off-by: Julien Fortin --- ifupdown2/addons/bridge.py | 12 + ifupdown2/addons/mstpctl.py | 384 ++++++++++++++++++++---- ifupdown2/ifupdown/scheduler.py | 4 + ifupdown2/ifupdown/utils.py | 51 ++++ ifupdown2/ifupdownaddons/cache.py | 24 -- ifupdown2/ifupdownaddons/mstpctlutil.py | 334 ++++++++++++++------- 6 files changed, 620 insertions(+), 189 deletions(-) delete mode 100644 ifupdown2/ifupdownaddons/cache.py diff --git a/ifupdown2/addons/bridge.py b/ifupdown2/addons/bridge.py index 70e40a0b..e70710ee 100644 --- a/ifupdown2/addons/bridge.py +++ b/ifupdown2/addons/bridge.py @@ -820,6 +820,9 @@ def __init__(self, *args, **kargs): except Exception: self.bridge_vni_per_svi_limit = -1 + # There can only one vlan-aware bridge if PVRST mode is enabled + self.pvrst_vlan_aware_bridge = None + # Cumulus-check try: self.cumulus = "cumulus" in utils.exec_commandl(["lsb_release", "-a"]).lower() @@ -2865,6 +2868,15 @@ def up_bridge(self, ifaceobj, ifaceobj_getfunc): bridge_vlan_aware = self.up_check_bridge_vlan_aware(ifaceobj, ifaceobj_getfunc, link_just_created) + if utils.is_pvrst_enabled() and bridge_vlan_aware and self.pvrst_vlan_aware_bridge: + + if not (ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_l3VNI): + self.log_error(f"{ifname}: when PVRST is enabled there can only be one vlan-aware " + f"bridge on the system ({self.pvrst_vlan_aware_bridge})", ifaceobj) + + elif bridge_vlan_aware: + self.pvrst_vlan_aware_bridge = ifname + self.up_apply_bridge_settings(ifaceobj, link_just_created, bridge_vlan_aware) try: diff --git a/ifupdown2/addons/mstpctl.py b/ifupdown2/addons/mstpctl.py index 52d29de7..f5e1c5a4 100644 --- a/ifupdown2/addons/mstpctl.py +++ b/ifupdown2/addons/mstpctl.py @@ -12,6 +12,7 @@ from ifupdown2.lib.addon import Addon, AddonException from ifupdown2.ifupdown.iface import ifaceType, ifaceLinkKind, ifaceLinkPrivFlags, ifaceStatus + from ifupdown2.ifupdown.statemanager import statemanager_api as statemanager from ifupdown2.ifupdown.utils import utils import ifupdown2.ifupdown.ifupdownflags as ifupdownflags @@ -25,6 +26,7 @@ from lib.addon import Addon, AddonException from ifupdown.iface import ifaceType, ifaceLinkKind, ifaceLinkPrivFlags, ifaceStatus + from ifupdown.statemanager import statemanager_api as statemanager from ifupdown.utils import utils import ifupdown.ifupdownflags as ifupdownflags @@ -62,6 +64,7 @@ class mstpctl(Addon, moduleBase): "mstpctl-treeprio": { "help": "tree priority", "default": "32768", + "jsonAttr": "treeprio", "validvals": [ "0", "4096", "8192", "12288", "16384", "20480", "24576", "28672", "32768", @@ -114,7 +117,6 @@ class mstpctl(Addon, moduleBase): "mstpctl-forcevers": { "help": "bridge force stp version", "validvals": ["rstp", "stp", "mstp"], - "default": "rstp", "required": False, "jsonAttr": "forceProtocolVersion", "example": ["mstpctl-forcevers rstp"] @@ -122,7 +124,7 @@ class mstpctl(Addon, moduleBase): "mstpctl-portpathcost": { "help": "bridge port path cost", "validvals": [""], - "validrange": ["0", "65535"], + "validrange": ["0", "200000000"], "default": "0", "jsonAttr": "adminExtPortCost", "required": False, @@ -252,33 +254,119 @@ class mstpctl(Addon, moduleBase): "under a port: mstpctl-portbpdufilter yes" ] }, + "mstpctl-vlan-priority": { + "help": "Configure PVRST per vlan priority", + "required": False, + "default": 32768, + "example": [ + "mstpctl-vlan-priority 1-200=4096 201-400=8192", + "bridge-stp-vlan-priority 1-200=4096 201-400=8192" + ], + "aliases": ["bridge-stp-vlan-priority"] + }, + "mstpctl-vlan-hello": { + "help": "Configure PVRST per vlan hello time (1-10)", + "required": False, + "default": 2, + "example": [ + "mstpctl-vlan-hello 1-200=10 201-400=5", + "bridge-stp-vlan-hello 1-200=10 201-400=5" + ], + "aliases": ["bridge-stp-vlan-hello"] + }, + "mstpctl-vlan-fdelay": { + "help": "Configure PVRST per vlan forward delay (4-30)", + "required": False, + "default": 15, + "example": [ + "mstpctl-vlan-fdelay 1-200=10 201-400=20", + "bridge-stp-vlan-fdelay 1-200=10 201-400=20" + ], + "aliases": ["bridge-stp-vlan-fdelay"] + }, + "mstpctl-vlan-maxage": { + "help": "Configure PVRST per vlan max age (6-40)", + "required": False, + "default": 20, + "example": [ + "mstpctl-vlan-maxage 1-200=6 201-400=40", + "bridge-stp-vlan-maxage 1-200=6 201-400=40" + ], + "aliases": ["bridge-stp-vlan-maxage"] + }, + "mstpctl-port-vlan-path-cost": { + "help": "Sets the port cost of the interface. The default is 0. " + "mstpd supports only long mode; 32 bits for the path cost (1-200000000)", + "required": False, + "default": 0, + "example": [ + "mstpctl-port-vlan-path-cost 1-200=1000 201-400=4000", + ], + }, + "mstpctl-port-vlan-priority": { + "help": "Set port priority (0-240) for the given vlan range. Priority should be in multiples of 16", + "required": False, + "default": 0, + "example": [ + "mstpctl-port-vlan-priority 1-200=16 201-400=32", + ], + }, + "mstpctl-pvrst-mode": { + "help": "Configure MSTP PVRST mode", + "required": False, + "default": "off", + "example": [ + "mstpctl-pvrst-mode on", + "mstpctl-pvrst-mode off", + ] + } } } - # Maps mstp bridge attribute names to corresponding mstpctl commands - # XXX: This can be encoded in the modules dict above - _attrs_map = OrderedDict([('mstpctl-treeprio' , 'treeprio'), - ('mstpctl-ageing' , 'ageing'), - ('mstpctl-fdelay' , 'fdelay'), - ('mstpctl-maxage' , 'maxage'), - ('mstpctl-maxhops' , 'maxhops'), - ('mstpctl-txholdcount' , 'txholdcount'), - ('mstpctl-forcevers', 'forcevers'), - ('mstpctl-hello' , 'hello')]) - - # Maps mstp port attribute names to corresponding mstpctl commands - # XXX: This can be encoded in the modules dict above - _port_attrs_map = {'mstpctl-portpathcost' : 'portpathcost', - 'mstpctl-portadminedge' : 'portadminedge', - 'mstpctl-portautoedge' : 'portautoedge' , - 'mstpctl-portp2p' : 'portp2p', - 'mstpctl-portrestrrole' : 'portrestrrole', - 'mstpctl-portrestrtcn' : 'portrestrtcn', - 'mstpctl-bpduguard' : 'bpduguard', - 'mstpctl-treeportprio' : 'treeportprio', - 'mstpctl-treeportcost' : 'treeportcost', - 'mstpctl-portnetwork' : 'portnetwork', - 'mstpctl-portbpdufilter' : 'portbpdufilter'} + def get_attrs_map(self): + compatible_attributes = { + "mstpctl-ageing": "ageing", + "mstpctl-txholdcount": "txholdcount", + "mstpctl-forcevers": "forcevers", + } + if utils.is_pvrst_enabled(): + return compatible_attributes + else: + # merge and return compatible attribute dict with rstp attribute dict + return { + **{ + "mstpctl-treeprio": "treeprio", + "mstpctl-fdelay": "fdelay", + "mstpctl-maxage": "maxage", + "mstpctl-maxhops": "maxhops", + "mstpctl-hello": "hello" + }, + **compatible_attributes + } + + def get_port_attrs_map(self): + compatible_attributes = { + "mstpctl-portpathcost": "portpathcost", + "mstpctl-portadminedge": "portadminedge", + "mstpctl-portautoedge": "portautoedge", + "mstpctl-portp2p": "portp2p", + "mstpctl-portrestrrole": "portrestrrole", + "mstpctl-portrestrtcn": "portrestrtcn", + "mstpctl-bpduguard": "bpduguard", + "mstpctl-portnetwork": "portnetwork", + "mstpctl-portbpdufilter": "portbpdufilter" + } + if utils.is_pvrst_enabled(): + return compatible_attributes + else: + # merge and return compatible attribute dict with rstp attribute dict + return { + **{ + "mstpctl-treeportprio": "treeportprio", + "mstpctl-treeportcost": "treeportcost", + }, + **compatible_attributes + } def __init__(self, *args, **kargs): Addon.__init__(self) @@ -286,7 +374,7 @@ def __init__(self, *args, **kargs): if not os.path.exists('/sbin/mstpctl'): raise moduleNotSupported('module init failed: no /sbin/mstpctl found') self.name = self.__class__.__name__ - self.mstpctlcmd = None + self.mstpctlcmd = mstpctlutil() self.mstpd_running = (True if systemUtils.is_process_running('mstpd') else False) @@ -322,6 +410,8 @@ def __init__(self, *args, **kargs): ) ) + self.pvrst_mode = None + def syntax_check(self, ifaceobj, ifaceobj_getfunc): if ( self._is_bridge(ifaceobj) @@ -348,7 +438,6 @@ def get_dependent_ifacenames(self, ifaceobj, ifacenames_all=None, old_ifaceobjs= 'mstpctl-ports'), ifacenames_all) def get_dependent_ifacenames_running(self, ifaceobj): - self._init_command_handlers() if (self.cache.bridge_exists(ifaceobj.name) and not self.mstpctlcmd.mstpbridge_exists(ifaceobj.name)): return None @@ -356,7 +445,7 @@ def get_dependent_ifacenames_running(self, ifaceobj): def _get_bridge_port_attr_value(self, bridgename, portname, attr): json_attr = self.get_mod_subattr(attr, 'jsonAttr') - return self.mstpctlcmd.get_bridge_port_attr(bridgename, + return self.mstpctlcmd.get_bridge_port_attribute_value(bridgename, portname, json_attr) @@ -417,15 +506,14 @@ def _apply_bridge_settings(self, ifaceobj, ifaceobj_getfunc): check = False if ifupdownflags.flags.PERFMODE else True try: # set bridge attributes - for attrname, dstattrname in list(self._attrs_map.items()): + for attrname, dstattrname in list(self.get_attrs_map().items()): config_val = ifaceobj.get_attr_value_first(attrname) default_val = policymanager.policymanager_api.get_iface_default(module_name=self.__class__.__name__, ifname=ifaceobj.name, attr=attrname) if not default_val: default_val = self.get_mod_subattr(attrname, 'default') jsonAttr = self.get_mod_subattr(attrname, 'jsonAttr') try: - running_val = self.mstpctlcmd.get_bridge_attr( - ifaceobj.name, jsonAttr) + running_val = self.mstpctlcmd.get_bridge_attribute_value(ifaceobj.name, jsonAttr) except Exception: self.logger.info('%s: could not get running %s value' %(ifaceobj.name, attrname)) @@ -453,7 +541,7 @@ def _apply_bridge_settings(self, ifaceobj, ifaceobj_getfunc): if not bridgeports: return # set bridge port attributes - for attrname, dstattrname in list(self._port_attrs_map.items()): + for attrname, dstattrname in list(self.get_port_attrs_map().items()): config_val = ifaceobj.get_attr_value_first(attrname) default_val = self.get_mod_subattr(attrname,'default') if not config_val: @@ -572,7 +660,7 @@ def _apply_bridge_port_settings(self, ifaceobj, bvlan_aware, bridgename=None, try: self.mstpctlcmd.set_bridge_port_attr(bridgename, ifaceobj.name, - self._port_attrs_map[attr], + self.get_port_attrs_map()[attr], config_val, json_attr=json_attr) except Exception as e: @@ -593,9 +681,12 @@ def _apply_bridge_port_settings(self, ifaceobj, bvlan_aware, bridgename=None, applied ) return applied + + self._apply_port_pvrst_attributes(ifaceobj, bridgename, ifaceobj.name) + # set bridge port attributes return self._apply_bridge_port_settings_attributes_list( - list(self._port_attrs_map.items()), + list(self.get_port_attrs_map().items()), ifaceobj, bridgeifaceobj, bridgename, @@ -603,6 +694,9 @@ def _apply_bridge_port_settings(self, ifaceobj, bvlan_aware, bridgename=None, ) def _apply_bridge_port_settings_attributes_list(self, attributes_list, ifaceobj, bridgeifaceobj, bridgename, applied): + # if the brport was just added to an existing bridge it might not be cached. + self.mstpctlcmd.cache_port(bridgename, ifaceobj.name) + for attrname, dstattrname in attributes_list: config_val = ifaceobj.get_attr_value_first(attrname) default_val = self._get_default_val(attrname, ifaceobj, bridgeifaceobj) @@ -610,7 +704,7 @@ def _apply_bridge_port_settings_attributes_list(self, attributes_list, ifaceobj, # to see the running value, stp would have to be on # so we would have parsed mstpctl showportdetail json output try: - running_val = self.mstpctlcmd.get_bridge_port_attr(bridgename, + running_val = self.mstpctlcmd.get_bridge_port_attribute_value(bridgename, ifaceobj.name, jsonAttr) except Exception: self.logger.info('%s %s: could not get running %s value' @@ -629,9 +723,9 @@ def _apply_bridge_port_settings_attributes_list(self, attributes_list, ifaceobj, ifaceobj.name, dstattrname, config_val, json_attr=jsonAttr) applied = True except Exception as e: - self.log_error('%s: error setting %s (%s)' - %(ifaceobj.name, attrname, str(e)), ifaceobj, - raise_error=False) + self.log_error('%s: error setting %s (%s)' + %(ifaceobj.name, attrname, str(e)), ifaceobj, + raise_error=False) return applied def _apply_bridge_port_settings_all(self, ifaceobj, @@ -645,7 +739,6 @@ def _apply_bridge_port_settings_all(self, ifaceobj, return bvlan_aware = self.cache.bridge_is_vlan_aware(ifaceobj.name) - for bport in bridgeports: self.logger.info('%s: processing mstp config for port %s' %(ifaceobj.name, bport)) @@ -689,7 +782,6 @@ def _is_running_userspace_stp_state_on(self, bridgename): return False def _up(self, ifaceobj, ifaceobj_getfunc=None): - # bridge port specific: if ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT: bridgename = self.cache.get_master(ifaceobj.name) @@ -699,9 +791,11 @@ def _up(self, ifaceobj, ifaceobj_getfunc=None): bvlan_aware = self.cache.bridge_is_vlan_aware(bridgename) mstpd_running = self.mstpd_running stp_running_on = bool(self.cache.get_bridge_stp(bridgename)) + self.mstpctlcmd.batch_start() applied = self._apply_bridge_port_settings(ifaceobj, bvlan_aware, bridgename, None, stp_running_on, mstpd_running) + self.mstpctlcmd.batch_commit() if applied: ifaceobj.module_flags[self.name] = \ ifaceobj.module_flags.setdefault(self.name,0) | \ @@ -746,6 +840,7 @@ def _up(self, ifaceobj, ifaceobj_getfunc=None): if self.mstpd_running and stp: self.mstpctlcmd.batch_start() + self._apply_bridge_pvrst_settings(ifaceobj, ifaceobj_getfunc) self._apply_bridge_settings(ifaceobj, ifaceobj_getfunc) self._apply_bridge_port_settings_all(ifaceobj, ifaceobj_getfunc=ifaceobj_getfunc) @@ -755,6 +850,157 @@ def _up(self, ifaceobj, ifaceobj_getfunc=None): if porterr: raise AddonException(porterrstr) + + pvrst_attribute_cache_key = { + "mstpctl-vlan-priority": "priority", + "mstpctl-vlan-hello": "Hello_Time", + "mstpctl-vlan-fdelay": "Forward_Delay", + "mstpctl-vlan-maxage": "Max_Age", + } + + pvrst_port_attribute_cache_key = { + "mstpctl-port-vlan-path-cost": "AdminPortCost[Internal]", + "mstpctl-port-vlan-priority": "treeportprio", + } + + def get_running_bridge_pvrst_attr_value(self, bridge_name, attribute_name): + attr_value = {} + + for vid, data in (self.mstpctlcmd.get_bridge_attribute_value(bridge_name, attribute_name, as_string=False) or {}).items(): + attr_value[int(vid)] = data[self.pvrst_attribute_cache_key[attribute_name]] + + return attr_value + + def get_running_bridge_port_vlan_pvrst_attr_value(self, bridge_name, ifname, attribute_name): + attr_value = {} + + for vid, data in (self.mstpctlcmd.get_bridge_port_attribute_value(bridge_name, ifname, attribute_name, as_string=False) or {}).items(): + try: + attr_value[int(vid)] = data.get(self.pvrst_port_attribute_cache_key[attribute_name]) + except ValueError: + pass + + return attr_value + + def get_pvrst_attr_dict(self, ifaceobj, ifname, attribute_name, attribute_value): + if not attribute_value: + return {} + + config = {} + try: + for line in attribute_value: + for entry in line.split(): + vlans, value = entry.split("=") + + for vlan in utils.ranges_to_ints([vlans]): + config[vlan] = int(value) + except Exception as e: + self.log_error(f"{ifname}: {attribute_name}: {e}", ifaceobj=ifaceobj) + return config + + PVRST_ATTRIBUTES = ( + ("mstpctl-vlan-priority", "vlan-priority"), + ("mstpctl-vlan-hello", "vlan-hello"), + ("mstpctl-vlan-maxage", "vlan-maxage"), + ("mstpctl-vlan-fdelay", "vlan-fdelay"), + ) + + def pvrst_clag_check(self, ifname, ifaceobj_getfunc): + for obj in [item for sublist in ifaceobj_getfunc(None, all=True).values() for item in sublist]: + if any([attr for attr in obj.config.keys() if attr.startswith("clagd-")]): + raise AssertionError(f"{ifname}: 'mstpctl-pvrst-mode on' is conflicting with clag configuration on {obj.name}") + + def reset_pvrst_cache(self, ifname): + self.mstpctlcmd.reset_cache(ifname) + + def _apply_bridge_pvrst_settings(self, ifaceobj, ifaceobj_getfunc): + ifname = ifaceobj.name + + if not utils.is_pvrst_enabled(): + return + + if not self.cache.bridge_is_vlan_aware(ifname): + raise AssertionError(f"{ifname}: PVRST mode is not supported on traditional bridge") + + self.pvrst_clag_check(ifname, ifaceobj_getfunc) + self.reset_pvrst_cache(ifname) + + for attr, mstpctl_attr in self.PVRST_ATTRIBUTES: + config_value = self.get_pvrst_attr_dict( + ifaceobj, + ifname, + attr, + ifaceobj.get_attr_value(attr) + ) + + # the default value used when a vlan is removed from the bridge + default_value = policymanager.policymanager_api.get_iface_default( + module_name=self.__class__.__name__, + ifname=ifname, attr=attr + ) + if not default_value: + default_value = self.get_mod_subattr(attr, "default") + + running_value = self.get_running_bridge_pvrst_attr_value(ifname, attr) + + # calculate delta between running config and /e/n/i config + # then apply the new config and reset what was removed from /e/n/i + vlan_delta = self.compute_config_delta(config_value, running_value, default_value) + + for vlan_range, value in utils.group_keys_as_range(vlan_delta).items(): + self.mstpctlcmd.set_pvrst_attribute(mstpctl_attr, ifname, vlan_range, value) + + PVRST_PORT_ATTRIBUTES = ( + ("mstpctl-port-vlan-path-cost", "vlantreeportcost"), + ("mstpctl-port-vlan-priority", "vlantreeportprio"), + ) + + def _apply_port_pvrst_attributes(self, ifaceobj, bridge_name, ifname): + + if not utils.is_pvrst_enabled(): + return + + for attr, mstpctl_attr in self.PVRST_PORT_ATTRIBUTES: + config_value = self.get_pvrst_attr_dict( + ifaceobj, + ifname, + attr, + ifaceobj.get_attr_value(attr) + ) + + # the default value used when a vlan is removed from the bridge + default_value = policymanager.policymanager_api.get_iface_default( + module_name=self.__class__.__name__, + ifname=ifname, attr=attr + ) + if not default_value: + default_value = self.get_mod_subattr(attr, "default") + + running_value = self.get_running_bridge_port_vlan_pvrst_attr_value(bridge_name, ifname, attr) + + # calculate delta between running config and /e/n/i config + # then apply the new config and reset what was removed from /e/n/i + vlan_delta = self.compute_config_delta(config_value, running_value, default_value) + + for vlan_range, value in utils.group_keys_as_range(vlan_delta).items(): + self.mstpctlcmd.set_pvrst_port_attribute(mstpctl_attr, bridge_name, ifname, vlan_range, value) + + @staticmethod + def compute_config_delta(config_value, running_value, default_value): + delta = {} + + # Check for updates or additions + for key, value in config_value.items(): + if key not in running_value or running_value[key] != value: + delta[key] = value + + # Check for deletions + for key, value in running_value.items(): + if key not in config_value and value != default_value: + delta[key] = default_value + + return delta + def _down(self, ifaceobj, ifaceobj_getfunc=None): if not self._is_bridge(ifaceobj): return @@ -842,6 +1088,16 @@ def _query_check_bridge(self, ifaceobj, ifaceobjcurr, # list of attributes that are not supported currently blacklistedattrs = ['mstpctl-portpathcost', 'mstpctl-treeportprio', 'mstpctl-treeportcost'] + + if utils.is_pvrst_enabled(): + blacklistedattrs.extend([ + "mstpctl-pvrst-mode", + "mstpctl-vlan-maxage", + "mstpctl-vlan-fdelay", + "mstpctl-vlan-hello", + "mstpctl-vlan-priority" + ]) + if not self.cache.bridge_exists(ifaceobj.name): self.logger.debug('bridge %s does not exist' %ifaceobj.name) return @@ -901,7 +1157,7 @@ def _query_check_bridge(self, ifaceobj, ifaceobjcurr, conf = config_val[bport] jsonAttr = self.get_mod_subattr(k, 'jsonAttr') try: - running_val = self.mstpctlcmd.get_bridge_port_attr(ifaceobj.name, bport, jsonAttr) + running_val = self.mstpctlcmd.get_bridge_port_attribute_value(ifaceobj.name, bport, jsonAttr) except Exception: self.logger.info('%s %s: could not get running %s value' %(ifaceobj.name, bport, attr)) @@ -1022,7 +1278,7 @@ def _query_check_bridge_vxlan_port(self, ifaceobj, ifaceobjcurr, continue config_val = 'yes' try: - running_val = self.mstpctlcmd.get_bridge_port_attr(bifaceobj.name, + running_val = self.mstpctlcmd.get_bridge_port_attribute_value(bifaceobj.name, ifaceobj.name, jsonAttr) except Exception: self.logger.info('%s %s: could not get running %s value' @@ -1033,8 +1289,10 @@ def _query_check_bridge_vxlan_port(self, ifaceobj, ifaceobjcurr, 0 if running_val == config_val else 1) return - def _query_check_bridge_port(self, ifaceobj, ifaceobjcurr): + if utils.is_pvrst_enabled(): + return + if not self.cache.link_exists(ifaceobj.name): #self.logger.debug('bridge port %s does not exist' %ifaceobj.name) ifaceobjcurr.status = ifaceStatus.NOTFOUND @@ -1043,14 +1301,14 @@ def _query_check_bridge_port(self, ifaceobj, ifaceobjcurr): if not self.cache.link_is_bridge_port(ifaceobj.name): # mark all the bridge attributes as error ifaceobjcurr.check_n_update_config_with_status_many(ifaceobj, - list(self._port_attrs_map.keys()), 0) + list(self.get_port_attrs_map().keys()), 0) return bridgename = self.cache.get_master(ifaceobj.name) # list of attributes that are not supported currently - blacklistedattrs = ['mstpctl-portpathcost', + blacklistedattrs = [ 'mstpctl-treeportprio', 'mstpctl-treeportcost'] ifaceattrs = self.dict_key_subset(ifaceobj.config, - list(self._port_attrs_map.keys())) + list(self.get_port_attrs_map().keys())) if not ifaceattrs: return runningattrs = self.mstpctlcmd.get_bridge_attrs(ifaceobj.name) @@ -1076,12 +1334,35 @@ def _query_check_bridge_port(self, ifaceobj, ifaceobjcurr): def _query_check(self, ifaceobj, ifaceobjcurr, ifaceobj_getfunc=None): if self._is_bridge(ifaceobj): self._query_check_bridge(ifaceobj, ifaceobjcurr, ifaceobj_getfunc) + self._query_bridge_pvrst_attributes(ifaceobj, ifaceobjcurr) elif ifaceobj.link_kind & ifaceLinkKind.VXLAN: self._query_check_bridge_vxlan_port(ifaceobj, ifaceobjcurr, ifaceobj_getfunc) else: self._query_check_bridge_port(ifaceobj, ifaceobjcurr) + def _query_bridge_pvrst_attributes(self, ifaceobj, ifaceobjcurr): + + if not utils.is_pvrst_enabled(): + return + + ifname = ifaceobj.name + + for attr, mstpctl_attr in self.PVRST_ATTRIBUTES: + config_value = self.get_pvrst_attr_dict( + ifaceobj, + ifname, + attr, + ifaceobj.get_attr_value(attr) + ) + + running_value = self.get_running_bridge_pvrst_attr_value(ifname, attr) + + ifaceobjcurr.update_config_with_status( + attr, " ".join([f"{k}={v}" for k, v in utils.group_keys_as_range(running_value).items()]), + config_value != running_value + ) + def _query_bridge_port_attr(self, ifaceobjrunning, bridgename, attr, value_cmp): v = self._get_bridge_port_attr_value(bridgename, ifaceobjrunning.name, @@ -1301,10 +1582,6 @@ def get_ops(self): """ returns list of ops supported by this module """ return list(self._run_ops.keys()) - def _init_command_handlers(self): - if not self.mstpctlcmd: - self.mstpctlcmd = mstpctlutil() - def run(self, ifaceobj, operation, query_ifaceobj=None, ifaceobj_getfunc=None, **extra_args): """ run mstp configuration on the interface object passed as argument @@ -1323,11 +1600,10 @@ def run(self, ifaceobj, operation, query_ifaceobj=None, as user required state in ifaceobj. error otherwise. """ if ifaceobj.type == ifaceType.BRIDGE_VLAN: - return + return op_handler = self._run_ops.get(operation) if not op_handler: - return - self._init_command_handlers() + return if operation == 'query-checkcurr': op_handler(self, ifaceobj, query_ifaceobj, ifaceobj_getfunc=ifaceobj_getfunc) diff --git a/ifupdown2/ifupdown/scheduler.py b/ifupdown2/ifupdown/scheduler.py index 63b57130..032ecfa3 100644 --- a/ifupdown2/ifupdown/scheduler.py +++ b/ifupdown2/ifupdown/scheduler.py @@ -92,6 +92,10 @@ def run_iface_op(cls, ifupdownobj, ifaceobj, op, cenv=None): query_ifaceobj.set_state_n_status(ifaceState.from_str(op), ifaceStatus.NOTFOUND) return + + # Very ugly but necessary since we don't support global attributes + utils.is_pvrst_enabled(ifupdownobj.get_ifaceobjs, no_act="query" in op or "down" in op) + for mname in ifupdownobj.module_ops.get(op): m = ifupdownobj.modules.get(mname) err = 0 diff --git a/ifupdown2/ifupdown/utils.py b/ifupdown2/ifupdown/utils.py index 312967c8..338685e7 100644 --- a/ifupdown2/ifupdown/utils.py +++ b/ifupdown2/ifupdown/utils.py @@ -152,6 +152,26 @@ def mac_str_to_int(cls, hw_address): mac += int(i, 16) return mac + + PVRST_MODE = None + @classmethod + def is_pvrst_enabled(cls, ifaceobj_getfunc=None, no_act=False): + if cls.PVRST_MODE != None: + return cls.PVRST_MODE + + for obj_list in (ifaceobj_getfunc(None, all=True) or {}).values(): + for obj in obj_list: + if cls.get_boolean_from_string(obj.get_attr_value_first("mstpctl-pvrst-mode")): + cls.PVRST_MODE = True + if not no_act: + cls.exec_command("mstpctl setmodepvrst") + return cls.PVRST_MODE + + cls.PVRST_MODE = False + if not no_act: + cls.exec_command("mstpctl clearmodepvrst") + return cls.PVRST_MODE + @staticmethod def get_onff_from_onezero(value): if value in utils._onoff_onezero: @@ -556,6 +576,37 @@ def get_vlan_vnis_in_map(cls, vlan_vni_map): vnis.extend([vni]) return (vlans, vnis) + @staticmethod + def group_keys_as_range(input_dict): + output_dict = {} + + if not input_dict: + return output_dict + + sorted_items = sorted(input_dict.items()) + + current_group_key_start = sorted_items[0][0] + current_group_key_end = sorted_items[0][0] + current_group_value = sorted_items[0][1] + + for key, value in sorted_items[1:]: + if value == current_group_value and key == current_group_key_end + 1: + current_group_key_end = key + else: + group_key = f"{current_group_key_start}-{current_group_key_end}" \ + if current_group_key_start != current_group_key_end else str(current_group_key_start) + output_dict[group_key] = current_group_value + + current_group_key_start = key + current_group_key_end = key + current_group_value = value + + group_key = f"{current_group_key_start}-{current_group_key_end}" \ + if current_group_key_start != current_group_key_end else str(current_group_key_start) + output_dict[group_key] = current_group_value + + return output_dict + @classmethod def get_vni_mcastgrp_in_map(cls, vni_mcastgrp_map): vnid = {} diff --git a/ifupdown2/ifupdownaddons/cache.py b/ifupdown2/ifupdownaddons/cache.py deleted file mode 100644 index 6e3e8045..00000000 --- a/ifupdown2/ifupdownaddons/cache.py +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved. -# Author: Roopa Prabhu, roopa@cumulusnetworks.com -# - - -class MSTPAttrsCache(): - bridges = {} - - @classmethod - def get(cls, bridgename, default=None): - if bridgename in MSTPAttrsCache.bridges: - return MSTPAttrsCache.bridges[bridgename] - else: - return default - - @classmethod - def set(cls, bridgename, attrs): - MSTPAttrsCache.bridges[bridgename] = attrs - - @classmethod - def invalidate(cls): - MSTPAttrsCache.bridges = {} diff --git a/ifupdown2/ifupdownaddons/mstpctlutil.py b/ifupdown2/ifupdownaddons/mstpctlutil.py index c3755ca7..a79ed896 100644 --- a/ifupdown2/ifupdownaddons/mstpctlutil.py +++ b/ifupdown2/ifupdownaddons/mstpctlutil.py @@ -4,17 +4,18 @@ # Author: Roopa Prabhu, roopa@cumulusnetworks.com # +import operator +from functools import reduce + try: from ifupdown2.ifupdown.iface import * from ifupdown2.ifupdown.utils import utils - from ifupdown2.ifupdownaddons.cache import * from ifupdown2.ifupdownaddons.utilsbase import * except ImportError: from ifupdown.iface import * from ifupdown.utils import utils - from ifupdownaddons.cache import * from ifupdownaddons.utilsbase import * @@ -63,6 +64,9 @@ def __init__(self, *args, **kargs): self.__batch = [] self.__batch_mode = False + self.pvrst_mode = None + self.cache = {} + def __add_to_batch(self, cmd): self.__batch.append(cmd) @@ -117,134 +121,55 @@ def is_mstpd_running(self): else: return True - def _extract_bridge_port_prio(self, portid): + def _extract_bridge_port_prio(self, portid, pvrst=False): try: - return str(int(portid[0], 16) * 16) + if pvrst: + return int(portid[0], 16) * 16 + else: + return str(int(portid[0], 16) * 16) except Exception: return mstpctlutil._DEFAULT_PORT_PRIO - def _get_bridge_and_port_attrs_from_cache(self, bridgename): - attrs = MSTPAttrsCache.get(bridgename, None) - if attrs is not None: - return attrs - mstpctl_bridgeport_attrs_dict = {} - try: - cmd = [utils.mstpctl_cmd, - 'showportdetail', bridgename, 'json'] - output = utils.exec_commandl(cmd) - if not output: - MSTPAttrsCache.set(bridgename, mstpctl_bridgeport_attrs_dict) - return mstpctl_bridgeport_attrs_dict - except Exception as e: - self.logger.info(str(e)) - return mstpctl_bridgeport_attrs_dict - portname = bridgename # assigning portname to avoid an exception, in the exception handler - try: - mstpctl_bridge_cache = json.loads(output.strip("\n")) - for portname in list(mstpctl_bridge_cache.keys()): - for portid in list(mstpctl_bridge_cache[portname].keys()): - mstpctl_bridgeport_attrs_dict[portname] = {} - mstpctl_bridgeport_attrs_dict[portname]['treeportprio'] = self._extract_bridge_port_prio(portid) - for jsonAttr in list(mstpctl_bridge_cache[portname][portid].keys()): - jsonVal = mstpctl_bridge_cache[portname][portid][jsonAttr] - mstpctl_bridgeport_attrs_dict[portname][jsonAttr] = str(jsonVal) - MSTPAttrsCache.set(bridgename, mstpctl_bridgeport_attrs_dict) - except Exception as e: - self.logger.info('%s: cannot fetch mstpctl bridge port attributes: %s' % (portname, str(e))) - - mstpctl_bridge_attrs_dict = {} - try: - cmd = [utils.mstpctl_cmd, - 'showbridge', 'json', bridgename] - output = utils.exec_commandl(cmd) - if not output: - return mstpctl_bridge_attrs_dict - except Exception as e: - self.logger.info(str(e)) - return mstpctl_bridge_attrs_dict - try: - mstpctl_bridge_cache = json.loads(output.strip('\n')) - for jsonAttr in list(mstpctl_bridge_cache[bridgename].keys()): - mstpctl_bridge_attrs_dict[jsonAttr] = ( - str(mstpctl_bridge_cache[bridgename][jsonAttr])) - mstpctl_bridge_attrs_dict['treeprio'] = '%d' %( - int(mstpctl_bridge_attrs_dict.get('bridgeId', - '').split('.')[0], base=16) * 4096) - del mstpctl_bridge_attrs_dict['bridgeId'] - MSTPAttrsCache.bridges[bridgename].update(mstpctl_bridge_attrs_dict) - except Exception as e: - self.logger.info('%s: cannot fetch mstpctl bridge attributes: %s' % (bridgename, str(e))) - return MSTPAttrsCache.get(bridgename) - - def get_bridge_ports_attrs(self, bridgename): - return self._get_bridge_and_port_attrs_from_cache(bridgename) - - def get_bridge_port_attr(self, bridgename, portname, attrname): - attrs = self._get_bridge_and_port_attrs_from_cache(bridgename) - value = attrs.get(portname, {}).get(attrname, 'no') - if value == 'True' or value == 'true': - return 'yes' - return str(value) - - def update_bridge_port_cache(self, bridgename, portname, attrname, value): - attrs = self.get_bridge_ports_attrs(bridgename) - if not attrs: - attrs = {} - if portname not in attrs: - attrs[portname] = {} - attrs[portname][attrname] = value - MSTPAttrsCache.set(bridgename, attrs) - - def update_bridge_cache(self, bridgename, attrname, value): - attrs = self.get_bridge_ports_attrs(bridgename) - if not attrs: - attrs = {} - attrs[attrname] = value - MSTPAttrsCache.set(bridgename, attrs) - def set_bridge_port_attr(self, bridgename, portname, attrname, value, json_attr=None): - cache_value = self.get_bridge_port_attr(bridgename, portname, json_attr) + cache_value = self.get_bridge_port_attribute_value(bridgename, portname, json_attr) + if cache_value and cache_value == value: return + + if value in ("yes", "no", "on", "off"): + if utils.get_boolean_from_string(value) == utils.get_boolean_from_string(cache_value): + return + if attrname == 'treeportcost' or attrname == 'treeportprio': self.__execute_or_batch("set%s %s %s 0 %s" % (attrname, bridgename, portname, value)) else: self.__execute_or_batch("set%s %s %s %s" % (attrname, bridgename, portname, value)) if json_attr: - self.update_bridge_port_cache(bridgename, portname, json_attr, value) + self.update_cached_bridge_port_attribute(bridgename, portname, json_attr, value) def get_bridge_attrs(self, bridgename): bridgeattrs = {} try: - bridgeattrs = dict((k, self.get_bridge_attr(bridgename, v)) + bridgeattrs = dict((k, self.get_bridge_attribute_value(bridgename, v)) for k,v in list(self._bridge_jsonAttr_map.items())) except Exception as e: self.logger.debug(bridgeattrs) self.logger.debug(str(e)) return bridgeattrs - def get_bridge_attr(self, bridgename, attrname): - if attrname == 'bridgeId': - attrname = 'treeprio' - return self._get_bridge_and_port_attrs_from_cache(bridgename).get(attrname) - def set_bridge_attr(self, bridgename, attrname, attrvalue, check=True): - if check: - if attrname == 'treeprio': - attrvalue_curr = self.get_bridge_attr(bridgename, attrname) - else: - attrvalue_curr = self.get_bridge_attr(bridgename, - self._bridge_jsonAttr_map[attrname]) + attrvalue_curr = self.get_bridge_attribute_value(bridgename, self._bridge_jsonAttr_map[attrname]) + if attrvalue_curr and attrvalue_curr == attrvalue: return if attrname == 'treeprio': self.__execute_or_batch("set%s %s 0 %s" % (attrname, bridgename, attrvalue)) - self.update_bridge_cache(bridgename, attrname, str(attrvalue)) + self.update_cached_bridge_attribute(bridgename, attrname, str(attrvalue)) else: self.__execute_or_batch("set%s %s %s" % (attrname, bridgename, attrvalue)) - self.update_bridge_cache(bridgename, + self.update_cached_bridge_attribute(bridgename, self._bridge_jsonAttr_map[attrname], str(attrvalue)) @@ -258,7 +183,7 @@ def set_bridge_attrs(self, bridgename, attrdict, check=True): self.logger.warning('%s: %s' %(bridgename, str(e))) def get_bridge_treeprio(self, bridgename): - return self.get_bridge_attr(bridgename, 'treeprio') + return self.get_bridge_attribute_value(bridgename, 'treeprio') def set_bridge_treeprio(self, bridgename, attrvalue, check=True): if check: @@ -267,23 +192,210 @@ def set_bridge_treeprio(self, bridgename, attrvalue, check=True): return self.__execute_or_batch("settreeprio %s 0 %s" % (bridgename, str(attrvalue))) - self.update_bridge_cache(bridgename, 'treeprio', str(attrvalue)) + self.update_cached_bridge_attribute(bridgename, 'treeprio', str(attrvalue)) - def showbridge(self, bridgename=None): - if bridgename: - return utils.exec_command('%s showbridge %s' % - (utils.mstpctl_cmd, bridgename)) - else: - return utils.exec_command('%s showbridge' %utils.mstpctl_cmd) + def set_pvrst_attribute(self, attr, bridge_name, vlan_range, value): + self.__execute_or_batch( + f"set{attr} {bridge_name} {vlan_range} {value}" + ) - def showportdetail(self, bridgename): - return utils.exec_command('%s showportdetail %s' % - (utils.mstpctl_cmd, bridgename)) + def set_pvrst_port_attribute(self, attr, bridge_name, port_name, vlan_range, value): + self.__execute_or_batch( + f"set{attr} {bridge_name} {port_name} {vlan_range} {value}" + ) def mstpbridge_exists(self, bridgename): try: - utils.exec_command('%s showbridge %s' % + utils.exec_command('%s showstpbridge %s' % (utils.mstpctl_cmd, bridgename)) return True except Exception: return False + + def pvrst_on(self): + if not self.pvrst_mode: + utils.exec_command("mstpctl setmodepvrst") + self.pvrst_mode = True + else: + self.logger.debug("pvrst mode already enabled") + + def pvrst_off(self): + # if pvrst_mode == None we should still run clearmodepvrst + if self.pvrst_mode != False: + utils.exec_command("mstpctl clearmodepvrst") + self.pvrst_mode = False + else: + self.logger.debug("pvrst mode already disabled") + + + ############################################################################################# + ############################################################################################# + ############################################################################################# + + @staticmethod + def __get_showstpportdetail_json(bridge_name): + try: + return json.loads( + utils.exec_commandl([utils.mstpctl_cmd, "showstpportdetail", bridge_name, "json"]) + ) + except: + return {} + + @staticmethod + def __get_showstpbridge_json(bridge_name): + try: + return json.loads( + utils.exec_commandl([utils.mstpctl_cmd, "showstpbridge", "json", bridge_name]) + ) + except: + return {} + + def __get_bridge_data(self, bridge_name): + bridge_data = self.cache.get(bridge_name) + + if not bridge_data: + bridge_data = self.__get_showstpbridge_json(bridge_name) + bridge_port_details = self.__get_showstpportdetail_json(bridge_name) + + pvrst = bridge_data["protocol"] == "rapid-pvst" + + if not pvrst: + # Convert bridgeId into treeprio for backward compatibility + bridge_id = bridge_data["BridgeInfo"]["trees"]["cist"]["bridgeId"] + bridge_data["BridgeInfo"]["trees"]["cist"][ + "treeprio"] = f'{(int(bridge_id.split(".")[0], base=16) * 4096)}' + + # Convert portId into treeportprio for backward compatibility + for port_objects in bridge_port_details.values(): + for port_data in port_objects.values(): + port_id = port_data.get("portId") + + if port_id: + port_data["treeportprio"] = self._extract_bridge_port_prio(port_id, pvrst) + + bridge_data["ports"] = bridge_port_details + self.cache[bridge_name] = bridge_data + + return bridge_data + + def reset_cache(self, ifname): + try: + del self.cache[ifname] + except: + pass + + # The current patch will only update the backend/cache mechanism + # To be safe we want to support both mstpctl attribute names but also + # JSON keys, just in case they are still in use in the mstpctl addon. + bridge_attribute_to_json_key = { + "maxhops": ["BridgeInfo", "maxHops"], + "maxHops": ["BridgeInfo", "maxHops"], + + "ageing": ["BridgeInfo", "ageingTime"], + "ageingTime": ["BridgeInfo", "ageingTime"], + + "hello": ["BridgeInfo", "helloTime"], + "helloTime": ["BridgeInfo", "helloTime"], + + "maxage": ["BridgeInfo", "bridgeMaxAge"], + "maxAge": ["BridgeInfo", "bridgeMaxAge"], + "bridgeMaxAge": ["BridgeInfo", "bridgeMaxAge"], + + "fdelay": ["BridgeInfo", "bridgeFwdDelay"], + "fwdDelay": ["BridgeInfo", "bridgeFwdDelay"], + "bridgeFwdDelay": ["BridgeInfo", "bridgeFwdDelay"], + + "txholdcount": ["BridgeInfo", "txHoldCounter"], + "txHoldCounter": ["BridgeInfo", "txHoldCounter"], + + "forcevers": ["BridgeInfo", "forceProtocolVersion"], + "forceProtocolVersion": ["BridgeInfo", "forceProtocolVersion"], + + "treeprio": ["BridgeInfo", "trees", "cist", "treeprio"], + "bridgeId": ["BridgeInfo", "trees", "cist", "treeprio"], + + # PVRST attributes have special handling in mstpctl.py + "mstpctl-vlan-hello": ["BridgeInfo", "trees"], + "mstpctl-vlan-fdelay": ["BridgeInfo", "trees"], + "mstpctl-vlan-maxage": ["BridgeInfo", "trees"], + "mstpctl-vlan-priority": ["BridgeInfo", "trees"], + } + + def reduce_cache_get(self, bridge_name, path_list): + return reduce(operator.getitem, path_list, self.__get_bridge_data(bridge_name)) + + def get_bridge_attribute_value(self, bridge_name, bridge_attr_name, as_string=True): + path = self.bridge_attribute_to_json_key[bridge_attr_name] + + try: + attr_value = self.reduce_cache_get(bridge_name, path) + except KeyError: + attr_value = None + + return str(attr_value) if as_string else attr_value + + bridge_port_attribute_to_json_key = { + "treeportprio": ["1", "treeportprio"], + + "extPortCost": ["commonPortInfo", "PortCost"], + "treeportcost": ["commonPortInfo", "PortCost"], + + "networkPort": ["commonPortInfo", "networkPort"], + "portnetwork": ["commonPortInfo", "networkPort"], + + "autoEdgePort": ["commonPortInfo", "autoEdgePort"], + + "bpduguard": ["commonPortInfo", "bpduGuardPort"], + "bpduGuardPort": ["commonPortInfo", "bpduGuardPort"], + + "portrestrtcn": ["commonPortInfo", "restrictedTCN"], + "restrictedTcn": ["commonPortInfo", "restrictedTCN"], + + "adminEdgePort": ["commonPortInfo", "adminEdgePort"], + "portadminedge": ["commonPortInfo", "adminEdgePort"], + + "portrestrrole": ["commonPortInfo", "restrictedRole"], + "restrictedRole": ["commonPortInfo", "restrictedRole"], + + "portbpdufilter": ["commonPortInfo", "bpduFilterPort"], + "bpduFilterPort": ["commonPortInfo", "bpduFilterPort"], + + "adminExtPortCost": ["commonPortInfo", "adminExtPortCost"], + "adminPointToPoint": ["commonPortInfo", "adminPointToPoint"], + + # [] will return the entire dict + "mstpctl-port-vlan-path-cost": [], + "mstpctl-port-vlan-priority": [], + } + + def get_bridge_port_attribute_value(self, bridge_name, port_name, attr_name, as_string=True): + path = self.bridge_port_attribute_to_json_key[attr_name] + + try: + attr_value = self.reduce_cache_get(bridge_name, ["ports", port_name, *path]) + except KeyError: + attr_value = None + + if not as_string: + return attr_value + + attr_value_str = str(attr_value) + + if attr_value_str.lower() == "true": + attr_value_str = "yes" + + return attr_value_str + + def reduce_cache_set(self, bridge_name, map_list, value): + reduce(operator.getitem, map_list[:-1], self.__get_bridge_data(bridge_name))[map_list[-1]] = value + + def update_cached_bridge_port_attribute(self, bridge_name, port_name, attr_name, value): + path = self.bridge_port_attribute_to_json_key[attr_name] + self.reduce_cache_set(bridge_name, ["ports", port_name, *path], value) + + def update_cached_bridge_attribute(self, bridge_name, attr_name, value): + self.reduce_cache_set(bridge_name, self.bridge_attribute_to_json_key[attr_name], value) + + def cache_port(self, bridge_name, ifname): + if ifname not in self.cache.get(bridge_name, {}).get("ports", {}).keys(): + self.reset_cache(bridge_name) From 19e2dc97c13991096666f5f3047499aca150ef58 Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Tue, 31 May 2022 15:37:22 +0200 Subject: [PATCH 120/132] SONAR: mstpctl: Rename class "mstpctlFlags" to match the regular expression ^_?([A-Z_][a-zA-Z0-9]*|[a-z_][a-z0-9_]*)$ Signed-off-by: Julien Fortin --- ifupdown2/addons/mstpctl.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/ifupdown2/addons/mstpctl.py b/ifupdown2/addons/mstpctl.py index f5e1c5a4..ca4459e3 100644 --- a/ifupdown2/addons/mstpctl.py +++ b/ifupdown2/addons/mstpctl.py @@ -38,7 +38,7 @@ from ifupdown.exceptions import moduleNotSupported -class mstpctlFlags: +class MstpctlFlags: PORT_PROCESSED = 0x1 class mstpctl(Addon, moduleBase): @@ -764,7 +764,7 @@ def _apply_bridge_port_settings_all(self, ifaceobj, # Dont process bridge port if it already has been processed if (bportifaceobj.module_flags.get(self.name,0x0) & \ - mstpctlFlags.PORT_PROCESSED): + MstpctlFlags.PORT_PROCESSED): continue try: self._apply_bridge_port_settings(bportifaceobj, bvlan_aware, @@ -799,7 +799,7 @@ def _up(self, ifaceobj, ifaceobj_getfunc=None): if applied: ifaceobj.module_flags[self.name] = \ ifaceobj.module_flags.setdefault(self.name,0) | \ - mstpctlFlags.PORT_PROCESSED + MstpctlFlags.PORT_PROCESSED return elif not self._is_bridge(ifaceobj): @@ -1517,12 +1517,12 @@ def _query(self, ifaceobj, ifaceobj_getfunc=None, **kwargs): """ add default policy attributes supported by the module """ if not self._is_bridge(ifaceobj): if (ifaceobj.module_flags.get(self.name,0x0) & - mstpctlFlags.PORT_PROCESSED): + MstpctlFlags.PORT_PROCESSED): return self._query_bridge_port(ifaceobj, ifaceobj_getfunc) ifaceobj.module_flags[self.name] = ( ifaceobj.module_flags.setdefault(self.name,0) | - mstpctlFlags.PORT_PROCESSED) + MstpctlFlags.PORT_PROCESSED) return lowerinfs = ifaceobj.lowerifaces if not lowerinfs: @@ -1549,7 +1549,7 @@ def _query(self, ifaceobj, ifaceobj_getfunc=None, **kwargs): bportobjlist = ifaceobj_getfunc(port) for bportobj in bportobjlist: if (bportobj.module_flags.get(self.name,0x0) & - mstpctlFlags.PORT_PROCESSED): + MstpctlFlags.PORT_PROCESSED): continue if bportobj.get_attr_value_first('vxlan-id'): if config: @@ -1566,7 +1566,7 @@ def _query(self, ifaceobj, ifaceobj_getfunc=None, **kwargs): bportobj.replace_config(attr, 'yes') bportobj.module_flags[self.name] = ( bportobj.module_flags.setdefault(self.name,0) | - mstpctlFlags.PORT_PROCESSED) + MstpctlFlags.PORT_PROCESSED) if config: ifaceobj.replace_config(attr, config) From 638f26b1890d629b75b501dea290d657057576af Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Thu, 27 Jul 2023 16:55:55 +0200 Subject: [PATCH 121/132] addons: mstpctl: set mstpctl-port-vlan-priority default to 128 Signed-off-by: Julien Fortin --- ifupdown2/addons/mstpctl.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ifupdown2/addons/mstpctl.py b/ifupdown2/addons/mstpctl.py index ca4459e3..96c609b8 100644 --- a/ifupdown2/addons/mstpctl.py +++ b/ifupdown2/addons/mstpctl.py @@ -306,7 +306,7 @@ class mstpctl(Addon, moduleBase): "mstpctl-port-vlan-priority": { "help": "Set port priority (0-240) for the given vlan range. Priority should be in multiples of 16", "required": False, - "default": 0, + "default": 128, "example": [ "mstpctl-port-vlan-priority 1-200=16 201-400=32", ], From 272e3f89c405beae61482d48a387aab4fbb98653 Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Tue, 10 Oct 2023 13:34:22 +0200 Subject: [PATCH 122/132] addons: mstpctl: remove pvrst-clag check PVRST is now supported along side clag Signed-off-by: Julien Fortin --- ifupdown2/addons/mstpctl.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/ifupdown2/addons/mstpctl.py b/ifupdown2/addons/mstpctl.py index 96c609b8..09ce5d21 100644 --- a/ifupdown2/addons/mstpctl.py +++ b/ifupdown2/addons/mstpctl.py @@ -905,11 +905,6 @@ def get_pvrst_attr_dict(self, ifaceobj, ifname, attribute_name, attribute_value) ("mstpctl-vlan-fdelay", "vlan-fdelay"), ) - def pvrst_clag_check(self, ifname, ifaceobj_getfunc): - for obj in [item for sublist in ifaceobj_getfunc(None, all=True).values() for item in sublist]: - if any([attr for attr in obj.config.keys() if attr.startswith("clagd-")]): - raise AssertionError(f"{ifname}: 'mstpctl-pvrst-mode on' is conflicting with clag configuration on {obj.name}") - def reset_pvrst_cache(self, ifname): self.mstpctlcmd.reset_cache(ifname) @@ -922,7 +917,6 @@ def _apply_bridge_pvrst_settings(self, ifaceobj, ifaceobj_getfunc): if not self.cache.bridge_is_vlan_aware(ifname): raise AssertionError(f"{ifname}: PVRST mode is not supported on traditional bridge") - self.pvrst_clag_check(ifname, ifaceobj_getfunc) self.reset_pvrst_cache(ifname) for attr, mstpctl_attr in self.PVRST_ATTRIBUTES: From 2139f4ba3b912449f6f0cdc533895837f9c2cba4 Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Thu, 27 Jul 2023 17:14:12 +0200 Subject: [PATCH 123/132] addons: mstpctl: fix KeyError in query-running path Signed-off-by: Julien Fortin --- ifupdown2/addons/mstpctl.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ifupdown2/addons/mstpctl.py b/ifupdown2/addons/mstpctl.py index 09ce5d21..d94c544e 100644 --- a/ifupdown2/addons/mstpctl.py +++ b/ifupdown2/addons/mstpctl.py @@ -1052,8 +1052,7 @@ def _query_running_attrs(self, ifaceobjrunning, bridge_vlan_aware=False): 'mstpctl-portp2p', 'mstpctl-portrestrrole', 'mstpctl-portrestrtcn', - 'mstpctl-bpduguard', - '']: + 'mstpctl-bpduguard']: v = self._get_bridge_port_attr_value(ifaceobjrunning.name, p, attr) if v and v != 'no': From 610a621e1980a2bc6005ee2a380d2bd8904211bc Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Fri, 4 Aug 2023 15:29:38 +0200 Subject: [PATCH 124/132] addons: mstpctl: cache brport before applying user config Signed-off-by: Julien Fortin --- ifupdown2/addons/mstpctl.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/ifupdown2/addons/mstpctl.py b/ifupdown2/addons/mstpctl.py index d94c544e..99523543 100644 --- a/ifupdown2/addons/mstpctl.py +++ b/ifupdown2/addons/mstpctl.py @@ -540,6 +540,9 @@ def _apply_bridge_settings(self, ifaceobj, ifaceobj_getfunc): bridgeports = self._get_bridge_port_list(ifaceobj) if not bridgeports: return + + self.reset_pvrst_cache(ifaceobj.name) + # set bridge port attributes for attrname, dstattrname in list(self.get_port_attrs_map().items()): config_val = ifaceobj.get_attr_value_first(attrname) @@ -562,7 +565,7 @@ def _apply_bridge_settings(self, ifaceobj, ifaceobj_getfunc): if attr_value: default_val = attr_value break - + self.mstpctlcmd.cache_port(ifaceobj.name, port) self.mstpctlcmd.set_bridge_port_attr(ifaceobj.name, port, dstattrname, @@ -589,6 +592,7 @@ def _apply_bridge_settings(self, ifaceobj, ifaceobj_getfunc): if not os.path.exists('/sys/class/net/%s/brport' %port): continue json_attr = self.get_mod_subattr(attrname, 'jsonAttr') + self.mstpctlcmd.cache_port(ifaceobj.name, port) self.mstpctlcmd.set_bridge_port_attr(ifaceobj.name, port, dstattrname, @@ -658,6 +662,7 @@ def _apply_bridge_port_settings(self, ifaceobj, bvlan_aware, bridgename=None, config_val = self._get_default_val(attr, ifaceobj, bridgeifaceobj) try: + self.mstpctlcmd.cache_port(bridgename, ifaceobj.name) self.mstpctlcmd.set_bridge_port_attr(bridgename, ifaceobj.name, self.get_port_attrs_map()[attr], @@ -719,9 +724,9 @@ def _apply_bridge_port_settings_attributes_list(self, attributes_list, ifaceobj, continue try: - self.mstpctlcmd.set_bridge_port_attr(bridgename, + self.mstpctlcmd.set_bridge_port_attr(bridgename, ifaceobj.name, dstattrname, config_val, json_attr=jsonAttr) - applied = True + applied = True except Exception as e: self.log_error('%s: error setting %s (%s)' %(ifaceobj.name, attrname, str(e)), ifaceobj, From 8825c56f3babc8fcc17c76965fd5a5494e89c84b Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Sat, 12 Aug 2023 02:18:14 +0200 Subject: [PATCH 125/132] mstpctl: fix KeyError when fetching treeportprio Signed-off-by: Julien Fortin --- ifupdown2/addons/mstpctl.py | 1 + ifupdown2/ifupdownaddons/mstpctlutil.py | 24 +++++++++++++++++++----- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/ifupdown2/addons/mstpctl.py b/ifupdown2/addons/mstpctl.py index 99523543..fe7534c3 100644 --- a/ifupdown2/addons/mstpctl.py +++ b/ifupdown2/addons/mstpctl.py @@ -724,6 +724,7 @@ def _apply_bridge_port_settings_attributes_list(self, attributes_list, ifaceobj, continue try: + self.mstpctlcmd.cache_port(bridgename, ifaceobj.name) self.mstpctlcmd.set_bridge_port_attr(bridgename, ifaceobj.name, dstattrname, config_val, json_attr=jsonAttr) applied = True diff --git a/ifupdown2/ifupdownaddons/mstpctlutil.py b/ifupdown2/ifupdownaddons/mstpctlutil.py index a79ed896..d10eeae9 100644 --- a/ifupdown2/ifupdownaddons/mstpctlutil.py +++ b/ifupdown2/ifupdownaddons/mstpctlutil.py @@ -23,7 +23,8 @@ class mstpctlutil(utilsBase): """ This class contains helper methods to interact with mstpd using mstputils commands """ - _DEFAULT_PORT_PRIO = '128' + _DEFAULT_PORT_PRIO = "128" + _DEFAULT_PORT_PRIO_INT = 128 _cache_fill_done = False @@ -128,7 +129,10 @@ def _extract_bridge_port_prio(self, portid, pvrst=False): else: return str(int(portid[0], 16) * 16) except Exception: - return mstpctlutil._DEFAULT_PORT_PRIO + if pvrst: + return mstpctlutil._DEFAULT_PORT_PRIO_INT + else: + return mstpctlutil._DEFAULT_PORT_PRIO def set_bridge_port_attr(self, bridgename, portname, attrname, value, json_attr=None): cache_value = self.get_bridge_port_attribute_value(bridgename, portname, json_attr) @@ -267,11 +271,21 @@ def __get_bridge_data(self, bridge_name): # Convert portId into treeportprio for backward compatibility for port_objects in bridge_port_details.values(): + treeportprio = None + for port_data in port_objects.values(): port_id = port_data.get("portId") - if port_id: - port_data["treeportprio"] = self._extract_bridge_port_prio(port_id, pvrst) + port_data["treeportprio"] = treeportprio = self._extract_bridge_port_prio(port_id, pvrst) + + try: + prio = port_objects.get("1", {}).get("treeportprio", treeportprio) + if not prio: + prio = mstpctlutil._DEFAULT_PORT_PRIO_INT + + port_objects["commonPortInfo"]["treeportprio"] = prio + except: + port_objects["commonPortInfo"]["treeportprio"] = mstpctlutil._DEFAULT_PORT_PRIO_INT bridge_data["ports"] = bridge_port_details self.cache[bridge_name] = bridge_data @@ -335,7 +349,7 @@ def get_bridge_attribute_value(self, bridge_name, bridge_attr_name, as_string=Tr return str(attr_value) if as_string else attr_value bridge_port_attribute_to_json_key = { - "treeportprio": ["1", "treeportprio"], + "treeportprio": ["commonPortInfo", "treeportprio"], "extPortCost": ["commonPortInfo", "PortCost"], "treeportcost": ["commonPortInfo", "PortCost"], From bce1e1f7da53b90ccea80ad6fca9f7d7c59aece4 Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Wed, 16 Aug 2023 16:09:57 +0200 Subject: [PATCH 126/132] addons: mstpctl: handle mstpctl empty output Signed-off-by: Julien Fortin --- ifupdown2/addons/mstpctl.py | 13 ++-- ifupdown2/ifupdownaddons/mstpctlutil.py | 84 +++++++++++++++---------- 2 files changed, 56 insertions(+), 41 deletions(-) diff --git a/ifupdown2/addons/mstpctl.py b/ifupdown2/addons/mstpctl.py index fe7534c3..4270c2d2 100644 --- a/ifupdown2/addons/mstpctl.py +++ b/ifupdown2/addons/mstpctl.py @@ -533,7 +533,7 @@ def _apply_bridge_settings(self, ifaceobj, ifaceobj_getfunc): self.mstpctlcmd.set_bridge_attr(ifaceobj.name, dstattrname, config_val, check) except Exception as e: - self.logger.warning('%s' %str(e)) + self.logger.warning('%s: error while setting mstpctl attribute: %s' % (ifaceobj.name, str(e))) if self.cache.bridge_is_vlan_aware(ifaceobj.name): return @@ -565,7 +565,6 @@ def _apply_bridge_settings(self, ifaceobj, ifaceobj_getfunc): if attr_value: default_val = attr_value break - self.mstpctlcmd.cache_port(ifaceobj.name, port) self.mstpctlcmd.set_bridge_port_attr(ifaceobj.name, port, dstattrname, @@ -592,7 +591,6 @@ def _apply_bridge_settings(self, ifaceobj, ifaceobj_getfunc): if not os.path.exists('/sys/class/net/%s/brport' %port): continue json_attr = self.get_mod_subattr(attrname, 'jsonAttr') - self.mstpctlcmd.cache_port(ifaceobj.name, port) self.mstpctlcmd.set_bridge_port_attr(ifaceobj.name, port, dstattrname, @@ -602,8 +600,9 @@ def _apply_bridge_settings(self, ifaceobj, ifaceobj_getfunc): self.log_error('%s: error setting %s (%s)' %(ifaceobj.name, attrname, str(e)), ifaceobj, raise_error=False) + except Exception as e: - self.log_warn(str(e)) + self.log_warn("%s: error while applying bridge config: %s" % (ifaceobj.name, str(e))) def _get_default_val(self, attr, ifaceobj, bridgeifaceobj): if (self.set_default_mstp_vxlan_bridge_config @@ -653,6 +652,7 @@ def _apply_bridge_port_settings(self, ifaceobj, bvlan_aware, bridgename=None, if (not bvlan_aware and self.set_default_mstp_vxlan_bridge_config and (ifaceobj.link_kind & ifaceLinkKind.VXLAN)): + self.mstpctlcmd.cache_port(bridgename, ifaceobj.name) for attr in ( 'mstpctl-portbpdufilter', 'mstpctl-bpduguard', @@ -662,7 +662,6 @@ def _apply_bridge_port_settings(self, ifaceobj, bvlan_aware, bridgename=None, config_val = self._get_default_val(attr, ifaceobj, bridgeifaceobj) try: - self.mstpctlcmd.cache_port(bridgename, ifaceobj.name) self.mstpctlcmd.set_bridge_port_attr(bridgename, ifaceobj.name, self.get_port_attrs_map()[attr], @@ -724,7 +723,6 @@ def _apply_bridge_port_settings_attributes_list(self, attributes_list, ifaceobj, continue try: - self.mstpctlcmd.cache_port(bridgename, ifaceobj.name) self.mstpctlcmd.set_bridge_port_attr(bridgename, ifaceobj.name, dstattrname, config_val, json_attr=jsonAttr) applied = True @@ -732,6 +730,7 @@ def _apply_bridge_port_settings_attributes_list(self, attributes_list, ifaceobj, self.log_error('%s: error setting %s (%s)' %(ifaceobj.name, attrname, str(e)), ifaceobj, raise_error=False) + return applied def _apply_bridge_port_settings_all(self, ifaceobj, @@ -776,7 +775,7 @@ def _apply_bridge_port_settings_all(self, ifaceobj, self._apply_bridge_port_settings(bportifaceobj, bvlan_aware, ifaceobj.name, ifaceobj) except Exception as e: - self.log_warn(str(e)) + self.log_warn("%s: processing mstp config: %s" % (ifaceobj.name, str(e))) def _is_running_userspace_stp_state_on(self, bridgename): stp_state_file = '/sys/class/net/%s/bridge/stp_state' %bridgename diff --git a/ifupdown2/ifupdownaddons/mstpctlutil.py b/ifupdown2/ifupdownaddons/mstpctlutil.py index d10eeae9..cc0d00c7 100644 --- a/ifupdown2/ifupdownaddons/mstpctlutil.py +++ b/ifupdown2/ifupdownaddons/mstpctlutil.py @@ -254,6 +254,12 @@ def __get_showstpbridge_json(bridge_name): except: return {} + def _invalid_mstpctl_output(self, bridge_name, bridge_data, bridge_port_details): + self.logger.info("%s: mstpctl output is incomplete" % bridge_name) + self.logger.debug("bridge_data=%s" % bridge_data) + self.logger.debug("bridge_port_details=%s" % bridge_port_details) + self.cache[bridge_name] = {"ports": bridge_port_details} + def __get_bridge_data(self, bridge_name): bridge_data = self.cache.get(bridge_name) @@ -261,34 +267,46 @@ def __get_bridge_data(self, bridge_name): bridge_data = self.__get_showstpbridge_json(bridge_name) bridge_port_details = self.__get_showstpportdetail_json(bridge_name) - pvrst = bridge_data["protocol"] == "rapid-pvst" + protocol = bridge_data.get("protocol") + + if not protocol: + self._invalid_mstpctl_output(bridge_name, bridge_data, bridge_port_details) + return + + try: + pvrst = protocol == "rapid-pvst" - if not pvrst: - # Convert bridgeId into treeprio for backward compatibility - bridge_id = bridge_data["BridgeInfo"]["trees"]["cist"]["bridgeId"] - bridge_data["BridgeInfo"]["trees"]["cist"][ - "treeprio"] = f'{(int(bridge_id.split(".")[0], base=16) * 4096)}' + if not pvrst: + # Convert bridgeId into treeprio for backward compatibility + bridge_id = bridge_data["BridgeInfo"]["trees"]["cist"]["bridgeId"] + bridge_data["BridgeInfo"]["trees"]["cist"][ + "treeprio"] = f'{(int(bridge_id.split(".")[0], base=16) * 4096)}' - # Convert portId into treeportprio for backward compatibility - for port_objects in bridge_port_details.values(): - treeportprio = None - for port_data in port_objects.values(): - port_id = port_data.get("portId") - if port_id: - port_data["treeportprio"] = treeportprio = self._extract_bridge_port_prio(port_id, pvrst) + # Convert portId into treeportprio for backward compatibility + for port_objects in bridge_port_details.values(): + treeportprio = None - try: - prio = port_objects.get("1", {}).get("treeportprio", treeportprio) - if not prio: - prio = mstpctlutil._DEFAULT_PORT_PRIO_INT + for port_data in port_objects.values(): + port_id = port_data.get("portId") + if port_id: + port_data["treeportprio"] = treeportprio = self._extract_bridge_port_prio(port_id, pvrst) - port_objects["commonPortInfo"]["treeportprio"] = prio - except: - port_objects["commonPortInfo"]["treeportprio"] = mstpctlutil._DEFAULT_PORT_PRIO_INT + try: + prio = port_objects.get("1", {}).get("treeportprio", treeportprio) + if not prio: + prio = mstpctlutil._DEFAULT_PORT_PRIO_INT - bridge_data["ports"] = bridge_port_details - self.cache[bridge_name] = bridge_data + port_objects["commonPortInfo"]["treeportprio"] = prio + except: + port_objects["commonPortInfo"]["treeportprio"] = mstpctlutil._DEFAULT_PORT_PRIO_INT + + bridge_data["ports"] = bridge_port_details + self.cache[bridge_name] = bridge_data + + except KeyError: + self._invalid_mstpctl_output(bridge_name, bridge_data, bridge_port_details) + return return bridge_data @@ -336,15 +354,14 @@ def reset_cache(self, ifname): } def reduce_cache_get(self, bridge_name, path_list): - return reduce(operator.getitem, path_list, self.__get_bridge_data(bridge_name)) + try: + return reduce(operator.getitem, path_list, self.__get_bridge_data(bridge_name)) + except (TypeError, KeyError): + return None def get_bridge_attribute_value(self, bridge_name, bridge_attr_name, as_string=True): path = self.bridge_attribute_to_json_key[bridge_attr_name] - - try: - attr_value = self.reduce_cache_get(bridge_name, path) - except KeyError: - attr_value = None + attr_value = self.reduce_cache_get(bridge_name, path) return str(attr_value) if as_string else attr_value @@ -384,11 +401,7 @@ def get_bridge_attribute_value(self, bridge_name, bridge_attr_name, as_string=Tr def get_bridge_port_attribute_value(self, bridge_name, port_name, attr_name, as_string=True): path = self.bridge_port_attribute_to_json_key[attr_name] - - try: - attr_value = self.reduce_cache_get(bridge_name, ["ports", port_name, *path]) - except KeyError: - attr_value = None + attr_value = self.reduce_cache_get(bridge_name, ["ports", port_name, *path]) if not as_string: return attr_value @@ -401,7 +414,10 @@ def get_bridge_port_attribute_value(self, bridge_name, port_name, attr_name, as_ return attr_value_str def reduce_cache_set(self, bridge_name, map_list, value): - reduce(operator.getitem, map_list[:-1], self.__get_bridge_data(bridge_name))[map_list[-1]] = value + try: + reduce(operator.getitem, map_list[:-1], self.__get_bridge_data(bridge_name))[map_list[-1]] = value + except KeyError: + pass def update_cached_bridge_port_attribute(self, bridge_name, port_name, attr_name, value): path = self.bridge_port_attribute_to_json_key[attr_name] From 81e349fb1658efe987b484abb803d4e3b5a65eaa Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Tue, 31 May 2022 15:45:51 +0200 Subject: [PATCH 127/132] SONAR: mstpctl: Remove unused local variable "jsonAttr" Signed-off-by: Julien Fortin --- ifupdown2/addons/mstpctl.py | 1 - 1 file changed, 1 deletion(-) diff --git a/ifupdown2/addons/mstpctl.py b/ifupdown2/addons/mstpctl.py index 4270c2d2..6d05d6b8 100644 --- a/ifupdown2/addons/mstpctl.py +++ b/ifupdown2/addons/mstpctl.py @@ -1501,7 +1501,6 @@ def _query_bridge_port(self, ifaceobj, ifaceobj_getfunc=None): for attr in ('mstpctl-portbpdufilter', 'mstpctl-bpduguard', 'mstpctl-portadminedge'): - jsonAttr = self.get_mod_subattr(attr, 'jsonAttr') config_val = ifaceobj.get_attr_value_first(attr) if config_val or not ifupdownflags.flags.WITHDEFAULTS: continue From 636fa07940edc869a526c9f3c37db8f7b021b98e Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Tue, 31 May 2022 15:44:43 +0200 Subject: [PATCH 128/132] SONAR: mstpctl: Remove unused local variable "mstpctlcmdattrname" Signed-off-by: Julien Fortin --- ifupdown2/addons/mstpctl.py | 1 - 1 file changed, 1 deletion(-) diff --git a/ifupdown2/addons/mstpctl.py b/ifupdown2/addons/mstpctl.py index 6d05d6b8..be77e9af 100644 --- a/ifupdown2/addons/mstpctl.py +++ b/ifupdown2/addons/mstpctl.py @@ -1212,7 +1212,6 @@ def _query_check_bridge(self, ifaceobj, ifaceobjcurr, # Now, look at port attributes # derive the mstpctlcmd attr name #mstpctlcmdattrname = k[12:] if k[:12] == 'mstpctl-port' else k[8:] - mstpctlcmdattrname = k[8:] # for port attributes, the attributes are in a list # = From cf8c7f256b4dc82d1c2bb6638bf9d743760cacf0 Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Sun, 1 Dec 2024 19:30:30 -0500 Subject: [PATCH 129/132] addons: mstpctl: SONAR cleanups Signed-off-by: Julien Fortin --- ifupdown2/addons/mstpctl.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/ifupdown2/addons/mstpctl.py b/ifupdown2/addons/mstpctl.py index be77e9af..c79e8cee 100644 --- a/ifupdown2/addons/mstpctl.py +++ b/ifupdown2/addons/mstpctl.py @@ -615,7 +615,7 @@ def _get_default_val(self, attr, ifaceobj, bridgeifaceobj): ): try: config_val = bridgeifaceobj.get_attr_value_first(attr) - except Exception as e: + except Exception: config_val = None if config_val: if ifaceobj.name not in [v.split('=')[0] for v in config_val.split()]: @@ -635,7 +635,6 @@ def _apply_bridge_port_settings(self, ifaceobj, bvlan_aware, bridgename=None, bridgeifaceobj=None, stp_running_on=True, mstpd_running=True): - check = False if ifupdownflags.flags.PERFMODE else True applied = False if not bridgename and bridgeifaceobj: bridgename = bridgeifaceobj.name @@ -1026,7 +1025,6 @@ def _query_running_attrs(self, ifaceobjrunning, bridge_vlan_aware=False): if k == 'stp' or not v: continue if k == 'ports': - ports = list(v.keys()) continue attrname = 'mstpctl-' + k if (v and v != self.get_mod_subattr(attrname, 'default') From b627f325e4dde43e605ab03c0c6a78112f7f9b12 Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Tue, 1 Aug 2023 01:40:12 +0200 Subject: [PATCH 130/132] utils: catch mstpctl setmodepvrst exception and log debug Signed-off-by: Julien Fortin --- ifupdown2/ifupdown/utils.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/ifupdown2/ifupdown/utils.py b/ifupdown2/ifupdown/utils.py index 338685e7..3a97b725 100644 --- a/ifupdown2/ifupdown/utils.py +++ b/ifupdown2/ifupdown/utils.py @@ -164,12 +164,18 @@ def is_pvrst_enabled(cls, ifaceobj_getfunc=None, no_act=False): if cls.get_boolean_from_string(obj.get_attr_value_first("mstpctl-pvrst-mode")): cls.PVRST_MODE = True if not no_act: - cls.exec_command("mstpctl setmodepvrst") + try: + cls.exec_command("mstpctl setmodepvrst") + except Exception as e: + cls.logger.debug("mstpctl setmodepvrst failed: %s" % str(e)) return cls.PVRST_MODE cls.PVRST_MODE = False if not no_act: - cls.exec_command("mstpctl clearmodepvrst") + try: + cls.exec_command("mstpctl clearmodepvrst") + except Exception as e: + cls.logger.debug("mstpctl clearmodepvrst failed: %s" % str(e)) return cls.PVRST_MODE @staticmethod From 9efd8a22d2535bc9cf309e5f3443805775ac0a7d Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Mon, 2 Dec 2024 16:58:09 -0500 Subject: [PATCH 131/132] sync with downstream repo Signed-off-by: Julien Fortin --- ifupdown2/ifupdown/main.py | 2 +- ifupdown2/sbin/start-networking | 2 +- setup.py | 7 +++++-- tests/__init__.py | 0 4 files changed, 7 insertions(+), 4 deletions(-) create mode 100644 tests/__init__.py diff --git a/ifupdown2/ifupdown/main.py b/ifupdown2/ifupdown/main.py index b6a8143d..b2ba540c 100644 --- a/ifupdown2/ifupdown/main.py +++ b/ifupdown2/ifupdown/main.py @@ -84,7 +84,7 @@ def main(self, stdin_buffer=None): if not str(e): return 1 # if args and args.debug: - raise + # raise # else: if log: log.error('main exception: ' + str(e)) diff --git a/ifupdown2/sbin/start-networking b/ifupdown2/sbin/start-networking index 180a1fe5..8b8a3f12 100755 --- a/ifupdown2/sbin/start-networking +++ b/ifupdown2/sbin/start-networking @@ -140,7 +140,7 @@ start) /var/lib/ifupdown2/hooks.d/start-networking-post-init-hook.sh fi ;; - + stop) if [ "$SKIP_DOWN_AT_SYSRESET" = "yes" ]; then SYSRESET=0 diff --git a/setup.py b/setup.py index b9d918bb..c4b3e7f3 100755 --- a/setup.py +++ b/setup.py @@ -7,13 +7,13 @@ from setuptools import find_packages INSTALL_REQUIRES = [ + 'daemon' ] DATA_FILES = [ ('/etc/default/', ['etc/default/networking']), ('/etc/network/ifupdown2/', ['etc/network/ifupdown2/addons.conf']), ('/etc/network/ifupdown2/', ['etc/network/ifupdown2/ifupdown2.conf']), - ('/usr/share/ifupdown2/sbin/', ['ifupdown2/sbin/start-networking']) ] SCRIPTS = [] @@ -29,7 +29,10 @@ def build_deb_package(): return False -if not build_deb_package(): +if build_deb_package(): + DATA_FILES.append(('/usr/share/ifupdown2/', ['ifupdown2/ifupdown2d'])) + DATA_FILES.append(('/usr/share/ifupdown2/sbin/', ['ifupdown2/sbin/start-networking'])) +else: ENTRY_POINTS = { 'console_scripts': [ 'ifup = ifupdown2.__main__:main', diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 00000000..e69de29b From 834bd6db936eb3f55f56a8b565b93b33cda0bae3 Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Fri, 13 Dec 2024 18:31:12 +0100 Subject: [PATCH 132/132] debian: changelog: new entry for version 3.9.0 Signed-off-by: Julien Fortin --- debian/changelog | 32 ++++++++++++++++++++++++++++++++ ifupdown2/__init__.py | 2 +- setup.py | 2 +- 3 files changed, 34 insertions(+), 2 deletions(-) diff --git a/debian/changelog b/debian/changelog index c1adcdee..ed0d6622 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,35 @@ +ifupdown2 (3.9.0) unstable; urgency=medium + + * New: ifreload: new --diff cli argument: only reload delta between /e/n/i + * New: Support for Per-VLAN Rapid Spanning Tree attributes: + mstpctl-pvrst-mode (on/off): Enable/disable PVRST mode + mstpctl-vlan-priority (range 4096-32768) + mstpctl-vlan-hello (range 1-10) + mstpctl-vlan-fdelay (range 4-30) + mstpctl-vlan-maxage (range 6-40) + mstpctl-port-vlan-path-cost (range 1-200000000) + mstpctl-port-vlan-priority (range 0-240) + * New: Get default mac address from policy file as 'address' iface_default + * New: Enable per vlan snooping when config mcqv4src + * New: Add vxlan hopping filter + * New: Add support for setting mac addresses via iface_defaults policy + * New: Reset mac address on switch port when 'hwaddress' is removed from eni + * New: Policy "dhclient_no_wait_on_reload": dhclient won't wait (default off) + * Fix: Bring DHCP config down if link-down yes is set + * Fix: Various code cleanups (SonarQube) + * Fix: Macvlan/VRR: set accept_dad=0 before link up + * Fix: Flush DHCP lease on boot up + * Fix: Disable persistent debug log for ifquery + * Fix: + * Fix: Vxlan clear last fdb entry when remoteip is removed from user config + * Fix: Vxlan reset local and group ip when removed from user config + * Fix: Re-applying link-speed to reset link-lanes to default when removed + * Fix: Missing json import in networkinterfaces.py and vxlan.py + * Fix: Nlmanager Invalid operation on null-like value range_flag + * Deprecated: remove bridge-hashel default value + + -- Julien Fortin Wed, 04 Dec 2024 23:42:00 -0800 + ifupdown2 (3.3.0) unstable; urgency=medium * New: performance improvement: replace glob.glob with os.listdir diff --git a/ifupdown2/__init__.py b/ifupdown2/__init__.py index cba93fa3..bba25fe3 100644 --- a/ifupdown2/__init__.py +++ b/ifupdown2/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- -__version__ = '3.0.0' +__version__ = '3.9.0' # Copyright (C) 2014,2015,2016,2017,2018,2019,2020 Cumulus Networks, Inc. All rights reserved # diff --git a/setup.py b/setup.py index c4b3e7f3..b555c7b2 100755 --- a/setup.py +++ b/setup.py @@ -65,7 +65,7 @@ def build_deb_package(): name='ifupdown2', packages=find_packages(), url='https://github.com/CumulusNetworks/ifupdown2', - version='3.0.0', + version='3.9.0', data_files=DATA_FILES, setup_requires=['setuptools'], scripts=SCRIPTS,