]>
Commit | Line | Data |
---|---|---|
f82758bf RP |
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 ifupdown.iface import * | |
8 | from utilsbase import * | |
9 | import os | |
10 | import re | |
11 | import logging | |
12 | from cache import * | |
13 | ||
14 | class brctl(utilsBase): | |
15 | """ This class contains helper functions to interact with the bridgeutils | |
16 | commands """ | |
17 | ||
18 | _cache_fill_done = False | |
19 | ||
20 | def __init__(self, *args, **kargs): | |
21 | utilsBase.__init__(self, *args, **kargs) | |
22 | if self.CACHE and not brctl._cache_fill_done: | |
23 | self._bridge_fill() | |
24 | brctl._cache_fill_done = True | |
25 | ||
26 | ||
27 | def _bridge_get_mcattrs_from_sysfs(self, bridgename): | |
28 | mcattrs = {} | |
29 | mcattrmap = {'mclmc': 'multicast_last_member_count', | |
30 | 'mcrouter': 'multicast_router', | |
31 | 'mcsnoop' : 'multicast_snooping', | |
32 | 'mcsqc' : 'multicast_startup_query_count', | |
33 | 'mcqifaddr' : 'multicast_query_use_ifaddr', | |
34 | 'mcquerier' : 'multicast_querier', | |
35 | 'hashel' : 'hash_elasticity', | |
36 | 'hashmax' : 'hash_max', | |
37 | 'mclmi' : 'multicast_last_member_interval', | |
38 | 'mcmi' : 'multicast_membership_interval', | |
39 | 'mcqpi' : 'multicast_querier_interval', | |
40 | 'mcqi' : 'multicast_query_interval', | |
41 | 'mcqri' : 'multicast_query_response_interval', | |
42 | 'mcsqi' : 'multicast_startup_query_interval'} | |
43 | ||
44 | mcattrsdivby100 = ['mclmi', 'mcmi', 'mcqpi', 'mcqi', 'mcqri', 'mcsqi'] | |
45 | ||
46 | for m, s in mcattrmap.items(): | |
47 | n = self.read_file_oneline('/sys/class/net/%s/bridge/%s' | |
48 | %(bridgename, s)) | |
49 | if m in mcattrsdivby100: | |
50 | try: | |
51 | v = int(n) / 100 | |
52 | mcattrs[m] = str(v) | |
53 | except Exception, e: | |
54 | self.logger.warn('error getting mc attr %s (%s)' | |
55 | %(m, str(e))) | |
56 | pass | |
57 | else: | |
58 | mcattrs[m] = n | |
59 | return mcattrs | |
60 | ||
61 | def _bridge_attrs_fill(self, bridgename): | |
62 | battrs = {} | |
63 | bports = {} | |
64 | ||
65 | brout = self.exec_command('/sbin/brctl showstp %s' %bridgename) | |
66 | chunks = re.split(r'\n\n', brout, maxsplit=0, flags=re.MULTILINE) | |
67 | ||
68 | try: | |
69 | # Get all bridge attributes | |
70 | broutlines = chunks[0].splitlines() | |
71 | #battrs['pathcost'] = broutlines[3].split('path cost')[1].strip() | |
72 | battrs['maxage'] = broutlines[4].split( | |
73 | 'bridge max age')[1].strip().replace('.00', '') | |
74 | battrs['hello'] = broutlines[5].split( | |
75 | 'bridge hello time')[1].strip().replace('.00', | |
76 | '') | |
77 | battrs['fd'] = broutlines[6].split( | |
78 | 'bridge forward delay')[1].strip( | |
79 | ).replace('.00', '') | |
80 | battrs.update(self._bridge_get_mcattrs_from_sysfs(bridgename)) | |
81 | ||
82 | # XXX: comment this out until mc attributes become available | |
83 | # with brctl again | |
84 | #battrs['hashel'] = broutlines[10].split('hash elasticity')[1].split()[0].strip() | |
85 | #battrs['hashmax'] = broutlines[10].split('hash max')[1].strip() | |
86 | #battrs['mclmc'] = broutlines[11].split('mc last member count')[1].split()[0].strip() | |
87 | #battrs['mciqc'] = broutlines[11].split('mc init query count')[1].strip() | |
88 | #battrs['mcrouter'] = broutlines[12].split('mc router')[1].split()[0].strip() | |
89 | ##battrs['mcsnoop'] = broutlines[12].split('mc snooping')[1].strip() | |
90 | #battrs['mclmt'] = broutlines[13].split('mc last member timer')[1].split()[0].strip() | |
91 | except Exception, e: | |
92 | self.logger.warn(str(e)) | |
93 | pass | |
94 | ||
95 | linkCache.update_attrdict([bridgename, 'linkinfo'], battrs) | |
96 | ||
97 | for cidx in range(1, len(chunks)): | |
98 | bpout = chunks[cidx].lstrip('\n') | |
99 | if not bpout or bpout[0] == ' ': | |
100 | continue | |
101 | bplines = bpout.splitlines() | |
102 | pname = bplines[0].split()[0] | |
103 | bportattrs = {} | |
104 | try: | |
105 | bportattrs['pathcost'] = bplines[2].split( | |
106 | 'path cost')[1].strip() | |
107 | bportattrs['fdelay'] = bplines[4].split( | |
108 | 'forward delay timer')[1].strip() | |
109 | bportattrs['mcrouter'] = self.read_file_oneline( | |
110 | '/sys/class/net/%s/brport/multicast_router' %pname) | |
111 | bportattrs['mcfl'] = self.read_file_oneline( | |
112 | '/sys/class/net/%s/brport/multicast_fast_leave' %pname) | |
113 | ||
114 | #bportattrs['mcrouters'] = bplines[6].split('mc router')[1].split()[0].strip() | |
115 | #bportattrs['mc fast leave'] = bplines[6].split('mc fast leave')[1].strip() | |
116 | except Exception, e: | |
117 | self.logger.warn(str(e)) | |
118 | pass | |
119 | bports[pname] = bportattrs | |
120 | linkCache.update_attrdict([bridgename, 'linkinfo', 'ports'], bports) | |
121 | ||
122 | def _bridge_fill(self, bridgename=None, refresh=False): | |
123 | try: | |
124 | # if cache is already filled, return | |
125 | linkCache.get_attr([bridgename, 'linkinfo', 'fd']) | |
126 | return | |
127 | except: | |
128 | pass | |
129 | if not bridgename: | |
130 | brctlout = self.exec_command('/sbin/brctl show') | |
131 | else: | |
132 | brctlout = self.exec_command('/sbin/brctl show ' + bridgename) | |
133 | if not brctlout: | |
134 | return | |
135 | ||
136 | for bline in brctlout.splitlines()[1:]: | |
137 | bitems = bline.split() | |
138 | if len(bitems) < 2: | |
139 | continue | |
140 | try: | |
141 | linkCache.update_attrdict([bitems[0], 'linkinfo'], | |
142 | {'stp' : bitems[2]}) | |
143 | except KeyError: | |
144 | linkCache.update_attrdict([bitems[0]], | |
145 | {'linkinfo' : {'stp' : bitems[2]}}) | |
146 | self._bridge_attrs_fill(bitems[0]) | |
147 | ||
148 | def _cache_get(self, attrlist, refresh=False): | |
149 | try: | |
150 | if self.DRYRUN: | |
151 | return None | |
152 | if self.CACHE: | |
153 | if not self._cache_fill_done: | |
154 | self._bridge_fill() | |
155 | self._cache_fill_done = True | |
156 | return linkCache.get_attr(attrlist) | |
157 | if not refresh: | |
158 | return linkCache.get_attr(attrlist) | |
159 | self._bridge_fill(attrlist[0], refresh) | |
160 | return linkCache.get_attr(attrlist) | |
161 | except Exception, e: | |
162 | self.logger.debug('_cache_get(%s) : [%s]' | |
163 | %(str(attrlist), str(e))) | |
164 | pass | |
165 | return None | |
166 | ||
167 | def _cache_check(self, attrlist, value, refresh=False): | |
168 | try: | |
169 | attrvalue = self._cache_get(attrlist, refresh) | |
170 | if attrvalue and attrvalue == value: | |
171 | return True | |
172 | except Exception, e: | |
173 | self.logger.debug('_cache_check(%s) : [%s]' | |
174 | %(str(attrlist), str(e))) | |
175 | pass | |
176 | return False | |
177 | ||
178 | def _cache_update(self, attrlist, value): | |
179 | if self.DRYRUN: return | |
180 | try: | |
181 | linkCache.add_attr(attrlist, value) | |
182 | except: | |
183 | pass | |
184 | ||
185 | def _cache_delete(self, attrlist): | |
186 | if self.DRYRUN: return | |
187 | try: | |
188 | linkCache.del_attr(attrlist) | |
189 | except: | |
190 | pass | |
191 | ||
192 | def _cache_invalidate(self): | |
193 | if self.DRYRUN: return | |
194 | linkCache.invalidate() | |
195 | ||
196 | def create_bridge(self, bridgename): | |
197 | if self.bridge_exists(bridgename): | |
198 | return | |
199 | self.exec_command('/sbin/brctl addbr %s' %bridgename) | |
200 | self._cache_update([bridgename], {}) | |
201 | ||
202 | def delete_bridge(self, bridgename): | |
203 | if not self.bridge_exists(bridgename): | |
204 | return | |
205 | self.exec_command('/sbin/brctl delbr %s' %bridgename) | |
206 | self._cache_invalidate() | |
207 | ||
208 | def add_bridge_port(self, bridgename, bridgeportname): | |
209 | """ Add port to bridge """ | |
210 | ports = self._cache_get([bridgename, 'linkinfo', 'ports']) | |
211 | if ports and ports.get(bridgeportname): | |
212 | return | |
213 | self.exec_command('/sbin/brctl addif ' + bridgename + ' ' + | |
214 | bridgeportname) | |
215 | self._cache_update([bridgename, 'linkinfo', 'ports', | |
216 | bridgeportname], {}) | |
217 | ||
218 | def delete_bridge_port(self, bridgename, bridgeportname): | |
219 | """ Delete port from bridge """ | |
220 | ports = self._cache_get([bridgename, 'linkinfo', 'ports']) | |
221 | if not ports or not ports.get(bridgeportname): | |
222 | return | |
223 | self.exec_command('/sbin/brctl delif ' + bridgename + ' ' + | |
224 | bridgeportname) | |
225 | self._cache_delete([bridgename, 'linkinfo', 'ports', | |
226 | 'bridgeportname']) | |
227 | ||
228 | def set_bridgeport_attrs(self, bridgename, bridgeportname, attrdict): | |
229 | portattrs = self._cache_get([bridgename, 'linkinfo', | |
230 | 'ports', bridgeportname]) | |
231 | if portattrs == None: portattrs = {} | |
232 | for k, v in attrdict.iteritems(): | |
233 | if self.CACHE: | |
234 | curval = portattrs.get(k) | |
235 | if curval and curval == v: | |
236 | continue | |
237 | self.exec_command('/sbin/brctl set%s %s %s %s' | |
238 | %(k, bridgename, bridgeportname, v)) | |
239 | ||
240 | def set_bridgeport_attr(self, bridgename, bridgeportname, | |
241 | attrname, attrval): | |
242 | if self._cache_check([bridgename, 'linkinfo', 'ports', | |
243 | bridgeportname, attrname], attrval): | |
244 | return | |
245 | self.exec_command('/sbin/brctl set%s %s %s %s' %(attrname, bridgename, | |
246 | bridgeportname, attrval)) | |
247 | ||
248 | def set_bridge_attrs(self, bridgename, attrdict): | |
249 | for k, v in attrdict.iteritems(): | |
250 | if not v: | |
251 | continue | |
252 | if self._cache_check([bridgename, 'linkinfo', k], v): | |
253 | continue | |
254 | try: | |
255 | self.exec_command('/sbin/brctl set%s %s %s' | |
256 | %(k, bridgename, v)) | |
257 | except Exception, e: | |
258 | self.logger.warn('%s: %s' %(bridgename, str(e))) | |
259 | pass | |
260 | ||
261 | def set_bridge_attr(self, bridgename, attrname, attrval): | |
262 | if self._cache_check([bridgename, 'linkinfo', attrname], attrval): | |
263 | return | |
264 | self.exec_command('/sbin/brctl set%s %s %s' | |
265 | %(attrname, bridgename, attrval)) | |
266 | ||
267 | def get_bridge_attrs(self, bridgename): | |
268 | return self._cache_get([bridgename, 'linkinfo']) | |
269 | ||
270 | def get_bridgeport_attrs(self, bridgename, bridgeportname): | |
271 | return self._cache_get([bridgename, 'linkinfo', 'ports', | |
272 | bridgeportname]) | |
273 | ||
274 | def get_bridgeport_attr(self, bridgename, bridgeportname, attrname): | |
275 | return self._cache_get([bridgename, 'linkinfo', 'ports', | |
276 | bridgeportname, attrname]) | |
277 | ||
278 | def set_stp(self, bridge, stp_state): | |
279 | self.exec_command('/sbin/brctl stp ' + bridge + ' ' + stp_state) | |
280 | ||
281 | def get_stp(self, bridge): | |
282 | sysfs_stpstate = '/sys/class/net/%s/bridge/stp_state' %bridge | |
283 | if not os.path.exists(sysfs_stpstate): | |
284 | return 'error' | |
285 | stpstate = self.read_file_oneline(sysfs_stpstate) | |
286 | if not stpstate: | |
287 | return 'error' | |
288 | try: | |
289 | if int(stpstate) > 0: | |
290 | return 'yes' | |
291 | elif int(stpstate) == 0: | |
292 | return 'no' | |
293 | except: | |
294 | return 'unknown' | |
295 | ||
296 | def conv_value_to_user(self, str): | |
297 | try: | |
298 | ret = int(str) / 100 | |
299 | except: | |
300 | return None | |
301 | finally: | |
302 | return '%d' %ret | |
303 | ||
304 | def read_value_from_sysfs(self, filename, preprocess_func): | |
305 | value = self.read_file_oneline(filename) | |
306 | if not value: | |
307 | return None | |
308 | return preprocess_func(value) | |
309 | ||
310 | def set_ageing(self, bridge, ageing): | |
311 | self.exec_command('/sbin/brctl setageing ' + bridge + ' ' + ageing) | |
312 | ||
313 | def get_ageing(self, bridge): | |
314 | return self.read_value_from_sysfs('/sys/class/net/%s/bridge/ageing_time' | |
315 | %bridge, self.conv_value_to_user) | |
316 | ||
317 | def set_bridgeprio(self, bridge, bridgeprio): | |
318 | self.exec_command('/sbin/brctl setbridgeprio ' + bridge + ' ' + | |
319 | bridgeprio) | |
320 | ||
321 | def get_bridgeprio(self, bridge): | |
322 | return self.read_file_oneline( | |
323 | '/sys/class/net/%s/bridge/priority' %bridge) | |
324 | ||
325 | def set_fd(self, bridge, fd): | |
326 | self.exec_command('/sbin/brctl setfd ' + bridge + ' ' + fd) | |
327 | ||
328 | def get_fd(self, bridge): | |
329 | return self.read_value_from_sysfs( | |
330 | '/sys/class/net/%s/bridge/forward_delay' | |
331 | %bridge, self.conv_value_to_user) | |
332 | ||
333 | def set_gcint(self, bridge, gcint): | |
334 | #cmd = '/sbin/brctl setgcint ' + bridge + ' ' + gcint | |
335 | raise Exception('set_gcint not implemented') | |
336 | ||
337 | def set_hello(self, bridge, hello): | |
338 | self.exec_command('/sbin/brctl sethello ' + bridge + ' ' + hello) | |
339 | ||
340 | def get_hello(self, bridge): | |
341 | return self.read_value_from_sysfs('/sys/class/net/%s/bridge/hello_time' | |
342 | %bridge, self.conv_value_to_user) | |
343 | ||
344 | def set_maxage(self, bridge, maxage): | |
345 | self.exec_command('/sbin/brctl setmaxage ' + bridge + ' ' + maxage) | |
346 | ||
347 | def get_maxage(self, bridge): | |
348 | return self.read_value_from_sysfs('/sys/class/net/%s/bridge/max_age' | |
349 | %bridge, self.conv_value_to_user) | |
350 | ||
351 | def set_pathcost(self, bridge, port, pathcost): | |
352 | self.exec_command('/sbin/brctl setpathcost %s' %bridge + ' %s' %port + | |
353 | ' %s' %pathcost) | |
354 | ||
355 | def get_pathcost(self, bridge, port): | |
356 | return self.read_file_oneline('/sys/class/net/%s/brport/path_cost' | |
357 | %port) | |
358 | ||
359 | def set_portprio(self, bridge, port, prio): | |
360 | self.exec_command('/sbin/brctl setportprio %s' %bridge + ' %s' %port + | |
361 | ' %s' %prio) | |
362 | ||
363 | def get_portprio(self, bridge, port): | |
364 | return self.read_file_oneline('/sys/class/net/%s/brport/priority' | |
365 | %port) | |
366 | ||
367 | def set_hashmax(self, bridge, hashmax): | |
368 | self.exec_command('/sbin/brctl sethashmax %s' %bridge + ' %s' %hashmax) | |
369 | ||
370 | def get_hashmax(self, bridge): | |
371 | return self.read_file_oneline('/sys/class/net/%s/bridge/hash_max' | |
372 | %bridge) | |
373 | ||
374 | def set_hashel(self, bridge, hashel): | |
375 | self.exec_command('/sbin/brctl sethashel %s' %bridge + ' %s' %hashel) | |
376 | ||
377 | def get_hashel(self, bridge): | |
378 | return self.read_file_oneline('/sys/class/net/%s/bridge/hash_elasticity' | |
379 | %bridge) | |
380 | ||
381 | def set_mclmc(self, bridge, mclmc): | |
382 | self.exec_command('/sbin/brctl setmclmc %s' %bridge + ' %s' %mclmc) | |
383 | ||
384 | def get_mclmc(self, bridge): | |
385 | return self.read_file_oneline( | |
386 | '/sys/class/net/%s/bridge/multicast_last_member_count' | |
387 | %bridge) | |
388 | ||
389 | def set_mcrouter(self, bridge, mcrouter): | |
390 | self.exec_command('/sbin/brctl setmcrouter %s' %bridge + | |
391 | ' %s' %mcrouter) | |
392 | ||
393 | def get_mcrouter(self, bridge): | |
394 | return self.read_file_oneline( | |
395 | '/sys/class/net/%s/bridge/multicast_router' %bridge) | |
396 | ||
397 | def set_mcsnoop(self, bridge, mcsnoop): | |
398 | self.exec_command('/sbin/brctl setmcsnoop %s' %bridge + | |
399 | ' %s' %mcsnoop) | |
400 | ||
401 | def get_mcsnoop(self, bridge): | |
402 | return self.read_file_oneline( | |
403 | '/sys/class/net/%s/bridge/multicast_snooping' %bridge) | |
404 | ||
405 | def set_mcsqc(self, bridge, mcsqc): | |
406 | self.exec_command('/sbin/brctl setmcsqc %s' %bridge + | |
407 | ' %s' %mcsqc) | |
408 | ||
409 | def get_mcsqc(self, bridge): | |
410 | return self.read_file_oneline( | |
411 | '/sys/class/net/%s/bridge/multicast_startup_query_count' | |
412 | %bridge) | |
413 | ||
414 | def set_mcqifaddr(self, bridge, mcqifaddr): | |
415 | self.exec_command('/sbin/brctl setmcqifaddr %s' %bridge + | |
416 | ' %s' %mcqifaddr) | |
417 | ||
418 | def get_mcqifaddr(self, bridge): | |
419 | return self.read_file_oneline( | |
420 | '/sys/class/net/%s/bridge/multicast_startup_query_use_ifaddr' | |
421 | %bridge) | |
422 | ||
423 | def set_mcquerier(self, bridge, mcquerier): | |
424 | self.exec_command('/sbin/brctl setmcquerier %s' %bridge + | |
425 | ' %s' %mcquerier) | |
426 | ||
427 | def get_mcquerier(self, bridge): | |
428 | return self.read_file_oneline( | |
429 | '/sys/class/net/%s/bridge/multicast_querier' %bridge) | |
430 | ||
431 | def set_mcqv4src(self, bridge, vlan, mcquerier): | |
432 | if vlan == 0 or vlan > 4095: | |
433 | self.logger.warn('mcqv4src vlan \'%d\' invalid range' %vlan) | |
434 | return | |
435 | ||
436 | ip = mcquerier.split('.') | |
437 | if len(ip) != 4: | |
438 | self.logger.warn('mcqv4src \'%s\' invalid IPv4 address' %mcquerier) | |
439 | return | |
440 | for k in ip: | |
441 | if not k.isdigit() or int(k, 10) < 0 or int(k, 10) > 255: | |
442 | self.logger.warn('mcqv4src \'%s\' invalid IPv4 address' %mcquerier) | |
443 | return | |
444 | ||
445 | self.exec_command('/sbin/brctl setmcqv4src %s' %bridge + | |
446 | ' %d %s' %(vlan, mcquerier)) | |
447 | ||
448 | def del_mcqv4src(self, bridge, vlan): | |
449 | self.exec_command('/sbin/brctl delmcqv4src %s %d' %(bridge, vlan)) | |
450 | ||
451 | def get_mcqv4src(self, bridge, vlan=None): | |
452 | mcqv4src = {} | |
453 | mcqout = self.exec_command('/sbin/brctl showmcqv4src %s' %bridge) | |
454 | if not mcqout: return None | |
455 | mcqlines = mcqout.splitlines() | |
456 | for l in mcqlines[1:]: | |
457 | l=l.strip() | |
458 | k, d, v = l.split('\t') | |
459 | if not k or not v: | |
460 | continue | |
461 | mcqv4src[k] = v | |
462 | if vlan: | |
463 | return mcqv4src.get(vlan) | |
464 | return mcqv4src | |
465 | ||
466 | def set_mclmi(self, bridge, mclmi): | |
467 | self.exec_command('/sbin/brctl setmclmi %s' %bridge + | |
468 | ' %s' %mclmi) | |
469 | ||
470 | def get_mclmi(self, bridge): | |
471 | return self.read_file_oneline( | |
472 | '/sys/class/net/%s/bridge/multicast_last_member_interval' | |
473 | %bridge) | |
474 | ||
475 | def set_mcmi(self, bridge, mcmi): | |
476 | self.exec_command('/sbin/brctl setmcmi %s' %bridge + | |
477 | ' %s' %mcmi) | |
478 | ||
479 | def get_mcmi(self, bridge): | |
480 | return self.read_file_oneline( | |
481 | '/sys/class/net/%s/bridge/multicast_membership_interval' | |
482 | %bridge) | |
483 | ||
484 | def bridge_exists(self, bridge): | |
485 | return os.path.exists('/sys/class/net/%s/bridge' %bridge) | |
486 | ||
487 | def is_bridge_port(self, ifacename): | |
488 | return os.path.exists('/sys/class/net/%s/brport' %ifacename) | |
489 | ||
490 | def bridge_port_exists(self, bridge, bridgeportname): | |
491 | try: | |
492 | return os.path.exists('/sys/class/net/%s/brif/%s' | |
493 | %(bridge, bridgeportname)) | |
494 | except Exception: | |
495 | return False | |
496 | ||
497 | def get_bridge_ports(self, bridgename): | |
498 | try: | |
499 | return os.listdir('/sys/class/net/%s/brif/' %bridgename) | |
500 | except: | |
501 | return [] |