]> git.proxmox.com Git - mirror_ifupdown2.git/blob - ifupdown2/addons/bond.py
SONAR: fix iface.py: Import only needed names or import the module and then use its...
[mirror_ifupdown2.git] / ifupdown2 / addons / bond.py
1 #!/usr/bin/env python3
2 #
3 # Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved.
4 # Authors:
5 # Roopa Prabhu, roopa@cumulusnetworks.com
6 # Julien Fortin, julien@cumulusnetworks.com
7 #
8
9 import os
10 from collections import OrderedDict
11
12 try:
13 from ifupdown2.nlmanager.ipnetwork import IPv4Address
14 from ifupdown2.lib.addon import Addon
15 from ifupdown2.nlmanager.nlmanager import Link
16
17 from ifupdown2.ifupdown.iface import ifaceRole, ifaceLinkKind, ifaceLinkPrivFlags, ifaceLinkType, ifaceDependencyType, ifaceStatus
18 from ifupdown2.ifupdown.utils import utils
19 from ifupdown2.ifupdown.statemanager import statemanager_api as statemanager
20
21 import ifupdown2.ifupdown.policymanager as policymanager
22 import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
23
24 from ifupdown2.ifupdownaddons.modulebase import moduleBase
25 except (ImportError, ModuleNotFoundError):
26 from nlmanager.ipnetwork import IPv4Address
27 from lib.addon import Addon
28 from nlmanager.nlmanager import Link
29
30 from ifupdown.iface import ifaceRole, ifaceLinkKind, ifaceLinkPrivFlags, ifaceLinkType, ifaceDependencyType, ifaceStatus
31 from ifupdown.utils import utils
32 from ifupdown.statemanager import statemanager_api as statemanager
33
34 from ifupdownaddons.modulebase import moduleBase
35
36 import ifupdown.policymanager as policymanager
37 import ifupdown.ifupdownflags as ifupdownflags
38
39 class bond(Addon, moduleBase):
40 """ ifupdown2 addon module to configure bond interfaces """
41
42 overrides_ifupdown_scripts = ['ifenslave', ]
43
44 _modinfo = {
45 "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 },
58 "bond-num-unsol-na": {
59 "help": "bond slave devices",
60 "validrange": ["0", "255"],
61 "default": "1",
62 "example": ["bond-num-unsol-na 1"]
63 },
64 "bond-xmit-hash-policy": {
65 "help": "bond slave devices",
66 "validvals": [
67 "0", "layer2",
68 "1", "layer3+4",
69 "2", "layer2+3",
70 "3", "encap2+3",
71 "4", "encap3+4",
72 "5", "vlan+srcmac"
73 ],
74 "default": "layer2",
75 "example": ["bond-xmit-hash-policy layer2"]
76 },
77 "bond-miimon": {
78 "help": "bond miimon",
79 "validrange": ["0", "255"],
80 "default": "0",
81 "example": ["bond-miimon 0"]
82 },
83 "bond-arp-interval": {
84 "help": "bond arp interval (only mode 0 and 2)",
85 "default": "0",
86 "example": ["bond-arp_interval 0"]
87 },
88 "bond-arp-ip-target": {
89 "help": "ipv4 addresses maximum 16",
90 "validvals": ["<ipv4>"],
91 "multiline": True,
92 "example": ["bond-arp-ip-target 10.0.12.3"]
93 },
94 "bond-mode": {
95 "help": "bond mode",
96 "validvals": [
97 "0", "balance-rr",
98 "1", "active-backup",
99 "2", "balance-xor",
100 "3", "broadcast",
101 "4", "802.3ad",
102 "5", "balance-tlb",
103 "6", "balance-alb"
104 ],
105 "default": "balance-rr",
106 "example": ["bond-mode 802.3ad"]
107 },
108 "bond-lacp-rate": {
109 "help": "bond lacp rate",
110 "validvals": ["0", "slow", "1", "fast"],
111 "default": "0",
112 "example": ["bond-lacp-rate 0"]
113 },
114 "bond-min-links": {
115 "help": "bond min links",
116 "default": "0",
117 "validrange": ["0", "255"],
118 "example": ["bond-min-links 0"]
119 },
120 "bond-ad-sys-priority": {
121 "help": "802.3ad system priority",
122 "default": "65535",
123 "validrange": ["0", "65535"],
124 "example": ["bond-ad-sys-priority 65535"],
125 "deprecated": True,
126 "new-attribute": "bond-ad-actor-sys-prio"
127 },
128 "bond-ad-actor-sys-prio": {
129 "help": "802.3ad system priority",
130 "default": "65535",
131 "validrange": ["0", "65535"],
132 "example": ["bond-ad-actor-sys-prio 65535"]
133 },
134 "bond-ad-sys-mac-addr": {
135 "help": "802.3ad system mac address",
136 "validvals": ["<mac>", ],
137 "example": ["bond-ad-sys-mac-addr 00:00:00:00:00:00"],
138 "deprecated": True,
139 "new-attribute": "bond-ad-actor-system"
140 },
141 "bond-ad-actor-system": {
142 "help": "802.3ad system mac address",
143 "validvals": ["<mac>", ],
144 "example": ["bond-ad-actor-system 00:00:00:00:00:00"],
145 },
146 "bond-lacp-bypass-allow": {
147 "help": "allow lacp bypass",
148 "validvals": ["yes", "no", "0", "1"],
149 "default": "no",
150 "example": ["bond-lacp-bypass-allow no"]
151 },
152 "bond-slaves": {
153 "help": "bond slaves",
154 "required": True,
155 "multivalue": True,
156 "validvals": ["<interface-list>"],
157 "example": [
158 "bond-slaves swp1 swp2",
159 "bond-slaves glob swp1-2",
160 "bond-slaves regex (swp[1|2])"
161 ],
162 "aliases": ["bond-ports"]
163 },
164 "bond-updelay": {
165 "help": "bond updelay",
166 "default": "0",
167 "validrange": ["0", "65535"],
168 "example": ["bond-updelay 100"]
169 },
170 "bond-downdelay": {
171 "help": "bond downdelay",
172 "default": "0",
173 "validrange": ["0", "65535"],
174 "example": ["bond-downdelay 100"]
175 },
176 "bond-primary": {
177 "help": "Control which slave interface is "
178 "preferred active member",
179 "example": ["bond-primary swp1"]
180 },
181 "bond-primary-reselect": {
182 "help": "bond primary reselect",
183 "validvals": [
184 "0", "always",
185 "1", "better",
186 "2", "failure",
187 ],
188 "example": ["bond-primary-reselect failure"]
189 },
190 "es-sys-mac": {
191 "help": "evpn-mh: system mac address",
192 "validvals": ["<mac>", ],
193 "example": ["es-sys-mac 00:00:00:00:00:42"],
194 }
195 }
196 }
197
198 _bond_attr_netlink_map = {
199 'bond-mode': Link.IFLA_BOND_MODE,
200 'bond-miimon': Link.IFLA_BOND_MIIMON,
201 'bond-arp-interval': Link.IFLA_BOND_ARP_INTERVAL,
202 'bond-arp-ip-target': Link.IFLA_BOND_ARP_IP_TARGET,
203 'bond-use-carrier': Link.IFLA_BOND_USE_CARRIER,
204 'bond-lacp-rate': Link.IFLA_BOND_AD_LACP_RATE,
205 'bond-xmit-hash-policy': Link.IFLA_BOND_XMIT_HASH_POLICY,
206 'bond-min-links': Link.IFLA_BOND_MIN_LINKS,
207 'bond-num-grat-arp': Link.IFLA_BOND_NUM_PEER_NOTIF,
208 'bond-num-unsol-na': Link.IFLA_BOND_NUM_PEER_NOTIF,
209 'es-sys-mac': Link.IFLA_BOND_AD_ACTOR_SYSTEM,
210 'bond-ad-sys-mac-addr': Link.IFLA_BOND_AD_ACTOR_SYSTEM,
211 'bond-ad-actor-system': Link.IFLA_BOND_AD_ACTOR_SYSTEM,
212 'bond-ad-sys-priority': Link.IFLA_BOND_AD_ACTOR_SYS_PRIO,
213 'bond-ad-actor-sys-prio': Link.IFLA_BOND_AD_ACTOR_SYS_PRIO,
214 'bond-lacp-bypass-allow': Link.IFLA_BOND_AD_LACP_BYPASS,
215 'bond-updelay': Link.IFLA_BOND_UPDELAY,
216 'bond-downdelay': Link.IFLA_BOND_DOWNDELAY,
217 'bond-primary': Link.IFLA_BOND_PRIMARY,
218 'bond-primary-reselect': Link.IFLA_BOND_PRIMARY_RESELECT
219
220 }
221
222 # ifquery-check attr dictionary with callable object to translate user data to netlink format
223 _bond_attr_ifquery_check_translate_func = {
224 Link.IFLA_BOND_MODE: lambda x: Link.ifla_bond_mode_tbl[x],
225 Link.IFLA_BOND_MIIMON: int,
226 Link.IFLA_BOND_ARP_INTERVAL: int,
227 Link.IFLA_BOND_ARP_IP_TARGET: lambda x: [IPv4Address(ip) for ip in x],
228 Link.IFLA_BOND_USE_CARRIER: utils.get_boolean_from_string,
229 Link.IFLA_BOND_AD_LACP_RATE: lambda x: int(utils.get_boolean_from_string(x)),
230 Link.IFLA_BOND_XMIT_HASH_POLICY: lambda x: Link.ifla_bond_xmit_hash_policy_tbl[x],
231 Link.IFLA_BOND_MIN_LINKS: int,
232 Link.IFLA_BOND_NUM_PEER_NOTIF: int,
233 Link.IFLA_BOND_AD_ACTOR_SYSTEM: str,
234 Link.IFLA_BOND_AD_ACTOR_SYS_PRIO: int,
235 Link.IFLA_BOND_AD_LACP_BYPASS: lambda x: int(utils.get_boolean_from_string(x)),
236 Link.IFLA_BOND_UPDELAY: int,
237 Link.IFLA_BOND_DOWNDELAY: int,
238 Link.IFLA_BOND_PRIMARY_RESELECT: lambda x: Link.ifla_bond_primary_reselect_tbl[x],
239 # Link.IFLA_BOND_PRIMARY: self.netlink.get_ifname is added in __init__()
240 }
241
242 # ifup attr list with callable object to translate user data to netlink format
243 # in the future this can be moved to a dictionary, whenever we detect that some
244 # netlink capabilities are missing we can dynamically remove them from the dict.
245 _bond_attr_set_list = (
246 ('bond-mode', Link.IFLA_BOND_MODE, lambda x: Link.ifla_bond_mode_tbl[x]),
247 ('bond-xmit-hash-policy', Link.IFLA_BOND_XMIT_HASH_POLICY, lambda x: Link.ifla_bond_xmit_hash_policy_tbl[x]),
248 ('bond-miimon', Link.IFLA_BOND_MIIMON, int),
249 ('bond-arp-interval', Link.IFLA_BOND_ARP_INTERVAL, int),
250 ('bond-min-links', Link.IFLA_BOND_MIN_LINKS, int),
251 ('bond-num-grat-arp', Link.IFLA_BOND_NUM_PEER_NOTIF, int),
252 ('bond-num-unsol-na', Link.IFLA_BOND_NUM_PEER_NOTIF, int),
253 ('bond-ad-sys-priority', Link.IFLA_BOND_AD_ACTOR_SYS_PRIO, int),
254 ('bond-ad-actor-sys-prio', Link.IFLA_BOND_AD_ACTOR_SYS_PRIO, int),
255 ('bond-updelay', Link.IFLA_BOND_UPDELAY, int),
256 ('bond-downdelay', Link.IFLA_BOND_DOWNDELAY, int),
257 ('bond-use-carrier', Link.IFLA_BOND_USE_CARRIER, lambda x: int(utils.get_boolean_from_string(x))),
258 ('bond-lacp-rate', Link.IFLA_BOND_AD_LACP_RATE, lambda x: int(utils.get_boolean_from_string(x))),
259 ('bond-lacp-bypass-allow', Link.IFLA_BOND_AD_LACP_BYPASS, lambda x: int(utils.get_boolean_from_string(x))),
260 ('es-sys-mac', Link.IFLA_BOND_AD_ACTOR_SYSTEM, str),
261 ('bond-ad-sys-mac-addr', Link.IFLA_BOND_AD_ACTOR_SYSTEM, str),
262 ('bond-ad-actor-system', Link.IFLA_BOND_AD_ACTOR_SYSTEM, str),
263 ('bond-primary-reselect', Link.IFLA_BOND_PRIMARY_RESELECT, lambda x: Link.ifla_bond_primary_reselect_tbl[x])
264 # ('bond-primary', Link.IFLA_BOND_PRIMARY, self.cache.get_ifindex) added in __init__()
265 )
266
267 def __init__(self, *args, **kargs):
268 Addon.__init__(self)
269 moduleBase.__init__(self, *args, **kargs)
270
271 if not os.path.exists('/sys/class/net/bonding_masters'):
272 try:
273 utils.exec_command('modprobe -q bonding')
274 except Exception as e:
275 self.logger.info("bond: error while loading bonding module: %s" % str(e))
276
277 self._bond_attr_ifquery_check_translate_func[Link.IFLA_BOND_PRIMARY] = self.cache.get_ifindex
278 self._bond_attr_set_list = self._bond_attr_set_list + (('bond-primary', Link.IFLA_BOND_PRIMARY, self.cache.get_ifindex),)
279
280 self.bond_mac_mgmt = utils.get_boolean_from_string(
281 policymanager.policymanager_api.get_module_globals(
282 module_name=self.__class__.__name__,
283 attr="bond_mac_mgmt"),
284 True
285 )
286
287 def get_bond_slaves(self, ifaceobj):
288 # bond-ports aliases should be translated to bond-slaves
289 return ifaceobj.get_attr_value_first('bond-slaves')
290
291 def _is_bond(self, ifaceobj):
292 # at first link_kind is not set but once ifupdownmain
293 # calls get_dependent_ifacenames link_kind is set to BOND
294 return ifaceobj.link_kind & ifaceLinkKind.BOND \
295 or ifaceobj.get_attr_value_first("bond-mode") \
296 or self.get_bond_slaves(ifaceobj)
297
298 def get_dependent_ifacenames(self, ifaceobj, ifacenames_all=None, old_ifaceobjs=False):
299 """ Returns list of interfaces dependent on ifaceobj """
300
301 if not self._is_bond(ifaceobj):
302 return None
303 slave_list = self.parse_port_list(ifaceobj.name,
304 self.get_bond_slaves(ifaceobj),
305 ifacenames_all)
306 ifaceobj.dependency_type = ifaceDependencyType.MASTER_SLAVE
307 # Also save a copy for future use
308 ifaceobj.priv_data = list(slave_list) if slave_list else []
309 if ifaceobj.link_type != ifaceLinkType.LINK_NA:
310 ifaceobj.link_type = ifaceLinkType.LINK_MASTER
311 ifaceobj.link_kind |= ifaceLinkKind.BOND
312 ifaceobj.role |= ifaceRole.MASTER
313
314 if ifaceobj.get_attr_value("es-sys-mac"):
315 ifaceobj.link_privflags |= ifaceLinkPrivFlags.ES_BOND
316
317 return slave_list
318
319 def syntax_check(self, ifaceobj, ifaceobj_getfunc):
320 return self.syntax_check_updown_delay(ifaceobj)
321
322 def get_dependent_ifacenames_running(self, ifaceobj):
323 return self.cache.get_slaves(ifaceobj.name)
324
325 def _get_slave_list(self, ifaceobj):
326 """ Returns slave list present in ifaceobj config """
327
328 # If priv data already has slave list use that first.
329 if ifaceobj.priv_data:
330 return ifaceobj.priv_data
331 slaves = self.get_bond_slaves(ifaceobj)
332 if slaves:
333 return self.parse_port_list(ifaceobj.name, slaves)
334 else:
335 return None
336
337 def enable_ipv6_if_prev_brport(self, ifname):
338 """
339 If the intf was previously enslaved to a bridge it is possible ipv6 is still disabled.
340 """
341 try:
342 for ifaceobj in statemanager.get_ifaceobjs(ifname) or []:
343 if ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT:
344 self.write_file("/proc/sys/net/ipv6/conf/%s/disable_ipv6" % ifname, "0")
345 return
346 except Exception as e:
347 self.logger.info(str(e))
348
349 def _is_clag_bond(self, ifaceobj):
350 if self.get_bond_slaves(ifaceobj):
351 attrval = ifaceobj.get_attr_value_first('clag-id')
352 if attrval and attrval != '0':
353 return True
354 return False
355
356 def _add_slaves(self, ifaceobj, runningslaves, ifaceobj_getfunc=None):
357 slaves = self._get_slave_list(ifaceobj)
358 if not slaves:
359 self.logger.debug('%s: no slaves found' %ifaceobj.name)
360 return
361
362 clag_bond = self._is_clag_bond(ifaceobj)
363
364 # remove duplicates and devices that are already enslaved
365 devices_to_enslave = []
366 for s in slaves:
367 if s not in runningslaves and s not in devices_to_enslave:
368 devices_to_enslave.append(s)
369
370 for slave in devices_to_enslave:
371 if (not ifupdownflags.flags.PERFMODE and
372 not self.cache.link_exists(slave)):
373 self.log_error('%s: skipping slave %s, does not exist'
374 %(ifaceobj.name, slave), ifaceobj,
375 raise_error=False)
376 continue
377 link_up = False
378 if self.cache.link_is_up(slave):
379 self.netlink.link_down_force(slave)
380 link_up = True
381
382 # if clag or ES bond: place the slave in a protodown state;
383 # (clagd will proto-up it when it is ready)
384 if clag_bond or ifaceobj.link_privflags & ifaceLinkPrivFlags.ES_BOND:
385 try:
386 self.netlink.link_set_protodown_on(slave)
387 if clag_bond:
388 self.iproute2.link_set_protodown_reason_clag_on(slave)
389 else:
390 self.iproute2.link_set_protodown_reason_frr_on(slave)
391 except Exception as e:
392 self.logger.error('%s: %s' % (ifaceobj.name, str(e)))
393
394 self.enable_ipv6_if_prev_brport(slave)
395 self.netlink.link_set_master(slave, ifaceobj.name)
396 runningslaves.append(slave)
397 # TODO: if this fail we should switch to iproute2
398 # start a batch: down - set master - up
399 if link_up or ifaceobj.link_type != ifaceLinkType.LINK_NA:
400 try:
401 if (ifaceobj_getfunc(slave)[0].link_privflags &
402 ifaceLinkPrivFlags.KEEP_LINK_DOWN):
403 self.netlink.link_down_force(slave)
404 else:
405 self.netlink.link_up_force(slave)
406 except Exception as e:
407 self.logger.debug('%s: %s' % (ifaceobj.name, str(e)))
408 pass
409
410 if runningslaves:
411 removed_slave = []
412
413 for s in runningslaves:
414 # make sure that slaves are not in protodown since we are not in the clag-bond or es-bond case
415 if not clag_bond and not ifaceobj.link_privflags & ifaceLinkPrivFlags.ES_BOND and self.cache.get_link_protodown(s):
416 self.iproute2.link_set_protodown_reason_clag_off(s)
417 self.netlink.link_set_protodown_off(s)
418 if s not in slaves:
419 self.sysfs.bond_remove_slave(ifaceobj.name, s)
420 removed_slave.append(s)
421 if clag_bond:
422 try:
423 self.iproute2.link_set_protodown_reason_clag_off(s)
424 self.netlink.link_set_protodown_off(s)
425 except Exception as e:
426 self.logger.error('%s: %s' % (ifaceobj.name, str(e)))
427 elif ifaceobj.link_privflags & ifaceLinkPrivFlags.ES_BOND:
428 self.netlink.link_set_protodown_off(s)
429
430 # ip link set $slave nomaster will set the slave admin down
431 # if the slave has an auto stanza, we should keep it admin up
432 # unless link-down yes is set
433 slave_class_auto = False
434 slave_link_down = False
435 for obj in ifaceobj_getfunc(s) or []:
436 if obj.auto:
437 slave_class_auto = True
438 if obj.link_privflags & ifaceLinkPrivFlags.KEEP_LINK_DOWN:
439 slave_link_down = True
440 if slave_class_auto and not slave_link_down:
441 self.netlink.link_up_force(s)
442 else:
443 # apply link-down config changes on running slaves
444 try:
445 link_up = self.cache.link_is_up(s)
446 config_link_down = (ifaceobj_getfunc(s)[0].link_privflags &
447 ifaceLinkPrivFlags.KEEP_LINK_DOWN)
448 if (config_link_down and link_up):
449 self.netlink.link_down_force(s)
450 elif (not config_link_down and not link_up):
451 self.netlink.link_up_force(s)
452 except Exception as e:
453 self.logger.warning('%s: %s' % (ifaceobj.name, str(e)))
454
455 for s in removed_slave:
456 try:
457 runningslaves.remove(s)
458 except Exception:
459 pass
460
461 return runningslaves
462
463 def _check_updown_delay_log(self, ifaceobj, attr_name, value):
464 ifaceobj.status = ifaceStatus.ERROR
465 self.logger.error('%s: unable to set %s %s as MII link monitoring is '
466 'disabled' % (ifaceobj.name, attr_name, value))
467 # return False to notify syntax_check that an error has been logged
468 return False
469
470 def syntax_check_updown_delay(self, ifaceobj):
471 result = True
472 updelay = ifaceobj.get_attr_value_first('bond-updelay')
473 downdelay = ifaceobj.get_attr_value_first('bond-downdelay')
474
475 if not updelay and not downdelay:
476 return True
477
478 try:
479 miimon = int(ifaceobj.get_attr_value_first('bond-miimon'))
480 except Exception:
481 try:
482 miimon = int(policymanager.policymanager_api.get_iface_default(
483 module_name=self.__class__.__name__,
484 ifname=ifaceobj.name,
485 attr='bond-miimon'))
486 except Exception:
487 miimon = 0
488
489 if not miimon:
490 # self._check_updown_delay_log returns False no matter what
491 if updelay and int(updelay):
492 result = self._check_updown_delay_log(ifaceobj, 'bond-updelay', updelay)
493 if downdelay and int(downdelay):
494 result = self._check_updown_delay_log(ifaceobj, 'bond-downdelay', downdelay)
495
496 return result
497
498 _bond_updown_delay_nl_list = (
499 (Link.IFLA_BOND_UPDELAY, 'bond-updelay'),
500 (Link.IFLA_BOND_DOWNDELAY, 'bond-downdelay')
501 )
502
503 def check_updown_delay_nl(self, link_exists, ifaceobj, ifla_info_data):
504 """
505 IFLA_BOND_MIIMON
506 Specifies the time, in milliseconds, to wait before enabling a slave
507 after a link recovery has been detected. This option is only valid
508 for the miimon link monitor. The updelay value should be a multiple
509 of the miimon value; if not, it will be rounded down to the nearest
510 multiple. The default value is 0.
511
512 This ifla_bond_miimon code should be move to get_ifla_bond_attr_from_user_config
513 but we need to know if the operation was successful to update the cache accordingly
514 """
515 ifla_bond_miimon = ifla_info_data.get(Link.IFLA_BOND_MIIMON)
516 if link_exists and ifla_bond_miimon is None:
517 ifla_bond_miimon = self.cache.get_link_info_data_attribute(ifaceobj.name, Link.IFLA_BOND_MIIMON)
518
519 if ifla_bond_miimon == 0:
520 for nl_attr, attr_name in self._bond_updown_delay_nl_list:
521 delay = ifla_info_data.get(nl_attr)
522 # if up-down-delay exists we need to remove it, if non zero log error
523 if delay is not None:
524 if delay > 0:
525 self._check_updown_delay_log(ifaceobj, attr_name, delay)
526 del ifla_info_data[nl_attr]
527 return True
528 return False
529
530 _bond_lacp_attrs = (
531 (Link.IFLA_BOND_AD_LACP_RATE, 'bond-lacp-rate'),
532 (Link.IFLA_BOND_AD_LACP_BYPASS, 'bond-lacp-bypass')
533 )
534
535 def _check_bond_mode_user_config(self, ifname, link_exists, ifla_info_data):
536 ifla_bond_mode = ifla_info_data.get(Link.IFLA_BOND_MODE)
537 if ifla_bond_mode is None and link_exists:
538 ifla_bond_mode = self.cache.get_link_info_data_attribute(ifname, Link.IFLA_BOND_MODE)
539 # in this case the link already exists (we have a cached value):
540 # if IFLA_BOND_MODE is not present in ifla_info_data it means:
541 # - that bond-mode was present in the user config and didn't change
542 # - never was in the user config so bond mode should be the system default value
543 # - was removed from the stanza so we might have to reset it to default value
544 # nevertheless we need to add it back to the ifla_info_data dict to check
545 # if we need to reset the mode to system default
546 ifla_info_data[Link.IFLA_BOND_MODE] = ifla_bond_mode
547
548 if ifla_bond_mode == 4: # 802.3ad
549 min_links = ifla_info_data.get(Link.IFLA_BOND_MIN_LINKS)
550 if min_links is None:
551 min_links = self.cache.get_link_info_data_attribute(ifname, Link.IFLA_BOND_MIN_LINKS)
552 # get_min_links_nl may return None so we need to strictly check 0
553 if min_links == 0:
554 self.logger.warning('%s: attribute bond-min-links is set to \'0\'' % ifname)
555 else:
556 # IFLA_BOND_AD_LACP_RATE and IFLA_BOND_AD_LACP_BYPASS only for 802.3ad mode (4)
557 for nl_attr, attr_name in self._bond_lacp_attrs:
558 if nl_attr in ifla_info_data:
559 self.logger.info('%s: ignoring %s: only available for 802.3ad mode (4)' % (ifname, attr_name))
560 del ifla_info_data[nl_attr]
561
562 @staticmethod
563 def get_saved_ifaceobj(link_exists, ifname):
564 if link_exists:
565 old_config = statemanager.get_ifaceobjs(ifname)
566 if old_config:
567 return old_config[0]
568 return None
569
570 def get_ifla_bond_attr_from_user_config(self, ifaceobj, link_exists):
571 """
572 Potential issue: if a user load the bond driver with custom
573 default values (say bond-mode 3), ifupdown2 has no knowledge
574 of these default values.
575 At bond creation everything should work, bonds will be created
576 with mode 3 (even if not specified under the stanza).
577 But, for example: if the user specifies a value under bond-mode
578 and later on the user removes the bond-mode line from the stanza
579 we will detect it and reset to MODINFO: BOND-MODE: DEFAULT aka 0
580 which is not the real default value that the user may expect.
581 """
582 ifname = ifaceobj.name
583 ifla_info_data = OrderedDict()
584 old_config = self.get_saved_ifaceobj(link_exists, ifname)
585
586 # for each bond attribute we fetch the user configuration
587 # if no configuration is provided we look for a config in policy files
588 for attr_name, netlink_attr, func_ptr in self._bond_attr_set_list:
589
590 cached_value = None
591 user_config = ifaceobj.get_attr_value_first(attr_name)
592
593 if not user_config:
594 user_config = policymanager.policymanager_api.get_iface_default(
595 module_name=self.__class__.__name__,
596 ifname=ifname,
597 attr=attr_name)
598 if user_config:
599 self.logger.debug('%s: %s %s: extracted from policy files'
600 % (ifname, attr_name, user_config))
601
602 # no policy override, do we need to reset an attr to default value?
603 if not user_config and old_config and old_config.get_attr_value_first(attr_name):
604 # if the link already exists but the value is set
605 # (potentially removed from the stanza, we need to reset it to default)
606 # might not work for specific cases, see explanation at the top of this function :)
607 user_config = self.get_attr_default_value(attr_name)
608 if user_config:
609 self.logger.debug('%s: %s: removed from stanza, resetting to default value: %s'
610 % (ifname, attr_name, user_config))
611
612 if user_config:
613 try:
614 nl_value = func_ptr(user_config.lower())
615
616 if link_exists:
617 cached_value = self.cache.get_link_info_data_attribute(ifname, netlink_attr)
618
619 if link_exists and cached_value is None:
620 # the link already exists but we don't have any value
621 # cached for this attr, it probably means that the
622 # capability is not available on this system (i.e old kernel)
623 self.logger.debug('%s: ignoring %s %s: capability '
624 'probably not supported on this system'
625 % (ifname, attr_name, user_config))
626 continue
627 elif link_exists:
628 # there should be a cached value if the link already exists
629 if cached_value == nl_value:
630 # if the user value is already cached: continue
631 continue
632
633 # else: the link doesn't exist so we create the bond with
634 # all the user/policy defined values without extra checks
635 ifla_info_data[netlink_attr] = nl_value
636
637 if cached_value is not None:
638 self.logger.info('%s: set %s %s (cache %s)' % (ifname, attr_name, user_config, cached_value))
639 else:
640 self.logger.info('%s: set %s %s' % (ifname, attr_name, user_config))
641
642 except KeyError:
643 self.logger.warning('%s: invalid %s value %s' % (ifname, attr_name, user_config))
644
645 self._check_bond_mode_user_config(ifname, link_exists, ifla_info_data)
646 return ifla_info_data
647
648 def check_miimon_arp(self, link_exists, ifaceobj, ifla_info_data):
649 """
650 Check bond checks either miimon or arp ip check
651 """
652 # miimon
653 ifla_bond_miimon = ifla_info_data.get(Link.IFLA_BOND_MIIMON)
654 if link_exists and ifla_bond_miimon is None:
655 ifla_bond_miimon = self.cache.get_link_info_data_attribute(ifaceobj.name, Link.IFLA_BOND_MIIMON)
656
657 if ifla_bond_miimon:
658 ifla_bond_miimon = str(ifla_bond_miimon)
659
660 # bond mode
661 ifla_bond_mode = ifla_info_data.get(Link.IFLA_BOND_MODE)
662 if link_exists and ifla_bond_mode is None:
663 ifla_bond_mode = self.cache.get_link_info_data_attribute(ifaceobj.name, Link.IFLA_BOND_MODE)
664
665 ifla_bond_mode = str(ifla_bond_mode)
666
667 # arp interval
668 ifla_arp_interval = ifla_info_data.get(Link.IFLA_BOND_ARP_INTERVAL)
669 if link_exists and ifla_arp_interval is None:
670 ifla_arp_interval = self.cache.get_link_info_data_attribute(ifaceobj.name, Link.IFLA_BOND_ARP_INTERVAL)
671
672 if ifla_arp_interval:
673 ifla_arp_interval = str(ifla_arp_interval)
674
675 # arp ip target
676 ifla_arp_ip_target = ifaceobj.get_attr_value('bond-arp-ip-target')
677 if ifla_arp_ip_target:
678 ifla_arp_ip_target = [IPv4Address(ip) for ip in ifla_arp_ip_target]
679
680 if ifla_arp_ip_target:
681 ifla_info_data[Link.IFLA_BOND_ARP_IP_TARGET] = ifla_arp_ip_target
682
683 # Only works in mode 0 and 2
684 if ifla_bond_mode not in ['0', '2', 'balance-rr', 'balance-xor']:
685 if Link.IFLA_BOND_ARP_INTERVAL in ifla_info_data:
686 self.logger.info('%s: bond arp interval/ip only works in balance-rr and balance-xor mode. Option bond-arp-interval is ignored' % ifaceobj.name)
687 del ifla_info_data[Link.IFLA_BOND_ARP_INTERVAL]
688 if Link.IFLA_BOND_ARP_IP_TARGET in ifla_info_data:
689 self.logger.info('%s: bond arp interval/ip only works in balance-rr and balance-xor mode. Option bond-arp-ip-target is ignored' % ifaceobj.name)
690 del ifla_info_data[Link.IFLA_BOND_ARP_IP_TARGET]
691
692 if ifla_bond_miimon and ifla_bond_miimon != '0' and Link.IFLA_BOND_ARP_INTERVAL in ifla_info_data:
693 self.logger.info('%s: bond arp interval and bond miimon are set. The options are mutually exclusive and bond-arp-interval is ignored' % ifaceobj.name)
694 del ifla_info_data[Link.IFLA_BOND_ARP_INTERVAL]
695
696 if ifla_arp_interval and ifla_arp_interval != '0' and Link.IFLA_BOND_MIIMON in ifla_info_data:
697 self.logger.info('%s: bond arp interval and bond miimon are set. The options are mutually exclusive and bond-miimon is ignored' % ifaceobj.name)
698 del ifla_info_data[Link.IFLA_BOND_MIIMON]
699
700 return ifla_info_data
701
702 _bond_down_nl_attributes_list = (
703 Link.IFLA_BOND_MODE,
704 Link.IFLA_BOND_XMIT_HASH_POLICY,
705 Link.IFLA_BOND_AD_LACP_RATE,
706 Link.IFLA_BOND_MIN_LINKS
707 )
708
709 def _should_down_bond(self, ifla_info_data):
710 for nl_attr in self._bond_down_nl_attributes_list:
711 if nl_attr in ifla_info_data:
712 return True
713 return False
714
715 def should_update_bond_mode(self, ifaceobj, ifname, is_link_up, ifla_info_data, bond_slaves):
716 # if bond-mode was changed the bond needs to be brought
717 # down and slaves un-slaved before bond mode is changed.
718 cached_bond_mode = self.cache.get_link_info_data_attribute(ifname, Link.IFLA_BOND_MODE)
719 ifla_bond_mode = ifla_info_data.get(Link.IFLA_BOND_MODE)
720
721 # bond-mode was changed or is not specified
722 if ifla_bond_mode is not None:
723 if ifla_bond_mode != cached_bond_mode:
724 self.logger.info('%s: bond mode changed to %s: running ops on bond and slaves'
725 % (ifname, ifla_bond_mode))
726 if is_link_up:
727 self.netlink.link_down(ifname)
728 is_link_up = False
729
730 for lower_dev in ifaceobj.lowerifaces:
731 self.netlink.link_set_nomaster(lower_dev)
732
733 # when unslaving a device from an ES bond we need to set
734 # protodown off
735 if ifaceobj.link_privflags & ifaceLinkPrivFlags.ES_BOND:
736 self.netlink.link_set_protodown_off(lower_dev)
737
738 try:
739 bond_slaves.remove(lower_dev)
740 except Exception:
741 pass
742
743 else:
744 # bond-mode user config value is the current running(cached) value
745 # no need to reset it again we can ignore this attribute
746 del ifla_info_data[Link.IFLA_BOND_MODE]
747
748 return is_link_up, bond_slaves
749
750 def create_or_set_bond_config(self, ifaceobj):
751 ifname = ifaceobj.name
752 link_exists, is_link_up = self.cache.link_exists_and_up(ifname)
753 ifla_info_data = self.get_ifla_bond_attr_from_user_config(ifaceobj, link_exists)
754 ifla_info_data = self.check_miimon_arp(link_exists, ifaceobj, ifla_info_data)
755 ifla_master = None
756
757 remove_delay_from_cache = self.check_updown_delay_nl(link_exists, ifaceobj, ifla_info_data)
758
759 # if link exists: down link if specific attributes are specified
760 if link_exists:
761 # if bond already exists we need to set IFLA_MASTER to the cached value otherwise
762 # we might loose some information in the cache due to some optimization.
763 ifla_master = self.cache.get_link_attribute(ifname, Link.IFLA_MASTER)
764
765 # did bond-mode changed?
766 is_link_up, bond_slaves = self.should_update_bond_mode(
767 ifaceobj,
768 ifname,
769 is_link_up,
770 ifla_info_data,
771 self.cache.get_slaves(ifname)
772 )
773
774 # if specific attributes need to be set we need to down the bond first
775 if ifla_info_data and is_link_up:
776 if self._should_down_bond(ifla_info_data):
777 self.netlink.link_down_force(ifname)
778 is_link_up = False
779 else:
780 bond_slaves = []
781
782 if link_exists and not ifla_info_data:
783 # if the bond already exists and no attrs need to be set
784 # ignore the netlink call
785 self.logger.info('%s: already exists, no change detected' % ifname)
786 else:
787 try:
788 self.netlink.link_add_bond_with_info_data(ifname, ifla_master, ifla_info_data)
789 except Exception as e:
790 # defensive code
791 # if anything happens, we try to set up the bond with the sysfs api
792 self.logger.debug('%s: bond setup: %s' % (ifname, str(e)))
793 self.create_or_set_bond_config_sysfs(ifaceobj, ifla_info_data)
794
795 if remove_delay_from_cache:
796 # making sure up/down delay attributes are set to 0 before caching
797 # this can be removed when moving to a nllistener/live cache
798 ifla_info_data[Link.IFLA_BOND_UPDELAY] = 0
799 ifla_info_data[Link.IFLA_BOND_DOWNDELAY] = 0
800
801 if link_exists and ifla_info_data and not is_link_up:
802 self.netlink.link_up_force(ifname)
803
804 return link_exists, bond_slaves
805
806 def create_or_set_bond_config_sysfs(self, ifaceobj, ifla_info_data):
807 if len(ifaceobj.name) > 15:
808 self.log_error("%s: cannot create bond: interface name exceeds max length of 15" % ifaceobj.name, ifaceobj)
809 return
810
811 if not self.cache.link_exists(ifaceobj.name):
812 self.sysfs.bond_create(ifaceobj.name)
813 self.sysfs.bond_set_attrs_nl(ifaceobj.name, ifla_info_data)
814
815 def _up(self, ifaceobj, ifaceobj_getfunc=None):
816 try:
817 link_exists, bond_slaves = self.create_or_set_bond_config(ifaceobj)
818 bond_slaves = self._add_slaves(
819 ifaceobj,
820 bond_slaves,
821 ifaceobj_getfunc,
822 )
823
824 if not self.bond_mac_mgmt or not link_exists or ifaceobj.get_attr_value_first("hwaddress"):
825 return
826
827 # check if the bond mac address is correctly inherited from it's
828 # first slave. There's a case where that might not be happening:
829 # $ ip link show swp1 | grep ether
830 # link/ether 08:00:27:04:d8:01 brd ff:ff:ff:ff:ff:ff
831 # $ ip link show swp2 | grep ether
832 # link/ether 08:00:27:04:d8:02 brd ff:ff:ff:ff:ff:ff
833 # $ ip link add dev bond0 type bond
834 # $ ip link set dev swp1 master bond0
835 # $ ip link set dev swp2 master bond0
836 # $ ip link show bond0 | grep ether
837 # link/ether 08:00:27:04:d8:01 brd ff:ff:ff:ff:ff:ff
838 # $ ip link add dev bond1 type bond
839 # $ ip link set dev swp1 master bond1
840 # $ ip link show swp1 | grep ether
841 # link/ether 08:00:27:04:d8:01 brd ff:ff:ff:ff:ff:ff
842 # $ ip link show swp2 | grep ether
843 # link/ether 08:00:27:04:d8:01 brd ff:ff:ff:ff:ff:ff
844 # $ ip link show bond0 | grep ether
845 # link/ether 08:00:27:04:d8:01 brd ff:ff:ff:ff:ff:ff
846 # $ ip link show bond1 | grep ether
847 # link/ether 08:00:27:04:d8:01 brd ff:ff:ff:ff:ff:ff
848 # $
849 # ifupdown2 will automatically correct and fix this unexpected behavior
850 bond_mac = self.cache.get_link_address(ifaceobj.name)
851
852 if bond_slaves:
853 first_slave_ifname = bond_slaves[0]
854 first_slave_mac = self.cache.get_link_info_slave_data_attribute(
855 first_slave_ifname,
856 Link.IFLA_BOND_SLAVE_PERM_HWADDR
857 )
858
859 if first_slave_mac and bond_mac != first_slave_mac:
860 self.logger.info(
861 "%s: invalid bond mac detected - resetting to %s's mac (%s)"
862 % (ifaceobj.name, first_slave_ifname, first_slave_mac)
863 )
864 self.netlink.link_set_address(ifaceobj.name, first_slave_mac, utils.mac_str_to_int(first_slave_mac))
865 except Exception as e:
866 self.log_error(str(e), ifaceobj)
867
868 def _down(self, ifaceobj, ifaceobj_getfunc=None):
869 try:
870 self.netlink.link_del(ifaceobj.name)
871 except Exception as e:
872 self.log_warn('%s: %s' % (ifaceobj.name, str(e)))
873
874 def _query_check_bond_slaves(self, ifaceobjcurr, attr, user_bond_slaves, running_bond_slaves):
875 query = 1
876
877 if user_bond_slaves and running_bond_slaves:
878 if not set(user_bond_slaves).symmetric_difference(running_bond_slaves):
879 query = 0
880
881 # we want to display the same bond-slaves list as provided
882 # in the interfaces file but if this list contains regexes or
883 # globs, for now, we won't try to change it.
884 if 'regex' in user_bond_slaves or 'glob' in user_bond_slaves:
885 user_bond_slaves = running_bond_slaves
886 else:
887 ordered = []
888 for slave in user_bond_slaves:
889 if slave in running_bond_slaves:
890 ordered.append(slave)
891 user_bond_slaves = ordered
892 ifaceobjcurr.update_config_with_status(attr, ' '.join(user_bond_slaves) if user_bond_slaves else 'None', query)
893
894 def _query_check(self, ifaceobj, ifaceobjcurr, ifaceobj_getfunc=None):
895 if not self.cache.bond_exists(ifaceobj.name):
896 self.logger.debug('bond iface %s does not exist' % ifaceobj.name)
897 return
898
899 iface_attrs = self.dict_key_subset(ifaceobj.config, self.get_mod_attrs())
900 if not iface_attrs:
901 return
902
903 # remove bond-slaves and bond-ports from the list,
904 # because there aren't any ifla_info_data netlink attr for slaves
905 # an exception is raised when index is not found, so query_slaves will stay False
906 query_slaves = False
907
908 user_bond_slaves = None
909 running_bond_slaves = None
910 try:
911 del iface_attrs[iface_attrs.index('bond-slaves')]
912
913 # if user specified bond-slaves we need to display it
914 query_slaves = True
915 if not user_bond_slaves:
916 user_bond_slaves = self._get_slave_list(ifaceobj)
917 running_bond_slaves = self.cache.get_slaves(ifaceobj.name)
918
919 self._query_check_bond_slaves(ifaceobjcurr, 'bond-slaves', user_bond_slaves, running_bond_slaves)
920 except Exception:
921 pass
922 try:
923 del iface_attrs[iface_attrs.index('bond-ports')]
924
925 # if user specified bond-ports we need to display it
926 if not query_slaves and not user_bond_slaves: # if get_slave_list was already called for slaves
927 user_bond_slaves = self._get_slave_list(ifaceobj)
928 running_bond_slaves = self.cache.get_slaves(ifaceobj.name)
929
930 self._query_check_bond_slaves(ifaceobjcurr, 'bond-ports', user_bond_slaves, running_bond_slaves)
931 except Exception:
932 pass
933 try:
934 attr = 'bond-arp-ip-target'
935 nl_attr = self._bond_attr_netlink_map[attr]
936 translate_func = self._bond_attr_ifquery_check_translate_func[nl_attr]
937 current_config = self.cache.get_link_info_data_attribute(ifaceobj.name, nl_attr)
938 user_config = ifaceobj.get_attr_value(attr)
939
940 del iface_attrs[iface_attrs.index('bond-arp-ip-target')]
941
942 current_config = [str(c.ip) for c in current_config]
943 user_config = [str(u) for u in user_config]
944
945 difference = list(set(current_config).symmetric_difference(user_config))
946 intersection = list(set(current_config).intersection(user_config))
947
948 for ip in difference:
949 ifaceobjcurr.update_config_with_status(attr, ip, 1)
950
951 for ip in intersection:
952 ifaceobjcurr.update_config_with_status(attr, ip, 0)
953 except Exception:
954 pass
955
956 user_config_translate_func = {
957 "es-sys-mac": lambda x: str(x).lower()
958 }
959
960 if "es-sys-mac" in iface_attrs and os.geteuid() != 0:
961 # for some reason es-sys-mac (IFLA_BOND_AD_ACTOR_SYSTEM) is not part
962 # of the netlink dump if requested by non-root user
963 try:
964 iface_attrs.remove("es-sys-mac")
965 self.logger.info("%s: non-root user can't check attribute \"es-sys-mac\" value" % ifaceobj.name)
966 except Exception:
967 pass
968
969 for attr in iface_attrs:
970 nl_attr = self._bond_attr_netlink_map[attr]
971 translate_func = self._bond_attr_ifquery_check_translate_func[nl_attr]
972 current_config = self.cache.get_link_info_data_attribute(ifaceobj.name, nl_attr)
973 user_config_f = user_config_translate_func.get(attr)
974
975 if user_config_f:
976 user_config = user_config_f(ifaceobj.get_attr_value_first(attr))
977 else:
978 user_config = ifaceobj.get_attr_value_first(attr)
979
980 if current_config == translate_func(user_config):
981 ifaceobjcurr.update_config_with_status(attr, user_config, 0)
982 else:
983 ifaceobjcurr.update_config_with_status(attr, str(current_config), 1)
984
985 @staticmethod
986 def translate_nl_value_yesno(value):
987 return 'yes' if value else 'no'
988
989 @staticmethod
990 def translate_nl_value_slowfast(value):
991 return 'fast' if value else 'slow'
992
993 def _query_running_attrs(self, bondname):
994 cached_vxlan_ifla_info_data = self.cache.get_link_info_data(bondname)
995
996 bond_attrs = {
997 'bond-mode': Link.ifla_bond_mode_pretty_tbl.get(cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_MODE)),
998 'bond-miimon': cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_MIIMON),
999 'bond-arp-interval': cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_ARP_INTERVAL),
1000 'bond-arp-ip-target': cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_ARP_IP_TARGET),
1001 'bond-use-carrier': self.translate_nl_value_yesno(cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_USE_CARRIER)),
1002 'bond-lacp-rate': self.translate_nl_value_slowfast(cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_AD_LACP_RATE)),
1003 'bond-min-links': cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_MIN_LINKS),
1004 'bond-ad-actor-system': cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_AD_ACTOR_SYSTEM),
1005 'es-sys-mac': cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_AD_ACTOR_SYSTEM),
1006 'bond-ad-actor-sys-prio': cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_AD_ACTOR_SYS_PRIO),
1007 '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)),
1008 'bond-lacp-bypass-allow': self.translate_nl_value_yesno(cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_AD_LACP_BYPASS)),
1009 'bond-num-unsol-na': cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_NUM_PEER_NOTIF),
1010 'bond-num-grat-arp': cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_NUM_PEER_NOTIF),
1011 'bond-updelay': cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_UPDELAY),
1012 'bond-downdelay': cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_DOWNDELAY)
1013 }
1014
1015 cached_bond_primary = cached_vxlan_ifla_info_data.get(Link.IFLA_BOND_PRIMARY)
1016 if cached_bond_primary:
1017 bond_attrs['bond-primary'] = self.cache.get_ifname(cached_bond_primary)
1018
1019 slaves = self.cache.get_slaves(bondname)
1020 if slaves:
1021 bond_attrs['bond-slaves'] = slaves
1022 return bond_attrs
1023
1024 def _query_running(self, ifaceobjrunning, ifaceobj_getfunc=None):
1025 if not self.cache.bond_exists(ifaceobjrunning.name):
1026 return
1027 bond_attrs = self._query_running_attrs(ifaceobjrunning.name)
1028 if bond_attrs.get('bond-slaves'):
1029 bond_attrs['bond-slaves'] = ' '.join(bond_attrs.get('bond-slaves'))
1030 if bond_attrs.get('bond-arp-ip-target'):
1031 for ip in bond_attrs.get('bond-arp-ip-target'):
1032 ifaceobjrunning.update_config('bond-arp-ip-target', str(ip.ip))
1033 del bond_attrs['bond-arp-ip-target']
1034
1035 [ifaceobjrunning.update_config(k, str(v))
1036 for k, v in list(bond_attrs.items())
1037 if v is not None]
1038
1039 _run_ops = {
1040 'pre-up': _up,
1041 'post-down': _down,
1042 'query-running': _query_running,
1043 'query-checkcurr': _query_check
1044 }
1045
1046 def get_ops(self):
1047 """ returns list of ops supported by this module """
1048 return list(self._run_ops.keys())
1049
1050 def run(self, ifaceobj, operation, query_ifaceobj=None,
1051 ifaceobj_getfunc=None):
1052 """ run bond configuration on the interface object passed as argument
1053
1054 Args:
1055 **ifaceobj** (object): iface object
1056
1057 **operation** (str): any of 'pre-up', 'post-down', 'query-checkcurr',
1058 'query-running'
1059
1060 Kwargs:
1061 **query_ifaceobj** (object): query check ifaceobject. This is only
1062 valid when op is 'query-checkcurr'. It is an object same as
1063 ifaceobj, but contains running attribute values and its config
1064 status. The modules can use it to return queried running state
1065 of interfaces. status is success if the running state is same
1066 as user required state in ifaceobj. error otherwise.
1067 """
1068 op_handler = self._run_ops.get(operation)
1069 if not op_handler:
1070 return
1071 if operation != 'query-running' and not self._is_bond(ifaceobj):
1072 return
1073 if operation == 'query-checkcurr':
1074 op_handler(self, ifaceobj, query_ifaceobj,
1075 ifaceobj_getfunc=ifaceobj_getfunc)
1076 else:
1077 op_handler(self, ifaceobj, ifaceobj_getfunc=ifaceobj_getfunc)