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