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"]
176 "help": "evpn-mh: system mac address",
177 "validvals": ["<mac>", ],
178 "example": ["es-sys-mac 00:00:00:00:00:42"],
183 _bond_attr_netlink_map
= {
184 'bond-mode': Link
.IFLA_BOND_MODE
,
185 'bond-miimon': Link
.IFLA_BOND_MIIMON
,
186 'bond-use-carrier': Link
.IFLA_BOND_USE_CARRIER
,
187 'bond-lacp-rate': Link
.IFLA_BOND_AD_LACP_RATE
,
188 'bond-xmit-hash-policy': Link
.IFLA_BOND_XMIT_HASH_POLICY
,
189 'bond-min-links': Link
.IFLA_BOND_MIN_LINKS
,
190 'bond-num-grat-arp': Link
.IFLA_BOND_NUM_PEER_NOTIF
,
191 'bond-num-unsol-na': Link
.IFLA_BOND_NUM_PEER_NOTIF
,
192 'es-sys-mac': Link
.IFLA_BOND_AD_ACTOR_SYSTEM
,
193 'bond-ad-sys-mac-addr': Link
.IFLA_BOND_AD_ACTOR_SYSTEM
,
194 'bond-ad-actor-system': Link
.IFLA_BOND_AD_ACTOR_SYSTEM
,
195 'bond-ad-sys-priority': Link
.IFLA_BOND_AD_ACTOR_SYS_PRIO
,
196 'bond-ad-actor-sys-prio': Link
.IFLA_BOND_AD_ACTOR_SYS_PRIO
,
197 'bond-lacp-bypass-allow': Link
.IFLA_BOND_AD_LACP_BYPASS
,
198 'bond-updelay': Link
.IFLA_BOND_UPDELAY
,
199 'bond-downdelay': Link
.IFLA_BOND_DOWNDELAY
,
200 'bond-primary': Link
.IFLA_BOND_PRIMARY
,
201 'bond-primary-reselect': Link
.IFLA_BOND_PRIMARY_RESELECT
205 # ifquery-check attr dictionary with callable object to translate user data to netlink format
206 _bond_attr_ifquery_check_translate_func
= {
207 Link
.IFLA_BOND_MODE
: lambda x
: Link
.ifla_bond_mode_tbl
[x
],
208 Link
.IFLA_BOND_MIIMON
: int,
209 Link
.IFLA_BOND_USE_CARRIER
: utils
.get_boolean_from_string
,
210 Link
.IFLA_BOND_AD_LACP_RATE
: lambda x
: int(utils
.get_boolean_from_string(x
)),
211 Link
.IFLA_BOND_XMIT_HASH_POLICY
: lambda x
: Link
.ifla_bond_xmit_hash_policy_tbl
[x
],
212 Link
.IFLA_BOND_MIN_LINKS
: int,
213 Link
.IFLA_BOND_NUM_PEER_NOTIF
: int,
214 Link
.IFLA_BOND_AD_ACTOR_SYSTEM
: str,
215 Link
.IFLA_BOND_AD_ACTOR_SYS_PRIO
: int,
216 Link
.IFLA_BOND_AD_LACP_BYPASS
: lambda x
: int(utils
.get_boolean_from_string(x
)),
217 Link
.IFLA_BOND_UPDELAY
: int,
218 Link
.IFLA_BOND_DOWNDELAY
: int,
219 Link
.IFLA_BOND_PRIMARY_RESELECT
: lambda x
: Link
.ifla_bond_primary_reselect_tbl
[x
],
220 # Link.IFLA_BOND_PRIMARY: self.netlink.get_ifname is added in __init__()
223 # ifup attr list with callable object to translate user data to netlink format
224 # in the future this can be moved to a dictionary, whenever we detect that some
225 # netlink capabilities are missing we can dynamically remove them from the dict.
226 _bond_attr_set_list
= (
227 ('bond-mode', Link
.IFLA_BOND_MODE
, lambda x
: Link
.ifla_bond_mode_tbl
[x
]),
228 ('bond-xmit-hash-policy', Link
.IFLA_BOND_XMIT_HASH_POLICY
, lambda x
: Link
.ifla_bond_xmit_hash_policy_tbl
[x
]),
229 ('bond-miimon', Link
.IFLA_BOND_MIIMON
, int),
230 ('bond-min-links', Link
.IFLA_BOND_MIN_LINKS
, int),
231 ('bond-num-grat-arp', Link
.IFLA_BOND_NUM_PEER_NOTIF
, int),
232 ('bond-num-unsol-na', Link
.IFLA_BOND_NUM_PEER_NOTIF
, int),
233 ('bond-ad-sys-priority', Link
.IFLA_BOND_AD_ACTOR_SYS_PRIO
, int),
234 ('bond-ad-actor-sys-prio', Link
.IFLA_BOND_AD_ACTOR_SYS_PRIO
, int),
235 ('bond-updelay', Link
.IFLA_BOND_UPDELAY
, int),
236 ('bond-downdelay', Link
.IFLA_BOND_DOWNDELAY
, int),
237 ('bond-use-carrier', Link
.IFLA_BOND_USE_CARRIER
, lambda x
: int(utils
.get_boolean_from_string(x
))),
238 ('bond-lacp-rate', Link
.IFLA_BOND_AD_LACP_RATE
, lambda x
: int(utils
.get_boolean_from_string(x
))),
239 ('bond-lacp-bypass-allow', Link
.IFLA_BOND_AD_LACP_BYPASS
, lambda x
: int(utils
.get_boolean_from_string(x
))),
240 ('es-sys-mac', Link
.IFLA_BOND_AD_ACTOR_SYSTEM
, str),
241 ('bond-ad-sys-mac-addr', Link
.IFLA_BOND_AD_ACTOR_SYSTEM
, str),
242 ('bond-ad-actor-system', Link
.IFLA_BOND_AD_ACTOR_SYSTEM
, str),
243 ('bond-primary-reselect', Link
.IFLA_BOND_PRIMARY_RESELECT
, lambda x
: Link
.ifla_bond_primary_reselect_tbl
[x
])
244 # ('bond-primary', Link.IFLA_BOND_PRIMARY, self.cache.get_ifindex) added in __init__()
247 def __init__(self
, *args
, **kargs
):
249 moduleBase
.__init
__(self
, *args
, **kargs
)
251 if not os
.path
.exists('/sys/class/net/bonding_masters'):
253 utils
.exec_command('modprobe -q bonding')
254 except Exception as e
:
255 self
.logger
.info("bond: error while loading bonding module: %s" % str(e
))
257 self
._bond
_attr
_ifquery
_check
_translate
_func
[Link
.IFLA_BOND_PRIMARY
] = self
.cache
.get_ifindex
258 self
._bond
_attr
_set
_list
= self
._bond
_attr
_set
_list
+ (('bond-primary', Link
.IFLA_BOND_PRIMARY
, self
.cache
.get_ifindex
),)
261 def get_bond_slaves(self
, ifaceobj
):
262 # bond-ports aliases should be translated to bond-slaves
263 return ifaceobj
.get_attr_value_first('bond-slaves')
265 def _is_bond(self
, ifaceobj
):
266 # at first link_kind is not set but once ifupdownmain
267 # calls get_dependent_ifacenames link_kind is set to BOND
268 return ifaceobj
.link_kind
& ifaceLinkKind
.BOND
or self
.get_bond_slaves(ifaceobj
)
270 def get_dependent_ifacenames(self
, ifaceobj
, ifacenames_all
=None):
271 """ Returns list of interfaces dependent on ifaceobj """
273 if not self
._is
_bond
(ifaceobj
):
275 slave_list
= self
.parse_port_list(ifaceobj
.name
,
276 self
.get_bond_slaves(ifaceobj
),
278 ifaceobj
.dependency_type
= ifaceDependencyType
.MASTER_SLAVE
279 # Also save a copy for future use
280 ifaceobj
.priv_data
= list(slave_list
)
281 if ifaceobj
.link_type
!= ifaceLinkType
.LINK_NA
:
282 ifaceobj
.link_type
= ifaceLinkType
.LINK_MASTER
283 ifaceobj
.link_kind |
= ifaceLinkKind
.BOND
284 ifaceobj
.role |
= ifaceRole
.MASTER
286 if ifaceobj
.get_attr_value("es-sys-mac"):
287 ifaceobj
.link_privflags |
= ifaceLinkPrivFlags
.ES_BOND
291 def syntax_check(self
, ifaceobj
, ifaceobj_getfunc
):
292 return self
.syntax_check_updown_delay(ifaceobj
)
294 def get_dependent_ifacenames_running(self
, ifaceobj
):
295 return self
.cache
.get_slaves(ifaceobj
.name
)
297 def _get_slave_list(self
, ifaceobj
):
298 """ Returns slave list present in ifaceobj config """
300 # If priv data already has slave list use that first.
301 if ifaceobj
.priv_data
:
302 return ifaceobj
.priv_data
303 slaves
= self
.get_bond_slaves(ifaceobj
)
305 return self
.parse_port_list(ifaceobj
.name
, slaves
)
309 def enable_ipv6_if_prev_brport(self
, ifname
):
311 If the intf was previously enslaved to a bridge it is possible ipv6 is still disabled.
314 for ifaceobj
in statemanager
.get_ifaceobjs(ifname
) or []:
315 if ifaceobj
.link_privflags
& ifaceLinkPrivFlags
.BRIDGE_PORT
:
316 self
.write_file("/proc/sys/net/ipv6/conf/%s/disable_ipv6" % ifname
, "0")
318 except Exception as e
:
319 self
.logger
.info(str(e
))
321 def _is_clag_bond(self
, ifaceobj
):
322 if self
.get_bond_slaves(ifaceobj
):
323 attrval
= ifaceobj
.get_attr_value_first('clag-id')
324 if attrval
and attrval
!= '0':
328 def _add_slaves(self
, ifaceobj
, runningslaves
, ifaceobj_getfunc
=None):
329 slaves
= self
._get
_slave
_list
(ifaceobj
)
331 self
.logger
.debug('%s: no slaves found' %ifaceobj
.name
)
334 clag_bond
= self
._is
_clag
_bond
(ifaceobj
)
336 # remove duplicates and devices that are already enslaved
337 devices_to_enslave
= []
339 if s
not in runningslaves
and s
not in devices_to_enslave
:
340 devices_to_enslave
.append(s
)
342 for slave
in devices_to_enslave
:
343 if (not ifupdownflags
.flags
.PERFMODE
and
344 not self
.cache
.link_exists(slave
)):
345 self
.log_error('%s: skipping slave %s, does not exist'
346 %(ifaceobj
.name
, slave
), ifaceobj
,
350 if self
.cache
.link_is_up(slave
):
351 self
.netlink
.link_down_force(slave
)
354 # if clag or ES bond: place the slave in a protodown state;
355 # (clagd will proto-up it when it is ready)
356 if clag_bond
or ifaceobj
.link_privflags
& ifaceLinkPrivFlags
.ES_BOND
:
358 self
.netlink
.link_set_protodown_on(slave
)
359 except Exception as e
:
360 self
.logger
.error('%s: %s' % (ifaceobj
.name
, str(e
)))
362 self
.enable_ipv6_if_prev_brport(slave
)
363 self
.netlink
.link_set_master(slave
, ifaceobj
.name
)
364 # TODO: if this fail we should switch to iproute2
365 # start a batch: down - set master - up
366 if link_up
or ifaceobj
.link_type
!= ifaceLinkType
.LINK_NA
:
368 if (ifaceobj_getfunc(slave
)[0].link_privflags
&
369 ifaceLinkPrivFlags
.KEEP_LINK_DOWN
):
370 self
.netlink
.link_down_force(slave
)
372 self
.netlink
.link_up(slave
)
373 except Exception as e
:
374 self
.logger
.debug('%s: %s' % (ifaceobj
.name
, str(e
)))
378 for s
in runningslaves
:
380 self
.sysfs
.bond_remove_slave(ifaceobj
.name
, s
)
383 self
.netlink
.link_set_protodown_off(s
)
384 except Exception as e
:
385 self
.logger
.error('%s: %s' % (ifaceobj
.name
, str(e
)))
387 # apply link-down config changes on running slaves
389 link_up
= self
.cache
.link_is_up(s
)
390 config_link_down
= (ifaceobj_getfunc(s
)[0].link_privflags
&
391 ifaceLinkPrivFlags
.KEEP_LINK_DOWN
)
392 if (config_link_down
and link_up
):
393 self
.netlink
.link_down_force(s
)
394 elif (not config_link_down
and not link_up
):
395 self
.netlink
.link_up_force(s
)
396 except Exception as e
:
397 self
.logger
.warning('%s: %s' % (ifaceobj
.name
, str(e
)))
399 def _check_updown_delay_log(self
, ifaceobj
, attr_name
, value
):
400 ifaceobj
.status
= ifaceStatus
.ERROR
401 self
.logger
.error('%s: unable to set %s %s as MII link monitoring is '
402 'disabled' % (ifaceobj
.name
, attr_name
, value
))
403 # return False to notify syntax_check that an error has been logged
406 def syntax_check_updown_delay(self
, ifaceobj
):
408 updelay
= ifaceobj
.get_attr_value_first('bond-updelay')
409 downdelay
= ifaceobj
.get_attr_value_first('bond-downdelay')
411 if not updelay
and not downdelay
:
415 miimon
= int(ifaceobj
.get_attr_value_first('bond-miimon'))
418 miimon
= int(policymanager
.policymanager_api
.get_iface_default(
419 module_name
=self
.__class
__.__name
__,
420 ifname
=ifaceobj
.name
,
426 # self._check_updown_delay_log returns False no matter what
427 if updelay
and int(updelay
):
428 result
= self
._check
_updown
_delay
_log
(ifaceobj
, 'bond-updelay', updelay
)
429 if downdelay
and int(downdelay
):
430 result
= self
._check
_updown
_delay
_log
(ifaceobj
, 'bond-downdelay', downdelay
)
434 _bond_updown_delay_nl_list
= (
435 (Link
.IFLA_BOND_UPDELAY
, 'bond-updelay'),
436 (Link
.IFLA_BOND_DOWNDELAY
, 'bond-downdelay')
439 def check_updown_delay_nl(self
, link_exists
, ifaceobj
, ifla_info_data
):
442 Specifies the time, in milliseconds, to wait before enabling a slave
443 after a link recovery has been detected. This option is only valid
444 for the miimon link monitor. The updelay value should be a multiple
445 of the miimon value; if not, it will be rounded down to the nearest
446 multiple. The default value is 0.
448 This ifla_bond_miimon code should be move to get_ifla_bond_attr_from_user_config
449 but we need to know if the operation was successful to update the cache accordingly
451 ifla_bond_miimon
= ifla_info_data
.get(Link
.IFLA_BOND_MIIMON
)
452 if link_exists
and ifla_bond_miimon
is None:
453 ifla_bond_miimon
= self
.cache
.get_link_info_data_attribute(ifaceobj
.name
, Link
.IFLA_BOND_MIIMON
)
455 if ifla_bond_miimon
== 0:
456 for nl_attr
, attr_name
in self
._bond
_updown
_delay
_nl
_list
:
457 delay
= ifla_info_data
.get(nl_attr
)
458 # if up-down-delay exists we need to remove it, if non zero log error
459 if delay
is not None:
461 self
._check
_updown
_delay
_log
(ifaceobj
, attr_name
, delay
)
462 del ifla_info_data
[nl_attr
]
467 (Link
.IFLA_BOND_AD_LACP_RATE
, 'bond-lacp-rate'),
468 (Link
.IFLA_BOND_AD_LACP_BYPASS
, 'bond-lacp-bypass')
471 def _check_bond_mode_user_config(self
, ifname
, link_exists
, ifla_info_data
):
472 ifla_bond_mode
= ifla_info_data
.get(Link
.IFLA_BOND_MODE
)
473 if ifla_bond_mode
is None and link_exists
:
474 ifla_bond_mode
= self
.cache
.get_link_info_data_attribute(ifname
, Link
.IFLA_BOND_MODE
)
475 # in this case the link already exists (we have a cached value):
476 # if IFLA_BOND_MODE is not present in ifla_info_data it means:
477 # - that bond-mode was present in the user config and didn't change
478 # - never was in the user config so bond mode should be the system default value
479 # - was removed from the stanza so we might have to reset it to default value
480 # nevertheless we need to add it back to the ifla_info_data dict to check
481 # if we need to reset the mode to system default
482 ifla_info_data
[Link
.IFLA_BOND_MODE
] = ifla_bond_mode
484 if ifla_bond_mode
== 4: # 802.3ad
485 min_links
= ifla_info_data
.get(Link
.IFLA_BOND_MIN_LINKS
)
486 if min_links
is None:
487 min_links
= self
.cache
.get_link_info_data_attribute(ifname
, Link
.IFLA_BOND_MIN_LINKS
)
488 # get_min_links_nl may return None so we need to strictly check 0
490 self
.logger
.warning('%s: attribute bond-min-links is set to \'0\'' % ifname
)
492 # IFLA_BOND_AD_LACP_RATE and IFLA_BOND_AD_LACP_BYPASS only for 802.3ad mode (4)
493 for nl_attr
, attr_name
in self
._bond
_lacp
_attrs
:
494 if nl_attr
in ifla_info_data
:
495 self
.logger
.info('%s: ignoring %s: only available for 802.3ad mode (4)' % (ifname
, attr_name
))
496 del ifla_info_data
[nl_attr
]
499 def get_saved_ifaceobj(link_exists
, ifname
):
501 old_config
= statemanager
.get_ifaceobjs(ifname
)
506 def get_ifla_bond_attr_from_user_config(self
, ifaceobj
, link_exists
):
508 Potential issue: if a user load the bond driver with custom
509 default values (say bond-mode 3), ifupdown2 has no knowledge
510 of these default values.
511 At bond creation everything should work, bonds will be created
512 with mode 3 (even if not specified under the stanza).
513 But, for example: if the user specifies a value under bond-mode
514 and later on the user removes the bond-mode line from the stanza
515 we will detect it and reset to MODINFO: BOND-MODE: DEFAULT aka 0
516 which is not the real default value that the user may expect.
518 ifname
= ifaceobj
.name
519 ifla_info_data
= OrderedDict()
520 old_config
= self
.get_saved_ifaceobj(link_exists
, ifname
)
522 # for each bond attribute we fetch the user configuration
523 # if no configuration is provided we look for a config in policy files
524 for attr_name
, netlink_attr
, func_ptr
in self
._bond
_attr
_set
_list
:
526 user_config
= ifaceobj
.get_attr_value_first(attr_name
)
529 user_config
= policymanager
.policymanager_api
.get_iface_default(
530 module_name
=self
.__class
__.__name
__,
534 self
.logger
.debug('%s: %s %s: extracted from policy files'
535 % (ifname
, attr_name
, user_config
))
537 # no policy override, do we need to reset an attr to default value?
538 if not user_config
and old_config
and old_config
.get_attr_value_first(attr_name
):
539 # if the link already exists but the value is set
540 # (potentially removed from the stanza, we need to reset it to default)
541 # might not work for specific cases, see explanation at the top of this function :)
542 user_config
= self
.get_attr_default_value(attr_name
)
544 self
.logger
.debug('%s: %s: removed from stanza, resetting to default value: %s'
545 % (ifname
, attr_name
, user_config
))
549 nl_value
= func_ptr(user_config
.lower())
552 cached_value
= self
.cache
.get_link_info_data_attribute(ifname
, netlink_attr
)
554 if link_exists
and cached_value
is None:
555 # the link already exists but we don't have any value
556 # cached for this attr, it probably means that the
557 # capability is not available on this system (i.e old kernel)
558 self
.logger
.debug('%s: ignoring %s %s: capability '
559 'probably not supported on this system'
560 % (ifname
, attr_name
, user_config
))
563 # there should be a cached value if the link already exists
564 if cached_value
== nl_value
:
565 # if the user value is already cached: continue
568 # else: the link doesn't exist so we create the bond with
569 # all the user/policy defined values without extra checks
570 ifla_info_data
[netlink_attr
] = nl_value
572 if cached_value
is not None:
573 self
.logger
.info('%s: set %s %s (cache %s)' % (ifname
, attr_name
, user_config
, cached_value
))
575 self
.logger
.info('%s: set %s %s' % (ifname
, attr_name
, user_config
))
578 self
.logger
.warning('%s: invalid %s value %s' % (ifname
, attr_name
, user_config
))
580 self
._check
_bond
_mode
_user
_config
(ifname
, link_exists
, ifla_info_data
)
581 return ifla_info_data
583 _bond_down_nl_attributes_list
= (
585 Link
.IFLA_BOND_XMIT_HASH_POLICY
,
586 Link
.IFLA_BOND_AD_LACP_RATE
,
587 Link
.IFLA_BOND_MIN_LINKS
590 def _should_down_bond(self
, ifla_info_data
):
591 for nl_attr
in self
._bond
_down
_nl
_attributes
_list
:
592 if nl_attr
in ifla_info_data
:
596 def should_update_bond_mode(self
, ifaceobj
, ifname
, is_link_up
, ifla_info_data
, bond_slaves
):
597 # if bond-mode was changed the bond needs to be brought
598 # down and slaves un-slaved before bond mode is changed.
599 cached_bond_mode
= self
.cache
.get_link_info_data_attribute(ifname
, Link
.IFLA_BOND_MODE
)
600 ifla_bond_mode
= ifla_info_data
.get(Link
.IFLA_BOND_MODE
)
602 # bond-mode was changed or is not specified
603 if ifla_bond_mode
is not None:
604 if ifla_bond_mode
!= cached_bond_mode
:
605 self
.logger
.info('%s: bond mode changed to %s: running ops on bond and slaves'
606 % (ifname
, ifla_bond_mode
))
608 self
.netlink
.link_down(ifname
)
611 for lower_dev
in ifaceobj
.lowerifaces
:
612 self
.netlink
.link_set_nomaster(lower_dev
)
614 # when unslaving a device from an ES bond we need to set
616 if ifaceobj
.link_privflags
& ifaceLinkPrivFlags
.ES_BOND
:
617 self
.netlink
.link_set_protodown_off(lower_dev
)
620 bond_slaves
.remove(lower_dev
)
625 # bond-mode user config value is the current running(cached) value
626 # no need to reset it again we can ignore this attribute
627 del ifla_info_data
[Link
.IFLA_BOND_MODE
]
629 return is_link_up
, bond_slaves
631 def create_or_set_bond_config(self
, ifaceobj
):
632 ifname
= ifaceobj
.name
633 link_exists
, is_link_up
= self
.cache
.link_exists_and_up(ifname
)
634 ifla_info_data
= self
.get_ifla_bond_attr_from_user_config(ifaceobj
, link_exists
)
636 remove_delay_from_cache
= self
.check_updown_delay_nl(link_exists
, ifaceobj
, ifla_info_data
)
638 # if link exists: down link if specific attributes are specified
640 # did bond-mode changed?
641 is_link_up
, bond_slaves
= self
.should_update_bond_mode(
646 self
.cache
.get_slaves(ifname
)
649 # if specific attributes need to be set we need to down the bond first
650 if ifla_info_data
and is_link_up
:
651 if self
._should
_down
_bond
(ifla_info_data
):
652 self
.netlink
.link_down_force(ifname
)
657 if link_exists
and not ifla_info_data
:
658 # if the bond already exists and no attrs need to be set
659 # ignore the netlink call
660 self
.logger
.info('%s: already exists, no change detected' % ifname
)
663 self
.netlink
.link_add_bond_with_info_data(ifname
, ifla_info_data
)
664 except Exception as e
:
666 # if anything happens, we try to set up the bond with the sysfs api
667 self
.logger
.debug('%s: bond setup: %s' % (ifname
, str(e
)))
668 self
.create_or_set_bond_config_sysfs(ifaceobj
, ifla_info_data
)
670 if remove_delay_from_cache
:
671 # making sure up/down delay attributes are set to 0 before caching
672 # this can be removed when moving to a nllistener/live cache
673 ifla_info_data
[Link
.IFLA_BOND_UPDELAY
] = 0
674 ifla_info_data
[Link
.IFLA_BOND_DOWNDELAY
] = 0
676 if link_exists
and ifla_info_data
and not is_link_up
:
677 self
.netlink
.link_up_force(ifname
)
681 def create_or_set_bond_config_sysfs(self
, ifaceobj
, ifla_info_data
):
682 if not self
.cache
.link_exists(ifaceobj
.name
):
683 self
.sysfs
.bond_create(ifaceobj
.name
)
684 self
.sysfs
.bond_set_attrs_nl(ifaceobj
.name
, ifla_info_data
)
686 def _up(self
, ifaceobj
, ifaceobj_getfunc
=None):
688 bond_slaves
= self
.create_or_set_bond_config(ifaceobj
)
694 except Exception as e
:
695 self
.log_error(str(e
), ifaceobj
)
697 def _down(self
, ifaceobj
, ifaceobj_getfunc
=None):
699 self
.netlink
.link_del(ifaceobj
.name
)
700 except Exception as e
:
701 self
.log_warn('%s: %s' % (ifaceobj
.name
, str(e
)))
703 def _query_check_bond_slaves(self
, ifaceobjcurr
, attr
, user_bond_slaves
, running_bond_slaves
):
706 if user_bond_slaves
and running_bond_slaves
:
707 if not set(user_bond_slaves
).symmetric_difference(running_bond_slaves
):
710 # we want to display the same bond-slaves list as provided
711 # in the interfaces file but if this list contains regexes or
712 # globs, for now, we won't try to change it.
713 if 'regex' in user_bond_slaves
or 'glob' in user_bond_slaves
:
714 user_bond_slaves
= running_bond_slaves
717 for slave
in user_bond_slaves
:
718 if slave
in running_bond_slaves
:
719 ordered
.append(slave
)
720 user_bond_slaves
= ordered
721 ifaceobjcurr
.update_config_with_status(attr
, ' '.join(user_bond_slaves
) if user_bond_slaves
else 'None', query
)
723 def _query_check(self
, ifaceobj
, ifaceobjcurr
, ifaceobj_getfunc
=None):
724 if not self
.cache
.bond_exists(ifaceobj
.name
):
725 self
.logger
.debug('bond iface %s does not exist' % ifaceobj
.name
)
728 iface_attrs
= self
.dict_key_subset(ifaceobj
.config
, self
.get_mod_attrs())
732 # remove bond-slaves and bond-ports from the list,
733 # because there aren't any ifla_info_data netlink attr for slaves
734 # an exception is raised when index is not found, so query_slaves will stay False
737 user_bond_slaves
= None
738 running_bond_slaves
= None
740 del iface_attrs
[iface_attrs
.index('bond-slaves')]
742 # if user specified bond-slaves we need to display it
744 if not user_bond_slaves
:
745 user_bond_slaves
= self
._get
_slave
_list
(ifaceobj
)
746 running_bond_slaves
= self
.cache
.get_slaves(ifaceobj
.name
)
748 self
._query
_check
_bond
_slaves
(ifaceobjcurr
, 'bond-slaves', user_bond_slaves
, running_bond_slaves
)
752 del iface_attrs
[iface_attrs
.index('bond-ports')]
754 # if user specified bond-ports we need to display it
755 if not query_slaves
and not user_bond_slaves
: # if get_slave_list was already called for slaves
756 user_bond_slaves
= self
._get
_slave
_list
(ifaceobj
)
757 running_bond_slaves
= self
.cache
.get_slaves(ifaceobj
.name
)
759 self
._query
_check
_bond
_slaves
(ifaceobjcurr
, 'bond-ports', user_bond_slaves
, running_bond_slaves
)
763 for attr
in iface_attrs
:
764 nl_attr
= self
._bond
_attr
_netlink
_map
[attr
]
765 translate_func
= self
._bond
_attr
_ifquery
_check
_translate
_func
[nl_attr
]
766 current_config
= self
.cache
.get_link_info_data_attribute(ifaceobj
.name
, nl_attr
)
767 user_config
= ifaceobj
.get_attr_value_first(attr
)
769 if current_config
== translate_func(user_config
):
770 ifaceobjcurr
.update_config_with_status(attr
, user_config
, 0)
772 ifaceobjcurr
.update_config_with_status(attr
, str(current_config
), 1)
775 def translate_nl_value_yesno(value
):
776 return 'yes' if value
else 'no'
779 def translate_nl_value_slowfast(value
):
780 return 'fast' if value
else 'slow'
782 def _query_running_attrs(self
, bondname
):
783 cached_vxlan_ifla_info_data
= self
.cache
.get_link_info_data(bondname
)
786 'bond-mode': Link
.ifla_bond_mode_pretty_tbl
.get(cached_vxlan_ifla_info_data
.get(Link
.IFLA_BOND_MODE
)),
787 'bond-miimon': cached_vxlan_ifla_info_data
.get(Link
.IFLA_BOND_MIIMON
),
788 'bond-use-carrier': self
.translate_nl_value_yesno(cached_vxlan_ifla_info_data
.get(Link
.IFLA_BOND_USE_CARRIER
)),
789 'bond-lacp-rate': self
.translate_nl_value_slowfast(cached_vxlan_ifla_info_data
.get(Link
.IFLA_BOND_AD_LACP_RATE
)),
790 'bond-min-links': cached_vxlan_ifla_info_data
.get(Link
.IFLA_BOND_MIN_LINKS
),
791 'bond-ad-actor-system': cached_vxlan_ifla_info_data
.get(Link
.IFLA_BOND_AD_ACTOR_SYSTEM
),
792 'es-sys-mac': cached_vxlan_ifla_info_data
.get(Link
.IFLA_BOND_AD_ACTOR_SYSTEM
),
793 'bond-ad-actor-sys-prio': cached_vxlan_ifla_info_data
.get(Link
.IFLA_BOND_AD_ACTOR_SYS_PRIO
),
794 '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
)),
795 'bond-lacp-bypass-allow': self
.translate_nl_value_yesno(cached_vxlan_ifla_info_data
.get(Link
.IFLA_BOND_AD_LACP_BYPASS
)),
796 'bond-num-unsol-na': cached_vxlan_ifla_info_data
.get(Link
.IFLA_BOND_NUM_PEER_NOTIF
),
797 'bond-num-grat-arp': cached_vxlan_ifla_info_data
.get(Link
.IFLA_BOND_NUM_PEER_NOTIF
),
798 'bond-updelay': cached_vxlan_ifla_info_data
.get(Link
.IFLA_BOND_UPDELAY
),
799 'bond-downdelay': cached_vxlan_ifla_info_data
.get(Link
.IFLA_BOND_DOWNDELAY
)
802 cached_bond_primary
= cached_vxlan_ifla_info_data
.get(Link
.IFLA_BOND_PRIMARY
)
803 if cached_bond_primary
:
804 bond_attrs
['bond-primary'] = self
.cache
.get_ifname(cached_bond_primary
)
806 slaves
= self
.cache
.get_slaves(bondname
)
808 bond_attrs
['bond-slaves'] = slaves
811 def _query_running(self
, ifaceobjrunning
, ifaceobj_getfunc
=None):
812 if not self
.cache
.bond_exists(ifaceobjrunning
.name
):
814 bond_attrs
= self
._query
_running
_attrs
(ifaceobjrunning
.name
)
815 if bond_attrs
.get('bond-slaves'):
816 bond_attrs
['bond-slaves'] = ' '.join(bond_attrs
.get('bond-slaves'))
818 [ifaceobjrunning
.update_config(k
, str(v
))
819 for k
, v
in list(bond_attrs
.items())
825 'query-running': _query_running
,
826 'query-checkcurr': _query_check
830 """ returns list of ops supported by this module """
831 return list(self
._run
_ops
.keys())
833 def run(self
, ifaceobj
, operation
, query_ifaceobj
=None,
834 ifaceobj_getfunc
=None):
835 """ run bond configuration on the interface object passed as argument
838 **ifaceobj** (object): iface object
840 **operation** (str): any of 'pre-up', 'post-down', 'query-checkcurr',
844 **query_ifaceobj** (object): query check ifaceobject. This is only
845 valid when op is 'query-checkcurr'. It is an object same as
846 ifaceobj, but contains running attribute values and its config
847 status. The modules can use it to return queried running state
848 of interfaces. status is success if the running state is same
849 as user required state in ifaceobj. error otherwise.
851 op_handler
= self
._run
_ops
.get(operation
)
854 if operation
!= 'query-running' and not self
._is
_bond
(ifaceobj
):
856 if operation
== 'query-checkcurr':
857 op_handler(self
, ifaceobj
, query_ifaceobj
,
858 ifaceobj_getfunc
=ifaceobj_getfunc
)
860 op_handler(self
, ifaceobj
, ifaceobj_getfunc
=ifaceobj_getfunc
)