]>
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 | |
d486dd0d | 9 | try: |
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 | 22 | except 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 |
37 | class mstpctlFlags: |
38 | PORT_PROCESSED = 0x1 | |
39 | ||
223ba5af | 40 | class 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) |