]>
Commit | Line | Data |
---|---|---|
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 | 7 | import os |
d486dd0d | 8 | |
421e9573 JF |
9 | from collections import OrderedDict |
10 | ||
d486dd0d | 11 | try: |
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 | 24 | except (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 |
39 | class mstpctlFlags: |
40 | PORT_PROCESSED = 0x1 | |
41 | ||
223ba5af | 42 | class 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) |