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