parsing /etc/network/interfaces file, loading, scheduling and state
management of interfaces.
-It dynamically loads python modules from /usr/share/ifupdownmodules (provided
- by the python-ifupdown2-addons package). To remain compatible with other
-packages that depend on ifupdown, it also executes scripts under /etc/network/.
+It dynamically loads python modules from /usr/share/ifupdownaddons.
+To remain compatible with other packages that depend on ifupdown,
+it also executes scripts under /etc/network/.
To make the transition smoother, a python module under
-/usr/share/ifupdownmodules will override a script by the same name under
+/usr/share/ifupdownaddons will override a script by the same name under
/etc/network/.
It publishes an interface object which is passed to all loadble python
Unlike original ifupdown, all interface configuration is moved to external
python modules. That includes inet, inet6 and dhcp configurations.
-A set of default modules are provided by the python-ifupdown2-addons deb.
+A set of default modules are included in the package.
python-ifupdown2 expects a few things from the pluggable modules:
- the module should implement a class by the same name
- or install from deb
dpkg -i python-ifupdown2-<ver>.deb
-
-- note that python-ifupdown2 requires python-ifupdown2-addons package to
- function. And python-ifupdown2-addons deb has an install dependency on
- python-ifupdown2
--- /dev/null
+#!/usr/bin/python
+#
+# Copyright 2014 Cumulus Networks, Inc. All rights reserved.
+# Author: Roopa Prabhu, roopa@cumulusnetworks.com
+#
+
+try:
+ from ipaddr import IPNetwork
+ from sets import Set
+ from ifupdown.iface import *
+ from ifupdownaddons.modulebase import moduleBase
+ from ifupdownaddons.iproute2 import iproute2
+ from ifupdownaddons.dhclient import dhclient
+except ImportError, e:
+ raise ImportError (str(e) + "- required module not found")
+
+class address(moduleBase):
+ """ ifupdown2 addon module to configure address, mtu, hwaddress, alias
+ (description) on an interface """
+
+ _modinfo = {'mhelp' : 'address configuration module for interfaces',
+ 'attrs': {
+ 'address' :
+ {'help' : 'ipv4 or ipv6 addresses',
+ 'example' : ['address 10.0.12.3/24',
+ 'address 2000:1000:1000:1000:3::5/128']},
+ 'netmask' :
+ {'help': 'netmask',
+ 'example' : ['netmask 255.255.255.0'],
+ 'compat' : True},
+ 'broadcast' :
+ {'help': 'broadcast address',
+ 'example' : ['broadcast 10.0.1.255']},
+ 'scope' :
+ {'help': 'scope',
+ 'example' : ['scope host']},
+ 'preferred-lifetime' :
+ {'help': 'preferred lifetime',
+ 'example' : ['preferred-lifetime forever',
+ 'preferred-lifetime 10']},
+ 'gateway' :
+ {'help': 'default gateway',
+ 'example' : ['gateway 255.255.255.0']},
+ 'mtu' :
+ { 'help': 'interface mtu',
+ 'example' : ['mtu 1600'],
+ 'default' : '1500'},
+ 'hwaddress' :
+ {'help' : 'hw address',
+ 'example': ['hwaddress 44:38:39:00:27:b8']},
+ 'alias' :
+ { 'help': 'description/alias',
+ 'example' : ['alias testnetwork']},
+ 'address-purge' :
+ { 'help': 'purge existing addresses. By default ' +
+ 'any existing ip addresses on an interface are ' +
+ 'purged to match persistant addresses in the ' +
+ 'interfaces file. Set this attribute to \'no\'' +
+ 'if you want to preserve existing addresses',
+ 'default' : 'yes',
+ 'example' : ['address-purge yes/no']}}}
+
+ def __init__(self, *args, **kargs):
+ moduleBase.__init__(self, *args, **kargs)
+ self.ipcmd = None
+ self._bridge_fdb_query_cache = {}
+
+ def _address_valid(self, addrs):
+ if not addrs:
+ return False
+ if any(map(lambda a: True if a[:7] != '0.0.0.0'
+ else False, addrs)):
+ return True
+ return False
+
+ def _process_bridge(self, ifaceobj, up):
+ hwaddress = ifaceobj.get_attr_value_first('hwaddress')
+ addrs = ifaceobj.get_attr_value_first('address')
+ is_vlan_dev_on_vlan_aware_bridge = False
+ is_bridge = self.ipcmd.is_bridge(ifaceobj.name)
+ if not is_bridge:
+ if '.' in ifaceobj.name:
+ (bridgename, vlan) = ifaceobj.name.split('.')
+ is_vlan_dev_on_vlan_aware_bridge = self.ipcmd.bridge_is_vlan_aware(bridgename)
+ if ((is_bridge and not self.ipcmd.bridge_is_vlan_aware(ifaceobj.name))
+ or is_vlan_dev_on_vlan_aware_bridge):
+ if self._address_valid(addrs):
+ if up:
+ self.write_file('/proc/sys/net/ipv4/conf/%s' %ifaceobj.name +
+ '/arp_accept', '1')
+ else:
+ self.write_file('/proc/sys/net/ipv4/conf/%s' %ifaceobj.name +
+ '/arp_accept', '0')
+ if hwaddress and is_vlan_dev_on_vlan_aware_bridge:
+ if up:
+ self.ipcmd.bridge_fdb_add(bridgename, hwaddress, vlan)
+ else:
+ self.ipcmd.bridge_fdb_del(bridgename, hwaddress, vlan)
+
+ def _inet_address_config(self, ifaceobj):
+ purge_addresses = ifaceobj.get_attr_value_first('address-purge')
+ if not purge_addresses:
+ purge_addresses = 'yes'
+ newaddrs = []
+ addrs = ifaceobj.get_attr_value('address')
+ if addrs:
+ # If user address is not in CIDR notation, convert them to CIDR
+ for addr_index in range(0, len(addrs)):
+ addr = addrs[addr_index]
+ if '/' in addr:
+ newaddrs.append(addr)
+ continue
+ netmask = ifaceobj.get_attr_value_n('netmask', addr_index)
+ if netmask:
+ prefixlen = IPNetwork('%s' %addr +
+ '/%s' %netmask).prefixlen
+ newaddrs.append(addr + '/%s' %prefixlen)
+ else:
+ newaddrs.append(addr)
+
+ if (not self.PERFMODE and
+ not (ifaceobj.flags & iface.HAS_SIBLINGS) and
+ purge_addresses == 'yes'):
+ # if perfmode is not set and also if iface has no sibling
+ # objects, purge addresses that are not present in the new
+ # config
+ runningaddrs = self.ipcmd.addr_get(ifaceobj.name, details=False)
+ if newaddrs == runningaddrs:
+ return
+ try:
+ # if primary address is not same, there is no need to keep any.
+ # reset all addresses
+ if (newaddrs and runningaddrs and
+ (newaddrs[0] != runningaddrs[0])):
+ self.ipcmd.del_addr_all(ifaceobj.name)
+ else:
+ self.ipcmd.del_addr_all(ifaceobj.name, newaddrs)
+ except Exception, e:
+ self.log_warn(str(e))
+ if not newaddrs:
+ return
+ for addr_index in range(0, len(newaddrs)):
+ try:
+ self.ipcmd.addr_add(ifaceobj.name, newaddrs[addr_index],
+ ifaceobj.get_attr_value_n('broadcast', addr_index),
+ ifaceobj.get_attr_value_n('pointopoint',addr_index),
+ ifaceobj.get_attr_value_n('scope', addr_index),
+ ifaceobj.get_attr_value_n('preferred-lifetime', addr_index))
+ except Exception, e:
+ self.log_error(str(e))
+
+ def _up(self, ifaceobj):
+ if not self.ipcmd.link_exists(ifaceobj.name):
+ return
+ addr_method = ifaceobj.addr_method
+ try:
+ # release any stale dhcp addresses if present
+ if (addr_method != "dhcp" and not self.PERFMODE and
+ not (ifaceobj.flags & iface.HAS_SIBLINGS)):
+ # if not running in perf mode and ifaceobj does not have
+ # any sibling iface objects, kill any stale dhclient
+ # processes
+ dhclientcmd = dhclient()
+ if dhclient.is_running(ifaceobj.name):
+ # release any dhcp leases
+ dhclientcmd.release(ifaceobj.name)
+ elif dhclient.is_running6(ifaceobj.name):
+ dhclientcmd.release6(ifaceobj.name)
+ except:
+ pass
+
+ self.ipcmd.batch_start()
+ if addr_method != "dhcp":
+ self._inet_address_config(ifaceobj)
+ mtu = ifaceobj.get_attr_value_first('mtu')
+ if mtu:
+ self.ipcmd.link_set(ifaceobj.name, 'mtu', mtu)
+ alias = ifaceobj.get_attr_value_first('alias')
+ if alias:
+ self.ipcmd.link_set_alias(ifaceobj.name, alias)
+ hwaddress = ifaceobj.get_attr_value_first('hwaddress')
+ if hwaddress:
+ self.ipcmd.link_set(ifaceobj.name, 'address', hwaddress)
+ self.ipcmd.batch_commit()
+
+ try:
+ # Handle special things on a bridge
+ self._process_bridge(ifaceobj, True)
+ except Exception, e:
+ self.log_warn('%s: %s' %(ifaceobj.name, str(e)))
+ pass
+
+ if addr_method != "dhcp":
+ self.ipcmd.route_add_gateway(ifaceobj.name,
+ ifaceobj.get_attr_value_first('gateway'))
+
+ def _down(self, ifaceobj):
+ try:
+ if not self.ipcmd.link_exists(ifaceobj.name):
+ return
+ addr_method = ifaceobj.addr_method
+ if addr_method != "dhcp":
+ self.ipcmd.route_del_gateway(ifaceobj.name,
+ ifaceobj.get_attr_value_first('gateway'),
+ ifaceobj.get_attr_value_first('metric'))
+ self.ipcmd.del_addr_all(ifaceobj.name)
+ mtu = ifaceobj.get_attr_value_first('mtu')
+ if mtu:
+ self.ipcmd.link_set(ifaceobj.name, 'mtu',
+ self.get_mod_subattr('mtu', 'default'))
+ alias = ifaceobj.get_attr_value_first('alias')
+ if alias:
+ self.ipcmd.link_set(ifaceobj.name, 'alias', "\'\'")
+ # XXX hwaddress reset cannot happen because we dont know last
+ # address.
+
+ # Handle special things on a bridge
+ self._process_bridge(ifaceobj, False)
+ except Exception, e:
+ self.logger.debug('%s : %s' %(ifaceobj.name, str(e)))
+ pass
+
+ def _get_iface_addresses(self, ifaceobj):
+ addrlist = ifaceobj.get_attr_value('address')
+ outaddrlist = []
+
+ if not addrlist: return None
+ for addrindex in range(0, len(addrlist)):
+ addr = addrlist[addrindex]
+ netmask = ifaceobj.get_attr_value_n('netmask', addrindex)
+ if netmask:
+ prefixlen = IPNetwork('%s' %addr +
+ '/%s' %netmask).prefixlen
+ addr = addr + '/%s' %prefixlen
+ outaddrlist.append(addr)
+ return outaddrlist
+
+ def _get_bridge_fdbs(self, bridgename, vlan):
+ fdbs = self._bridge_fdb_query_cache.get(bridgename)
+ if not fdbs:
+ fdbs = self.ipcmd.bridge_fdb_show_dev(bridgename)
+ if not fdbs:
+ return
+ self._bridge_fdb_query_cache[bridgename] = fdbs
+ return fdbs.get(vlan)
+
+ def _check_addresses_in_bridge(self, ifaceobj, hwaddress):
+ """ If the device is a bridge, make sure the addresses
+ are in the bridge """
+ if '.' in ifaceobj.name:
+ (bridgename, vlan) = ifaceobj.name.split('.')
+ if self.ipcmd.bridge_is_vlan_aware(bridgename):
+ fdb_addrs = self._get_bridge_fdbs(bridgename, vlan)
+ if not fdb_addrs or hwaddress not in fdb_addrs:
+ return False
+ return True
+
+ def _query_check(self, ifaceobj, ifaceobjcurr):
+ runningaddrsdict = None
+ if not self.ipcmd.link_exists(ifaceobj.name):
+ self.logger.debug('iface %s not found' %ifaceobj.name)
+ return
+ addr_method = ifaceobj.addr_method
+ self.query_n_update_ifaceobjcurr_attr(ifaceobj, ifaceobjcurr,
+ 'mtu', self.ipcmd.link_get_mtu)
+ hwaddress = ifaceobj.get_attr_value_first('hwaddress')
+ if hwaddress:
+ rhwaddress = self.ipcmd.link_get_hwaddress(ifaceobj.name)
+ if not rhwaddress or rhwaddress != hwaddress:
+ ifaceobjcurr.update_config_with_status('hwaddress', rhwaddress,
+ 1)
+ elif not self._check_addresses_in_bridge(ifaceobj, hwaddress):
+ # XXX: hw address is not in bridge
+ ifaceobjcurr.update_config_with_status('hwaddress', rhwaddress,
+ 1)
+ ifaceobjcurr.status_str = 'bridge fdb error'
+ else:
+ ifaceobjcurr.update_config_with_status('hwaddress', rhwaddress,
+ 0)
+ self.query_n_update_ifaceobjcurr_attr(ifaceobj, ifaceobjcurr,
+ 'alias', self.ipcmd.link_get_alias)
+ # compare addresses
+ if addr_method == 'dhcp':
+ return
+ addrs = self._get_iface_addresses(ifaceobj)
+ runningaddrsdict = self.ipcmd.addr_get(ifaceobj.name)
+
+ # Set ifaceobjcurr method and family
+ ifaceobjcurr.addr_method = ifaceobj.addr_method
+ ifaceobjcurr.addr_family = ifaceobj.addr_family
+ if not runningaddrsdict and not addrs:
+ return
+ runningaddrs = runningaddrsdict.keys() if runningaddrsdict else []
+ if runningaddrs != addrs:
+ runningaddrsset = set(runningaddrs) if runningaddrs else set([])
+ addrsset = set(addrs) if addrs else set([])
+ if (ifaceobj.flags & iface.HAS_SIBLINGS):
+ if not addrsset:
+ return
+ # only check for addresses present in running config
+ addrsdiff = addrsset.difference(runningaddrsset)
+ for addr in addrs:
+ if addr in addrsdiff:
+ ifaceobjcurr.update_config_with_status('address',
+ addr, 1)
+ else:
+ ifaceobjcurr.update_config_with_status('address',
+ addr, 0)
+ else:
+ addrsdiff = addrsset.symmetric_difference(runningaddrsset)
+ for addr in addrsset.union(runningaddrsset):
+ if addr in addrsdiff:
+ ifaceobjcurr.update_config_with_status('address',
+ addr, 1)
+ else:
+ ifaceobjcurr.update_config_with_status('address',
+ addr, 0)
+ elif addrs:
+ [ifaceobjcurr.update_config_with_status('address',
+ addr, 0) for addr in addrs]
+ #XXXX Check broadcast address, scope, etc
+ return
+
+ def _query_running(self, ifaceobjrunning):
+ if not self.ipcmd.link_exists(ifaceobjrunning.name):
+ self.logger.debug('iface %s not found' %ifaceobjrunning.name)
+ return
+ dhclientcmd = dhclient()
+ if (dhclientcmd.is_running(ifaceobjrunning.name) or
+ dhclientcmd.is_running6(ifaceobjrunning.name)):
+ # If dhcp is configured on the interface, we skip it
+ return
+ isloopback = self.ipcmd.link_isloopback(ifaceobjrunning.name)
+ if isloopback:
+ default_addrs = ['127.0.0.1/8', '::1/128']
+ ifaceobjrunning.addr_family = 'inet'
+ ifaceobjrunning.addr_method = 'loopback'
+ else:
+ default_addrs = []
+ runningaddrsdict = self.ipcmd.addr_get(ifaceobjrunning.name)
+ if runningaddrsdict:
+ [ifaceobjrunning.update_config('address', addr)
+ for addr, addrattrs in runningaddrsdict.items()
+ if addr not in default_addrs]
+ mtu = self.ipcmd.link_get_mtu(ifaceobjrunning.name)
+ if (mtu and
+ (ifaceobjrunning.name == 'lo' and mtu != '16436') or
+ (ifaceobjrunning.name != 'lo' and
+ mtu != self.get_mod_subattr('mtu', 'default'))):
+ ifaceobjrunning.update_config('mtu', mtu)
+ alias = self.ipcmd.link_get_alias(ifaceobjrunning.name)
+ if alias:
+ ifaceobjrunning.update_config('alias', alias)
+
+ _run_ops = {'up' : _up,
+ 'down' : _down,
+ 'query-checkcurr' : _query_check,
+ 'query-running' : _query_running }
+
+ def get_ops(self):
+ """ returns list of ops supported by this module """
+ return self._run_ops.keys()
+
+ def _init_command_handlers(self):
+ if not self.ipcmd:
+ self.ipcmd = iproute2(**self.get_flags())
+
+ def run(self, ifaceobj, operation, query_ifaceobj=None, **extra_args):
+ """ run address configuration on the interface object passed as argument
+
+ Args:
+ **ifaceobj** (object): iface object
+
+ **operation** (str): any of 'up', '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.
+ """
+ if ifaceobj.type == ifaceType.BRIDGE_VLAN:
+ return
+ op_handler = self._run_ops.get(operation)
+ if not op_handler:
+ 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 ifupdown.iface import *
+from ifupdownaddons.modulebase import moduleBase
+from ifupdownaddons.iproute2 import iproute2
+import ifupdown.rtnetlink_api as rtnetlink_api
+from ipaddr import IPNetwork
+import logging
+import os
+import glob
+
+class addressvirtual(moduleBase):
+ """ ifupdown2 addon module to configure virtual addresses """
+
+ _modinfo = {'mhelp' : 'address module configures virtual addresses for ' +
+ 'interfaces. It creates a macvlan interface for ' +
+ 'every mac ip address-virtual line',
+ 'attrs' : {
+ 'address-virtual' :
+ { 'help' : 'bridge router virtual mac and ip',
+ 'example' : ['address-virtual 00:11:22:33:44:01 11.0.1.254/24 11.0.1.254/24']}
+ }}
+
+
+ def __init__(self, *args, **kargs):
+ moduleBase.__init__(self, *args, **kargs)
+ self.ipcmd = None
+ self._bridge_fdb_query_cache = {}
+
+ def _is_supported(self, ifaceobj):
+ if ifaceobj.get_attr_value_first('address-virtual'):
+ return True
+ return False
+
+ def _get_macvlan_prefix(self, ifaceobj):
+ return '%s-v' %ifaceobj.name[0:13].replace('.', '-')
+
+ def _add_addresses_to_bridge(self, ifaceobj, hwaddress):
+ # XXX: batch the addresses
+ if '.' in ifaceobj.name:
+ (bridgename, vlan) = ifaceobj.name.split('.')
+ if self.ipcmd.bridge_is_vlan_aware(bridgename):
+ [self.ipcmd.bridge_fdb_add(bridgename, addr,
+ vlan) for addr in hwaddress]
+ elif self.ipcmd.is_bridge(ifaceobj.name):
+ [self.ipcmd.bridge_fdb_add(ifaceobj.name, addr)
+ for addr in hwaddress]
+
+ def _remove_addresses_from_bridge(self, ifaceobj, hwaddress):
+ # XXX: batch the addresses
+ bridgename = None
+ if '.' in ifaceobj.name:
+ if self.ipcmd.bridge_is_vlan_aware(bridgename):
+ (bridgename, vlan) = ifaceobj.name.split('.')
+ elif self.ipcmd.is_bridge(ifaceobj.name):
+ vlan = None
+ bridgename = ifaceobj.name
+ if not bridgename:
+ return
+ for addr in hwaddress:
+ try:
+ self.ipcmd.bridge_fdb_del(bridgename, addr, vlan)
+ except Exception, e:
+ self.logger.debug("%s: %s" %(ifaceobj.name, str(e)))
+ pass
+
+ def _get_bridge_fdbs(self, bridgename, vlan):
+ fdbs = self._bridge_fdb_query_cache.get(bridgename)
+ if not fdbs:
+ fdbs = self.ipcmd.bridge_fdb_show_dev(bridgename)
+ if not fdbs:
+ return
+ self._bridge_fdb_query_cache[bridgename] = fdbs
+ return fdbs.get(vlan)
+
+ def _check_addresses_in_bridge(self, ifaceobj, hwaddress):
+ """ If the device is a bridge, make sure the addresses
+ are in the bridge """
+ if '.' in ifaceobj.name:
+ (bridgename, vlan) = ifaceobj.name.split('.')
+ if self.ipcmd.bridge_is_vlan_aware(bridgename):
+ fdb_addrs = self._get_bridge_fdbs(bridgename, vlan)
+ if not fdb_addrs or hwaddress not in fdb_addrs:
+ return False
+ return True
+
+ def _fix_connected_route(self, ifaceobj, vifacename, addr):
+ #
+ # XXX: Hack to make sure the primary address
+ # is the first in the routing table.
+ #
+ # We use `ip route get` on the vrr network to see which
+ # device the kernel returns. if it is the mac vlan device,
+ # flap the macvlan device to adjust the routing table entry.
+ #
+ # flapping the macvlan device makes sure the macvlan
+ # connected route goes through delete + add, hence adjusting
+ # the order in the routing table.
+ #
+ try:
+ self.logger.info('%s: checking route entry ...' %ifaceobj.name)
+ ip = IPNetwork(addr)
+ route_prefix = '%s/%d' %(ip.network, ip.prefixlen)
+
+ dev = self.ipcmd.ip_route_get_dev(route_prefix)
+ if dev and dev == vifacename:
+ self.logger.info('%s: preferred routing entry ' %ifaceobj.name +
+ 'seems to be of the macvlan dev %s'
+ %vifacename +
+ ' .. flapping macvlan dev to fix entry.')
+ self.ipcmd.link_down(vifacename)
+ self.ipcmd.link_up(vifacename)
+ except Exception, e:
+ self.logger.debug('%s: fixing route entry failed (%s)'
+ %str(e))
+ pass
+
+ def _apply_address_config(self, ifaceobj, address_virtual_list):
+ purge_existing = False if self.PERFMODE else True
+
+ hwaddress = []
+ self.ipcmd.batch_start()
+ av_idx = 0
+ macvlan_prefix = self._get_macvlan_prefix(ifaceobj)
+ for av in address_virtual_list:
+ av_attrs = av.split()
+ if len(av_attrs) < 2:
+ self.logger.warn("%s: incorrect address-virtual attrs '%s'"
+ %(ifaceobj.name, av))
+ av_idx += 1
+ continue
+
+ # Create a macvlan device on this device and set the virtual
+ # router mac and ip on it
+ link_created = False
+ macvlan_ifacename = '%s%d' %(macvlan_prefix, av_idx)
+ if not self.ipcmd.link_exists(macvlan_ifacename):
+ rtnetlink_api.rtnl_api.create_macvlan(macvlan_ifacename,
+ ifaceobj.name)
+ link_created = True
+ if av_attrs[0] != 'None':
+ self.ipcmd.link_set_hwaddress(macvlan_ifacename, av_attrs[0])
+ hwaddress.append(av_attrs[0])
+ self.ipcmd.addr_add_multiple(macvlan_ifacename, av_attrs[1:],
+ purge_existing)
+ # If link existed before, flap the link
+ if not link_created:
+ self._fix_connected_route(ifaceobj, macvlan_ifacename,
+ av_attrs[1])
+ av_idx += 1
+ self.ipcmd.batch_commit()
+
+ # if ifaceobj is a bridge and bridge is a vlan aware bridge
+ # add the vid to the bridge
+ self._add_addresses_to_bridge(ifaceobj, hwaddress)
+
+ def _remove_running_address_config(self, ifaceobj):
+ if not self.ipcmd.link_exists(ifaceobj.name):
+ return
+ hwaddress = []
+ self.ipcmd.batch_start()
+ macvlan_prefix = self._get_macvlan_prefix(ifaceobj)
+ for macvlan_ifacename in glob.glob("/sys/class/net/%s-*" %macvlan_prefix):
+ macvlan_ifacename = os.path.basename(macvlan_ifacename)
+ if not self.ipcmd.link_exists(macvlan_ifacename):
+ continue
+ hwaddress.append(self.ipcmd.link_get_hwaddress(macvlan_ifacename))
+ self.ipcmd.link_delete(os.path.basename(macvlan_ifacename))
+ # XXX: Also delete any fdb addresses. This requires, checking mac address
+ # on individual macvlan interfaces and deleting the vlan from that.
+ self.ipcmd.batch_commit()
+ if any(hwaddress):
+ self._remove_addresses_from_bridge(ifaceobj, hwaddress)
+
+ def _remove_address_config(self, ifaceobj, address_virtual_list=None):
+ if not address_virtual_list:
+ self._remove_running_address_config(ifaceobj)
+ return
+
+ if not self.ipcmd.link_exists(ifaceobj.name):
+ return
+ hwaddress = []
+ self.ipcmd.batch_start()
+ av_idx = 0
+ macvlan_prefix = self._get_macvlan_prefix(ifaceobj)
+ for av in address_virtual_list:
+ av_attrs = av.split()
+ if len(av_attrs) < 2:
+ self.logger.warn("%s: incorrect address-virtual attrs '%s'"
+ %(ifaceobj.name, av))
+ av_idx += 1
+ continue
+
+ # Delete the macvlan device on this device
+ macvlan_ifacename = '%s%d' %(macvlan_prefix, av_idx)
+ self.ipcmd.link_delete(os.path.basename(macvlan_ifacename))
+ if av_attrs[0] != 'None':
+ hwaddress.append(av_attrs[0])
+ av_idx += 1
+ self.ipcmd.batch_commit()
+ self._remove_addresses_from_bridge(ifaceobj, hwaddress)
+
+ def _up(self, ifaceobj):
+ address_virtual_list = ifaceobj.get_attr_value('address-virtual')
+ if not address_virtual_list:
+ # XXX: address virtual is not present. In which case,
+ # delete stale macvlan devices.
+ self._remove_address_config(ifaceobj, address_virtual_list)
+ return
+
+ if not self.ipcmd.link_exists(ifaceobj.name):
+ return
+ self._apply_address_config(ifaceobj, address_virtual_list)
+
+ def _down(self, ifaceobj):
+ try:
+ self._remove_address_config(ifaceobj,
+ ifaceobj.get_attr_value('address-virtual'))
+ except Exception, e:
+ self.log_warn(str(e))
+
+ def _query_check(self, ifaceobj, ifaceobjcurr):
+ address_virtual_list = ifaceobj.get_attr_value('address-virtual')
+ if not address_virtual_list:
+ return
+ if not self.ipcmd.link_exists(ifaceobj.name):
+ return
+ av_idx = 0
+ macvlan_prefix = self._get_macvlan_prefix(ifaceobj)
+ for address_virtual in address_virtual_list:
+ av_attrs = address_virtual.split()
+ if len(av_attrs) < 2:
+ self.logger.warn("%s: incorrect address-virtual attrs '%s'"
+ %(ifaceobj.name, address_virtual))
+ av_idx += 1
+ continue
+
+ # Check if the macvlan device on this interface
+ macvlan_ifacename = '%s%d' %(macvlan_prefix, av_idx)
+ if not self.ipcmd.link_exists(macvlan_ifacename):
+ ifaceobjcurr.update_config_with_status('address-virtual',
+ '', 1)
+ av_idx += 1
+ continue
+ # Check mac and ip address
+ rhwaddress = self.ipcmd.link_get_hwaddress(macvlan_ifacename)
+ raddrs = self.ipcmd.addr_get(macvlan_ifacename)
+ if not raddrs or not rhwaddress:
+ ifaceobjcurr.update_config_with_status('address-virtual', '', 1)
+ av_idx += 1
+ continue
+ raddrs = raddrs.keys()
+ if (rhwaddress == av_attrs[0] and raddrs == av_attrs[1:] and
+ self._check_addresses_in_bridge(ifaceobj, av_attrs[0])):
+ ifaceobjcurr.update_config_with_status('address-virtual',
+ address_virtual, 0)
+ else:
+ raddress_virtual = '%s %s' %(rhwaddress, ' '.join(raddrs))
+ ifaceobjcurr.update_config_with_status('address-virtual',
+ raddress_virtual, 1)
+ av_idx += 1
+ return
+
+ def _query_running(self, ifaceobjrunning):
+ macvlan_prefix = self._get_macvlan_prefix(ifaceobjrunning)
+ address_virtuals = glob.glob("/sys/class/net/%s*" %macvlan_prefix)
+ for av in address_virtuals:
+ macvlan_ifacename = os.path.basename(av)
+ rhwaddress = self.ipcmd.link_get_hwaddress(macvlan_ifacename)
+ raddress = self.ipcmd.addr_get(macvlan_ifacename)
+ if not raddress:
+ self.logger.warn('%s: no running addresses'
+ %ifaceobjrunning.name)
+ raddress = []
+ ifaceobjrunning.update_config('address-virtual',
+ '%s %s' %(rhwaddress, ''.join(raddress)))
+ return
+
+ _run_ops = {'up' : _up,
+ 'down' : _down,
+ 'query-checkcurr' : _query_check,
+ 'query-running' : _query_running}
+
+ def get_ops(self):
+ """ returns list of ops supported by this module """
+ return self._run_ops.keys()
+
+ def _init_command_handlers(self):
+ if not self.ipcmd:
+ self.ipcmd = iproute2(**self.get_flags())
+
+ def run(self, ifaceobj, operation, query_ifaceobj=None, **extra_args):
+ """ run vlan 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.
+ """
+ if ifaceobj.type == ifaceType.BRIDGE_VLAN:
+ return
+ op_handler = self._run_ops.get(operation)
+ if not op_handler:
+ 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 *
+from ifupdownaddons.modulebase import moduleBase
+from ifupdownaddons.bridgeutils import brctl
+from ifupdownaddons.iproute2 import iproute2
+from collections import Counter
+import ifupdown.rtnetlink_api as rtnetlink_api
+import itertools
+import re
+import time
+
+class bridge(moduleBase):
+ """ ifupdown2 addon module to configure linux bridges """
+
+ _modinfo = { 'mhelp' : 'Bridge configuration module. Supports both ' +
+ 'vlan aware and non vlan aware bridges. For the vlan ' +
+ 'aware bridge, the port specific attributes must be ' +
+ 'specified under the port. And for vlan unaware bridge ' +
+ 'port specific attributes must be specified under the ' +
+ 'bridge.',
+ 'attrs' : {
+ 'bridge-vlan-aware' :
+ {'help' : 'vlan aware bridge. Setting this ' +
+ 'attribute to yes enables vlan filtering' +
+ ' on the bridge',
+ 'example' : ['bridge-vlan-aware yes/no']},
+ 'bridge-ports' :
+ {'help' : 'bridge ports',
+ 'required' : True,
+ 'example' : ['bridge-ports swp1.100 swp2.100 swp3.100',
+ 'bridge-ports glob swp1-3.100',
+ 'bridge-ports regex (swp[1|2|3].100)']},
+ 'bridge-stp' :
+ {'help': 'bridge-stp yes/no',
+ 'example' : ['bridge-stp no'],
+ 'validvals' : ['yes', 'on', 'off', 'no'],
+ 'default' : 'no'},
+ 'bridge-bridgeprio' :
+ {'help': 'bridge priority',
+ 'example' : ['bridge-bridgeprio 32768'],
+ 'default' : '32768'},
+ 'bridge-ageing' :
+ {'help': 'bridge ageing',
+ 'example' : ['bridge-ageing 300'],
+ 'default' : '300'},
+ 'bridge-fd' :
+ { 'help' : 'bridge forward delay',
+ 'example' : ['bridge-fd 15'],
+ 'default' : '15'},
+ 'bridge-gcint' :
+ # XXX: recheck values
+ { 'help' : 'bridge garbage collection interval in secs',
+ 'example' : ['bridge-gcint 4'],
+ 'default' : '4'},
+ 'bridge-hello' :
+ { 'help' : 'bridge set hello time',
+ 'example' : ['bridge-hello 2'],
+ 'default' : '2'},
+ 'bridge-maxage' :
+ { 'help' : 'bridge set maxage',
+ 'example' : ['bridge-maxage 20'],
+ 'default' : '20'},
+ 'bridge-pathcosts' :
+ { 'help' : 'bridge set port path costs',
+ 'example' : ['bridge-pathcosts swp1=100 swp2=100'],
+ 'default' : '100'},
+ 'bridge-portprios' :
+ { 'help' : 'bridge port prios',
+ 'example' : ['bridge-portprios swp1=32 swp2=32'],
+ 'default' : '32'},
+ 'bridge-mclmc' :
+ { 'help' : 'set multicast last member count',
+ 'example' : ['bridge-mclmc 2'],
+ 'default' : '2'},
+ 'bridge-mcrouter' :
+ { 'help' : 'set multicast router',
+ 'default' : '1',
+ 'example' : ['bridge-mcrouter 1']},
+ 'bridge-mcsnoop' :
+ { 'help' : 'set multicast snooping',
+ 'default' : '1',
+ 'example' : ['bridge-mcsnoop 1']},
+ 'bridge-mcsqc' :
+ { 'help' : 'set multicast startup query count',
+ 'default' : '2',
+ 'example' : ['bridge-mcsqc 2']},
+ 'bridge-mcqifaddr' :
+ { 'help' : 'set multicast query to use ifaddr',
+ 'default' : '0',
+ 'example' : ['bridge-mcqifaddr 0']},
+ 'bridge-mcquerier' :
+ { 'help' : 'set multicast querier',
+ 'default' : '0',
+ 'example' : ['bridge-mcquerier 0']},
+ 'bridge-hashel' :
+ { 'help' : 'set hash elasticity',
+ 'default' : '4096',
+ 'example' : ['bridge-hashel 4096']},
+ 'bridge-hashmax' :
+ { 'help' : 'set hash max',
+ 'default' : '4096',
+ 'example' : ['bridge-hashmax 4096']},
+ 'bridge-mclmi' :
+ { 'help' : 'set multicast last member interval (in secs)',
+ 'default' : '1',
+ 'example' : ['bridge-mclmi 1']},
+ 'bridge-mcmi' :
+ { 'help' : 'set multicast membership interval (in secs)',
+ 'default' : '260',
+ 'example' : ['bridge-mcmi 260']},
+ 'bridge-mcqpi' :
+ { 'help' : 'set multicast querier interval (in secs)',
+ 'default' : '255',
+ 'example' : ['bridge-mcqpi 255']},
+ 'bridge-mcqi' :
+ { 'help' : 'set multicast query interval (in secs)',
+ 'default' : '125',
+ 'example' : ['bridge-mcqi 125']},
+ 'bridge-mcqri' :
+ { 'help' : 'set multicast query response interval (in secs)',
+ 'default' : '10',
+ 'example' : ['bridge-mcqri 10']},
+ 'bridge-mcsqi' :
+ { 'help' : 'set multicast startup query interval (in secs)',
+ 'default' : '31',
+ 'example' : ['bridge-mcsqi 31']},
+ 'bridge-mcqv4src' :
+ { 'help' : 'set per VLAN v4 multicast querier source address',
+ 'compat' : True,
+ 'example' : ['bridge-mcqv4src 100=172.16.100.1 101=172.16.101.1']},
+ 'bridge-portmcrouter' :
+ { 'help' : 'set port multicast routers',
+ 'default' : '1',
+ 'example' : ['under the bridge: bridge-portmcrouter swp1=1 swp2=1',
+ 'under the port: bridge-portmcrouter 1']},
+ 'bridge-portmcfl' :
+ { 'help' : 'port multicast fast leave.',
+ 'default' : '0',
+ 'example' : ['under the bridge: bridge-portmcfl swp1=0 swp2=0',
+ 'under the port: bridge-portmcfl 0']},
+ 'bridge-waitport' :
+ { 'help' : 'wait for a max of time secs for the' +
+ ' specified ports to become available,' +
+ 'if no ports are specified then those' +
+ ' specified on bridge-ports will be' +
+ ' used here. Specifying no ports here ' +
+ 'should not be used if we are using ' +
+ 'regex or \"all\" on bridge_ports,' +
+ 'as it wouldnt work.',
+ 'default' : '0',
+ 'example' : ['bridge-waitport 4 swp1 swp2']},
+ 'bridge-maxwait' :
+ { 'help' : 'forces to time seconds the maximum time ' +
+ 'that the Debian bridge setup scripts will ' +
+ 'wait for the bridge ports to get to the ' +
+ 'forwarding status, doesn\'t allow factional ' +
+ 'part. If it is equal to 0 then no waiting' +
+ ' is done',
+ 'default' : '0',
+ 'example' : ['bridge-maxwait 3']},
+ 'bridge-vids' :
+ { 'help' : 'bridge port vids. Can be specified ' +
+ 'under the bridge or under the port. ' +
+ 'If specified under the bridge the ports ' +
+ 'inherit it unless overridden by a ' +
+ 'bridge-vids attribuet under the port',
+ 'example' : ['bridge-vids 4000',
+ 'bridge-vids 2000 2200-3000']},
+ 'bridge-pvid' :
+ { 'help' : 'bridge port pvid. Must be specified under' +
+ ' the bridge port',
+ 'example' : ['bridge-pvid 1']},
+ 'bridge-access' :
+ { 'help' : 'bridge port access vlan. Must be ' +
+ 'specified under the bridge port',
+ 'example' : ['bridge-access 300']},
+ 'bridge-port-vids' :
+ { 'help' : 'bridge vlans',
+ 'compat': True,
+ 'example' : ['bridge-port-vids bond0=1-1000,1010-1020']},
+ 'bridge-port-pvids' :
+ { 'help' : 'bridge port vlans',
+ 'compat': True,
+ 'example' : ['bridge-port-pvids bond0=100 bond1=200']},
+ }}
+
+ # declare some ifaceobj priv_flags.
+ # XXX: This assumes that the priv_flags is owned by this module
+ # which it is not.
+ _BRIDGE_PORT_PROCESSED = 0x1
+
+ def __init__(self, *args, **kargs):
+ moduleBase.__init__(self, *args, **kargs)
+ self.ipcmd = None
+ self.brctlcmd = None
+ self._running_vidinfo = {}
+ self._running_vidinfo_valid = False
+ self._resv_vlan_range = self._get_reserved_vlan_range()
+ self.logger.debug('%s: using reserved vlan range %s'
+ %(self.__class__.__name__, str(self._resv_vlan_range)))
+
+ def _is_bridge(self, ifaceobj):
+ if ifaceobj.get_attr_value_first('bridge-ports'):
+ return True
+ return False
+
+ def _is_bridge_port(self, ifaceobj):
+ if self.brctlcmd.is_bridge_port(ifaceobj.name):
+ return True
+ return False
+
+ def get_dependent_ifacenames(self, ifaceobj, ifacenames_all=None):
+ if not self._is_bridge(ifaceobj):
+ return None
+ if ifaceobj.link_type != ifaceLinkType.LINK_NA:
+ ifaceobj.link_type = ifaceLinkType.LINK_MASTER
+ ifaceobj.link_kind = ifaceLinkKind.BRIDGE
+ return self.parse_port_list(ifaceobj.get_attr_value_first(
+ 'bridge-ports'), ifacenames_all)
+
+ def get_dependent_ifacenames_running(self, ifaceobj):
+ self._init_command_handlers()
+ if not self.brctlcmd.bridge_exists(ifaceobj.name):
+ return None
+ return self.brctlcmd.get_bridge_ports(ifaceobj.name)
+
+ def _get_bridge_port_list(self, ifaceobj):
+
+ # port list is also available in the previously
+ # parsed dependent list. Use that if available, instead
+ # of parsing port expr again
+ port_list = ifaceobj.lowerifaces
+ if port_list:
+ return port_list
+ ports = ifaceobj.get_attr_value_first('bridge-ports')
+ if ports:
+ return self.parse_port_list(ports)
+ else:
+ return None
+
+ def _process_bridge_waitport(self, ifaceobj, portlist):
+ waitport_value = ifaceobj.get_attr_value_first('bridge-waitport')
+ if not waitport_value: return
+ try:
+ waitportvals = re.split(r'[\s\t]\s*', waitport_value, 1)
+ if not waitportvals: return
+ try:
+ waitporttime = int(waitportvals[0])
+ except:
+ self.log_warn('%s: invalid waitport value \'%s\''
+ %(ifaceobj.name, waitporttime))
+ return
+ if waitporttime <= 0: return
+ try:
+ waitportlist = self.parse_port_list(waitportvals[1])
+ except IndexError, e:
+ # ignore error and use all bridge ports
+ waitportlist = portlist
+ pass
+ if not waitportlist: return
+ self.logger.info('%s: waiting for ports %s to exist ...'
+ %(ifaceobj.name, str(waitportlist)))
+ starttime = time.time()
+ while ((time.time() - starttime) < waitporttime):
+ if all([False for p in waitportlist
+ if not self.ipcmd.link_exists(p)]):
+ break;
+ time.sleep(1)
+ except Exception, e:
+ self.log_warn('%s: unable to process waitport: %s'
+ %(ifaceobj.name, str(e)))
+
+ def _ports_enable_disable_ipv6(self, ports, enable='1'):
+ for p in ports:
+ try:
+ self.write_file('/proc/sys/net/ipv6/conf/%s' %p +
+ '/disable_ipv6', enable)
+ except Exception, e:
+ self.logger.info(str(e))
+ pass
+
+ def _add_ports(self, ifaceobj):
+ bridgeports = self._get_bridge_port_list(ifaceobj)
+ runningbridgeports = []
+ removedbridgeports = []
+
+ self.ipcmd.batch_start()
+ self._process_bridge_waitport(ifaceobj, bridgeports)
+ self.ipcmd.batch_start()
+ # Delete active ports not in the new port list
+ if not self.PERFMODE:
+ runningbridgeports = self.brctlcmd.get_bridge_ports(ifaceobj.name)
+ if runningbridgeports:
+ for bport in runningbridgeports:
+ if not bridgeports or bport not in bridgeports:
+ self.ipcmd.link_set(bport, 'nomaster')
+ removedbridgeports.append(bport)
+ else:
+ runningbridgeports = []
+ if not bridgeports:
+ self.ipcmd.batch_commit()
+ return
+ err = 0
+ for bridgeport in Set(bridgeports).difference(Set(runningbridgeports)):
+ try:
+ if not self.DRYRUN and not self.ipcmd.link_exists(bridgeport):
+ self.log_warn('%s: bridge port %s does not exist'
+ %(ifaceobj.name, bridgeport))
+ err += 1
+ continue
+ hwaddress = self.ipcmd.link_get_hwaddress(bridgeport)
+ if not self._valid_ethaddr(hwaddress):
+ self.log_warn('%s: skipping port %s, ' %(ifaceobj.name,
+ bridgeport) + 'invalid ether addr %s'
+ %hwaddress)
+ continue
+ self.ipcmd.link_set(bridgeport, 'master', ifaceobj.name)
+ self.ipcmd.addr_flush(bridgeport)
+ except Exception, e:
+ self.logger.error(str(e))
+ pass
+ try:
+ self.ipcmd.batch_commit()
+ except Exception, e:
+ self.logger.error(str(e))
+ pass
+
+ # enable ipv6 for ports that were removed
+ self._ports_enable_disable_ipv6(removedbridgeports, '0')
+ if err:
+ self.log_error('bridge configuration failed (missing ports)')
+
+
+ def _process_bridge_maxwait(self, ifaceobj, portlist):
+ maxwait = ifaceobj.get_attr_value_first('bridge-maxwait')
+ if not maxwait: return
+ try:
+ maxwait = int(maxwait)
+ except:
+ self.log_warn('%s: invalid maxwait value \'%s\'' %(ifaceobj.name,
+ maxwait))
+ return
+ if not maxwait: return
+ self.logger.info('%s: waiting for ports to go to fowarding state ..'
+ %ifaceobj.name)
+ try:
+ starttime = time.time()
+ while ((time.time() - starttime) < maxwait):
+ if all([False for p in portlist
+ if self.read_file_oneline(
+ '/sys/class/net/%s/brif/%s/state'
+ %(ifaceobj.name, p)) != '3']):
+ break;
+ time.sleep(1)
+ except Exception, e:
+ self.log_warn('%s: unable to process maxwait: %s'
+ %(ifaceobj.name, str(e)))
+
+ def _ints_to_ranges(self, ints):
+ for a, b in itertools.groupby(enumerate(ints), lambda (x, y): y - x):
+ b = list(b)
+ yield b[0][1], b[-1][1]
+
+ def _ranges_to_ints(self, rangelist):
+ """ returns expanded list of integers given set of string ranges
+ example: ['1', '2-4', '6'] returns [1, 2, 3, 4, 6]
+ """
+ result = []
+ for part in rangelist:
+ if '-' in part:
+ a, b = part.split('-')
+ a, b = int(a), int(b)
+ result.extend(range(a, b + 1))
+ else:
+ a = int(part)
+ result.append(a)
+ return result
+
+ def _diff_vids(self, vids1, vids2):
+ vids_to_add = None
+ vids_to_del = None
+
+ vids1_ints = self._ranges_to_ints(vids1)
+ vids2_ints = self._ranges_to_ints(vids2)
+ vids1_diff = Set(vids1_ints).difference(vids2_ints)
+ vids2_diff = Set(vids2_ints).difference(vids1_ints)
+ if vids1_diff:
+ vids_to_add = ['%d' %start if start == end else '%d-%d' %(start, end)
+ for start, end in self._ints_to_ranges(vids1_diff)]
+ if vids2_diff:
+ vids_to_del = ['%d' %start if start == end else '%d-%d' %(start, end)
+ for start, end in self._ints_to_ranges(vids2_diff)]
+ return (vids_to_del, vids_to_add)
+
+ def _compare_vids(self, vids1, vids2):
+ """ Returns true if the vids are same else return false """
+
+ vids1_ints = self._ranges_to_ints(vids1)
+ vids2_ints = self._ranges_to_ints(vids2)
+ if Set(vids1_ints).symmetric_difference(vids2_ints):
+ return False
+ else:
+ return True
+
+ def _set_bridge_mcqv4src_compat(self, ifaceobj):
+ #
+ # Sets old style igmp querier
+ #
+ attrval = ifaceobj.get_attr_value_first('bridge-mcqv4src')
+ if attrval:
+ running_mcqv4src = {}
+ if not self.PERFMODE:
+ running_mcqv4src = self.brctlcmd.get_mcqv4src(ifaceobj.name)
+ mcqs = {}
+ srclist = attrval.split()
+ for s in srclist:
+ k, v = s.split('=')
+ mcqs[k] = v
+
+ k_to_del = Set(running_mcqv4src.keys()).difference(mcqs.keys())
+ for v in k_to_del:
+ self.brctlcmd.del_mcqv4src(ifaceobj.name, v)
+ for v in mcqs.keys():
+ self.brctlcmd.set_mcqv4src(ifaceobj.name, v, mcqs[v])
+
+ def _get_running_vidinfo(self):
+ if self._running_vidinfo_valid:
+ return self._running_vidinfo
+ self._running_vidinfo = {}
+ if not self.PERFMODE:
+ self._running_vidinfo = self.ipcmd.bridge_port_vids_get_all()
+ self._running_vidinfo_valid = True
+ return self._running_vidinfo
+
+ def _flush_running_vidinfo(self):
+ self._running_vidinfo = {}
+ self._running_vidinfo_valid = False
+
+ def _set_bridge_vidinfo_compat(self, ifaceobj):
+ #
+ # Supports old style vlan vid info format
+ # for compatibility
+ #
+
+ # Handle bridge vlan attrs
+ running_vidinfo = self._get_running_vidinfo()
+
+ # Install pvids
+ attrval = ifaceobj.get_attr_value_first('bridge-port-pvids')
+ 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, pvid) = p.split('=')
+ running_pvid = running_vidinfo.get(port, {}).get('pvid')
+ if running_pvid:
+ if running_pvid == pvid:
+ continue
+ else:
+ self.ipcmd.bridge_port_pvid_del(port, running_pvid)
+ self.ipcmd.bridge_port_pvid_add(port, pvid)
+ except Exception, e:
+ self.log_warn('%s: failed to set pvid `%s` (%s)'
+ %(ifaceobj.name, p, str(e)))
+
+ # install port vids
+ attrval = ifaceobj.get_attr_value_first('bridge-port-vids')
+ 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('=')
+ vids = val.split(',')
+ if running_vidinfo.get(port):
+ (vids_to_del, vids_to_add) = \
+ self._diff_vids(vids,
+ running_vidinfo.get(port).get('vlan'))
+ if vids_to_del:
+ self.ipcmd.bridge_port_vids_del(port, vids_to_del)
+ if vids_to_add:
+ self.ipcmd.bridge_port_vids_add(port, vids_to_add)
+ else:
+ self.ipcmd.bridge_port_vids_add(port, vids)
+ except Exception, e:
+ self.log_warn('%s: failed to set vid `%s` (%s)'
+ %(ifaceobj.name, p, str(e)))
+
+ # install vids
+ # XXX: Commenting out this code for now because it was decided
+ # that this is not needed
+ #attrval = ifaceobj.get_attr_value_first('bridge-vids')
+ #if attrval:
+ # vids = re.split(r'[\s\t]\s*', attrval)
+ # if running_vidinfo.get(ifaceobj.name):
+ # (vids_to_del, vids_to_add) = \
+ # self._diff_vids(vids,
+ # running_vidinfo.get(ifaceobj.name).get('vlan'))
+ # if vids_to_del:
+ # self.ipcmd.bridge_vids_del(ifaceobj.name, vids_to_del)
+ # if vids_to_add:
+ # self.ipcmd.bridge_vids_add(ifaceobj.name, vids_to_add)
+ # else:
+ # self.ipcmd.bridge_vids_add(ifaceobj.name, vids)
+ #else:
+ # running_vids = running_vidinfo.get(ifaceobj.name)
+ # if running_vids:
+ # self.ipcmd.bridge_vids_del(ifaceobj.name, running_vids)
+
+ def _apply_bridge_settings(self, ifaceobj):
+ try:
+ stp = ifaceobj.get_attr_value_first('bridge-stp')
+ if stp:
+ self.brctlcmd.set_stp(ifaceobj.name, stp)
+ else:
+ # If stp not specified and running stp state on, set it to off
+ running_stp_state = self.read_file_oneline(
+ '/sys/class/net/%s/bridge/stp_state' %ifaceobj.name)
+ if running_stp_state and running_stp_state != '0':
+ self.brctlcmd.set_stp(ifaceobj.name, 'no')
+
+ if ifaceobj.get_attr_value_first('bridge-vlan-aware') == 'yes':
+ self.write_file('/sys/class/net/%s/bridge/vlan_filtering'
+ %ifaceobj.name, '1')
+ # Use the brctlcmd bulk set method: first build a dictionary
+ # and then call set
+ bridgeattrs = { k:v for k,v in
+ {'ageing' :
+ ifaceobj.get_attr_value_first('bridge-ageing'),
+ 'bridgeprio' :
+ ifaceobj.get_attr_value_first(
+ 'bridge-bridgeprio'),
+ 'fd' :
+ ifaceobj.get_attr_value_first('bridge-fd'),
+ 'gcint' :
+ ifaceobj.get_attr_value_first('bridge-gcint'),
+ 'hello' :
+ ifaceobj.get_attr_value_first('bridge-hello'),
+ 'maxage' :
+ ifaceobj.get_attr_value_first('bridge-maxage'),
+ 'mclmc' :
+ ifaceobj.get_attr_value_first('bridge-mclmc'),
+ 'mcrouter' :
+ ifaceobj.get_attr_value_first(
+ 'bridge-mcrouter'),
+ 'mcsnoop' :
+ ifaceobj.get_attr_value_first('bridge-mcsnoop'),
+ 'mcsqc' :
+ ifaceobj.get_attr_value_first('bridge-mcsqc'),
+ 'mcqifaddr' :
+ ifaceobj.get_attr_value_first(
+ 'bridge-mcqifaddr'),
+ 'mcquerier' :
+ ifaceobj.get_attr_value_first(
+ 'bridge-mcquerier'),
+ 'hashel' :
+ ifaceobj.get_attr_value_first('bridge-hashel'),
+ 'hashmax' :
+ ifaceobj.get_attr_value_first('bridge-hashmax'),
+ 'mclmi' :
+ ifaceobj.get_attr_value_first('bridge-mclmi'),
+ 'mcmi' :
+ ifaceobj.get_attr_value_first('bridge-mcmi'),
+ 'mcqpi' :
+ ifaceobj.get_attr_value_first('bridge-mcqpi'),
+ 'mcqi' :
+ ifaceobj.get_attr_value_first('bridge-mcqi'),
+ 'mcqri' :
+ ifaceobj.get_attr_value_first('bridge-mcqri'),
+ 'mcsqi' :
+ ifaceobj.get_attr_value_first('bridge-mcsqi')
+ }.items()
+ if v }
+ if bridgeattrs:
+ self.brctlcmd.set_bridge_attrs(ifaceobj.name, bridgeattrs)
+ portattrs = {}
+ for attrname, dstattrname in {'bridge-pathcosts' : 'pathcost',
+ 'bridge-portprios' : 'portprio',
+ 'bridge-portmcrouter' : 'portmcrouter',
+ 'bridge-portmcfl' : 'portmcfl'}.items():
+ attrval = ifaceobj.get_attr_value_first(attrname)
+ if not attrval:
+ continue
+ portlist = self.parse_port_list(attrval)
+ if not portlist:
+ self.log_warn('%s: could not parse \'%s %s\''
+ %(ifaceobj.name, attrname, attrval))
+ continue
+ for p in portlist:
+ try:
+ (port, val) = p.split('=')
+ if not portattrs.get(port):
+ portattrs[port] = {}
+ portattrs[port].update({dstattrname : val})
+ except Exception, e:
+ self.log_warn('%s: could not parse %s (%s)'
+ %(ifaceobj.name, attrname, str(e)))
+ for port, attrdict in portattrs.iteritems():
+ try:
+ self.brctlcmd.set_bridgeport_attrs(ifaceobj.name, port,
+ attrdict)
+ except Exception, e:
+ self.log_warn('%s: %s', str(e))
+ pass
+ self._set_bridge_vidinfo_compat(ifaceobj)
+ self._set_bridge_mcqv4src_compat(ifaceobj)
+ self._process_bridge_maxwait(ifaceobj,
+ self._get_bridge_port_list(ifaceobj))
+ except Exception, e:
+ self.log_warn(str(e))
+
+ def _check_vids(self, ifaceobj, vids):
+ ret = True
+ for v in vids:
+ if '-' in v:
+ va, vb = v.split('-')
+ va, vb = int(va), int(vb)
+ if (self._handle_reserved_vlan(va, ifaceobj.name) or
+ self._handle_reserved_vlan(vb, ifaceobj.name)):
+ ret = False
+ else:
+ va = int(v)
+ if self._handle_reserved_vlan(va, ifaceobj.name):
+ ret = False
+ return ret
+
+ def _apply_bridge_vids(self, bportifaceobj, vids, running_vids, isbridge):
+ try:
+ if not self._check_vids(bportifaceobj, vids):
+ return
+ if running_vids:
+ (vids_to_del, vids_to_add) = \
+ self._diff_vids(vids, running_vids)
+ if vids_to_del:
+ self.ipcmd.bridge_vids_del(bportifaceobj.name,
+ vids_to_del, isbridge)
+ if vids_to_add:
+ self.ipcmd.bridge_vids_add(bportifaceobj.name,
+ vids_to_add, isbridge)
+ else:
+ self.ipcmd.bridge_vids_add(bportifaceobj.name, vids, isbridge)
+ except Exception, e:
+ self.log_warn('%s: failed to set vid `%s` (%s)'
+ %(bportifaceobj.name, str(vids), str(e)))
+
+ def _apply_bridge_port_pvids(self, bportifaceobj, pvid, running_pvid):
+ # Install pvids
+ try:
+ if running_pvid:
+ if running_pvid != pvid:
+ self.ipcmd.bridge_port_pvid_del(bportifaceobj.name,
+ running_pvid)
+ self.ipcmd.bridge_port_pvid_add(bportifaceobj.name, pvid)
+ else:
+ self.ipcmd.bridge_port_pvid_add(bportifaceobj.name, pvid)
+ except Exception, e:
+ self.log_warn('%s: failed to set pvid `%s` (%s)'
+ %(bportifaceobj.name, pvid, str(e)))
+
+ def _apply_bridge_vlan_aware_port_settings_all(self, bportifaceobj,
+ bridge_vids=None,
+ bridge_pvid=None):
+ running_vidinfo = self._get_running_vidinfo()
+ vids = None
+ pvids = None
+ bport_access = bportifaceobj.get_attr_value_first('bridge-access')
+ if bport_access:
+ vids = re.split(r'[\s\t]\s*', bport_access)
+ pvids = vids
+ else:
+ bport_vids = bportifaceobj.get_attr_value_first('bridge-vids')
+ if bport_vids:
+ vids = re.split(r'[\s\t,]\s*', bport_vids)
+
+ bport_pvids = bportifaceobj.get_attr_value_first('bridge-pvid')
+ if bport_pvids:
+ pvids = re.split(r'[\s\t]\s*', bport_pvids)
+
+ if pvids:
+ self._apply_bridge_port_pvids(bportifaceobj, pvids[0],
+ running_vidinfo.get(bportifaceobj.name, {}).get('pvid'))
+ elif bridge_pvid:
+ self._apply_bridge_port_pvids(bportifaceobj,
+ bridge_pvid, running_vidinfo.get(bportifaceobj.name,
+ {}).get('pvid'))
+ # XXX: default pvid is already one
+ #else:
+ # self._apply_bridge_port_pvids(bportifaceobj,
+ # '1', running_vidinfo.get(bportifaceobj.name,
+ # {}).get('pvid'))
+
+ if vids:
+ self._apply_bridge_vids(bportifaceobj, vids,
+ running_vidinfo.get(bportifaceobj.name,
+ {}).get('vlan'), False)
+ elif bridge_vids:
+ self._apply_bridge_vids(bportifaceobj,
+ bridge_vids, running_vidinfo.get(
+ bportifaceobj.name, {}).get('vlan'), False)
+
+
+ def _apply_bridge_port_settings(self, bportifaceobj, bridgename=None,
+ bridgeifaceobj=None):
+ if not bridgename and bridgeifaceobj:
+ bridgename = bridgeifaceobj.name
+ # Set other stp and igmp attributes
+ portattrs = {}
+ for attrname, dstattrname in {
+ 'bridge-pathcosts' : 'pathcost',
+ 'bridge-portprios' : 'portprio',
+ 'bridge-portmcrouter' : 'portmcrouter',
+ 'bridge-portmcfl' : 'portmcfl'}.items():
+ attrval = bportifaceobj.get_attr_value_first(attrname)
+ if not attrval:
+ # Check if bridge has that attribute
+ #if bridgeifaceobj:
+ # attrval = bridgeifaceobj.get_attr_value_first(attrname)
+ # if not attrval:
+ # continue
+ #else:
+ continue
+ portattrs[dstattrname] = attrval
+ try:
+ self.brctlcmd.set_bridgeport_attrs(bridgename,
+ bportifaceobj.name, portattrs)
+ except Exception, e:
+ self.log_warn(str(e))
+
+ def _apply_bridge_port_settings_all(self, ifaceobj,
+ ifaceobj_getfunc=None):
+ err = False
+ bridge_vlan_aware = ifaceobj.get_attr_value_first(
+ 'bridge-vlan-aware')
+ if bridge_vlan_aware and bridge_vlan_aware == 'yes':
+ bridge_vlan_aware = True
+ else:
+ bridge_vlan_aware = False
+
+ if (ifaceobj.get_attr_value_first('bridge-port-vids') and
+ ifaceobj.get_attr_value_first('bridge-port-pvids')):
+ # Old style bridge port vid info
+ # skip new style setting on ports
+ return
+ self.logger.info('%s: applying bridge configuration '
+ %ifaceobj.name + 'specific to ports')
+
+ bridge_vids = ifaceobj.get_attr_value_first('bridge-vids')
+ if bridge_vids:
+ bridge_vids = re.split(r'[\s\t,]\s*', bridge_vids)
+ else:
+ bridge_vids = None
+
+ bridge_pvid = ifaceobj.get_attr_value_first('bridge-pvid')
+ if bridge_pvid:
+ bridge_pvid = re.split(r'[\s\t]\s*', bridge_pvid)[0]
+ else:
+ bridge_pvid = None
+
+ bridgeports = self._get_bridge_port_list(ifaceobj)
+ if not bridgeports:
+ self.logger.debug('%s: cannot find bridgeports' %ifaceobj.name)
+ return
+ for bport in bridgeports:
+ # Use the brctlcmd bulk set method: first build a dictionary
+ # and then call set
+ if not self.ipcmd.bridge_port_exists(ifaceobj.name, bport):
+ self.logger.info('%s: skipping bridge config' %ifaceobj.name +
+ ' for port %s (missing port)' %bport)
+ continue
+ self.logger.info('%s: processing bridge config for port %s'
+ %(ifaceobj.name, bport))
+ bportifaceobjlist = ifaceobj_getfunc(bport)
+ if not bportifaceobjlist:
+ continue
+ for bportifaceobj in bportifaceobjlist:
+ # Dont process bridge port if it already has been processed
+ if bportifaceobj.priv_flags & self._BRIDGE_PORT_PROCESSED:
+ continue
+ try:
+ # Add attributes specific to the vlan aware bridge
+ if bridge_vlan_aware:
+ self._apply_bridge_vlan_aware_port_settings_all(
+ bportifaceobj, bridge_vids, bridge_pvid)
+ self._apply_bridge_port_settings(bportifaceobj,
+ bridgeifaceobj=ifaceobj)
+ except Exception, e:
+ err = True
+ self.logger.warn('%s: %s' %(ifaceobj.name, str(e)))
+ pass
+ if err:
+ raise Exception('%s: errors applying port settings' %ifaceobj.name)
+
+ def _up(self, ifaceobj, ifaceobj_getfunc=None):
+ # Check if bridge port
+ bridgename = self.ipcmd.bridge_port_get_bridge_name(ifaceobj.name)
+ if bridgename:
+ if self.ipcmd.bridge_is_vlan_aware(bridgename):
+ bridge_vids = self._get_bridge_vids(bridgename,
+ ifaceobj_getfunc)
+ bridge_pvid = self._get_bridge_pvid(bridgename,
+ ifaceobj_getfunc)
+ self._apply_bridge_vlan_aware_port_settings_all(ifaceobj,
+ bridge_vids,
+ bridge_pvid)
+ self._apply_bridge_port_settings(ifaceobj, bridgename=bridgename)
+ ifaceobj.priv_flags |= self._BRIDGE_PORT_PROCESSED
+ return
+ if not self._is_bridge(ifaceobj):
+ return
+ err = False
+ errstr = ''
+ running_ports = ''
+ try:
+ if not self.PERFMODE:
+ if not self.ipcmd.link_exists(ifaceobj.name):
+ self.ipcmd.link_create(ifaceobj.name, 'bridge')
+ else:
+ self.ipcmd.link_create(ifaceobj.name, 'bridge')
+ except Exception, e:
+ raise Exception(str(e))
+ try:
+ self._add_ports(ifaceobj)
+ except Exception, e:
+ err = True
+ errstr = str(e)
+ pass
+
+ try:
+ self._apply_bridge_settings(ifaceobj)
+ except Exception, e:
+ err = True
+ errstr = str(e)
+ pass
+
+ try:
+ running_ports = self.brctlcmd.get_bridge_ports(ifaceobj.name)
+ if not running_ports:
+ return
+ # disable ipv6 for ports that were added to bridge
+ self._ports_enable_disable_ipv6(running_ports, '1')
+ self._apply_bridge_port_settings_all(ifaceobj,
+ ifaceobj_getfunc=ifaceobj_getfunc)
+ except Exception, e:
+ err = True
+ errstr = str(e)
+ pass
+ #self._flush_running_vidinfo()
+ finally:
+ if ifaceobj.link_type != ifaceLinkType.LINK_NA:
+ for p in running_ports:
+ try:
+ rtnetlink_api.rtnl_api.link_set(p, "up")
+ except Exception, e:
+ self.logger.debug('%s: %s: link set up (%s)'
+ %(ifaceobj.name, p, str(e)))
+ pass
+
+ if ifaceobj.addr_method == 'manual':
+ rtnetlink_api.rtnl_api.link_set(ifaceobj.name, "up")
+ if err:
+ raise Exception(errstr)
+
+ def _down(self, ifaceobj, ifaceobj_getfunc=None):
+ try:
+ if ifaceobj.get_attr_value_first('bridge-ports'):
+ ports = self.brctlcmd.get_bridge_ports(ifaceobj.name)
+ self.brctlcmd.delete_bridge(ifaceobj.name)
+ if ports:
+ self._ports_enable_disable_ipv6(ports, '0')
+ if ifaceobj.link_type != ifaceLinkType.LINK_NA:
+ map(lambda p: rtnetlink_api.rtnl_api.link_set(p,
+ "down"), ports)
+ except Exception, e:
+ self.log_error(str(e))
+
+ def _query_running_vidinfo_compat(self, ifaceobjrunning, ports):
+ running_attrs = {}
+ running_vidinfo = self._get_running_vidinfo()
+ if ports:
+ running_bridge_port_vids = ''
+ for p in ports:
+ try:
+ running_vids = running_vidinfo.get(p, {}).get('vlan')
+ if running_vids:
+ running_bridge_port_vids += ' %s=%s' %(p,
+ ','.join(running_vids))
+ except Exception:
+ pass
+ running_attrs['bridge-port-vids'] = running_bridge_port_vids
+
+ running_bridge_port_pvids = ''
+ for p in ports:
+ try:
+ running_pvids = running_vidinfo.get(p, {}).get('pvid')
+ if running_pvids:
+ running_bridge_port_pvids += ' %s=%s' %(p,
+ running_pvids)
+ except Exception:
+ pass
+ running_attrs['bridge-port-pvids'] = running_bridge_port_pvids
+
+ running_bridge_vids = running_vidinfo.get(ifaceobjrunning.name,
+ {}).get('vlan')
+ if running_bridge_vids:
+ running_attrs['bridge-vids'] = ','.join(running_bridge_vids)
+ return running_attrs
+
+ def _query_running_vidinfo(self, ifaceobjrunning, ifaceobj_getfunc,
+ bridgeports=None):
+ running_attrs = {}
+ running_vidinfo = self._get_running_vidinfo()
+ if not running_vidinfo:
+ return running_attrs
+
+ # 'bridge-vids' under the bridge is all about 'vids' on the port.
+ # so query the ports
+ running_bridgeport_vids = []
+ running_bridgeport_pvids = []
+ for bport in bridgeports:
+ vids = running_vidinfo.get(bport, {}).get('vlan')
+ if vids:
+ running_bridgeport_vids.append(' '.join(vids))
+ pvids = running_vidinfo.get(bport, {}).get('pvid')
+ if pvids:
+ running_bridgeport_pvids.append(pvids[0])
+
+ bridge_vids = None
+ if running_bridgeport_vids:
+ (vidval, freq) = Counter(running_bridgeport_vids).most_common()[0]
+ if freq == len(bridgeports):
+ running_attrs['bridge-vids'] = vidval
+ bridge_vids = vidval.split()
+
+ bridge_pvid = None
+ if running_bridgeport_pvids:
+ (vidval, freq) = Counter(running_bridgeport_pvids).most_common()[0]
+ if freq == len(bridgeports) and vidval != '1':
+ running_attrs['bridge-pvid'] = vidval
+ bridge_pvid = vidval.split()
+
+ # Go through all bridge ports and find their vids
+ for bport in bridgeports:
+ bportifaceobj = ifaceobj_getfunc(bport)
+ if not bportifaceobj:
+ continue
+ bport_vids = None
+ bport_pvids = None
+ vids = running_vidinfo.get(bport, {}).get('vlan')
+ if vids and vids != bridge_vids:
+ bport_vids = vids
+ pvids = running_vidinfo.get(bport, {}).get('pvid')
+ if pvids and pvids[0] != bridge_pvid:
+ bport_pvids = pvids
+ if not bport_vids and bport_pvids and bport_pvids[0] != '1':
+ bportifaceobj[0].replace_config('bridge-access', bport_pvids[0])
+ else:
+ if bport_pvids and bport_pvids[0] != '1':
+ bportifaceobj[0].replace_config('bridge-pvid', bport_pvids[0])
+ else:
+ # delete any stale bridge-vids under ports
+ bportifaceobj[0].delete_config('bridge-pvid')
+ if bport_vids:
+ bportifaceobj[0].replace_config('bridge-vids',
+ ' '.join(bport_vids))
+ else:
+ # delete any stale bridge-vids under ports
+ bportifaceobj[0].delete_config('bridge-vids')
+ return running_attrs
+
+ def _query_running_mcqv4src(self, ifaceobjrunning):
+ running_mcqv4src = self.brctlcmd.get_mcqv4src(ifaceobjrunning.name)
+ mcqs = ['%s=%s' %(v, i) for v, i in running_mcqv4src.items()]
+ mcqs.sort()
+ mcq = ' '.join(mcqs)
+ return mcq
+
+ def _query_running_attrs(self, ifaceobjrunning, ifaceobj_getfunc,
+ bridge_vlan_aware=False):
+ bridgeattrdict = {}
+ userspace_stp = 0
+ ports = None
+ skip_kernel_stp_attrs = 0
+
+ if self.sysctl_get('net.bridge.bridge-stp-user-space') == '1':
+ userspace_stp = 1
+
+ tmpbridgeattrdict = self.brctlcmd.get_bridge_attrs(ifaceobjrunning.name)
+ if not tmpbridgeattrdict:
+ self.logger.warn('%s: unable to get bridge attrs'
+ %ifaceobjrunning.name)
+ return bridgeattrdict
+
+ # Fill bridge_ports and bridge stp attributes first
+ ports = tmpbridgeattrdict.get('ports')
+ if ports:
+ bridgeattrdict['bridge-ports'] = [' '.join(ports.keys())]
+ stp = tmpbridgeattrdict.get('stp', 'no')
+ if stp != self.get_mod_subattr('bridge-stp', 'default'):
+ bridgeattrdict['bridge-stp'] = [stp]
+
+ if stp == 'yes' and userspace_stp:
+ skip_kernel_stp_attrs = 1
+
+ # pick all other attributes
+ for k,v in tmpbridgeattrdict.items():
+ if not v:
+ continue
+ if k == 'ports' or k == 'stp':
+ continue
+
+ if skip_kernel_stp_attrs and k[:2] != 'mc':
+ # only include igmp attributes if kernel stp is off
+ continue
+ attrname = 'bridge-' + k
+ if v != self.get_mod_subattr(attrname, 'default'):
+ bridgeattrdict[attrname] = [v]
+
+ if bridge_vlan_aware:
+ bridgevidinfo = self._query_running_vidinfo(ifaceobjrunning,
+ ifaceobj_getfunc,
+ ports.keys())
+ else:
+ bridgevidinfo = self._query_running_vidinfo_compat(ifaceobjrunning,
+ ports)
+ if bridgevidinfo:
+ bridgeattrdict.update({k : [v] for k, v in bridgevidinfo.items()
+ if v})
+
+ mcq = self._query_running_mcqv4src(ifaceobjrunning)
+ if mcq:
+ bridgeattrdict['bridge-mcqv4src'] = [mcq]
+
+ if skip_kernel_stp_attrs:
+ return bridgeattrdict
+
+ if ports:
+ portconfig = {'bridge-pathcosts' : '',
+ 'bridge-portprios' : ''}
+ for p, v in ports.items():
+ v = self.brctlcmd.get_pathcost(ifaceobjrunning.name, p)
+ if v and v != self.get_mod_subattr('bridge-pathcosts',
+ 'default'):
+ portconfig['bridge-pathcosts'] += ' %s=%s' %(p, v)
+
+ v = self.brctlcmd.get_portprio(ifaceobjrunning.name, p)
+ if v and v != self.get_mod_subattr('bridge-portprios',
+ 'default'):
+ portconfig['bridge-portprios'] += ' %s=%s' %(p, v)
+
+ bridgeattrdict.update({k : [v] for k, v in portconfig.items()
+ if v})
+
+ return bridgeattrdict
+
+ def _query_check_mcqv4src(self, ifaceobj, ifaceobjcurr):
+ running_mcqs = self._query_running_mcqv4src(ifaceobj)
+ attrval = ifaceobj.get_attr_value_first('bridge-mcqv4src')
+ if attrval:
+ mcqs = attrval.split()
+ mcqs.sort()
+ mcqsout = ' '.join(mcqs)
+ ifaceobjcurr.update_config_with_status('bridge-mcqv4src',
+ running_mcqs, 1 if running_mcqs != mcqsout else 0)
+
+ def _query_check_bridge_vidinfo(self, ifaceobj, ifaceobjcurr):
+ err = 0
+ running_vidinfo = self._get_running_vidinfo()
+ attrval = ifaceobj.get_attr_value_first('bridge-port-vids')
+ if attrval:
+ running_bridge_port_vids = ''
+ portlist = self.parse_port_list(attrval)
+ if not portlist:
+ self.log_warn('%s: could not parse \'%s %s\''
+ %(ifaceobj.name, attrname, attrval))
+ return
+ err = 0
+ for p in portlist:
+ try:
+ (port, val) = p.split('=')
+ vids = val.split(',')
+ running_vids = running_vidinfo.get(port, {}).get('vlan')
+ if running_vids:
+ if not self._compare_vids(vids, running_vids):
+ err += 1
+ running_bridge_port_vids += ' %s=%s' %(port,
+ ','.join(running_vids))
+ else:
+ running_bridge_port_vids += ' %s' %p
+ else:
+ err += 1
+ except Exception, e:
+ self.log_warn('%s: failure checking vid %s (%s)'
+ %(ifaceobj.name, p, str(e)))
+ if err:
+ ifaceobjcurr.update_config_with_status('bridge-port-vids',
+ running_bridge_port_vids, 1)
+ else:
+ ifaceobjcurr.update_config_with_status('bridge-port-vids',
+ attrval, 0)
+
+ attrval = ifaceobj.get_attr_value_first('bridge-port-pvids')
+ 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
+ running_bridge_port_pvids = ''
+ err = 0
+ for p in portlist:
+ try:
+ (port, pvid) = p.split('=')
+ running_pvid = running_vidinfo.get(port, {}).get('pvid')
+ if running_pvid and running_pvid == pvid:
+ running_bridge_port_pvids += ' %s' %p
+ else:
+ err += 1
+ running_bridge_port_pvids += ' %s=%s' %(port,
+ running_pvid)
+ except Exception, e:
+ self.log_warn('%s: failure checking pvid %s (%s)'
+ %(ifaceobj.name, pvid, str(e)))
+ if err:
+ ifaceobjcurr.update_config_with_status('bridge-port-pvids',
+ running_bridge_port_pvids, 1)
+ else:
+ ifaceobjcurr.update_config_with_status('bridge-port-pvids',
+ running_bridge_port_pvids, 0)
+
+ # XXX: No need to check for bridge-vids on the bridge
+ # This is used by the ports. The vids on the bridge
+ # come from the vlan interfaces on the bridge.
+ #
+ attrval = ifaceobj.get_attr_value_first('bridge-vids')
+ #if attrval:
+ # vids = re.split(r'[\s\t]\s*', attrval)
+ # running_vids = running_vidinfo.get(ifaceobj.name, {}).get('vlan')
+ # if running_vids:
+ # if self._compare_vids(vids, running_vids):
+ # ifaceobjcurr.update_config_with_status('bridge-vids',
+ # attrval, 0)
+ # else:
+ # ifaceobjcurr.update_config_with_status('bridge-vids',
+ # ','.join(running_vids), 1)
+ # else:
+ # ifaceobjcurr.update_config_with_status('bridge-vids', attrval,
+ # 1)
+ if attrval:
+ ifaceobjcurr.update_config_with_status('bridge-vids', attrval, -1)
+
+ def _query_check_bridge(self, ifaceobj, ifaceobjcurr,
+ ifaceobj_getfunc=None):
+ if not self._is_bridge(ifaceobj):
+ return
+ if not self.brctlcmd.bridge_exists(ifaceobj.name):
+ self.logger.info('%s: bridge: does not exist' %(ifaceobj.name))
+ return
+
+ ifaceattrs = self.dict_key_subset(ifaceobj.config,
+ self.get_mod_attrs())
+ if not ifaceattrs:
+ return
+ try:
+ runningattrs = self.brctlcmd.get_bridge_attrs(ifaceobj.name)
+ if not runningattrs:
+ self.logger.debug('%s: bridge: unable to get bridge attrs'
+ %ifaceobj.name)
+ runningattrs = {}
+ except Exception, e:
+ self.logger.warn(str(e))
+ runningattrs = {}
+ filterattrs = ['bridge-vids', 'bridge-port-vids',
+ 'bridge-port-pvids']
+ for k in Set(ifaceattrs).difference(filterattrs):
+ # get the corresponding ifaceobj attr
+ v = ifaceobj.get_attr_value_first(k)
+ if not v:
+ continue
+ rv = runningattrs.get(k[7:])
+ if k == 'bridge-mcqv4src':
+ continue
+ if k == 'bridge-vlan-aware' and v == 'yes':
+ if self.ipcmd.bridge_is_vlan_aware(ifaceobj.name):
+ ifaceobjcurr.update_config_with_status('bridge-vlan-aware',
+ v, 0)
+ else:
+ ifaceobjcurr.update_config_with_status('bridge-vlan-aware',
+ v, 1)
+ elif k == 'bridge-stp':
+ # special case stp compare because it may
+ # contain more than one valid values
+ stp_on_vals = ['on', 'yes']
+ stp_off_vals = ['off']
+ if ((v in stp_on_vals and rv in stp_on_vals) or
+ (v in stp_off_vals and rv in stp_off_vals)):
+ ifaceobjcurr.update_config_with_status('bridge-stp',
+ v, 0)
+ else:
+ ifaceobjcurr.update_config_with_status('bridge-stp',
+ v, 1)
+ elif k == 'bridge-ports':
+ # special case ports because it can contain regex or glob
+ running_port_list = rv.keys() if rv else []
+ bridge_port_list = self._get_bridge_port_list(ifaceobj)
+ if not running_port_list and not bridge_port_list:
+ continue
+ portliststatus = 1
+ if running_port_list and bridge_port_list:
+ difference = set(running_port_list
+ ).symmetric_difference(bridge_port_list)
+ if not difference:
+ portliststatus = 0
+ ifaceobjcurr.update_config_with_status('bridge-ports',
+ ' '.join(running_port_list)
+ if running_port_list else '', portliststatus)
+ elif (k == 'bridge-pathcosts' or
+ k == 'bridge-portprios' or k == 'bridge-portmcrouter'
+ or k == 'bridge-portmcfl'):
+ brctlcmdattrname = k[11:].rstrip('s')
+ # for port attributes, the attributes are in a list
+ # <portname>=<portattrvalue>
+ status = 0
+ currstr = ''
+ vlist = self.parse_port_list(v)
+ if not vlist:
+ continue
+ for vlistitem in vlist:
+ try:
+ (p, v) = vlistitem.split('=')
+ currv = self.brctlcmd.get_bridgeport_attr(
+ ifaceobj.name, p,
+ brctlcmdattrname)
+ if currv:
+ currstr += ' %s=%s' %(p, currv)
+ else:
+ currstr += ' %s=%s' %(p, 'None')
+ if currv != v:
+ status = 1
+ except Exception, e:
+ self.log_warn(str(e))
+ pass
+ ifaceobjcurr.update_config_with_status(k, currstr, status)
+ elif not rv:
+ ifaceobjcurr.update_config_with_status(k, 'notfound', 1)
+ continue
+ elif v != rv:
+ ifaceobjcurr.update_config_with_status(k, rv, 1)
+ else:
+ ifaceobjcurr.update_config_with_status(k, rv, 0)
+
+ self._query_check_bridge_vidinfo(ifaceobj, ifaceobjcurr)
+
+ self._query_check_mcqv4src(ifaceobj, ifaceobjcurr)
+
+ def _get_bridge_vids(self, bridgename, ifaceobj_getfunc):
+ ifaceobjs = ifaceobj_getfunc(bridgename)
+ for ifaceobj in ifaceobjs:
+ vids = ifaceobj.get_attr_value_first('bridge-vids')
+ if vids: return re.split(r'[\s\t,]\s*', vids)
+ return None
+
+ def _get_bridge_pvid(self, bridgename, ifaceobj_getfunc):
+ ifaceobjs = ifaceobj_getfunc(bridgename)
+ pvid = None
+ for ifaceobj in ifaceobjs:
+ pvid = ifaceobj.get_attr_value_first('bridge-pvid')
+ return pvid
+
+ def _get_bridge_name(self, ifaceobj):
+ return self.ipcmd.bridge_port_get_bridge_name(ifaceobj.name)
+
+ def _query_check_bridge_port_vidinfo(self, ifaceobj, ifaceobjcurr,
+ ifaceobj_getfunc, bridgename):
+ running_vidinfo = self._get_running_vidinfo()
+
+ attr_name = 'bridge-access'
+ vids = ifaceobj.get_attr_value_first(attr_name)
+ if vids:
+ running_pvids = running_vidinfo.get(ifaceobj.name,
+ {}).get('pvid')
+ running_vids = running_vidinfo.get(ifaceobj.name,
+ {}).get('vlan')
+ if (not running_pvids or running_pvids != vids or
+ running_vids):
+ ifaceobjcurr.update_config_with_status(attr_name,
+ running_pvids, 1)
+ else:
+ ifaceobjcurr.update_config_with_status(attr_name, vids, 0)
+ return
+
+ attr_name = 'bridge-vids'
+ vids = ifaceobj.get_attr_value_first(attr_name)
+ if vids:
+ vids = re.split(r'[\s\t]\s*', vids)
+ running_vids = running_vidinfo.get(ifaceobj.name,
+ {}).get('vlan')
+ if not running_vids or not self._compare_vids(vids, running_vids):
+ ifaceobjcurr.update_config_with_status(attr_name,
+ ' '.join(running_vids), 1)
+ else:
+ ifaceobjcurr.update_config_with_status(attr_name,
+ ' '.join(running_vids), 0)
+ else:
+ # check if it matches the bridge vids
+ bridge_vids = self._get_bridge_vids(bridgename, ifaceobj_getfunc)
+ running_vids = running_vidinfo.get(ifaceobj.name,
+ {}).get('vlan')
+ if (bridge_vids and (not running_vids or
+ not self._compare_vids(bridge_vids, running_vids))):
+ ifaceobjcurr.status = ifaceStatus.ERROR
+ ifaceobjcurr.status_str = 'bridge vid error'
+
+ running_pvid = running_vidinfo.get(ifaceobj.name,
+ {}).get('pvid')
+ attr_name = 'bridge-pvid'
+ pvid = ifaceobj.get_attr_value_first(attr_name)
+ if pvid:
+ if running_pvid and running_pvid == pvid:
+ ifaceobjcurr.update_config_with_status(attr_name,
+ running_pvid, 0)
+ else:
+ ifaceobjcurr.update_config_with_status(attr_name,
+ running_pvid, 1)
+ elif not running_pvid or running_pvid != '1':
+ ifaceobjcurr.status = ifaceStatus.ERROR
+ ifaceobjcurr.status_str = 'bridge pvid error'
+
+ def _query_check_bridge_port(self, ifaceobj, ifaceobjcurr,
+ ifaceobj_getfunc):
+ if not self._is_bridge_port(ifaceobj):
+ # Mark all bridge attributes as failed
+ ifaceobjcurr.check_n_update_config_with_status_many(ifaceobj,
+ ['bridge-vids', 'bridge-pvid', 'bridge-access',
+ 'bridge-pathcosts', 'bridge-portprios',
+ 'bridge-portmcrouter',
+ 'bridge-portmcfl'], 1)
+ return
+ bridgename = self._get_bridge_name(ifaceobj)
+ if not bridgename:
+ self.logger.warn('%s: unable to determine bridge name'
+ %ifaceobj.name)
+ return
+
+ if self.ipcmd.bridge_is_vlan_aware(bridgename):
+ self._query_check_bridge_port_vidinfo(ifaceobj, ifaceobjcurr,
+ ifaceobj_getfunc,
+ bridgename)
+ for attr, dstattr in {'bridge-pathcosts' : 'pathcost',
+ 'bridge-portprios' : 'priority',
+ 'bridge-portmcrouter' : 'mcrouter',
+ 'bridge-portmcfl' : 'mcfl' }.items():
+ attrval = ifaceobj.get_attr_value_first(attr)
+ if not attrval:
+ continue
+
+ try:
+ running_attrval = self.brctlcmd.get_bridgeport_attr(
+ bridgename, ifaceobj.name, dstattr)
+ if running_attrval != attrval:
+ ifaceobjcurr.update_config_with_status(attr,
+ running_attrval, 1)
+ else:
+ ifaceobjcurr.update_config_with_status(attr,
+ running_attrval, 0)
+ except Exception, e:
+ self.log_warn('%s: %s' %(ifaceobj.name, str(e)))
+
+ def _query_check(self, ifaceobj, ifaceobjcurr, ifaceobj_getfunc=None):
+ if self._is_bridge(ifaceobj):
+ self._query_check_bridge(ifaceobj, ifaceobjcurr)
+ else:
+ self._query_check_bridge_port(ifaceobj, ifaceobjcurr,
+ ifaceobj_getfunc)
+
+ def _query_running_bridge(self, ifaceobjrunning, ifaceobj_getfunc):
+ if self.ipcmd.bridge_is_vlan_aware(ifaceobjrunning.name):
+ ifaceobjrunning.update_config('bridge-vlan-aware', 'yes')
+ ifaceobjrunning.update_config_dict(self._query_running_attrs(
+ ifaceobjrunning,
+ ifaceobj_getfunc,
+ bridge_vlan_aware=True))
+ else:
+ ifaceobjrunning.update_config_dict(self._query_running_attrs(
+ ifaceobjrunning, None))
+
+ def _query_running_bridge_port_attrs(self, ifaceobjrunning, bridgename):
+ if self.sysctl_get('net.bridge.bridge-stp-user-space') == '1':
+ return
+
+ v = self.brctlcmd.get_pathcost(bridgename, ifaceobjrunning.name)
+ if v and v != self.get_mod_subattr('bridge-pathcosts', 'default'):
+ ifaceobjrunning.update_config('bridge-pathcosts', v)
+
+ v = self.brctlcmd.get_pathcost(bridgename, ifaceobjrunning.name)
+ if v and v != self.get_mod_subattr('bridge-portprios', 'default'):
+ ifaceobjrunning.update_config('bridge-portprios', v)
+
+ def _query_running_bridge_port(self, ifaceobjrunning,
+ ifaceobj_getfunc=None):
+ bridgename = self.ipcmd.bridge_port_get_bridge_name(
+ ifaceobjrunning.name)
+ bridge_vids = None
+ bridge_pvid = None
+ if not bridgename:
+ self.logger.warn('%s: unable to find bridgename'
+ %ifaceobjrunning.name)
+ return
+ if not self.ipcmd.bridge_is_vlan_aware(bridgename):
+ return
+
+ running_vidinfo = self._get_running_vidinfo()
+ bridge_port_vids = running_vidinfo.get(ifaceobjrunning.name,
+ {}).get('vlan')
+ bridge_port_pvid = running_vidinfo.get(ifaceobjrunning.name,
+ {}).get('pvid')
+
+ bridgeifaceobjlist = ifaceobj_getfunc(bridgename)
+ if bridgeifaceobjlist:
+ bridge_vids = bridgeifaceobjlist[0].get_attr_value('bridge-vids')
+ bridge_pvid = bridgeifaceobjlist[0].get_attr_value_first('bridge-pvid')
+
+ if not bridge_port_vids and bridge_port_pvid:
+ # must be an access port
+ if bridge_port_pvid != '1':
+ ifaceobjrunning.update_config('bridge-access',
+ bridge_port_pvid)
+ else:
+ if bridge_port_vids:
+ if (not bridge_vids or bridge_port_vids != bridge_vids):
+ ifaceobjrunning.update_config('bridge-vids',
+ ' '.join(bridge_port_vids))
+ if bridge_port_pvid and bridge_port_pvid != '1':
+ if (not bridge_pvid or (bridge_port_pvid != bridge_pvid)):
+ ifaceobjrunning.update_config('bridge-pvid',
+ bridge_port_pvid)
+ self._query_running_bridge_port_attrs(ifaceobjrunning, bridgename)
+
+ def _query_running(self, ifaceobjrunning, ifaceobj_getfunc=None):
+ if self.brctlcmd.bridge_exists(ifaceobjrunning.name):
+ self._query_running_bridge(ifaceobjrunning, ifaceobj_getfunc)
+ elif self.brctlcmd.is_bridge_port(ifaceobjrunning.name):
+ self._query_running_bridge_port(ifaceobjrunning, ifaceobj_getfunc)
+
+ _run_ops = {'pre-up' : _up,
+ 'post-down' : _down,
+ 'query-checkcurr' : _query_check,
+ 'query-running' : _query_running}
+
+ 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.brctlcmd:
+ self.brctlcmd = brctl(**flags)
+
+ def run(self, ifaceobj, operation, query_ifaceobj=None,
+ ifaceobj_getfunc=None):
+ """ run bridge configuration on the interface object passed as
+ argument. Can create bridge interfaces if they dont exist already
+
+ 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
+ self._init_command_handlers()
+ self._flush_running_vidinfo()
+ if operation == 'query-checkcurr':
+ op_handler(self, ifaceobj, query_ifaceobj,
+ ifaceobj_getfunc=ifaceobj_getfunc)
+ else:
+ op_handler(self, ifaceobj, ifaceobj_getfunc=ifaceobj_getfunc)
--- /dev/null
+#!/usr/bin/python
+#
+# Copyright 2014 Cumulus Networks, Inc. All rights reserved.
+# Author: Roopa Prabhu, roopa@cumulusnetworks.com
+#
+
+from ifupdown.iface import *
+from ifupdownaddons.modulebase import moduleBase
+from ifupdownaddons.iproute2 import iproute2
+from ifupdownaddons.bridgeutils import brctl
+import logging
+
+class bridgevlan(moduleBase):
+ """ ifupdown2 addon module to configure vlan attributes on a vlan
+ aware bridge """
+
+ _modinfo = {'mhelp' : 'bridgevlan module configures vlan attributes ' +
+ 'on a vlan aware bridge. This module only ' +
+ 'understands vlan interface name ' +
+ 'with dot notations. eg br0.100. where br0 is the ' +
+ 'vlan aware bridge this config is for',
+ 'attrs' : {
+ 'bridge-igmp-querier-src' :
+ { 'help' : 'bridge igmp querier src. Must be ' +
+ 'specified under the vlan interface',
+ 'example' : ['bridge-igmp-querier-src 172.16.101.1']}}}
+
+ def __init__(self, *args, **kargs):
+ moduleBase.__init__(self, *args, **kargs)
+ self.brctlcmd = None
+ self.ipcmd = None
+
+ def _is_bridge_vlan_device(self, ifaceobj):
+ if ifaceobj.type == ifaceType.BRIDGE_VLAN:
+ return True
+ return False
+
+ def _get_bridge_n_vlan(self, ifaceobj):
+ vlist = ifaceobj.name.split('.', 1)
+ if len(vlist) == 2:
+ return (vlist[0], vlist[1])
+ return None
+
+ def _get_bridgename(self, ifaceobj):
+ vlist = ifaceobj.name.split('.', 1)
+ if len(vlist) == 2:
+ return vlist[0]
+ return None
+
+ def get_dependent_ifacenames(self, ifaceobj, ifaceobjs_all=None):
+ if not self._is_bridge_vlan_device(ifaceobj):
+ return None
+ return [self._get_bridgename(ifaceobj)]
+
+ def _up(self, ifaceobj):
+ try:
+ (bridgename, vlan) = self._get_bridge_n_vlan(ifaceobj)
+ vlanid = int(vlan, 10)
+ except:
+ self.logger.warn('%s: bridge vlan interface name ' %ifaceobj.name +
+ 'does not correspond to format (eg. br0.100)')
+ raise
+
+ if not self.ipcmd.link_exists(bridgename):
+ #self.logger.warn('%s: bridge %s does not exist' %(ifaceobj.name,
+ # bridgename))
+ return
+
+ running_mcqv4src = {}
+ if not self.PERFMODE:
+ running_mcqv4src = self.brctlcmd.get_mcqv4src(bridgename)
+ if running_mcqv4src:
+ r_mcqv4src = running_mcqv4src.get(vlan)
+ else:
+ r_mcqv4src = None
+ mcqv4src = ifaceobj.get_attr_value_first('bridge-igmp-querier-src')
+ if not mcqv4src:
+ if r_mcqv4src:
+ self.brctlcmd.del_mcqv4src(bridgename, vlanid)
+ return
+
+ if r_mcqv4src and r_mcqv4src != mcqv4src:
+ self.brctlcmd.del_mcqv4src(bridgename, vlanid)
+ self.brctlcmd.set_mcqv4src(bridgename, vlanid, mcqv4src)
+ else:
+ self.brctlcmd.set_mcqv4src(bridgename, vlanid, mcqv4src)
+
+ def _down(self, ifaceobj):
+ try:
+ (bridgename, vlan) = self._get_bridge_n_vlan(ifaceobj)
+ vlanid = int(vlan, 10)
+ except:
+ self.logger.warn('%s: bridge vlan interface name ' %ifaceobj.name +
+ 'does not correspond to format (eg. br0.100)')
+ raise
+
+ if not self.ipcmd.link_exists(bridgename):
+ #self.logger.warn('%s: bridge %s does not exist' %(ifaceobj.name,
+ # bridgename))
+ return
+ mcqv4src = ifaceobj.get_attr_value_first('bridge-igmp-querier-src')
+ if mcqv4src:
+ self.brctlcmd.del_mcqv4src(bridgename, vlanid)
+
+ def _query_running_bridge_igmp_querier_src(self, ifaceobj):
+ (bridgename, vlanid) = ifaceobj.name.split('.')
+ running_mcqv4src = self.brctlcmd.get_mcqv4src(bridgename)
+ if running_mcqv4src:
+ return running_mcqv4src.get(vlanid)
+ return None
+
+ def _query_check(self, ifaceobj, ifaceobjcurr):
+ attrval = ifaceobj.get_attr_value_first('bridge-igmp-querier-src')
+ if attrval:
+ running_mcq = self._query_running_bridge_igmp_querier_src(ifaceobj)
+ if not running_mcq or running_mcq != attrval:
+ ifaceobjcurr.update_config_with_status(
+ 'bridge-igmp-querier-src', running_mcq, 1)
+ else:
+ ifaceobjcurr.update_config_with_status(
+ 'bridge-igmp-querier-src', attrval, 0)
+ ifaceobjcurr.status = ifaceStatus.SUCCESS
+ return
+
+ def _query_running(self, ifaceobjrunning):
+ # XXX not supported
+ return
+
+ _run_ops = {'pre-up' : _up,
+ 'post-down' : _down,
+ 'query-checkcurr' : _query_check,
+ 'query-running' : _query_running}
+
+ def get_ops(self):
+ """ returns list of ops supported by this module """
+ return self._run_ops.keys()
+
+ def _init_command_handlers(self):
+ if not self.ipcmd:
+ self.ipcmd = iproute2(**self.get_flags())
+ if not self.brctlcmd:
+ self.brctlcmd = brctl(**self.get_flags())
+
+ def run(self, ifaceobj, operation, query_ifaceobj=None, **extra_args):
+ """ run vlan 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_bridge_vlan_device(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
+#
+
+try:
+ from ipaddr import IPNetwork
+ from sets import Set
+ from ifupdown.iface import *
+ from ifupdownaddons.modulebase import moduleBase
+ from ifupdownaddons.dhclient import dhclient
+ from ifupdownaddons.iproute2 import iproute2
+except ImportError, e:
+ raise ImportError (str(e) + "- required module not found")
+
+class dhcp(moduleBase):
+ """ ifupdown2 addon module to configure dhcp on interface """
+
+ def __init__(self, *args, **kargs):
+ moduleBase.__init__(self, *args, **kargs)
+ self.dhclientcmd = dhclient(**kargs)
+ self.ipcmd = None
+
+ def _up(self, ifaceobj):
+ try:
+ if ifaceobj.addr_family == 'inet':
+ # First release any existing dhclient processes
+ try:
+ if not self.PERFMODE:
+ self.dhclientcmd.stop(ifaceobj.name)
+ except:
+ pass
+ self.dhclientcmd.start(ifaceobj.name)
+ elif ifaceobj.addr_family == 'inet6':
+ accept_ra = ifaceobj.get_attr_value_first('accept_ra')
+ if accept_ra:
+ # XXX: Validate value
+ self.sysctl_set('net.ipv6.conf.%s' %ifaceobj.name +
+ '.accept_ra', accept_ra)
+ autoconf = ifaceobj.get_attr_value_first('autoconf')
+ if autoconf:
+ # XXX: Validate value
+ self.sysctl_set('net.ipv6.conf.%s' %ifaceobj.name +
+ '.autoconf', autoconf)
+ try:
+ self.dhclientcmd.stop6(ifaceobj.name)
+ except:
+ pass
+ self.dhclientcmd.start6(ifaceobj.name)
+ except Exception, e:
+ self.log_error(str(e))
+
+ def _down(self, ifaceobj):
+ self.dhclientcmd.release(ifaceobj.name)
+ self.ipcmd.link_down(ifaceobj.name)
+
+ def _query_check(self, ifaceobj, ifaceobjcurr):
+ if self.dhclientcmd.is_running(ifaceobjcurr.name):
+ ifaceobjcurr.addr_family = 'inet'
+ if ifaceobj.addr_family != 'inet':
+ ifaceobjcurr.status = ifaceStatus.ERROR
+ ifaceobjcurr.addr_method = 'dhcp'
+ ifaceobjcurr.status = ifaceStatus.SUCCESS
+ elif self.dhclientcmd.is_running6(ifaceobjcurr.name):
+ ifaceobjcurr.addr_family = 'inet6'
+ if ifaceobj.addr_family != 'inet6':
+ ifaceobjcurr.status = ifaceStatus.ERROR
+ ifaceobjcurr.addr_method = 'dhcp'
+ ifaceobjcurr.status = ifaceStatus.SUCCESS
+ else:
+ ifaceobjcurr.addr_family = None
+ ifaceobjcurr.status = ifaceStatus.ERROR
+
+ def _query_running(self, ifaceobjrunning):
+ if not self.ipcmd.link_exists(ifaceobjrunning.name):
+ return
+ if self.dhclientcmd.is_running(ifaceobjrunning.name):
+ ifaceobjrunning.addr_family = 'inet'
+ ifaceobjrunning.addr_method = 'dhcp'
+ elif self.dhclientcmd.is_running6(ifaceobjrunning.name):
+ ifaceobjrunning.addr_family = 'inet6'
+ ifaceobjrunning.addr_method = 'dhcp6'
+
+ _run_ops = {'up' : _up,
+ 'down' : _down,
+ 'query-checkcurr' : _query_check,
+ 'query-running' : _query_running }
+
+ def get_ops(self):
+ """ returns list of ops supported by this module """
+ return self._run_ops.keys()
+
+ def _init_command_handlers(self):
+ if not self.ipcmd:
+ self.ipcmd = iproute2(**self.get_flags())
+
+ def run(self, ifaceobj, operation, query_ifaceobj=None, **extra_args):
+ """ run dhcp configuration on the interface object passed as argument
+
+ Args:
+ **ifaceobj** (object): iface object
+
+ **operation** (str): any of 'up', '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
+ try:
+ if (operation != 'query-running' and
+ (ifaceobj.addr_method != 'dhcp' and
+ ifaceobj.addr_method != 'dhcp6')):
+ return
+ except:
+ 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
+#
+
+try:
+ from ipaddr import IPNetwork
+ from sets import Set
+ from ifupdown.iface import *
+ from ifupdownaddons.modulebase import moduleBase
+ from ifupdownaddons.iproute2 import iproute2
+except ImportError, e:
+ raise ImportError (str(e) + "- required module not found")
+
+class ethtool(moduleBase):
+ """ ifupdown2 addon module to configure ethtool attributes """
+
+ _modinfo = {'mhelp' : 'ethtool configuration module for interfaces',
+ 'attrs': {
+ 'link-speed' :
+ {'help' : 'set link speed',
+ 'example' : ['link-speed 1000']},
+ 'link-duplex' :
+ {'help': 'set link duplex',
+ 'example' : ['link-duplex full'],
+ 'validvals' : ['half', 'full'],
+ 'default' : 'half'},
+ 'link-autoneg' :
+ {'help': 'set autonegotiation',
+ 'example' : ['link-autoneg on'],
+ 'validvals' : ['on', 'off'],
+ 'default' : 'off'}}}
+
+ def __init__(self, *args, **kargs):
+ moduleBase.__init__(self, *args, **kargs)
+ self.ipcmd = None
+
+ def _post_up(self, ifaceobj):
+ if not self.ipcmd.link_exists(ifaceobj.name):
+ return
+ cmd = ''
+ attrval = ifaceobj.get_attr_value_first('link-speed')
+ if attrval:
+ cmd += ' speed %s' %attrval
+ attrval = ifaceobj.get_attr_value_first('link-duplex')
+ if attrval:
+ cmd += ' duplex %s' %attrval
+ attrval = ifaceobj.get_attr_value_first('link-autoneg')
+ if attrval:
+ cmd += ' autoneg %s' %attrval
+ if cmd:
+ try:
+ cmd = 'ethtool -s %s %s' %(ifaceobj.name, cmd)
+ self.exec_command(cmd)
+ except Exception, e:
+ ifaceobj.status = ifaceStatus.ERROR
+ self.log_warn('%s: %s' %(ifaceobj.name, str(e)))
+
+ def _query_check(self, ifaceobj, ifaceobjcurr):
+ """
+ Advertised auto-negotiation: No
+ Speed: 1000Mb/s
+ Duplex: Full"""
+ ethtool_attrs = self.dict_key_subset(ifaceobj.config,
+ self.get_mod_attrs())
+ if not ethtool_attrs:
+ return
+ try:
+ speed = ifaceobj.get_attr_value_first('link-speed')
+ if speed:
+ running_speed = self.read_file_oneline(
+ '/sys/class/net/%s/speed' %ifaceobj.name)
+ if running_speed and running_speed != speed:
+ ifaceobjcurr.update_config_with_status('link-speed',
+ running_speed, 1)
+ else:
+ ifaceobjcurr.update_config_with_status('link-speed',
+ running_speed, 0)
+ duplex = ifaceobj.get_attr_value_first('link-duplex')
+ if duplex:
+ running_duplex = self.read_file_oneline(
+ '/sys/class/net/%s/duplex' %ifaceobj.name)
+ if running_duplex and running_duplex != duplex:
+ ifaceobjcurr.update_config_with_status('link-duplex',
+ running_duplex, 1)
+ else:
+ ifaceobjcurr.update_config_with_status('link-duplex',
+ running_duplex, 0)
+ except Exception:
+ pass
+ return
+
+ def _query_running(self, ifaceobjrunning):
+ return
+
+ _run_ops = {'post-up' : _post_up,
+ 'query-checkcurr' : _query_check,
+ 'query-running' : _query_running }
+
+ def get_ops(self):
+ """ returns list of ops supported by this module """
+ return self._run_ops.keys()
+
+ def _init_command_handlers(self):
+ if not self.ipcmd:
+ self.ipcmd = iproute2(**self.get_flags())
+
+ def run(self, ifaceobj, operation, query_ifaceobj=None, **extra_args):
+ """ run ethtool configuration on the interface object passed as
+ argument
+
+ Args:
+ **ifaceobj** (object): iface object
+
+ **operation** (str): any of 'post-up', '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
+ 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-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)
+
+ # 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
+ 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']:
+ # 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-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);
+ 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 ]
+
+ 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
+
+ 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)}
+ 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)
--- /dev/null
+#!/usr/bin/python
+
+# This should be pretty simple and might not really even need to exist.
+# The key is that we need to call link_create with a type of "dummy"
+# since that will translate to 'ip link add loopbackX type dummy'
+# The config file should probably just indicate that the type is
+# loopback or dummy.
+
+from ifupdown.iface import *
+from ifupdownaddons.modulebase import moduleBase
+from ifupdownaddons.iproute2 import iproute2
+import logging
+
+class loopback(moduleBase):
+ _modinfo = {'mhelp' : 'configure extra loopback module based on ' +
+ 'dummy device' }
+
+ def __init__(self, *args, **kargs):
+ moduleBase.__init__(self, *args, **kargs)
+ self.ipcmd = None
+
+ def _is_loopback_by_name(self, ifacename):
+ return 'loop' in ifacename
+
+ def _up(self, ifaceobj):
+ if self._is_loopback_by_name(ifaceobj.name):
+ self.ipcmd.link_create(ifaceobj.name, 'dummy')
+
+ def _down(self, ifaceobj):
+ if not self.PERFMODE and not self.ipcmd.link_exists(ifaceobj.name):
+ return
+ try:
+ self.ipcmd.link_delete(ifaceobj.name)
+ except Exception, e:
+ self.log_warn(str(e))
+
+ def _query_check(self, ifaceobj, ifaceobjcurr):
+ if not self.ipcmd.link_exists(ifaceobj.name):
+ return
+
+ _run_ops = {'pre-up' : _up,
+ 'post-down' : _down,
+ 'query-checkcurr' : _query_check}
+
+ def get_ops(self):
+ return self._run_ops.keys()
+
+ def _init_command_handlers(self):
+ if not self.ipcmd:
+ self.ipcmd = iproute2(**self.get_flags())
+
+ def run(self, ifaceobj, operation, query_ifaceobj=None, **extra_args):
+ op_handler = self._run_ops.get(operation)
+ if not op_handler:
+ return
+ if (operation != 'query-running' and
+ not self._is_loopback_by_name(ifaceobj.name)):
+ 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 *
+from ifupdownaddons.modulebase import moduleBase
+from ifupdownaddons.bridgeutils import brctl
+from ifupdownaddons.iproute2 import iproute2
+from ifupdownaddons.mstpctlutil import mstpctlutil
+import traceback
+
+class mstpctl(moduleBase):
+ """ ifupdown2 addon module to configure mstp attributes """
+
+ _modinfo = {'mhelp' : 'mstp configuration module for bridges',
+ 'attrs' : {
+ 'mstpctl-ports' :
+ {'help' : 'mstp ports',
+ 'compat' : True},
+ 'mstpctl-stp' :
+ {'help': 'bridge stp yes/no',
+ 'compat' : True,
+ 'default' : 'no'},
+ 'mstpctl-treeprio' :
+ {'help': 'tree priority',
+ 'default' : '32768',
+ 'validrange' : ['0', '65535'],
+ 'required' : False,
+ 'example' : ['mstpctl-treeprio 32768']},
+ 'mstpctl-ageing' :
+ {'help': 'ageing time',
+ 'default' : '300',
+ 'required' : False,
+ 'example' : ['mstpctl-ageing 300']},
+ 'mstpctl-maxage' :
+ { 'help' : 'max message age',
+ 'default' : '20',
+ 'required' : False,
+ 'example' : ['mstpctl-maxage 20']},
+ 'mstpctl-fdelay' :
+ { 'help' : 'set forwarding delay',
+ 'default' : '15',
+ 'required' : False,
+ 'example' : ['mstpctl-fdelay 15']},
+ 'mstpctl-maxhops' :
+ { 'help' : 'bridge max hops',
+ 'default' : '15',
+ 'required' : False,
+ 'example' : ['mstpctl-maxhops 15']},
+ 'mstpctl-txholdcount' :
+ { 'help' : 'bridge transmit holdcount',
+ 'default' : '6',
+ 'required' : False,
+ 'example' : ['mstpctl-txholdcount 6']},
+ 'mstpctl-forcevers' :
+ { 'help' : 'bridge force stp version',
+ 'default' : 'rstp',
+ 'required' : False,
+ 'example' : ['mstpctl-forcevers rstp']},
+ 'mstpctl-portpathcost' :
+ { 'help' : 'bridge port path cost',
+ 'default' : '0',
+ 'required' : False,
+ 'example' : ['mstpctl-portpathcost swp1=0 swp2=1']},
+ 'mstpctl-portp2p' :
+ { 'help' : 'bridge port p2p detection mode',
+ 'default' : 'auto',
+ 'validvals' : ['yes', 'no', 'auto'],
+ 'required' : False,
+ 'example' : ['mstpctl-portp2p swp1=no swp2=no']},
+ 'mstpctl-portrestrrole' :
+ { 'help' :
+ 'enable/disable port ability to take root role of the port',
+ 'default' : 'no',
+ 'validvals' : ['yes', 'no'],
+ 'required' : False,
+ 'example' : ['mstpctl-portrestrrole swp1=no swp2=no']},
+ 'mstpctl-portrestrtcn' :
+ { 'help' :
+ 'enable/disable port ability to propagate received topology change notification of the port',
+ 'default' : 'no',
+ 'validvals' : ['yes', 'no'],
+ 'required' : False,
+ 'example' : ['mstpctl-portrestrtcn swp1=no swp2=no']},
+ 'mstpctl-bpduguard' :
+ { 'help' :
+ 'enable/disable bpduguard',
+ 'default' : 'no',
+ 'validvals' : ['yes', 'no'],
+ 'required' : False,
+ 'example' : ['mstpctl-bpduguard swp1=no swp2=no']},
+ 'mstpctl-treeportprio' :
+ { 'help' :
+ 'port priority for MSTI instance',
+ 'default' : '128',
+ 'validrange' : ['0', '240'],
+ 'required' : False,
+ 'example' : ['mstpctl-treeportprio swp1=128 swp2=128']},
+ 'mstpctl-hello' :
+ { 'help' : 'set hello time',
+ 'default' : '2',
+ 'required' : False,
+ 'example' : ['mstpctl-hello 2']},
+ 'mstpctl-portnetwork' :
+ { 'help' : 'enable/disable bridge assurance capability for a port',
+ 'validvals' : ['yes', 'no'],
+ 'default' : 'no',
+ 'required' : False,
+ 'example' : ['mstpctl-portnetwork swp1=no swp2=no']},
+ 'mstpctl-portadminedge' :
+ { 'help' : 'enable/disable initial edge state of the port',
+ 'validvals' : ['yes', 'no'],
+ 'default' : 'no',
+ 'required' : False,
+ 'example' : ['mstpctl-portadminedge swp1=no swp2=no']},
+ 'mstpctl-portautoedge' :
+ { 'help' : 'enable/disable auto transition to/from edge state of the port',
+ 'validvals' : ['yes', 'no'],
+ 'default' : 'no',
+ 'required' : False,
+ 'example' : ['mstpctl-portautoedge swp1=yes swp2=yes']},
+ 'mstpctl-treeportcost' :
+ { 'help' : 'port tree cost',
+ 'required' : False},
+ 'mstpctl-portbpdufilter' :
+ { 'help' : 'enable/disable bpdu filter on a port. ' +
+ 'syntax varies when defined under a bridge ' +
+ 'vs under a port',
+ 'validvals' : ['yes', 'no'],
+ 'default' : 'no',
+ 'required' : False,
+ 'example' : ['under a bridge: mstpctl-portbpdufilter swp1=no swp2=no',
+ 'under a port: mstpctl-portbpdufilter yes']},
+ }}
+
+ # Maps mstp bridge attribute names to corresponding mstpctl commands
+ # XXX: This can be encoded in the modules dict above
+ _attrs_map = OrderedDict([('mstpctl-treeprio' , 'treeprio'),
+ ('mstpctl-ageing' , 'ageing'),
+ ('mstpctl-maxage' , 'maxage'),
+ ('mstpctl-fdelay' , 'fdelay'),
+ ('mstpctl-maxhops' , 'maxhops'),
+ ('mstpctl-txholdcount' , 'txholdcount'),
+ ('mstpctl-forcevers', 'forcevers'),
+ ('mstpctl-hello' , 'hello')])
+
+ # Maps mstp port attribute names to corresponding mstpctl commands
+ # XXX: This can be encoded in the modules dict above
+ _port_attrs_map = {'mstpctl-portpathcost' : 'portpathcost',
+ 'mstpctl-portadminedge' : 'portadminedge',
+ 'mstpctl-portautoedge' : 'portautoedge' ,
+ 'mstpctl-portp2p' : 'portp2p',
+ 'mstpctl-portrestrrole' : 'portrestrrole',
+ 'mstpctl-portrestrtcn' : 'portrestrtcn',
+ 'mstpctl-bpduguard' : 'bpduguard',
+ 'mstpctl-treeportprio' : 'treeportprio',
+ 'mstpctl-treeportcost' : 'treeportcost',
+ 'mstpctl-portnetwork' : 'portnetwork',
+ 'mstpctl-portbpdufilter' : 'portbpdufilter'}
+
+ # declare some ifaceobj priv_flags.
+ # XXX: This assumes that the priv_flags is owned by this module
+ # which it is not.
+ _BRIDGE_PORT_PROCESSED = 0x1
+
+ def __init__(self, *args, **kargs):
+ moduleBase.__init__(self, *args, **kargs)
+ self.ipcmd = None
+ self.brctlcmd = None
+ self.mstpctlcmd = None
+
+ def _is_bridge(self, ifaceobj):
+ if (ifaceobj.get_attr_value_first('mstpctl-ports') or
+ ifaceobj.get_attr_value_first('bridge-ports')):
+ return True
+ return False
+
+ def _is_bridge_port(self, ifaceobj):
+ if self.brctlcmd.is_bridge_port(ifaceobj.name):
+ return True
+ return False
+
+ def get_dependent_ifacenames(self, ifaceobj, ifacenames_all=None):
+ if not self._is_bridge(ifaceobj):
+ return None
+ return self.parse_port_list(ifaceobj.get_attr_value_first(
+ 'mstpctl-ports'), ifacenames_all)
+
+ def get_dependent_ifacenames_running(self, ifaceobj):
+ self._init_command_handlers()
+ if (self.brctlcmd.bridge_exists(ifaceobj.name) and
+ not self.mstpctlcmd.mstpbridge_exists(ifaceobj.name)):
+ return None
+ return self.brctlcmd.get_bridge_ports(ifaceobj.name)
+
+ def _get_bridge_port_list(self, ifaceobj):
+
+ # port list is also available in the previously
+ # parsed dependent list. Use that if available, instead
+ # of parsing port expr again
+ port_list = ifaceobj.lowerifaces
+ if port_list:
+ return port_list
+ ports = ifaceobj.get_attr_value_first('mstpctl-ports')
+ if ports:
+ return self.parse_port_list(ports)
+ else:
+ return None
+
+ def _ports_enable_disable_ipv6(self, ports, enable='1'):
+ for p in ports:
+ try:
+ self.write_file('/proc/sys/net/ipv6/conf/%s' %p +
+ '/disable_ipv6', enable)
+ except Exception, e:
+ self.logger.info(str(e))
+ pass
+
+ def _add_ports(self, ifaceobj):
+ bridgeports = self._get_bridge_port_list(ifaceobj)
+
+ runningbridgeports = []
+ # Delete active ports not in the new port list
+ if not self.PERFMODE:
+ runningbridgeports = self.brctlcmd.get_bridge_ports(ifaceobj.name)
+ if runningbridgeports:
+ [self.ipcmd.link_set(bport, 'nomaster')
+ for bport in runningbridgeports
+ if not bridgeports or bport not in bridgeports]
+ else:
+ runningbridgeports = []
+ if not bridgeports:
+ return
+ err = 0
+ for bridgeport in Set(bridgeports).difference(Set(runningbridgeports)):
+ try:
+ if not self.DRYRUN and not self.ipcmd.link_exists(bridgeport):
+ self.log_warn('%s: bridge port %s does not exist'
+ %(ifaceobj.name, bridgeport))
+ err += 1
+ continue
+ self.ipcmd.link_set(bridgeport, 'master', ifaceobj.name)
+ self.ipcmd.addr_flush(bridgeport)
+ except Exception, e:
+ self.log_error(str(e))
+
+ if err:
+ self.log_error('error configuring bridge (missing ports)')
+
+ def _apply_bridge_settings(self, ifaceobj):
+ check = False if self.PERFMODE else True
+ try:
+ # set bridge attributes
+ for attrname, dstattrname in self._attrs_map.items():
+ try:
+ v = ifaceobj.get_attr_value_first(attrname)
+ if not v:
+ continue
+ if attrname == 'mstpctl-treeprio':
+ self.mstpctlcmd.set_bridge_treeprio(ifaceobj.name,
+ v, check)
+ else:
+ self.mstpctlcmd.set_bridge_attr(ifaceobj.name,
+ dstattrname, v, check)
+ except Exception, e:
+ self.logger.warn('%s' %str(e))
+ pass
+
+ # set bridge port attributes
+ for attrname, dstattrname in self._port_attrs_map.items():
+ attrval = ifaceobj.get_attr_value_first(attrname)
+ if not attrval:
+ continue
+ portlist = self.parse_port_list(attrval)
+ if not portlist:
+ self.log_warn('%s: error parsing \'%s %s\''
+ %(ifaceobj.name, attrname, attrval))
+ continue
+ for p in portlist:
+ try:
+ (port, val) = p.split('=')
+ self.mstpctlcmd.set_bridgeport_attr(ifaceobj.name,
+ port, dstattrname, val, check)
+ except Exception, e:
+ self.log_warn('%s: error setting %s (%s)'
+ %(ifaceobj.name, attrname, str(e)))
+ except Exception, e:
+ self.log_warn(str(e))
+ pass
+
+ def _apply_bridge_port_settings(self, ifaceobj, bridgename=None,
+ bridgeifaceobj=None, stp_on=True,
+ mstpd_running=True):
+ check = False if self.PERFMODE else True
+ if not bridgename and bridgeifaceobj:
+ bridgename = bridgeifaceobj.name
+ # set bridge port attributes
+ for attrname, dstattrname in self._port_attrs_map.items():
+ attrval = ifaceobj.get_attr_value_first(attrname)
+ if not attrval:
+ #if bridgeifaceobj:
+ # # If bridge object available, check if the bridge
+ # # has the attribute set, in which case,
+ # # inherit it from the bridge
+ # attrval = bridgeifaceobj.get_attr_value_first(attrname)
+ # if not attrval:
+ # continue
+ #else:
+ continue
+ if not stp_on:
+ self.logger.warn('%s: cannot set %s (stp on bridge %s not on)\n'
+ %(ifaceobj.name, attrname, bridgename))
+ continue
+ if not mstpd_running:
+ continue
+ try:
+ self.mstpctlcmd.set_bridgeport_attr(bridgename,
+ ifaceobj.name, dstattrname, attrval, check)
+ except Exception, e:
+ self.log_warn('%s: error setting %s (%s)'
+ %(ifaceobj.name, attrname, str(e)))
+
+ def _apply_bridge_port_settings_all(self, ifaceobj,
+ ifaceobj_getfunc=None):
+ self.logger.info('%s: applying mstp configuration '
+ %ifaceobj.name + 'specific to ports')
+ # Query running bridge ports. and only apply attributes on them
+ bridgeports = self.brctlcmd.get_bridge_ports(ifaceobj.name)
+ if not bridgeports:
+ self.logger.debug('%s: cannot find bridgeports' %ifaceobj.name)
+ return
+ for bport in bridgeports:
+ self.logger.info('%s: processing mstp config for port %s'
+ %(ifaceobj.name, bport))
+ if not self.ipcmd.link_exists(bport):
+ continue
+ bportifaceobjlist = ifaceobj_getfunc(bport)
+ if not bportifaceobjlist:
+ continue
+ for bportifaceobj in bportifaceobjlist:
+ # Dont process bridge port if it already has been processed
+ if bportifaceobj.priv_flags & self._BRIDGE_PORT_PROCESSED:
+ continue
+ try:
+ self._apply_bridge_port_settings(bportifaceobj,
+ ifaceobj.name, ifaceobj)
+ except Exception, e:
+ self.log_warn(str(e))
+
+ def _up(self, ifaceobj, ifaceobj_getfunc=None):
+ # Check if bridge port
+ bridgename = self.ipcmd.bridge_port_get_bridge_name(ifaceobj.name)
+ if bridgename:
+ mstpd_running = (True if self.mstpctlcmd.is_mstpd_running()
+ else False)
+ stp_on = (True if self.read_file_oneline(
+ '/sys/class/net/%s/bridge/stp_state'
+ %bridgename) == '2' else False)
+ self._apply_bridge_port_settings(ifaceobj, bridgename, None,
+ stp_on, mstpd_running)
+ ifaceobj.priv_flags |= self._BRIDGE_PORT_PROCESSED
+ return
+ if not self._is_bridge(ifaceobj):
+ return
+ stp = None
+ try:
+ porterr = False
+ porterrstr = ''
+ if ifaceobj.get_attr_value_first('mstpctl-ports'):
+ # If bridge ports specified with mstpctl attr, create the
+ # bridge and also add its ports
+ self.ipcmd.batch_start()
+ if not self.PERFMODE:
+ if not self.ipcmd.link_exists(ifaceobj.name):
+ self.ipcmd.link_create(ifaceobj.name, 'bridge')
+ else:
+ self.ipcmd.link_create(ifaceobj.name, 'bridge')
+ try:
+ self._add_ports(ifaceobj)
+ except Exception, e:
+ porterr = True
+ porterrstr = str(e)
+ pass
+ finally:
+ self.ipcmd.batch_commit()
+ running_ports = self.brctlcmd.get_bridge_ports(ifaceobj.name)
+ if running_ports:
+ # disable ipv6 for ports that were added to bridge
+ self._ports_enable_disable_ipv6(running_ports, '1')
+
+ stp = ifaceobj.get_attr_value_first('mstpctl-stp')
+ if stp:
+ self.set_iface_attr(ifaceobj, 'mstpctl-stp',
+ self.brctlcmd.set_stp)
+ else:
+ stp = self.brctlcmd.get_stp(ifaceobj.name)
+ if (self.mstpctlcmd.is_mstpd_running() and
+ (stp == 'yes' or stp == 'on')):
+ self._apply_bridge_settings(ifaceobj)
+ self._apply_bridge_port_settings_all(ifaceobj,
+ ifaceobj_getfunc=ifaceobj_getfunc)
+ except Exception, e:
+ self.log_error(str(e))
+ if porterr:
+ raise Exception(porterrstr)
+
+ def _down(self, ifaceobj, ifaceobj_getfunc=None):
+ if not self._is_bridge(ifaceobj):
+ return
+ try:
+ if ifaceobj.get_attr_value_first('mstpctl-ports'):
+ # If bridge ports specified with mstpctl attr, delete the
+ # bridge
+ ports = self.brctlcmd.get_bridge_ports(ifaceobj.name)
+ if ports:
+ self._ports_enable_disable_ipv6(ports, '0')
+ self.brctlcmd.delete_bridge(ifaceobj.name)
+ except Exception, e:
+ self.log_error(str(e))
+
+ def _query_running_attrs(self, ifaceobjrunning):
+ bridgeattrdict = {}
+
+ tmpbridgeattrdict = self.mstpctlcmd.get_bridge_attrs(ifaceobjrunning.name)
+ if not tmpbridgeattrdict:
+ return bridgeattrdict
+
+ for k,v in tmpbridgeattrdict.items():
+ if k == 'stp' or not v:
+ continue
+ if k == 'ports':
+ ports = v.keys()
+ continue
+ attrname = 'mstpctl-' + k
+ if v and v != self.get_mod_subattr(attrname, 'default'):
+ bridgeattrdict[attrname] = [v]
+
+ ports = self.brctlcmd.get_bridge_ports(ifaceobjrunning.name)
+ if ports:
+ portconfig = {'mstpctl-portnetwork' : '',
+ 'mstpctl-portpathcost' : '',
+ 'mstpctl-portadminedge' : '',
+ 'mstpctl-portautoedge' : '',
+ 'mstpctl-portp2p' : '',
+ 'mstpctl-portrestrrole' : '',
+ 'mstpctl-portrestrtcn' : '',
+ 'mstpctl-bpduguard' : '',
+ 'mstpctl-treeportprio' : '',
+ 'mstpctl-treeportcost' : ''}
+
+ for p in ports:
+ v = self.mstpctlcmd.get_bridgeport_attr(ifaceobjrunning.name,
+ p, 'portnetwork')
+ if v and v != 'no':
+ portconfig['mstpctl-portnetwork'] += ' %s=%s' %(p, v)
+
+ # XXX: Can we really get path cost of a port ???
+ #v = self.mstpctlcmd.get_portpathcost(ifaceobjrunning.name, p)
+ #if v and v != self.get_mod_subattr('mstpctl-portpathcost',
+ # 'default'):
+ # portconfig['mstpctl-portpathcost'] += ' %s=%s' %(p, v)
+
+ v = self.mstpctlcmd.get_bridgeport_attr(ifaceobjrunning.name,
+ p, 'portadminedge')
+ if v and v != 'no':
+ portconfig['mstpctl-portadminedge'] += ' %s=%s' %(p, v)
+
+ v = self.mstpctlcmd.get_bridgeport_attr(ifaceobjrunning.name,
+ p, 'portp2p')
+ if v and v != 'no':
+ portconfig['mstpctl-portp2p'] += ' %s=%s' %(p, v)
+
+ v = self.mstpctlcmd.get_bridgeport_attr(ifaceobjrunning.name,
+ p, 'portrestrrole')
+ if v and v != 'no':
+ portconfig['mstpctl-portrestrrole'] += ' %s=%s' %(p, v)
+
+ v = self.mstpctlcmd.get_bridgeport_attr(ifaceobjrunning.name,
+ p, 'portrestrtcn')
+ if v and v != 'no':
+ portconfig['mstpctl-portrestrtcn'] += ' %s=%s' %(p, v)
+
+ v = self.mstpctlcmd.get_bridgeport_attr(ifaceobjrunning.name,
+ p, 'bpduguard')
+ if v and v != 'no':
+ portconfig['mstpctl-bpduguard'] += ' %s=%s' %(p, v)
+
+ # XXX: Can we really get path cost of a port ???
+ #v = self.mstpctlcmd.get_bridgeport_attr(ifaceobjrunning.name,
+ # p, 'treeprio')
+ #if v and v != self.get_mod_subattr('mstpctl-treeportprio',
+ # 'default'):
+ # portconfig['mstpctl-treeportprio'] += ' %s=%s' %(p, v)
+
+ #v = self.mstpctlcmd.get_bridgeport_attr(ifaceobjrunning.name,
+ # p, 'treecost')
+ #if v and v != self.get_mod_subattr('mstpctl-treeportcost',
+ # 'default'):
+ # portconfig['mstpctl-treeportcost'] += ' %s=%s' %(p, v)
+
+ bridgeattrdict.update({k : [v] for k, v in portconfig.items()
+ if v})
+ return bridgeattrdict
+
+ def _query_check_bridge(self, ifaceobj, ifaceobjcurr):
+ # list of attributes that are not supported currently
+ blacklistedattrs = ['mstpctl-portpathcost',
+ 'mstpctl-treeportprio', 'mstpctl-treeportcost']
+ if not self.brctlcmd.bridge_exists(ifaceobj.name):
+ self.logger.debug('bridge %s does not exist' %ifaceobj.name)
+ return
+ ifaceattrs = self.dict_key_subset(ifaceobj.config,
+ self.get_mod_attrs())
+ if not ifaceattrs:
+ return
+ runningattrs = self.mstpctlcmd.get_bridge_attrs(ifaceobj.name)
+ if not runningattrs:
+ runningattrs = {}
+ for k in ifaceattrs:
+ # for all mstpctl options
+ if k in blacklistedattrs:
+ continue
+ # get the corresponding ifaceobj attr
+ v = ifaceobj.get_attr_value_first(k)
+ if not v:
+ continue
+
+ # Get the running attribute
+ rv = runningattrs.get(k[8:])
+ if k == 'mstpctl-stp':
+ # special case stp compare because it may
+ # contain more than one valid values
+ stp_on_vals = ['on', 'yes']
+ stp_off_vals = ['off']
+ rv = self.brctlcmd.get_stp(ifaceobj.name)
+ if ((v in stp_on_vals and rv in stp_on_vals) or
+ (v in stp_off_vals and rv in stp_off_vals)):
+ ifaceobjcurr.update_config_with_status('mstpctl-stp', v, 0)
+ else:
+ ifaceobjcurr.update_config_with_status('mstpctl-stp', v, 1)
+ continue
+
+ if k == 'mstpctl-ports':
+ # special case ports because it can contain regex or glob
+ # XXX: We get all info from mstputils, which means if
+ # mstpd is down, we will not be returning any bridge bridgeports
+ running_port_list = self.brctlcmd.get_bridge_ports(ifaceobj.name)
+ bridge_port_list = self._get_bridge_port_list(ifaceobj)
+ if not running_port_list and not bridge_port_list:
+ continue
+ portliststatus = 1
+ if running_port_list and bridge_port_list:
+ difference = Set(running_port_list).symmetric_difference(
+ Set(bridge_port_list))
+ if not difference:
+ portliststatus = 0
+ ifaceobjcurr.update_config_with_status('mstpctl-ports',
+ ' '.join(running_port_list)
+ if running_port_list else '', portliststatus)
+ elif k[:12] == 'mstpctl-port' or k == 'mstpctl-bpduguard':
+ # Now, look at port attributes
+ # derive the mstpctlcmd attr name
+ #mstpctlcmdattrname = k[12:] if k[:12] == 'mstpctl-port' else k[8:]
+ mstpctlcmdattrname = k[8:]
+
+ # for port attributes, the attributes are in a list
+ # <portname>=<portattrvalue>
+ status = 0
+ currstr = ''
+ vlist = self.parse_port_list(v)
+ if not vlist:
+ continue
+ for vlistitem in vlist:
+ try:
+ (p, v) = vlistitem.split('=')
+ currv = self.mstpctlcmd.get_bridgeport_attr(
+ ifaceobj.name, p, mstpctlcmdattrname)
+ if currv:
+ currstr += ' %s=%s' %(p, currv)
+ else:
+ currstr += ' %s=%s' %(p, 'None')
+ if currv != v:
+ status = 1
+ except Exception, e:
+ self.log_warn(str(e))
+ pass
+ ifaceobjcurr.update_config_with_status(k, currstr, status)
+ elif not rv:
+ ifaceobjcurr.update_config_with_status(k, '', 1)
+ elif v != rv:
+ ifaceobjcurr.update_config_with_status(k, rv, 1)
+ else:
+ ifaceobjcurr.update_config_with_status(k, rv, 0)
+
+ def _query_check_bridge_port(self, ifaceobj, ifaceobjcurr):
+ if not self.ipcmd.link_exists(ifaceobj.name):
+ #self.logger.debug('bridge port %s does not exist' %ifaceobj.name)
+ ifaceobjcurr.status = ifaceStatus.NOTFOUND
+ return
+ # Check if this is a bridge port
+ if not self._is_bridge_port(ifaceobj):
+ # mark all the bridge attributes as error
+ ifaceobjcurr.check_n_update_config_with_status_many(ifaceobj,
+ self._port_attrs_map.keys(), 0)
+ return
+ bridgename = self.ipcmd.bridge_port_get_bridge_name(ifaceobj.name)
+ # list of attributes that are not supported currently
+ blacklistedattrs = ['mstpctl-portpathcost',
+ 'mstpctl-treeportprio', 'mstpctl-treeportcost']
+ ifaceattrs = self.dict_key_subset(ifaceobj.config,
+ self._port_attrs_map.keys())
+ if not ifaceattrs:
+ return
+ runningattrs = self.mstpctlcmd.get_bridge_attrs(ifaceobj.name)
+ if not runningattrs:
+ runningattrs = {}
+ for k in ifaceattrs:
+ # for all mstpctl options
+ # get the corresponding ifaceobj attr
+ v = ifaceobj.get_attr_value_first(k)
+ if not v or k in blacklistedattrs:
+ ifaceobjcurr.update_config_with_status(k, v, -1)
+ continue
+ currv = self.mstpctlcmd.get_bridgeport_attr(bridgename,
+ ifaceobj.name, self._port_attrs_map.get(k))
+ if currv:
+ if currv != v:
+ ifaceobjcurr.update_config_with_status(k, currv, 1)
+ else:
+ ifaceobjcurr.update_config_with_status(k, currv, 0)
+ else:
+ ifaceobjcurr.update_config_with_status(k, None, 1)
+
+ def _query_check(self, ifaceobj, ifaceobjcurr, ifaceobj_getfunc=None):
+ if self._is_bridge(ifaceobj):
+ self._query_check_bridge(ifaceobj, ifaceobjcurr)
+ else:
+ self._query_check_bridge_port(ifaceobj, ifaceobjcurr)
+
+ def _query_running_bridge_port(self, ifaceobjrunning):
+ bridgename = self.ipcmd.bridge_port_get_bridge_name(
+ ifaceobjrunning.name)
+ if not bridgename:
+ self.logger.warn('%s: unable to determine bridgename'
+ %ifaceobjrunning.name)
+ return
+ if self.brctlcmd.get_stp(bridgename) == 'no':
+ # This bridge does not run stp, return
+ return
+ # if userspace stp not set, return
+ if self.sysctl_get('net.bridge.bridge-stp-user-space') != '1':
+ return
+ v = self.mstpctlcmd.get_bridgeport_attr(bridgename,
+ ifaceobjrunning.name,
+ 'portnetwork')
+ if v and v != 'no':
+ ifaceobjrunning.update_config('mstpctl-network', v)
+
+ # XXX: Can we really get path cost of a port ???
+ #v = self.mstpctlcmd.get_portpathcost(ifaceobjrunning.name, p)
+ #if v and v != self.get_mod_subattr('mstpctl-pathcost',
+ # 'default'):
+ # ifaceobjrunning.update_config('mstpctl-network', v)
+
+ v = self.mstpctlcmd.get_bridgeport_attr(bridgename,
+ ifaceobjrunning.name, 'portadminedge')
+ if v and v != 'no':
+ ifaceobjrunning.update_config('mstpctl-portadminedge', v)
+
+ v = self.mstpctlcmd.get_bridgeport_attr(bridgename,
+ ifaceobjrunning.name,'portp2p')
+ if v and v != 'auto':
+ ifaceobjrunning.update_config('mstpctl-portp2p', v)
+
+ v = self.mstpctlcmd.get_bridgeport_attr(bridgename,
+ ifaceobjrunning.name, 'portrestrrole')
+ if v and v != 'no':
+ ifaceobjrunning.update_config('mstpctl-portrestrrole', v)
+
+ v = self.mstpctlcmd.get_bridgeport_attr(bridgename,
+ ifaceobjrunning.name, 'restrtcn')
+ if v and v != 'no':
+ ifaceobjrunning.update_config('mstpctl-portrestrtcn', v)
+
+ v = self.mstpctlcmd.get_bridgeport_attr(bridgename,
+ ifaceobjrunning.name, 'bpduguard')
+ if v and v != 'no':
+ ifaceobjrunning.update_config('mstpctl-bpduguard', v)
+
+ # XXX: Can we really get path cost of a port ???
+ #v = self.mstpctlcmd.get_bridgeport_attr(ifaceobjrunning.name,
+ # p, 'treeprio')
+ #if v and v != self.get_mod_subattr('mstpctl-treeportprio',
+ # 'default'):
+ # portconfig['mstpctl-treeportprio'] += ' %s=%s' %(p, v)
+
+ #v = self.mstpctlcmd.get_bridgeport_attr(ifaceobjrunning.name,
+ # p, 'treecost')
+ #if v and v != self.get_mod_subattr('mstpctl-treeportcost',
+ # 'default'):
+ # portconfig['mstpctl-treeportcost'] += ' %s=%s' %(p, v)
+
+ def _query_running_bridge(self, ifaceobjrunning):
+ if self.brctlcmd.get_stp(ifaceobjrunning.name) == 'no':
+ # This bridge does not run stp, return
+ return
+ # if userspace stp not set, return
+ if self.sysctl_get('net.bridge.bridge-stp-user-space') != '1':
+ return
+ # Check if mstp really knows about this bridge
+ if not self.mstpctlcmd.mstpbridge_exists(ifaceobjrunning.name):
+ return
+ ifaceobjrunning.update_config_dict(self._query_running_attrs(
+ ifaceobjrunning))
+
+ def _query_running(self, ifaceobjrunning, **extra_args):
+ if self.brctlcmd.bridge_exists(ifaceobjrunning.name):
+ self._query_running_bridge(ifaceobjrunning)
+ elif self.brctlcmd.is_bridge_port(ifaceobjrunning.name):
+ self._query_running_bridge_port(ifaceobjrunning)
+
+ _run_ops = {'pre-up' : _up,
+ 'post-down' : _down,
+ 'query-checkcurr' : _query_check,
+ 'query-running' : _query_running}
+
+ 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.brctlcmd:
+ self.brctlcmd = brctl(**flags)
+ if not self.mstpctlcmd:
+ self.mstpctlcmd = mstpctlutil(**flags)
+
+ def run(self, ifaceobj, operation, query_ifaceobj=None,
+ ifaceobj_getfunc=None, **extra_args):
+ """ run mstp 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.
+ """
+ if ifaceobj.type == ifaceType.BRIDGE_VLAN:
+ return
+ op_handler = self._run_ops.get(operation)
+ if not op_handler:
+ return
+ self._init_command_handlers()
+ if operation == 'query-checkcurr':
+ op_handler(self, ifaceobj, query_ifaceobj,
+ ifaceobj_getfunc=ifaceobj_getfunc)
+ else:
+ op_handler(self, ifaceobj, ifaceobj_getfunc=ifaceobj_getfunc)
--- /dev/null
+#!/usr/bin/python
+#
+# Copyright 2014 Cumulus Networks, Inc. All rights reserved.
+# Author: Roopa Prabhu, roopa@cumulusnetworks.com
+#
+
+import subprocess
+import ifupdownaddons
+
+class usercmds(ifupdownaddons.modulebase.moduleBase):
+ """ ifupdown2 addon module to configure user specified commands """
+
+ _modinfo = {'mhelp' : 'user commands for interfaces',
+ 'attrs' : {
+ 'pre-up' :
+ {'help' : 'run command before bringing the interface up'},
+ 'up' :
+ {'help' : 'run command at interface bring up'},
+ 'post-up' :
+ {'help' : 'run command after interface bring up'},
+ 'pre-down' :
+ {'help' : 'run command before bringing the interface down'},
+ 'down' :
+ {'help' : 'run command at interface down'},
+ 'post-down' :
+ {'help' : 'run command after bringing interface down'}}}
+
+ def _exec_user_cmd(self, cmd):
+ """ exec's commands using subprocess Popen
+
+ special wrapper using use closefds=True and shell=True
+ for user commands
+ """
+
+ cmd_returncode = 0
+ try:
+ self.logger.info('executing %s' %cmd)
+ if self.DRYRUN:
+ return
+ ch = subprocess.Popen(cmd,
+ stdout=subprocess.PIPE,
+ shell=True,
+ stderr=subprocess.STDOUT,
+ close_fds=True)
+ cmd_returncode = ch.wait()
+ cmdout = ch.communicate()[0]
+ except Exception, e:
+ raise Exception('failed to execute cmd \'%s\' (%s)'
+ %(cmd, str(e)))
+ if cmd_returncode != 0:
+ raise Exception(cmdout)
+ return cmdout
+
+ def _run_command(self, ifaceobj, op):
+ cmd_list = ifaceobj.get_attr_value(op)
+ if cmd_list:
+ for cmd in cmd_list:
+ self.logger.info('executing cmd \'%s\'' %cmd)
+ try:
+ self._exec_user_cmd(cmd)
+ except Exception, e:
+ if not self.ignore_error(str(e)):
+ self.logger.warn('%s: %s cmd \'%s\' failed (%s)'
+ %(ifaceobj.name, op, cmd, str(e).strip('\n')))
+ pass
+
+ _run_ops = {'pre-up' : _run_command,
+ 'pre-down' : _run_command,
+ 'up' : _run_command,
+ 'post-up' : _run_command,
+ 'down' : _run_command,
+ 'post-down' : _run_command}
+
+ def get_ops(self):
+ """ returns list of ops supported by this module """
+ return self._run_ops.keys()
+
+ def run(self, ifaceobj, operation, query_ifaceobj=None, **extra_args):
+ """ run user commands
+
+ Args:
+ **ifaceobj** (object): iface object
+
+ **operation** (str): list of ops
+
+ 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
+ op_handler(self, ifaceobj, operation)
--- /dev/null
+#!/usr/bin/python
+#
+# Copyright 2014 Cumulus Networks, Inc. All rights reserved.
+# Author: Roopa Prabhu, roopa@cumulusnetworks.com
+#
+
+from ifupdown.iface import *
+from ifupdownaddons.modulebase import moduleBase
+from ifupdownaddons.iproute2 import iproute2
+import ifupdown.rtnetlink_api as rtnetlink_api
+import logging
+import re
+
+class vlan(moduleBase):
+ """ ifupdown2 addon module to configure vlans """
+
+ _modinfo = {'mhelp' : 'vlan module configures vlan interfaces.' +
+ 'This module understands vlan interfaces with dot ' +
+ 'notations. eg swp1.100. Vlan interfaces with any ' +
+ 'other names need to have raw device and vlan id ' +
+ 'attributes',
+ 'attrs' : {
+ 'vlan-raw-device' :
+ {'help' : 'vlan raw device'},
+ 'vlan-id' :
+ {'help' : 'vlan id'}}}
+
+
+ def __init__(self, *args, **kargs):
+ moduleBase.__init__(self, *args, **kargs)
+ self.ipcmd = None
+ self._bridge_vids_query_cache = {}
+ self._resv_vlan_range = self._get_reserved_vlan_range()
+ self.logger.debug('%s: using reserved vlan range %s'
+ %(self.__class__.__name__, str(self._resv_vlan_range)))
+
+ def _is_vlan_device(self, ifaceobj):
+ vlan_raw_device = ifaceobj.get_attr_value_first('vlan-raw-device')
+ if vlan_raw_device:
+ return True
+ elif '.' in ifaceobj.name:
+ return True
+ return False
+
+ def _get_vlan_id(self, ifaceobj):
+ """ Derives vlanid from iface name
+
+ Example:
+ Returns 1 for ifname vlan0001 returns 1
+ Returns 1 for ifname vlan1
+ Returns 1 for ifname eth0.1
+
+ Returns -1 if vlan id cannot be determined
+ """
+ vid_str = ifaceobj.get_attr_value_first('vlan-id')
+ try:
+ if vid_str: return int(vid_str)
+ except:
+ return -1
+
+ if '.' in ifaceobj.name:
+ vid_str = ifaceobj.name.split('.', 1)[1]
+ elif ifaceobj.name.startswith('vlan'):
+ vid_str = ifaceobj.name[4:]
+ else:
+ return -1
+ try:
+ vid = int(vid_str)
+ except:
+ return -1
+ return vid
+
+ def _is_vlan_by_name(self, ifacename):
+ return '.' in ifacename
+
+ def _get_vlan_raw_device_from_ifacename(self, ifacename):
+ """ Returns vlan raw device from ifname
+ Example:
+ Returns eth0 for ifname eth0.100
+
+ Returns None if vlan raw device name cannot
+ be determined
+ """
+ vlist = ifacename.split('.', 1)
+ if len(vlist) == 2:
+ return vlist[0]
+ return None
+
+ def _get_vlan_raw_device(self, ifaceobj):
+ vlan_raw_device = ifaceobj.get_attr_value_first('vlan-raw-device')
+ if vlan_raw_device:
+ return vlan_raw_device
+ return self._get_vlan_raw_device_from_ifacename(ifaceobj.name)
+
+ def get_dependent_ifacenames(self, ifaceobj, ifaceobjs_all=None):
+ if not self._is_vlan_device(ifaceobj):
+ return None
+ return [self._get_vlan_raw_device(ifaceobj)]
+
+ def _bridge_vid_add_del(self, ifaceobj, bridgename, vlanid,
+ add=True):
+ """ If the lower device is a vlan aware bridge, add/del the vlanid
+ to the bridge """
+ if self.ipcmd.bridge_is_vlan_aware(bridgename):
+ if add:
+ rtnetlink_api.rtnl_api.bridge_vlan(add=True, dev=bridgename,
+ vid=vlanid, master=False)
+ else:
+ rtnetlink_api.rtnl_api.bridge_vlan(add=False, dev=bridgename,
+ vid=vlanid, master=False)
+
+ def _bridge_vid_check(self, ifaceobj, ifaceobjcurr, bridgename, vlanid):
+ """ If the lower device is a vlan aware bridge, check if the vlanid
+ is configured on the bridge """
+ if not self.ipcmd.bridge_is_vlan_aware(bridgename):
+ return
+ vids = self._bridge_vids_query_cache.get(bridgename)
+ if vids == None:
+ vids = self.ipcmd.bridge_port_vids_get(bridgename)
+ self._bridge_vids_query_cache[bridgename] = vids
+ if not vids or vlanid not in vids:
+ ifaceobjcurr.status = ifaceStatus.ERROR
+ ifaceobjcurr.status_str = 'bridge vid error'
+
+ def _up(self, ifaceobj):
+ vlanid = self._get_vlan_id(ifaceobj)
+ if vlanid == -1:
+ raise Exception('could not determine vlanid')
+ if self._handle_reserved_vlan(vlanid, ifaceobj.name):
+ return
+ vlanrawdevice = self._get_vlan_raw_device(ifaceobj)
+ if not vlanrawdevice:
+ raise Exception('could not determine vlan raw device')
+ if not self.PERFMODE:
+ if not self.ipcmd.link_exists(vlanrawdevice):
+ raise Exception('rawdevice %s not present' %vlanrawdevice)
+ if self.ipcmd.link_exists(ifaceobj.name):
+ self._bridge_vid_add_del(ifaceobj, vlanrawdevice, vlanid)
+ return
+ rtnetlink_api.rtnl_api.create_vlan(vlanrawdevice,
+ ifaceobj.name, vlanid)
+ self._bridge_vid_add_del(ifaceobj, vlanrawdevice, vlanid)
+ if ifaceobj.addr_method == 'manual':
+ rtnetlink_api.rtnl_api.link_set(ifaceobj.name, "up")
+
+ def _down(self, ifaceobj):
+ vlanid = self._get_vlan_id(ifaceobj)
+ if vlanid == -1:
+ raise Exception('could not determine vlanid')
+ vlanrawdevice = self._get_vlan_raw_device(ifaceobj)
+ if not vlanrawdevice:
+ raise Exception('could not determine vlan raw device')
+ if not self.PERFMODE and not self.ipcmd.link_exists(ifaceobj.name):
+ return
+ try:
+ self.ipcmd.link_delete(ifaceobj.name)
+ self._bridge_vid_add_del(ifaceobj, vlanrawdevice, vlanid, add=False)
+ except Exception, e:
+ self.log_warn(str(e))
+
+ def _query_check(self, ifaceobj, ifaceobjcurr):
+ if not self.ipcmd.link_exists(ifaceobj.name):
+ return
+ if not '.' in ifaceobj.name:
+ # if vlan name is not in the dot format, check its running state
+ (vlanrawdev, vlanid) = self.ipcmd.get_vlandev_attrs(ifaceobj.name)
+ if vlanrawdev != ifaceobj.get_attr_value_first('vlan-raw-device'):
+ ifaceobjcurr.update_config_with_status('vlan-raw-device',
+ vlanrawdev, 1)
+ else:
+ ifaceobjcurr.update_config_with_status('vlan-raw-device',
+ vlanrawdev, 0)
+ if vlanid != ifaceobj.get_attr_value_first('vlan-id'):
+ ifaceobjcurr.update_config_with_status('vlan-id', vlanid, 1)
+ else:
+ ifaceobjcurr.update_config_with_status('vlan-id',
+ vlanid, 0)
+ self._bridge_vid_check(ifaceobj, ifaceobjcurr, vlanrawdev, vlanid)
+
+ def _query_running(self, ifaceobjrunning):
+ if not self.ipcmd.link_exists(ifaceobjrunning.name):
+ return
+ if not self.ipcmd.get_vlandev_attrs(ifaceobjrunning.name):
+ return
+ # If vlan name is not in the dot format, get the
+ # vlan dev and vlan id
+ if not '.' in ifaceobjrunning.name:
+ (vlanrawdev, vlanid) = self.ipcmd.get_vlandev_attrs(ifaceobjrunning.name)
+ ifaceobjrunning.update_config_dict({(k, v) for k, v in
+ {'vlan-raw-device' : vlanrawdev,
+ 'vlan-id' : vlanid}.items()
+ if v})
+
+ _run_ops = {'pre-up' : _up,
+ 'post-down' : _down,
+ 'query-checkcurr' : _query_check,
+ 'query-running' : _query_running}
+
+ def get_ops(self):
+ """ returns list of ops supported by this module """
+ return self._run_ops.keys()
+
+ def _init_command_handlers(self):
+ if not self.ipcmd:
+ self.ipcmd = iproute2(**self.get_flags())
+
+
+ def run(self, ifaceobj, operation, query_ifaceobj=None, **extra_args):
+ """ run vlan 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.
+ """
+ if ifaceobj.type == ifaceType.BRIDGE_VLAN:
+ return
+ op_handler = self._run_ops.get(operation)
+ if not op_handler:
+ return
+ if (operation != 'query-running' and
+ not self._is_vlan_device(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
+#
+
+try:
+ from ipaddr import IPNetwork
+ from sets import Set
+ from ifupdown.iface import *
+ from ifupdownaddons.modulebase import moduleBase
+ from ifupdownaddons.iproute2 import iproute2
+ import os
+ import glob
+ import logging
+ import signal
+ import subprocess
+ import re
+except ImportError, e:
+ raise ImportError (str(e) + "- required module not found")
+
+class vrrpd(moduleBase):
+ """ ifupdown2 addon module to configure vrrpd attributes """
+
+ _modinfo = {'mhelp' : 'ethtool configuration module for interfaces',
+ 'attrs': {
+ 'vrrp-id' :
+ {'help' : 'vrrp instance id',
+ 'example' : ['vrrp-id 1']},
+ 'vrrp-priority' :
+ {'help': 'set vrrp priority',
+ 'example' : ['vrrp-priority 20']},
+ 'vrrp-virtual-ip' :
+ {'help': 'set vrrp virtual ip',
+ 'example' : ['vrrp-virtual-ip 10.0.1.254']}}}
+
+ def __init__(self, *args, **kargs):
+ moduleBase.__init__(self, *args, **kargs)
+ self.ipcmd = None
+
+ def _check_if_process_is_running(self, cmdname, cmdline):
+ targetpids = []
+ pidstr = ''
+ try:
+ pidstr = subprocess.check_output(['/bin/pidof',
+ '%s' %cmdname]).strip('\n')
+ except:
+ pass
+ if not pidstr:
+ return []
+
+ pids = pidstr.split()
+ if not pids:
+ return targetpids
+ for pid in pids:
+ tmpcmdline = cmdline.replace(' ', '')
+ try:
+ pcmdline = self.read_file_oneline('/proc/%s/cmdline' %pid)
+ pcmdline = re.sub(r'\\(.)', r'\1', pcmdline)
+ self.logger.info('(%s)' %(pcmdline))
+ self.logger.info('(%s)' %(tmpcmdline))
+ self.logger.info('(%d) (%d)' %(len(pcmdline), len(tmpcmdline)))
+ if pcmdline and pcmdline == tmpcmdline:
+ targetpids.append(pid)
+ except:
+ pass
+ return targetpids
+
+ def _up(self, ifaceobj):
+ """ up vrrpd -n -D -i $IFACE -v 1 -p 20 10.0.1.254
+ up ifplugd -i $IFACE -b -f -u0 -d1 -I -p -q """
+
+ if (not self.DRYRUN and
+ not os.path.exists('/sys/class/net/%s' %ifaceobj.name)):
+ return
+
+ cmd = ''
+ attrval = ifaceobj.get_attr_value_first('vrrp-id')
+ if attrval:
+ cmd += ' -v %s' %attrval
+ else:
+ return
+ attrval = ifaceobj.get_attr_value_first('vrrp-priority')
+ if attrval:
+ cmd += ' -p %s' %attrval
+ else:
+ self.logger.warn('%s: incomplete vrrp parameters ' %ifaceobj.name,
+ '(priority not found)')
+ attrval = ifaceobj.get_attr_value_first('vrrp-virtual-ip')
+ if attrval:
+ cmd += ' %s' %attrval
+ else:
+ self.logger.warn('%s: incomplete vrrp arguments ' %ifaceobj.name,
+ '(virtual ip not found)')
+ return
+ cmd = '/usr/sbin/vrrpd -n -D -i %s %s' %(ifaceobj.name, cmd)
+ self.exec_command(cmd)
+
+ cmd = '/usr/sbin/ifplugd -i %s -b -f -u0 -d1 -I -p -q' %ifaceobj.name
+ if self._check_if_process_is_running('/usr/sbin/ifplugd', cmd):
+ self.logger.info('%s: ifplugd already running' %ifaceobj.name)
+ return
+ self.exec_command(cmd)
+
+ def _kill_pid_from_file(self, pidfilename):
+ if os.path.exists(pidfilename):
+ pid = self.read_file_oneline(pidfilename)
+ if os.path.exists('/proc/%s' %pid):
+ os.kill(int(pid), signal.SIGTERM)
+
+ def _down(self, ifaceobj):
+ """ down ifplugd -k -i $IFACE
+ down kill $(cat /var/run/vrrpd_$IFACE_*.pid) """
+ attrval = ifaceobj.get_attr_value_first('vrrp-id')
+ if not attrval:
+ return
+ try:
+ self.exec_command('/usr/sbin/ifplugd -k -i %s' %ifaceobj.name)
+ except Exception, e:
+ self.logger.debug('%s: ifplugd down error (%s)'
+ %(ifaceobj.name, str(e)))
+ pass
+
+ for pidfile in glob.glob('/var/run/vrrpd_%s_*.pid' %ifaceobj.name):
+ try:
+ self._kill_pid_from_file(pidfile)
+ except Exception, e:
+ self.logger.debug('%s: vrrpd down error (%s)'
+ %(ifaceobj.name, str(e)))
+ pass
+
+ def _query_check(self, ifaceobj, ifaceobjcurr):
+ # XXX
+ return
+
+
+ _run_ops = {'post-up' : _up,
+ 'pre-down' : _down}
+
+ def get_ops(self):
+ """ returns list of ops supported by this module """
+ return self._run_ops.keys()
+
+ def run(self, ifaceobj, operation, query_ifaceobj=None, **extra_args):
+ """ run ethtool configuration on the interface object passed as
+ argument
+
+ Args:
+ **ifaceobj** (object): iface object
+
+ **operation** (str): any of 'post-up', '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-checkcurr':
+ op_handler(self, ifaceobj, query_ifaceobj)
+ else:
+ op_handler(self, ifaceobj)
--- /dev/null
+#!/usr/bin/python
+
+from ifupdown.iface import *
+from ifupdownaddons.modulebase import moduleBase
+from ifupdownaddons.iproute2 import iproute2
+import ifupdown.rtnetlink_api as rtnetlink_api
+import logging
+from sets import Set
+
+class vxlan(moduleBase):
+ _modinfo = {'mhelp' : 'vxlan module configures vxlan interfaces.',
+ 'attrs' : {
+ 'vxlan-id' :
+ {'help' : 'vxlan id',
+ 'required' : True,
+ 'example': ['vxlan-id 100']},
+ 'vxlan-local-tunnelip' :
+ {'help' : 'vxlan local tunnel ip',
+ 'example': ['vxlan-local-tunnelip 172.16.20.103']},
+ 'vxlan-svcnodeip' :
+ {'help' : 'vxlan id',
+ 'example': ['vxlan-svcnodeip 172.16.22.125']},
+ 'vxlan-peernodeip' :
+ {'help' : 'vxlan peer node ip',
+ 'example': ['vxlan-peernodeip 172.16.22.127']},
+ 'vxlan-learning' :
+ {'help' : 'vxlan learning on/off',
+ 'example': ['vxlan-learning off'],
+ 'default': 'on'},
+ }}
+
+ def __init__(self, *args, **kargs):
+ moduleBase.__init__(self, *args, **kargs)
+ self.ipcmd = None
+
+ def _is_vxlan_device(self, ifaceobj):
+ if ifaceobj.get_attr_value_first('vxlan-id'):
+ return True
+ return False
+
+ def _up(self, ifaceobj):
+ vxlanid = ifaceobj.get_attr_value_first('vxlan-id')
+ if vxlanid:
+ self.ipcmd.link_create_vxlan(ifaceobj.name, vxlanid,
+ localtunnelip=ifaceobj.get_attr_value_first('vxlan-local-tunnelip'),
+ svcnodeips=ifaceobj.get_attr_value('vxlan-svcnodeip'),
+ peernodeips=ifaceobj.get_attr_value('vxlan-peernodeip'),
+ learning=ifaceobj.get_attr_value_first('vxlan-learning'))
+ if ifaceobj.addr_method == 'manual':
+ rtnetlink_api.rtnl_api.link_set(ifaceobj.name, "up")
+
+ def _down(self, ifaceobj):
+ try:
+ self.ipcmd.link_delete(ifaceobj.name)
+ except Exception, e:
+ self.log_warn(str(e))
+
+ def _query_check_n_update(self, ifaceobjcurr, attrname, attrval,
+ running_attrval):
+ if running_attrval and attrval == running_attrval:
+ ifaceobjcurr.update_config_with_status(attrname, attrval, 0)
+ else:
+ ifaceobjcurr.update_config_with_status(attrname, running_attrval, 1)
+
+ def _query_check_n_update_addresses(self, ifaceobjcurr, attrname,
+ addresses, running_addresses):
+ if addresses:
+ for a in addresses:
+ if a in running_addresses:
+ ifaceobjcurr.update_config_with_status(attrname, a, 0)
+ else:
+ ifaceobjcurr.update_config_with_status(attrname, a, 1)
+ running_addresses = Set(running_addresses).difference(
+ Set(addresses))
+ [ifaceobjcurr.update_config_with_status(attrname, a, 1)
+ for a in running_addresses]
+
+ def _query_check(self, ifaceobj, ifaceobjcurr):
+ if not self.ipcmd.link_exists(ifaceobj.name):
+ return
+ # Update vxlan object
+ vxlanattrs = self.ipcmd.get_vxlandev_attrs(ifaceobj.name)
+ if not vxlanattrs:
+ ifaceobjcurr.check_n_update_config_with_status_many(ifaceobj,
+ self.get_mod_attrs(), -1)
+ return
+ self._query_check_n_update(ifaceobjcurr, 'vxlan-id',
+ ifaceobj.get_attr_value_first('vxlan-id'),
+ vxlanattrs.get('vxlanid'))
+
+ self._query_check_n_update(ifaceobjcurr, 'vxlan-local-tunnelip',
+ ifaceobj.get_attr_value_first('vxlan-local-tunnelip'),
+ vxlanattrs.get('local'))
+
+ self._query_check_n_update_addresses(ifaceobjcurr, 'vxlan-svcnodeip',
+ ifaceobj.get_attr_value('vxlan-svcnodeip'),
+ vxlanattrs.get('svcnode', []))
+
+ self._query_check_n_update_addresses(ifaceobjcurr, 'vxlan-peernodeip',
+ ifaceobj.get_attr_value('vxlan-peernodeip'),
+ vxlanattrs.get('peernode', []))
+
+ learning = ifaceobj.get_attr_value_first('vxlan-learning')
+ running_learning = vxlanattrs.get('learning')
+ if learning == running_learning:
+ ifaceobjcurr.update_config_with_status('vxlan-learning',
+ running_learning, 0)
+ else:
+ ifaceobjcurr.update_config_with_status('vxlan-learning',
+ running_learning, 1)
+
+ def _query_running(self, ifaceobjrunning):
+ vxlanattrs = self.ipcmd.get_vxlandev_attrs(ifaceobjrunning.name)
+ if not vxlanattrs:
+ return
+ attrval = vxlanattrs.get('vxlanid')
+ if attrval:
+ ifaceobjrunning.update_config('vxlan-id', vxlanattrs.get('vxlanid'))
+ attrval = vxlanattrs.get('local')
+ if attrval:
+ ifaceobjrunning.update_config('vxlan-local-tunnelip', attrval)
+ attrval = vxlanattrs.get('svcnode')
+ if attrval:
+ [ifaceobjrunning.update_config('vxlan-svcnode', a)
+ for a in attrval]
+ attrval = vxlanattrs.get('peernode')
+ if attrval:
+ [ifaceobjrunning.update_config('vxlan-peernode', a)
+ for a in attrval]
+ attrval = vxlanattrs.get('learning')
+ if attrval and attrval == 'on':
+ ifaceobjrunning.update_config('vxlan-learning', 'on')
+
+
+ _run_ops = {'pre-up' : _up,
+ 'post-down' : _down,
+ 'query-checkcurr' : _query_check,
+ 'query-running' : _query_running}
+
+ def get_ops(self):
+ return self._run_ops.keys()
+
+ def _init_command_handlers(self):
+ if not self.ipcmd:
+ self.ipcmd = iproute2(**self.get_flags())
+
+ def run(self, ifaceobj, operation, query_ifaceobj=None, **extra_args):
+ op_handler = self._run_ops.get(operation)
+ if not op_handler:
+ return
+ if (operation != 'query-running' and
+ not self._is_vxlan_device(ifaceobj)):
+ return
+ self._init_command_handlers()
+ if operation == 'query-checkcurr':
+ op_handler(self, ifaceobj, query_ifaceobj)
+ else:
+ op_handler(self, ifaceobj)
--- /dev/null
+pre-up,ifenslave
+pre-up,clagd
+pre-up,vlan
+pre-up,vxlan
+pre-up,usercmds
+pre-up,bridge
+pre-up,bridgevlan
+pre-up,mstpctl
+up,dhcp
+up,address
+up,addressvirtual
+up,usercmds
+post-up,ethtool
+post-up,usercmds
+post-up,clagd
+pre-down,usercmds
+down,dhcp
+down,addressvirtual
+down,address
+down,usercmds
+post-down,clagd
+post-down,mstpctl
+post-down,bridgevlan
+post-down,bridge
+post-down,vxlan
+post-down,vlan
+post-down,ifenslave
+post-down,usercmds
# default template lookup path during template rendering
template_lookuppath=/etc/network/ifupdown2/templates
+
+# Support /etc/network/if-*/ scripts
+addon_scripts_support=0
+
+# By default ifupdown2 only supports a single vlan filtering bridge
+# on the system. Set this flag to 1 to support multiple vlan
+# filtering bridges
+multiple_vlan_aware_bridge_support=0
+
+# ifquery check status strings.
+# By default `ifquery --check` prints the check and
+# cross marks against interface attributes.
+# Use the below strings to modify the default behaviour.
+#
+ifquery_check_success_str=[pass]
+ifquery_check_error_str=[fail]
+ifquery_check_unknown_str=
+#
+
+# This attribute controls iface/vlan range expansions
+# in ifquery default output.
+ifquery_ifacename_expand_range=0
+
+# Let link master (bridges, bonds) own the link state of slaves
+link_master_slave=1
+
+# Delay admin state change till the end
+delay_admin_state_change=0
+
--- /dev/null
+#
+#
+# Parameters for the /etc/init.d/networking script
+#
+#
+
+# Change the below to yes if you want verbose logging to be enabled
+VERBOSE="no"
+
+# Change the below to yes if you want debug logging to be enabled
+DEBUG="no"
+
+# Change the below to yes if you want logging to go to syslog
+SYSLOG="no"
+
+# Exclude interfaces
+EXCLUDE_INTERFACES=
fi
fi
- [ -e /sbin/ifup ] || ln -s /sbin/ifupdown /sbin/ifup
- [ -e /sbin/ifdown ] || ln -s /sbin/ifupdown /sbin/ifdown
- [ -e /sbin/ifquery ] || ln -s /sbin/ifupdown /sbin/ifquery
- [ -e /sbin/ifreload ] || ln -s /sbin/ifupdown /sbin/ifreload
+ [ -e /sbin/ifup ] || ln -sf /sbin/ifupdown /sbin/ifup
+ [ -e /sbin/ifdown ] || ln -sf /sbin/ifupdown /sbin/ifdown
+ [ -e /sbin/ifquery ] || ln -sf /sbin/ifupdown /sbin/ifquery
+ [ -e /sbin/ifreload ] || ln -sf /sbin/ifupdown /sbin/ifreload
(cd /usr/share/man/man8/ && ln -sf /usr/share/man/man8/ifup.8.gz ifdown.8.gz)
--- /dev/null
+# Makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line.
+SPHINXOPTS =
+SPHINXBUILD = sphinx-build
+PAPER =
+BUILDDIR = build
+
+# Internal variables.
+PAPEROPT_a4 = -D latex_paper_size=a4
+PAPEROPT_letter = -D latex_paper_size=letter
+ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source
+# the i18n builder cannot share the environment and doctrees with the others
+I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source
+
+.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
+
+help:
+ @echo "Please use \`make <target>' where <target> is one of"
+ @echo " html to make standalone HTML files"
+ @echo " dirhtml to make HTML files named index.html in directories"
+ @echo " singlehtml to make a single large HTML file"
+ @echo " pickle to make pickle files"
+ @echo " json to make JSON files"
+ @echo " htmlhelp to make HTML files and a HTML help project"
+ @echo " qthelp to make HTML files and a qthelp project"
+ @echo " devhelp to make HTML files and a Devhelp project"
+ @echo " epub to make an epub"
+ @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
+ @echo " latexpdf to make LaTeX files and run them through pdflatex"
+ @echo " text to make text files"
+ @echo " man to make manual pages"
+ @echo " texinfo to make Texinfo files"
+ @echo " info to make Texinfo files and run them through makeinfo"
+ @echo " gettext to make PO message catalogs"
+ @echo " changes to make an overview of all changed/added/deprecated items"
+ @echo " linkcheck to check all external links for integrity"
+ @echo " doctest to run all doctests embedded in the documentation (if enabled)"
+
+clean:
+ -rm -rf $(BUILDDIR)/*
+
+html:
+ $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
+ @echo
+ @echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
+
+dirhtml:
+ $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
+ @echo
+ @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
+
+singlehtml:
+ $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
+ @echo
+ @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
+
+pickle:
+ $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
+ @echo
+ @echo "Build finished; now you can process the pickle files."
+
+json:
+ $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
+ @echo
+ @echo "Build finished; now you can process the JSON files."
+
+htmlhelp:
+ $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
+ @echo
+ @echo "Build finished; now you can run HTML Help Workshop with the" \
+ ".hhp project file in $(BUILDDIR)/htmlhelp."
+
+qthelp:
+ $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
+ @echo
+ @echo "Build finished; now you can run "qcollectiongenerator" with the" \
+ ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
+ @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/ifupdown2.qhcp"
+ @echo "To view the help file:"
+ @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/ifupdown2.qhc"
+
+devhelp:
+ $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
+ @echo
+ @echo "Build finished."
+ @echo "To view the help file:"
+ @echo "# mkdir -p $$HOME/.local/share/devhelp/ifupdown2"
+ @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/ifupdown2"
+ @echo "# devhelp"
+
+epub:
+ $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
+ @echo
+ @echo "Build finished. The epub file is in $(BUILDDIR)/epub."
+
+latex:
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+ @echo
+ @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
+ @echo "Run \`make' in that directory to run these through (pdf)latex" \
+ "(use \`make latexpdf' here to do that automatically)."
+
+latexpdf:
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+ @echo "Running LaTeX files through pdflatex..."
+ $(MAKE) -C $(BUILDDIR)/latex all-pdf
+ @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
+
+text:
+ $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
+ @echo
+ @echo "Build finished. The text files are in $(BUILDDIR)/text."
+
+man:
+ $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
+ @echo
+ @echo "Build finished. The manual pages are in $(BUILDDIR)/man."
+
+texinfo:
+ $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
+ @echo
+ @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
+ @echo "Run \`make' in that directory to run these through makeinfo" \
+ "(use \`make info' here to do that automatically)."
+
+info:
+ $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
+ @echo "Running Texinfo files through makeinfo..."
+ make -C $(BUILDDIR)/texinfo info
+ @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
+
+gettext:
+ $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
+ @echo
+ @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
+
+changes:
+ $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
+ @echo
+ @echo "The overview file is in $(BUILDDIR)/changes."
+
+linkcheck:
+ $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
+ @echo
+ @echo "Link check complete; look for any errors in the above output " \
+ "or in $(BUILDDIR)/linkcheck/output.txt."
+
+doctest:
+ $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
+ @echo "Testing of doctests in the sources finished, look at the " \
+ "results in $(BUILDDIR)/doctest/output.txt."
--- /dev/null
+Documentation for the ifupdownaddons default addons modules
+***********************************************************
+
+address
+=======
+
+.. automodule:: address
+
+.. autoclass:: address
+ :members: run, get_ops
+
+
+bridge
+======
+
+.. automodule:: bridge
+
+.. autoclass:: bridge
+ :members: run, get_ops
+
+dhcp
+====
+
+.. automodule:: dhcp
+
+.. autoclass:: dhcp
+
+ethtool
+=======
+
+.. automodule:: ethtool
+
+.. autoclass:: ethtool
+
+ifenslave
+=========
+
+.. automodule:: ifenslave
+
+.. autoclass:: ifenslave
+
+mstpctl
+=======
+
+.. automodule:: mstpctl
+
+.. autoclass:: mstpctl
+
+usercmds
+========
+
+.. automodule:: usercmds
+
+.. autoclass:: usercmds
+
+vlan
+====
+
+.. automodule:: vlan
+
+.. autoclass:: vlan
--- /dev/null
+Documentation for the ifupdownaddons package helper modules
+***********************************************************
+
+This package contains modules that provide helper methods
+for ifupdown2 addon modules to interact directly with tools
+like iproute2, brctl etc.
+
+
+bridgeutils
+===========
+
+Helper module to work with bridgeutil commands
+
+.. automodule:: bridgeutils
+
+.. autoclass:: brctl
+
+ifenslaveutil
+=============
+
+Helper module to interact with linux api to create bonds.
+Currently this is via sysfs.
+
+.. automodule:: ifenslaveutil
+
+.. autoclass:: ifenslaveutil
+
+dhclient
+========
+
+Helper module to interact with dhclient tools.
+
+.. automodule:: dhclient
+
+.. autoclass:: dhclient
+
+iproute2
+========
+
+Helper module to interact with iproute2 tools.
+
+.. automodule:: iproute2
+
+.. autoclass:: iproute2
--- /dev/null
+# -*- coding: utf-8 -*-
+#
+# ifupdown2-addons documentation build configuration file, created by
+# sphinx-quickstart on Mon Jul 21 11:17:17 2014.
+#
+# This file is execfile()d with the current directory set to its containing dir.
+#
+# Note that not all possible configuration values are present in this
+# autogenerated file.
+#
+# All configuration values have a default; values that are commented out
+# serve to show the default.
+
+import sys, os
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+#sys.path.insert(0, os.path.abspath('.'))
+
+sys.path.insert(0, os.path.abspath('../../addons'))
+sys.path.append(os.path.abspath('../../'))
+sys.path.append(os.path.abspath('../../ifupdownaddons'))
+sys.path.append(os.path.abspath('../../../ifupdown2'))
+
+# -- General configuration -----------------------------------------------------
+
+# If your documentation needs a minimal Sphinx version, state it here.
+#needs_sphinx = '1.0'
+
+# Add any Sphinx extension module names here, as strings. They can be extensions
+# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
+extensions = ['sphinx.ext.autodoc']
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['_templates']
+
+# The suffix of source filenames.
+source_suffix = '.rst'
+
+# The encoding of source files.
+#source_encoding = 'utf-8-sig'
+
+# The master toctree document.
+master_doc = 'index'
+
+# General information about the project.
+project = u'ifupdown2-addons'
+copyright = u'2014, Roopa Prabhu'
+
+# The version info for the project you're documenting, acts as replacement for
+# |version| and |release|, also used in various other places throughout the
+# built documents.
+#
+# The short X.Y version.
+version = '0.1'
+# The full version, including alpha/beta/rc tags.
+release = '0.1'
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+#language = None
+
+# There are two options for replacing |today|: either, you set today to some
+# non-false value, then it is used:
+#today = ''
+# Else, today_fmt is used as the format for a strftime call.
+#today_fmt = '%B %d, %Y'
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+exclude_patterns = []
+
+# The reST default role (used for this markup: `text`) to use for all documents.
+#default_role = None
+
+# If true, '()' will be appended to :func: etc. cross-reference text.
+#add_function_parentheses = True
+
+# If true, the current module name will be prepended to all description
+# unit titles (such as .. function::).
+#add_module_names = True
+
+# If true, sectionauthor and moduleauthor directives will be shown in the
+# output. They are ignored by default.
+#show_authors = False
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'sphinx'
+
+# A list of ignored prefixes for module index sorting.
+#modindex_common_prefix = []
+
+
+# -- Options for HTML output ---------------------------------------------------
+
+# The theme to use for HTML and HTML Help pages. See the documentation for
+# a list of builtin themes.
+html_theme = 'default'
+
+# Theme options are theme-specific and customize the look and feel of a theme
+# further. For a list of options available for each theme, see the
+# documentation.
+#html_theme_options = {}
+
+# Add any paths that contain custom themes here, relative to this directory.
+#html_theme_path = []
+
+# The name for this set of Sphinx documents. If None, it defaults to
+# "<project> v<release> documentation".
+#html_title = None
+
+# A shorter title for the navigation bar. Default is the same as html_title.
+#html_short_title = None
+
+# The name of an image file (relative to this directory) to place at the top
+# of the sidebar.
+#html_logo = None
+
+# The name of an image file (within the static path) to use as favicon of the
+# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
+# pixels large.
+#html_favicon = None
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = ['_static']
+
+# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
+# using the given strftime format.
+#html_last_updated_fmt = '%b %d, %Y'
+
+# If true, SmartyPants will be used to convert quotes and dashes to
+# typographically correct entities.
+#html_use_smartypants = True
+
+# Custom sidebar templates, maps document names to template names.
+#html_sidebars = {}
+
+# Additional templates that should be rendered to pages, maps page names to
+# template names.
+#html_additional_pages = {}
+
+# If false, no module index is generated.
+#html_domain_indices = True
+
+# If false, no index is generated.
+#html_use_index = True
+
+# If true, the index is split into individual pages for each letter.
+#html_split_index = False
+
+# If true, links to the reST sources are added to the pages.
+#html_show_sourcelink = True
+
+# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
+#html_show_sphinx = True
+
+# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
+#html_show_copyright = True
+
+# If true, an OpenSearch description file will be output, and all pages will
+# contain a <link> tag referring to it. The value of this option must be the
+# base URL from which the finished HTML is served.
+#html_use_opensearch = ''
+
+# This is the file name suffix for HTML files (e.g. ".xhtml").
+#html_file_suffix = None
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = 'ifupdown2-addonsdoc'
+
+
+# -- Options for LaTeX output --------------------------------------------------
+
+latex_elements = {
+# The paper size ('letterpaper' or 'a4paper').
+#'papersize': 'letterpaper',
+
+# The font size ('10pt', '11pt' or '12pt').
+#'pointsize': '10pt',
+
+# Additional stuff for the LaTeX preamble.
+#'preamble': '',
+}
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title, author, documentclass [howto/manual]).
+latex_documents = [
+ ('index', 'ifupdown2-addons.tex', u'ifupdown2-addons Documentation',
+ u'Roopa Prabhu', 'manual'),
+]
+
+# The name of an image file (relative to this directory) to place at the top of
+# the title page.
+#latex_logo = None
+
+# For "manual" documents, if this is true, then toplevel headings are parts,
+# not chapters.
+#latex_use_parts = False
+
+# If true, show page references after internal links.
+#latex_show_pagerefs = False
+
+# If true, show URL addresses after external links.
+#latex_show_urls = False
+
+# Documents to append as an appendix to all manuals.
+#latex_appendices = []
+
+# If false, no module index is generated.
+#latex_domain_indices = True
+
+
+# -- Options for manual page output --------------------------------------------
+
+# One entry per manual page. List of tuples
+# (source start file, name, description, authors, manual section).
+man_pages = [
+ ('index', 'ifupdown2-addons', u'ifupdown2-addons Documentation',
+ [u'Roopa Prabhu'], 1)
+]
+
+# If true, show URL addresses after external links.
+#man_show_urls = False
+
+
+# -- Options for Texinfo output ------------------------------------------------
+
+# Grouping the document tree into Texinfo files. List of tuples
+# (source start file, target name, title, author,
+# dir menu entry, description, category)
+texinfo_documents = [
+ ('index', 'ifupdown2-addons', u'ifupdown2-addons Documentation',
+ u'Roopa Prabhu', 'ifupdown2-addons', 'One line description of project.',
+ 'Miscellaneous'),
+]
+
+# Documents to append as an appendix to all manuals.
+#texinfo_appendices = []
+
+# If false, no module index is generated.
+#texinfo_domain_indices = True
+
+# How to display URL addresses: 'footnote', 'no', or 'inline'.
+#texinfo_show_urls = 'footnote'
--- /dev/null
+Development Corner
+==================
+
+Writing a ifupdown2 addon module
+--------------------------------
+ifupdown2 addon modules are part of the python-ifupdown2-addons package.
+They are installed under /usr/share/ifupdownaddons directory on the target
+system.
+
+The center of the universe for an addon module is the 'class iface' object
+exported by the python-ifupdown2 package.
+
+The iface object is modeled after an iface entry in the user provided network
+configuration file (eg. /etc/network/interfaces). For more details see
+the api reference for the iface class.
+
+ifupdown2 dynamically loads a python addon module. It expects the addon module
+to implement a few methods.
+
+* all addon modules must inherit from moduleBase class
+* the module must implement a class by the same name
+* the network interface object (class iface) and the operation to be performed
+ is passed to the modules. Operation can be any of 'pre-up', 'up', 'post-up',
+ 'pre-down', 'down', 'post-down', 'query-check', 'query-running'.
+ The module can choose to support a subset or all operations.
+ In cases when the operation is query-check, the module must compare between
+ the given and running state and return the checked state of the object in
+ queryobjcur passed as argument to the run menthod.
+* the python addon class must provide a few methods:
+ * run() : method to configure the interface.
+ * get_ops() : must return a list of operations it supports.
+ eg: 'pre-up', 'post-down'
+ * get_dependent_ifacenames() : must return a list of interfaces the
+ supported interface is dependent on. This is used to build the
+ dependency list for sorting and executing interfaces in dependency order.
+ * if the module supports -r option to ifquery, ie ability to construct the
+ ifaceobj from running state, it can optionally implement the
+ get_dependent_ifacenames_running() method, to return the list of
+ dependent interfaces derived from running state of the interface.
+ This is different from get_dependent_ifacenames() where the dependent
+ interfaces are derived from the interfaces config file (provided by the
+ user).
+ * provide a dictionary of all supported attributes in the _modinfo
+ attribute. This is useful for syntax help and man page generation.
+
+python-ifupdown2-addons package also installs ifupdownaddons python package
+that contains helper modules for all addon modules. Its optional for the addon
+module to use this package.
+
+see example address handling module /usr/share/ifupdownaddons/address.py
+
+API reference
+-------------
+.. toctree::
+ :maxdepth: 2
+
+ addonsapiref.rst
+ addonshelperapiref.rst
--- /dev/null
+Getting Started
+===============
+
+Prerequisites
+-------------
+* python-ifupdown2-addons is currently only tested on debian wheezy
+* python-ifupdown2-addons needs python version 2.6 or greater
+* build depends on: python-stdeb (for deb builds), python-docutils (for rst2man)
+* depends on python-gvgen package for printing interface graphs (this will be made optional in the future)
+* optional dependency for template engine: python-mako
+* python-ifupdown2-addons has an install dependency on python-ifupdown2
+
+Building
+--------
+$git clone <ifupdown2 git url> ifupdown2
+
+$cd ifupdown2/ifupdown2-addons
+
+$./build.sh
+
+Installing
+----------
+install generated python-ifupdown2-addons-<ver>.deb using dpkg
+
+$dpkg -i <python-ifupdown2-addons-<ver>.deb
+
+
+
+
--- /dev/null
+.. ifupdown2 documentation master file, created by
+ sphinx-quickstart on Sun Jul 6 23:49:20 2014.
+ You can adapt this file completely to your liking, but it should at least
+ contain the root `toctree` directive.
+
+Welcome to ifupdown2-addons documentation!
+==========================================
+
+Contents:
+=========
+
+.. toctree::
+ :maxdepth: 2
+
+ intro.rst
+ gettingstarted.rst
+ developmentcorner.rst
+
+Indices and tables
+==================
+
+* :ref:`genindex`
+* :ref:`modindex`
+* :ref:`search`
+
--- /dev/null
+python-ifupdown2-addons
+-----------------------
+
+The python-ifupdown2-addons package contains ifupdown2 addon modules.
+
+addon modules are responsible for applying interface configuration.
+The modules are installed under /usr/share/ifupdownmodules.
+
+Each module can declare its own set of supported attributes. Each module
+is passed the iface object (which is a representation of /etc/network/interfaces
+iface entry). Each module is also passed the operation to be performed.
+
+Example modules are /usr/share/ifupdownmodules/address.py,
+/usr/share/ifupdownmodules/bridge.py etc
+
+The order in which these modules are invoked is listed in
+/var/lib/ifupdownaddons/addons.conf. There is a ifaddon utility in the works
+to better manage the module ordering.
+
+For details on how to add a module, see the api reference and development
+documentation.
# Except bridge-ports, none of the other attributes are required. Default
# values are documented in the ifupdown-addons-interfaces(5) man page.
#
+# The bridge in the example below is a vlan unaware bridge (classic linux
+# bridge)
+#
auto br-300
iface br-300 inet static
address 12.0.0.3/24
--- /dev/null
+#
+# vlan-aware bridge simple example
+#
+# 'bridge' is a vlan aware bridge with all ports (swp1-52).
+# native vlan is by default 1
+#
+# 'bridge-vids' attribute is used to declare vlans.
+# 'bridge-pvid' attribute is used to specify native vlans if other than 1
+# 'bridge-access' attribute is used to declare access port
+#
+
+#
+# ports swp1-swp52 are trunk ports which inherit vlans from 'bridge'
+# ie vlans 310 700 707 712 850 910
+
+#
+# the following is a vlan aware bridge with ports swp1-swp52
+# It has stp on
+#
+auto bridge
+iface bridge
+ bridge-vlan-aware yes
+ bridge-ports glob swp1-52
+ bridge-stp on
+ bridge-vids 310 700 707 712 850 910
--- /dev/null
+#
+# vlan-aware bridge access ports and pruned vlan example
+#
+# 'bridge' is a vlan aware bridge with all ports (swp1-52).
+# native vlan is by default 1
+#
+# 'bridge-vids' attribute is used to declare vlans.
+# 'bridge-pvid' attribute is used to specify native vlans if other than 1
+# 'bridge-access' attribute is used to declare access port
+#
+#
+
+# The following is an access port to vlan 310, no trunking
+auto swp1
+iface swp1
+ bridge-access 310
+ mstpctl-portadminedge yes
+ mstpctl-bpduguard yes
+
+# The following is a truk port that is "pruned".
+# native vlan is 1, but only .1q tags of 707, 712, 850 are
+# sent and received
+#
+auto swp2
+iface swp2
+ bridge-vids 707 712 850
+ mstpctl-portadminedge yes
+ mstpctl-bpduguard yes
+
+# The following port is the trunk uplink and inherits all vlans
+# from 'bridge'
+auto swp49
+iface swp49
+ mstpctl-portpathcost 10
+ # Enable bridge assurance on uplink port using 'portnetwork' attribute
+ mstpctl-portnetwork yes
+
+# The following port is the trunk uplink and inherits all vlans
+# from 'bridge'
+auto swp50
+iface swp50
+ mstpctl-portpathcost 0
+ # Enable bridge assurance on uplink port using 'portnetwork' attribute
+ mstpctl-portnetwork yes
+
+#
+# ports swp3-swp48 are trunk ports which inherit vlans from the 'bridge'
+# ie vlans 310,700,707,712,850,910
+
+#
+# the following is a vlan aware bridge with ports swp1-swp52
+# It has stp on
+#
+auto bridge
+iface bridge
+ bridge-vlan-aware yes
+ bridge-ports glob swp1-52
+ bridge-stp on
+ bridge-vids 310 700 707 712 850 910
--- /dev/null
+#
+# vlan-aware bridge with bonds example
+#
+# uplink1, peerlink and downlink are bond interfaces.
+# 'bridge' is a vlan aware bridge with ports uplink1, peerlink
+# and downlink (swp2-20).
+#
+# native vlan is by default 1
+#
+# 'bridge-vids' attribute is used to declare vlans.
+# 'bridge-pvid' attribute is used to specify native vlans if other than 1
+# 'bridge-access' attribute is used to declare access port
+#
+auto lo
+iface lo
+
+auto eth0
+iface eth0 inet dhcp
+
+# bond interface
+auto uplink1
+iface uplink1
+ bond-slaves swp32
+ bond-mode 802.3ad
+ bond-miimon 100
+ bond-use-carrier 1
+ bond-lacp-rate 1
+ bond-min-links 1
+ bond-xmit-hash-policy layer2
+ bridge-vids 2000-2079
+
+# bond interface
+auto peerlink
+iface peerlink
+ bond-slaves swp30 swp31
+ bond-mode 802.3ad
+ bond-miimon 100
+ bond-use-carrier 1
+ bond-lacp-rate 1
+ bond-min-links 1
+ bond-xmit-hash-policy layer3+4
+ bridge-vids 2000-2079 4094
+
+# bond interface
+auto downlink
+iface downlink
+ bond-slaves swp1
+ bond-mode 802.3ad
+ bond-miimon 100
+ bond-use-carrier 1
+ bond-lacp-rate 1
+ bond-min-links 1
+ bond-xmit-hash-policy layer3+4
+ bridge-vids 2000-2079
+
+#
+# Declare vlans for all swp ports
+# swp2-20 get vlans from 2004 to 2022.
+# The below uses mako templates to generate iface sections
+# with vlans for swp ports
+#
+%for port, vlanid in zip(range(2, 20), range(2004, 2022)) :
+ auto swp${port}
+ iface swp${port}
+ bridge-vids ${vlanid}
+
+%endfor
+
+# svi vlan 4094
+auto bridge.4094
+iface bridge.4094
+ address 11.100.1.252/24
+
+# l2 attributes for vlan 4094
+auto bridge.4094
+vlan bridge.4094
+ bridge-igmp-querier-src 172.16.101.1
+
+#
+# vlan aware bridge
+#
+auto bridge
+iface bridge
+ bridge-vlan-aware yes
+ bridge-ports uplink1 peerlink downlink glob swp2-20
+ bridge-stp on
+
+# svi peerlink vlan
+auto peerlink.4094
+iface peerlink.4094
+ address 192.168.10.1/30
+ broadcast 192.168.10.3
--- /dev/null
+#
+# vlan-aware bridge with clag example
+#
+#
+# 'bridge' is a vlan aware bridge with ports:
+# 'peer-bond spine-bond glob host-bond-0[1-2]'
+#
+# All ports inherit 'vlans 10 20-23' from the 'bridge-vids' attribute
+# under the bridge
+#
+# native vlan is by default 1
+#
+# 'bridge-vids' attribute is used to declare vlans.
+# 'bridge-pvid' attribute is used to specify native vlans if other than 1
+# 'bridge-access' attribute is used to declare access port
+#
+# 'spine-bond host-bond-0[1-2]' are clag bonds and will be considered by
+# clagd for dual connection. clag-id has to be a non-zero and has to match
+# across the peer switches for the bonds to become dual connected.
+
+# spine bond
+#
+auto spine-bond
+iface spine-bond
+ bond-slaves glob swp19-22
+ bond-mode 802.3ad
+ bond-miimon 100
+ bond-use-carrier 1
+ bond-lacp-rate 1
+ bond-min-links 1
+ bond-xmit-hash-policy layer3+4
+ clag-id 100
+
+# mlag bond and peer interface
+#
+auto peer-bond
+iface peer-bond
+ bond-slaves glob swp23-24
+ bond-mode 802.3ad
+ bond-miimon 100
+ bond-use-carrier 1
+ bond-lacp-rate 1
+ bond-min-links 1
+ bond-xmit-hash-policy layer3+4
+
+# sub-interface for clagd communication
+#
+auto peer-bond.4094
+iface peer-bond.4094
+ address 169.254.0.1/30
+ clagd-peer-ip 169.254.0.2
+ clagd-sys-mac 44:38:39:ff:00:01
+ #clagd-priority 4096
+ # Please see man clagd for more options
+ # clagd-args --peerTimeout 30
+
+# host ports
+#
+auto host-bond-01
+iface host-bond-01
+ bond-slaves swp1
+ bond-mode 802.3ad
+ bond-miimon 100
+ bond-use-carrier 1
+ bond-lacp-rate 1
+ bond-min-links 1
+ bond-xmit-hash-policy layer3+4
+ clag-id 1
+
+auto host-bond-02
+iface host-bond-02
+ bond-slaves swp2
+ bond-mode 802.3ad
+ bond-miimon 100
+ bond-use-carrier 1
+ bond-lacp-rate 1
+ bond-min-links 1
+ bond-xmit-hash-policy layer3+4
+ clag-id 2
+
+# the bridge
+auto bridge
+iface bridge
+ bridge-vlan-aware yes
+ bridge-ports peer-bond spine-bond glob host-bond-0[1-2]
+ bridge-stp on
+ bridge-vids 10 20-23
import logging
import json
+class ifaceType():
+ UNKNOWN = 0x0
+ IFACE = 0x1
+ BRIDGE_VLAN = 0x2
+
+class ifaceLinkKind():
+ UNKNOWN = 0x0
+ BRIDGE = 0x1
+ BOND = 0x2
+
+class ifaceLinkType():
+ LINK_UNKNOWN = 0x0
+ LINK_SLAVE = 0x1
+ LINK_MASTER = 0x2
+ LINK_NA = 0x3
+
class ifaceStatus():
"""Enumerates iface status """
"""
# flag to indicate that the object was created from pickled state
- _PICKLED = 0x1
- HAS_SIBLINGS = 0x2
+ _PICKLED = 0x00000001
+ HAS_SIBLINGS = 0x00000010
+ IFACERANGE_ENTRY = 0x00000100
+ IFACERANGE_START = 0x00001000
version = '0.1'
"""iface state (of type ifaceState) """
self.status = ifaceStatus.UNKNOWN
"""iface status (of type ifaceStatus) """
+ self.status_str = None
+ """iface status str (string representing the status) """
self.flags = 0x0
"""iface flags """
self.priv_flags = 0x0
"""iface priv flags. can be used by the external object manager """
self.refcnt = 0
"""iface refcnt (incremented for each dependent this interface has) """
- self.lowerifaces = None
+ self.lowerifaces = None
"""lower iface list (in other words: slaves of this interface """
self.upperifaces = None
"""upper iface list (in other words: master of this interface """
"""interface config/attributes in raw format (eg: as it appeared in the interfaces file)"""
self.linkstate = None
"""linkstate of the interface"""
+ self.type = ifaceType.UNKNOWN
+ """interface type"""
+ self.priv_data = None
+ self.realname = None
+ self.link_type = ifaceLinkType.LINK_UNKNOWN
+ self.link_kind = ifaceLinkKind.UNKNOWN
def _set_attrs_from_dict(self, attrdict):
self.auto = attrdict.get('auto', False)
return attr_value_list[0]
return None
+ def get_attrs_value_first(self, attrs):
+ """ get first value of the first attr in the list.
+ Useful when you have multiple attrs representing the
+ same thing.
+ """
+ for attr in attrs:
+ attr_value_list = self.config.get(attr)
+ if attr_value_list:
+ return attr_value_list[0]
+ return None
+
def get_attr_value_n(self, attr_name, attr_index):
""" get n'th value of the specified attr name """
attr_value_list = self.config.get(attr_name)
""" add attribute name and value to the interface config """
self.config.setdefault(attr_name, []).append(attr_value)
+ def replace_config(self, attr_name, attr_value):
+ """ add attribute name and value to the interface config """
+ self.config[attr_name] = [attr_value]
+
+ def delete_config(self, attr_name):
+ """ add attribute name and value to the interface config """
+ try:
+ del self.config[attr_name]
+ except:
+ pass
+
def update_config_dict(self, attrdict):
self.config.update(attrdict)
attr_value = ''
self.config.setdefault(attr_name, []).append(attr_value)
self._config_status.setdefault(attr_name, []).append(attr_status)
-
# set global iface state
- if attr_status:
+ if attr_status == 1:
self.status = ifaceStatus.ERROR
elif self.status != ifaceStatus.ERROR:
# Not already error, mark success
self.status = ifaceStatus.SUCCESS
+ def check_n_update_config_with_status_many(self, ifaceobjorig, attr_names,
+ attr_status=0):
+ # set multiple attribute status to zero
+ # also updates status only if the attribute is present
+ for attr_name in attr_names:
+ if not ifaceobjorig.get_attr_value_first(attr_name):
+ continue
+ self.config.setdefault(attr_name, []).append('')
+ self._config_status.setdefault(attr_name, []).append(attr_status)
+
def get_config_attr_status(self, attr_name, idx=0):
""" get status of a attribute config on this interface.
Returns True if object self is same as dstiface and False otherwise """
if self.name != dstiface.name: return False
+ if self.type != dstiface.type: return False
if self.addr_family != dstiface.addr_family: return False
if self.addr_method != dstiface.addr_method: return False
if self.auto != dstiface.auto: return False
if v != dstiface.config.get(k)): return False
return True
+
def __getstate__(self):
odict = self.__dict__.copy()
del odict['state']
del odict['raw_config']
del odict['linkstate']
del odict['env']
+ del odict['link_type']
+ del odict['link_kind']
return odict
def __setstate__(self, dict):
self.priv_flags = 0
self.raw_config = []
self.flags |= self._PICKLED
+ self.link_type = ifaceLinkType.LINK_NA
+ self.link_kind = ifaceLinkKind.UNKNOWN
def dump_raw(self, logger):
indent = ' '
logger.info('}')
def dump_pretty(self, with_status=False,
- successstr='success', errorstr='error'):
+ successstr='success', errorstr='error',
+ unknownstr='unknown', use_realname=False):
indent = '\t'
outbuf = ''
+ if use_realname and self.realname:
+ name = '%s' %self.realname
+ else:
+ name = '%s' %self.name
if self.auto:
- outbuf += 'auto %s\n' %self.name
- outbuf += 'iface %s' %self.name
+ outbuf += 'auto %s\n' %name
+ ifaceline = ''
+ if self.type == ifaceType.BRIDGE_VLAN:
+ ifaceline += 'vlan %s' %name
+ else:
+ ifaceline += 'iface %s' %name
if self.addr_family:
- outbuf += ' %s' %self.addr_family
+ ifaceline += ' %s' %self.addr_family
if self.addr_method:
- outbuf += ' %s' %self.addr_method
+ ifaceline += ' %s' %self.addr_method
if with_status:
- if (self.status == ifaceStatus.NOTFOUND or
- self.status == ifaceStatus.ERROR):
- outbuf += ' (%s)' %errorstr
+ status_str = None
+ if (self.status == ifaceStatus.ERROR or
+ self.status == ifaceStatus.NOTFOUND):
+ if self.status_str:
+ ifaceline += ' (%s)' %self.status_str
+ status_str = errorstr
elif self.status == ifaceStatus.SUCCESS:
- outbuf += ' (%s)' %successstr
+ status_str = successstr
+ if status_str:
+ outbuf += '{0:65} {1:>8}'.format(ifaceline, status_str) + '\n'
+ else:
+ outbuf += ifaceline + '\n'
if self.status == ifaceStatus.NOTFOUND:
- if with_status:
- outbuf = (outbuf.encode('utf8')
- if isinstance(outbuf, unicode) else outbuf)
+ outbuf = (outbuf.encode('utf8')
+ if isinstance(outbuf, unicode) else outbuf)
print outbuf + '\n'
return
- outbuf += '\n'
+ else:
+ outbuf += ifaceline + '\n'
config = self.config
if config:
for cname, cvaluelist in config.items():
idx = 0
for cv in cvaluelist:
- if not cv: continue
if with_status:
s = self.get_config_attr_status(cname, idx)
- if s:
- outbuf += (indent + '%s %s (%s)\n'
- %(cname, cv, errorstr))
+ if s == -1:
+ status_str = unknownstr
+ elif s == 1:
+ status_str = errorstr
elif s == 0:
- outbuf += (indent + '%s %s (%s)\n'
- %(cname, cv, successstr))
+ status_str = successstr
+ outbuf += (indent + '{0:55} {1:>10}'.format(
+ '%s %s' %(cname, cv), status_str)) + '\n'
else:
outbuf += indent + '%s %s\n' %(cname, cv)
idx += 1
--- /dev/null
+#!/usr/bin/env python
+#
+# Copyright 2014 Cumulus Networks, Inc. All rights reserved.
+#
+# Author: Scott Feldman, sfeldma@cumulusnetworks.com
+#
+#
+# from /usr/include/linux/if.h
+#
+
+# Standard interface flags (netdevice->flags).
+
+IFF_UP = 0x1 # interface is up
+IFF_BROADCAST = 0x2 # broadcast address valid
+IFF_DEBUG = 0x4 # turn on debugging
+IFF_LOOPBACK = 0x8 # is a loopback net
+IFF_POINTOPOINT = 0x10 # interface is has p-p link
+IFF_NOTRAILERS = 0x20 # avoid use of trailers
+IFF_RUNNING = 0x40 # interface RFC2863 OPER_UP
+IFF_NOARP = 0x80 # no ARP protocol
+IFF_PROMISC = 0x100 # receive all packets
+IFF_ALLMULTI = 0x200 # receive all multicast packets
+
+IFF_MASTER = 0x400 # master of a load balancer
+IFF_SLAVE = 0x800 # slave of a load balancer
+
+IFF_MULTICAST = 0x1000 # Supports multicast
+
+IFF_PORTSEL = 0x2000 # can set media type
+IFF_AUTOMEDIA = 0x4000 # auto media select active
+IFF_DYNAMIC = 0x8000 # dialup device with changing addresses
+
+IFF_LOWER_UP = 0x10000 # driver signals L1 up
+IFF_DORMANT = 0x20000 # driver signals dormant
+
+IFF_ECHO = 0x40000 # echo sent packets
import re
import os
from iface import *
+import rtnetlink_api as rtnetlink_api
class ifupdownBase(object):
return os.path.exists('/sys/class/net/%s' %ifacename)
def link_up(self, ifacename):
- self.exec_command('ifconfig %s up' %ifacename)
+ rtnetlink_api.rtnl_api.link_set(ifacename, "up")
def link_down(self, ifacename):
- self.exec_command('ifconfig %s down' %ifacename)
+ rtnetlink_api.rtnl_api.link_set(ifacename, "down")
_tickmark = u'\u2713'
_crossmark = u'\u2717'
-_success_sym = _tickmark
-_error_sym = _crossmark
+_success_sym = '(%s)' %_tickmark
+_error_sym = '(%s)' %_crossmark
+
+class ifupdownFlags():
+ FORCE = False
+ DRYRUN = False
+ NOWAIT = False
+ PERFMODE = False
+ CACHE = False
+
+ # Flags
+ CACHE_FLAGS = 0x0
class ifupdownMain(ifupdownBase):
""" ifupdown2 main class """
ADDONS_ENABLE = False
# priv flags to mark iface objects
- BUILTIN = 0x1
- NOCONFIG = 0x2
+ BUILTIN = 0x0001
+ NOCONFIG = 0x0010
scripts_dir='/etc/network'
addon_modules_dir='/usr/share/ifupdownaddons'
# Handlers for ops that ifupdown2 owns
def run_up(self, ifaceobj):
- ifacename = ifaceobj.name
- if self.link_exists(ifacename):
- self.link_up(ifacename)
+ # Skip link sets on ifaceobjs of type 'vlan' (used for l2 attrs).
+ # there is no real interface behind it
+ if ifaceobj.type == ifaceType.BRIDGE_VLAN:
+ return
+ if (ifaceobj.addr_method and
+ ifaceobj.addr_method == 'manual'):
+ return
+ if self._delay_admin_state:
+ self._delay_admin_state_iface_queue.append(ifaceobj.name)
+ return
+ # If this object is a link slave, ie its link is controlled
+ # by its link master interface, then dont set the link state.
+ # But do allow user to change state of the link if the interface
+ # is already with its link master (hence the master check).
+ if ifaceobj.link_type == ifaceLinkType.LINK_SLAVE:
+ return
+ if not self.link_exists(ifaceobj.name):
+ return
+ self.link_up(ifaceobj.name)
def run_down(self, ifaceobj):
- ifacename = ifaceobj.name
- if self.link_exists(ifacename):
- self.link_down(ifacename)
+ # Skip link sets on ifaceobjs of type 'vlan' (used for l2 attrs)
+ # there is no real interface behind it
+ if ifaceobj.type == ifaceType.BRIDGE_VLAN:
+ return
+ if (ifaceobj.addr_method and
+ ifaceobj.addr_method == 'manual'):
+ return
+ if self._delay_admin_state:
+ self._delay_admin_state_iface_queue.append(ifaceobj.name)
+ return
+ # If this object is a link slave, ie its link is controlled
+ # by its link master interface, then dont set the link state.
+ # But do allow user to change state of the link if the interface
+ # is already with its link master (hence the master check).
+ if ifaceobj.link_type == ifaceLinkType.LINK_SLAVE:
+ return
+ if not self.link_exists(ifaceobj.name):
+ return
+ self.link_down(ifaceobj.name)
# ifupdown object interface operation handlers
ops_handlers = OrderedDict([('up', run_up),
self.config = config
self.logger.debug(self.config)
+ self.type = ifaceType.UNKNOWN
+
# Can be used to provide hints for caching
self.CACHE_FLAGS = 0x0
self._DELETE_DEPENDENT_IFACES_WITH_NOCONFIG = False
self.ADDONS_ENABLE = addons_enable
+ # Copy flags into ifupdownFlags
+ # XXX: before we transition fully to ifupdownFlags
+ ifupdownFlags.FORCE = force
+ ifupdownFlags.DRYRUN = dryrun
+ ifupdownFlags.NOWAIT = nowait
+ ifupdownFlags.PERFMODE = perfmode
+ ifupdownFlags.CACHE = cache
+
self.ifaces = OrderedDict()
self.njobs = njobs
self.pp = pprint.PrettyPrinter(indent=4)
self.load_scripts(self.scripts_dir)
self.dependency_graph = OrderedDict({})
+ self._cache_no_repeats = {}
+
if self.STATEMANAGER_ENABLE:
try:
self.statemanager = stateManager()
raise
else:
self.STATEMANAGER_UPDATE = False
+ self._delay_admin_state = True if self.config.get(
+ 'delay_admin_state_change', '0') == '1' else False
+ self._delay_admin_state_iface_queue = []
+ if self._delay_admin_state:
+ self.logger.info('\'delay_admin_state_change\' is set. admin ' +
+ 'state changes will be delayed till the end.')
+
+ self._link_master_slave = True if self.config.get(
+ 'link_master_slave', '0') == '1' else False
+ if self._link_master_slave:
+ self.logger.info('\'link_master_slave\' is set. slave admin ' +
+ 'state changes will be delayed till the ' +
+ 'masters admin state change.')
+
+ def link_master_slave_ignore_error(self, errorstr):
+ # If link master slave flag is set,
+ # there may be cases where the lowerdev may not be
+ # up resulting in 'Network is down' error
+ # This can happen if the lowerdev is a LINK_SLAVE
+ # of another interface which is not up yet
+ # example of such a case:
+ # bringing up a vlan on a bond interface and the bond
+ # is a LINK_SLAVE of a bridge (in other words the bond is
+ # part of a bridge) which is not up yet
+ if self._link_master_slave:
+ if 'Network is down':
+ return True
+ return False
def get_ifaceobjs(self, ifacename):
return self.ifaceobjdict.get(ifacename)
+ def get_ifaceobjs_saved(self, ifacename):
+ """ Return ifaceobjects from statemanager """
+ if self.STATEMANAGER_ENABLE:
+ return self.statemanager.get_ifaceobjs(ifacename)
+ else:
+ None
+
def get_ifaceobj_first(self, ifacename):
ifaceobjs = self.get_ifaceobjs(ifacename)
if ifaceobjs:
def get_iface_obj_last(self, ifacename):
return self.ifaceobjdict.get(ifacename)[-1]
+
+ def must_follow_upperifaces(self, ifacename):
+ #
+ # XXX: This bleeds the knowledge of iface
+ # types in the infrastructure module.
+ # Cant think of a better fix at the moment.
+ # In future maybe the module can set a flag
+ # to indicate if we should follow upperifaces
+ #
+ ifaceobj = self.get_ifaceobj_first(ifacename)
+ if ifaceobj.type == ifaceType.BRIDGE_VLAN:
+ return False
+ return True
+
def create_n_save_ifaceobj(self, ifacename, priv_flags=None,
increfcnt=False):
""" creates a iface object and adds it to the iface dictionary """
ifaceobj.name = ifacename
ifaceobj.priv_flags = priv_flags
ifaceobj.auto = True
+ if not self._link_master_slave:
+ ifaceobj.link_type = ifaceLinkType.LINK_NA
if increfcnt:
ifaceobj.inc_refcnt()
self.ifaceobjdict[ifacename] = [ifaceobj]
"""
ifaceobjcurr = iface()
ifaceobjcurr.name = ifaceobj.name
+ ifaceobjcurr.type = ifaceobj.type
ifaceobjcurr.lowerifaces = ifaceobj.lowerifaces
ifaceobjcurr.priv_flags = ifaceobj.priv_flags
ifaceobjcurr.auto = ifaceobj.auto
if not ifaceobj: return True
return self.is_ifaceobj_noconfig(ifaceobj)
- def preprocess_dependency_list(self, upperifacename, dlist, ops):
+ def preprocess_dependency_list(self, upperifaceobj, dlist, ops):
""" We go through the dependency list and
delete or add interfaces from the interfaces dict by
applying the following rules:
for d in dlist:
dilist = self.get_ifaceobjs(d)
if not dilist:
+ ni = None
if self.is_iface_builtin_byname(d):
- self.create_n_save_ifaceobj(d, self.BUILTIN | self.NOCONFIG,
- True).add_to_upperifaces(upperifacename)
+ ni = self.create_n_save_ifaceobj(d,
+ self.BUILTIN | self.NOCONFIG, True)
elif not self._DELETE_DEPENDENT_IFACES_WITH_NOCONFIG:
- self.create_n_save_ifaceobj(d, self.NOCONFIG,
- True).add_to_upperifaces(upperifacename)
+ ni = self.create_n_save_ifaceobj(d, self.NOCONFIG,
+ True)
else:
del_list.append(d)
+ if ni:
+ ni.add_to_upperifaces(upperifaceobj.name)
+ if upperifaceobj.link_type == ifaceLinkType.LINK_MASTER:
+ ni.link_type = ifaceLinkType.LINK_SLAVE
else:
for di in dilist:
di.inc_refcnt()
- di.add_to_upperifaces(upperifacename)
-
+ di.add_to_upperifaces(upperifaceobj.name)
+ if upperifaceobj.link_type == ifaceLinkType.LINK_MASTER:
+ di.link_type = ifaceLinkType.LINK_SLAVE
for d in del_list:
dlist.remove(d)
- def query_dependents(self, ifaceobj, ops):
+ def query_dependents(self, ifaceobj, ops, ifacenames, type=None):
""" Gets iface dependents by calling into respective modules """
- dlist = None
+ ret_dlist = []
# Get dependents for interface by querying respective modules
- for mname, module in self.modules.items():
- module = self.modules.get(mname)
+ for module in self.modules.values():
try:
if ops[0] == 'query-running':
if (not hasattr(module,
if (not hasattr(module, 'get_dependent_ifacenames')):
continue
dlist = module.get_dependent_ifacenames(ifaceobj,
- self.ifaceobjdict.keys())
+ ifacenames)
except Exception, e:
self.logger.warn('%s: error getting dependent interfaces (%s)'
%(ifaceobj.name, str(e)))
dlist = None
pass
- if dlist:
- break
- return dlist
+ if dlist: ret_dlist.extend(dlist)
+ return list(set(ret_dlist))
def populate_dependency_info(self, ops, ifacenames=None):
""" recursive function to generate iface dependency info """
continue
dlist = ifaceobj.lowerifaces
if not dlist:
- dlist = self.query_dependents(ifaceobj, ops)
+ dlist = self.query_dependents(ifaceobj, ops, ifacenames)
else:
continue
if dlist:
- self.preprocess_dependency_list(ifaceobj.name,
+ self.preprocess_dependency_list(ifaceobj,
dlist, ops)
ifaceobj.lowerifaces = dlist
[iqueue.append(d) for d in dlist]
if not self.dependency_graph.get(i):
self.dependency_graph[i] = dlist
+ def _check_config_no_repeats(self, ifaceobj):
+ """ check if object has an attribute that is
+ restricted to a single object in the system.
+ if yes, warn and return """
+ for k,v in self._cache_no_repeats.items():
+ iv = ifaceobj.config.get(k)
+ if iv and iv[0] == v:
+ self.logger.error('ignoring interface %s. ' %ifaceobj.name +
+ 'Only one object with attribute ' +
+ '\'%s %s\' allowed.' %(k, v))
+ return True
+ for k, v in self.config.get('no_repeats', {}).items():
+ iv = ifaceobj.config.get(k)
+ if iv and iv[0] == v:
+ self._cache_no_repeats[k] = v
+ return False
+
def _save_iface(self, ifaceobj):
+ if self._check_config_no_repeats(ifaceobj):
+ return
+ if not self._link_master_slave:
+ ifaceobj.link_type = ifaceLinkType.LINK_NA
currentifaceobjlist = self.ifaceobjdict.get(ifaceobj.name)
if not currentifaceobjlist:
self.ifaceobjdict[ifaceobj.name]= [ifaceobj]
if ifaceobj.compare(currentifaceobjlist[0]):
self.logger.warn('duplicate interface %s found' %ifaceobj.name)
return
- currentifaceobjlist[0].flags |= iface.HAS_SIBLINGS
- ifaceobj.flags |= iface.HAS_SIBLINGS
+ if currentifaceobjlist[0].type == ifaceobj.type:
+ currentifaceobjlist[0].flags |= iface.HAS_SIBLINGS
+ ifaceobj.flags |= iface.HAS_SIBLINGS
self.ifaceobjdict[ifaceobj.name].append(ifaceobj)
def _iface_configattr_syntax_checker(self, attrname, attrval):
def _ifaceobj_syntax_checker(self, ifaceobj):
err = False
- for attrname in ifaceobj.config:
+ for attrname, attrvalue in ifaceobj.config.items():
found = False
for k, v in self.module_attrs.items():
if v and v.get('attrs', {}).get(attrname):
with open(self.addon_modules_configfile, 'r') as f:
lines = f.readlines()
for l in lines:
- litems = l.rstrip(' \n\t\r').split(',')
- operation = litems[0]
- mname = litems[1]
- self.module_ops[operation].append(mname)
+ try:
+ litems = l.strip(' \n\t\r').split(',')
+ if not litems or len(litems) < 2:
+ continue
+ operation = litems[0]
+ mname = litems[1]
+ self.module_ops[operation].append(mname)
+ except Exception, e:
+ self.logger.warn('error reading line \'%s\'' %(l, str(e)))
+ continue
def load_addon_modules(self, modules_dir):
""" load python modules from modules_dir
# continue reading
pass
- def _sched_ifaces(self, ifacenames, ops):
+ def _sched_ifaces(self, ifacenames, ops, skipupperifaces=False):
self.logger.debug('scheduling \'%s\' for %s'
%(str(ops), str(ifacenames)))
-
self._pretty_print_ordered_dict('dependency graph',
self.dependency_graph)
return ifaceScheduler.sched_ifaces(self, ifacenames, ops,
order=ifaceSchedulerFlags.INORDER
if 'down' in ops[0]
else ifaceSchedulerFlags.POSTORDER,
- followdependents=True if self.WITH_DEPENDS else False)
-
- def _validate_ifaces(self, ifacenames):
+ followdependents=True if self.WITH_DEPENDS else False,
+ skipupperifaces=skipupperifaces)
+
+ def _render_ifacename(self, ifacename):
+ new_ifacenames = []
+ vlan_match = re.match("^([\d]+)-([\d]+)", ifacename)
+ if vlan_match:
+ vlan_groups = vlan_match.groups()
+ if vlan_groups[0] and vlan_groups[1]:
+ [new_ifacenames.append('%d' %v)
+ for v in range(int(vlan_groups[0]),
+ int(vlan_groups[1])+1)]
+ return new_ifacenames
+
+ def _preprocess_ifacenames(self, ifacenames):
""" validates interface list for config existance.
returns -1 if one or more interface not found. else, returns 0
"""
+ new_ifacenames = []
err_iface = ''
for i in ifacenames:
ifaceobjs = self.get_ifaceobjs(i)
if not ifaceobjs:
- err_iface += ' ' + i
+ # if name not available, render interface name and check again
+ rendered_ifacenames = utils.expand_iface_range(i)
+ if rendered_ifacenames:
+ for ri in rendered_ifacenames:
+ ifaceobjs = self.get_ifaceobjs(ri)
+ if not ifaceobjs:
+ err_iface += ' ' + ri
+ else:
+ new_ifacenames.append(ri)
+ else:
+ err_iface += ' ' + i
+ else:
+ new_ifacenames.append(i)
if err_iface:
raise Exception('cannot find interfaces:%s' %err_iface)
- return True
+ return new_ifacenames
def _iface_whitelisted(self, auto, allow_classes, excludepats, ifacename):
""" Checks if interface is whitelisted depending on set of parameters.
interfaces are checked against the allow_classes and auto lists.
"""
-
if excludepats:
for e in excludepats:
if re.search(e, ifacename):
traceback.print_tb(t)
self.logger.warning('error saving state (%s)' %str(e))
+ def set_type(self, type):
+ if type == 'iface':
+ self.type = ifaceType.IFACE
+ elif type == 'vlan':
+ self.type = ifaceType.BRIDGE_VLAN
+ else:
+ self.type = ifaceType.UNKNOWN
+
+ def _process_delay_admin_state_queue(self, op):
+ if not self._delay_admin_state_iface_queue:
+ return
+ if op == 'up':
+ func = self.link_up
+ elif op == 'down':
+ func = self.link_down
+ else:
+ return
+ for i in self._delay_admin_state_iface_queue:
+ try:
+ if self.link_exists(i):
+ func(i)
+ except Exception, e:
+ self.logger.warn(str(e))
+ pass
+
def up(self, ops, auto=False, allow_classes=None, ifacenames=None,
- excludepats=None, printdependency=None, syntaxcheck=False):
+ excludepats=None, printdependency=None, syntaxcheck=False,
+ type=None, skipupperifaces=False):
"""This brings the interface(s) up
Args:
- ops (list): list of ops to perform on the interface(s). Eg: ['pre-up', 'up', 'post-up'
+ ops (list): list of ops to perform on the interface(s).
+ Eg: ['pre-up', 'up', 'post-up'
Kwargs:
auto (bool): act on interfaces marked auto
syntaxcheck (bool): only perform syntax check
"""
+ self.set_type(type)
+
if allow_classes:
self.IFACE_CLASS = True
if not self.ADDONS_ENABLE: self.STATEMANAGER_UPDATE = False
return
if ifacenames:
- # If iface list is given by the caller, always check if iface
- # is present
- self._validate_ifaces(ifacenames)
+ ifacenames = self._preprocess_ifacenames(ifacenames)
# if iface list not given by user, assume all from config file
if not ifacenames: ifacenames = self.ifaceobjdict.keys()
self.populate_dependency_info(ops)
try:
- self._sched_ifaces(filtered_ifacenames, ops)
+ self._sched_ifaces(filtered_ifacenames, ops,
+ skipupperifaces=skipupperifaces)
finally:
+ self._process_delay_admin_state_queue('up')
if not self.DRYRUN and self.ADDONS_ENABLE:
self._save_state()
def down(self, ops, auto=False, allow_classes=None, ifacenames=None,
- excludepats=None, printdependency=None, usecurrentconfig=False):
+ excludepats=None, printdependency=None, usecurrentconfig=False,
+ type=None):
""" down an interface """
+ self.set_type(type)
+
if allow_classes:
self.IFACE_CLASS = True
if not self.ADDONS_ENABLE: self.STATEMANAGER_UPDATE = False
# If iface list is given by the caller, always check if iface
# is present
try:
- self._validate_ifaces(ifacenames)
+ ifacenames = self._preprocess_ifacenames(ifacenames)
except Exception, e:
raise Exception('%s' %str(e) +
' (interface was probably never up ?)')
excludepats, i)]
if not filtered_ifacenames:
raise Exception('no ifaces found matching given allow lists ' +
- '(interfaces were probably never up)')
+ '(or interfaces were probably never up ?)')
if printdependency:
self.populate_dependency_info(ops, filtered_ifacenames)
try:
self._sched_ifaces(filtered_ifacenames, ops)
finally:
+ self._process_delay_admin_state_queue('down')
if not self.DRYRUN and self.ADDONS_ENABLE:
self._save_state()
def query(self, ops, auto=False, allow_classes=None, ifacenames=None,
excludepats=None, printdependency=None,
- format='native'):
+ format='native', type=None):
""" query an interface """
+ self.set_type(type)
+
if allow_classes:
self.IFACE_CLASS = True
if self.STATEMANAGER_ENABLE and ops[0] == 'query-savedstate':
raise
if ifacenames and ops[0] != 'query-running':
- # If iface list is given, always check if iface is present
- self._validate_ifaces(ifacenames)
+ # If iface list is given, always check if iface is present
+ ifacenames = self._preprocess_ifacenames(ifacenames)
# if iface list not given by user, assume all from config file
if not ifacenames: ifacenames = self.ifaceobjdict.keys()
raise Exception('no ifaces found matching ' +
'given allow lists')
- self.populate_dependency_info(ops, filtered_ifacenames)
+ self.populate_dependency_info(ops)
if ops[0] == 'query-dependency' and printdependency:
self.print_dependency(filtered_ifacenames, printdependency)
return
self.print_ifaceobjsrunning_pretty(filtered_ifacenames, format)
return
- def reload(self, upops, downops, auto=False, allow=None,
- ifacenames=None, excludepats=None, usecurrentconfig=False):
- """ reload interface config """
+ def _reload_currentlyup(self, upops, downops, auto=True, allow=None,
+ ifacenames=None, excludepats=None, usecurrentconfig=False,
+ **extra_args):
+ """ reload currently up interfaces """
allow_classes = []
new_ifaceobjdict = {}
- self.logger.debug('reloading interface config ..')
+ # Override auto to true
+ auto = True
if auto:
self.ALL = True
self.WITH_DEPENDS = True
+ try:
+ self.read_iface_config()
+ except:
+ raise
+ if not self.ifaceobjdict:
+ self.logger.warn("nothing to reload ..exiting.")
+ return
+ already_up_ifacenames = []
+ # generate dependency graph of interfaces
+ self.populate_dependency_info(upops)
+ if (not usecurrentconfig and self.STATEMANAGER_ENABLE
+ and self.statemanager.ifaceobjdict):
+ already_up_ifacenames = self.statemanager.ifaceobjdict.keys()
+
+ if not ifacenames: ifacenames = self.ifaceobjdict.keys()
+ filtered_ifacenames = [i for i in ifacenames
+ if self._iface_whitelisted(auto, allow_classes,
+ excludepats, i)]
+
+ # Get already up interfaces that still exist in the interfaces file
+ already_up_ifacenames_not_present = Set(
+ already_up_ifacenames).difference(ifacenames)
+ already_up_ifacenames_still_present = Set(
+ already_up_ifacenames).difference(
+ already_up_ifacenames_not_present)
+ interfaces_to_up = Set(already_up_ifacenames_still_present).union(
+ filtered_ifacenames)
+
+ if (already_up_ifacenames_not_present and
+ self.config.get('ifreload_currentlyup_down_notpresent') == '1'):
+ self.logger.info('reload: schedule down on interfaces: %s'
+ %str(already_up_ifacenames_not_present))
+
+ # Save a copy of new iface objects and dependency_graph
+ new_ifaceobjdict = dict(self.ifaceobjdict)
+ new_dependency_graph = dict(self.dependency_graph)
+
+ # old interface config is read into self.ifaceobjdict
+ self.read_old_iface_config()
+
+ # reinitialize dependency graph
+ self.dependency_graph = OrderedDict({})
+ self.populate_dependency_info(downops,
+ already_up_ifacenames_not_present)
+ self._sched_ifaces(already_up_ifacenames_not_present, downops)
+ else:
+ self.logger.debug('no interfaces to down ..')
+
+ # Now, run 'up' with new config dict
+ # reset statemanager update flag to default
+ if new_ifaceobjdict:
+ self.ifaceobjdict = new_ifaceobjdict
+ self.dependency_graph = new_dependency_graph
+
+ if not self.ifaceobjdict:
+ return
+ self.logger.info('reload: scheduling up on interfaces: %s'
+ %str(interfaces_to_up))
+ self._sched_ifaces(interfaces_to_up, upops)
+ if self.DRYRUN:
+ return
+ self._save_state()
+ def _reload_default(self, upops, downops, auto=False, allow=None,
+ ifacenames=None, excludepats=None, usecurrentconfig=False,
+ **extra_args):
+ """ reload interface config """
+ allow_classes = []
+ new_ifaceobjdict = {}
+
+ if auto:
+ self.ALL = True
+ self.WITH_DEPENDS = True
try:
self.read_iface_config()
except:
if not self.ifaceobjdict:
self.logger.warn("nothing to reload ..exiting.")
return
-
# generate dependency graph of interfaces
self.populate_dependency_info(upops)
if (not usecurrentconfig and self.STATEMANAGER_ENABLE
# config
#
ifacedownlist = []
- for ifname, lastifaceobjlist in self.ifaceobjdict.items():
+ for ifname in filtered_ifacenames:
+ lastifaceobjlist = self.ifaceobjdict.get(ifname)
objidx = 0
# If interface is not present in the new file
# append it to the down list
continue
if ifacedownlist:
- self.logger.info('Executing down on interfaces: %s'
+ self.logger.info('reload: scheduling down on interfaces: %s'
%str(ifacedownlist))
# reinitialize dependency graph
self.dependency_graph = OrderedDict({})
# Generate dependency info for old config
self.populate_dependency_info(downops, ifacedownlist)
- self._sched_ifaces(ifacedownlist, downops)
+ try:
+ self._sched_ifaces(ifacedownlist, downops)
+ except Exception, e:
+ self.logger.error(str(e))
+ pass
+ finally:
+ self._process_delay_admin_state_queue('down')
else:
self.logger.debug('no interfaces to down ..')
if self._iface_whitelisted(auto, allow_classes,
excludepats, i)]
- self.logger.info('Scheduling up on interfaces: %s'
- %str(filtered_ifacenames))
- self._sched_ifaces(filtered_ifacenames, upops)
+ self.logger.info('reload: scheduling up on interfaces: %s'
+ %str(filtered_ifacenames))
+ try:
+ self._sched_ifaces(filtered_ifacenames, upops)
+ except Exception, e:
+ self.logger.error(str(e))
+ pass
+ finally:
+ self._process_delay_admin_state_queue('up')
if self.DRYRUN:
return
self._save_state()
+ def reload(self, *args, **kargs):
+ """ reload interface config """
+ self.logger.debug('reloading interface config ..')
+ if kargs.get('currentlyup', False):
+ self._reload_currentlyup(*args, **kargs)
+ else:
+ self._reload_default(*args, **kargs)
+
def _pretty_print_ordered_dict(self, prefix, argdict):
outbuf = prefix + ' {\n'
for k, vlist in argdict.items():
print json.dumps(ifaceobjs, cls=ifaceJsonEncoder,
indent=4, separators=(',', ': '))
else:
- map(lambda i: i.dump_pretty(), ifaceobjs)
+ expand = int(self.config.get('ifquery_ifacename_expand_range', '0'))
+ for i in ifaceobjs:
+ if not expand and (i.flags & iface.IFACERANGE_ENTRY):
+ # print only the first one
+ if i.flags & iface.IFACERANGE_START:
+ i.dump_pretty(use_realname=True)
+ else:
+ i.dump_pretty()
def _get_ifaceobjscurr_pretty(self, ifacenames, ifaceobjs):
ret = 0
ifaceobjs = []
ret = self._get_ifaceobjscurr_pretty(ifacenames, ifaceobjs)
if not ifaceobjs: return
- self.logger.debug(ifaceobjs)
if format == 'json':
print json.dumps(ifaceobjs, cls=ifaceJsonEncoder, indent=2,
separators=(',', ': '))
else:
map(lambda i: i.dump_pretty(with_status=True,
- successstr=self.config.get('check_success_str',
- _success_sym),
- errorstr=self.config.get('check_error_str', _error_sym)),
- ifaceobjs)
+ successstr=self.config.get('ifquery_check_success_str',
+ _success_sym),
+ errorstr=self.config.get('ifquery_check_error_str', _error_sym),
+ unknownstr=self.config.get('ifquery_check_unknown_str', '')),
+ ifaceobjs)
return ret
def print_ifaceobjsrunning_pretty(self, ifacenames, format='native'):
--- /dev/null
+#!/usr/bin/env python
+#
+# Copyright 2014 Cumulus Networks, Inc. All rights reserved.
+# Author: Scott Feldman, sfeldma@cumulusnetworks.com
+#
+
+from os import strerror
+import select
+from time import time
+import socket
+from ctypes import *
+from errno import *
+import logging
+
+logger = logging.getLogger(__name__)
+
+#
+# from /usr/include/linux/netlink.h
+#
+
+NETLINK_ROUTE = 0 # Routing/device hook
+NETLINK_UNUSED = 1 # Unused number
+NETLINK_USERSOCK = 2 # Reserved for user mode socket protocols
+NETLINK_FIREWALL = 3 # Firewalling hook
+NETLINK_INET_DIAG = 4 # INET socket monitoring
+NETLINK_NFLOG = 5 # netfilter/iptables ULOG
+NETLINK_XFRM = 6 # ipsec
+NETLINK_SELINUX = 7 # SELinux event notifications
+NETLINK_ISCSI = 8 # Open-iSCSI
+NETLINK_AUDIT = 9 # auditing
+NETLINK_FIB_LOOKUP = 10
+NETLINK_CONNECTOR = 11
+NETLINK_NETFILTER = 12 # netfilter subsystem
+NETLINK_IP6_FW = 13
+NETLINK_DNRTMSG = 14 # DECnet routing messages
+NETLINK_KOBJECT_UEVENT = 15 # Kernel messages to userspace
+NETLINK_GENERIC = 16
+NETLINK_SCSITRANSPORT = 18 # SCSI Transports
+NETLINK_ECRYPTFS = 19
+NETLINK_RDMA = 20
+NETLINK_CRYPTO = 21 # Crypto layer
+
+NLMSG_NOOP = 1 # Nothing.
+NLMSG_ERROR = 2 # Error
+NLMSG_DONE = 3 # End of a dump
+NLMSG_OVERRUN = 4 # Data lost
+
+NETLINK_NO_ENOBUFS = 5
+
+SOL_NETLINK = 270
+
+class Nlmsghdr(Structure):
+
+ _fields_ = [
+ ('nlmsg_len', c_uint32),
+ ('nlmsg_type', c_uint16),
+ ('nlmsg_flags', c_uint16),
+ ('nlmsg_seq', c_uint32),
+ ('nlmsg_pid', c_uint32)
+ ]
+
+ def dump(self):
+ print 'nlmsg_len', self.nlmsg_len
+ print 'nlmsg_type', self.nlmsg_type
+ print 'nlmsg_flags 0x%04x' % self.nlmsg_flags
+ print 'nlmsg_seq', self.nlmsg_seq
+ print 'nlmsg_pid', self.nlmsg_pid
+
+# Flags values
+
+NLM_F_REQUEST = 1 # It is request message.
+NLM_F_MULTI = 2 # Multipart message, terminated by NLMSG_DONE
+NLM_F_ACK = 4 # Reply with ack, with zero or error code
+NLM_F_ECHO = 8 # Echo this request
+NLM_F_DUMP_INTR = 16 # Dump was inconsistent due to sequence change
+
+# Modifiers to GET request
+NLM_F_ROOT = 0x100 # specify tree root
+NLM_F_MATCH = 0x200 # return all matching
+NLM_F_ATOMIC = 0x400 # atomic GET
+NLM_F_DUMP = (NLM_F_ROOT|NLM_F_MATCH)
+
+# Modifiers to NEW request
+NLM_F_REPLACE = 0x100 # Override existing
+NLM_F_EXCL = 0x200 # Do not touch, if it exists
+NLM_F_CREATE = 0x400 # Create, if it does not exist
+NLM_F_APPEND = 0x800 # Add to end of list
+
+NLMSG_ALIGNTO = 4
+def NLMSG_ALIGN(len):
+ return (len + NLMSG_ALIGNTO - 1) & ~(NLMSG_ALIGNTO - 1)
+def NLMSG_HDRLEN():
+ return NLMSG_ALIGN(sizeof(Nlmsghdr))
+def NLMSG_LENGTH(len):
+ return len + NLMSG_ALIGN(NLMSG_HDRLEN())
+def NLMSG_SPACE(len):
+ return NLMSG_ALIGN(NLMSG_LENGTH(len))
+def NLMSG_DATA(nlh):
+ return addressof(nlh) + NLMSG_LENGTH(0)
+def NLMSG_NEXT(nlh, len):
+ cur = NLMSG_ALIGN(nlh.nlmsg_len)
+ nlh = Nlmsghdr.from_address(addressof(nlh) + cur)
+ return len - cur, nlh
+def NLMSG_OK(nlh, len):
+ return len >= sizeof(Nlmsghdr) and \
+ nlh.nlmsg_len >= sizeof(Nlmsghdr) and \
+ nlh.nlmsg_len <= len
+
+class Nlmsgerr(Structure):
+
+ _fields_ = [
+ ('error', c_int),
+ ('msg', Nlmsghdr),
+ ]
+
+class NetlinkError(Exception):
+
+ def __init__(self, message):
+ Exception.__init__(self, message)
+ #print(message)
+
+class Netlink(socket.socket):
+
+ def __init__(self, pid, proto):
+
+ self.pid = pid
+ self.recvbuf = bytearray(8 * 1024)
+ self.sendbuf = bytearray(8 * 1024)
+ self.seq = int(time())
+
+ try:
+
+ socket.socket.__init__(self, socket.AF_NETLINK, \
+ socket.SOCK_RAW, proto)
+ self.setblocking(0)
+
+ # Need to turn off ENOBUFS for netlink socket otherwise
+ # in a kernel overrun situation, the socket will return
+ # ENOBUFS on socket recv and be stuck for future recvs.
+
+ self.setsockopt(SOL_NETLINK, NETLINK_NO_ENOBUFS, 1)
+
+ except socket.error as (errno, string):
+ raise NetlinkError("open: socket err[%d]: %s" % \
+ (errno, string))
+
+ def bind(self, groups, cb):
+
+ self._nl_cb = cb
+
+ try:
+ socket.socket.bind(self, (self.pid, groups))
+
+ except socket.error as (errno, string):
+ raise NetlinkError("bind: socket err[%d]: %s" % \
+ (errno, string))
+
+ def sendall(self, string):
+ try:
+ socket.socket.sendall(self, string)
+ except socket.error as (errno, string):
+ raise NetlinkError("send: socket err[%d]: %s" % \
+ (errno, string))
+
+ def _process_nlh(self, recv, nlh):
+ while NLMSG_OK(nlh, recv):
+ yield recv, nlh
+ recv, nlh = NLMSG_NEXT(nlh, recv)
+
+ def process(self, tokens=[]):
+
+ found_done = False
+
+ try:
+ recv, src_addr = self.recvfrom_into(self.recvbuf)
+ if not recv:
+ # EOF
+ print "EOF"
+ return False
+
+ except socket.error as (errno, string):
+ if errno in [EINTR, EAGAIN]:
+ return False
+ raise NetlinkError("netlink: socket err[%d]: %s" % \
+ (errno, string))
+
+ nlh = Nlmsghdr.from_buffer(self.recvbuf)
+ for recv, nlh in self._process_nlh(recv, nlh):
+
+# print "type %u, seq %u, pid %u" % \
+# (nlh.nlmsg_type, nlh.nlmsg_seq, nlh.nlmsg_pid)
+
+ l = nlh.nlmsg_len - sizeof(Nlmsghdr)
+
+ if l < 0 or nlh.nlmsg_len > recv:
+ raise NetlinkError("netlink: malformed msg: len %d" % \
+ nlh.nlmsg_len)
+
+ if tokens:
+ current = (nlh.nlmsg_pid, nlh.nlmsg_seq)
+ if current not in tokens:
+ continue
+
+ if nlh.nlmsg_type == NLMSG_DONE:
+ found_done = True
+ break
+
+ if nlh.nlmsg_type == NLMSG_ERROR:
+ err = Nlmsgerr.from_address(NLMSG_DATA(nlh))
+ if err.error == 0:
+ return False
+ raise NetlinkError("netlink: %s" % strerror(abs(err.error)))
+
+ if self._nl_cb:
+ self._nl_cb(nlh)
+
+ if found_done:
+ return False
+
+ remnant = recv - NLMSG_ALIGN(nlh.nlmsg_len) > 0
+ if remnant:
+ raise NetlinkError("netlink: remnant of size %d" % \
+ remnant)
+
+ return True
+
+ def process_wait(self, tokens):
+ while self.process(tokens):
+ pass
+
+ def process_forever(self):
+ epoll = select.epoll()
+ epoll.register(self.fileno(), select.EPOLLIN)
+ while True:
+ events = epoll.poll()
+ for fileno, event in events:
+ if fileno == self.fileno():
+ self.process()
+
+ def process_event(self, event):
+ return self.process()
import glob
import re
import os
+import copy
+from utils import utils
from iface import *
from template import templateEngine
hotplugs = {}
auto_ifaces = []
callbacks = {}
+ auto_all = False
_addrfams = {'inet' : ['static', 'manual', 'loopback', 'dhcp', 'dhcp6'],
'inet6' : ['static', 'manual', 'loopback', 'dhcp', 'dhcp6']}
else:
self.logger.error('%s: line%d: %s' %(filename, lineno, msg))
+ def _parse_warn(self, filename, lineno, msg):
+ if lineno == -1 or self._currentfile_has_template:
+ self.logger.warn('%s: %s' %(filename, msg))
+ else:
+ self.logger.warn('%s: line%d: %s' %(filename, lineno, msg))
+
def _validate_addr_family(self, ifaceobj, lineno=-1):
if ifaceobj.addr_family:
if not self._addrfams.get(ifaceobj.addr_family):
self.logger.debug('processing sourced line ..\'%s\'' %lines[cur_idx])
sourced_file = re.split(self._ws_split_regex, lines[cur_idx], 2)[1]
if sourced_file:
- for f in glob.glob(sourced_file):
+ filenames = glob.glob(sourced_file)
+ if not filenames:
+ self._parse_warn(self._currentfile, lineno,
+ 'cannot find source file %s' %sourced_file)
+ return 0
+ for f in filenames:
self.read_file(f)
else:
self._parse_error(self._currentfile, lineno,
self._parse_error(self._currentfile, lineno,
'invalid auto line \'%s\''%lines[cur_idx])
return 0
- [self.auto_ifaces.append(a) for a in auto_ifaces]
+ for a in auto_ifaces:
+ if a == 'all':
+ self.auto_all = True
+ break
+ r = utils.parse_iface_range(a)
+ if r:
+ for i in range(r[1], r[2]):
+ self.auto_ifaces.append('%s-%d' %(r[0], i))
+ self.auto_ifaces.append(a)
return 0
def _add_to_iface_config(self, ifacename, iface_config, attrname,
attrval, lineno):
newattrname = attrname.replace("_", "-")
try:
- if not self.callbacks.get('validateifaceattr')(newattrname, attrval):
+ if not self.callbacks.get('validateifaceattr')(newattrname,
+ attrval):
self._parse_error(self._currentfile, lineno,
'iface %s: unsupported keyword (%s)'
%(ifacename, attrname))
else:
iface_config[newattrname].append(attrval)
- def process_iface(self, lines, cur_idx, lineno):
+ def parse_iface(self, lines, cur_idx, lineno, ifaceobj):
lines_consumed = 0
line_idx = cur_idx
- ifaceobj = iface()
iface_line = lines[cur_idx].strip(whitespaces)
iface_attrs = re.split(self._ws_split_regex, iface_line)
ifacename = iface_attrs[1]
+ # in cases where mako is unable to render the template
+ # or incorrectly renders it due to user template
+ # errors, we maybe left with interface names with
+ # mako variables in them. There is no easy way to
+ # recognize and warn about these. In the below check
+ # we try to warn the user of such cases by looking for
+ # variable patterns ('$') in interface names.
+ if '$' in ifacename:
+ self._parse_warn(self._currentfile, lineno,
+ '%s: unexpected characters in interface name' %ifacename)
+
ifaceobj.raw_config.append(iface_line)
-
iface_config = collections.OrderedDict()
for line_idx in range(cur_idx + 1, len(lines)):
l = lines[line_idx].strip(whitespaces)
pass
self._validate_addr_family(ifaceobj, lineno)
- if ifaceobj.name in self.auto_ifaces:
+ if self.auto_all or (ifaceobj.name in self.auto_ifaces):
ifaceobj.auto = True
classes = self.get_allow_classes_for_iface(ifaceobj.name)
if classes:
[ifaceobj.set_class(c) for c in classes]
+
+ return lines_consumed # Return next index
+
+ def process_iface(self, lines, cur_idx, lineno):
+ ifaceobj = iface()
+ lines_consumed = self.parse_iface(lines, cur_idx, lineno, ifaceobj)
+
+ range_val = utils.parse_iface_range(ifaceobj.name)
+ if range_val:
+ for v in range(range_val[1], range_val[2]):
+ ifaceobj_new = copy.deepcopy(ifaceobj)
+ ifaceobj_new.realname = '%s' %ifaceobj.name
+ ifaceobj_new.name = '%s%d' %(range_val[0], v)
+ ifaceobj_new.flags = iface.IFACERANGE_ENTRY
+ if v == range_val[1]:
+ ifaceobj_new.flags |= iface.IFACERANGE_START
+ self.callbacks.get('iface_found')(ifaceobj_new)
+ else:
+ self.callbacks.get('iface_found')(ifaceobj)
- # Call iface found callback
- self.callbacks.get('iface_found')(ifaceobj)
return lines_consumed # Return next index
+ def process_vlan(self, lines, cur_idx, lineno):
+ ifaceobj = iface()
+ lines_consumed = self.parse_iface(lines, cur_idx, lineno, ifaceobj)
+
+ range_val = utils.parse_iface_range(ifaceobj.name)
+ if range_val:
+ for v in range(range_val[1], range_val[2]):
+ ifaceobj_new = copy.deepcopy(ifaceobj)
+ ifaceobj_new.realname = '%s' %ifaceobj.name
+ ifaceobj_new.name = '%s%d' %(range_val[0], v)
+ ifaceobj_new.type = ifaceType.BRIDGE_VLAN
+ ifaceobj_new.flags = iface.IFACERANGE_ENTRY
+ if v == range_val[1]:
+ ifaceobj_new.flags |= iface.IFACERANGE_START
+ self.callbacks.get('iface_found')(ifaceobj_new)
+ else:
+ ifaceobj.type = ifaceType.BRIDGE_VLAN
+ self.callbacks.get('iface_found')(ifaceobj)
+
+ return lines_consumed # Return next index
network_elems = { 'source' : process_source,
'allow' : process_allow,
'auto' : process_auto,
- 'iface' : process_iface}
+ 'iface' : process_iface,
+ 'vlan' : process_vlan}
def _is_keyword(self, str):
# The additional split here is for allow- keyword
return classes
def process_interfaces(self, filedata):
+
+ # process line continuations
+ filedata = ' '.join(d.strip() for d in filedata.split('\\'))
+
line_idx = 0
lines_consumed = 0
raw_config = filedata.split('\n')
def read_filedata(self, filedata):
self._currentfile_has_template = False
- # process line continuations
- filedata = ' '.join(d.strip() for d in filedata.split('\\'))
# run through template engine
try:
rendered_filedata = self._template_engine.render(filedata)
if rendered_filedata is filedata:
- self._currentfile_has_template = True
- else:
self._currentfile_has_template = False
+ else:
+ self._currentfile_has_template = True
except Exception, e:
self._parse_error(self._currentfile, -1,
'failed to render template (%s). ' %str(e) +
fp = open(filename)
ifacedicts = json.load(fp)
#object_hook=ifaceJsonDecoder.json_object_hook)
+
+ # we need to handle both lists and non lists formats (e.g. {{}})
+ if not isinstance(ifacedicts,list):
+ ifacedicts = [ifacedicts]
+
for ifacedict in ifacedicts:
ifaceobj = ifaceJsonDecoder.json_to_ifaceobj(ifacedict)
if ifaceobj:
--- /dev/null
+#!/usr/bin/env python
+#
+# Copyright 2014 Cumulus Networks, Inc. All rights reserved.
+#
+# Author: Scott Feldman, sfeldma@cumulusnetworks.com
+# Author: Roopa Prabhu, roopa@cumulusnetworks.com
+#
+#
+from socket import NETLINK_ROUTE, AF_INET, AF_INET6
+from string import printable
+from ipaddr import *
+from ctypes import *
+from netlink import *
+import logging
+
+logger = logging.getLogger(__name__)
+
+#
+# from /usr/include/linux/rtnetlink.h
+#
+
+RTMGRP_LINK = 0x1
+RTMGRP_IPV4_IFADDR = 0x10
+RTMGRP_IPV4_ROUTE = 0x40
+RTMGRP_IPV6_IFADDR = 0x100
+RTMGRP_IPV6_ROUTE = 0x400
+
+RTM_NEWLINK = 16
+RTM_DELLINK = 17
+RTM_GETLINK = 18
+RTM_SETLINK = 19
+RTM_NEWADDR = 20
+RTM_DELADDR = 21
+RTM_GETADDR = 22
+RTM_NEWROUTE = 24
+RTM_DELROUTE = 25
+RTM_GETROUTE = 26
+
+# Definitions used in routing table administration.
+
+class Nlmsg(Structure):
+
+ def _stringify(self):
+ return string_at(addressof(self), sizeof(self))
+
+ def __eq__(self, other):
+ return self._stringify() == other._stringify() and \
+ self.__dict__ == other.__dict__
+
+ def to_rta(self):
+ return Rtattr.from_address(addressof(self) + NLMSG_ALIGN(sizeof(self)))
+
+ def pack_extra(self, extra, addr):
+ memmove(addr, addressof(extra), sizeof(extra))
+ return NLMSG_ALIGN(sizeof(extra))
+
+ def pack_rtas(self, rtas, addr):
+ total_len = 0
+ for rta_type, value in rtas.iteritems():
+ rta = Rtattr.from_address(addr)
+ rta.rta_type = rta_type
+ pack_fn = self.rta_fn(rta_type)
+ rta_len = NLMSG_ALIGN(pack_fn(rta, value))
+ total_len += rta_len
+ addr += rta_len
+ return total_len
+
+ def pack_rtas_new(self, rtas, addr, policy):
+ total_len = 0
+
+ for rta_type, value in rtas.iteritems():
+ if type(value) == dict:
+ rta = Rtattr.from_address(addr)
+ rta.rta_type = rta_type
+ rta.rta_len = RTA_LENGTH(0)
+ rta_len = NLMSG_ALIGN(rta.rta_len)
+ total_len += rta_len
+ addr += rta_len
+ pack_fn = policy.get(rta_type)
+ rta_len = NLMSG_ALIGN(pack_fn(addr, value))
+
+ rta.rta_len += rta_len
+ else:
+ rta = Rtattr.from_address(addr)
+ rta.rta_type = rta_type
+ pack_fn = policy.get(rta_type)
+ rta_len = NLMSG_ALIGN(pack_fn(rta, value))
+ total_len += rta_len
+ addr += rta_len
+ return total_len
+
+ def rta_linkinfo(self, addr, rtas):
+ total_len = 0
+
+ # Check interface kind
+ kind = rtas.get(IFLA_INFO_KIND)
+ if kind == 'vlan':
+ data_policy = self.rta_linkinfo_data_vlan_policy()
+ else:
+ data_policy = self.rta_linkinfo_data_macvlan_policy()
+
+ # Pack info kind
+ rta = Rtattr.from_address(addr)
+ rta.rta_type = IFLA_INFO_KIND
+ rta_len = NLMSG_ALIGN(self.rta_string(rta, kind))
+ total_len += rta_len
+ addr += rta_len
+
+ # nest start link info data
+ rta = Rtattr.from_address(addr)
+ rta.rta_type = IFLA_INFO_DATA
+ rta.rta_len = RTA_LENGTH(0)
+ rta_len = NLMSG_ALIGN(rta.rta_len)
+ total_len += rta_len
+ addr += rta_len
+ rta_len = self.pack_rtas_new(rtas.get(IFLA_INFO_DATA), addr,
+ data_policy)
+ rta.rta_len += rta_len
+
+ total_len += rta_len
+ addr += rta_len
+
+ return total_len
+
+ def rta_bridge_vlan_info(self, rta, value):
+ if value:
+ data = RTA_DATA(rta)
+ memmove(data, addressof(value), sizeof(value))
+ rta.rta_len = RTA_LENGTH(sizeof(value))
+ return rta.rta_len
+
+ def rta_af_spec(self, addr, rtas):
+ total_len = 0
+
+ # XXX: Check family (Assumes bridge family for now)
+ rta_len = self.pack_rtas_new(rtas, addr,
+ self.rta_bridge_af_spec_policy())
+ total_len += rta_len
+ return total_len
+
+ def unpack_rtas(self, which_ones=[]):
+ len = self.nlh.nlmsg_len - NLMSG_LENGTH(sizeof(self))
+ rta = self.to_rta()
+ rtas = {}
+ while RTA_OK(rta, len):
+ rta_type = rta.rta_type
+ if not which_ones or rta_type in which_ones:
+ unpack_fn = self.rta_fn(rta_type)
+ rtas[rta_type] = unpack_fn(rta)
+ len, rta = RTA_NEXT(rta, len)
+ return rtas
+
+ def dump_rtas(self):
+ rtas = self.unpack_rtas()
+ for type, value in rtas.iteritems():
+ print "rta", type, ":", value
+
+ class _IPv6Addr(BigEndianStructure):
+ _fields_ = [
+ ('upper', c_uint64),
+ ('lower', c_uint64),
+ ]
+
+ class _IPv4Addr(BigEndianStructure):
+ _fields_ = [
+ ('addr', c_uint32),
+ ]
+
+ def rta_uint8(self, rta, value=None):
+ data = RTA_DATA(rta)
+ if value:
+ c_uint8.from_address(data).value = value
+ rta.rta_len = RTA_LENGTH(sizeof(c_uint8))
+ return rta.rta_len
+ else:
+ return c_uint8.from_address(data).value
+
+ def rta_uint16(self, rta, value=None):
+ data = RTA_DATA(rta)
+ if value:
+ c_uint16.from_address(data).value = value
+ rta.rta_len = RTA_LENGTH(sizeof(c_uint16))
+ return rta.rta_len
+ else:
+ return c_uint16.from_address(data).value
+
+ def rta_uint32(self, rta, value=None):
+ data = RTA_DATA(rta)
+ if value:
+ c_uint32.from_address(data).value = value
+ rta.rta_len = RTA_LENGTH(sizeof(c_uint32))
+ return rta.rta_len
+ else:
+ return c_uint32.from_address(data).value
+
+ def rta_string(self, rta, value=None):
+ data = RTA_DATA(rta)
+ if value:
+ s = create_string_buffer(value)
+ memmove(data, addressof(s), len(value))
+ rta.rta_len = RTA_LENGTH(len(value))
+ return rta.rta_len
+ else:
+ return c_char_p(data).value
+
+ def rta_addr(self, rta, value=None):
+ data = RTA_DATA(rta)
+ if value:
+ if isinstance(value, IPv4Address):
+ self._IPv4Addr.from_address(data).addr = value._ip
+ rta.rta_len = RTA_LENGTH(sizeof(self._IPv4Addr))
+ elif isinstance(value, IPv6Address):
+ addr = self._IPv6Addr.from_address(data)
+ addr.upper = value._ip >> 64
+ addr.lower = value._ip & 0xffffffffffffffff
+ rta.rta_len = RTA_LENGTH(sizeof(self._IPv6Addr))
+ else:
+ assert(False)
+ return rta.rta_len
+ else:
+ if RTA_PAYLOAD(rta) == 4:
+ addr = c_uint32.__ctype_be__.from_address(data).value
+ addr = IPv4Address(addr)
+ else:
+ addr = self._IPv6Addr.from_address(data)
+ addr = IPv6Address((addr.upper << 64) + addr.lower)
+ return addr
+
+ def rta_uint8_array(self, rta, value=None):
+ data = RTA_DATA(rta)
+ if value:
+ s = (c_uint8 * len(value)).from_buffer_copy(value)
+ memmove(data, addressof(s), len(value))
+ rta.rta_len = RTA_LENGTH(len(value))
+ return rta.rta_len
+ else:
+ array = (c_uint8 * RTA_PAYLOAD(rta))()
+ memmove(array, data, RTA_PAYLOAD(rta))
+ return array
+
+ def rta_uint32_array(self, rta, value=None):
+ if value:
+ assert(False)
+ else:
+ data = RTA_DATA(rta)
+ size = RTA_PAYLOAD(rta) / sizeof(c_uint32)
+ array = (c_uint32 * size)()
+ memmove(array, data, RTA_PAYLOAD(rta))
+ return array
+
+ def rta_multipath(self, rta, value=None):
+ # XXX implement this
+ return None
+
+ def rta_wtf(self, rta, value=None):
+ return None
+
+ def rta_none(self, rta, value=None):
+ return None
+
+ def rta_fn(self, rta_type):
+ return None
+
+
+# rtm_type
+
+RTN_UNSPEC = 0
+RTN_UNICAST = 1 # Gateway or direct route
+RTN_LOCAL = 2 # Accept locally
+RTN_BROADCAST = 3 # Accept locally as broadcast,
+ # send as broadcast
+RTN_ANYCAST = 4 # Accept locally as broadcast,
+ # but send as unicast
+RTN_MULTICAST = 5 # Multicast route
+RTN_BLACKHOLE = 6 # Drop
+RTN_UNREACHABLE = 7 # Destination is unreachable
+RTN_PROHIBIT = 8 # Administratively prohibited
+RTN_THROW = 9 # Not in this table
+RTN_NAT = 10 # Translate this address
+RTN_XRESOLVE = 11 # Use external resolver
+RTN_MAX = 11
+
+# rtm_protocol
+
+RTPROT_UNSPEC = 0
+RTPROT_REDIRECT = 1 # Route installed by ICMP redirects;
+ # not used by current IPv4
+RTPROT_KERNEL = 2 # Route installed by kernel
+RTPROT_BOOT = 3 # Route installed during boot
+RTPROT_STATIC = 4 # Route installed by administrator
+
+# Values of protocol >= RTPROT_STATIC are not interpreted by kernel;
+# they are just passed from user and back as is.
+# It will be used by hypothetical multiple routing daemons.
+# Note that protocol values should be standardized in order to
+# avoid conflicts.
+
+RTPROT_GATED = 8 # Apparently, GateD
+RTPROT_RA = 9 # RDISC/ND router advertisements
+RTPROT_MRT = 10 # Merit MRT
+RTPROT_ZEBRA = 11 # Zebra
+RTPROT_BIRD = 12 # BIRD
+RTPROT_DNROUTED = 13 # DECnet routing daemon
+RTPROT_XORP = 14 # XORP
+RTPROT_NTK = 15 # Netsukuku
+RTPROT_DHCP = 16 # DHCP client
+
+# rtm_scope
+
+# Really it is not scope, but sort of distance to the destination.
+# NOWHERE are reserved for not existing destinations, HOST is our
+# local addresses, LINK are destinations, located on directly attached
+# link and UNIVERSE is everywhere in the Universe.
+
+# Intermediate values are also possible f.e. interior routes
+# could be assigned a value between UNIVERSE and LINK.
+
+RT_SCOPE_UNIVERSE = 0
+# User defined values
+RT_SCOPE_SITE = 200
+RT_SCOPE_LINK = 253
+RT_SCOPE_HOST = 254
+RT_SCOPE_NOWHERE=255
+
+# rtm_flags
+
+RTM_F_NOTIFY = 0x100 # Notify user of route change
+RTM_F_CLONED = 0x200 # This route is cloned
+RTM_F_EQUALIZE = 0x400 # Multipath equalizer: NI
+RTM_F_PREFIX = 0x800 # Prefix addresses
+
+# Reserved table identifiers
+
+RT_TABLE_UNSPEC = 0
+# User defined values
+RT_TABLE_COMPAT = 252
+RT_TABLE_DEFAULT = 253
+RT_TABLE_MAIN = 254
+RT_TABLE_LOCAL = 255
+RT_TABLE_MAX = 0xFFFFFFFF
+
+# Generic structure for encapsulation of optional route information.
+# It is reminiscent of sockaddr, but with sa_family replaced
+# with attribute type.
+
+class Rtattr(Structure):
+
+ _fields_ = [
+ ('rta_len', c_uint16),
+ ('rta_type', c_uint16),
+ ]
+
+# Routing message attributes
+
+RTA_UNSPEC = 0
+RTA_DST = 1
+RTA_SRC = 2
+RTA_IIF = 3
+RTA_OIF = 4
+RTA_GATEWAY = 5
+RTA_PRIORITY = 6
+RTA_PREFSRC = 7
+RTA_METRICS = 8
+RTA_MULTIPATH = 9
+RTA_PROTOINFO = 10 # no longer used
+RTA_FLOW = 11
+RTA_CACHEINFO = 12
+RTA_SESSION = 13 # no longer used
+RTA_MP_ALGO = 14 # no longer used
+RTA_TABLE = 15
+RTA_MAX = 15
+
+# Macros to handle rtattributes
+
+RTA_ALIGNTO = 4
+def RTA_ALIGN(len):
+ return (len + RTA_ALIGNTO - 1) & ~(RTA_ALIGNTO - 1)
+def RTA_OK(rta, len):
+ return len >= sizeof(Rtattr) and \
+ rta.rta_len >= sizeof(Rtattr) and \
+ rta.rta_len <= len
+def RTA_NEXT(rta, len):
+ cur = RTA_ALIGN(rta.rta_len)
+ rta = Rtattr.from_address(addressof(rta) + cur)
+ return len - cur, rta
+def RTA_LENGTH(len):
+ return len + RTA_ALIGN(sizeof(Rtattr))
+def RTA_SPACE(len):
+ return RTA_ALIGN(RTA_LENGTH(len))
+def RTA_DATA(rta):
+ return addressof(rta) + RTA_LENGTH(0)
+def RTA_PAYLOAD(rta):
+ return rta.rta_len - RTA_LENGTH(0)
+
+RTNH_F_DEAD = 1 # Nexthop is dead (used by multipath)
+RTNH_F_PERVASIVE = 2 # Do recursive gateway lookup
+RTNH_F_ONLINK = 4 # Gateway is forced on link
+
+# Reserved table identifiers
+
+RT_TABLE_UNSPEC = 0
+# User defined values
+RT_TABLE_COMPAT = 252
+RT_TABLE_DEFAULT = 253
+RT_TABLE_MAIN = 254
+RT_TABLE_LOCAL = 255
+RT_TABLE_MAX = 0xFFFFFFFF
+
+class Rtmsg(Nlmsg):
+
+ _fields_ = [
+ ('rtm_family', c_uint8),
+ ('rtm_dst_len', c_uint8),
+ ('rtm_src_len', c_uint8),
+ ('rtm_tos', c_uint8),
+ ('rtm_table', c_uint8),
+ ('rtm_protocol', c_uint8),
+ ('rtm_scope', c_uint8),
+ ('rtm_type', c_uint8),
+ ('rtm_flags', c_uint32),
+ ]
+
+ _table_str = {
+ RT_TABLE_UNSPEC: "unspecified",
+ RT_TABLE_COMPAT: "compat",
+ RT_TABLE_DEFAULT: "default",
+ RT_TABLE_MAIN: "main",
+ RT_TABLE_LOCAL: "local",
+ }
+
+ _proto_str = {
+ RTPROT_UNSPEC: "none",
+ RTPROT_REDIRECT: "redirect",
+ RTPROT_KERNEL: "kernel",
+ RTPROT_BOOT: "boot",
+ RTPROT_STATIC: "static",
+ RTPROT_GATED: "gated",
+ RTPROT_RA: "ra",
+ RTPROT_MRT: "mrtmrt",
+ RTPROT_ZEBRA: "zebra",
+ RTPROT_BIRD: "bird",
+ RTPROT_DNROUTED: "dnrouted",
+ RTPROT_XORP: "xorp",
+ RTPROT_NTK: "ntk",
+ RTPROT_DHCP: "dhcp",
+ }
+
+ _scope_str = {
+ RT_SCOPE_UNIVERSE: "universe",
+ RT_SCOPE_SITE: "site",
+ RT_SCOPE_LINK: "link",
+ RT_SCOPE_HOST: "host",
+ RT_SCOPE_NOWHERE: "nowhere",
+ }
+
+ _type_str = {
+ RTN_UNSPEC: "unspecified",
+ RTN_UNICAST: "unicast",
+ RTN_LOCAL: "local",
+ RTN_BROADCAST: "broadcast",
+ RTN_ANYCAST: "anycast",
+ RTN_MULTICAST: "multicast",
+ RTN_BLACKHOLE: "blackhole",
+ RTN_UNREACHABLE: "unreachable",
+ RTN_PROHIBIT: "prohibit",
+ RTN_THROW: "throw",
+ RTN_NAT: "nat",
+ RTN_XRESOLVE: "xresolve",
+ }
+
+ def dump(self):
+ print 'rtm_family', self.rtm_family
+ print 'rtm_dst_len', self.rtm_dst_len
+ print 'rtm_src_len', self.rtm_src_len
+ print 'rtm_tos', self.rtm_tos
+ print 'rtm_table', self._table_str.get(self.rtm_table, self.rtm_table)
+ print 'rtm_protocol', self._proto_str.get(self.rtm_protocol)
+ print 'rtm_scope', self._scope_str.get(self.rtm_scope)
+ print 'rtm_type', self._type_str.get(self.rtm_type)
+ print 'rtm_flags 0x%08x' % self.rtm_flags
+
+ def rta_fn(self, rta_type):
+ fns = {
+ RTA_DST: self.rta_addr,
+ RTA_SRC: self.rta_addr,
+ RTA_IIF: self.rta_uint32,
+ RTA_OIF: self.rta_uint32,
+ RTA_GATEWAY: self.rta_addr,
+ RTA_PRIORITY: self.rta_uint32,
+ RTA_PREFSRC: self.rta_addr,
+ RTA_METRICS: self.rta_uint32_array,
+ RTA_MULTIPATH: self.rta_multipath,
+ RTA_PROTOINFO: self.rta_none,
+ RTA_FLOW: self.rta_uint32,
+ RTA_CACHEINFO: self.rta_none,
+ RTA_SESSION: self.rta_none,
+ RTA_MP_ALGO: self.rta_none,
+ RTA_TABLE: self.rta_uint32,
+ }
+
+ return fns.get(rta_type)
+
+class Rtgenmsg(Nlmsg):
+
+ _fields_ = [
+ ('rtgen_family', c_uint8),
+ ]
+
+ def dump(self):
+ print 'rtgen_family', self.rtgen_family
+
+# New extended info filters for IFLA_EXT_MASK
+RTEXT_FILTER_VF = (1 << 0)
+
+# passes link level specific information, not dependent
+# on network protocol.
+
+IFLA_UNSPEC = 0
+IFLA_ADDRESS = 1
+IFLA_BROADCAST = 2
+IFLA_IFNAME = 3
+IFLA_MTU = 4
+IFLA_LINK = 5
+IFLA_QDISC = 6
+IFLA_STATS = 7
+IFLA_COST = 8
+IFLA_PRIORITY = 9
+IFLA_MASTER = 10
+IFLA_WIRELESS = 11 # Wireless Extension event - see wireless.h
+IFLA_PROTINFO = 12 # Protocol specific information for a link
+IFLA_TXQLEN = 13
+IFLA_MAP = 14
+IFLA_WEIGHT = 15
+IFLA_OPERSTATE = 16
+IFLA_LINKMODE = 17
+IFLA_LINKINFO = 18
+IFLA_NET_NS_PID = 19
+IFLA_IFALIAS = 20
+IFLA_NUM_VF = 21 # Number of VFs if device is SR-IOV PF
+IFLA_VFINFO_LIST = 22
+IFLA_STATS64 = 23
+IFLA_VF_PORTS = 24
+IFLA_PORT_SELF = 25
+IFLA_AF_SPEC = 26
+IFLA_GROUP = 27 # Group the device belongs to
+IFLA_NET_NS_FD = 28
+IFLA_EXT_MASK = 29 # Extended info mask, VFs, etc
+IFLA_MAX = 29
+
+
+# IFLA_LINKINFO attributes
+IFLA_INFO_UNSPEC = 0
+IFLA_INFO_KIND = 1
+IFLA_INFO_DATA = 2
+IFLA_INFO_XSTATS = 3
+IFLA_INFO_MAX = 4
+
+# IFLA_LINKINFO_DATA attributes for vlan
+IFLA_VLAN_UNSPEC = 0
+IFLA_VLAN_ID = 1
+
+# IFLA_LINKINFO_DATA attributes for macvlan
+IFLA_MACVLAN_UNSPEC = 0
+IFLA_MACVLAN_MODE = 1
+
+# macvlan modes
+MACVLAN_MODE_PRIVATE = 1
+MACVLAN_MODE_VEPA = 2
+MACVLAN_MODE_BRIDGE = 3
+MACVLAN_MODE_PASSTHRU = 4
+
+# BRIDGE IFLA_AF_SPEC attributes
+IFLA_BRIDGE_FLAGS = 0
+IFLA_BRIDGE_MODE = 1
+IFLA_BRIDGE_VLAN_INFO = 2
+
+# BRIDGE_VLAN_INFO flags
+BRIDGE_VLAN_INFO_MASTER = 1
+BRIDGE_VLAN_INFO_PVID = 2
+BRIDGE_VLAN_INFO_UNTAGGED = 4
+
+# Bridge flags
+BRIDGE_FLAGS_MASTER = 1
+BRIDGE_FLAGS_SELF = 2
+
+class BridgeVlanInfo(Structure):
+ _fields_ = [
+ ('flags', c_uint16),
+ ('vid', c_uint16),
+ ('vid_end', c_uint16),
+ ]
+
+class Ifinfomsg(Nlmsg):
+
+ _fields_ = [
+ ('ifi_family', c_uint8),
+ ('__ifi_pad', c_uint8),
+ ('ifi_type', c_uint16), # ARPHRD_*
+ ('ifi_index', c_int32), # Link index
+ ('ifi_flags', c_uint32), # IFF_* flags
+ ('ifi_change', c_uint32), # IFF_* change mask
+ ]
+
+ def dump(self):
+ print 'ifi_family', self.ifi_family
+ print 'ifi_type', self.ifi_type
+ print 'ifi_index', self.ifi_index
+ print 'ifi_flags 0x%08x' % self.ifi_flags
+ print 'ifi_change 0x%08x' % self.ifi_change
+
+ def rta_linkinfo_data_vlan_policy(self):
+ fns = {
+ IFLA_VLAN_ID : self.rta_uint16,
+ }
+ return fns
+
+ def rta_linkinfo_data_macvlan_policy(self):
+ fns = {
+ IFLA_MACVLAN_MODE : self.rta_uint32,
+ }
+ return fns
+
+ def rta_linkinfo_policy(self):
+ fns = {
+ IFLA_INFO_KIND : self.rta_string,
+ IFLA_INFO_DATA : self.rta_linkinfo_data,
+ }
+ return fns
+
+ def rta_bridge_af_spec_policy(self):
+ # Assume bridge family for now
+ fns = {
+ IFLA_BRIDGE_FLAGS : self.rta_uint16,
+ IFLA_BRIDGE_VLAN_INFO : self.rta_bridge_vlan_info,
+ }
+ return fns
+
+ def rta_policy(self):
+ fns = {
+ IFLA_UNSPEC: self.rta_wtf,
+ IFLA_ADDRESS: self.rta_uint8_array,
+ IFLA_BROADCAST: self.rta_uint8_array,
+ IFLA_IFNAME: self.rta_string,
+ IFLA_MTU: self.rta_uint32,
+ IFLA_LINK: self.rta_uint32,
+ IFLA_QDISC: self.rta_string,
+ IFLA_STATS: self.rta_none,
+ IFLA_COST: self.rta_none,
+ IFLA_PRIORITY: self.rta_none,
+ IFLA_MASTER: self.rta_uint32,
+ IFLA_WIRELESS: self.rta_none,
+ IFLA_PROTINFO: self.rta_none,
+ IFLA_TXQLEN: self.rta_uint32,
+ IFLA_MAP: self.rta_none,
+ IFLA_WEIGHT: self.rta_uint32,
+ IFLA_OPERSTATE: self.rta_uint8,
+ IFLA_LINKMODE: self.rta_uint8,
+ IFLA_LINKINFO: self.rta_linkinfo,
+ IFLA_NET_NS_PID: self.rta_uint32,
+ IFLA_IFALIAS: self.rta_string,
+ IFLA_NUM_VF: self.rta_uint32,
+ IFLA_VFINFO_LIST: self.rta_none,
+ IFLA_STATS64: self.rta_none,
+ IFLA_VF_PORTS: self.rta_none,
+ IFLA_PORT_SELF: self.rta_none,
+ IFLA_AF_SPEC: self.rta_af_spec,
+ IFLA_GROUP: self.rta_none,
+ IFLA_NET_NS_FD: self.rta_none,
+ IFLA_EXT_MASK: self.rta_none,
+ }
+ return fns;
+
+ def rta_fn(self, rta_type):
+ fns = {
+ IFLA_UNSPEC: self.rta_wtf,
+ IFLA_ADDRESS: self.rta_uint8_array,
+ IFLA_BROADCAST: self.rta_uint8_array,
+ IFLA_IFNAME: self.rta_string,
+ IFLA_MTU: self.rta_uint32,
+ IFLA_LINK: self.rta_uint32,
+ IFLA_QDISC: self.rta_string,
+ IFLA_STATS: self.rta_none,
+ IFLA_COST: self.rta_none,
+ IFLA_PRIORITY: self.rta_none,
+ IFLA_MASTER: self.rta_uint32,
+ IFLA_WIRELESS: self.rta_none,
+ IFLA_PROTINFO: self.rta_none,
+ IFLA_TXQLEN: self.rta_uint32,
+ IFLA_MAP: self.rta_none,
+ IFLA_WEIGHT: self.rta_uint32,
+ IFLA_OPERSTATE: self.rta_uint8,
+ IFLA_LINKMODE: self.rta_uint8,
+ IFLA_LINKINFO: self.rta_linkinfo,
+ IFLA_NET_NS_PID: self.rta_uint32,
+ IFLA_IFALIAS: self.rta_string,
+ IFLA_NUM_VF: self.rta_uint32,
+ IFLA_VFINFO_LIST: self.rta_none,
+ IFLA_STATS64: self.rta_none,
+ IFLA_VF_PORTS: self.rta_none,
+ IFLA_PORT_SELF: self.rta_none,
+ IFLA_AF_SPEC: self.rta_af_spec,
+ IFLA_GROUP: self.rta_none,
+ IFLA_NET_NS_FD: self.rta_none,
+ IFLA_EXT_MASK: self.rta_none,
+ }
+ return fns.get(rta_type)
+
+# passes address specific information
+
+# Important comment:
+# IFA_ADDRESS is prefix address, rather than local interface address.
+# It makes no difference for normally configured broadcast interfaces,
+# but for point-to-point IFA_ADDRESS is DESTINATION address,
+# local address is supplied in IFA_LOCAL attribute.
+
+IFA_UNSPEC = 0
+IFA_ADDRESS = 1
+IFA_LOCAL = 2
+IFA_LABEL = 3
+IFA_BROADCAST = 4
+IFA_ANYCAST = 5
+IFA_CACHEINFO = 6
+IFA_MULTICAST = 7
+IFA_MAX = 7
+
+class Ifaddrmsg(Nlmsg):
+
+ _fields_ = [
+ ('ifa_family', c_uint8),
+ ('ifa_prefixlen', c_uint8), # The prefix length
+ ('ifa_flags', c_uint8), # Flags
+ ('ifa_scope', c_uint8), # Address scope
+ ('ifa_index', c_uint32), # Link index
+ ]
+
+ _family_str = {
+ AF_INET: "inet",
+ AF_INET6: "inet6",
+ }
+
+ def dump(self):
+ print 'ifa_family', self.ifa_family
+ print 'ifa_prefixlen', self.ifa_prefixlen
+ print 'ifa_flags 0x%02x' % self.ifa_flags
+ print 'ifa_scope', self.ifa_scope
+ print 'ifa_index', self.ifa_index
+
+ def rta_fn(self, rta_type):
+ fns = {
+ IFA_ADDRESS: self.rta_addr,
+ IFA_LOCAL: self.rta_addr,
+ IFA_LABEL: self.rta_string,
+ IFA_BROADCAST: self.rta_addr,
+ IFA_ANYCAST: self.rta_addr,
+ IFA_CACHEINFO: self.rta_none,
+ IFA_MULTICAST: self.rta_addr,
+ }
+ return fns.get(rta_type)
+
+class RtNetlinkError(Exception):
+
+ def __init__(self, message):
+ Exception.__init__(self, message)
+ logger.error(message)
+
+class RtNetlink(Netlink):
+
+ def __init__(self, pid):
+ Netlink.__init__(self, pid, NETLINK_ROUTE)
+
+ _rt_nlmsg_type_str = {
+ RTM_NEWROUTE: "RTM_NEWROUTE",
+ RTM_DELROUTE: "RTM_DELROUTE",
+ RTM_NEWLINK: "RTM_NEWLINK",
+ RTM_SETLINK: "RTM_SETLINK",
+ RTM_DELLINK: "RTM_DELLINK",
+ RTM_GETLINK: "RTM_GETLINK",
+ RTM_NEWADDR: "RTM_NEWADDR",
+ RTM_DELADDR: "RTM_DELADDR",
+ }
+
+ def _hexdump(self, buf):
+ while buf:
+ chunk = buf[:16]
+ buf = buf[16:]
+ nums = ["%02x" % c for c in chunk]
+ txt = [chr(c) if chr(c) in printable[:-5] else '.' for c in chunk]
+ print " ".join(nums).ljust(48), "".join(txt)
+
+ def dump(self, nlh):
+ nlmsg = self.nlmsg(nlh)
+ print
+ self._hexdump(self.sendbuf[:nlh.nlmsg_len])
+ print
+ nlh.dump()
+ print
+ nlmsg.dump()
+ print
+ nlmsg.dump_rtas()
+
+ def nlmsg(self, nlh):
+ nlmsg_struct = {
+ RTM_NEWROUTE: Rtmsg,
+ RTM_DELROUTE: Rtmsg,
+ RTM_GETROUTE: Rtmsg,
+ RTM_NEWLINK: Ifinfomsg,
+ RTM_SETLINK: Ifinfomsg,
+ RTM_DELLINK: Ifinfomsg,
+ RTM_GETLINK: Rtgenmsg,
+ RTM_NEWADDR: Ifaddrmsg,
+ RTM_DELADDR: Ifaddrmsg,
+ RTM_GETADDR: Rtgenmsg,
+ }
+ nldata = NLMSG_DATA(nlh)
+ nlmsg = nlmsg_struct[nlh.nlmsg_type].from_address(nldata)
+ nlmsg.nlh = nlh
+ return nlmsg
+
+ def _nl_cb(self, nlh):
+# print "nl cb", self._rt_nlmsg_type_str[nlh.nlmsg_type]
+
+ if nlh.nlmsg_type in self._cbs:
+
+ nlmsg = self.nlmsg(nlh)
+
+ # validate nl length
+ if nlh.nlmsg_len - NLMSG_LENGTH(sizeof(nlmsg)) < 0:
+ raise RtNetlinkError("invalid nl length")
+
+ self._cbs[nlh.nlmsg_type](nlh, nlmsg)
+
+ def bind(self, groups, cbs):
+ self._cbs = cbs
+ Netlink.bind(self, groups, self._nl_cb)
+
+ def request(self, nlmsg_type, flags, extra, rtas={}):
+
+ nlh = Nlmsghdr.from_buffer(self.sendbuf)
+ nlh_p = addressof(nlh)
+
+ seq = self.seq
+ pid = self.pid
+
+ nlh.nlmsg_len = NLMSG_HDRLEN()
+ nlh.nlmsg_type = nlmsg_type
+ nlh.nlmsg_flags = flags
+ nlh.nlmsg_pid = pid
+ nlh.nlmsg_seq = seq
+
+ nlmsg = self.nlmsg(nlh)
+
+ nlh.nlmsg_len += nlmsg.pack_extra(extra, nlh_p + nlh.nlmsg_len)
+ nlh.nlmsg_len += nlmsg.pack_rtas_new(rtas, nlh_p + nlh.nlmsg_len,
+ nlmsg.rta_policy())
+ #self.dump(nlh)
+ self.sendall(string_at(nlh_p, nlh.nlmsg_len))
+ self.seq += 1
+
+ token = (pid, seq)
+ return token
--- /dev/null
+#!/usr/bin/env python
+#
+# Copyright 2014 Cumulus Networks, Inc. All rights reserved.
+#
+# Author: Roopa Prabhu, roopa@cumulusnetworks.com
+#
+#
+
+from os import getpid
+from socket import AF_UNSPEC
+from socket import AF_BRIDGE
+from iff import IFF_UP
+from rtnetlink import *
+import os
+import ifupdownmain
+
+class rtnetlinkApi(RtNetlink):
+
+ bind_done = False
+
+ def __init__(self, pid):
+ RtNetlink.__init__(self, pid)
+ self.logger = logging.getLogger('ifupdown.' +
+ self.__class__.__name__)
+ self.bind(0, None)
+ self.bind_done = True
+ self.ifindexmap = {}
+
+ def do_bind(self):
+ if self.bind_done:
+ return True
+ self.bind(0, None)
+ self.bind_done = True
+
+ def get_ifindex(self, ifname):
+ ifindex = self.ifindexmap.get(ifname)
+ if not ifindex:
+ with open('/sys/class/net/%s/ifindex' %ifname, 'r') as f:
+ ifindex = int(f.read())
+ self.ifindexmap[ifname] = ifindex
+ return ifindex
+
+ def create_vlan(self, link, ifname, vlanid):
+ self.logger.info('rtnetlink: creating vlan interface %s' %ifname)
+ if ifupdownmain.ifupdownFlags.DRYRUN:
+ return
+ try:
+ ifindex = self.get_ifindex(link)
+ except Exception, e:
+ raise Exception('cannot determine ifindex for link %s (%s)'
+ %(link, str(e)))
+
+ ifm = Ifinfomsg(AF_UNSPEC)
+ rtas = {IFLA_IFNAME: ifname,
+ IFLA_LINK : ifindex,
+ IFLA_LINKINFO : {
+ IFLA_INFO_KIND : 'vlan',
+ IFLA_INFO_DATA : {
+ IFLA_VLAN_ID : vlanid,
+ }
+ }
+ }
+ token = self.request(RTM_NEWLINK,
+ NLM_F_CREATE | NLM_F_REQUEST | NLM_F_ACK, ifm, rtas)
+ self.process_wait([token])
+
+ def create_macvlan(self, ifname, link, mode='private'):
+ self.logger.info('rtnetlink: creating macvlan interface %s' %ifname)
+ if ifupdownmain.ifupdownFlags.DRYRUN:
+ return
+ try:
+ ifindex = self.get_ifindex(link)
+ except Exception, e:
+ raise Exception('cannot determine ifindex for link %s (%s)'
+ %(link, str(e)))
+
+ ifm = Ifinfomsg(AF_UNSPEC)
+ rtas = {IFLA_IFNAME: ifname,
+ IFLA_LINK : ifindex,
+ IFLA_LINKINFO : {
+ IFLA_INFO_KIND : 'macvlan',
+ IFLA_INFO_DATA : {
+ IFLA_MACVLAN_MODE : MACVLAN_MODE_PRIVATE,
+ }
+ }
+ }
+ token = self.request(RTM_NEWLINK, NLM_F_CREATE | NLM_F_REQUEST |
+ NLM_F_ACK, ifm, rtas)
+ self.process_wait([token])
+
+ def link_set(self, ifname, state):
+ flags = 0
+ self.logger.info('rtnetlink: setting link %s %s' %(ifname, state))
+ if ifupdownmain.ifupdownFlags.DRYRUN:
+ return
+
+ if state == "up":
+ flags |= IFF_UP
+ else:
+ flags &= ~IFF_UP
+
+ ifm = Ifinfomsg(AF_UNSPEC, ifi_change=IFF_UP, ifi_flags=flags)
+ rtas = {IFLA_IFNAME: ifname}
+
+ token = self.request(RTM_NEWLINK, NLM_F_REQUEST | NLM_F_ACK, ifm, rtas)
+ self.process_wait([token])
+
+ def link_set_hwaddress(self, ifname, hwaddress):
+ flags = 0
+ self.logger.info('rtnetlink: setting link hwaddress %s %s' %(ifname, hwaddress))
+ if ifupdownmain.ifupdownFlags.DRYRUN:
+ return
+
+ flags &= ~IFF_UP
+ ifm = Ifinfomsg(AF_UNSPEC, ifi_change=IFF_UP)
+ rtas = {IFLA_IFNAME: ifname,
+ IFLA_ADDRESS : str(bytearray([int(a,16) for a in hwaddress.split(':')]))}
+
+ self.logger.info(rtas)
+
+ token = self.request(RTM_NEWLINK, NLM_F_REQUEST | NLM_F_ACK, ifm, rtas)
+ self.process_wait([token])
+
+ def addr_add(self, ifname, address, broadcast=None, peer=None, scope=None,
+ preferred_lifetime=None):
+ self.logger.info('rtnetlink: setting address')
+ if ifupdownmain.ifupdownFlags.DRYRUN:
+ return
+
+ try:
+ ifindex = self.get_ifindex(link)
+ except Exception, e:
+ raise Exception('cannot determine ifindex for link %s (%s)'
+ %(link, str(e)))
+ ifa_scope = RT_SCOPE_
+ if scope:
+ if scope == "universe":
+ ifa_scope = RT_SCOPE_UNIVERSE
+ elif scope == "site":
+ ifa_scope = RT_SCOPE_SITE
+ elif scope == "link":
+ ifa_scope = RT_SCOPE_LINK
+ elif scope == "host":
+ ifa_scope = RT_SCOPE_HOST
+ elif scope == "nowhere":
+ ifa_scope = RT_SCOPE_NOWHERE
+ rtas = {IFLA_ADDRESS: ifname}
+
+ ifa = Ifaddrmsg(AF_UNSPEC, ifa_scope=ifa_scope, ifa_index=ifindex)
+
+ token = self.request(RTM_NEWADDR, NLM_F_REQUEST | NLM_F_ACK, ifa, rtas)
+ self.process_wait([token])
+
+ def link_set_many(self, ifname, ifattrs):
+ _ifattr_to_rta_map = {'dev' : IFLA_NAME,
+ 'address' : IFLA_ADDRESS,
+ 'broadcast' : IFLA_BROADCAST,
+ 'mtu' : IFLA_MTU,
+ 'master' : IFLA_MASTER}
+ flags = 0
+ ifi_change = IFF_UP
+ rtas = {}
+ self.logger.info('rtnetlink: setting link %s %s' %(ifname, state))
+ if ifupdownmain.ifupdownFlags.DRYRUN:
+ return
+ if not ifattrs:
+ return
+ state = ifattrs.get('state')
+ if state == 'up':
+ flags |= IFF_UP
+ elif state == 'down':
+ flags &= ~IFF_UP
+ else:
+ ifi_change = 0
+
+ if ifi_change:
+ ifm = Ifinfomsg(AF_UNSPEC, ifi_change=IFF_UP, ifi_flags=flags)
+ else:
+ ifm = Ifinfomsg(AF_UNSPEC)
+
+ for attr, attrval in ifattrs.items():
+ rta_attr = _ifattr_to_rta_map.get(attr)
+ if rta_attr:
+ if attr == 'hwaddress':
+ rtas[rta_attr] = str(bytearray([int(a,16) for a in attrval.split(':')]))
+ else:
+ rtas[rta_attr] = attrval
+
+ token = self.request(RTM_NEWLINK, NLM_F_REQUEST | NLM_F_ACK, ifm, rtas)
+ self.process_wait([token])
+
+ def bridge_vlan(self, add=True, vid=None, dev=None, pvid=False,
+ untagged=False, master=True):
+ flags = 0
+ vflags = 0
+ if not vid or not dev:
+ return
+ self.logger.info('rtnetlink: bridge vlan add vid %s %s %s dev %s %s'
+ %(vid, 'untagged' if untagged else '',
+ 'pvid' if pvid else '', dev,
+ 'self' if self else ''))
+ if ifupdownmain.ifupdownFlags.DRYRUN:
+ return
+ try:
+ ifindex = self.get_ifindex(dev)
+ except Exception, e:
+ raise Exception('cannot determine ifindex for dev %s (%s)'
+ %(dev, str(e)))
+ if not master:
+ flags = BRIDGE_FLAGS_SELF
+
+ if pvid:
+ vflags = BRIDGE_VLAN_INFO_PVID
+ vflags |= BRIDGE_VLAN_INFO_UNTAGGED
+ elif untagged:
+ vflags |= BRIDGE_VLAN_INFO_UNTAGGED
+
+ ifm = Ifinfomsg(AF_BRIDGE, ifi_index=ifindex)
+ rtas = {IFLA_AF_SPEC: {
+ IFLA_BRIDGE_FLAGS: flags,
+ IFLA_BRIDGE_VLAN_INFO : BridgeVlanInfo(vflags, int(vid), int(vid))
+ }
+ }
+ if add:
+ token = self.request(RTM_SETLINK,
+ NLM_F_REQUEST | NLM_F_ACK, ifm, rtas)
+ else:
+ token = self.request(RTM_DELLINK,
+ NLM_F_REQUEST | NLM_F_ACK, ifm, rtas)
+ self.process_wait([token])
+
+ def bridge_vlan_many(self, add=True, vids=[], dev=None, pvid=False,
+ untagged=False, master=True):
+ for v in vids:
+ self.bridge_vlan_add(add, v, dev, ispvid, isuntagged, master)
+
+rtnl_api = rtnetlinkApi(os.getpid())
from collections import deque
from threading import *
from ifupdownbase import *
+from sets import Set
class ifaceSchedulerFlags():
""" Enumerates scheduler flags """
""" Runs sub operation on an interface """
ifacename = ifaceobj.name
- if (cls._STATE_CHECK and
- (ifaceobj.state >= ifaceState.from_str(op))):
- ifupdownobj.logger.debug('%s: already in state %s' %(ifacename, op))
+ if ifupdownobj.type and ifupdownobj.type != ifaceobj.type:
return
+
if not ifupdownobj.ADDONS_ENABLE: return
if op == 'query-checkcurr':
query_ifaceobj=ifupdownobj.create_n_save_ifaceobjcurr(ifaceobj)
+ # If not type bridge vlan and the object does not exist,
+ # mark not found and return
+ if (not ifupdownobj.link_exists(ifaceobj.name) and
+ ifaceobj.type != ifaceType.BRIDGE_VLAN):
+ query_ifaceobj.set_state_n_status(ifaceState.from_str(op),
+ ifaceStatus.NOTFOUND)
+ return
for mname in ifupdownobj.module_ops.get(op):
m = ifupdownobj.modules.get(mname)
err = 0
if (ifaceobj.priv_flags & ifupdownobj.NOCONFIG):
continue
ifupdownobj.logger.debug(msg)
- m.run(ifaceobj, op, query_ifaceobj)
+ m.run(ifaceobj, op, query_ifaceobj,
+ ifaceobj_getfunc=ifupdownobj.get_ifaceobjs)
else:
ifupdownobj.logger.debug(msg)
- m.run(ifaceobj, op)
+ m.run(ifaceobj, op,
+ ifaceobj_getfunc=ifupdownobj.get_ifaceobjs)
except Exception, e:
- err = 1
- ifupdownobj.log_error(str(e))
- err = 0 # error can be ignored by log_error, in which case
- # reset err flag
+ if not ifupdownobj.ignore_error(str(e)):
+ err = 1
+ ifupdownobj.logger.warn(str(e))
+ # Continue with rest of the modules
+ pass
finally:
if err or ifaceobj.status == ifaceStatus.ERROR:
ifaceobj.set_state_n_status(ifaceState.from_str(op),
if 'up' in op or 'down' in op:
cls._SCHED_RETVAL = False
else:
+ # Mark success only if the interface was not already
+ # marked with error
+ status = (ifaceobj.status
+ if ifaceobj.status == ifaceStatus.ERROR
+ else ifaceStatus.SUCCESS)
ifaceobj.set_state_n_status(ifaceState.from_str(op),
- ifaceStatus.SUCCESS)
+ status)
- if ifupdownobj.COMPAT_EXEC_SCRIPTS:
+ if ifupdownobj.config.get('addon_scripts_support', '0') == '1':
# execute /etc/network/ scripts
for mname in ifupdownobj.script_ops.get(op, []):
ifupdownobj.logger.debug('%s: %s : running script %s'
""" Runs all operations on a list of interface
configurations for the same interface
"""
+
# minor optimization. If operation is 'down', proceed only
# if interface exists in the system
ifacename = ifaceobjs[0].name
+ ifupdownobj.logger.info('%s: running ops ...' %ifacename)
if ('down' in ops[0] and
+ ifaceobjs[0].type != ifaceType.BRIDGE_VLAN and
not ifupdownobj.link_exists(ifacename)):
ifupdownobj.logger.debug('%s: does not exist' %ifacename)
# run posthook before you get out of here, so that
# for the first object in the list
handler = ifupdownobj.ops_handlers.get(op)
if handler:
- if (not ifaceobjs[0].addr_method or
- (ifaceobjs[0].addr_method and
- ifaceobjs[0].addr_method != 'manual')):
+ try:
handler(ifupdownobj, ifaceobjs[0])
+ except Exception, e:
+ if not ifupdownobj.link_master_slave_ignore_error(str(e)):
+ ifupdownobj.logger.warn('%s: %s'
+ %(ifaceobjs[0].name, str(e)))
+ pass
for ifaceobj in ifaceobjs:
cls.run_iface_op(ifupdownobj, ifaceobj, op,
cenv=ifupdownobj.generate_running_env(ifaceobj, op)
- if ifupdownobj.COMPAT_EXEC_SCRIPTS else None)
- posthookfunc = ifupdownobj.sched_hooks.get('posthook')
- if posthookfunc:
- posthookfunc(ifupdownobj, ifaceobj, op)
+ if ifupdownobj.config.get('addon_scripts_support',
+ '0') == '1' else None)
+ posthookfunc = ifupdownobj.sched_hooks.get('posthook')
+ if posthookfunc:
+ try:
+ [posthookfunc(ifupdownobj, ifaceobj, ops[0])
+ for ifaceobj in ifaceobjs]
+ except Exception, e:
+ ifupdownobj.logger.warn('%s' %str(e))
+ pass
@classmethod
def _check_upperifaces(cls, ifupdownobj, ifaceobj, ops, parent,
if not ifaceobjs:
raise Exception('%s: not found' %ifacename)
+ # Check state of the dependent. If it is already brought up, return
+ if (cls._STATE_CHECK and
+ (ifaceobjs[0].state == ifaceState.from_str(ops[-1]))):
+ ifupdownobj.logger.debug('%s: already processed' %ifacename)
+ return
+
for ifaceobj in ifaceobjs:
if not cls._check_upperifaces(ifupdownobj, ifaceobj,
ops, parent, followdependents):
- return
+ return
# If inorder, run the iface first and then its dependents
if order == ifaceSchedulerFlags.INORDER:
if not ifaceobjs:
raise Exception('%s: not found' %ifacename)
+ if (cls._STATE_CHECK and
+ (ifaceobjs[0].state == ifaceState.from_str(ops[-1]))):
+ ifupdownobj.logger.debug('%s: already processed' %ifacename)
+ return
+
if not skip_root:
# run the iface first and then its upperifaces
cls.run_iface_list_ops(ifupdownobj, ifaceobjs, ops)
ifupdownobj.logger.warn('%s : %s' %(ifacename, str(e)))
pass
+ @classmethod
+ def _get_valid_upperifaces(cls, ifupdownobj, ifacenames,
+ allupperifacenames):
+ """ Recursively find valid upperifaces
+
+ valid upperifaces are:
+ - An upperiface which had no user config (example builtin
+ interfaces. usually vlan interfaces.)
+ - or had config and previously up
+ - and interface currently does not exist
+ - or is a bridge (because if your upperiface was a bridge
+ - u will have to execute up on the bridge
+ to enslave the port and apply bridge attributes to the port) """
+
+ upperifacenames = []
+ for ifacename in ifacenames:
+ # get upperifaces
+ ifaceobj = ifupdownobj.get_ifaceobj_first(ifacename)
+ if not ifaceobj:
+ continue
+ ulist = Set(ifaceobj.upperifaces).difference(upperifacenames)
+ nulist = []
+ for u in ulist:
+ uifaceobj = ifupdownobj.get_ifaceobj_first(u)
+ if not uifaceobj:
+ continue
+ has_config = not bool(uifaceobj.priv_flags
+ & ifupdownobj.NOCONFIG)
+ if (((has_config and ifupdownobj.get_ifaceobjs_saved(u)) or
+ not has_config) and (not ifupdownobj.link_exists(u)
+ or uifaceobj.link_kind == ifaceLinkKind.BRIDGE)):
+ nulist.append(u)
+ upperifacenames.extend(nulist)
+ allupperifacenames.extend(upperifacenames)
+ if upperifacenames:
+ cls._get_valid_upperifaces(ifupdownobj, upperifacenames,
+ allupperifacenames)
+ return
+
+ @classmethod
+ def run_upperifaces(cls, ifupdownobj, ifacenames, ops,
+ continueonfailure=True):
+ """ Run through valid upperifaces """
+ upperifaces = []
+
+ cls._get_valid_upperifaces(ifupdownobj, ifacenames, upperifaces)
+ if not upperifaces:
+ return
+ # dump valid upperifaces
+ ifupdownobj.logger.debug(upperifaces)
+ for u in upperifaces:
+ try:
+ ifaceobjs = ifupdownobj.get_ifaceobjs(u)
+ if not ifaceobjs:
+ continue
+ cls.run_iface_list_ops(ifupdownobj, ifaceobjs, ops)
+ except Exception, e:
+ if continueonfailure:
+ self.logger.warn('%s' %str(e))
+
@classmethod
def get_sorted_iface_list(cls, ifupdownobj, ifacenames, ops,
dependency_graph, indegrees=None):
def sched_ifaces(cls, ifupdownobj, ifacenames, ops,
dependency_graph=None, indegrees=None,
order=ifaceSchedulerFlags.POSTORDER,
- followdependents=True):
+ followdependents=True, skipupperifaces=False):
""" runs interface configuration modules on interfaces passed as
argument. Runs topological sort on interface dependency graph.
#
# Run any upperifaces if available
#
- followupperifaces = []
+ followupperifaces = False
run_queue = []
skip_ifacesort = int(ifupdownobj.config.get('skip_ifacesort', '0'))
if not skip_ifacesort and not indegrees:
# If there is any interface that does exist, maybe it is a
# logical interface and we have to followupperifaces when it
# comes up, so get that list.
- followupperifaces = (True if
+ if any([True for i in ifacenames
+ if ifupdownobj.must_follow_upperifaces(i)]):
+ followupperifaces = (True if
[i for i in ifacenames
if not ifupdownobj.link_exists(i)]
else False)
run_queue.reverse()
# run interface list
- ifupdownobj.logger.info('running interfaces: %s' %str(run_queue))
cls.run_iface_list(ifupdownobj, run_queue, ops,
parent=None, order=order,
followdependents=followdependents)
if not cls._SCHED_RETVAL:
raise Exception()
- if (ifupdownobj.config.get('skip_upperifaces', '0') == '0' and
+ if (not skipupperifaces and
+ ifupdownobj.config.get('skip_upperifaces', '0') == '0' and
((not ifupdownobj.ALL and followdependents) or
followupperifaces) and
'up' in ops[0]):
ifupdownobj.logger.info('running upperifaces (parent interfaces) ' +
'if available ..')
cls._STATE_CHECK = False
- cls.run_iface_list_upper(ifupdownobj, ifacenames, ops,
- skip_root=True)
+ cls.run_upperifaces(ifupdownobj, ifacenames, ops)
cls._STATE_CHECK = True
op (str): ifupdown operation
"""
- self.logger.debug('%s: statemanager sync state' %ifaceobj.name)
+ self.logger.debug('%s: statemanager sync state %s'
+ %(ifaceobj.name, op))
old_ifaceobjs = self.ifaceobjdict.get(ifaceobj.name)
if 'up' in op:
if not old_ifaceobjs:
#
import os
import fcntl
+import re
class utils():
return False
return True
+ @classmethod
+ def parse_iface_range(cls, name):
+ range_match = re.match("^([\w\.]+)\[([\d]+)-([\d]+)\]", name)
+ if range_match:
+ range_groups = range_match.groups()
+ if range_groups[1] and range_groups[2]:
+ return (range_groups[0], int(range_groups[1], 10),
+ int(range_groups[2], 10))
+ return None
+
+ @classmethod
+ def expand_iface_range(cls, name):
+ ifacenames = []
+ iface_range = cls.parse_iface_range(name)
+ if iface_range:
+ for i in range(iface_range[1], iface_range[2]):
+ ifacenames.append('%s-%d' %(iface_range[0], i))
+ return ifacenames
+
--- /dev/null
+#!/usr/bin/python
+#
+# Copyright 2014 Cumulus Networks, Inc. All rights reserved.
+# Author: Roopa Prabhu, roopa@cumulusnetworks.com
+#
+
+from ifupdown.iface import *
+from utilsbase import *
+import os
+import re
+import logging
+from cache import *
+
+class brctl(utilsBase):
+ """ This class contains helper functions to interact with the bridgeutils
+ commands """
+
+ _cache_fill_done = False
+
+ def __init__(self, *args, **kargs):
+ utilsBase.__init__(self, *args, **kargs)
+ if self.CACHE and not brctl._cache_fill_done:
+ self._bridge_fill()
+ brctl._cache_fill_done = True
+
+
+ def _bridge_get_mcattrs_from_sysfs(self, bridgename):
+ mcattrs = {}
+ mcattrmap = {'mclmc': 'multicast_last_member_count',
+ 'mcrouter': 'multicast_router',
+ 'mcsnoop' : 'multicast_snooping',
+ 'mcsqc' : 'multicast_startup_query_count',
+ 'mcqifaddr' : 'multicast_query_use_ifaddr',
+ 'mcquerier' : 'multicast_querier',
+ 'hashel' : 'hash_elasticity',
+ 'hashmax' : 'hash_max',
+ 'mclmi' : 'multicast_last_member_interval',
+ 'mcmi' : 'multicast_membership_interval',
+ 'mcqpi' : 'multicast_querier_interval',
+ 'mcqi' : 'multicast_query_interval',
+ 'mcqri' : 'multicast_query_response_interval',
+ 'mcsqi' : 'multicast_startup_query_interval'}
+
+ mcattrsdivby100 = ['mclmi', 'mcmi', 'mcqpi', 'mcqi', 'mcqri', 'mcsqi']
+
+ for m, s in mcattrmap.items():
+ n = self.read_file_oneline('/sys/class/net/%s/bridge/%s'
+ %(bridgename, s))
+ if m in mcattrsdivby100:
+ try:
+ v = int(n) / 100
+ mcattrs[m] = str(v)
+ except Exception, e:
+ self.logger.warn('error getting mc attr %s (%s)'
+ %(m, str(e)))
+ pass
+ else:
+ mcattrs[m] = n
+ return mcattrs
+
+ def _bridge_attrs_fill(self, bridgename):
+ battrs = {}
+ bports = {}
+
+ brout = self.exec_command('/sbin/brctl showstp %s' %bridgename)
+ chunks = re.split(r'\n\n', brout, maxsplit=0, flags=re.MULTILINE)
+
+ try:
+ # Get all bridge attributes
+ broutlines = chunks[0].splitlines()
+ #battrs['pathcost'] = broutlines[3].split('path cost')[1].strip()
+ battrs['maxage'] = broutlines[4].split(
+ 'bridge max age')[1].strip().replace('.00', '')
+ battrs['hello'] = broutlines[5].split(
+ 'bridge hello time')[1].strip().replace('.00',
+ '')
+ battrs['fd'] = broutlines[6].split(
+ 'bridge forward delay')[1].strip(
+ ).replace('.00', '')
+ battrs.update(self._bridge_get_mcattrs_from_sysfs(bridgename))
+
+ # XXX: comment this out until mc attributes become available
+ # with brctl again
+ #battrs['hashel'] = broutlines[10].split('hash elasticity')[1].split()[0].strip()
+ #battrs['hashmax'] = broutlines[10].split('hash max')[1].strip()
+ #battrs['mclmc'] = broutlines[11].split('mc last member count')[1].split()[0].strip()
+ #battrs['mciqc'] = broutlines[11].split('mc init query count')[1].strip()
+ #battrs['mcrouter'] = broutlines[12].split('mc router')[1].split()[0].strip()
+ ##battrs['mcsnoop'] = broutlines[12].split('mc snooping')[1].strip()
+ #battrs['mclmt'] = broutlines[13].split('mc last member timer')[1].split()[0].strip()
+ except Exception, e:
+ self.logger.warn(str(e))
+ pass
+
+ linkCache.update_attrdict([bridgename, 'linkinfo'], battrs)
+
+ for cidx in range(1, len(chunks)):
+ bpout = chunks[cidx].lstrip('\n')
+ if not bpout or bpout[0] == ' ':
+ continue
+ bplines = bpout.splitlines()
+ pname = bplines[0].split()[0]
+ bportattrs = {}
+ try:
+ bportattrs['pathcost'] = bplines[2].split(
+ 'path cost')[1].strip()
+ bportattrs['fdelay'] = bplines[4].split(
+ 'forward delay timer')[1].strip()
+ bportattrs['mcrouter'] = self.read_file_oneline(
+ '/sys/class/net/%s/brport/multicast_router' %pname)
+ bportattrs['mcfl'] = self.read_file_oneline(
+ '/sys/class/net/%s/brport/multicast_fast_leave' %pname)
+
+ #bportattrs['mcrouters'] = bplines[6].split('mc router')[1].split()[0].strip()
+ #bportattrs['mc fast leave'] = bplines[6].split('mc fast leave')[1].strip()
+ except Exception, e:
+ self.logger.warn(str(e))
+ pass
+ bports[pname] = bportattrs
+ linkCache.update_attrdict([bridgename, 'linkinfo', 'ports'], bports)
+
+ def _bridge_fill(self, bridgename=None, refresh=False):
+ try:
+ # if cache is already filled, return
+ linkCache.get_attr([bridgename, 'linkinfo', 'fd'])
+ return
+ except:
+ pass
+ if not bridgename:
+ brctlout = self.exec_command('/sbin/brctl show')
+ else:
+ brctlout = self.exec_command('/sbin/brctl show ' + bridgename)
+ if not brctlout:
+ return
+
+ for bline in brctlout.splitlines()[1:]:
+ bitems = bline.split()
+ if len(bitems) < 2:
+ continue
+ try:
+ linkCache.update_attrdict([bitems[0], 'linkinfo'],
+ {'stp' : bitems[2]})
+ except KeyError:
+ linkCache.update_attrdict([bitems[0]],
+ {'linkinfo' : {'stp' : bitems[2]}})
+ self._bridge_attrs_fill(bitems[0])
+
+ def _cache_get(self, attrlist, refresh=False):
+ try:
+ if self.DRYRUN:
+ return None
+ if self.CACHE:
+ if not self._cache_fill_done:
+ self._bridge_fill()
+ self._cache_fill_done = True
+ return linkCache.get_attr(attrlist)
+ if not refresh:
+ return linkCache.get_attr(attrlist)
+ self._bridge_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:
+ linkCache.add_attr(attrlist, value)
+ except:
+ pass
+
+ def _cache_delete(self, attrlist):
+ if self.DRYRUN: return
+ try:
+ linkCache.del_attr(attrlist)
+ except:
+ pass
+
+ def _cache_invalidate(self):
+ if self.DRYRUN: return
+ linkCache.invalidate()
+
+ def create_bridge(self, bridgename):
+ if self.bridge_exists(bridgename):
+ return
+ self.exec_command('/sbin/brctl addbr %s' %bridgename)
+ self._cache_update([bridgename], {})
+
+ def delete_bridge(self, bridgename):
+ if not self.bridge_exists(bridgename):
+ return
+ self.exec_command('/sbin/brctl delbr %s' %bridgename)
+ self._cache_invalidate()
+
+ def add_bridge_port(self, bridgename, bridgeportname):
+ """ Add port to bridge """
+ ports = self._cache_get([bridgename, 'linkinfo', 'ports'])
+ if ports and ports.get(bridgeportname):
+ return
+ self.exec_command('/sbin/brctl addif ' + bridgename + ' ' +
+ bridgeportname)
+ self._cache_update([bridgename, 'linkinfo', 'ports',
+ bridgeportname], {})
+
+ def delete_bridge_port(self, bridgename, bridgeportname):
+ """ Delete port from bridge """
+ ports = self._cache_get([bridgename, 'linkinfo', 'ports'])
+ if not ports or not ports.get(bridgeportname):
+ return
+ self.exec_command('/sbin/brctl delif ' + bridgename + ' ' +
+ bridgeportname)
+ self._cache_delete([bridgename, 'linkinfo', 'ports',
+ 'bridgeportname'])
+
+ def set_bridgeport_attrs(self, bridgename, bridgeportname, attrdict):
+ portattrs = self._cache_get([bridgename, 'linkinfo',
+ 'ports', bridgeportname])
+ if portattrs == None: portattrs = {}
+ for k, v in attrdict.iteritems():
+ if self.CACHE:
+ curval = portattrs.get(k)
+ if curval and curval == v:
+ continue
+ self.exec_command('/sbin/brctl set%s %s %s %s'
+ %(k, bridgename, bridgeportname, v))
+
+ def set_bridgeport_attr(self, bridgename, bridgeportname,
+ attrname, attrval):
+ if self._cache_check([bridgename, 'linkinfo', 'ports',
+ bridgeportname, attrname], attrval):
+ return
+ self.exec_command('/sbin/brctl set%s %s %s %s' %(attrname, bridgename,
+ bridgeportname, attrval))
+
+ def set_bridge_attrs(self, bridgename, attrdict):
+ for k, v in attrdict.iteritems():
+ if not v:
+ continue
+ if self._cache_check([bridgename, 'linkinfo', k], v):
+ continue
+ try:
+ self.exec_command('/sbin/brctl set%s %s %s'
+ %(k, bridgename, v))
+ except Exception, e:
+ self.logger.warn('%s: %s' %(bridgename, str(e)))
+ pass
+
+ def set_bridge_attr(self, bridgename, attrname, attrval):
+ if self._cache_check([bridgename, 'linkinfo', attrname], attrval):
+ return
+ self.exec_command('/sbin/brctl set%s %s %s'
+ %(attrname, bridgename, attrval))
+
+ def get_bridge_attrs(self, bridgename):
+ return self._cache_get([bridgename, 'linkinfo'])
+
+ def get_bridgeport_attrs(self, bridgename, bridgeportname):
+ return self._cache_get([bridgename, 'linkinfo', 'ports',
+ bridgeportname])
+
+ def get_bridgeport_attr(self, bridgename, bridgeportname, attrname):
+ return self._cache_get([bridgename, 'linkinfo', 'ports',
+ bridgeportname, attrname])
+
+ def set_stp(self, bridge, stp_state):
+ self.exec_command('/sbin/brctl stp ' + bridge + ' ' + stp_state)
+
+ def get_stp(self, bridge):
+ sysfs_stpstate = '/sys/class/net/%s/bridge/stp_state' %bridge
+ if not os.path.exists(sysfs_stpstate):
+ return 'error'
+ stpstate = self.read_file_oneline(sysfs_stpstate)
+ if not stpstate:
+ return 'error'
+ try:
+ if int(stpstate) > 0:
+ return 'yes'
+ elif int(stpstate) == 0:
+ return 'no'
+ except:
+ return 'unknown'
+
+ def conv_value_to_user(self, str):
+ try:
+ ret = int(str) / 100
+ except:
+ return None
+ finally:
+ return '%d' %ret
+
+ def read_value_from_sysfs(self, filename, preprocess_func):
+ value = self.read_file_oneline(filename)
+ if not value:
+ return None
+ return preprocess_func(value)
+
+ def set_ageing(self, bridge, ageing):
+ self.exec_command('/sbin/brctl setageing ' + bridge + ' ' + ageing)
+
+ def get_ageing(self, bridge):
+ return self.read_value_from_sysfs('/sys/class/net/%s/bridge/ageing_time'
+ %bridge, self.conv_value_to_user)
+
+ def set_bridgeprio(self, bridge, bridgeprio):
+ self.exec_command('/sbin/brctl setbridgeprio ' + bridge + ' ' +
+ bridgeprio)
+
+ def get_bridgeprio(self, bridge):
+ return self.read_file_oneline(
+ '/sys/class/net/%s/bridge/priority' %bridge)
+
+ def set_fd(self, bridge, fd):
+ self.exec_command('/sbin/brctl setfd ' + bridge + ' ' + fd)
+
+ def get_fd(self, bridge):
+ return self.read_value_from_sysfs(
+ '/sys/class/net/%s/bridge/forward_delay'
+ %bridge, self.conv_value_to_user)
+
+ def set_gcint(self, bridge, gcint):
+ #cmd = '/sbin/brctl setgcint ' + bridge + ' ' + gcint
+ raise Exception('set_gcint not implemented')
+
+ def set_hello(self, bridge, hello):
+ self.exec_command('/sbin/brctl sethello ' + bridge + ' ' + hello)
+
+ def get_hello(self, bridge):
+ return self.read_value_from_sysfs('/sys/class/net/%s/bridge/hello_time'
+ %bridge, self.conv_value_to_user)
+
+ def set_maxage(self, bridge, maxage):
+ self.exec_command('/sbin/brctl setmaxage ' + bridge + ' ' + maxage)
+
+ def get_maxage(self, bridge):
+ return self.read_value_from_sysfs('/sys/class/net/%s/bridge/max_age'
+ %bridge, self.conv_value_to_user)
+
+ def set_pathcost(self, bridge, port, pathcost):
+ self.exec_command('/sbin/brctl setpathcost %s' %bridge + ' %s' %port +
+ ' %s' %pathcost)
+
+ def get_pathcost(self, bridge, port):
+ return self.read_file_oneline('/sys/class/net/%s/brport/path_cost'
+ %port)
+
+ def set_portprio(self, bridge, port, prio):
+ self.exec_command('/sbin/brctl setportprio %s' %bridge + ' %s' %port +
+ ' %s' %prio)
+
+ def get_portprio(self, bridge, port):
+ return self.read_file_oneline('/sys/class/net/%s/brport/priority'
+ %port)
+
+ def set_hashmax(self, bridge, hashmax):
+ self.exec_command('/sbin/brctl sethashmax %s' %bridge + ' %s' %hashmax)
+
+ def get_hashmax(self, bridge):
+ return self.read_file_oneline('/sys/class/net/%s/bridge/hash_max'
+ %bridge)
+
+ def set_hashel(self, bridge, hashel):
+ self.exec_command('/sbin/brctl sethashel %s' %bridge + ' %s' %hashel)
+
+ def get_hashel(self, bridge):
+ return self.read_file_oneline('/sys/class/net/%s/bridge/hash_elasticity'
+ %bridge)
+
+ def set_mclmc(self, bridge, mclmc):
+ self.exec_command('/sbin/brctl setmclmc %s' %bridge + ' %s' %mclmc)
+
+ def get_mclmc(self, bridge):
+ return self.read_file_oneline(
+ '/sys/class/net/%s/bridge/multicast_last_member_count'
+ %bridge)
+
+ def set_mcrouter(self, bridge, mcrouter):
+ self.exec_command('/sbin/brctl setmcrouter %s' %bridge +
+ ' %s' %mcrouter)
+
+ def get_mcrouter(self, bridge):
+ return self.read_file_oneline(
+ '/sys/class/net/%s/bridge/multicast_router' %bridge)
+
+ def set_mcsnoop(self, bridge, mcsnoop):
+ self.exec_command('/sbin/brctl setmcsnoop %s' %bridge +
+ ' %s' %mcsnoop)
+
+ def get_mcsnoop(self, bridge):
+ return self.read_file_oneline(
+ '/sys/class/net/%s/bridge/multicast_snooping' %bridge)
+
+ def set_mcsqc(self, bridge, mcsqc):
+ self.exec_command('/sbin/brctl setmcsqc %s' %bridge +
+ ' %s' %mcsqc)
+
+ def get_mcsqc(self, bridge):
+ return self.read_file_oneline(
+ '/sys/class/net/%s/bridge/multicast_startup_query_count'
+ %bridge)
+
+ def set_mcqifaddr(self, bridge, mcqifaddr):
+ self.exec_command('/sbin/brctl setmcqifaddr %s' %bridge +
+ ' %s' %mcqifaddr)
+
+ def get_mcqifaddr(self, bridge):
+ return self.read_file_oneline(
+ '/sys/class/net/%s/bridge/multicast_startup_query_use_ifaddr'
+ %bridge)
+
+ def set_mcquerier(self, bridge, mcquerier):
+ self.exec_command('/sbin/brctl setmcquerier %s' %bridge +
+ ' %s' %mcquerier)
+
+ def get_mcquerier(self, bridge):
+ return self.read_file_oneline(
+ '/sys/class/net/%s/bridge/multicast_querier' %bridge)
+
+ def set_mcqv4src(self, bridge, vlan, mcquerier):
+ if vlan == 0 or vlan > 4095:
+ self.logger.warn('mcqv4src vlan \'%d\' invalid range' %vlan)
+ return
+
+ ip = mcquerier.split('.')
+ if len(ip) != 4:
+ self.logger.warn('mcqv4src \'%s\' invalid IPv4 address' %mcquerier)
+ return
+ for k in ip:
+ if not k.isdigit() or int(k, 10) < 0 or int(k, 10) > 255:
+ self.logger.warn('mcqv4src \'%s\' invalid IPv4 address' %mcquerier)
+ return
+
+ self.exec_command('/sbin/brctl setmcqv4src %s' %bridge +
+ ' %d %s' %(vlan, mcquerier))
+
+ def del_mcqv4src(self, bridge, vlan):
+ self.exec_command('/sbin/brctl delmcqv4src %s %d' %(bridge, vlan))
+
+ def get_mcqv4src(self, bridge, vlan=None):
+ mcqv4src = {}
+ mcqout = self.exec_command('/sbin/brctl showmcqv4src %s' %bridge)
+ if not mcqout: return None
+ mcqlines = mcqout.splitlines()
+ for l in mcqlines[1:]:
+ l=l.strip()
+ k, d, v = l.split('\t')
+ if not k or not v:
+ continue
+ mcqv4src[k] = v
+ if vlan:
+ return mcqv4src.get(vlan)
+ return mcqv4src
+
+ def set_mclmi(self, bridge, mclmi):
+ self.exec_command('/sbin/brctl setmclmi %s' %bridge +
+ ' %s' %mclmi)
+
+ def get_mclmi(self, bridge):
+ return self.read_file_oneline(
+ '/sys/class/net/%s/bridge/multicast_last_member_interval'
+ %bridge)
+
+ def set_mcmi(self, bridge, mcmi):
+ self.exec_command('/sbin/brctl setmcmi %s' %bridge +
+ ' %s' %mcmi)
+
+ def get_mcmi(self, bridge):
+ return self.read_file_oneline(
+ '/sys/class/net/%s/bridge/multicast_membership_interval'
+ %bridge)
+
+ def bridge_exists(self, bridge):
+ return os.path.exists('/sys/class/net/%s/bridge' %bridge)
+
+ def is_bridge_port(self, ifacename):
+ return os.path.exists('/sys/class/net/%s/brport' %ifacename)
+
+ def bridge_port_exists(self, bridge, bridgeportname):
+ try:
+ return os.path.exists('/sys/class/net/%s/brif/%s'
+ %(bridge, bridgeportname))
+ except Exception:
+ return False
+
+ def get_bridge_ports(self, bridgename):
+ try:
+ return os.listdir('/sys/class/net/%s/brif/' %bridgename)
+ except:
+ return []
--- /dev/null
+#!/usr/bin/python
+#
+# Copyright 2014 Cumulus Networks, Inc. All rights reserved.
+# Author: Roopa Prabhu, roopa@cumulusnetworks.com
+#
+
+import pprint
+from collections import OrderedDict
+
+class linkCache():
+ """ This class contains methods and instance variables to cache
+ link info """
+
+ _shared_state = {}
+
+ """ { <ifacename> : { 'ifindex': <index>,
+ 'mtu': <mtu>,
+ 'state' : <state>',
+ 'flags' : <flags>,
+ 'kind' : <kind: bridge, bond, vlan>,
+ 'linkinfo' : {<attr1> : <attrval1>,
+ <attr2> : <attrval2>,
+ <ports> : {
+ } """
+ links = {}
+ @classmethod
+ def get_attr(cls, mapList):
+ return reduce(lambda d, k: d[k], mapList, linkCache.links)
+
+ @classmethod
+ def set_attr(cls, mapList, value):
+ cls.get_attr(mapList[:-1])[mapList[-1]] = value
+
+ @classmethod
+ def del_attr(cls, mapList):
+ try:
+ del cls.get_attr(mapList[:-1])[mapList[-1]]
+ except:
+ pass
+
+ @classmethod
+ def update_attrdict(cls, mapList, valuedict):
+ try:
+ cls.get_attr(mapList[:-1])[mapList[-1]].update(valuedict)
+ except:
+ cls.get_attr(mapList[:-1])[mapList[-1]] = valuedict
+ pass
+
+ @classmethod
+ def append_to_attrlist(cls, mapList, value):
+ cls.get_attr(mapList[:-1])[mapList[-1]].append(value)
+
+ @classmethod
+ def remove_from_attrlist(cls, mapList, value):
+ try:
+ cls.get_attr(mapList[:-1])[mapList[-1]].remove(value)
+ except:
+ pass
+
+ @classmethod
+ def check_attr(cls, attrlist, value=None):
+ try:
+ cachedvalue = cls.get_attr(attrlist)
+ if value:
+ if cachedvalue == value:
+ return True
+ else:
+ return False
+ elif cachedvalue:
+ return True
+ else:
+ return False
+ except:
+ return False
+
+ @classmethod
+ def invalidate(cls):
+ cls.links = {}
+
+ @classmethod
+ def dump(cls):
+ print 'Dumping link cache'
+ pp = pprint.PrettyPrinter(indent=4)
+ pp.pprint(cls.links)
+
+ @classmethod
+ def dump_link(cls, linkname):
+ print 'Dumping link %s' %linkname
+ pp = pprint.PrettyPrinter(indent=4)
+ pp.pprint(cls.links.get(linkname))
--- /dev/null
+#!/usr/bin/python
+#
+# Copyright 2014 Cumulus Networks, Inc. All rights reserved.
+# Author: Roopa Prabhu, roopa@cumulusnetworks.com
+#
+
+from utilsbase import *
+import subprocess
+import os
+
+FNULL = open(os.devnull, 'w')
+
+class dhclient(utilsBase):
+ """ This class contains helper methods to interact with the dhclient
+ utility """
+
+ def _pid_exists(self, pidfilename):
+ if os.path.exists(pidfilename):
+ pid = self.read_file_oneline(pidfilename)
+ if not os.path.exists('/proc/%s' %pid):
+ return False
+ else:
+ return False
+ return True
+
+ def is_running(self, ifacename):
+ return self._pid_exists('/run/dhclient.%s.pid' %ifacename)
+
+ def is_running6(self, ifacename):
+ return self._pid_exists('/run/dhclient6.%s.pid' %ifacename)
+
+ def stop(self, ifacename):
+ if os.path.exists('/sbin/dhclient3'):
+ cmd = ['/sbin/dhclient3', '-x', '-pf',
+ '/run/dhclient.%s.pid' %ifacename, '-lf',
+ '/var/lib/dhcp3/dhclient.%s.leases' %ifacename,
+ '%s' %ifacename]
+ else:
+ cmd = ['/sbin/dhclient', '-x', '-pf',
+ '/run/dhclient.%s.pid' %ifacename,
+ '-lf', '/var/lib/dhcp/dhclient.%s.leases' %ifacename,
+ '%s' %ifacename]
+ self.subprocess_check_call(cmd)
+
+ def start(self, ifacename):
+ if os.path.exists('/sbin/dhclient3'):
+ cmd = ['/sbin/dhclient3', '-pf',
+ '/run/dhclient.%s.pid' %ifacename,
+ '-lf', '/var/lib/dhcp3/dhclient.%s.leases' %ifacename,
+ '%s' %ifacename]
+ else:
+ cmd = ['/sbin/dhclient', '-pf', '/run/dhclient.%s.pid' %ifacename,
+ '-lf', '/var/lib/dhcp/dhclient.%s.leases' %ifacename,
+ '%s' %ifacename]
+ self.subprocess_check_call(cmd)
+
+ def release(self, ifacename):
+ if os.path.exists('/sbin/dhclient3'):
+ cmd = ['/sbin/dhclient3', '-r', '-pf',
+ '/run/dhclient.%s.pid' %ifacename, '-lf',
+ '/var/lib/dhcp3/dhclient.%s.leases' %ifacename,
+ '%s' %ifacename]
+ else:
+ cmd = ['/sbin/dhclient', '-r', '-pf',
+ '/run/dhclient.%s.pid' %ifacename,
+ '-lf', '/var/lib/dhcp/dhclient.%s.leases' %ifacename,
+ '%s' %ifacename]
+ self.subprocess_check_call(cmd)
+
+ def start6(self, ifacename):
+ self.subprocess_check_call(['dhclient', '-6', '-pf',
+ '/run/dhclient6.%s.pid' %ifacename, '-lf',
+ '/var/lib/dhcp/dhclient.%s.leases ' %ifacename,
+ '%s' %ifacename])
+
+ def stop6(self, ifacename):
+ self.subprocess_check_call(['dhclient', '-6', '-x', '-pf',
+ '/run/dhclient.%s.pid' %ifacename, '-lf',
+ '/var/lib/dhcp/dhclient.%s.leases ' %ifacename,
+ '%s' %ifacename])
+
+ def release6(self, ifacename):
+ self.subprocess_check_call(['dhclient', '-6', '-r', '-pf',
+ '/run/dhclient6.%s.pid' %ifacename, '-lf',
+ '/var/lib/dhcp/dhclient6.%s.leases' %ifacename,
+ '%s' %ifacename])
--- /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',
+ '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 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
+from collections import OrderedDict
+from utilsbase import *
+from cache import *
+
+class iproute2(utilsBase):
+ """ This class contains helper methods to cache and interact with the
+ commands in the iproute2 package """
+
+ _cache_fill_done = False
+ ipbatchbuf = ''
+ ipbatch = False
+ ipbatch_pause = False
+
+ def __init__(self, *args, **kargs):
+ utilsBase.__init__(self, *args, **kargs)
+ if self.CACHE and not iproute2._cache_fill_done:
+ self._link_fill()
+ self._addr_fill()
+ iproute2._cache_fill_done = True
+
+ def _link_fill(self, ifacename=None, refresh=False):
+ """ fills cache with link information
+
+ if ifacename argument given, fill cache for ifacename, else
+ fill cache for all interfaces in the system
+ """
+
+ linkout = {}
+ if iproute2._cache_fill_done and not refresh: return
+ try:
+ # if ifacename already present, return
+ if (ifacename and not refresh and
+ linkCache.get_attr([ifacename, 'ifflag'])):
+ return
+ except:
+ pass
+ cmdout = self.link_show(ifacename=ifacename)
+ if not cmdout:
+ return
+ for c in cmdout.splitlines():
+ citems = c.split()
+ ifnamenlink = citems[1].split('@')
+ if len(ifnamenlink) > 1:
+ ifname = ifnamenlink[0]
+ iflink = ifnamenlink[1].strip(':')
+ else:
+ ifname = ifnamenlink[0].strip(':')
+ iflink = None
+ linkattrs = {}
+ linkattrs['link'] = iflink
+ linkattrs['ifindex'] = citems[0].strip(':')
+ flags = citems[2].strip('<>').split(',')
+ linkattrs['flags'] = flags
+ linkattrs['ifflag'] = 'UP' if 'UP' in flags else 'DOWN'
+ for i in range(0, len(citems)):
+ if citems[i] == 'mtu': linkattrs['mtu'] = citems[i+1]
+ elif citems[i] == 'state': linkattrs['state'] = citems[i+1]
+ elif citems[i] == 'link/ether': linkattrs['hwaddress'] = citems[i+1]
+ elif citems[i] == 'vlan' and citems[i+1] == 'id':
+ linkattrs['linkinfo'] = {'vlanid' : citems[i+2]}
+ elif citems[i] == 'vxlan' and citems[i+1] == 'id':
+ vattrs = {'vxlanid' : citems[i+2],
+ 'svcnode' : [],
+ 'learning': 'on'}
+ for j in range(i+2, len(citems)):
+ if citems[j] == 'local':
+ vattrs['local'] = citems[j+1]
+ elif citems[j] == 'svcnode':
+ vattrs['svcnode'].append(citems[j+1])
+ elif citems[j] == 'peernode':
+ vattrs['peernode'].append(citems[j+1])
+ elif citems[j] == 'nolearning':
+ vattrs['learning'] = 'off'
+ linkattrs['linkinfo'] = vattrs
+ break
+ #linkattrs['alias'] = self.read_file_oneline(
+ # '/sys/class/net/%s/ifalias' %ifname)
+ linkout[ifname] = linkattrs
+ [linkCache.update_attrdict([ifname], linkattrs)
+ for ifname, linkattrs in linkout.items()]
+
+ def _addr_filter(self, addr, scope=None):
+ default_addrs = ['127.0.0.1/8', '::1/128' , '0.0.0.0']
+ if addr in default_addrs:
+ return True
+ if scope and scope == 'link':
+ return True
+ return False
+
+ def _addr_fill(self, ifacename=None, refresh=False):
+ """ fills cache with address information
+
+ if ifacename argument given, fill cache for ifacename, else
+ fill cache for all interfaces in the system
+ """
+
+ linkout = {}
+ if iproute2._cache_fill_done: return
+ try:
+ # Check if ifacename is already full, in which case, return
+ if ifacename:
+ linkCache.get_attr([ifacename, 'addrs'])
+ return
+ except:
+ pass
+ cmdout = self.addr_show(ifacename=ifacename)
+ if not cmdout:
+ return
+ for c in cmdout.splitlines():
+ citems = c.split()
+ ifnamenlink = citems[1].split('@')
+ if len(ifnamenlink) > 1:
+ ifname = ifnamenlink[0]
+ else:
+ ifname = ifnamenlink[0].strip(':')
+ if citems[2] == 'inet':
+ if self._addr_filter(citems[3], scope=citems[5]):
+ continue
+ addrattrs = {}
+ addrattrs['scope'] = citems[5]
+ addrattrs['type'] = 'inet'
+ linkout[ifname]['addrs'][citems[3]] = addrattrs
+ elif citems[2] == 'inet6':
+ if self._addr_filter(citems[3], scope=citems[5]):
+ continue
+ if citems[5] == 'link': continue #skip 'link' addresses
+ addrattrs = {}
+ addrattrs['scope'] = citems[5]
+ addrattrs['type'] = 'inet6'
+ linkout[ifname]['addrs'][citems[3]] = addrattrs
+ else:
+ linkattrs = {}
+ linkattrs['addrs'] = OrderedDict({})
+ try:
+ linkout[ifname].update(linkattrs)
+ except KeyError:
+ linkout[ifname] = linkattrs
+
+ [linkCache.update_attrdict([ifname], linkattrs)
+ for ifname, linkattrs in linkout.items()]
+
+ def _cache_get(self, type, attrlist, refresh=False):
+ try:
+ if self.DRYRUN:
+ return False
+ if self.CACHE:
+ if not iproute2._cache_fill_done:
+ self._link_fill()
+ self._addr_fill()
+ iproute2._cache_fill_done = True
+ return linkCache.get_attr(attrlist)
+ if not refresh:
+ return linkCache.get_attr(attrlist)
+ if type == 'link':
+ self._link_fill(attrlist[0], refresh)
+ elif type == 'addr':
+ self._addr_fill(attrlist[0], refresh)
+ else:
+ self._link_fill(attrlist[0], refresh)
+ self._addr_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, type, attrlist, value, refresh=False):
+ try:
+ attrvalue = self._cache_get(type, 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:
+ linkCache.add_attr(attrlist, value)
+ except:
+ pass
+
+ def _cache_delete(self, attrlist):
+ if self.DRYRUN: return
+ try:
+ linkCache.del_attr(attrlist)
+ except:
+ pass
+
+ def _cache_invalidate(self):
+ linkCache.invalidate()
+
+ def batch_start(self):
+ self.ipbatcbuf = ''
+ self.ipbatch = True
+ self.ipbatch_pause = False
+
+ def add_to_batch(self, cmd):
+ self.ipbatchbuf += cmd + '\n'
+
+ def batch_pause(self):
+ self.ipbatch_pause = True
+
+ def batch_resume(self):
+ self.ipbatch_pause = False
+
+ def batch_commit(self):
+ if not self.ipbatchbuf:
+ return
+ try:
+ self.exec_command_talk_stdin('ip -force -batch -',
+ stdinbuf=self.ipbatchbuf)
+ except Exception:
+ raise
+ finally:
+ self.ipbatchbuf = ''
+ self.ipbatch = False
+ self.ipbatch_pause = False
+
+ def addr_show(self, ifacename=None):
+ if ifacename:
+ if not self.link_exists(ifacename):
+ return
+ return self.exec_commandl(['ip','-o', 'addr', 'show', 'dev',
+ '%s' %ifacename])
+ else:
+ return self.exec_commandl(['ip', '-o', 'addr', 'show'])
+
+ def link_show(self, ifacename=None):
+ if ifacename:
+ return self.exec_commandl(['ip', '-o', '-d', 'link',
+ 'show', 'dev', '%s' %ifacename])
+ else:
+ return self.exec_commandl(['ip', '-o', '-d', 'link', 'show'])
+
+ def addr_add(self, ifacename, address, broadcast=None,
+ peer=None, scope=None, preferred_lifetime=None):
+ if not address:
+ return
+ cmd = 'addr add %s' %address
+ if broadcast:
+ cmd += ' broadcast %s' %broadcast
+ if peer:
+ cmd += ' peer %s' %peer
+ if scope:
+ cmd += ' scope %s' %scope
+ if preferred_lifetime:
+ cmd += ' preferred_lft %s' %preferred_lifetime
+ cmd += ' dev %s' %ifacename
+ if self.ipbatch and not self.ipbatch_pause:
+ self.add_to_batch(cmd)
+ else:
+ self.exec_command('ip ' + cmd)
+ self._cache_update([ifacename, 'addrs', address], {})
+
+ def addr_del(self, ifacename, address, broadcast=None,
+ peer=None, scope=None):
+ """ Delete ipv4 address """
+ if not address:
+ return
+ if not self._cache_get('addr', [ifacename, 'addrs', address]):
+ return
+ cmd = 'addr del %s' %address
+ if broadcast:
+ cmd += 'broadcast %s' %broadcast
+ if peer:
+ cmd += 'peer %s' %peer
+ if scope:
+ cmd += 'scope %s' %scope
+ cmd += ' dev %s' %ifacename
+ self.exec_command('ip ' + cmd)
+ self._cache_delete([ifacename, 'addrs', address])
+
+ def addr_flush(self, ifacename):
+ cmd = 'addr flush dev %s' %ifacename
+ if self.ipbatch and not self.ipbatch_pause:
+ self.add_to_batch(cmd)
+ else:
+ self.exec_command('ip ' + cmd)
+ self._cache_delete([ifacename, 'addrs'])
+
+ def del_addr_all(self, ifacename, skip_addrs=[]):
+ if not skip_addrs: skip_addrs = []
+ runningaddrsdict = self.addr_get(ifacename)
+ try:
+ # XXX: ignore errors. Fix this to delete secondary addresses
+ # first
+ [self.addr_del(ifacename, a) for a in
+ set(runningaddrsdict.keys()).difference(skip_addrs)]
+ except:
+ # ignore errors
+ pass
+
+ def addr_get(self, ifacename, details=True):
+ addrs = self._cache_get('addr', [ifacename, 'addrs'])
+ if not addrs:
+ return None
+ if details:
+ return addrs
+ return addrs.keys()
+
+ def addr_add_multiple(self, ifacename, addrs, purge_existing=False):
+ # purges address
+ if purge_existing:
+ # if perfmode is not set and also if iface has no sibling
+ # objects, purge addresses that are not present in the new
+ # config
+ runningaddrs = self.addr_get(ifacename, details=False)
+ if addrs == runningaddrs:
+ return
+ try:
+ # if primary address is not same, there is no need to keep any.
+ # reset all addresses
+ if (addrs and runningaddrs and
+ (addrs[0] != runningaddrs[0])):
+ self.del_addr_all(ifacename)
+ else:
+ self.del_addr_all(ifacename, addrs)
+ except Exception, e:
+ self.log_warn(str(e))
+ for a in addrs:
+ try:
+ self.addr_add(ifacename, a)
+ except Exception, e:
+ self.logger.error(str(e))
+
+ def _link_set_ifflag(self, ifacename, value):
+ # Dont look at the cache, the cache may have stale value
+ # because link status can be changed by external
+ # entity (One such entity is ifupdown main program)
+ cmd = 'link set dev %s %s' %(ifacename, value.lower())
+ if self.ipbatch:
+ self.add_to_batch(cmd)
+ else:
+ self.exec_command('ip ' + cmd)
+
+ def link_up(self, ifacename):
+ self._link_set_ifflag(ifacename, 'UP')
+
+ def link_down(self, ifacename):
+ self._link_set_ifflag(ifacename, 'DOWN')
+
+ def link_set(self, ifacename, key, value=None, force=False):
+ if not force:
+ if (key not in ['master', 'nomaster'] and
+ self._cache_check('link', [ifacename, key], value)):
+ return
+ cmd = 'link set dev %s %s' %(ifacename, key)
+ if value:
+ cmd += ' %s' %value
+ if self.ipbatch:
+ self.add_to_batch(cmd)
+ else:
+ self.exec_command('ip ' + cmd)
+ if key not in ['master', 'nomaster']:
+ self._cache_update([ifacename, key], value)
+
+ def link_set_hwaddress(self, ifacename, hwaddress, force=False):
+ if not force:
+ if self._cache_check('link', [ifacename, 'hwaddress'], hwaddress):
+ return
+ self.link_down(ifacename)
+ cmd = 'link set dev %s address %s' %(ifacename, hwaddress)
+ if self.ipbatch:
+ self.add_to_batch(cmd)
+ else:
+ self.exec_command('ip ' + cmd)
+ self.link_up(ifacename)
+ self._cache_update([ifacename, 'hwaddress'], hwaddress)
+
+ def link_set_alias(self, ifacename, alias):
+ self.exec_commandl(['ip', 'link', 'set', 'dev',
+ ifacename, 'alias', alias])
+
+ def link_get_alias(self, ifacename):
+ return self.read_file_oneline('/sys/class/net/%s/ifalias'
+ %ifacename)
+
+ def link_isloopback(self, ifacename):
+ flags = self._cache_get('link', [ifacename, 'flags'])
+ if not flags:
+ return
+ if 'LOOPBACK' in flags:
+ return True
+ return False
+
+ def link_get_status(self, ifacename):
+ return self._cache_get('link', [ifacename, 'ifflag'], refresh=True)
+
+ def route_add_gateway(self, ifacename, gateway, metric=None):
+ if not gateway:
+ return
+ cmd = 'ip route add default via %s' %gateway
+ # Add metric
+ if metric:
+ cmd += 'metric %s' %metric
+ cmd += ' dev %s' %ifacename
+ self.exec_command(cmd)
+
+ def route_del_gateway(self, ifacename, gateway, metric=None):
+ # delete default gw
+ if not gateway:
+ return
+ cmd = 'ip route del default via %s' %gateway
+ if metric:
+ cmd += ' metric %s' %metric
+ cmd += ' dev %s' %ifacename
+ self.exec_command(cmd)
+
+ def route6_add_gateway(self, ifacename, gateway):
+ if not gateway:
+ return
+ return self.exec_command('ip -6 route add default via %s' %gateway +
+ ' dev %s' %ifacename)
+
+ def route6_del_gateway(self, ifacename, gateway):
+ if not gateway:
+ return
+ return self.exec_command('ip -6 route del default via %s' %gateway +
+ 'dev %s' %ifacename)
+
+ def link_create_vlan(self, vlan_device_name, vlan_raw_device, vlanid):
+ if self.link_exists(vlan_device_name):
+ return
+ self.exec_command('ip link add link %s' %vlan_raw_device +
+ ' name %s' %vlan_device_name +
+ ' type vlan id %d' %vlanid)
+ self._cache_update([vlan_device_name], {})
+
+ def link_create_vlan_from_name(self, vlan_device_name):
+ v = vlan_device_name.split('.')
+ if len(v) != 2:
+ self.logger.warn('invalid vlan device name %s' %vlan_device_name)
+ return
+ self.link_create_vlan(vlan_device_name, v[0], v[1])
+
+ def link_create_macvlan(self, name, linkdev, mode='private'):
+ if self.link_exists(name):
+ return
+ cmd = ('link add link %s' %linkdev +
+ ' name %s' %name +
+ ' type macvlan mode %s' %mode)
+ if self.ipbatch and not self.ipbatch_pause:
+ self.add_to_batch(cmd)
+ else:
+ self.exec_command('ip %s' %cmd)
+ self._cache_update([name], {})
+
+ def link_create_vxlan(self, name, vxlanid,
+ localtunnelip=None,
+ svcnodeips=None,
+ peernodeips=None,
+ learning='on'):
+ if svcnodeips and peernodeips:
+ raise Exception("svcnodeip and peernodeip is mutually exclusive")
+ args = ''
+ if localtunnelip:
+ args += ' local %s' %localtunnelip
+ if svcnodeips:
+ for s in svcnodeips:
+ args += ' svcnode %s' %s
+ if peernodeips:
+ for s in peernodeips:
+ args += ' peernode %s' %s
+ if learning == 'off':
+ args += ' nolearning'
+
+ if self.link_exists(name):
+ cmd = 'link set dev %s type vxlan ' %(name)
+ else:
+ cmd = 'link add dev %s type vxlan id %s' %(name, vxlanid)
+ cmd += args
+
+ if self.ipbatch and not self.ipbatch_pause:
+ self.add_to_batch(cmd)
+ else:
+ self.exec_command('ip %s' %cmd)
+ # XXX: update linkinfo correctly
+ self._cache_update([name], {})
+
+ def link_exists(self, ifacename):
+ if self.DRYRUN:
+ return True
+ return os.path.exists('/sys/class/net/%s' %ifacename)
+
+ def is_vlan_device_by_name(self, ifacename):
+ if re.search(r'\.', ifacename):
+ return True
+ return False
+
+ def route_add(self, route):
+ self.exec_command('ip route add ' + route)
+
+ def route6_add(self, route):
+ self.exec_command('ip -6 route add ' + route)
+
+ def get_vlandev_attrs(self, ifacename):
+ return (self._cache_get('link', [ifacename, 'linkinfo', 'link']),
+ self._cache_get('link', [ifacename, 'linkinfo', 'vlanid']))
+
+ def get_vxlandev_attrs(self, ifacename):
+ return self._cache_get('link', [ifacename, 'linkinfo'])
+
+ def link_get_mtu(self, ifacename):
+ return self._cache_get('link', [ifacename, 'mtu'])
+
+ def link_get_hwaddress(self, ifacename):
+ address = self._cache_get('link', [ifacename, 'hwaddress'])
+ # newly created logical interface addresses dont end up in the cache
+ # read hwaddress from sysfs file for these interfaces
+ if not address:
+ address = self.read_file_oneline('/sys/class/net/%s/address'
+ %ifacename)
+ return address
+
+ def link_create(self, ifacename, type, link=None):
+ if self.link_exists(ifacename):
+ return
+ cmd = 'link add'
+ if link:
+ cmd += ' link %s' %link
+ cmd += ' name %s type %s' %(ifacename, type)
+ if self.ipbatch and not self.ipbatch_pause:
+ self.add_to_batch(cmd)
+ else:
+ self.exec_command('ip %s' %cmd)
+ self._cache_update([ifacename], {})
+
+ def link_delete(self, ifacename):
+ if not self.link_exists(ifacename):
+ return
+ cmd = 'link del %s' %ifacename
+ if self.ipbatch and not self.ipbatch_pause:
+ self.add_to_batch(cmd)
+ else:
+ self.exec_command('ip %s' %cmd)
+ self._cache_invalidate()
+
+ def bridge_port_vids_add(self, bridgeportname, vids):
+ [self.exec_command('bridge vlan add vid %s dev %s'
+ %(v, bridgeportname)) for v in vids]
+
+ def bridge_port_vids_del(self, bridgeportname, vids):
+ if not vids:
+ return
+ [self.exec_command('bridge vlan del vid %s dev %s'
+ %(v, bridgeportname)) for v in vids]
+
+ def bridge_port_vids_flush(self, bridgeportname):
+ self.exec_command('bridge vlan del vid %s dev %s'
+ %(vid, bridgeportname))
+
+ def bridge_port_vids_get(self, bridgeportname):
+ self.exec_command('/bin/bridge vlan show %s' %bridgeportname)
+ bridgeout = self.exec_command('/bin/bridge vlan show dev %s'
+ %bridgeportname)
+ if not bridgeout: return []
+ brvlanlines = bridgeout.readlines()[2:]
+ vids = [l.strip() for l in brvlanlines]
+ return [vid for v in vids if vid]
+
+ def bridge_port_vids_get_all(self):
+ brvlaninfo = {}
+ bridgeout = self.exec_command('/bin/bridge vlan show')
+ if not bridgeout: return brvlaninfo
+ brvlanlines = bridgeout.splitlines()
+ brportname=None
+ for l in brvlanlines[1:]:
+ if l and l[0] not in [' ', '\t']:
+ brportname = None
+ l=l.strip()
+ if not l:
+ brportname=None
+ continue
+ if 'PVID' in l:
+ attrs = l.split()
+ brportname = attrs[0]
+ brvlaninfo[brportname] = {'pvid' : attrs[1],
+ 'vlan' : []}
+ elif brportname:
+ if 'Egress Untagged' not in l:
+ brvlaninfo[brportname]['vlan'].append(l)
+ elif not brportname:
+ attrs = l.split()
+ if attrs[1] == 'None' or 'Egress Untagged' in attrs[1]:
+ continue
+ brportname = attrs[0]
+ brvlaninfo[brportname] = {'vlan' : [attrs[1]]}
+ return brvlaninfo
+
+ def bridge_port_pvid_add(self, bridgeportname, pvid):
+ self.exec_command('bridge vlan add vid %s untagged pvid dev %s'
+ %(pvid, bridgeportname))
+
+ def bridge_port_pvid_del(self, bridgeportname, pvid):
+ self.exec_command('bridge vlan del vid %s untagged pvid dev %s'
+ %(pvid, bridgeportname))
+
+ def bridge_port_pvids_get(self, bridgeportname):
+ return self.read_file_oneline('/sys/class/net/%s/brport/pvid'
+ %bridgeportname)
+
+ def bridge_vids_add(self, bridgeportname, vids, bridge=True):
+ target = 'self' if bridge else ''
+ [self.exec_command('bridge vlan add vid %s dev %s %s'
+ %(v, bridgeportname, target)) for v in vids]
+
+ def bridge_vids_del(self, bridgeportname, vids, bridge=True):
+ target = 'self' if bridge else ''
+ [self.exec_command('bridge vlan del vid %s dev %s %s'
+ %(v, bridgeportname, target)) for v in vids]
+
+ def bridge_fdb_add(self, dev, address, vlan=None, bridge=True):
+ target = 'self' if bridge else ''
+ if vlan:
+ self.exec_command('bridge fdb replace %s dev %s vlan %s %s'
+ %(address, dev, vlan, target))
+ else:
+ self.exec_command('bridge fdb replace %s dev %s %s'
+ %(address, dev, target))
+
+ def bridge_fdb_del(self, dev, address, vlan=None, bridge=True):
+ target = 'self' if bridge else ''
+ if vlan:
+ self.exec_command('bridge fdb del %s dev %s vlan %s %s'
+ %(address, dev, vlan, target))
+ else:
+ self.exec_command('bridge fdb del %s dev %s %s'
+ %(address, dev, target))
+
+ def bridge_is_vlan_aware(self, bridgename):
+ filename = '/sys/class/net/%s/bridge/vlan_filtering' %bridgename
+ if os.path.exists(filename) and self.read_file_oneline(filename) == '1':
+ return True
+ return False
+
+ def bridge_port_get_bridge_name(self, bridgeport):
+ filename = '/sys/class/net/%s/brport/bridge' %bridgeport
+ try:
+ return os.path.basename(os.readlink(filename))
+ except:
+ return None
+
+ def bridge_port_exists(self, bridge, bridgeportname):
+ try:
+ return os.path.exists('/sys/class/net/%s/brif/%s'
+ %(bridge, bridgeportname))
+ except Exception:
+ return False
+
+ def bridge_fdb_show_dev(self, dev):
+ try:
+ fdbs = {}
+ output = self.exec_command('bridge fdb show dev %s' %dev)
+ if output:
+ for fdb_entry in output.splitlines():
+ try:
+ entries = fdb_entry.split()
+ fdbs.setdefault(entries[2], []).append(entries[0])
+ except:
+ self.logger.debug('%s: invalid fdb line \'%s\''
+ %(dev, fdb_entry))
+ pass
+ return fdbs
+ except Exception:
+ return None
+
+ def is_bridge(self, bridge):
+ return os.path.exists('/sys/class/net/%s/bridge' %bridge)
+
+ def is_link_up(self, ifacename):
+ ret = False
+ try:
+ flags = self.read_file_oneline('/sys/class/net/%s/flags' %ifacename)
+ iflags = int(flags, 16)
+ if (iflags & 0x0001):
+ ret = True
+ except:
+ ret = False
+ pass
+ return ret
+
+ def ip_route_get_dev(self, prefix):
+ try:
+ output = self.exec_command('ip route get %s' %prefix)
+ if output:
+ rline = output.splitlines()[0]
+ if rline:
+ rattrs = rline.split()
+ return rattrs[rattrs.index('dev') + 1]
+ except Exception, e:
+ self.logger.debug('ip_route_get_dev: failed .. %s' %str(e))
+ pass
+ return None
--- /dev/null
+#!/usr/bin/python
+#
+# Copyright 2014 Cumulus Networks, Inc. All rights reserved.
+# Author: Roopa Prabhu, roopa@cumulusnetworks.com
+#
+
+import os
+import re
+import io
+import logging
+import subprocess
+import traceback
+from ifupdown.iface import *
+#from ifupdownaddons.iproute2 import *
+#from ifupdownaddons.dhclient import *
+#from ifupdownaddons.bridgeutils import *
+#from ifupdownaddons.mstpctlutil import *
+#from ifupdownaddons.ifenslaveutil import *
+
+class moduleBase(object):
+ """ Base class for ifupdown addon modules
+
+ Provides common infrastructure methods for all addon modules """
+
+ def __init__(self, *args, **kargs):
+ modulename = self.__class__.__name__
+ self.logger = logging.getLogger('ifupdown.' + modulename)
+ self.FORCE = kargs.get('force', False)
+ """force interface configuration"""
+ self.DRYRUN = kargs.get('dryrun', False)
+ """only predend you are applying configuration, dont really do it"""
+ self.NOWAIT = kargs.get('nowait', False)
+ self.PERFMODE = kargs.get('perfmode', False)
+ self.CACHE = kargs.get('cache', False)
+ self.CACHE_FLAGS = kargs.get('cacheflags', 0x0)
+
+ def log_warn(self, str):
+ """ log a warning if err str is not one of which we should ignore """
+ if not self.ignore_error(str):
+ if self.logger.getEffectiveLevel() == logging.DEBUG:
+ traceback.print_stack()
+ self.logger.warn(str)
+ pass
+
+ def log_error(self, str):
+ """ log an err if err str is not one of which we should ignore and raise an exception """
+ if not self.ignore_error(str):
+ if self.logger.getEffectiveLevel() == logging.DEBUG:
+ traceback.print_stack()
+ raise Exception(str)
+ else:
+ pass
+
+ def is_process_running(self, procName):
+ try:
+ self.exec_command('/bin/pidof -x %s' % procName)
+ except:
+ return False
+ else:
+ return True
+
+ def exec_command(self, cmd, cmdenv=None):
+ """ execute command passed as argument.
+
+ Args:
+ cmd (str): command to execute
+
+ Kwargs:
+ cmdenv (dict): environment variable name value pairs
+ """
+ cmd_returncode = 0
+ cmdout = ''
+
+ try:
+ self.logger.info('Executing ' + cmd)
+ if self.DRYRUN:
+ return cmdout
+ ch = subprocess.Popen(cmd.split(),
+ stdout=subprocess.PIPE,
+ shell=False, env=cmdenv,
+ stderr=subprocess.STDOUT,
+ close_fds=True)
+ cmdout = ch.communicate()[0]
+ cmd_returncode = ch.wait()
+ except OSError, e:
+ raise Exception('could not execute ' + cmd +
+ '(' + str(e) + ')')
+ if cmd_returncode != 0:
+ raise Exception('error executing cmd \'%s\'' %cmd +
+ '(' + cmdout.strip('\n ') + ')')
+ return cmdout
+
+ def exec_command_talk_stdin(self, cmd, stdinbuf):
+ """ execute command passed as argument and write contents of stdinbuf
+ into stdin of the cmd
+
+ Args:
+ cmd (str): command to execute
+ stdinbuf (str): string to write to stdin of the cmd process
+ """
+ cmd_returncode = 0
+ cmdout = ''
+
+ try:
+ self.logger.info('Executing %s (stdin=%s)' %(cmd, stdinbuf))
+ if self.DRYRUN:
+ return cmdout
+ ch = subprocess.Popen(cmd.split(),
+ stdout=subprocess.PIPE,
+ stdin=subprocess.PIPE,
+ shell=False, env=cmdenv,
+ stderr=subprocess.STDOUT,
+ close_fds=True)
+ cmdout = ch.communicate(input=stdinbuf)[0]
+ cmd_returncode = ch.wait()
+ except OSError, e:
+ raise Exception('could not execute ' + cmd +
+ '(' + str(e) + ')')
+ if cmd_returncode != 0:
+ raise Exception('error executing cmd \'%s (%s)\''
+ %(cmd, stdinbuf) + '(' + cmdout.strip('\n ') + ')')
+ return cmdout
+
+ def get_ifaces_from_proc(self):
+ ifacenames = []
+ with open('/proc/net/dev') as f:
+ try:
+ lines = f.readlines()
+ for line in lines:
+ ifacenames.append(line.split()[0].strip(': '))
+ except:
+ raise
+ return ifacenames
+
+ def parse_regex(self, expr, ifacenames=None):
+ try:
+ proc_ifacenames = self.get_ifaces_from_proc()
+ except:
+ self.logger.warn('error reading ifaces from proc')
+ for proc_ifacename in proc_ifacenames:
+ if re.search(expr + '$', proc_ifacename):
+ yield proc_ifacename
+ if not ifacenames:
+ return
+ for ifacename in ifacenames:
+ if re.search(expr + '$', ifacename):
+ yield ifacename
+
+ def parse_glob(self, expr):
+ errmsg = ('error parsing glob expression \'%s\'' %expr +
+ ' (supported glob syntax: swp1-10 or swp[1-10])')
+ start_index = 0
+ end_index = 0
+ try:
+ regexs = [re.compile(r"([A-Za-z0-9\-]+[A-Za-z])(\d+)\-(\d+)(.*)"),
+ re.compile(r"([A-Za-z0-9\-]+)\[(\d+)\-(\d+)\](.*)")]
+ for r in regexs:
+ m = r.match(expr)
+ if not m:
+ continue
+ mlist = m.groups()
+ if len(mlist) != 4:
+ raise Exception(errmsg + '(unexpected len)')
+ prefix = mlist[0]
+ suffix = mlist[3]
+ start_index = int(mlist[1])
+ end_index = int(mlist[2])
+ except:
+ self.logger.warn(errmsg)
+ pass
+ if not start_index and not end_index:
+ self.logger.warn(errmsg)
+ yield expr
+ else:
+ for i in range(start_index, end_index + 1):
+ yield prefix + '%d' %i + suffix
+
+ def parse_port_list(self, port_expr, ifacenames=None):
+ """ parse port list containing glob and regex
+
+ Args:
+ port_expr (str): expression
+ ifacenames (list): list of interface names. This needs to be specified if the expression has a regular expression
+ """
+ regex = 0
+ glob = 0
+ portlist = []
+
+ if not port_expr:
+ return None
+ for expr in re.split(r'[\s\t]\s*', port_expr):
+ if expr == 'noregex':
+ regex = 0
+ elif expr == 'noglob':
+ glob = 0
+ elif expr == 'regex':
+ regex = 1
+ elif expr == 'glob':
+ glob = 1
+ elif regex:
+ for port in self.parse_regex(expr, ifacenames):
+ if port not in portlist:
+ portlist.append(port)
+ regex = 0
+ elif glob:
+ for port in self.parse_glob(expr):
+ portlist.append(port)
+ glob = 0
+ else:
+ portlist.append(expr)
+ if not portlist:
+ return None
+ return portlist
+
+ def ignore_error(self, errmsg):
+ if (self.FORCE or re.search(r'exists', errmsg,
+ re.IGNORECASE | re.MULTILINE)):
+ return True
+ return False
+
+ def write_file(self, filename, strexpr):
+ """ writes string to a file """
+ try:
+ self.logger.info('writing \'%s\'' %strexpr +
+ ' to file %s' %filename)
+ if self.DRYRUN:
+ return 0
+ with open(filename, 'w') as f:
+ f.write(strexpr)
+ except IOError, e:
+ self.logger.warn('error writing to file %s'
+ %filename + '(' + str(e) + ')')
+ return -1
+ return 0
+
+ def read_file(self, filename):
+ """ read file and return lines from the file """
+ try:
+ self.logger.info('reading \'%s\'' %filename)
+ with open(filename, 'r') as f:
+ return f.readlines()
+ except:
+ return None
+ return None
+
+ def read_file_oneline(self, filename):
+ """ reads and returns first line from the file """
+ try:
+ self.logger.info('reading \'%s\'' %filename)
+ with open(filename, 'r') as f:
+ return f.readline().strip('\n')
+ except:
+ return None
+ return None
+
+ def sysctl_set(self, variable, value):
+ """ set sysctl variable to value passed as argument """
+ self.exec_command('sysctl %s=' %variable + '%s' %value)
+
+ def sysctl_get(self, variable):
+ """ get value of sysctl variable """
+ return self.exec_command('sysctl %s' %variable).split('=')[1].strip()
+
+ def set_iface_attr(self, ifaceobj, attr_name, attr_valsetfunc,
+ prehook=None, prehookargs=None):
+ ifacename = ifaceobj.name
+ attrvalue = ifaceobj.get_attr_value_first(attr_name)
+ if attrvalue:
+ if prehook:
+ if prehookargs:
+ prehook(prehookargs)
+ else:
+ prehook(ifacename)
+ attr_valsetfunc(ifacename, attrvalue)
+
+ def query_n_update_ifaceobjcurr_attr(self, ifaceobj, ifaceobjcurr,
+ attr_name, attr_valgetfunc,
+ attr_valgetextraarg=None):
+ attrvalue = ifaceobj.get_attr_value_first(attr_name)
+ if not attrvalue:
+ return
+ if attr_valgetextraarg:
+ runningattrvalue = attr_valgetfunc(ifaceobj.name,
+ attr_valgetextraarg)
+ else:
+ runningattrvalue = attr_valgetfunc(ifaceobj.name)
+ if (not runningattrvalue or
+ (runningattrvalue != attrvalue)):
+ ifaceobjcurr.update_config_with_status(attr_name,
+ runningattrvalue, 1)
+ else:
+ ifaceobjcurr.update_config_with_status(attr_name,
+ runningattrvalue, 0)
+
+ def dict_key_subset(self, a, b):
+ """ returns a list of differing keys """
+ return [x for x in a if x in b]
+
+ def get_mod_attrs(self):
+ """ returns list of all module attrs defined in the module _modinfo dict"""
+ try:
+ return self._modinfo.get('attrs').keys()
+ except:
+ return None
+
+ def get_mod_attr(self, attrname):
+ """ returns module attr info """
+ try:
+ return self._modinfo.get('attrs', {}).get(attrname)
+ except:
+ return None
+
+ def get_mod_subattr(self, attrname, subattrname):
+ """ returns module attrs defined in the module _modinfo dict"""
+ try:
+ return reduce(lambda d, k: d[k], ['attrs', attrname, subattrname],
+ self._modinfo)
+ except:
+ return None
+
+ def get_modinfo(self):
+ """ return module info """
+ try:
+ return self._modinfo
+ except:
+ return None
+
+ def get_flags(self):
+ return dict(force=self.FORCE, dryrun=self.DRYRUN, nowait=self.NOWAIT,
+ perfmode=self.PERFMODE, cache=self.CACHE,
+ cacheflags=self.CACHE_FLAGS)
+
+ def _get_reserved_vlan_range(self):
+ start = end = 0
+ get_resvvlan = '/usr/share/python-ifupdown2/get_reserved_vlan_range.sh'
+ if not os.path.exists(get_resvvlan):
+ return (start, end)
+ try:
+ (s, e) = self.exec_command(get_resvvlan).strip('\n').split('-')
+ start = int(s)
+ end = int(e)
+ except Exception, e:
+ self.logger.debug('%s failed (%s)' %(get_resvvlan, str(e)))
+ # ignore errors
+ pass
+ return (start, end)
+
+ def _handle_reserved_vlan(self, vlanid, logprefix=''):
+ """ Helper function to check and warn if the vlanid falls in the
+ reserved vlan range """
+ if vlanid in range(self._resv_vlan_range[0],
+ self._resv_vlan_range[1]):
+ self.logger.error('%s: reserved vlan %d being used'
+ %(logprefix, vlanid) + ' (reserved vlan range %d-%d)'
+ %(self._resv_vlan_range[0], self._resv_vlan_range[1]))
+ return True
+ return False
+
+ def _valid_ethaddr(self, ethaddr):
+ """ Check if address is 00:00:00:00:00:00 """
+ if not ethaddr or re.match('00:00:00:00:00:00', ethaddr):
+ return False
+ return True
--- /dev/null
+#!/usr/bin/python
+#
+# Copyright 2014 Cumulus Networks, Inc. All rights reserved.
+# Author: Roopa Prabhu, roopa@cumulusnetworks.com
+#
+
+from utilsbase import *
+from ifupdown.iface import *
+from cache import *
+import re
+
+class mstpctlutil(utilsBase):
+ """ This class contains helper methods to interact with mstpd using
+ mstputils commands """
+
+ _cache_fill_done = False
+
+ _bridgeattrmap = {'bridgeid' : 'bridge-id',
+ 'maxage' : 'max-age',
+ 'fdelay' : 'forward-delay',
+ 'txholdcount' : 'tx-hold-count',
+ 'maxhops' : 'max-hops',
+ 'ageing' : 'ageing-time',
+ 'hello' : 'hello-time',
+ 'forcevers' : 'force-protocol-version'}
+
+ _bridgeportattrmap = {'portadminedge' : 'admin-edge-port',
+ 'portp2p' : 'admin-point-to-point',
+ 'portrestrrole' : 'restricted-role',
+ 'portrestrtcn' : 'restricted-TCN',
+ 'bpduguard' : 'bpdu-guard-port',
+ 'portautoedge' : 'auto-edge-port',
+ 'portnetwork' : 'network-port',
+ 'portbpdufilter' : 'bpdufilter-port'}
+
+ def __init__(self, *args, **kargs):
+ utilsBase.__init__(self, *args, **kargs)
+
+ def is_mstpd_running(self):
+ try:
+ self.exec_command('/bin/pidof mstpd')
+ except:
+ return False
+ else:
+ return True
+
+ def get_bridgeport_attr(self, bridgename, portname, attrname):
+ try:
+ return self.subprocess_check_output(['/sbin/mstpctl',
+ 'showportdetail', '%s' %bridgename, '%s' %portname,
+ self._bridgeportattrmap[attrname]]).strip('\n')
+ except Exception, e:
+ pass
+ return None
+
+ def get_bridgeport_attrs(self, bridgename, portname):
+ bridgeattrs = {}
+ try:
+ bridgeattrs = dict((k, self.get_bridgeport_attr(bridgename, v))
+ for k, v in self._bridgeattrmap.items())
+ bridgeattrs['treeprio'] = int(bridgeattrs.get('bridgeid',
+ '').split('.')[0], base=16) * 4096
+ except Exception, e:
+ self.logger.warn(str(e))
+ pass
+ return bridgeattrs
+
+ def set_bridgeport_attrs(self, bridgename, bridgeportname, attrdict,
+ check=True):
+ for k, v in attrdict.iteritems():
+ if not v:
+ continue
+ try:
+ self.set_bridgeport_attr(self, bridgename, bridgeportname,
+ k, v, check)
+ except Exception, e:
+ self.logger.warn(str(e))
+
+ def set_bridgeport_attr(self, bridgename, bridgeportname, attrname,
+ attrvalue, check=True):
+ if check:
+ attrvalue_curr = self.get_bridgeport_attr(bridgename,
+ bridgeportname, attrname)
+ if attrvalue_curr and attrvalue_curr == attrvalue:
+ return
+ if attrname == 'treeportcost' or attrname == 'treeportprio':
+ self.subprocess_check_output(['/sbin/mstpctl', 'set%s' %attrname,
+ '%s' %bridgename, '%s' %bridgeportname, '0', '%s' %attrvalue])
+ else:
+ self.subprocess_check_output(['/sbin/mstpctl', 'set%s' %attrname,
+ '%s' %bridgename, '%s' %bridgeportname, '%s' %attrvalue])
+
+ def get_bridge_attrs(self, bridgename):
+ bridgeattrs = {}
+ try:
+ bridgeattrs = dict((k, self.get_bridge_attr(bridgename, k))
+ for k in self._bridgeattrmap.keys())
+ bridgeattrs['treeprio'] = '%d' %(int(bridgeattrs.get('bridgeid',
+ '').split('.')[0], base=16) * 4096)
+ del bridgeattrs['bridgeid']
+ except Exception, e:
+ self.logger.debug(bridgeattrs)
+ self.logger.debug(str(e))
+ pass
+ return bridgeattrs
+
+ def get_bridge_attr(self, bridgename, attrname):
+ try:
+ return self.subprocess_check_output(['/sbin/mstpctl',
+ 'showbridge', '%s' %bridgename,
+ self._bridgeattrmap[attrname]]).strip('\n')
+ except Exception, e:
+ pass
+ return None
+
+ def set_bridge_attr(self, bridgename, attrname, attrvalue, check=True):
+
+ if check:
+ attrvalue_curr = self.get_bridge_attr(bridgename, attrname)
+ if attrvalue_curr and attrvalue_curr == attrvalue:
+ return
+ if attrname == 'treeprio':
+ self.subprocess_check_call(['/sbin/mstpctl', 'set%s' %attrname,
+ '%s' %bridgename, '0', '%s' %attrvalue])
+ else:
+ self.subprocess_check_call(['/sbin/mstpctl', 'set%s' %attrname,
+ '%s' %bridgename, '%s' %attrvalue])
+
+ def set_bridge_attrs(self, bridgename, attrdict, check=True):
+ for k, v in attrdict.iteritems():
+ if not v:
+ continue
+ try:
+ self.set_bridge_attr(bridgename, k, v, check)
+ except Exception, e:
+ self.logger.warn('%s: %s' %(bridgename, str(e)))
+ pass
+
+ def get_bridge_treeprio(self, bridgename):
+ try:
+ bridgeid = subprocess.check_output(['/sbin/mstpctl',
+ 'showbridge', '%s' %bridgename,
+ self._bridgeattrmap['bridgeid']]).strip('\n')
+ return '%d' %(int(bridgeid.split('.')[0], base=16) * 4096)
+ except:
+ pass
+ return None
+
+ def set_bridge_treeprio(self, bridgename, attrvalue, check=True):
+ if check:
+ attrvalue_curr = self.get_bridge_treeprio(bridgename)
+ if attrvalue_curr and attrvalue_curr == attrvalue:
+ return
+ self.subprocess_check_output(['/sbin/mstpctl', 'settreeprio',
+ '%s' %bridgename, '0', '%s' %attrvalue])
+
+ def showbridge(self, bridgename=None):
+ if bridgename:
+ return self.exec_command('/sbin/mstpctl showbridge %s' %bridgename)
+ else:
+ return self.exec_command('/sbin/mstpctl showbridge')
+
+ def showportdetail(self, bridgename):
+ return self.exec_command('/sbin/mstpctl showportdetail %s' %bridgename)
+
+ def mstpbridge_exists(self, bridgename):
+ try:
+ subprocess.check_call('mstpctl showbridge %s' %bridgename)
+ return True
+ except:
+ return False
--- /dev/null
+#!/usr/bin/python
+#
+# Copyright 2014 Cumulus Networks, Inc. All rights reserved.
+# Author: Roopa Prabhu, roopa@cumulusnetworks.com
+#
+
+import logging
+import subprocess
+import re
+import io
+from ifupdown.iface import *
+from cache import *
+
+#import timeit
+import time
+import logging
+
+def profile(func):
+ def wrap(*args, **kwargs):
+ started_at = time.time()
+ result = func(*args, **kwargs)
+ print str(func)
+ print (time.time() - started_at)
+ return result
+ return wrap
+
+class utilsBase(object):
+ """ Base class for ifupdown addon utilities """
+
+ def __init__(self, *args, **kargs):
+ modulename = self.__class__.__name__
+ self.logger = logging.getLogger('ifupdown.' + modulename)
+ self.FORCE = kargs.get('force', False)
+ self.DRYRUN = kargs.get('dryrun', False)
+ self.NOWAIT = kargs.get('nowait', False)
+ self.PERFMODE = kargs.get('perfmode', False)
+ self.CACHE = kargs.get('cache', False)
+
+ def exec_commandl(self, cmdl, cmdenv=None):
+ """ Executes command """
+
+ cmd_returncode = 0
+ cmdout = ''
+ try:
+ self.logger.info('executing ' + ' '.join(cmdl))
+ if self.DRYRUN:
+ return cmdout
+ ch = subprocess.Popen(cmdl,
+ stdout=subprocess.PIPE,
+ shell=False, env=cmdenv,
+ stderr=subprocess.STDOUT,
+ close_fds=True)
+ cmdout = ch.communicate()[0]
+ cmd_returncode = ch.wait()
+ except OSError, e:
+ raise Exception('failed to execute cmd \'%s\' (%s)'
+ %(' '.join(cmdl), str(e)))
+ if cmd_returncode != 0:
+ raise Exception('failed to execute cmd \'%s\''
+ %' '.join(cmdl) + '(' + cmdout.strip('\n ') + ')')
+ return cmdout
+
+ def exec_command(self, cmd, cmdenv=None):
+ """ Executes command given as string in the argument cmd """
+
+ return self.exec_commandl(cmd.split(), cmdenv)
+
+ def exec_command_talk_stdin(self, cmd, stdinbuf):
+ """ Executes command and writes to stdin of the process """
+ cmd_returncode = 0
+ cmdout = ''
+ try:
+ self.logger.info('executing %s [%s]' %(cmd, stdinbuf))
+ if self.DRYRUN:
+ return cmdout
+ ch = subprocess.Popen(cmd.split(),
+ stdout=subprocess.PIPE,
+ stdin=subprocess.PIPE,
+ shell=False,
+ stderr=subprocess.STDOUT,
+ close_fds=True)
+ cmdout = ch.communicate(input=stdinbuf)[0]
+ cmd_returncode = ch.wait()
+ except OSError, e:
+ raise Exception('failed to execute cmd \'%s\' (%s)'
+ %(cmd, str(e)))
+ if cmd_returncode != 0:
+ raise Exception('failed to execute cmd \'%s [%s]\''
+ %(cmd, stdinbuf) + '(' + cmdout.strip('\n ') + ')')
+ return cmdout
+
+ def subprocess_check_output(self, cmdl):
+ self.logger.info('executing ' + ' '.join(cmdl))
+ if self.DRYRUN:
+ return
+ try:
+ return subprocess.check_output(cmdl, stderr=subprocess.STDOUT)
+ except Exception, e:
+ raise Exception('failed to execute cmd \'%s\' (%s)'
+ %(' '.join(cmdl), e.output))
+
+ def subprocess_check_call(self, cmdl):
+ """ subprocess check_call implementation using popen
+
+ Uses popen because it needs the close_fds argument
+ """
+
+ cmd_returncode = 0
+ try:
+ self.logger.info('executing ' + ' '.join(cmdl))
+ if self.DRYRUN:
+ return
+ ch = subprocess.Popen(cmdl,
+ stdout=None,
+ shell=False,
+ stderr=None,
+ close_fds=True)
+ cmd_returncode = ch.wait()
+ except Exception, e:
+ raise Exception('failed to execute cmd \'%s\' (%s)'
+ %(' '.join(cmdl), str(e)))
+ if cmd_returncode != 0:
+ raise Exception('failed to execute cmd \'%s\''
+ %' '.join(cmdl))
+ return
+
+ def write_file(self, filename, strexpr):
+ try:
+ self.logger.info('writing \'%s\'' %strexpr +
+ ' to file %s' %filename)
+ if self.DRYRUN:
+ return 0
+ with open(filename, 'w') as f:
+ f.write(strexpr)
+ except IOError, e:
+ self.logger.warn('error writing to file %s'
+ %filename + '(' + str(e) + ')')
+ return -1
+ return 0
+
+ def read_file(self, filename):
+ try:
+ self.logger.debug('reading \'%s\'' %filename)
+ with open(filename, 'r') as f:
+ return f.readlines()
+ except:
+ return None
+ return None
+
+ def read_file_oneline(self, filename):
+ try:
+ self.logger.debug('reading \'%s\'' %filename)
+ with open(filename, 'r') as f:
+ return f.readline().strip('\n')
+ except:
+ return None
+ return None
+
+ def sysctl_set(self, variable, value):
+ self.exec_command('sysctl %s=' %variable + '%s' %value)
+
+ def sysctl_get(self, variable):
+ return self.exec_command('sysctl %s' %variable).split('=')[1].strip()
. /lib/lsb/init-functions
CONFIGURE_INTERFACES=yes
-EXCLUDE_INTERFACES=
-REMOTE_SYSLOG_SERVER=
-VERBOSE=no
-verbose=
+EXTRA_ARGS=
[ -f /etc/default/networking ] && . /etc/default/networking
-[ "$VERBOSE" = yes ] && verbose=-v
+[ "$VERBOSE" = yes ] && EXTRA_ARGS=-v
+[ "$DEBUG" = yes ] && EXTRA_ARGS="$EXTRA_ARGS -d"
+[ "$SYSLOG" = yes ] && EXTRA_ARGS="$EXTRA_ARGS --syslog"
gen_examples() {
# Generate sample interfaces file. The interfaces files are
return
}
-is_bootup() {
- # Return 0 if its bootup or return 1
- [ -f /var/tmp/network/ifstatenew ] && return 1
-
- return 0
-}
-
perf_options() {
# At bootup lets set perfmode
- if is_bootup ; then
- echo -n "--perfmode"
- else
- echo -n ""
- fi
+ [ -f /var/tmp/network/ifstatenew ] && echo -n "" && return
+
+ echo -n "--perfmode"
}
process_exclusions() {
exclusions=$(process_exclusions)
perfoptions=$(perf_options)
log_action_begin_msg "Configuring network interfaces"
- if is_bootup ; then
- ifup -a $verbose $perfoptions 2>&1 | /usr/bin/logger \
- $REMOTE_SYSLOG_SERVER -s -i -t $SCRIPTNAME
- else
- ifup -a $verbose $perfoptions
- fi
+ ifup -a $EXTRA_ARGS $exclusions $perfoptions
log_action_end_msg $?
;;
ifupdown_init
check_network_file_systems
check_network_swap
+ exclusions=$(process_exclusions)
log_action_begin_msg "Deconfiguring network interfaces"
- if ifdown -a --exclude=lo $verbose; then
- log_action_end_msg $?
- else
- log_action_end_msg $?
- fi
+ ifdown -a $EXTRA_ARGS $exclusions
+ log_action_end_msg $?
;;
reload)
ifupdown_init
log_action_begin_msg "Reloading network interfaces configuration"
- if ifreload -a
- then
- log_action_end_msg $?
- else
- log_action_end_msg $?
- fi
+ ifreload -a $EXTRA_ARGS
+ log_action_end_msg $?
+ ;;
+
+reload-currently-up)
+
+ ifupdown_init
+ log_action_begin_msg "Reloading currently up network interfaces configuration"
+
+ ifreload --currently-up $EXTRA_ARGS
+ log_action_end_msg $?
;;
force-reload)
ifupdown_init
log_action_begin_msg "Reloading network interfaces configuration"
- if ifreload -a
- then
- log_action_end_msg $?
- else
- log_action_end_msg $?
- fi
+ ifreload -f -a $EXTRA_ARGS
+ log_action_end_msg $?
;;
restart)
ifupdown_init
- log_action_begin_msg "Reconfiguring network interfaces"
- ifdown -a --exclude=lo $verbose || true
set -f
exclusions=$(process_exclusions)
- if ifup -a --exclude=lo $verbose
- then
- log_action_end_msg $?
- else
- log_action_end_msg $?
- fi
+ log_action_begin_msg "Reconfiguring network interfaces"
+ ifdown -a $EXTRA_ARGS $exclusions || true
+ ifup -a $EXTRA_ARGS $exclusions
+ log_action_end_msg $?
;;
*)
-v, --verbose verbose
-d, --debug output debug info
-
- -l, --allow CLASS ignore non-"allow-CLASS" interfaces
+ --allow CLASS ignore non-"allow-CLASS" interfaces
-w, --with-depends run with all dependent interfaces. This option
is redundant when -a is specified. When '-a' is
Use interfaces file instead of default
/etc/network/interfaces
+ -t {native,json}, --interfaces-format {native,json}
+ interfaces file format
+
-r, --running print raw interfaces file entries
-c, --check check interface file contents against running state
-x, --raw print raw config file entries
-
-o {native,json}, --format {native,json}
interface display format
SYNOPSIS
========
- ifreload [-h] [-a] [-v] [-d] [-f] [-n]
+ ifreload [-h] (-a|-c) [-v] [-d] [-f] [-n]
DESCRIPTION
===========
but it skips **ifdown** for interfaces that did not change in the config
file.
+ If you do not wish to execute **down** on any interfaces, but only **up** on
+ interfaces that were already **up**, please see the **--currently-up**
+ option below.
+
OPTIONS
=======
-f, --force force run all operations
+ -c, --currently-up only reload auto and other interfaces that are
+ currently up. This can be used as a non-disruptive
+ alternative to -a because it will not down any
+ interfaces
+
EXAMPLES
========
# reload all auto interfaces in **interfaces(5)** file
**service networking reload**
+ # reload all currently up interfaces without bringing any interfaces down
+
+ **service networking reload-currently-up**
+
SEE ALSO
========
ifup(8),
-v, --verbose verbose
-d, --debug output debug info
-
- -l, --allow CLASS ignore non-"allow-CLASS" interfaces
+ --allow CLASS ignore non-"allow-CLASS" interfaces
-w, --with-depends run with all dependent interfaces. This option
is redundant when -a is specified. When '-a' is
Exclude interfaces from the list of interfaces to
operate on. Can be specified multiple times
+ -i INTERFACESFILE, --interfaces INTERFACESFILE
+ Use interfaces file instead of default
+ /etc/network/interfaces
+
+ -t {native,json}, --interfaces-format {native,json}
+ interfaces file format
+
-f, --force force run all operations
-n, --no-act print out what would happen, but don't do it
--- /dev/null
+==========================
+ifupdown-addons-interfaces
+==========================
+---------------------------------------------------------
+ifupdown2 addon modules interface configuration
+---------------------------------------------------------
+:Author: roopa@cumulusnetworks.com
+:Date: 2013-09-25
+:Copyright: Copyright 2013 Cumulus Networks, Inc. All rights reserved.
+:Version: 0.1
+:Manual section: 5
+
+
+DESCRIPTION
+===========
+ ifupdown2 addon modules add incremental functionality to
+ core ifupdown2 tool.
+
+ All installed addon modules are executed on every interface
+ listed in the interfaces file. Addon modules are installed under
+ /usr/share/ifupdownaddons. To see the list of active addon
+ modules, see ifaddon(8).
+
+ Addon modules add new attributes to the interfaces(5) file.
+ Below is a list of attribute options provided by each module.
+ These can be listed under each iface section in the interfaces(5)
+ file.
+
+
+EXAMPLES
+========
+ Listed below are addon modules and their supported attributes.
+ The attributes if applicable go under the iface section in the
+ interfaces(5) file.
+
+ **ethtool**: ethtool configuration module for interfaces
+
+
+ **link-duplex**
+
+ **help**: set link duplex
+
+
+ **required**: False
+
+ **default**: half
+
+ **validvals**: half,full
+
+ **example**:
+ link-duplex full
+
+
+ **link-autoneg**
+
+ **help**: set autonegotiation
+
+
+ **required**: False
+
+ **default**: off
+
+ **validvals**: on,off
+
+ **example**:
+ link-autoneg on
+
+
+ **link-speed**
+
+ **help**: set link speed
+
+
+ **required**: False
+
+ **example**:
+ link-speed 1000
+
+
+
+ **bridge**: bridge configuration module
+
+
+ **bridge-mcqifaddr**
+
+ **help**: set multicast query to use ifaddr
+
+
+ **required**: False
+
+ **default**: 0
+
+ **example**:
+ bridge-mcqifaddr 0
+
+
+ **bridge-gcint**
+
+ **help**: bridge garbage collection interval in secs
+
+
+ **required**: False
+
+ **default**: 4
+
+ **example**:
+ bridge-gcint 4
+
+
+ **bridge-mcsqc**
+
+ **help**: set multicast startup query count
+
+
+ **required**: False
+
+ **default**: 2
+
+ **example**:
+ bridge-mcsqc 2
+
+
+ **bridge-stp**
+
+ **help**: bridge-stp yes/no
+
+
+ **required**: False
+
+ **default**: no
+
+ **validvals**: yes,on,off,no
+
+ **example**:
+ bridge-stp no
+
+
+ **bridge-mcsqi**
+
+ **help**: set multicast startup query interval (in secs)
+
+
+ **required**: False
+
+ **default**: 31
+
+ **example**:
+ bridge-mcsqi 31
+
+
+ **bridge-mcmi**
+
+ **help**: set multicast membership interval (in secs)
+
+
+ **required**: False
+
+ **default**: 260
+
+ **example**:
+ bridge-mcmi 260
+
+
+ **bridge-ports**
+
+ **help**: bridge ports
+
+
+ **required**: True
+
+ **example**:
+ bridge-ports swp1.100 swp2.100 swp3.100
+
+ bridge-ports glob swp1-3.100
+
+ bridge-ports regex (swp[1|2|3].100)
+
+
+ **bridge-mcsnoop**
+
+ **help**: set multicast snooping
+
+
+ **required**: False
+
+ **default**: 1
+
+ **example**:
+ bridge-mcsnoop 1
+
+
+ **bridge-maxwait**
+
+ **help**: forces to time seconds the maximum time that the Deb
+ ian bridge setup scripts will wait for the bridge ports to ge
+ t to the forwarding status, doesn't allow factional part. If i
+ t is equal to 0 then no waiting is done
+
+
+ **required**: False
+
+ **default**: 0
+
+ **example**:
+ bridge-maxwait 3
+
+
+ **bridge-pathcosts**
+
+ **help**: bridge set port path costs
+
+
+ **required**: False
+
+ **default**: 100
+
+ **example**:
+ bridge-pathcosts swp1=100 swp2=100
+
+
+ **bridge-portprios**
+
+ **help**: bridge port prios
+
+
+ **required**: False
+
+ **default**: 32
+
+ **example**:
+ bridge-portprios swp1=32 swp2=32
+
+
+ **bridge-fd**
+
+ **help**: bridge forward delay
+
+
+ **required**: False
+
+ **default**: 15
+
+ **example**:
+ bridge-fd 15
+
+
+ **bridge-ageing**
+
+ **help**: bridge ageing
+
+
+ **required**: False
+
+ **default**: 300
+
+ **example**:
+ bridge-ageing 300
+
+
+ **bridge-hello**
+
+ **help**: bridge set hello time
+
+
+ **required**: False
+
+ **default**: 2
+
+ **example**:
+ bridge-hello 2
+
+
+ **bridge-mcquerier**
+
+ **help**: set multicast querier
+
+
+ **required**: False
+
+ **default**: 0
+
+ **example**:
+ bridge-mcquerier 0
+
+
+ **bridge-mclmc**
+
+ **help**: set multicast last member count
+
+
+ **required**: False
+
+ **default**: 2
+
+ **example**:
+ bridge-mclmc 2
+
+
+ **bridge-mcrouter**
+
+ **help**: set multicast router
+
+
+ **required**: False
+
+ **default**: 1
+
+ **example**:
+ bridge-mcrouter 1
+
+
+ **bridge-portmcrouter**
+
+ **help**: set port multicast routers
+
+
+ **required**: False
+
+ **default**: 1
+
+ **example**:
+ bridge-portmcrouter swp1=1 swp2=1
+
+
+ **bridge-mclmi**
+
+ **help**: set multicast last member interval (in secs)
+
+
+ **required**: False
+
+ **default**: 1
+
+ **example**:
+ bridge-mclmi 1
+
+
+ **bridge-hashmax**
+
+ **help**: set hash max
+
+
+ **required**: False
+
+ **default**: 4096
+
+ **example**:
+ bridge-hashmax 4096
+
+
+ **bridge-waitport**
+
+ **help**: wait for a max of time secs for the specified ports
+ to become available,if no ports are specified then those speci
+ fied on bridge-ports will be used here. Specifying no ports he
+ re should not be used if we are using regex or "all" on bridge
+ _ports,as it wouldnt work.
+
+
+ **required**: False
+
+ **default**: 0
+
+ **example**:
+ bridge-waitport 4
+
+
+ **bridge-mcqri**
+
+ **help**: set multicast query response interval (in secs)
+
+
+ **required**: False
+
+ **default**: 10
+
+ **example**:
+ bridge-mcqri 10
+
+
+ **bridge-hashel**
+
+ **help**: set hash elasticity
+
+
+ **required**: False
+
+ **default**: 4096
+
+ **example**:
+ bridge-hashel 4096
+
+
+ **bridge-mcqpi**
+
+ **help**: set multicast querier interval (in secs)
+
+
+ **required**: False
+
+ **default**: 255
+
+ **example**:
+ bridge-mcqpi 255
+
+
+ **bridge-bridgeprio**
+
+ **help**: bridge priority
+
+
+ **required**: False
+
+ **default**: 32768
+
+ **example**:
+ bridge-bridgeprio 32768
+
+
+ **bridge-maxage**
+
+ **help**: bridge set maxage
+
+
+ **required**: False
+
+ **default**: 20
+
+ **example**:
+ bridge-maxage 20
+
+
+ **bridge-portmcfl**
+
+ **help**: port multicast fast leave
+
+
+ **required**: False
+
+ **default**: 0
+
+ **example**:
+ bridge-portmcfl swp1=0 swp2=0
+
+
+ **bridge-mcqi**
+
+ **help**: set multicast query interval (in secs)
+
+
+ **required**: False
+
+ **default**: 125
+
+ **example**:
+ bridge-mcqi 125
+
+
+
+ **usercmds**: user commands for interfaces
+
+
+ **down**
+
+ **help**: run command at interface down
+
+
+ **required**: False
+
+ **post-up**
+
+ **help**: run command after interface bring up
+
+
+ **required**: False
+
+ **up**
+
+ **help**: run command at interface bring up
+
+
+ **required**: False
+
+ **pre-down**
+
+ **help**: run command before bringing the interface down
+
+
+ **required**: False
+
+ **pre-up**
+
+ **help**: run command before bringing the interface up
+
+
+ **required**: False
+
+ **post-down**
+
+ **help**: run command after bringing interface down
+
+
+ **required**: False
+
+
+ **mstpctl**: mstp configuration module for bridges
+
+
+ **mstpctl-fdelay**
+
+ **help**: set forwarding delay
+
+
+ **required**: False
+
+ **default**: 15
+
+ **example**:
+ mstpctl-fdelay 15
+
+
+ **mstpctl-txholdcount**
+
+ **help**: bridge transmit holdcount
+
+
+ **required**: False
+
+ **default**: 6
+
+ **example**:
+ mstpctl-txholdcount 6
+
+
+ **mstpctl-portautoedge**
+
+ **help**: enable/disable auto transition to/from edge state of
+ the port
+
+
+ **required**: False
+
+ **default**: no
+
+ **validvals**: yes,no
+
+ **example**:
+ mstpctl-portautoedge swp1=yes swp2=yes
+
+
+ **mstpctl-portrestrrole**
+
+ **help**: enable/disable port ability to take root role of the
+ port
+
+
+ **required**: False
+
+ **default**: no
+
+ **validvals**: yes,no
+
+ **example**:
+ mstpctl-portrestrrole swp1=no swp2=no
+
+
+ **mstpctl-portnetwork**
+
+ **help**: enable/disable bridge assurance capability for a por
+ t
+
+
+ **required**: False
+
+ **default**: no
+
+ **validvals**: yes,no
+
+ **example**:
+ mstpctl-portnetwork swp1=no swp2=no
+
+
+ **mstpctl-portp2p**
+
+ **help**: bridge port p2p detection mode
+
+
+ **required**: False
+
+ **default**: no
+
+ **validvals**: yes,no
+
+ **example**:
+ mstpctl-portp2p swp1=no swp2=no
+
+
+ **mstpctl-treeprio**
+
+ **help**: tree priority
+
+
+ **required**: False
+
+ **default**: 32768
+
+ validrange: 0-65535
+
+ **example**:
+ mstpctl-treeprio 32768
+
+
+ **mstpctl-treeportprio**
+
+ **help**: port priority for MSTI instance
+
+
+ **required**: False
+
+ **default**: 128
+
+ validrange: 0-240
+
+ **example**:
+ mstpctl-treeportprio swp1=128 swp2=128
+
+
+ **mstpctl-hello**
+
+ **help**: set hello time
+
+
+ **required**: False
+
+ **default**: 2
+
+ **example**:
+ mstpctl-hello 2
+
+
+ **mstpctl-ageing**
+
+ **help**: ageing time
+
+
+ **required**: False
+
+ **default**: 300
+
+ **example**:
+ mstpctl-ageing 300
+
+
+ **mstpctl-portadminedge**
+
+ **help**: enable/disable initial edge state of the port
+
+
+ **required**: False
+
+ **default**: no
+
+ **validvals**: yes,no
+
+ **example**:
+ mstpctl-portadminedge swp1=no swp2=no
+
+
+ **mstpctl-maxage**
+
+ **help**: max message age
+
+
+ **required**: False
+
+ **default**: 20
+
+ **example**:
+ mstpctl-maxage 20
+
+
+ **mstpctl-maxhops**
+
+ **help**: bridge max hops
+
+
+ **required**: False
+
+ **default**: 15
+
+ **example**:
+ mstpctl-maxhops 15
+
+
+ **mstpctl-portrestrtcn**
+
+ **help**: enable/disable port ability to propagate received to
+ pology change notification of the port
+
+
+ **required**: False
+
+ **default**: no
+
+ **validvals**: yes,no
+
+ **example**:
+ mstpctl-portrestrtcn swp1=no swp2=no
+
+
+ **mstpctl-portpathcost**
+
+ **help**: bridge port path cost
+
+
+ **required**: False
+
+ **default**: 0
+
+ **example**:
+ mstpctl-portpathcost swp1=0 swp2=1
+
+
+ **mstpctl-portadminage**
+
+ **help**: bridge port admin age
+
+
+ **required**: False
+
+ **default**: no
+
+ **validvals**: yes,no
+
+ **example**:
+ mstpctl-portadminage swp1=no swp2=no
+
+
+ **mstpctl-portbpdufilter**
+
+ **help**: enable/disable bpdu filter on a port
+
+
+ **required**: False
+
+ **default**: no
+
+ **validvals**: yes,no
+
+ **example**:
+ mstpctl-portbpdufilter swp1=no swp2=no
+
+
+ **mstpctl-forcevers**
+
+ **help**: bridge force stp version
+
+
+ **required**: False
+
+ **default**: rstp
+
+ **example**:
+ mstpctl-forcevers rstp
+
+
+ **mstpctl-treeportcost**
+
+ **help**: port tree cost
+
+
+ **required**: False
+
+ **mstpctl-bpduguard**
+
+ **help**: enable/disable bpduguard
+
+
+ **required**: False
+
+ **default**: no
+
+ **validvals**: yes,no
+
+ **example**:
+ mstpctl-bpduguard swp1=no swp2=no
+
+
+
+ **vlan**: vlan module configures vlan interfaces.This module under
+ stands vlan interfaces with dot notations. eg swp1.100. Vlan inter
+ faces with any other names need to have raw device and vlan id att
+ ributes
+
+
+ **vlan-id**
+
+ **help**: vlan id
+
+
+ **required**: False
+
+ **vlan-raw-device**
+
+ **help**: vlan raw device
+
+
+ **required**: False
+
+
+ **ifenslave**: bond configuration module
+
+
+ **bond-miimon**
+
+ **help**: bond miimon
+
+
+ **required**: False
+
+ **default**: 0
+
+ validrange: 0-255
+
+ **example**:
+ bond-miimon 0
+
+
+ **bond-slaves**
+
+ **help**: bond slaves
+
+
+ **required**: True
+
+ **example**:
+ bond-slaves swp1 swp2
+
+ bond-slaves glob swp1-2
+
+ bond-slaves regex (swp[1|2)
+
+
+ **bond-mode**
+
+ **help**: bond mode
+
+
+ **required**: False
+
+ **default**: balance-rr
+
+ **validvals**: balance-rr,active-backup,balance-xor,broadcast,802.3ad,balance-tlb,balance-alb
+
+ **example**:
+ bond-mode 802.3ad
+
+
+ **bond-num-grat-arp**
+
+ **help**: bond use carrier
+
+
+ **required**: False
+
+ **default**: 1
+
+ validrange: 0-255
+
+ **example**:
+ bond-num-grat-arp 1
+
+
+ **bond-ad-sys-mac-addr**
+
+ **help**: 802.3ad system mac address
+
+
+ **required**: False
+
+ **default**: 00:00:00:00:00:00
+
+ **example**:
+ bond-ad-sys-mac-addr 00:00:00:00:00:00
+
+
+ **bond-use-carrier**
+
+ **help**: bond use carrier
+
+
+ **required**: False
+
+ **default**: 1
+
+ **validvals**: 0,1
+
+ **example**:
+ bond-use-carrier 1
+
+
+ **bond-lacp-rate**
+
+ **help**: bond use carrier
+
+
+ **required**: False
+
+ **default**: 0
+
+ **validvals**: 0,1
+
+ **example**:
+ bond-lacp-rate 0
+
+
+ **bond-min-links**
+
+ **help**: bond min links
+
+
+ **required**: False
+
+ **default**: 0
+
+ **example**:
+ bond-min-links 0
+
+
+ **bond-num-unsol-na**
+
+ **help**: bond slave devices
+
+
+ **required**: False
+
+ **default**: 1
+
+ validrange: 0-255
+
+ **example**:
+ bond-num-unsol-na 1
+
+
+ **bond-ad-sys-priority**
+
+ **help**: 802.3ad system priority
+
+
+ **required**: False
+
+ **default**: 65535
+
+ **example**:
+ bond-ad-sys-priority 65535
+
+
+ **bond-xmit-hash-policy**
+
+ **help**: bond slave devices
+
+
+ **required**: False
+
+ **default**: layer2
+
+ **validvals**: layer2,layer3+4,layer2+3
+
+ **example**:
+ bond-xmit-hash-policy layer2
+
+
+
+ **address**: address configuration module for interfaces
+
+
+ **broadcast**
+
+ **help**: broadcast address
+
+
+ **required**: False
+
+ **example**:
+ broadcast 10.0.1.255
+
+
+ **hwaddress**
+
+ **help**: hw address
+
+
+ **required**: False
+
+ **example**:
+ hwaddress 44:38:39:00:27:b8
+
+
+ **alias**
+
+ **help**: description/alias
+
+
+ **required**: False
+
+ **example**:
+ alias testnetwork
+
+
+ **address**
+
+ **help**: ipv4 or ipv6 addresses
+
+
+ **required**: False
+
+ **example**:
+ address 10.0.12.3/24
+
+ address 2000:1000:1000:1000:3::5/128
+
+
+ **scope**
+
+ **help**: scope
+
+
+ **required**: False
+
+ **example**:
+ scope host
+
+
+ **preferred-lifetime**
+
+ **help**: preferred lifetime
+
+
+ **required**: False
+
+ **example**:
+ preferred-lifetime forever
+
+ preferred-lifetime 10
+
+
+ **gateway**
+
+ **help**: default gateway
+
+
+ **required**: False
+
+ **example**:
+ gateway 255.255.255.0
+
+
+ **mtu**
+
+ **help**: interface mtu
+
+
+ **required**: False
+
+ **default**: 1500
+
+ **example**:
+ mtu 1600
+
+
+
+SEE ALSO
+========
+ interfaces(5),
+ ifup(8),
+ ip(8),
+ mstpctl(8),
+ brctl(8),
+ ethtool(8)
--- /dev/null
+.\" Man page generated from reStructeredText.
+.
+.TH IFQUERY 8 "2014-02-05" "0.1" ""
+.SH NAME
+ifquery \- query network interface configuration
+.
+.nr rst2man-indent-level 0
+.
+.de1 rstReportMargin
+\\$1 \\n[an-margin]
+level \\n[rst2man-indent-level]
+level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
+-
+\\n[rst2man-indent0]
+\\n[rst2man-indent1]
+\\n[rst2man-indent2]
+..
+.de1 INDENT
+.\" .rstReportMargin pre:
+. RS \\$1
+. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
+. nr rst2man-indent-level +1
+.\" .rstReportMargin post:
+..
+.de UNINDENT
+. RE
+.\" indent \\n[an-margin]
+.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
+.nr rst2man-indent-level -1
+.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
+.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
+..
+.SH SYNOPSIS
+.INDENT 0.0
+.INDENT 3.5
+\fBifquery [\-v] [\-\-allow CLASS] [\-\-with\-depends] \-a|IFACE...\fP
+.sp
+\fBifquery [\-v] [\-r|\-\-running] [\-\-allow CLASS] [\-\-with\-depends] \-a|IFACE...\fP
+.sp
+\fBifquery [\-v] [\-c|\-\-check] [\-\-allow CLASS] [\-\-with\-depends] \-a|IFACE...\fP
+.sp
+\fBifquery [\-v] [\-p|\-\-print\-dependency {list,dot}] [\-\-allow CLASS] [\-\-with\-depends] \-a|IFACE...\fP
+.sp
+\fBifquery [\-v] \-s|\-\-syntax\-help\fP
+.UNINDENT
+.UNINDENT
+.SH DESCRIPTION
+.INDENT 0.0
+.INDENT 3.5
+\fBifquery\fP can be used to parse interface configuration file, query
+running state or check running state of the interface with configuration
+in \fB/etc/network/interfaces\fP file.
+.sp
+\fBifquery\fP always works on the current \fBinterfaces(5)\fP file
+\fB/etc/network/interfaces\fP unless an alternate interfaces file is
+provided with the \fB\-i\fP option.
+.UNINDENT
+.UNINDENT
+.SH OPTIONS
+.INDENT 0.0
+.INDENT 3.5
+positional arguments:
+.sp
+\fBIFACE\fP interface list separated by spaces. \fBIFACE\fP list and \fB\(aq\-a\(aq\fP argument are mutually exclusive.
+.sp
+optional arguments:
+.INDENT 0.0
+.TP
+.B \-h, \-\-help
+show this help message and exit
+.TP
+.B \-a, \-\-all
+process all interfaces marked "auto"
+.TP
+.B \-v, \-\-verbose
+verbose
+.TP
+.B \-d, \-\-debug
+output debug info
+.TP
+.BI \-\-allow \ CLASS
+ignore non\-"allow\-CLASS" interfaces
+.TP
+.B \-w, \-\-with\-depends
+run with all dependent interfaces. This option
+is redundant when \-a is specified. When \(aq\-a\(aq is
+specified, interfaces are always executed in
+dependency order.
+.TP
+.BI \-X \ EXCLUDEPATS, \ \-\-exclude \ EXCLUDEPATS
+Exclude interfaces from the list of interfaces to
+operate on. Can be specified multiple times
+.TP
+.BI \-i \ INTERFACESFILE, \ \-\-interfaces \ INTERFACESFILE
+Use interfaces file instead of default
+/etc/network/interfaces
+.UNINDENT
+.INDENT 0.0
+.TP
+.B \-t {native,json}, \-\-interfaces\-format {native,json}
+interfaces file format
+.UNINDENT
+.INDENT 0.0
+.TP
+.B \-r, \-\-running
+print raw interfaces file entries
+.TP
+.B \-c, \-\-check
+check interface file contents against running state
+of an interface. Returns exit code 0 on success and
+1 on error
+.TP
+.B \-x, \-\-raw
+print raw config file entries
+.UNINDENT
+.INDENT 0.0
+.TP
+.B \-o {native,json}, \-\-format {native,json}
+interface display format
+.TP
+.B \-p, \-\-print\-dependency {list,dot}
+print iface dependency in list or dot format
+.UNINDENT
+.INDENT 0.0
+.TP
+.B \-s, \-\-syntax\-help
+print supported interface config syntax. Scans all
+addon modules and dumps supported syntax from them
+if provided by the module.
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.SH EXAMPLES
+.INDENT 0.0
+.INDENT 3.5
+# dump all or some interfaces config file entries
+# (pretty prints user provided entries)
+.INDENT 0.0
+.INDENT 3.5
+\fBifquery \-a\fP
+.sp
+\fBifquery br0\fP
+.UNINDENT
+.UNINDENT
+.sp
+# Same as above but dump with dependencies
+.INDENT 0.0
+.INDENT 3.5
+\fBifquery br0 \-\-with\-depends\fP
+.UNINDENT
+.UNINDENT
+.sp
+# Check running state with the config in /etc/network/interfaces
+.INDENT 0.0
+.INDENT 3.5
+\fBifquery \-\-check br0\fP
+.sp
+\fBifquery \-\-check \-\-with\-depends br0\fP
+.sp
+\fBifquery \-\-check \-a\fP
+.UNINDENT
+.UNINDENT
+.sp
+# dump running state of all interfaces in /etc/network/interfaces format
+.INDENT 0.0
+.INDENT 3.5
+\fBifquery \-\-running br0\fP
+.sp
+\fBifquery \-\-running \-\-with\-depends br0\fP
+.sp
+\fBifquery \-\-running \-a\fP
+.UNINDENT
+.UNINDENT
+.sp
+# print dependency info in list format
+.INDENT 0.0
+.INDENT 3.5
+\fBifquery \-\-print\-dependency=list \-a\fP
+.sp
+\fBifquery \-\-print\-dependency=list br2000\fP
+.UNINDENT
+.UNINDENT
+.sp
+# print dependency info in dot format
+.INDENT 0.0
+.INDENT 3.5
+\fBifquery \-\-print\-dependency=dot \-a\fP
+.sp
+\fBifquery \-\-print\-dependency=dot br2000\fP
+.UNINDENT
+.UNINDENT
+.sp
+# Create an image (png) from the dot format
+.INDENT 0.0
+.INDENT 3.5
+\fBifquery \-\-print\-dependency=dot \-a > interfaces.dot\fP
+.sp
+\fBdot \-Tpng interfaces.dot > interfaces.png\fP
+.sp
+(The above command only works on a system with dot installed)
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.SH KNOWN_ISSUES
+.INDENT 0.0
+.INDENT 3.5
+\fBifquery \-\-check\fP is currently experimental
+.sp
+\fBifquery \-\-check\fP cannot validate usercommands given under pre\-up, post\-up etc
+There is currently no support to check/validate ethtool iface attributes
+.UNINDENT
+.UNINDENT
+.SH SEE ALSO
+.INDENT 0.0
+.INDENT 3.5
+ifup(8),
+ifdown(8),
+ifreload(8),
+interfaces(5),
+ifupdown\-addons\-interfaces(5)
+.UNINDENT
+.UNINDENT
+.SH AUTHOR
+Roopa Prabhu <roopa@cumulusnetworks.com>
+.SH COPYRIGHT
+Copyright 2014 Cumulus Networks, Inc. All rights reserved.
+.\" Generated by docutils manpage writer.
+.\"
+.
--- /dev/null
+.\" Man page generated from reStructeredText.
+.
+.TH IFRELOAD 8 "2014-02-05" "0.1" ""
+.SH NAME
+ifreload \- reload network interface configuration
+.
+.nr rst2man-indent-level 0
+.
+.de1 rstReportMargin
+\\$1 \\n[an-margin]
+level \\n[rst2man-indent-level]
+level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
+-
+\\n[rst2man-indent0]
+\\n[rst2man-indent1]
+\\n[rst2man-indent2]
+..
+.de1 INDENT
+.\" .rstReportMargin pre:
+. RS \\$1
+. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
+. nr rst2man-indent-level +1
+.\" .rstReportMargin post:
+..
+.de UNINDENT
+. RE
+.\" indent \\n[an-margin]
+.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
+.nr rst2man-indent-level -1
+.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
+.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
+..
+.SH SYNOPSIS
+.INDENT 0.0
+.INDENT 3.5
+ifreload [\-h] (\-a|\-c) [\-v] [\-d] [\-f] [\-n]
+.UNINDENT
+.UNINDENT
+.SH DESCRIPTION
+.INDENT 0.0
+.INDENT 3.5
+reloads network \fBinterfaces(5)\fP file \fB/etc/network/interfaces\fP.
+.sp
+Runs \fBifdown\fP on interfaces that changed in the interfaces file and
+subsequently runs \fBifup\fP on all interfaces.
+.sp
+\fBifreload\fP is equivalent to \fBifdown \-a\fP followed by \fBifup \-a\fP
+but it skips \fBifdown\fP for interfaces that did not change in the config
+file.
+.sp
+If you do not wish to execute \fBdown\fP on any interfaces, but only \fBup\fP on
+interfaces that were already \fBup\fP, please see the \fB\-\-currently\-up\fP
+option below.
+.UNINDENT
+.UNINDENT
+.SH OPTIONS
+.INDENT 0.0
+.INDENT 3.5
+.INDENT 0.0
+.TP
+.B \-h, \-\-help
+show this help message and exit
+.TP
+.B \-a, \-\-all
+process all interfaces marked "auto"
+.TP
+.B \-v, \-\-verbose
+verbose
+.TP
+.B \-d, \-\-debug
+output debug info
+.TP
+.B \-f, \-\-force
+force run all operations
+.TP
+.B \-c, \-\-currently\-up
+only reload auto and other interfaces that are
+currently up. This can be used as a non\-disruptive
+alternative to \-a because it will not down any
+interfaces
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.SH EXAMPLES
+.INDENT 0.0
+.INDENT 3.5
+# reload all auto interfaces in \fBinterfaces(5)\fP file
+.sp
+\fBifreload \-a\fP
+.sp
+# reload all interfaces using service command
+.sp
+\fBservice networking reload\fP
+.sp
+# reload all currently up interfaces without bringing any interfaces down
+.sp
+\fBservice networking reload\-currently\-up\fP
+.UNINDENT
+.UNINDENT
+.SH SEE ALSO
+.INDENT 0.0
+.INDENT 3.5
+ifup(8),
+ifdown(8),
+ifquery(8),
+interfaces(5),
+ifupdown\-addons\-interfaces(5)
+.UNINDENT
+.UNINDENT
+.SH AUTHOR
+Roopa Prabhu <roopa@cumulusnetworks.com>
+.SH COPYRIGHT
+Copyright 2014 Cumulus Networks, Inc. All rights reserved.
+.\" Generated by docutils manpage writer.
+.\"
+.
--- /dev/null
+.\" Man page generated from reStructeredText.
+.
+.TH IFUP 8 "2014-02-05" "0.1" ""
+.SH NAME
+ifup \- network interface management commands
+.
+.nr rst2man-indent-level 0
+.
+.de1 rstReportMargin
+\\$1 \\n[an-margin]
+level \\n[rst2man-indent-level]
+level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
+-
+\\n[rst2man-indent0]
+\\n[rst2man-indent1]
+\\n[rst2man-indent2]
+..
+.de1 INDENT
+.\" .rstReportMargin pre:
+. RS \\$1
+. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
+. nr rst2man-indent-level +1
+.\" .rstReportMargin post:
+..
+.de UNINDENT
+. RE
+.\" indent \\n[an-margin]
+.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
+.nr rst2man-indent-level -1
+.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
+.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
+..
+.SH NAME
+.INDENT 0.0
+.INDENT 3.5
+\fBifup\fP \- bring a network interface up
+.sp
+\fBifdown\fP \- take a network interface down
+.UNINDENT
+.UNINDENT
+.SH SYNOPSIS
+.INDENT 0.0
+.INDENT 3.5
+.INDENT 0.0
+.TP
+.B ifup [\-h] [\-a] [\-v] [\-d] [\-\-allow CLASS] [\-\-with\-depends]
+\fB[\-X EXCLUDEPATS] [\-f] [\-n] [\-\-print\-dependency {list,dot}]\fP
+\fB[IFACE [IFACE ...]]\fP
+.TP
+.B ifdown [\-h] [\-a] [\-v] [\-d] [\-\-allow CLASS] [\-\-with\-depends]
+\fB[\-X EXCLUDEPATS] [\-f] [\-n] [\-\-print\-dependency {list,dot}]\fP
+\fB[IFACE [IFACE ...]]\fP
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.SH DESCRIPTION
+.INDENT 0.0
+.INDENT 3.5
+\fBifup\fP and \fBifdown\fP commands can be used to configure (or, respectively,
+deconfigure) network interfaces based on interface definitions in the
+file \fB/etc/network/interfaces/\fP file.
+.sp
+\fBifquery(8)\fP maybe used in conjunction with \fBifup\fP and \fBifdown\fP
+commands to query and validate applied/running configuration.
+.sp
+\fBifup\fP always works on the current \fBinterfaces(5)\fP file under
+\fB/etc/network/interfaces\fP. \fBifdown\fP works on the last applied interface
+configuration.
+.sp
+\fBifup\fP on an already ifup\(aqed interface will re\-apply the configuration,
+skipping already applied configuration whereever possible. In many cases
+where config commands are idempotent, you will see that ifup/ifdown will
+reapply the config even if the interface already has that config.
+.sp
+\fBifup\fP and \fBifdown\fP understands interface dependency order.
+.sp
+For logical interfaces like vlans, bridges, bonds, \fBifup\fP creates the
+interface and \fBifdown\fP deletes the interface. Use \fB\-\-admin\-state\fP
+option if you only want to administratively bring the interface up/down.
+.sp
+When \fBifup\fP and \fBifdown\fP are used with interfaces on command line,
+they must be have a \fBiface\fP section in the \fBinterfaces(5)\fP file.
+.UNINDENT
+.UNINDENT
+.SH OPTIONS
+.INDENT 0.0
+.INDENT 3.5
+positional arguments:
+.sp
+\fBIFACE\fP interface list separated by spaces. \fBIFACE\fP list and \fB\(aq\-a\(aq\fP
+argument are mutually exclusive.
+.sp
+optional arguments:
+.INDENT 0.0
+.TP
+.B \-h, \-\-help
+show this help message and exit
+.TP
+.B \-a, \-\-all
+process all interfaces marked "auto"
+.TP
+.B \-v, \-\-verbose
+verbose
+.TP
+.B \-d, \-\-debug
+output debug info
+.TP
+.BI \-\-allow \ CLASS
+ignore non\-"allow\-CLASS" interfaces
+.TP
+.B \-w, \-\-with\-depends
+run with all dependent interfaces. This option
+is redundant when \-a is specified. When \(aq\-a\(aq is
+specified, interfaces are always executed in
+dependency order.
+.TP
+.BI \-X \ EXCLUDEPATS, \ \-\-exclude \ EXCLUDEPATS
+Exclude interfaces from the list of interfaces to
+operate on. Can be specified multiple times
+.TP
+.BI \-i \ INTERFACESFILE, \ \-\-interfaces \ INTERFACESFILE
+Use interfaces file instead of default
+/etc/network/interfaces
+.UNINDENT
+.INDENT 0.0
+.TP
+.B \-t {native,json}, \-\-interfaces\-format {native,json}
+interfaces file format
+.UNINDENT
+.INDENT 0.0
+.TP
+.B \-f, \-\-force
+force run all operations
+.TP
+.B \-n, \-\-no\-act
+print out what would happen, but don\(aqt do it
+.UNINDENT
+.INDENT 0.0
+.TP
+.B \-p, \-\-print\-dependency {list,dot}
+print iface dependency in list or dot format
+.UNINDENT
+.INDENT 0.0
+.TP
+.B \-m, \-\-admin\-state, \-\-no\-scripts
+dont run any addon modules/scripts. Only bring
+the interface administratively up/down
+.TP
+.B \-u, \-\-use\-current\-config
+By default ifdown looks at the saved state for
+interfaces to bring down. This option allows ifdown
+to look at the current interfaces file. Useful when
+your state file is corrupted or you want down to use
+the latest from the interfaces file
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.SH EXAMPLES
+.INDENT 0.0
+.INDENT 3.5
+# bringing up all interfaces
+.INDENT 0.0
+.INDENT 3.5
+\fBifup \-a\fP
+.UNINDENT
+.UNINDENT
+.sp
+# bringing up interface list
+.INDENT 0.0
+.INDENT 3.5
+\fBifup swp1 swp2\fP
+.UNINDENT
+.UNINDENT
+.sp
+# bringing up interface with its dependents
+.INDENT 0.0
+.INDENT 3.5
+\fBifup br0 \-\-with\-depends\fP
+.UNINDENT
+.UNINDENT
+.sp
+# bringing down all interfaces
+.INDENT 0.0
+.INDENT 3.5
+\fBifdown \-a\fP
+.UNINDENT
+.UNINDENT
+.sp
+# bringing down a single interface
+.INDENT 0.0
+.INDENT 3.5
+\fBifdown swp1\fP
+.UNINDENT
+.UNINDENT
+.sp
+# excluding interfaces using \-X option
+.INDENT 0.0
+.INDENT 3.5
+\fBifdown \-X eth0 \-a\fP
+.sp
+\fBifup \-X eth0 \-a\fP
+.sp
+\fBifdown \-X eth0 \-X lo \-a\fP
+.UNINDENT
+.UNINDENT
+.sp
+# using verbose \-v option to see what is going on
+.INDENT 0.0
+.INDENT 3.5
+\fBifup \-v \-a\fP
+.UNINDENT
+.UNINDENT
+.sp
+# using debug \-d option to see more of what is going on
+.INDENT 0.0
+.INDENT 3.5
+\fBifup \-d \-a\fP
+.UNINDENT
+.UNINDENT
+.sp
+# ignore errors
+.INDENT 0.0
+.INDENT 3.5
+\fBifup \-a \-f\fP
+.sp
+\fBifdown \-a \-f\fP
+.UNINDENT
+.UNINDENT
+.sp
+# run ifdown and ifup on all interfaces using service command/init script
+.INDENT 0.0
+.INDENT 3.5
+\fBservice networking restart\fP
+.UNINDENT
+.UNINDENT
+.sp
+# run ifup on all interfaces using service command/init script
+.INDENT 0.0
+.INDENT 3.5
+\fBservice networking start\fP
+.UNINDENT
+.UNINDENT
+.sp
+# ifdown on all interfaces using service command/init script
+.INDENT 0.0
+.INDENT 3.5
+\fBservice networking stop\fP
+.UNINDENT
+.UNINDENT
+.sp
+# To run ifup/ifdown on only interfaces that changed see \fBifreload(8)\fP
+.UNINDENT
+.UNINDENT
+.SH SEE ALSO
+.INDENT 0.0
+.INDENT 3.5
+ifquery(8),
+ifreload(8),
+interfaces(5),
+ifupdown\-addons\-interfaces(5)
+.UNINDENT
+.UNINDENT
+.SH AUTHOR
+Roopa Prabhu <roopa@cumulusnetworks.com>
+.SH COPYRIGHT
+Copyright 2014 Cumulus Networks, Inc. All rights reserved.
+.\" Generated by docutils manpage writer.
+.\"
+.
--- /dev/null
+.\" Man page generated from reStructeredText.
+.
+.TH IFUPDOWN-ADDONS-INTERFACES 5 "2013-09-25" "0.1" ""
+.SH NAME
+ifupdown-addons-interfaces \- ifupdown2 addon modules interface configuration
+.
+.nr rst2man-indent-level 0
+.
+.de1 rstReportMargin
+\\$1 \\n[an-margin]
+level \\n[rst2man-indent-level]
+level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
+-
+\\n[rst2man-indent0]
+\\n[rst2man-indent1]
+\\n[rst2man-indent2]
+..
+.de1 INDENT
+.\" .rstReportMargin pre:
+. RS \\$1
+. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
+. nr rst2man-indent-level +1
+.\" .rstReportMargin post:
+..
+.de UNINDENT
+. RE
+.\" indent \\n[an-margin]
+.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
+.nr rst2man-indent-level -1
+.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
+.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
+..
+.SH DESCRIPTION
+.INDENT 0.0
+.INDENT 3.5
+ifupdown2 addon modules add incremental functionality to
+core ifupdown2 tool.
+.sp
+All installed addon modules are executed on every interface
+listed in the interfaces file. Addon modules are installed under
+/usr/share/ifupdownaddons. To see the list of active addon
+modules, see ifaddon(8).
+.sp
+Addon modules add new attributes to the interfaces(5) file.
+Below is a list of attribute options provided by each module.
+These can be listed under each iface section in the interfaces(5)
+file.
+.UNINDENT
+.UNINDENT
+.SH EXAMPLES
+.INDENT 0.0
+.INDENT 3.5
+Listed below are addon modules and their supported attributes.
+The attributes if applicable go under the iface section in the
+interfaces(5) file.
+.sp
+\fBethtool\fP: ethtool configuration module for interfaces
+.INDENT 0.0
+.INDENT 3.5
+\fBlink\-duplex\fP
+.INDENT 0.0
+.INDENT 3.5
+\fBhelp\fP: set link duplex
+.sp
+\fBrequired\fP: False
+.sp
+\fBdefault\fP: half
+.sp
+\fBvalidvals\fP: half,full
+.INDENT 0.0
+.TP
+.B \fBexample\fP:
+link\-duplex full
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.sp
+\fBlink\-autoneg\fP
+.INDENT 0.0
+.INDENT 3.5
+\fBhelp\fP: set autonegotiation
+.sp
+\fBrequired\fP: False
+.sp
+\fBdefault\fP: off
+.sp
+\fBvalidvals\fP: on,off
+.INDENT 0.0
+.TP
+.B \fBexample\fP:
+link\-autoneg on
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.sp
+\fBlink\-speed\fP
+.INDENT 0.0
+.INDENT 3.5
+\fBhelp\fP: set link speed
+.sp
+\fBrequired\fP: False
+.INDENT 0.0
+.TP
+.B \fBexample\fP:
+link\-speed 1000
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.sp
+\fBbridge\fP: bridge configuration module
+.INDENT 0.0
+.INDENT 3.5
+\fBbridge\-mcqifaddr\fP
+.INDENT 0.0
+.INDENT 3.5
+\fBhelp\fP: set multicast query to use ifaddr
+.sp
+\fBrequired\fP: False
+.sp
+\fBdefault\fP: 0
+.INDENT 0.0
+.TP
+.B \fBexample\fP:
+bridge\-mcqifaddr 0
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.sp
+\fBbridge\-gcint\fP
+.INDENT 0.0
+.INDENT 3.5
+\fBhelp\fP: bridge garbage collection interval in secs
+.sp
+\fBrequired\fP: False
+.sp
+\fBdefault\fP: 4
+.INDENT 0.0
+.TP
+.B \fBexample\fP:
+bridge\-gcint 4
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.sp
+\fBbridge\-mcsqc\fP
+.INDENT 0.0
+.INDENT 3.5
+\fBhelp\fP: set multicast startup query count
+.sp
+\fBrequired\fP: False
+.sp
+\fBdefault\fP: 2
+.INDENT 0.0
+.TP
+.B \fBexample\fP:
+bridge\-mcsqc 2
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.sp
+\fBbridge\-stp\fP
+.INDENT 0.0
+.INDENT 3.5
+\fBhelp\fP: bridge\-stp yes/no
+.sp
+\fBrequired\fP: False
+.sp
+\fBdefault\fP: no
+.sp
+\fBvalidvals\fP: yes,on,off,no
+.INDENT 0.0
+.TP
+.B \fBexample\fP:
+bridge\-stp no
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.sp
+\fBbridge\-mcsqi\fP
+.INDENT 0.0
+.INDENT 3.5
+\fBhelp\fP: set multicast startup query interval (in secs)
+.sp
+\fBrequired\fP: False
+.sp
+\fBdefault\fP: 31
+.INDENT 0.0
+.TP
+.B \fBexample\fP:
+bridge\-mcsqi 31
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.sp
+\fBbridge\-mcmi\fP
+.INDENT 0.0
+.INDENT 3.5
+\fBhelp\fP: set multicast membership interval (in secs)
+.sp
+\fBrequired\fP: False
+.sp
+\fBdefault\fP: 260
+.INDENT 0.0
+.TP
+.B \fBexample\fP:
+bridge\-mcmi 260
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.sp
+\fBbridge\-ports\fP
+.INDENT 0.0
+.INDENT 3.5
+\fBhelp\fP: bridge ports
+.sp
+\fBrequired\fP: True
+.INDENT 0.0
+.TP
+.B \fBexample\fP:
+bridge\-ports swp1.100 swp2.100 swp3.100
+.sp
+bridge\-ports glob swp1\-3.100
+.sp
+bridge\-ports regex (swp[1|2|3].100)
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.sp
+\fBbridge\-mcsnoop\fP
+.INDENT 0.0
+.INDENT 3.5
+\fBhelp\fP: set multicast snooping
+.sp
+\fBrequired\fP: False
+.sp
+\fBdefault\fP: 1
+.INDENT 0.0
+.TP
+.B \fBexample\fP:
+bridge\-mcsnoop 1
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.sp
+\fBbridge\-maxwait\fP
+.INDENT 0.0
+.INDENT 3.5
+\fBhelp\fP: forces to time seconds the maximum time that the Deb
+ian bridge setup scripts will wait for the bridge ports to ge
+t to the forwarding status, doesn\(aqt allow factional part. If i
+t is equal to 0 then no waiting is done
+.sp
+\fBrequired\fP: False
+.sp
+\fBdefault\fP: 0
+.INDENT 0.0
+.TP
+.B \fBexample\fP:
+bridge\-maxwait 3
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.sp
+\fBbridge\-pathcosts\fP
+.INDENT 0.0
+.INDENT 3.5
+\fBhelp\fP: bridge set port path costs
+.sp
+\fBrequired\fP: False
+.sp
+\fBdefault\fP: 100
+.INDENT 0.0
+.TP
+.B \fBexample\fP:
+bridge\-pathcosts swp1=100 swp2=100
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.sp
+\fBbridge\-portprios\fP
+.INDENT 0.0
+.INDENT 3.5
+\fBhelp\fP: bridge port prios
+.sp
+\fBrequired\fP: False
+.sp
+\fBdefault\fP: 32
+.INDENT 0.0
+.TP
+.B \fBexample\fP:
+bridge\-portprios swp1=32 swp2=32
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.sp
+\fBbridge\-fd\fP
+.INDENT 0.0
+.INDENT 3.5
+\fBhelp\fP: bridge forward delay
+.sp
+\fBrequired\fP: False
+.sp
+\fBdefault\fP: 15
+.INDENT 0.0
+.TP
+.B \fBexample\fP:
+bridge\-fd 15
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.sp
+\fBbridge\-ageing\fP
+.INDENT 0.0
+.INDENT 3.5
+\fBhelp\fP: bridge ageing
+.sp
+\fBrequired\fP: False
+.sp
+\fBdefault\fP: 300
+.INDENT 0.0
+.TP
+.B \fBexample\fP:
+bridge\-ageing 300
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.sp
+\fBbridge\-hello\fP
+.INDENT 0.0
+.INDENT 3.5
+\fBhelp\fP: bridge set hello time
+.sp
+\fBrequired\fP: False
+.sp
+\fBdefault\fP: 2
+.INDENT 0.0
+.TP
+.B \fBexample\fP:
+bridge\-hello 2
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.sp
+\fBbridge\-mcquerier\fP
+.INDENT 0.0
+.INDENT 3.5
+\fBhelp\fP: set multicast querier
+.sp
+\fBrequired\fP: False
+.sp
+\fBdefault\fP: 0
+.INDENT 0.0
+.TP
+.B \fBexample\fP:
+bridge\-mcquerier 0
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.sp
+\fBbridge\-mclmc\fP
+.INDENT 0.0
+.INDENT 3.5
+\fBhelp\fP: set multicast last member count
+.sp
+\fBrequired\fP: False
+.sp
+\fBdefault\fP: 2
+.INDENT 0.0
+.TP
+.B \fBexample\fP:
+bridge\-mclmc 2
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.sp
+\fBbridge\-mcrouter\fP
+.INDENT 0.0
+.INDENT 3.5
+\fBhelp\fP: set multicast router
+.sp
+\fBrequired\fP: False
+.sp
+\fBdefault\fP: 1
+.INDENT 0.0
+.TP
+.B \fBexample\fP:
+bridge\-mcrouter 1
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.sp
+\fBbridge\-portmcrouter\fP
+.INDENT 0.0
+.INDENT 3.5
+\fBhelp\fP: set port multicast routers
+.sp
+\fBrequired\fP: False
+.sp
+\fBdefault\fP: 1
+.INDENT 0.0
+.TP
+.B \fBexample\fP:
+bridge\-portmcrouter swp1=1 swp2=1
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.sp
+\fBbridge\-mclmi\fP
+.INDENT 0.0
+.INDENT 3.5
+\fBhelp\fP: set multicast last member interval (in secs)
+.sp
+\fBrequired\fP: False
+.sp
+\fBdefault\fP: 1
+.INDENT 0.0
+.TP
+.B \fBexample\fP:
+bridge\-mclmi 1
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.sp
+\fBbridge\-hashmax\fP
+.INDENT 0.0
+.INDENT 3.5
+\fBhelp\fP: set hash max
+.sp
+\fBrequired\fP: False
+.sp
+\fBdefault\fP: 4096
+.INDENT 0.0
+.TP
+.B \fBexample\fP:
+bridge\-hashmax 4096
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.sp
+\fBbridge\-waitport\fP
+.INDENT 0.0
+.INDENT 3.5
+\fBhelp\fP: wait for a max of time secs for the specified ports
+to become available,if no ports are specified then those speci
+fied on bridge\-ports will be used here. Specifying no ports he
+re should not be used if we are using regex or "all" on bridge
+_ports,as it wouldnt work.
+.sp
+\fBrequired\fP: False
+.sp
+\fBdefault\fP: 0
+.INDENT 0.0
+.TP
+.B \fBexample\fP:
+bridge\-waitport 4
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.sp
+\fBbridge\-mcqri\fP
+.INDENT 0.0
+.INDENT 3.5
+\fBhelp\fP: set multicast query response interval (in secs)
+.sp
+\fBrequired\fP: False
+.sp
+\fBdefault\fP: 10
+.INDENT 0.0
+.TP
+.B \fBexample\fP:
+bridge\-mcqri 10
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.sp
+\fBbridge\-hashel\fP
+.INDENT 0.0
+.INDENT 3.5
+\fBhelp\fP: set hash elasticity
+.sp
+\fBrequired\fP: False
+.sp
+\fBdefault\fP: 4096
+.INDENT 0.0
+.TP
+.B \fBexample\fP:
+bridge\-hashel 4096
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.sp
+\fBbridge\-mcqpi\fP
+.INDENT 0.0
+.INDENT 3.5
+\fBhelp\fP: set multicast querier interval (in secs)
+.sp
+\fBrequired\fP: False
+.sp
+\fBdefault\fP: 255
+.INDENT 0.0
+.TP
+.B \fBexample\fP:
+bridge\-mcqpi 255
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.sp
+\fBbridge\-bridgeprio\fP
+.INDENT 0.0
+.INDENT 3.5
+\fBhelp\fP: bridge priority
+.sp
+\fBrequired\fP: False
+.sp
+\fBdefault\fP: 32768
+.INDENT 0.0
+.TP
+.B \fBexample\fP:
+bridge\-bridgeprio 32768
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.sp
+\fBbridge\-maxage\fP
+.INDENT 0.0
+.INDENT 3.5
+\fBhelp\fP: bridge set maxage
+.sp
+\fBrequired\fP: False
+.sp
+\fBdefault\fP: 20
+.INDENT 0.0
+.TP
+.B \fBexample\fP:
+bridge\-maxage 20
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.sp
+\fBbridge\-portmcfl\fP
+.INDENT 0.0
+.INDENT 3.5
+\fBhelp\fP: port multicast fast leave
+.sp
+\fBrequired\fP: False
+.sp
+\fBdefault\fP: 0
+.INDENT 0.0
+.TP
+.B \fBexample\fP:
+bridge\-portmcfl swp1=0 swp2=0
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.sp
+\fBbridge\-mcqi\fP
+.INDENT 0.0
+.INDENT 3.5
+\fBhelp\fP: set multicast query interval (in secs)
+.sp
+\fBrequired\fP: False
+.sp
+\fBdefault\fP: 125
+.INDENT 0.0
+.TP
+.B \fBexample\fP:
+bridge\-mcqi 125
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.sp
+\fBusercmds\fP: user commands for interfaces
+.INDENT 0.0
+.INDENT 3.5
+\fBdown\fP
+.INDENT 0.0
+.INDENT 3.5
+\fBhelp\fP: run command at interface down
+.sp
+\fBrequired\fP: False
+.UNINDENT
+.UNINDENT
+.sp
+\fBpost\-up\fP
+.INDENT 0.0
+.INDENT 3.5
+\fBhelp\fP: run command after interface bring up
+.sp
+\fBrequired\fP: False
+.UNINDENT
+.UNINDENT
+.sp
+\fBup\fP
+.INDENT 0.0
+.INDENT 3.5
+\fBhelp\fP: run command at interface bring up
+.sp
+\fBrequired\fP: False
+.UNINDENT
+.UNINDENT
+.sp
+\fBpre\-down\fP
+.INDENT 0.0
+.INDENT 3.5
+\fBhelp\fP: run command before bringing the interface down
+.sp
+\fBrequired\fP: False
+.UNINDENT
+.UNINDENT
+.sp
+\fBpre\-up\fP
+.INDENT 0.0
+.INDENT 3.5
+\fBhelp\fP: run command before bringing the interface up
+.sp
+\fBrequired\fP: False
+.UNINDENT
+.UNINDENT
+.sp
+\fBpost\-down\fP
+.INDENT 0.0
+.INDENT 3.5
+\fBhelp\fP: run command after bringing interface down
+.sp
+\fBrequired\fP: False
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.sp
+\fBmstpctl\fP: mstp configuration module for bridges
+.INDENT 0.0
+.INDENT 3.5
+\fBmstpctl\-fdelay\fP
+.INDENT 0.0
+.INDENT 3.5
+\fBhelp\fP: set forwarding delay
+.sp
+\fBrequired\fP: False
+.sp
+\fBdefault\fP: 15
+.INDENT 0.0
+.TP
+.B \fBexample\fP:
+mstpctl\-fdelay 15
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.sp
+\fBmstpctl\-txholdcount\fP
+.INDENT 0.0
+.INDENT 3.5
+\fBhelp\fP: bridge transmit holdcount
+.sp
+\fBrequired\fP: False
+.sp
+\fBdefault\fP: 6
+.INDENT 0.0
+.TP
+.B \fBexample\fP:
+mstpctl\-txholdcount 6
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.sp
+\fBmstpctl\-portautoedge\fP
+.INDENT 0.0
+.INDENT 3.5
+\fBhelp\fP: enable/disable auto transition to/from edge state of
+the port
+.sp
+\fBrequired\fP: False
+.sp
+\fBdefault\fP: no
+.sp
+\fBvalidvals\fP: yes,no
+.INDENT 0.0
+.TP
+.B \fBexample\fP:
+mstpctl\-portautoedge swp1=yes swp2=yes
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.sp
+\fBmstpctl\-portrestrrole\fP
+.INDENT 0.0
+.INDENT 3.5
+\fBhelp\fP: enable/disable port ability to take root role of the
+port
+.sp
+\fBrequired\fP: False
+.sp
+\fBdefault\fP: no
+.sp
+\fBvalidvals\fP: yes,no
+.INDENT 0.0
+.TP
+.B \fBexample\fP:
+mstpctl\-portrestrrole swp1=no swp2=no
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.sp
+\fBmstpctl\-portnetwork\fP
+.INDENT 0.0
+.INDENT 3.5
+\fBhelp\fP: enable/disable bridge assurance capability for a por
+t
+.sp
+\fBrequired\fP: False
+.sp
+\fBdefault\fP: no
+.sp
+\fBvalidvals\fP: yes,no
+.INDENT 0.0
+.TP
+.B \fBexample\fP:
+mstpctl\-portnetwork swp1=no swp2=no
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.sp
+\fBmstpctl\-portp2p\fP
+.INDENT 0.0
+.INDENT 3.5
+\fBhelp\fP: bridge port p2p detection mode
+.sp
+\fBrequired\fP: False
+.sp
+\fBdefault\fP: no
+.sp
+\fBvalidvals\fP: yes,no
+.INDENT 0.0
+.TP
+.B \fBexample\fP:
+mstpctl\-portp2p swp1=no swp2=no
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.sp
+\fBmstpctl\-treeprio\fP
+.INDENT 0.0
+.INDENT 3.5
+\fBhelp\fP: tree priority
+.sp
+\fBrequired\fP: False
+.sp
+\fBdefault\fP: 32768
+.sp
+validrange: 0\-65535
+.INDENT 0.0
+.TP
+.B \fBexample\fP:
+mstpctl\-treeprio 32768
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.sp
+\fBmstpctl\-treeportprio\fP
+.INDENT 0.0
+.INDENT 3.5
+\fBhelp\fP: port priority for MSTI instance
+.sp
+\fBrequired\fP: False
+.sp
+\fBdefault\fP: 128
+.sp
+validrange: 0\-240
+.INDENT 0.0
+.TP
+.B \fBexample\fP:
+mstpctl\-treeportprio swp1=128 swp2=128
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.sp
+\fBmstpctl\-hello\fP
+.INDENT 0.0
+.INDENT 3.5
+\fBhelp\fP: set hello time
+.sp
+\fBrequired\fP: False
+.sp
+\fBdefault\fP: 2
+.INDENT 0.0
+.TP
+.B \fBexample\fP:
+mstpctl\-hello 2
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.sp
+\fBmstpctl\-ageing\fP
+.INDENT 0.0
+.INDENT 3.5
+\fBhelp\fP: ageing time
+.sp
+\fBrequired\fP: False
+.sp
+\fBdefault\fP: 300
+.INDENT 0.0
+.TP
+.B \fBexample\fP:
+mstpctl\-ageing 300
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.sp
+\fBmstpctl\-portadminedge\fP
+.INDENT 0.0
+.INDENT 3.5
+\fBhelp\fP: enable/disable initial edge state of the port
+.sp
+\fBrequired\fP: False
+.sp
+\fBdefault\fP: no
+.sp
+\fBvalidvals\fP: yes,no
+.INDENT 0.0
+.TP
+.B \fBexample\fP:
+mstpctl\-portadminedge swp1=no swp2=no
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.sp
+\fBmstpctl\-maxage\fP
+.INDENT 0.0
+.INDENT 3.5
+\fBhelp\fP: max message age
+.sp
+\fBrequired\fP: False
+.sp
+\fBdefault\fP: 20
+.INDENT 0.0
+.TP
+.B \fBexample\fP:
+mstpctl\-maxage 20
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.sp
+\fBmstpctl\-maxhops\fP
+.INDENT 0.0
+.INDENT 3.5
+\fBhelp\fP: bridge max hops
+.sp
+\fBrequired\fP: False
+.sp
+\fBdefault\fP: 15
+.INDENT 0.0
+.TP
+.B \fBexample\fP:
+mstpctl\-maxhops 15
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.sp
+\fBmstpctl\-portrestrtcn\fP
+.INDENT 0.0
+.INDENT 3.5
+\fBhelp\fP: enable/disable port ability to propagate received to
+pology change notification of the port
+.sp
+\fBrequired\fP: False
+.sp
+\fBdefault\fP: no
+.sp
+\fBvalidvals\fP: yes,no
+.INDENT 0.0
+.TP
+.B \fBexample\fP:
+mstpctl\-portrestrtcn swp1=no swp2=no
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.sp
+\fBmstpctl\-portpathcost\fP
+.INDENT 0.0
+.INDENT 3.5
+\fBhelp\fP: bridge port path cost
+.sp
+\fBrequired\fP: False
+.sp
+\fBdefault\fP: 0
+.INDENT 0.0
+.TP
+.B \fBexample\fP:
+mstpctl\-portpathcost swp1=0 swp2=1
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.sp
+\fBmstpctl\-portadminage\fP
+.INDENT 0.0
+.INDENT 3.5
+\fBhelp\fP: bridge port admin age
+.sp
+\fBrequired\fP: False
+.sp
+\fBdefault\fP: no
+.sp
+\fBvalidvals\fP: yes,no
+.INDENT 0.0
+.TP
+.B \fBexample\fP:
+mstpctl\-portadminage swp1=no swp2=no
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.sp
+\fBmstpctl\-portbpdufilter\fP
+.INDENT 0.0
+.INDENT 3.5
+\fBhelp\fP: enable/disable bpdu filter on a port
+.sp
+\fBrequired\fP: False
+.sp
+\fBdefault\fP: no
+.sp
+\fBvalidvals\fP: yes,no
+.INDENT 0.0
+.TP
+.B \fBexample\fP:
+mstpctl\-portbpdufilter swp1=no swp2=no
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.sp
+\fBmstpctl\-forcevers\fP
+.INDENT 0.0
+.INDENT 3.5
+\fBhelp\fP: bridge force stp version
+.sp
+\fBrequired\fP: False
+.sp
+\fBdefault\fP: rstp
+.INDENT 0.0
+.TP
+.B \fBexample\fP:
+mstpctl\-forcevers rstp
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.sp
+\fBmstpctl\-treeportcost\fP
+.INDENT 0.0
+.INDENT 3.5
+\fBhelp\fP: port tree cost
+.sp
+\fBrequired\fP: False
+.UNINDENT
+.UNINDENT
+.sp
+\fBmstpctl\-bpduguard\fP
+.INDENT 0.0
+.INDENT 3.5
+\fBhelp\fP: enable/disable bpduguard
+.sp
+\fBrequired\fP: False
+.sp
+\fBdefault\fP: no
+.sp
+\fBvalidvals\fP: yes,no
+.INDENT 0.0
+.TP
+.B \fBexample\fP:
+mstpctl\-bpduguard swp1=no swp2=no
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.sp
+\fBvlan\fP: vlan module configures vlan interfaces.This module under
+stands vlan interfaces with dot notations. eg swp1.100. Vlan inter
+faces with any other names need to have raw device and vlan id att
+ributes
+.INDENT 0.0
+.INDENT 3.5
+\fBvlan\-id\fP
+.INDENT 0.0
+.INDENT 3.5
+\fBhelp\fP: vlan id
+.sp
+\fBrequired\fP: False
+.UNINDENT
+.UNINDENT
+.sp
+\fBvlan\-raw\-device\fP
+.INDENT 0.0
+.INDENT 3.5
+\fBhelp\fP: vlan raw device
+.sp
+\fBrequired\fP: False
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.sp
+\fBifenslave\fP: bond configuration module
+.INDENT 0.0
+.INDENT 3.5
+\fBbond\-miimon\fP
+.INDENT 0.0
+.INDENT 3.5
+\fBhelp\fP: bond miimon
+.sp
+\fBrequired\fP: False
+.sp
+\fBdefault\fP: 0
+.sp
+validrange: 0\-255
+.INDENT 0.0
+.TP
+.B \fBexample\fP:
+bond\-miimon 0
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.sp
+\fBbond\-slaves\fP
+.INDENT 0.0
+.INDENT 3.5
+\fBhelp\fP: bond slaves
+.sp
+\fBrequired\fP: True
+.INDENT 0.0
+.TP
+.B \fBexample\fP:
+bond\-slaves swp1 swp2
+.sp
+bond\-slaves glob swp1\-2
+.sp
+bond\-slaves regex (swp[1|2)
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.sp
+\fBbond\-mode\fP
+.INDENT 0.0
+.INDENT 3.5
+\fBhelp\fP: bond mode
+.sp
+\fBrequired\fP: False
+.sp
+\fBdefault\fP: balance\-rr
+.sp
+\fBvalidvals\fP: balance\-rr,active\-backup,balance\-xor,broadcast,802.3ad,balance\-tlb,balance\-alb
+.INDENT 0.0
+.TP
+.B \fBexample\fP:
+bond\-mode 802.3ad
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.sp
+\fBbond\-num\-grat\-arp\fP
+.INDENT 0.0
+.INDENT 3.5
+\fBhelp\fP: bond use carrier
+.sp
+\fBrequired\fP: False
+.sp
+\fBdefault\fP: 1
+.sp
+validrange: 0\-255
+.INDENT 0.0
+.TP
+.B \fBexample\fP:
+bond\-num\-grat\-arp 1
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.sp
+\fBbond\-ad\-sys\-mac\-addr\fP
+.INDENT 0.0
+.INDENT 3.5
+\fBhelp\fP: 802.3ad system mac address
+.sp
+\fBrequired\fP: False
+.sp
+\fBdefault\fP: 00:00:00:00:00:00
+.INDENT 0.0
+.TP
+.B \fBexample\fP:
+bond\-ad\-sys\-mac\-addr 00:00:00:00:00:00
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.sp
+\fBbond\-use\-carrier\fP
+.INDENT 0.0
+.INDENT 3.5
+\fBhelp\fP: bond use carrier
+.sp
+\fBrequired\fP: False
+.sp
+\fBdefault\fP: 1
+.sp
+\fBvalidvals\fP: 0,1
+.INDENT 0.0
+.TP
+.B \fBexample\fP:
+bond\-use\-carrier 1
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.sp
+\fBbond\-lacp\-rate\fP
+.INDENT 0.0
+.INDENT 3.5
+\fBhelp\fP: bond use carrier
+.sp
+\fBrequired\fP: False
+.sp
+\fBdefault\fP: 0
+.sp
+\fBvalidvals\fP: 0,1
+.INDENT 0.0
+.TP
+.B \fBexample\fP:
+bond\-lacp\-rate 0
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.sp
+\fBbond\-min\-links\fP
+.INDENT 0.0
+.INDENT 3.5
+\fBhelp\fP: bond min links
+.sp
+\fBrequired\fP: False
+.sp
+\fBdefault\fP: 0
+.INDENT 0.0
+.TP
+.B \fBexample\fP:
+bond\-min\-links 0
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.sp
+\fBbond\-num\-unsol\-na\fP
+.INDENT 0.0
+.INDENT 3.5
+\fBhelp\fP: bond slave devices
+.sp
+\fBrequired\fP: False
+.sp
+\fBdefault\fP: 1
+.sp
+validrange: 0\-255
+.INDENT 0.0
+.TP
+.B \fBexample\fP:
+bond\-num\-unsol\-na 1
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.sp
+\fBbond\-ad\-sys\-priority\fP
+.INDENT 0.0
+.INDENT 3.5
+\fBhelp\fP: 802.3ad system priority
+.sp
+\fBrequired\fP: False
+.sp
+\fBdefault\fP: 65535
+.INDENT 0.0
+.TP
+.B \fBexample\fP:
+bond\-ad\-sys\-priority 65535
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.sp
+\fBbond\-xmit\-hash\-policy\fP
+.INDENT 0.0
+.INDENT 3.5
+\fBhelp\fP: bond slave devices
+.sp
+\fBrequired\fP: False
+.sp
+\fBdefault\fP: layer2
+.sp
+\fBvalidvals\fP: layer2,layer3+4,layer2+3
+.INDENT 0.0
+.TP
+.B \fBexample\fP:
+bond\-xmit\-hash\-policy layer2
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.sp
+\fBaddress\fP: address configuration module for interfaces
+.INDENT 0.0
+.INDENT 3.5
+\fBbroadcast\fP
+.INDENT 0.0
+.INDENT 3.5
+\fBhelp\fP: broadcast address
+.sp
+\fBrequired\fP: False
+.INDENT 0.0
+.TP
+.B \fBexample\fP:
+broadcast 10.0.1.255
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.sp
+\fBhwaddress\fP
+.INDENT 0.0
+.INDENT 3.5
+\fBhelp\fP: hw address
+.sp
+\fBrequired\fP: False
+.INDENT 0.0
+.TP
+.B \fBexample\fP:
+hwaddress 44:38:39:00:27:b8
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.sp
+\fBalias\fP
+.INDENT 0.0
+.INDENT 3.5
+\fBhelp\fP: description/alias
+.sp
+\fBrequired\fP: False
+.INDENT 0.0
+.TP
+.B \fBexample\fP:
+alias testnetwork
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.sp
+\fBaddress\fP
+.INDENT 0.0
+.INDENT 3.5
+\fBhelp\fP: ipv4 or ipv6 addresses
+.sp
+\fBrequired\fP: False
+.INDENT 0.0
+.TP
+.B \fBexample\fP:
+address 10.0.12.3/24
+.sp
+address 2000:1000:1000:1000:3::5/128
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.sp
+\fBscope\fP
+.INDENT 0.0
+.INDENT 3.5
+\fBhelp\fP: scope
+.sp
+\fBrequired\fP: False
+.INDENT 0.0
+.TP
+.B \fBexample\fP:
+scope host
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.sp
+\fBpreferred\-lifetime\fP
+.INDENT 0.0
+.INDENT 3.5
+\fBhelp\fP: preferred lifetime
+.sp
+\fBrequired\fP: False
+.INDENT 0.0
+.TP
+.B \fBexample\fP:
+preferred\-lifetime forever
+.sp
+preferred\-lifetime 10
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.sp
+\fBgateway\fP
+.INDENT 0.0
+.INDENT 3.5
+\fBhelp\fP: default gateway
+.sp
+\fBrequired\fP: False
+.INDENT 0.0
+.TP
+.B \fBexample\fP:
+gateway 255.255.255.0
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.sp
+\fBmtu\fP
+.INDENT 0.0
+.INDENT 3.5
+\fBhelp\fP: interface mtu
+.sp
+\fBrequired\fP: False
+.sp
+\fBdefault\fP: 1500
+.INDENT 0.0
+.TP
+.B \fBexample\fP:
+mtu 1600
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.SH SEE ALSO
+.INDENT 0.0
+.INDENT 3.5
+interfaces(5),
+ifup(8),
+ip(8),
+mstpctl(8),
+brctl(8),
+ethtool(8)
+.UNINDENT
+.UNINDENT
+.SH AUTHOR
+roopa@cumulusnetworks.com
+.SH COPYRIGHT
+Copyright 2013 Cumulus Networks, Inc. All rights reserved.
+.\" Generated by docutils manpage writer.
+.\"
+.
--- /dev/null
+.\" Man page generated from reStructeredText.
+.
+.TH INTERFACES 5 "2014-02-05" "0.1" ""
+.SH NAME
+interfaces \- network interface configuration for ifupdown
+.
+.nr rst2man-indent-level 0
+.
+.de1 rstReportMargin
+\\$1 \\n[an-margin]
+level \\n[rst2man-indent-level]
+level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
+-
+\\n[rst2man-indent0]
+\\n[rst2man-indent1]
+\\n[rst2man-indent2]
+..
+.de1 INDENT
+.\" .rstReportMargin pre:
+. RS \\$1
+. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
+. nr rst2man-indent-level +1
+.\" .rstReportMargin post:
+..
+.de UNINDENT
+. RE
+.\" indent \\n[an-margin]
+.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
+.nr rst2man-indent-level -1
+.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
+.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
+..
+.SH DESCRIPTION
+.INDENT 0.0
+.INDENT 3.5
+\fB/etc/network/interfaces\fP contains network interface configuration
+information for the \fBifup(8)\fP, \fBifdown(8)\fP and \fBifquery(8)\fP commands.
+.sp
+This is where you configure how your system is connected to the network.
+.sp
+Lines starting with # are ignored. Note that end\-of\-line comments are
+NOT supported, comments must be on a line of their own.
+.sp
+A line may be extended across multiple lines by making the last character
+a backslash.
+.sp
+The file consists of zero or more "iface", "auto", "allow\-"
+and "source" stanzas. Here is an example:
+.sp
+.nf
+.ft C
+auto lo eth0
+allow\-hotplug eth1
+
+iface lo inet loopback
+
+source /etc/network/interfaces.d/bridges
+
+iface eth0 inet static
+ address 192.168.1.1/24
+ up flush\-mail
+
+iface eth1 inet dhcp
+.ft P
+.fi
+.sp
+Lines beginning with the word "auto" are used to identify the physical
+interfaces to be brought up when ifup is run with the \-a option.
+(This option is used by the system boot scripts.) Physical interface names
+should follow the word "auto" on the same line. There can be multiple
+"auto" stanzas.
+.sp
+Lines beginning with "allow\-" are used to identify interfaces that
+should be brought up automatically by various subsytems. This may be
+done using a command such as "ifup \-\-allow=hotplug eth0 eth1", which
+will only bring up eth0 or eth1 if it is listed in an "allow\-hotplug"
+line. Note that "allow\-auto" and "auto" are synonyms.
+.sp
+Lines beginning with "source" are used to include stanzas from other
+files, so configuration can be split into many files. The word "source"
+is followed by the path of file to be sourced. Shell wildcards can be
+used. Currently only supports absolute
+path names.
+.sp
+iface is normally given a interface name as its first non\-option
+argument.
+.sp
+The interface name is followed by the name of the address family that the
+interface uses. This will be "inet" for TCP/IP networking and inet6 for
+ipv6. Following that is the name of the method used to configure the
+interface.
+.sp
+ifupdown supports iface stanzas without a family or a method. This enables
+using the same stanza for inet and inet6 family addresses. And the method
+defaults to "static"
+.sp
+Additional interface options/attributes can be given on subsequent lines
+in the iface stanza. These options come from addon modules. see
+\fBifupdown\-addons\-interfaces(5)\fP for these options.
+.sp
+example bridge interface with additional attributes listed in the
+\fBifupdown\-addons\-interfaces(5)\fP man page:
+.sp
+.nf
+.ft C
+auto br0
+iface br0
+ address 12.0.0.4/24
+ address 2000:1000:1000:1000:3::5/128
+ bridge\-ports swp1 swp2 swp3
+ bridge\-stp on
+.ft P
+.fi
+.sp
+ifupdown supports python\-mako style templates in the interfaces file.
+See examples section for details.
+.sp
+See \fB/usr/share/doc/python\-ifupdown2/examples/\fP for \fBinterfaces(5)\fP
+file examples and interfaces file generation scripts.
+.UNINDENT
+.UNINDENT
+.SH METHODS
+.INDENT 0.0
+.INDENT 3.5
+Both \fBinet\fP and \fBinet6\fP address family interfaces can use the following
+methods (However they are not required):
+.INDENT 0.0
+.TP
+.B The loopback Method
+This method may be used to define the loopback interface.
+.TP
+.B The static Method
+This method may be used to define ethernet interfaces with
+statically allocated addresses.
+.TP
+.B The dhcp Method
+This method may be used to obtain an address via DHCP.
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.SH BUILTIN INTERFACES
+.INDENT 0.0
+.INDENT 3.5
+\fBiface\fP sections for some interfaces like physical interfaces or vlan
+interfaces in dot notation (like eth1.100) are understood by ifupdown.
+These interfaces do not need an entry in the interfaces file if
+they are dependents of other interfaces and dont need any specific
+configurations like addresses etc.
+.UNINDENT
+.UNINDENT
+.SH EXAMPLES
+.INDENT 0.0
+.INDENT 3.5
+Sample /etc/network/interfaces file:
+.sp
+.nf
+.ft C
+auto lo
+iface lo
+ address 192.168.2.0/24
+ address 2001:dee:eeee:1::4/128
+
+auto eth0
+iface eth0 inet dhcp
+
+auto eth1
+iface eth1 inet manual
+ address 192.168.2.0/24
+ address 2001:dee:eeee:1::4/128
+
+# source files from a directory /etc/network/interfaces.d
+source /etc/network/interfaces.d/*
+
+# Using mako style templates
+% for v in [11,12]:
+ auto vlan${v}
+ iface vlan${v} inet static
+ address 10.20.${v}.3/24
+% endfor
+.ft P
+.fi
+.sp
+For additional syntax and examples see \fBifupdown\-addons\-interfaces(5)\fP
+.UNINDENT
+.UNINDENT
+.SH FILES
+.INDENT 0.0
+.INDENT 3.5
+/etc/network/interfaces
+.UNINDENT
+.UNINDENT
+.SH SEE ALSO
+.INDENT 0.0
+.INDENT 3.5
+ifupdown\-addons\-interfaces(5),
+ifup(8),
+ifquery(8),
+ifreload(8)
+.UNINDENT
+.UNINDENT
+.SH AUTHOR
+Roopa Prabhu <roopa@cumulusnetworks.com>
+.SH COPYRIGHT
+Copyright 2014 Cumulus Networks, Inc. All rights reserved.
+.\" Generated by docutils manpage writer.
+.\"
+.
import ConfigParser
import StringIO
import logging
+import logging.handlers
+import resource
from ifupdown.ifupdownmain import *
from ifupdown.utils import *
ifupdown_handle.up(['up'], args.all, args.CLASS, iflist,
excludepats=args.excludepats,
printdependency=args.printdependency,
- syntaxcheck=args.syntaxcheck)
+ syntaxcheck=args.syntaxcheck, type=args.type,
+ skipupperifaces=args.skipupperifaces)
else:
ifupdown_handle.up(['pre-up', 'up', 'post-up'],
args.all, args.CLASS, iflist,
excludepats=args.excludepats,
printdependency=args.printdependency,
- syntaxcheck=args.syntaxcheck)
+ syntaxcheck=args.syntaxcheck, type=args.type,
+ skipupperifaces=args.skipupperifaces)
except:
raise
args.all, args.CLASS, iflist,
excludepats=args.excludepats,
printdependency=args.printdependency,
- usecurrentconfig=args.usecurrentconfig)
+ usecurrentconfig=args.usecurrentconfig,
+ type=args.type)
except:
raise
ifupdown_handle.query([qop], args.all, args.CLASS, iflist,
excludepats=args.excludepats,
printdependency=args.printdependency,
- format=args.format)
+ format=args.format, type=args.type)
except:
raise
perfmode=args.perfmode)
ifupdown_handle.reload(['pre-up', 'up', 'post-up'],
['pre-down', 'down', 'post-down'],
- args.all, None, None,
+ auto=args.all, allow=None, ifacenames=None,
excludepats=args.excludepats,
- usecurrentconfig=args.usecurrentconfig)
+ usecurrentconfig=args.usecurrentconfig,
+ currentlyup=args.currentlyup)
except:
raise
log_level = logging.DEBUG
try:
- logging.basicConfig(level=log_level,
- format='%(levelname)s: %(message)s')
- logging.addLevelName(logging.ERROR, 'error')
- logging.addLevelName(logging.WARNING, 'warning')
- logging.addLevelName(logging.DEBUG, 'debug')
- logging.addLevelName(logging.INFO, 'info')
- logger = logging.getLogger('ifupdown')
+ if hasattr(args, 'syslog') and args.syslog:
+ root_logger = logging.getLogger()
+ syslog_handler = logging.handlers.SysLogHandler(address='/dev/log',
+ facility=logging.handlers.SysLogHandler.LOG_DAEMON)
+ logging.addLevelName(logging.ERROR, 'error')
+ logging.addLevelName(logging.WARNING, 'warning')
+ logging.addLevelName(logging.DEBUG, 'debug')
+ logging.addLevelName(logging.INFO, 'info')
+ root_logger.setLevel(log_level)
+ syslog_handler.setFormatter(logging.Formatter(
+ '%(name)s: %(levelname)s: %(message)s'))
+ root_logger.addHandler(syslog_handler)
+ logger = logging.getLogger('ifupdown')
+ else:
+ logging.basicConfig(level=log_level,
+ format='%(levelname)s: %(message)s')
+ logging.addLevelName(logging.ERROR, 'error')
+ logging.addLevelName(logging.WARNING, 'warning')
+ logging.addLevelName(logging.DEBUG, 'debug')
+ logging.addLevelName(logging.INFO, 'info')
+ logger = logging.getLogger('ifupdown')
except:
raise
default='native',
choices=['native', 'json'],
help='interfaces file format')
+ argparser.add_argument('-T', '--type',
+ dest='type',
+ default=None,
+ choices=['iface', 'vlan'],
+ help='type of interface entry (iface or vlan).' +
+ 'This option can be used in case of ambiguity between ' +
+ 'a vlan interface and an iface interface of the same name')
def update_ifupdown_argparser(argparser):
""" common arg parser for ifup and ifdown """
argparser.add_argument('-f', '--force', dest='force',
action='store_true',
help='force run all operations')
+ argparser.add_argument('-l', '--syslog', dest='syslog',
+ action='store_true',
+ help=argparse.SUPPRESS)
group = argparser.add_mutually_exclusive_group(required=False)
group.add_argument('-n', '--no-act', dest='noact',
action='store_true', help='print out what would happen,' +
argparser.add_argument('-s', '--syntax-check', dest='syntaxcheck',
action='store_true',
help='Only run the interfaces file parser')
+ argparser.add_argument('-k', '--skip-upperifaces', dest='skipupperifaces',
+ action='store_true',
+ help='ifup by default tries to add newly created interfaces' +
+ ' into its upper/parent interfaces. Eg. if a bridge port is' +
+ ' created as a result of ifup on the port, ifup automatically' +
+ ' adds the port to the bridge. This option can be used to ' +
+ 'disable this default behaviour')
update_ifupdown_argparser(argparser)
def update_ifdown_argparser(argparser):
def update_ifreload_argparser(argparser):
""" parser for ifreload """
- argparser.add_argument('-a', '--all', action='store_true', required=True,
+ group = argparser.add_mutually_exclusive_group(required=True)
+ group.add_argument('-a', '--all', action='store_true',
help='process all interfaces marked \"auto\"')
+ group.add_argument('-c', '--currently-up', dest='currentlyup',
+ action='store_true',
+ help='only reload auto and other interfaces that are ' +
+ 'currently up')
argparser.add_argument('iflist', metavar='IFACE',
nargs='*', help=argparse.SUPPRESS)
argparser.add_argument('-n', '--no-act', dest='noact',
' only look at the current interfaces file. Useful when your ' +
'state file is corrupted or you want down to use the latest '
'from the interfaces file')
+ argparser.add_argument('-l', '--syslog', dest='syslog',
+ action='store_true',
+ help=argparse.SUPPRESS)
+ argparser.add_argument('-f', '--force', dest='force',
+ action='store_true',
+ help='force run all operations')
def parse_args(argsv, op):
if op == 'query':
if op == 'query' and args.syntaxhelp:
return True
if op == 'reload':
- if not args.all:
- print '\'-a\' option is required'
+ if not args.all and not args.currentlyup:
+ print '\'-a\' or \'-c\' option is required'
return False
elif (not args.iflist and
not args.all and not args.CLASS):
return False
return True
-def read_config():
+def read_config(args):
global configmap_g
config = open(configfile, 'r').read()
parser.readfp(configFP)
configmap_g = dict(parser.items('ifupdown2'))
+ # Preprocess config map
+ configval = configmap_g.get('multiple_vlan_aware_bridge_support', '0')
+ if configval == '0':
+ # if multiple bridges not allowed, set the bridge-vlan-aware
+ # attribute in the 'no_repeats' config, so that the ifupdownmain
+ # module can catch it appropriately
+ configmap_g['no_repeats'] = {'bridge-vlan-aware' : 'yes'}
+
+
+ configval = configmap_g.get('link_master_slave', '0')
+ if configval == '1':
+ # link_master_slave is only valid when all is set
+ if hasattr(args, 'all') and not args.all:
+ configmap_g['link_master_slave'] = '0'
+
+ configval = configmap_g.get('delay_admin_state_change', '0')
+ if configval == '1':
+ # reset link_master_slave if delay_admin_state_change is on
+ configmap_g['link_master_slave'] = '0'
+
def main(argv):
""" main function """
args = None
print 'Another instance of this program is already running.'
exit(0)
- read_config()
+ read_config(args)
init(args)
handlers.get(op)(args)
except Exception, e:
if __name__ == "__main__":
-
# required during boot
os.putenv('PATH', ENVPATH)
+ resource.setrlimit(resource.RLIMIT_CORE, (0,0))
main(sys.argv)
author='Roopa Prabhu',
author_email='roopa@cumulusnetworks.com',
url='cumulusnetworks.com',
- packages=['ifupdown'],
+ packages=['ifupdown', 'ifupdownaddons'],
scripts = ['sbin/ifupdown'],
- install_requires = ['python-gvgen'],
+ install_requires = ['python-gvgen', 'python-argcomplete', 'python-ipaddr'],
data_files=[('share/man/man8/',
['man/ifup.8', 'man/ifquery.8', 'man/ifreload.8']),
('share/man/man5/',
- ['man/interfaces.5']),
+ ['man/interfaces.5', 'man/ifupdown-addons-interfaces.5']),
('/etc/init.d/',
['init.d/networking']),
('/sbin/', ['sbin/ifupdown']),
('/etc/network/ifupdown2/',
['config/ifupdown2.conf']),
+ ('/etc/default/',
+ ['config/networking']),
('/usr/share/python-ifupdown2/',
['docs/examples/generate_interfaces.py']),
('/usr/share/doc/python-ifupdown2/examples/',
['docs/examples/interfaces',
'docs/examples/interfaces_bridge_template_func',
- 'docs/examples/interfaces_with_template']),
- ('/etc/bash_completion.d/', ['completion/ifup'])]
+ 'docs/examples/interfaces_with_template',
+ 'docs/examples/interfaces_bridge_igmp_mstp']),
+ ('/usr/share/doc/python-ifupdown2/examples/vlan_aware_bridges',
+ ['docs/examples/vlan_aware_bridges/interfaces.basic',
+ 'docs/examples/vlan_aware_bridges/interfaces.vlan_prune_and_access_ports',
+ 'docs/examples/vlan_aware_bridges/interfaces.with_bonds',
+ '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/mstpctl.py', 'addons/address.py',
+ 'addons/dhcp.py', 'addons/usercmds.py',
+ 'addons/ethtool.py', 'addons/loopback.py',
+ 'addons/addressvirtual.py', 'addons/vxlan.py',
+ 'addons/bridgevlan.py']),
+ ('/var/lib/ifupdownaddons/', ['config/addons.conf'])
+ ]
)