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:
13
TODO.addons
Normal file
13
TODO.addons
Normal 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
300
addons/address.py
Normal 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
169
addons/addressvirtual.py
Normal 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
895
addons/bridge.py
Normal 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
942
addons/bridgevlanaware.py
Normal 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
129
addons/dhcp.py
Normal 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
102
addons/ethtool.py
Normal 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
380
addons/ifenslave.py
Normal 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
64
addons/loopback.py
Normal 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
710
addons/mstpctl.py
Normal 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
97
addons/usercmds.py
Normal 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
191
addons/vlan.py
Normal 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
87
addons/vxlan.py
Normal 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
28
config/addons.conf
Normal 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
153
docs.addons/Makefile
Normal 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."
|
61
docs.addons/source/addonsapiref.rst
Normal file
61
docs.addons/source/addonsapiref.rst
Normal 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
|
44
docs.addons/source/addonshelperapiref.rst
Normal file
44
docs.addons/source/addonshelperapiref.rst
Normal 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
247
docs.addons/source/conf.py
Normal 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'
|
58
docs.addons/source/developmentcorner.rst
Normal file
58
docs.addons/source/developmentcorner.rst
Normal 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
|
29
docs.addons/source/gettingstarted.rst
Normal file
29
docs.addons/source/gettingstarted.rst
Normal 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
|
||||
|
||||
|
||||
|
||||
|
25
docs.addons/source/index.rst
Normal file
25
docs.addons/source/index.rst
Normal 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`
|
||||
|
21
docs.addons/source/intro.rst
Normal file
21
docs.addons/source/intro.rst
Normal 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.
|
0
ifupdownaddons/__init__.py
Normal file
0
ifupdownaddons/__init__.py
Normal file
498
ifupdownaddons/bridgeutils.py
Normal file
498
ifupdownaddons/bridgeutils.py
Normal 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
90
ifupdownaddons/cache.py
Normal 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))
|
86
ifupdownaddons/dhclient.py
Normal file
86
ifupdownaddons/dhclient.py
Normal 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])
|
407
ifupdownaddons/ifenslaveutil.py
Normal file
407
ifupdownaddons/ifenslaveutil.py
Normal 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
613
ifupdownaddons/iproute2.py
Normal 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
|
318
ifupdownaddons/modulebase.py
Normal file
318
ifupdownaddons/modulebase.py
Normal 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)
|
171
ifupdownaddons/mstpctlutil.py
Normal file
171
ifupdownaddons/mstpctlutil.py
Normal 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
163
ifupdownaddons/utilsbase.py
Normal 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()
|
1079
man.rst/ifupdown-addons-interfaces.5.rst
Normal file
1079
man.rst/ifupdown-addons-interfaces.5.rst
Normal file
File diff suppressed because it is too large
Load Diff
350
sbin/ifaddon
Executable file
350
sbin/ifaddon
Executable 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)
|
15
setup.py
15
setup.py
@ -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'])
|
||||
]
|
||||
)
|
||||
|
Reference in New Issue
Block a user