]> git.proxmox.com Git - mirror_ifupdown2.git/blob - ifupdown2/addons/bridge.py
Merge 'vlan filtering bridge + vxlan + mlag + vrr' support from internal
[mirror_ifupdown2.git] / ifupdown2 / 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 from collections import Counter
13 import ifupdown.rtnetlink_api as rtnetlink_api
14 import itertools
15 import re
16 import time
17
18 class bridge(moduleBase):
19 """ ifupdown2 addon module to configure linux bridges """
20
21 _modinfo = { 'mhelp' : 'Bridge configuration module. Supports both ' +
22 'vlan aware and non vlan aware bridges. For the vlan ' +
23 'aware bridge, the port specific attributes must be ' +
24 'specified under the port. And for vlan unaware bridge ' +
25 'port specific attributes must be specified under the ' +
26 'bridge.',
27 'attrs' : {
28 'bridge-vlan-aware' :
29 {'help' : 'vlan aware bridge. Setting this ' +
30 'attribute to yes enables vlan filtering' +
31 ' on the bridge',
32 'example' : ['bridge-vlan-aware yes/no']},
33 'bridge-ports' :
34 {'help' : 'bridge ports',
35 'required' : True,
36 'example' : ['bridge-ports swp1.100 swp2.100 swp3.100',
37 'bridge-ports glob swp1-3.100',
38 'bridge-ports regex (swp[1|2|3].100)']},
39 'bridge-stp' :
40 {'help': 'bridge-stp yes/no',
41 'example' : ['bridge-stp no'],
42 'validvals' : ['yes', 'on', 'off', 'no'],
43 'default' : 'no'},
44 'bridge-bridgeprio' :
45 {'help': 'bridge priority',
46 'example' : ['bridge-bridgeprio 32768'],
47 'default' : '32768'},
48 'bridge-ageing' :
49 {'help': 'bridge ageing',
50 'example' : ['bridge-ageing 300'],
51 'default' : '300'},
52 'bridge-fd' :
53 { 'help' : 'bridge forward delay',
54 'example' : ['bridge-fd 15'],
55 'default' : '15'},
56 'bridge-gcint' :
57 # XXX: recheck values
58 { 'help' : 'bridge garbage collection interval in secs',
59 'example' : ['bridge-gcint 4'],
60 'default' : '4'},
61 'bridge-hello' :
62 { 'help' : 'bridge set hello time',
63 'example' : ['bridge-hello 2'],
64 'default' : '2'},
65 'bridge-maxage' :
66 { 'help' : 'bridge set maxage',
67 'example' : ['bridge-maxage 20'],
68 'default' : '20'},
69 'bridge-pathcosts' :
70 { 'help' : 'bridge set port path costs',
71 'example' : ['bridge-pathcosts swp1=100 swp2=100'],
72 'default' : '100'},
73 'bridge-portprios' :
74 { 'help' : 'bridge port prios',
75 'example' : ['bridge-portprios swp1=32 swp2=32'],
76 'default' : '32'},
77 'bridge-mclmc' :
78 { 'help' : 'set multicast last member count',
79 'example' : ['bridge-mclmc 2'],
80 'default' : '2'},
81 'bridge-mcrouter' :
82 { 'help' : 'set multicast router',
83 'default' : '1',
84 'example' : ['bridge-mcrouter 1']},
85 'bridge-mcsnoop' :
86 { 'help' : 'set multicast snooping',
87 'default' : '1',
88 'example' : ['bridge-mcsnoop 1']},
89 'bridge-mcsqc' :
90 { 'help' : 'set multicast startup query count',
91 'default' : '2',
92 'example' : ['bridge-mcsqc 2']},
93 'bridge-mcqifaddr' :
94 { 'help' : 'set multicast query to use ifaddr',
95 'default' : '0',
96 'example' : ['bridge-mcqifaddr 0']},
97 'bridge-mcquerier' :
98 { 'help' : 'set multicast querier',
99 'default' : '0',
100 'example' : ['bridge-mcquerier 0']},
101 'bridge-hashel' :
102 { 'help' : 'set hash elasticity',
103 'default' : '4096',
104 'example' : ['bridge-hashel 4096']},
105 'bridge-hashmax' :
106 { 'help' : 'set hash max',
107 'default' : '4096',
108 'example' : ['bridge-hashmax 4096']},
109 'bridge-mclmi' :
110 { 'help' : 'set multicast last member interval (in secs)',
111 'default' : '1',
112 'example' : ['bridge-mclmi 1']},
113 'bridge-mcmi' :
114 { 'help' : 'set multicast membership interval (in secs)',
115 'default' : '260',
116 'example' : ['bridge-mcmi 260']},
117 'bridge-mcqpi' :
118 { 'help' : 'set multicast querier interval (in secs)',
119 'default' : '255',
120 'example' : ['bridge-mcqpi 255']},
121 'bridge-mcqi' :
122 { 'help' : 'set multicast query interval (in secs)',
123 'default' : '125',
124 'example' : ['bridge-mcqi 125']},
125 'bridge-mcqri' :
126 { 'help' : 'set multicast query response interval (in secs)',
127 'default' : '10',
128 'example' : ['bridge-mcqri 10']},
129 'bridge-mcsqi' :
130 { 'help' : 'set multicast startup query interval (in secs)',
131 'default' : '31',
132 'example' : ['bridge-mcsqi 31']},
133 'bridge-mcqv4src' :
134 { 'help' : 'set per VLAN v4 multicast querier source address',
135 'compat' : True,
136 'example' : ['bridge-mcqv4src 100=172.16.100.1 101=172.16.101.1']},
137 'bridge-portmcrouter' :
138 { 'help' : 'set port multicast routers',
139 'default' : '1',
140 'example' : ['under the bridge: bridge-portmcrouter swp1=1 swp2=1',
141 'under the port: bridge-portmcrouter 1']},
142 'bridge-portmcfl' :
143 { 'help' : 'port multicast fast leave.',
144 'default' : '0',
145 'example' : ['under the bridge: bridge-portmcfl swp1=0 swp2=0',
146 'under the port: bridge-portmcfl 0']},
147 'bridge-waitport' :
148 { 'help' : 'wait for a max of time secs for the' +
149 ' specified ports to become available,' +
150 'if no ports are specified then those' +
151 ' specified on bridge-ports will be' +
152 ' used here. Specifying no ports here ' +
153 'should not be used if we are using ' +
154 'regex or \"all\" on bridge_ports,' +
155 'as it wouldnt work.',
156 'default' : '0',
157 'example' : ['bridge-waitport 4 swp1 swp2']},
158 'bridge-maxwait' :
159 { 'help' : 'forces to time seconds the maximum time ' +
160 'that the Debian bridge setup scripts will ' +
161 'wait for the bridge ports to get to the ' +
162 'forwarding status, doesn\'t allow factional ' +
163 'part. If it is equal to 0 then no waiting' +
164 ' is done',
165 'default' : '0',
166 'example' : ['bridge-maxwait 3']},
167 'bridge-vids' :
168 { 'help' : 'bridge port vids. Can be specified ' +
169 'under the bridge or under the port. ' +
170 'If specified under the bridge the ports ' +
171 'inherit it unless overridden by a ' +
172 'bridge-vids attribuet under the port',
173 'example' : ['bridge-vids 4000',
174 'bridge-vids 2000 2200-3000']},
175 'bridge-pvid' :
176 { 'help' : 'bridge port pvid. Must be specified under' +
177 ' the bridge port',
178 'example' : ['bridge-pvid 1']},
179 'bridge-access' :
180 { 'help' : 'bridge port access vlan. Must be ' +
181 'specified under the bridge port',
182 'example' : ['bridge-access 300']},
183 'bridge-port-vids' :
184 { 'help' : 'bridge vlans',
185 'compat': True,
186 'example' : ['bridge-port-vids bond0=1-1000,1010-1020']},
187 'bridge-port-pvids' :
188 { 'help' : 'bridge port vlans',
189 'compat': True,
190 'example' : ['bridge-port-pvids bond0=100 bond1=200']},
191 }}
192
193 # declare some ifaceobj priv_flags.
194 # XXX: This assumes that the priv_flags is owned by this module
195 # which it is not.
196 _BRIDGE_PORT_PROCESSED = 0x1
197
198 def __init__(self, *args, **kargs):
199 moduleBase.__init__(self, *args, **kargs)
200 self.ipcmd = None
201 self.brctlcmd = None
202 self._running_vidinfo = {}
203 self._running_vidinfo_valid = False
204 self._resv_vlan_range = self._get_reserved_vlan_range()
205 self.logger.debug('%s: using reserved vlan range %s'
206 %(self.__class__.__name__, str(self._resv_vlan_range)))
207
208 def _is_bridge(self, ifaceobj):
209 if ifaceobj.get_attr_value_first('bridge-ports'):
210 return True
211 return False
212
213 def _is_bridge_port(self, ifaceobj):
214 if self.brctlcmd.is_bridge_port(ifaceobj.name):
215 return True
216 return False
217
218 def get_dependent_ifacenames(self, ifaceobj, ifacenames_all=None):
219 if not self._is_bridge(ifaceobj):
220 return None
221 if ifaceobj.link_type != ifaceLinkType.LINK_NA:
222 ifaceobj.link_type = ifaceLinkType.LINK_MASTER
223 ifaceobj.link_kind = ifaceLinkKind.BRIDGE
224 return self.parse_port_list(ifaceobj.get_attr_value_first(
225 'bridge-ports'), ifacenames_all)
226
227 def get_dependent_ifacenames_running(self, ifaceobj):
228 self._init_command_handlers()
229 if not self.brctlcmd.bridge_exists(ifaceobj.name):
230 return None
231 return self.brctlcmd.get_bridge_ports(ifaceobj.name)
232
233 def _get_bridge_port_list(self, ifaceobj):
234
235 # port list is also available in the previously
236 # parsed dependent list. Use that if available, instead
237 # of parsing port expr again
238 port_list = ifaceobj.lowerifaces
239 if port_list:
240 return port_list
241 ports = ifaceobj.get_attr_value_first('bridge-ports')
242 if ports:
243 return self.parse_port_list(ports)
244 else:
245 return None
246
247 def _process_bridge_waitport(self, ifaceobj, portlist):
248 waitport_value = ifaceobj.get_attr_value_first('bridge-waitport')
249 if not waitport_value: return
250 try:
251 waitportvals = re.split(r'[\s\t]\s*', waitport_value, 1)
252 if not waitportvals: return
253 try:
254 waitporttime = int(waitportvals[0])
255 except:
256 self.log_warn('%s: invalid waitport value \'%s\''
257 %(ifaceobj.name, waitporttime))
258 return
259 if waitporttime <= 0: return
260 try:
261 waitportlist = self.parse_port_list(waitportvals[1])
262 except IndexError, e:
263 # ignore error and use all bridge ports
264 waitportlist = portlist
265 pass
266 if not waitportlist: return
267 self.logger.info('%s: waiting for ports %s to exist ...'
268 %(ifaceobj.name, str(waitportlist)))
269 starttime = time.time()
270 while ((time.time() - starttime) < waitporttime):
271 if all([False for p in waitportlist
272 if not self.ipcmd.link_exists(p)]):
273 break;
274 time.sleep(1)
275 except Exception, e:
276 self.log_warn('%s: unable to process waitport: %s'
277 %(ifaceobj.name, str(e)))
278
279 def _ports_enable_disable_ipv6(self, ports, enable='1'):
280 for p in ports:
281 try:
282 self.write_file('/proc/sys/net/ipv6/conf/%s' %p +
283 '/disable_ipv6', enable)
284 except Exception, e:
285 self.logger.info(str(e))
286 pass
287
288 def _add_ports(self, ifaceobj):
289 bridgeports = self._get_bridge_port_list(ifaceobj)
290 runningbridgeports = []
291 removedbridgeports = []
292
293 self.ipcmd.batch_start()
294 self._process_bridge_waitport(ifaceobj, bridgeports)
295 self.ipcmd.batch_start()
296 # Delete active ports not in the new port list
297 if not self.PERFMODE:
298 runningbridgeports = self.brctlcmd.get_bridge_ports(ifaceobj.name)
299 if runningbridgeports:
300 for bport in runningbridgeports:
301 if not bridgeports or bport not in bridgeports:
302 self.ipcmd.link_set(bport, 'nomaster')
303 removedbridgeports.append(bport)
304 else:
305 runningbridgeports = []
306 if not bridgeports:
307 self.ipcmd.batch_commit()
308 return
309 err = 0
310 for bridgeport in Set(bridgeports).difference(Set(runningbridgeports)):
311 try:
312 if not self.DRYRUN and not self.ipcmd.link_exists(bridgeport):
313 self.log_warn('%s: bridge port %s does not exist'
314 %(ifaceobj.name, bridgeport))
315 err += 1
316 continue
317 hwaddress = self.ipcmd.link_get_hwaddress(bridgeport)
318 if not self._valid_ethaddr(hwaddress):
319 self.log_warn('%s: skipping port %s, ' %(ifaceobj.name,
320 bridgeport) + 'invalid ether addr %s'
321 %hwaddress)
322 continue
323 self.ipcmd.link_set(bridgeport, 'master', ifaceobj.name)
324 self.ipcmd.addr_flush(bridgeport)
325 except Exception, e:
326 self.logger.error(str(e))
327 pass
328 try:
329 self.ipcmd.batch_commit()
330 except Exception, e:
331 self.logger.error(str(e))
332 pass
333
334 # enable ipv6 for ports that were removed
335 self._ports_enable_disable_ipv6(removedbridgeports, '0')
336 if err:
337 self.log_error('bridge configuration failed (missing ports)')
338
339
340 def _process_bridge_maxwait(self, ifaceobj, portlist):
341 maxwait = ifaceobj.get_attr_value_first('bridge-maxwait')
342 if not maxwait: return
343 try:
344 maxwait = int(maxwait)
345 except:
346 self.log_warn('%s: invalid maxwait value \'%s\'' %(ifaceobj.name,
347 maxwait))
348 return
349 if not maxwait: return
350 self.logger.info('%s: waiting for ports to go to fowarding state ..'
351 %ifaceobj.name)
352 try:
353 starttime = time.time()
354 while ((time.time() - starttime) < maxwait):
355 if all([False for p in portlist
356 if self.read_file_oneline(
357 '/sys/class/net/%s/brif/%s/state'
358 %(ifaceobj.name, p)) != '3']):
359 break;
360 time.sleep(1)
361 except Exception, e:
362 self.log_warn('%s: unable to process maxwait: %s'
363 %(ifaceobj.name, str(e)))
364
365 def _ints_to_ranges(self, ints):
366 for a, b in itertools.groupby(enumerate(ints), lambda (x, y): y - x):
367 b = list(b)
368 yield b[0][1], b[-1][1]
369
370 def _ranges_to_ints(self, rangelist):
371 """ returns expanded list of integers given set of string ranges
372 example: ['1', '2-4', '6'] returns [1, 2, 3, 4, 6]
373 """
374 result = []
375 for part in rangelist:
376 if '-' in part:
377 a, b = part.split('-')
378 a, b = int(a), int(b)
379 result.extend(range(a, b + 1))
380 else:
381 a = int(part)
382 result.append(a)
383 return result
384
385 def _diff_vids(self, vids1, vids2):
386 vids_to_add = None
387 vids_to_del = None
388
389 vids1_ints = self._ranges_to_ints(vids1)
390 vids2_ints = self._ranges_to_ints(vids2)
391 vids1_diff = Set(vids1_ints).difference(vids2_ints)
392 vids2_diff = Set(vids2_ints).difference(vids1_ints)
393 if vids1_diff:
394 vids_to_add = ['%d' %start if start == end else '%d-%d' %(start, end)
395 for start, end in self._ints_to_ranges(vids1_diff)]
396 if vids2_diff:
397 vids_to_del = ['%d' %start if start == end else '%d-%d' %(start, end)
398 for start, end in self._ints_to_ranges(vids2_diff)]
399 return (vids_to_del, vids_to_add)
400
401 def _compare_vids(self, vids1, vids2):
402 """ Returns true if the vids are same else return false """
403
404 vids1_ints = self._ranges_to_ints(vids1)
405 vids2_ints = self._ranges_to_ints(vids2)
406 if Set(vids1_ints).symmetric_difference(vids2_ints):
407 return False
408 else:
409 return True
410
411 def _set_bridge_mcqv4src_compat(self, ifaceobj):
412 #
413 # Sets old style igmp querier
414 #
415 attrval = ifaceobj.get_attr_value_first('bridge-mcqv4src')
416 if attrval:
417 running_mcqv4src = {}
418 if not self.PERFMODE:
419 running_mcqv4src = self.brctlcmd.get_mcqv4src(ifaceobj.name)
420 mcqs = {}
421 srclist = attrval.split()
422 for s in srclist:
423 k, v = s.split('=')
424 mcqs[k] = v
425
426 k_to_del = Set(running_mcqv4src.keys()).difference(mcqs.keys())
427 for v in k_to_del:
428 self.brctlcmd.del_mcqv4src(ifaceobj.name, v)
429 for v in mcqs.keys():
430 self.brctlcmd.set_mcqv4src(ifaceobj.name, v, mcqs[v])
431
432 def _get_running_vidinfo(self):
433 if self._running_vidinfo_valid:
434 return self._running_vidinfo
435 self._running_vidinfo = {}
436 if not self.PERFMODE:
437 self._running_vidinfo = self.ipcmd.bridge_port_vids_get_all()
438 self._running_vidinfo_valid = True
439 return self._running_vidinfo
440
441 def _flush_running_vidinfo(self):
442 self._running_vidinfo = {}
443 self._running_vidinfo_valid = False
444
445 def _set_bridge_vidinfo_compat(self, ifaceobj):
446 #
447 # Supports old style vlan vid info format
448 # for compatibility
449 #
450
451 # Handle bridge vlan attrs
452 running_vidinfo = self._get_running_vidinfo()
453
454 # Install pvids
455 attrval = ifaceobj.get_attr_value_first('bridge-port-pvids')
456 if attrval:
457 portlist = self.parse_port_list(attrval)
458 if not portlist:
459 self.log_warn('%s: could not parse \'%s %s\''
460 %(ifaceobj.name, attrname, attrval))
461 return
462 for p in portlist:
463 try:
464 (port, pvid) = p.split('=')
465 running_pvid = running_vidinfo.get(port, {}).get('pvid')
466 if running_pvid:
467 if running_pvid == pvid:
468 continue
469 else:
470 self.ipcmd.bridge_port_pvid_del(port, running_pvid)
471 self.ipcmd.bridge_port_pvid_add(port, pvid)
472 except Exception, e:
473 self.log_warn('%s: failed to set pvid `%s` (%s)'
474 %(ifaceobj.name, p, str(e)))
475
476 # install port vids
477 attrval = ifaceobj.get_attr_value_first('bridge-port-vids')
478 if attrval:
479 portlist = self.parse_port_list(attrval)
480 if not portlist:
481 self.log_warn('%s: could not parse \'%s %s\''
482 %(ifaceobj.name, attrname, attrval))
483 return
484 for p in portlist:
485 try:
486 (port, val) = p.split('=')
487 vids = val.split(',')
488 if running_vidinfo.get(port):
489 (vids_to_del, vids_to_add) = \
490 self._diff_vids(vids,
491 running_vidinfo.get(port).get('vlan'))
492 if vids_to_del:
493 self.ipcmd.bridge_port_vids_del(port, vids_to_del)
494 if vids_to_add:
495 self.ipcmd.bridge_port_vids_add(port, vids_to_add)
496 else:
497 self.ipcmd.bridge_port_vids_add(port, vids)
498 except Exception, e:
499 self.log_warn('%s: failed to set vid `%s` (%s)'
500 %(ifaceobj.name, p, str(e)))
501
502 # install vids
503 # XXX: Commenting out this code for now because it was decided
504 # that this is not needed
505 #attrval = ifaceobj.get_attr_value_first('bridge-vids')
506 #if attrval:
507 # vids = re.split(r'[\s\t]\s*', attrval)
508 # if running_vidinfo.get(ifaceobj.name):
509 # (vids_to_del, vids_to_add) = \
510 # self._diff_vids(vids,
511 # running_vidinfo.get(ifaceobj.name).get('vlan'))
512 # if vids_to_del:
513 # self.ipcmd.bridge_vids_del(ifaceobj.name, vids_to_del)
514 # if vids_to_add:
515 # self.ipcmd.bridge_vids_add(ifaceobj.name, vids_to_add)
516 # else:
517 # self.ipcmd.bridge_vids_add(ifaceobj.name, vids)
518 #else:
519 # running_vids = running_vidinfo.get(ifaceobj.name)
520 # if running_vids:
521 # self.ipcmd.bridge_vids_del(ifaceobj.name, running_vids)
522
523 def _apply_bridge_settings(self, ifaceobj):
524 try:
525 stp = ifaceobj.get_attr_value_first('bridge-stp')
526 if stp:
527 self.brctlcmd.set_stp(ifaceobj.name, stp)
528 else:
529 # If stp not specified and running stp state on, set it to off
530 running_stp_state = self.read_file_oneline(
531 '/sys/class/net/%s/bridge/stp_state' %ifaceobj.name)
532 if running_stp_state and running_stp_state != '0':
533 self.brctlcmd.set_stp(ifaceobj.name, 'no')
534
535 if ifaceobj.get_attr_value_first('bridge-vlan-aware') == 'yes':
536 self.write_file('/sys/class/net/%s/bridge/vlan_filtering'
537 %ifaceobj.name, '1')
538 # Use the brctlcmd bulk set method: first build a dictionary
539 # and then call set
540 bridgeattrs = { k:v for k,v in
541 {'ageing' :
542 ifaceobj.get_attr_value_first('bridge-ageing'),
543 'bridgeprio' :
544 ifaceobj.get_attr_value_first(
545 'bridge-bridgeprio'),
546 'fd' :
547 ifaceobj.get_attr_value_first('bridge-fd'),
548 'gcint' :
549 ifaceobj.get_attr_value_first('bridge-gcint'),
550 'hello' :
551 ifaceobj.get_attr_value_first('bridge-hello'),
552 'maxage' :
553 ifaceobj.get_attr_value_first('bridge-maxage'),
554 'mclmc' :
555 ifaceobj.get_attr_value_first('bridge-mclmc'),
556 'mcrouter' :
557 ifaceobj.get_attr_value_first(
558 'bridge-mcrouter'),
559 'mcsnoop' :
560 ifaceobj.get_attr_value_first('bridge-mcsnoop'),
561 'mcsqc' :
562 ifaceobj.get_attr_value_first('bridge-mcsqc'),
563 'mcqifaddr' :
564 ifaceobj.get_attr_value_first(
565 'bridge-mcqifaddr'),
566 'mcquerier' :
567 ifaceobj.get_attr_value_first(
568 'bridge-mcquerier'),
569 'hashel' :
570 ifaceobj.get_attr_value_first('bridge-hashel'),
571 'hashmax' :
572 ifaceobj.get_attr_value_first('bridge-hashmax'),
573 'mclmi' :
574 ifaceobj.get_attr_value_first('bridge-mclmi'),
575 'mcmi' :
576 ifaceobj.get_attr_value_first('bridge-mcmi'),
577 'mcqpi' :
578 ifaceobj.get_attr_value_first('bridge-mcqpi'),
579 'mcqi' :
580 ifaceobj.get_attr_value_first('bridge-mcqi'),
581 'mcqri' :
582 ifaceobj.get_attr_value_first('bridge-mcqri'),
583 'mcsqi' :
584 ifaceobj.get_attr_value_first('bridge-mcsqi')
585 }.items()
586 if v }
587 if bridgeattrs:
588 self.brctlcmd.set_bridge_attrs(ifaceobj.name, bridgeattrs)
589 portattrs = {}
590 for attrname, dstattrname in {'bridge-pathcosts' : 'pathcost',
591 'bridge-portprios' : 'portprio',
592 'bridge-portmcrouter' : 'portmcrouter',
593 'bridge-portmcfl' : 'portmcfl'}.items():
594 attrval = ifaceobj.get_attr_value_first(attrname)
595 if not attrval:
596 continue
597 portlist = self.parse_port_list(attrval)
598 if not portlist:
599 self.log_warn('%s: could not parse \'%s %s\''
600 %(ifaceobj.name, attrname, attrval))
601 continue
602 for p in portlist:
603 try:
604 (port, val) = p.split('=')
605 if not portattrs.get(port):
606 portattrs[port] = {}
607 portattrs[port].update({dstattrname : val})
608 except Exception, e:
609 self.log_warn('%s: could not parse %s (%s)'
610 %(ifaceobj.name, attrname, str(e)))
611 for port, attrdict in portattrs.iteritems():
612 try:
613 self.brctlcmd.set_bridgeport_attrs(ifaceobj.name, port,
614 attrdict)
615 except Exception, e:
616 self.log_warn('%s: %s', str(e))
617 pass
618 self._set_bridge_vidinfo_compat(ifaceobj)
619 self._set_bridge_mcqv4src_compat(ifaceobj)
620 self._process_bridge_maxwait(ifaceobj,
621 self._get_bridge_port_list(ifaceobj))
622 except Exception, e:
623 self.log_warn(str(e))
624
625 def _check_vids(self, ifaceobj, vids):
626 ret = True
627 for v in vids:
628 if '-' in v:
629 va, vb = v.split('-')
630 va, vb = int(va), int(vb)
631 if (self._handle_reserved_vlan(va, ifaceobj.name) or
632 self._handle_reserved_vlan(vb, ifaceobj.name)):
633 ret = False
634 else:
635 va = int(v)
636 if self._handle_reserved_vlan(va, ifaceobj.name):
637 ret = False
638 return ret
639
640 def _apply_bridge_vids(self, bportifaceobj, vids, running_vids, isbridge):
641 try:
642 if not self._check_vids(bportifaceobj, vids):
643 return
644 if running_vids:
645 (vids_to_del, vids_to_add) = \
646 self._diff_vids(vids, running_vids)
647 if vids_to_del:
648 self.ipcmd.bridge_vids_del(bportifaceobj.name,
649 vids_to_del, isbridge)
650 if vids_to_add:
651 self.ipcmd.bridge_vids_add(bportifaceobj.name,
652 vids_to_add, isbridge)
653 else:
654 self.ipcmd.bridge_vids_add(bportifaceobj.name, vids, isbridge)
655 except Exception, e:
656 self.log_warn('%s: failed to set vid `%s` (%s)'
657 %(bportifaceobj.name, str(vids), str(e)))
658
659 def _apply_bridge_port_pvids(self, bportifaceobj, pvid, running_pvid):
660 # Install pvids
661 try:
662 if running_pvid:
663 if running_pvid != pvid:
664 self.ipcmd.bridge_port_pvid_del(bportifaceobj.name,
665 running_pvid)
666 self.ipcmd.bridge_port_pvid_add(bportifaceobj.name, pvid)
667 else:
668 self.ipcmd.bridge_port_pvid_add(bportifaceobj.name, pvid)
669 except Exception, e:
670 self.log_warn('%s: failed to set pvid `%s` (%s)'
671 %(bportifaceobj.name, pvid, str(e)))
672
673 def _apply_bridge_vlan_aware_port_settings_all(self, bportifaceobj,
674 bridge_vids=None,
675 bridge_pvid=None):
676 running_vidinfo = self._get_running_vidinfo()
677 vids = None
678 pvids = None
679 bport_access = bportifaceobj.get_attr_value_first('bridge-access')
680 if bport_access:
681 vids = re.split(r'[\s\t]\s*', bport_access)
682 pvids = vids
683 else:
684 bport_vids = bportifaceobj.get_attr_value_first('bridge-vids')
685 if bport_vids:
686 vids = re.split(r'[\s\t,]\s*', bport_vids)
687
688 bport_pvids = bportifaceobj.get_attr_value_first('bridge-pvid')
689 if bport_pvids:
690 pvids = re.split(r'[\s\t]\s*', bport_pvids)
691
692 if pvids:
693 self._apply_bridge_port_pvids(bportifaceobj, pvids[0],
694 running_vidinfo.get(bportifaceobj.name, {}).get('pvid'))
695 elif bridge_pvid:
696 self._apply_bridge_port_pvids(bportifaceobj,
697 bridge_pvid, running_vidinfo.get(bportifaceobj.name,
698 {}).get('pvid'))
699 # XXX: default pvid is already one
700 #else:
701 # self._apply_bridge_port_pvids(bportifaceobj,
702 # '1', running_vidinfo.get(bportifaceobj.name,
703 # {}).get('pvid'))
704
705 if vids:
706 self._apply_bridge_vids(bportifaceobj, vids,
707 running_vidinfo.get(bportifaceobj.name,
708 {}).get('vlan'), False)
709 elif bridge_vids:
710 self._apply_bridge_vids(bportifaceobj,
711 bridge_vids, running_vidinfo.get(
712 bportifaceobj.name, {}).get('vlan'), False)
713
714
715 def _apply_bridge_port_settings(self, bportifaceobj, bridgename=None,
716 bridgeifaceobj=None):
717 if not bridgename and bridgeifaceobj:
718 bridgename = bridgeifaceobj.name
719 # Set other stp and igmp attributes
720 portattrs = {}
721 for attrname, dstattrname in {
722 'bridge-pathcosts' : 'pathcost',
723 'bridge-portprios' : 'portprio',
724 'bridge-portmcrouter' : 'portmcrouter',
725 'bridge-portmcfl' : 'portmcfl'}.items():
726 attrval = bportifaceobj.get_attr_value_first(attrname)
727 if not attrval:
728 # Check if bridge has that attribute
729 #if bridgeifaceobj:
730 # attrval = bridgeifaceobj.get_attr_value_first(attrname)
731 # if not attrval:
732 # continue
733 #else:
734 continue
735 portattrs[dstattrname] = attrval
736 try:
737 self.brctlcmd.set_bridgeport_attrs(bridgename,
738 bportifaceobj.name, portattrs)
739 except Exception, e:
740 self.log_warn(str(e))
741
742 def _apply_bridge_port_settings_all(self, ifaceobj,
743 ifaceobj_getfunc=None):
744 err = False
745 bridge_vlan_aware = ifaceobj.get_attr_value_first(
746 'bridge-vlan-aware')
747 if bridge_vlan_aware and bridge_vlan_aware == 'yes':
748 bridge_vlan_aware = True
749 else:
750 bridge_vlan_aware = False
751
752 if (ifaceobj.get_attr_value_first('bridge-port-vids') and
753 ifaceobj.get_attr_value_first('bridge-port-pvids')):
754 # Old style bridge port vid info
755 # skip new style setting on ports
756 return
757 self.logger.info('%s: applying bridge configuration '
758 %ifaceobj.name + 'specific to ports')
759
760 bridge_vids = ifaceobj.get_attr_value_first('bridge-vids')
761 if bridge_vids:
762 bridge_vids = re.split(r'[\s\t,]\s*', bridge_vids)
763 else:
764 bridge_vids = None
765
766 bridge_pvid = ifaceobj.get_attr_value_first('bridge-pvid')
767 if bridge_pvid:
768 bridge_pvid = re.split(r'[\s\t]\s*', bridge_pvid)[0]
769 else:
770 bridge_pvid = None
771
772 bridgeports = self._get_bridge_port_list(ifaceobj)
773 if not bridgeports:
774 self.logger.debug('%s: cannot find bridgeports' %ifaceobj.name)
775 return
776 for bport in bridgeports:
777 # Use the brctlcmd bulk set method: first build a dictionary
778 # and then call set
779 if not self.ipcmd.bridge_port_exists(ifaceobj.name, bport):
780 self.logger.info('%s: skipping bridge config' %ifaceobj.name +
781 ' for port %s (missing port)' %bport)
782 continue
783 self.logger.info('%s: processing bridge config for port %s'
784 %(ifaceobj.name, bport))
785 bportifaceobjlist = ifaceobj_getfunc(bport)
786 if not bportifaceobjlist:
787 continue
788 for bportifaceobj in bportifaceobjlist:
789 # Dont process bridge port if it already has been processed
790 if bportifaceobj.priv_flags & self._BRIDGE_PORT_PROCESSED:
791 continue
792 try:
793 # Add attributes specific to the vlan aware bridge
794 if bridge_vlan_aware:
795 self._apply_bridge_vlan_aware_port_settings_all(
796 bportifaceobj, bridge_vids, bridge_pvid)
797 self._apply_bridge_port_settings(bportifaceobj,
798 bridgeifaceobj=ifaceobj)
799 except Exception, e:
800 err = True
801 self.logger.warn('%s: %s' %(ifaceobj.name, str(e)))
802 pass
803 if err:
804 raise Exception('%s: errors applying port settings' %ifaceobj.name)
805
806 def _up(self, ifaceobj, ifaceobj_getfunc=None):
807 # Check if bridge port
808 bridgename = self.ipcmd.bridge_port_get_bridge_name(ifaceobj.name)
809 if bridgename:
810 if self.ipcmd.bridge_is_vlan_aware(bridgename):
811 bridge_vids = self._get_bridge_vids(bridgename,
812 ifaceobj_getfunc)
813 bridge_pvid = self._get_bridge_pvid(bridgename,
814 ifaceobj_getfunc)
815 self._apply_bridge_vlan_aware_port_settings_all(ifaceobj,
816 bridge_vids,
817 bridge_pvid)
818 self._apply_bridge_port_settings(ifaceobj, bridgename=bridgename)
819 ifaceobj.priv_flags |= self._BRIDGE_PORT_PROCESSED
820 return
821 if not self._is_bridge(ifaceobj):
822 return
823 err = False
824 errstr = ''
825 running_ports = ''
826 try:
827 if not self.PERFMODE:
828 if not self.ipcmd.link_exists(ifaceobj.name):
829 self.ipcmd.link_create(ifaceobj.name, 'bridge')
830 else:
831 self.ipcmd.link_create(ifaceobj.name, 'bridge')
832 except Exception, e:
833 raise Exception(str(e))
834 try:
835 self._add_ports(ifaceobj)
836 except Exception, e:
837 err = True
838 errstr = str(e)
839 pass
840
841 try:
842 self._apply_bridge_settings(ifaceobj)
843 except Exception, e:
844 err = True
845 errstr = str(e)
846 pass
847
848 try:
849 running_ports = self.brctlcmd.get_bridge_ports(ifaceobj.name)
850 if not running_ports:
851 return
852 # disable ipv6 for ports that were added to bridge
853 self._ports_enable_disable_ipv6(running_ports, '1')
854 self._apply_bridge_port_settings_all(ifaceobj,
855 ifaceobj_getfunc=ifaceobj_getfunc)
856 except Exception, e:
857 err = True
858 errstr = str(e)
859 pass
860 #self._flush_running_vidinfo()
861 finally:
862 if ifaceobj.link_type != ifaceLinkType.LINK_NA:
863 for p in running_ports:
864 try:
865 rtnetlink_api.rtnl_api.link_set(p, "up")
866 except Exception, e:
867 self.logger.debug('%s: %s: link set up (%s)'
868 %(ifaceobj.name, p, str(e)))
869 pass
870
871 if ifaceobj.addr_method == 'manual':
872 rtnetlink_api.rtnl_api.link_set(ifaceobj.name, "up")
873 if err:
874 raise Exception(errstr)
875
876 def _down(self, ifaceobj, ifaceobj_getfunc=None):
877 try:
878 if ifaceobj.get_attr_value_first('bridge-ports'):
879 ports = self.brctlcmd.get_bridge_ports(ifaceobj.name)
880 self.brctlcmd.delete_bridge(ifaceobj.name)
881 if ports:
882 self._ports_enable_disable_ipv6(ports, '0')
883 if ifaceobj.link_type != ifaceLinkType.LINK_NA:
884 map(lambda p: rtnetlink_api.rtnl_api.link_set(p,
885 "down"), ports)
886 except Exception, e:
887 self.log_error(str(e))
888
889 def _query_running_vidinfo_compat(self, ifaceobjrunning, ports):
890 running_attrs = {}
891 running_vidinfo = self._get_running_vidinfo()
892 if ports:
893 running_bridge_port_vids = ''
894 for p in ports:
895 try:
896 running_vids = running_vidinfo.get(p, {}).get('vlan')
897 if running_vids:
898 running_bridge_port_vids += ' %s=%s' %(p,
899 ','.join(running_vids))
900 except Exception:
901 pass
902 running_attrs['bridge-port-vids'] = running_bridge_port_vids
903
904 running_bridge_port_pvids = ''
905 for p in ports:
906 try:
907 running_pvids = running_vidinfo.get(p, {}).get('pvid')
908 if running_pvids:
909 running_bridge_port_pvids += ' %s=%s' %(p,
910 running_pvids)
911 except Exception:
912 pass
913 running_attrs['bridge-port-pvids'] = running_bridge_port_pvids
914
915 running_bridge_vids = running_vidinfo.get(ifaceobjrunning.name,
916 {}).get('vlan')
917 if running_bridge_vids:
918 running_attrs['bridge-vids'] = ','.join(running_bridge_vids)
919 return running_attrs
920
921 def _query_running_vidinfo(self, ifaceobjrunning, ifaceobj_getfunc,
922 bridgeports=None):
923 running_attrs = {}
924 running_vidinfo = self._get_running_vidinfo()
925 if not running_vidinfo:
926 return running_attrs
927
928 # 'bridge-vids' under the bridge is all about 'vids' on the port.
929 # so query the ports
930 running_bridgeport_vids = []
931 running_bridgeport_pvids = []
932 for bport in bridgeports:
933 vids = running_vidinfo.get(bport, {}).get('vlan')
934 if vids:
935 running_bridgeport_vids.append(' '.join(vids))
936 pvids = running_vidinfo.get(bport, {}).get('pvid')
937 if pvids:
938 running_bridgeport_pvids.append(pvids[0])
939
940 bridge_vids = None
941 if running_bridgeport_vids:
942 (vidval, freq) = Counter(running_bridgeport_vids).most_common()[0]
943 if freq == len(bridgeports):
944 running_attrs['bridge-vids'] = vidval
945 bridge_vids = vidval.split()
946
947 bridge_pvid = None
948 if running_bridgeport_pvids:
949 (vidval, freq) = Counter(running_bridgeport_pvids).most_common()[0]
950 if freq == len(bridgeports) and vidval != '1':
951 running_attrs['bridge-pvid'] = vidval
952 bridge_pvid = vidval.split()
953
954 # Go through all bridge ports and find their vids
955 for bport in bridgeports:
956 bportifaceobj = ifaceobj_getfunc(bport)
957 if not bportifaceobj:
958 continue
959 bport_vids = None
960 bport_pvids = None
961 vids = running_vidinfo.get(bport, {}).get('vlan')
962 if vids and vids != bridge_vids:
963 bport_vids = vids
964 pvids = running_vidinfo.get(bport, {}).get('pvid')
965 if pvids and pvids[0] != bridge_pvid:
966 bport_pvids = pvids
967 if not bport_vids and bport_pvids and bport_pvids[0] != '1':
968 bportifaceobj[0].replace_config('bridge-access', bport_pvids[0])
969 else:
970 if bport_pvids and bport_pvids[0] != '1':
971 bportifaceobj[0].replace_config('bridge-pvid', bport_pvids[0])
972 else:
973 # delete any stale bridge-vids under ports
974 bportifaceobj[0].delete_config('bridge-pvid')
975 if bport_vids:
976 bportifaceobj[0].replace_config('bridge-vids',
977 ' '.join(bport_vids))
978 else:
979 # delete any stale bridge-vids under ports
980 bportifaceobj[0].delete_config('bridge-vids')
981 return running_attrs
982
983 def _query_running_mcqv4src(self, ifaceobjrunning):
984 running_mcqv4src = self.brctlcmd.get_mcqv4src(ifaceobjrunning.name)
985 mcqs = ['%s=%s' %(v, i) for v, i in running_mcqv4src.items()]
986 mcqs.sort()
987 mcq = ' '.join(mcqs)
988 return mcq
989
990 def _query_running_attrs(self, ifaceobjrunning, ifaceobj_getfunc,
991 bridge_vlan_aware=False):
992 bridgeattrdict = {}
993 userspace_stp = 0
994 ports = None
995 skip_kernel_stp_attrs = 0
996
997 if self.sysctl_get('net.bridge.bridge-stp-user-space') == '1':
998 userspace_stp = 1
999
1000 tmpbridgeattrdict = self.brctlcmd.get_bridge_attrs(ifaceobjrunning.name)
1001 if not tmpbridgeattrdict:
1002 self.logger.warn('%s: unable to get bridge attrs'
1003 %ifaceobjrunning.name)
1004 return bridgeattrdict
1005
1006 # Fill bridge_ports and bridge stp attributes first
1007 ports = tmpbridgeattrdict.get('ports')
1008 if ports:
1009 bridgeattrdict['bridge-ports'] = [' '.join(ports.keys())]
1010 stp = tmpbridgeattrdict.get('stp', 'no')
1011 if stp != self.get_mod_subattr('bridge-stp', 'default'):
1012 bridgeattrdict['bridge-stp'] = [stp]
1013
1014 if stp == 'yes' and userspace_stp:
1015 skip_kernel_stp_attrs = 1
1016
1017 # pick all other attributes
1018 for k,v in tmpbridgeattrdict.items():
1019 if not v:
1020 continue
1021 if k == 'ports' or k == 'stp':
1022 continue
1023
1024 if skip_kernel_stp_attrs and k[:2] != 'mc':
1025 # only include igmp attributes if kernel stp is off
1026 continue
1027 attrname = 'bridge-' + k
1028 if v != self.get_mod_subattr(attrname, 'default'):
1029 bridgeattrdict[attrname] = [v]
1030
1031 if bridge_vlan_aware:
1032 bridgevidinfo = self._query_running_vidinfo(ifaceobjrunning,
1033 ifaceobj_getfunc,
1034 ports.keys())
1035 else:
1036 bridgevidinfo = self._query_running_vidinfo_compat(ifaceobjrunning,
1037 ports)
1038 if bridgevidinfo:
1039 bridgeattrdict.update({k : [v] for k, v in bridgevidinfo.items()
1040 if v})
1041
1042 mcq = self._query_running_mcqv4src(ifaceobjrunning)
1043 if mcq:
1044 bridgeattrdict['bridge-mcqv4src'] = [mcq]
1045
1046 if skip_kernel_stp_attrs:
1047 return bridgeattrdict
1048
1049 if ports:
1050 portconfig = {'bridge-pathcosts' : '',
1051 'bridge-portprios' : ''}
1052 for p, v in ports.items():
1053 v = self.brctlcmd.get_pathcost(ifaceobjrunning.name, p)
1054 if v and v != self.get_mod_subattr('bridge-pathcosts',
1055 'default'):
1056 portconfig['bridge-pathcosts'] += ' %s=%s' %(p, v)
1057
1058 v = self.brctlcmd.get_portprio(ifaceobjrunning.name, p)
1059 if v and v != self.get_mod_subattr('bridge-portprios',
1060 'default'):
1061 portconfig['bridge-portprios'] += ' %s=%s' %(p, v)
1062
1063 bridgeattrdict.update({k : [v] for k, v in portconfig.items()
1064 if v})
1065
1066 return bridgeattrdict
1067
1068 def _query_check_mcqv4src(self, ifaceobj, ifaceobjcurr):
1069 running_mcqs = self._query_running_mcqv4src(ifaceobj)
1070 attrval = ifaceobj.get_attr_value_first('bridge-mcqv4src')
1071 if attrval:
1072 mcqs = attrval.split()
1073 mcqs.sort()
1074 mcqsout = ' '.join(mcqs)
1075 ifaceobjcurr.update_config_with_status('bridge-mcqv4src',
1076 running_mcqs, 1 if running_mcqs != mcqsout else 0)
1077
1078 def _query_check_bridge_vidinfo(self, ifaceobj, ifaceobjcurr):
1079 err = 0
1080 running_vidinfo = self._get_running_vidinfo()
1081 attrval = ifaceobj.get_attr_value_first('bridge-port-vids')
1082 if attrval:
1083 running_bridge_port_vids = ''
1084 portlist = self.parse_port_list(attrval)
1085 if not portlist:
1086 self.log_warn('%s: could not parse \'%s %s\''
1087 %(ifaceobj.name, attrname, attrval))
1088 return
1089 err = 0
1090 for p in portlist:
1091 try:
1092 (port, val) = p.split('=')
1093 vids = val.split(',')
1094 running_vids = running_vidinfo.get(port, {}).get('vlan')
1095 if running_vids:
1096 if not self._compare_vids(vids, running_vids):
1097 err += 1
1098 running_bridge_port_vids += ' %s=%s' %(port,
1099 ','.join(running_vids))
1100 else:
1101 running_bridge_port_vids += ' %s' %p
1102 else:
1103 err += 1
1104 except Exception, e:
1105 self.log_warn('%s: failure checking vid %s (%s)'
1106 %(ifaceobj.name, p, str(e)))
1107 if err:
1108 ifaceobjcurr.update_config_with_status('bridge-port-vids',
1109 running_bridge_port_vids, 1)
1110 else:
1111 ifaceobjcurr.update_config_with_status('bridge-port-vids',
1112 attrval, 0)
1113
1114 attrval = ifaceobj.get_attr_value_first('bridge-port-pvids')
1115 if attrval:
1116 portlist = self.parse_port_list(attrval)
1117 if not portlist:
1118 self.log_warn('%s: could not parse \'%s %s\''
1119 %(ifaceobj.name, attrname, attrval))
1120 return
1121 running_bridge_port_pvids = ''
1122 err = 0
1123 for p in portlist:
1124 try:
1125 (port, pvid) = p.split('=')
1126 running_pvid = running_vidinfo.get(port, {}).get('pvid')
1127 if running_pvid and running_pvid == pvid:
1128 running_bridge_port_pvids += ' %s' %p
1129 else:
1130 err += 1
1131 running_bridge_port_pvids += ' %s=%s' %(port,
1132 running_pvid)
1133 except Exception, e:
1134 self.log_warn('%s: failure checking pvid %s (%s)'
1135 %(ifaceobj.name, pvid, str(e)))
1136 if err:
1137 ifaceobjcurr.update_config_with_status('bridge-port-pvids',
1138 running_bridge_port_pvids, 1)
1139 else:
1140 ifaceobjcurr.update_config_with_status('bridge-port-pvids',
1141 running_bridge_port_pvids, 0)
1142
1143 # XXX: No need to check for bridge-vids on the bridge
1144 # This is used by the ports. The vids on the bridge
1145 # come from the vlan interfaces on the bridge.
1146 #
1147 attrval = ifaceobj.get_attr_value_first('bridge-vids')
1148 #if attrval:
1149 # vids = re.split(r'[\s\t]\s*', attrval)
1150 # running_vids = running_vidinfo.get(ifaceobj.name, {}).get('vlan')
1151 # if running_vids:
1152 # if self._compare_vids(vids, running_vids):
1153 # ifaceobjcurr.update_config_with_status('bridge-vids',
1154 # attrval, 0)
1155 # else:
1156 # ifaceobjcurr.update_config_with_status('bridge-vids',
1157 # ','.join(running_vids), 1)
1158 # else:
1159 # ifaceobjcurr.update_config_with_status('bridge-vids', attrval,
1160 # 1)
1161 if attrval:
1162 ifaceobjcurr.update_config_with_status('bridge-vids', attrval, -1)
1163
1164 def _query_check_bridge(self, ifaceobj, ifaceobjcurr,
1165 ifaceobj_getfunc=None):
1166 if not self._is_bridge(ifaceobj):
1167 return
1168 if not self.brctlcmd.bridge_exists(ifaceobj.name):
1169 self.logger.info('%s: bridge: does not exist' %(ifaceobj.name))
1170 return
1171
1172 ifaceattrs = self.dict_key_subset(ifaceobj.config,
1173 self.get_mod_attrs())
1174 if not ifaceattrs:
1175 return
1176 try:
1177 runningattrs = self.brctlcmd.get_bridge_attrs(ifaceobj.name)
1178 if not runningattrs:
1179 self.logger.debug('%s: bridge: unable to get bridge attrs'
1180 %ifaceobj.name)
1181 runningattrs = {}
1182 except Exception, e:
1183 self.logger.warn(str(e))
1184 runningattrs = {}
1185 filterattrs = ['bridge-vids', 'bridge-port-vids',
1186 'bridge-port-pvids']
1187 for k in Set(ifaceattrs).difference(filterattrs):
1188 # get the corresponding ifaceobj attr
1189 v = ifaceobj.get_attr_value_first(k)
1190 if not v:
1191 continue
1192 rv = runningattrs.get(k[7:])
1193 if k == 'bridge-mcqv4src':
1194 continue
1195 if k == 'bridge-vlan-aware' and v == 'yes':
1196 if self.ipcmd.bridge_is_vlan_aware(ifaceobj.name):
1197 ifaceobjcurr.update_config_with_status('bridge-vlan-aware',
1198 v, 0)
1199 else:
1200 ifaceobjcurr.update_config_with_status('bridge-vlan-aware',
1201 v, 1)
1202 elif k == 'bridge-stp':
1203 # special case stp compare because it may
1204 # contain more than one valid values
1205 stp_on_vals = ['on', 'yes']
1206 stp_off_vals = ['off']
1207 if ((v in stp_on_vals and rv in stp_on_vals) or
1208 (v in stp_off_vals and rv in stp_off_vals)):
1209 ifaceobjcurr.update_config_with_status('bridge-stp',
1210 v, 0)
1211 else:
1212 ifaceobjcurr.update_config_with_status('bridge-stp',
1213 v, 1)
1214 elif k == 'bridge-ports':
1215 # special case ports because it can contain regex or glob
1216 running_port_list = rv.keys() if rv else []
1217 bridge_port_list = self._get_bridge_port_list(ifaceobj)
1218 if not running_port_list and not bridge_port_list:
1219 continue
1220 portliststatus = 1
1221 if running_port_list and bridge_port_list:
1222 difference = set(running_port_list
1223 ).symmetric_difference(bridge_port_list)
1224 if not difference:
1225 portliststatus = 0
1226 ifaceobjcurr.update_config_with_status('bridge-ports',
1227 ' '.join(running_port_list)
1228 if running_port_list else '', portliststatus)
1229 elif (k == 'bridge-pathcosts' or
1230 k == 'bridge-portprios' or k == 'bridge-portmcrouter'
1231 or k == 'bridge-portmcfl'):
1232 brctlcmdattrname = k[11:].rstrip('s')
1233 # for port attributes, the attributes are in a list
1234 # <portname>=<portattrvalue>
1235 status = 0
1236 currstr = ''
1237 vlist = self.parse_port_list(v)
1238 if not vlist:
1239 continue
1240 for vlistitem in vlist:
1241 try:
1242 (p, v) = vlistitem.split('=')
1243 currv = self.brctlcmd.get_bridgeport_attr(
1244 ifaceobj.name, p,
1245 brctlcmdattrname)
1246 if currv:
1247 currstr += ' %s=%s' %(p, currv)
1248 else:
1249 currstr += ' %s=%s' %(p, 'None')
1250 if currv != v:
1251 status = 1
1252 except Exception, e:
1253 self.log_warn(str(e))
1254 pass
1255 ifaceobjcurr.update_config_with_status(k, currstr, status)
1256 elif not rv:
1257 ifaceobjcurr.update_config_with_status(k, 'notfound', 1)
1258 continue
1259 elif v != rv:
1260 ifaceobjcurr.update_config_with_status(k, rv, 1)
1261 else:
1262 ifaceobjcurr.update_config_with_status(k, rv, 0)
1263
1264 self._query_check_bridge_vidinfo(ifaceobj, ifaceobjcurr)
1265
1266 self._query_check_mcqv4src(ifaceobj, ifaceobjcurr)
1267
1268 def _get_bridge_vids(self, bridgename, ifaceobj_getfunc):
1269 ifaceobjs = ifaceobj_getfunc(bridgename)
1270 for ifaceobj in ifaceobjs:
1271 vids = ifaceobj.get_attr_value_first('bridge-vids')
1272 if vids: return re.split(r'[\s\t,]\s*', vids)
1273 return None
1274
1275 def _get_bridge_pvid(self, bridgename, ifaceobj_getfunc):
1276 ifaceobjs = ifaceobj_getfunc(bridgename)
1277 pvid = None
1278 for ifaceobj in ifaceobjs:
1279 pvid = ifaceobj.get_attr_value_first('bridge-pvid')
1280 return pvid
1281
1282 def _get_bridge_name(self, ifaceobj):
1283 return self.ipcmd.bridge_port_get_bridge_name(ifaceobj.name)
1284
1285 def _query_check_bridge_port_vidinfo(self, ifaceobj, ifaceobjcurr,
1286 ifaceobj_getfunc, bridgename):
1287 running_vidinfo = self._get_running_vidinfo()
1288
1289 attr_name = 'bridge-access'
1290 vids = ifaceobj.get_attr_value_first(attr_name)
1291 if vids:
1292 running_pvids = running_vidinfo.get(ifaceobj.name,
1293 {}).get('pvid')
1294 running_vids = running_vidinfo.get(ifaceobj.name,
1295 {}).get('vlan')
1296 if (not running_pvids or running_pvids != vids or
1297 running_vids):
1298 ifaceobjcurr.update_config_with_status(attr_name,
1299 running_pvids, 1)
1300 else:
1301 ifaceobjcurr.update_config_with_status(attr_name, vids, 0)
1302 return
1303
1304 attr_name = 'bridge-vids'
1305 vids = ifaceobj.get_attr_value_first(attr_name)
1306 if vids:
1307 vids = re.split(r'[\s\t]\s*', vids)
1308 running_vids = running_vidinfo.get(ifaceobj.name,
1309 {}).get('vlan')
1310 if not running_vids or not self._compare_vids(vids, running_vids):
1311 ifaceobjcurr.update_config_with_status(attr_name,
1312 ' '.join(running_vids), 1)
1313 else:
1314 ifaceobjcurr.update_config_with_status(attr_name,
1315 ' '.join(running_vids), 0)
1316 else:
1317 # check if it matches the bridge vids
1318 bridge_vids = self._get_bridge_vids(bridgename, ifaceobj_getfunc)
1319 running_vids = running_vidinfo.get(ifaceobj.name,
1320 {}).get('vlan')
1321 if (bridge_vids and (not running_vids or
1322 not self._compare_vids(bridge_vids, running_vids))):
1323 ifaceobjcurr.status = ifaceStatus.ERROR
1324 ifaceobjcurr.status_str = 'bridge vid error'
1325
1326 running_pvid = running_vidinfo.get(ifaceobj.name,
1327 {}).get('pvid')
1328 attr_name = 'bridge-pvid'
1329 pvid = ifaceobj.get_attr_value_first(attr_name)
1330 if pvid:
1331 if running_pvid and running_pvid == pvid:
1332 ifaceobjcurr.update_config_with_status(attr_name,
1333 running_pvid, 0)
1334 else:
1335 ifaceobjcurr.update_config_with_status(attr_name,
1336 running_pvid, 1)
1337 elif not running_pvid or running_pvid != '1':
1338 ifaceobjcurr.status = ifaceStatus.ERROR
1339 ifaceobjcurr.status_str = 'bridge pvid error'
1340
1341 def _query_check_bridge_port(self, ifaceobj, ifaceobjcurr,
1342 ifaceobj_getfunc):
1343 if not self._is_bridge_port(ifaceobj):
1344 # Mark all bridge attributes as failed
1345 ifaceobjcurr.check_n_update_config_with_status_many(ifaceobj,
1346 ['bridge-vids', 'bridge-pvid', 'bridge-access',
1347 'bridge-pathcosts', 'bridge-portprios',
1348 'bridge-portmcrouter',
1349 'bridge-portmcfl'], 1)
1350 return
1351 bridgename = self._get_bridge_name(ifaceobj)
1352 if not bridgename:
1353 self.logger.warn('%s: unable to determine bridge name'
1354 %ifaceobj.name)
1355 return
1356
1357 if self.ipcmd.bridge_is_vlan_aware(bridgename):
1358 self._query_check_bridge_port_vidinfo(ifaceobj, ifaceobjcurr,
1359 ifaceobj_getfunc,
1360 bridgename)
1361 for attr, dstattr in {'bridge-pathcosts' : 'pathcost',
1362 'bridge-portprios' : 'priority',
1363 'bridge-portmcrouter' : 'mcrouter',
1364 'bridge-portmcfl' : 'mcfl' }.items():
1365 attrval = ifaceobj.get_attr_value_first(attr)
1366 if not attrval:
1367 continue
1368
1369 try:
1370 running_attrval = self.brctlcmd.get_bridgeport_attr(
1371 bridgename, ifaceobj.name, dstattr)
1372 if running_attrval != attrval:
1373 ifaceobjcurr.update_config_with_status(attr,
1374 running_attrval, 1)
1375 else:
1376 ifaceobjcurr.update_config_with_status(attr,
1377 running_attrval, 0)
1378 except Exception, e:
1379 self.log_warn('%s: %s' %(ifaceobj.name, str(e)))
1380
1381 def _query_check(self, ifaceobj, ifaceobjcurr, ifaceobj_getfunc=None):
1382 if self._is_bridge(ifaceobj):
1383 self._query_check_bridge(ifaceobj, ifaceobjcurr)
1384 else:
1385 self._query_check_bridge_port(ifaceobj, ifaceobjcurr,
1386 ifaceobj_getfunc)
1387
1388 def _query_running_bridge(self, ifaceobjrunning, ifaceobj_getfunc):
1389 if self.ipcmd.bridge_is_vlan_aware(ifaceobjrunning.name):
1390 ifaceobjrunning.update_config('bridge-vlan-aware', 'yes')
1391 ifaceobjrunning.update_config_dict(self._query_running_attrs(
1392 ifaceobjrunning,
1393 ifaceobj_getfunc,
1394 bridge_vlan_aware=True))
1395 else:
1396 ifaceobjrunning.update_config_dict(self._query_running_attrs(
1397 ifaceobjrunning, None))
1398
1399 def _query_running_bridge_port_attrs(self, ifaceobjrunning, bridgename):
1400 if self.sysctl_get('net.bridge.bridge-stp-user-space') == '1':
1401 return
1402
1403 v = self.brctlcmd.get_pathcost(bridgename, ifaceobjrunning.name)
1404 if v and v != self.get_mod_subattr('bridge-pathcosts', 'default'):
1405 ifaceobjrunning.update_config('bridge-pathcosts', v)
1406
1407 v = self.brctlcmd.get_pathcost(bridgename, ifaceobjrunning.name)
1408 if v and v != self.get_mod_subattr('bridge-portprios', 'default'):
1409 ifaceobjrunning.update_config('bridge-portprios', v)
1410
1411 def _query_running_bridge_port(self, ifaceobjrunning,
1412 ifaceobj_getfunc=None):
1413 bridgename = self.ipcmd.bridge_port_get_bridge_name(
1414 ifaceobjrunning.name)
1415 bridge_vids = None
1416 bridge_pvid = None
1417 if not bridgename:
1418 self.logger.warn('%s: unable to find bridgename'
1419 %ifaceobjrunning.name)
1420 return
1421 if not self.ipcmd.bridge_is_vlan_aware(bridgename):
1422 return
1423
1424 running_vidinfo = self._get_running_vidinfo()
1425 bridge_port_vids = running_vidinfo.get(ifaceobjrunning.name,
1426 {}).get('vlan')
1427 bridge_port_pvid = running_vidinfo.get(ifaceobjrunning.name,
1428 {}).get('pvid')
1429
1430 bridgeifaceobjlist = ifaceobj_getfunc(bridgename)
1431 if bridgeifaceobjlist:
1432 bridge_vids = bridgeifaceobjlist[0].get_attr_value('bridge-vids')
1433 bridge_pvid = bridgeifaceobjlist[0].get_attr_value_first('bridge-pvid')
1434
1435 if not bridge_port_vids and bridge_port_pvid:
1436 # must be an access port
1437 if bridge_port_pvid != '1':
1438 ifaceobjrunning.update_config('bridge-access',
1439 bridge_port_pvid)
1440 else:
1441 if bridge_port_vids:
1442 if (not bridge_vids or bridge_port_vids != bridge_vids):
1443 ifaceobjrunning.update_config('bridge-vids',
1444 ' '.join(bridge_port_vids))
1445 if bridge_port_pvid and bridge_port_pvid != '1':
1446 if (not bridge_pvid or (bridge_port_pvid != bridge_pvid)):
1447 ifaceobjrunning.update_config('bridge-pvid',
1448 bridge_port_pvid)
1449 self._query_running_bridge_port_attrs(ifaceobjrunning, bridgename)
1450
1451 def _query_running(self, ifaceobjrunning, ifaceobj_getfunc=None):
1452 if self.brctlcmd.bridge_exists(ifaceobjrunning.name):
1453 self._query_running_bridge(ifaceobjrunning, ifaceobj_getfunc)
1454 elif self.brctlcmd.is_bridge_port(ifaceobjrunning.name):
1455 self._query_running_bridge_port(ifaceobjrunning, ifaceobj_getfunc)
1456
1457 _run_ops = {'pre-up' : _up,
1458 'post-down' : _down,
1459 'query-checkcurr' : _query_check,
1460 'query-running' : _query_running}
1461
1462 def get_ops(self):
1463 """ returns list of ops supported by this module """
1464 return self._run_ops.keys()
1465
1466 def _init_command_handlers(self):
1467 flags = self.get_flags()
1468 if not self.ipcmd:
1469 self.ipcmd = iproute2(**flags)
1470 if not self.brctlcmd:
1471 self.brctlcmd = brctl(**flags)
1472
1473 def run(self, ifaceobj, operation, query_ifaceobj=None,
1474 ifaceobj_getfunc=None):
1475 """ run bridge configuration on the interface object passed as
1476 argument. Can create bridge interfaces if they dont exist already
1477
1478 Args:
1479 **ifaceobj** (object): iface object
1480
1481 **operation** (str): any of 'pre-up', 'post-down', 'query-checkcurr',
1482 'query-running'
1483
1484 Kwargs:
1485 **query_ifaceobj** (object): query check ifaceobject. This is only
1486 valid when op is 'query-checkcurr'. It is an object same as
1487 ifaceobj, but contains running attribute values and its config
1488 status. The modules can use it to return queried running state
1489 of interfaces. status is success if the running state is same
1490 as user required state in ifaceobj. error otherwise.
1491 """
1492 op_handler = self._run_ops.get(operation)
1493 if not op_handler:
1494 return
1495 self._init_command_handlers()
1496 self._flush_running_vidinfo()
1497 if operation == 'query-checkcurr':
1498 op_handler(self, ifaceobj, query_ifaceobj,
1499 ifaceobj_getfunc=ifaceobj_getfunc)
1500 else:
1501 op_handler(self, ifaceobj, ifaceobj_getfunc=ifaceobj_getfunc)