]> git.proxmox.com Git - mirror_ifupdown2.git/blobdiff - ifupdown2/addons/bond.py
Add support for xmit-hash-policy vlan+srcmac
[mirror_ifupdown2.git] / ifupdown2 / addons / bond.py
index 394192d00d65dbc990a730c58bc6434d12e2ba31..6eb4a7f203792b008b1d873fefeeefd12e36f031 100644 (file)
@@ -65,7 +65,8 @@ class bond(Addon, moduleBase):
                     "1", "layer3+4",
                     "2", "layer2+3",
                     "3", "encap2+3",
-                    "4", "encap3+4"
+                    "4", "encap3+4",
+                    "5", "vlan+srcmac"
                 ],
                 "default": "layer2",
                 "example": ["bond-xmit-hash-policy layer2"]
@@ -142,7 +143,7 @@ class bond(Addon, moduleBase):
                 "example": [
                     "bond-slaves swp1 swp2",
                     "bond-slaves glob swp1-2",
-                    "bond-slaves regex (swp[1|2)"
+                    "bond-slaves regex (swp[1|2])"
                 ],
                 "aliases": ["bond-ports"]
             },
@@ -162,6 +163,20 @@ class bond(Addon, moduleBase):
                 "help": "Control which slave interface is "
                         "preferred active member",
                 "example": ["bond-primary swp1"]
+            },
+            "bond-primary-reselect": {
+                "help": "bond primary reselect",
+                "validvals": [
+                    "0", "always",
+                    "1", "better",
+                    "2", "failure",
+                ],
+                "example": ["bond-primary-reselect failure"]
+            },
+            "es-sys-mac": {
+                "help": "evpn-mh: system mac address",
+                "validvals": ["<mac>", ],
+                "example": ["es-sys-mac 00:00:00:00:00:42"],
             }
         }
     }
@@ -175,6 +190,7 @@ class bond(Addon, moduleBase):
         'bond-min-links': Link.IFLA_BOND_MIN_LINKS,
         'bond-num-grat-arp': Link.IFLA_BOND_NUM_PEER_NOTIF,
         'bond-num-unsol-na': Link.IFLA_BOND_NUM_PEER_NOTIF,
+        'es-sys-mac': Link.IFLA_BOND_AD_ACTOR_SYSTEM,
         'bond-ad-sys-mac-addr': Link.IFLA_BOND_AD_ACTOR_SYSTEM,
         'bond-ad-actor-system': Link.IFLA_BOND_AD_ACTOR_SYSTEM,
         'bond-ad-sys-priority': Link.IFLA_BOND_AD_ACTOR_SYS_PRIO,
@@ -182,7 +198,9 @@ class bond(Addon, moduleBase):
         'bond-lacp-bypass-allow': Link.IFLA_BOND_AD_LACP_BYPASS,
         'bond-updelay': Link.IFLA_BOND_UPDELAY,
         'bond-downdelay': Link.IFLA_BOND_DOWNDELAY,
-        'bond-primary': Link.IFLA_BOND_PRIMARY
+        'bond-primary': Link.IFLA_BOND_PRIMARY,
+        'bond-primary-reselect': Link.IFLA_BOND_PRIMARY_RESELECT
+
     }
 
     # ifquery-check attr dictionary with callable object to translate user data to netlink format
@@ -199,6 +217,7 @@ class bond(Addon, moduleBase):
         Link.IFLA_BOND_AD_LACP_BYPASS: lambda x: int(utils.get_boolean_from_string(x)),
         Link.IFLA_BOND_UPDELAY: int,
         Link.IFLA_BOND_DOWNDELAY: int,
+        Link.IFLA_BOND_PRIMARY_RESELECT: lambda x: Link.ifla_bond_primary_reselect_tbl[x],
         # Link.IFLA_BOND_PRIMARY: self.netlink.get_ifname is added in __init__()
     }
 
@@ -219,8 +238,10 @@ class bond(Addon, moduleBase):
         ('bond-use-carrier', Link.IFLA_BOND_USE_CARRIER, lambda x: int(utils.get_boolean_from_string(x))),
         ('bond-lacp-rate', Link.IFLA_BOND_AD_LACP_RATE, lambda x: int(utils.get_boolean_from_string(x))),
         ('bond-lacp-bypass-allow', Link.IFLA_BOND_AD_LACP_BYPASS, lambda x: int(utils.get_boolean_from_string(x))),
+        ('es-sys-mac', Link.IFLA_BOND_AD_ACTOR_SYSTEM, str),
         ('bond-ad-sys-mac-addr', Link.IFLA_BOND_AD_ACTOR_SYSTEM, str),
-        ('bond-ad-actor-system', Link.IFLA_BOND_AD_ACTOR_SYSTEM, str)
+        ('bond-ad-actor-system', Link.IFLA_BOND_AD_ACTOR_SYSTEM, str),
+        ('bond-primary-reselect', Link.IFLA_BOND_PRIMARY_RESELECT, lambda x: Link.ifla_bond_primary_reselect_tbl[x])
         # ('bond-primary', Link.IFLA_BOND_PRIMARY, self.cache.get_ifindex) added in __init__()
     )
 
@@ -237,19 +258,23 @@ class bond(Addon, moduleBase):
         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),)
 
-    @staticmethod
-    def get_bond_slaves(ifaceobj):
-        slaves = ifaceobj.get_attr_value_first('bond-slaves')
-        if not slaves:
-            slaves = ifaceobj.get_attr_value_first('bond-ports')
-        return slaves
+        self.bond_mac_mgmt = utils.get_boolean_from_string(
+            policymanager.policymanager_api.get_module_globals(
+                module_name=self.__class__.__name__,
+                attr="bond_mac_mgmt"),
+            True
+        )
+
+    def get_bond_slaves(self, ifaceobj):
+        # bond-ports aliases should be translated to bond-slaves
+        return ifaceobj.get_attr_value_first('bond-slaves')
 
     def _is_bond(self, ifaceobj):
         # at first link_kind is not set but once ifupdownmain
         # calls get_dependent_ifacenames link_kind is set to BOND
         return ifaceobj.link_kind & ifaceLinkKind.BOND or self.get_bond_slaves(ifaceobj)
 
-    def get_dependent_ifacenames(self, ifaceobj, ifacenames_all=None):
+    def get_dependent_ifacenames(self, ifaceobj, ifacenames_all=None, old_ifaceobjs=False):
         """ Returns list of interfaces dependent on ifaceobj """
 
         if not self._is_bond(ifaceobj):
@@ -265,6 +290,9 @@ class bond(Addon, moduleBase):
         ifaceobj.link_kind |= ifaceLinkKind.BOND
         ifaceobj.role |= ifaceRole.MASTER
 
+        if ifaceobj.get_attr_value("es-sys-mac"):
+            ifaceobj.link_privflags |= ifaceLinkPrivFlags.ES_BOND
+
         return slave_list
 
     def syntax_check(self, ifaceobj, ifaceobj_getfunc):
@@ -312,7 +340,13 @@ class bond(Addon, moduleBase):
 
         clag_bond = self._is_clag_bond(ifaceobj)
 
-        for slave in set(slaves).difference(set(runningslaves)):
+        # remove duplicates and devices that are already enslaved
+        devices_to_enslave = []
+        for s in slaves:
+            if s not in runningslaves and s not in devices_to_enslave:
+                devices_to_enslave.append(s)
+
+        for slave in devices_to_enslave:
             if (not ifupdownflags.flags.PERFMODE and
                 not self.cache.link_exists(slave)):
                     self.log_error('%s: skipping slave %s, does not exist'
@@ -323,9 +357,10 @@ class bond(Addon, moduleBase):
             if self.cache.link_is_up(slave):
                 self.netlink.link_down_force(slave)
                 link_up = True
-            # If clag bond place the slave in a protodown state; clagd
-            # will protoup it when it is ready
-            if clag_bond:
+
+            # if clag or ES bond: place the slave in a protodown state;
+            # (clagd will proto-up it when it is ready)
+            if clag_bond or ifaceobj.link_privflags & ifaceLinkPrivFlags.ES_BOND:
                 try:
                     self.netlink.link_set_protodown_on(slave)
                 except Exception as e:
@@ -333,6 +368,7 @@ class bond(Addon, moduleBase):
 
             self.enable_ipv6_if_prev_brport(slave)
             self.netlink.link_set_master(slave, ifaceobj.name)
+            runningslaves.append(slave)
             # TODO: if this fail we should switch to iproute2
             # start a batch: down - set master - up
             if link_up or ifaceobj.link_type != ifaceLinkType.LINK_NA:
@@ -341,20 +377,40 @@ class bond(Addon, moduleBase):
                         ifaceLinkPrivFlags.KEEP_LINK_DOWN):
                         self.netlink.link_down_force(slave)
                     else:
-                        self.netlink.link_up(slave)
+                        self.netlink.link_up_force(slave)
                except Exception as e:
                     self.logger.debug('%s: %s' % (ifaceobj.name, str(e)))
                     pass
 
         if runningslaves:
+            removed_slave = []
+
             for s in runningslaves:
+                # make sure that slaves are not in protodown since we are not in the clag-bond or es-bond case
+                if not clag_bond and not ifaceobj.link_privflags & ifaceLinkPrivFlags.ES_BOND and self.cache.get_link_protodown(s):
+                    self.netlink.link_set_protodown_off(s)
+
                 if s not in slaves:
                     self.sysfs.bond_remove_slave(ifaceobj.name, s)
+                    removed_slave.append(s)
                     if clag_bond:
                         try:
                             self.netlink.link_set_protodown_off(s)
                         except Exception as e:
                             self.logger.error('%s: %s' % (ifaceobj.name, str(e)))
+
+                    # ip link set $slave nomaster will set the slave admin down
+                    # if the slave has an auto stanza, we should keep it admin up
+                    # unless link-down yes is set
+                    slave_class_auto = False
+                    slave_link_down = False
+                    for obj in ifaceobj_getfunc(s) or []:
+                        if obj.auto:
+                            slave_class_auto = True
+                        if obj.link_privflags & ifaceLinkPrivFlags.KEEP_LINK_DOWN:
+                            slave_link_down = True
+                    if slave_class_auto and not slave_link_down:
+                        self.netlink.link_up_force(s)
                 else:
                     # apply link-down config changes on running slaves
                     try:
@@ -368,6 +424,14 @@ class bond(Addon, moduleBase):
                     except Exception as e:
                         self.logger.warning('%s: %s' % (ifaceobj.name, str(e)))
 
+            for s in removed_slave:
+                try:
+                    runningslaves.remove(s)
+                except:
+                    pass
+
+        return  runningslaves
+
     def _check_updown_delay_log(self, ifaceobj, attr_name, value):
         ifaceobj.status = ifaceStatus.ERROR
         self.logger.error('%s: unable to set %s %s as MII link monitoring is '
@@ -385,13 +449,13 @@ class bond(Addon, moduleBase):
 
         try:
             miimon = int(ifaceobj.get_attr_value_first('bond-miimon'))
-        except:
+        except Exception:
             try:
                 miimon = int(policymanager.policymanager_api.get_iface_default(
                     module_name=self.__class__.__name__,
                     ifname=ifaceobj.name,
                     attr='bond-miimon'))
-            except:
+            except Exception:
                 miimon = 0
 
         if not miimon:
@@ -582,9 +646,15 @@ class bond(Addon, moduleBase):
 
                 for lower_dev in ifaceobj.lowerifaces:
                     self.netlink.link_set_nomaster(lower_dev)
+
+                    # when unslaving a device from an ES bond we need to set
+                    # protodown off
+                    if ifaceobj.link_privflags & ifaceLinkPrivFlags.ES_BOND:
+                        self.netlink.link_set_protodown_off(lower_dev)
+
                     try:
                         bond_slaves.remove(lower_dev)
-                    except:
+                    except Exception:
                         pass
 
             else:
@@ -642,21 +712,67 @@ class bond(Addon, moduleBase):
         if link_exists and ifla_info_data and not is_link_up:
             self.netlink.link_up_force(ifname)
 
-        return bond_slaves
+        return link_exists, bond_slaves
 
     def create_or_set_bond_config_sysfs(self, ifaceobj, ifla_info_data):
+        if len(ifaceobj.name) > 15:
+            self.log_error("%s: cannot create bond: interface name exceeds max length of 15" % ifaceobj.name, ifaceobj)
+            return
+
         if not self.cache.link_exists(ifaceobj.name):
             self.sysfs.bond_create(ifaceobj.name)
         self.sysfs.bond_set_attrs_nl(ifaceobj.name, ifla_info_data)
 
     def _up(self, ifaceobj, ifaceobj_getfunc=None):
         try:
-            bond_slaves = self.create_or_set_bond_config(ifaceobj)
-            self._add_slaves(
+            link_exists, bond_slaves = self.create_or_set_bond_config(ifaceobj)
+            bond_slaves = self._add_slaves(
                 ifaceobj,
                 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))
         except Exception as e:
             self.log_error(str(e), ifaceobj)
 
@@ -712,7 +828,7 @@ class bond(Addon, moduleBase):
                 running_bond_slaves = self.cache.get_slaves(ifaceobj.name)
 
             self._query_check_bond_slaves(ifaceobjcurr, 'bond-slaves', user_bond_slaves, running_bond_slaves)
-        except:
+        except Exception:
             pass
         try:
             del iface_attrs[iface_attrs.index('bond-ports')]
@@ -723,7 +839,7 @@ class bond(Addon, moduleBase):
                 running_bond_slaves = self.cache.get_slaves(ifaceobj.name)
 
             self._query_check_bond_slaves(ifaceobjcurr, 'bond-ports', user_bond_slaves, running_bond_slaves)
-        except:
+        except Exception:
             pass
 
         for attr in iface_attrs:
@@ -755,6 +871,7 @@ class bond(Addon, moduleBase):
             'bond-lacp-rate': self.translate_nl_value_slowfast(cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_AD_LACP_RATE)),
             'bond-min-links': cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_MIN_LINKS),
             'bond-ad-actor-system': cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_AD_ACTOR_SYSTEM),
+            'es-sys-mac': cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_AD_ACTOR_SYSTEM),
             'bond-ad-actor-sys-prio': cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_AD_ACTOR_SYS_PRIO),
             'bond-xmit-hash-policy': Link.ifla_bond_xmit_hash_policy_pretty_tbl.get(cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_XMIT_HASH_POLICY)),
             'bond-lacp-bypass-allow': self.translate_nl_value_yesno(cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_AD_LACP_BYPASS)),