]> git.proxmox.com Git - mirror_ifupdown2.git/commitdiff
Merge 'vlan filtering bridge + vxlan + mlag + vrr' support from internal
authorRoopa Prabhu <roopa@cumulusnetworks.com>
Sat, 7 Mar 2015 05:46:10 +0000 (21:46 -0800)
committerRoopa Prabhu <roopa@cumulusnetworks.com>
Sat, 7 Mar 2015 05:46:10 +0000 (21:46 -0800)
tree to external

This also combines python-ifupdown2 and python-ifupdown2-addons package
into a single python-ifupdown2 package

63 files changed:
ifupdown2/README.rst
ifupdown2/addons/address.py [new file with mode: 0644]
ifupdown2/addons/addressvirtual.py [new file with mode: 0644]
ifupdown2/addons/bridge.py [new file with mode: 0644]
ifupdown2/addons/bridgevlan.py [new file with mode: 0644]
ifupdown2/addons/dhcp.py [new file with mode: 0644]
ifupdown2/addons/ethtool.py [new file with mode: 0644]
ifupdown2/addons/ifenslave.py [new file with mode: 0644]
ifupdown2/addons/loopback.py [new file with mode: 0644]
ifupdown2/addons/mstpctl.py [new file with mode: 0644]
ifupdown2/addons/usercmds.py [new file with mode: 0644]
ifupdown2/addons/vlan.py [new file with mode: 0644]
ifupdown2/addons/vrrpd.py [new file with mode: 0644]
ifupdown2/addons/vxlan.py [new file with mode: 0644]
ifupdown2/config/addons.conf [new file with mode: 0644]
ifupdown2/config/ifupdown2.conf
ifupdown2/config/networking [new file with mode: 0644]
ifupdown2/debian/python-ifupdown2.postinst
ifupdown2/docs.addons/Makefile [new file with mode: 0644]
ifupdown2/docs.addons/source/addonsapiref.rst [new file with mode: 0644]
ifupdown2/docs.addons/source/addonshelperapiref.rst [new file with mode: 0644]
ifupdown2/docs.addons/source/conf.py [new file with mode: 0644]
ifupdown2/docs.addons/source/developmentcorner.rst [new file with mode: 0644]
ifupdown2/docs.addons/source/gettingstarted.rst [new file with mode: 0644]
ifupdown2/docs.addons/source/index.rst [new file with mode: 0644]
ifupdown2/docs.addons/source/intro.rst [new file with mode: 0644]
ifupdown2/docs/examples/interfaces_bridge_igmp_mstp
ifupdown2/docs/examples/vlan_aware_bridges/interfaces.basic [new file with mode: 0644]
ifupdown2/docs/examples/vlan_aware_bridges/interfaces.vlan_prune_and_access_ports [new file with mode: 0644]
ifupdown2/docs/examples/vlan_aware_bridges/interfaces.with_bonds [new file with mode: 0644]
ifupdown2/docs/examples/vlan_aware_bridges/interfaces.with_clag [new file with mode: 0644]
ifupdown2/ifupdown/iface.py
ifupdown2/ifupdown/iff.py [new file with mode: 0644]
ifupdown2/ifupdown/ifupdownbase.py
ifupdown2/ifupdown/ifupdownmain.py
ifupdown2/ifupdown/netlink.py [new file with mode: 0644]
ifupdown2/ifupdown/networkinterfaces.py
ifupdown2/ifupdown/rtnetlink.py [new file with mode: 0644]
ifupdown2/ifupdown/rtnetlink_api.py [new file with mode: 0644]
ifupdown2/ifupdown/scheduler.py
ifupdown2/ifupdown/statemanager.py
ifupdown2/ifupdown/utils.py
ifupdown2/ifupdownaddons/__init__.py [new file with mode: 0644]
ifupdown2/ifupdownaddons/bridgeutils.py [new file with mode: 0644]
ifupdown2/ifupdownaddons/cache.py [new file with mode: 0644]
ifupdown2/ifupdownaddons/dhclient.py [new file with mode: 0644]
ifupdown2/ifupdownaddons/ifenslaveutil.py [new file with mode: 0644]
ifupdown2/ifupdownaddons/iproute2.py [new file with mode: 0644]
ifupdown2/ifupdownaddons/modulebase.py [new file with mode: 0644]
ifupdown2/ifupdownaddons/mstpctlutil.py [new file with mode: 0644]
ifupdown2/ifupdownaddons/utilsbase.py [new file with mode: 0644]
ifupdown2/init.d/networking
ifupdown2/man.rst/ifquery.8.rst
ifupdown2/man.rst/ifreload.8.rst
ifupdown2/man.rst/ifup.8.rst
ifupdown2/man.rst/ifupdown-addons-interfaces.5.rst [new file with mode: 0644]
ifupdown2/man/ifquery.8 [new file with mode: 0644]
ifupdown2/man/ifreload.8 [new file with mode: 0644]
ifupdown2/man/ifup.8 [new file with mode: 0644]
ifupdown2/man/ifupdown-addons-interfaces.5 [new file with mode: 0644]
ifupdown2/man/interfaces.5 [new file with mode: 0644]
ifupdown2/sbin/ifupdown
ifupdown2/setup.py

index 8ab82529b53ee6fa2415dbb02f027495103c8714..6187ec0f10fe58d2eb0d8f1e9730d87e087bfb4a 100644 (file)
@@ -9,11 +9,11 @@ The python-ifupdown2 package provides the infrastructure for
 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
@@ -26,7 +26,7 @@ pluggable python modules:
 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
@@ -72,7 +72,3 @@ install
 
 - 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
diff --git a/ifupdown2/addons/address.py b/ifupdown2/addons/address.py
new file mode 100644 (file)
index 0000000..627837e
--- /dev/null
@@ -0,0 +1,393 @@
+#!/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)
diff --git a/ifupdown2/addons/addressvirtual.py b/ifupdown2/addons/addressvirtual.py
new file mode 100644 (file)
index 0000000..98ac536
--- /dev/null
@@ -0,0 +1,321 @@
+#!/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)
diff --git a/ifupdown2/addons/bridge.py b/ifupdown2/addons/bridge.py
new file mode 100644 (file)
index 0000000..ebc9fbf
--- /dev/null
@@ -0,0 +1,1501 @@
+#!/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)
diff --git a/ifupdown2/addons/bridgevlan.py b/ifupdown2/addons/bridgevlan.py
new file mode 100644 (file)
index 0000000..44824c7
--- /dev/null
@@ -0,0 +1,170 @@
+#!/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)
diff --git a/ifupdown2/addons/dhcp.py b/ifupdown2/addons/dhcp.py
new file mode 100644 (file)
index 0000000..ed68466
--- /dev/null
@@ -0,0 +1,129 @@
+#!/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)
diff --git a/ifupdown2/addons/ethtool.py b/ifupdown2/addons/ethtool.py
new file mode 100644 (file)
index 0000000..579fdfa
--- /dev/null
@@ -0,0 +1,133 @@
+#!/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)
diff --git a/ifupdown2/addons/ifenslave.py b/ifupdown2/addons/ifenslave.py
new file mode 100644 (file)
index 0000000..6c5bda3
--- /dev/null
@@ -0,0 +1,438 @@
+#!/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)
diff --git a/ifupdown2/addons/loopback.py b/ifupdown2/addons/loopback.py
new file mode 100644 (file)
index 0000000..dd35917
--- /dev/null
@@ -0,0 +1,63 @@
+#!/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)
diff --git a/ifupdown2/addons/mstpctl.py b/ifupdown2/addons/mstpctl.py
new file mode 100644 (file)
index 0000000..33ea693
--- /dev/null
@@ -0,0 +1,770 @@
+#!/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)
diff --git a/ifupdown2/addons/usercmds.py b/ifupdown2/addons/usercmds.py
new file mode 100644 (file)
index 0000000..72915ea
--- /dev/null
@@ -0,0 +1,97 @@
+#!/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)
diff --git a/ifupdown2/addons/vlan.py b/ifupdown2/addons/vlan.py
new file mode 100644 (file)
index 0000000..6e8e2fc
--- /dev/null
@@ -0,0 +1,236 @@
+#!/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)
diff --git a/ifupdown2/addons/vrrpd.py b/ifupdown2/addons/vrrpd.py
new file mode 100644 (file)
index 0000000..9791141
--- /dev/null
@@ -0,0 +1,167 @@
+#!/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)
diff --git a/ifupdown2/addons/vxlan.py b/ifupdown2/addons/vxlan.py
new file mode 100644 (file)
index 0000000..a3a3fbc
--- /dev/null
@@ -0,0 +1,158 @@
+#!/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)
diff --git a/ifupdown2/config/addons.conf b/ifupdown2/config/addons.conf
new file mode 100644 (file)
index 0000000..e9aa487
--- /dev/null
@@ -0,0 +1,28 @@
+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
index bcd1401377f911fe1ba92333c1ac26831a06680e..8186e488afc936ba735bd6208b09eea09376b06f 100644 (file)
@@ -9,3 +9,32 @@ template_engine=mako
 
 # 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
+
diff --git a/ifupdown2/config/networking b/ifupdown2/config/networking
new file mode 100644 (file)
index 0000000..8b0474c
--- /dev/null
@@ -0,0 +1,17 @@
+#
+#
+# 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=
index 924797565c8161db9047379bdb75244374a2f1f6..10ca49abe01e99d31503ff155cc8dae3a7cce52b 100644 (file)
@@ -50,10 +50,10 @@ case "$1" in
             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)
 
diff --git a/ifupdown2/docs.addons/Makefile b/ifupdown2/docs.addons/Makefile
new file mode 100644 (file)
index 0000000..9b5efd7
--- /dev/null
@@ -0,0 +1,153 @@
+# 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."
diff --git a/ifupdown2/docs.addons/source/addonsapiref.rst b/ifupdown2/docs.addons/source/addonsapiref.rst
new file mode 100644 (file)
index 0000000..0954d27
--- /dev/null
@@ -0,0 +1,61 @@
+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
diff --git a/ifupdown2/docs.addons/source/addonshelperapiref.rst b/ifupdown2/docs.addons/source/addonshelperapiref.rst
new file mode 100644 (file)
index 0000000..01d9a41
--- /dev/null
@@ -0,0 +1,44 @@
+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
diff --git a/ifupdown2/docs.addons/source/conf.py b/ifupdown2/docs.addons/source/conf.py
new file mode 100644 (file)
index 0000000..e9ddb8d
--- /dev/null
@@ -0,0 +1,247 @@
+# -*- 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'
diff --git a/ifupdown2/docs.addons/source/developmentcorner.rst b/ifupdown2/docs.addons/source/developmentcorner.rst
new file mode 100644 (file)
index 0000000..be5c563
--- /dev/null
@@ -0,0 +1,58 @@
+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
diff --git a/ifupdown2/docs.addons/source/gettingstarted.rst b/ifupdown2/docs.addons/source/gettingstarted.rst
new file mode 100644 (file)
index 0000000..e4345ca
--- /dev/null
@@ -0,0 +1,29 @@
+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
+
+
+
+
diff --git a/ifupdown2/docs.addons/source/index.rst b/ifupdown2/docs.addons/source/index.rst
new file mode 100644 (file)
index 0000000..27b6fd6
--- /dev/null
@@ -0,0 +1,25 @@
+.. 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`
+
diff --git a/ifupdown2/docs.addons/source/intro.rst b/ifupdown2/docs.addons/source/intro.rst
new file mode 100644 (file)
index 0000000..35f187e
--- /dev/null
@@ -0,0 +1,21 @@
+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.
index 5efb99cb8c18a3725a54a6e4ae7a3c9ac2508406..f4916f4f95c2049e3f026f967855d784b5754a49 100644 (file)
@@ -6,6 +6,9 @@
 # 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
diff --git a/ifupdown2/docs/examples/vlan_aware_bridges/interfaces.basic b/ifupdown2/docs/examples/vlan_aware_bridges/interfaces.basic
new file mode 100644 (file)
index 0000000..7506175
--- /dev/null
@@ -0,0 +1,25 @@
+#
+# 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
diff --git a/ifupdown2/docs/examples/vlan_aware_bridges/interfaces.vlan_prune_and_access_ports b/ifupdown2/docs/examples/vlan_aware_bridges/interfaces.vlan_prune_and_access_ports
new file mode 100644 (file)
index 0000000..9641a82
--- /dev/null
@@ -0,0 +1,59 @@
+#
+# 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
diff --git a/ifupdown2/docs/examples/vlan_aware_bridges/interfaces.with_bonds b/ifupdown2/docs/examples/vlan_aware_bridges/interfaces.with_bonds
new file mode 100644 (file)
index 0000000..f3425dd
--- /dev/null
@@ -0,0 +1,92 @@
+#
+# 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
diff --git a/ifupdown2/docs/examples/vlan_aware_bridges/interfaces.with_clag b/ifupdown2/docs/examples/vlan_aware_bridges/interfaces.with_clag
new file mode 100644 (file)
index 0000000..8cdccc9
--- /dev/null
@@ -0,0 +1,87 @@
+#
+# 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
index 93d541100cd87b3ca7daaeb62331a95adb74ce25..79840f9222b29376920486e7bdbe850df320a305 100644 (file)
@@ -17,6 +17,22 @@ from collections import OrderedDict
 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 """
 
@@ -172,8 +188,10 @@ class iface():
     """
 
     # 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'
 
@@ -185,13 +203,15 @@ class iface():
         """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 """
@@ -203,6 +223,12 @@ class iface():
         """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)
@@ -266,6 +292,17 @@ class iface():
             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)
@@ -300,6 +337,17 @@ class iface():
         """ 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)
 
@@ -310,14 +358,23 @@ class iface():
             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.
         
@@ -330,6 +387,7 @@ class iface():
         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
@@ -340,6 +398,7 @@ class iface():
                     if v != dstiface.config.get(k)): return False
         return True
 
+
     def __getstate__(self):
         odict = self.__dict__.copy()
         del odict['state']
@@ -353,6 +412,8 @@ class iface():
         del odict['raw_config']
         del odict['linkstate']
         del odict['env']
+        del odict['link_type']
+        del odict['link_kind']
         return odict
 
     def __setstate__(self, dict):
@@ -369,6 +430,8 @@ class iface():
         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 = '  '
@@ -402,43 +465,60 @@ class iface():
         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
diff --git a/ifupdown2/ifupdown/iff.py b/ifupdown2/ifupdown/iff.py
new file mode 100644 (file)
index 0000000..f191883
--- /dev/null
@@ -0,0 +1,36 @@
+#!/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
index efccfeb3870c77599818f64c71eee919a19b7345..2a6b4801bc6b8b433a6fed94d7835d263288e7de 100644 (file)
@@ -12,6 +12,7 @@ import subprocess
 import re
 import os
 from iface import *
+import rtnetlink_api as rtnetlink_api
 
 class ifupdownBase(object):
 
@@ -65,7 +66,7 @@ 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")
index 2c60bd1082ff938416b15b179aa78e668bf8e3ed..691e5f42da08c2962585d93e654ffe8b8cfec976 100644 (file)
@@ -34,8 +34,18 @@ from sets import Set
 
 _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 """
@@ -50,8 +60,8 @@ class ifupdownMain(ifupdownBase):
     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'
@@ -98,14 +108,46 @@ class ifupdownMain(ifupdownBase):
 
     # 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),
@@ -155,11 +197,21 @@ class ifupdownMain(ifupdownBase):
         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)
@@ -171,6 +223,8 @@ class ifupdownMain(ifupdownBase):
             self.load_scripts(self.scripts_dir)
         self.dependency_graph = OrderedDict({})
 
+        self._cache_no_repeats = {}
+
         if self.STATEMANAGER_ENABLE:
             try:
                 self.statemanager = stateManager()
@@ -181,10 +235,45 @@ class ifupdownMain(ifupdownBase):
                 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:
@@ -197,6 +286,20 @@ class ifupdownMain(ifupdownBase):
     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 """
@@ -204,6 +307,8 @@ class ifupdownMain(ifupdownBase):
         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]
@@ -215,6 +320,7 @@ class ifupdownMain(ifupdownBase):
         """
         ifaceobjcurr = iface()
         ifaceobjcurr.name = ifaceobj.name
+        ifaceobjcurr.type = ifaceobj.type
         ifaceobjcurr.lowerifaces = ifaceobj.lowerifaces
         ifaceobjcurr.priv_flags = ifaceobj.priv_flags
         ifaceobjcurr.auto = ifaceobj.auto
@@ -278,7 +384,7 @@ class ifupdownMain(ifupdownBase):
         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:
@@ -298,29 +404,34 @@ class ifupdownMain(ifupdownBase):
         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,
@@ -331,15 +442,14 @@ class ifupdownMain(ifupdownBase):
                     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 """
@@ -357,18 +467,39 @@ class ifupdownMain(ifupdownBase):
                 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]
@@ -376,8 +507,9 @@ class ifupdownMain(ifupdownBase):
         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):
@@ -394,7 +526,7 @@ class ifupdownMain(ifupdownBase):
 
     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):
@@ -430,10 +562,16 @@ class ifupdownMain(ifupdownBase):
         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
@@ -547,10 +685,9 @@ class ifupdownMain(ifupdownBase):
                 # 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,
@@ -558,22 +695,47 @@ class ifupdownMain(ifupdownBase):
                         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.
@@ -581,7 +743,6 @@ class ifupdownMain(ifupdownBase):
         interfaces are checked against the allow_classes and auto lists.
 
         """
-
         if excludepats:
             for e in excludepats:
                 if re.search(e, ifacename):
@@ -643,12 +804,39 @@ class ifupdownMain(ifupdownBase):
                 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
@@ -658,6 +846,8 @@ class ifupdownMain(ifupdownBase):
             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
@@ -674,9 +864,7 @@ class ifupdownMain(ifupdownBase):
             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()
@@ -696,15 +884,20 @@ class ifupdownMain(ifupdownBase):
             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
@@ -729,7 +922,7 @@ class ifupdownMain(ifupdownBase):
             # 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 ?)')
@@ -743,7 +936,7 @@ class ifupdownMain(ifupdownBase):
                                                 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)
@@ -755,14 +948,17 @@ class ifupdownMain(ifupdownBase):
         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':
@@ -787,8 +983,8 @@ class ifupdownMain(ifupdownBase):
                 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()
@@ -804,7 +1000,7 @@ class ifupdownMain(ifupdownBase):
                 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
@@ -825,17 +1021,91 @@ class ifupdownMain(ifupdownBase):
             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:
@@ -844,7 +1114,6 @@ class ifupdownMain(ifupdownBase):
         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
@@ -875,7 +1144,8 @@ class ifupdownMain(ifupdownBase):
             #     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
@@ -897,13 +1167,19 @@ class ifupdownMain(ifupdownBase):
                         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 ..')
 
@@ -918,13 +1194,27 @@ class ifupdownMain(ifupdownBase):
                                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():
@@ -985,7 +1275,14 @@ class ifupdownMain(ifupdownBase):
             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
@@ -1016,16 +1313,16 @@ class ifupdownMain(ifupdownBase):
         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'):
diff --git a/ifupdown2/ifupdown/netlink.py b/ifupdown2/ifupdown/netlink.py
new file mode 100644 (file)
index 0000000..5a01043
--- /dev/null
@@ -0,0 +1,241 @@
+#!/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()
index 46f905328560938e14bedb89b60e9a306a2e766d..f241e454c94e52f9cddf19f2ed649a42184521c9 100644 (file)
@@ -12,6 +12,8 @@ import logging
 import glob
 import re
 import os
+import copy
+from utils import utils
 from iface import *
 from template import templateEngine
 
@@ -23,6 +25,7 @@ class networkInterfaces():
     hotplugs = {}
     auto_ifaces = []
     callbacks = {}
+    auto_all = False
 
     _addrfams = {'inet' : ['static', 'manual', 'loopback', 'dhcp', 'dhcp6'],
                  'inet6' : ['static', 'manual', 'loopback', 'dhcp', 'dhcp6']}
@@ -74,6 +77,12 @@ class networkInterfaces():
         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):
@@ -138,7 +147,12 @@ class networkInterfaces():
         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,
@@ -151,14 +165,23 @@ class networkInterfaces():
             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))
@@ -188,17 +211,26 @@ class networkInterfaces():
         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)
@@ -238,22 +270,60 @@ class networkInterfaces():
             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
@@ -274,6 +344,10 @@ class networkInterfaces():
         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')
@@ -299,15 +373,13 @@ class networkInterfaces():
 
     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) +
@@ -340,6 +412,11 @@ class networkInterfaces():
             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:
diff --git a/ifupdown2/ifupdown/rtnetlink.py b/ifupdown2/ifupdown/rtnetlink.py
new file mode 100644 (file)
index 0000000..9b13ad5
--- /dev/null
@@ -0,0 +1,860 @@
+#!/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
diff --git a/ifupdown2/ifupdown/rtnetlink_api.py b/ifupdown2/ifupdown/rtnetlink_api.py
new file mode 100644 (file)
index 0000000..f9bba10
--- /dev/null
@@ -0,0 +1,237 @@
+#!/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())
index 7782f12fefd60eb4da3465809e9ca3526734a853..4b4bb57d41790c0fde47642fca685d4fbb4f214e 100644 (file)
@@ -19,6 +19,7 @@ from graph import *
 from collections import deque
 from threading import *
 from ifupdownbase import *
+from sets import Set
 
 class ifaceSchedulerFlags():
     """ Enumerates scheduler flags """
@@ -43,13 +44,19 @@ class ifaceScheduler():
         """ 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
@@ -62,15 +69,18 @@ class ifaceScheduler():
                         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),
@@ -78,10 +88,15 @@ class ifaceScheduler():
                     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'
@@ -96,10 +111,13 @@ class ifaceScheduler():
         """ 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
@@ -115,17 +133,26 @@ class ifaceScheduler():
             # 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,
@@ -182,10 +209,16 @@ class ifaceScheduler():
         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:
@@ -258,6 +291,11 @@ class ifaceScheduler():
         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)
@@ -294,6 +332,66 @@ class ifaceScheduler():
                 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):
@@ -321,7 +419,7 @@ class ifaceScheduler():
     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.
 
@@ -354,7 +452,7 @@ class ifaceScheduler():
         #
         # 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:
@@ -366,7 +464,9 @@ class ifaceScheduler():
             # 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)
@@ -403,14 +503,14 @@ class ifaceScheduler():
                 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]):
@@ -419,6 +519,5 @@ class ifaceScheduler():
             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
index 83d6e52744394b5b1f1ab3c3a5eabb861057ae5c..ef9ca7f1a168a1935592dc7fe91580504c1bcaff 100644 (file)
@@ -100,7 +100,8 @@ class stateManager():
             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:
index 9dd6910df239ff5b3027d8eb44bbf27035dca080..caf6583804a1b5e38e8aea383312e3577406e5d8 100644 (file)
@@ -8,6 +8,7 @@
 #
 import os
 import fcntl
+import re
 
 class utils():
 
@@ -29,4 +30,23 @@ 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
+
 
diff --git a/ifupdown2/ifupdownaddons/__init__.py b/ifupdown2/ifupdownaddons/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/ifupdown2/ifupdownaddons/bridgeutils.py b/ifupdown2/ifupdownaddons/bridgeutils.py
new file mode 100644 (file)
index 0000000..73f28b1
--- /dev/null
@@ -0,0 +1,501 @@
+#!/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 []
diff --git a/ifupdown2/ifupdownaddons/cache.py b/ifupdown2/ifupdownaddons/cache.py
new file mode 100644 (file)
index 0000000..61773b5
--- /dev/null
@@ -0,0 +1,90 @@
+#!/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))
diff --git a/ifupdown2/ifupdownaddons/dhclient.py b/ifupdown2/ifupdownaddons/dhclient.py
new file mode 100644 (file)
index 0000000..811ff40
--- /dev/null
@@ -0,0 +1,86 @@
+#!/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])
diff --git a/ifupdown2/ifupdownaddons/ifenslaveutil.py b/ifupdown2/ifupdownaddons/ifenslaveutil.py
new file mode 100644 (file)
index 0000000..c7e0d42
--- /dev/null
@@ -0,0 +1,421 @@
+#!/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)
diff --git a/ifupdown2/ifupdownaddons/iproute2.py b/ifupdown2/ifupdownaddons/iproute2.py
new file mode 100644 (file)
index 0000000..555b12f
--- /dev/null
@@ -0,0 +1,704 @@
+#!/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
diff --git a/ifupdown2/ifupdownaddons/modulebase.py b/ifupdown2/ifupdownaddons/modulebase.py
new file mode 100644 (file)
index 0000000..47df790
--- /dev/null
@@ -0,0 +1,363 @@
+#!/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
diff --git a/ifupdown2/ifupdownaddons/mstpctlutil.py b/ifupdown2/ifupdownaddons/mstpctlutil.py
new file mode 100644 (file)
index 0000000..4716195
--- /dev/null
@@ -0,0 +1,171 @@
+#!/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
diff --git a/ifupdown2/ifupdownaddons/utilsbase.py b/ifupdown2/ifupdownaddons/utilsbase.py
new file mode 100644 (file)
index 0000000..73fdfd5
--- /dev/null
@@ -0,0 +1,163 @@
+#!/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()
index df5e43fe7c13530e0405af7be1a781578f74e181..6e5e0704b24151f1aa1723659cae929ce3093b43 100644 (file)
@@ -22,15 +22,14 @@ SCRIPTNAME=/etc/init.d/$NAME
 . /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
@@ -51,20 +50,11 @@ gen_examples() {
     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() {
@@ -152,12 +142,7 @@ start)
        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 $?
        ;;
 
@@ -165,13 +150,11 @@ stop)
        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)
@@ -179,12 +162,17 @@ 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)
@@ -192,27 +180,19 @@ 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 $?
        ;;
 
 *)
index 1ed793969330fb89e0770fd0b93a47212a1ed83e..ba443ce6fbbe450d4bc6ecad8969943dff6eebba 100644 (file)
@@ -50,8 +50,7 @@ OPTIONS
     -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
@@ -66,6 +65,9 @@ OPTIONS
                           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
@@ -74,7 +76,6 @@ OPTIONS
 
     -x, --raw             print raw config file entries
 
-
     -o {native,json}, --format {native,json}
                           interface display format
 
index 9f17546846386702065e7a145e5aeef3a738c4dc..397a662b12dbba8ed68131f92920c3e974c29eba 100644 (file)
@@ -14,7 +14,7 @@ reload network interface configuration
 
 SYNOPSIS
 ========
-    ifreload [-h] [-a] [-v] [-d] [-f] [-n] 
+    ifreload [-h] (-a|-c) [-v] [-d] [-f] [-n] 
 
 DESCRIPTION
 ===========
@@ -27,6 +27,10 @@ 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
 =======
@@ -40,6 +44,11 @@ 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
@@ -50,6 +59,10 @@ EXAMPLES
 
     **service networking reload**
 
+    # reload all currently up interfaces without bringing any interfaces down
+
+    **service networking reload-currently-up**
+
 SEE ALSO
 ========
     ifup(8),
index 2176935a01b3e3be888d293bb218c2247ee36838..407b44bbb9d921cde238c4819123ce76c2acd09b 100644 (file)
@@ -72,8 +72,7 @@ OPTIONS
     -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
@@ -84,6 +83,13 @@ OPTIONS
                           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
diff --git a/ifupdown2/man.rst/ifupdown-addons-interfaces.5.rst b/ifupdown2/man.rst/ifupdown-addons-interfaces.5.rst
new file mode 100644 (file)
index 0000000..24d18c7
--- /dev/null
@@ -0,0 +1,1079 @@
+==========================
+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)
diff --git a/ifupdown2/man/ifquery.8 b/ifupdown2/man/ifquery.8
new file mode 100644 (file)
index 0000000..65e89e2
--- /dev/null
@@ -0,0 +1,230 @@
+.\" 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.
+.\" 
+.
diff --git a/ifupdown2/man/ifreload.8 b/ifupdown2/man/ifreload.8
new file mode 100644 (file)
index 0000000..a39da36
--- /dev/null
@@ -0,0 +1,116 @@
+.\" 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.
+.\" 
+.
diff --git a/ifupdown2/man/ifup.8 b/ifupdown2/man/ifup.8
new file mode 100644 (file)
index 0000000..ae72d9f
--- /dev/null
@@ -0,0 +1,269 @@
+.\" 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.
+.\" 
+.
diff --git a/ifupdown2/man/ifupdown-addons-interfaces.5 b/ifupdown2/man/ifupdown-addons-interfaces.5
new file mode 100644 (file)
index 0000000..86d71a7
--- /dev/null
@@ -0,0 +1,1343 @@
+.\" 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.
+.\" 
+.
diff --git a/ifupdown2/man/interfaces.5 b/ifupdown2/man/interfaces.5
new file mode 100644 (file)
index 0000000..68f7d48
--- /dev/null
@@ -0,0 +1,207 @@
+.\" 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.
+.\" 
+.
index 6ead2ed8e0fa5ee4092410139da75981de9e2b67..d6bdeb25491da3d7a8eb9e3e5177c366abe49bba 100755 (executable)
@@ -13,6 +13,8 @@ import argparse
 import ConfigParser
 import StringIO
 import logging
+import logging.handlers
+import resource
 from ifupdown.ifupdownmain import *
 from ifupdown.utils import *
 
@@ -49,13 +51,15 @@ def run_up(args):
             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
 
@@ -79,7 +83,8 @@ def run_down(args):
                              args.all, args.CLASS, iflist,
                              excludepats=args.excludepats,
                              printdependency=args.printdependency,
-                             usecurrentconfig=args.usecurrentconfig)
+                             usecurrentconfig=args.usecurrentconfig,
+                             type=args.type)
     except:
         raise
 
@@ -121,7 +126,7 @@ def run_query(args):
         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
 
@@ -135,9 +140,10 @@ def run_reload(args):
                                        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
 
@@ -152,13 +158,27 @@ def init(args):
         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
 
@@ -210,12 +230,22 @@ def update_argparser(argparser):
                 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,' +
@@ -232,6 +262,13 @@ def update_ifup_argparser(argparser):
     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):
@@ -275,8 +312,13 @@ def update_ifquery_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',
@@ -308,6 +350,12 @@ def update_ifreload_argparser(argparser):
                 ' 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':
@@ -345,8 +393,8 @@ def validate_args(op, args):
     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):
@@ -361,7 +409,7 @@ def validate_args(op, args):
         return False
     return True
 
-def read_config():
+def read_config(args):
     global configmap_g
 
     config = open(configfile, 'r').read()
@@ -371,6 +419,26 @@ def read_config():
     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
@@ -401,7 +469,7 @@ def main(argv):
             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:
@@ -422,7 +490,7 @@ def main(argv):
 
 if __name__ == "__main__":
 
-
     # required during boot
     os.putenv('PATH', ENVPATH)
+    resource.setrlimit(resource.RLIMIT_CORE, (0,0))
     main(sys.argv)
index b6ca032646d58623c2590bc40edd7add106fbfd9..58218e34bc792dfbb22bd45cdf25ac08231b4cec 100755 (executable)
@@ -6,23 +6,40 @@ setup(name='ifupdown2',
       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'])
+                  ]
       )