]> git.proxmox.com Git - mirror_ifupdown2.git/blob - ifupdown2/addons/bond.py
Merge branch 'master-next' into python3
[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 }
167 }
168
169 _bond_attr_netlink_map = {
170 'bond-mode': Link.IFLA_BOND_MODE,
171 'bond-miimon': Link.IFLA_BOND_MIIMON,
172 'bond-use-carrier': Link.IFLA_BOND_USE_CARRIER,
173 'bond-lacp-rate': Link.IFLA_BOND_AD_LACP_RATE,
174 'bond-xmit-hash-policy': Link.IFLA_BOND_XMIT_HASH_POLICY,
175 'bond-min-links': Link.IFLA_BOND_MIN_LINKS,
176 'bond-num-grat-arp': Link.IFLA_BOND_NUM_PEER_NOTIF,
177 'bond-num-unsol-na': Link.IFLA_BOND_NUM_PEER_NOTIF,
178 'bond-ad-sys-mac-addr': Link.IFLA_BOND_AD_ACTOR_SYSTEM,
179 'bond-ad-actor-system': Link.IFLA_BOND_AD_ACTOR_SYSTEM,
180 'bond-ad-sys-priority': Link.IFLA_BOND_AD_ACTOR_SYS_PRIO,
181 'bond-ad-actor-sys-prio': Link.IFLA_BOND_AD_ACTOR_SYS_PRIO,
182 'bond-lacp-bypass-allow': Link.IFLA_BOND_AD_LACP_BYPASS,
183 'bond-updelay': Link.IFLA_BOND_UPDELAY,
184 'bond-downdelay': Link.IFLA_BOND_DOWNDELAY,
185 'bond-primary': Link.IFLA_BOND_PRIMARY
186 }
187
188 # ifquery-check attr dictionary with callable object to translate user data to netlink format
189 _bond_attr_ifquery_check_translate_func = {
190 Link.IFLA_BOND_MODE: lambda x: Link.ifla_bond_mode_tbl[x],
191 Link.IFLA_BOND_MIIMON: int,
192 Link.IFLA_BOND_USE_CARRIER: utils.get_boolean_from_string,
193 Link.IFLA_BOND_AD_LACP_RATE: lambda x: int(utils.get_boolean_from_string(x)),
194 Link.IFLA_BOND_XMIT_HASH_POLICY: lambda x: Link.ifla_bond_xmit_hash_policy_tbl[x],
195 Link.IFLA_BOND_MIN_LINKS: int,
196 Link.IFLA_BOND_NUM_PEER_NOTIF: int,
197 Link.IFLA_BOND_AD_ACTOR_SYSTEM: str,
198 Link.IFLA_BOND_AD_ACTOR_SYS_PRIO: int,
199 Link.IFLA_BOND_AD_LACP_BYPASS: lambda x: int(utils.get_boolean_from_string(x)),
200 Link.IFLA_BOND_UPDELAY: int,
201 Link.IFLA_BOND_DOWNDELAY: int,
202 # Link.IFLA_BOND_PRIMARY: self.netlink.get_ifname is added in __init__()
203 }
204
205 # ifup attr list with callable object to translate user data to netlink format
206 # in the future this can be moved to a dictionary, whenever we detect that some
207 # netlink capabilities are missing we can dynamically remove them from the dict.
208 _bond_attr_set_list = (
209 ('bond-mode', Link.IFLA_BOND_MODE, lambda x: Link.ifla_bond_mode_tbl[x]),
210 ('bond-xmit-hash-policy', Link.IFLA_BOND_XMIT_HASH_POLICY, lambda x: Link.ifla_bond_xmit_hash_policy_tbl[x]),
211 ('bond-miimon', Link.IFLA_BOND_MIIMON, int),
212 ('bond-min-links', Link.IFLA_BOND_MIN_LINKS, int),
213 ('bond-num-grat-arp', Link.IFLA_BOND_NUM_PEER_NOTIF, int),
214 ('bond-num-unsol-na', Link.IFLA_BOND_NUM_PEER_NOTIF, int),
215 ('bond-ad-sys-priority', Link.IFLA_BOND_AD_ACTOR_SYS_PRIO, int),
216 ('bond-ad-actor-sys-prio', Link.IFLA_BOND_AD_ACTOR_SYS_PRIO, int),
217 ('bond-updelay', Link.IFLA_BOND_UPDELAY, int),
218 ('bond-downdelay', Link.IFLA_BOND_DOWNDELAY, int),
219 ('bond-use-carrier', Link.IFLA_BOND_USE_CARRIER, lambda x: int(utils.get_boolean_from_string(x))),
220 ('bond-lacp-rate', Link.IFLA_BOND_AD_LACP_RATE, lambda x: int(utils.get_boolean_from_string(x))),
221 ('bond-lacp-bypass-allow', Link.IFLA_BOND_AD_LACP_BYPASS, lambda x: int(utils.get_boolean_from_string(x))),
222 ('bond-ad-sys-mac-addr', Link.IFLA_BOND_AD_ACTOR_SYSTEM, str),
223 ('bond-ad-actor-system', Link.IFLA_BOND_AD_ACTOR_SYSTEM, str)
224 # ('bond-primary', Link.IFLA_BOND_PRIMARY, self.cache.get_ifindex) added in __init__()
225 )
226
227 def __init__(self, *args, **kargs):
228 Addon.__init__(self)
229 moduleBase.__init__(self, *args, **kargs)
230
231 if not os.path.exists('/sys/class/net/bonding_masters'):
232 try:
233 utils.exec_command('modprobe -q bonding')
234 except Exception as e:
235 self.logger.info("bond: error while loading bonding module: %s" % str(e))
236
237 self._bond_attr_ifquery_check_translate_func[Link.IFLA_BOND_PRIMARY] = self.cache.get_ifindex
238 self._bond_attr_set_list = self._bond_attr_set_list + (('bond-primary', Link.IFLA_BOND_PRIMARY, self.cache.get_ifindex),)
239
240 @staticmethod
241 def get_bond_slaves(ifaceobj):
242 slaves = ifaceobj.get_attr_value_first('bond-slaves')
243 if not slaves:
244 slaves = ifaceobj.get_attr_value_first('bond-ports')
245 return slaves
246
247 def _is_bond(self, ifaceobj):
248 # at first link_kind is not set but once ifupdownmain
249 # calls get_dependent_ifacenames link_kind is set to BOND
250 return ifaceobj.link_kind & ifaceLinkKind.BOND or self.get_bond_slaves(ifaceobj)
251
252 def get_dependent_ifacenames(self, ifaceobj, ifacenames_all=None):
253 """ Returns list of interfaces dependent on ifaceobj """
254
255 if not self._is_bond(ifaceobj):
256 return None
257 slave_list = self.parse_port_list(ifaceobj.name,
258 self.get_bond_slaves(ifaceobj),
259 ifacenames_all)
260 ifaceobj.dependency_type = ifaceDependencyType.MASTER_SLAVE
261 # Also save a copy for future use
262 ifaceobj.priv_data = list(slave_list)
263 if ifaceobj.link_type != ifaceLinkType.LINK_NA:
264 ifaceobj.link_type = ifaceLinkType.LINK_MASTER
265 ifaceobj.link_kind |= ifaceLinkKind.BOND
266 ifaceobj.role |= ifaceRole.MASTER
267
268 return slave_list
269
270 def syntax_check(self, ifaceobj, ifaceobj_getfunc):
271 return self.syntax_check_updown_delay(ifaceobj)
272
273 def get_dependent_ifacenames_running(self, ifaceobj):
274 return self.cache.get_slaves(ifaceobj.name)
275
276 def _get_slave_list(self, ifaceobj):
277 """ Returns slave list present in ifaceobj config """
278
279 # If priv data already has slave list use that first.
280 if ifaceobj.priv_data:
281 return ifaceobj.priv_data
282 slaves = self.get_bond_slaves(ifaceobj)
283 if slaves:
284 return self.parse_port_list(ifaceobj.name, slaves)
285 else:
286 return None
287
288 def enable_ipv6_if_prev_brport(self, ifname):
289 """
290 If the intf was previously enslaved to a bridge it is possible ipv6 is still disabled.
291 """
292 try:
293 for ifaceobj in statemanager.get_ifaceobjs(ifname) or []:
294 if ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT:
295 self.write_file("/proc/sys/net/ipv6/conf/%s/disable_ipv6" % ifname, "0")
296 return
297 except Exception as e:
298 self.logger.info(str(e))
299
300 def _is_clag_bond(self, ifaceobj):
301 if self.get_bond_slaves(ifaceobj):
302 attrval = ifaceobj.get_attr_value_first('clag-id')
303 if attrval and attrval != '0':
304 return True
305 return False
306
307 def _add_slaves(self, ifaceobj, runningslaves, ifaceobj_getfunc=None):
308 slaves = self._get_slave_list(ifaceobj)
309 if not slaves:
310 self.logger.debug('%s: no slaves found' %ifaceobj.name)
311 return
312
313 clag_bond = self._is_clag_bond(ifaceobj)
314
315 for slave in set(slaves).difference(set(runningslaves)):
316 if (not ifupdownflags.flags.PERFMODE and
317 not self.cache.link_exists(slave)):
318 self.log_error('%s: skipping slave %s, does not exist'
319 %(ifaceobj.name, slave), ifaceobj,
320 raise_error=False)
321 continue
322 link_up = False
323 if self.cache.link_is_up(slave):
324 self.netlink.link_down_force(slave)
325 link_up = True
326 # If clag bond place the slave in a protodown state; clagd
327 # will protoup it when it is ready
328 if clag_bond:
329 try:
330 self.netlink.link_set_protodown_on(slave)
331 except Exception as e:
332 self.logger.error('%s: %s' % (ifaceobj.name, str(e)))
333
334 self.enable_ipv6_if_prev_brport(slave)
335 self.netlink.link_set_master(slave, ifaceobj.name)
336 # TODO: if this fail we should switch to iproute2
337 # start a batch: down - set master - up
338 if link_up or ifaceobj.link_type != ifaceLinkType.LINK_NA:
339 try:
340 if (ifaceobj_getfunc(slave)[0].link_privflags &
341 ifaceLinkPrivFlags.KEEP_LINK_DOWN):
342 self.netlink.link_down_force(slave)
343 else:
344 self.netlink.link_up(slave)
345 except Exception as e:
346 self.logger.debug('%s: %s' % (ifaceobj.name, str(e)))
347 pass
348
349 if runningslaves:
350 for s in runningslaves:
351 if s not in slaves:
352 self.sysfs.bond_remove_slave(ifaceobj.name, s)
353 if clag_bond:
354 try:
355 self.netlink.link_set_protodown_off(s)
356 except Exception as e:
357 self.logger.error('%s: %s' % (ifaceobj.name, str(e)))
358 else:
359 # apply link-down config changes on running slaves
360 try:
361 link_up = self.cache.link_is_up(s)
362 config_link_down = (ifaceobj_getfunc(s)[0].link_privflags &
363 ifaceLinkPrivFlags.KEEP_LINK_DOWN)
364 if (config_link_down and link_up):
365 self.netlink.link_down_force(s)
366 elif (not config_link_down and not link_up):
367 self.netlink.link_up_force(s)
368 except Exception as e:
369 self.logger.warning('%s: %s' % (ifaceobj.name, str(e)))
370
371 def _check_updown_delay_log(self, ifaceobj, attr_name, value):
372 ifaceobj.status = ifaceStatus.ERROR
373 self.logger.error('%s: unable to set %s %s as MII link monitoring is '
374 'disabled' % (ifaceobj.name, attr_name, value))
375 # return False to notify syntax_check that an error has been logged
376 return False
377
378 def syntax_check_updown_delay(self, ifaceobj):
379 result = True
380 updelay = ifaceobj.get_attr_value_first('bond-updelay')
381 downdelay = ifaceobj.get_attr_value_first('bond-downdelay')
382
383 if not updelay and not downdelay:
384 return True
385
386 try:
387 miimon = int(ifaceobj.get_attr_value_first('bond-miimon'))
388 except:
389 try:
390 miimon = int(policymanager.policymanager_api.get_iface_default(
391 module_name=self.__class__.__name__,
392 ifname=ifaceobj.name,
393 attr='bond-miimon'))
394 except:
395 miimon = 0
396
397 if not miimon:
398 # self._check_updown_delay_log returns False no matter what
399 if updelay and int(updelay):
400 result = self._check_updown_delay_log(ifaceobj, 'bond-updelay', updelay)
401 if downdelay and int(downdelay):
402 result = self._check_updown_delay_log(ifaceobj, 'bond-downdelay', downdelay)
403
404 return result
405
406 _bond_updown_delay_nl_list = (
407 (Link.IFLA_BOND_UPDELAY, 'bond-updelay'),
408 (Link.IFLA_BOND_DOWNDELAY, 'bond-downdelay')
409 )
410
411 def check_updown_delay_nl(self, link_exists, ifaceobj, ifla_info_data):
412 """
413 IFLA_BOND_MIIMON
414 Specifies the time, in milliseconds, to wait before enabling a slave
415 after a link recovery has been detected. This option is only valid
416 for the miimon link monitor. The updelay value should be a multiple
417 of the miimon value; if not, it will be rounded down to the nearest
418 multiple. The default value is 0.
419
420 This ifla_bond_miimon code should be move to get_ifla_bond_attr_from_user_config
421 but we need to know if the operation was successful to update the cache accordingly
422 """
423 ifla_bond_miimon = ifla_info_data.get(Link.IFLA_BOND_MIIMON)
424 if link_exists and ifla_bond_miimon is None:
425 ifla_bond_miimon = self.cache.get_link_info_data_attribute(ifaceobj.name, Link.IFLA_BOND_MIIMON)
426
427 if ifla_bond_miimon == 0:
428 for nl_attr, attr_name in self._bond_updown_delay_nl_list:
429 delay = ifla_info_data.get(nl_attr)
430 # if up-down-delay exists we need to remove it, if non zero log error
431 if delay is not None:
432 if delay > 0:
433 self._check_updown_delay_log(ifaceobj, attr_name, delay)
434 del ifla_info_data[nl_attr]
435 return True
436 return False
437
438 _bond_lacp_attrs = (
439 (Link.IFLA_BOND_AD_LACP_RATE, 'bond-lacp-rate'),
440 (Link.IFLA_BOND_AD_LACP_BYPASS, 'bond-lacp-bypass')
441 )
442
443 def _check_bond_mode_user_config(self, ifname, link_exists, ifla_info_data):
444 ifla_bond_mode = ifla_info_data.get(Link.IFLA_BOND_MODE)
445 if ifla_bond_mode is None and link_exists:
446 ifla_bond_mode = self.cache.get_link_info_data_attribute(ifname, Link.IFLA_BOND_MODE)
447 # in this case the link already exists (we have a cached value):
448 # if IFLA_BOND_MODE is not present in ifla_info_data it means:
449 # - that bond-mode was present in the user config and didn't change
450 # - never was in the user config so bond mode should be the system default value
451 # - was removed from the stanza so we might have to reset it to default value
452 # nevertheless we need to add it back to the ifla_info_data dict to check
453 # if we need to reset the mode to system default
454 ifla_info_data[Link.IFLA_BOND_MODE] = ifla_bond_mode
455
456 if ifla_bond_mode == 4: # 802.3ad
457 min_links = ifla_info_data.get(Link.IFLA_BOND_MIN_LINKS)
458 if min_links is None:
459 min_links = self.cache.get_link_info_data_attribute(ifname, Link.IFLA_BOND_MIN_LINKS)
460 # get_min_links_nl may return None so we need to strictly check 0
461 if min_links == 0:
462 self.logger.warning('%s: attribute bond-min-links is set to \'0\'' % ifname)
463 else:
464 # IFLA_BOND_AD_LACP_RATE and IFLA_BOND_AD_LACP_BYPASS only for 802.3ad mode (4)
465 for nl_attr, attr_name in self._bond_lacp_attrs:
466 if nl_attr in ifla_info_data:
467 self.logger.info('%s: ignoring %s: only available for 802.3ad mode (4)' % (ifname, attr_name))
468 del ifla_info_data[nl_attr]
469
470 @staticmethod
471 def get_saved_ifaceobj(link_exists, ifname):
472 if link_exists:
473 old_config = statemanager.get_ifaceobjs(ifname)
474 if old_config:
475 return old_config[0]
476 return None
477
478 def get_ifla_bond_attr_from_user_config(self, ifaceobj, link_exists):
479 """
480 Potential issue: if a user load the bond driver with custom
481 default values (say bond-mode 3), ifupdown2 has no knowledge
482 of these default values.
483 At bond creation everything should work, bonds will be created
484 with mode 3 (even if not specified under the stanza).
485 But, for example: if the user specifies a value under bond-mode
486 and later on the user removes the bond-mode line from the stanza
487 we will detect it and reset to MODINFO: BOND-MODE: DEFAULT aka 0
488 which is not the real default value that the user may expect.
489 """
490 ifname = ifaceobj.name
491 ifla_info_data = OrderedDict()
492 old_config = self.get_saved_ifaceobj(link_exists, ifname)
493
494 # for each bond attribute we fetch the user configuration
495 # if no configuration is provided we look for a config in policy files
496 for attr_name, netlink_attr, func_ptr in self._bond_attr_set_list:
497 cached_value = None
498 user_config = ifaceobj.get_attr_value_first(attr_name)
499
500 if not user_config:
501 user_config = policymanager.policymanager_api.get_iface_default(
502 module_name=self.__class__.__name__,
503 ifname=ifname,
504 attr=attr_name)
505 if user_config:
506 self.logger.debug('%s: %s %s: extracted from policy files'
507 % (ifname, attr_name, user_config))
508
509 # no policy override, do we need to reset an attr to default value?
510 if not user_config and old_config and old_config.get_attr_value_first(attr_name):
511 # if the link already exists but the value is set
512 # (potentially removed from the stanza, we need to reset it to default)
513 # might not work for specific cases, see explanation at the top of this function :)
514 user_config = self.get_attr_default_value(attr_name)
515 if user_config:
516 self.logger.debug('%s: %s: removed from stanza, resetting to default value: %s'
517 % (ifname, attr_name, user_config))
518
519 if user_config:
520 try:
521 nl_value = func_ptr(user_config.lower())
522
523 if link_exists:
524 cached_value = self.cache.get_link_info_data_attribute(ifname, netlink_attr)
525
526 if link_exists and cached_value is None:
527 # the link already exists but we don't have any value
528 # cached for this attr, it probably means that the
529 # capability is not available on this system (i.e old kernel)
530 self.logger.debug('%s: ignoring %s %s: capability '
531 'probably not supported on this system'
532 % (ifname, attr_name, user_config))
533 continue
534 elif link_exists:
535 # there should be a cached value if the link already exists
536 if cached_value == nl_value:
537 # if the user value is already cached: continue
538 continue
539
540 # else: the link doesn't exist so we create the bond with
541 # all the user/policy defined values without extra checks
542 ifla_info_data[netlink_attr] = nl_value
543
544 if cached_value is not None:
545 self.logger.info('%s: set %s %s (cache %s)' % (ifname, attr_name, user_config, cached_value))
546 else:
547 self.logger.info('%s: set %s %s' % (ifname, attr_name, user_config))
548
549 except KeyError:
550 self.logger.warning('%s: invalid %s value %s' % (ifname, attr_name, user_config))
551
552 self._check_bond_mode_user_config(ifname, link_exists, ifla_info_data)
553 return ifla_info_data
554
555 _bond_down_nl_attributes_list = (
556 Link.IFLA_BOND_MODE,
557 Link.IFLA_BOND_XMIT_HASH_POLICY,
558 Link.IFLA_BOND_AD_LACP_RATE,
559 Link.IFLA_BOND_MIN_LINKS
560 )
561
562 def _should_down_bond(self, ifla_info_data):
563 for nl_attr in self._bond_down_nl_attributes_list:
564 if nl_attr in ifla_info_data:
565 return True
566 return False
567
568 def should_update_bond_mode(self, ifaceobj, ifname, is_link_up, ifla_info_data, bond_slaves):
569 # if bond-mode was changed the bond needs to be brought
570 # down and slaves un-slaved before bond mode is changed.
571 cached_bond_mode = self.cache.get_link_info_data_attribute(ifname, Link.IFLA_BOND_MODE)
572 ifla_bond_mode = ifla_info_data.get(Link.IFLA_BOND_MODE)
573
574 # bond-mode was changed or is not specified
575 if ifla_bond_mode is not None:
576 if ifla_bond_mode != cached_bond_mode:
577 self.logger.info('%s: bond mode changed to %s: running ops on bond and slaves'
578 % (ifname, ifla_bond_mode))
579 if is_link_up:
580 self.netlink.link_down(ifname)
581 is_link_up = False
582
583 for lower_dev in ifaceobj.lowerifaces:
584 self.netlink.link_set_nomaster(lower_dev)
585 try:
586 bond_slaves.remove(lower_dev)
587 except:
588 pass
589
590 else:
591 # bond-mode user config value is the current running(cached) value
592 # no need to reset it again we can ignore this attribute
593 del ifla_info_data[Link.IFLA_BOND_MODE]
594
595 return is_link_up, bond_slaves
596
597 def create_or_set_bond_config(self, ifaceobj):
598 ifname = ifaceobj.name
599 link_exists, is_link_up = self.cache.link_exists_and_up(ifname)
600 ifla_info_data = self.get_ifla_bond_attr_from_user_config(ifaceobj, link_exists)
601
602 remove_delay_from_cache = self.check_updown_delay_nl(link_exists, ifaceobj, ifla_info_data)
603
604 # if link exists: down link if specific attributes are specified
605 if link_exists:
606 # did bond-mode changed?
607 is_link_up, bond_slaves = self.should_update_bond_mode(
608 ifaceobj,
609 ifname,
610 is_link_up,
611 ifla_info_data,
612 self.cache.get_slaves(ifname)
613 )
614
615 # if specific attributes need to be set we need to down the bond first
616 if ifla_info_data and is_link_up:
617 if self._should_down_bond(ifla_info_data):
618 self.netlink.link_down_force(ifname)
619 is_link_up = False
620 else:
621 bond_slaves = []
622
623 if link_exists and not ifla_info_data:
624 # if the bond already exists and no attrs need to be set
625 # ignore the netlink call
626 self.logger.info('%s: already exists, no change detected' % ifname)
627 else:
628 try:
629 self.netlink.link_add_bond_with_info_data(ifname, ifla_info_data)
630 except Exception as e:
631 # defensive code
632 # if anything happens, we try to set up the bond with the sysfs api
633 self.logger.debug('%s: bond setup: %s' % (ifname, str(e)))
634 self.create_or_set_bond_config_sysfs(ifaceobj, ifla_info_data)
635
636 if remove_delay_from_cache:
637 # making sure up/down delay attributes are set to 0 before caching
638 # this can be removed when moving to a nllistener/live cache
639 ifla_info_data[Link.IFLA_BOND_UPDELAY] = 0
640 ifla_info_data[Link.IFLA_BOND_DOWNDELAY] = 0
641
642 if link_exists and ifla_info_data and not is_link_up:
643 self.netlink.link_up_force(ifname)
644
645 return bond_slaves
646
647 def create_or_set_bond_config_sysfs(self, ifaceobj, ifla_info_data):
648 if not self.cache.link_exists(ifaceobj.name):
649 self.sysfs.bond_create(ifaceobj.name)
650 self.sysfs.bond_set_attrs_nl(ifaceobj.name, ifla_info_data)
651
652 def _up(self, ifaceobj, ifaceobj_getfunc=None):
653 try:
654 bond_slaves = self.create_or_set_bond_config(ifaceobj)
655 self._add_slaves(
656 ifaceobj,
657 bond_slaves,
658 ifaceobj_getfunc,
659 )
660 except Exception as e:
661 self.log_error(str(e), ifaceobj)
662
663 def _down(self, ifaceobj, ifaceobj_getfunc=None):
664 try:
665 self.netlink.link_del(ifaceobj.name)
666 except Exception as e:
667 self.log_warn('%s: %s' % (ifaceobj.name, str(e)))
668
669 def _query_check_bond_slaves(self, ifaceobjcurr, attr, user_bond_slaves, running_bond_slaves):
670 query = 1
671
672 if user_bond_slaves and running_bond_slaves:
673 if not set(user_bond_slaves).symmetric_difference(running_bond_slaves):
674 query = 0
675
676 # we want to display the same bond-slaves list as provided
677 # in the interfaces file but if this list contains regexes or
678 # globs, for now, we won't try to change it.
679 if 'regex' in user_bond_slaves or 'glob' in user_bond_slaves:
680 user_bond_slaves = running_bond_slaves
681 else:
682 ordered = []
683 for slave in user_bond_slaves:
684 if slave in running_bond_slaves:
685 ordered.append(slave)
686 user_bond_slaves = ordered
687 ifaceobjcurr.update_config_with_status(attr, ' '.join(user_bond_slaves) if user_bond_slaves else 'None', query)
688
689 def _query_check(self, ifaceobj, ifaceobjcurr, ifaceobj_getfunc=None):
690 if not self.cache.bond_exists(ifaceobj.name):
691 self.logger.debug('bond iface %s does not exist' % ifaceobj.name)
692 return
693
694 iface_attrs = self.dict_key_subset(ifaceobj.config, self.get_mod_attrs())
695 if not iface_attrs:
696 return
697
698 # remove bond-slaves and bond-ports from the list,
699 # because there aren't any ifla_info_data netlink attr for slaves
700 # an exception is raised when index is not found, so query_slaves will stay False
701 query_slaves = False
702
703 user_bond_slaves = None
704 running_bond_slaves = None
705 try:
706 del iface_attrs[iface_attrs.index('bond-slaves')]
707
708 # if user specified bond-slaves we need to display it
709 query_slaves = True
710 if not user_bond_slaves:
711 user_bond_slaves = self._get_slave_list(ifaceobj)
712 running_bond_slaves = self.cache.get_slaves(ifaceobj.name)
713
714 self._query_check_bond_slaves(ifaceobjcurr, 'bond-slaves', user_bond_slaves, running_bond_slaves)
715 except:
716 pass
717 try:
718 del iface_attrs[iface_attrs.index('bond-ports')]
719
720 # if user specified bond-ports we need to display it
721 if not query_slaves and not user_bond_slaves: # if get_slave_list was already called for slaves
722 user_bond_slaves = self._get_slave_list(ifaceobj)
723 running_bond_slaves = self.cache.get_slaves(ifaceobj.name)
724
725 self._query_check_bond_slaves(ifaceobjcurr, 'bond-ports', user_bond_slaves, running_bond_slaves)
726 except:
727 pass
728
729 for attr in iface_attrs:
730 nl_attr = self._bond_attr_netlink_map[attr]
731 translate_func = self._bond_attr_ifquery_check_translate_func[nl_attr]
732 current_config = self.cache.get_link_info_data_attribute(ifaceobj.name, nl_attr)
733 user_config = ifaceobj.get_attr_value_first(attr)
734
735 if current_config == translate_func(user_config):
736 ifaceobjcurr.update_config_with_status(attr, user_config, 0)
737 else:
738 ifaceobjcurr.update_config_with_status(attr, str(current_config), 1)
739
740 @staticmethod
741 def translate_nl_value_yesno(value):
742 return 'yes' if value else 'no'
743
744 @staticmethod
745 def translate_nl_value_slowfast(value):
746 return 'fast' if value else 'slow'
747
748 def _query_running_attrs(self, bondname):
749 cached_vxlan_ifla_info_data = self.cache.get_link_info_data(bondname)
750
751 bond_attrs = {
752 'bond-mode': Link.ifla_bond_mode_pretty_tbl.get(cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_MODE)),
753 'bond-miimon': cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_MIIMON),
754 'bond-use-carrier': self.translate_nl_value_yesno(cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_USE_CARRIER)),
755 'bond-lacp-rate': self.translate_nl_value_slowfast(cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_AD_LACP_RATE)),
756 'bond-min-links': cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_MIN_LINKS),
757 'bond-ad-actor-system': cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_AD_ACTOR_SYSTEM),
758 'bond-ad-actor-sys-prio': cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_AD_ACTOR_SYS_PRIO),
759 '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)),
760 'bond-lacp-bypass-allow': self.translate_nl_value_yesno(cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_AD_LACP_BYPASS)),
761 'bond-num-unsol-na': cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_NUM_PEER_NOTIF),
762 'bond-num-grat-arp': cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_NUM_PEER_NOTIF),
763 'bond-updelay': cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_UPDELAY),
764 'bond-downdelay': cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_DOWNDELAY)
765 }
766
767 cached_bond_primary = cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_PRIMARY)
768 if cached_bond_primary:
769 bond_attrs['bond-primary'] = self.cache.get_ifname(cached_bond_primary)
770
771 slaves = self.cache.get_slaves(bondname)
772 if slaves:
773 bond_attrs['bond-slaves'] = slaves
774 return bond_attrs
775
776 def _query_running(self, ifaceobjrunning, ifaceobj_getfunc=None):
777 if not self.cache.bond_exists(ifaceobjrunning.name):
778 return
779 bond_attrs = self._query_running_attrs(ifaceobjrunning.name)
780 if bond_attrs.get('bond-slaves'):
781 bond_attrs['bond-slaves'] = ' '.join(bond_attrs.get('bond-slaves'))
782
783 [ifaceobjrunning.update_config(k, str(v))
784 for k, v in list(bond_attrs.items())
785 if v is not None]
786
787 _run_ops = {
788 'pre-up': _up,
789 'post-down': _down,
790 'query-running': _query_running,
791 'query-checkcurr': _query_check
792 }
793
794 def get_ops(self):
795 """ returns list of ops supported by this module """
796 return list(self._run_ops.keys())
797
798 def run(self, ifaceobj, operation, query_ifaceobj=None,
799 ifaceobj_getfunc=None):
800 """ run bond configuration on the interface object passed as argument
801
802 Args:
803 **ifaceobj** (object): iface object
804
805 **operation** (str): any of 'pre-up', 'post-down', 'query-checkcurr',
806 'query-running'
807
808 Kwargs:
809 **query_ifaceobj** (object): query check ifaceobject. This is only
810 valid when op is 'query-checkcurr'. It is an object same as
811 ifaceobj, but contains running attribute values and its config
812 status. The modules can use it to return queried running state
813 of interfaces. status is success if the running state is same
814 as user required state in ifaceobj. error otherwise.
815 """
816 op_handler = self._run_ops.get(operation)
817 if not op_handler:
818 return
819 if operation != 'query-running' and not self._is_bond(ifaceobj):
820 return
821 if operation == 'query-checkcurr':
822 op_handler(self, ifaceobj, query_ifaceobj,
823 ifaceobj_getfunc=ifaceobj_getfunc)
824 else:
825 op_handler(self, ifaceobj, ifaceobj_getfunc=ifaceobj_getfunc)