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