]> git.proxmox.com Git - mirror_ifupdown2.git/blob - ifupdown2/addons/bond.py
use "except Exception:" instead of "except:"
[mirror_ifupdown2.git] / ifupdown2 / addons / bond.py
1 #!/usr/bin/env python3
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
9 import os
10
11 try:
12 from ifupdown2.lib.addon import Addon
13 from ifupdown2.nlmanager.nlmanager import Link
14
15 from ifupdown2.ifupdown.iface import *
16 from ifupdown2.ifupdown.utils import utils
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
22 from ifupdown2.ifupdownaddons.modulebase import moduleBase
23 except (ImportError, ModuleNotFoundError):
24 from lib.addon import Addon
25 from nlmanager.nlmanager import Link
26
27 from ifupdown.iface import *
28 from ifupdown.utils import utils
29 from ifupdown.statemanager import statemanager_api as statemanager
30
31 from ifupdownaddons.modulebase import moduleBase
32
33 import ifupdown.policymanager as policymanager
34 import ifupdown.ifupdownflags as ifupdownflags
35
36 class bond(Addon, moduleBase):
37 """ ifupdown2 addon module to configure bond interfaces """
38
39 overrides_ifupdown_scripts = ['ifenslave', ]
40
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"]
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"]
174 },
175 "es-sys-mac": {
176 "help": "evpn-mh: system mac address",
177 "validvals": ["<mac>", ],
178 "example": ["es-sys-mac 00:00:00:00:00:42"],
179 }
180 }
181 }
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,
192 'es-sys-mac': Link.IFLA_BOND_AD_ACTOR_SYSTEM,
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,
199 'bond-downdelay': Link.IFLA_BOND_DOWNDELAY,
200 'bond-primary': Link.IFLA_BOND_PRIMARY,
201 'bond-primary-reselect': Link.IFLA_BOND_PRIMARY_RESELECT
202
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,
218 Link.IFLA_BOND_DOWNDELAY: int,
219 Link.IFLA_BOND_PRIMARY_RESELECT: lambda x: Link.ifla_bond_primary_reselect_tbl[x],
220 # Link.IFLA_BOND_PRIMARY: self.netlink.get_ifname is added in __init__()
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))),
240 ('es-sys-mac', Link.IFLA_BOND_AD_ACTOR_SYSTEM, str),
241 ('bond-ad-sys-mac-addr', Link.IFLA_BOND_AD_ACTOR_SYSTEM, str),
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])
244 # ('bond-primary', Link.IFLA_BOND_PRIMARY, self.cache.get_ifindex) added in __init__()
245 )
246
247 def __init__(self, *args, **kargs):
248 Addon.__init__(self)
249 moduleBase.__init__(self, *args, **kargs)
250
251 if not os.path.exists('/sys/class/net/bonding_masters'):
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))
256
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
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')
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
268 return ifaceobj.link_kind & ifaceLinkKind.BOND or self.get_bond_slaves(ifaceobj)
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
286 if ifaceobj.get_attr_value("es-sys-mac"):
287 ifaceobj.link_privflags |= ifaceLinkPrivFlags.ES_BOND
288
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):
295 return self.cache.get_slaves(ifaceobj.name)
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
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:
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
318 except Exception as e:
319 self.logger.info(str(e))
320
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
328 def _add_slaves(self, ifaceobj, runningslaves, ifaceobj_getfunc=None):
329 slaves = self._get_slave_list(ifaceobj)
330 if not slaves:
331 self.logger.debug('%s: no slaves found' %ifaceobj.name)
332 return
333
334 clag_bond = self._is_clag_bond(ifaceobj)
335
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:
343 if (not ifupdownflags.flags.PERFMODE and
344 not self.cache.link_exists(slave)):
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
350 if self.cache.link_is_up(slave):
351 self.netlink.link_down_force(slave)
352 link_up = True
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:
357 try:
358 self.netlink.link_set_protodown_on(slave)
359 except Exception as e:
360 self.logger.error('%s: %s' % (ifaceobj.name, str(e)))
361
362 self.enable_ipv6_if_prev_brport(slave)
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
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):
370 self.netlink.link_down_force(slave)
371 else:
372 self.netlink.link_up(slave)
373 except Exception as e:
374 self.logger.debug('%s: %s' % (ifaceobj.name, str(e)))
375 pass
376
377 if runningslaves:
378 for s in runningslaves:
379 if s not in slaves:
380 self.sysfs.bond_remove_slave(ifaceobj.name, s)
381 if clag_bond:
382 try:
383 self.netlink.link_set_protodown_off(s)
384 except Exception as e:
385 self.logger.error('%s: %s' % (ifaceobj.name, str(e)))
386 else:
387 # apply link-down config changes on running slaves
388 try:
389 link_up = self.cache.link_is_up(s)
390 config_link_down = (ifaceobj_getfunc(s)[0].link_privflags &
391 ifaceLinkPrivFlags.KEEP_LINK_DOWN)
392 if (config_link_down and link_up):
393 self.netlink.link_down_force(s)
394 elif (not config_link_down and not link_up):
395 self.netlink.link_up_force(s)
396 except Exception as e:
397 self.logger.warning('%s: %s' % (ifaceobj.name, str(e)))
398
399 def _check_updown_delay_log(self, ifaceobj, attr_name, value):
400 ifaceobj.status = ifaceStatus.ERROR
401 self.logger.error('%s: unable to set %s %s as MII link monitoring is '
402 'disabled' % (ifaceobj.name, attr_name, value))
403 # return False to notify syntax_check that an error has been logged
404 return False
405
406 def syntax_check_updown_delay(self, ifaceobj):
407 result = True
408 updelay = ifaceobj.get_attr_value_first('bond-updelay')
409 downdelay = ifaceobj.get_attr_value_first('bond-downdelay')
410
411 if not updelay and not downdelay:
412 return True
413
414 try:
415 miimon = int(ifaceobj.get_attr_value_first('bond-miimon'))
416 except Exception:
417 try:
418 miimon = int(policymanager.policymanager_api.get_iface_default(
419 module_name=self.__class__.__name__,
420 ifname=ifaceobj.name,
421 attr='bond-miimon'))
422 except Exception:
423 miimon = 0
424
425 if not miimon:
426 # self._check_updown_delay_log returns False no matter what
427 if updelay and int(updelay):
428 result = self._check_updown_delay_log(ifaceobj, 'bond-updelay', updelay)
429 if downdelay and int(downdelay):
430 result = self._check_updown_delay_log(ifaceobj, 'bond-downdelay', downdelay)
431
432 return result
433
434 _bond_updown_delay_nl_list = (
435 (Link.IFLA_BOND_UPDELAY, 'bond-updelay'),
436 (Link.IFLA_BOND_DOWNDELAY, 'bond-downdelay')
437 )
438
439 def check_updown_delay_nl(self, link_exists, ifaceobj, ifla_info_data):
440 """
441 IFLA_BOND_MIIMON
442 Specifies the time, in milliseconds, to wait before enabling a slave
443 after a link recovery has been detected. This option is only valid
444 for the miimon link monitor. The updelay value should be a multiple
445 of the miimon value; if not, it will be rounded down to the nearest
446 multiple. The default value is 0.
447
448 This ifla_bond_miimon code should be move to get_ifla_bond_attr_from_user_config
449 but we need to know if the operation was successful to update the cache accordingly
450 """
451 ifla_bond_miimon = ifla_info_data.get(Link.IFLA_BOND_MIIMON)
452 if link_exists and ifla_bond_miimon is None:
453 ifla_bond_miimon = self.cache.get_link_info_data_attribute(ifaceobj.name, Link.IFLA_BOND_MIIMON)
454
455 if ifla_bond_miimon == 0:
456 for nl_attr, attr_name in self._bond_updown_delay_nl_list:
457 delay = ifla_info_data.get(nl_attr)
458 # if up-down-delay exists we need to remove it, if non zero log error
459 if delay is not None:
460 if delay > 0:
461 self._check_updown_delay_log(ifaceobj, attr_name, delay)
462 del ifla_info_data[nl_attr]
463 return True
464 return False
465
466 _bond_lacp_attrs = (
467 (Link.IFLA_BOND_AD_LACP_RATE, 'bond-lacp-rate'),
468 (Link.IFLA_BOND_AD_LACP_BYPASS, 'bond-lacp-bypass')
469 )
470
471 def _check_bond_mode_user_config(self, ifname, link_exists, ifla_info_data):
472 ifla_bond_mode = ifla_info_data.get(Link.IFLA_BOND_MODE)
473 if ifla_bond_mode is None and link_exists:
474 ifla_bond_mode = self.cache.get_link_info_data_attribute(ifname, Link.IFLA_BOND_MODE)
475 # in this case the link already exists (we have a cached value):
476 # if IFLA_BOND_MODE is not present in ifla_info_data it means:
477 # - that bond-mode was present in the user config and didn't change
478 # - never was in the user config so bond mode should be the system default value
479 # - was removed from the stanza so we might have to reset it to default value
480 # nevertheless we need to add it back to the ifla_info_data dict to check
481 # if we need to reset the mode to system default
482 ifla_info_data[Link.IFLA_BOND_MODE] = ifla_bond_mode
483
484 if ifla_bond_mode == 4: # 802.3ad
485 min_links = ifla_info_data.get(Link.IFLA_BOND_MIN_LINKS)
486 if min_links is None:
487 min_links = self.cache.get_link_info_data_attribute(ifname, Link.IFLA_BOND_MIN_LINKS)
488 # get_min_links_nl may return None so we need to strictly check 0
489 if min_links == 0:
490 self.logger.warning('%s: attribute bond-min-links is set to \'0\'' % ifname)
491 else:
492 # IFLA_BOND_AD_LACP_RATE and IFLA_BOND_AD_LACP_BYPASS only for 802.3ad mode (4)
493 for nl_attr, attr_name in self._bond_lacp_attrs:
494 if nl_attr in ifla_info_data:
495 self.logger.info('%s: ignoring %s: only available for 802.3ad mode (4)' % (ifname, attr_name))
496 del ifla_info_data[nl_attr]
497
498 @staticmethod
499 def get_saved_ifaceobj(link_exists, ifname):
500 if link_exists:
501 old_config = statemanager.get_ifaceobjs(ifname)
502 if old_config:
503 return old_config[0]
504 return None
505
506 def get_ifla_bond_attr_from_user_config(self, ifaceobj, link_exists):
507 """
508 Potential issue: if a user load the bond driver with custom
509 default values (say bond-mode 3), ifupdown2 has no knowledge
510 of these default values.
511 At bond creation everything should work, bonds will be created
512 with mode 3 (even if not specified under the stanza).
513 But, for example: if the user specifies a value under bond-mode
514 and later on the user removes the bond-mode line from the stanza
515 we will detect it and reset to MODINFO: BOND-MODE: DEFAULT aka 0
516 which is not the real default value that the user may expect.
517 """
518 ifname = ifaceobj.name
519 ifla_info_data = OrderedDict()
520 old_config = self.get_saved_ifaceobj(link_exists, ifname)
521
522 # for each bond attribute we fetch the user configuration
523 # if no configuration is provided we look for a config in policy files
524 for attr_name, netlink_attr, func_ptr in self._bond_attr_set_list:
525 cached_value = None
526 user_config = ifaceobj.get_attr_value_first(attr_name)
527
528 if not user_config:
529 user_config = policymanager.policymanager_api.get_iface_default(
530 module_name=self.__class__.__name__,
531 ifname=ifname,
532 attr=attr_name)
533 if user_config:
534 self.logger.debug('%s: %s %s: extracted from policy files'
535 % (ifname, attr_name, user_config))
536
537 # no policy override, do we need to reset an attr to default value?
538 if not user_config and old_config and old_config.get_attr_value_first(attr_name):
539 # if the link already exists but the value is set
540 # (potentially removed from the stanza, we need to reset it to default)
541 # might not work for specific cases, see explanation at the top of this function :)
542 user_config = self.get_attr_default_value(attr_name)
543 if user_config:
544 self.logger.debug('%s: %s: removed from stanza, resetting to default value: %s'
545 % (ifname, attr_name, user_config))
546
547 if user_config:
548 try:
549 nl_value = func_ptr(user_config.lower())
550
551 if link_exists:
552 cached_value = self.cache.get_link_info_data_attribute(ifname, netlink_attr)
553
554 if link_exists and cached_value is None:
555 # the link already exists but we don't have any value
556 # cached for this attr, it probably means that the
557 # capability is not available on this system (i.e old kernel)
558 self.logger.debug('%s: ignoring %s %s: capability '
559 'probably not supported on this system'
560 % (ifname, attr_name, user_config))
561 continue
562 elif link_exists:
563 # there should be a cached value if the link already exists
564 if cached_value == nl_value:
565 # if the user value is already cached: continue
566 continue
567
568 # else: the link doesn't exist so we create the bond with
569 # all the user/policy defined values without extra checks
570 ifla_info_data[netlink_attr] = nl_value
571
572 if cached_value is not None:
573 self.logger.info('%s: set %s %s (cache %s)' % (ifname, attr_name, user_config, cached_value))
574 else:
575 self.logger.info('%s: set %s %s' % (ifname, attr_name, user_config))
576
577 except KeyError:
578 self.logger.warning('%s: invalid %s value %s' % (ifname, attr_name, user_config))
579
580 self._check_bond_mode_user_config(ifname, link_exists, ifla_info_data)
581 return ifla_info_data
582
583 _bond_down_nl_attributes_list = (
584 Link.IFLA_BOND_MODE,
585 Link.IFLA_BOND_XMIT_HASH_POLICY,
586 Link.IFLA_BOND_AD_LACP_RATE,
587 Link.IFLA_BOND_MIN_LINKS
588 )
589
590 def _should_down_bond(self, ifla_info_data):
591 for nl_attr in self._bond_down_nl_attributes_list:
592 if nl_attr in ifla_info_data:
593 return True
594 return False
595
596 def should_update_bond_mode(self, ifaceobj, ifname, is_link_up, ifla_info_data, bond_slaves):
597 # if bond-mode was changed the bond needs to be brought
598 # down and slaves un-slaved before bond mode is changed.
599 cached_bond_mode = self.cache.get_link_info_data_attribute(ifname, Link.IFLA_BOND_MODE)
600 ifla_bond_mode = ifla_info_data.get(Link.IFLA_BOND_MODE)
601
602 # bond-mode was changed or is not specified
603 if ifla_bond_mode is not None:
604 if ifla_bond_mode != cached_bond_mode:
605 self.logger.info('%s: bond mode changed to %s: running ops on bond and slaves'
606 % (ifname, ifla_bond_mode))
607 if is_link_up:
608 self.netlink.link_down(ifname)
609 is_link_up = False
610
611 for lower_dev in ifaceobj.lowerifaces:
612 self.netlink.link_set_nomaster(lower_dev)
613
614 # when unslaving a device from an ES bond we need to set
615 # protodown off
616 if ifaceobj.link_privflags & ifaceLinkPrivFlags.ES_BOND:
617 self.netlink.link_set_protodown_off(lower_dev)
618
619 try:
620 bond_slaves.remove(lower_dev)
621 except Exception:
622 pass
623
624 else:
625 # bond-mode user config value is the current running(cached) value
626 # no need to reset it again we can ignore this attribute
627 del ifla_info_data[Link.IFLA_BOND_MODE]
628
629 return is_link_up, bond_slaves
630
631 def create_or_set_bond_config(self, ifaceobj):
632 ifname = ifaceobj.name
633 link_exists, is_link_up = self.cache.link_exists_and_up(ifname)
634 ifla_info_data = self.get_ifla_bond_attr_from_user_config(ifaceobj, link_exists)
635
636 remove_delay_from_cache = self.check_updown_delay_nl(link_exists, ifaceobj, ifla_info_data)
637
638 # if link exists: down link if specific attributes are specified
639 if link_exists:
640 # did bond-mode changed?
641 is_link_up, bond_slaves = self.should_update_bond_mode(
642 ifaceobj,
643 ifname,
644 is_link_up,
645 ifla_info_data,
646 self.cache.get_slaves(ifname)
647 )
648
649 # if specific attributes need to be set we need to down the bond first
650 if ifla_info_data and is_link_up:
651 if self._should_down_bond(ifla_info_data):
652 self.netlink.link_down_force(ifname)
653 is_link_up = False
654 else:
655 bond_slaves = []
656
657 if link_exists and not ifla_info_data:
658 # if the bond already exists and no attrs need to be set
659 # ignore the netlink call
660 self.logger.info('%s: already exists, no change detected' % ifname)
661 else:
662 try:
663 self.netlink.link_add_bond_with_info_data(ifname, ifla_info_data)
664 except Exception as e:
665 # defensive code
666 # if anything happens, we try to set up the bond with the sysfs api
667 self.logger.debug('%s: bond setup: %s' % (ifname, str(e)))
668 self.create_or_set_bond_config_sysfs(ifaceobj, ifla_info_data)
669
670 if remove_delay_from_cache:
671 # making sure up/down delay attributes are set to 0 before caching
672 # this can be removed when moving to a nllistener/live cache
673 ifla_info_data[Link.IFLA_BOND_UPDELAY] = 0
674 ifla_info_data[Link.IFLA_BOND_DOWNDELAY] = 0
675
676 if link_exists and ifla_info_data and not is_link_up:
677 self.netlink.link_up_force(ifname)
678
679 return bond_slaves
680
681 def create_or_set_bond_config_sysfs(self, ifaceobj, ifla_info_data):
682 if not self.cache.link_exists(ifaceobj.name):
683 self.sysfs.bond_create(ifaceobj.name)
684 self.sysfs.bond_set_attrs_nl(ifaceobj.name, ifla_info_data)
685
686 def _up(self, ifaceobj, ifaceobj_getfunc=None):
687 try:
688 bond_slaves = self.create_or_set_bond_config(ifaceobj)
689 self._add_slaves(
690 ifaceobj,
691 bond_slaves,
692 ifaceobj_getfunc,
693 )
694 except Exception as e:
695 self.log_error(str(e), ifaceobj)
696
697 def _down(self, ifaceobj, ifaceobj_getfunc=None):
698 try:
699 self.netlink.link_del(ifaceobj.name)
700 except Exception as e:
701 self.log_warn('%s: %s' % (ifaceobj.name, str(e)))
702
703 def _query_check_bond_slaves(self, ifaceobjcurr, attr, user_bond_slaves, running_bond_slaves):
704 query = 1
705
706 if user_bond_slaves and running_bond_slaves:
707 if not set(user_bond_slaves).symmetric_difference(running_bond_slaves):
708 query = 0
709
710 # we want to display the same bond-slaves list as provided
711 # in the interfaces file but if this list contains regexes or
712 # globs, for now, we won't try to change it.
713 if 'regex' in user_bond_slaves or 'glob' in user_bond_slaves:
714 user_bond_slaves = running_bond_slaves
715 else:
716 ordered = []
717 for slave in user_bond_slaves:
718 if slave in running_bond_slaves:
719 ordered.append(slave)
720 user_bond_slaves = ordered
721 ifaceobjcurr.update_config_with_status(attr, ' '.join(user_bond_slaves) if user_bond_slaves else 'None', query)
722
723 def _query_check(self, ifaceobj, ifaceobjcurr, ifaceobj_getfunc=None):
724 if not self.cache.bond_exists(ifaceobj.name):
725 self.logger.debug('bond iface %s does not exist' % ifaceobj.name)
726 return
727
728 iface_attrs = self.dict_key_subset(ifaceobj.config, self.get_mod_attrs())
729 if not iface_attrs:
730 return
731
732 # remove bond-slaves and bond-ports from the list,
733 # because there aren't any ifla_info_data netlink attr for slaves
734 # an exception is raised when index is not found, so query_slaves will stay False
735 query_slaves = False
736
737 user_bond_slaves = None
738 running_bond_slaves = None
739 try:
740 del iface_attrs[iface_attrs.index('bond-slaves')]
741
742 # if user specified bond-slaves we need to display it
743 query_slaves = True
744 if not user_bond_slaves:
745 user_bond_slaves = self._get_slave_list(ifaceobj)
746 running_bond_slaves = self.cache.get_slaves(ifaceobj.name)
747
748 self._query_check_bond_slaves(ifaceobjcurr, 'bond-slaves', user_bond_slaves, running_bond_slaves)
749 except Exception:
750 pass
751 try:
752 del iface_attrs[iface_attrs.index('bond-ports')]
753
754 # if user specified bond-ports we need to display it
755 if not query_slaves and not user_bond_slaves: # if get_slave_list was already called for slaves
756 user_bond_slaves = self._get_slave_list(ifaceobj)
757 running_bond_slaves = self.cache.get_slaves(ifaceobj.name)
758
759 self._query_check_bond_slaves(ifaceobjcurr, 'bond-ports', user_bond_slaves, running_bond_slaves)
760 except Exception:
761 pass
762
763 for attr in iface_attrs:
764 nl_attr = self._bond_attr_netlink_map[attr]
765 translate_func = self._bond_attr_ifquery_check_translate_func[nl_attr]
766 current_config = self.cache.get_link_info_data_attribute(ifaceobj.name, nl_attr)
767 user_config = ifaceobj.get_attr_value_first(attr)
768
769 if current_config == translate_func(user_config):
770 ifaceobjcurr.update_config_with_status(attr, user_config, 0)
771 else:
772 ifaceobjcurr.update_config_with_status(attr, str(current_config), 1)
773
774 @staticmethod
775 def translate_nl_value_yesno(value):
776 return 'yes' if value else 'no'
777
778 @staticmethod
779 def translate_nl_value_slowfast(value):
780 return 'fast' if value else 'slow'
781
782 def _query_running_attrs(self, bondname):
783 cached_vxlan_ifla_info_data = self.cache.get_link_info_data(bondname)
784
785 bond_attrs = {
786 'bond-mode': Link.ifla_bond_mode_pretty_tbl.get(cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_MODE)),
787 'bond-miimon': cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_MIIMON),
788 'bond-use-carrier': self.translate_nl_value_yesno(cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_USE_CARRIER)),
789 'bond-lacp-rate': self.translate_nl_value_slowfast(cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_AD_LACP_RATE)),
790 'bond-min-links': cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_MIN_LINKS),
791 'bond-ad-actor-system': cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_AD_ACTOR_SYSTEM),
792 'es-sys-mac': cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_AD_ACTOR_SYSTEM),
793 'bond-ad-actor-sys-prio': cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_AD_ACTOR_SYS_PRIO),
794 '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)),
795 'bond-lacp-bypass-allow': self.translate_nl_value_yesno(cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_AD_LACP_BYPASS)),
796 'bond-num-unsol-na': cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_NUM_PEER_NOTIF),
797 'bond-num-grat-arp': cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_NUM_PEER_NOTIF),
798 'bond-updelay': cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_UPDELAY),
799 'bond-downdelay': cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_DOWNDELAY)
800 }
801
802 cached_bond_primary = cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_PRIMARY)
803 if cached_bond_primary:
804 bond_attrs['bond-primary'] = self.cache.get_ifname(cached_bond_primary)
805
806 slaves = self.cache.get_slaves(bondname)
807 if slaves:
808 bond_attrs['bond-slaves'] = slaves
809 return bond_attrs
810
811 def _query_running(self, ifaceobjrunning, ifaceobj_getfunc=None):
812 if not self.cache.bond_exists(ifaceobjrunning.name):
813 return
814 bond_attrs = self._query_running_attrs(ifaceobjrunning.name)
815 if bond_attrs.get('bond-slaves'):
816 bond_attrs['bond-slaves'] = ' '.join(bond_attrs.get('bond-slaves'))
817
818 [ifaceobjrunning.update_config(k, str(v))
819 for k, v in list(bond_attrs.items())
820 if v is not None]
821
822 _run_ops = {
823 'pre-up': _up,
824 'post-down': _down,
825 'query-running': _query_running,
826 'query-checkcurr': _query_check
827 }
828
829 def get_ops(self):
830 """ returns list of ops supported by this module """
831 return list(self._run_ops.keys())
832
833 def run(self, ifaceobj, operation, query_ifaceobj=None,
834 ifaceobj_getfunc=None):
835 """ run bond configuration on the interface object passed as argument
836
837 Args:
838 **ifaceobj** (object): iface object
839
840 **operation** (str): any of 'pre-up', 'post-down', 'query-checkcurr',
841 'query-running'
842
843 Kwargs:
844 **query_ifaceobj** (object): query check ifaceobject. This is only
845 valid when op is 'query-checkcurr'. It is an object same as
846 ifaceobj, but contains running attribute values and its config
847 status. The modules can use it to return queried running state
848 of interfaces. status is success if the running state is same
849 as user required state in ifaceobj. error otherwise.
850 """
851 op_handler = self._run_ops.get(operation)
852 if not op_handler:
853 return
854 if operation != 'query-running' and not self._is_bond(ifaceobj):
855 return
856 if operation == 'query-checkcurr':
857 op_handler(self, ifaceobj, query_ifaceobj,
858 ifaceobj_getfunc=ifaceobj_getfunc)
859 else:
860 op_handler(self, ifaceobj, ifaceobj_getfunc=ifaceobj_getfunc)