]> git.proxmox.com Git - mirror_ifupdown2.git/blame - ifupdown2/addons/bond.py
addons: bond: set proto-up on slaves if bond used to be a clag bond or es-bond
[mirror_ifupdown2.git] / ifupdown2 / addons / bond.py
CommitLineData
35681c06 1#!/usr/bin/env python3
d486dd0d
JF
2#
3# Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved.
4# Authors:
5# Roopa Prabhu, roopa@cumulusnetworks.com
6# Julien Fortin, julien@cumulusnetworks.com
7#
8
9import os
10
d486dd0d 11try:
223ba5af 12 from ifupdown2.lib.addon import Addon
d486dd0d
JF
13 from ifupdown2.nlmanager.nlmanager import Link
14
15 from ifupdown2.ifupdown.iface import *
16 from ifupdown2.ifupdown.utils import utils
d486dd0d
JF
17 from ifupdown2.ifupdown.statemanager import statemanager_api as statemanager
18
19 import ifupdown2.ifupdown.policymanager as policymanager
20 import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
21
d486dd0d 22 from ifupdown2.ifupdownaddons.modulebase import moduleBase
bd441a51 23except (ImportError, ModuleNotFoundError):
223ba5af 24 from lib.addon import Addon
d486dd0d
JF
25 from nlmanager.nlmanager import Link
26
27 from ifupdown.iface import *
28 from ifupdown.utils import utils
d486dd0d
JF
29 from ifupdown.statemanager import statemanager_api as statemanager
30
d486dd0d
JF
31 from ifupdownaddons.modulebase import moduleBase
32
33 import ifupdown.policymanager as policymanager
34 import ifupdown.ifupdownflags as ifupdownflags
35
223ba5af 36class bond(Addon, moduleBase):
d486dd0d
JF
37 """ ifupdown2 addon module to configure bond interfaces """
38
39 overrides_ifupdown_scripts = ['ifenslave', ]
40
223ba5af
JF
41 _modinfo = {
42 "mhelp": "bond configuration module",
43 "attrs": {
44 "bond-use-carrier": {
45 "help": "bond use carrier",
46 "validvals": ["yes", "no", "0", "1"],
47 "default": "yes",
48 "example": ["bond-use-carrier yes"]},
49 "bond-num-grat-arp": {
50 "help": "bond use carrier",
51 "validrange": ["0", "255"],
52 "default": "1",
53 "example": ["bond-num-grat-arp 1"]
54 },
55 "bond-num-unsol-na": {
56 "help": "bond slave devices",
57 "validrange": ["0", "255"],
58 "default": "1",
59 "example": ["bond-num-unsol-na 1"]
60 },
61 "bond-xmit-hash-policy": {
62 "help": "bond slave devices",
63 "validvals": [
64 "0", "layer2",
65 "1", "layer3+4",
66 "2", "layer2+3",
67 "3", "encap2+3",
68 "4", "encap3+4"
69 ],
70 "default": "layer2",
71 "example": ["bond-xmit-hash-policy layer2"]
72 },
73 "bond-miimon": {
74 "help": "bond miimon",
75 "validrange": ["0", "255"],
76 "default": "0",
77 "example": ["bond-miimon 0"]
78 },
79 "bond-mode": {
80 "help": "bond mode",
81 "validvals": [
82 "0", "balance-rr",
83 "1", "active-backup",
84 "2", "balance-xor",
85 "3", "broadcast",
86 "4", "802.3ad",
87 "5", "balance-tlb",
88 "6", "balance-alb"
89 ],
90 "default": "balance-rr",
91 "example": ["bond-mode 802.3ad"]
92 },
93 "bond-lacp-rate": {
94 "help": "bond lacp rate",
95 "validvals": ["0", "slow", "1", "fast"],
96 "default": "0",
97 "example": ["bond-lacp-rate 0"]
98 },
99 "bond-min-links": {
100 "help": "bond min links",
101 "default": "0",
102 "validrange": ["0", "255"],
103 "example": ["bond-min-links 0"]
104 },
105 "bond-ad-sys-priority": {
106 "help": "802.3ad system priority",
107 "default": "65535",
108 "validrange": ["0", "65535"],
109 "example": ["bond-ad-sys-priority 65535"],
110 "deprecated": True,
111 "new-attribute": "bond-ad-actor-sys-prio"
112 },
113 "bond-ad-actor-sys-prio": {
114 "help": "802.3ad system priority",
115 "default": "65535",
116 "validrange": ["0", "65535"],
117 "example": ["bond-ad-actor-sys-prio 65535"]
118 },
119 "bond-ad-sys-mac-addr": {
120 "help": "802.3ad system mac address",
121 "validvals": ["<mac>", ],
122 "example": ["bond-ad-sys-mac-addr 00:00:00:00:00:00"],
123 "deprecated": True,
124 "new-attribute": "bond-ad-actor-system"
125 },
126 "bond-ad-actor-system": {
127 "help": "802.3ad system mac address",
128 "validvals": ["<mac>", ],
129 "example": ["bond-ad-actor-system 00:00:00:00:00:00"],
130 },
131 "bond-lacp-bypass-allow": {
132 "help": "allow lacp bypass",
133 "validvals": ["yes", "no", "0", "1"],
134 "default": "no",
135 "example": ["bond-lacp-bypass-allow no"]
136 },
137 "bond-slaves": {
138 "help": "bond slaves",
139 "required": True,
140 "multivalue": True,
141 "validvals": ["<interface-list>"],
142 "example": [
143 "bond-slaves swp1 swp2",
144 "bond-slaves glob swp1-2",
145 "bond-slaves regex (swp[1|2)"
146 ],
147 "aliases": ["bond-ports"]
148 },
149 "bond-updelay": {
150 "help": "bond updelay",
151 "default": "0",
152 "validrange": ["0", "65535"],
153 "example": ["bond-updelay 100"]
154 },
155 "bond-downdelay": {
156 "help": "bond downdelay",
157 "default": "0",
158 "validrange": ["0", "65535"],
159 "example": ["bond-downdelay 100"]
160 },
161 "bond-primary": {
162 "help": "Control which slave interface is "
163 "preferred active member",
164 "example": ["bond-primary swp1"]
716316cf
AD
165 },
166 "bond-primary-reselect": {
167 "help": "bond primary reselect",
168 "validvals": [
169 "0", "always",
170 "1", "better",
171 "2", "failure",
172 ],
173 "example": ["bond-primary-reselect failure"]
9808982e
JF
174 },
175 "es-sys-mac": {
176 "help": "evpn-mh: system mac address",
177 "validvals": ["<mac>", ],
4b706d71 178 "example": ["es-sys-mac 00:00:00:00:00:42"],
223ba5af
JF
179 }
180 }
181 }
d486dd0d
JF
182
183 _bond_attr_netlink_map = {
184 'bond-mode': Link.IFLA_BOND_MODE,
185 'bond-miimon': Link.IFLA_BOND_MIIMON,
186 'bond-use-carrier': Link.IFLA_BOND_USE_CARRIER,
187 'bond-lacp-rate': Link.IFLA_BOND_AD_LACP_RATE,
188 'bond-xmit-hash-policy': Link.IFLA_BOND_XMIT_HASH_POLICY,
189 'bond-min-links': Link.IFLA_BOND_MIN_LINKS,
190 'bond-num-grat-arp': Link.IFLA_BOND_NUM_PEER_NOTIF,
191 'bond-num-unsol-na': Link.IFLA_BOND_NUM_PEER_NOTIF,
9808982e 192 'es-sys-mac': Link.IFLA_BOND_AD_ACTOR_SYSTEM,
d486dd0d
JF
193 'bond-ad-sys-mac-addr': Link.IFLA_BOND_AD_ACTOR_SYSTEM,
194 'bond-ad-actor-system': Link.IFLA_BOND_AD_ACTOR_SYSTEM,
195 'bond-ad-sys-priority': Link.IFLA_BOND_AD_ACTOR_SYS_PRIO,
196 'bond-ad-actor-sys-prio': Link.IFLA_BOND_AD_ACTOR_SYS_PRIO,
197 'bond-lacp-bypass-allow': Link.IFLA_BOND_AD_LACP_BYPASS,
198 'bond-updelay': Link.IFLA_BOND_UPDELAY,
223ba5af 199 'bond-downdelay': Link.IFLA_BOND_DOWNDELAY,
716316cf
AD
200 'bond-primary': Link.IFLA_BOND_PRIMARY,
201 'bond-primary-reselect': Link.IFLA_BOND_PRIMARY_RESELECT
202
d486dd0d
JF
203 }
204
205 # ifquery-check attr dictionary with callable object to translate user data to netlink format
206 _bond_attr_ifquery_check_translate_func = {
207 Link.IFLA_BOND_MODE: lambda x: Link.ifla_bond_mode_tbl[x],
208 Link.IFLA_BOND_MIIMON: int,
209 Link.IFLA_BOND_USE_CARRIER: utils.get_boolean_from_string,
210 Link.IFLA_BOND_AD_LACP_RATE: lambda x: int(utils.get_boolean_from_string(x)),
211 Link.IFLA_BOND_XMIT_HASH_POLICY: lambda x: Link.ifla_bond_xmit_hash_policy_tbl[x],
212 Link.IFLA_BOND_MIN_LINKS: int,
213 Link.IFLA_BOND_NUM_PEER_NOTIF: int,
214 Link.IFLA_BOND_AD_ACTOR_SYSTEM: str,
215 Link.IFLA_BOND_AD_ACTOR_SYS_PRIO: int,
216 Link.IFLA_BOND_AD_LACP_BYPASS: lambda x: int(utils.get_boolean_from_string(x)),
217 Link.IFLA_BOND_UPDELAY: int,
223ba5af 218 Link.IFLA_BOND_DOWNDELAY: int,
716316cf 219 Link.IFLA_BOND_PRIMARY_RESELECT: lambda x: Link.ifla_bond_primary_reselect_tbl[x],
223ba5af 220 # Link.IFLA_BOND_PRIMARY: self.netlink.get_ifname is added in __init__()
d486dd0d
JF
221 }
222
223 # ifup attr list with callable object to translate user data to netlink format
224 # in the future this can be moved to a dictionary, whenever we detect that some
225 # netlink capabilities are missing we can dynamically remove them from the dict.
226 _bond_attr_set_list = (
227 ('bond-mode', Link.IFLA_BOND_MODE, lambda x: Link.ifla_bond_mode_tbl[x]),
228 ('bond-xmit-hash-policy', Link.IFLA_BOND_XMIT_HASH_POLICY, lambda x: Link.ifla_bond_xmit_hash_policy_tbl[x]),
229 ('bond-miimon', Link.IFLA_BOND_MIIMON, int),
230 ('bond-min-links', Link.IFLA_BOND_MIN_LINKS, int),
231 ('bond-num-grat-arp', Link.IFLA_BOND_NUM_PEER_NOTIF, int),
232 ('bond-num-unsol-na', Link.IFLA_BOND_NUM_PEER_NOTIF, int),
233 ('bond-ad-sys-priority', Link.IFLA_BOND_AD_ACTOR_SYS_PRIO, int),
234 ('bond-ad-actor-sys-prio', Link.IFLA_BOND_AD_ACTOR_SYS_PRIO, int),
235 ('bond-updelay', Link.IFLA_BOND_UPDELAY, int),
236 ('bond-downdelay', Link.IFLA_BOND_DOWNDELAY, int),
237 ('bond-use-carrier', Link.IFLA_BOND_USE_CARRIER, lambda x: int(utils.get_boolean_from_string(x))),
238 ('bond-lacp-rate', Link.IFLA_BOND_AD_LACP_RATE, lambda x: int(utils.get_boolean_from_string(x))),
239 ('bond-lacp-bypass-allow', Link.IFLA_BOND_AD_LACP_BYPASS, lambda x: int(utils.get_boolean_from_string(x))),
9808982e 240 ('es-sys-mac', Link.IFLA_BOND_AD_ACTOR_SYSTEM, str),
d486dd0d 241 ('bond-ad-sys-mac-addr', Link.IFLA_BOND_AD_ACTOR_SYSTEM, str),
716316cf
AD
242 ('bond-ad-actor-system', Link.IFLA_BOND_AD_ACTOR_SYSTEM, str),
243 ('bond-primary-reselect', Link.IFLA_BOND_PRIMARY_RESELECT, lambda x: Link.ifla_bond_primary_reselect_tbl[x])
223ba5af 244 # ('bond-primary', Link.IFLA_BOND_PRIMARY, self.cache.get_ifindex) added in __init__()
d486dd0d
JF
245 )
246
247 def __init__(self, *args, **kargs):
223ba5af 248 Addon.__init__(self)
d486dd0d 249 moduleBase.__init__(self, *args, **kargs)
d486dd0d
JF
250
251 if not os.path.exists('/sys/class/net/bonding_masters'):
0c4237d5
JF
252 try:
253 utils.exec_command('modprobe -q bonding')
254 except Exception as e:
255 self.logger.info("bond: error while loading bonding module: %s" % str(e))
d486dd0d 256
223ba5af
JF
257 self._bond_attr_ifquery_check_translate_func[Link.IFLA_BOND_PRIMARY] = self.cache.get_ifindex
258 self._bond_attr_set_list = self._bond_attr_set_list + (('bond-primary', Link.IFLA_BOND_PRIMARY, self.cache.get_ifindex),)
259
6bc9fadc
JF
260
261 def get_bond_slaves(self, ifaceobj):
262 # bond-ports aliases should be translated to bond-slaves
263 return ifaceobj.get_attr_value_first('bond-slaves')
d486dd0d
JF
264
265 def _is_bond(self, ifaceobj):
266 # at first link_kind is not set but once ifupdownmain
267 # calls get_dependent_ifacenames link_kind is set to BOND
223ba5af 268 return ifaceobj.link_kind & ifaceLinkKind.BOND or self.get_bond_slaves(ifaceobj)
d486dd0d
JF
269
270 def get_dependent_ifacenames(self, ifaceobj, ifacenames_all=None):
271 """ Returns list of interfaces dependent on ifaceobj """
272
273 if not self._is_bond(ifaceobj):
274 return None
275 slave_list = self.parse_port_list(ifaceobj.name,
276 self.get_bond_slaves(ifaceobj),
277 ifacenames_all)
278 ifaceobj.dependency_type = ifaceDependencyType.MASTER_SLAVE
279 # Also save a copy for future use
280 ifaceobj.priv_data = list(slave_list)
281 if ifaceobj.link_type != ifaceLinkType.LINK_NA:
282 ifaceobj.link_type = ifaceLinkType.LINK_MASTER
283 ifaceobj.link_kind |= ifaceLinkKind.BOND
284 ifaceobj.role |= ifaceRole.MASTER
285
9808982e
JF
286 if ifaceobj.get_attr_value("es-sys-mac"):
287 ifaceobj.link_privflags |= ifaceLinkPrivFlags.ES_BOND
288
d486dd0d
JF
289 return slave_list
290
291 def syntax_check(self, ifaceobj, ifaceobj_getfunc):
292 return self.syntax_check_updown_delay(ifaceobj)
293
294 def get_dependent_ifacenames_running(self, ifaceobj):
223ba5af 295 return self.cache.get_slaves(ifaceobj.name)
d486dd0d
JF
296
297 def _get_slave_list(self, ifaceobj):
298 """ Returns slave list present in ifaceobj config """
299
300 # If priv data already has slave list use that first.
301 if ifaceobj.priv_data:
302 return ifaceobj.priv_data
303 slaves = self.get_bond_slaves(ifaceobj)
304 if slaves:
305 return self.parse_port_list(ifaceobj.name, slaves)
306 else:
307 return None
308
ff775ef1
JF
309 def enable_ipv6_if_prev_brport(self, ifname):
310 """
311 If the intf was previously enslaved to a bridge it is possible ipv6 is still disabled.
312 """
313 try:
223ba5af
JF
314 for ifaceobj in statemanager.get_ifaceobjs(ifname) or []:
315 if ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT:
316 self.write_file("/proc/sys/net/ipv6/conf/%s/disable_ipv6" % ifname, "0")
317 return
3b01ed76 318 except Exception as e:
ff775ef1
JF
319 self.logger.info(str(e))
320
d486dd0d
JF
321 def _is_clag_bond(self, ifaceobj):
322 if self.get_bond_slaves(ifaceobj):
323 attrval = ifaceobj.get_attr_value_first('clag-id')
324 if attrval and attrval != '0':
325 return True
326 return False
327
223ba5af 328 def _add_slaves(self, ifaceobj, runningslaves, ifaceobj_getfunc=None):
d486dd0d
JF
329 slaves = self._get_slave_list(ifaceobj)
330 if not slaves:
331 self.logger.debug('%s: no slaves found' %ifaceobj.name)
332 return
333
d486dd0d
JF
334 clag_bond = self._is_clag_bond(ifaceobj)
335
1f928890
JF
336 # remove duplicates and devices that are already enslaved
337 devices_to_enslave = []
338 for s in slaves:
339 if s not in runningslaves and s not in devices_to_enslave:
340 devices_to_enslave.append(s)
341
342 for slave in devices_to_enslave:
d486dd0d 343 if (not ifupdownflags.flags.PERFMODE and
223ba5af 344 not self.cache.link_exists(slave)):
d486dd0d
JF
345 self.log_error('%s: skipping slave %s, does not exist'
346 %(ifaceobj.name, slave), ifaceobj,
347 raise_error=False)
348 continue
349 link_up = False
223ba5af
JF
350 if self.cache.link_is_up(slave):
351 self.netlink.link_down_force(slave)
d486dd0d 352 link_up = True
9808982e
JF
353
354 # if clag or ES bond: place the slave in a protodown state;
355 # (clagd will proto-up it when it is ready)
356 if clag_bond or ifaceobj.link_privflags & ifaceLinkPrivFlags.ES_BOND:
d486dd0d 357 try:
223ba5af 358 self.netlink.link_set_protodown_on(slave)
3b01ed76 359 except Exception as e:
d486dd0d 360 self.logger.error('%s: %s' % (ifaceobj.name, str(e)))
223ba5af 361
ff775ef1 362 self.enable_ipv6_if_prev_brport(slave)
223ba5af
JF
363 self.netlink.link_set_master(slave, ifaceobj.name)
364 # TODO: if this fail we should switch to iproute2
365 # start a batch: down - set master - up
d486dd0d
JF
366 if link_up or ifaceobj.link_type != ifaceLinkType.LINK_NA:
367 try:
368 if (ifaceobj_getfunc(slave)[0].link_privflags &
369 ifaceLinkPrivFlags.KEEP_LINK_DOWN):
223ba5af 370 self.netlink.link_down_force(slave)
d486dd0d 371 else:
223ba5af 372 self.netlink.link_up(slave)
3b01ed76 373 except Exception as e:
d486dd0d
JF
374 self.logger.debug('%s: %s' % (ifaceobj.name, str(e)))
375 pass
376
377 if runningslaves:
378 for s in runningslaves:
d079ad3f
JF
379 # make sure that slaves are not in protodown since we are not in the clag-bond or es-bond case
380 if not clag_bond and not ifaceobj.link_privflags & ifaceLinkPrivFlags.ES_BOND and self.cache.get_link_protodown(s):
381 self.netlink.link_set_protodown_off(s)
382
d486dd0d 383 if s not in slaves:
223ba5af 384 self.sysfs.bond_remove_slave(ifaceobj.name, s)
d486dd0d
JF
385 if clag_bond:
386 try:
223ba5af 387 self.netlink.link_set_protodown_off(s)
3b01ed76 388 except Exception as e:
d486dd0d
JF
389 self.logger.error('%s: %s' % (ifaceobj.name, str(e)))
390 else:
391 # apply link-down config changes on running slaves
392 try:
223ba5af 393 link_up = self.cache.link_is_up(s)
d486dd0d
JF
394 config_link_down = (ifaceobj_getfunc(s)[0].link_privflags &
395 ifaceLinkPrivFlags.KEEP_LINK_DOWN)
396 if (config_link_down and link_up):
223ba5af 397 self.netlink.link_down_force(s)
d486dd0d 398 elif (not config_link_down and not link_up):
223ba5af 399 self.netlink.link_up_force(s)
3b01ed76 400 except Exception as e:
c46af1c9 401 self.logger.warning('%s: %s' % (ifaceobj.name, str(e)))
d486dd0d
JF
402
403 def _check_updown_delay_log(self, ifaceobj, attr_name, value):
404 ifaceobj.status = ifaceStatus.ERROR
405 self.logger.error('%s: unable to set %s %s as MII link monitoring is '
406 'disabled' % (ifaceobj.name, attr_name, value))
407 # return False to notify syntax_check that an error has been logged
408 return False
409
410 def syntax_check_updown_delay(self, ifaceobj):
411 result = True
412 updelay = ifaceobj.get_attr_value_first('bond-updelay')
413 downdelay = ifaceobj.get_attr_value_first('bond-downdelay')
414
415 if not updelay and not downdelay:
416 return True
417
418 try:
419 miimon = int(ifaceobj.get_attr_value_first('bond-miimon'))
3218f49d 420 except Exception:
d486dd0d
JF
421 try:
422 miimon = int(policymanager.policymanager_api.get_iface_default(
423 module_name=self.__class__.__name__,
424 ifname=ifaceobj.name,
425 attr='bond-miimon'))
3218f49d 426 except Exception:
d486dd0d
JF
427 miimon = 0
428
429 if not miimon:
430 # self._check_updown_delay_log returns False no matter what
431 if updelay and int(updelay):
432 result = self._check_updown_delay_log(ifaceobj, 'bond-updelay', updelay)
433 if downdelay and int(downdelay):
434 result = self._check_updown_delay_log(ifaceobj, 'bond-downdelay', downdelay)
435
436 return result
437
438 _bond_updown_delay_nl_list = (
439 (Link.IFLA_BOND_UPDELAY, 'bond-updelay'),
440 (Link.IFLA_BOND_DOWNDELAY, 'bond-downdelay')
441 )
442
443 def check_updown_delay_nl(self, link_exists, ifaceobj, ifla_info_data):
444 """
445 IFLA_BOND_MIIMON
446 Specifies the time, in milliseconds, to wait before enabling a slave
447 after a link recovery has been detected. This option is only valid
448 for the miimon link monitor. The updelay value should be a multiple
449 of the miimon value; if not, it will be rounded down to the nearest
450 multiple. The default value is 0.
451
452 This ifla_bond_miimon code should be move to get_ifla_bond_attr_from_user_config
453 but we need to know if the operation was successful to update the cache accordingly
454 """
455 ifla_bond_miimon = ifla_info_data.get(Link.IFLA_BOND_MIIMON)
456 if link_exists and ifla_bond_miimon is None:
223ba5af 457 ifla_bond_miimon = self.cache.get_link_info_data_attribute(ifaceobj.name, Link.IFLA_BOND_MIIMON)
d486dd0d
JF
458
459 if ifla_bond_miimon == 0:
460 for nl_attr, attr_name in self._bond_updown_delay_nl_list:
461 delay = ifla_info_data.get(nl_attr)
462 # if up-down-delay exists we need to remove it, if non zero log error
463 if delay is not None:
464 if delay > 0:
465 self._check_updown_delay_log(ifaceobj, attr_name, delay)
466 del ifla_info_data[nl_attr]
467 return True
468 return False
469
470 _bond_lacp_attrs = (
471 (Link.IFLA_BOND_AD_LACP_RATE, 'bond-lacp-rate'),
472 (Link.IFLA_BOND_AD_LACP_BYPASS, 'bond-lacp-bypass')
473 )
474
475 def _check_bond_mode_user_config(self, ifname, link_exists, ifla_info_data):
476 ifla_bond_mode = ifla_info_data.get(Link.IFLA_BOND_MODE)
477 if ifla_bond_mode is None and link_exists:
223ba5af 478 ifla_bond_mode = self.cache.get_link_info_data_attribute(ifname, Link.IFLA_BOND_MODE)
d486dd0d
JF
479 # in this case the link already exists (we have a cached value):
480 # if IFLA_BOND_MODE is not present in ifla_info_data it means:
481 # - that bond-mode was present in the user config and didn't change
482 # - never was in the user config so bond mode should be the system default value
483 # - was removed from the stanza so we might have to reset it to default value
484 # nevertheless we need to add it back to the ifla_info_data dict to check
485 # if we need to reset the mode to system default
486 ifla_info_data[Link.IFLA_BOND_MODE] = ifla_bond_mode
487
488 if ifla_bond_mode == 4: # 802.3ad
489 min_links = ifla_info_data.get(Link.IFLA_BOND_MIN_LINKS)
490 if min_links is None:
223ba5af 491 min_links = self.cache.get_link_info_data_attribute(ifname, Link.IFLA_BOND_MIN_LINKS)
d486dd0d
JF
492 # get_min_links_nl may return None so we need to strictly check 0
493 if min_links == 0:
c46af1c9 494 self.logger.warning('%s: attribute bond-min-links is set to \'0\'' % ifname)
d486dd0d
JF
495 else:
496 # IFLA_BOND_AD_LACP_RATE and IFLA_BOND_AD_LACP_BYPASS only for 802.3ad mode (4)
497 for nl_attr, attr_name in self._bond_lacp_attrs:
498 if nl_attr in ifla_info_data:
499 self.logger.info('%s: ignoring %s: only available for 802.3ad mode (4)' % (ifname, attr_name))
500 del ifla_info_data[nl_attr]
501
502 @staticmethod
503 def get_saved_ifaceobj(link_exists, ifname):
504 if link_exists:
505 old_config = statemanager.get_ifaceobjs(ifname)
506 if old_config:
507 return old_config[0]
508 return None
509
510 def get_ifla_bond_attr_from_user_config(self, ifaceobj, link_exists):
511 """
512 Potential issue: if a user load the bond driver with custom
513 default values (say bond-mode 3), ifupdown2 has no knowledge
514 of these default values.
515 At bond creation everything should work, bonds will be created
516 with mode 3 (even if not specified under the stanza).
517 But, for example: if the user specifies a value under bond-mode
518 and later on the user removes the bond-mode line from the stanza
519 we will detect it and reset to MODINFO: BOND-MODE: DEFAULT aka 0
520 which is not the real default value that the user may expect.
521 """
522 ifname = ifaceobj.name
523 ifla_info_data = OrderedDict()
524 old_config = self.get_saved_ifaceobj(link_exists, ifname)
525
526 # for each bond attribute we fetch the user configuration
527 # if no configuration is provided we look for a config in policy files
528 for attr_name, netlink_attr, func_ptr in self._bond_attr_set_list:
529 cached_value = None
530 user_config = ifaceobj.get_attr_value_first(attr_name)
531
532 if not user_config:
533 user_config = policymanager.policymanager_api.get_iface_default(
534 module_name=self.__class__.__name__,
535 ifname=ifname,
536 attr=attr_name)
537 if user_config:
538 self.logger.debug('%s: %s %s: extracted from policy files'
539 % (ifname, attr_name, user_config))
540
541 # no policy override, do we need to reset an attr to default value?
542 if not user_config and old_config and old_config.get_attr_value_first(attr_name):
543 # if the link already exists but the value is set
544 # (potentially removed from the stanza, we need to reset it to default)
545 # might not work for specific cases, see explanation at the top of this function :)
546 user_config = self.get_attr_default_value(attr_name)
547 if user_config:
548 self.logger.debug('%s: %s: removed from stanza, resetting to default value: %s'
549 % (ifname, attr_name, user_config))
550
551 if user_config:
552 try:
553 nl_value = func_ptr(user_config.lower())
554
555 if link_exists:
223ba5af 556 cached_value = self.cache.get_link_info_data_attribute(ifname, netlink_attr)
d486dd0d
JF
557
558 if link_exists and cached_value is None:
559 # the link already exists but we don't have any value
560 # cached for this attr, it probably means that the
561 # capability is not available on this system (i.e old kernel)
562 self.logger.debug('%s: ignoring %s %s: capability '
563 'probably not supported on this system'
564 % (ifname, attr_name, user_config))
565 continue
566 elif link_exists:
567 # there should be a cached value if the link already exists
568 if cached_value == nl_value:
569 # if the user value is already cached: continue
570 continue
571
572 # else: the link doesn't exist so we create the bond with
573 # all the user/policy defined values without extra checks
574 ifla_info_data[netlink_attr] = nl_value
575
576 if cached_value is not None:
577 self.logger.info('%s: set %s %s (cache %s)' % (ifname, attr_name, user_config, cached_value))
578 else:
579 self.logger.info('%s: set %s %s' % (ifname, attr_name, user_config))
580
581 except KeyError:
582 self.logger.warning('%s: invalid %s value %s' % (ifname, attr_name, user_config))
583
584 self._check_bond_mode_user_config(ifname, link_exists, ifla_info_data)
585 return ifla_info_data
586
587 _bond_down_nl_attributes_list = (
588 Link.IFLA_BOND_MODE,
589 Link.IFLA_BOND_XMIT_HASH_POLICY,
590 Link.IFLA_BOND_AD_LACP_RATE,
591 Link.IFLA_BOND_MIN_LINKS
592 )
593
594 def _should_down_bond(self, ifla_info_data):
595 for nl_attr in self._bond_down_nl_attributes_list:
596 if nl_attr in ifla_info_data:
597 return True
598 return False
599
223ba5af 600 def should_update_bond_mode(self, ifaceobj, ifname, is_link_up, ifla_info_data, bond_slaves):
d486dd0d
JF
601 # if bond-mode was changed the bond needs to be brought
602 # down and slaves un-slaved before bond mode is changed.
223ba5af 603 cached_bond_mode = self.cache.get_link_info_data_attribute(ifname, Link.IFLA_BOND_MODE)
d486dd0d
JF
604 ifla_bond_mode = ifla_info_data.get(Link.IFLA_BOND_MODE)
605
606 # bond-mode was changed or is not specified
607 if ifla_bond_mode is not None:
608 if ifla_bond_mode != cached_bond_mode:
609 self.logger.info('%s: bond mode changed to %s: running ops on bond and slaves'
610 % (ifname, ifla_bond_mode))
611 if is_link_up:
223ba5af 612 self.netlink.link_down(ifname)
d486dd0d
JF
613 is_link_up = False
614
615 for lower_dev in ifaceobj.lowerifaces:
223ba5af 616 self.netlink.link_set_nomaster(lower_dev)
9808982e
JF
617
618 # when unslaving a device from an ES bond we need to set
619 # protodown off
620 if ifaceobj.link_privflags & ifaceLinkPrivFlags.ES_BOND:
621 self.netlink.link_set_protodown_off(lower_dev)
622
223ba5af
JF
623 try:
624 bond_slaves.remove(lower_dev)
3218f49d 625 except Exception:
223ba5af 626 pass
d486dd0d 627
d486dd0d
JF
628 else:
629 # bond-mode user config value is the current running(cached) value
630 # no need to reset it again we can ignore this attribute
631 del ifla_info_data[Link.IFLA_BOND_MODE]
632
223ba5af 633 return is_link_up, bond_slaves
d486dd0d
JF
634
635 def create_or_set_bond_config(self, ifaceobj):
636 ifname = ifaceobj.name
223ba5af 637 link_exists, is_link_up = self.cache.link_exists_and_up(ifname)
d486dd0d
JF
638 ifla_info_data = self.get_ifla_bond_attr_from_user_config(ifaceobj, link_exists)
639
640 remove_delay_from_cache = self.check_updown_delay_nl(link_exists, ifaceobj, ifla_info_data)
641
642 # if link exists: down link if specific attributes are specified
643 if link_exists:
644 # did bond-mode changed?
223ba5af
JF
645 is_link_up, bond_slaves = self.should_update_bond_mode(
646 ifaceobj,
647 ifname,
648 is_link_up,
649 ifla_info_data,
650 self.cache.get_slaves(ifname)
651 )
d486dd0d
JF
652
653 # if specific attributes need to be set we need to down the bond first
654 if ifla_info_data and is_link_up:
655 if self._should_down_bond(ifla_info_data):
223ba5af 656 self.netlink.link_down_force(ifname)
d486dd0d 657 is_link_up = False
223ba5af
JF
658 else:
659 bond_slaves = []
d486dd0d
JF
660
661 if link_exists and not ifla_info_data:
662 # if the bond already exists and no attrs need to be set
663 # ignore the netlink call
664 self.logger.info('%s: already exists, no change detected' % ifname)
665 else:
666 try:
223ba5af 667 self.netlink.link_add_bond_with_info_data(ifname, ifla_info_data)
d486dd0d
JF
668 except Exception as e:
669 # defensive code
670 # if anything happens, we try to set up the bond with the sysfs api
671 self.logger.debug('%s: bond setup: %s' % (ifname, str(e)))
672 self.create_or_set_bond_config_sysfs(ifaceobj, ifla_info_data)
673
674 if remove_delay_from_cache:
675 # making sure up/down delay attributes are set to 0 before caching
676 # this can be removed when moving to a nllistener/live cache
677 ifla_info_data[Link.IFLA_BOND_UPDELAY] = 0
678 ifla_info_data[Link.IFLA_BOND_DOWNDELAY] = 0
679
d486dd0d 680 if link_exists and ifla_info_data and not is_link_up:
223ba5af
JF
681 self.netlink.link_up_force(ifname)
682
683 return bond_slaves
d486dd0d
JF
684
685 def create_or_set_bond_config_sysfs(self, ifaceobj, ifla_info_data):
d0d657ed
JF
686 if len(ifaceobj.name) > 15:
687 self.log_error("%s: cannot create bond: interface name exceeds max length of 15" % ifaceobj.name, ifaceobj)
688 return
689
223ba5af
JF
690 if not self.cache.link_exists(ifaceobj.name):
691 self.sysfs.bond_create(ifaceobj.name)
692 self.sysfs.bond_set_attrs_nl(ifaceobj.name, ifla_info_data)
d486dd0d
JF
693
694 def _up(self, ifaceobj, ifaceobj_getfunc=None):
695 try:
223ba5af
JF
696 bond_slaves = self.create_or_set_bond_config(ifaceobj)
697 self._add_slaves(
698 ifaceobj,
699 bond_slaves,
700 ifaceobj_getfunc,
701 )
3b01ed76 702 except Exception as e:
d486dd0d
JF
703 self.log_error(str(e), ifaceobj)
704
705 def _down(self, ifaceobj, ifaceobj_getfunc=None):
706 try:
223ba5af 707 self.netlink.link_del(ifaceobj.name)
d486dd0d
JF
708 except Exception as e:
709 self.log_warn('%s: %s' % (ifaceobj.name, str(e)))
710
711 def _query_check_bond_slaves(self, ifaceobjcurr, attr, user_bond_slaves, running_bond_slaves):
712 query = 1
713
714 if user_bond_slaves and running_bond_slaves:
715 if not set(user_bond_slaves).symmetric_difference(running_bond_slaves):
716 query = 0
717
718 # we want to display the same bond-slaves list as provided
719 # in the interfaces file but if this list contains regexes or
720 # globs, for now, we won't try to change it.
721 if 'regex' in user_bond_slaves or 'glob' in user_bond_slaves:
722 user_bond_slaves = running_bond_slaves
723 else:
724 ordered = []
725 for slave in user_bond_slaves:
726 if slave in running_bond_slaves:
727 ordered.append(slave)
728 user_bond_slaves = ordered
729 ifaceobjcurr.update_config_with_status(attr, ' '.join(user_bond_slaves) if user_bond_slaves else 'None', query)
730
731 def _query_check(self, ifaceobj, ifaceobjcurr, ifaceobj_getfunc=None):
223ba5af 732 if not self.cache.bond_exists(ifaceobj.name):
d486dd0d
JF
733 self.logger.debug('bond iface %s does not exist' % ifaceobj.name)
734 return
735
736 iface_attrs = self.dict_key_subset(ifaceobj.config, self.get_mod_attrs())
737 if not iface_attrs:
738 return
739
740 # remove bond-slaves and bond-ports from the list,
741 # because there aren't any ifla_info_data netlink attr for slaves
742 # an exception is raised when index is not found, so query_slaves will stay False
743 query_slaves = False
744
745 user_bond_slaves = None
746 running_bond_slaves = None
747 try:
748 del iface_attrs[iface_attrs.index('bond-slaves')]
749
750 # if user specified bond-slaves we need to display it
751 query_slaves = True
752 if not user_bond_slaves:
753 user_bond_slaves = self._get_slave_list(ifaceobj)
223ba5af 754 running_bond_slaves = self.cache.get_slaves(ifaceobj.name)
d486dd0d
JF
755
756 self._query_check_bond_slaves(ifaceobjcurr, 'bond-slaves', user_bond_slaves, running_bond_slaves)
3218f49d 757 except Exception:
d486dd0d
JF
758 pass
759 try:
760 del iface_attrs[iface_attrs.index('bond-ports')]
761
762 # if user specified bond-ports we need to display it
763 if not query_slaves and not user_bond_slaves: # if get_slave_list was already called for slaves
764 user_bond_slaves = self._get_slave_list(ifaceobj)
223ba5af 765 running_bond_slaves = self.cache.get_slaves(ifaceobj.name)
d486dd0d
JF
766
767 self._query_check_bond_slaves(ifaceobjcurr, 'bond-ports', user_bond_slaves, running_bond_slaves)
3218f49d 768 except Exception:
d486dd0d
JF
769 pass
770
771 for attr in iface_attrs:
772 nl_attr = self._bond_attr_netlink_map[attr]
773 translate_func = self._bond_attr_ifquery_check_translate_func[nl_attr]
223ba5af 774 current_config = self.cache.get_link_info_data_attribute(ifaceobj.name, nl_attr)
d486dd0d
JF
775 user_config = ifaceobj.get_attr_value_first(attr)
776
777 if current_config == translate_func(user_config):
778 ifaceobjcurr.update_config_with_status(attr, user_config, 0)
779 else:
780 ifaceobjcurr.update_config_with_status(attr, str(current_config), 1)
781
782 @staticmethod
783 def translate_nl_value_yesno(value):
784 return 'yes' if value else 'no'
785
786 @staticmethod
787 def translate_nl_value_slowfast(value):
788 return 'fast' if value else 'slow'
789
790 def _query_running_attrs(self, bondname):
223ba5af
JF
791 cached_vxlan_ifla_info_data = self.cache.get_link_info_data(bondname)
792
d486dd0d 793 bond_attrs = {
223ba5af
JF
794 'bond-mode': Link.ifla_bond_mode_pretty_tbl.get(cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_MODE)),
795 'bond-miimon': cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_MIIMON),
796 'bond-use-carrier': self.translate_nl_value_yesno(cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_USE_CARRIER)),
797 'bond-lacp-rate': self.translate_nl_value_slowfast(cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_AD_LACP_RATE)),
798 'bond-min-links': cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_MIN_LINKS),
799 'bond-ad-actor-system': cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_AD_ACTOR_SYSTEM),
9808982e 800 'es-sys-mac': cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_AD_ACTOR_SYSTEM),
223ba5af
JF
801 'bond-ad-actor-sys-prio': cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_AD_ACTOR_SYS_PRIO),
802 '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)),
803 'bond-lacp-bypass-allow': self.translate_nl_value_yesno(cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_AD_LACP_BYPASS)),
804 'bond-num-unsol-na': cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_NUM_PEER_NOTIF),
805 'bond-num-grat-arp': cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_NUM_PEER_NOTIF),
806 'bond-updelay': cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_UPDELAY),
807 'bond-downdelay': cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_DOWNDELAY)
d486dd0d 808 }
223ba5af
JF
809
810 cached_bond_primary = cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_PRIMARY)
811 if cached_bond_primary:
812 bond_attrs['bond-primary'] = self.cache.get_ifname(cached_bond_primary)
813
814 slaves = self.cache.get_slaves(bondname)
d486dd0d
JF
815 if slaves:
816 bond_attrs['bond-slaves'] = slaves
817 return bond_attrs
818
819 def _query_running(self, ifaceobjrunning, ifaceobj_getfunc=None):
223ba5af 820 if not self.cache.bond_exists(ifaceobjrunning.name):
d486dd0d
JF
821 return
822 bond_attrs = self._query_running_attrs(ifaceobjrunning.name)
823 if bond_attrs.get('bond-slaves'):
824 bond_attrs['bond-slaves'] = ' '.join(bond_attrs.get('bond-slaves'))
825
826 [ifaceobjrunning.update_config(k, str(v))
3b01ed76 827 for k, v in list(bond_attrs.items())
d486dd0d
JF
828 if v is not None]
829
830 _run_ops = {
831 'pre-up': _up,
832 'post-down': _down,
833 'query-running': _query_running,
834 'query-checkcurr': _query_check
835 }
836
837 def get_ops(self):
838 """ returns list of ops supported by this module """
3b01ed76 839 return list(self._run_ops.keys())
d486dd0d 840
d486dd0d
JF
841 def run(self, ifaceobj, operation, query_ifaceobj=None,
842 ifaceobj_getfunc=None):
843 """ run bond configuration on the interface object passed as argument
844
845 Args:
846 **ifaceobj** (object): iface object
847
848 **operation** (str): any of 'pre-up', 'post-down', 'query-checkcurr',
849 'query-running'
850
851 Kwargs:
852 **query_ifaceobj** (object): query check ifaceobject. This is only
853 valid when op is 'query-checkcurr'. It is an object same as
854 ifaceobj, but contains running attribute values and its config
855 status. The modules can use it to return queried running state
856 of interfaces. status is success if the running state is same
857 as user required state in ifaceobj. error otherwise.
858 """
859 op_handler = self._run_ops.get(operation)
860 if not op_handler:
861 return
862 if operation != 'query-running' and not self._is_bond(ifaceobj):
863 return
d486dd0d
JF
864 if operation == 'query-checkcurr':
865 op_handler(self, ifaceobj, query_ifaceobj,
866 ifaceobj_getfunc=ifaceobj_getfunc)
867 else:
868 op_handler(self, ifaceobj, ifaceobj_getfunc=ifaceobj_getfunc)