mirror of
https://github.com/CumulusNetworks/ifupdown2.git
synced 2024-05-06 15:54:50 +00:00
failures Ticket: CM-4382 Reviewed By: Testing Done: Tested with bridge port addition failure example in the bug report
761 lines
34 KiB
Python
761 lines
34 KiB
Python
#!/usr/bin/python
|
|
#
|
|
# Copyright 2014 Cumulus Networks, Inc. All rights reserved.
|
|
# Author: Roopa Prabhu, roopa@cumulusnetworks.com
|
|
#
|
|
|
|
from sets import Set
|
|
from ifupdown.iface import *
|
|
from ifupdownaddons.modulebase import moduleBase
|
|
from ifupdownaddons.bridgeutils import brctl
|
|
from ifupdownaddons.iproute2 import iproute2
|
|
from ifupdownaddons.mstpctlutil import mstpctlutil
|
|
import traceback
|
|
|
|
class mstpctl(moduleBase):
|
|
""" ifupdown2 addon module to configure mstp attributes """
|
|
|
|
_modinfo = {'mhelp' : 'mstp configuration module for bridges',
|
|
'attrs' : {
|
|
'mstpctl-ports' :
|
|
{'help' : 'mstp ports',
|
|
'compat' : True},
|
|
'mstpctl-stp' :
|
|
{'help': 'bridge stp yes/no',
|
|
'compat' : True,
|
|
'default' : 'no'},
|
|
'mstpctl-treeprio' :
|
|
{'help': 'tree priority',
|
|
'default' : '32768',
|
|
'validrange' : ['0', '65535'],
|
|
'required' : False,
|
|
'example' : ['mstpctl-treeprio 32768']},
|
|
'mstpctl-ageing' :
|
|
{'help': 'ageing time',
|
|
'default' : '300',
|
|
'required' : False,
|
|
'example' : ['mstpctl-ageing 300']},
|
|
'mstpctl-maxage' :
|
|
{ 'help' : 'max message age',
|
|
'default' : '20',
|
|
'required' : False,
|
|
'example' : ['mstpctl-maxage 20']},
|
|
'mstpctl-fdelay' :
|
|
{ 'help' : 'set forwarding delay',
|
|
'default' : '15',
|
|
'required' : False,
|
|
'example' : ['mstpctl-fdelay 15']},
|
|
'mstpctl-maxhops' :
|
|
{ 'help' : 'bridge max hops',
|
|
'default' : '15',
|
|
'required' : False,
|
|
'example' : ['mstpctl-maxhops 15']},
|
|
'mstpctl-txholdcount' :
|
|
{ 'help' : 'bridge transmit holdcount',
|
|
'default' : '6',
|
|
'required' : False,
|
|
'example' : ['mstpctl-txholdcount 6']},
|
|
'mstpctl-forcevers' :
|
|
{ 'help' : 'bridge force stp version',
|
|
'default' : 'rstp',
|
|
'required' : False,
|
|
'example' : ['mstpctl-forcevers rstp']},
|
|
'mstpctl-portpathcost' :
|
|
{ 'help' : 'bridge port path cost',
|
|
'default' : '0',
|
|
'required' : False,
|
|
'example' : ['mstpctl-portpathcost swp1=0 swp2=1']},
|
|
'mstpctl-portp2p' :
|
|
{ 'help' : 'bridge port p2p detection mode',
|
|
'default' : 'auto',
|
|
'validvals' : ['yes', 'no', 'auto'],
|
|
'required' : False,
|
|
'example' : ['mstpctl-portp2p swp1=no swp2=no']},
|
|
'mstpctl-portrestrrole' :
|
|
{ 'help' :
|
|
'enable/disable port ability to take root role of the port',
|
|
'default' : 'no',
|
|
'validvals' : ['yes', 'no'],
|
|
'required' : False,
|
|
'example' : ['mstpctl-portrestrrole swp1=no swp2=no']},
|
|
'mstpctl-portrestrtcn' :
|
|
{ 'help' :
|
|
'enable/disable port ability to propagate received topology change notification of the port',
|
|
'default' : 'no',
|
|
'validvals' : ['yes', 'no'],
|
|
'required' : False,
|
|
'example' : ['mstpctl-portrestrtcn swp1=no swp2=no']},
|
|
'mstpctl-bpduguard' :
|
|
{ 'help' :
|
|
'enable/disable bpduguard',
|
|
'default' : 'no',
|
|
'validvals' : ['yes', 'no'],
|
|
'required' : False,
|
|
'example' : ['mstpctl-bpduguard swp1=no swp2=no']},
|
|
'mstpctl-treeportprio' :
|
|
{ 'help' :
|
|
'port priority for MSTI instance',
|
|
'default' : '128',
|
|
'validrange' : ['0', '240'],
|
|
'required' : False,
|
|
'example' : ['mstpctl-treeportprio swp1=128 swp2=128']},
|
|
'mstpctl-hello' :
|
|
{ 'help' : 'set hello time',
|
|
'default' : '2',
|
|
'required' : False,
|
|
'example' : ['mstpctl-hello 2']},
|
|
'mstpctl-portnetwork' :
|
|
{ 'help' : 'enable/disable bridge assurance capability for a port',
|
|
'validvals' : ['yes', 'no'],
|
|
'default' : 'no',
|
|
'required' : False,
|
|
'example' : ['mstpctl-portnetwork swp1=no swp2=no']},
|
|
'mstpctl-portadminedge' :
|
|
{ 'help' : 'enable/disable initial edge state of the port',
|
|
'validvals' : ['yes', 'no'],
|
|
'default' : 'no',
|
|
'required' : False,
|
|
'example' : ['mstpctl-portadminedge swp1=no swp2=no']},
|
|
'mstpctl-portautoedge' :
|
|
{ 'help' : 'enable/disable auto transition to/from edge state of the port',
|
|
'validvals' : ['yes', 'no'],
|
|
'default' : 'no',
|
|
'required' : False,
|
|
'example' : ['mstpctl-portautoedge swp1=yes swp2=yes']},
|
|
'mstpctl-treeportcost' :
|
|
{ 'help' : 'port tree cost',
|
|
'required' : False},
|
|
'mstpctl-portbpdufilter' :
|
|
{ 'help' : 'enable/disable bpdu filter on a port. ' +
|
|
'syntax varies when defined under a bridge ' +
|
|
'vs under a port',
|
|
'validvals' : ['yes', 'no'],
|
|
'default' : 'no',
|
|
'required' : False,
|
|
'example' : ['under a bridge: mstpctl-portbpdufilter swp1=no swp2=no',
|
|
'under a port: mstpctl-portbpdufilter yes']},
|
|
}}
|
|
|
|
# Maps mstp bridge attribute names to corresponding mstpctl commands
|
|
# XXX: This can be encoded in the modules dict above
|
|
_attrs_map = OrderedDict([('mstpctl-treeprio' , 'treeprio'),
|
|
('mstpctl-ageing' , 'ageing'),
|
|
('mstpctl-maxage' , 'maxage'),
|
|
('mstpctl-fdelay' , 'fdelay'),
|
|
('mstpctl-maxhops' , 'maxhops'),
|
|
('mstpctl-txholdcount' , 'txholdcount'),
|
|
('mstpctl-forcevers', 'forcevers'),
|
|
('mstpctl-hello' , 'hello')])
|
|
|
|
# Maps mstp port attribute names to corresponding mstpctl commands
|
|
# XXX: This can be encoded in the modules dict above
|
|
_port_attrs_map = {'mstpctl-portpathcost' : 'portpathcost',
|
|
'mstpctl-portadminedge' : 'portadminedge',
|
|
'mstpctl-portautoedge' : 'portautoedge' ,
|
|
'mstpctl-portp2p' : 'portp2p',
|
|
'mstpctl-portrestrrole' : 'portrestrrole',
|
|
'mstpctl-portrestrtcn' : 'portrestrtcn',
|
|
'mstpctl-bpduguard' : 'bpduguard',
|
|
'mstpctl-treeportprio' : 'treeportprio',
|
|
'mstpctl-treeportcost' : 'treeportcost',
|
|
'mstpctl-portnetwork' : 'portnetwork',
|
|
'mstpctl-portbpdufilter' : 'portbpdufilter'}
|
|
|
|
# declare some ifaceobj priv_flags.
|
|
# XXX: This assumes that the priv_flags is owned by this module
|
|
# which it is not.
|
|
_BRIDGE_PORT_PROCESSED = 0x1
|
|
|
|
def __init__(self, *args, **kargs):
|
|
moduleBase.__init__(self, *args, **kargs)
|
|
self.ipcmd = None
|
|
self.brctlcmd = None
|
|
self.mstpctlcmd = None
|
|
|
|
def _is_bridge(self, ifaceobj):
|
|
if (ifaceobj.get_attr_value_first('mstpctl-ports') or
|
|
ifaceobj.get_attr_value_first('bridge-ports')):
|
|
return True
|
|
return False
|
|
|
|
def _is_bridge_port(self, ifaceobj):
|
|
if self.brctlcmd.is_bridge_port(ifaceobj.name):
|
|
return True
|
|
return False
|
|
|
|
def get_dependent_ifacenames(self, ifaceobj, ifacenames_all=None):
|
|
if not self._is_bridge(ifaceobj):
|
|
return None
|
|
return self.parse_port_list(ifaceobj.get_attr_value_first(
|
|
'mstpctl-ports'), ifacenames_all)
|
|
|
|
def get_dependent_ifacenames_running(self, ifaceobj):
|
|
self._init_command_handlers()
|
|
if (self.brctlcmd.bridge_exists(ifaceobj.name) and
|
|
not self.mstpctlcmd.mstpbridge_exists(ifaceobj.name)):
|
|
return None
|
|
return self.brctlcmd.get_bridge_ports(ifaceobj.name)
|
|
|
|
def _get_bridge_port_list(self, ifaceobj):
|
|
|
|
# port list is also available in the previously
|
|
# parsed dependent list. Use that if available, instead
|
|
# of parsing port expr again
|
|
port_list = ifaceobj.lowerifaces
|
|
if port_list:
|
|
return port_list
|
|
ports = ifaceobj.get_attr_value_first('mstpctl-ports')
|
|
if ports:
|
|
return self.parse_port_list(ports)
|
|
else:
|
|
return None
|
|
|
|
def _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:
|
|
# set bridge attributes
|
|
for attrname, dstattrname in self._attrs_map.items():
|
|
try:
|
|
v = ifaceobj.get_attr_value_first(attrname)
|
|
if not v:
|
|
continue
|
|
if attrname == 'mstpctl-treeprio':
|
|
self.mstpctlcmd.set_bridge_treeprio(ifaceobj.name,
|
|
v, check)
|
|
else:
|
|
self.mstpctlcmd.set_bridge_attr(ifaceobj.name,
|
|
dstattrname, v, check)
|
|
except Exception, e:
|
|
self.logger.warn('%s' %str(e))
|
|
pass
|
|
|
|
# set bridge port attributes
|
|
for attrname, dstattrname in self._port_attrs_map.items():
|
|
attrval = ifaceobj.get_attr_value_first(attrname)
|
|
if not attrval:
|
|
continue
|
|
portlist = self.parse_port_list(attrval)
|
|
if not portlist:
|
|
self.log_warn('%s: error parsing \'%s %s\''
|
|
%(ifaceobj.name, attrname, attrval))
|
|
continue
|
|
for p in portlist:
|
|
try:
|
|
(port, val) = p.split('=')
|
|
self.mstpctlcmd.set_bridgeport_attr(ifaceobj.name,
|
|
port, dstattrname, val, check)
|
|
except Exception, e:
|
|
self.log_warn('%s: error setting %s (%s)'
|
|
%(ifaceobj.name, attrname, str(e)))
|
|
except Exception, e:
|
|
self.log_warn(str(e))
|
|
pass
|
|
|
|
def _apply_bridge_port_settings(self, ifaceobj, bridgename=None,
|
|
bridgeifaceobj=None, stp_on=True,
|
|
mstpd_running=True):
|
|
check = False if self.PERFMODE else True
|
|
if not bridgename and bridgeifaceobj:
|
|
bridgename = bridgeifaceobj.name
|
|
# set bridge port attributes
|
|
for attrname, dstattrname in self._port_attrs_map.items():
|
|
attrval = ifaceobj.get_attr_value_first(attrname)
|
|
if not attrval:
|
|
#if bridgeifaceobj:
|
|
# # If bridge object available, check if the bridge
|
|
# # has the attribute set, in which case,
|
|
# # inherit it from the bridge
|
|
# attrval = bridgeifaceobj.get_attr_value_first(attrname)
|
|
# if not attrval:
|
|
# continue
|
|
#else:
|
|
continue
|
|
if not stp_on:
|
|
self.logger.warn('%s: cannot set %s (stp on bridge %s not on)\n'
|
|
%(ifaceobj.name, attrname, bridgename))
|
|
continue
|
|
if not mstpd_running:
|
|
continue
|
|
try:
|
|
self.mstpctlcmd.set_bridgeport_attr(bridgename,
|
|
ifaceobj.name, dstattrname, attrval, check)
|
|
except Exception, e:
|
|
self.log_warn('%s: error setting %s (%s)'
|
|
%(ifaceobj.name, attrname, str(e)))
|
|
|
|
def _apply_bridge_port_settings_all(self, ifaceobj,
|
|
ifaceobj_getfunc=None):
|
|
self.logger.info('%s: applying mstp configuration '
|
|
%ifaceobj.name + 'specific to ports')
|
|
# Query running bridge ports. and only apply attributes on them
|
|
bridgeports = self.brctlcmd.get_bridge_ports(ifaceobj.name)
|
|
if not bridgeports:
|
|
self.logger.debug('%s: cannot find bridgeports' %ifaceobj.name)
|
|
return
|
|
for bport in bridgeports:
|
|
self.logger.info('%s: processing mstp config for port %s'
|
|
%(ifaceobj.name, bport))
|
|
if not self.ipcmd.link_exists(bport):
|
|
continue
|
|
bportifaceobjlist = ifaceobj_getfunc(bport)
|
|
if not bportifaceobjlist:
|
|
continue
|
|
for bportifaceobj in bportifaceobjlist:
|
|
# Dont process bridge port if it already has been processed
|
|
if bportifaceobj.priv_flags & self._BRIDGE_PORT_PROCESSED:
|
|
continue
|
|
try:
|
|
self._apply_bridge_port_settings(bportifaceobj,
|
|
ifaceobj.name, ifaceobj)
|
|
except Exception, e:
|
|
self.log_warn(str(e))
|
|
|
|
def _up(self, ifaceobj, ifaceobj_getfunc=None):
|
|
# Check if bridge port
|
|
bridgename = self.ipcmd.bridge_port_get_bridge_name(ifaceobj.name)
|
|
if bridgename:
|
|
mstpd_running = (True if self.mstpctlcmd.is_mstpd_running()
|
|
else False)
|
|
stp_on = (True if self.read_file_oneline(
|
|
'/sys/class/net/%s/bridge/stp_state'
|
|
%bridgename) == '2' else False)
|
|
self._apply_bridge_port_settings(ifaceobj, bridgename, None,
|
|
stp_on, mstpd_running)
|
|
ifaceobj.priv_flags |= self._BRIDGE_PORT_PROCESSED
|
|
return
|
|
if not self._is_bridge(ifaceobj):
|
|
return
|
|
stp = None
|
|
try:
|
|
porterr = False
|
|
porterrstr = ''
|
|
if ifaceobj.get_attr_value_first('mstpctl-ports'):
|
|
# If bridge ports specified with mstpctl attr, create the
|
|
# bridge and also add its ports
|
|
self.ipcmd.batch_start()
|
|
if not self.PERFMODE:
|
|
if not self.ipcmd.link_exists(ifaceobj.name):
|
|
self.ipcmd.link_create(ifaceobj.name, 'bridge')
|
|
else:
|
|
self.ipcmd.link_create(ifaceobj.name, 'bridge')
|
|
try:
|
|
self._add_ports(ifaceobj)
|
|
except Exception, e:
|
|
porterr = True
|
|
porterrstr = str(e)
|
|
pass
|
|
finally:
|
|
self.ipcmd.batch_commit()
|
|
stp = ifaceobj.get_attr_value_first('mstpctl-stp')
|
|
if stp:
|
|
self.set_iface_attr(ifaceobj, 'mstpctl-stp',
|
|
self.brctlcmd.set_stp)
|
|
else:
|
|
stp = self.brctlcmd.get_stp(ifaceobj.name)
|
|
if (self.mstpctlcmd.is_mstpd_running() and
|
|
(stp == 'yes' or stp == 'on')):
|
|
self._apply_bridge_settings(ifaceobj)
|
|
self._apply_bridge_port_settings_all(ifaceobj,
|
|
ifaceobj_getfunc=ifaceobj_getfunc)
|
|
except Exception, e:
|
|
self.log_error(str(e))
|
|
if porterr:
|
|
raise Exception(porterrstr)
|
|
|
|
def _down(self, ifaceobj, ifaceobj_getfunc=None):
|
|
if not self._is_bridge(ifaceobj):
|
|
return
|
|
try:
|
|
if ifaceobj.get_attr_value_first('mstpctl-ports'):
|
|
# If bridge ports specified with mstpctl attr, delete the
|
|
# bridge
|
|
ports = self.brctlcmd.get_bridge_ports(ifaceobj.name)
|
|
if ports:
|
|
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})
|
|
return bridgeattrdict
|
|
|
|
def _query_check_bridge(self, ifaceobj, ifaceobjcurr):
|
|
# list of attributes that are not supported currently
|
|
blacklistedattrs = ['mstpctl-portpathcost',
|
|
'mstpctl-treeportprio', 'mstpctl-treeportcost']
|
|
if not self.brctlcmd.bridge_exists(ifaceobj.name):
|
|
self.logger.debug('bridge %s does not exist' %ifaceobj.name)
|
|
return
|
|
ifaceattrs = self.dict_key_subset(ifaceobj.config,
|
|
self.get_mod_attrs())
|
|
if not ifaceattrs:
|
|
return
|
|
runningattrs = self.mstpctlcmd.get_bridge_attrs(ifaceobj.name)
|
|
if not runningattrs:
|
|
runningattrs = {}
|
|
for k in ifaceattrs:
|
|
# for all mstpctl options
|
|
if k in blacklistedattrs:
|
|
continue
|
|
# get the corresponding ifaceobj attr
|
|
v = ifaceobj.get_attr_value_first(k)
|
|
if not v:
|
|
continue
|
|
|
|
# Get the running attribute
|
|
rv = runningattrs.get(k[8:])
|
|
if k == 'mstpctl-stp':
|
|
# special case stp compare because it may
|
|
# contain more than one valid values
|
|
stp_on_vals = ['on', 'yes']
|
|
stp_off_vals = ['off']
|
|
rv = self.brctlcmd.get_stp(ifaceobj.name)
|
|
if ((v in stp_on_vals and rv in stp_on_vals) or
|
|
(v in stp_off_vals and rv in stp_off_vals)):
|
|
ifaceobjcurr.update_config_with_status('mstpctl-stp', v, 0)
|
|
else:
|
|
ifaceobjcurr.update_config_with_status('mstpctl-stp', v, 1)
|
|
continue
|
|
|
|
if k == 'mstpctl-ports':
|
|
# special case ports because it can contain regex or glob
|
|
# XXX: We get all info from mstputils, which means if
|
|
# mstpd is down, we will not be returning any bridge bridgeports
|
|
running_port_list = self.brctlcmd.get_bridge_ports(ifaceobj.name)
|
|
bridge_port_list = self._get_bridge_port_list(ifaceobj)
|
|
if not running_port_list and not bridge_port_list:
|
|
continue
|
|
portliststatus = 1
|
|
if running_port_list and bridge_port_list:
|
|
difference = Set(running_port_list).symmetric_difference(
|
|
Set(bridge_port_list))
|
|
if not difference:
|
|
portliststatus = 0
|
|
ifaceobjcurr.update_config_with_status('mstpctl-ports',
|
|
' '.join(running_port_list)
|
|
if running_port_list else '', portliststatus)
|
|
elif k[:12] == 'mstpctl-port' or k == 'mstpctl-bpduguard':
|
|
# Now, look at port attributes
|
|
# derive the mstpctlcmd attr name
|
|
#mstpctlcmdattrname = k[12:] if k[:12] == 'mstpctl-port' else k[8:]
|
|
mstpctlcmdattrname = k[8:]
|
|
|
|
# for port attributes, the attributes are in a list
|
|
# <portname>=<portattrvalue>
|
|
status = 0
|
|
currstr = ''
|
|
vlist = self.parse_port_list(v)
|
|
if not vlist:
|
|
continue
|
|
for vlistitem in vlist:
|
|
try:
|
|
(p, v) = vlistitem.split('=')
|
|
currv = self.mstpctlcmd.get_bridgeport_attr(
|
|
ifaceobj.name, p, mstpctlcmdattrname)
|
|
if currv:
|
|
currstr += ' %s=%s' %(p, currv)
|
|
else:
|
|
currstr += ' %s=%s' %(p, 'None')
|
|
if currv != v:
|
|
status = 1
|
|
except Exception, e:
|
|
self.log_warn(str(e))
|
|
pass
|
|
ifaceobjcurr.update_config_with_status(k, currstr, status)
|
|
elif not rv:
|
|
ifaceobjcurr.update_config_with_status(k, '', 1)
|
|
elif v != rv:
|
|
ifaceobjcurr.update_config_with_status(k, rv, 1)
|
|
else:
|
|
ifaceobjcurr.update_config_with_status(k, rv, 0)
|
|
|
|
def _query_check_bridge_port(self, ifaceobj, ifaceobjcurr):
|
|
if not self.ipcmd.link_exists(ifaceobj.name):
|
|
#self.logger.debug('bridge port %s does not exist' %ifaceobj.name)
|
|
ifaceobjcurr.status = ifaceStatus.NOTFOUND
|
|
return
|
|
# Check if this is a bridge port
|
|
if not self._is_bridge_port(ifaceobj):
|
|
# mark all the bridge attributes as error
|
|
ifaceobjcurr.check_n_update_config_with_status_many(ifaceobj,
|
|
self._port_attrs_map.keys(), 0)
|
|
return
|
|
bridgename = self.ipcmd.bridge_port_get_bridge_name(ifaceobj.name)
|
|
# list of attributes that are not supported currently
|
|
blacklistedattrs = ['mstpctl-portpathcost',
|
|
'mstpctl-treeportprio', 'mstpctl-treeportcost']
|
|
ifaceattrs = self.dict_key_subset(ifaceobj.config,
|
|
self._port_attrs_map.keys())
|
|
if not ifaceattrs:
|
|
return
|
|
runningattrs = self.mstpctlcmd.get_bridge_attrs(ifaceobj.name)
|
|
if not runningattrs:
|
|
runningattrs = {}
|
|
for k in ifaceattrs:
|
|
# for all mstpctl options
|
|
# get the corresponding ifaceobj attr
|
|
v = ifaceobj.get_attr_value_first(k)
|
|
if not v or k in blacklistedattrs:
|
|
ifaceobjcurr.update_config_with_status(k, v, -1)
|
|
continue
|
|
currv = self.mstpctlcmd.get_bridgeport_attr(bridgename,
|
|
ifaceobj.name, self._port_attrs_map.get(k))
|
|
if currv:
|
|
if currv != v:
|
|
ifaceobjcurr.update_config_with_status(k, currv, 1)
|
|
else:
|
|
ifaceobjcurr.update_config_with_status(k, currv, 0)
|
|
else:
|
|
ifaceobjcurr.update_config_with_status(k, None, 1)
|
|
|
|
def _query_check(self, ifaceobj, ifaceobjcurr, ifaceobj_getfunc=None):
|
|
if self._is_bridge(ifaceobj):
|
|
self._query_check_bridge(ifaceobj, ifaceobjcurr)
|
|
else:
|
|
self._query_check_bridge_port(ifaceobj, ifaceobjcurr)
|
|
|
|
def _query_running_bridge_port(self, ifaceobjrunning):
|
|
bridgename = self.ipcmd.bridge_port_get_bridge_name(
|
|
ifaceobjrunning.name)
|
|
if not bridgename:
|
|
self.logger.warn('%s: unable to determine bridgename'
|
|
%ifaceobjrunning.name)
|
|
return
|
|
if self.brctlcmd.get_stp(bridgename) == 'no':
|
|
# This bridge does not run stp, return
|
|
return
|
|
# if userspace stp not set, return
|
|
if self.sysctl_get('net.bridge.bridge-stp-user-space') != '1':
|
|
return
|
|
v = self.mstpctlcmd.get_bridgeport_attr(bridgename,
|
|
ifaceobjrunning.name,
|
|
'portnetwork')
|
|
if v and v != 'no':
|
|
ifaceobjrunning.update_config('mstpctl-network', v)
|
|
|
|
# XXX: Can we really get path cost of a port ???
|
|
#v = self.mstpctlcmd.get_portpathcost(ifaceobjrunning.name, p)
|
|
#if v and v != self.get_mod_subattr('mstpctl-pathcost',
|
|
# 'default'):
|
|
# ifaceobjrunning.update_config('mstpctl-network', v)
|
|
|
|
v = self.mstpctlcmd.get_bridgeport_attr(bridgename,
|
|
ifaceobjrunning.name, 'portadminedge')
|
|
if v and v != 'no':
|
|
ifaceobjrunning.update_config('mstpctl-portadminedge', v)
|
|
|
|
v = self.mstpctlcmd.get_bridgeport_attr(bridgename,
|
|
ifaceobjrunning.name,'portp2p')
|
|
if v and v != 'auto':
|
|
ifaceobjrunning.update_config('mstpctl-portp2p', v)
|
|
|
|
v = self.mstpctlcmd.get_bridgeport_attr(bridgename,
|
|
ifaceobjrunning.name, 'portrestrrole')
|
|
if v and v != 'no':
|
|
ifaceobjrunning.update_config('mstpctl-portrestrrole', v)
|
|
|
|
v = self.mstpctlcmd.get_bridgeport_attr(bridgename,
|
|
ifaceobjrunning.name, 'restrtcn')
|
|
if v and v != 'no':
|
|
ifaceobjrunning.update_config('mstpctl-portrestrtcn', v)
|
|
|
|
v = self.mstpctlcmd.get_bridgeport_attr(bridgename,
|
|
ifaceobjrunning.name, 'bpduguard')
|
|
if v and v != 'no':
|
|
ifaceobjrunning.update_config('mstpctl-bpduguard', v)
|
|
|
|
# XXX: Can we really get path cost of a port ???
|
|
#v = self.mstpctlcmd.get_bridgeport_attr(ifaceobjrunning.name,
|
|
# p, 'treeprio')
|
|
#if v and v != self.get_mod_subattr('mstpctl-treeportprio',
|
|
# 'default'):
|
|
# portconfig['mstpctl-treeportprio'] += ' %s=%s' %(p, v)
|
|
|
|
#v = self.mstpctlcmd.get_bridgeport_attr(ifaceobjrunning.name,
|
|
# p, 'treecost')
|
|
#if v and v != self.get_mod_subattr('mstpctl-treeportcost',
|
|
# 'default'):
|
|
# portconfig['mstpctl-treeportcost'] += ' %s=%s' %(p, v)
|
|
|
|
def _query_running_bridge(self, ifaceobjrunning):
|
|
if self.brctlcmd.get_stp(ifaceobjrunning.name) == 'no':
|
|
# This bridge does not run stp, return
|
|
return
|
|
# if userspace stp not set, return
|
|
if self.sysctl_get('net.bridge.bridge-stp-user-space') != '1':
|
|
return
|
|
# Check if mstp really knows about this bridge
|
|
if not self.mstpctlcmd.mstpbridge_exists(ifaceobjrunning.name):
|
|
return
|
|
ifaceobjrunning.update_config_dict(self._query_running_attrs(
|
|
ifaceobjrunning))
|
|
|
|
def _query_running(self, ifaceobjrunning, **extra_args):
|
|
if self.brctlcmd.bridge_exists(ifaceobjrunning.name):
|
|
self._query_running_bridge(ifaceobjrunning)
|
|
elif self.brctlcmd.is_bridge_port(ifaceobjrunning.name):
|
|
self._query_running_bridge_port(ifaceobjrunning)
|
|
|
|
_run_ops = {'pre-up' : _up,
|
|
'post-down' : _down,
|
|
'query-checkcurr' : _query_check,
|
|
'query-running' : _query_running}
|
|
|
|
def get_ops(self):
|
|
""" returns list of ops supported by this module """
|
|
return self._run_ops.keys()
|
|
|
|
def _init_command_handlers(self):
|
|
flags = self.get_flags()
|
|
if not self.ipcmd:
|
|
self.ipcmd = iproute2(**flags)
|
|
if not self.brctlcmd:
|
|
self.brctlcmd = brctl(**flags)
|
|
if not self.mstpctlcmd:
|
|
self.mstpctlcmd = mstpctlutil(**flags)
|
|
|
|
def run(self, ifaceobj, operation, query_ifaceobj=None,
|
|
ifaceobj_getfunc=None, **extra_args):
|
|
""" run mstp configuration on the interface object passed as argument
|
|
|
|
Args:
|
|
**ifaceobj** (object): iface object
|
|
|
|
**operation** (str): any of 'pre-up', 'post-down', 'query-checkcurr',
|
|
'query-running'
|
|
Kwargs:
|
|
**query_ifaceobj** (object): query check ifaceobject. This is only
|
|
valid when op is 'query-checkcurr'. It is an object same as
|
|
ifaceobj, but contains running attribute values and its config
|
|
status. The modules can use it to return queried running state
|
|
of interfaces. status is success if the running state is same
|
|
as user required state in ifaceobj. error otherwise.
|
|
"""
|
|
if ifaceobj.type == ifaceType.BRIDGE_VLAN:
|
|
return
|
|
op_handler = self._run_ops.get(operation)
|
|
if not op_handler:
|
|
return
|
|
self._init_command_handlers()
|
|
if operation == 'query-checkcurr':
|
|
op_handler(self, ifaceobj, query_ifaceobj,
|
|
ifaceobj_getfunc=ifaceobj_getfunc)
|
|
else:
|
|
op_handler(self, ifaceobj, ifaceobj_getfunc=ifaceobj_getfunc)
|