]> git.proxmox.com Git - mirror_ifupdown2.git/blame - addons/mstpctl.py
ifupdownaddons: iproute2: fix set_mtu api to return if --no-act (DRYRUN) is set
[mirror_ifupdown2.git] / addons / mstpctl.py
CommitLineData
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 7import os
15ef32ea
RP
8from sets import Set
9from ifupdown.iface import *
10from ifupdownaddons.modulebase import moduleBase
11from ifupdownaddons.bridgeutils import brctl
12from ifupdownaddons.iproute2 import iproute2
13from ifupdownaddons.mstpctlutil import mstpctlutil
5f8c03e7 14from ifupdownaddons.systemutils import systemUtils
fc5e1735 15import ifupdown.ifupdownflags as ifupdownflags
15ef32ea 16
4c773918
ST
17class mstpctlFlags:
18 PORT_PROCESSED = 0x1
19
15ef32ea
RP
20class 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)