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