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