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