]> git.proxmox.com Git - mirror_ifupdown2.git/blob - addons/bridge.py
Fix bridge port vid settings during ifup of a bridge port
[mirror_ifupdown2.git] / addons / bridge.py
1 #!/usr/bin/python
2 #
3 # Copyright 2014 Cumulus Networks, Inc. All rights reserved.
4 # Author: Roopa Prabhu, roopa@cumulusnetworks.com
5 #
6
7 from sets import Set
8 from ifupdown.iface import *
9 from ifupdownaddons.modulebase import moduleBase
10 from ifupdownaddons.bridgeutils import brctl
11 from ifupdownaddons.iproute2 import iproute2
12 import itertools
13 import re
14 import time
15
16 class bridge(moduleBase):
17 """ ifupdown2 addon module to configure linux bridges """
18
19 _modinfo = { 'mhelp' : 'bridge configuration module',
20 'attrs' : {
21 'bridge-vlan-aware' :
22 {'help' : 'bridge vlan aware',
23 'example' : ['bridge-vlan-aware yes/no']},
24 'bridge-ports' :
25 {'help' : 'bridge ports',
26 'required' : True,
27 'example' : ['bridge-ports swp1.100 swp2.100 swp3.100',
28 'bridge-ports glob swp1-3.100',
29 'bridge-ports regex (swp[1|2|3].100)']},
30 'bridge-stp' :
31 {'help': 'bridge-stp yes/no',
32 'example' : ['bridge-stp no'],
33 'validvals' : ['yes', 'on', 'off', 'no'],
34 'default' : 'no'},
35 'bridge-bridgeprio' :
36 {'help': 'bridge priority',
37 'example' : ['bridge-bridgeprio 32768'],
38 'default' : '32768'},
39 'bridge-ageing' :
40 {'help': 'bridge ageing',
41 'example' : ['bridge-ageing 300'],
42 'default' : '300'},
43 'bridge-fd' :
44 { 'help' : 'bridge forward delay',
45 'example' : ['bridge-fd 15'],
46 'default' : '15'},
47 'bridge-gcint' :
48 # XXX: recheck values
49 { 'help' : 'bridge garbage collection interval in secs',
50 'example' : ['bridge-gcint 4'],
51 'default' : '4'},
52 'bridge-hello' :
53 { 'help' : 'bridge set hello time',
54 'example' : ['bridge-hello 2'],
55 'default' : '2'},
56 'bridge-maxage' :
57 { 'help' : 'bridge set maxage',
58 'example' : ['bridge-maxage 20'],
59 'default' : '20'},
60 'bridge-pathcosts' :
61 { 'help' : 'bridge set port path costs',
62 'example' : ['bridge-pathcosts swp1=100 swp2=100'],
63 'default' : '100'},
64 'bridge-portprios' :
65 { 'help' : 'bridge port prios',
66 'example' : ['bridge-portprios swp1=32 swp2=32'],
67 'default' : '32'},
68 'bridge-mclmc' :
69 { 'help' : 'set multicast last member count',
70 'example' : ['bridge-mclmc 2'],
71 'default' : '2'},
72 'bridge-mcrouter' :
73 { 'help' : 'set multicast router',
74 'default' : '1',
75 'example' : ['bridge-mcrouter 1']},
76 'bridge-mcsnoop' :
77 { 'help' : 'set multicast snooping',
78 'default' : '1',
79 'example' : ['bridge-mcsnoop 1']},
80 'bridge-mcsqc' :
81 { 'help' : 'set multicast startup query count',
82 'default' : '2',
83 'example' : ['bridge-mcsqc 2']},
84 'bridge-mcqifaddr' :
85 { 'help' : 'set multicast query to use ifaddr',
86 'default' : '0',
87 'example' : ['bridge-mcqifaddr 0']},
88 'bridge-mcquerier' :
89 { 'help' : 'set multicast querier',
90 'default' : '0',
91 'example' : ['bridge-mcquerier 0']},
92 'bridge-hashel' :
93 { 'help' : 'set hash elasticity',
94 'default' : '4096',
95 'example' : ['bridge-hashel 4096']},
96 'bridge-hashmax' :
97 { 'help' : 'set hash max',
98 'default' : '4096',
99 'example' : ['bridge-hashmax 4096']},
100 'bridge-mclmi' :
101 { 'help' : 'set multicast last member interval (in secs)',
102 'default' : '1',
103 'example' : ['bridge-mclmi 1']},
104 'bridge-mcmi' :
105 { 'help' : 'set multicast membership interval (in secs)',
106 'default' : '260',
107 'example' : ['bridge-mcmi 260']},
108 'bridge-mcqpi' :
109 { 'help' : 'set multicast querier interval (in secs)',
110 'default' : '255',
111 'example' : ['bridge-mcqpi 255']},
112 'bridge-mcqi' :
113 { 'help' : 'set multicast query interval (in secs)',
114 'default' : '125',
115 'example' : ['bridge-mcqi 125']},
116 'bridge-mcqri' :
117 { 'help' : 'set multicast query response interval (in secs)',
118 'default' : '10',
119 'example' : ['bridge-mcqri 10']},
120 'bridge-mcsqi' :
121 { 'help' : 'set multicast startup query interval (in secs)',
122 'default' : '31',
123 'example' : ['bridge-mcsqi 31']},
124 'bridge-mcqv4src' :
125 { 'help' : 'set per VLAN v4 multicast querier source address',
126 'example' : ['bridge-mcqv4src 100=172.16.100.1 101=172.16.101.1']},
127 'bridge-portmcrouter' :
128 { 'help' : 'set port multicast routers',
129 'default' : '1',
130 'example' : ['bridge-portmcrouter swp1=1 swp2=1']},
131 'bridge-portmcfl' :
132 { 'help' : 'port multicast fast leave',
133 'default' : '0',
134 'example' : ['bridge-portmcfl swp1=0 swp2=0']},
135 'bridge-waitport' :
136 { 'help' : 'wait for a max of time secs for the' +
137 ' specified ports to become available,' +
138 'if no ports are specified then those' +
139 ' specified on bridge-ports will be' +
140 ' used here. Specifying no ports here ' +
141 'should not be used if we are using ' +
142 'regex or \"all\" on bridge_ports,' +
143 'as it wouldnt work.',
144 'default' : '0',
145 'example' : ['bridge-waitport 4 swp1 swp2']},
146 'bridge-maxwait' :
147 { 'help' : 'forces to time seconds the maximum time ' +
148 'that the Debian bridge setup scripts will ' +
149 'wait for the bridge ports to get to the ' +
150 'forwarding status, doesn\'t allow factional ' +
151 'part. If it is equal to 0 then no waiting' +
152 ' is done',
153 'default' : '0',
154 'example' : ['bridge-maxwait 3']},
155 'bridge-vids' :
156 { 'help' : 'bridge vlans',
157 'example' : ['bridge-vids 4000']},
158 'bridge-pvid' :
159 { 'help' : 'bridge vlans',
160 'example' : ['bridge-pvid 1']},
161 'bridge-access' :
162 { 'help' : 'bridge access vlans',
163 'example' : ['bridge-access 300']},
164 'bridge-port-vids' :
165 { 'help' : 'bridge vlans',
166 'example' : ['bridge-port-vids bond0=1-1000,1010-1020']},
167 'bridge-port-pvids' :
168 { 'help' : 'bridge port vlans',
169 'example' : ['bridge-port-pvids bond0=100 bond1=200']},
170 'bridge-pathcost' :
171 { 'help' : 'bridge port path cost',
172 'example' : ['bridge-pathcost 10']},
173 'bridge-priority' :
174 { 'help' : 'bridge port priority',
175 'example' : ['bridge-priority 10']},
176 'bridge-multicast-router' :
177 { 'help' : 'bridge multicast router',
178 'example' : ['bridge-multicast-router 1']},
179 'bridge-multicast-fast-leave' :
180 { 'help' : 'bridge multicast fast leave',
181 'example' : ['bridge-multicast-fast-leave 1']},
182 'bridge-igmp-querier-src' :
183 { 'help' : 'bridge igmp querier src',
184 'example' : ['bridge-igmp-querier-src 172.16.101.1']},
185 }}
186
187 def __init__(self, *args, **kargs):
188 moduleBase.__init__(self, *args, **kargs)
189 self.ipcmd = None
190 self.brctlcmd = None
191 self._running_vidinfo = {}
192 self._running_vidinfo_valid = False
193
194 def _is_bridge(self, ifaceobj):
195 if ifaceobj.get_attr_value_first('bridge-ports'):
196 return True
197 return False
198
199 def _is_bridge_port(self, ifaceobj):
200 if self.brctlcmd.is_bridge_port(ifaceobj.name):
201 return True
202 return False
203
204 def get_dependent_ifacenames(self, ifaceobj, ifacenames_all=None):
205 if not self._is_bridge(ifaceobj):
206 return None
207 return self.parse_port_list(ifaceobj.get_attr_value_first(
208 'bridge-ports'), ifacenames_all)
209
210 def get_dependent_ifacenames_running(self, ifaceobj):
211 self._init_command_handlers()
212 if not self.brctlcmd.bridge_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('bridge-ports')
225 if ports:
226 return self.parse_port_list(ports)
227 else:
228 return None
229
230 def _process_bridge_waitport(self, ifaceobj, portlist):
231 waitport_value = ifaceobj.get_attr_value_first('bridge-waitport')
232 if not waitport_value: return
233 try:
234 waitportvals = re.split(r'[\s\t]\s*', waitport_value, 1)
235 if not waitportvals: return
236 try:
237 waitporttime = int(waitportvals[0])
238 except:
239 self.log_warn('%s: invalid waitport value \'%s\''
240 %(ifaceobj.name, waitporttime))
241 return
242 if waitporttime <= 0: return
243 try:
244 waitportlist = self.parse_port_list(waitportvals[1])
245 except IndexError, e:
246 # ignore error and use all bridge ports
247 waitportlist = portlist
248 pass
249 if not waitportlist: return
250 self.logger.info('%s: waiting for ports %s to exist ...'
251 %(ifaceobj.name, str(waitportlist)))
252 starttime = time.time()
253 while ((time.time() - starttime) < waitporttime):
254 if all([False for p in waitportlist
255 if not self.ipcmd.link_exists(p)]):
256 break;
257 time.sleep(1)
258 except Exception, e:
259 self.log_warn('%s: unable to process waitport: %s'
260 %(ifaceobj.name, str(e)))
261
262 def _add_ports(self, ifaceobj):
263 bridgeports = self._get_bridge_port_list(ifaceobj)
264 runningbridgeports = []
265
266 self._process_bridge_waitport(ifaceobj, bridgeports)
267 # Delete active ports not in the new port list
268 if not self.PERFMODE:
269 runningbridgeports = self.brctlcmd.get_bridge_ports(ifaceobj.name)
270 if runningbridgeports:
271 [self.ipcmd.link_set(bport, 'nomaster')
272 for bport in runningbridgeports
273 if not bridgeports or bport not in bridgeports]
274 else:
275 runningbridgeports = []
276 if not bridgeports:
277 return
278 err = 0
279 for bridgeport in Set(bridgeports).difference(Set(runningbridgeports)):
280 try:
281 if not self.DRYRUN and not self.ipcmd.link_exists(bridgeport):
282 self.log_warn('%s: bridge port %s does not exist'
283 %(ifaceobj.name, bridgeport))
284 err += 1
285 continue
286 self.ipcmd.link_set(bridgeport, 'master', ifaceobj.name)
287 self.write_file('/proc/sys/net/ipv6/conf/%s' %bridgeport +
288 '/disable_ipv6', '1')
289 self.ipcmd.addr_flush(bridgeport)
290 except Exception, e:
291 self.log_error(str(e))
292 if err:
293 self.log_error('bridge configuration failed (missing ports)')
294
295 def _process_bridge_maxwait(self, ifaceobj, portlist):
296 maxwait = ifaceobj.get_attr_value_first('bridge-maxwait')
297 if not maxwait: return
298 try:
299 maxwait = int(maxwait)
300 except:
301 self.log_warn('%s: invalid maxwait value \'%s\'' %(ifaceobj.name,
302 maxwait))
303 return
304 if not maxwait: return
305 self.logger.info('%s: waiting for ports to go to fowarding state ..'
306 %ifaceobj.name)
307 try:
308 starttime = time.time()
309 while ((time.time() - starttime) < maxwait):
310 if all([False for p in portlist
311 if self.read_file_oneline(
312 '/sys/class/net/%s/brif/%s/state'
313 %(ifaceobj.name, p)) != '3']):
314 break;
315 time.sleep(1)
316 except Exception, e:
317 self.log_warn('%s: unable to process maxwait: %s'
318 %(ifaceobj.name, str(e)))
319
320 def _ints_to_ranges(self, ints):
321 for a, b in itertools.groupby(enumerate(ints), lambda (x, y): y - x):
322 b = list(b)
323 yield b[0][1], b[-1][1]
324
325 def _ranges_to_ints(self, rangelist):
326 """ returns expanded list of integers given set of string ranges
327 example: ['1', '2-4', '6'] returns [1, 2, 3, 4, 6]
328 """
329 result = []
330 for part in rangelist:
331 if '-' in part:
332 a, b = part.split('-')
333 a, b = int(a), int(b)
334 result.extend(range(a, b + 1))
335 else:
336 a = int(part)
337 result.append(a)
338 return result
339
340 def _diff_vids(self, vids1, vids2):
341 vids_to_add = None
342 vids_to_del = None
343
344 vids1_ints = self._ranges_to_ints(vids1)
345 vids2_ints = self._ranges_to_ints(vids2)
346 vids1_diff = Set(vids1_ints).difference(vids2_ints)
347 vids2_diff = Set(vids2_ints).difference(vids1_ints)
348 if vids1_diff:
349 vids_to_add = ['%d' %start if start == end else '%d-%d' %(start, end)
350 for start, end in self._ints_to_ranges(vids1_diff)]
351 if vids2_diff:
352 vids_to_del = ['%d' %start if start == end else '%d-%d' %(start, end)
353 for start, end in self._ints_to_ranges(vids2_diff)]
354 return (vids_to_del, vids_to_add)
355
356 def _compare_vids(self, vids1, vids2):
357 """ Returns true if the vids are same else return false """
358
359 vids1_ints = self._ranges_to_ints(vids1)
360 vids2_ints = self._ranges_to_ints(vids2)
361 if Set(vids1_ints).symmetric_difference(vids2_ints):
362 return False
363 else:
364 return True
365
366 def _set_bridge_mcqv4src_compat(self, ifaceobj):
367 #
368 # Sets old style igmp querier
369 #
370 attrval = ifaceobj.get_attr_value_first('bridge-mcqv4src')
371 if attrval:
372 running_mcqv4src = {}
373 if not self.PERFMODE:
374 running_mcqv4src = self.brctlcmd.get_mcqv4src(ifaceobj.name)
375 mcqs = {}
376 srclist = attrval.split()
377 for s in srclist:
378 k, v = s.split('=')
379 mcqs[k] = v
380
381 k_to_del = Set(running_mcqv4src.keys()).difference(mcqs.keys())
382 for v in k_to_del:
383 self.brctlcmd.del_mcqv4src(ifaceobj.name, v)
384 for v in mcqs.keys():
385 self.brctlcmd.set_mcqv4src(ifaceobj.name, v, mcqs[v])
386
387 def _get_running_vidinfo(self):
388 if self._running_vidinfo_valid:
389 return self._running_vidinfo
390 self._running_vidinfo = {}
391 if not self.PERFMODE:
392 self._running_vidinfo = self.ipcmd.bridge_port_vids_get_all()
393 self._running_vidinfo_valid = True
394 return self._running_vidinfo
395
396 def _flush_running_vidinfo(self):
397 self._running_vidinfo = {}
398 self._running_vidinfo_valid = False
399
400 def _set_bridge_vidinfo_compat(self, ifaceobj):
401 #
402 # Supports old style vlan vid info format
403 # for compatibility
404 #
405
406 # Handle bridge vlan attrs
407 running_vidinfo = self._get_running_vidinfo()
408
409 # Install pvids
410 attrval = ifaceobj.get_attr_value_first('bridge-port-pvids')
411 if attrval:
412 portlist = self.parse_port_list(attrval)
413 if not portlist:
414 self.log_warn('%s: could not parse \'%s %s\''
415 %(ifaceobj.name, attrname, attrval))
416 return
417 for p in portlist:
418 try:
419 (port, pvid) = p.split('=')
420 running_pvid = running_vidinfo.get(port, {}).get('pvid')
421 if running_pvid:
422 if running_pvid == pvid:
423 continue
424 else:
425 self.ipcmd.bridge_port_pvid_del(port, running_pvid)
426 self.ipcmd.bridge_port_pvid_add(port, pvid)
427 except Exception, e:
428 self.log_warn('%s: failed to set pvid `%s` (%s)'
429 %(ifaceobj.name, p, str(e)))
430
431 # install port vids
432 attrval = ifaceobj.get_attr_value_first('bridge-port-vids')
433 if attrval:
434 portlist = self.parse_port_list(attrval)
435 if not portlist:
436 self.log_warn('%s: could not parse \'%s %s\''
437 %(ifaceobj.name, attrname, attrval))
438 return
439 for p in portlist:
440 try:
441 (port, val) = p.split('=')
442 vids = val.split(',')
443 if running_vidinfo.get(port):
444 (vids_to_del, vids_to_add) = \
445 self._diff_vids(vids,
446 running_vidinfo.get(port).get('vlan'))
447 if vids_to_del:
448 self.ipcmd.bridge_port_vids_del(port, vids_to_del)
449 if vids_to_add:
450 self.ipcmd.bridge_port_vids_add(port, vids_to_add)
451 else:
452 self.ipcmd.bridge_port_vids_add(port, vids)
453 except Exception, e:
454 self.log_warn('%s: failed to set vid `%s` (%s)'
455 %(ifaceobj.name, p, str(e)))
456
457 # install vids
458 attrval = ifaceobj.get_attr_value_first('bridge-vids')
459 if attrval:
460 vids = re.split(r'[\s\t]\s*', attrval)
461 if running_vidinfo.get(ifaceobj.name):
462 (vids_to_del, vids_to_add) = \
463 self._diff_vids(vids,
464 running_vidinfo.get(ifaceobj.name).get('vlan'))
465 if vids_to_del:
466 self.ipcmd.bridge_vids_del(ifaceobj.name, vids_to_del)
467 if vids_to_add:
468 self.ipcmd.bridge_vids_add(ifaceobj.name, vids_to_add)
469 else:
470 self.ipcmd.bridge_vids_add(ifaceobj.name, vids)
471 else:
472 running_vids = running_vidinfo.get(ifaceobj.name)
473 if running_vids:
474 self.ipcmd.bridge_vids_del(ifaceobj.name, running_vids)
475
476 def _apply_bridge_settings(self, ifaceobj):
477 try:
478 stp = ifaceobj.get_attr_value_first('bridge-stp')
479 if stp:
480 self.brctlcmd.set_stp(ifaceobj.name, stp)
481 # Use the brctlcmd bulk set method: first build a dictionary
482 # and then call set
483 bridgeattrs = { k:v for k,v in
484 {'ageing' :
485 ifaceobj.get_attr_value_first('bridge-ageing'),
486 'bridgeprio' :
487 ifaceobj.get_attr_value_first(
488 'bridge-bridgeprio'),
489 'fd' :
490 ifaceobj.get_attr_value_first('bridge-fd'),
491 'gcint' :
492 ifaceobj.get_attr_value_first('bridge-gcint'),
493 'hello' :
494 ifaceobj.get_attr_value_first('bridge-hello'),
495 'maxage' :
496 ifaceobj.get_attr_value_first('bridge-maxage'),
497 'mclmc' :
498 ifaceobj.get_attr_value_first('bridge-mclmc'),
499 'mcrouter' :
500 ifaceobj.get_attr_value_first(
501 'bridge-mcrouter'),
502 'mcsnoop' :
503 ifaceobj.get_attr_value_first('bridge-mcsnoop'),
504 'mcsqc' :
505 ifaceobj.get_attr_value_first('bridge-mcsqc'),
506 'mcqifaddr' :
507 ifaceobj.get_attr_value_first(
508 'bridge-mcqifaddr'),
509 'mcquerier' :
510 ifaceobj.get_attr_value_first(
511 'bridge-mcquerier'),
512 'hashel' :
513 ifaceobj.get_attr_value_first('bridge-hashel'),
514 'hashmax' :
515 ifaceobj.get_attr_value_first('bridge-hashmax'),
516 'mclmi' :
517 ifaceobj.get_attr_value_first('bridge-mclmi'),
518 'mcmi' :
519 ifaceobj.get_attr_value_first('bridge-mcmi'),
520 'mcqpi' :
521 ifaceobj.get_attr_value_first('bridge-mcqpi'),
522 'mcqi' :
523 ifaceobj.get_attr_value_first('bridge-mcqi'),
524 'mcqri' :
525 ifaceobj.get_attr_value_first('bridge-mcqri'),
526 'mcsqi' :
527 ifaceobj.get_attr_value_first('bridge-mcsqi')
528 }.items()
529 if v }
530 if bridgeattrs:
531 self.brctlcmd.set_bridge_attrs(ifaceobj.name, bridgeattrs)
532 portattrs = {}
533 for attrname, dstattrname in {'bridge-pathcosts' : 'pathcost',
534 'bridge-portprios' : 'portprio',
535 'bridge-portmcrouter' : 'portmcrouter',
536 'bridge-portmcfl' : 'portmcfl'}.items():
537 attrval = ifaceobj.get_attr_value_first(attrname)
538 if not attrval:
539 continue
540 portlist = self.parse_port_list(attrval)
541 if not portlist:
542 self.log_warn('%s: could not parse \'%s %s\''
543 %(ifaceobj.name, attrname, attrval))
544 continue
545 for p in portlist:
546 try:
547 (port, val) = p.split('=')
548 if not portattrs.get(port):
549 portattrs[port] = {}
550 portattrs[port].update({dstattrname : val})
551 except Exception, e:
552 self.log_warn('%s: could not parse %s (%s)'
553 %(ifaceobj.name, attrname, str(e)))
554 for port, attrdict in portattrs.iteritems():
555 self.brctlcmd.set_bridgeport_attrs(ifaceobj.name, port,
556 attrdict)
557 self._set_bridge_vidinfo_compat(ifaceobj)
558
559 self._set_bridge_mcqv4src_compat(ifaceobj)
560
561 self._process_bridge_maxwait(ifaceobj,
562 self._get_bridge_port_list(ifaceobj))
563 except Exception, e:
564 self.log_warn(str(e))
565
566 def _apply_bridge_vids(self, bportifaceobj, vids, running_vids, isbridge):
567 try:
568 if running_vids:
569 (vids_to_del, vids_to_add) = \
570 self._diff_vids(vids, running_vids)
571 if vids_to_del:
572 self.ipcmd.bridge_vids_del(bportifaceobj.name,
573 vids_to_del, isbridge)
574 if vids_to_add:
575 self.ipcmd.bridge_vids_add(bportifaceobj.name,
576 vids_to_add, isbridge)
577 else:
578 self.ipcmd.bridge_vids_add(bportifaceobj.name, vids, isbridge)
579 except Exception, e:
580 self.log_warn('%s: failed to set vid `%s` (%s)'
581 %(bportifaceobj.name, str(vids), str(e)))
582
583 def _apply_bridge_port_pvids(self, bportifaceobj, pvid, running_pvid):
584 # Install pvids
585 try:
586 if running_pvid:
587 if running_pvid != pvid:
588 self.ipcmd.bridge_port_pvid_del(bportifaceobj.name,
589 running_pvid)
590 self.ipcmd.bridge_port_pvid_add(bportifaceobj.name, pvid)
591 except Exception, e:
592 self.log_warn('%s: failed to set pvid `%s` (%s)'
593 %(bportifaceobj.name, pvid, str(e)))
594
595 def _apply_bridge_vlan_aware_port_settings_all(self, bportifaceobj,
596 bridge_vids=None):
597 running_vidinfo = self._get_running_vidinfo()
598 vids = None
599 pvids = None
600 bport_access = bportifaceobj.get_attr_value_first('bridge-access')
601 if bport_access:
602 vids = re.split(r'[\s\t]\s*', bport_access)
603 pvids = vids
604
605 bport_vids = bportifaceobj.get_attr_value_first('bridge-vids')
606 if bport_vids:
607 vids = re.split(r'[\s\t]\s*', bport_vids)
608
609 bport_pvids = bportifaceobj.get_attr_value_first('bridge-pvid')
610 if bport_pvids:
611 pvids = re.split(r'[\s\t]\s*', bport_pvids)
612
613 if pvids:
614 self._apply_bridge_port_pvids(bportifaceobj, pvids[0],
615 running_vidinfo.get(bportifaceobj.name, {}).get('pvid'))
616 else:
617 self._apply_bridge_port_pvids(bportifaceobj,
618 '1', running_vidinfo.get(bportifaceobj.name,
619 {}).get('pvid'))
620
621 if vids:
622 self._apply_bridge_vids(bportifaceobj, vids,
623 running_vidinfo.get(bportifaceobj.name,
624 {}).get('vlan'), False)
625 elif bridge_vids:
626 self._apply_bridge_vids(bportifaceobj,
627 bridge_vids, running_vidinfo.get(
628 bportifaceobj.name, {}).get('vlan'), False)
629
630
631 def _apply_bridge_port_settings(self, bportifaceobj, bridgename=None,
632 bridgeifaceobj=None):
633 if not bridgename and bridgeifaceobj:
634 bridgename = bridgeifaceobj.name
635 # Set other stp and igmp attributes
636 portattrs = {}
637 for attrname, dstattrname in {
638 'bridge-pathcost' : 'pathcost',
639 'bridge-prio' : 'portprio',
640 'bridge-priority' : 'portprio',
641 'bridge-mcrouter' : 'portmcrouter',
642 'bridge-multicast-router' : 'portmcrouter',
643 'bridge-mcfl' : 'portmcfl',
644 'bridge-multicast-fast-leave' : 'portmcfl'}.items():
645 attrval = bportifaceobj.get_attr_value_first(attrname)
646 if not attrval:
647 # Check if bridge has that attribute
648 #if bridgeifaceobj:
649 # attrval = bridgeifaceobj.get_attr_value_first(attrname)
650 # if not attrval:
651 # continue
652 #else:
653 continue
654 portattrs[dstattrname] = attrval
655 try:
656 self.brctlcmd.set_bridgeport_attrs(bridgename,
657 bportifaceobj.name, portattrs)
658 except Exception, e:
659 self.log_warn(str(e))
660
661 def _apply_bridge_port_settings_all(self, ifaceobj,
662 ifaceobj_getfunc=None):
663 bridge_vlan_aware = ifaceobj.get_attr_value_first(
664 'bridge-vlan-aware')
665 if bridge_vlan_aware and bridge_vlan_aware == 'yes':
666 bridge_vlan_aware = True
667 else:
668 bridge_vlan_aware = False
669
670 if (ifaceobj.get_attr_value_first('bridge-port-vids') and
671 ifaceobj.get_attr_value_first('bridge-port-pvids')):
672 # Old style bridge port vid info
673 # skip new style setting on ports
674 return
675 self.logger.info('%s: applying bridge configuration '
676 %ifaceobj.name + 'specific to ports')
677
678 bridge_vids = ifaceobj.get_attr_value_first('bridge-vids')
679 if bridge_vids:
680 bridge_vids = re.split(r'[\s\t]\s*', bridge_vids)
681 else:
682 bridge_vids = None
683
684 bridgeports = self._get_bridge_port_list(ifaceobj)
685 for bport in bridgeports:
686 # Use the brctlcmd bulk set method: first build a dictionary
687 # and then call set
688 self.logger.info('%s: processing bridge config for port %s'
689 %(ifaceobj.name, bport))
690 bportifaceobjlist = ifaceobj_getfunc(bport)
691 if not bportifaceobjlist:
692 continue
693 for bportifaceobj in bportifaceobjlist:
694 # Add attributes specific to the vlan aware bridge
695 if bridge_vlan_aware:
696 self._apply_bridge_vlan_aware_port_settings_all(
697 bportifaceobj, bridge_vids)
698 self._apply_bridge_port_settings(
699 bportifaceobj, bridgeifaceobj=ifaceobj)
700
701 def _up(self, ifaceobj, ifaceobj_getfunc=None):
702 # Check if bridge port
703 if self._is_bridge_port(ifaceobj):
704 bridgename = ifaceobj.upperifaces[0]
705 if not bridgename:
706 self.logger.warn('%s: unable to determine bridge name'
707 %ifaceobj.name)
708 return
709 if self.ipcmd.bridge_is_vlan_aware(bridgename):
710 self._apply_bridge_vlan_aware_port_settings_all(
711 ifaceobj)
712 self._apply_bridge_port_settings(ifaceobj, bridgename=bridgename)
713 return
714
715 if not self._is_bridge(ifaceobj):
716 return
717 try:
718 porterr = False
719 porterrstr = ''
720 self.ipcmd.batch_start()
721 if not self.PERFMODE:
722 if not self.ipcmd.link_exists(ifaceobj.name):
723 self.ipcmd.link_create(ifaceobj.name, 'bridge')
724 else:
725 self.ipcmd.link_create(ifaceobj.name, 'bridge')
726 try:
727 self._add_ports(ifaceobj)
728 except Exception, e:
729 porterr = True
730 porterrstr = str(e)
731 pass
732 finally:
733 self.ipcmd.batch_commit()
734 self._apply_bridge_settings(ifaceobj)
735 self._apply_bridge_port_settings_all(ifaceobj,
736 ifaceobj_getfunc=ifaceobj_getfunc)
737 self._flush_running_vidinfo()
738 except Exception, e:
739 self.log_error(str(e))
740 if porterr:
741 raise Exception(porterrstr)
742
743 def _down(self, ifaceobj, ifaceobj_getfunc=None):
744 try:
745 if ifaceobj.get_attr_value_first('bridge-ports'):
746 ports = self.brctlcmd.get_bridge_ports(ifaceobj.name)
747 if ports:
748 for p in ports:
749 proc_file = ('/proc/sys/net/ipv6/conf/%s' %p +
750 '/disable_ipv6')
751 self.write_file(proc_file, '0')
752 self.brctlcmd.delete_bridge(ifaceobj.name)
753 except Exception, e:
754 self.log_error(str(e))
755
756 def _query_running_vidinfo(self, ifaceobjrunning, ports):
757 running_attrs = {}
758 running_vidinfo = self._get_running_vidinfo()
759 if ports:
760 running_bridge_port_vids = ''
761 for p in ports:
762 try:
763 running_vids = running_vidinfo.get(p, {}).get('vlan')
764 if running_vids:
765 running_bridge_port_vids += ' %s=%s' %(p,
766 ','.join(running_vids))
767 except Exception:
768 pass
769 running_attrs['bridge-port-vids'] = running_bridge_port_vids
770
771 running_bridge_port_pvids = ''
772 for p in ports:
773 try:
774 running_pvids = running_vidinfo.get(p, {}).get('pvid')
775 if running_pvids:
776 running_bridge_port_pvids += ' %s=%s' %(p,
777 running_pvids)
778 except Exception:
779 pass
780 running_attrs['bridge-port-pvids'] = running_bridge_port_pvids
781
782 running_bridge_vids = running_vidinfo.get(ifaceobjrunning.name,
783 {}).get('vlan')
784 if running_bridge_vids:
785 running_attrs['bridge-vids'] = ','.join(running_bridge_vids)
786 return running_attrs
787
788 def _query_running_mcqv4src(self, ifaceobjrunning):
789 running_mcqv4src = self.brctlcmd.get_mcqv4src(ifaceobjrunning.name)
790 mcqs = ['%s=%s' %(v, i) for v, i in running_mcqv4src.items()]
791 mcqs.sort()
792 mcq = ' '.join(mcqs)
793 return mcq
794
795 def _query_running_attrs(self, ifaceobjrunning):
796 bridgeattrdict = {}
797 userspace_stp = 0
798 ports = None
799 skip_kernel_stp_attrs = 0
800
801 if self.sysctl_get('net.bridge.bridge-stp-user-space') == '1':
802 userspace_stp = 1
803
804 tmpbridgeattrdict = self.brctlcmd.get_bridge_attrs(ifaceobjrunning.name)
805 if not tmpbridgeattrdict:
806 self.logger.warn('%s: unable to get bridge attrs'
807 %ifaceobjrunning.name)
808 return bridgeattrdict
809
810 # Fill bridge_ports and bridge stp attributes first
811 ports = tmpbridgeattrdict.get('ports')
812 if ports:
813 bridgeattrdict['bridge-ports'] = [' '.join(ports.keys())]
814 stp = tmpbridgeattrdict.get('stp', 'no')
815 if stp != self.get_mod_subattr('bridge-stp', 'default'):
816 bridgeattrdict['bridge-stp'] = [stp]
817
818 if stp == 'yes' and userspace_stp:
819 skip_kernel_stp_attrs = 1
820
821 # pick all other attributes
822 for k,v in tmpbridgeattrdict.items():
823 if not v:
824 continue
825 if k == 'ports' or k == 'stp':
826 continue
827
828 if skip_kernel_stp_attrs and k[:2] != 'mc':
829 # only include igmp attributes if kernel stp is off
830 continue
831 attrname = 'bridge-' + k
832 if v != self.get_mod_subattr(attrname, 'default'):
833 bridgeattrdict[attrname] = [v]
834
835 bridgevidinfo = self._query_running_vidinfo(ifaceobjrunning, ports)
836 if bridgevidinfo:
837 bridgeattrdict.update({k : [v] for k, v in bridgevidinfo.items()
838 if v})
839
840 mcq = self._query_running_mcqv4src(ifaceobjrunning)
841 if mcq:
842 bridgeattrdict['bridge-mcqv4src'] = [mcq]
843
844 if skip_kernel_stp_attrs:
845 return bridgeattrdict
846
847 if ports:
848 portconfig = {'bridge-pathcosts' : '',
849 'bridge-portprios' : ''}
850 for p, v in ports.items():
851 v = self.brctlcmd.get_pathcost(ifaceobjrunning.name, p)
852 if v and v != self.get_mod_subattr('bridge-pathcosts',
853 'default'):
854 portconfig['bridge-pathcosts'] += ' %s=%s' %(p, v)
855
856 v = self.brctlcmd.get_portprio(ifaceobjrunning.name, p)
857 if v and v != self.get_mod_subattr('bridge-portprios',
858 'default'):
859 portconfig['bridge-portprios'] += ' %s=%s' %(p, v)
860
861 bridgeattrdict.update({k : [v] for k, v in portconfig.items()
862 if v})
863
864 return bridgeattrdict
865
866 def _query_check_mcqv4src(self, ifaceobj, ifaceobjcurr):
867 running_mcqs = self._query_running_mcqv4src(ifaceobj)
868 attrval = ifaceobj.get_attr_value_first('bridge-mcqv4src')
869 if attrval:
870 mcqs = attrval.split()
871 mcqs.sort()
872 mcqsout = ' '.join(mcqs)
873 ifaceobjcurr.update_config_with_status('bridge-mcqv4src',
874 running_mcqs, 1 if running_mcqs != mcqsout else 0)
875
876 def _query_check_vidinfo(self, ifaceobj, ifaceobjcurr):
877
878 err = 0
879 running_vidinfo = self.ipcmd.bridge_port_vids_get_all()
880 attrval = ifaceobj.get_attr_value_first('bridge-port-vids')
881 if attrval:
882 running_bridge_port_vids = ''
883 portlist = self.parse_port_list(attrval)
884 if not portlist:
885 self.log_warn('%s: could not parse \'%s %s\''
886 %(ifaceobj.name, attrname, attrval))
887 return
888 err = 0
889 for p in portlist:
890 try:
891 (port, val) = p.split('=')
892 vids = val.split(',')
893 running_vids = running_vidinfo.get(port, {}).get('vlan')
894 if running_vids:
895 if not self._compare_vids(vids, running_vids):
896 err += 1
897 running_bridge_port_vids += ' %s=%s' %(port,
898 ','.join(running_vids))
899 else:
900 running_bridge_port_vids += ' %s' %p
901 else:
902 err += 1
903 except Exception, e:
904 self.log_warn('%s: failure checking vid %s (%s)'
905 %(ifaceobj.name, p, str(e)))
906 if err:
907 ifaceobjcurr.update_config_with_status('bridge-port-vids',
908 running_bridge_port_vids, 1)
909 else:
910 ifaceobjcurr.update_config_with_status('bridge-port-vids',
911 attrval, 0)
912
913 # Install pvids
914 attrval = ifaceobj.get_attr_value_first('bridge-port-pvids')
915 if attrval:
916 portlist = self.parse_port_list(attrval)
917 if not portlist:
918 self.log_warn('%s: could not parse \'%s %s\''
919 %(ifaceobj.name, attrname, attrval))
920 return
921 running_bridge_port_pvids = ''
922 err = 0
923 for p in portlist:
924 try:
925 (port, pvid) = p.split('=')
926 running_pvid = running_vidinfo.get(port, {}).get('pvid')
927 if running_pvid and running_pvid == pvid:
928 running_bridge_port_pvids += ' %s' %p
929 else:
930 err += 1
931 running_bridge_port_pvids += ' %s=%s' %(port,
932 running_pvid)
933 except Exception, e:
934 self.log_warn('%s: failure checking pvid %s (%s)'
935 %(ifaceobj.name, pvid, str(e)))
936 if err:
937 ifaceobjcurr.update_config_with_status('bridge-port-pvids',
938 running_bridge_port_pvids, 1)
939 else:
940 ifaceobjcurr.update_config_with_status('bridge-port-pvids',
941 running_bridge_port_pvids, 0)
942
943 attrval = ifaceobj.get_attr_value_first('bridge-vids')
944 if attrval:
945 vids = re.split(r'[\s\t]\s*', attrval)
946 running_vids = running_vidinfo.get(ifaceobj.name, {}).get('vlan')
947 if running_vids:
948 if self._compare_vids(vids, running_vids):
949 ifaceobjcurr.update_config_with_status('bridge-vids',
950 attrval, 0)
951 else:
952 ifaceobjcurr.update_config_with_status('bridge-vids',
953 ','.join(running_vids), 1)
954 else:
955 ifaceobjcurr.update_config_with_status('bridge-vids', attrval,
956 1)
957
958 def _query_check(self, ifaceobj, ifaceobjcurr, ifaceobj_getfunc=None):
959 if not self._is_bridge(ifaceobj):
960 return
961 if not self.brctlcmd.bridge_exists(ifaceobj.name):
962 self.logger.info('%s: bridge: does not exist' %(ifaceobj.name))
963 ifaceobjcurr.status = ifaceStatus.NOTFOUND
964 return
965 ifaceattrs = self.dict_key_subset(ifaceobj.config,
966 self.get_mod_attrs())
967 if not ifaceattrs:
968 return
969 try:
970 runningattrs = self.brctlcmd.get_bridge_attrs(ifaceobj.name)
971 if not runningattrs:
972 self.logger.debug('%s: bridge: unable to get bridge attrs'
973 %ifaceobj.name)
974 runningattrs = {}
975 except Exception, e:
976 self.logger.warn(str(e))
977 runningattrs = {}
978 filterattrs = ['bridge-vids', 'bridge-port-vids',
979 'bridge-port-pvids']
980 for k in Set(ifaceattrs).difference(filterattrs):
981 # get the corresponding ifaceobj attr
982 v = ifaceobj.get_attr_value_first(k)
983 if not v:
984 continue
985 rv = runningattrs.get(k[7:])
986 if k == 'bridge-mcqv4src':
987 continue
988 if k == 'bridge-stp':
989 # special case stp compare because it may
990 # contain more than one valid values
991 stp_on_vals = ['on', 'yes']
992 stp_off_vals = ['off']
993 if ((v in stp_on_vals and rv in stp_on_vals) or
994 (v in stp_off_vals and rv in stp_off_vals)):
995 ifaceobjcurr.update_config_with_status('bridge-stp',
996 v, 0)
997 else:
998 ifaceobjcurr.update_config_with_status('bridge-stp',
999 v, 1)
1000 elif k == 'bridge-ports':
1001 # special case ports because it can contain regex or glob
1002 running_port_list = rv.keys() if rv else []
1003 bridge_port_list = self._get_bridge_port_list(ifaceobj)
1004 if not running_port_list and not bridge_port_list:
1005 continue
1006 portliststatus = 1
1007 if running_port_list and bridge_port_list:
1008 difference = set(running_port_list
1009 ).symmetric_difference(bridge_port_list)
1010 if not difference:
1011 portliststatus = 0
1012 ifaceobjcurr.update_config_with_status('bridge-ports',
1013 ' '.join(running_port_list)
1014 if running_port_list else '', portliststatus)
1015 elif (k == 'bridge-pathcosts' or
1016 k == 'bridge-portprios' or k == 'bridge-portmcrouter'
1017 or k == 'bridge-portmcfl'):
1018 brctlcmdattrname = k[11:].rstrip('s')
1019 # for port attributes, the attributes are in a list
1020 # <portname>=<portattrvalue>
1021 status = 0
1022 currstr = ''
1023 vlist = self.parse_port_list(v)
1024 if not vlist:
1025 continue
1026 for vlistitem in vlist:
1027 try:
1028 (p, v) = vlistitem.split('=')
1029 currv = self.brctlcmd.get_bridgeport_attr(
1030 ifaceobj.name, p,
1031 brctlcmdattrname)
1032 if currv:
1033 currstr += ' %s=%s' %(p, currv)
1034 else:
1035 currstr += ' %s=%s' %(p, 'None')
1036 if currv != v:
1037 status = 1
1038 except Exception, e:
1039 self.log_warn(str(e))
1040 pass
1041 ifaceobjcurr.update_config_with_status(k, currstr, status)
1042 elif not rv:
1043 ifaceobjcurr.update_config_with_status(k, 'notfound', 1)
1044 continue
1045 elif v != rv:
1046 ifaceobjcurr.update_config_with_status(k, rv, 1)
1047 else:
1048 ifaceobjcurr.update_config_with_status(k, rv, 0)
1049
1050 self._query_check_vidinfo(ifaceobj, ifaceobjcurr)
1051
1052 self._query_check_mcqv4src(ifaceobj, ifaceobjcurr)
1053
1054 def _query_running(self, ifaceobjrunning, ifaceobj_getfunc=None):
1055 if not self.brctlcmd.bridge_exists(ifaceobjrunning.name):
1056 return
1057 ifaceobjrunning.update_config_dict(self._query_running_attrs(
1058 ifaceobjrunning))
1059
1060 _run_ops = {'pre-up' : _up,
1061 'post-down' : _down,
1062 'query-checkcurr' : _query_check,
1063 'query-running' : _query_running}
1064
1065 def get_ops(self):
1066 """ returns list of ops supported by this module """
1067 return self._run_ops.keys()
1068
1069 def _init_command_handlers(self):
1070 flags = self.get_flags()
1071 if not self.ipcmd:
1072 self.ipcmd = iproute2(**flags)
1073 if not self.brctlcmd:
1074 self.brctlcmd = brctl(**flags)
1075
1076 def run(self, ifaceobj, operation, query_ifaceobj=None,
1077 ifaceobj_getfunc=None):
1078 """ run bridge configuration on the interface object passed as
1079 argument. Can create bridge interfaces if they dont exist already
1080
1081 Args:
1082 **ifaceobj** (object): iface object
1083
1084 **operation** (str): any of 'pre-up', 'post-down', 'query-checkcurr',
1085 'query-running'
1086
1087 Kwargs:
1088 **query_ifaceobj** (object): query check ifaceobject. This is only
1089 valid when op is 'query-checkcurr'. It is an object same as
1090 ifaceobj, but contains running attribute values and its config
1091 status. The modules can use it to return queried running state
1092 of interfaces. status is success if the running state is same
1093 as user required state in ifaceobj. error otherwise.
1094 """
1095 op_handler = self._run_ops.get(operation)
1096 if not op_handler:
1097 return
1098 self._init_command_handlers()
1099 self._flush_running_vidinfo()
1100 if operation == 'query-checkcurr':
1101 op_handler(self, ifaceobj, query_ifaceobj,
1102 ifaceobj_getfunc=ifaceobj_getfunc)
1103 else:
1104 op_handler(self, ifaceobj, ifaceobj_getfunc=ifaceobj_getfunc)