1
0
mirror of https://github.com/CumulusNetworks/ifupdown2.git synced 2024-05-06 15:54:50 +00:00
Sam Tannous b97687bc96 ifupdown not restoring mstpctl attributes (e.g. bpdufilter, bpduguard) in mstpctl
Ticket: CM-7756
Reviewed By: roopa
Testing Done: built powerpc and tested ifupdown2 as well as new tests

Once mstpctl-portbpdufilter or mstpctl-bpduguard is enabled for
an interface, removing the configuration in /etc/network/interfaces
does not toggle the mstpctl state back to no.

The root cause of this problem is that "ifreload" does not check default settings for MSTP configuration
for bridge ports and use a default when that setting is removed from the configuration.

This patch adds a check in the existing loop on attribute values.
If there is no configured value, we check to see if a default exists.
self._modinfo['attrs'][attrname]['default'] exists.
If it exists and it is different then the currently running value
we reset the attribute to its default. This is what a customer would expect if
they removed a configured value.

(also added test in cl-tests to check this functionality)

(cherry picked from commit 63d0f7082d44fedabe002aebbb658751dc655a46)
2015-12-09 14:02:29 -05:00

213 lines
8.5 KiB
Python

#!/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
import json
class mstpctlutil(utilsBase):
""" This class contains helper methods to interact with mstpd using
mstputils commands """
_cache_fill_done = False
_bridgeattrmap = {'bridgeid' : 'bridge-id',
'maxage' : 'max-age',
'fdelay' : 'forward-delay',
'txholdcount' : 'tx-hold-count',
'maxhops' : 'max-hops',
'ageing' : 'ageing-time',
'hello' : 'hello-time',
'forcevers' : 'force-protocol-version'}
_bridgeportattrmap = {'portadminedge' : 'admin-edge-port',
'portp2p' : 'admin-point-to-point',
'portrestrrole' : 'restricted-role',
'portrestrtcn' : 'restricted-TCN',
'bpduguard' : 'bpdu-guard-port',
'portautoedge' : 'auto-edge-port',
'portnetwork' : 'network-port',
'portbpdufilter' : 'bpdufilter-port'}
def __init__(self, *args, **kargs):
utilsBase.__init__(self, *args, **kargs)
def is_mstpd_running(self):
try:
self.exec_command('/bin/pidof mstpd')
except:
return False
else:
return True
def get_bridgeport_attr(self, bridgename, portname, attrname):
try:
return self.subprocess_check_output(['/sbin/mstpctl',
'showportdetail', '%s' %bridgename, '%s' %portname,
self._bridgeportattrmap[attrname]]).strip('\n')
except Exception, e:
pass
return None
def get_bridgeport_attrs(self, bridgename, portname):
bridgeattrs = {}
try:
bridgeattrs = dict((k, self.get_bridgeport_attr(bridgename, v))
for k, v in self._bridgeattrmap.items())
bridgeattrs['treeprio'] = int(bridgeattrs.get('bridgeid',
'').split('.')[0], base=16) * 4096
except Exception, e:
self.logger.warn(str(e))
pass
return bridgeattrs
def cache_bridgeport_attrs(self,bridgename):
'''
This method grab output of a mstpctl showportdetail json and caches
it this should save on the overhead of checking each attribute
for every port in the bridge.
'''
self.mstpctl_bridgeport_attrs_dict = {}
self.mstpctl_bridgeport_attrs_dict[bridgename] = {}
showall_output = self.subprocess_check_output(['/sbin/mstpctl',
'showportdetail', bridgename, 'json']).strip('\n')
if showall_output == None or showall_output == '':
return
mstpctl_bridge_cache = json.loads(showall_output)
for portname in mstpctl_bridge_cache.keys():
# we will ignore the portid for now and just index
# by bridgename, portname, and json attribute
for portid in mstpctl_bridge_cache[portname].keys():
self.mstpctl_bridgeport_attrs_dict[bridgename][portname] = {}
for jsonAttr in mstpctl_bridge_cache[portname][portid].keys():
jsonVal = mstpctl_bridge_cache[portname][portid][jsonAttr]
self.mstpctl_bridgeport_attrs_dict[bridgename][portname]\
[jsonAttr] = str(jsonVal)
def get_mstpctl_bridgeport_attr(self,bridgename=None, portname=None,
jsonAttr=None):
'''
Just return the JSON attribute we cached earlier making
sure to convert integers to strings for later comparison.
'''
if not bridgename or not portname or not jsonAttr:
return
# just return the value or None if there is no JSON attr defined the
# output will not show anything if the value is no so we default to no
val = self.mstpctl_bridgeport_attrs_dict.get(bridgename,{}).get(portname,{}).\
get(jsonAttr,'no')
if val == 'True':
val = 'yes'
# some values are integers so we need to return only strings
return str(val)
def set_bridgeport_attrs(self, bridgename, bridgeportname, attrdict,
check=True):
for k, v in attrdict.iteritems():
if not v:
continue
try:
self.set_bridgeport_attr(self, bridgename, bridgeportname,
k, v, check)
except Exception, e:
self.logger.warn(str(e))
def set_bridgeport_attr(self, bridgename, bridgeportname, attrname,
attrvalue, check=True):
if check:
attrvalue_curr = self.get_bridgeport_attr(bridgename,
bridgeportname, attrname)
if attrvalue_curr and attrvalue_curr == attrvalue:
return
if attrname == 'treeportcost' or attrname == 'treeportprio':
self.subprocess_check_output(['/sbin/mstpctl', 'set%s' %attrname,
'%s' %bridgename, '%s' %bridgeportname, '0', '%s' %attrvalue])
else:
self.subprocess_check_output(['/sbin/mstpctl', 'set%s' %attrname,
'%s' %bridgename, '%s' %bridgeportname, '%s' %attrvalue])
def get_bridge_attrs(self, bridgename):
bridgeattrs = {}
try:
bridgeattrs = dict((k, self.get_bridge_attr(bridgename, k))
for k in self._bridgeattrmap.keys())
bridgeattrs['treeprio'] = '%d' %(int(bridgeattrs.get('bridgeid',
'').split('.')[0], base=16) * 4096)
del bridgeattrs['bridgeid']
except Exception, e:
self.logger.debug(bridgeattrs)
self.logger.debug(str(e))
pass
return bridgeattrs
def get_bridge_attr(self, bridgename, attrname):
try:
return self.subprocess_check_output(['/sbin/mstpctl',
'showbridge', '%s' %bridgename,
self._bridgeattrmap[attrname]]).strip('\n')
except Exception, e:
pass
return None
def set_bridge_attr(self, bridgename, attrname, attrvalue, check=True):
if check:
attrvalue_curr = self.get_bridge_attr(bridgename, attrname)
if attrvalue_curr and attrvalue_curr == attrvalue:
return
if attrname == 'treeprio':
self.subprocess_check_call(['/sbin/mstpctl', 'set%s' %attrname,
'%s' %bridgename, '0', '%s' %attrvalue])
else:
self.subprocess_check_call(['/sbin/mstpctl', 'set%s' %attrname,
'%s' %bridgename, '%s' %attrvalue])
def set_bridge_attrs(self, bridgename, attrdict, check=True):
for k, v in attrdict.iteritems():
if not v:
continue
try:
self.set_bridge_attr(bridgename, k, v, check)
except Exception, e:
self.logger.warn('%s: %s' %(bridgename, str(e)))
pass
def get_bridge_treeprio(self, bridgename):
try:
bridgeid = subprocess.check_output(['/sbin/mstpctl',
'showbridge', '%s' %bridgename,
self._bridgeattrmap['bridgeid']]).strip('\n')
return '%d' %(int(bridgeid.split('.')[0], base=16) * 4096)
except:
pass
return None
def set_bridge_treeprio(self, bridgename, attrvalue, check=True):
if check:
attrvalue_curr = self.get_bridge_treeprio(bridgename)
if attrvalue_curr and attrvalue_curr == attrvalue:
return
self.subprocess_check_output(['/sbin/mstpctl', 'settreeprio',
'%s' %bridgename, '0', '%s' %attrvalue])
def showbridge(self, bridgename=None):
if bridgename:
return self.exec_command('/sbin/mstpctl showbridge %s' %bridgename)
else:
return self.exec_command('/sbin/mstpctl showbridge')
def showportdetail(self, bridgename):
return self.exec_command('/sbin/mstpctl showportdetail %s' %bridgename)
def mstpbridge_exists(self, bridgename):
try:
subprocess.check_call('mstpctl showbridge %s' %bridgename)
return True
except:
return False