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