]>
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 * | |
10 | from ifupdownaddons.modulebase import moduleBase | |
11 | from ifupdownaddons.bridgeutils import brctl | |
12 | from ifupdownaddons.iproute2 import iproute2 | |
13 | from ifupdownaddons.mstpctlutil import mstpctlutil | |
5f8c03e7 | 14 | from ifupdownaddons.systemutils import systemUtils |
fc5e1735 | 15 | import ifupdown.ifupdownflags as ifupdownflags |
15ef32ea | 16 | |
4c773918 ST |
17 | class mstpctlFlags: |
18 | PORT_PROCESSED = 0x1 | |
19 | ||
15ef32ea RP |
20 | class mstpctl(moduleBase): |
21 | """ ifupdown2 addon module to configure mstp attributes """ | |
22 | ||
23 | _modinfo = {'mhelp' : 'mstp configuration module for bridges', | |
24 | 'attrs' : { | |
25 | 'mstpctl-ports' : | |
26 | {'help' : 'mstp ports', | |
48689b55 RP |
27 | 'compat' : True, |
28 | 'deprecated': True, | |
29 | 'new-attribute': 'bridge-ports'}, | |
15ef32ea RP |
30 | 'mstpctl-stp' : |
31 | {'help': 'bridge stp yes/no', | |
32 | 'compat' : True, | |
4bdf2d1c JF |
33 | 'default' : 'no', |
34 | 'deprecated': True, | |
35 | 'new-attribute': 'bridge-stp'}, | |
15ef32ea RP |
36 | 'mstpctl-treeprio' : |
37 | {'help': 'tree priority', | |
38 | 'default' : '32768', | |
39 | 'validrange' : ['0', '65535'], | |
40 | 'required' : False, | |
41 | 'example' : ['mstpctl-treeprio 32768']}, | |
42 | 'mstpctl-ageing' : | |
43 | {'help': 'ageing time', | |
44 | 'default' : '300', | |
45 | 'required' : False, | |
46 | 'example' : ['mstpctl-ageing 300']}, | |
47 | 'mstpctl-maxage' : | |
48 | { 'help' : 'max message age', | |
49 | 'default' : '20', | |
50 | 'required' : False, | |
51 | 'example' : ['mstpctl-maxage 20']}, | |
52 | 'mstpctl-fdelay' : | |
53 | { 'help' : 'set forwarding delay', | |
54 | 'default' : '15', | |
55 | 'required' : False, | |
56 | 'example' : ['mstpctl-fdelay 15']}, | |
57 | 'mstpctl-maxhops' : | |
58 | { 'help' : 'bridge max hops', | |
59 | 'default' : '15', | |
60 | 'required' : False, | |
61 | 'example' : ['mstpctl-maxhops 15']}, | |
62 | 'mstpctl-txholdcount' : | |
63 | { 'help' : 'bridge transmit holdcount', | |
64 | 'default' : '6', | |
65 | 'required' : False, | |
66 | 'example' : ['mstpctl-txholdcount 6']}, | |
67 | 'mstpctl-forcevers' : | |
68 | { 'help' : 'bridge force stp version', | |
69 | 'default' : 'rstp', | |
70 | 'required' : False, | |
71 | 'example' : ['mstpctl-forcevers rstp']}, | |
72 | 'mstpctl-portpathcost' : | |
73 | { 'help' : 'bridge port path cost', | |
74 | 'default' : '0', | |
b97687bc | 75 | 'jsonAttr' : 'adminExtPortCost', |
15ef32ea RP |
76 | 'required' : False, |
77 | 'example' : ['mstpctl-portpathcost swp1=0 swp2=1']}, | |
15ef32ea RP |
78 | 'mstpctl-portp2p' : |
79 | { 'help' : 'bridge port p2p detection mode', | |
e759a20a | 80 | 'default' : 'auto', |
b97687bc | 81 | 'jsonAttr' : 'adminPointToPoint', |
e759a20a | 82 | 'validvals' : ['yes', 'no', 'auto'], |
15ef32ea RP |
83 | 'required' : False, |
84 | 'example' : ['mstpctl-portp2p swp1=no swp2=no']}, | |
85 | 'mstpctl-portrestrrole' : | |
86 | { 'help' : | |
87 | 'enable/disable port ability to take root role of the port', | |
88 | 'default' : 'no', | |
b97687bc | 89 | 'jsonAttr' : 'restrictedRole', |
15ef32ea RP |
90 | 'validvals' : ['yes', 'no'], |
91 | 'required' : False, | |
92 | 'example' : ['mstpctl-portrestrrole swp1=no swp2=no']}, | |
93 | 'mstpctl-portrestrtcn' : | |
94 | { 'help' : | |
95 | 'enable/disable port ability to propagate received topology change notification of the port', | |
96 | 'default' : 'no', | |
b97687bc | 97 | 'jsonAttr' : 'restrictedTcn', |
15ef32ea RP |
98 | 'validvals' : ['yes', 'no'], |
99 | 'required' : False, | |
100 | 'example' : ['mstpctl-portrestrtcn swp1=no swp2=no']}, | |
101 | 'mstpctl-bpduguard' : | |
102 | { 'help' : | |
103 | 'enable/disable bpduguard', | |
104 | 'default' : 'no', | |
b97687bc | 105 | 'jsonAttr' : 'bpduGuardPort', |
15ef32ea RP |
106 | 'validvals' : ['yes', 'no'], |
107 | 'required' : False, | |
108 | 'example' : ['mstpctl-bpduguard swp1=no swp2=no']}, | |
109 | 'mstpctl-treeportprio' : | |
110 | { 'help' : | |
111 | 'port priority for MSTI instance', | |
112 | 'default' : '128', | |
113 | 'validrange' : ['0', '240'], | |
114 | 'required' : False, | |
115 | 'example' : ['mstpctl-treeportprio swp1=128 swp2=128']}, | |
116 | 'mstpctl-hello' : | |
117 | { 'help' : 'set hello time', | |
118 | 'default' : '2', | |
119 | 'required' : False, | |
120 | 'example' : ['mstpctl-hello 2']}, | |
121 | 'mstpctl-portnetwork' : | |
122 | { 'help' : 'enable/disable bridge assurance capability for a port', | |
123 | 'validvals' : ['yes', 'no'], | |
124 | 'default' : 'no', | |
b97687bc | 125 | 'jsonAttr' : 'networkPort', |
15ef32ea RP |
126 | 'required' : False, |
127 | 'example' : ['mstpctl-portnetwork swp1=no swp2=no']}, | |
128 | 'mstpctl-portadminedge' : | |
129 | { 'help' : 'enable/disable initial edge state of the port', | |
130 | 'validvals' : ['yes', 'no'], | |
131 | 'default' : 'no', | |
b97687bc | 132 | 'jsonAttr' : 'adminEdgePort', |
15ef32ea RP |
133 | 'required' : False, |
134 | 'example' : ['mstpctl-portadminedge swp1=no swp2=no']}, | |
135 | 'mstpctl-portautoedge' : | |
136 | { 'help' : 'enable/disable auto transition to/from edge state of the port', | |
137 | 'validvals' : ['yes', 'no'], | |
be1faada | 138 | 'default' : 'yes', |
b97687bc | 139 | 'jsonAttr' : 'autoEdgePort', |
15ef32ea RP |
140 | 'required' : False, |
141 | 'example' : ['mstpctl-portautoedge swp1=yes swp2=yes']}, | |
142 | 'mstpctl-treeportcost' : | |
143 | { 'help' : 'port tree cost', | |
144 | 'required' : False}, | |
145 | 'mstpctl-portbpdufilter' : | |
9e012f9e RP |
146 | { 'help' : 'enable/disable bpdu filter on a port. ' + |
147 | 'syntax varies when defined under a bridge ' + | |
148 | 'vs under a port', | |
15ef32ea | 149 | 'validvals' : ['yes', 'no'], |
b97687bc | 150 | 'jsonAttr' : 'bpduFilterPort', |
15ef32ea RP |
151 | 'default' : 'no', |
152 | 'required' : False, | |
9e012f9e RP |
153 | 'example' : ['under a bridge: mstpctl-portbpdufilter swp1=no swp2=no', |
154 | 'under a port: mstpctl-portbpdufilter yes']}, | |
15ef32ea RP |
155 | }} |
156 | ||
4c39c7b8 RP |
157 | # Maps mstp bridge attribute names to corresponding mstpctl commands |
158 | # XXX: This can be encoded in the modules dict above | |
159 | _attrs_map = OrderedDict([('mstpctl-treeprio' , 'treeprio'), | |
160 | ('mstpctl-ageing' , 'ageing'), | |
4c39c7b8 | 161 | ('mstpctl-fdelay' , 'fdelay'), |
caa23e5f | 162 | ('mstpctl-maxage' , 'maxage'), |
4c39c7b8 RP |
163 | ('mstpctl-maxhops' , 'maxhops'), |
164 | ('mstpctl-txholdcount' , 'txholdcount'), | |
165 | ('mstpctl-forcevers', 'forcevers'), | |
166 | ('mstpctl-hello' , 'hello')]) | |
167 | ||
168 | # Maps mstp port attribute names to corresponding mstpctl commands | |
169 | # XXX: This can be encoded in the modules dict above | |
e1601369 RP |
170 | _port_attrs_map = {'mstpctl-portpathcost' : 'portpathcost', |
171 | 'mstpctl-portadminedge' : 'portadminedge', | |
d8e3554d | 172 | 'mstpctl-portautoedge' : 'portautoedge' , |
e1601369 RP |
173 | 'mstpctl-portp2p' : 'portp2p', |
174 | 'mstpctl-portrestrrole' : 'portrestrrole', | |
175 | 'mstpctl-portrestrtcn' : 'portrestrtcn', | |
15ef32ea | 176 | 'mstpctl-bpduguard' : 'bpduguard', |
e1601369 RP |
177 | 'mstpctl-treeportprio' : 'treeportprio', |
178 | 'mstpctl-treeportcost' : 'treeportcost', | |
179 | 'mstpctl-portnetwork' : 'portnetwork', | |
180 | 'mstpctl-portbpdufilter' : 'portbpdufilter'} | |
15ef32ea RP |
181 | |
182 | def __init__(self, *args, **kargs): | |
183 | moduleBase.__init__(self, *args, **kargs) | |
184 | self.ipcmd = None | |
4c773918 | 185 | self.name = self.__class__.__name__ |
15ef32ea RP |
186 | self.brctlcmd = None |
187 | self.mstpctlcmd = None | |
5f8c03e7 RP |
188 | self.mstpd_running = (True if systemUtils.is_process_running('mstpd') |
189 | else False) | |
15ef32ea RP |
190 | |
191 | def _is_bridge(self, ifaceobj): | |
192 | if (ifaceobj.get_attr_value_first('mstpctl-ports') or | |
84ca006f RP |
193 | ifaceobj.get_attr_value_first('bridge-ports')): |
194 | return True | |
195 | return False | |
196 | ||
197 | def _is_bridge_port(self, ifaceobj): | |
198 | if self.brctlcmd.is_bridge_port(ifaceobj.name): | |
15ef32ea RP |
199 | return True |
200 | return False | |
201 | ||
202 | def get_dependent_ifacenames(self, ifaceobj, ifacenames_all=None): | |
203 | if not self._is_bridge(ifaceobj): | |
204 | return None | |
0c8332bc RP |
205 | return self.parse_port_list(ifaceobj.name, |
206 | ifaceobj.get_attr_value_first( | |
15ef32ea RP |
207 | 'mstpctl-ports'), ifacenames_all) |
208 | ||
209 | def get_dependent_ifacenames_running(self, ifaceobj): | |
210 | self._init_command_handlers() | |
211 | if (self.brctlcmd.bridge_exists(ifaceobj.name) and | |
212 | not self.mstpctlcmd.mstpbridge_exists(ifaceobj.name)): | |
213 | return None | |
214 | return self.brctlcmd.get_bridge_ports(ifaceobj.name) | |
215 | ||
216 | def _get_bridge_port_list(self, ifaceobj): | |
217 | ||
218 | # port list is also available in the previously | |
219 | # parsed dependent list. Use that if available, instead | |
220 | # of parsing port expr again | |
221 | port_list = ifaceobj.lowerifaces | |
222 | if port_list: | |
223 | return port_list | |
224 | ports = ifaceobj.get_attr_value_first('mstpctl-ports') | |
225 | if ports: | |
0c8332bc | 226 | return self.parse_port_list(ifaceobj.name, ports) |
15ef32ea RP |
227 | else: |
228 | return None | |
229 | ||
404cc695 RP |
230 | def _ports_enable_disable_ipv6(self, ports, enable='1'): |
231 | for p in ports: | |
232 | try: | |
233 | self.write_file('/proc/sys/net/ipv6/conf/%s' %p + | |
234 | '/disable_ipv6', enable) | |
235 | except Exception, e: | |
236 | self.logger.info(str(e)) | |
237 | pass | |
238 | ||
15ef32ea RP |
239 | def _add_ports(self, ifaceobj): |
240 | bridgeports = self._get_bridge_port_list(ifaceobj) | |
241 | ||
242 | runningbridgeports = [] | |
243 | # Delete active ports not in the new port list | |
fc5e1735 | 244 | if not ifupdownflags.flags.PERFMODE: |
15ef32ea RP |
245 | runningbridgeports = self.brctlcmd.get_bridge_ports(ifaceobj.name) |
246 | if runningbridgeports: | |
247 | [self.ipcmd.link_set(bport, 'nomaster') | |
248 | for bport in runningbridgeports | |
249 | if not bridgeports or bport not in bridgeports] | |
250 | else: | |
251 | runningbridgeports = [] | |
252 | if not bridgeports: | |
253 | return | |
254 | err = 0 | |
255 | for bridgeport in Set(bridgeports).difference(Set(runningbridgeports)): | |
256 | try: | |
fc5e1735 RP |
257 | if (not ifupdownflags.flags.DRYRUN and |
258 | not self.ipcmd.link_exists(bridgeport)): | |
15ef32ea RP |
259 | self.log_warn('%s: bridge port %s does not exist' |
260 | %(ifaceobj.name, bridgeport)) | |
261 | err += 1 | |
262 | continue | |
263 | self.ipcmd.link_set(bridgeport, 'master', ifaceobj.name) | |
15ef32ea RP |
264 | self.ipcmd.addr_flush(bridgeport) |
265 | except Exception, e: | |
266 | self.log_error(str(e)) | |
404cc695 | 267 | |
15ef32ea RP |
268 | if err: |
269 | self.log_error('error configuring bridge (missing ports)') | |
270 | ||
271 | def _apply_bridge_settings(self, ifaceobj): | |
fc5e1735 | 272 | check = False if ifupdownflags.flags.PERFMODE else True |
15ef32ea | 273 | try: |
4c39c7b8 RP |
274 | # set bridge attributes |
275 | for attrname, dstattrname in self._attrs_map.items(): | |
276 | try: | |
277 | v = ifaceobj.get_attr_value_first(attrname) | |
2722bb19 ST |
278 | if not v: |
279 | continue | |
4c39c7b8 RP |
280 | if attrname == 'mstpctl-treeprio': |
281 | self.mstpctlcmd.set_bridge_treeprio(ifaceobj.name, | |
282 | v, check) | |
283 | else: | |
284 | self.mstpctlcmd.set_bridge_attr(ifaceobj.name, | |
285 | dstattrname, v, check) | |
286 | except Exception, e: | |
287 | self.logger.warn('%s' %str(e)) | |
288 | pass | |
15ef32ea RP |
289 | |
290 | # set bridge port attributes | |
4c39c7b8 | 291 | for attrname, dstattrname in self._port_attrs_map.items(): |
b97687bc ST |
292 | config_val = ifaceobj.get_attr_value_first(attrname) |
293 | default_val = self.get_mod_subattr(attrname,'default') | |
294 | if not config_val: | |
295 | # nothing configured, we may need to reset all ports to defaults | |
296 | # if the default exists and jsonAttribute conversion exists | |
297 | try: | |
298 | jsonAttr = self.get_mod_subattr(attrname, 'jsonAttr') | |
299 | if default_val and jsonAttr: | |
300 | bridgeports = self._get_bridge_port_list(ifaceobj) | |
301 | for port in bridgeports: | |
302 | running_val = self.mstpctlcmd.get_mstpctl_bridgeport_attr(ifaceobj.name, | |
303 | port, jsonAttr) | |
304 | if running_val != default_val: | |
305 | # we will not bother checking since we already checked | |
306 | self.mstpctlcmd.set_bridgeport_attr(ifaceobj.name, | |
307 | port, dstattrname, default_val, False) | |
308 | except: | |
309 | self.logger.info('%s: not resetting %s config' | |
310 | %(ifaceobj.name, attrname)) | |
311 | # leave the loop for this attribute | |
15ef32ea | 312 | continue |
b97687bc ST |
313 | |
314 | portlist = self.parse_port_list(ifaceobj.name, config_val) | |
15ef32ea RP |
315 | if not portlist: |
316 | self.log_warn('%s: error parsing \'%s %s\'' | |
b97687bc | 317 | %(ifaceobj.name, attrname, config_val)) |
15ef32ea | 318 | continue |
b97687bc ST |
319 | # there was a configured value so we need to parse it |
320 | # and set the attribute for each port configured | |
15ef32ea RP |
321 | for p in portlist: |
322 | try: | |
323 | (port, val) = p.split('=') | |
42a9d193 RP |
324 | # if it is not bridge port, continue |
325 | if not os.path.exists('/sys/class/net/%s/brport' %port): | |
326 | continue | |
15ef32ea RP |
327 | self.mstpctlcmd.set_bridgeport_attr(ifaceobj.name, |
328 | port, dstattrname, val, check) | |
329 | except Exception, e: | |
330 | self.log_warn('%s: error setting %s (%s)' | |
331 | %(ifaceobj.name, attrname, str(e))) | |
332 | except Exception, e: | |
333 | self.log_warn(str(e)) | |
334 | pass | |
335 | ||
84ca006f | 336 | def _apply_bridge_port_settings(self, ifaceobj, bridgename=None, |
641cbd1e RP |
337 | bridgeifaceobj=None, |
338 | stp_running_on=True, | |
d8e3554d | 339 | mstpd_running=True): |
fc5e1735 | 340 | check = False if ifupdownflags.flags.PERFMODE else True |
641cbd1e | 341 | applied = False |
84ca006f RP |
342 | if not bridgename and bridgeifaceobj: |
343 | bridgename = bridgeifaceobj.name | |
b97687bc ST |
344 | |
345 | if not stp_running_on: | |
346 | # stp may get turned on at a later point | |
347 | self.logger.info('%s: ignoring config' | |
348 | %(ifaceobj.name) + | |
349 | ' (stp on bridge %s is not on yet)' %bridgename) | |
350 | return applied | |
351 | if (not mstpd_running or | |
352 | not os.path.exists('/sys/class/net/%s/brport' %ifaceobj.name) or | |
353 | not self.ipcmd.bridge_is_vlan_aware(bridgename)): | |
354 | return applied | |
84ca006f RP |
355 | # set bridge port attributes |
356 | for attrname, dstattrname in self._port_attrs_map.items(): | |
357 | attrval = ifaceobj.get_attr_value_first(attrname) | |
b97687bc ST |
358 | config_val = ifaceobj.get_attr_value_first(attrname) |
359 | default_val = self.get_mod_subattr(attrname,'default') | |
360 | jsonAttr = self.get_mod_subattr(attrname, 'jsonAttr') | |
361 | # to see the running value, stp would have to be on | |
362 | # so we would have parsed mstpctl showportdetail json output | |
363 | try: | |
364 | running_val = self.mstpctlcmd.get_mstpctl_bridgeport_attr(bridgename, | |
365 | ifaceobj.name, jsonAttr) | |
366 | except: | |
367 | self.logger.info('%s %s: could not get running %s value' | |
368 | %(bridgename, ifaceobj.name, attrname)) | |
369 | running_val = None | |
370 | if (not config_val and default_val and (running_val != default_val)): | |
371 | # this happens when users remove an attribute from a port | |
372 | # and expect the default to be restored with ifreload. | |
373 | config_val = default_val | |
374 | elif not config_val: | |
375 | # there is nothing configured and no default to reset | |
42a9d193 | 376 | continue |
b97687bc | 377 | |
84ca006f RP |
378 | try: |
379 | self.mstpctlcmd.set_bridgeport_attr(bridgename, | |
b97687bc | 380 | ifaceobj.name, dstattrname, config_val, check) |
641cbd1e | 381 | applied = True |
84ca006f RP |
382 | except Exception, e: |
383 | self.log_warn('%s: error setting %s (%s)' | |
384 | %(ifaceobj.name, attrname, str(e))) | |
641cbd1e | 385 | return applied |
84ca006f RP |
386 | |
387 | def _apply_bridge_port_settings_all(self, ifaceobj, | |
388 | ifaceobj_getfunc=None): | |
98b5ee73 | 389 | self.logger.info('%s: applying mstp configuration ' |
84ca006f | 390 | %ifaceobj.name + 'specific to ports') |
f6a0fa15 RP |
391 | # Query running bridge ports. and only apply attributes on them |
392 | bridgeports = self.brctlcmd.get_bridge_ports(ifaceobj.name) | |
4c39c7b8 RP |
393 | if not bridgeports: |
394 | self.logger.debug('%s: cannot find bridgeports' %ifaceobj.name) | |
395 | return | |
84ca006f | 396 | for bport in bridgeports: |
98b5ee73 | 397 | self.logger.info('%s: processing mstp config for port %s' |
84ca006f | 398 | %(ifaceobj.name, bport)) |
3e6ea735 | 399 | if not self.ipcmd.link_exists(bport): |
3e6ea735 | 400 | continue |
42a9d193 RP |
401 | if not os.path.exists('/sys/class/net/%s/brport' %bport): |
402 | continue | |
84ca006f RP |
403 | bportifaceobjlist = ifaceobj_getfunc(bport) |
404 | if not bportifaceobjlist: | |
405 | continue | |
406 | for bportifaceobj in bportifaceobjlist: | |
98b5ee73 | 407 | # Dont process bridge port if it already has been processed |
4c773918 ST |
408 | if (bportifaceobj.module_flags.get(self.name,0x0) & \ |
409 | mstpctlFlags.PORT_PROCESSED): | |
98b5ee73 | 410 | continue |
15ef32ea | 411 | try: |
84ca006f RP |
412 | self._apply_bridge_port_settings(bportifaceobj, |
413 | ifaceobj.name, ifaceobj) | |
15ef32ea | 414 | except Exception, e: |
84ca006f | 415 | self.log_warn(str(e)) |
15ef32ea | 416 | |
641cbd1e RP |
417 | def _is_running_userspace_stp_state_on(self, bridgename): |
418 | stp_state_file = '/sys/class/net/%s/bridge/stp_state' %bridgename | |
419 | if not stp_state_file: | |
420 | return False | |
421 | running_stp_state = self.read_file_oneline(stp_state_file) | |
422 | if running_stp_state and running_stp_state == '2': | |
423 | return True | |
424 | return False | |
425 | ||
84ca006f | 426 | def _up(self, ifaceobj, ifaceobj_getfunc=None): |
15ef32ea | 427 | # Check if bridge port |
98b5ee73 RP |
428 | bridgename = self.ipcmd.bridge_port_get_bridge_name(ifaceobj.name) |
429 | if bridgename: | |
5f8c03e7 | 430 | mstpd_running = self.mstpd_running |
641cbd1e RP |
431 | stp_running_on = self._is_running_userspace_stp_state_on(bridgename) |
432 | applied = self._apply_bridge_port_settings(ifaceobj, bridgename, | |
433 | None, stp_running_on, | |
434 | mstpd_running) | |
435 | if applied: | |
436 | ifaceobj.module_flags[self.name] = \ | |
437 | ifaceobj.module_flags.setdefault(self.name,0) | \ | |
438 | mstpctlFlags.PORT_PROCESSED | |
15ef32ea | 439 | return |
39804250 RP |
440 | if not self._is_bridge(ifaceobj): |
441 | return | |
b97687bc | 442 | # we are now here because the ifaceobj is a bridge |
15ef32ea RP |
443 | stp = None |
444 | try: | |
445 | porterr = False | |
446 | porterrstr = '' | |
447 | if ifaceobj.get_attr_value_first('mstpctl-ports'): | |
448 | # If bridge ports specified with mstpctl attr, create the | |
449 | # bridge and also add its ports | |
450 | self.ipcmd.batch_start() | |
fc5e1735 | 451 | if not ifupdownflags.flags.PERFMODE: |
15ef32ea RP |
452 | if not self.ipcmd.link_exists(ifaceobj.name): |
453 | self.ipcmd.link_create(ifaceobj.name, 'bridge') | |
454 | else: | |
455 | self.ipcmd.link_create(ifaceobj.name, 'bridge') | |
456 | try: | |
457 | self._add_ports(ifaceobj) | |
458 | except Exception, e: | |
459 | porterr = True | |
460 | porterrstr = str(e) | |
461 | pass | |
462 | finally: | |
463 | self.ipcmd.batch_commit() | |
d5c0f703 RP |
464 | running_ports = self.brctlcmd.get_bridge_ports(ifaceobj.name) |
465 | if running_ports: | |
466 | # disable ipv6 for ports that were added to bridge | |
467 | self._ports_enable_disable_ipv6(running_ports, '1') | |
404cc695 | 468 | |
15ef32ea RP |
469 | stp = ifaceobj.get_attr_value_first('mstpctl-stp') |
470 | if stp: | |
471 | self.set_iface_attr(ifaceobj, 'mstpctl-stp', | |
472 | self.brctlcmd.set_stp) | |
473 | else: | |
474 | stp = self.brctlcmd.get_stp(ifaceobj.name) | |
5f8c03e7 | 475 | if (self.mstpd_running and |
15ef32ea RP |
476 | (stp == 'yes' or stp == 'on')): |
477 | self._apply_bridge_settings(ifaceobj) | |
84ca006f RP |
478 | self._apply_bridge_port_settings_all(ifaceobj, |
479 | ifaceobj_getfunc=ifaceobj_getfunc) | |
15ef32ea RP |
480 | except Exception, e: |
481 | self.log_error(str(e)) | |
482 | if porterr: | |
483 | raise Exception(porterrstr) | |
484 | ||
84ca006f RP |
485 | def _down(self, ifaceobj, ifaceobj_getfunc=None): |
486 | if not self._is_bridge(ifaceobj): | |
487 | return | |
15ef32ea RP |
488 | try: |
489 | if ifaceobj.get_attr_value_first('mstpctl-ports'): | |
490 | # If bridge ports specified with mstpctl attr, delete the | |
491 | # bridge | |
492 | ports = self.brctlcmd.get_bridge_ports(ifaceobj.name) | |
493 | if ports: | |
404cc695 | 494 | self._ports_enable_disable_ipv6(ports, '0') |
15ef32ea RP |
495 | self.brctlcmd.delete_bridge(ifaceobj.name) |
496 | except Exception, e: | |
497 | self.log_error(str(e)) | |
498 | ||
499 | def _query_running_attrs(self, ifaceobjrunning): | |
500 | bridgeattrdict = {} | |
501 | ||
502 | tmpbridgeattrdict = self.mstpctlcmd.get_bridge_attrs(ifaceobjrunning.name) | |
503 | if not tmpbridgeattrdict: | |
504 | return bridgeattrdict | |
505 | ||
506 | for k,v in tmpbridgeattrdict.items(): | |
507 | if k == 'stp' or not v: | |
508 | continue | |
509 | if k == 'ports': | |
510 | ports = v.keys() | |
511 | continue | |
512 | attrname = 'mstpctl-' + k | |
513 | if v and v != self.get_mod_subattr(attrname, 'default'): | |
514 | bridgeattrdict[attrname] = [v] | |
515 | ||
516 | ports = self.brctlcmd.get_bridge_ports(ifaceobjrunning.name) | |
517 | if ports: | |
518 | portconfig = {'mstpctl-portnetwork' : '', | |
519 | 'mstpctl-portpathcost' : '', | |
520 | 'mstpctl-portadminedge' : '', | |
521 | 'mstpctl-portautoedge' : '', | |
522 | 'mstpctl-portp2p' : '', | |
523 | 'mstpctl-portrestrrole' : '', | |
524 | 'mstpctl-portrestrtcn' : '', | |
525 | 'mstpctl-bpduguard' : '', | |
526 | 'mstpctl-treeportprio' : '', | |
527 | 'mstpctl-treeportcost' : ''} | |
528 | ||
529 | for p in ports: | |
530 | v = self.mstpctlcmd.get_bridgeport_attr(ifaceobjrunning.name, | |
531 | p, 'portnetwork') | |
532 | if v and v != 'no': | |
533 | portconfig['mstpctl-portnetwork'] += ' %s=%s' %(p, v) | |
534 | ||
535 | # XXX: Can we really get path cost of a port ??? | |
536 | #v = self.mstpctlcmd.get_portpathcost(ifaceobjrunning.name, p) | |
537 | #if v and v != self.get_mod_subattr('mstpctl-portpathcost', | |
538 | # 'default'): | |
539 | # portconfig['mstpctl-portpathcost'] += ' %s=%s' %(p, v) | |
540 | ||
541 | v = self.mstpctlcmd.get_bridgeport_attr(ifaceobjrunning.name, | |
542 | p, 'portadminedge') | |
543 | if v and v != 'no': | |
544 | portconfig['mstpctl-portadminedge'] += ' %s=%s' %(p, v) | |
545 | ||
546 | v = self.mstpctlcmd.get_bridgeport_attr(ifaceobjrunning.name, | |
547 | p, 'portp2p') | |
548 | if v and v != 'no': | |
549 | portconfig['mstpctl-portp2p'] += ' %s=%s' %(p, v) | |
550 | ||
551 | v = self.mstpctlcmd.get_bridgeport_attr(ifaceobjrunning.name, | |
552 | p, 'portrestrrole') | |
553 | if v and v != 'no': | |
554 | portconfig['mstpctl-portrestrrole'] += ' %s=%s' %(p, v) | |
555 | ||
556 | v = self.mstpctlcmd.get_bridgeport_attr(ifaceobjrunning.name, | |
557 | p, 'portrestrtcn') | |
558 | if v and v != 'no': | |
559 | portconfig['mstpctl-portrestrtcn'] += ' %s=%s' %(p, v) | |
560 | ||
561 | v = self.mstpctlcmd.get_bridgeport_attr(ifaceobjrunning.name, | |
562 | p, 'bpduguard') | |
563 | if v and v != 'no': | |
564 | portconfig['mstpctl-bpduguard'] += ' %s=%s' %(p, v) | |
565 | ||
566 | # XXX: Can we really get path cost of a port ??? | |
567 | #v = self.mstpctlcmd.get_bridgeport_attr(ifaceobjrunning.name, | |
568 | # p, 'treeprio') | |
569 | #if v and v != self.get_mod_subattr('mstpctl-treeportprio', | |
570 | # 'default'): | |
571 | # portconfig['mstpctl-treeportprio'] += ' %s=%s' %(p, v) | |
572 | ||
573 | #v = self.mstpctlcmd.get_bridgeport_attr(ifaceobjrunning.name, | |
574 | # p, 'treecost') | |
575 | #if v and v != self.get_mod_subattr('mstpctl-treeportcost', | |
576 | # 'default'): | |
577 | # portconfig['mstpctl-treeportcost'] += ' %s=%s' %(p, v) | |
578 | ||
579 | bridgeattrdict.update({k : [v] for k, v in portconfig.items() | |
580 | if v}) | |
15ef32ea RP |
581 | return bridgeattrdict |
582 | ||
583 | def _query_check_bridge(self, ifaceobj, ifaceobjcurr): | |
584 | # list of attributes that are not supported currently | |
585 | blacklistedattrs = ['mstpctl-portpathcost', | |
586 | 'mstpctl-treeportprio', 'mstpctl-treeportcost'] | |
587 | if not self.brctlcmd.bridge_exists(ifaceobj.name): | |
588 | self.logger.debug('bridge %s does not exist' %ifaceobj.name) | |
15ef32ea RP |
589 | return |
590 | ifaceattrs = self.dict_key_subset(ifaceobj.config, | |
591 | self.get_mod_attrs()) | |
592 | if not ifaceattrs: | |
593 | return | |
594 | runningattrs = self.mstpctlcmd.get_bridge_attrs(ifaceobj.name) | |
595 | if not runningattrs: | |
596 | runningattrs = {} | |
597 | for k in ifaceattrs: | |
598 | # for all mstpctl options | |
599 | if k in blacklistedattrs: | |
600 | continue | |
601 | # get the corresponding ifaceobj attr | |
602 | v = ifaceobj.get_attr_value_first(k) | |
603 | if not v: | |
604 | continue | |
605 | ||
606 | # Get the running attribute | |
607 | rv = runningattrs.get(k[8:]) | |
608 | if k == 'mstpctl-stp': | |
609 | # special case stp compare because it may | |
610 | # contain more than one valid values | |
611 | stp_on_vals = ['on', 'yes'] | |
612 | stp_off_vals = ['off'] | |
613 | rv = self.brctlcmd.get_stp(ifaceobj.name) | |
614 | if ((v in stp_on_vals and rv in stp_on_vals) or | |
615 | (v in stp_off_vals and rv in stp_off_vals)): | |
616 | ifaceobjcurr.update_config_with_status('mstpctl-stp', v, 0) | |
617 | else: | |
618 | ifaceobjcurr.update_config_with_status('mstpctl-stp', v, 1) | |
619 | continue | |
620 | ||
621 | if k == 'mstpctl-ports': | |
622 | # special case ports because it can contain regex or glob | |
623 | # XXX: We get all info from mstputils, which means if | |
624 | # mstpd is down, we will not be returning any bridge bridgeports | |
625 | running_port_list = self.brctlcmd.get_bridge_ports(ifaceobj.name) | |
626 | bridge_port_list = self._get_bridge_port_list(ifaceobj) | |
627 | if not running_port_list and not bridge_port_list: | |
628 | continue | |
629 | portliststatus = 1 | |
630 | if running_port_list and bridge_port_list: | |
631 | difference = Set(running_port_list).symmetric_difference( | |
632 | Set(bridge_port_list)) | |
633 | if not difference: | |
634 | portliststatus = 0 | |
635 | ifaceobjcurr.update_config_with_status('mstpctl-ports', | |
636 | ' '.join(running_port_list) | |
637 | if running_port_list else '', portliststatus) | |
638 | elif k[:12] == 'mstpctl-port' or k == 'mstpctl-bpduguard': | |
639 | # Now, look at port attributes | |
640 | # derive the mstpctlcmd attr name | |
641 | #mstpctlcmdattrname = k[12:] if k[:12] == 'mstpctl-port' else k[8:] | |
642 | mstpctlcmdattrname = k[8:] | |
643 | ||
644 | # for port attributes, the attributes are in a list | |
645 | # <portname>=<portattrvalue> | |
646 | status = 0 | |
647 | currstr = '' | |
0c8332bc | 648 | vlist = self.parse_port_list(ifaceobj.name, v) |
15ef32ea RP |
649 | if not vlist: |
650 | continue | |
651 | for vlistitem in vlist: | |
652 | try: | |
653 | (p, v) = vlistitem.split('=') | |
654 | currv = self.mstpctlcmd.get_bridgeport_attr( | |
655 | ifaceobj.name, p, mstpctlcmdattrname) | |
656 | if currv: | |
657 | currstr += ' %s=%s' %(p, currv) | |
658 | else: | |
659 | currstr += ' %s=%s' %(p, 'None') | |
660 | if currv != v: | |
661 | status = 1 | |
662 | except Exception, e: | |
663 | self.log_warn(str(e)) | |
664 | pass | |
665 | ifaceobjcurr.update_config_with_status(k, currstr, status) | |
666 | elif not rv: | |
667 | ifaceobjcurr.update_config_with_status(k, '', 1) | |
668 | elif v != rv: | |
669 | ifaceobjcurr.update_config_with_status(k, rv, 1) | |
670 | else: | |
671 | ifaceobjcurr.update_config_with_status(k, rv, 0) | |
672 | ||
84ca006f | 673 | def _query_check_bridge_port(self, ifaceobj, ifaceobjcurr): |
19f90a91 | 674 | if not self.ipcmd.link_exists(ifaceobj.name): |
3e6ea735 | 675 | #self.logger.debug('bridge port %s does not exist' %ifaceobj.name) |
15ef32ea RP |
676 | ifaceobjcurr.status = ifaceStatus.NOTFOUND |
677 | return | |
e1601369 | 678 | # Check if this is a bridge port |
19f90a91 | 679 | if not self._is_bridge_port(ifaceobj): |
e1601369 | 680 | # mark all the bridge attributes as error |
a070c90e | 681 | ifaceobjcurr.check_n_update_config_with_status_many(ifaceobj, |
e1601369 RP |
682 | self._port_attrs_map.keys(), 0) |
683 | return | |
98b5ee73 | 684 | bridgename = self.ipcmd.bridge_port_get_bridge_name(ifaceobj.name) |
e1601369 | 685 | # list of attributes that are not supported currently |
16d854b4 RP |
686 | blacklistedattrs = ['mstpctl-portpathcost', |
687 | 'mstpctl-treeportprio', 'mstpctl-treeportcost'] | |
15ef32ea RP |
688 | ifaceattrs = self.dict_key_subset(ifaceobj.config, |
689 | self._port_attrs_map.keys()) | |
690 | if not ifaceattrs: | |
691 | return | |
692 | runningattrs = self.mstpctlcmd.get_bridge_attrs(ifaceobj.name) | |
693 | if not runningattrs: | |
694 | runningattrs = {} | |
695 | for k in ifaceattrs: | |
696 | # for all mstpctl options | |
15ef32ea RP |
697 | # get the corresponding ifaceobj attr |
698 | v = ifaceobj.get_attr_value_first(k) | |
16d854b4 | 699 | if not v or k in blacklistedattrs: |
e1601369 | 700 | ifaceobjcurr.update_config_with_status(k, v, -1) |
15ef32ea | 701 | continue |
e1601369 | 702 | currv = self.mstpctlcmd.get_bridgeport_attr(bridgename, |
15ef32ea RP |
703 | ifaceobj.name, self._port_attrs_map.get(k)) |
704 | if currv: | |
705 | if currv != v: | |
706 | ifaceobjcurr.update_config_with_status(k, currv, 1) | |
707 | else: | |
708 | ifaceobjcurr.update_config_with_status(k, currv, 0) | |
709 | else: | |
710 | ifaceobjcurr.update_config_with_status(k, None, 1) | |
711 | ||
84ca006f | 712 | def _query_check(self, ifaceobj, ifaceobjcurr, ifaceobj_getfunc=None): |
e1601369 | 713 | if self._is_bridge(ifaceobj): |
84ca006f | 714 | self._query_check_bridge(ifaceobj, ifaceobjcurr) |
e1601369 RP |
715 | else: |
716 | self._query_check_bridge_port(ifaceobj, ifaceobjcurr) | |
15ef32ea | 717 | |
e1601369 RP |
718 | def _query_running_bridge_port(self, ifaceobjrunning): |
719 | bridgename = self.ipcmd.bridge_port_get_bridge_name( | |
720 | ifaceobjrunning.name) | |
721 | if not bridgename: | |
722 | self.logger.warn('%s: unable to determine bridgename' | |
723 | %ifaceobjrunning.name) | |
15ef32ea | 724 | return |
e1601369 RP |
725 | if self.brctlcmd.get_stp(bridgename) == 'no': |
726 | # This bridge does not run stp, return | |
727 | return | |
15ef32ea RP |
728 | # if userspace stp not set, return |
729 | if self.sysctl_get('net.bridge.bridge-stp-user-space') != '1': | |
e1601369 RP |
730 | return |
731 | v = self.mstpctlcmd.get_bridgeport_attr(bridgename, | |
732 | ifaceobjrunning.name, | |
733 | 'portnetwork') | |
734 | if v and v != 'no': | |
735 | ifaceobjrunning.update_config('mstpctl-network', v) | |
736 | ||
737 | # XXX: Can we really get path cost of a port ??? | |
738 | #v = self.mstpctlcmd.get_portpathcost(ifaceobjrunning.name, p) | |
739 | #if v and v != self.get_mod_subattr('mstpctl-pathcost', | |
740 | # 'default'): | |
741 | # ifaceobjrunning.update_config('mstpctl-network', v) | |
742 | ||
743 | v = self.mstpctlcmd.get_bridgeport_attr(bridgename, | |
744 | ifaceobjrunning.name, 'portadminedge') | |
745 | if v and v != 'no': | |
7adb4b77 | 746 | ifaceobjrunning.update_config('mstpctl-portadminedge', v) |
e1601369 RP |
747 | |
748 | v = self.mstpctlcmd.get_bridgeport_attr(bridgename, | |
749 | ifaceobjrunning.name,'portp2p') | |
e759a20a | 750 | if v and v != 'auto': |
7adb4b77 | 751 | ifaceobjrunning.update_config('mstpctl-portp2p', v) |
e1601369 RP |
752 | |
753 | v = self.mstpctlcmd.get_bridgeport_attr(bridgename, | |
754 | ifaceobjrunning.name, 'portrestrrole') | |
755 | if v and v != 'no': | |
7adb4b77 | 756 | ifaceobjrunning.update_config('mstpctl-portrestrrole', v) |
e1601369 RP |
757 | |
758 | v = self.mstpctlcmd.get_bridgeport_attr(bridgename, | |
759 | ifaceobjrunning.name, 'restrtcn') | |
760 | if v and v != 'no': | |
7adb4b77 | 761 | ifaceobjrunning.update_config('mstpctl-portrestrtcn', v) |
e1601369 RP |
762 | |
763 | v = self.mstpctlcmd.get_bridgeport_attr(bridgename, | |
764 | ifaceobjrunning.name, 'bpduguard') | |
765 | if v and v != 'no': | |
766 | ifaceobjrunning.update_config('mstpctl-bpduguard', v) | |
767 | ||
768 | # XXX: Can we really get path cost of a port ??? | |
769 | #v = self.mstpctlcmd.get_bridgeport_attr(ifaceobjrunning.name, | |
770 | # p, 'treeprio') | |
771 | #if v and v != self.get_mod_subattr('mstpctl-treeportprio', | |
772 | # 'default'): | |
773 | # portconfig['mstpctl-treeportprio'] += ' %s=%s' %(p, v) | |
774 | ||
775 | #v = self.mstpctlcmd.get_bridgeport_attr(ifaceobjrunning.name, | |
776 | # p, 'treecost') | |
777 | #if v and v != self.get_mod_subattr('mstpctl-treeportcost', | |
778 | # 'default'): | |
779 | # portconfig['mstpctl-treeportcost'] += ' %s=%s' %(p, v) | |
780 | ||
781 | def _query_running_bridge(self, ifaceobjrunning): | |
782 | if self.brctlcmd.get_stp(ifaceobjrunning.name) == 'no': | |
783 | # This bridge does not run stp, return | |
784 | return | |
785 | # if userspace stp not set, return | |
786 | if self.sysctl_get('net.bridge.bridge-stp-user-space') != '1': | |
787 | return | |
15ef32ea RP |
788 | # Check if mstp really knows about this bridge |
789 | if not self.mstpctlcmd.mstpbridge_exists(ifaceobjrunning.name): | |
790 | return | |
791 | ifaceobjrunning.update_config_dict(self._query_running_attrs( | |
792 | ifaceobjrunning)) | |
793 | ||
e1601369 RP |
794 | def _query_running(self, ifaceobjrunning, **extra_args): |
795 | if self.brctlcmd.bridge_exists(ifaceobjrunning.name): | |
796 | self._query_running_bridge(ifaceobjrunning) | |
797 | elif self.brctlcmd.is_bridge_port(ifaceobjrunning.name): | |
798 | self._query_running_bridge_port(ifaceobjrunning) | |
799 | ||
15ef32ea RP |
800 | _run_ops = {'pre-up' : _up, |
801 | 'post-down' : _down, | |
802 | 'query-checkcurr' : _query_check, | |
803 | 'query-running' : _query_running} | |
804 | ||
805 | def get_ops(self): | |
806 | """ returns list of ops supported by this module """ | |
807 | return self._run_ops.keys() | |
808 | ||
809 | def _init_command_handlers(self): | |
15ef32ea | 810 | if not self.ipcmd: |
fc5e1735 | 811 | self.ipcmd = iproute2() |
15ef32ea | 812 | if not self.brctlcmd: |
fc5e1735 | 813 | self.brctlcmd = brctl() |
15ef32ea | 814 | if not self.mstpctlcmd: |
fc5e1735 | 815 | self.mstpctlcmd = mstpctlutil() |
15ef32ea | 816 | |
84ca006f RP |
817 | def run(self, ifaceobj, operation, query_ifaceobj=None, |
818 | ifaceobj_getfunc=None, **extra_args): | |
15ef32ea RP |
819 | """ run mstp configuration on the interface object passed as argument |
820 | ||
821 | Args: | |
822 | **ifaceobj** (object): iface object | |
823 | ||
824 | **operation** (str): any of 'pre-up', 'post-down', 'query-checkcurr', | |
825 | 'query-running' | |
826 | Kwargs: | |
827 | **query_ifaceobj** (object): query check ifaceobject. This is only | |
828 | valid when op is 'query-checkcurr'. It is an object same as | |
829 | ifaceobj, but contains running attribute values and its config | |
830 | status. The modules can use it to return queried running state | |
831 | of interfaces. status is success if the running state is same | |
832 | as user required state in ifaceobj. error otherwise. | |
833 | """ | |
3e6ea735 RP |
834 | if ifaceobj.type == ifaceType.BRIDGE_VLAN: |
835 | return | |
15ef32ea RP |
836 | op_handler = self._run_ops.get(operation) |
837 | if not op_handler: | |
838 | return | |
15ef32ea RP |
839 | self._init_command_handlers() |
840 | if operation == 'query-checkcurr': | |
84ca006f RP |
841 | op_handler(self, ifaceobj, query_ifaceobj, |
842 | ifaceobj_getfunc=ifaceobj_getfunc) | |
15ef32ea | 843 | else: |
84ca006f | 844 | op_handler(self, ifaceobj, ifaceobj_getfunc=ifaceobj_getfunc) |