]> git.proxmox.com Git - mirror_ifupdown2.git/blame - ifupdown2/addons/mstpctl.py
SONAR: mstpctl: Merge if statement with the enclosing one
[mirror_ifupdown2.git] / ifupdown2 / addons / mstpctl.py
CommitLineData
35681c06 1#!/usr/bin/env python3
15ef32ea 2#
d486dd0d 3# Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved.
15ef32ea
RP
4# Author: Roopa Prabhu, roopa@cumulusnetworks.com
5#
6
42a9d193 7import os
d486dd0d 8
421e9573
JF
9from collections import OrderedDict
10
d486dd0d 11try:
223ba5af
JF
12 from ifupdown2.lib.addon import Addon
13
421e9573 14 from ifupdown2.ifupdown.iface import ifaceType, ifaceLinkKind, ifaceLinkPrivFlags, ifaceStatus
d486dd0d 15 from ifupdown2.ifupdown.utils import utils
d486dd0d
JF
16
17 import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
18 import ifupdown2.ifupdown.policymanager as policymanager
19
d486dd0d
JF
20 from ifupdown2.ifupdownaddons.modulebase import moduleBase
21 from ifupdown2.ifupdownaddons.mstpctlutil import mstpctlutil
22 from ifupdown2.ifupdownaddons.systemutils import systemUtils
c20b7810 23 from ifupdown2.ifupdown.exceptions import moduleNotSupported
bd441a51 24except (ImportError, ModuleNotFoundError):
223ba5af
JF
25 from lib.addon import Addon
26
421e9573 27 from ifupdown.iface import ifaceType, ifaceLinkKind, ifaceLinkPrivFlags, ifaceStatus
d486dd0d 28 from ifupdown.utils import utils
d486dd0d
JF
29
30 import ifupdown.ifupdownflags as ifupdownflags
31 import ifupdown.policymanager as policymanager
32
d486dd0d
JF
33 from ifupdownaddons.modulebase import moduleBase
34 from ifupdownaddons.mstpctlutil import mstpctlutil
35 from ifupdownaddons.systemutils import systemUtils
c20b7810 36 from ifupdown.exceptions import moduleNotSupported
d486dd0d 37
15ef32ea 38
4c773918
ST
39class mstpctlFlags:
40 PORT_PROCESSED = 0x1
41
223ba5af 42class mstpctl(Addon, moduleBase):
15ef32ea
RP
43 """ ifupdown2 addon module to configure mstp attributes """
44
223ba5af
JF
45 _modinfo = {
46 "mhelp": "mstp configuration module for bridges",
47 "attrs": {
48 "mstpctl-ports": {
49 "help": "mstp ports",
50 "compat": True,
51 "deprecated": True,
52 "new-attribute": "bridge-ports"
53 },
54 "mstpctl-stp": {
55 "help": "bridge stp yes/no",
56 "validvals": ["yes", "no", "on", "off"],
57 "compat": True,
58 "default": "no",
59 "deprecated": True,
60 "new-attribute": "bridge-stp"
61 },
62 "mstpctl-treeprio": {
63 "help": "tree priority",
64 "default": "32768",
65 "validvals": [
66 "0", "4096", "8192", "12288", "16384",
67 "20480", "24576", "28672", "32768",
68 "36864", "40960", "45056", "49152",
69 "53248", "57344", "61440"
70 ],
71 "required": False,
72 "example": ["mstpctl-treeprio 32768"]
73 },
74 "mstpctl-ageing": {
75 "help": "ageing time",
76 "validrange": ["0", "4096"],
77 "default": "300",
78 "required": False,
79 "jsonAttr": "ageingTime",
80 "example": ["mstpctl-ageing 300"]
81 },
82 "mstpctl-maxage": {
83 "help": "max message age",
84 "validrange": ["0", "255"],
85 "default": "20",
86 "jsonAttr": "bridgeMaxAge",
87 "required": False,
88 "example": ["mstpctl-maxage 20"]
89 },
90 "mstpctl-fdelay": {
91 "help": "set forwarding delay",
92 "validrange": ["0", "255"],
93 "default": "15",
94 "jsonAttr": "bridgeFwdDelay",
95 "required": False,
96 "example": ["mstpctl-fdelay 15"]
97 },
98 "mstpctl-maxhops": {
99 "help": "bridge max hops",
100 "validrange": ["0", "255"],
101 "default": "20",
102 "jsonAttr": "maxHops",
103 "required": False,
104 "example": ["mstpctl-maxhops 15"]
105 },
106 "mstpctl-txholdcount": {
107 "help": "bridge transmit holdcount",
108 "validrange": ["0", "255"],
109 "default": "6",
110 "jsonAttr": "txHoldCounter",
111 "required": False,
112 "example": ["mstpctl-txholdcount 6"]
113 },
114 "mstpctl-forcevers": {
115 "help": "bridge force stp version",
441d92a3 116 "validvals": ["rstp", "stp", "mstp"],
223ba5af
JF
117 "default": "rstp",
118 "required": False,
119 "jsonAttr": "forceProtocolVersion",
120 "example": ["mstpctl-forcevers rstp"]
121 },
122 "mstpctl-portpathcost": {
123 "help": "bridge port path cost",
124 "validvals": ["<interface-range-list>"],
125 "validrange": ["0", "65535"],
126 "default": "0",
127 "jsonAttr": "adminExtPortCost",
128 "required": False,
129 "example": [
130 "under the bridge: mstpctl-portpathcost swp1=0 swp2=1",
131 "under the port (recommended): mstpctl-portpathcost 0"
132 ]
133 },
134 "mstpctl-portp2p": {
135 "help": "bridge port p2p detection mode",
136 "default": "auto",
137 "jsonAttr": "adminPointToPoint",
138 "validvals": ["<interface-yes-no-auto-list>"],
139 "required": False,
140 "example": [
141 "under the bridge: mstpctl-portp2p swp1=yes swp2=no",
142 "under the port (recommended): mstpctl-portp2p yes"
143 ]
144 },
145 "mstpctl-portrestrrole": {
146 "help":
147 "enable/disable port ability to take root role of the port",
148 "default": "no",
149 "jsonAttr": "restrictedRole",
150 "validvals": ["<interface-yes-no-list>"],
151 "required": False,
152 "example": [
153 "under the bridge: mstpctl-portrestrrole swp1=yes swp2=no",
154 "under the port (recommended): mstpctl-portrestrrole yes"
155 ]
156 },
157 "mstpctl-portrestrtcn": {
158 "help":
159 "enable/disable port ability to propagate received "
160 "topology change notification of the port",
161 "default": "no",
162 "jsonAttr": "restrictedTcn",
163 "validvals": ["<interface-yes-no-list>"],
164 "required": False,
165 "example": [
166 "under the bridge: mstpctl-portrestrtcn swp1=yes swp2=no",
167 "under the port (recommended): mstpctl-portrestrtcn yes"
168 ]
169 },
170 "mstpctl-bpduguard": {
171 "help":
172 "enable/disable bpduguard",
173 "default": "no",
174 "jsonAttr": "bpduGuardPort",
175 "validvals": ["<interface-yes-no-list>"],
176 "required": False,
177 "example": [
178 "under the bridge: mstpctl-bpduguard swp1=yes swp2=no",
179 "under the port (recommended): mstpctl-bpduguard yes"
180 ]
181 },
182 "mstpctl-treeportprio": {
183 "help": "Sets the <port>'s priority MSTI instance. "
184 "The priority value must be a number between 0 and 240 "
185 "and a multiple of 16.",
186 "default": "128",
187 "validvals": ["<interface-range-list-multiple-of-16>"],
188 "validrange": ["0", "240"],
189 "jsonAttr": "treeportprio",
190 "required": False,
191 "example": [
192 "under the bridge: mstpctl-treeportprio swp1=128 swp2=128",
193 "under the port (recommended): mstpctl-treeportprio 128"
194 ]
195 },
196 "mstpctl-hello": {
197 "help": "set hello time",
198 "validrange": ["0", "255"],
199 "default": "2",
200 "required": False,
201 "jsonAttr": "helloTime",
202 "example": ["mstpctl-hello 2"]
203 },
204 "mstpctl-portnetwork": {
205 "help": "enable/disable bridge assurance capability for a port",
206 "validvals": ["<interface-yes-no-list>"],
207 "default": "no",
208 "jsonAttr": "networkPort",
209 "required": False,
210 "example": [
211 "under the bridge: mstpctl-portnetwork swp1=yes swp2=no",
212 "under the port (recommended): mstpctl-portnetwork yes"
213 ]
214 },
215 "mstpctl-portadminedge": {
216 "help": "enable/disable initial edge state of the port",
217 "validvals": ["<interface-yes-no-list>"],
218 "default": "no",
219 "jsonAttr": "adminEdgePort",
220 "required": False,
221 "example": [
222 "under the bridge: mstpctl-portadminedge swp1=yes swp2=no",
223 "under the port (recommended): mstpctl-portadminedge yes"
224 ]
225 },
226 "mstpctl-portautoedge": {
227 "help": "enable/disable auto transition to/from edge state of the port",
228 "validvals": ["<interface-yes-no-list>"],
229 "default": "yes",
230 "jsonAttr": "autoEdgePort",
231 "required": False,
232 "example": [
233 "under the bridge: mstpctl-portautoedge swp1=yes swp2=no",
234 "under the port (recommended): mstpctl-portautoedge yes"
235 ]
236 },
237 "mstpctl-treeportcost": {
238 "help": "port tree cost",
239 # "validrange": ["0", "255"],
240 "required": False,
241 "jsonAttr": "extPortCost",
242 },
243 "mstpctl-portbpdufilter": {
244 "help": "enable/disable bpdu filter on a port. syntax varies "
245 "when defined under a bridge vs under a port",
246 "validvals": ["<interface-yes-no-list>"],
247 "jsonAttr": "bpduFilterPort",
248 "default": "no",
249 "required": False,
250 "example": [
251 "under a bridge: mstpctl-portbpdufilter swp1=no swp2=no",
252 "under a port: mstpctl-portbpdufilter yes"
253 ]
254 },
255 }
256 }
15ef32ea 257
4c39c7b8
RP
258 # Maps mstp bridge attribute names to corresponding mstpctl commands
259 # XXX: This can be encoded in the modules dict above
260 _attrs_map = OrderedDict([('mstpctl-treeprio' , 'treeprio'),
261 ('mstpctl-ageing' , 'ageing'),
4c39c7b8 262 ('mstpctl-fdelay' , 'fdelay'),
caa23e5f 263 ('mstpctl-maxage' , 'maxage'),
4c39c7b8
RP
264 ('mstpctl-maxhops' , 'maxhops'),
265 ('mstpctl-txholdcount' , 'txholdcount'),
266 ('mstpctl-forcevers', 'forcevers'),
267 ('mstpctl-hello' , 'hello')])
268
269 # Maps mstp port attribute names to corresponding mstpctl commands
270 # XXX: This can be encoded in the modules dict above
e1601369
RP
271 _port_attrs_map = {'mstpctl-portpathcost' : 'portpathcost',
272 'mstpctl-portadminedge' : 'portadminedge',
d8e3554d 273 'mstpctl-portautoedge' : 'portautoedge' ,
e1601369
RP
274 'mstpctl-portp2p' : 'portp2p',
275 'mstpctl-portrestrrole' : 'portrestrrole',
276 'mstpctl-portrestrtcn' : 'portrestrtcn',
15ef32ea 277 'mstpctl-bpduguard' : 'bpduguard',
e1601369
RP
278 'mstpctl-treeportprio' : 'treeportprio',
279 'mstpctl-treeportcost' : 'treeportcost',
280 'mstpctl-portnetwork' : 'portnetwork',
281 'mstpctl-portbpdufilter' : 'portbpdufilter'}
15ef32ea
RP
282
283 def __init__(self, *args, **kargs):
223ba5af 284 Addon.__init__(self)
15ef32ea 285 moduleBase.__init__(self, *args, **kargs)
c20b7810
AD
286 if not os.path.exists('/sbin/mstpctl'):
287 raise moduleNotSupported('module init failed: no /sbin/mstpctl found')
4c773918 288 self.name = self.__class__.__name__
15ef32ea 289 self.mstpctlcmd = None
5f8c03e7
RP
290 self.mstpd_running = (True if systemUtils.is_process_running('mstpd')
291 else False)
d486dd0d
JF
292
293 # Background -
294 # The ask is to make "mstpctl-portadminedge yes" part of the default ifupdown2
295 # policy for all vxlan interfaces. In the absence of this, the mstp work flow
296 # is flawed in the event of vxlan flap.
297 # Details -
298 # As of today, for vxlan interfaces "oper edge port" is set to 'yes' and also
299 # "bpdufilter port" is also set to 'yes'. So, in a case where bridge has multiple
300 # vxlan interfaces, if one vxlan interface is flapped, this would trigger mstp
301 # re-evaluation of states on other vxlan interfaces, creating momentary traffic
302 # glitch on those vxlans. Setting "admin edge port" to yes (in addition to the
303 # defaults we already have) prevents this.
304 #
305 # We use to only support 'mstpctl-vxlan-always-set-bpdu-params' but introducing a
306 # separate policy attribute doesn't make sense, we should have one single
307 # attribute to handle the whole thing (and deprecate mstpctl-vxlan-always-set-bpdu-params)
308 # mstpctl-set-default-vxlan-bridge-attrs=yes will set
309 # mstpctl-portbpdufilter
310 # mstpctl-bpduguard
311 # mstpctl-portadminedge
312 #
313 self.set_default_mstp_vxlan_bridge_config = utils.get_boolean_from_string(
314 policymanager.policymanager_api.get_module_globals(
315 module_name=self.__class__.__name__,
316 attr='mstpctl-vxlan-always-set-bpdu-params'
317 )
318 ) or utils.get_boolean_from_string(
319 policymanager.policymanager_api.get_module_globals(
320 module_name=self.__class__.__name__,
321 attr='mstpctl-set-default-vxlan-bridge-attrs'
322 )
323 )
15ef32ea 324
8e9fc178 325 def syntax_check(self, ifaceobj, ifaceobj_getfunc):
e5e64b25
JF
326 if (
327 self._is_bridge(ifaceobj)
328 and ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_VLAN_AWARE
329 and ifaceobj.get_attr_value_first('mstpctl-portadminedge')
330 ):
331 self.logger.error("%s: unsupported use of keyword "
332 "\'mstpctl-portadminedge\' when "
333 "bridge-vlan-aware is on"
334 % ifaceobj.name)
335 return False
8252491e
JF
336 return True
337
15ef32ea 338 def _is_bridge(self, ifaceobj):
223ba5af
JF
339 return ifaceobj.link_kind & ifaceLinkKind.BRIDGE \
340 or ifaceobj.get_attr_value_first('mstpctl-ports') \
341 or ifaceobj.get_attr_value_first('bridge-ports')
15ef32ea 342
59ab29fb 343 def get_dependent_ifacenames(self, ifaceobj, ifacenames_all=None, old_ifaceobjs=False):
15ef32ea
RP
344 if not self._is_bridge(ifaceobj):
345 return None
0c8332bc
RP
346 return self.parse_port_list(ifaceobj.name,
347 ifaceobj.get_attr_value_first(
15ef32ea
RP
348 'mstpctl-ports'), ifacenames_all)
349
350 def get_dependent_ifacenames_running(self, ifaceobj):
351 self._init_command_handlers()
223ba5af 352 if (self.cache.bridge_exists(ifaceobj.name) and
15ef32ea
RP
353 not self.mstpctlcmd.mstpbridge_exists(ifaceobj.name)):
354 return None
223ba5af 355 return self.cache.get_slaves(ifaceobj.name)
15ef32ea 356
9d3f53c6
JF
357 def _get_bridge_port_attr_value(self, bridgename, portname, attr):
358 json_attr = self.get_mod_subattr(attr, 'jsonAttr')
359 return self.mstpctlcmd.get_bridge_port_attr(bridgename,
360 portname,
361 json_attr)
362
15ef32ea
RP
363 def _get_bridge_port_list(self, ifaceobj):
364
365 # port list is also available in the previously
366 # parsed dependent list. Use that if available, instead
367 # of parsing port expr again
368 port_list = ifaceobj.lowerifaces
369 if port_list:
370 return port_list
371 ports = ifaceobj.get_attr_value_first('mstpctl-ports')
372 if ports:
0c8332bc 373 return self.parse_port_list(ifaceobj.name, ports)
15ef32ea
RP
374 else:
375 return None
376
404cc695
RP
377 def _ports_enable_disable_ipv6(self, ports, enable='1'):
378 for p in ports:
379 try:
380 self.write_file('/proc/sys/net/ipv6/conf/%s' %p +
381 '/disable_ipv6', enable)
3b01ed76 382 except Exception as e:
404cc695
RP
383 self.logger.info(str(e))
384 pass
385
15ef32ea
RP
386 def _add_ports(self, ifaceobj):
387 bridgeports = self._get_bridge_port_list(ifaceobj)
388
389 runningbridgeports = []
390 # Delete active ports not in the new port list
fc5e1735 391 if not ifupdownflags.flags.PERFMODE:
223ba5af 392 runningbridgeports = self.cache.get_slaves(ifaceobj.name)
15ef32ea 393 if runningbridgeports:
223ba5af
JF
394 for bport in runningbridgeports:
395 if not bridgeports or bport not in bridgeports:
396 self.netlink.link_set_nomaster(bport)
15ef32ea
RP
397 else:
398 runningbridgeports = []
399 if not bridgeports:
400 return
401 err = 0
3b01ed76 402 for bridgeport in set(bridgeports).difference(set(runningbridgeports)):
15ef32ea 403 try:
fc5e1735 404 if (not ifupdownflags.flags.DRYRUN and
223ba5af 405 not self.cache.link_exists(bridgeport)):
15ef32ea
RP
406 self.log_warn('%s: bridge port %s does not exist'
407 %(ifaceobj.name, bridgeport))
408 err += 1
409 continue
223ba5af
JF
410 self.netlink.link_set_master(bridgeport, ifaceobj.name)
411 self.netlink.addr_flush(bridgeport)
3b01ed76 412 except Exception as e:
bf3eda91 413 self.log_error(str(e), ifaceobj)
404cc695 414
15ef32ea
RP
415 if err:
416 self.log_error('error configuring bridge (missing ports)')
417
d486dd0d 418 def _apply_bridge_settings(self, ifaceobj, ifaceobj_getfunc):
fc5e1735 419 check = False if ifupdownflags.flags.PERFMODE else True
15ef32ea 420 try:
4c39c7b8 421 # set bridge attributes
3b01ed76 422 for attrname, dstattrname in list(self._attrs_map.items()):
e3089aa8
N
423 config_val = ifaceobj.get_attr_value_first(attrname)
424 default_val = policymanager.policymanager_api.get_iface_default(module_name=self.__class__.__name__, ifname=ifaceobj.name, attr=attrname)
425 if not default_val:
d486dd0d
JF
426 default_val = self.get_mod_subattr(attrname, 'default')
427 jsonAttr = self.get_mod_subattr(attrname, 'jsonAttr')
e3089aa8
N
428 try:
429 running_val = self.mstpctlcmd.get_bridge_attr(
430 ifaceobj.name, jsonAttr)
3218f49d 431 except Exception:
e3089aa8
N
432 self.logger.info('%s: could not get running %s value'
433 %(ifaceobj.name, attrname))
434 running_val = None
435 if (not config_val and default_val and (running_val != default_val)):
436 # this happens when users remove an attribute from a port
437 # and expect the default to be restored with ifreload.
438 config_val = default_val
439 elif not config_val:
440 # there is nothing configured and no default to reset
441 continue
4c39c7b8 442 try:
4c39c7b8
RP
443 if attrname == 'mstpctl-treeprio':
444 self.mstpctlcmd.set_bridge_treeprio(ifaceobj.name,
e3089aa8 445 config_val, check)
4c39c7b8
RP
446 else:
447 self.mstpctlcmd.set_bridge_attr(ifaceobj.name,
e3089aa8 448 dstattrname, config_val, check)
3b01ed76 449 except Exception as e:
c46af1c9 450 self.logger.warning('%s' %str(e))
4c39c7b8 451 pass
15ef32ea 452
223ba5af
JF
453 if self.cache.bridge_is_vlan_aware(ifaceobj.name):
454 return
455 bridgeports = self._get_bridge_port_list(ifaceobj)
456 if not bridgeports:
bb70f923 457 return
15ef32ea 458 # set bridge port attributes
3b01ed76 459 for attrname, dstattrname in list(self._port_attrs_map.items()):
b97687bc
ST
460 config_val = ifaceobj.get_attr_value_first(attrname)
461 default_val = self.get_mod_subattr(attrname,'default')
462 if not config_val:
463 # nothing configured, we may need to reset all ports to defaults
464 # if the default exists and jsonAttribute conversion exists
465 try:
466 jsonAttr = self.get_mod_subattr(attrname, 'jsonAttr')
467 if default_val and jsonAttr:
b97687bc 468 for port in bridgeports:
223ba5af 469 if not self.cache.link_is_bridge_port(port):
bf3eda91 470 continue
d486dd0d 471
4b0b3fa3
JF
472 bport_ifaceobjs = ifaceobj_getfunc(port)
473 if bport_ifaceobjs:
474 default_val = self._get_default_val(attrname, bport_ifaceobjs[0], ifaceobj)
475 for brport_ifaceobj in bport_ifaceobjs or []:
476 attr_value = brport_ifaceobj.get_attr_value_first(attrname)
477 if attr_value:
478 default_val = attr_value
479 break
d486dd0d 480
9d3f53c6
JF
481 self.mstpctlcmd.set_bridge_port_attr(ifaceobj.name,
482 port,
483 dstattrname,
484 default_val,
485 json_attr=jsonAttr)
d486dd0d
JF
486 except Exception as e:
487 self.logger.debug('%s' % str(e))
b97687bc
ST
488 self.logger.info('%s: not resetting %s config'
489 %(ifaceobj.name, attrname))
490 # leave the loop for this attribute
15ef32ea 491 continue
b97687bc
ST
492
493 portlist = self.parse_port_list(ifaceobj.name, config_val)
15ef32ea 494 if not portlist:
bf3eda91
RP
495 self.log_error('%s: error parsing \'%s %s\''
496 %(ifaceobj.name, attrname, config_val), ifaceobj)
15ef32ea 497 continue
b97687bc
ST
498 # there was a configured value so we need to parse it
499 # and set the attribute for each port configured
15ef32ea
RP
500 for p in portlist:
501 try:
502 (port, val) = p.split('=')
42a9d193
RP
503 # if it is not bridge port, continue
504 if not os.path.exists('/sys/class/net/%s/brport' %port):
505 continue
9d3f53c6
JF
506 json_attr = self.get_mod_subattr(attrname, 'jsonAttr')
507 self.mstpctlcmd.set_bridge_port_attr(ifaceobj.name,
508 port,
509 dstattrname,
510 val,
511 json_attr=json_attr)
3b01ed76 512 except Exception as e:
bf3eda91
RP
513 self.log_error('%s: error setting %s (%s)'
514 %(ifaceobj.name, attrname, str(e)),
515 ifaceobj, raise_error=False)
3b01ed76 516 except Exception as e:
15ef32ea
RP
517 self.log_warn(str(e))
518 pass
519
2e4dc724 520 def _get_default_val(self, attr, ifaceobj, bridgeifaceobj):
d486dd0d
JF
521 if (self.set_default_mstp_vxlan_bridge_config
522 and ifaceobj.link_kind & ifaceLinkKind.VXLAN
523 and attr in (
524 'mstpctl-portbpdufilter',
525 'mstpctl-bpduguard',
526 'mstpctl-portadminedge',
527 )
528 ):
2e4dc724
N
529 try:
530 config_val = bridgeifaceobj.get_attr_value_first(attr)
3b01ed76 531 except Exception as e:
2e4dc724
N
532 config_val = None
533 if config_val:
534 if ifaceobj.name not in [v.split('=')[0] for v in config_val.split()]:
535 return 'yes'
536 else:
537 index = [v.split('=')[0] for v in config_val.split()].index(ifaceobj.name)
538 return [v.split('=')[1] for v in config_val.split()][index]
539 else:
540 return 'yes'
541 else:
e3089aa8
N
542 default_val = policymanager.policymanager_api.get_iface_default(module_name=self.__class__.__name__, ifname=ifaceobj.name, attr=attr)
543 if not default_val:
544 return self.get_mod_subattr(attr,'default')
545 return default_val
2e4dc724 546
223ba5af 547 def _apply_bridge_port_settings(self, ifaceobj, bvlan_aware, bridgename=None,
641cbd1e
RP
548 bridgeifaceobj=None,
549 stp_running_on=True,
d8e3554d 550 mstpd_running=True):
fc5e1735 551 check = False if ifupdownflags.flags.PERFMODE else True
641cbd1e 552 applied = False
84ca006f
RP
553 if not bridgename and bridgeifaceobj:
554 bridgename = bridgeifaceobj.name
b97687bc
ST
555
556 if not stp_running_on:
557 # stp may get turned on at a later point
558 self.logger.info('%s: ignoring config'
559 %(ifaceobj.name) +
560 ' (stp on bridge %s is not on yet)' %bridgename)
561 return applied
562 if (not mstpd_running or
563 not os.path.exists('/sys/class/net/%s/brport' %ifaceobj.name) or
2e4dc724
N
564 not bvlan_aware):
565 if (not bvlan_aware and
d486dd0d 566 self.set_default_mstp_vxlan_bridge_config and
2e4dc724 567 (ifaceobj.link_kind & ifaceLinkKind.VXLAN)):
d486dd0d
JF
568 for attr in (
569 'mstpctl-portbpdufilter',
570 'mstpctl-bpduguard',
571 'mstpctl-portadminedge'
572 ):
9d3f53c6
JF
573 json_attr = self.get_mod_subattr(attr, 'jsonAttr')
574 config_val = self._get_default_val(attr, ifaceobj,
575 bridgeifaceobj)
2e4dc724 576 try:
9d3f53c6
JF
577 self.mstpctlcmd.set_bridge_port_attr(bridgename,
578 ifaceobj.name,
579 self._port_attrs_map[attr],
580 config_val,
581 json_attr=json_attr)
3b01ed76 582 except Exception as e:
2e4dc724 583 self.log_warn('%s: error setting %s (%s)'
9d3f53c6 584 % (ifaceobj.name, attr, str(e)))
d486dd0d
JF
585
586 if not bvlan_aware:
587 # for "traditional" bridges we also want to let the user configure
588 # some attributes (possibly all of them in the future)
589 applied = self._apply_bridge_port_settings_attributes_list(
590 (
591 ('mstpctl-portrestrrole', 'portrestrrole'),
592 ('mstpctl-portautoedge', 'portautoedge')
593 ),
594 ifaceobj,
595 bridgeifaceobj,
596 bridgename,
597 applied
598 )
2e4dc724 599 return applied
84ca006f 600 # set bridge port attributes
d486dd0d 601 return self._apply_bridge_port_settings_attributes_list(
3b01ed76 602 list(self._port_attrs_map.items()),
d486dd0d
JF
603 ifaceobj,
604 bridgeifaceobj,
605 bridgename,
606 applied
607 )
608
609 def _apply_bridge_port_settings_attributes_list(self, attributes_list, ifaceobj, bridgeifaceobj, bridgename, applied):
610 for attrname, dstattrname in attributes_list:
b97687bc 611 config_val = ifaceobj.get_attr_value_first(attrname)
2e4dc724 612 default_val = self._get_default_val(attrname, ifaceobj, bridgeifaceobj)
b97687bc
ST
613 jsonAttr = self.get_mod_subattr(attrname, 'jsonAttr')
614 # to see the running value, stp would have to be on
615 # so we would have parsed mstpctl showportdetail json output
616 try:
9d3f53c6 617 running_val = self.mstpctlcmd.get_bridge_port_attr(bridgename,
b97687bc 618 ifaceobj.name, jsonAttr)
3218f49d 619 except Exception:
b97687bc
ST
620 self.logger.info('%s %s: could not get running %s value'
621 %(bridgename, ifaceobj.name, attrname))
622 running_val = None
623 if (not config_val and default_val and (running_val != default_val)):
624 # this happens when users remove an attribute from a port
625 # and expect the default to be restored with ifreload.
626 config_val = default_val
627 elif not config_val:
628 # there is nothing configured and no default to reset
42a9d193 629 continue
b97687bc 630
84ca006f 631 try:
9d3f53c6
JF
632 self.mstpctlcmd.set_bridge_port_attr(bridgename,
633 ifaceobj.name, dstattrname, config_val, json_attr=jsonAttr)
641cbd1e 634 applied = True
3b01ed76 635 except Exception as e:
bf3eda91
RP
636 self.log_error('%s: error setting %s (%s)'
637 %(ifaceobj.name, attrname, str(e)), ifaceobj,
638 raise_error=False)
641cbd1e 639 return applied
84ca006f
RP
640
641 def _apply_bridge_port_settings_all(self, ifaceobj,
642 ifaceobj_getfunc=None):
98b5ee73 643 self.logger.info('%s: applying mstp configuration '
84ca006f 644 %ifaceobj.name + 'specific to ports')
f6a0fa15 645 # Query running bridge ports. and only apply attributes on them
223ba5af 646 bridgeports = self.cache.get_slaves(ifaceobj.name)
4c39c7b8
RP
647 if not bridgeports:
648 self.logger.debug('%s: cannot find bridgeports' %ifaceobj.name)
649 return
223ba5af
JF
650
651 bvlan_aware = self.cache.bridge_is_vlan_aware(ifaceobj.name)
652
84ca006f 653 for bport in bridgeports:
98b5ee73 654 self.logger.info('%s: processing mstp config for port %s'
84ca006f 655 %(ifaceobj.name, bport))
223ba5af
JF
656 if not self.cache.link_exists(bport):
657 continue
42a9d193
RP
658 if not os.path.exists('/sys/class/net/%s/brport' %bport):
659 continue
84ca006f
RP
660 bportifaceobjlist = ifaceobj_getfunc(bport)
661 if not bportifaceobjlist:
662 continue
663 for bportifaceobj in bportifaceobjlist:
98b5ee73 664 # Dont process bridge port if it already has been processed
4c773918
ST
665 if (bportifaceobj.module_flags.get(self.name,0x0) & \
666 mstpctlFlags.PORT_PROCESSED):
98b5ee73 667 continue
15ef32ea 668 try:
223ba5af 669 self._apply_bridge_port_settings(bportifaceobj, bvlan_aware,
84ca006f 670 ifaceobj.name, ifaceobj)
3b01ed76 671 except Exception as e:
2e4dc724 672 pass
84ca006f 673 self.log_warn(str(e))
15ef32ea 674
641cbd1e
RP
675 def _is_running_userspace_stp_state_on(self, bridgename):
676 stp_state_file = '/sys/class/net/%s/bridge/stp_state' %bridgename
677 if not stp_state_file:
678 return False
679 running_stp_state = self.read_file_oneline(stp_state_file)
680 if running_stp_state and running_stp_state == '2':
681 return True
682 return False
683
84ca006f 684 def _up(self, ifaceobj, ifaceobj_getfunc=None):
223ba5af
JF
685
686 # bridge port specific:
687 if ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT:
688 bridgename = self.cache.get_master(ifaceobj.name)
689
690 if not bridgename:
691 return
692 bvlan_aware = self.cache.bridge_is_vlan_aware(bridgename)
5f8c03e7 693 mstpd_running = self.mstpd_running
223ba5af
JF
694 stp_running_on = bool(self.cache.get_bridge_stp(bridgename))
695 applied = self._apply_bridge_port_settings(ifaceobj, bvlan_aware, bridgename,
641cbd1e
RP
696 None, stp_running_on,
697 mstpd_running)
698 if applied:
699 ifaceobj.module_flags[self.name] = \
700 ifaceobj.module_flags.setdefault(self.name,0) | \
701 mstpctlFlags.PORT_PROCESSED
15ef32ea 702 return
223ba5af
JF
703
704 elif not self._is_bridge(ifaceobj):
39804250 705 return
b97687bc 706 # we are now here because the ifaceobj is a bridge
15ef32ea
RP
707 stp = None
708 try:
709 porterr = False
710 porterrstr = ''
711 if ifaceobj.get_attr_value_first('mstpctl-ports'):
712 # If bridge ports specified with mstpctl attr, create the
713 # bridge and also add its ports
223ba5af
JF
714 if not self.cache.link_exists(ifaceobj.name):
715 self.netlink.link_add_bridge(ifaceobj.name)
716
15ef32ea
RP
717 try:
718 self._add_ports(ifaceobj)
3b01ed76 719 except Exception as e:
15ef32ea
RP
720 porterr = True
721 porterrstr = str(e)
722 pass
223ba5af
JF
723
724 running_ports = self.cache.get_slaves(ifaceobj.name)
d5c0f703
RP
725 if running_ports:
726 # disable ipv6 for ports that were added to bridge
727 self._ports_enable_disable_ipv6(running_ports, '1')
404cc695 728
15ef32ea
RP
729 stp = ifaceobj.get_attr_value_first('mstpctl-stp')
730 if stp:
731 self.set_iface_attr(ifaceobj, 'mstpctl-stp',
223ba5af 732 self.iproute2.bridge_set_stp)
15ef32ea 733 else:
24931ffa
JF
734 stp = ifaceobj.get_attr_value_first('bridge-stp')
735
736 if not stp:
737 stp = self.cache.get_bridge_stp(ifaceobj.name)
738 else:
739 stp = utils.get_boolean_from_string(stp)
740
223ba5af
JF
741 if self.mstpd_running and stp:
742 self.mstpctlcmd.batch_start()
d486dd0d 743 self._apply_bridge_settings(ifaceobj, ifaceobj_getfunc)
84ca006f
RP
744 self._apply_bridge_port_settings_all(ifaceobj,
745 ifaceobj_getfunc=ifaceobj_getfunc)
223ba5af 746 self.mstpctlcmd.batch_commit()
3b01ed76 747 except Exception as e:
bf3eda91 748 self.log_error(str(e), ifaceobj)
15ef32ea
RP
749 if porterr:
750 raise Exception(porterrstr)
751
84ca006f
RP
752 def _down(self, ifaceobj, ifaceobj_getfunc=None):
753 if not self._is_bridge(ifaceobj):
754 return
15ef32ea
RP
755 try:
756 if ifaceobj.get_attr_value_first('mstpctl-ports'):
757 # If bridge ports specified with mstpctl attr, delete the
758 # bridge
223ba5af 759 ports = self.cache.get_slaves(ifaceobj.name)
15ef32ea 760 if ports:
404cc695 761 self._ports_enable_disable_ipv6(ports, '0')
223ba5af 762 self.netlink.link_del(ifaceobj.name)
3b01ed76 763 except Exception as e:
bf3eda91 764 self.log_error(str(e), ifaceobj)
15ef32ea 765
0dd2f0d1 766 def _query_running_attrs(self, ifaceobjrunning, bridge_vlan_aware=False):
15ef32ea
RP
767 bridgeattrdict = {}
768
769 tmpbridgeattrdict = self.mstpctlcmd.get_bridge_attrs(ifaceobjrunning.name)
9d3f53c6 770 #self.logger.info('A' + str(tmpbridgeattrdict))
15ef32ea
RP
771 if not tmpbridgeattrdict:
772 return bridgeattrdict
773
3b01ed76 774 for k,v in list(tmpbridgeattrdict.items()):
15ef32ea
RP
775 if k == 'stp' or not v:
776 continue
777 if k == 'ports':
3b01ed76 778 ports = list(v.keys())
15ef32ea
RP
779 continue
780 attrname = 'mstpctl-' + k
0dd2f0d1
N
781 if (v and v != self.get_mod_subattr(attrname, 'default')
782 and attrname != 'mstpctl-maxhops'):
15ef32ea
RP
783 bridgeattrdict[attrname] = [v]
784
223ba5af 785 ports = self.cache.get_slaves(ifaceobjrunning.name)
0dd2f0d1
N
786 # Do this only for vlan-UNAWARE-bridge
787 if ports and not bridge_vlan_aware:
22945dd6
N
788 portconfig = {'mstpctl-portautoedge' : '',
789 'mstpctl-portbpdufilter' : '',
790 'mstpctl-portnetwork' : '',
15ef32ea
RP
791 'mstpctl-portpathcost' : '',
792 'mstpctl-portadminedge' : '',
15ef32ea
RP
793 'mstpctl-portp2p' : '',
794 'mstpctl-portrestrrole' : '',
795 'mstpctl-portrestrtcn' : '',
796 'mstpctl-bpduguard' : '',
797 'mstpctl-treeportprio' : '',
798 'mstpctl-treeportcost' : ''}
799
800 for p in ports:
9d3f53c6
JF
801
802 for attr in ['mstpctl-portautoedge',
803 'mstpctl-portbpdufilter',
804 'mstpctl-portnetwork',
805 'mstpctl-portadminedge',
806 'mstpctl-portp2p',
807 'mstpctl-portrestrrole',
808 'mstpctl-portrestrtcn',
809 'mstpctl-bpduguard',
810 '']:
811 v = self._get_bridge_port_attr_value(ifaceobjrunning.name,
812 p, attr)
813 if v and v != 'no':
814 portconfig[attr] += ' %s=%s' % (p, v)
815
816 for attr in ['mstpctl-portpathcost', 'mstpctl-treeportcost']:
817 v = self._get_bridge_port_attr_value(ifaceobjrunning.name,
818 p, attr)
819 if v and v != self.get_mod_subattr(attr, 'default'):
820 portconfig[attr] += ' %s=%s' % (p, v)
15ef32ea 821
3b01ed76 822 bridgeattrdict.update({k : [v] for k, v in list(portconfig.items())
15ef32ea 823 if v})
15ef32ea
RP
824 return bridgeattrdict
825
d03b0695
N
826 def _get_config_stp(self, ifaceobj):
827 stp = (ifaceobj.get_attr_value_first('mstpctl-stp') or
828 ifaceobj.get_attr_value_first('bridge-stp') or
829 policymanager.policymanager_api.get_iface_default(module_name=self.__class__.__name__, ifname=ifaceobj.name, attr='mstpctl-stp') or
830 # this is a temporary method to access policy default value of bridge-stp
831 policymanager.policymanager_api.get_iface_default(module_name='bridge', ifname=ifaceobj.name, attr='bridge-stp'))
832 return utils.get_boolean_from_string(stp)
833
2e4dc724
N
834 def _query_check_bridge(self, ifaceobj, ifaceobjcurr,
835 ifaceobj_getfunc=None):
15ef32ea
RP
836 # list of attributes that are not supported currently
837 blacklistedattrs = ['mstpctl-portpathcost',
838 'mstpctl-treeportprio', 'mstpctl-treeportcost']
223ba5af 839 if not self.cache.bridge_exists(ifaceobj.name):
15ef32ea 840 self.logger.debug('bridge %s does not exist' %ifaceobj.name)
15ef32ea
RP
841 return
842 ifaceattrs = self.dict_key_subset(ifaceobj.config,
843 self.get_mod_attrs())
d486dd0d
JF
844 if self.set_default_mstp_vxlan_bridge_config:
845 for attr in ('mstpctl-portbpdufilter', 'mstpctl-bpduguard', 'mstpctl-portadminedge'):
2e4dc724
N
846 if attr not in ifaceattrs:
847 ifaceattrs.append(attr)
15ef32ea
RP
848 if not ifaceattrs:
849 return
850 runningattrs = self.mstpctlcmd.get_bridge_attrs(ifaceobj.name)
9d3f53c6 851 #self.logger.info('B' + str(runningattrs))
15ef32ea
RP
852 if not runningattrs:
853 runningattrs = {}
d03b0695 854 config_stp = self._get_config_stp(ifaceobj)
223ba5af
JF
855 running_stp = self.cache.get_bridge_stp(ifaceobj.name)
856 running_port_list = self.cache.get_slaves(ifaceobj.name)
15ef32ea
RP
857 for k in ifaceattrs:
858 # for all mstpctl options
859 if k in blacklistedattrs:
860 continue
d486dd0d 861 if k in ('mstpctl-portbpdufilter', 'mstpctl-bpduguard', 'mstpctl-portadminedge'):
2e4dc724
N
862 #special case, 'ifquery --check --with-defaults' on a VLAN
863 #unaware bridge
864 if not running_port_list:
865 continue
d03b0695
N
866 if (not config_stp or not running_stp):
867 continue
2e4dc724
N
868 v = ifaceobj.get_attr_value_first(k)
869 config_val = {}
870 running_val = {}
871 result = 0
872 bridge_ports = {}
873 state = ''
874 if v:
875 for bportval in v.split():
876 config_val[bportval.split('=')[0]] = bportval.split('=')[1]
877 #for bport in bridgeports:
878 for bport in running_port_list:
879 bportifaceobjlist = ifaceobj_getfunc(bport)
880 if not bportifaceobjlist:
881 continue
882 for bportifaceobj in bportifaceobjlist:
883 if (bport not in config_val):
884 if (bportifaceobj.link_kind & ifaceLinkKind.VXLAN):
885 if (not ifupdownflags.flags.WITHDEFAULTS or
886 (ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_VLAN_AWARE)):
887 continue
888 conf = 'yes'
889 else:
890 continue
891 else:
892 if ((bportifaceobj.link_kind & ifaceLinkKind.VXLAN) and
893 (ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_VLAN_AWARE)):
894 continue
895 conf = config_val[bport]
896 jsonAttr = self.get_mod_subattr(k, 'jsonAttr')
897 try:
9d3f53c6 898 running_val = self.mstpctlcmd.get_bridge_port_attr(ifaceobj.name, bport, jsonAttr)
3218f49d 899 except Exception:
2e4dc724
N
900 self.logger.info('%s %s: could not get running %s value'
901 %(ifaceobj.name, bport, attr))
902 running_val = None
903 if conf != running_val:
904 result = 1
905 bridge_ports.update({bport : running_val})
3b01ed76 906 for port, val in list(bridge_ports.items()):
2e4dc724
N
907 #running state format
908 #mstpctl-portbpdufilter swp2=yes swp1=yes vx-14567101=yes [pass]
909 #mstpctl-bpduguard swp2=yes swp1=yes vx-14567101=yes [pass]
910 state += port + '=' + val + ' '
911 if state:
912 ifaceobjcurr.update_config_with_status(k, state, result)
913 continue
914
15ef32ea
RP
915 # get the corresponding ifaceobj attr
916 v = ifaceobj.get_attr_value_first(k)
917 if not v:
918 continue
919
920 # Get the running attribute
921 rv = runningattrs.get(k[8:])
922 if k == 'mstpctl-stp':
923 # special case stp compare because it may
924 # contain more than one valid values
925 stp_on_vals = ['on', 'yes']
926 stp_off_vals = ['off']
223ba5af 927 rv = self.sysfs.bridge_get_stp(ifaceobj.name)
15ef32ea
RP
928 if ((v in stp_on_vals and rv in stp_on_vals) or
929 (v in stp_off_vals and rv in stp_off_vals)):
930 ifaceobjcurr.update_config_with_status('mstpctl-stp', v, 0)
931 else:
932 ifaceobjcurr.update_config_with_status('mstpctl-stp', v, 1)
933 continue
934
935 if k == 'mstpctl-ports':
936 # special case ports because it can contain regex or glob
937 # XXX: We get all info from mstputils, which means if
938 # mstpd is down, we will not be returning any bridge bridgeports
15ef32ea
RP
939 bridge_port_list = self._get_bridge_port_list(ifaceobj)
940 if not running_port_list and not bridge_port_list:
941 continue
942 portliststatus = 1
943 if running_port_list and bridge_port_list:
3b01ed76
JF
944 difference = set(running_port_list).symmetric_difference(
945 set(bridge_port_list))
15ef32ea
RP
946 if not difference:
947 portliststatus = 0
948 ifaceobjcurr.update_config_with_status('mstpctl-ports',
949 ' '.join(running_port_list)
950 if running_port_list else '', portliststatus)
951 elif k[:12] == 'mstpctl-port' or k == 'mstpctl-bpduguard':
952 # Now, look at port attributes
953 # derive the mstpctlcmd attr name
954 #mstpctlcmdattrname = k[12:] if k[:12] == 'mstpctl-port' else k[8:]
955 mstpctlcmdattrname = k[8:]
956
957 # for port attributes, the attributes are in a list
958 # <portname>=<portattrvalue>
959 status = 0
960 currstr = ''
0c8332bc 961 vlist = self.parse_port_list(ifaceobj.name, v)
15ef32ea
RP
962 if not vlist:
963 continue
964 for vlistitem in vlist:
965 try:
966 (p, v) = vlistitem.split('=')
9d3f53c6 967 currv = self._get_bridge_port_attr_value(ifaceobj.name, p, k)
15ef32ea
RP
968 if currv:
969 currstr += ' %s=%s' %(p, currv)
970 else:
971 currstr += ' %s=%s' %(p, 'None')
972 if currv != v:
973 status = 1
3b01ed76 974 except Exception as e:
15ef32ea
RP
975 self.log_warn(str(e))
976 pass
977 ifaceobjcurr.update_config_with_status(k, currstr, status)
978 elif not rv:
979 ifaceobjcurr.update_config_with_status(k, '', 1)
980 elif v != rv:
981 ifaceobjcurr.update_config_with_status(k, rv, 1)
982 else:
983 ifaceobjcurr.update_config_with_status(k, rv, 0)
984
2e4dc724
N
985 def _query_check_bridge_vxlan_port(self, ifaceobj, ifaceobjcurr,
986 ifaceobj_getfunc=None):
987 masters = ifaceobj.upperifaces
988 if not masters:
989 return
990 for bridge in masters:
991 bifaceobjlist = ifaceobj_getfunc(bridge)
992 for bifaceobj in bifaceobjlist:
993 if (self._is_bridge(bifaceobj) and
d486dd0d 994 self.set_default_mstp_vxlan_bridge_config and
2e4dc724 995 (bifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_VLAN_AWARE)):
d03b0695 996 config_stp = self._get_config_stp(bifaceobj)
223ba5af 997 running_stp = self.cache.get_bridge_stp(bifaceobj.name)
d03b0695
N
998 if (not config_stp or not running_stp):
999 continue
d486dd0d
JF
1000 for attr in (
1001 'mstpctl-portbpdufilter',
1002 'mstpctl-bpduguard',
1003 'mstpctl-portadminedge'
1004 ):
2e4dc724
N
1005 jsonAttr = self.get_mod_subattr(attr, 'jsonAttr')
1006 config_val = bifaceobj.get_attr_value_first(attr)
1007 if config_val:
1008 if ifaceobj.name not in [v.split('=')[0] for v in config_val.split()]:
1009 if not ifupdownflags.flags.WITHDEFAULTS:
1010 continue
1011 config_val = 'yes'
1012 else:
1013 index = [v.split('=')[0] for v in config_val.split()].index(ifaceobj.name)
1014 config_val = [v.split('=')[1] for v in config_val.split()][index]
1015 else:
1016 if not ifupdownflags.flags.WITHDEFAULTS:
1017 continue
1018 config_val = 'yes'
1019 try:
9d3f53c6 1020 running_val = self.mstpctlcmd.get_bridge_port_attr(bifaceobj.name,
2e4dc724 1021 ifaceobj.name, jsonAttr)
3218f49d 1022 except Exception:
2e4dc724
N
1023 self.logger.info('%s %s: could not get running %s value'
1024 %(bifaceobj.name, ifaceobj.name, attr))
1025 running_val = None
1026 ifaceobjcurr.update_config_with_status(attr,
1027 running_val,
1028 0 if running_val == config_val else 1)
1029 return
1030
1031
84ca006f 1032 def _query_check_bridge_port(self, ifaceobj, ifaceobjcurr):
223ba5af 1033 if not self.cache.link_exists(ifaceobj.name):
3e6ea735 1034 #self.logger.debug('bridge port %s does not exist' %ifaceobj.name)
15ef32ea
RP
1035 ifaceobjcurr.status = ifaceStatus.NOTFOUND
1036 return
e1601369 1037 # Check if this is a bridge port
223ba5af 1038 if not self.cache.link_is_bridge_port(ifaceobj.name):
e1601369 1039 # mark all the bridge attributes as error
a070c90e 1040 ifaceobjcurr.check_n_update_config_with_status_many(ifaceobj,
3b01ed76 1041 list(self._port_attrs_map.keys()), 0)
e1601369 1042 return
223ba5af 1043 bridgename = self.cache.get_master(ifaceobj.name)
e1601369 1044 # list of attributes that are not supported currently
16d854b4
RP
1045 blacklistedattrs = ['mstpctl-portpathcost',
1046 'mstpctl-treeportprio', 'mstpctl-treeportcost']
15ef32ea 1047 ifaceattrs = self.dict_key_subset(ifaceobj.config,
3b01ed76 1048 list(self._port_attrs_map.keys()))
15ef32ea
RP
1049 if not ifaceattrs:
1050 return
1051 runningattrs = self.mstpctlcmd.get_bridge_attrs(ifaceobj.name)
9d3f53c6 1052 #self.logger.info('C' + str(runningattrs))
15ef32ea
RP
1053 if not runningattrs:
1054 runningattrs = {}
1055 for k in ifaceattrs:
1056 # for all mstpctl options
15ef32ea
RP
1057 # get the corresponding ifaceobj attr
1058 v = ifaceobj.get_attr_value_first(k)
16d854b4 1059 if not v or k in blacklistedattrs:
e1601369 1060 ifaceobjcurr.update_config_with_status(k, v, -1)
15ef32ea 1061 continue
9d3f53c6 1062 currv = self._get_bridge_port_attr_value(bridgename, ifaceobj.name, k)
15ef32ea
RP
1063 if currv:
1064 if currv != v:
1065 ifaceobjcurr.update_config_with_status(k, currv, 1)
1066 else:
1067 ifaceobjcurr.update_config_with_status(k, currv, 0)
1068 else:
1069 ifaceobjcurr.update_config_with_status(k, None, 1)
1070
84ca006f 1071 def _query_check(self, ifaceobj, ifaceobjcurr, ifaceobj_getfunc=None):
e1601369 1072 if self._is_bridge(ifaceobj):
2e4dc724
N
1073 self._query_check_bridge(ifaceobj, ifaceobjcurr, ifaceobj_getfunc)
1074 elif ifaceobj.link_kind & ifaceLinkKind.VXLAN:
1075 self._query_check_bridge_vxlan_port(ifaceobj, ifaceobjcurr,
1076 ifaceobj_getfunc)
e1601369
RP
1077 else:
1078 self._query_check_bridge_port(ifaceobj, ifaceobjcurr)
15ef32ea 1079
9d3f53c6
JF
1080 def _query_bridge_port_attr(self, ifaceobjrunning, bridgename, attr, value_cmp):
1081 v = self._get_bridge_port_attr_value(bridgename,
1082 ifaceobjrunning.name,
1083 attr)
1084 if v and value_cmp and v != value_cmp:
1085 ifaceobjrunning.update_config(attr, v)
1086 elif v and not value_cmp:
1087 ifaceobjrunning.update_config(attr, v)
1088
e1601369 1089 def _query_running_bridge_port(self, ifaceobjrunning):
223ba5af 1090 bridgename = self.cache.get_master(ifaceobjrunning.name)
e1601369 1091 if not bridgename:
c46af1c9 1092 self.logger.warning('%s: unable to determine bridgename'
e1601369 1093 %ifaceobjrunning.name)
15ef32ea 1094 return
223ba5af 1095 if self.sysfs.bridge_get_stp(bridgename) == 'no':
e1601369
RP
1096 # This bridge does not run stp, return
1097 return
15ef32ea 1098 # if userspace stp not set, return
9d3f53c6
JF
1099 if self.systcl_get_net_bridge_stp_user_space() != '1':
1100 return
1101
1102 self._query_bridge_port_attr(ifaceobjrunning, bridgename,
1103 'mstpctl-portautoedge',
1104 self.get_mod_subattr('mstpctl-portautoedge', 'default'))
1105
1106 self._query_bridge_port_attr(ifaceobjrunning, bridgename,
1107 'mstpctl-portbpdufilter',
1108 'no')
22945dd6 1109
9d3f53c6
JF
1110 self._query_bridge_port_attr(ifaceobjrunning, bridgename,
1111 'mstpctl-portnetwork',
1112 'no')
e1601369
RP
1113
1114 # XXX: Can we really get path cost of a port ???
1115 #v = self.mstpctlcmd.get_portpathcost(ifaceobjrunning.name, p)
1116 #if v and v != self.get_mod_subattr('mstpctl-pathcost',
1117 # 'default'):
1118 # ifaceobjrunning.update_config('mstpctl-network', v)
1119
9d3f53c6
JF
1120 self._query_bridge_port_attr(ifaceobjrunning, bridgename,
1121 'mstpctl-portadminedge',
1122 'no')
e1601369 1123
9d3f53c6
JF
1124 self._query_bridge_port_attr(ifaceobjrunning, bridgename,
1125 'mstpctl-portp2p',
1126 'auto')
e1601369 1127
9d3f53c6
JF
1128 self._query_bridge_port_attr(ifaceobjrunning, bridgename,
1129 'mstpctl-portrestrrole',
1130 'no')
e1601369 1131
9d3f53c6
JF
1132 self._query_bridge_port_attr(ifaceobjrunning, bridgename,
1133 'mstpctl-portrestrtcn',
1134 'no')
e1601369 1135
9d3f53c6
JF
1136 self._query_bridge_port_attr(ifaceobjrunning, bridgename,
1137 'mstpctl-bpduguard',
1138 'no')
e1601369
RP
1139
1140 # XXX: Can we really get path cost of a port ???
1141 #v = self.mstpctlcmd.get_bridgeport_attr(ifaceobjrunning.name,
1142 # p, 'treeprio')
1143 #if v and v != self.get_mod_subattr('mstpctl-treeportprio',
1144 # 'default'):
1145 # portconfig['mstpctl-treeportprio'] += ' %s=%s' %(p, v)
1146
1147 #v = self.mstpctlcmd.get_bridgeport_attr(ifaceobjrunning.name,
1148 # p, 'treecost')
1149 #if v and v != self.get_mod_subattr('mstpctl-treeportcost',
1150 # 'default'):
1151 # portconfig['mstpctl-treeportcost'] += ' %s=%s' %(p, v)
1152
1153 def _query_running_bridge(self, ifaceobjrunning):
223ba5af 1154 if self.sysfs.bridge_get_stp(ifaceobjrunning.name) == 'no':
e1601369
RP
1155 # This bridge does not run stp, return
1156 return
1157 # if userspace stp not set, return
9d3f53c6
JF
1158 if self.systcl_get_net_bridge_stp_user_space() != '1':
1159 return
15ef32ea
RP
1160 # Check if mstp really knows about this bridge
1161 if not self.mstpctlcmd.mstpbridge_exists(ifaceobjrunning.name):
1162 return
0dd2f0d1
N
1163 bridge_vlan_aware = False
1164 if ifaceobjrunning.get_attr_value_first('bridge-vlan-aware') == 'yes':
1165 bridge_vlan_aware = True
15ef32ea 1166 ifaceobjrunning.update_config_dict(self._query_running_attrs(
0dd2f0d1
N
1167 ifaceobjrunning,
1168 bridge_vlan_aware))
15ef32ea 1169
e1601369 1170 def _query_running(self, ifaceobjrunning, **extra_args):
223ba5af 1171 if self.cache.bridge_exists(ifaceobjrunning.name):
e1601369 1172 self._query_running_bridge(ifaceobjrunning)
223ba5af 1173 elif self.cache.link_is_bridge_port(ifaceobjrunning.name):
e1601369
RP
1174 self._query_running_bridge_port(ifaceobjrunning)
1175
286a6b99
N
1176 def _query_bridge_port(self, ifaceobj, ifaceobj_getfunc=None):
1177 """
1178 Example:
1179 Configuration:
1180 auto vxlan1wd
1181 iface vxlan1wd
1182 vxlan-id 1001
1183
1184 auto vxlan2wd
1185 iface vxlan2wd
1186 vxlan-id 1002
1187
1188 auto brwithdef2
1189 iface brwithdef2
1190 bridge_ports vxlan1wd vxlan2wd
1191 bridge-vlan-aware yes
1192
1193 Output:
1194 $ ifquery vxlan1wd
1195 auto vxlan1wd
1196 iface vxlan1wd
1197 vxlan-id 1001
1198
1199 $ ifquery --with-defaults vxlan1wd
1200 auto vxlan1wd
1201 iface vxlan1wd
1202 vxlan-id 1001
1203 mstpctl-portbpdufilter yes
1204 mstpctl-bpduguard yes
1205 """
1206 masters = ifaceobj.upperifaces
1207 if not masters:
1208 return
1209 try:
1210 for bridge in masters:
1211 bifaceobj = ifaceobj_getfunc(bridge)[0]
1212 if (self._is_bridge(bifaceobj) and
d486dd0d 1213 self.set_default_mstp_vxlan_bridge_config and
85d04bab
JF
1214 (bifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_VLAN_AWARE)
1215 and ifaceobj.link_kind & ifaceLinkKind.VXLAN
1216 ):
d486dd0d
JF
1217 for attr in ('mstpctl-portbpdufilter',
1218 'mstpctl-bpduguard',
1219 'mstpctl-portadminedge'):
286a6b99
N
1220 jsonAttr = self.get_mod_subattr(attr, 'jsonAttr')
1221 config_val = ifaceobj.get_attr_value_first(attr)
1222 if config_val or not ifupdownflags.flags.WITHDEFAULTS:
1223 continue
1224 config_val = 'yes'
1225 ifaceobj.replace_config(attr, config_val)
1226 return
3b01ed76 1227 except Exception as e:
286a6b99
N
1228 self.logger.info("%s: %s" %(ifaceobj.name, str(e)))
1229 pass
1230
2e4dc724
N
1231 def _query(self, ifaceobj, ifaceobj_getfunc=None, **kwargs):
1232 """ add default policy attributes supported by the module """
1233 if not self._is_bridge(ifaceobj):
286a6b99
N
1234 if (ifaceobj.module_flags.get(self.name,0x0) &
1235 mstpctlFlags.PORT_PROCESSED):
1236 return
1237 self._query_bridge_port(ifaceobj, ifaceobj_getfunc)
1238 ifaceobj.module_flags[self.name] = (
1239 ifaceobj.module_flags.setdefault(self.name,0) |
1240 mstpctlFlags.PORT_PROCESSED)
2e4dc724
N
1241 return
1242 lowerinfs = ifaceobj.lowerifaces
1243 if not lowerinfs:
1244 return
1245 if ifaceobj.get_attr_value_first('bridge-vlan-aware') != 'yes':
d486dd0d 1246 for attr in ('mstpctl-portbpdufilter', 'mstpctl-bpduguard', 'mstpctl-portadminedge'):
2e4dc724
N
1247 state = ''
1248 config = ifaceobj.get_attr_value_first(attr)
1249 for port in lowerinfs:
1250 bportobjlist = ifaceobj_getfunc(port)
1251 for bportobj in bportobjlist:
1252 if bportobj.get_attr_value_first('vxlan-id'):
1253 if config:
1254 if port not in [v.split('=')[0] for v in config.split()]:
1255 config += ' %s=yes' %port
1256 else:
1257 state += '%s=yes ' %port
1258 ifaceobj.replace_config(attr, config if config else state)
1259 else:
d486dd0d 1260 for attr in ('mstpctl-portbpdufilter', 'mstpctl-bpduguard', 'mstpctl-portadminedge'):
2e4dc724
N
1261 state = ''
1262 config = ifaceobj.get_attr_value_first(attr)
1263 for port in lowerinfs:
1264 bportobjlist = ifaceobj_getfunc(port)
1265 for bportobj in bportobjlist:
286a6b99
N
1266 if (bportobj.module_flags.get(self.name,0x0) &
1267 mstpctlFlags.PORT_PROCESSED):
1268 continue
2e4dc724
N
1269 if bportobj.get_attr_value_first('vxlan-id'):
1270 if config:
1271 if port not in [v.split('=')[0] for v in config.split()]:
1272 bportobj.update_config(attr, 'yes')
1273 else:
1274 index = [v.split('=')[0] for v in config.split()].index(port)
1275 state = [v.split('=')[1] for v in config.split()][index]
1276 bportobj.update_config(attr, '%s' %state)
1277 v = config.split()
1278 del v[index]
1279 config = ' '.join(v)
1280 else:
286a6b99
N
1281 bportobj.replace_config(attr, 'yes')
1282 bportobj.module_flags[self.name] = (
1283 bportobj.module_flags.setdefault(self.name,0) |
1284 mstpctlFlags.PORT_PROCESSED)
2e4dc724
N
1285 if config:
1286 ifaceobj.replace_config(attr, config)
2e4dc724 1287
223ba5af
JF
1288 _run_ops = {
1289 "pre-up": _up,
1290 "post-down": _down,
1291 "query-checkcurr": _query_check,
1292 "query-running": _query_running,
1293 "query": _query
1294 }
15ef32ea
RP
1295
1296 def get_ops(self):
1297 """ returns list of ops supported by this module """
3b01ed76 1298 return list(self._run_ops.keys())
15ef32ea
RP
1299
1300 def _init_command_handlers(self):
15ef32ea 1301 if not self.mstpctlcmd:
fc5e1735 1302 self.mstpctlcmd = mstpctlutil()
15ef32ea 1303
84ca006f
RP
1304 def run(self, ifaceobj, operation, query_ifaceobj=None,
1305 ifaceobj_getfunc=None, **extra_args):
15ef32ea
RP
1306 """ run mstp configuration on the interface object passed as argument
1307
1308 Args:
1309 **ifaceobj** (object): iface object
1310
1311 **operation** (str): any of 'pre-up', 'post-down', 'query-checkcurr',
1312 'query-running'
1313 Kwargs:
1314 **query_ifaceobj** (object): query check ifaceobject. This is only
1315 valid when op is 'query-checkcurr'. It is an object same as
1316 ifaceobj, but contains running attribute values and its config
1317 status. The modules can use it to return queried running state
1318 of interfaces. status is success if the running state is same
1319 as user required state in ifaceobj. error otherwise.
1320 """
3e6ea735
RP
1321 if ifaceobj.type == ifaceType.BRIDGE_VLAN:
1322 return
15ef32ea
RP
1323 op_handler = self._run_ops.get(operation)
1324 if not op_handler:
1325 return
15ef32ea
RP
1326 self._init_command_handlers()
1327 if operation == 'query-checkcurr':
84ca006f
RP
1328 op_handler(self, ifaceobj, query_ifaceobj,
1329 ifaceobj_getfunc=ifaceobj_getfunc)
15ef32ea 1330 else:
84ca006f 1331 op_handler(self, ifaceobj, ifaceobj_getfunc=ifaceobj_getfunc)