mirror of
https://github.com/CumulusNetworks/ifupdown2.git
synced 2024-05-06 15:54:50 +00:00
When clagd anycast ip configuration changes on an existing setup, we have two issues: - populate_dependency_info is run twice (in the ifreload case), first on the new ifaceobjs, then on the old ifaceobjs. Thus hitting vxlan.get_dependent_ifacenames twice where vxlan._clagd_vxlan_anycast_ip is set (the first time properly, then reset to it's old value). The fix: add a "old_ifaceobjs" flag to avoid resetting vxlan._clagd_vxlan_anycast_ip - when clagd anycast ip changes, clagd also updates the vxlan's ip but there's a chance that the ifupdown2 cache won't get the netlink notification in time before UP ops are running on the vxlans, running on a stale cache is no bueno. The fix: add additional checks to see if we should trust the cache of not. Signed-off-by: Julien Fortin <jfortin@nvidia.com>
1328 lines
60 KiB
Python
1328 lines
60 KiB
Python
#!/usr/bin/env python3
|
|
#
|
|
# Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved.
|
|
# Author: Roopa Prabhu, roopa@cumulusnetworks.com
|
|
#
|
|
|
|
import os
|
|
|
|
try:
|
|
from ifupdown2.lib.addon import Addon
|
|
|
|
from ifupdown2.ifupdown.iface import *
|
|
from ifupdown2.ifupdown.utils import utils
|
|
|
|
import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
|
|
import ifupdown2.ifupdown.policymanager as policymanager
|
|
|
|
from ifupdown2.ifupdownaddons.modulebase import moduleBase
|
|
from ifupdown2.ifupdownaddons.mstpctlutil import mstpctlutil
|
|
from ifupdown2.ifupdownaddons.systemutils import systemUtils
|
|
from ifupdown2.ifupdown.exceptions import moduleNotSupported
|
|
except (ImportError, ModuleNotFoundError):
|
|
from lib.addon import Addon
|
|
|
|
from ifupdown.iface import *
|
|
from ifupdown.utils import utils
|
|
|
|
import ifupdown.ifupdownflags as ifupdownflags
|
|
import ifupdown.policymanager as policymanager
|
|
|
|
from ifupdownaddons.modulebase import moduleBase
|
|
from ifupdownaddons.mstpctlutil import mstpctlutil
|
|
from ifupdownaddons.systemutils import systemUtils
|
|
from ifupdown.exceptions import moduleNotSupported
|
|
|
|
|
|
class mstpctlFlags:
|
|
PORT_PROCESSED = 0x1
|
|
|
|
class mstpctl(Addon, moduleBase):
|
|
""" ifupdown2 addon module to configure mstp attributes """
|
|
|
|
_modinfo = {
|
|
"mhelp": "mstp configuration module for bridges",
|
|
"attrs": {
|
|
"mstpctl-ports": {
|
|
"help": "mstp ports",
|
|
"compat": True,
|
|
"deprecated": True,
|
|
"new-attribute": "bridge-ports"
|
|
},
|
|
"mstpctl-stp": {
|
|
"help": "bridge stp yes/no",
|
|
"validvals": ["yes", "no", "on", "off"],
|
|
"compat": True,
|
|
"default": "no",
|
|
"deprecated": True,
|
|
"new-attribute": "bridge-stp"
|
|
},
|
|
"mstpctl-treeprio": {
|
|
"help": "tree priority",
|
|
"default": "32768",
|
|
"validvals": [
|
|
"0", "4096", "8192", "12288", "16384",
|
|
"20480", "24576", "28672", "32768",
|
|
"36864", "40960", "45056", "49152",
|
|
"53248", "57344", "61440"
|
|
],
|
|
"required": False,
|
|
"example": ["mstpctl-treeprio 32768"]
|
|
},
|
|
"mstpctl-ageing": {
|
|
"help": "ageing time",
|
|
"validrange": ["0", "4096"],
|
|
"default": "300",
|
|
"required": False,
|
|
"jsonAttr": "ageingTime",
|
|
"example": ["mstpctl-ageing 300"]
|
|
},
|
|
"mstpctl-maxage": {
|
|
"help": "max message age",
|
|
"validrange": ["0", "255"],
|
|
"default": "20",
|
|
"jsonAttr": "bridgeMaxAge",
|
|
"required": False,
|
|
"example": ["mstpctl-maxage 20"]
|
|
},
|
|
"mstpctl-fdelay": {
|
|
"help": "set forwarding delay",
|
|
"validrange": ["0", "255"],
|
|
"default": "15",
|
|
"jsonAttr": "bridgeFwdDelay",
|
|
"required": False,
|
|
"example": ["mstpctl-fdelay 15"]
|
|
},
|
|
"mstpctl-maxhops": {
|
|
"help": "bridge max hops",
|
|
"validrange": ["0", "255"],
|
|
"default": "20",
|
|
"jsonAttr": "maxHops",
|
|
"required": False,
|
|
"example": ["mstpctl-maxhops 15"]
|
|
},
|
|
"mstpctl-txholdcount": {
|
|
"help": "bridge transmit holdcount",
|
|
"validrange": ["0", "255"],
|
|
"default": "6",
|
|
"jsonAttr": "txHoldCounter",
|
|
"required": False,
|
|
"example": ["mstpctl-txholdcount 6"]
|
|
},
|
|
"mstpctl-forcevers": {
|
|
"help": "bridge force stp version",
|
|
"validvals": ["rstp", ],
|
|
"default": "rstp",
|
|
"required": False,
|
|
"jsonAttr": "forceProtocolVersion",
|
|
"example": ["mstpctl-forcevers rstp"]
|
|
},
|
|
"mstpctl-portpathcost": {
|
|
"help": "bridge port path cost",
|
|
"validvals": ["<interface-range-list>"],
|
|
"validrange": ["0", "65535"],
|
|
"default": "0",
|
|
"jsonAttr": "adminExtPortCost",
|
|
"required": False,
|
|
"example": [
|
|
"under the bridge: mstpctl-portpathcost swp1=0 swp2=1",
|
|
"under the port (recommended): mstpctl-portpathcost 0"
|
|
]
|
|
},
|
|
"mstpctl-portp2p": {
|
|
"help": "bridge port p2p detection mode",
|
|
"default": "auto",
|
|
"jsonAttr": "adminPointToPoint",
|
|
"validvals": ["<interface-yes-no-auto-list>"],
|
|
"required": False,
|
|
"example": [
|
|
"under the bridge: mstpctl-portp2p swp1=yes swp2=no",
|
|
"under the port (recommended): mstpctl-portp2p yes"
|
|
]
|
|
},
|
|
"mstpctl-portrestrrole": {
|
|
"help":
|
|
"enable/disable port ability to take root role of the port",
|
|
"default": "no",
|
|
"jsonAttr": "restrictedRole",
|
|
"validvals": ["<interface-yes-no-list>"],
|
|
"required": False,
|
|
"example": [
|
|
"under the bridge: mstpctl-portrestrrole swp1=yes swp2=no",
|
|
"under the port (recommended): mstpctl-portrestrrole yes"
|
|
]
|
|
},
|
|
"mstpctl-portrestrtcn": {
|
|
"help":
|
|
"enable/disable port ability to propagate received "
|
|
"topology change notification of the port",
|
|
"default": "no",
|
|
"jsonAttr": "restrictedTcn",
|
|
"validvals": ["<interface-yes-no-list>"],
|
|
"required": False,
|
|
"example": [
|
|
"under the bridge: mstpctl-portrestrtcn swp1=yes swp2=no",
|
|
"under the port (recommended): mstpctl-portrestrtcn yes"
|
|
]
|
|
},
|
|
"mstpctl-bpduguard": {
|
|
"help":
|
|
"enable/disable bpduguard",
|
|
"default": "no",
|
|
"jsonAttr": "bpduGuardPort",
|
|
"validvals": ["<interface-yes-no-list>"],
|
|
"required": False,
|
|
"example": [
|
|
"under the bridge: mstpctl-bpduguard swp1=yes swp2=no",
|
|
"under the port (recommended): mstpctl-bpduguard yes"
|
|
]
|
|
},
|
|
"mstpctl-treeportprio": {
|
|
"help": "Sets the <port>'s priority MSTI instance. "
|
|
"The priority value must be a number between 0 and 240 "
|
|
"and a multiple of 16.",
|
|
"default": "128",
|
|
"validvals": ["<interface-range-list-multiple-of-16>"],
|
|
"validrange": ["0", "240"],
|
|
"jsonAttr": "treeportprio",
|
|
"required": False,
|
|
"example": [
|
|
"under the bridge: mstpctl-treeportprio swp1=128 swp2=128",
|
|
"under the port (recommended): mstpctl-treeportprio 128"
|
|
]
|
|
},
|
|
"mstpctl-hello": {
|
|
"help": "set hello time",
|
|
"validrange": ["0", "255"],
|
|
"default": "2",
|
|
"required": False,
|
|
"jsonAttr": "helloTime",
|
|
"example": ["mstpctl-hello 2"]
|
|
},
|
|
"mstpctl-portnetwork": {
|
|
"help": "enable/disable bridge assurance capability for a port",
|
|
"validvals": ["<interface-yes-no-list>"],
|
|
"default": "no",
|
|
"jsonAttr": "networkPort",
|
|
"required": False,
|
|
"example": [
|
|
"under the bridge: mstpctl-portnetwork swp1=yes swp2=no",
|
|
"under the port (recommended): mstpctl-portnetwork yes"
|
|
]
|
|
},
|
|
"mstpctl-portadminedge": {
|
|
"help": "enable/disable initial edge state of the port",
|
|
"validvals": ["<interface-yes-no-list>"],
|
|
"default": "no",
|
|
"jsonAttr": "adminEdgePort",
|
|
"required": False,
|
|
"example": [
|
|
"under the bridge: mstpctl-portadminedge swp1=yes swp2=no",
|
|
"under the port (recommended): mstpctl-portadminedge yes"
|
|
]
|
|
},
|
|
"mstpctl-portautoedge": {
|
|
"help": "enable/disable auto transition to/from edge state of the port",
|
|
"validvals": ["<interface-yes-no-list>"],
|
|
"default": "yes",
|
|
"jsonAttr": "autoEdgePort",
|
|
"required": False,
|
|
"example": [
|
|
"under the bridge: mstpctl-portautoedge swp1=yes swp2=no",
|
|
"under the port (recommended): mstpctl-portautoedge yes"
|
|
]
|
|
},
|
|
"mstpctl-treeportcost": {
|
|
"help": "port tree cost",
|
|
# "validrange": ["0", "255"],
|
|
"required": False,
|
|
"jsonAttr": "extPortCost",
|
|
},
|
|
"mstpctl-portbpdufilter": {
|
|
"help": "enable/disable bpdu filter on a port. syntax varies "
|
|
"when defined under a bridge vs under a port",
|
|
"validvals": ["<interface-yes-no-list>"],
|
|
"jsonAttr": "bpduFilterPort",
|
|
"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-fdelay' , 'fdelay'),
|
|
('mstpctl-maxage' , 'maxage'),
|
|
('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'}
|
|
|
|
def __init__(self, *args, **kargs):
|
|
Addon.__init__(self)
|
|
moduleBase.__init__(self, *args, **kargs)
|
|
if not os.path.exists('/sbin/mstpctl'):
|
|
raise moduleNotSupported('module init failed: no /sbin/mstpctl found')
|
|
self.name = self.__class__.__name__
|
|
self.mstpctlcmd = None
|
|
self.mstpd_running = (True if systemUtils.is_process_running('mstpd')
|
|
else False)
|
|
|
|
# Background -
|
|
# The ask is to make "mstpctl-portadminedge yes" part of the default ifupdown2
|
|
# policy for all vxlan interfaces. In the absence of this, the mstp work flow
|
|
# is flawed in the event of vxlan flap.
|
|
# Details -
|
|
# As of today, for vxlan interfaces "oper edge port" is set to 'yes' and also
|
|
# "bpdufilter port" is also set to 'yes'. So, in a case where bridge has multiple
|
|
# vxlan interfaces, if one vxlan interface is flapped, this would trigger mstp
|
|
# re-evaluation of states on other vxlan interfaces, creating momentary traffic
|
|
# glitch on those vxlans. Setting "admin edge port" to yes (in addition to the
|
|
# defaults we already have) prevents this.
|
|
#
|
|
# We use to only support 'mstpctl-vxlan-always-set-bpdu-params' but introducing a
|
|
# separate policy attribute doesn't make sense, we should have one single
|
|
# attribute to handle the whole thing (and deprecate mstpctl-vxlan-always-set-bpdu-params)
|
|
# mstpctl-set-default-vxlan-bridge-attrs=yes will set
|
|
# mstpctl-portbpdufilter
|
|
# mstpctl-bpduguard
|
|
# mstpctl-portadminedge
|
|
#
|
|
self.set_default_mstp_vxlan_bridge_config = utils.get_boolean_from_string(
|
|
policymanager.policymanager_api.get_module_globals(
|
|
module_name=self.__class__.__name__,
|
|
attr='mstpctl-vxlan-always-set-bpdu-params'
|
|
)
|
|
) or utils.get_boolean_from_string(
|
|
policymanager.policymanager_api.get_module_globals(
|
|
module_name=self.__class__.__name__,
|
|
attr='mstpctl-set-default-vxlan-bridge-attrs'
|
|
)
|
|
)
|
|
|
|
def syntax_check(self, ifaceobj, ifaceobj_getfunc):
|
|
if self._is_bridge(ifaceobj):
|
|
if (ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_VLAN_AWARE
|
|
and ifaceobj.get_attr_value_first('mstpctl-portadminedge')):
|
|
self.logger.error('%s: unsupported use of keyword '
|
|
'\'mstpctl-portadminedge\' when '
|
|
'bridge-vlan-aware is on'
|
|
% ifaceobj.name)
|
|
return False
|
|
return True
|
|
|
|
def _is_bridge(self, ifaceobj):
|
|
return ifaceobj.link_kind & ifaceLinkKind.BRIDGE \
|
|
or ifaceobj.get_attr_value_first('mstpctl-ports') \
|
|
or ifaceobj.get_attr_value_first('bridge-ports')
|
|
|
|
def get_dependent_ifacenames(self, ifaceobj, ifacenames_all=None, old_ifaceobjs=False):
|
|
if not self._is_bridge(ifaceobj):
|
|
return None
|
|
return self.parse_port_list(ifaceobj.name,
|
|
ifaceobj.get_attr_value_first(
|
|
'mstpctl-ports'), ifacenames_all)
|
|
|
|
def get_dependent_ifacenames_running(self, ifaceobj):
|
|
self._init_command_handlers()
|
|
if (self.cache.bridge_exists(ifaceobj.name) and
|
|
not self.mstpctlcmd.mstpbridge_exists(ifaceobj.name)):
|
|
return None
|
|
return self.cache.get_slaves(ifaceobj.name)
|
|
|
|
def _get_bridge_port_attr_value(self, bridgename, portname, attr):
|
|
json_attr = self.get_mod_subattr(attr, 'jsonAttr')
|
|
return self.mstpctlcmd.get_bridge_port_attr(bridgename,
|
|
portname,
|
|
json_attr)
|
|
|
|
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(ifaceobj.name, ports)
|
|
else:
|
|
return None
|
|
|
|
def _ports_enable_disable_ipv6(self, ports, enable='1'):
|
|
for p in ports:
|
|
try:
|
|
self.write_file('/proc/sys/net/ipv6/conf/%s' %p +
|
|
'/disable_ipv6', enable)
|
|
except Exception as e:
|
|
self.logger.info(str(e))
|
|
pass
|
|
|
|
def _add_ports(self, ifaceobj):
|
|
bridgeports = self._get_bridge_port_list(ifaceobj)
|
|
|
|
runningbridgeports = []
|
|
# Delete active ports not in the new port list
|
|
if not ifupdownflags.flags.PERFMODE:
|
|
runningbridgeports = self.cache.get_slaves(ifaceobj.name)
|
|
if runningbridgeports:
|
|
for bport in runningbridgeports:
|
|
if not bridgeports or bport not in bridgeports:
|
|
self.netlink.link_set_nomaster(bport)
|
|
else:
|
|
runningbridgeports = []
|
|
if not bridgeports:
|
|
return
|
|
err = 0
|
|
for bridgeport in set(bridgeports).difference(set(runningbridgeports)):
|
|
try:
|
|
if (not ifupdownflags.flags.DRYRUN and
|
|
not self.cache.link_exists(bridgeport)):
|
|
self.log_warn('%s: bridge port %s does not exist'
|
|
%(ifaceobj.name, bridgeport))
|
|
err += 1
|
|
continue
|
|
self.netlink.link_set_master(bridgeport, ifaceobj.name)
|
|
self.netlink.addr_flush(bridgeport)
|
|
except Exception as e:
|
|
self.log_error(str(e), ifaceobj)
|
|
|
|
if err:
|
|
self.log_error('error configuring bridge (missing ports)')
|
|
|
|
def _apply_bridge_settings(self, ifaceobj, ifaceobj_getfunc):
|
|
check = False if ifupdownflags.flags.PERFMODE else True
|
|
try:
|
|
# set bridge attributes
|
|
for attrname, dstattrname in list(self._attrs_map.items()):
|
|
config_val = ifaceobj.get_attr_value_first(attrname)
|
|
default_val = policymanager.policymanager_api.get_iface_default(module_name=self.__class__.__name__, ifname=ifaceobj.name, attr=attrname)
|
|
if not default_val:
|
|
default_val = self.get_mod_subattr(attrname, 'default')
|
|
jsonAttr = self.get_mod_subattr(attrname, 'jsonAttr')
|
|
try:
|
|
running_val = self.mstpctlcmd.get_bridge_attr(
|
|
ifaceobj.name, jsonAttr)
|
|
except Exception:
|
|
self.logger.info('%s: could not get running %s value'
|
|
%(ifaceobj.name, attrname))
|
|
running_val = None
|
|
if (not config_val and default_val and (running_val != default_val)):
|
|
# this happens when users remove an attribute from a port
|
|
# and expect the default to be restored with ifreload.
|
|
config_val = default_val
|
|
elif not config_val:
|
|
# there is nothing configured and no default to reset
|
|
continue
|
|
try:
|
|
if attrname == 'mstpctl-treeprio':
|
|
self.mstpctlcmd.set_bridge_treeprio(ifaceobj.name,
|
|
config_val, check)
|
|
else:
|
|
self.mstpctlcmd.set_bridge_attr(ifaceobj.name,
|
|
dstattrname, config_val, check)
|
|
except Exception as e:
|
|
self.logger.warning('%s' %str(e))
|
|
pass
|
|
|
|
if self.cache.bridge_is_vlan_aware(ifaceobj.name):
|
|
return
|
|
bridgeports = self._get_bridge_port_list(ifaceobj)
|
|
if not bridgeports:
|
|
return
|
|
# set bridge port attributes
|
|
for attrname, dstattrname in list(self._port_attrs_map.items()):
|
|
config_val = ifaceobj.get_attr_value_first(attrname)
|
|
default_val = self.get_mod_subattr(attrname,'default')
|
|
if not config_val:
|
|
# nothing configured, we may need to reset all ports to defaults
|
|
# if the default exists and jsonAttribute conversion exists
|
|
try:
|
|
jsonAttr = self.get_mod_subattr(attrname, 'jsonAttr')
|
|
if default_val and jsonAttr:
|
|
for port in bridgeports:
|
|
if not self.cache.link_is_bridge_port(port):
|
|
continue
|
|
|
|
bport_ifaceobjs = ifaceobj_getfunc(port)
|
|
if bport_ifaceobjs:
|
|
default_val = self._get_default_val(attrname, bport_ifaceobjs[0], ifaceobj)
|
|
for brport_ifaceobj in bport_ifaceobjs or []:
|
|
attr_value = brport_ifaceobj.get_attr_value_first(attrname)
|
|
if attr_value:
|
|
default_val = attr_value
|
|
break
|
|
|
|
self.mstpctlcmd.set_bridge_port_attr(ifaceobj.name,
|
|
port,
|
|
dstattrname,
|
|
default_val,
|
|
json_attr=jsonAttr)
|
|
except Exception as e:
|
|
self.logger.debug('%s' % str(e))
|
|
self.logger.info('%s: not resetting %s config'
|
|
%(ifaceobj.name, attrname))
|
|
# leave the loop for this attribute
|
|
continue
|
|
|
|
portlist = self.parse_port_list(ifaceobj.name, config_val)
|
|
if not portlist:
|
|
self.log_error('%s: error parsing \'%s %s\''
|
|
%(ifaceobj.name, attrname, config_val), ifaceobj)
|
|
continue
|
|
# there was a configured value so we need to parse it
|
|
# and set the attribute for each port configured
|
|
for p in portlist:
|
|
try:
|
|
(port, val) = p.split('=')
|
|
# if it is not bridge port, continue
|
|
if not os.path.exists('/sys/class/net/%s/brport' %port):
|
|
continue
|
|
json_attr = self.get_mod_subattr(attrname, 'jsonAttr')
|
|
self.mstpctlcmd.set_bridge_port_attr(ifaceobj.name,
|
|
port,
|
|
dstattrname,
|
|
val,
|
|
json_attr=json_attr)
|
|
except Exception as e:
|
|
self.log_error('%s: error setting %s (%s)'
|
|
%(ifaceobj.name, attrname, str(e)),
|
|
ifaceobj, raise_error=False)
|
|
except Exception as e:
|
|
self.log_warn(str(e))
|
|
pass
|
|
|
|
def _get_default_val(self, attr, ifaceobj, bridgeifaceobj):
|
|
if (self.set_default_mstp_vxlan_bridge_config
|
|
and ifaceobj.link_kind & ifaceLinkKind.VXLAN
|
|
and attr in (
|
|
'mstpctl-portbpdufilter',
|
|
'mstpctl-bpduguard',
|
|
'mstpctl-portadminedge',
|
|
)
|
|
):
|
|
try:
|
|
config_val = bridgeifaceobj.get_attr_value_first(attr)
|
|
except Exception as e:
|
|
config_val = None
|
|
if config_val:
|
|
if ifaceobj.name not in [v.split('=')[0] for v in config_val.split()]:
|
|
return 'yes'
|
|
else:
|
|
index = [v.split('=')[0] for v in config_val.split()].index(ifaceobj.name)
|
|
return [v.split('=')[1] for v in config_val.split()][index]
|
|
else:
|
|
return 'yes'
|
|
else:
|
|
default_val = policymanager.policymanager_api.get_iface_default(module_name=self.__class__.__name__, ifname=ifaceobj.name, attr=attr)
|
|
if not default_val:
|
|
return self.get_mod_subattr(attr,'default')
|
|
return default_val
|
|
|
|
def _apply_bridge_port_settings(self, ifaceobj, bvlan_aware, bridgename=None,
|
|
bridgeifaceobj=None,
|
|
stp_running_on=True,
|
|
mstpd_running=True):
|
|
check = False if ifupdownflags.flags.PERFMODE else True
|
|
applied = False
|
|
if not bridgename and bridgeifaceobj:
|
|
bridgename = bridgeifaceobj.name
|
|
|
|
if not stp_running_on:
|
|
# stp may get turned on at a later point
|
|
self.logger.info('%s: ignoring config'
|
|
%(ifaceobj.name) +
|
|
' (stp on bridge %s is not on yet)' %bridgename)
|
|
return applied
|
|
if (not mstpd_running or
|
|
not os.path.exists('/sys/class/net/%s/brport' %ifaceobj.name) or
|
|
not bvlan_aware):
|
|
if (not bvlan_aware and
|
|
self.set_default_mstp_vxlan_bridge_config and
|
|
(ifaceobj.link_kind & ifaceLinkKind.VXLAN)):
|
|
for attr in (
|
|
'mstpctl-portbpdufilter',
|
|
'mstpctl-bpduguard',
|
|
'mstpctl-portadminedge'
|
|
):
|
|
json_attr = self.get_mod_subattr(attr, 'jsonAttr')
|
|
config_val = self._get_default_val(attr, ifaceobj,
|
|
bridgeifaceobj)
|
|
try:
|
|
self.mstpctlcmd.set_bridge_port_attr(bridgename,
|
|
ifaceobj.name,
|
|
self._port_attrs_map[attr],
|
|
config_val,
|
|
json_attr=json_attr)
|
|
except Exception as e:
|
|
self.log_warn('%s: error setting %s (%s)'
|
|
% (ifaceobj.name, attr, str(e)))
|
|
|
|
if not bvlan_aware:
|
|
# for "traditional" bridges we also want to let the user configure
|
|
# some attributes (possibly all of them in the future)
|
|
applied = self._apply_bridge_port_settings_attributes_list(
|
|
(
|
|
('mstpctl-portrestrrole', 'portrestrrole'),
|
|
('mstpctl-portautoedge', 'portautoedge')
|
|
),
|
|
ifaceobj,
|
|
bridgeifaceobj,
|
|
bridgename,
|
|
applied
|
|
)
|
|
return applied
|
|
# set bridge port attributes
|
|
return self._apply_bridge_port_settings_attributes_list(
|
|
list(self._port_attrs_map.items()),
|
|
ifaceobj,
|
|
bridgeifaceobj,
|
|
bridgename,
|
|
applied
|
|
)
|
|
|
|
def _apply_bridge_port_settings_attributes_list(self, attributes_list, ifaceobj, bridgeifaceobj, bridgename, applied):
|
|
for attrname, dstattrname in attributes_list:
|
|
config_val = ifaceobj.get_attr_value_first(attrname)
|
|
default_val = self._get_default_val(attrname, ifaceobj, bridgeifaceobj)
|
|
jsonAttr = self.get_mod_subattr(attrname, 'jsonAttr')
|
|
# to see the running value, stp would have to be on
|
|
# so we would have parsed mstpctl showportdetail json output
|
|
try:
|
|
running_val = self.mstpctlcmd.get_bridge_port_attr(bridgename,
|
|
ifaceobj.name, jsonAttr)
|
|
except Exception:
|
|
self.logger.info('%s %s: could not get running %s value'
|
|
%(bridgename, ifaceobj.name, attrname))
|
|
running_val = None
|
|
if (not config_val and default_val and (running_val != default_val)):
|
|
# this happens when users remove an attribute from a port
|
|
# and expect the default to be restored with ifreload.
|
|
config_val = default_val
|
|
elif not config_val:
|
|
# there is nothing configured and no default to reset
|
|
continue
|
|
|
|
try:
|
|
self.mstpctlcmd.set_bridge_port_attr(bridgename,
|
|
ifaceobj.name, dstattrname, config_val, json_attr=jsonAttr)
|
|
applied = True
|
|
except Exception as e:
|
|
self.log_error('%s: error setting %s (%s)'
|
|
%(ifaceobj.name, attrname, str(e)), ifaceobj,
|
|
raise_error=False)
|
|
return applied
|
|
|
|
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.cache.get_slaves(ifaceobj.name)
|
|
if not bridgeports:
|
|
self.logger.debug('%s: cannot find bridgeports' %ifaceobj.name)
|
|
return
|
|
|
|
bvlan_aware = self.cache.bridge_is_vlan_aware(ifaceobj.name)
|
|
|
|
for bport in bridgeports:
|
|
self.logger.info('%s: processing mstp config for port %s'
|
|
%(ifaceobj.name, bport))
|
|
if not self.cache.link_exists(bport):
|
|
continue
|
|
if not os.path.exists('/sys/class/net/%s/brport' %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.module_flags.get(self.name,0x0) & \
|
|
mstpctlFlags.PORT_PROCESSED):
|
|
continue
|
|
try:
|
|
self._apply_bridge_port_settings(bportifaceobj, bvlan_aware,
|
|
ifaceobj.name, ifaceobj)
|
|
except Exception as e:
|
|
pass
|
|
self.log_warn(str(e))
|
|
|
|
def _is_running_userspace_stp_state_on(self, bridgename):
|
|
stp_state_file = '/sys/class/net/%s/bridge/stp_state' %bridgename
|
|
if not stp_state_file:
|
|
return False
|
|
running_stp_state = self.read_file_oneline(stp_state_file)
|
|
if running_stp_state and running_stp_state == '2':
|
|
return True
|
|
return False
|
|
|
|
def _up(self, ifaceobj, ifaceobj_getfunc=None):
|
|
|
|
# bridge port specific:
|
|
if ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT:
|
|
bridgename = self.cache.get_master(ifaceobj.name)
|
|
|
|
if not bridgename:
|
|
return
|
|
bvlan_aware = self.cache.bridge_is_vlan_aware(bridgename)
|
|
mstpd_running = self.mstpd_running
|
|
stp_running_on = bool(self.cache.get_bridge_stp(bridgename))
|
|
applied = self._apply_bridge_port_settings(ifaceobj, bvlan_aware, bridgename,
|
|
None, stp_running_on,
|
|
mstpd_running)
|
|
if applied:
|
|
ifaceobj.module_flags[self.name] = \
|
|
ifaceobj.module_flags.setdefault(self.name,0) | \
|
|
mstpctlFlags.PORT_PROCESSED
|
|
return
|
|
|
|
elif not self._is_bridge(ifaceobj):
|
|
return
|
|
# we are now here because the ifaceobj is a bridge
|
|
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
|
|
if not self.cache.link_exists(ifaceobj.name):
|
|
self.netlink.link_add_bridge(ifaceobj.name)
|
|
|
|
try:
|
|
self._add_ports(ifaceobj)
|
|
except Exception as e:
|
|
porterr = True
|
|
porterrstr = str(e)
|
|
pass
|
|
|
|
running_ports = self.cache.get_slaves(ifaceobj.name)
|
|
if running_ports:
|
|
# disable ipv6 for ports that were added to bridge
|
|
self._ports_enable_disable_ipv6(running_ports, '1')
|
|
|
|
stp = ifaceobj.get_attr_value_first('mstpctl-stp')
|
|
if stp:
|
|
self.set_iface_attr(ifaceobj, 'mstpctl-stp',
|
|
self.iproute2.bridge_set_stp)
|
|
else:
|
|
stp = ifaceobj.get_attr_value_first('bridge-stp')
|
|
|
|
if not stp:
|
|
stp = self.cache.get_bridge_stp(ifaceobj.name)
|
|
else:
|
|
stp = utils.get_boolean_from_string(stp)
|
|
|
|
if self.mstpd_running and stp:
|
|
self.mstpctlcmd.batch_start()
|
|
self._apply_bridge_settings(ifaceobj, ifaceobj_getfunc)
|
|
self._apply_bridge_port_settings_all(ifaceobj,
|
|
ifaceobj_getfunc=ifaceobj_getfunc)
|
|
self.mstpctlcmd.batch_commit()
|
|
except Exception as e:
|
|
self.log_error(str(e), ifaceobj)
|
|
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.cache.get_slaves(ifaceobj.name)
|
|
if ports:
|
|
self._ports_enable_disable_ipv6(ports, '0')
|
|
self.netlink.link_del(ifaceobj.name)
|
|
except Exception as e:
|
|
self.log_error(str(e), ifaceobj)
|
|
|
|
def _query_running_attrs(self, ifaceobjrunning, bridge_vlan_aware=False):
|
|
bridgeattrdict = {}
|
|
|
|
tmpbridgeattrdict = self.mstpctlcmd.get_bridge_attrs(ifaceobjrunning.name)
|
|
#self.logger.info('A' + str(tmpbridgeattrdict))
|
|
if not tmpbridgeattrdict:
|
|
return bridgeattrdict
|
|
|
|
for k,v in list(tmpbridgeattrdict.items()):
|
|
if k == 'stp' or not v:
|
|
continue
|
|
if k == 'ports':
|
|
ports = list(v.keys())
|
|
continue
|
|
attrname = 'mstpctl-' + k
|
|
if (v and v != self.get_mod_subattr(attrname, 'default')
|
|
and attrname != 'mstpctl-maxhops'):
|
|
bridgeattrdict[attrname] = [v]
|
|
|
|
ports = self.cache.get_slaves(ifaceobjrunning.name)
|
|
# Do this only for vlan-UNAWARE-bridge
|
|
if ports and not bridge_vlan_aware:
|
|
portconfig = {'mstpctl-portautoedge' : '',
|
|
'mstpctl-portbpdufilter' : '',
|
|
'mstpctl-portnetwork' : '',
|
|
'mstpctl-portpathcost' : '',
|
|
'mstpctl-portadminedge' : '',
|
|
'mstpctl-portp2p' : '',
|
|
'mstpctl-portrestrrole' : '',
|
|
'mstpctl-portrestrtcn' : '',
|
|
'mstpctl-bpduguard' : '',
|
|
'mstpctl-treeportprio' : '',
|
|
'mstpctl-treeportcost' : ''}
|
|
|
|
for p in ports:
|
|
|
|
for attr in ['mstpctl-portautoedge',
|
|
'mstpctl-portbpdufilter',
|
|
'mstpctl-portnetwork',
|
|
'mstpctl-portadminedge',
|
|
'mstpctl-portp2p',
|
|
'mstpctl-portrestrrole',
|
|
'mstpctl-portrestrtcn',
|
|
'mstpctl-bpduguard',
|
|
'']:
|
|
v = self._get_bridge_port_attr_value(ifaceobjrunning.name,
|
|
p, attr)
|
|
if v and v != 'no':
|
|
portconfig[attr] += ' %s=%s' % (p, v)
|
|
|
|
for attr in ['mstpctl-portpathcost', 'mstpctl-treeportcost']:
|
|
v = self._get_bridge_port_attr_value(ifaceobjrunning.name,
|
|
p, attr)
|
|
if v and v != self.get_mod_subattr(attr, 'default'):
|
|
portconfig[attr] += ' %s=%s' % (p, v)
|
|
|
|
bridgeattrdict.update({k : [v] for k, v in list(portconfig.items())
|
|
if v})
|
|
return bridgeattrdict
|
|
|
|
def _get_config_stp(self, ifaceobj):
|
|
stp = (ifaceobj.get_attr_value_first('mstpctl-stp') or
|
|
ifaceobj.get_attr_value_first('bridge-stp') or
|
|
policymanager.policymanager_api.get_iface_default(module_name=self.__class__.__name__, ifname=ifaceobj.name, attr='mstpctl-stp') or
|
|
# this is a temporary method to access policy default value of bridge-stp
|
|
policymanager.policymanager_api.get_iface_default(module_name='bridge', ifname=ifaceobj.name, attr='bridge-stp'))
|
|
return utils.get_boolean_from_string(stp)
|
|
|
|
def _query_check_bridge(self, ifaceobj, ifaceobjcurr,
|
|
ifaceobj_getfunc=None):
|
|
# list of attributes that are not supported currently
|
|
blacklistedattrs = ['mstpctl-portpathcost',
|
|
'mstpctl-treeportprio', 'mstpctl-treeportcost']
|
|
if not self.cache.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 self.set_default_mstp_vxlan_bridge_config:
|
|
for attr in ('mstpctl-portbpdufilter', 'mstpctl-bpduguard', 'mstpctl-portadminedge'):
|
|
if attr not in ifaceattrs:
|
|
ifaceattrs.append(attr)
|
|
if not ifaceattrs:
|
|
return
|
|
runningattrs = self.mstpctlcmd.get_bridge_attrs(ifaceobj.name)
|
|
#self.logger.info('B' + str(runningattrs))
|
|
if not runningattrs:
|
|
runningattrs = {}
|
|
config_stp = self._get_config_stp(ifaceobj)
|
|
running_stp = self.cache.get_bridge_stp(ifaceobj.name)
|
|
running_port_list = self.cache.get_slaves(ifaceobj.name)
|
|
for k in ifaceattrs:
|
|
# for all mstpctl options
|
|
if k in blacklistedattrs:
|
|
continue
|
|
if k in ('mstpctl-portbpdufilter', 'mstpctl-bpduguard', 'mstpctl-portadminedge'):
|
|
#special case, 'ifquery --check --with-defaults' on a VLAN
|
|
#unaware bridge
|
|
if not running_port_list:
|
|
continue
|
|
if (not config_stp or not running_stp):
|
|
continue
|
|
v = ifaceobj.get_attr_value_first(k)
|
|
config_val = {}
|
|
running_val = {}
|
|
result = 0
|
|
bridge_ports = {}
|
|
state = ''
|
|
if v:
|
|
for bportval in v.split():
|
|
config_val[bportval.split('=')[0]] = bportval.split('=')[1]
|
|
#for bport in bridgeports:
|
|
for bport in running_port_list:
|
|
bportifaceobjlist = ifaceobj_getfunc(bport)
|
|
if not bportifaceobjlist:
|
|
continue
|
|
for bportifaceobj in bportifaceobjlist:
|
|
if (bport not in config_val):
|
|
if (bportifaceobj.link_kind & ifaceLinkKind.VXLAN):
|
|
if (not ifupdownflags.flags.WITHDEFAULTS or
|
|
(ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_VLAN_AWARE)):
|
|
continue
|
|
conf = 'yes'
|
|
else:
|
|
continue
|
|
else:
|
|
if ((bportifaceobj.link_kind & ifaceLinkKind.VXLAN) and
|
|
(ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_VLAN_AWARE)):
|
|
continue
|
|
conf = config_val[bport]
|
|
jsonAttr = self.get_mod_subattr(k, 'jsonAttr')
|
|
try:
|
|
running_val = self.mstpctlcmd.get_bridge_port_attr(ifaceobj.name, bport, jsonAttr)
|
|
except Exception:
|
|
self.logger.info('%s %s: could not get running %s value'
|
|
%(ifaceobj.name, bport, attr))
|
|
running_val = None
|
|
if conf != running_val:
|
|
result = 1
|
|
bridge_ports.update({bport : running_val})
|
|
for port, val in list(bridge_ports.items()):
|
|
#running state format
|
|
#mstpctl-portbpdufilter swp2=yes swp1=yes vx-14567101=yes [pass]
|
|
#mstpctl-bpduguard swp2=yes swp1=yes vx-14567101=yes [pass]
|
|
state += port + '=' + val + ' '
|
|
if state:
|
|
ifaceobjcurr.update_config_with_status(k, state, result)
|
|
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.sysfs.bridge_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
|
|
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(ifaceobj.name, v)
|
|
if not vlist:
|
|
continue
|
|
for vlistitem in vlist:
|
|
try:
|
|
(p, v) = vlistitem.split('=')
|
|
currv = self._get_bridge_port_attr_value(ifaceobj.name, p, k)
|
|
if currv:
|
|
currstr += ' %s=%s' %(p, currv)
|
|
else:
|
|
currstr += ' %s=%s' %(p, 'None')
|
|
if currv != v:
|
|
status = 1
|
|
except Exception as 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_vxlan_port(self, ifaceobj, ifaceobjcurr,
|
|
ifaceobj_getfunc=None):
|
|
masters = ifaceobj.upperifaces
|
|
if not masters:
|
|
return
|
|
for bridge in masters:
|
|
bifaceobjlist = ifaceobj_getfunc(bridge)
|
|
for bifaceobj in bifaceobjlist:
|
|
if (self._is_bridge(bifaceobj) and
|
|
self.set_default_mstp_vxlan_bridge_config and
|
|
(bifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_VLAN_AWARE)):
|
|
config_stp = self._get_config_stp(bifaceobj)
|
|
running_stp = self.cache.get_bridge_stp(bifaceobj.name)
|
|
if (not config_stp or not running_stp):
|
|
continue
|
|
for attr in (
|
|
'mstpctl-portbpdufilter',
|
|
'mstpctl-bpduguard',
|
|
'mstpctl-portadminedge'
|
|
):
|
|
jsonAttr = self.get_mod_subattr(attr, 'jsonAttr')
|
|
config_val = bifaceobj.get_attr_value_first(attr)
|
|
if config_val:
|
|
if ifaceobj.name not in [v.split('=')[0] for v in config_val.split()]:
|
|
if not ifupdownflags.flags.WITHDEFAULTS:
|
|
continue
|
|
config_val = 'yes'
|
|
else:
|
|
index = [v.split('=')[0] for v in config_val.split()].index(ifaceobj.name)
|
|
config_val = [v.split('=')[1] for v in config_val.split()][index]
|
|
else:
|
|
if not ifupdownflags.flags.WITHDEFAULTS:
|
|
continue
|
|
config_val = 'yes'
|
|
try:
|
|
running_val = self.mstpctlcmd.get_bridge_port_attr(bifaceobj.name,
|
|
ifaceobj.name, jsonAttr)
|
|
except Exception:
|
|
self.logger.info('%s %s: could not get running %s value'
|
|
%(bifaceobj.name, ifaceobj.name, attr))
|
|
running_val = None
|
|
ifaceobjcurr.update_config_with_status(attr,
|
|
running_val,
|
|
0 if running_val == config_val else 1)
|
|
return
|
|
|
|
|
|
def _query_check_bridge_port(self, ifaceobj, ifaceobjcurr):
|
|
if not self.cache.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.cache.link_is_bridge_port(ifaceobj.name):
|
|
# mark all the bridge attributes as error
|
|
ifaceobjcurr.check_n_update_config_with_status_many(ifaceobj,
|
|
list(self._port_attrs_map.keys()), 0)
|
|
return
|
|
bridgename = self.cache.get_master(ifaceobj.name)
|
|
# list of attributes that are not supported currently
|
|
blacklistedattrs = ['mstpctl-portpathcost',
|
|
'mstpctl-treeportprio', 'mstpctl-treeportcost']
|
|
ifaceattrs = self.dict_key_subset(ifaceobj.config,
|
|
list(self._port_attrs_map.keys()))
|
|
if not ifaceattrs:
|
|
return
|
|
runningattrs = self.mstpctlcmd.get_bridge_attrs(ifaceobj.name)
|
|
#self.logger.info('C' + str(runningattrs))
|
|
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._get_bridge_port_attr_value(bridgename, ifaceobj.name, 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, ifaceobj_getfunc)
|
|
elif ifaceobj.link_kind & ifaceLinkKind.VXLAN:
|
|
self._query_check_bridge_vxlan_port(ifaceobj, ifaceobjcurr,
|
|
ifaceobj_getfunc)
|
|
else:
|
|
self._query_check_bridge_port(ifaceobj, ifaceobjcurr)
|
|
|
|
def _query_bridge_port_attr(self, ifaceobjrunning, bridgename, attr, value_cmp):
|
|
v = self._get_bridge_port_attr_value(bridgename,
|
|
ifaceobjrunning.name,
|
|
attr)
|
|
if v and value_cmp and v != value_cmp:
|
|
ifaceobjrunning.update_config(attr, v)
|
|
elif v and not value_cmp:
|
|
ifaceobjrunning.update_config(attr, v)
|
|
|
|
def _query_running_bridge_port(self, ifaceobjrunning):
|
|
bridgename = self.cache.get_master(ifaceobjrunning.name)
|
|
if not bridgename:
|
|
self.logger.warning('%s: unable to determine bridgename'
|
|
%ifaceobjrunning.name)
|
|
return
|
|
if self.sysfs.bridge_get_stp(bridgename) == 'no':
|
|
# This bridge does not run stp, return
|
|
return
|
|
# if userspace stp not set, return
|
|
if self.systcl_get_net_bridge_stp_user_space() != '1':
|
|
return
|
|
|
|
self._query_bridge_port_attr(ifaceobjrunning, bridgename,
|
|
'mstpctl-portautoedge',
|
|
self.get_mod_subattr('mstpctl-portautoedge', 'default'))
|
|
|
|
self._query_bridge_port_attr(ifaceobjrunning, bridgename,
|
|
'mstpctl-portbpdufilter',
|
|
'no')
|
|
|
|
self._query_bridge_port_attr(ifaceobjrunning, bridgename,
|
|
'mstpctl-portnetwork',
|
|
'no')
|
|
|
|
# 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)
|
|
|
|
self._query_bridge_port_attr(ifaceobjrunning, bridgename,
|
|
'mstpctl-portadminedge',
|
|
'no')
|
|
|
|
self._query_bridge_port_attr(ifaceobjrunning, bridgename,
|
|
'mstpctl-portp2p',
|
|
'auto')
|
|
|
|
self._query_bridge_port_attr(ifaceobjrunning, bridgename,
|
|
'mstpctl-portrestrrole',
|
|
'no')
|
|
|
|
self._query_bridge_port_attr(ifaceobjrunning, bridgename,
|
|
'mstpctl-portrestrtcn',
|
|
'no')
|
|
|
|
self._query_bridge_port_attr(ifaceobjrunning, bridgename,
|
|
'mstpctl-bpduguard',
|
|
'no')
|
|
|
|
# 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.sysfs.bridge_get_stp(ifaceobjrunning.name) == 'no':
|
|
# This bridge does not run stp, return
|
|
return
|
|
# if userspace stp not set, return
|
|
if self.systcl_get_net_bridge_stp_user_space() != '1':
|
|
return
|
|
# Check if mstp really knows about this bridge
|
|
if not self.mstpctlcmd.mstpbridge_exists(ifaceobjrunning.name):
|
|
return
|
|
bridge_vlan_aware = False
|
|
if ifaceobjrunning.get_attr_value_first('bridge-vlan-aware') == 'yes':
|
|
bridge_vlan_aware = True
|
|
ifaceobjrunning.update_config_dict(self._query_running_attrs(
|
|
ifaceobjrunning,
|
|
bridge_vlan_aware))
|
|
|
|
def _query_running(self, ifaceobjrunning, **extra_args):
|
|
if self.cache.bridge_exists(ifaceobjrunning.name):
|
|
self._query_running_bridge(ifaceobjrunning)
|
|
elif self.cache.link_is_bridge_port(ifaceobjrunning.name):
|
|
self._query_running_bridge_port(ifaceobjrunning)
|
|
|
|
def _query_bridge_port(self, ifaceobj, ifaceobj_getfunc=None):
|
|
"""
|
|
Example:
|
|
Configuration:
|
|
auto vxlan1wd
|
|
iface vxlan1wd
|
|
vxlan-id 1001
|
|
|
|
auto vxlan2wd
|
|
iface vxlan2wd
|
|
vxlan-id 1002
|
|
|
|
auto brwithdef2
|
|
iface brwithdef2
|
|
bridge_ports vxlan1wd vxlan2wd
|
|
bridge-vlan-aware yes
|
|
|
|
Output:
|
|
$ ifquery vxlan1wd
|
|
auto vxlan1wd
|
|
iface vxlan1wd
|
|
vxlan-id 1001
|
|
|
|
$ ifquery --with-defaults vxlan1wd
|
|
auto vxlan1wd
|
|
iface vxlan1wd
|
|
vxlan-id 1001
|
|
mstpctl-portbpdufilter yes
|
|
mstpctl-bpduguard yes
|
|
"""
|
|
masters = ifaceobj.upperifaces
|
|
if not masters:
|
|
return
|
|
try:
|
|
for bridge in masters:
|
|
bifaceobj = ifaceobj_getfunc(bridge)[0]
|
|
if (self._is_bridge(bifaceobj) and
|
|
self.set_default_mstp_vxlan_bridge_config and
|
|
(bifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_VLAN_AWARE)
|
|
and ifaceobj.link_kind & ifaceLinkKind.VXLAN
|
|
):
|
|
for attr in ('mstpctl-portbpdufilter',
|
|
'mstpctl-bpduguard',
|
|
'mstpctl-portadminedge'):
|
|
jsonAttr = self.get_mod_subattr(attr, 'jsonAttr')
|
|
config_val = ifaceobj.get_attr_value_first(attr)
|
|
if config_val or not ifupdownflags.flags.WITHDEFAULTS:
|
|
continue
|
|
config_val = 'yes'
|
|
ifaceobj.replace_config(attr, config_val)
|
|
return
|
|
except Exception as e:
|
|
self.logger.info("%s: %s" %(ifaceobj.name, str(e)))
|
|
pass
|
|
|
|
def _query(self, ifaceobj, ifaceobj_getfunc=None, **kwargs):
|
|
""" add default policy attributes supported by the module """
|
|
if not self._is_bridge(ifaceobj):
|
|
if (ifaceobj.module_flags.get(self.name,0x0) &
|
|
mstpctlFlags.PORT_PROCESSED):
|
|
return
|
|
self._query_bridge_port(ifaceobj, ifaceobj_getfunc)
|
|
ifaceobj.module_flags[self.name] = (
|
|
ifaceobj.module_flags.setdefault(self.name,0) |
|
|
mstpctlFlags.PORT_PROCESSED)
|
|
return
|
|
lowerinfs = ifaceobj.lowerifaces
|
|
if not lowerinfs:
|
|
return
|
|
if ifaceobj.get_attr_value_first('bridge-vlan-aware') != 'yes':
|
|
for attr in ('mstpctl-portbpdufilter', 'mstpctl-bpduguard', 'mstpctl-portadminedge'):
|
|
state = ''
|
|
config = ifaceobj.get_attr_value_first(attr)
|
|
for port in lowerinfs:
|
|
bportobjlist = ifaceobj_getfunc(port)
|
|
for bportobj in bportobjlist:
|
|
if bportobj.get_attr_value_first('vxlan-id'):
|
|
if config:
|
|
if port not in [v.split('=')[0] for v in config.split()]:
|
|
config += ' %s=yes' %port
|
|
else:
|
|
state += '%s=yes ' %port
|
|
ifaceobj.replace_config(attr, config if config else state)
|
|
else:
|
|
for attr in ('mstpctl-portbpdufilter', 'mstpctl-bpduguard', 'mstpctl-portadminedge'):
|
|
state = ''
|
|
config = ifaceobj.get_attr_value_first(attr)
|
|
for port in lowerinfs:
|
|
bportobjlist = ifaceobj_getfunc(port)
|
|
for bportobj in bportobjlist:
|
|
if (bportobj.module_flags.get(self.name,0x0) &
|
|
mstpctlFlags.PORT_PROCESSED):
|
|
continue
|
|
if bportobj.get_attr_value_first('vxlan-id'):
|
|
if config:
|
|
if port not in [v.split('=')[0] for v in config.split()]:
|
|
bportobj.update_config(attr, 'yes')
|
|
else:
|
|
index = [v.split('=')[0] for v in config.split()].index(port)
|
|
state = [v.split('=')[1] for v in config.split()][index]
|
|
bportobj.update_config(attr, '%s' %state)
|
|
v = config.split()
|
|
del v[index]
|
|
config = ' '.join(v)
|
|
else:
|
|
bportobj.replace_config(attr, 'yes')
|
|
bportobj.module_flags[self.name] = (
|
|
bportobj.module_flags.setdefault(self.name,0) |
|
|
mstpctlFlags.PORT_PROCESSED)
|
|
if config:
|
|
ifaceobj.replace_config(attr, config)
|
|
|
|
_run_ops = {
|
|
"pre-up": _up,
|
|
"post-down": _down,
|
|
"query-checkcurr": _query_check,
|
|
"query-running": _query_running,
|
|
"query": _query
|
|
}
|
|
|
|
def get_ops(self):
|
|
""" returns list of ops supported by this module """
|
|
return list(self._run_ops.keys())
|
|
|
|
def _init_command_handlers(self):
|
|
if not self.mstpctlcmd:
|
|
self.mstpctlcmd = mstpctlutil()
|
|
|
|
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)
|