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
23 except (ImportError, ModuleNotFoundError
):
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"]
166 "bond-primary-reselect": {
167 "help": "bond primary reselect",
173 "example": ["bond-primary-reselect failure"]
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
,
193 'bond-downdelay': Link
.IFLA_BOND_DOWNDELAY
,
194 'bond-primary': Link
.IFLA_BOND_PRIMARY
,
195 'bond-primary-reselect': Link
.IFLA_BOND_PRIMARY_RESELECT
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,
212 Link
.IFLA_BOND_DOWNDELAY
: int,
213 Link
.IFLA_BOND_PRIMARY_RESELECT
: lambda x
: Link
.ifla_bond_primary_reselect_tbl
[x
],
214 # Link.IFLA_BOND_PRIMARY: self.netlink.get_ifname is added in __init__()
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),
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
])
237 # ('bond-primary', Link.IFLA_BOND_PRIMARY, self.cache.get_ifindex) added in __init__()
240 def __init__(self
, *args
, **kargs
):
242 moduleBase
.__init
__(self
, *args
, **kargs
)
244 if not os
.path
.exists('/sys/class/net/bonding_masters'):
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
))
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
),)
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')
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
261 return ifaceobj
.link_kind
& ifaceLinkKind
.BOND
or self
.get_bond_slaves(ifaceobj
)
263 def get_dependent_ifacenames(self
, ifaceobj
, ifacenames_all
=None):
264 """ Returns list of interfaces dependent on ifaceobj """
266 if not self
._is
_bond
(ifaceobj
):
268 slave_list
= self
.parse_port_list(ifaceobj
.name
,
269 self
.get_bond_slaves(ifaceobj
),
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
281 def syntax_check(self
, ifaceobj
, ifaceobj_getfunc
):
282 return self
.syntax_check_updown_delay(ifaceobj
)
284 def get_dependent_ifacenames_running(self
, ifaceobj
):
285 return self
.cache
.get_slaves(ifaceobj
.name
)
287 def _get_slave_list(self
, ifaceobj
):
288 """ Returns slave list present in ifaceobj config """
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
)
295 return self
.parse_port_list(ifaceobj
.name
, slaves
)
299 def enable_ipv6_if_prev_brport(self
, ifname
):
301 If the intf was previously enslaved to a bridge it is possible ipv6 is still disabled.
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")
308 except Exception as e
:
309 self
.logger
.info(str(e
))
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':
318 def _add_slaves(self
, ifaceobj
, runningslaves
, ifaceobj_getfunc
=None):
319 slaves
= self
._get
_slave
_list
(ifaceobj
)
321 self
.logger
.debug('%s: no slaves found' %ifaceobj
.name
)
324 clag_bond
= self
._is
_clag
_bond
(ifaceobj
)
326 # remove duplicates and devices that are already enslaved
327 devices_to_enslave
= []
329 if s
not in runningslaves
and s
not in devices_to_enslave
:
330 devices_to_enslave
.append(s
)
332 for slave
in devices_to_enslave
:
333 if (not ifupdownflags
.flags
.PERFMODE
and
334 not self
.cache
.link_exists(slave
)):
335 self
.log_error('%s: skipping slave %s, does not exist'
336 %(ifaceobj
.name
, slave
), ifaceobj
,
340 if self
.cache
.link_is_up(slave
):
341 self
.netlink
.link_down_force(slave
)
343 # If clag bond place the slave in a protodown state; clagd
344 # will protoup it when it is ready
347 self
.netlink
.link_set_protodown_on(slave
)
348 except Exception as e
:
349 self
.logger
.error('%s: %s' % (ifaceobj
.name
, str(e
)))
351 self
.enable_ipv6_if_prev_brport(slave
)
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
355 if link_up
or ifaceobj
.link_type
!= ifaceLinkType
.LINK_NA
:
357 if (ifaceobj_getfunc(slave
)[0].link_privflags
&
358 ifaceLinkPrivFlags
.KEEP_LINK_DOWN
):
359 self
.netlink
.link_down_force(slave
)
361 self
.netlink
.link_up(slave
)
362 except Exception as e
:
363 self
.logger
.debug('%s: %s' % (ifaceobj
.name
, str(e
)))
367 for s
in runningslaves
:
369 self
.sysfs
.bond_remove_slave(ifaceobj
.name
, s
)
372 self
.netlink
.link_set_protodown_off(s
)
373 except Exception as e
:
374 self
.logger
.error('%s: %s' % (ifaceobj
.name
, str(e
)))
376 # apply link-down config changes on running slaves
378 link_up
= self
.cache
.link_is_up(s
)
379 config_link_down
= (ifaceobj_getfunc(s
)[0].link_privflags
&
380 ifaceLinkPrivFlags
.KEEP_LINK_DOWN
)
381 if (config_link_down
and link_up
):
382 self
.netlink
.link_down_force(s
)
383 elif (not config_link_down
and not link_up
):
384 self
.netlink
.link_up_force(s
)
385 except Exception as e
:
386 self
.logger
.warning('%s: %s' % (ifaceobj
.name
, str(e
)))
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
395 def syntax_check_updown_delay(self
, ifaceobj
):
397 updelay
= ifaceobj
.get_attr_value_first('bond-updelay')
398 downdelay
= ifaceobj
.get_attr_value_first('bond-downdelay')
400 if not updelay
and not downdelay
:
404 miimon
= int(ifaceobj
.get_attr_value_first('bond-miimon'))
407 miimon
= int(policymanager
.policymanager_api
.get_iface_default(
408 module_name
=self
.__class
__.__name
__,
409 ifname
=ifaceobj
.name
,
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
)
423 _bond_updown_delay_nl_list
= (
424 (Link
.IFLA_BOND_UPDELAY
, 'bond-updelay'),
425 (Link
.IFLA_BOND_DOWNDELAY
, 'bond-downdelay')
428 def check_updown_delay_nl(self
, link_exists
, ifaceobj
, ifla_info_data
):
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.
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
440 ifla_bond_miimon
= ifla_info_data
.get(Link
.IFLA_BOND_MIIMON
)
441 if link_exists
and ifla_bond_miimon
is None:
442 ifla_bond_miimon
= self
.cache
.get_link_info_data_attribute(ifaceobj
.name
, Link
.IFLA_BOND_MIIMON
)
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:
450 self
._check
_updown
_delay
_log
(ifaceobj
, attr_name
, delay
)
451 del ifla_info_data
[nl_attr
]
456 (Link
.IFLA_BOND_AD_LACP_RATE
, 'bond-lacp-rate'),
457 (Link
.IFLA_BOND_AD_LACP_BYPASS
, 'bond-lacp-bypass')
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
:
463 ifla_bond_mode
= self
.cache
.get_link_info_data_attribute(ifname
, Link
.IFLA_BOND_MODE
)
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
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:
476 min_links
= self
.cache
.get_link_info_data_attribute(ifname
, Link
.IFLA_BOND_MIN_LINKS
)
477 # get_min_links_nl may return None so we need to strictly check 0
479 self
.logger
.warning('%s: attribute bond-min-links is set to \'0\'' % ifname
)
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
]
488 def get_saved_ifaceobj(link_exists
, ifname
):
490 old_config
= statemanager
.get_ifaceobjs(ifname
)
495 def get_ifla_bond_attr_from_user_config(self
, ifaceobj
, link_exists
):
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.
507 ifname
= ifaceobj
.name
508 ifla_info_data
= OrderedDict()
509 old_config
= self
.get_saved_ifaceobj(link_exists
, ifname
)
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
:
515 user_config
= ifaceobj
.get_attr_value_first(attr_name
)
518 user_config
= policymanager
.policymanager_api
.get_iface_default(
519 module_name
=self
.__class
__.__name
__,
523 self
.logger
.debug('%s: %s %s: extracted from policy files'
524 % (ifname
, attr_name
, user_config
))
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
)
533 self
.logger
.debug('%s: %s: removed from stanza, resetting to default value: %s'
534 % (ifname
, attr_name
, user_config
))
538 nl_value
= func_ptr(user_config
.lower())
541 cached_value
= self
.cache
.get_link_info_data_attribute(ifname
, netlink_attr
)
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
))
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
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
561 if cached_value
is not None:
562 self
.logger
.info('%s: set %s %s (cache %s)' % (ifname
, attr_name
, user_config
, cached_value
))
564 self
.logger
.info('%s: set %s %s' % (ifname
, attr_name
, user_config
))
567 self
.logger
.warning('%s: invalid %s value %s' % (ifname
, attr_name
, user_config
))
569 self
._check
_bond
_mode
_user
_config
(ifname
, link_exists
, ifla_info_data
)
570 return ifla_info_data
572 _bond_down_nl_attributes_list
= (
574 Link
.IFLA_BOND_XMIT_HASH_POLICY
,
575 Link
.IFLA_BOND_AD_LACP_RATE
,
576 Link
.IFLA_BOND_MIN_LINKS
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
:
585 def should_update_bond_mode(self
, ifaceobj
, ifname
, is_link_up
, ifla_info_data
, bond_slaves
):
586 # if bond-mode was changed the bond needs to be brought
587 # down and slaves un-slaved before bond mode is changed.
588 cached_bond_mode
= self
.cache
.get_link_info_data_attribute(ifname
, Link
.IFLA_BOND_MODE
)
589 ifla_bond_mode
= ifla_info_data
.get(Link
.IFLA_BOND_MODE
)
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
))
597 self
.netlink
.link_down(ifname
)
600 for lower_dev
in ifaceobj
.lowerifaces
:
601 self
.netlink
.link_set_nomaster(lower_dev
)
603 bond_slaves
.remove(lower_dev
)
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
]
612 return is_link_up
, bond_slaves
614 def create_or_set_bond_config(self
, ifaceobj
):
615 ifname
= ifaceobj
.name
616 link_exists
, is_link_up
= self
.cache
.link_exists_and_up(ifname
)
617 ifla_info_data
= self
.get_ifla_bond_attr_from_user_config(ifaceobj
, link_exists
)
619 remove_delay_from_cache
= self
.check_updown_delay_nl(link_exists
, ifaceobj
, ifla_info_data
)
621 # if link exists: down link if specific attributes are specified
623 # did bond-mode changed?
624 is_link_up
, bond_slaves
= self
.should_update_bond_mode(
629 self
.cache
.get_slaves(ifname
)
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
):
635 self
.netlink
.link_down_force(ifname
)
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
)
646 self
.netlink
.link_add_bond_with_info_data(ifname
, ifla_info_data
)
647 except Exception as e
:
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
)
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
659 if link_exists
and ifla_info_data
and not is_link_up
:
660 self
.netlink
.link_up_force(ifname
)
664 def create_or_set_bond_config_sysfs(self
, ifaceobj
, ifla_info_data
):
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
)
669 def _up(self
, ifaceobj
, ifaceobj_getfunc
=None):
671 bond_slaves
= self
.create_or_set_bond_config(ifaceobj
)
677 except Exception as e
:
678 self
.log_error(str(e
), ifaceobj
)
680 def _down(self
, ifaceobj
, ifaceobj_getfunc
=None):
682 self
.netlink
.link_del(ifaceobj
.name
)
683 except Exception as e
:
684 self
.log_warn('%s: %s' % (ifaceobj
.name
, str(e
)))
686 def _query_check_bond_slaves(self
, ifaceobjcurr
, attr
, user_bond_slaves
, running_bond_slaves
):
689 if user_bond_slaves
and running_bond_slaves
:
690 if not set(user_bond_slaves
).symmetric_difference(running_bond_slaves
):
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
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
)
706 def _query_check(self
, ifaceobj
, ifaceobjcurr
, ifaceobj_getfunc
=None):
707 if not self
.cache
.bond_exists(ifaceobj
.name
):
708 self
.logger
.debug('bond iface %s does not exist' % ifaceobj
.name
)
711 iface_attrs
= self
.dict_key_subset(ifaceobj
.config
, self
.get_mod_attrs())
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
720 user_bond_slaves
= None
721 running_bond_slaves
= None
723 del iface_attrs
[iface_attrs
.index('bond-slaves')]
725 # if user specified bond-slaves we need to display it
727 if not user_bond_slaves
:
728 user_bond_slaves
= self
._get
_slave
_list
(ifaceobj
)
729 running_bond_slaves
= self
.cache
.get_slaves(ifaceobj
.name
)
731 self
._query
_check
_bond
_slaves
(ifaceobjcurr
, 'bond-slaves', user_bond_slaves
, running_bond_slaves
)
735 del iface_attrs
[iface_attrs
.index('bond-ports')]
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
)
740 running_bond_slaves
= self
.cache
.get_slaves(ifaceobj
.name
)
742 self
._query
_check
_bond
_slaves
(ifaceobjcurr
, 'bond-ports', user_bond_slaves
, running_bond_slaves
)
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
]
749 current_config
= self
.cache
.get_link_info_data_attribute(ifaceobj
.name
, nl_attr
)
750 user_config
= ifaceobj
.get_attr_value_first(attr
)
752 if current_config
== translate_func(user_config
):
753 ifaceobjcurr
.update_config_with_status(attr
, user_config
, 0)
755 ifaceobjcurr
.update_config_with_status(attr
, str(current_config
), 1)
758 def translate_nl_value_yesno(value
):
759 return 'yes' if value
else 'no'
762 def translate_nl_value_slowfast(value
):
763 return 'fast' if value
else 'slow'
765 def _query_running_attrs(self
, bondname
):
766 cached_vxlan_ifla_info_data
= self
.cache
.get_link_info_data(bondname
)
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
)
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
)
788 slaves
= self
.cache
.get_slaves(bondname
)
790 bond_attrs
['bond-slaves'] = slaves
793 def _query_running(self
, ifaceobjrunning
, ifaceobj_getfunc
=None):
794 if not self
.cache
.bond_exists(ifaceobjrunning
.name
):
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'))
800 [ifaceobjrunning
.update_config(k
, str(v
))
801 for k
, v
in list(bond_attrs
.items())
807 'query-running': _query_running
,
808 'query-checkcurr': _query_check
812 """ returns list of ops supported by this module """
813 return list(self
._run
_ops
.keys())
815 def run(self
, ifaceobj
, operation
, query_ifaceobj
=None,
816 ifaceobj_getfunc
=None):
817 """ run bond configuration on the interface object passed as argument
820 **ifaceobj** (object): iface object
822 **operation** (str): any of 'pre-up', 'post-down', 'query-checkcurr',
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.
833 op_handler
= self
._run
_ops
.get(operation
)
836 if operation
!= 'query-running' and not self
._is
_bond
(ifaceobj
):
838 if operation
== 'query-checkcurr':
839 op_handler(self
, ifaceobj
, query_ifaceobj
,
840 ifaceobj_getfunc
=ifaceobj_getfunc
)
842 op_handler(self
, ifaceobj
, ifaceobj_getfunc
=ifaceobj_getfunc
)