3 # Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved.
5 # Roopa Prabhu, roopa@cumulusnetworks.com
6 # Julien Fortin, julien@cumulusnetworks.com
12 from ifupdown2
.lib
.addon
import Addon
13 from ifupdown2
.nlmanager
.nlmanager
import Link
15 from ifupdown2
.ifupdown
.iface
import *
16 from ifupdown2
.ifupdown
.utils
import utils
17 from ifupdown2
.ifupdown
.statemanager
import statemanager_api
as statemanager
19 import ifupdown2
.ifupdown
.policymanager
as policymanager
20 import ifupdown2
.ifupdown
.ifupdownflags
as ifupdownflags
22 from ifupdown2
.ifupdownaddons
.modulebase
import moduleBase
24 from lib
.addon
import Addon
25 from nlmanager
.nlmanager
import Link
27 from ifupdown
.iface
import *
28 from ifupdown
.utils
import utils
29 from ifupdown
.statemanager
import statemanager_api
as statemanager
31 from ifupdownaddons
.modulebase
import moduleBase
33 import ifupdown
.policymanager
as policymanager
34 import ifupdown
.ifupdownflags
as ifupdownflags
36 class bond(Addon
, moduleBase
):
37 """ ifupdown2 addon module to configure bond interfaces """
39 overrides_ifupdown_scripts
= ['ifenslave', ]
42 "mhelp": "bond configuration module",
45 "help": "bond use carrier",
46 "validvals": ["yes", "no", "0", "1"],
48 "example": ["bond-use-carrier yes"]},
49 "bond-num-grat-arp": {
50 "help": "bond use carrier",
51 "validrange": ["0", "255"],
53 "example": ["bond-num-grat-arp 1"]
55 "bond-num-unsol-na": {
56 "help": "bond slave devices",
57 "validrange": ["0", "255"],
59 "example": ["bond-num-unsol-na 1"]
61 "bond-xmit-hash-policy": {
62 "help": "bond slave devices",
71 "example": ["bond-xmit-hash-policy layer2"]
74 "help": "bond miimon",
75 "validrange": ["0", "255"],
77 "example": ["bond-miimon 0"]
90 "default": "balance-rr",
91 "example": ["bond-mode 802.3ad"]
94 "help": "bond lacp rate",
95 "validvals": ["0", "slow", "1", "fast"],
97 "example": ["bond-lacp-rate 0"]
100 "help": "bond min links",
102 "validrange": ["0", "255"],
103 "example": ["bond-min-links 0"]
105 "bond-ad-sys-priority": {
106 "help": "802.3ad system priority",
108 "validrange": ["0", "65535"],
109 "example": ["bond-ad-sys-priority 65535"],
111 "new-attribute": "bond-ad-actor-sys-prio"
113 "bond-ad-actor-sys-prio": {
114 "help": "802.3ad system priority",
116 "validrange": ["0", "65535"],
117 "example": ["bond-ad-actor-sys-prio 65535"]
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"],
124 "new-attribute": "bond-ad-actor-system"
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"],
131 "bond-lacp-bypass-allow": {
132 "help": "allow lacp bypass",
133 "validvals": ["yes", "no", "0", "1"],
135 "example": ["bond-lacp-bypass-allow no"]
138 "help": "bond slaves",
141 "validvals": ["<interface-list>"],
143 "bond-slaves swp1 swp2",
144 "bond-slaves glob swp1-2",
145 "bond-slaves regex (swp[1|2)"
147 "aliases": ["bond-ports"]
150 "help": "bond updelay",
152 "validrange": ["0", "65535"],
153 "example": ["bond-updelay 100"]
156 "help": "bond downdelay",
158 "validrange": ["0", "65535"],
159 "example": ["bond-downdelay 100"]
162 "help": "Control which slave interface is "
163 "preferred active member",
164 "example": ["bond-primary swp1"]
169 _bond_attr_netlink_map
= {
170 'bond-mode': Link
.IFLA_BOND_MODE
,
171 'bond-miimon': Link
.IFLA_BOND_MIIMON
,
172 'bond-use-carrier': Link
.IFLA_BOND_USE_CARRIER
,
173 'bond-lacp-rate': Link
.IFLA_BOND_AD_LACP_RATE
,
174 'bond-xmit-hash-policy': Link
.IFLA_BOND_XMIT_HASH_POLICY
,
175 'bond-min-links': Link
.IFLA_BOND_MIN_LINKS
,
176 'bond-num-grat-arp': Link
.IFLA_BOND_NUM_PEER_NOTIF
,
177 'bond-num-unsol-na': Link
.IFLA_BOND_NUM_PEER_NOTIF
,
178 'bond-ad-sys-mac-addr': Link
.IFLA_BOND_AD_ACTOR_SYSTEM
,
179 'bond-ad-actor-system': Link
.IFLA_BOND_AD_ACTOR_SYSTEM
,
180 'bond-ad-sys-priority': Link
.IFLA_BOND_AD_ACTOR_SYS_PRIO
,
181 'bond-ad-actor-sys-prio': Link
.IFLA_BOND_AD_ACTOR_SYS_PRIO
,
182 'bond-lacp-bypass-allow': Link
.IFLA_BOND_AD_LACP_BYPASS
,
183 'bond-updelay': Link
.IFLA_BOND_UPDELAY
,
184 'bond-downdelay': Link
.IFLA_BOND_DOWNDELAY
,
185 'bond-primary': Link
.IFLA_BOND_PRIMARY
188 # ifquery-check attr dictionary with callable object to translate user data to netlink format
189 _bond_attr_ifquery_check_translate_func
= {
190 Link
.IFLA_BOND_MODE
: lambda x
: Link
.ifla_bond_mode_tbl
[x
],
191 Link
.IFLA_BOND_MIIMON
: int,
192 Link
.IFLA_BOND_USE_CARRIER
: utils
.get_boolean_from_string
,
193 Link
.IFLA_BOND_AD_LACP_RATE
: lambda x
: int(utils
.get_boolean_from_string(x
)),
194 Link
.IFLA_BOND_XMIT_HASH_POLICY
: lambda x
: Link
.ifla_bond_xmit_hash_policy_tbl
[x
],
195 Link
.IFLA_BOND_MIN_LINKS
: int,
196 Link
.IFLA_BOND_NUM_PEER_NOTIF
: int,
197 Link
.IFLA_BOND_AD_ACTOR_SYSTEM
: str,
198 Link
.IFLA_BOND_AD_ACTOR_SYS_PRIO
: int,
199 Link
.IFLA_BOND_AD_LACP_BYPASS
: lambda x
: int(utils
.get_boolean_from_string(x
)),
200 Link
.IFLA_BOND_UPDELAY
: int,
201 Link
.IFLA_BOND_DOWNDELAY
: int,
202 # Link.IFLA_BOND_PRIMARY: self.netlink.get_ifname is added in __init__()
205 # ifup attr list with callable object to translate user data to netlink format
206 # in the future this can be moved to a dictionary, whenever we detect that some
207 # netlink capabilities are missing we can dynamically remove them from the dict.
208 _bond_attr_set_list
= (
209 ('bond-mode', Link
.IFLA_BOND_MODE
, lambda x
: Link
.ifla_bond_mode_tbl
[x
]),
210 ('bond-xmit-hash-policy', Link
.IFLA_BOND_XMIT_HASH_POLICY
, lambda x
: Link
.ifla_bond_xmit_hash_policy_tbl
[x
]),
211 ('bond-miimon', Link
.IFLA_BOND_MIIMON
, int),
212 ('bond-min-links', Link
.IFLA_BOND_MIN_LINKS
, int),
213 ('bond-num-grat-arp', Link
.IFLA_BOND_NUM_PEER_NOTIF
, int),
214 ('bond-num-unsol-na', Link
.IFLA_BOND_NUM_PEER_NOTIF
, int),
215 ('bond-ad-sys-priority', Link
.IFLA_BOND_AD_ACTOR_SYS_PRIO
, int),
216 ('bond-ad-actor-sys-prio', Link
.IFLA_BOND_AD_ACTOR_SYS_PRIO
, int),
217 ('bond-updelay', Link
.IFLA_BOND_UPDELAY
, int),
218 ('bond-downdelay', Link
.IFLA_BOND_DOWNDELAY
, int),
219 ('bond-use-carrier', Link
.IFLA_BOND_USE_CARRIER
, lambda x
: int(utils
.get_boolean_from_string(x
))),
220 ('bond-lacp-rate', Link
.IFLA_BOND_AD_LACP_RATE
, lambda x
: int(utils
.get_boolean_from_string(x
))),
221 ('bond-lacp-bypass-allow', Link
.IFLA_BOND_AD_LACP_BYPASS
, lambda x
: int(utils
.get_boolean_from_string(x
))),
222 ('bond-ad-sys-mac-addr', Link
.IFLA_BOND_AD_ACTOR_SYSTEM
, str),
223 ('bond-ad-actor-system', Link
.IFLA_BOND_AD_ACTOR_SYSTEM
, str)
224 # ('bond-primary', Link.IFLA_BOND_PRIMARY, self.cache.get_ifindex) added in __init__()
227 def __init__(self
, *args
, **kargs
):
229 moduleBase
.__init
__(self
, *args
, **kargs
)
231 if not os
.path
.exists('/sys/class/net/bonding_masters'):
233 utils
.exec_command('modprobe -q bonding')
234 except Exception as e
:
235 self
.logger
.info("bond: error while loading bonding module: %s" % str(e
))
237 self
._bond
_attr
_ifquery
_check
_translate
_func
[Link
.IFLA_BOND_PRIMARY
] = self
.cache
.get_ifindex
238 self
._bond
_attr
_set
_list
= self
._bond
_attr
_set
_list
+ (('bond-primary', Link
.IFLA_BOND_PRIMARY
, self
.cache
.get_ifindex
),)
241 def get_bond_slaves(ifaceobj
):
242 slaves
= ifaceobj
.get_attr_value_first('bond-slaves')
244 slaves
= ifaceobj
.get_attr_value_first('bond-ports')
247 def _is_bond(self
, ifaceobj
):
248 # at first link_kind is not set but once ifupdownmain
249 # calls get_dependent_ifacenames link_kind is set to BOND
250 return ifaceobj
.link_kind
& ifaceLinkKind
.BOND
or self
.get_bond_slaves(ifaceobj
)
252 def get_dependent_ifacenames(self
, ifaceobj
, ifacenames_all
=None):
253 """ Returns list of interfaces dependent on ifaceobj """
255 if not self
._is
_bond
(ifaceobj
):
257 slave_list
= self
.parse_port_list(ifaceobj
.name
,
258 self
.get_bond_slaves(ifaceobj
),
260 ifaceobj
.dependency_type
= ifaceDependencyType
.MASTER_SLAVE
261 # Also save a copy for future use
262 ifaceobj
.priv_data
= list(slave_list
)
263 if ifaceobj
.link_type
!= ifaceLinkType
.LINK_NA
:
264 ifaceobj
.link_type
= ifaceLinkType
.LINK_MASTER
265 ifaceobj
.link_kind |
= ifaceLinkKind
.BOND
266 ifaceobj
.role |
= ifaceRole
.MASTER
270 def syntax_check(self
, ifaceobj
, ifaceobj_getfunc
):
271 return self
.syntax_check_updown_delay(ifaceobj
)
273 def get_dependent_ifacenames_running(self
, ifaceobj
):
274 return self
.cache
.get_slaves(ifaceobj
.name
)
276 def _get_slave_list(self
, ifaceobj
):
277 """ Returns slave list present in ifaceobj config """
279 # If priv data already has slave list use that first.
280 if ifaceobj
.priv_data
:
281 return ifaceobj
.priv_data
282 slaves
= self
.get_bond_slaves(ifaceobj
)
284 return self
.parse_port_list(ifaceobj
.name
, slaves
)
288 def enable_ipv6_if_prev_brport(self
, ifname
):
290 If the intf was previously enslaved to a bridge it is possible ipv6 is still disabled.
293 for ifaceobj
in statemanager
.get_ifaceobjs(ifname
) or []:
294 if ifaceobj
.link_privflags
& ifaceLinkPrivFlags
.BRIDGE_PORT
:
295 self
.write_file("/proc/sys/net/ipv6/conf/%s/disable_ipv6" % ifname
, "0")
297 except Exception as e
:
298 self
.logger
.info(str(e
))
300 def _is_clag_bond(self
, ifaceobj
):
301 if self
.get_bond_slaves(ifaceobj
):
302 attrval
= ifaceobj
.get_attr_value_first('clag-id')
303 if attrval
and attrval
!= '0':
307 def _add_slaves(self
, ifaceobj
, runningslaves
, ifaceobj_getfunc
=None):
308 slaves
= self
._get
_slave
_list
(ifaceobj
)
310 self
.logger
.debug('%s: no slaves found' %ifaceobj
.name
)
313 clag_bond
= self
._is
_clag
_bond
(ifaceobj
)
315 for slave
in set(slaves
).difference(set(runningslaves
)):
316 if (not ifupdownflags
.flags
.PERFMODE
and
317 not self
.cache
.link_exists(slave
)):
318 self
.log_error('%s: skipping slave %s, does not exist'
319 %(ifaceobj
.name
, slave
), ifaceobj
,
323 if self
.cache
.link_is_up(slave
):
324 self
.netlink
.link_down_force(slave
)
326 # If clag bond place the slave in a protodown state; clagd
327 # will protoup it when it is ready
330 self
.netlink
.link_set_protodown_on(slave
)
331 except Exception as e
:
332 self
.logger
.error('%s: %s' % (ifaceobj
.name
, str(e
)))
334 self
.enable_ipv6_if_prev_brport(slave
)
335 self
.netlink
.link_set_master(slave
, ifaceobj
.name
)
336 # TODO: if this fail we should switch to iproute2
337 # start a batch: down - set master - up
338 if link_up
or ifaceobj
.link_type
!= ifaceLinkType
.LINK_NA
:
340 if (ifaceobj_getfunc(slave
)[0].link_privflags
&
341 ifaceLinkPrivFlags
.KEEP_LINK_DOWN
):
342 self
.netlink
.link_down_force(slave
)
344 self
.netlink
.link_up(slave
)
345 except Exception as e
:
346 self
.logger
.debug('%s: %s' % (ifaceobj
.name
, str(e
)))
350 for s
in runningslaves
:
352 self
.sysfs
.bond_remove_slave(ifaceobj
.name
, s
)
355 self
.netlink
.link_set_protodown_off(s
)
356 except Exception as e
:
357 self
.logger
.error('%s: %s' % (ifaceobj
.name
, str(e
)))
359 # apply link-down config changes on running slaves
361 link_up
= self
.cache
.link_is_up(s
)
362 config_link_down
= (ifaceobj_getfunc(s
)[0].link_privflags
&
363 ifaceLinkPrivFlags
.KEEP_LINK_DOWN
)
364 if (config_link_down
and link_up
):
365 self
.netlink
.link_down_force(s
)
366 elif (not config_link_down
and not link_up
):
367 self
.netlink
.link_up_force(s
)
368 except Exception as e
:
369 self
.logger
.warning('%s: %s' % (ifaceobj
.name
, str(e
)))
371 def _check_updown_delay_log(self
, ifaceobj
, attr_name
, value
):
372 ifaceobj
.status
= ifaceStatus
.ERROR
373 self
.logger
.error('%s: unable to set %s %s as MII link monitoring is '
374 'disabled' % (ifaceobj
.name
, attr_name
, value
))
375 # return False to notify syntax_check that an error has been logged
378 def syntax_check_updown_delay(self
, ifaceobj
):
380 updelay
= ifaceobj
.get_attr_value_first('bond-updelay')
381 downdelay
= ifaceobj
.get_attr_value_first('bond-downdelay')
383 if not updelay
and not downdelay
:
387 miimon
= int(ifaceobj
.get_attr_value_first('bond-miimon'))
390 miimon
= int(policymanager
.policymanager_api
.get_iface_default(
391 module_name
=self
.__class
__.__name
__,
392 ifname
=ifaceobj
.name
,
398 # self._check_updown_delay_log returns False no matter what
399 if updelay
and int(updelay
):
400 result
= self
._check
_updown
_delay
_log
(ifaceobj
, 'bond-updelay', updelay
)
401 if downdelay
and int(downdelay
):
402 result
= self
._check
_updown
_delay
_log
(ifaceobj
, 'bond-downdelay', downdelay
)
406 _bond_updown_delay_nl_list
= (
407 (Link
.IFLA_BOND_UPDELAY
, 'bond-updelay'),
408 (Link
.IFLA_BOND_DOWNDELAY
, 'bond-downdelay')
411 def check_updown_delay_nl(self
, link_exists
, ifaceobj
, ifla_info_data
):
414 Specifies the time, in milliseconds, to wait before enabling a slave
415 after a link recovery has been detected. This option is only valid
416 for the miimon link monitor. The updelay value should be a multiple
417 of the miimon value; if not, it will be rounded down to the nearest
418 multiple. The default value is 0.
420 This ifla_bond_miimon code should be move to get_ifla_bond_attr_from_user_config
421 but we need to know if the operation was successful to update the cache accordingly
423 ifla_bond_miimon
= ifla_info_data
.get(Link
.IFLA_BOND_MIIMON
)
424 if link_exists
and ifla_bond_miimon
is None:
425 ifla_bond_miimon
= self
.cache
.get_link_info_data_attribute(ifaceobj
.name
, Link
.IFLA_BOND_MIIMON
)
427 if ifla_bond_miimon
== 0:
428 for nl_attr
, attr_name
in self
._bond
_updown
_delay
_nl
_list
:
429 delay
= ifla_info_data
.get(nl_attr
)
430 # if up-down-delay exists we need to remove it, if non zero log error
431 if delay
is not None:
433 self
._check
_updown
_delay
_log
(ifaceobj
, attr_name
, delay
)
434 del ifla_info_data
[nl_attr
]
439 (Link
.IFLA_BOND_AD_LACP_RATE
, 'bond-lacp-rate'),
440 (Link
.IFLA_BOND_AD_LACP_BYPASS
, 'bond-lacp-bypass')
443 def _check_bond_mode_user_config(self
, ifname
, link_exists
, ifla_info_data
):
444 ifla_bond_mode
= ifla_info_data
.get(Link
.IFLA_BOND_MODE
)
445 if ifla_bond_mode
is None and link_exists
:
446 ifla_bond_mode
= self
.cache
.get_link_info_data_attribute(ifname
, Link
.IFLA_BOND_MODE
)
447 # in this case the link already exists (we have a cached value):
448 # if IFLA_BOND_MODE is not present in ifla_info_data it means:
449 # - that bond-mode was present in the user config and didn't change
450 # - never was in the user config so bond mode should be the system default value
451 # - was removed from the stanza so we might have to reset it to default value
452 # nevertheless we need to add it back to the ifla_info_data dict to check
453 # if we need to reset the mode to system default
454 ifla_info_data
[Link
.IFLA_BOND_MODE
] = ifla_bond_mode
456 if ifla_bond_mode
== 4: # 802.3ad
457 min_links
= ifla_info_data
.get(Link
.IFLA_BOND_MIN_LINKS
)
458 if min_links
is None:
459 min_links
= self
.cache
.get_link_info_data_attribute(ifname
, Link
.IFLA_BOND_MIN_LINKS
)
460 # get_min_links_nl may return None so we need to strictly check 0
462 self
.logger
.warning('%s: attribute bond-min-links is set to \'0\'' % ifname
)
464 # IFLA_BOND_AD_LACP_RATE and IFLA_BOND_AD_LACP_BYPASS only for 802.3ad mode (4)
465 for nl_attr
, attr_name
in self
._bond
_lacp
_attrs
:
466 if nl_attr
in ifla_info_data
:
467 self
.logger
.info('%s: ignoring %s: only available for 802.3ad mode (4)' % (ifname
, attr_name
))
468 del ifla_info_data
[nl_attr
]
471 def get_saved_ifaceobj(link_exists
, ifname
):
473 old_config
= statemanager
.get_ifaceobjs(ifname
)
478 def get_ifla_bond_attr_from_user_config(self
, ifaceobj
, link_exists
):
480 Potential issue: if a user load the bond driver with custom
481 default values (say bond-mode 3), ifupdown2 has no knowledge
482 of these default values.
483 At bond creation everything should work, bonds will be created
484 with mode 3 (even if not specified under the stanza).
485 But, for example: if the user specifies a value under bond-mode
486 and later on the user removes the bond-mode line from the stanza
487 we will detect it and reset to MODINFO: BOND-MODE: DEFAULT aka 0
488 which is not the real default value that the user may expect.
490 ifname
= ifaceobj
.name
491 ifla_info_data
= OrderedDict()
492 old_config
= self
.get_saved_ifaceobj(link_exists
, ifname
)
494 # for each bond attribute we fetch the user configuration
495 # if no configuration is provided we look for a config in policy files
496 for attr_name
, netlink_attr
, func_ptr
in self
._bond
_attr
_set
_list
:
498 user_config
= ifaceobj
.get_attr_value_first(attr_name
)
501 user_config
= policymanager
.policymanager_api
.get_iface_default(
502 module_name
=self
.__class
__.__name
__,
506 self
.logger
.debug('%s: %s %s: extracted from policy files'
507 % (ifname
, attr_name
, user_config
))
509 # no policy override, do we need to reset an attr to default value?
510 if not user_config
and old_config
and old_config
.get_attr_value_first(attr_name
):
511 # if the link already exists but the value is set
512 # (potentially removed from the stanza, we need to reset it to default)
513 # might not work for specific cases, see explanation at the top of this function :)
514 user_config
= self
.get_attr_default_value(attr_name
)
516 self
.logger
.debug('%s: %s: removed from stanza, resetting to default value: %s'
517 % (ifname
, attr_name
, user_config
))
521 nl_value
= func_ptr(user_config
.lower())
524 cached_value
= self
.cache
.get_link_info_data_attribute(ifname
, netlink_attr
)
526 if link_exists
and cached_value
is None:
527 # the link already exists but we don't have any value
528 # cached for this attr, it probably means that the
529 # capability is not available on this system (i.e old kernel)
530 self
.logger
.debug('%s: ignoring %s %s: capability '
531 'probably not supported on this system'
532 % (ifname
, attr_name
, user_config
))
535 # there should be a cached value if the link already exists
536 if cached_value
== nl_value
:
537 # if the user value is already cached: continue
540 # else: the link doesn't exist so we create the bond with
541 # all the user/policy defined values without extra checks
542 ifla_info_data
[netlink_attr
] = nl_value
544 if cached_value
is not None:
545 self
.logger
.info('%s: set %s %s (cache %s)' % (ifname
, attr_name
, user_config
, cached_value
))
547 self
.logger
.info('%s: set %s %s' % (ifname
, attr_name
, user_config
))
550 self
.logger
.warning('%s: invalid %s value %s' % (ifname
, attr_name
, user_config
))
552 self
._check
_bond
_mode
_user
_config
(ifname
, link_exists
, ifla_info_data
)
553 return ifla_info_data
555 _bond_down_nl_attributes_list
= (
557 Link
.IFLA_BOND_XMIT_HASH_POLICY
,
558 Link
.IFLA_BOND_AD_LACP_RATE
,
559 Link
.IFLA_BOND_MIN_LINKS
562 def _should_down_bond(self
, ifla_info_data
):
563 for nl_attr
in self
._bond
_down
_nl
_attributes
_list
:
564 if nl_attr
in ifla_info_data
:
568 def should_update_bond_mode(self
, ifaceobj
, ifname
, is_link_up
, ifla_info_data
, bond_slaves
):
569 # if bond-mode was changed the bond needs to be brought
570 # down and slaves un-slaved before bond mode is changed.
571 cached_bond_mode
= self
.cache
.get_link_info_data_attribute(ifname
, Link
.IFLA_BOND_MODE
)
572 ifla_bond_mode
= ifla_info_data
.get(Link
.IFLA_BOND_MODE
)
574 # bond-mode was changed or is not specified
575 if ifla_bond_mode
is not None:
576 if ifla_bond_mode
!= cached_bond_mode
:
577 self
.logger
.info('%s: bond mode changed to %s: running ops on bond and slaves'
578 % (ifname
, ifla_bond_mode
))
580 self
.netlink
.link_down(ifname
)
583 for lower_dev
in ifaceobj
.lowerifaces
:
584 self
.netlink
.link_set_nomaster(lower_dev
)
586 bond_slaves
.remove(lower_dev
)
591 # bond-mode user config value is the current running(cached) value
592 # no need to reset it again we can ignore this attribute
593 del ifla_info_data
[Link
.IFLA_BOND_MODE
]
595 return is_link_up
, bond_slaves
597 def create_or_set_bond_config(self
, ifaceobj
):
598 ifname
= ifaceobj
.name
599 link_exists
, is_link_up
= self
.cache
.link_exists_and_up(ifname
)
600 ifla_info_data
= self
.get_ifla_bond_attr_from_user_config(ifaceobj
, link_exists
)
602 remove_delay_from_cache
= self
.check_updown_delay_nl(link_exists
, ifaceobj
, ifla_info_data
)
604 # if link exists: down link if specific attributes are specified
606 # did bond-mode changed?
607 is_link_up
, bond_slaves
= self
.should_update_bond_mode(
612 self
.cache
.get_slaves(ifname
)
615 # if specific attributes need to be set we need to down the bond first
616 if ifla_info_data
and is_link_up
:
617 if self
._should
_down
_bond
(ifla_info_data
):
618 self
.netlink
.link_down_force(ifname
)
623 if link_exists
and not ifla_info_data
:
624 # if the bond already exists and no attrs need to be set
625 # ignore the netlink call
626 self
.logger
.info('%s: already exists, no change detected' % ifname
)
629 self
.netlink
.link_add_bond_with_info_data(ifname
, ifla_info_data
)
630 except Exception as e
:
632 # if anything happens, we try to set up the bond with the sysfs api
633 self
.logger
.debug('%s: bond setup: %s' % (ifname
, str(e
)))
634 self
.create_or_set_bond_config_sysfs(ifaceobj
, ifla_info_data
)
636 if remove_delay_from_cache
:
637 # making sure up/down delay attributes are set to 0 before caching
638 # this can be removed when moving to a nllistener/live cache
639 ifla_info_data
[Link
.IFLA_BOND_UPDELAY
] = 0
640 ifla_info_data
[Link
.IFLA_BOND_DOWNDELAY
] = 0
642 if link_exists
and ifla_info_data
and not is_link_up
:
643 self
.netlink
.link_up_force(ifname
)
647 def create_or_set_bond_config_sysfs(self
, ifaceobj
, ifla_info_data
):
648 if not self
.cache
.link_exists(ifaceobj
.name
):
649 self
.sysfs
.bond_create(ifaceobj
.name
)
650 self
.sysfs
.bond_set_attrs_nl(ifaceobj
.name
, ifla_info_data
)
652 def _up(self
, ifaceobj
, ifaceobj_getfunc
=None):
654 bond_slaves
= self
.create_or_set_bond_config(ifaceobj
)
660 except Exception as e
:
661 self
.log_error(str(e
), ifaceobj
)
663 def _down(self
, ifaceobj
, ifaceobj_getfunc
=None):
665 self
.netlink
.link_del(ifaceobj
.name
)
666 except Exception as e
:
667 self
.log_warn('%s: %s' % (ifaceobj
.name
, str(e
)))
669 def _query_check_bond_slaves(self
, ifaceobjcurr
, attr
, user_bond_slaves
, running_bond_slaves
):
672 if user_bond_slaves
and running_bond_slaves
:
673 if not set(user_bond_slaves
).symmetric_difference(running_bond_slaves
):
676 # we want to display the same bond-slaves list as provided
677 # in the interfaces file but if this list contains regexes or
678 # globs, for now, we won't try to change it.
679 if 'regex' in user_bond_slaves
or 'glob' in user_bond_slaves
:
680 user_bond_slaves
= running_bond_slaves
683 for slave
in user_bond_slaves
:
684 if slave
in running_bond_slaves
:
685 ordered
.append(slave
)
686 user_bond_slaves
= ordered
687 ifaceobjcurr
.update_config_with_status(attr
, ' '.join(user_bond_slaves
) if user_bond_slaves
else 'None', query
)
689 def _query_check(self
, ifaceobj
, ifaceobjcurr
, ifaceobj_getfunc
=None):
690 if not self
.cache
.bond_exists(ifaceobj
.name
):
691 self
.logger
.debug('bond iface %s does not exist' % ifaceobj
.name
)
694 iface_attrs
= self
.dict_key_subset(ifaceobj
.config
, self
.get_mod_attrs())
698 # remove bond-slaves and bond-ports from the list,
699 # because there aren't any ifla_info_data netlink attr for slaves
700 # an exception is raised when index is not found, so query_slaves will stay False
703 user_bond_slaves
= None
704 running_bond_slaves
= None
706 del iface_attrs
[iface_attrs
.index('bond-slaves')]
708 # if user specified bond-slaves we need to display it
710 if not user_bond_slaves
:
711 user_bond_slaves
= self
._get
_slave
_list
(ifaceobj
)
712 running_bond_slaves
= self
.cache
.get_slaves(ifaceobj
.name
)
714 self
._query
_check
_bond
_slaves
(ifaceobjcurr
, 'bond-slaves', user_bond_slaves
, running_bond_slaves
)
718 del iface_attrs
[iface_attrs
.index('bond-ports')]
720 # if user specified bond-ports we need to display it
721 if not query_slaves
and not user_bond_slaves
: # if get_slave_list was already called for slaves
722 user_bond_slaves
= self
._get
_slave
_list
(ifaceobj
)
723 running_bond_slaves
= self
.cache
.get_slaves(ifaceobj
.name
)
725 self
._query
_check
_bond
_slaves
(ifaceobjcurr
, 'bond-ports', user_bond_slaves
, running_bond_slaves
)
729 for attr
in iface_attrs
:
730 nl_attr
= self
._bond
_attr
_netlink
_map
[attr
]
731 translate_func
= self
._bond
_attr
_ifquery
_check
_translate
_func
[nl_attr
]
732 current_config
= self
.cache
.get_link_info_data_attribute(ifaceobj
.name
, nl_attr
)
733 user_config
= ifaceobj
.get_attr_value_first(attr
)
735 if current_config
== translate_func(user_config
):
736 ifaceobjcurr
.update_config_with_status(attr
, user_config
, 0)
738 ifaceobjcurr
.update_config_with_status(attr
, str(current_config
), 1)
741 def translate_nl_value_yesno(value
):
742 return 'yes' if value
else 'no'
745 def translate_nl_value_slowfast(value
):
746 return 'fast' if value
else 'slow'
748 def _query_running_attrs(self
, bondname
):
749 cached_vxlan_ifla_info_data
= self
.cache
.get_link_info_data(bondname
)
752 'bond-mode': Link
.ifla_bond_mode_pretty_tbl
.get(cached_vxlan_ifla_info_data
.get(Link
.IFLA_BOND_MODE
)),
753 'bond-miimon': cached_vxlan_ifla_info_data
.get(Link
.IFLA_BOND_MIIMON
),
754 'bond-use-carrier': self
.translate_nl_value_yesno(cached_vxlan_ifla_info_data
.get(Link
.IFLA_BOND_USE_CARRIER
)),
755 'bond-lacp-rate': self
.translate_nl_value_slowfast(cached_vxlan_ifla_info_data
.get(Link
.IFLA_BOND_AD_LACP_RATE
)),
756 'bond-min-links': cached_vxlan_ifla_info_data
.get(Link
.IFLA_BOND_MIN_LINKS
),
757 'bond-ad-actor-system': cached_vxlan_ifla_info_data
.get(Link
.IFLA_BOND_AD_ACTOR_SYSTEM
),
758 'bond-ad-actor-sys-prio': cached_vxlan_ifla_info_data
.get(Link
.IFLA_BOND_AD_ACTOR_SYS_PRIO
),
759 '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
)),
760 'bond-lacp-bypass-allow': self
.translate_nl_value_yesno(cached_vxlan_ifla_info_data
.get(Link
.IFLA_BOND_AD_LACP_BYPASS
)),
761 'bond-num-unsol-na': cached_vxlan_ifla_info_data
.get(Link
.IFLA_BOND_NUM_PEER_NOTIF
),
762 'bond-num-grat-arp': cached_vxlan_ifla_info_data
.get(Link
.IFLA_BOND_NUM_PEER_NOTIF
),
763 'bond-updelay': cached_vxlan_ifla_info_data
.get(Link
.IFLA_BOND_UPDELAY
),
764 'bond-downdelay': cached_vxlan_ifla_info_data
.get(Link
.IFLA_BOND_DOWNDELAY
)
767 cached_bond_primary
= cached_vxlan_ifla_info_data
.get(Link
.IFLA_BOND_PRIMARY
)
768 if cached_bond_primary
:
769 bond_attrs
['bond-primary'] = self
.cache
.get_ifname(cached_bond_primary
)
771 slaves
= self
.cache
.get_slaves(bondname
)
773 bond_attrs
['bond-slaves'] = slaves
776 def _query_running(self
, ifaceobjrunning
, ifaceobj_getfunc
=None):
777 if not self
.cache
.bond_exists(ifaceobjrunning
.name
):
779 bond_attrs
= self
._query
_running
_attrs
(ifaceobjrunning
.name
)
780 if bond_attrs
.get('bond-slaves'):
781 bond_attrs
['bond-slaves'] = ' '.join(bond_attrs
.get('bond-slaves'))
783 [ifaceobjrunning
.update_config(k
, str(v
))
784 for k
, v
in list(bond_attrs
.items())
790 'query-running': _query_running
,
791 'query-checkcurr': _query_check
795 """ returns list of ops supported by this module """
796 return list(self
._run
_ops
.keys())
798 def run(self
, ifaceobj
, operation
, query_ifaceobj
=None,
799 ifaceobj_getfunc
=None):
800 """ run bond configuration on the interface object passed as argument
803 **ifaceobj** (object): iface object
805 **operation** (str): any of 'pre-up', 'post-down', 'query-checkcurr',
809 **query_ifaceobj** (object): query check ifaceobject. This is only
810 valid when op is 'query-checkcurr'. It is an object same as
811 ifaceobj, but contains running attribute values and its config
812 status. The modules can use it to return queried running state
813 of interfaces. status is success if the running state is same
814 as user required state in ifaceobj. error otherwise.
816 op_handler
= self
._run
_ops
.get(operation
)
819 if operation
!= 'query-running' and not self
._is
_bond
(ifaceobj
):
821 if operation
== 'query-checkcurr':
822 op_handler(self
, ifaceobj
, query_ifaceobj
,
823 ifaceobj_getfunc
=ifaceobj_getfunc
)
825 op_handler(self
, ifaceobj
, ifaceobj_getfunc
=ifaceobj_getfunc
)