]>
git.proxmox.com Git - mirror_ifupdown2.git/blob - addons/mstpctl.py
3 # Copyright 2014 Cumulus Networks, Inc. All rights reserved.
4 # Author: Roopa Prabhu, roopa@cumulusnetworks.com
8 from ifupdown
.iface
import *
9 from ifupdownaddons
.modulebase
import moduleBase
10 from ifupdownaddons
.bridgeutils
import brctl
11 from ifupdownaddons
.iproute2
import iproute2
12 from ifupdownaddons
.mstpctlutil
import mstpctlutil
15 class mstpctl(moduleBase
):
16 """ ifupdown2 addon module to configure mstp attributes """
18 _modinfo
= {'mhelp' : 'mstp configuration module for bridges',
21 {'help' : 'mstp ports',
24 {'help': 'bridge stp yes/no',
28 {'help': 'tree priority',
30 'validrange' : ['0', '65535'],
32 'example' : ['mstpctl-treeprio 32768']},
34 {'help': 'ageing time',
37 'example' : ['mstpctl-ageing 300']},
39 { 'help' : 'max message age',
42 'example' : ['mstpctl-maxage 20']},
44 { 'help' : 'set forwarding delay',
47 'example' : ['mstpctl-fdelay 15']},
49 { 'help' : 'bridge max hops',
52 'example' : ['mstpctl-maxhops 15']},
53 'mstpctl-txholdcount' :
54 { 'help' : 'bridge transmit holdcount',
57 'example' : ['mstpctl-txholdcount 6']},
59 { 'help' : 'bridge force stp version',
62 'example' : ['mstpctl-forcevers rstp']},
63 'mstpctl-portpathcost' :
64 { 'help' : 'bridge port path cost',
67 'example' : ['mstpctl-portpathcost swp1=0 swp2=1']},
68 'mstpctl-portadminage' :
69 { 'help' : 'bridge port admin age',
71 'validvals' : ['yes', 'no'],
73 'example' : ['mstpctl-portadminage swp1=no swp2=no']},
75 { 'help' : 'bridge port p2p detection mode',
77 'validvals' : ['yes', 'no'],
79 'example' : ['mstpctl-portp2p swp1=no swp2=no']},
80 'mstpctl-portrestrrole' :
82 'enable/disable port ability to take root role of the port',
84 'validvals' : ['yes', 'no'],
86 'example' : ['mstpctl-portrestrrole swp1=no swp2=no']},
87 'mstpctl-portrestrtcn' :
89 'enable/disable port ability to propagate received topology change notification of the port',
91 'validvals' : ['yes', 'no'],
93 'example' : ['mstpctl-portrestrtcn swp1=no swp2=no']},
96 'enable/disable bpduguard',
98 'validvals' : ['yes', 'no'],
100 'example' : ['mstpctl-bpduguard swp1=no swp2=no']},
101 'mstpctl-treeportprio' :
103 'port priority for MSTI instance',
105 'validrange' : ['0', '240'],
107 'example' : ['mstpctl-treeportprio swp1=128 swp2=128']},
109 { 'help' : 'set hello time',
112 'example' : ['mstpctl-hello 2']},
113 'mstpctl-portnetwork' :
114 { 'help' : 'enable/disable bridge assurance capability for a port',
115 'validvals' : ['yes', 'no'],
118 'example' : ['mstpctl-portnetwork swp1=no swp2=no']},
119 'mstpctl-portadminedge' :
120 { 'help' : 'enable/disable initial edge state of the port',
121 'validvals' : ['yes', 'no'],
124 'example' : ['mstpctl-portadminedge swp1=no swp2=no']},
125 'mstpctl-portautoedge' :
126 { 'help' : 'enable/disable auto transition to/from edge state of the port',
127 'validvals' : ['yes', 'no'],
130 'example' : ['mstpctl-portautoedge swp1=yes swp2=yes']},
131 'mstpctl-treeportcost' :
132 { 'help' : 'port tree cost',
134 'mstpctl-portbpdufilter' :
135 { 'help' : 'enable/disable bpdu filter on a port',
136 'validvals' : ['yes', 'no'],
139 'example' : ['mstpctl-portbpdufilter swp1=no swp2=no']},
141 { 'help' : 'port path cost',
144 'example' : ['mstpctl-pathcost 1']},
146 { 'help' : 'bridge port admin age',
148 'validvals' : ['yes', 'no'],
150 'example' : ['mstpctl-adminage no']},
152 { 'help' : 'bridge port p2p detection mode',
154 'validvals' : ['yes', 'no'],
156 'example' : ['mstpctl-p2p yes']},
157 'mstpctl-restrrole' :
159 'enable/disable port ability to take root role of the port',
161 'validvals' : ['yes', 'no'],
163 'example' : ['mstpctl-restrrole yes']},
166 'enable/disable port ability to propagate received topology change notification of the port',
168 'validvals' : ['yes', 'no'],
170 'example' : ['mstpctl-restrtcn yes']},
173 'port priority for MSTI instance',
175 'validrange' : ['0', '240'],
177 'example' : ['mstpctl-treeprio 128']},
179 { 'help' : 'enable/disable bridge assurance capability for a port',
180 'validvals' : ['yes', 'no'],
183 'example' : ['mstpctl-network no']},
184 'mstpctl-adminedge' :
185 { 'help' : 'enable/disable initial edge state of the port',
186 'validvals' : ['yes', 'no'],
189 'example' : ['mstpctl-adminedge no']},
191 { 'help' : 'enable/disable auto transition to/from edge state of the port',
192 'validvals' : ['yes', 'no'],
195 'example' : ['mstpctl-autoedge yes']},
197 { 'help' : 'port tree cost',
199 'mstpctl-bpdufilter' :
200 { 'help' : 'enable/disable bpdu filter on a port',
201 'validvals' : ['yes', 'no'],
204 'example' : ['mstpctl-bpdufilter yes']},
207 _port_attrs_map
= {'mstpctl-pathcost' : 'portpathcost',
208 'mstpctl-adminedge' : 'portadminedge',
209 'mstpctl-p2p' : 'portp2p',
210 'mstpctl-restrrole' : 'portrestrrole',
211 'mstpctl-restrtcn' : 'portrestrtcn',
212 'mstpctl-bpduguard' : 'bpduguard',
213 'mstpctl-treeprio' : 'treeportprio',
214 'mstpctl-treecost' : 'treeportcost',
215 'mstpctl-network' : 'portnetwork',
216 'mstpctl-bpdufilter' : 'portbpdufilter'}
218 def __init__(self
, *args
, **kargs
):
219 moduleBase
.__init
__(self
, *args
, **kargs
)
222 self
.mstpctlcmd
= None
224 def _is_bridge(self
, ifaceobj
):
225 if (ifaceobj
.get_attr_value_first('mstpctl-ports') or
226 ifaceobj
.get_attr_value_first('bridge-ports')):
230 def get_dependent_ifacenames(self
, ifaceobj
, ifacenames_all
=None):
231 if not self
._is
_bridge
(ifaceobj
):
233 return self
.parse_port_list(ifaceobj
.get_attr_value_first(
234 'mstpctl-ports'), ifacenames_all
)
236 def get_dependent_ifacenames_running(self
, ifaceobj
):
237 self
._init
_command
_handlers
()
238 if (self
.brctlcmd
.bridge_exists(ifaceobj
.name
) and
239 not self
.mstpctlcmd
.mstpbridge_exists(ifaceobj
.name
)):
241 return self
.brctlcmd
.get_bridge_ports(ifaceobj
.name
)
243 def _get_bridge_port_list(self
, ifaceobj
):
245 # port list is also available in the previously
246 # parsed dependent list. Use that if available, instead
247 # of parsing port expr again
248 port_list
= ifaceobj
.lowerifaces
251 ports
= ifaceobj
.get_attr_value_first('mstpctl-ports')
253 return self
.parse_port_list(ports
)
257 def _add_ports(self
, ifaceobj
):
258 bridgeports
= self
._get
_bridge
_port
_list
(ifaceobj
)
260 runningbridgeports
= []
261 # Delete active ports not in the new port list
262 if not self
.PERFMODE
:
263 runningbridgeports
= self
.brctlcmd
.get_bridge_ports(ifaceobj
.name
)
264 if runningbridgeports
:
265 [self
.ipcmd
.link_set(bport
, 'nomaster')
266 for bport
in runningbridgeports
267 if not bridgeports
or bport
not in bridgeports
]
269 runningbridgeports
= []
273 for bridgeport
in Set(bridgeports
).difference(Set(runningbridgeports
)):
275 if not self
.DRYRUN
and not self
.ipcmd
.link_exists(bridgeport
):
276 self
.log_warn('%s: bridge port %s does not exist'
277 %(ifaceobj
.name
, bridgeport
))
280 self
.ipcmd
.link_set(bridgeport
, 'master', ifaceobj
.name
)
281 self
.write_file('/proc/sys/net/ipv6/conf/%s' %bridgeport
+
282 '/disable_ipv6', '1')
283 self
.ipcmd
.addr_flush(bridgeport
)
285 self
.log_error(str(e
))
287 self
.log_error('error configuring bridge (missing ports)')
289 def _apply_bridge_settings(self
, ifaceobj
):
290 check
= False if self
.PERFMODE
else True
292 bridgeattrs
= {k
:v
for k
,v
in
294 ifaceobj
.get_attr_value_first('mstpctl-treeprio'),
296 ifaceobj
.get_attr_value_first('mstpctl-ageing'),
298 ifaceobj
.get_attr_value_first('mstpctl-maxage'),
300 ifaceobj
.get_attr_value_first('mstpctl-fdelay'),
302 ifaceobj
.get_attr_value_first('mstpctl-maxhops'),
304 ifaceobj
.get_attr_value_first('mstpctl-txholdcount'),
306 ifaceobj
.get_attr_value_first('mstpctl-forcevers'),
308 ifaceobj
.get_attr_value_first('mstpctl-hello')
312 # set bridge attributes
313 for k
,v
in bridgeattrs
.items():
318 self
.mstpctlcmd
.set_bridge_attr(ifaceobj
.name
, k
,
321 self
.logger
.warn('%s' %str
(e
))
323 if bridgeattrs
.get('treeprio'):
325 self
.mstpctlcmd
.set_bridge_treeprio(ifaceobj
.name
,
326 bridgeattrs
['treeprio'], check
)
328 self
.logger
.warn('%s' %str
(e
))
331 # set bridge port attributes
332 for attrname
in ['mstpctl-portpathcost', 'mstpctl-portadminedge',
333 'mstpctl-portp2p', 'mstpctl-portrestrrole',
334 'mstpctl-portrestrtcn', 'mstpctl-bpduguard',
335 'mstpctl-treeportprio', 'mstpctl-treeportcost',
336 'mstpctl-portnetwork', 'mstpctl-portbpdufilter']:
337 attrval
= ifaceobj
.get_attr_value_first(attrname
)
340 dstattrname
= attrname
.split('-')[1]
341 portlist
= self
.parse_port_list(attrval
)
343 self
.log_warn('%s: error parsing \'%s %s\''
344 %(ifaceobj
.name
, attrname
, attrval
))
348 (port
, val
) = p
.split('=')
349 self
.mstpctlcmd
.set_bridgeport_attr(ifaceobj
.name
,
350 port
, dstattrname
, val
, check
)
352 self
.log_warn('%s: error setting %s (%s)'
353 %(ifaceobj
.name
, attrname
, str(e
)))
355 self
.log_warn(str(e
))
358 def _apply_bridge_port_settings(self
, ifaceobj
, bridge
):
359 check
= False if self
.PERFMODE
else True
361 # set bridge port attributes
362 for attrname
, dstattrname
in self
._port
_attrs
_map
.items():
363 attrval
= ifaceobj
.get_attr_value_first(attrname
)
367 self
.mstpctlcmd
.set_bridgeport_attr(bridge
,
368 ifaceobj
.name
, dstattrname
, attrval
, check
)
370 self
.log_warn('%s: error setting %s (%s)'
371 %(ifaceobj
.name
, attrname
, str(e
)))
373 self
.log_warn(str(e
))
376 def _up(self
, ifaceobj
):
377 # Check if bridge port
378 bridge
= ifaceobj
.get_attr_value_first('bridge')
380 if self
.mstpctlcmd
.is_mstpd_running():
381 self
._apply
_bridge
_port
_settings
(ifaceobj
, bridge
)
388 if ifaceobj
.get_attr_value_first('mstpctl-ports'):
389 # If bridge ports specified with mstpctl attr, create the
390 # bridge and also add its ports
391 self
.ipcmd
.batch_start()
392 if not self
.PERFMODE
:
393 if not self
.ipcmd
.link_exists(ifaceobj
.name
):
394 self
.ipcmd
.link_create(ifaceobj
.name
, 'bridge')
396 self
.ipcmd
.link_create(ifaceobj
.name
, 'bridge')
398 self
._add
_ports
(ifaceobj
)
404 self
.ipcmd
.batch_commit()
405 stp
= ifaceobj
.get_attr_value_first('mstpctl-stp')
407 self
.set_iface_attr(ifaceobj
, 'mstpctl-stp',
408 self
.brctlcmd
.set_stp
)
410 stp
= self
.brctlcmd
.get_stp(ifaceobj
.name
)
411 if (self
.mstpctlcmd
.is_mstpd_running() and
412 (stp
== 'yes' or stp
== 'on')):
413 self
._apply
_bridge
_settings
(ifaceobj
)
415 self
.log_error(str(e
))
417 raise Exception(porterrstr
)
419 def _down(self
, ifaceobj
):
421 if ifaceobj
.get_attr_value_first('mstpctl-ports'):
422 # If bridge ports specified with mstpctl attr, delete the
424 ports
= self
.brctlcmd
.get_bridge_ports(ifaceobj
.name
)
427 proc_file
= ('/proc/sys/net/ipv6/conf/%s' %p
+
429 self
.write_file(proc_file
, '0')
430 self
.brctlcmd
.delete_bridge(ifaceobj
.name
)
432 self
.log_error(str(e
))
434 def _query_running_attrs(self
, ifaceobjrunning
):
437 tmpbridgeattrdict
= self
.mstpctlcmd
.get_bridge_attrs(ifaceobjrunning
.name
)
438 if not tmpbridgeattrdict
:
439 return bridgeattrdict
441 for k
,v
in tmpbridgeattrdict
.items():
442 if k
== 'stp' or not v
:
447 attrname
= 'mstpctl-' + k
448 if v
and v
!= self
.get_mod_subattr(attrname
, 'default'):
449 bridgeattrdict
[attrname
] = [v
]
451 ports
= self
.brctlcmd
.get_bridge_ports(ifaceobjrunning
.name
)
453 portconfig
= {'mstpctl-portnetwork' : '',
454 'mstpctl-portpathcost' : '',
455 'mstpctl-portadminedge' : '',
456 'mstpctl-portautoedge' : '',
457 'mstpctl-portp2p' : '',
458 'mstpctl-portrestrrole' : '',
459 'mstpctl-portrestrtcn' : '',
460 'mstpctl-bpduguard' : '',
461 'mstpctl-treeportprio' : '',
462 'mstpctl-treeportcost' : ''}
465 v
= self
.mstpctlcmd
.get_bridgeport_attr(ifaceobjrunning
.name
,
468 portconfig
['mstpctl-portnetwork'] += ' %s=%s' %(p
, v
)
470 # XXX: Can we really get path cost of a port ???
471 #v = self.mstpctlcmd.get_portpathcost(ifaceobjrunning.name, p)
472 #if v and v != self.get_mod_subattr('mstpctl-portpathcost',
474 # portconfig['mstpctl-portpathcost'] += ' %s=%s' %(p, v)
476 v
= self
.mstpctlcmd
.get_bridgeport_attr(ifaceobjrunning
.name
,
479 portconfig
['mstpctl-portadminedge'] += ' %s=%s' %(p
, v
)
481 v
= self
.mstpctlcmd
.get_bridgeport_attr(ifaceobjrunning
.name
,
484 portconfig
['mstpctl-portp2p'] += ' %s=%s' %(p
, v
)
486 v
= self
.mstpctlcmd
.get_bridgeport_attr(ifaceobjrunning
.name
,
489 portconfig
['mstpctl-portrestrrole'] += ' %s=%s' %(p
, v
)
491 v
= self
.mstpctlcmd
.get_bridgeport_attr(ifaceobjrunning
.name
,
494 portconfig
['mstpctl-portrestrtcn'] += ' %s=%s' %(p
, v
)
496 v
= self
.mstpctlcmd
.get_bridgeport_attr(ifaceobjrunning
.name
,
499 portconfig
['mstpctl-bpduguard'] += ' %s=%s' %(p
, v
)
501 # XXX: Can we really get path cost of a port ???
502 #v = self.mstpctlcmd.get_bridgeport_attr(ifaceobjrunning.name,
504 #if v and v != self.get_mod_subattr('mstpctl-treeportprio',
506 # portconfig['mstpctl-treeportprio'] += ' %s=%s' %(p, v)
508 #v = self.mstpctlcmd.get_bridgeport_attr(ifaceobjrunning.name,
510 #if v and v != self.get_mod_subattr('mstpctl-treeportcost',
512 # portconfig['mstpctl-treeportcost'] += ' %s=%s' %(p, v)
514 bridgeattrdict
.update({k
: [v
] for k
, v
in portconfig
.items()
516 self
.logger
.debug(bridgeattrdict
)
517 return bridgeattrdict
519 def _query_check_bridge(self
, ifaceobj
, ifaceobjcurr
):
520 # list of attributes that are not supported currently
521 blacklistedattrs
= ['mstpctl-portpathcost',
522 'mstpctl-treeportprio', 'mstpctl-treeportcost']
523 if not self
.brctlcmd
.bridge_exists(ifaceobj
.name
):
524 self
.logger
.debug('bridge %s does not exist' %ifaceobj
.name
)
525 ifaceobjcurr
.status
= ifaceStatus
.NOTFOUND
527 ifaceattrs
= self
.dict_key_subset(ifaceobj
.config
,
528 self
.get_mod_attrs())
531 runningattrs
= self
.mstpctlcmd
.get_bridge_attrs(ifaceobj
.name
)
535 # for all mstpctl options
536 if k
in blacklistedattrs
:
538 # get the corresponding ifaceobj attr
539 v
= ifaceobj
.get_attr_value_first(k
)
543 # Get the running attribute
544 rv
= runningattrs
.get(k
[8:])
545 if k
== 'mstpctl-stp':
546 # special case stp compare because it may
547 # contain more than one valid values
548 stp_on_vals
= ['on', 'yes']
549 stp_off_vals
= ['off']
550 rv
= self
.brctlcmd
.get_stp(ifaceobj
.name
)
551 if ((v
in stp_on_vals
and rv
in stp_on_vals
) or
552 (v
in stp_off_vals
and rv
in stp_off_vals
)):
553 ifaceobjcurr
.update_config_with_status('mstpctl-stp', v
, 0)
555 ifaceobjcurr
.update_config_with_status('mstpctl-stp', v
, 1)
558 if k
== 'mstpctl-ports':
559 # special case ports because it can contain regex or glob
560 # XXX: We get all info from mstputils, which means if
561 # mstpd is down, we will not be returning any bridge bridgeports
562 running_port_list
= self
.brctlcmd
.get_bridge_ports(ifaceobj
.name
)
563 bridge_port_list
= self
._get
_bridge
_port
_list
(ifaceobj
)
564 if not running_port_list
and not bridge_port_list
:
567 if running_port_list
and bridge_port_list
:
568 difference
= Set(running_port_list
).symmetric_difference(
569 Set(bridge_port_list
))
572 ifaceobjcurr
.update_config_with_status('mstpctl-ports',
573 ' '.join(running_port_list
)
574 if running_port_list
else '', portliststatus
)
575 elif k
[:12] == 'mstpctl-port' or k
== 'mstpctl-bpduguard':
576 # Now, look at port attributes
577 # derive the mstpctlcmd attr name
578 #mstpctlcmdattrname = k[12:] if k[:12] == 'mstpctl-port' else k[8:]
579 mstpctlcmdattrname
= k
[8:]
581 # for port attributes, the attributes are in a list
582 # <portname>=<portattrvalue>
585 vlist
= self
.parse_port_list(v
)
588 for vlistitem
in vlist
:
590 (p
, v
) = vlistitem
.split('=')
591 currv
= self
.mstpctlcmd
.get_bridgeport_attr(
592 ifaceobj
.name
, p
, mstpctlcmdattrname
)
594 currstr
+= ' %s=%s' %(p
, currv
)
596 currstr
+= ' %s=%s' %(p
, 'None')
600 self
.log_warn(str(e
))
602 ifaceobjcurr
.update_config_with_status(k
, currstr
, status
)
604 ifaceobjcurr
.update_config_with_status(k
, '', 1)
606 ifaceobjcurr
.update_config_with_status(k
, rv
, 1)
608 ifaceobjcurr
.update_config_with_status(k
, rv
, 0)
610 def _query_check_bridge_port(self
, ifaceobj
, ifaceobjcurr
, bridge
):
611 # list of attributes that are not supported currently
612 blacklistedattrs
= ['mstpctl-pathcost',
613 'mstpctl-treeprio', 'mstpctl-treecost']
614 if not self
.ipcmd
.link_exists():
615 self
.logger
.debug('bridge port %s does not exist' %ifaceobj
.name
)
616 ifaceobjcurr
.status
= ifaceStatus
.NOTFOUND
618 ifaceattrs
= self
.dict_key_subset(ifaceobj
.config
,
619 self
._port
_attrs
_map
.keys())
622 runningattrs
= self
.mstpctlcmd
.get_bridge_attrs(ifaceobj
.name
)
626 # for all mstpctl options
627 if k
in blacklistedattrs
:
629 # get the corresponding ifaceobj attr
630 v
= ifaceobj
.get_attr_value_first(k
)
634 currv
= self
.mstpctlcmd
.get_bridgeport_attr(bridge
,
635 ifaceobj
.name
, self
._port
_attrs
_map
.get(k
))
638 ifaceobjcurr
.update_config_with_status(k
, currv
, 1)
640 ifaceobjcurr
.update_config_with_status(k
, currv
, 0)
642 ifaceobjcurr
.update_config_with_status(k
, None, 1)
644 def _query_check(self
, ifaceobj
, ifaceobjcurr
):
645 # Check if bridge port
646 bridge
= ifaceobj
.get_attr_value_first('bridge')
648 self
._query
_check
_bridge
_port
(ifaceobj
, ifaceobjcurr
, bridge
)
650 self
._query
_check
_bridge
(ifaceobj
, ifaceobjcurr
)
652 def _query_running(self
, ifaceobjrunning
):
653 if not self
.brctlcmd
.bridge_exists(ifaceobjrunning
.name
):
655 if self
.brctlcmd
.get_stp(ifaceobjrunning
.name
) == 'no':
656 # This bridge does not run stp, return
658 # if userspace stp not set, return
659 if self
.sysctl_get('net.bridge.bridge-stp-user-space') != '1':
661 # Check if mstp really knows about this bridge
662 if not self
.mstpctlcmd
.mstpbridge_exists(ifaceobjrunning
.name
):
664 ifaceobjrunning
.update_config_dict(self
._query
_running
_attrs
(
667 _run_ops
= {'pre-up' : _up
,
669 'query-checkcurr' : _query_check
,
670 'query-running' : _query_running
}
673 """ returns list of ops supported by this module """
674 return self
._run
_ops
.keys()
676 def _init_command_handlers(self
):
677 flags
= self
.get_flags()
679 self
.ipcmd
= iproute2(**flags
)
680 if not self
.brctlcmd
:
681 self
.brctlcmd
= brctl(**flags
)
682 if not self
.mstpctlcmd
:
683 self
.mstpctlcmd
= mstpctlutil(**flags
)
685 def run(self
, ifaceobj
, operation
, query_ifaceobj
=None):
686 """ run mstp configuration on the interface object passed as argument
689 **ifaceobj** (object): iface object
691 **operation** (str): any of 'pre-up', 'post-down', 'query-checkcurr',
694 **query_ifaceobj** (object): query check ifaceobject. This is only
695 valid when op is 'query-checkcurr'. It is an object same as
696 ifaceobj, but contains running attribute values and its config
697 status. The modules can use it to return queried running state
698 of interfaces. status is success if the running state is same
699 as user required state in ifaceobj. error otherwise.
701 op_handler
= self
._run
_ops
.get(operation
)
704 if operation
!= 'query-running' and not self
._is
_bridge
(ifaceobj
):
706 self
._init
_command
_handlers
()
707 if operation
== 'query-checkcurr':
708 op_handler(self
, ifaceobj
, query_ifaceobj
)
710 op_handler(self
, ifaceobj
)