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