From 29020b6d1ffa617676c2ea3c6eb38a8f90039eb2 Mon Sep 17 00:00:00 2001 From: Christoph Heiss Date: Mon, 14 Jul 2025 14:03:13 +0200 Subject: [PATCH 1/9] nlmanager: nlpacket: add support for decoding IFLA_PROP_LIST Pretty straight forward, it's just a list of the usual netlink length-type-data structure. Signed-off-by: Christoph Heiss --- ifupdown2/nlmanager/nlpacket.py | 63 +++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/ifupdown2/nlmanager/nlpacket.py b/ifupdown2/nlmanager/nlpacket.py index ed463c20..efc4c4ea 100644 --- a/ifupdown2/nlmanager/nlpacket.py +++ b/ifupdown2/nlmanager/nlpacket.py @@ -3464,6 +3464,49 @@ def get_pretty_value(self, obj=None): return value_pretty +class AttributeLinkPropList(Attribute): + """ + IFLA_PROP_LIST - list of (additional) interface properties. + """ + def __init__(self, atype, string, family, logger): + Attribute.__init__(self, atype, string, logger) + self.family = family + + def decode(self, parent_msg, data): + self.decode_length_type(data) + self.value = {} + + data = self.data[4:] + + while data: + (sub_attr_length, sub_attr_type) = unpack('=HH', data[:4]) + sub_attr_end = padded_length(sub_attr_length) + + if not sub_attr_length: + self.log.error('parsed a zero length sub-attr') + continue + + (attr_name, AttrClass) = Link.attribute_to_class[sub_attr_type] + attr = AttrClass(sub_attr_type, attr_name, self.family, self.log) + attr.decode(self, data[:sub_attr_end]) + + if sub_attr_type in self.value: + self.value[sub_attr_type].append(attr.value) + else: + self.value[sub_attr_type] = [attr.value] + + data = data[sub_attr_end:] + + def get_pretty_value(self, obj=None): + if obj and callable(obj): + return obj(self.value) + + value_pretty = {} + for (sub_key, sub_value) in self.value.items(): + sub_key_pretty = "(%2d) %s" % (sub_key, Link.attribute_to_class[sub_key][0]) + value_pretty[sub_key_pretty] = sub_value + + return value_pretty class NetlinkPacket(object): """ @@ -4163,6 +4206,16 @@ class Link(NetlinkPacket, NetlinkPacket_IFLA_LINKINFO_Attributes): IFLA_NEW_IFINDEX = 49 IFLA_MIN_MTU = 50 IFLA_MAX_MTU = 51 + IFLA_PROP_LIST = 52 + IFLA_ALT_IFNAME = 53 + IFLA_PERM_ADDRESS = 54 + IFLA_PROTO_DOWN_REASON = 55 + IFLA_PARENT_DEV_NAME = 56 + IFLA_PARENT_DEV_BUS_NAME = 57 + IFLA_GRO_MAX_SIZE = 58 + IFLA_TSO_MAX_SIZE = 59 + IFLA_TSO_MAX_SEGS = 60 + IFLA_ALLMULTI = 61 attribute_to_class = { IFLA_UNSPEC : ('IFLA_UNSPEC', AttributeGeneric), @@ -4217,6 +4270,16 @@ class Link(NetlinkPacket, NetlinkPacket_IFLA_LINKINFO_Attributes): IFLA_NEW_IFINDEX : ('IFLA_NEW_IFINDEX', AttributeFourByteValue), IFLA_MIN_MTU : ('IFLA_MIN_MTU', AttributeFourByteValue), IFLA_MAX_MTU : ('IFLA_MAX_MTU', AttributeFourByteValue), + IFLA_PROP_LIST : ('IFLA_PROP_LIST', AttributeLinkPropList), + IFLA_ALT_IFNAME : ('IFLA_ALT_IFNAME', AttributeString), + IFLA_PERM_ADDRESS : ('IFLA_PERM_ADDRESS', AttributeMACAddress), + IFLA_PROTO_DOWN_REASON : ('IFLA_PROTO_DOWN_REASON', AttributeFourByteValue), + IFLA_PARENT_DEV_NAME : ('IFLA_PARENT_DEV_NAME', AttributeString), + IFLA_PARENT_DEV_BUS_NAME : ('IFLA_PARENT_DEV_BUS_NAME', AttributeString), + IFLA_GRO_MAX_SIZE : ('IFLA_GRO_MAX_SIZE', AttributeFourByteValue), + IFLA_TSO_MAX_SIZE : ('IFLA_TSO_MAX_SIZE', AttributeFourByteValue), + IFLA_TSO_MAX_SEGS : ('IFLA_TSO_MAX_SEGS', AttributeFourByteValue), + IFLA_ALLMULTI : ('IFLA_ALLMULTI', AttributeFourByteValue), } # Link flags From 88c4ed8325da6c316b94109063a95bffc8afdb05 Mon Sep 17 00:00:00 2001 From: Christoph Heiss Date: Mon, 14 Jul 2025 14:06:11 +0200 Subject: [PATCH 2/9] nlcache: cache links under interface altnames too This ensures that each detected link gets cached under their primary interface name as well as each respective altname. This makes lookups for either name transparent, making code using the cache easier to reason about. Signed-off-by: Christoph Heiss --- ifupdown2/lib/nlcache.py | 44 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/ifupdown2/lib/nlcache.py b/ifupdown2/lib/nlcache.py index bbd2a8bc..6248b471 100644 --- a/ifupdown2/lib/nlcache.py +++ b/ifupdown2/lib/nlcache.py @@ -1219,6 +1219,13 @@ def add_link(self, link): # dictionaries if the master has changed or was un-enslaved. old_ifla_master = None + try: + proplist = link.get_attribute_value(Link.IFLA_PROP_LIST) + ifaltnames = proplist[Link.IFLA_ALT_IFNAME] + except (AttributeError, TypeError): + # no altnames no this link + ifaltnames = [] + with self._cache_lock: # do we have a wait event registered for RTM_NEWLINK this ifname @@ -1242,6 +1249,14 @@ def add_link(self, link): self._link_cache[ifname] = link + # For each altname, also cache the reference to the `Link` object + # it under that name. + # _cache_lock already ensures no concurrent access to the same + # interface through different names. + for altname in ifaltnames: + log.debug(f'registering {altname} as altname for {ifname}') + self._link_cache[altname] = link + ###################################################### # update helper dictionaries and handle link renamed # ###################################################### @@ -1253,6 +1268,9 @@ def add_link(self, link): # in get_ifname/get_ifindex/get_master to do the work. self._ifindex_by_ifname[ifname] = ifindex + # For mapping ifname -> ifindex, also consider altnames + for altname in ifaltnames: + self._ifindex_by_ifname[altname] = ifindex rename_detected = False old_ifname_entry_for_ifindex = self._ifname_by_ifindex.get(ifindex) @@ -1263,6 +1281,7 @@ def add_link(self, link): # renamed. We need to update the cache accordingly. rename_detected = True + # ifindex will just map to the primary ifname self._ifname_by_ifindex[ifindex] = ifname if rename_detected: @@ -1501,9 +1520,17 @@ def remove_link(self, link, link_ifname=None, link_ifindex=None): self._ignore_rtm_newlinkq.remove(ifname) except ValueError: pass + + try: + proplist = link.get_attribute_value(Link.IFLA_PROP_LIST) + ifaltnames = proplist[Link.IFLA_ALT_IFNAME] + except (AttributeError, TypeError): + # no altnames no this link + ifaltnames = [] else: ifname = link_ifname ifindex = link_ifindex + ifaltnames = [] link_ifla_master = None # when an enslaved device is removed we receive the RTM_DELLINK @@ -1532,6 +1559,17 @@ def remove_link(self, link, link_ifname=None, link_ifindex=None): # KeyError means that the link doesn't exists in the cache log.debug('del _link_cache: KeyError ifname: %s' % ifname) + # also delete altnames + for altname in ifaltnames: + try: + del self._link_cache[altname] + except KeyError: + # link is not present under the altname in the cache + log.debug(f'{altname} not present in _link_cache as altname for {ifname}?') + pass + + # for the rest of caches here, only the primary ifname is ever used + try: # like in __unslave_nolock() we need to make sure that all deleted link # have their bridge-vlans and _slaves_master entries cleared. @@ -1560,6 +1598,12 @@ def remove_link(self, link, link_ifname=None, link_ifindex=None): except KeyError: log.debug('del _ifindex_by_ifname: KeyError ifname: %s' % ifname) + for altname in ifaltnames: + try: + del self._ifindex_by_ifname[altname] + except KeyError: + log.debug('del _ifindex_by_ifname: KeyError ifaltname: %s' % altname) + try: del self._addr_cache[ifname] except KeyError: From 89d544d95ca8af407152a0bbadba8253b9728b1c Mon Sep 17 00:00:00 2001 From: Christoph Heiss Date: Mon, 14 Jul 2025 14:05:06 +0200 Subject: [PATCH 3/9] main: re-use lower-level netlink function to check for link existence Signed-off-by: Christoph Heiss --- ifupdown2/ifupdown/ifupdownmain.py | 7 +++++-- ifupdown2/lib/nlcache.py | 5 +++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/ifupdown2/ifupdown/ifupdownmain.py b/ifupdown2/ifupdown/ifupdownmain.py index 51f54609..807c8460 100644 --- a/ifupdown2/ifupdown/ifupdownmain.py +++ b/ifupdown2/ifupdown/ifupdownmain.py @@ -228,7 +228,7 @@ def log_error(self, str): pass def link_exists(self, ifacename): - return os.path.exists('/sys/class/net/%s' %ifacename) + return self.netlink.link_exists(ifacename) def __init__(self, config={}, args=None, daemon=False, force=False, dryrun=False, nowait=False, @@ -1879,7 +1879,10 @@ def check_running_configuration(self, filtered_ifacenames, all=False): auto = False break - if not ifupdownflags.flags.DRYRUN and auto and not os.path.exists("/sys/class/net/%s" % ifname) and not self._is_ifaceobj_bridge_vlan(ifaceobj_list): + if (not ifupdownflags.flags.DRYRUN + and auto + and not self.link_exists(ifname) + and not self._is_ifaceobj_bridge_vlan(ifaceobj_list)): self.logger.warning("%s: interface not recognized - please check interface configuration" % ifname) def _is_ifaceobj_bridge_vlan(self, ifaceobj_list): diff --git a/ifupdown2/lib/nlcache.py b/ifupdown2/lib/nlcache.py index 6248b471..4962c947 100644 --- a/ifupdown2/lib/nlcache.py +++ b/ifupdown2/lib/nlcache.py @@ -3183,6 +3183,11 @@ def link_set_brport_with_info_slave_data_dry_run(self, ifname, kind, ifla_info_d self.log_info_ifname_dry_run(ifname, "netlink: ip link set dev %s: bridge port attributes" % ifname) self.logger.debug("attributes: %s" % ifla_info_slave_data) + ### + + def link_exists(self, ifname): + return self.cache.link_exists(ifname) + ############################################################################ # ADDRESS ############################################################################ From cbfc1ec5d8a347f08102bb95cd229d586ca3e18c Mon Sep 17 00:00:00 2001 From: Christoph Heiss Date: Mon, 14 Jul 2025 14:11:06 +0200 Subject: [PATCH 4/9] main: create interface objects under primary name and altnames This enables transparent look-up of interfaces, enabling users of `ifaceobjdict` and `ifaceobjcurrdict` to look up interface by either name. Signed-off-by: Christoph Heiss --- ifupdown2/ifupdown/ifupdownmain.py | 16 ++++++--- ifupdown2/lib/nlcache.py | 53 ++++++++++++++++++++++++++++++ ifupdown2/nlmanager/nlmanager.py | 15 +++++++++ 3 files changed, 80 insertions(+), 4 deletions(-) diff --git a/ifupdown2/ifupdown/ifupdownmain.py b/ifupdown2/ifupdown/ifupdownmain.py index 807c8460..ff5631ed 100644 --- a/ifupdown2/ifupdown/ifupdownmain.py +++ b/ifupdown2/ifupdown/ifupdownmain.py @@ -498,14 +498,18 @@ def create_n_save_ifaceobj(self, ifacename, priv_flags=None, increfcnt=False): """ creates a iface object and adds it to the iface dictionary """ ifaceobj = iface() - ifaceobj.name = ifacename + ifaceobj.name = self.netlink.link_translate_altname(ifacename) ifaceobj.priv_flags = priv_flags ifaceobj.auto = True if not self._link_master_slave: ifaceobj.link_type = ifaceLinkType.LINK_NA if increfcnt: ifaceobj.inc_refcnt() + self.ifaceobjdict[ifacename] = [ifaceobj] + for altname in self.netlink.link_get_altnames(ifacename): + self.ifaceobjdict[ifacename] = [ifaceobj] + return ifaceobj def create_n_save_ifaceobjcurr(self, ifaceobj): @@ -513,13 +517,17 @@ def create_n_save_ifaceobjcurr(self, ifaceobj): dict containing current iface objects """ ifaceobjcurr = iface() - ifaceobjcurr.name = ifaceobj.name + ifaceobjcurr.name = self.netlink.link_translate_altname(ifaceobj.name) ifaceobjcurr.type = ifaceobj.type ifaceobjcurr.lowerifaces = ifaceobj.lowerifaces ifaceobjcurr.priv_flags = copy.deepcopy(ifaceobj.priv_flags) ifaceobjcurr.auto = ifaceobj.auto + self.ifaceobjcurrdict.setdefault(ifaceobj.name, []).append(ifaceobjcurr) + for altname in self.netlink.link_get_altnames(ifaceobj.name): + self.ifaceobjcurrdict.setdefault(altname, []).append(ifaceobjcurr) + return ifaceobjcurr def get_ifaceobjcurr(self, ifacename, idx=0): @@ -722,7 +730,7 @@ def query_lowerifaces(self, ifaceobj, ops, ifacenames, old_ifaceobjs): self.logger.warning("%s: %s: error getting dependent interfaces (%s)" % (ifaceobj.name, module, str(e))) dlist = None if dlist: ret_dlist.extend(dlist) - return list(set(ret_dlist)) + return self.netlink.link_translate_altnames(list(set(ret_dlist))) def query_upperifaces(self, ifaceobj, ops, ifacenames, type=None): """ Gets iface upperifaces by calling into respective modules """ @@ -746,7 +754,7 @@ def query_upperifaces(self, ifaceobj, ops, ifacenames, type=None): ulist = None pass if ulist: ret_ulist.extend(ulist) - return list(set(ret_ulist)) + return self.netlink.link_translate_altnames(list(set(ret_ulist))) def _remove_circular_veth_dependencies(self, ifaceobj, dlist): # if ifaceobj isn't a veth link, ignore it. diff --git a/ifupdown2/lib/nlcache.py b/ifupdown2/lib/nlcache.py index 4962c947..891524e2 100644 --- a/ifupdown2/lib/nlcache.py +++ b/ifupdown2/lib/nlcache.py @@ -804,6 +804,44 @@ def get_link_info_slave_data_attribute(self, ifname, info_slave_data_attribute, except TypeError as e: return self.__handle_type_error(inspect.currentframe().f_code.co_name, ifname, str(e), return_value=default) + def link_get_altnames(self, ifname): + """ + Returns the list of altnames for the given interface. + :param ifname: + :return: list[str] + """ + proplist = self.get_link_attribute(ifname, Link.IFLA_PROP_LIST, default={}) + if Link.IFLA_ALT_IFNAME in proplist: + return proplist[Link.IFLA_ALT_IFNAME] + return [] + + def link_translate_altname(self, ifname): + """ + Translates a interface name into the respective primary interface name, + if needed. Effectively no-op if ifname is already the primary name. + :param ifname: interface name + :return: primary interface names + """ + if ifname: + return self.get_link_attribute(ifname, Link.IFLA_IFNAME, default=ifname) + return None + + def link_translate_altnames(self, ifnames): + """ + Translates all interface altnames in the given list into their respective + primary ifnames. + :param ifnames: list of interface names + :return: list of interface names which only contains primary interface names + """ + if ifnames is None: + return None + + with self._cache_lock: + return list(map( + lambda ifname: self.get_link_attribute(ifname, Link.IFLA_IFNAME, default=ifname), + ifnames + )) + ################ # MASTER & SLAVE ################ @@ -1625,6 +1663,12 @@ def remove_link(self, link, link_ifname=None, link_ifindex=None): log.debug('_masters_and_slaves[if%s].remove(%s): KeyError' % (link_ifla_master, ifname)) def _address_get_ifname_and_ifindex(self, addr): + """ + Returns the primary ifname and ifindex of the interface the given address + belongs too. + :param addr: address to inspect + :return: ifname and ifindex the specified address belongs to + """ ifindex = addr.ifindex label = addr.get_attribute_value(Address.IFA_LABEL) @@ -3188,6 +3232,15 @@ def link_set_brport_with_info_slave_data_dry_run(self, ifname, kind, ifla_info_d def link_exists(self, ifname): return self.cache.link_exists(ifname) + def link_get_altnames(self, ifname): + return self.cache.link_get_altnames(ifname) + + def link_translate_altname(self, ifname): + return self.cache.link_translate_altname(ifname) + + def link_translate_altnames(self, ifnames): + return self.cache.link_translate_altnames(ifnames) + ############################################################################ # ADDRESS ############################################################################ diff --git a/ifupdown2/nlmanager/nlmanager.py b/ifupdown2/nlmanager/nlmanager.py index 5e0cd8e2..d57ddcf1 100644 --- a/ifupdown2/nlmanager/nlmanager.py +++ b/ifupdown2/nlmanager/nlmanager.py @@ -584,6 +584,21 @@ def get_iface_name(self, ifindex): return iface.attributes[Link.IFLA_IFNAME].get_pretty_value(str) return None + def get_iface_altnames(self, ifindex): + """ + Returns the list of altnames for the specified interface. + :param ifindex: Index of the network interface + :return: List of interface altnames + """ + iface = self._get_iface_by_index(ifindex) + + if iface and Link.IFLA_PROP_LIST in iface.attributes: + proplist = iface.get_attribute_value(Link.IFLA_PROP_LIST) + if Link.IFLA_ALT_IFNAME in proplist: + return proplist[Link.IFLA_ALT_IFNAME] + + return [] + def link_dump(self, ifname=None): debug = RTM_GETLINK in self.debug msg = Link(RTM_GETLINK, debug, use_color=self.use_color) From 7f2efb211b050d5739e195e1f92647fa7846cd5c Mon Sep 17 00:00:00 2001 From: Christoph Heiss Date: Mon, 14 Jul 2025 14:02:02 +0200 Subject: [PATCH 5/9] addons: bridge: drop duplicated method This method is already inherited from the `Addon` class. Signed-off-by: Christoph Heiss --- ifupdown2/addons/bridge.py | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/ifupdown2/addons/bridge.py b/ifupdown2/addons/bridge.py index 4d6c091c..7c09ef5e 100644 --- a/ifupdown2/addons/bridge.py +++ b/ifupdown2/addons/bridge.py @@ -1133,20 +1133,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 From a7eea31e797d5bff69eb0025357b6a94edcb151e Mon Sep 17 00:00:00 2001 From: Christoph Heiss Date: Mon, 14 Jul 2025 14:19:13 +0200 Subject: [PATCH 6/9] addon: translate altnames in `bridge-ports` to primary interface names Enables using the `bridge` addon with altnames. Signed-off-by: Christoph Heiss --- ifupdown2/lib/addon.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/ifupdown2/lib/addon.py b/ifupdown2/lib/addon.py index 47e42c76..77056b25 100644 --- a/ifupdown2/lib/addon.py +++ b/ifupdown2/lib/addon.py @@ -166,13 +166,13 @@ def __check_l3vni_bridge(self, ifaceobj): and len(self._get_ifaceobj_bridge_ports(ifaceobj, as_list=True)) == 1: ifaceobj.link_privflags |= ifaceLinkPrivFlags.BRIDGE_l3VNI - @staticmethod - def _get_ifaceobj_bridge_ports(ifaceobj, as_list=False): + def _get_ifaceobj_bridge_ports(self, ifaceobj, as_list=False): bridge_ports = [] for brport in ifaceobj.get_attr_value('bridge-ports') or []: if brport != 'none': bridge_ports.extend(brport.split()) + bridge_ports = self.cache.link_translate_altnames(bridge_ports) if as_list: return bridge_ports @@ -185,14 +185,15 @@ def _get_bridge_port_list(self, ifaceobj): # of parsing port expr again port_list = ifaceobj.lowerifaces if port_list: - return port_list + return self.cache.link_translate_altnames(port_list) + ports = self._get_ifaceobj_bridge_ports(ifaceobj) if ports: + # _get_ifaceobj_bridge_ports() already translates altnames return self.parse_port_list(ifaceobj.name, ports) else: return None - class AddonWithIpBlackList(Addon): try: ip_blacklist = [ipnetwork.IPNetwork(ip).ip for ip in policymanager.policymanager_api.get_module_globals( From 6e3eac51a0c2214a3fe22753632b922b627ee3e7 Mon Sep 17 00:00:00 2001 From: Christoph Heiss Date: Mon, 14 Jul 2025 14:19:56 +0200 Subject: [PATCH 7/9] addons: bond: translate altnames in config items to primary interface names Enables using the `bond` addon with altnames. Signed-off-by: Christoph Heiss --- ifupdown2/addons/bond.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ifupdown2/addons/bond.py b/ifupdown2/addons/bond.py index f2d38de5..1dae0190 100644 --- a/ifupdown2/addons/bond.py +++ b/ifupdown2/addons/bond.py @@ -277,6 +277,7 @@ def __init__(self, *args, **kargs): except Exception as e: self.logger.info("bond: error while loading bonding module: %s" % str(e)) + # get_ifindex() always returns the primary ifname, so no need for translating self._bond_attr_ifquery_check_translate_func[Link.IFLA_BOND_PRIMARY] = self.cache.get_ifindex self._bond_attr_set_list = self._bond_attr_set_list + (('bond-primary', Link.IFLA_BOND_PRIMARY, self.cache.get_ifindex),) @@ -292,7 +293,7 @@ def __init__(self, *args, **kargs): def get_bond_slaves(self, ifaceobj): # bond-ports aliases should be translated to bond-slaves - return ifaceobj.get_attr_value_first('bond-slaves') + return self.cache.link_translate_altname(ifaceobj.get_attr_value_first('bond-slaves')) def _is_bond(self, ifaceobj): # at first link_kind is not set but once ifupdownmain From 55b5f4e64b578abbb0d1fbe7d921bef51ea274d3 Mon Sep 17 00:00:00 2001 From: Christoph Heiss Date: Mon, 14 Jul 2025 14:20:50 +0200 Subject: [PATCH 8/9] addons: vlan: translate altnames in config items to primary interface names Enables using the `vlan` addon with altnames. Signed-off-by: Christoph Heiss --- ifupdown2/addons/vlan.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/ifupdown2/addons/vlan.py b/ifupdown2/addons/vlan.py index 4380dd8a..a53b2f9d 100644 --- a/ifupdown2/addons/vlan.py +++ b/ifupdown2/addons/vlan.py @@ -64,8 +64,17 @@ def __init__(self, *args, **kargs): Addon.__init__(self) moduleBase.__init__(self, *args, **kargs) + def _get_vlan_raw_device_first(self, ifaceobj): + """ + Returns the ifname of the `vlan-raw-device` property. Translates + altnames to the primary ifname if needed. + :param ifaceobj: interface config object + :return: `vlan-raw-device` ifname + """ + return self.cache.link_translate_altname(ifaceobj.get_attr_value_first('vlan-raw-device')) + def _is_vlan_device(self, ifaceobj): - vlan_raw_device = ifaceobj.get_attr_value_first('vlan-raw-device') + vlan_raw_device = self._get_vlan_raw_device_first(ifaceobj) if vlan_raw_device: return True elif '.' in ifaceobj.name: @@ -176,7 +185,7 @@ def _up(self, ifaceobj): vlan_exists = self.cache.link_exists(ifaceobj.name) if vlan_exists: - user_vlan_raw_device = ifaceobj.get_attr_value_first('vlan-raw-device') + user_vlan_raw_device = self._get_vlan_raw_device_first(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: @@ -239,7 +248,7 @@ def _query_check(self, ifaceobj, ifaceobjcurr): ifaceobjcurr.update_config_with_status( 'vlan-raw-device', cached_vlan_raw_device, - cached_vlan_raw_device != ifaceobj.get_attr_value_first('vlan-raw-device') + cached_vlan_raw_device != self._get_vlan_raw_device_first(ifaceobj) ) # From 235d7d00d668571c13546b2682dd84423ffe006a Mon Sep 17 00:00:00 2001 From: Christoph Heiss Date: Mon, 14 Jul 2025 14:21:11 +0200 Subject: [PATCH 9/9] addons: ovs: translate altnames in config items to primary interface names Enables using the `openvswitch` addon with altnames. Signed-off-by: Christoph Heiss --- ifupdown2/addons/openvswitch.py | 1 + ifupdown2/addons/openvswitch_port.py | 31 +++++++++++++++++++++------- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/ifupdown2/addons/openvswitch.py b/ifupdown2/addons/openvswitch.py index 0a124ba2..0b64e06f 100644 --- a/ifupdown2/addons/openvswitch.py +++ b/ifupdown2/addons/openvswitch.py @@ -103,6 +103,7 @@ def _get_ovs_ports (self, ifaceobj): ovs_ports.extend(port.split()) if ovs_ports: + ovs_ports = self.cache.link_translate_altnames(ovs_ports) return self.parse_port_list(ifaceobj.name, ' '.join(ovs_ports)) else: return None diff --git a/ifupdown2/addons/openvswitch_port.py b/ifupdown2/addons/openvswitch_port.py index ae8549ac..02bd737e 100644 --- a/ifupdown2/addons/openvswitch_port.py +++ b/ifupdown2/addons/openvswitch_port.py @@ -94,15 +94,28 @@ def __init__ (self, *args, **kargs): def _is_ovs_port (self, ifaceobj): ovstype = ifaceobj.get_attr_value_first ('ovs-type') - ovsbridge = ifaceobj.get_attr_value_first ('ovs-bridge') + ovsbridge = self._get_ovs_bridge(ifaceobj) if ovstype and ovsbridge: return True return False + def _get_ovs_bridge(self, ifaceobj): + """ + Returns the ifname of the `ovs-bridge` property. Translates altnames + to the primary ifname if needed. + :param ifaceobj: interface config object + :return: `ovs-bridge` ifname + """ + ifname = ifaceobj.get_attr_value_first('ovs-bridge') + if ifname: + return self.cache.link_translate_altname(ifname) + return None + def _get_bond_ifaces (self, ifaceobj): ovs_bonds = ifaceobj.get_attr_value_first ('ovs-bonds') if ovs_bonds: - return sorted (ovs_bonds.split ()) + ovs_bonds = self.cache.link_translate_altnames(ovs_bonds.split()) + return sorted(ovs_bonds) return None def _ovs_vsctl(self, ifaceobj, cmdlist): @@ -129,10 +142,13 @@ def _ovs_vsctl(self, ifaceobj, cmdlist): def _addport (self, ifaceobj): iface = ifaceobj.name - ovsbridge = ifaceobj.get_attr_value_first ('ovs-bridge') + ovsbridge = self._get_ovs_bridge(ifaceobj) ovsoptions = ifaceobj.get_attr_value_first ('ovs-options') ovstype = ifaceobj.get_attr_value_first ('ovs-type') - ovsbonds = ifaceobj.get_attr_value_first ('ovs-bonds') + + ovsbonds_list = self._get_bond_ifaces(ifaceobj) + ovsbonds = ' '.join(ovsbonds_list) if ovsbonds_list else None + ovsextra = ifaceobj.get_attr_value('ovs-extra') cmd_list = [] @@ -183,11 +199,10 @@ def _addport (self, ifaceobj): #mtu ovsmtu = ifaceobj.get_attr_value_first ('ovs-mtu') - ovsbonds_list = self._get_bond_ifaces(ifaceobj) if ovsmtu is not None: #we can't set mtu on bond fake interface, we apply it on slaves interfaces if ovstype == 'OVSBond' and ovsbonds_list is not None: - for slave in ovsbonds_list: + for slave in ovsbonds_list: cmd = "set Interface %s mtu_request=%s"%(slave,ovsmtu) cmd_list.append(cmd) @@ -207,7 +222,7 @@ def _addport (self, ifaceobj): def _delport (self, ifaceobj): iface = ifaceobj.name - ovsbridge = ifaceobj.get_attr_value_first ('ovs-bridge') + ovsbridge = self._get_ovs_bridge(ifaceobj) cmd = "--if-exists del-port %s %s"%(ovsbridge, iface) self._ovs_vsctl(ifaceobj, [cmd]) @@ -219,7 +234,7 @@ def get_dependent_ifacenames(self, ifaceobj, ifacenames_all=None, old_ifaceobjs= ifaceobj.link_privflags |= ifaceLinkPrivFlags.OPENVSWITCH - ovsbridge = ifaceobj.get_attr_value_first ('ovs-bridge') + ovsbridge = self._get_ovs_bridge(ifaceobj) return [ovsbridge] def _up (self, ifaceobj):