--- /dev/null
+#!/usr/bin/python
+#
+# Copyright 2014 Cumulus Networks, Inc. All rights reserved.
+# Author: Roopa Prabhu, roopa@cumulusnetworks.com
+#
+
+from sets import Set
+from ifupdown.iface import *
+import ifupdownaddons
+from ifupdownaddons.modulebase import moduleBase
+from ifupdownaddons.bondutil import bondutil
+from ifupdownaddons.iproute2 import iproute2
+import ifupdown.rtnetlink_api as rtnetlink_api
+import ifupdown.policymanager as policymanager
+
+class bond(moduleBase):
+ """ ifupdown2 addon module to configure bond interfaces """
+ _modinfo = { 'mhelp' : 'bond configuration module',
+ 'attrs' : {
+ 'bond-use-carrier':
+ {'help' : 'bond use carrier',
+ 'validvals' : ['0', '1'],
+ 'default' : '1',
+ 'example': ['bond-use-carrier 1']},
+ 'bond-num-grat-arp':
+ {'help' : 'bond use carrier',
+ 'validrange' : ['0', '255'],
+ 'default' : '1',
+ 'example' : ['bond-num-grat-arp 1']},
+ 'bond-num-unsol-na' :
+ {'help' : 'bond slave devices',
+ 'validrange' : ['0', '255'],
+ 'default' : '1',
+ 'example' : ['bond-num-unsol-na 1']},
+ 'bond-xmit-hash-policy' :
+ {'help' : 'bond slave devices',
+ 'validvals' : ['layer2', 'layer3+4', 'layer2+3'],
+ 'default' : 'layer2',
+ 'example' : ['bond-xmit-hash-policy layer2']},
+ 'bond-miimon' :
+ {'help' : 'bond miimon',
+ 'validrange' : ['0', '255'],
+ 'default' : '0',
+ 'example' : ['bond-miimon 0']},
+ 'bond-mode' :
+ {'help' : 'bond mode',
+ 'validvals' : ['balance-rr', 'active-backup',
+ 'balance-xor', 'broadcast', '802.3ad',
+ 'balance-tlb', 'balance-alb'],
+ 'default' : 'balance-rr',
+ 'example' : ['bond-mode 802.3ad']},
+ 'bond-lacp-rate':
+ {'help' : 'bond lacp rate',
+ 'validvals' : ['0', '1'],
+ 'default' : '0',
+ 'example' : ['bond-lacp-rate 0']},
+ 'bond-min-links':
+ {'help' : 'bond min links',
+ 'default' : '0',
+ 'example' : ['bond-min-links 0']},
+ 'bond-ad-sys-priority':
+ {'help' : '802.3ad system priority',
+ 'default' : '65535',
+ 'example' : ['bond-ad-sys-priority 65535']},
+ 'bond-ad-sys-mac-addr':
+ {'help' : '802.3ad system mac address',
+ 'default' : '00:00:00:00:00:00',
+ 'example' : ['bond-ad-sys-mac-addr 00:00:00:00:00:00']},
+ 'bond-lacp-fallback-allow':
+ {'help' : 'allow lacp fall back',
+ 'compat' : True,
+ 'validvals' : ['0', '1'],
+ 'default' : '0',
+ 'example' : ['bond-lacp-fallback-allow 0']},
+ 'bond-lacp-fallback-period':
+ {'help' : 'grace period (seconds) for lacp fall back',
+ 'compat' : True,
+ 'validrange' : ['0', '100'],
+ 'default' : '90',
+ 'example' : ['bond-lacp-fallback-period 100']},
+ 'bond-lacp-fallback-priority':
+ {'help' : 'slave priority for lacp fall back',
+ 'compat' : True,
+ 'example' : ['bond-lacp-fallback-priority swp1=1 swp2=1 swp3=2']},
+ 'bond-lacp-bypass-allow':
+ {'help' : 'allow lacp bypass',
+ 'validvals' : ['0', '1'],
+ 'default' : '0',
+ 'example' : ['bond-lacp-bypass-allow 0']},
+ 'bond-lacp-bypass-period':
+ {'help' : 'grace period (seconds) for lacp bypass',
+ 'validrange' : ['0', '900'],
+ 'default' : '0',
+ 'example' : ['bond-lacp-bypass-period 100']},
+ 'bond-lacp-bypass-priority':
+ {'help' : 'slave priority for lacp bypass',
+ 'example' : ['bond-lacp-bypass-priority swp1=1 swp2=1 swp3=2']},
+ 'bond-lacp-bypass-all-active':
+ {'help' : 'allow all slaves to be active in lacp bypass irrespective of priority',
+ 'validvals' : ['0', '1'],
+ 'default' : '0',
+ 'example' : ['bond-lacp-bypass-all-active 1']},
+ 'bond-slaves' :
+ {'help' : 'bond slaves',
+ 'required' : True,
+ 'example' : ['bond-slaves swp1 swp2',
+ 'bond-slaves glob swp1-2',
+ 'bond-slaves regex (swp[1|2)']}}}
+
+ def __init__(self, *args, **kargs):
+ ifupdownaddons.modulebase.moduleBase.__init__(self, *args, **kargs)
+ self.ipcmd = None
+ self.bondcmd = None
+
+ def _is_bond(self, ifaceobj):
+ if ifaceobj.get_attr_value_first('bond-slaves'):
+ return True
+ return False
+
+ def get_dependent_ifacenames(self, ifaceobj, ifacenames_all=None):
+ """ Returns list of interfaces dependent on ifaceobj """
+
+ if not self._is_bond(ifaceobj):
+ return None
+ slave_list = self.parse_port_list(ifaceobj.get_attr_value_first(
+ 'bond-slaves'), ifacenames_all)
+ ifaceobj.dependency_type = ifaceDependencyType.MASTER_SLAVE
+ # Also save a copy for future use
+ ifaceobj.priv_data = list(slave_list)
+ if ifaceobj.link_type != ifaceLinkType.LINK_NA:
+ ifaceobj.link_type = ifaceLinkType.LINK_MASTER
+ ifaceobj.link_kind |= ifaceLinkKind.BOND
+ ifaceobj.role |= ifaceRole.MASTER
+
+ return slave_list
+
+ def get_dependent_ifacenames_running(self, ifaceobj):
+ self._init_command_handlers()
+ return self.bondcmd.get_slaves(ifaceobj.name)
+
+ def _get_slave_list(self, ifaceobj):
+ """ Returns slave list present in ifaceobj config """
+
+ # If priv data already has slave list use that first.
+ if ifaceobj.priv_data:
+ return ifaceobj.priv_data
+ slaves = ifaceobj.get_attr_value_first('bond-slaves')
+ if slaves:
+ return self.parse_port_list(slaves)
+ else:
+ return None
+
+ def fetch_attr(self, ifaceobj, attrname):
+ attrval = ifaceobj.get_attr_value_first(attrname)
+ # grab the defaults from the policy file in case the
+ # user did not specify something.
+ policy_default_val = policymanager.policymanager_api.\
+ get_iface_default(module_name=self.__class__.__name__,
+ ifname=ifaceobj.name,
+ attr=attrname)
+ if attrval:
+ msg = ('%s: invalid value %s for attr %s.'
+ %(ifaceobj.name, attrval, attrname))
+ optiondict = self.get_mod_attr(attrname)
+ if not optiondict:
+ return None
+ validvals = optiondict.get('validvals')
+ if validvals and attrval not in validvals:
+ raise Exception(msg + ' Valid values are %s' %str(validvals))
+ validrange = optiondict.get('validrange')
+ if validrange:
+ if (int(attrval) < int(validrange[0]) or
+ int(attrval) > int(validrange[1])):
+ raise Exception(msg + ' Valid range is [%s,%s]'
+ %(validrange[0], validrange[1]))
+ if attrname == 'bond-mode' and attrval == '802.3ad':
+ dattrname = 'bond-min-links'
+ min_links = ifaceobj.get_attr_value_first(dattrname)
+ if not min_links or min_links == '0':
+ self.logger.warn('%s: required attribute %s'
+ %(ifaceobj.name, dattrname) +
+ ' not present or set to \'0\'')
+ elif policy_default_val:
+ return policy_default_val
+ elif attrname in ['bond-lacp-bypass-allow', 'bond-lacp-bypass-all-active', 'bond-lacp-bypass-period']:
+ # For some attrs, set default values
+ optiondict = self.get_mod_attr(attrname)
+ if optiondict:
+ return optiondict.get('default')
+ return attrval
+
+ def _apply_master_settings(self, ifaceobj):
+ have_attrs_to_set = 0
+ linkup = False
+ bondcmd_attrmap = OrderedDict([('bond-mode' , 'mode'),
+ ('bond-miimon' , 'miimon'),
+ ('bond-use-carrier', 'use_carrier'),
+ ('bond-lacp-rate' , 'lacp_rate'),
+ ('bond-xmit-hash-policy' , 'xmit_hash_policy'),
+ ('bond-min-links' , 'min_links'),
+ ('bond-num-grat-arp' , 'num_grat_arp'),
+ ('bond-num-unsol-na' , 'num_unsol_na'),
+ ('bond-ad-sys-mac-addr' , 'ad_sys_mac_addr'),
+ ('bond-ad-sys-priority' , 'ad_sys_priority'),
+ ('bond-lacp-fallback-allow', 'lacp_bypass_allow'),
+ ('bond-lacp-fallback-period', 'lacp_bypass_period'),
+ ('bond-lacp-bypass-allow', 'lacp_bypass_allow'),
+ ('bond-lacp-bypass-all-active', 'lacp_bypass_all_active'),
+ ('bond-lacp-bypass-period', 'lacp_bypass_period')])
+ linkup = self.ipcmd.is_link_up(ifaceobj.name)
+ try:
+ # order of attributes set matters for bond, so
+ # construct the list sequentially
+ attrstoset = OrderedDict()
+ for k, dstk in bondcmd_attrmap.items():
+ v = self.fetch_attr(ifaceobj, k)
+ if v:
+ attrstoset[dstk] = v
+ if not attrstoset:
+ return
+ have_attrs_to_set = 1
+ self.bondcmd.set_attrs(ifaceobj.name, attrstoset,
+ self.ipcmd.link_down if linkup else None)
+ except:
+ raise
+ finally:
+ if have_attrs_to_set and linkup:
+ self.ipcmd.link_up(ifaceobj.name)
+
+ def _add_slaves(self, ifaceobj):
+ runningslaves = []
+
+ slaves = self._get_slave_list(ifaceobj)
+ if not slaves:
+ self.logger.debug('%s: no slaves found' %ifaceobj.name)
+ return
+
+ if not self.PERFMODE:
+ runningslaves = self.bondcmd.get_slaves(ifaceobj.name);
+
+ for slave in Set(slaves).difference(Set(runningslaves)):
+ if not self.PERFMODE and not self.ipcmd.link_exists(slave):
+ self.log_warn('%s: skipping slave %s, does not exist'
+ %(ifaceobj.name, slave))
+ continue
+ link_up = False
+ if self.ipcmd.is_link_up(slave):
+ rtnetlink_api.rtnl_api.link_set(slave, "down")
+ link_up = True
+ self.ipcmd.link_set(slave, 'master', ifaceobj.name)
+ if link_up or ifaceobj.link_type != ifaceLinkType.LINK_NA:
+ try:
+ rtnetlink_api.rtnl_api.link_set(slave, "up")
+ except Exception, e:
+ self.logger.debug('%s: %s: link set up (%s)'
+ %(ifaceobj.name, slave, str(e)))
+ pass
+
+ if runningslaves:
+ # Delete active slaves not in the new slave list
+ [ self.bondcmd.remove_slave(ifaceobj.name, s)
+ for s in runningslaves if s not in slaves ]
+
+ def _set_clag_enable(self, ifaceobj):
+ attrval = ifaceobj.get_attr_value_first('clag-id')
+ attrval = attrval if attrval else '0'
+ self.bondcmd.set_clag_enable(ifaceobj.name, attrval)
+
+ def _apply_slaves_lacp_bypass_prio(self, ifaceobj):
+ slaves = self.bondcmd.get_slaves(ifaceobj.name)
+ if not slaves:
+ return
+ attrval = ifaceobj.get_attrs_value_first(['bond-lacp-bypass-priority',
+ 'bond-lacp-fallback-priority'])
+ if attrval:
+ portlist = self.parse_port_list(attrval)
+ if not portlist:
+ self.log_warn('%s: could not parse \'%s %s\''
+ %(ifaceobj.name, attrname, attrval))
+ return
+ for p in portlist:
+ try:
+ (port, val) = p.split('=')
+ if port not in slaves:
+ self.log_warn('%s: skipping slave %s, does not exist'
+ %(ifaceobj.name, port))
+ continue
+ slaves.remove(port)
+ self.bondcmd.set_lacp_fallback_priority(
+ ifaceobj.name, port, val)
+ except Exception, e:
+ self.log_warn('%s: failed to set lacp_fallback_priority %s (%s)'
+ %(ifaceobj.name, port, str(e)))
+
+ for p in slaves:
+ try:
+ self.bondcmd.set_lacp_fallback_priority(ifaceobj.name, p, '0')
+ except Exception, e:
+ self.log_warn('%s: failed to clear lacp_bypass_priority %s (%s)'
+ %(ifaceobj.name, p, str(e)))
+
+
+ def _up(self, ifaceobj):
+ try:
+ if not self.ipcmd.link_exists(ifaceobj.name):
+ self.bondcmd.create_bond(ifaceobj.name)
+ self._apply_master_settings(ifaceobj)
+ # clag_enable has to happen before the slaves are added to the bond
+ self._set_clag_enable(ifaceobj)
+ self._add_slaves(ifaceobj)
+ self._apply_slaves_lacp_bypass_prio(ifaceobj)
+ if ifaceobj.addr_method == 'manual':
+ rtnetlink_api.rtnl_api.link_set(ifaceobj.name, "up")
+ except Exception, e:
+ self.log_error(str(e))
+
+ def _down(self, ifaceobj):
+ try:
+ self.bondcmd.delete_bond(ifaceobj.name)
+ except Exception, e:
+ self.log_warn(str(e))
+
+ def _query_check(self, ifaceobj, ifaceobjcurr):
+ slaves = None
+
+ if not self.bondcmd.bond_exists(ifaceobj.name):
+ self.logger.debug('bond iface %s' %ifaceobj.name +
+ ' does not exist')
+ return
+
+ ifaceattrs = self.dict_key_subset(ifaceobj.config,
+ self.get_mod_attrs())
+ if not ifaceattrs: return
+ runningattrs = self._query_running_attrs(ifaceobj.name)
+
+ # backward compat change
+ runningattrs.update({'bond-lacp-fallback-allow': runningattrs.get(
+ 'bond-lacp-bypass-allow'),
+ 'bond-lacp-fallback-period': runningattrs.get(
+ 'bond-lacp-bypass-period'),
+ 'bond-lacp-fallback-priority': runningattrs.get(
+ 'bond-lacp-bypass-priority')})
+ for k in ifaceattrs:
+ v = ifaceobj.get_attr_value_first(k)
+ if not v:
+ continue
+ if k == 'bond-slaves':
+ slaves = self._get_slave_list(ifaceobj)
+ continue
+ rv = runningattrs.get(k)
+ if not rv:
+ ifaceobjcurr.update_config_with_status(k, 'None', 1)
+ else:
+ if (k == 'bond-lacp-bypass-priority' or
+ k == 'bond-lacp-fallback-priority'):
+ prios = v.split()
+ prios.sort()
+ prio_str = ' '.join(prios)
+ ifaceobjcurr.update_config_with_status(k, rv,
+ 1 if prio_str != rv else 0)
+ continue
+ ifaceobjcurr.update_config_with_status(k, rv,
+ 1 if v != rv else 0)
+ runningslaves = runningattrs.get('bond-slaves')
+ if not slaves and not runningslaves:
+ return
+ retslave = 1
+ if slaves and runningslaves:
+ if slaves and runningslaves:
+ difference = set(slaves).symmetric_difference(runningslaves)
+ if not difference:
+ retslave = 0
+ ifaceobjcurr.update_config_with_status('bond-slaves',
+ ' '.join(runningslaves)
+ if runningslaves else 'None', retslave)
+
+ def _query_running_attrs(self, bondname):
+ bondattrs = {'bond-mode' :
+ self.bondcmd.get_mode(bondname),
+ 'bond-miimon' :
+ self.bondcmd.get_miimon(bondname),
+ 'bond-use-carrier' :
+ self.bondcmd.get_use_carrier(bondname),
+ 'bond-lacp-rate' :
+ self.bondcmd.get_lacp_rate(bondname),
+ 'bond-min-links' :
+ self.bondcmd.get_min_links(bondname),
+ 'bond-ad-sys-mac-addr' :
+ self.bondcmd.get_ad_sys_mac_addr(bondname),
+ 'bond-ad-sys-priority' :
+ self.bondcmd.get_ad_sys_priority(bondname),
+ 'bond-xmit-hash-policy' :
+ self.bondcmd.get_xmit_hash_policy(bondname),
+ 'bond-lacp-bypass-allow' :
+ self.bondcmd.get_lacp_fallback_allow(bondname),
+ 'bond-lacp-bypass-period' :
+ self.bondcmd.get_lacp_fallback_period(bondname),
+ 'bond-lacp-bypass-priority' :
+ self.bondcmd.get_lacp_fallback_priority(bondname),
+ 'bond-lacp-bypass-all-active' :
+ self.bondcmd.get_lacp_fallback_all_active(bondname)}
+ slaves = self.bondcmd.get_slaves(bondname)
+ if slaves:
+ bondattrs['bond-slaves'] = slaves
+ return bondattrs
+
+ def _query_running(self, ifaceobjrunning):
+ if not self.bondcmd.bond_exists(ifaceobjrunning.name):
+ return
+ bondattrs = self._query_running_attrs(ifaceobjrunning.name)
+ if bondattrs.get('bond-slaves'):
+ bondattrs['bond-slaves'] = ' '.join(bondattrs.get('bond-slaves'))
+ [ifaceobjrunning.update_config(k, v)
+ for k, v in bondattrs.items()
+ if v and v != self.get_mod_subattr(k, 'default')]
+
+ _run_ops = {'pre-up' : _up,
+ 'post-down' : _down,
+ 'query-running' : _query_running,
+ 'query-checkcurr' : _query_check}
+
+ def get_ops(self):
+ """ returns list of ops supported by this module """
+ return self._run_ops.keys()
+
+ def _init_command_handlers(self):
+ flags = self.get_flags()
+ if not self.ipcmd:
+ self.ipcmd = iproute2(**flags)
+ if not self.bondcmd:
+ self.bondcmd = bondutil(**flags)
+
+ def run(self, ifaceobj, operation, query_ifaceobj=None, **extra_args):
+ """ run bond configuration on the interface object passed as argument
+
+ Args:
+ **ifaceobj** (object): iface object
+
+ **operation** (str): any of 'pre-up', 'post-down', 'query-checkcurr',
+ 'query-running'
+
+ Kwargs:
+ **query_ifaceobj** (object): query check ifaceobject. This is only
+ valid when op is 'query-checkcurr'. It is an object same as
+ ifaceobj, but contains running attribute values and its config
+ status. The modules can use it to return queried running state
+ of interfaces. status is success if the running state is same
+ as user required state in ifaceobj. error otherwise.
+ """
+ op_handler = self._run_ops.get(operation)
+ if not op_handler:
+ return
+ if operation != 'query-running' and not self._is_bond(ifaceobj):
+ return
+ self._init_command_handlers()
+ if operation == 'query-checkcurr':
+ op_handler(self, ifaceobj, query_ifaceobj)
+ else:
+ op_handler(self, ifaceobj)
+++ /dev/null
-#!/usr/bin/python
-#
-# Copyright 2014 Cumulus Networks, Inc. All rights reserved.
-# Author: Roopa Prabhu, roopa@cumulusnetworks.com
-#
-
-from sets import Set
-from ifupdown.iface import *
-import ifupdownaddons
-from ifupdownaddons.modulebase import moduleBase
-from ifupdownaddons.ifenslaveutil import ifenslaveutil
-from ifupdownaddons.iproute2 import iproute2
-import ifupdown.rtnetlink_api as rtnetlink_api
-
-class ifenslave(moduleBase):
- """ ifupdown2 addon module to configure bond interfaces """
- _modinfo = { 'mhelp' : 'bond configuration module',
- 'attrs' : {
- 'bond-use-carrier':
- {'help' : 'bond use carrier',
- 'validvals' : ['0', '1'],
- 'default' : '1',
- 'example': ['bond-use-carrier 1']},
- 'bond-num-grat-arp':
- {'help' : 'bond use carrier',
- 'validrange' : ['0', '255'],
- 'default' : '1',
- 'example' : ['bond-num-grat-arp 1']},
- 'bond-num-unsol-na' :
- {'help' : 'bond slave devices',
- 'validrange' : ['0', '255'],
- 'default' : '1',
- 'example' : ['bond-num-unsol-na 1']},
- 'bond-xmit-hash-policy' :
- {'help' : 'bond slave devices',
- 'validvals' : ['layer2', 'layer3+4', 'layer2+3'],
- 'default' : 'layer2',
- 'example' : ['bond-xmit-hash-policy layer2']},
- 'bond-miimon' :
- {'help' : 'bond miimon',
- 'validrange' : ['0', '255'],
- 'default' : '0',
- 'example' : ['bond-miimon 0']},
- 'bond-mode' :
- {'help' : 'bond mode',
- 'validvals' : ['balance-rr', 'active-backup',
- 'balance-xor', 'broadcast', '802.3ad',
- 'balance-tlb', 'balance-alb'],
- 'default' : 'balance-rr',
- 'example' : ['bond-mode 802.3ad']},
- 'bond-lacp-rate':
- {'help' : 'bond lacp rate',
- 'validvals' : ['0', '1'],
- 'default' : '0',
- 'example' : ['bond-lacp-rate 0']},
- 'bond-min-links':
- {'help' : 'bond min links',
- 'default' : '0',
- 'example' : ['bond-min-links 0']},
- 'bond-ad-sys-priority':
- {'help' : '802.3ad system priority',
- 'default' : '65535',
- 'example' : ['bond-ad-sys-priority 65535']},
- 'bond-ad-sys-mac-addr':
- {'help' : '802.3ad system mac address',
- 'default' : '00:00:00:00:00:00',
- 'example' : ['bond-ad-sys-mac-addr 00:00:00:00:00:00']},
- 'bond-lacp-fallback-allow':
- {'help' : 'allow lacp fall back',
- 'compat' : True,
- 'validvals' : ['0', '1'],
- 'default' : '0',
- 'example' : ['bond-lacp-fallback-allow 0']},
- 'bond-lacp-fallback-period':
- {'help' : 'grace period (seconds) for lacp fall back',
- 'compat' : True,
- 'validrange' : ['0', '100'],
- 'default' : '90',
- 'example' : ['bond-lacp-fallback-period 100']},
- 'bond-lacp-fallback-priority':
- {'help' : 'slave priority for lacp fall back',
- 'compat' : True,
- 'example' : ['bond-lacp-fallback-priority swp1=1 swp2=1 swp3=2']},
- 'bond-lacp-bypass-allow':
- {'help' : 'allow lacp bypass',
- 'validvals' : ['0', '1'],
- 'default' : '0',
- 'example' : ['bond-lacp-bypass-allow 0']},
- 'bond-lacp-bypass-period':
- {'help' : 'grace period (seconds) for lacp bypass',
- 'validrange' : ['0', '900'],
- 'default' : '0',
- 'example' : ['bond-lacp-bypass-period 100']},
- 'bond-lacp-bypass-priority':
- {'help' : 'slave priority for lacp bypass',
- 'example' : ['bond-lacp-bypass-priority swp1=1 swp2=1 swp3=2']},
- 'bond-lacp-bypass-all-active':
- {'help' : 'allow all slaves to be active in lacp bypass irrespective of priority',
- 'validvals' : ['0', '1'],
- 'default' : '0',
- 'example' : ['bond-lacp-bypass-all-active 1']},
- 'bond-slaves' :
- {'help' : 'bond slaves',
- 'required' : True,
- 'example' : ['bond-slaves swp1 swp2',
- 'bond-slaves glob swp1-2',
- 'bond-slaves regex (swp[1|2)']}}}
-
- def __init__(self, *args, **kargs):
- ifupdownaddons.modulebase.moduleBase.__init__(self, *args, **kargs)
- self.ipcmd = None
- self.ifenslavecmd = None
-
- def _is_bond(self, ifaceobj):
- if ifaceobj.get_attr_value_first('bond-slaves'):
- return True
- return False
-
- def get_dependent_ifacenames(self, ifaceobj, ifacenames_all=None):
- """ Returns list of interfaces dependent on ifaceobj """
-
- if not self._is_bond(ifaceobj):
- return None
- slave_list = self.parse_port_list(ifaceobj.get_attr_value_first(
- 'bond-slaves'), ifacenames_all)
- ifaceobj.dependency_type = ifaceDependencyType.MASTER_SLAVE
- # Also save a copy for future use
- ifaceobj.priv_data = list(slave_list)
- if ifaceobj.link_type != ifaceLinkType.LINK_NA:
- ifaceobj.link_type = ifaceLinkType.LINK_MASTER
- ifaceobj.link_kind |= ifaceLinkKind.BOND
- ifaceobj.role |= ifaceRole.MASTER
-
- return slave_list
-
- def get_dependent_ifacenames_running(self, ifaceobj):
- self._init_command_handlers()
- return self.ifenslavecmd.get_slaves(ifaceobj.name)
-
- def _get_slave_list(self, ifaceobj):
- """ Returns slave list present in ifaceobj config """
-
- # If priv data already has slave list use that first.
- if ifaceobj.priv_data:
- return ifaceobj.priv_data
- slaves = ifaceobj.get_attr_value_first('bond-slaves')
- if slaves:
- return self.parse_port_list(slaves)
- else:
- return None
-
- def fetch_attr(self, ifaceobj, attrname):
- attrval = ifaceobj.get_attr_value_first(attrname)
- if attrval:
- msg = ('%s: invalid value %s for attr %s.'
- %(ifaceobj.name, attrval, attrname))
- optiondict = self.get_mod_attr(attrname)
- if not optiondict:
- return None
- validvals = optiondict.get('validvals')
- if validvals and attrval not in validvals:
- raise Exception(msg + ' Valid values are %s' %str(validvals))
- validrange = optiondict.get('validrange')
- if validrange:
- if (int(attrval) < int(validrange[0]) or
- int(attrval) > int(validrange[1])):
- raise Exception(msg + ' Valid range is [%s,%s]'
- %(validrange[0], validrange[1]))
- if attrname == 'bond-mode' and attrval == '802.3ad':
- dattrname = 'bond-min-links'
- min_links = ifaceobj.get_attr_value_first(dattrname)
- if not min_links or min_links == '0':
- self.logger.warn('%s: required attribute %s'
- %(ifaceobj.name, dattrname) +
- ' not present or set to \'0\'')
- elif attrname in ['bond-lacp-bypass-allow', 'bond-lacp-bypass-all-active', 'bond-lacp-bypass-period']:
- # For some attrs, set default values
- optiondict = self.get_mod_attr(attrname)
- if optiondict:
- return optiondict.get('default')
- return attrval
-
- def _apply_master_settings(self, ifaceobj):
- have_attrs_to_set = 0
- linkup = False
- ifenslavecmd_attrmap = OrderedDict([('bond-mode' , 'mode'),
- ('bond-miimon' , 'miimon'),
- ('bond-use-carrier', 'use_carrier'),
- ('bond-lacp-rate' , 'lacp_rate'),
- ('bond-xmit-hash-policy' , 'xmit_hash_policy'),
- ('bond-min-links' , 'min_links'),
- ('bond-num-grat-arp' , 'num_grat_arp'),
- ('bond-num-unsol-na' , 'num_unsol_na'),
- ('bond-ad-sys-mac-addr' , 'ad_sys_mac_addr'),
- ('bond-ad-sys-priority' , 'ad_sys_priority'),
- ('bond-lacp-fallback-allow', 'lacp_bypass_allow'),
- ('bond-lacp-fallback-period', 'lacp_bypass_period'),
- ('bond-lacp-bypass-allow', 'lacp_bypass_allow'),
- ('bond-lacp-bypass-all-active', 'lacp_bypass_all_active'),
- ('bond-lacp-bypass-period', 'lacp_bypass_period')])
- linkup = self.ipcmd.is_link_up(ifaceobj.name)
- try:
- # order of attributes set matters for bond, so
- # construct the list sequentially
- attrstoset = OrderedDict()
- for k, dstk in ifenslavecmd_attrmap.items():
- v = self.fetch_attr(ifaceobj, k)
- if v:
- attrstoset[dstk] = v
- if not attrstoset:
- return
- have_attrs_to_set = 1
- self.ifenslavecmd.set_attrs(ifaceobj.name, attrstoset,
- self.ipcmd.link_down if linkup else None)
- except:
- raise
- finally:
- if have_attrs_to_set and linkup:
- self.ipcmd.link_up(ifaceobj.name)
-
- def _add_slaves(self, ifaceobj):
- runningslaves = []
-
- slaves = self._get_slave_list(ifaceobj)
- if not slaves:
- self.logger.debug('%s: no slaves found' %ifaceobj.name)
- return
-
- if not self.PERFMODE:
- runningslaves = self.ifenslavecmd.get_slaves(ifaceobj.name);
-
- for slave in Set(slaves).difference(Set(runningslaves)):
- if not self.PERFMODE and not self.ipcmd.link_exists(slave):
- self.log_warn('%s: skipping slave %s, does not exist'
- %(ifaceobj.name, slave))
- continue
- link_up = False
- if self.ipcmd.is_link_up(slave):
- rtnetlink_api.rtnl_api.link_set(slave, "down")
- link_up = True
- self.ipcmd.link_set(slave, 'master', ifaceobj.name)
- if link_up or ifaceobj.link_type != ifaceLinkType.LINK_NA:
- try:
- rtnetlink_api.rtnl_api.link_set(slave, "up")
- except Exception, e:
- self.logger.debug('%s: %s: link set up (%s)'
- %(ifaceobj.name, slave, str(e)))
- pass
-
- if runningslaves:
- # Delete active slaves not in the new slave list
- [ self.ifenslavecmd.remove_slave(ifaceobj.name, s)
- for s in runningslaves if s not in slaves ]
-
- def _set_clag_enable(self, ifaceobj):
- attrval = ifaceobj.get_attr_value_first('clag-id')
- attrval = attrval if attrval else '0'
- self.ifenslavecmd.set_clag_enable(ifaceobj.name, attrval)
-
- def _apply_slaves_lacp_bypass_prio(self, ifaceobj):
- slaves = self.ifenslavecmd.get_slaves(ifaceobj.name)
- if not slaves:
- return
- attrval = ifaceobj.get_attrs_value_first(['bond-lacp-bypass-priority',
- 'bond-lacp-fallback-priority'])
- if attrval:
- portlist = self.parse_port_list(attrval)
- if not portlist:
- self.log_warn('%s: could not parse \'%s %s\''
- %(ifaceobj.name, attrname, attrval))
- return
- for p in portlist:
- try:
- (port, val) = p.split('=')
- if port not in slaves:
- self.log_warn('%s: skipping slave %s, does not exist'
- %(ifaceobj.name, port))
- continue
- slaves.remove(port)
- self.ifenslavecmd.set_lacp_fallback_priority(
- ifaceobj.name, port, val)
- except Exception, e:
- self.log_warn('%s: failed to set lacp_fallback_priority %s (%s)'
- %(ifaceobj.name, port, str(e)))
-
- for p in slaves:
- try:
- self.ifenslavecmd.set_lacp_fallback_priority(ifaceobj.name, p, '0')
- except Exception, e:
- self.log_warn('%s: failed to clear lacp_bypass_priority %s (%s)'
- %(ifaceobj.name, p, str(e)))
-
-
- def _up(self, ifaceobj):
- try:
- if not self.ipcmd.link_exists(ifaceobj.name):
- self.ifenslavecmd.create_bond(ifaceobj.name)
- self._apply_master_settings(ifaceobj)
- # clag_enable has to happen before the slaves are added to the bond
- self._set_clag_enable(ifaceobj)
- self._add_slaves(ifaceobj)
- self._apply_slaves_lacp_bypass_prio(ifaceobj)
- if ifaceobj.addr_method == 'manual':
- rtnetlink_api.rtnl_api.link_set(ifaceobj.name, "up")
- except Exception, e:
- self.log_error(str(e))
-
- def _down(self, ifaceobj):
- try:
- self.ifenslavecmd.delete_bond(ifaceobj.name)
- except Exception, e:
- self.log_warn(str(e))
-
- def _query_check(self, ifaceobj, ifaceobjcurr):
- slaves = None
-
- if not self.ifenslavecmd.bond_exists(ifaceobj.name):
- self.logger.debug('bond iface %s' %ifaceobj.name +
- ' does not exist')
- return
-
- ifaceattrs = self.dict_key_subset(ifaceobj.config,
- self.get_mod_attrs())
- if not ifaceattrs: return
- runningattrs = self._query_running_attrs(ifaceobj.name)
-
- # backward compat change
- runningattrs.update({'bond-lacp-fallback-allow': runningattrs.get(
- 'bond-lacp-bypass-allow'),
- 'bond-lacp-fallback-period': runningattrs.get(
- 'bond-lacp-bypass-period'),
- 'bond-lacp-fallback-priority': runningattrs.get(
- 'bond-lacp-bypass-priority')})
- for k in ifaceattrs:
- v = ifaceobj.get_attr_value_first(k)
- if not v:
- continue
- if k == 'bond-slaves':
- slaves = self._get_slave_list(ifaceobj)
- continue
- rv = runningattrs.get(k)
- if not rv:
- ifaceobjcurr.update_config_with_status(k, 'None', 1)
- else:
- if (k == 'bond-lacp-bypass-priority' or
- k == 'bond-lacp-fallback-priority'):
- prios = v.split()
- prios.sort()
- prio_str = ' '.join(prios)
- ifaceobjcurr.update_config_with_status(k, rv,
- 1 if prio_str != rv else 0)
- continue
- ifaceobjcurr.update_config_with_status(k, rv,
- 1 if v != rv else 0)
- runningslaves = runningattrs.get('bond-slaves')
- if not slaves and not runningslaves:
- return
- retslave = 1
- if slaves and runningslaves:
- if slaves and runningslaves:
- difference = set(slaves).symmetric_difference(runningslaves)
- if not difference:
- retslave = 0
- ifaceobjcurr.update_config_with_status('bond-slaves',
- ' '.join(runningslaves)
- if runningslaves else 'None', retslave)
-
- def _query_running_attrs(self, bondname):
- bondattrs = {'bond-mode' :
- self.ifenslavecmd.get_mode(bondname),
- 'bond-miimon' :
- self.ifenslavecmd.get_miimon(bondname),
- 'bond-use-carrier' :
- self.ifenslavecmd.get_use_carrier(bondname),
- 'bond-lacp-rate' :
- self.ifenslavecmd.get_lacp_rate(bondname),
- 'bond-min-links' :
- self.ifenslavecmd.get_min_links(bondname),
- 'bond-ad-sys-mac-addr' :
- self.ifenslavecmd.get_ad_sys_mac_addr(bondname),
- 'bond-ad-sys-priority' :
- self.ifenslavecmd.get_ad_sys_priority(bondname),
- 'bond-xmit-hash-policy' :
- self.ifenslavecmd.get_xmit_hash_policy(bondname),
- 'bond-lacp-bypass-allow' :
- self.ifenslavecmd.get_lacp_fallback_allow(bondname),
- 'bond-lacp-bypass-period' :
- self.ifenslavecmd.get_lacp_fallback_period(bondname),
- 'bond-lacp-bypass-priority' :
- self.ifenslavecmd.get_lacp_fallback_priority(bondname),
- 'bond-lacp-bypass-all-active' :
- self.ifenslavecmd.get_lacp_fallback_all_active(bondname)}
- slaves = self.ifenslavecmd.get_slaves(bondname)
- if slaves:
- bondattrs['bond-slaves'] = slaves
- return bondattrs
-
- def _query_running(self, ifaceobjrunning):
- if not self.ifenslavecmd.bond_exists(ifaceobjrunning.name):
- return
- bondattrs = self._query_running_attrs(ifaceobjrunning.name)
- if bondattrs.get('bond-slaves'):
- bondattrs['bond-slaves'] = ' '.join(bondattrs.get('bond-slaves'))
- [ifaceobjrunning.update_config(k, v)
- for k, v in bondattrs.items()
- if v and v != self.get_mod_subattr(k, 'default')]
-
- _run_ops = {'pre-up' : _up,
- 'post-down' : _down,
- 'query-running' : _query_running,
- 'query-checkcurr' : _query_check}
-
- def get_ops(self):
- """ returns list of ops supported by this module """
- return self._run_ops.keys()
-
- def _init_command_handlers(self):
- flags = self.get_flags()
- if not self.ipcmd:
- self.ipcmd = iproute2(**flags)
- if not self.ifenslavecmd:
- self.ifenslavecmd = ifenslaveutil(**flags)
-
- def run(self, ifaceobj, operation, query_ifaceobj=None, **extra_args):
- """ run bond configuration on the interface object passed as argument
-
- Args:
- **ifaceobj** (object): iface object
-
- **operation** (str): any of 'pre-up', 'post-down', 'query-checkcurr',
- 'query-running'
-
- Kwargs:
- **query_ifaceobj** (object): query check ifaceobject. This is only
- valid when op is 'query-checkcurr'. It is an object same as
- ifaceobj, but contains running attribute values and its config
- status. The modules can use it to return queried running state
- of interfaces. status is success if the running state is same
- as user required state in ifaceobj. error otherwise.
- """
- op_handler = self._run_ops.get(operation)
- if not op_handler:
- return
- if operation != 'query-running' and not self._is_bond(ifaceobj):
- return
- self._init_command_handlers()
- if operation == 'query-checkcurr':
- op_handler(self, ifaceobj, query_ifaceobj)
- else:
- op_handler(self, ifaceobj)
-pre-up,ifenslave
+pre-up,bond
pre-up,vlan
pre-up,vxlan
pre-up,clagd
post-down,bridge
post-down,vxlan
post-down,vlan
-post-down,ifenslave
+post-down,bond
post-down,usercmds
.. autoclass:: ethtool
-ifenslave
-=========
+bond
+====
-.. automodule:: ifenslave
+.. automodule:: bond
-.. autoclass:: ifenslave
+.. autoclass:: bond
mstpctl
=======
.. autoclass:: brctl
-ifenslaveutil
-=============
+bondutil
+========
Helper module to interact with linux api to create bonds.
Currently this is via sysfs.
-.. automodule:: ifenslaveutil
+.. automodule:: bondutil
-.. autoclass:: ifenslaveutil
+.. autoclass:: bondutil
dhclient
========
--- /dev/null
+#!/usr/bin/python
+#
+# Copyright 2014 Cumulus Networks, Inc. All rights reserved.
+# Author: Roopa Prabhu, roopa@cumulusnetworks.com
+#
+
+import os
+import re
+from ifupdown.iface import *
+from utilsbase import *
+from iproute2 import *
+from cache import *
+
+class bondutil(utilsBase):
+ """ This class contains methods to interact with linux kernel bond
+ related interfaces """
+
+ _cache_fill_done = False
+
+ def __init__(self, *args, **kargs):
+ utilsBase.__init__(self, *args, **kargs)
+ if self.CACHE and not self._cache_fill_done:
+ self._bond_linkinfo_fill_all()
+ self._cache_fill_done = True
+
+ def _bond_linkinfo_fill_attrs(self, bondname):
+ try:
+ linkCache.links[bondname]['linkinfo'] = {}
+ except:
+ linkCache.links[bondname] = {'linkinfo': {}}
+
+ try:
+ linkCache.set_attr([bondname, 'linkinfo', 'slaves'],
+ self.read_file_oneline('/sys/class/net/%s/bonding/slaves'
+ %bondname).split())
+ linkCache.set_attr([bondname, 'linkinfo', 'mode'],
+ self.read_file_oneline('/sys/class/net/%s/bonding/mode'
+ %bondname).split()[0])
+ linkCache.set_attr([bondname, 'linkinfo', 'xmit_hash_policy'],
+ self.read_file_oneline(
+ '/sys/class/net/%s/bonding/xmit_hash_policy'
+ %bondname).split()[0])
+ linkCache.set_attr([bondname, 'linkinfo', 'lacp_rate'],
+ self.read_file_oneline('/sys/class/net/%s/bonding/lacp_rate'
+ %bondname).split()[1])
+ linkCache.set_attr([bondname, 'linkinfo', 'ad_sys_priority'],
+ self.read_file_oneline('/sys/class/net/%s/bonding/ad_sys_priority'
+ %bondname))
+ linkCache.set_attr([bondname, 'linkinfo', 'ad_sys_mac_addr'],
+ self.read_file_oneline('/sys/class/net/%s/bonding/ad_sys_mac_addr'
+ %bondname))
+ map(lambda x: linkCache.set_attr([bondname, 'linkinfo', x],
+ self.read_file_oneline('/sys/class/net/%s/bonding/%s'
+ %(bondname, x))),
+ ['use_carrier', 'miimon', 'min_links', 'num_unsol_na',
+ 'num_grat_arp', 'lacp_bypass_allow', 'lacp_bypass_period',
+ 'lacp_bypass_all_active', 'clag_enable'])
+ except Exception, e:
+ pass
+
+ def _bond_linkinfo_fill_all(self):
+ bondstr = self.read_file_oneline('/sys/class/net/bonding_masters')
+ if not bondstr:
+ return
+ [self._bond_linkinfo_fill_attrs(b) for b in bondstr.split()]
+
+ def _bond_linkinfo_fill(self, bondname, refresh=False):
+ try:
+ linkCache.get_attr([bondname, 'linkinfo', 'slaves'])
+ return
+ except:
+ pass
+ bondstr = self.read_file_oneline('/sys/class/net/bonding_masters')
+ if (not bondstr or bondname not in bondstr.split()):
+ raise Exception('bond %s not found' %bondname)
+ self._bond_linkinfo_fill_attrs(bondname)
+
+ def _cache_get(self, attrlist, refresh=False):
+ try:
+ if self.DRYRUN:
+ return None
+ if self.CACHE:
+ if not bondutil._cache_fill_done:
+ self._bond_linkinfo_fill_all()
+ bondutil._cache_fill_done = True
+ return linkCache.get_attr(attrlist)
+ if not refresh:
+ return linkCache.get_attr(attrlist)
+ self._bond_linkinfo_fill(attrlist[0], refresh)
+ return linkCache.get_attr(attrlist)
+ except Exception, e:
+ self.logger.debug('_cache_get(%s) : [%s]'
+ %(str(attrlist), str(e)))
+ pass
+ return None
+
+ def _cache_check(self, attrlist, value, refresh=False):
+ try:
+ attrvalue = self._cache_get(attrlist, refresh)
+ if attrvalue and attrvalue == value:
+ return True
+ except Exception, e:
+ self.logger.debug('_cache_check(%s) : [%s]'
+ %(str(attrlist), str(e)))
+ pass
+ return False
+
+ def _cache_update(self, attrlist, value):
+ if self.DRYRUN: return
+ try:
+ if attrlist[-1] == 'slaves':
+ linkCache.add_to_attrlist(attrlist, value)
+ return
+ linkCache.add_attr(attrlist, value)
+ except:
+ pass
+
+ def _cache_delete(self, attrlist, value=None):
+ if self.DRYRUN: return
+ try:
+ if attrlist[-1] == 'slaves':
+ linkCache.remove_from_attrlist(attrlist, value)
+ return
+ linkCache.del_attr(attrlist)
+ except:
+ pass
+
+ def _cache_invalidate(self):
+ if self.DRYRUN: return
+ linkCache.invalidate()
+
+ def set_attrs(self, bondname, attrdict, prehook):
+ for attrname, attrval in attrdict.items():
+ if (self._cache_check([bondname, 'linkinfo',
+ attrname], attrval)):
+ continue
+ if (attrname == 'mode' or attrname == 'xmit_hash_policy' or
+ attrname == 'lacp_rate' or attrname == 'min_links'):
+ if prehook:
+ prehook(bondname)
+ try:
+ self.write_file('/sys/class/net/%s/bonding/%s'
+ %(bondname, attrname), attrval)
+ except Exception, e:
+ if self.FORCE:
+ self.logger.warn(str(e))
+ pass
+ else:
+ raise
+
+ def set_use_carrier(self, bondname, use_carrier):
+ if not use_carrier or (use_carrier != '0' and use_carrier != '1'):
+ return
+ if (self._cache_check([bondname, 'linkinfo', 'use_carrier'],
+ use_carrier)):
+ return
+ self.write_file('/sys/class/net/%s' %bondname +
+ '/bonding/use_carrier', use_carrier)
+ self._cache_update([bondname, 'linkinfo',
+ 'use_carrier'], use_carrier)
+
+ def get_use_carrier(self, bondname):
+ return self._cache_get([bondname, 'linkinfo', 'use_carrier'])
+
+ def set_xmit_hash_policy(self, bondname, hash_policy, prehook=None):
+ valid_values = ['layer2', 'layer3+4', 'layer2+3']
+ if not hash_policy:
+ return
+ if hash_policy not in valid_values:
+ raise Exception('invalid hash policy value %s' %hash_policy)
+ if (self._cache_check([bondname, 'linkinfo', 'xmit_hash_policy'],
+ hash_policy)):
+ return
+ if prehook:
+ prehook(bondname)
+ self.write_file('/sys/class/net/%s' %bondname +
+ '/bonding/xmit_hash_policy', hash_policy)
+ self._cache_update([bondname, 'linkinfo', 'xmit_hash_policy'],
+ hash_policy)
+
+ def get_xmit_hash_policy(self, bondname):
+ return self._cache_get([bondname, 'linkinfo', 'xmit_hash_policy'])
+
+ def set_miimon(self, bondname, miimon):
+ if (self._cache_check([bondname, 'linkinfo', 'miimon'],
+ miimon)):
+ return
+ self.write_file('/sys/class/net/%s' %bondname +
+ '/bonding/miimon', miimon)
+ self._cache_update([bondname, 'linkinfo', 'miimon'], miimon)
+
+ def get_miimon(self, bondname):
+ return self._cache_get([bondname, 'linkinfo', 'miimon'])
+
+ def set_clag_enable(self, bondname, clag_id):
+ clag_enable = '0' if clag_id == '0' else '1'
+ if self._cache_check([bondname, 'linkinfo', 'clag_enable'],
+ clag_enable) == False:
+ self.write_file('/sys/class/net/%s' %bondname +
+ '/bonding/clag_enable', clag_enable)
+ self._cache_update([bondname, 'linkinfo', 'clag_enable'],
+ clag_enable)
+
+ def get_clag_enable(self, bondname):
+ return self._cache_get([bondname, 'linkinfo', 'clag_enable'])
+
+ def set_mode(self, bondname, mode, prehook=None):
+ valid_modes = ['balance-rr', 'active-backup', 'balance-xor',
+ 'broadcast', '802.3ad', 'balance-tlb', 'balance-alb']
+ if not mode:
+ return
+ if mode not in valid_modes:
+ raise Exception('invalid mode %s' %mode)
+ if (self._cache_check([bondname, 'linkinfo', 'mode'],
+ mode)):
+ return
+ if prehook:
+ prehook(bondname)
+ self.write_file('/sys/class/net/%s' %bondname + '/bonding/mode', mode)
+ self._cache_update([bondname, 'linkinfo', 'mode'], mode)
+
+ def get_mode(self, bondname):
+ return self._cache_get([bondname, 'linkinfo', 'mode'])
+
+ def set_lacp_rate(self, bondname, lacp_rate, prehook=None, posthook=None):
+ if not lacp_rate or (lacp_rate != '0' and lacp_rate != '1'):
+ return
+ if (self._cache_check([bondname, 'linkinfo', 'lacp_rate'],
+ lacp_rate)):
+ return
+ if prehook:
+ prehook(bondname)
+ try:
+ self.write_file('/sys/class/net/%s' %bondname +
+ '/bonding/lacp_rate', lacp_rate)
+ except:
+ raise
+ finally:
+ if posthook:
+ prehook(bondname)
+ self._cache_update([bondname, 'linkinfo',
+ 'lacp_rate'], lacp_rate)
+
+ def get_lacp_rate(self, bondname):
+ return self._cache_get([bondname, 'linkinfo', 'lacp_rate'])
+
+ def set_lacp_fallback_allow(self, bondname, allow, prehook=None, posthook=None):
+ if (self._cache_check([bondname, 'linkinfo', 'lacp_bypass_allow'],
+ lacp_bypass_allow)):
+ return
+ if prehook:
+ prehook(bondname)
+ try:
+ self.write_file('/sys/class/net/%s' %bondname +
+ '/bonding/lacp_bypass_allow', allow)
+ except:
+ raise
+ finally:
+ if posthook:
+ posthook(bondname)
+ self._cache_update([bondname, 'linkinfo',
+ 'lacp_bypass_allow'], allow)
+
+ def get_lacp_fallback_allow(self, bondname):
+ return self._cache_get([bondname, 'linkinfo', 'lacp_bypass_allow'])
+
+ def set_lacp_fallback_period(self, bondname, period, prehook=None, posthook=None):
+ if (self._cache_check([bondname, 'linkinfo', 'lacp_bypass_period'],
+ lacp_bypass_period)):
+ return
+ if prehook:
+ prehook(bondname)
+ try:
+ self.write_file('/sys/class/net/%s' %bondname +
+ '/bonding/lacp_bypass_period', period)
+ except:
+ raise
+ finally:
+ if posthook:
+ posthook(bondname)
+ self._cache_update([bondname, 'linkinfo',
+ 'lacp_bypass_period'], period)
+
+ def get_lacp_fallback_period(self, bondname):
+ return self._cache_get([bondname, 'linkinfo', 'lacp_bypass_period'])
+
+ def set_min_links(self, bondname, min_links, prehook=None):
+ if (self._cache_check([bondname, 'linkinfo', 'min_links'],
+ min_links)):
+ return
+ if prehook:
+ prehook(bondname)
+ self.write_file('/sys/class/net/%s/bonding/min_links' %bondname,
+ min_links)
+ self._cache_update([bondname, 'linkinfo', 'min_links'], min_links)
+
+ def get_min_links(self, bondname):
+ return self._cache_get([bondname, 'linkinfo', 'min_links'])
+
+ def set_lacp_fallback_priority(self, bondname, port, val):
+ slavefile = '/sys/class/net/%s/bonding_slave/lacp_bypass_priority' %port
+ if os.path.exists(slavefile):
+ self.write_file(slavefile, val)
+
+ def get_lacp_fallback_priority(self, bondname):
+ slaves = self.get_slaves(bondname)
+ if not slaves:
+ return slaves
+ prios = []
+ for slave in slaves:
+ priofile = '/sys/class/net/%s/bonding_slave/lacp_bypass_priority' %slave
+ if os.path.exists(priofile):
+ val = self.read_file_oneline(priofile)
+ if val and val != '0':
+ prio = slave + '=' + val
+ prios.append(prio)
+ prios.sort()
+ prio_str = ' '.join(prios)
+ return prio_str
+
+ def set_lacp_fallback_all_active(self, bondname, useprio, prehook=None, posthook=None):
+ if (self._cache_check([bondname, 'linkinfo', 'lacp_bypass_all_active'],
+ lacp_bypass_all_active)):
+ return
+ if prehook:
+ prehook(bondname)
+ try:
+ self.write_file('/sys/class/net/%s' %bondname +
+ '/bonding/lacp_bypass_all_active', useprio)
+ except:
+ raise
+ finally:
+ if posthook:
+ posthook(bondname)
+ self._cache_update([bondname, 'linkinfo',
+ 'lacp_bypass_all_active'], useprio)
+
+ def get_lacp_fallback_all_active(self, bondname):
+ return self._cache_get([bondname, 'linkinfo', 'lacp_bypass_all_active'])
+
+ def get_ad_sys_mac_addr(self, bondname):
+ return self._cache_get([bondname, 'linkinfo', 'ad_sys_mac_addr'])
+
+ def get_ad_sys_priority(self, bondname):
+ return self._cache_get([bondname, 'linkinfo', 'ad_sys_priority'])
+
+ def enslave_slave(self, bondname, slave, prehook=None, posthook=None):
+ slaves = self._cache_get([bondname, 'linkinfo', 'slaves'])
+ if slaves and slave in slaves: return
+ if prehook:
+ prehook(slave)
+ self.write_file('/sys/class/net/%s' %bondname +
+ '/bonding/slaves', '+' + slave)
+ if posthook:
+ posthook(slave)
+ self._cache_update([bondname, 'linkinfo', 'slaves'], slave)
+
+ def remove_slave(self, bondname, slave):
+ slaves = self._cache_get([bondname, 'linkinfo', 'slaves'])
+ if slave not in slaves:
+ return
+ sysfs_bond_path = ('/sys/class/net/%s' %bondname +
+ '/bonding/slaves')
+ if not os.path.exists(sysfs_bond_path):
+ return
+ self.write_file(sysfs_bond_path, '-' + slave)
+ self._cache_delete([bondname, 'linkinfo', 'slaves'], slave)
+
+ def remove_slaves_all(self, bondname):
+ if not _self._cache_get([bondname, 'linkinfo', 'slaves']):
+ return
+ slaves = None
+ sysfs_bond_path = ('/sys/class/net/%s' %bondname +
+ '/bonding/slaves')
+ ipcmd = iproute2()
+ try:
+ f = open(sysfs_bond_path, 'r')
+ slaves = f.readline().strip().split()
+ f.close()
+ except IOError, e:
+ raise Exception('error reading slaves of bond %s' %bondname
+ + '(' + str(e) + ')')
+ for slave in slaves:
+ ipcmd.ip_link_down(slave)
+ try:
+ self.remove_slave(bondname, slave)
+ except Exception, e:
+ if not self.FORCE:
+ raise Exception('error removing slave %s'
+ %slave + ' from bond %s' %bondname +
+ '(%s)' %str(e))
+ else:
+ pass
+ self._cache_del([bondname, 'linkinfo', 'slaves'])
+
+ def load_bonding_module(self):
+ return self.exec_command('modprobe -q bonding')
+
+ def create_bond(self, bondname):
+ if self.bond_exists(bondname):
+ return
+ sysfs_net = '/sys/class/net/'
+ sysfs_bonding_masters = sysfs_net + 'bonding_masters'
+ if not os.path.exists(sysfs_bonding_masters):
+ self.logger.debug('loading bonding driver')
+ self.load_bonding_module()
+ return True
+ self.write_file(sysfs_bonding_masters, '+' + bondname)
+ self._cache_update([bondname], {})
+
+ def delete_bond(self, bondname):
+ if not os.path.exists('/sys/class/net/%s' %bondname):
+ return
+ self.write_file('/sys/class/net/bonding_masters', '-' + bondname)
+ self._cache_delete([bondname])
+
+ def unset_master(self, bondname):
+ print 'Do nothing yet'
+ return 0
+
+ def get_slaves(self, bondname):
+ slaves = self._cache_get([bondname, 'linkinfo', 'slaves'])
+ if slaves:
+ return list(slaves)
+ slavefile = '/sys/class/net/%s/bonding/slaves' %bondname
+ if os.path.exists(slavefile):
+ buf = self.read_file_oneline(slavefile)
+ if buf:
+ slaves = buf.split()
+ if not slaves:
+ return slaves
+ self._cache_update([bondname, 'linkinfo', 'slaves'], slaves)
+ return list(slaves)
+
+ def bond_slave_exists(self, bond, slave):
+ slaves = self.get_slaves(bond)
+ if not slaves: return False
+ return slave in slaves
+
+ def bond_exists(self, bondname):
+ return os.path.exists('/sys/class/net/%s/bonding' %bondname)
+++ /dev/null
-#!/usr/bin/python
-#
-# Copyright 2014 Cumulus Networks, Inc. All rights reserved.
-# Author: Roopa Prabhu, roopa@cumulusnetworks.com
-#
-
-import os
-import re
-from ifupdown.iface import *
-from utilsbase import *
-from iproute2 import *
-from cache import *
-
-class ifenslaveutil(utilsBase):
- """ This class contains methods to interact with linux kernel bond
- related interfaces """
-
- _cache_fill_done = False
-
- def __init__(self, *args, **kargs):
- utilsBase.__init__(self, *args, **kargs)
- if self.CACHE and not self._cache_fill_done:
- self._bond_linkinfo_fill_all()
- self._cache_fill_done = True
-
- def _bond_linkinfo_fill_attrs(self, bondname):
- try:
- linkCache.links[bondname]['linkinfo'] = {}
- except:
- linkCache.links[bondname] = {'linkinfo': {}}
-
- try:
- linkCache.set_attr([bondname, 'linkinfo', 'slaves'],
- self.read_file_oneline('/sys/class/net/%s/bonding/slaves'
- %bondname).split())
- linkCache.set_attr([bondname, 'linkinfo', 'mode'],
- self.read_file_oneline('/sys/class/net/%s/bonding/mode'
- %bondname).split()[0])
- linkCache.set_attr([bondname, 'linkinfo', 'xmit_hash_policy'],
- self.read_file_oneline(
- '/sys/class/net/%s/bonding/xmit_hash_policy'
- %bondname).split()[0])
- linkCache.set_attr([bondname, 'linkinfo', 'lacp_rate'],
- self.read_file_oneline('/sys/class/net/%s/bonding/lacp_rate'
- %bondname).split()[1])
- linkCache.set_attr([bondname, 'linkinfo', 'ad_sys_priority'],
- self.read_file_oneline('/sys/class/net/%s/bonding/ad_sys_priority'
- %bondname))
- linkCache.set_attr([bondname, 'linkinfo', 'ad_sys_mac_addr'],
- self.read_file_oneline('/sys/class/net/%s/bonding/ad_sys_mac_addr'
- %bondname))
- map(lambda x: linkCache.set_attr([bondname, 'linkinfo', x],
- self.read_file_oneline('/sys/class/net/%s/bonding/%s'
- %(bondname, x))),
- ['use_carrier', 'miimon', 'min_links', 'num_unsol_na',
- 'num_grat_arp', 'lacp_bypass_allow', 'lacp_bypass_period',
- 'lacp_bypass_all_active', 'clag_enable'])
- except Exception, e:
- pass
-
- def _bond_linkinfo_fill_all(self):
- bondstr = self.read_file_oneline('/sys/class/net/bonding_masters')
- if not bondstr:
- return
- [self._bond_linkinfo_fill_attrs(b) for b in bondstr.split()]
-
- def _bond_linkinfo_fill(self, bondname, refresh=False):
- try:
- linkCache.get_attr([bondname, 'linkinfo', 'slaves'])
- return
- except:
- pass
- bondstr = self.read_file_oneline('/sys/class/net/bonding_masters')
- if (not bondstr or bondname not in bondstr.split()):
- raise Exception('bond %s not found' %bondname)
- self._bond_linkinfo_fill_attrs(bondname)
-
- def _cache_get(self, attrlist, refresh=False):
- try:
- if self.DRYRUN:
- return None
- if self.CACHE:
- if not ifenslaveutil._cache_fill_done:
- self._bond_linkinfo_fill_all()
- ifenslaveutil._cache_fill_done = True
- return linkCache.get_attr(attrlist)
- if not refresh:
- return linkCache.get_attr(attrlist)
- self._bond_linkinfo_fill(attrlist[0], refresh)
- return linkCache.get_attr(attrlist)
- except Exception, e:
- self.logger.debug('_cache_get(%s) : [%s]'
- %(str(attrlist), str(e)))
- pass
- return None
-
- def _cache_check(self, attrlist, value, refresh=False):
- try:
- attrvalue = self._cache_get(attrlist, refresh)
- if attrvalue and attrvalue == value:
- return True
- except Exception, e:
- self.logger.debug('_cache_check(%s) : [%s]'
- %(str(attrlist), str(e)))
- pass
- return False
-
- def _cache_update(self, attrlist, value):
- if self.DRYRUN: return
- try:
- if attrlist[-1] == 'slaves':
- linkCache.add_to_attrlist(attrlist, value)
- return
- linkCache.add_attr(attrlist, value)
- except:
- pass
-
- def _cache_delete(self, attrlist, value=None):
- if self.DRYRUN: return
- try:
- if attrlist[-1] == 'slaves':
- linkCache.remove_from_attrlist(attrlist, value)
- return
- linkCache.del_attr(attrlist)
- except:
- pass
-
- def _cache_invalidate(self):
- if self.DRYRUN: return
- linkCache.invalidate()
-
- def set_attrs(self, bondname, attrdict, prehook):
- for attrname, attrval in attrdict.items():
- if (self._cache_check([bondname, 'linkinfo',
- attrname], attrval)):
- continue
- if (attrname == 'mode' or attrname == 'xmit_hash_policy' or
- attrname == 'lacp_rate' or attrname == 'min_links'):
- if prehook:
- prehook(bondname)
- try:
- self.write_file('/sys/class/net/%s/bonding/%s'
- %(bondname, attrname), attrval)
- except Exception, e:
- if self.FORCE:
- self.logger.warn(str(e))
- pass
- else:
- raise
-
- def set_use_carrier(self, bondname, use_carrier):
- if not use_carrier or (use_carrier != '0' and use_carrier != '1'):
- return
- if (self._cache_check([bondname, 'linkinfo', 'use_carrier'],
- use_carrier)):
- return
- self.write_file('/sys/class/net/%s' %bondname +
- '/bonding/use_carrier', use_carrier)
- self._cache_update([bondname, 'linkinfo',
- 'use_carrier'], use_carrier)
-
- def get_use_carrier(self, bondname):
- return self._cache_get([bondname, 'linkinfo', 'use_carrier'])
-
- def set_xmit_hash_policy(self, bondname, hash_policy, prehook=None):
- valid_values = ['layer2', 'layer3+4', 'layer2+3']
- if not hash_policy:
- return
- if hash_policy not in valid_values:
- raise Exception('invalid hash policy value %s' %hash_policy)
- if (self._cache_check([bondname, 'linkinfo', 'xmit_hash_policy'],
- hash_policy)):
- return
- if prehook:
- prehook(bondname)
- self.write_file('/sys/class/net/%s' %bondname +
- '/bonding/xmit_hash_policy', hash_policy)
- self._cache_update([bondname, 'linkinfo', 'xmit_hash_policy'],
- hash_policy)
-
- def get_xmit_hash_policy(self, bondname):
- return self._cache_get([bondname, 'linkinfo', 'xmit_hash_policy'])
-
- def set_miimon(self, bondname, miimon):
- if (self._cache_check([bondname, 'linkinfo', 'miimon'],
- miimon)):
- return
- self.write_file('/sys/class/net/%s' %bondname +
- '/bonding/miimon', miimon)
- self._cache_update([bondname, 'linkinfo', 'miimon'], miimon)
-
- def get_miimon(self, bondname):
- return self._cache_get([bondname, 'linkinfo', 'miimon'])
-
- def set_clag_enable(self, bondname, clag_id):
- clag_enable = '0' if clag_id == '0' else '1'
- if self._cache_check([bondname, 'linkinfo', 'clag_enable'],
- clag_enable) == False:
- self.write_file('/sys/class/net/%s' %bondname +
- '/bonding/clag_enable', clag_enable)
- self._cache_update([bondname, 'linkinfo', 'clag_enable'],
- clag_enable)
-
- def get_clag_enable(self, bondname):
- return self._cache_get([bondname, 'linkinfo', 'clag_enable'])
-
- def set_mode(self, bondname, mode, prehook=None):
- valid_modes = ['balance-rr', 'active-backup', 'balance-xor',
- 'broadcast', '802.3ad', 'balance-tlb', 'balance-alb']
- if not mode:
- return
- if mode not in valid_modes:
- raise Exception('invalid mode %s' %mode)
- if (self._cache_check([bondname, 'linkinfo', 'mode'],
- mode)):
- return
- if prehook:
- prehook(bondname)
- self.write_file('/sys/class/net/%s' %bondname + '/bonding/mode', mode)
- self._cache_update([bondname, 'linkinfo', 'mode'], mode)
-
- def get_mode(self, bondname):
- return self._cache_get([bondname, 'linkinfo', 'mode'])
-
- def set_lacp_rate(self, bondname, lacp_rate, prehook=None, posthook=None):
- if not lacp_rate or (lacp_rate != '0' and lacp_rate != '1'):
- return
- if (self._cache_check([bondname, 'linkinfo', 'lacp_rate'],
- lacp_rate)):
- return
- if prehook:
- prehook(bondname)
- try:
- self.write_file('/sys/class/net/%s' %bondname +
- '/bonding/lacp_rate', lacp_rate)
- except:
- raise
- finally:
- if posthook:
- prehook(bondname)
- self._cache_update([bondname, 'linkinfo',
- 'lacp_rate'], lacp_rate)
-
- def get_lacp_rate(self, bondname):
- return self._cache_get([bondname, 'linkinfo', 'lacp_rate'])
-
- def set_lacp_fallback_allow(self, bondname, allow, prehook=None, posthook=None):
- if (self._cache_check([bondname, 'linkinfo', 'lacp_bypass_allow'],
- lacp_bypass_allow)):
- return
- if prehook:
- prehook(bondname)
- try:
- self.write_file('/sys/class/net/%s' %bondname +
- '/bonding/lacp_bypass_allow', allow)
- except:
- raise
- finally:
- if posthook:
- posthook(bondname)
- self._cache_update([bondname, 'linkinfo',
- 'lacp_bypass_allow'], allow)
-
- def get_lacp_fallback_allow(self, bondname):
- return self._cache_get([bondname, 'linkinfo', 'lacp_bypass_allow'])
-
- def set_lacp_fallback_period(self, bondname, period, prehook=None, posthook=None):
- if (self._cache_check([bondname, 'linkinfo', 'lacp_bypass_period'],
- lacp_bypass_period)):
- return
- if prehook:
- prehook(bondname)
- try:
- self.write_file('/sys/class/net/%s' %bondname +
- '/bonding/lacp_bypass_period', period)
- except:
- raise
- finally:
- if posthook:
- posthook(bondname)
- self._cache_update([bondname, 'linkinfo',
- 'lacp_bypass_period'], period)
-
- def get_lacp_fallback_period(self, bondname):
- return self._cache_get([bondname, 'linkinfo', 'lacp_bypass_period'])
-
- def set_min_links(self, bondname, min_links, prehook=None):
- if (self._cache_check([bondname, 'linkinfo', 'min_links'],
- min_links)):
- return
- if prehook:
- prehook(bondname)
- self.write_file('/sys/class/net/%s/bonding/min_links' %bondname,
- min_links)
- self._cache_update([bondname, 'linkinfo', 'min_links'], min_links)
-
- def get_min_links(self, bondname):
- return self._cache_get([bondname, 'linkinfo', 'min_links'])
-
- def set_lacp_fallback_priority(self, bondname, port, val):
- slavefile = '/sys/class/net/%s/bonding_slave/lacp_bypass_priority' %port
- if os.path.exists(slavefile):
- self.write_file(slavefile, val)
-
- def get_lacp_fallback_priority(self, bondname):
- slaves = self.get_slaves(bondname)
- if not slaves:
- return slaves
- prios = []
- for slave in slaves:
- priofile = '/sys/class/net/%s/bonding_slave/lacp_bypass_priority' %slave
- if os.path.exists(priofile):
- val = self.read_file_oneline(priofile)
- if val and val != '0':
- prio = slave + '=' + val
- prios.append(prio)
- prios.sort()
- prio_str = ' '.join(prios)
- return prio_str
-
- def set_lacp_fallback_all_active(self, bondname, useprio, prehook=None, posthook=None):
- if (self._cache_check([bondname, 'linkinfo', 'lacp_bypass_all_active'],
- lacp_bypass_all_active)):
- return
- if prehook:
- prehook(bondname)
- try:
- self.write_file('/sys/class/net/%s' %bondname +
- '/bonding/lacp_bypass_all_active', useprio)
- except:
- raise
- finally:
- if posthook:
- posthook(bondname)
- self._cache_update([bondname, 'linkinfo',
- 'lacp_bypass_all_active'], useprio)
-
- def get_lacp_fallback_all_active(self, bondname):
- return self._cache_get([bondname, 'linkinfo', 'lacp_bypass_all_active'])
-
- def get_ad_sys_mac_addr(self, bondname):
- return self._cache_get([bondname, 'linkinfo', 'ad_sys_mac_addr'])
-
- def get_ad_sys_priority(self, bondname):
- return self._cache_get([bondname, 'linkinfo', 'ad_sys_priority'])
-
- def enslave_slave(self, bondname, slave, prehook=None, posthook=None):
- slaves = self._cache_get([bondname, 'linkinfo', 'slaves'])
- if slaves and slave in slaves: return
- if prehook:
- prehook(slave)
- self.write_file('/sys/class/net/%s' %bondname +
- '/bonding/slaves', '+' + slave)
- if posthook:
- posthook(slave)
- self._cache_update([bondname, 'linkinfo', 'slaves'], slave)
-
- def remove_slave(self, bondname, slave):
- slaves = self._cache_get([bondname, 'linkinfo', 'slaves'])
- if slave not in slaves:
- return
- sysfs_bond_path = ('/sys/class/net/%s' %bondname +
- '/bonding/slaves')
- if not os.path.exists(sysfs_bond_path):
- return
- self.write_file(sysfs_bond_path, '-' + slave)
- self._cache_delete([bondname, 'linkinfo', 'slaves'], slave)
-
- def remove_slaves_all(self, bondname):
- if not _self._cache_get([bondname, 'linkinfo', 'slaves']):
- return
- slaves = None
- sysfs_bond_path = ('/sys/class/net/%s' %bondname +
- '/bonding/slaves')
- ipcmd = iproute2()
- try:
- f = open(sysfs_bond_path, 'r')
- slaves = f.readline().strip().split()
- f.close()
- except IOError, e:
- raise Exception('error reading slaves of bond %s' %bondname
- + '(' + str(e) + ')')
- for slave in slaves:
- ipcmd.ip_link_down(slave)
- try:
- self.remove_slave(bondname, slave)
- except Exception, e:
- if not self.FORCE:
- raise Exception('error removing slave %s'
- %slave + ' from bond %s' %bondname +
- '(%s)' %str(e))
- else:
- pass
- self._cache_del([bondname, 'linkinfo', 'slaves'])
-
- def load_bonding_module(self):
- return self.exec_command('modprobe -q bonding')
-
- def create_bond(self, bondname):
- if self.bond_exists(bondname):
- return
- sysfs_net = '/sys/class/net/'
- sysfs_bonding_masters = sysfs_net + 'bonding_masters'
- if not os.path.exists(sysfs_bonding_masters):
- self.logger.debug('loading bonding driver')
- self.load_bonding_module()
- return True
- self.write_file(sysfs_bonding_masters, '+' + bondname)
- self._cache_update([bondname], {})
-
- def delete_bond(self, bondname):
- if not os.path.exists('/sys/class/net/%s' %bondname):
- return
- self.write_file('/sys/class/net/bonding_masters', '-' + bondname)
- self._cache_delete([bondname])
-
- def unset_master(self, bondname):
- print 'Do nothing yet'
- return 0
-
- def get_slaves(self, bondname):
- slaves = self._cache_get([bondname, 'linkinfo', 'slaves'])
- if slaves:
- return list(slaves)
- slavefile = '/sys/class/net/%s/bonding/slaves' %bondname
- if os.path.exists(slavefile):
- buf = self.read_file_oneline(slavefile)
- if buf:
- slaves = buf.split()
- if not slaves:
- return slaves
- self._cache_update([bondname, 'linkinfo', 'slaves'], slaves)
- return list(slaves)
-
- def bond_slave_exists(self, bond, slave):
- slaves = self.get_slaves(bond)
- if not slaves: return False
- return slave in slaves
-
- def bond_exists(self, bondname):
- return os.path.exists('/sys/class/net/%s/bonding' %bondname)
#from ifupdownaddons.dhclient import *
#from ifupdownaddons.bridgeutils import *
#from ifupdownaddons.mstpctlutil import *
-#from ifupdownaddons.ifenslaveutil import *
+#from ifupdownaddons.bondutil import *
class moduleBase(object):
""" Base class for ifupdown addon modules
- **ifenslave**: bond configuration module
+ **bond**: bond configuration module
**bond-use-carrier**
'docs/examples/vlan_aware_bridges/interfaces.with_clag']),
('/etc/bash_completion.d/', ['completion/ifup']),
('/usr/share/ifupdownaddons/', ['addons/bridge.py',
- 'addons/ifenslave.py', 'addons/vlan.py',
+ 'addons/bond.py', 'addons/vlan.py',
'addons/mstpctl.py', 'addons/address.py',
'addons/dhcp.py', 'addons/usercmds.py',
'addons/ethtool.py', 'addons/loopback.py',