3 # Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved.
5 # Roopa Prabhu, roopa@cumulusnetworks.com
6 # Julien Fortin, julien@cumulusnetworks.com
10 from collections
import OrderedDict
13 from ifupdown2
.nlmanager
.ipnetwork
import IPv4Address
14 from ifupdown2
.lib
.addon
import Addon
15 from ifupdown2
.nlmanager
.nlmanager
import Link
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
21 import ifupdown2
.ifupdown
.policymanager
as policymanager
22 import ifupdown2
.ifupdown
.ifupdownflags
as ifupdownflags
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
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
34 from ifupdownaddons
.modulebase
import moduleBase
36 import ifupdown
.policymanager
as policymanager
37 import ifupdown
.ifupdownflags
as ifupdownflags
39 class bond(Addon
, moduleBase
):
40 """ ifupdown2 addon module to configure bond interfaces """
42 overrides_ifupdown_scripts
= ['ifenslave', ]
45 "mhelp": "bond configuration module",
48 "help": "bond use carrier",
49 "validvals": ["yes", "no", "0", "1"],
51 "example": ["bond-use-carrier yes"]},
52 "bond-num-grat-arp": {
53 "help": "bond use carrier",
54 "validrange": ["0", "255"],
56 "example": ["bond-num-grat-arp 1"]
58 "bond-num-unsol-na": {
59 "help": "bond slave devices",
60 "validrange": ["0", "255"],
62 "example": ["bond-num-unsol-na 1"]
64 "bond-xmit-hash-policy": {
65 "help": "bond slave devices",
75 "example": ["bond-xmit-hash-policy layer2"]
78 "help": "bond miimon",
79 "validrange": ["0", "255"],
81 "example": ["bond-miimon 0"]
83 "bond-arp-interval": {
84 "help": "bond arp interval (only mode 0 and 2)",
86 "example": ["bond-arp_interval 0"]
88 "bond-arp-ip-target": {
89 "help": "ipv4 addresses maximum 16",
90 "validvals": ["<ipv4>"],
92 "example": ["bond-arp-ip-target 10.0.12.3"]
105 "default": "balance-rr",
106 "example": ["bond-mode 802.3ad"]
109 "help": "bond lacp rate",
110 "validvals": ["0", "slow", "1", "fast"],
112 "example": ["bond-lacp-rate 0"]
115 "help": "bond min links",
117 "validrange": ["0", "255"],
118 "example": ["bond-min-links 0"]
120 "bond-ad-sys-priority": {
121 "help": "802.3ad system priority",
123 "validrange": ["0", "65535"],
124 "example": ["bond-ad-sys-priority 65535"],
126 "new-attribute": "bond-ad-actor-sys-prio"
128 "bond-ad-actor-sys-prio": {
129 "help": "802.3ad system priority",
131 "validrange": ["0", "65535"],
132 "example": ["bond-ad-actor-sys-prio 65535"]
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"],
139 "new-attribute": "bond-ad-actor-system"
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"],
146 "bond-lacp-bypass-allow": {
147 "help": "allow lacp bypass",
148 "validvals": ["yes", "no", "0", "1"],
150 "example": ["bond-lacp-bypass-allow no"]
153 "help": "bond slaves",
156 "validvals": ["<interface-list>"],
158 "bond-slaves swp1 swp2",
159 "bond-slaves glob swp1-2",
160 "bond-slaves regex (swp[1|2])"
162 "aliases": ["bond-ports"]
165 "help": "bond updelay",
167 "validrange": ["0", "65535"],
168 "example": ["bond-updelay 100"]
171 "help": "bond downdelay",
173 "validrange": ["0", "65535"],
174 "example": ["bond-downdelay 100"]
177 "help": "Control which slave interface is "
178 "preferred active member",
179 "example": ["bond-primary swp1"]
181 "bond-primary-reselect": {
182 "help": "bond primary reselect",
188 "example": ["bond-primary-reselect failure"]
191 "help": "evpn-mh: system mac address",
192 "validvals": ["<mac>", ],
193 "example": ["es-sys-mac 00:00:00:00:00:42"],
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
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__()
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__()
267 def __init__(self
, *args
, **kargs
):
269 moduleBase
.__init
__(self
, *args
, **kargs
)
271 if not os
.path
.exists('/sys/class/net/bonding_masters'):
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
))
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
),)
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"),
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')
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
)
298 def get_dependent_ifacenames(self
, ifaceobj
, ifacenames_all
=None, old_ifaceobjs
=False):
299 """ Returns list of interfaces dependent on ifaceobj """
301 if not self
._is
_bond
(ifaceobj
):
303 slave_list
= self
.parse_port_list(ifaceobj
.name
,
304 self
.get_bond_slaves(ifaceobj
),
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
314 if ifaceobj
.get_attr_value("es-sys-mac"):
315 ifaceobj
.link_privflags |
= ifaceLinkPrivFlags
.ES_BOND
319 def syntax_check(self
, ifaceobj
, ifaceobj_getfunc
):
320 return self
.syntax_check_updown_delay(ifaceobj
)
322 def get_dependent_ifacenames_running(self
, ifaceobj
):
323 return self
.cache
.get_slaves(ifaceobj
.name
)
325 def _get_slave_list(self
, ifaceobj
):
326 """ Returns slave list present in ifaceobj config """
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
)
333 return self
.parse_port_list(ifaceobj
.name
, slaves
)
337 def enable_ipv6_if_prev_brport(self
, ifname
):
339 If the intf was previously enslaved to a bridge it is possible ipv6 is still disabled.
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")
346 except Exception as e
:
347 self
.logger
.info(str(e
))
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':
356 def _add_slaves(self
, ifaceobj
, runningslaves
, ifaceobj_getfunc
=None):
357 slaves
= self
._get
_slave
_list
(ifaceobj
)
359 self
.logger
.debug('%s: no slaves found' %ifaceobj
.name
)
362 clag_bond
= self
._is
_clag
_bond
(ifaceobj
)
364 # remove duplicates and devices that are already enslaved
365 devices_to_enslave
= []
367 if s
not in runningslaves
and s
not in devices_to_enslave
:
368 devices_to_enslave
.append(s
)
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
,
378 if self
.cache
.link_is_up(slave
):
379 self
.netlink
.link_down_force(slave
)
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
:
386 self
.netlink
.link_set_protodown_on(slave
)
388 self
.iproute2
.link_set_protodown_reason_clag_on(slave
)
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
)))
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
:
401 if (ifaceobj_getfunc(slave
)[0].link_privflags
&
402 ifaceLinkPrivFlags
.KEEP_LINK_DOWN
):
403 self
.netlink
.link_down_force(slave
)
405 self
.netlink
.link_up_force(slave
)
406 except Exception as e
:
407 self
.logger
.debug('%s: %s' % (ifaceobj
.name
, str(e
)))
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
)
419 self
.sysfs
.bond_remove_slave(ifaceobj
.name
, s
)
420 removed_slave
.append(s
)
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
)
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 []:
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
)
443 # apply link-down config changes on running slaves
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
)))
455 for s
in removed_slave
:
457 runningslaves
.remove(s
)
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
470 def syntax_check_updown_delay(self
, ifaceobj
):
472 updelay
= ifaceobj
.get_attr_value_first('bond-updelay')
473 downdelay
= ifaceobj
.get_attr_value_first('bond-downdelay')
475 if not updelay
and not downdelay
:
479 miimon
= int(ifaceobj
.get_attr_value_first('bond-miimon'))
482 miimon
= int(policymanager
.policymanager_api
.get_iface_default(
483 module_name
=self
.__class
__.__name
__,
484 ifname
=ifaceobj
.name
,
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
)
498 _bond_updown_delay_nl_list
= (
499 (Link
.IFLA_BOND_UPDELAY
, 'bond-updelay'),
500 (Link
.IFLA_BOND_DOWNDELAY
, 'bond-downdelay')
503 def check_updown_delay_nl(self
, link_exists
, ifaceobj
, ifla_info_data
):
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.
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
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
)
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:
525 self
._check
_updown
_delay
_log
(ifaceobj
, attr_name
, delay
)
526 del ifla_info_data
[nl_attr
]
531 (Link
.IFLA_BOND_AD_LACP_RATE
, 'bond-lacp-rate'),
532 (Link
.IFLA_BOND_AD_LACP_BYPASS
, 'bond-lacp-bypass')
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
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
554 self
.logger
.warning('%s: attribute bond-min-links is set to \'0\'' % ifname
)
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
]
563 def get_saved_ifaceobj(link_exists
, ifname
):
565 old_config
= statemanager
.get_ifaceobjs(ifname
)
570 def get_ifla_bond_attr_from_user_config(self
, ifaceobj
, link_exists
):
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.
582 ifname
= ifaceobj
.name
583 ifla_info_data
= OrderedDict()
584 old_config
= self
.get_saved_ifaceobj(link_exists
, ifname
)
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
:
591 user_config
= ifaceobj
.get_attr_value_first(attr_name
)
594 user_config
= policymanager
.policymanager_api
.get_iface_default(
595 module_name
=self
.__class
__.__name
__,
599 self
.logger
.debug('%s: %s %s: extracted from policy files'
600 % (ifname
, attr_name
, user_config
))
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
)
609 self
.logger
.debug('%s: %s: removed from stanza, resetting to default value: %s'
610 % (ifname
, attr_name
, user_config
))
614 nl_value
= func_ptr(user_config
.lower())
617 cached_value
= self
.cache
.get_link_info_data_attribute(ifname
, netlink_attr
)
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
))
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
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
637 if cached_value
is not None:
638 self
.logger
.info('%s: set %s %s (cache %s)' % (ifname
, attr_name
, user_config
, cached_value
))
640 self
.logger
.info('%s: set %s %s' % (ifname
, attr_name
, user_config
))
643 self
.logger
.warning('%s: invalid %s value %s' % (ifname
, attr_name
, user_config
))
645 self
._check
_bond
_mode
_user
_config
(ifname
, link_exists
, ifla_info_data
)
646 return ifla_info_data
648 def check_miimon_arp(self
, link_exists
, ifaceobj
, ifla_info_data
):
650 Check bond checks either miimon or arp ip check
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
)
658 ifla_bond_miimon
= str(ifla_bond_miimon
)
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
)
665 ifla_bond_mode
= str(ifla_bond_mode
)
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
)
672 if ifla_arp_interval
:
673 ifla_arp_interval
= str(ifla_arp_interval
)
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
]
680 if ifla_arp_ip_target
:
681 ifla_info_data
[Link
.IFLA_BOND_ARP_IP_TARGET
] = ifla_arp_ip_target
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
]
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
]
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
]
700 return ifla_info_data
702 _bond_down_nl_attributes_list
= (
704 Link
.IFLA_BOND_XMIT_HASH_POLICY
,
705 Link
.IFLA_BOND_AD_LACP_RATE
,
706 Link
.IFLA_BOND_MIN_LINKS
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
:
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
)
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
))
727 self
.netlink
.link_down(ifname
)
730 for lower_dev
in ifaceobj
.lowerifaces
:
731 self
.netlink
.link_set_nomaster(lower_dev
)
733 # when unslaving a device from an ES bond we need to set
735 if ifaceobj
.link_privflags
& ifaceLinkPrivFlags
.ES_BOND
:
736 self
.netlink
.link_set_protodown_off(lower_dev
)
739 bond_slaves
.remove(lower_dev
)
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
]
748 return is_link_up
, bond_slaves
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
)
757 remove_delay_from_cache
= self
.check_updown_delay_nl(link_exists
, ifaceobj
, ifla_info_data
)
759 # if link exists: down link if specific attributes are specified
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
)
765 # did bond-mode changed?
766 is_link_up
, bond_slaves
= self
.should_update_bond_mode(
771 self
.cache
.get_slaves(ifname
)
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
)
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
)
788 self
.netlink
.link_add_bond_with_info_data(ifname
, ifla_master
, ifla_info_data
)
789 except Exception as e
:
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
)
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
801 if link_exists
and ifla_info_data
and not is_link_up
:
802 self
.netlink
.link_up_force(ifname
)
804 return link_exists
, bond_slaves
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
)
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
)
815 def _up(self
, ifaceobj
, ifaceobj_getfunc
=None):
817 link_exists
, bond_slaves
= self
.create_or_set_bond_config(ifaceobj
)
818 bond_slaves
= self
._add
_slaves
(
824 if not self
.bond_mac_mgmt
or not link_exists
or ifaceobj
.get_attr_value_first("hwaddress"):
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
849 # ifupdown2 will automatically correct and fix this unexpected behavior
850 bond_mac
= self
.cache
.get_link_address(ifaceobj
.name
)
853 first_slave_ifname
= bond_slaves
[0]
854 first_slave_mac
= self
.cache
.get_link_info_slave_data_attribute(
856 Link
.IFLA_BOND_SLAVE_PERM_HWADDR
859 if first_slave_mac
and bond_mac
!= first_slave_mac
:
861 "%s: invalid bond mac detected - resetting to %s's mac (%s)"
862 % (ifaceobj
.name
, first_slave_ifname
, first_slave_mac
)
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
)
868 def _down(self
, ifaceobj
, ifaceobj_getfunc
=None):
870 self
.netlink
.link_del(ifaceobj
.name
)
871 except Exception as e
:
872 self
.log_warn('%s: %s' % (ifaceobj
.name
, str(e
)))
874 def _query_check_bond_slaves(self
, ifaceobjcurr
, attr
, user_bond_slaves
, running_bond_slaves
):
877 if user_bond_slaves
and running_bond_slaves
:
878 if not set(user_bond_slaves
).symmetric_difference(running_bond_slaves
):
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
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
)
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
)
899 iface_attrs
= self
.dict_key_subset(ifaceobj
.config
, self
.get_mod_attrs())
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
908 user_bond_slaves
= None
909 running_bond_slaves
= None
911 del iface_attrs
[iface_attrs
.index('bond-slaves')]
913 # if user specified bond-slaves we need to display it
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
)
919 self
._query
_check
_bond
_slaves
(ifaceobjcurr
, 'bond-slaves', user_bond_slaves
, running_bond_slaves
)
923 del iface_attrs
[iface_attrs
.index('bond-ports')]
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
)
930 self
._query
_check
_bond
_slaves
(ifaceobjcurr
, 'bond-ports', user_bond_slaves
, running_bond_slaves
)
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
)
940 del iface_attrs
[iface_attrs
.index('bond-arp-ip-target')]
942 current_config
= [str(c
.ip
) for c
in current_config
]
943 user_config
= [str(u
) for u
in user_config
]
945 difference
= list(set(current_config
).symmetric_difference(user_config
))
946 intersection
= list(set(current_config
).intersection(user_config
))
948 for ip
in difference
:
949 ifaceobjcurr
.update_config_with_status(attr
, ip
, 1)
951 for ip
in intersection
:
952 ifaceobjcurr
.update_config_with_status(attr
, ip
, 0)
956 user_config_translate_func
= {
957 "es-sys-mac": lambda x
: str(x
).lower()
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
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
)
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
)
976 user_config
= user_config_f(ifaceobj
.get_attr_value_first(attr
))
978 user_config
= ifaceobj
.get_attr_value_first(attr
)
980 if current_config
== translate_func(user_config
):
981 ifaceobjcurr
.update_config_with_status(attr
, user_config
, 0)
983 ifaceobjcurr
.update_config_with_status(attr
, str(current_config
), 1)
986 def translate_nl_value_yesno(value
):
987 return 'yes' if value
else 'no'
990 def translate_nl_value_slowfast(value
):
991 return 'fast' if value
else 'slow'
993 def _query_running_attrs(self
, bondname
):
994 cached_vxlan_ifla_info_data
= self
.cache
.get_link_info_data(bondname
)
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
)
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
)
1019 slaves
= self
.cache
.get_slaves(bondname
)
1021 bond_attrs
['bond-slaves'] = slaves
1024 def _query_running(self
, ifaceobjrunning
, ifaceobj_getfunc
=None):
1025 if not self
.cache
.bond_exists(ifaceobjrunning
.name
):
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']
1035 [ifaceobjrunning
.update_config(k
, str(v
))
1036 for k
, v
in list(bond_attrs
.items())
1042 'query-running': _query_running
,
1043 'query-checkcurr': _query_check
1047 """ returns list of ops supported by this module """
1048 return list(self
._run
_ops
.keys())
1050 def run(self
, ifaceobj
, operation
, query_ifaceobj
=None,
1051 ifaceobj_getfunc
=None):
1052 """ run bond configuration on the interface object passed as argument
1055 **ifaceobj** (object): iface object
1057 **operation** (str): any of 'pre-up', 'post-down', 'query-checkcurr',
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.
1068 op_handler
= self
._run
_ops
.get(operation
)
1071 if operation
!= 'query-running' and not self
._is
_bond
(ifaceobj
):
1073 if operation
== 'query-checkcurr':
1074 op_handler(self
, ifaceobj
, query_ifaceobj
,
1075 ifaceobj_getfunc
=ifaceobj_getfunc
)
1077 op_handler(self
, ifaceobj
, ifaceobj_getfunc
=ifaceobj_getfunc
)