1
0
mirror of https://github.com/CumulusNetworks/ifupdown2.git synced 2024-05-06 15:54:50 +00:00

Move ifupdown2addons into ifupdown2 pacakge

Ticket: CM-3864
Reviewed By:
Testing Done: Tested build and install

open item:
- cleanup stale ifupdown2-addons package files
This commit is contained in:
Roopa Prabhu
2014-10-09 16:02:46 -07:00
parent 679e656768
commit 15ef32ea14
34 changed files with 8532 additions and 3 deletions

13
TODO.addons Normal file
View File

@ -0,0 +1,13 @@
TODO:
====
- run python code guideline checker
- more code documentation
- move all cache handling to decorators in ifupdownaddons package
- input validation (present in some cases not all)
- support the vlan0004 style vlans
- ifquery coverage. currently it is at 80%.
- vxlan module
- fix and release ifaddon utility to manage module priorities
- Deep compare in query for address module (does not compare address attributes like scope)
- Maybe a pure netlink backend
- improve caching

300
addons/address.py Normal file
View File

@ -0,0 +1,300 @@
#!/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']}}}
def __init__(self, *args, **kargs):
moduleBase.__init__(self, *args, **kargs)
self.ipcmd = None
def _inet_address_config(self, ifaceobj):
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):
# 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
try:
# release any stale dhcp addresses if present
if (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 = self.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()
self._inet_address_config(ifaceobj)
mtu = ifaceobj.get_attr_value_first('mtu')
if mtu:
self.ipcmd.link_set(ifaceobj.name, 'mtu', mtu)
hwaddress = ifaceobj.get_attr_value_first('hwaddress')
if hwaddress:
self.ipcmd.link_set(ifaceobj.name, 'address', hwaddress)
alias = ifaceobj.get_attr_value_first('alias')
if alias:
self.ipcmd.link_set_alias(ifaceobj.name, alias)
self.ipcmd.batch_commit()
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
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', "\'\'")
except Exception, e:
self.log_warn(str(e))
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 _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
self.query_n_update_ifaceobjcurr_attr(ifaceobj, ifaceobjcurr,
'mtu', self.ipcmd.link_get_mtu)
self.query_n_update_ifaceobjcurr_attr(ifaceobj, ifaceobjcurr,
'hwaddress', self.ipcmd.link_get_hwaddress)
self.query_n_update_ifaceobjcurr_attr(ifaceobj, ifaceobjcurr,
'alias', self.ipcmd.link_get_alias)
# compare addresses
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)
ifaceobjrunning.status = ifaceStatus.NOTFOUND
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):
""" 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.
"""
op_handler = self._run_ops.get(operation)
if not op_handler:
return
if (operation != 'query-running' and ifaceobj.addr_family and
ifaceobj.addr_family != 'inet' and
ifaceobj.addr_family != 'inet6'):
return
if (operation != 'query-running' and ifaceobj.addr_method and
ifaceobj.addr_method != 'static' and
ifaceobj.addr_method != 'loopback'):
return
self._init_command_handlers()
if operation == 'query-checkcurr':
op_handler(self, ifaceobj, query_ifaceobj)
else:
op_handler(self, ifaceobj)

169
addons/addressvirtual.py Normal file
View File

@ -0,0 +1,169 @@
#!/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 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
def _is_supported(self, ifaceobj):
if ifaceobj.get_attr_value_first('address-virtual'):
return True
return False
def _apply_address_config(self, ifaceobj, realifacename, address_virtual_list):
purge_existing = False if self.PERFMODE else True
self.ipcmd.batch_start()
av_idx = 0
macvlan_prefix = '%s-virt' %ifaceobj.name.replace('.', '-')
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
macvlan_ifacename = '%s-%d' %(macvlan_prefix, av_idx)
self.ipcmd.link_create_macvlan(macvlan_ifacename, realifacename)
self.ipcmd.link_set_hwaddress(macvlan_ifacename, av_attrs[0])
self.ipcmd.addr_add_multiple(macvlan_ifacename, av_attrs[1:],
purge_existing)
av_idx += 1
self.ipcmd.batch_commit()
def _remove_address_config(self, ifaceobj, ifacename):
if not self.ipcmd.link_exists(ifacename):
return
self.ipcmd.batch_start()
macvlan_prefix = '%s-virt' %ifacename.replace('.', '-')
for macvlan_ifacename in glob.glob("/sys/class/net/%s-*" %macvlan_prefix):
self.ipcmd.link_delete(os.path.basename(macvlan_ifacename))
self.ipcmd.batch_commit()
def _get_real_ifacename(self, ifaceobj):
realifacename = ifaceobj.name
if ifaceobj.type == ifaceType.BRIDGE_VLAN:
bridgename = ifaceobj.get_attr_value_first('bridge')
if bridgename:
realifacename = '%s.%s' %(bridgename, ifaceobj.priv_data)
return realifacename
def _up(self, ifaceobj):
realifacename = self._get_real_ifacename(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 any macvlan devices.
self._remove_address_config(ifaceobj, realifacename)
return
if not self.ipcmd.link_exists(realifacename):
self.log_warn('%s: target link %s does not exist'
%(ifaceobj.name, realifacename))
return
self._apply_address_config(ifaceobj, realifacename, address_virtual_list)
def _down(self, ifaceobj):
realifacename = self._get_real_ifacename(ifaceobj)
try:
self._remove_address_config(ifaceobj, realifacename)
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
realifacename = self._get_real_ifacename(ifaceobj)
av_idx = 0
macvlan_prefix = '%s-virt' %realifacename.replace('.', '-')
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 self.ipcmd.link_exists(macvlan_ifacename):
# XXX Check mac and ip address
rhwaddress = self.ipcmd.link_get_hwaddress(macvlan_ifacename)
raddrs = self.ipcmd.addr_get(macvlan_ifacename)
if rhwaddress == av_attrs[0] and raddrs == av_attrs[1:]:
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):
# Not implemented
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):
""" 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
self._init_command_handlers()
if operation == 'query-checkcurr':
op_handler(self, ifaceobj, query_ifaceobj)
else:
op_handler(self, ifaceobj)

895
addons/bridge.py Normal file
View File

@ -0,0 +1,895 @@
#!/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
import itertools
import re
class bridge(moduleBase):
""" ifupdown2 addon module to configure linux bridges """
_modinfo = { 'mhelp' : 'bridge configuration module',
'attrs' : {
'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',
'example' : ['bridge-mcqv4src 100=172.16.100.1 101=172.16.101.1']},
'bridge-portmcrouter' :
{ 'help' : 'set port multicast routers',
'default' : '1',
'example' : ['bridge-portmcrouter swp1=1 swp2=1']},
'bridge-portmcfl' :
{ 'help' : 'port multicast fast leave',
'default' : '0',
'example' : ['bridge-portmcfl swp1=0 swp2=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 vlans',
'example' : ['bridge-vids 4000']},
'bridge-port-vids' :
{ 'help' : 'bridge vlans',
'example' : ['bridge-port-vids bond0=1-1000,1010-1020']},
'bridge-port-pvids' :
{ 'help' : 'bridge port vlans',
'example' : ['bridge-port-pvids bond0=100 bond1=200']},
}}
def __init__(self, *args, **kargs):
moduleBase.__init__(self, *args, **kargs)
self.ipcmd = None
self.brctlcmd = None
def _is_bridge(self, ifaceobj):
if ifaceobj.get_attr_value_first('bridge-ports'):
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(
'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 _add_ports(self, ifaceobj):
bridgeports = self._get_bridge_port_list(ifaceobj)
runningbridgeports = []
self._process_bridge_waitport(ifaceobj, bridgeports)
# 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.write_file('/proc/sys/net/ipv6/conf/%s' %bridgeport +
'/disable_ipv6', '1')
self.ipcmd.addr_flush(bridgeport)
except Exception, e:
self.log_error(str(e))
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(self, ifaceobj):
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 _set_bridge_vidinfo(self, ifaceobj):
# Handle bridge vlan attrs
running_vidinfo = {}
if not self.PERFMODE:
running_vidinfo = self.ipcmd.bridge_port_vids_get_all()
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 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)))
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)
# 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():
self.brctlcmd.set_bridgeport_attrs(ifaceobj.name, port,
attrdict)
self._set_bridge_vidinfo(ifaceobj)
self._set_bridge_mcqv4src(ifaceobj)
self._process_bridge_maxwait(ifaceobj,
self._get_bridge_port_list(ifaceobj))
except Exception, e:
self.log_warn(str(e))
def _up(self, ifaceobj):
try:
porterr = False
porterrstr = ''
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()
self._apply_bridge_settings(ifaceobj)
except Exception, e:
self.log_error(str(e))
if porterr:
raise Exception(porterrstr)
def _down(self, ifaceobj):
try:
if ifaceobj.get_attr_value_first('bridge-ports'):
ports = self.brctlcmd.get_bridge_ports(ifaceobj.name)
if ports:
for p in ports:
proc_file = ('/proc/sys/net/ipv6/conf/%s' %p +
'/disable_ipv6')
self.write_file(proc_file, '0')
self.brctlcmd.delete_bridge(ifaceobj.name)
except Exception, e:
self.log_error(str(e))
def _query_running_vidinfo(self, ifaceobjrunning, ports):
running_attrs = {}
running_vidinfo = self.ipcmd.bridge_port_vids_get_all()
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_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):
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]
bridgevidinfo = self._query_running_vidinfo(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_vidinfo(self, ifaceobj, ifaceobjcurr):
err = 0
running_vidinfo = self.ipcmd.bridge_port_vids_get_all()
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)
# 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
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)
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)
def _query_check(self, ifaceobj, ifaceobjcurr):
if not self.brctlcmd.bridge_exists(ifaceobj.name):
self.logger.info('%s: bridge: does not exist' %(ifaceobj.name))
ifaceobjcurr.status = ifaceStatus.NOTFOUND
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-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_vidinfo(ifaceobj, ifaceobjcurr)
self._query_check_mcqv4src(ifaceobj, ifaceobjcurr)
def _query_running(self, ifaceobjrunning):
if not self.brctlcmd.bridge_exists(ifaceobjrunning.name):
return
ifaceobjrunning.update_config_dict(self._query_running_attrs(
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)
def run(self, ifaceobj, operation, query_ifaceobj=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
if (operation != 'query-running' and
not self._is_bridge(ifaceobj)):
return
self._init_command_handlers()
if operation == 'query-checkcurr':
op_handler(self, ifaceobj, query_ifaceobj)
else:
op_handler(self, ifaceobj)

942
addons/bridgevlanaware.py Normal file
View File

@ -0,0 +1,942 @@
#!/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
import itertools
import re
import os
class bridgevlanaware(moduleBase):
""" ifupdown2 addon module to configure linux bridges """
_modinfo = { 'mhelp' : 'bridge configuration module',
'attrs' : {
'bridge' :
{'help': 'bridge this interface is part of',
'example' : ['bridge br0']},
'bridge-vlan-aware' :
{'help': 'Is this a vlan aware bridge ?',
'default' : 'no',
'required' : False,
'example' : ['bridge-vlan-aware yes']},
'type' :
{'help': 'type of interface this module supports',
'example' : ['type bridge']},
'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-priority' :
{ 'help' : 'bridge port priority',
'example' : ['bridge-priority 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 172.16.100.1']},
'bridge-igmp-querier-src' :
{ 'help' : 'set per VLAN v4 multicast querier source address',
'example' : ['bridge-igmp-querier-src 172.16.100.1']},
'bridge-mcfl' :
{ 'help' : 'port multicast fast leave',
'default' : '0',
'example' : ['bridge-mcfl 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-vlan' :
{ 'help' : 'bridge vlans',
'example' : ['bridge-vlan 4000']},
'bridge-vlan-native' :
{ 'compat' : True,
'help' : 'bridge port vlan',
'example' : ['bridge-vlan-native 1']},
}}
def __init__(self, *args, **kargs):
moduleBase.__init__(self, *args, **kargs)
self.ipcmd = None
self.brctlcmd = None
def _is_bridge_port(self, ifaceobj):
if ifaceobj.get_attr_value_first('bridge'):
return True
return False
def _is_bridge(self, ifaceobj):
if ifaceobj.get_attr_value_first('type') == 'bridge':
return True
return False
def _is_bridge_vlan(self, ifaceobj):
if ifaceobj.type & ifaceType.BRIDGE_VLAN:
return True
return False
def get_dependent_ifacenames(self, ifaceobj, ifacenames_all=None):
bridge = ifaceobj.get_attr_value_first('bridge')
if bridge:
match = re.match("^%s-([\d]+)" %bridge, ifaceobj.name)
if match:
ifaceobj.priv_data = int(match.groups()[0], 10)
# XXX: mark this iface as a bridge_vlan iface
ifaceobj.type = ifaceType.BRIDGE_VLAN
return [bridge]
return None
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 _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 _add_ports(self, ifaceobj):
bridgeports = self._get_bridge_port_list(ifaceobj)
runningbridgeports = []
self._process_bridge_waitport(ifaceobj, bridgeports)
# 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.write_file('/proc/sys/net/ipv6/conf/%s' %bridgeport +
'/disable_ipv6', '1')
self.ipcmd.addr_flush(bridgeport)
except Exception, e:
self.log_error(str(e))
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(self, ifaceobj):
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, int(v, 10))
for v in mcqs.keys():
self.brctlcmd.set_mcqv4src(ifaceobj.name, int(v, 10), mcqs[v])
def _set_bridge_vidinfo(self, ifaceobj, isbridge=True):
# Handle bridge vlan attrs
running_vidinfo = {}
if not self.PERFMODE:
running_vidinfo = self.ipcmd.bridge_port_vids_get_all()
attrval = ifaceobj.get_attr_value_first('bridge-vlan')
if attrval:
vids = re.split(r'[\s\t]\s*', attrval)
#vids = attrval.split(',')
try:
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, isbridge)
if vids_to_add:
self.ipcmd.bridge_vids_add(ifaceobj.name,
vids_to_add, isbridge)
else:
self.ipcmd.bridge_vids_add(ifaceobj.name, vids,
isbridge)
except Exception, e:
self.log_warn('%s: failed to set vid `%s` (%s)'
%(ifaceobj.name, str(vids), str(e)))
else:
running_vids = running_vidinfo.get(ifaceobj.name, {}).get('vlan')
if running_vids:
self.ipcmd.bridge_vids_del(ifaceobj.name, running_vids)
# Install pvids
pvid = ifaceobj.get_attr_value_first('bridge-vlan-native')
if pvid:
try:
running_pvid = running_vidinfo.get(ifaceobj.name,
{}).get('pvid')
if running_pvid:
if running_pvid != pvid:
self.ipcmd.bridge_port_pvid_del(ifaceobj.name,
running_pvid)
else:
self.ipcmd.bridge_port_pvid_add(ifaceobj.name, pvid)
else:
self.ipcmd.bridge_port_pvid_add(ifaceobj.name, pvid)
except Exception, e:
self.log_warn('%s: failed to set pvid `%s` (%s)'
%(ifaceobj.name, pvid, str(e)))
def _apply_bridge_settings(self, ifaceobj):
try:
stp = ifaceobj.get_attr_value_first('bridge-stp')
if stp:
self.brctlcmd.set_stp(ifaceobj.name, stp)
# 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)
self._set_bridge_vidinfo(ifaceobj)
self._set_bridge_mcqv4src(ifaceobj)
#self._process_bridge_maxwait(ifaceobj,
# self._get_bridge_port_list(ifaceobj))
except Exception, e:
self.log_warn(str(e))
def _apply_bridge_port_settings(self, ifaceobj, bridge):
try:
# Use the brctlcmd bulk set method: first build a dictionary
# and then call set
portattrs = {}
for attrname, dstattrname in {'bridge-pathcost' : 'pathcost',
'bridge-prio' : 'portprio',
'bridge-mcrouter' : 'portmcrouter',
'bridge-mcfl' : 'portmcfl'}.items():
attrval = ifaceobj.get_attr_value_first(attrname)
if not attrval:
continue
portattrs[ifaceobj.name] = attrval
self.brctlcmd.set_bridgeport_attrs(bridge, ifaceobj.name, portattrs)
self._set_bridge_vidinfo(ifaceobj, isbridge=False)
except Exception, e:
self.log_warn(str(e))
def _apply_bridge_vlan_settings(self, ifaceobj, bridge):
mcq = ifaceobj.get_attrs_value_first(['bridge-mcqv4src',
'bridge-igmp-querier-src'])
if mcq:
running_mcq = None
if not self.PERFMODE:
running_mcq = self.brctlcmd.get_mcqv4src(bridge,
vlan=ifaceobj.priv_data)
if running_mcq != mcq:
self.brctlcmd.set_mcqv4src(bridge, ifaceobj.priv_data, mcq)
self.ipcmd.bridge_vids_add(bridge, [ifaceobj.priv_data], True)
def _delete_bridge_vlan_settings(self, ifaceobj, bridge):
# delete vlan from bridge
self.ipcmd.bridge_vids_del(bridge, [ifaceobj.priv_data], True)
mcq = ifaceobj.get_attr_value_first('bridge-mcqv4src')
if mcq:
self.brctlcmd.del_mcqv4src(bridge, ifaceobj.name)
def _up_bridge(self, ifaceobj):
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')
self._apply_bridge_settings(ifaceobj)
except Exception, e:
self.log_error(str(e))
def _up_bridge_port(self, ifaceobj, bridge):
try:
self.ipcmd.link_set(ifaceobj.name, 'master', bridge)
self._apply_bridge_port_settings(ifaceobj, bridge)
except Exception, e:
self.log_error(str(e))
def _up_bridge_vlan(self, ifaceobj, bridge):
purge_existing = False if self.PERFMODE else True
try:
address = ifaceobj.get_attr_value('address')
if address:
# Create a vlan device,
ifacename = '%s.%s' %(bridge, ifaceobj.priv_data)
if not self.ipcmd.link_exists(ifacename):
self.ipcmd.link_create_vlan(ifacename, bridge,
ifaceobj.priv_data)
purge_existing = False
hwaddress = ifaceobj.get_attr_value_first('hwaddress')
if hwaddress:
self.ipcmd.link_set_hwaddress(ifacename, hwaddress)
self.ipcmd.addr_add_multiple(ifacename, address, purge_existing)
self._apply_bridge_vlan_settings(ifaceobj, bridge)
except Exception, e:
self.log_error(str(e))
def _up(self, ifaceobj):
bridge = ifaceobj.get_attr_value_first('bridge')
if ifaceobj.type == ifaceType.BRIDGE_VLAN:
self._up_bridge_vlan(ifaceobj, bridge)
elif bridge:
self._up_bridge_port(ifaceobj, bridge)
elif self._is_bridge(ifaceobj):
self._up_bridge(ifaceobj)
else:
# Was this interface part of the bridge at some point and now
# got removed ?. If we attached it to the bridge last time
# we should release it
if os.path.exists('/sys/class/net/%s/brport' %ifaceobj.name):
bridgelink = os.readlink('/sys/class/net/%s/brport/bridge'
%ifaceobj.name)
if bridgelink:
bridge = os.path.basename(bridgelink)
if (not ifaceobj.upperifaces or
bridge not in ifaceobj.upperifaces):
# set nomaster
self.ipcmd.link_set(ifaceobj.name, 'nomaster')
def _down_bridge(self, ifaceobj):
try:
self.brctlcmd.delete_bridge(ifaceobj.name)
except Exception, e:
self.log_error(str(e))
def _down_bridge_port(self, ifaceobj):
self.ipcmd.link_set(ifaceobj.name, 'nomaster')
def _down_bridge_vlan(self, ifaceobj, bridge):
try:
address = ifaceobj.get_attr_value('address')
if address:
# Create a vlan device,
ifacename = '%s.%s' %(bridge, ifaceobj.priv_data)
self.ipcmd.link_delete(ifacename)
self._delete_bridge_vlan_settings(ifaceobj, bridge)
except Exception, e:
self.log_error(str(e))
def _down(self, ifaceobj):
bridge = ifaceobj.get_attr_value_first('bridge')
if ifaceobj.type == ifaceType.BRIDGE_VLAN:
self._down_bridge_vlan(ifaceobj, bridge)
elif bridge:
self._down_bridge_port(ifaceobj)
elif self._is_bridge(ifaceobj):
self._down_bridge(ifaceobj)
def _query_running_vidinfo(self, ifaceobjrunning, ports):
running_attrs = {}
running_vidinfo = self.ipcmd.bridge_port_vids_get_all()
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_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):
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]
bridgevidinfo = self._query_running_vidinfo(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_vidinfo(self, ifaceobj, ifaceobjcurr):
err = 0
running_vidinfo = self.ipcmd.bridge_port_vids_get_all()
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)
# 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
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)
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)
def _query_check(self, ifaceobj, ifaceobjcurr):
if not self.brctlcmd.bridge_exists(ifaceobj.name):
self.logger.info('%s: bridge: does not exist' %(ifaceobj.name))
ifaceobjcurr.status = ifaceStatus.NOTFOUND
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-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_vidinfo(ifaceobj, ifaceobjcurr)
self._query_check_mcqv4src(ifaceobj, ifaceobjcurr)
def _query_running(self, ifaceobjrunning):
if not self.brctlcmd.bridge_exists(ifaceobjrunning.name):
return
ifaceobjrunning.update_config_dict(self._query_running_attrs(
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)
def run(self, ifaceobj, operation, query_ifaceobj=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()
if operation == 'query-checkcurr':
op_handler(self, ifaceobj, query_ifaceobj)
else:
op_handler(self, ifaceobj)

129
addons/dhcp.py Normal file
View File

@ -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'
elif self.dhclientcmd.is_running6(ifaceobjcurr.name):
ifaceobjcurr.addr_family = 'inet6'
if ifaceobj.addr_family != 'inet6':
ifaceobjcurr.status = ifaceStatus.ERROR
ifaceobjcurr.addr_method = 'dhcp'
else:
ifaceobjcurr.addr_family = None
ifaceobjcurr.status = ifaceStatus.ERROR
def _query_running(self, ifaceobjrunning):
if not self.ipcmd.link_exists(ifaceobjrunning.name):
self.logger.debug('iface %s not found' %ifaceobjrunning.name)
ifaceobjrunning.status = ifaceStatus.NOTFOUND
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):
""" 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)

102
addons/ethtool.py Normal file
View File

@ -0,0 +1,102 @@
#!/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):
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):
""" 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)

380
addons/ifenslave.py Normal file
View File

@ -0,0 +1,380 @@
#!/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
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 use carrier',
'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',
'validvals' : ['0', '1'],
'default' : '0',
'example' : ['bond-lacp-fallback-allow 0']},
'bond-lacp-fallback-period':
{'help' : 'grace period (seconds) for lacp fall back',
'validrange' : ['0', '100'],
'default' : '90',
'example' : ['bond-lacp-fallback-period 100']},
'bond-lacp-fallback-priority':
{'help' : 'slave priority for lacp fall back',
'example' : ['bond-lacp-fallback-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)
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]))
return attrval
def _apply_master_settings(self, ifaceobj):
have_attrs_to_set = 0
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_fallback_allow'),
('bond-lacp-fallback-period', 'lacp_fallback_period')])
linkstatus = self.ipcmd.link_get_status(ifaceobj.name)
if not linkstatus:
# assume link status is 'UP'
linkstatus = 'UP'
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 linkstatus == 'UP' else None)
except:
raise
finally:
if have_attrs_to_set and linkstatus == 'UP':
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
self.ifenslavecmd.enslave_slave(ifaceobj.name, slave,
prehook=self.ipcmd.link_down,
posthook=self.ipcmd.link_up)
def _apply_slaves_lacp_fallback_prio(self, ifaceobj):
slaves = self.ifenslavecmd.get_slaves(ifaceobj.name)
attrval = ifaceobj.get_attr_value_first('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_fallback_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)
self._add_slaves(ifaceobj)
self._apply_slaves_lacp_fallback_prio(ifaceobj)
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')
ifaceobjcurr.status = ifaceStatus.NOTFOUND
return
ifaceattrs = self.dict_key_subset(ifaceobj.config,
self.get_mod_attrs())
if not ifaceattrs: return
runningattrs = self._query_running_attrs(ifaceobj.name)
for k in ifaceattrs:
v = ifaceobj.get_attr_value_first(k)
if not v:
continue
if k == 'bond-slaves':
slaves = v.split()
continue
rv = runningattrs.get(k)
if not rv:
ifaceobjcurr.update_config_with_status(k, 'None', 1)
else:
if 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-fallback-allow' :
self.ifenslavecmd.get_lacp_fallback_allow(bondname),
'bond-lacp-fallback-period' :
self.ifenslavecmd.get_lacp_fallback_period(bondname),
'bond-lacp-fallback-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):
""" 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)

64
addons/loopback.py Normal file
View File

@ -0,0 +1,64 @@
#!/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):
ifaceobjcurr.status = ifaceStatus.NOTFOUND
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):
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)

710
addons/mstpctl.py Normal file
View File

@ -0,0 +1,710 @@
#!/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-portadminage' :
{ 'help' : 'bridge port admin age',
'default' : 'no',
'validvals' : ['yes', 'no'],
'required' : False,
'example' : ['mstpctl-portadminage swp1=no swp2=no']},
'mstpctl-portp2p' :
{ 'help' : 'bridge port p2p detection mode',
'default' : 'no',
'validvals' : ['yes', 'no'],
'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',
'validvals' : ['yes', 'no'],
'default' : 'no',
'required' : False,
'example' : ['mstpctl-portbpdufilter swp1=no swp2=no']},
'mstpctl-pathcost' :
{ 'help' : 'port path cost',
'default' : '0',
'required' : False,
'example' : ['mstpctl-pathcost 1']},
'mstpctl-adminage' :
{ 'help' : 'bridge port admin age',
'default' : 'no',
'validvals' : ['yes', 'no'],
'required' : False,
'example' : ['mstpctl-adminage no']},
'mstpctl-p2p' :
{ 'help' : 'bridge port p2p detection mode',
'default' : 'no',
'validvals' : ['yes', 'no'],
'required' : False,
'example' : ['mstpctl-p2p yes']},
'mstpctl-restrrole' :
{ 'help' :
'enable/disable port ability to take root role of the port',
'default' : 'no',
'validvals' : ['yes', 'no'],
'required' : False,
'example' : ['mstpctl-restrrole yes']},
'mstpctl-restrtcn' :
{ 'help' :
'enable/disable port ability to propagate received topology change notification of the port',
'default' : 'no',
'validvals' : ['yes', 'no'],
'required' : False,
'example' : ['mstpctl-restrtcn yes']},
'mstpctl-treeprio' :
{ 'help' :
'port priority for MSTI instance',
'default' : '128',
'validrange' : ['0', '240'],
'required' : False,
'example' : ['mstpctl-treeprio 128']},
'mstpctl-network' :
{ 'help' : 'enable/disable bridge assurance capability for a port',
'validvals' : ['yes', 'no'],
'default' : 'no',
'required' : False,
'example' : ['mstpctl-network no']},
'mstpctl-adminedge' :
{ 'help' : 'enable/disable initial edge state of the port',
'validvals' : ['yes', 'no'],
'default' : 'no',
'required' : False,
'example' : ['mstpctl-adminedge no']},
'mstpctl-autoedge' :
{ 'help' : 'enable/disable auto transition to/from edge state of the port',
'validvals' : ['yes', 'no'],
'default' : 'no',
'required' : False,
'example' : ['mstpctl-autoedge yes']},
'mstpctl-treecost' :
{ 'help' : 'port tree cost',
'required' : False},
'mstpctl-bpdufilter' :
{ 'help' : 'enable/disable bpdu filter on a port',
'validvals' : ['yes', 'no'],
'default' : 'no',
'required' : False,
'example' : ['mstpctl-bpdufilter yes']},
}}
_port_attrs_map = {'mstpctl-pathcost' : 'portpathcost',
'mstpctl-adminedge' : 'portadminedge',
'mstpctl-p2p' : 'portp2p',
'mstpctl-restrrole' : 'portrestrrole',
'mstpctl-restrtcn' : 'portrestrtcn',
'mstpctl-bpduguard' : 'bpduguard',
'mstpctl-treeprio' : 'treeportprio',
'mstpctl-treecost' : 'treeportcost',
'mstpctl-network' : 'portnetwork',
'mstpctl-bpdufilter' : 'portbpdufilter'}
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 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 _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.write_file('/proc/sys/net/ipv6/conf/%s' %bridgeport +
'/disable_ipv6', '1')
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:
bridgeattrs = {k:v for k,v in
{'treeprio' :
ifaceobj.get_attr_value_first('mstpctl-treeprio'),
'ageing' :
ifaceobj.get_attr_value_first('mstpctl-ageing'),
'maxage' :
ifaceobj.get_attr_value_first('mstpctl-maxage'),
'fdelay' :
ifaceobj.get_attr_value_first('mstpctl-fdelay'),
'maxhops' :
ifaceobj.get_attr_value_first('mstpctl-maxhops'),
'txholdcount' :
ifaceobj.get_attr_value_first('mstpctl-txholdcount'),
'forcevers' :
ifaceobj.get_attr_value_first('mstpctl-forcevers'),
'hello' :
ifaceobj.get_attr_value_first('mstpctl-hello')
}.items() if v}
if bridgeattrs:
# set bridge attributes
for k,v in bridgeattrs.items():
if k == 'treeprio':
continue
try:
if v:
self.mstpctlcmd.set_bridge_attr(ifaceobj.name, k,
v, check)
except Exception, e:
self.logger.warn('%s' %str(e))
pass
if bridgeattrs.get('treeprio'):
try:
self.mstpctlcmd.set_bridge_treeprio(ifaceobj.name,
bridgeattrs['treeprio'], check)
except Exception, e:
self.logger.warn('%s' %str(e))
pass
# set bridge port attributes
for attrname in ['mstpctl-portpathcost', 'mstpctl-portadminedge',
'mstpctl-portp2p', 'mstpctl-portrestrrole',
'mstpctl-portrestrtcn', 'mstpctl-bpduguard',
'mstpctl-treeportprio', 'mstpctl-treeportcost',
'mstpctl-portnetwork', 'mstpctl-portbpdufilter']:
attrval = ifaceobj.get_attr_value_first(attrname)
if not attrval:
continue
dstattrname = attrname.split('-')[1]
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, bridge):
check = False if self.PERFMODE else True
try:
# set bridge port attributes
for attrname, dstattrname in self._port_attrs_map.items():
attrval = ifaceobj.get_attr_value_first(attrname)
if not attrval:
continue
try:
self.mstpctlcmd.set_bridgeport_attr(bridge,
ifaceobj.name, dstattrname, attrval, 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 _up(self, ifaceobj):
# Check if bridge port
bridge = ifaceobj.get_attr_value_first('bridge')
if bridge:
if self.mstpctlcmd.is_mstpd_running():
self._apply_bridge_port_settings(ifaceobj, bridge)
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()
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)
except Exception, e:
self.log_error(str(e))
if porterr:
raise Exception(porterrstr)
def _down(self, ifaceobj):
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:
for p in ports:
proc_file = ('/proc/sys/net/ipv6/conf/%s' %p +
'/disable_ipv6')
self.write_file(proc_file, '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})
self.logger.debug(bridgeattrdict)
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)
ifaceobjcurr.status = ifaceStatus.NOTFOUND
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, bridge):
# list of attributes that are not supported currently
blacklistedattrs = ['mstpctl-pathcost',
'mstpctl-treeprio', 'mstpctl-treecost']
if not self.ipcmd.link_exists():
self.logger.debug('bridge port %s does not exist' %ifaceobj.name)
ifaceobjcurr.status = ifaceStatus.NOTFOUND
return
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
if k in blacklistedattrs:
continue
# get the corresponding ifaceobj attr
v = ifaceobj.get_attr_value_first(k)
if not v:
continue
currv = self.mstpctlcmd.get_bridgeport_attr(bridge,
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):
# Check if bridge port
bridge = ifaceobj.get_attr_value_first('bridge')
if bridge:
self._query_check_bridge_port(ifaceobj, ifaceobjcurr, bridge)
return
self._query_check_bridge(ifaceobj, ifaceobjcurr)
def _query_running(self, ifaceobjrunning):
if not self.brctlcmd.bridge_exists(ifaceobjrunning.name):
return
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))
_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):
""" 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.
"""
op_handler = self._run_ops.get(operation)
if not op_handler:
return
if operation != 'query-running' and not self._is_bridge(ifaceobj):
return
self._init_command_handlers()
if operation == 'query-checkcurr':
op_handler(self, ifaceobj, query_ifaceobj)
else:
op_handler(self, ifaceobj)

97
addons/usercmds.py Normal file
View File

@ -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):
""" 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)

191
addons/vlan.py Normal file
View File

@ -0,0 +1,191 @@
#!/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 logging
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
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 ifaceobj.name.startswith('vlan'):
vid_str = ifaceobj.name[4:]
elif '.' in ifaceobj.name:
vid_str = ifaceobj.name.split('.', 1)[1]
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 _up(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')
self.ipcmd.link_create_vlan(ifaceobj.name,
vlanrawdevice, vlanid)
def _down(self, ifaceobj):
vlanid = self._get_vlan_id(ifaceobj)
if vlanid == -1:
raise Exception('could not determine vlanid')
vlan_raw_device = self._get_vlan_raw_device(ifaceobj)
if not vlan_raw_device:
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)
except Exception, e:
self.log_warn(str(e))
def _query_check(self, ifaceobj, ifaceobjcurr):
if not self.ipcmd.link_exists(ifaceobj.name):
ifaceobjcurr.status = ifaceStatus.NOTFOUND
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)
def _query_running(self, ifaceobjrunning):
if not self.ipcmd.link_exists(ifaceobjrunning.name):
if self._is_vlan_by_name(ifaceobjrunning.name):
ifaceobjcurr.status = ifaceStatus.NOTFOUND
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):
""" 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_vlan_device(ifaceobj)):
return
self._init_command_handlers()
if operation == 'query-checkcurr':
op_handler(self, ifaceobj, query_ifaceobj)
else:
op_handler(self, ifaceobj)

87
addons/vxlan.py Normal file
View File

@ -0,0 +1,87 @@
#!/usr/bin/python
from ifupdown.iface import *
from ifupdownaddons.modulebase import moduleBase
from ifupdownaddons.iproute2 import iproute2
import logging
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 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'))
def _down(self, ifaceobj):
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):
ifaceobjcurr.status = ifaceStatus.NOTFOUND
return
# Update vxlan object
def _query_running(self, ifaceobjrunning):
# Not implemented
return
_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):
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)

28
config/addons.conf Normal file
View File

@ -0,0 +1,28 @@
pre-up,bridge
pre-up,mstpctl
pre-up,vlan
pre-up,vxlan
pre-up,ifenslave
pre-up,bridgevlanaware
pre-up,usercmds
pre-up,loopback
up,dhcp
up,address
up,addressvirtual
up,usercmds
up,loopback
post-up,ethtool
post-up,usercmds
pre-down,usercmds
down,dhcp
down,addressvirtual
down,address
down,usercmds
post-down,bridgevlanaware
post-down,bridge
post-down,mstpctl
post-down,vxlan
post-down,vlan
post-down,ifenslave
post-down,usercmds
post-down,loopback

153
docs.addons/Makefile Normal file
View File

@ -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."

View File

@ -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

View File

@ -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

247
docs.addons/source/conf.py Normal file
View File

@ -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'

View File

@ -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

View File

@ -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

View File

@ -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`

View File

@ -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.

View File

View File

@ -0,0 +1,498 @@
#!/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 bridge_port_exists(self, bridge, bridgeportname):
try:
return bridgeportname in os.listdir('/sys/class/net/%s/brif'
%bridge)
except Exception:
return False
def get_bridge_ports(self, bridgename):
try:
return os.listdir('/sys/class/net/%s/brif/' %bridgename)
except:
return {}

90
ifupdownaddons/cache.py Normal file
View File

@ -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))

View File

@ -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])

View File

@ -0,0 +1,407 @@
#!/usr/bin/python
#
# Copyright 2014 Cumulus Networks, Inc. All rights reserved.
# Author: Roopa Prabhu, roopa@cumulusnetworks.com
#
import os
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'])
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_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_fallback_allow'],
lacp_fallback_allow)):
return
if prehook:
prehook(bondname)
try:
self.write_file('/sys/class/net/%s' %bondname +
'/bonding/lacp_fallback_allow', allow)
except:
raise
finally:
if posthook:
posthook(bondname)
self._cache_update([bondname, 'linkinfo',
'lacp_fallback_allow'], allow)
def get_lacp_fallback_allow(self, bondname):
return self._cache_get([bondname, 'linkinfo', 'lacp_fallback_allow'])
def set_lacp_fallback_period(self, bondname, period, prehook=None, posthook=None):
if (self._cache_check([bondname, 'linkinfo', 'lacp_fallback_period'],
lacp_fallback_period)):
return
if prehook:
prehook(bondname)
try:
self.write_file('/sys/class/net/%s' %bondname +
'/bonding/lacp_fallback_period', period)
except:
raise
finally:
if posthook:
posthook(bondname)
self._cache_update([bondname, 'linkinfo',
'lacp_fallback_period'], period)
def get_lacp_fallback_period(self, bondname):
return self._cache_get([bondname, 'linkinfo', 'lacp_fallback_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_fallback_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_fallback_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)

613
ifupdownaddons/iproute2.py Normal file
View File

@ -0,0 +1,613 @@
#!/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' : []}
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])
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='off'):
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 == 'on':
args += ' learning'
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):
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 link_get_mtu(self, ifacename):
return self._cache_get('link', [ifacename, 'mtu'])
def link_get_hwaddress(self, ifacename):
return self._cache_get('link', [ifacename, 'hwaddress'])
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_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

View File

@ -0,0 +1,318 @@
#!/usr/bin/python
#
# Copyright 2014 Cumulus Networks, Inc. All rights reserved.
# Author: Roopa Prabhu, roopa@cumulusnetworks.com
#
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 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 == '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)

View File

@ -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' : '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.info(bridgeattrs)
self.logger.warn(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

163
ifupdownaddons/utilsbase.py Normal file
View File

@ -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()

File diff suppressed because it is too large Load Diff

350
sbin/ifaddon Executable file
View File

@ -0,0 +1,350 @@
#!/usr/bin/python
import sys
import os
import re
import argparse
from collections import OrderedDict
lockfile="/run/network/.lock"
modules_configfile='/var/lib/ifupdownaddons/addons.conf'
modules_dir='/usr/share/ifupdownaddons'
addon_config = OrderedDict([('pre-up', []),
('up', []),
('post-up', []),
('pre-down', []),
('down', []),
('post-down', [])])
def read_modules_config():
with open(modules_configfile, 'r') as f:
lines = f.readlines()
for l in lines:
litems = l.rstrip(' \n').split(',')
operation = litems[0]
mname = litems[1]
addon_config[operation].append(mname)
def man_rst_header():
print '=========================='
print 'ifupdown-addons-interfaces'
print '=========================='
print '---------------------------------------------------------'
print 'ifupdown2 addon modules interface configuration'
print '---------------------------------------------------------'
print ':Author: roopa@cumulusnetworks.com'
print ':Date: 2013-09-25'
print ':Copyright: Copyright 2013 Cumulus Networks, Inc. All rights reserved.'
print ':Version: 0.1'
print ':Manual section: 5'
print '\n'
def man_rst_body():
print 'DESCRIPTION'
print '==========='
print (''' 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. ''')
print '\n'
def get_addon_modinfo(modules_dir):
""" load python modules from modules_dir
Default modules_dir is /usr/share/ifupdownmodules
"""
if not modules_dir in sys.path:
sys.path.append(modules_dir)
read_modules_config()
modinfo = {}
try:
for op, mlist in addon_config.items():
for mname in mlist:
if mname in modinfo.keys(): continue
mpath = modules_dir + '/' + mname + '.py'
if os.path.exists(mpath):
try:
m = __import__(mname)
mclass = getattr(m, mname)
except:
pass
continue
minstance = mclass()
if hasattr(minstance, 'get_modinfo'):
modinfo[mname] = minstance.get_modinfo()
except:
raise
return modinfo
def print_long_string(indent, strarg):
slen = 70 - len(indent)
tmphelpstr = strarg
l = len(strarg)
while l > 0:
rem = slen if l >= slen else l
print('%s%s' %(indent, tmphelpstr[:rem]))
tmphelpstr = tmphelpstr[rem:].strip()
l -= rem
def man_rst_examples():
print 'EXAMPLES'
print '========'
print ''' Listed below are addon modules and their supported attributes.
The attributes if applicable go under the iface section in the
interfaces(5) file.\n'''
indent = ' '
modinfo = get_addon_modinfo(modules_dir)
for m, mdict in modinfo.items():
aindent = indent + ' '
aindentplus = aindent + ' '
if not mdict:
continue
print_long_string(indent, '**%s**: %s' %(m, mdict.get('mhelp', '')))
attrdict = mdict.get('attrs')
if not attrdict:
continue
print '\n'
try:
for attrname, attrvaldict in attrdict.items():
if attrvaldict.get('compat', False):
continue
print('%s**%s**\n' %(aindent, attrname))
print_long_string(aindentplus, '**help**: %s'
%(attrvaldict.get('help', '')))
print '\n'
print('%s**required**: %s\n' %(aindentplus,
attrvaldict.get('required', False)))
default = attrvaldict.get('default')
if default:
print('%s**default**: %s\n' %(aindentplus, default))
validrange = attrvaldict.get('validrange')
if validrange:
print('%svalidrange: %s\n'
%(aindentplus, '-'.join(validrange)))
validvals = attrvaldict.get('validvals')
if validvals:
print('%s**validvals**: %s\n'
%(aindentplus, ','.join(validvals)))
examples = attrvaldict.get('example')
if not examples:
continue
print '%s**example**:' %(aindentplus)
for e in examples:
print '%s%s\n' %(aindentplus + indent, e)
print ''
except Exception, e:
print "Roopa: m = %s, str(e) = %s\n" %(m, str(e))
pass
print ''
def man_rst_see_also():
print 'SEE ALSO'
print '========'
print ''' interfaces(5),
ifup(8),
ip(8),
mstpctl(8),
brctl(8),
ethtool(8)'''
def show_man_rst():
man_rst_header()
man_rst_body()
man_rst_examples()
man_rst_see_also()
def show():
for operation, mlist in addon_config.items():
postion = 1
for m in mlist:
print '%d. %s' %(postion, m)
postion += 1
def write_modules_config():
with open(modules_configfile, 'w') as f:
for op, mlist in addon_config.items():
[f.write('%s,%s\n' %(op, m)) for m in mlist]
def process_add_cmd(args):
op = args.operation
module = args.module
position = args.position
if not op:
for k, vlist in addon_config.items():
if module not in vlist:
addon_config[k].append(module)
else:
print '%s: module %s already present' %(k, module)
return
if module in addon_config.get(op):
print 'module already present'
return
if position:
try:
addon_config[op].insert(position, module)
except Exception, e:
print ('error inserting module %s at postion %s (%s)'
%(module, position, str(e)))
raise
else:
addon_config[op].append(module)
def process_del_cmd(args):
op = args.operation
module = args.module
if op:
del addon_config[op]
else:
try:
[addon_config[op].remove(module) for op in addon_config.keys()]
except ValueError:
pass
def process_move_cmd(args):
op = args.operation
module = args.module
pos = 0
try:
pos = int(args.position)
if pos < 0 or pos > len(addon_config.get(op)):
raise Exception('invalid value for position')
except:
raise
if addon_config[op].index(module) == pos:
print '%s module %s already at location %d' %(op, module, pos)
return
addon_config[op].remove(module)
addon_config[op].insert(pos, module)
def print_mlist(mlist, indent):
for idx, val in enumerate(mlist):
print '%s%d. %s' %(indent, idx, val)
def process_show_cmd(args):
indent = ' '
op = args.operation
if args.man:
show_man_rst()
return
if op:
mlist = addon_config[op]
print '%s:' %op
print_mlist(mlist, indent)
else:
for op, mlist in addon_config.items():
print '%s:' %op
print_mlist(mlist, indent)
print ''
cmdhandlers = {'add' : process_add_cmd,
'del' : process_del_cmd,
'move' : process_move_cmd,
'show' : process_show_cmd}
def update_subparser_add(subparser):
subparser.add_argument('module', metavar='MODULE', help='module name')
subparser.add_argument('operation', metavar='OPERATION',
choices=['pre-up', 'up', 'post-up',
'pre-down', 'down', 'post-down'],
help='operations', nargs='?')
subparser.add_argument('position', metavar='POSITION', nargs='?',
help='position')
subparser.set_defaults(func=process_add_cmd)
def update_subparser_del(subparser):
subparser.add_argument('module', metavar='MODULE', help='module name')
subparser.add_argument('operation', metavar='OPERATION',
choices=['pre-up', 'up', 'post-up',
'pre-down', 'down', 'post-down'],
help='operations', nargs='?')
subparser.add_argument('position', metavar='POSITION', nargs='?',
help='position')
subparser.set_defaults(func=process_del_cmd)
def update_subparser_move(subparser):
subparser.add_argument('module', metavar='MODULE', help='module name')
subparser.add_argument('operation', metavar='OPERATION',
choices=['pre-up', 'up', 'post-up',
'pre-down', 'down', 'post-down'],
help='operations')
subparser.add_argument('position', metavar='POSITION',
help='position')
subparser.set_defaults(func=process_move_cmd)
def update_subparser_show(subparser):
subparser.add_argument('--man', action='store_true',
help=argparse.SUPPRESS)
subparser.add_argument('operation', metavar='OPERATION',
choices=addon_config.keys(),
help='operations %s' %str(addon_config.keys()),
nargs='?')
subparser.set_defaults(func=process_show_cmd)
def update_argparser(argparser):
subparsers = argparser.add_subparsers(help='sub-command help')
parser_add = subparsers.add_parser('add')
update_subparser_add(parser_add)
parser_del = subparsers.add_parser('del', help='del help')
update_subparser_del(parser_del)
parser_move = subparsers.add_parser('move', help='move help')
update_subparser_move(parser_move)
parser_show = subparsers.add_parser('show', help='show help')
update_subparser_show(parser_show)
def parse_args(argsv):
descr = 'ifupdown addon modules management command.\n \
This command helps add/del/display/reorder modules \n \
in all ifupdown module categories'
argparser = argparse.ArgumentParser(description=descr)
update_argparser(argparser)
args = argparser.parse_args(argsv)
return args
def main(argv):
""" main function """
try:
# Command line arg parser
args = parse_args(argv[1:])
read_modules_config()
args.func(args)
write_modules_config()
except Exception, e:
print 'error processing command (%s)' %str(e)
if __name__ == "__main__":
if not os.geteuid() == 0:
print 'Error: Must be root to run this command'
exit(1)
main(sys.argv)

View File

@ -6,13 +6,13 @@ 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'],
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']),
@ -24,5 +24,14 @@ setup(name='ifupdown2',
['docs/examples/interfaces',
'docs/examples/interfaces_bridge_template_func',
'docs/examples/interfaces_with_template']),
('/etc/bash_completion.d/', ['completion/ifup'])]
('/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/bridgevlanaware.py']),
('/var/lib/ifupdownaddons/', ['config/addons.conf'])
]
)