]>
Commit | Line | Data |
---|---|---|
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 | ||
7 | import os | |
8 | from collections import OrderedDict | |
9 | from utilsbase import * | |
10 | from cache import * | |
11 | ||
44533c72 WK |
12 | VXLAN_UDP_PORT = 4789 |
13 | ||
15ef32ea RP |
14 | class iproute2(utilsBase): |
15 | """ This class contains helper methods to cache and interact with the | |
16 | commands in the iproute2 package """ | |
17 | ||
18 | _cache_fill_done = False | |
19 | ipbatchbuf = '' | |
20 | ipbatch = False | |
21 | ipbatch_pause = False | |
22 | ||
23 | def __init__(self, *args, **kargs): | |
24 | utilsBase.__init__(self, *args, **kargs) | |
25 | if self.CACHE and not iproute2._cache_fill_done: | |
26 | self._link_fill() | |
27 | self._addr_fill() | |
28 | iproute2._cache_fill_done = True | |
29 | ||
30 | def _link_fill(self, ifacename=None, refresh=False): | |
31 | """ fills cache with link information | |
32 | ||
33 | if ifacename argument given, fill cache for ifacename, else | |
34 | fill cache for all interfaces in the system | |
35 | """ | |
36 | ||
37 | linkout = {} | |
38 | if iproute2._cache_fill_done and not refresh: return | |
39 | try: | |
40 | # if ifacename already present, return | |
41 | if (ifacename and not refresh and | |
42 | linkCache.get_attr([ifacename, 'ifflag'])): | |
43 | return | |
44 | except: | |
45 | pass | |
46 | cmdout = self.link_show(ifacename=ifacename) | |
47 | if not cmdout: | |
48 | return | |
49 | for c in cmdout.splitlines(): | |
50 | citems = c.split() | |
51 | ifnamenlink = citems[1].split('@') | |
52 | if len(ifnamenlink) > 1: | |
53 | ifname = ifnamenlink[0] | |
54 | iflink = ifnamenlink[1].strip(':') | |
55 | else: | |
56 | ifname = ifnamenlink[0].strip(':') | |
57 | iflink = None | |
58 | linkattrs = {} | |
59 | linkattrs['link'] = iflink | |
60 | linkattrs['ifindex'] = citems[0].strip(':') | |
61 | flags = citems[2].strip('<>').split(',') | |
62 | linkattrs['flags'] = flags | |
63 | linkattrs['ifflag'] = 'UP' if 'UP' in flags else 'DOWN' | |
64 | for i in range(0, len(citems)): | |
65 | if citems[i] == 'mtu': linkattrs['mtu'] = citems[i+1] | |
66 | elif citems[i] == 'state': linkattrs['state'] = citems[i+1] | |
67 | elif citems[i] == 'link/ether': linkattrs['hwaddress'] = citems[i+1] | |
68 | elif citems[i] == 'vlan' and citems[i+1] == 'id': | |
69 | linkattrs['linkinfo'] = {'vlanid' : citems[i+2]} | |
70 | elif citems[i] == 'vxlan' and citems[i+1] == 'id': | |
71 | vattrs = {'vxlanid' : citems[i+2], | |
9e012f9e | 72 | 'svcnode' : [], |
44533c72 | 73 | 'remote' : [], |
88a5c4c8 | 74 | 'ageing' : citems[i+2], |
9e012f9e | 75 | 'learning': 'on'} |
15ef32ea RP |
76 | for j in range(i+2, len(citems)): |
77 | if citems[j] == 'local': | |
78 | vattrs['local'] = citems[j+1] | |
79 | elif citems[j] == 'svcnode': | |
80 | vattrs['svcnode'].append(citems[j+1]) | |
88a5c4c8 | 81 | elif citems[j] == 'ageing': |
76cb1a63 | 82 | vattrs['ageing'] = citems[j+1] |
9e012f9e RP |
83 | elif citems[j] == 'nolearning': |
84 | vattrs['learning'] = 'off' | |
44533c72 WK |
85 | # get vxlan peer nodes |
86 | peers = self.get_vxlan_peers(ifname) | |
87 | if peers: | |
88 | vattrs['remote'] = peers | |
15ef32ea RP |
89 | linkattrs['linkinfo'] = vattrs |
90 | break | |
91 | #linkattrs['alias'] = self.read_file_oneline( | |
92 | # '/sys/class/net/%s/ifalias' %ifname) | |
93 | linkout[ifname] = linkattrs | |
94 | [linkCache.update_attrdict([ifname], linkattrs) | |
95 | for ifname, linkattrs in linkout.items()] | |
96 | ||
97 | def _addr_filter(self, addr, scope=None): | |
98 | default_addrs = ['127.0.0.1/8', '::1/128' , '0.0.0.0'] | |
99 | if addr in default_addrs: | |
100 | return True | |
101 | if scope and scope == 'link': | |
102 | return True | |
103 | return False | |
104 | ||
105 | def _addr_fill(self, ifacename=None, refresh=False): | |
106 | """ fills cache with address information | |
107 | ||
108 | if ifacename argument given, fill cache for ifacename, else | |
109 | fill cache for all interfaces in the system | |
110 | """ | |
111 | ||
112 | linkout = {} | |
113 | if iproute2._cache_fill_done: return | |
114 | try: | |
115 | # Check if ifacename is already full, in which case, return | |
116 | if ifacename: | |
117 | linkCache.get_attr([ifacename, 'addrs']) | |
118 | return | |
119 | except: | |
120 | pass | |
121 | cmdout = self.addr_show(ifacename=ifacename) | |
122 | if not cmdout: | |
123 | return | |
124 | for c in cmdout.splitlines(): | |
125 | citems = c.split() | |
126 | ifnamenlink = citems[1].split('@') | |
127 | if len(ifnamenlink) > 1: | |
128 | ifname = ifnamenlink[0] | |
129 | else: | |
130 | ifname = ifnamenlink[0].strip(':') | |
c78cae3d RP |
131 | if not linkout.get(ifname): |
132 | linkattrs = {} | |
133 | linkattrs['addrs'] = OrderedDict({}) | |
134 | try: | |
135 | linkout[ifname].update(linkattrs) | |
136 | except KeyError: | |
137 | linkout[ifname] = linkattrs | |
15ef32ea RP |
138 | if citems[2] == 'inet': |
139 | if self._addr_filter(citems[3], scope=citems[5]): | |
140 | continue | |
141 | addrattrs = {} | |
142 | addrattrs['scope'] = citems[5] | |
143 | addrattrs['type'] = 'inet' | |
144 | linkout[ifname]['addrs'][citems[3]] = addrattrs | |
145 | elif citems[2] == 'inet6': | |
146 | if self._addr_filter(citems[3], scope=citems[5]): | |
147 | continue | |
148 | if citems[5] == 'link': continue #skip 'link' addresses | |
149 | addrattrs = {} | |
150 | addrattrs['scope'] = citems[5] | |
151 | addrattrs['type'] = 'inet6' | |
152 | linkout[ifname]['addrs'][citems[3]] = addrattrs | |
15ef32ea RP |
153 | [linkCache.update_attrdict([ifname], linkattrs) |
154 | for ifname, linkattrs in linkout.items()] | |
155 | ||
156 | def _cache_get(self, type, attrlist, refresh=False): | |
157 | try: | |
158 | if self.DRYRUN: | |
159 | return False | |
160 | if self.CACHE: | |
161 | if not iproute2._cache_fill_done: | |
162 | self._link_fill() | |
163 | self._addr_fill() | |
164 | iproute2._cache_fill_done = True | |
165 | return linkCache.get_attr(attrlist) | |
166 | if not refresh: | |
167 | return linkCache.get_attr(attrlist) | |
168 | if type == 'link': | |
169 | self._link_fill(attrlist[0], refresh) | |
170 | elif type == 'addr': | |
171 | self._addr_fill(attrlist[0], refresh) | |
172 | else: | |
173 | self._link_fill(attrlist[0], refresh) | |
174 | self._addr_fill(attrlist[0], refresh) | |
175 | return linkCache.get_attr(attrlist) | |
176 | except Exception, e: | |
177 | self.logger.debug('_cache_get(%s) : [%s]' | |
178 | %(str(attrlist), str(e))) | |
179 | pass | |
180 | return None | |
181 | ||
182 | def _cache_check(self, type, attrlist, value, refresh=False): | |
183 | try: | |
184 | attrvalue = self._cache_get(type, attrlist, refresh) | |
185 | if attrvalue and attrvalue == value: | |
186 | return True | |
187 | except Exception, e: | |
188 | self.logger.debug('_cache_check(%s) : [%s]' | |
189 | %(str(attrlist), str(e))) | |
190 | pass | |
191 | return False | |
192 | ||
193 | def _cache_update(self, attrlist, value): | |
194 | if self.DRYRUN: return | |
195 | try: | |
196 | linkCache.add_attr(attrlist, value) | |
197 | except: | |
198 | pass | |
199 | ||
200 | def _cache_delete(self, attrlist): | |
201 | if self.DRYRUN: return | |
202 | try: | |
203 | linkCache.del_attr(attrlist) | |
204 | except: | |
205 | pass | |
206 | ||
207 | def _cache_invalidate(self): | |
208 | linkCache.invalidate() | |
209 | ||
210 | def batch_start(self): | |
211 | self.ipbatcbuf = '' | |
212 | self.ipbatch = True | |
213 | self.ipbatch_pause = False | |
214 | ||
215 | def add_to_batch(self, cmd): | |
216 | self.ipbatchbuf += cmd + '\n' | |
217 | ||
218 | def batch_pause(self): | |
219 | self.ipbatch_pause = True | |
220 | ||
221 | def batch_resume(self): | |
222 | self.ipbatch_pause = False | |
223 | ||
224 | def batch_commit(self): | |
225 | if not self.ipbatchbuf: | |
226 | return | |
227 | try: | |
228 | self.exec_command_talk_stdin('ip -force -batch -', | |
229 | stdinbuf=self.ipbatchbuf) | |
230 | except Exception: | |
231 | raise | |
232 | finally: | |
233 | self.ipbatchbuf = '' | |
234 | self.ipbatch = False | |
235 | self.ipbatch_pause = False | |
236 | ||
237 | def addr_show(self, ifacename=None): | |
238 | if ifacename: | |
239 | if not self.link_exists(ifacename): | |
240 | return | |
241 | return self.exec_commandl(['ip','-o', 'addr', 'show', 'dev', | |
242 | '%s' %ifacename]) | |
243 | else: | |
244 | return self.exec_commandl(['ip', '-o', 'addr', 'show']) | |
245 | ||
246 | def link_show(self, ifacename=None): | |
247 | if ifacename: | |
248 | return self.exec_commandl(['ip', '-o', '-d', 'link', | |
249 | 'show', 'dev', '%s' %ifacename]) | |
250 | else: | |
251 | return self.exec_commandl(['ip', '-o', '-d', 'link', 'show']) | |
252 | ||
253 | def addr_add(self, ifacename, address, broadcast=None, | |
254 | peer=None, scope=None, preferred_lifetime=None): | |
255 | if not address: | |
256 | return | |
257 | cmd = 'addr add %s' %address | |
258 | if broadcast: | |
259 | cmd += ' broadcast %s' %broadcast | |
260 | if peer: | |
261 | cmd += ' peer %s' %peer | |
262 | if scope: | |
263 | cmd += ' scope %s' %scope | |
264 | if preferred_lifetime: | |
265 | cmd += ' preferred_lft %s' %preferred_lifetime | |
266 | cmd += ' dev %s' %ifacename | |
267 | if self.ipbatch and not self.ipbatch_pause: | |
268 | self.add_to_batch(cmd) | |
269 | else: | |
270 | self.exec_command('ip ' + cmd) | |
271 | self._cache_update([ifacename, 'addrs', address], {}) | |
272 | ||
273 | def addr_del(self, ifacename, address, broadcast=None, | |
274 | peer=None, scope=None): | |
275 | """ Delete ipv4 address """ | |
276 | if not address: | |
277 | return | |
278 | if not self._cache_get('addr', [ifacename, 'addrs', address]): | |
279 | return | |
280 | cmd = 'addr del %s' %address | |
281 | if broadcast: | |
282 | cmd += 'broadcast %s' %broadcast | |
283 | if peer: | |
284 | cmd += 'peer %s' %peer | |
285 | if scope: | |
286 | cmd += 'scope %s' %scope | |
287 | cmd += ' dev %s' %ifacename | |
288 | self.exec_command('ip ' + cmd) | |
289 | self._cache_delete([ifacename, 'addrs', address]) | |
290 | ||
291 | def addr_flush(self, ifacename): | |
292 | cmd = 'addr flush dev %s' %ifacename | |
293 | if self.ipbatch and not self.ipbatch_pause: | |
294 | self.add_to_batch(cmd) | |
295 | else: | |
296 | self.exec_command('ip ' + cmd) | |
297 | self._cache_delete([ifacename, 'addrs']) | |
298 | ||
299 | def del_addr_all(self, ifacename, skip_addrs=[]): | |
300 | if not skip_addrs: skip_addrs = [] | |
301 | runningaddrsdict = self.addr_get(ifacename) | |
302 | try: | |
303 | # XXX: ignore errors. Fix this to delete secondary addresses | |
304 | # first | |
305 | [self.addr_del(ifacename, a) for a in | |
306 | set(runningaddrsdict.keys()).difference(skip_addrs)] | |
307 | except: | |
308 | # ignore errors | |
309 | pass | |
310 | ||
311 | def addr_get(self, ifacename, details=True): | |
312 | addrs = self._cache_get('addr', [ifacename, 'addrs']) | |
313 | if not addrs: | |
314 | return None | |
315 | if details: | |
316 | return addrs | |
317 | return addrs.keys() | |
318 | ||
319 | def addr_add_multiple(self, ifacename, addrs, purge_existing=False): | |
320 | # purges address | |
321 | if purge_existing: | |
322 | # if perfmode is not set and also if iface has no sibling | |
323 | # objects, purge addresses that are not present in the new | |
324 | # config | |
325 | runningaddrs = self.addr_get(ifacename, details=False) | |
326 | if addrs == runningaddrs: | |
327 | return | |
328 | try: | |
329 | # if primary address is not same, there is no need to keep any. | |
330 | # reset all addresses | |
331 | if (addrs and runningaddrs and | |
332 | (addrs[0] != runningaddrs[0])): | |
333 | self.del_addr_all(ifacename) | |
334 | else: | |
335 | self.del_addr_all(ifacename, addrs) | |
336 | except Exception, e: | |
337 | self.log_warn(str(e)) | |
338 | for a in addrs: | |
339 | try: | |
340 | self.addr_add(ifacename, a) | |
341 | except Exception, e: | |
342 | self.logger.error(str(e)) | |
343 | ||
344 | def _link_set_ifflag(self, ifacename, value): | |
345 | # Dont look at the cache, the cache may have stale value | |
346 | # because link status can be changed by external | |
347 | # entity (One such entity is ifupdown main program) | |
348 | cmd = 'link set dev %s %s' %(ifacename, value.lower()) | |
349 | if self.ipbatch: | |
350 | self.add_to_batch(cmd) | |
351 | else: | |
352 | self.exec_command('ip ' + cmd) | |
353 | ||
354 | def link_up(self, ifacename): | |
355 | self._link_set_ifflag(ifacename, 'UP') | |
356 | ||
357 | def link_down(self, ifacename): | |
358 | self._link_set_ifflag(ifacename, 'DOWN') | |
359 | ||
360 | def link_set(self, ifacename, key, value=None, force=False): | |
361 | if not force: | |
362 | if (key not in ['master', 'nomaster'] and | |
363 | self._cache_check('link', [ifacename, key], value)): | |
364 | return | |
365 | cmd = 'link set dev %s %s' %(ifacename, key) | |
366 | if value: | |
367 | cmd += ' %s' %value | |
368 | if self.ipbatch: | |
369 | self.add_to_batch(cmd) | |
370 | else: | |
371 | self.exec_command('ip ' + cmd) | |
372 | if key not in ['master', 'nomaster']: | |
373 | self._cache_update([ifacename, key], value) | |
374 | ||
375 | def link_set_hwaddress(self, ifacename, hwaddress, force=False): | |
376 | if not force: | |
377 | if self._cache_check('link', [ifacename, 'hwaddress'], hwaddress): | |
61c4d724 | 378 | return |
15ef32ea RP |
379 | self.link_down(ifacename) |
380 | cmd = 'link set dev %s address %s' %(ifacename, hwaddress) | |
381 | if self.ipbatch: | |
382 | self.add_to_batch(cmd) | |
383 | else: | |
384 | self.exec_command('ip ' + cmd) | |
385 | self.link_up(ifacename) | |
386 | self._cache_update([ifacename, 'hwaddress'], hwaddress) | |
387 | ||
388 | def link_set_alias(self, ifacename, alias): | |
389 | self.exec_commandl(['ip', 'link', 'set', 'dev', | |
390 | ifacename, 'alias', alias]) | |
391 | ||
392 | def link_get_alias(self, ifacename): | |
393 | return self.read_file_oneline('/sys/class/net/%s/ifalias' | |
394 | %ifacename) | |
395 | ||
396 | def link_isloopback(self, ifacename): | |
397 | flags = self._cache_get('link', [ifacename, 'flags']) | |
398 | if not flags: | |
399 | return | |
400 | if 'LOOPBACK' in flags: | |
401 | return True | |
402 | return False | |
403 | ||
404 | def link_get_status(self, ifacename): | |
405 | return self._cache_get('link', [ifacename, 'ifflag'], refresh=True) | |
406 | ||
407 | def route_add_gateway(self, ifacename, gateway, metric=None): | |
408 | if not gateway: | |
409 | return | |
410 | cmd = 'ip route add default via %s' %gateway | |
411 | # Add metric | |
412 | if metric: | |
413 | cmd += 'metric %s' %metric | |
414 | cmd += ' dev %s' %ifacename | |
415 | self.exec_command(cmd) | |
416 | ||
417 | def route_del_gateway(self, ifacename, gateway, metric=None): | |
418 | # delete default gw | |
419 | if not gateway: | |
420 | return | |
421 | cmd = 'ip route del default via %s' %gateway | |
422 | if metric: | |
423 | cmd += ' metric %s' %metric | |
424 | cmd += ' dev %s' %ifacename | |
425 | self.exec_command(cmd) | |
426 | ||
427 | def route6_add_gateway(self, ifacename, gateway): | |
428 | if not gateway: | |
429 | return | |
430 | return self.exec_command('ip -6 route add default via %s' %gateway + | |
431 | ' dev %s' %ifacename) | |
432 | ||
433 | def route6_del_gateway(self, ifacename, gateway): | |
434 | if not gateway: | |
435 | return | |
436 | return self.exec_command('ip -6 route del default via %s' %gateway + | |
437 | 'dev %s' %ifacename) | |
438 | ||
439 | def link_create_vlan(self, vlan_device_name, vlan_raw_device, vlanid): | |
440 | if self.link_exists(vlan_device_name): | |
441 | return | |
442 | self.exec_command('ip link add link %s' %vlan_raw_device + | |
443 | ' name %s' %vlan_device_name + | |
444 | ' type vlan id %d' %vlanid) | |
445 | self._cache_update([vlan_device_name], {}) | |
446 | ||
447 | def link_create_vlan_from_name(self, vlan_device_name): | |
448 | v = vlan_device_name.split('.') | |
449 | if len(v) != 2: | |
450 | self.logger.warn('invalid vlan device name %s' %vlan_device_name) | |
451 | return | |
452 | self.link_create_vlan(vlan_device_name, v[0], v[1]) | |
453 | ||
454 | def link_create_macvlan(self, name, linkdev, mode='private'): | |
455 | if self.link_exists(name): | |
456 | return | |
457 | cmd = ('link add link %s' %linkdev + | |
458 | ' name %s' %name + | |
459 | ' type macvlan mode %s' %mode) | |
460 | if self.ipbatch and not self.ipbatch_pause: | |
461 | self.add_to_batch(cmd) | |
462 | else: | |
463 | self.exec_command('ip %s' %cmd) | |
464 | self._cache_update([name], {}) | |
465 | ||
44533c72 WK |
466 | def get_vxlan_peers(self, dev): |
467 | cmd = 'bridge fdb show brport %s' % dev | |
468 | cur_peers = [] | |
469 | try: | |
470 | ps = subprocess.Popen((cmd).split(), stdout=subprocess.PIPE, close_fds=True) | |
471 | output = subprocess.check_output(('grep', '00:00:00:00:00:00'), stdin=ps.stdout) | |
472 | ps.wait() | |
473 | try: | |
474 | ppat = re.compile('\s+dst\s+(\d+.\d+.\d+.\d+)\s+') | |
475 | for l in output.split('\n'): | |
476 | m = ppat.search(l) | |
477 | if m: | |
478 | cur_peers.append(m.group(1)) | |
479 | except: | |
480 | self.logger.warn('error parsing ip link output') | |
481 | pass | |
482 | except subprocess.CalledProcessError as e: | |
483 | if e.returncode != 1: | |
484 | self.logger.error(str(e)) | |
485 | ||
486 | return cur_peers | |
487 | ||
15ef32ea RP |
488 | def link_create_vxlan(self, name, vxlanid, |
489 | localtunnelip=None, | |
490 | svcnodeips=None, | |
44533c72 | 491 | remoteips=None, |
88a5c4c8 ST |
492 | learning='on', |
493 | ageing=None): | |
44533c72 WK |
494 | if svcnodeips and remoteips: |
495 | raise Exception("svcnodeip and remoteip is mutually exclusive") | |
15ef32ea RP |
496 | args = '' |
497 | if localtunnelip: | |
498 | args += ' local %s' %localtunnelip | |
499 | if svcnodeips: | |
500 | for s in svcnodeips: | |
501 | args += ' svcnode %s' %s | |
88a5c4c8 ST |
502 | if ageing: |
503 | args += ' ageing %s' %ageing | |
fce93c54 | 504 | if learning == 'off': |
9e012f9e | 505 | args += ' nolearning' |
88a5c4c8 | 506 | |
15ef32ea | 507 | if self.link_exists(name): |
44533c72 | 508 | cmd = 'link set dev %s type vxlan dstport %d' %(name, VXLAN_UDP_PORT) |
15ef32ea | 509 | else: |
44533c72 | 510 | cmd = 'link add dev %s type vxlan id %s dstport %d' %(name, vxlanid, VXLAN_UDP_PORT) |
15ef32ea RP |
511 | cmd += args |
512 | ||
513 | if self.ipbatch and not self.ipbatch_pause: | |
514 | self.add_to_batch(cmd) | |
515 | else: | |
516 | self.exec_command('ip %s' %cmd) | |
44533c72 WK |
517 | |
518 | # figure out the diff for remotes and do the bridge fdb updates | |
519 | cur_peers = set(self.get_vxlan_peers(name)) | |
520 | if remoteips: | |
521 | new_peers = set(remoteips) | |
522 | del_list = cur_peers.difference(new_peers) | |
523 | add_list = new_peers.difference(cur_peers) | |
524 | else: | |
525 | del_list = cur_peers | |
526 | add_list = [] | |
527 | ||
528 | try: | |
529 | for addr in del_list: | |
530 | self.bridge_fdb_del(name, '00:00:00:00:00:00', None, True, addr) | |
531 | except: | |
532 | pass | |
533 | ||
534 | try: | |
535 | for addr in add_list: | |
536 | self.bridge_fdb_append(name, '00:00:00:00:00:00', None, True, addr) | |
537 | except: | |
538 | pass | |
539 | ||
15ef32ea RP |
540 | # XXX: update linkinfo correctly |
541 | self._cache_update([name], {}) | |
542 | ||
543 | def link_exists(self, ifacename): | |
84ca006f RP |
544 | if self.DRYRUN: |
545 | return True | |
15ef32ea RP |
546 | return os.path.exists('/sys/class/net/%s' %ifacename) |
547 | ||
548 | def is_vlan_device_by_name(self, ifacename): | |
549 | if re.search(r'\.', ifacename): | |
550 | return True | |
551 | return False | |
552 | ||
553 | def route_add(self, route): | |
554 | self.exec_command('ip route add ' + route) | |
555 | ||
556 | def route6_add(self, route): | |
557 | self.exec_command('ip -6 route add ' + route) | |
558 | ||
559 | def get_vlandev_attrs(self, ifacename): | |
560 | return (self._cache_get('link', [ifacename, 'linkinfo', 'link']), | |
561 | self._cache_get('link', [ifacename, 'linkinfo', 'vlanid'])) | |
562 | ||
9e012f9e RP |
563 | def get_vxlandev_attrs(self, ifacename): |
564 | return self._cache_get('link', [ifacename, 'linkinfo']) | |
565 | ||
15ef32ea RP |
566 | def link_get_mtu(self, ifacename): |
567 | return self._cache_get('link', [ifacename, 'mtu']) | |
568 | ||
569 | def link_get_hwaddress(self, ifacename): | |
5828d8c5 RP |
570 | address = self._cache_get('link', [ifacename, 'hwaddress']) |
571 | # newly created logical interface addresses dont end up in the cache | |
572 | # read hwaddress from sysfs file for these interfaces | |
573 | if not address: | |
574 | address = self.read_file_oneline('/sys/class/net/%s/address' | |
575 | %ifacename) | |
576 | return address | |
15ef32ea RP |
577 | |
578 | def link_create(self, ifacename, type, link=None): | |
579 | if self.link_exists(ifacename): | |
580 | return | |
581 | cmd = 'link add' | |
582 | if link: | |
583 | cmd += ' link %s' %link | |
584 | cmd += ' name %s type %s' %(ifacename, type) | |
585 | if self.ipbatch and not self.ipbatch_pause: | |
586 | self.add_to_batch(cmd) | |
587 | else: | |
588 | self.exec_command('ip %s' %cmd) | |
589 | self._cache_update([ifacename], {}) | |
590 | ||
591 | def link_delete(self, ifacename): | |
592 | if not self.link_exists(ifacename): | |
593 | return | |
594 | cmd = 'link del %s' %ifacename | |
595 | if self.ipbatch and not self.ipbatch_pause: | |
596 | self.add_to_batch(cmd) | |
597 | else: | |
598 | self.exec_command('ip %s' %cmd) | |
599 | self._cache_invalidate() | |
600 | ||
601 | def bridge_port_vids_add(self, bridgeportname, vids): | |
602 | [self.exec_command('bridge vlan add vid %s dev %s' | |
603 | %(v, bridgeportname)) for v in vids] | |
604 | ||
605 | def bridge_port_vids_del(self, bridgeportname, vids): | |
606 | if not vids: | |
607 | return | |
608 | [self.exec_command('bridge vlan del vid %s dev %s' | |
609 | %(v, bridgeportname)) for v in vids] | |
610 | ||
611 | def bridge_port_vids_flush(self, bridgeportname): | |
612 | self.exec_command('bridge vlan del vid %s dev %s' | |
613 | %(vid, bridgeportname)) | |
614 | ||
615 | def bridge_port_vids_get(self, bridgeportname): | |
616 | self.exec_command('/bin/bridge vlan show %s' %bridgeportname) | |
617 | bridgeout = self.exec_command('/bin/bridge vlan show dev %s' | |
618 | %bridgeportname) | |
619 | if not bridgeout: return [] | |
620 | brvlanlines = bridgeout.readlines()[2:] | |
621 | vids = [l.strip() for l in brvlanlines] | |
622 | return [vid for v in vids if vid] | |
623 | ||
624 | def bridge_port_vids_get_all(self): | |
625 | brvlaninfo = {} | |
626 | bridgeout = self.exec_command('/bin/bridge vlan show') | |
627 | if not bridgeout: return brvlaninfo | |
628 | brvlanlines = bridgeout.splitlines() | |
629 | brportname=None | |
630 | for l in brvlanlines[1:]: | |
631 | if l and l[0] not in [' ', '\t']: | |
632 | brportname = None | |
633 | l=l.strip() | |
634 | if not l: | |
635 | brportname=None | |
636 | continue | |
637 | if 'PVID' in l: | |
638 | attrs = l.split() | |
639 | brportname = attrs[0] | |
640 | brvlaninfo[brportname] = {'pvid' : attrs[1], | |
641 | 'vlan' : []} | |
642 | elif brportname: | |
643 | if 'Egress Untagged' not in l: | |
644 | brvlaninfo[brportname]['vlan'].append(l) | |
645 | elif not brportname: | |
646 | attrs = l.split() | |
647 | if attrs[1] == 'None' or 'Egress Untagged' in attrs[1]: | |
648 | continue | |
649 | brportname = attrs[0] | |
650 | brvlaninfo[brportname] = {'vlan' : [attrs[1]]} | |
651 | return brvlaninfo | |
652 | ||
653 | def bridge_port_pvid_add(self, bridgeportname, pvid): | |
654 | self.exec_command('bridge vlan add vid %s untagged pvid dev %s' | |
655 | %(pvid, bridgeportname)) | |
656 | ||
657 | def bridge_port_pvid_del(self, bridgeportname, pvid): | |
658 | self.exec_command('bridge vlan del vid %s untagged pvid dev %s' | |
659 | %(pvid, bridgeportname)) | |
660 | ||
661 | def bridge_port_pvids_get(self, bridgeportname): | |
662 | return self.read_file_oneline('/sys/class/net/%s/brport/pvid' | |
663 | %bridgeportname) | |
664 | ||
665 | def bridge_vids_add(self, bridgeportname, vids, bridge=True): | |
666 | target = 'self' if bridge else '' | |
667 | [self.exec_command('bridge vlan add vid %s dev %s %s' | |
668 | %(v, bridgeportname, target)) for v in vids] | |
669 | ||
670 | def bridge_vids_del(self, bridgeportname, vids, bridge=True): | |
671 | target = 'self' if bridge else '' | |
672 | [self.exec_command('bridge vlan del vid %s dev %s %s' | |
673 | %(v, bridgeportname, target)) for v in vids] | |
674 | ||
44533c72 | 675 | def bridge_fdb_add(self, dev, address, vlan=None, bridge=True, remote=None): |
e1601369 | 676 | target = 'self' if bridge else '' |
44533c72 | 677 | vlan_str = '' |
8e113d63 | 678 | if vlan: |
44533c72 WK |
679 | vlan_str = 'vlan %s ' % vlan |
680 | ||
681 | dst_str = '' | |
682 | if remote: | |
683 | dst_str = 'dst %s ' % remote | |
684 | ||
685 | self.exec_command('bridge fdb replace %s dev %s %s %s %s' | |
686 | %(address, dev, vlan_str, target, dst_str)) | |
e1601369 | 687 | |
44533c72 | 688 | def bridge_fdb_append(self, dev, address, vlan=None, bridge=True, remote=None): |
e1601369 | 689 | target = 'self' if bridge else '' |
44533c72 | 690 | vlan_str = '' |
8e113d63 | 691 | if vlan: |
44533c72 WK |
692 | vlan_str = 'vlan %s ' % vlan |
693 | ||
694 | dst_str = '' | |
695 | if remote: | |
696 | dst_str = 'dst %s ' % remote | |
697 | ||
698 | self.exec_command('bridge fdb append %s dev %s %s %s %s' | |
699 | %(address, dev, vlan_str, target, dst_str)) | |
700 | ||
701 | def bridge_fdb_del(self, dev, address, vlan=None, bridge=True, remote=None): | |
702 | target = 'self' if bridge else '' | |
703 | vlan_str = '' | |
704 | if vlan: | |
705 | vlan_str = 'vlan %s ' % vlan | |
706 | ||
707 | dst_str = '' | |
708 | if remote: | |
709 | dst_str = 'dst %s ' % remote | |
710 | self.exec_command('bridge fdb del %s dev %s %s %s %s' | |
711 | %(address, dev, vlan_str, target, dst_str)) | |
e1601369 | 712 | |
15ef32ea RP |
713 | def bridge_is_vlan_aware(self, bridgename): |
714 | filename = '/sys/class/net/%s/bridge/vlan_filtering' %bridgename | |
715 | if os.path.exists(filename) and self.read_file_oneline(filename) == '1': | |
716 | return True | |
717 | return False | |
e1601369 RP |
718 | |
719 | def bridge_port_get_bridge_name(self, bridgeport): | |
720 | filename = '/sys/class/net/%s/brport/bridge' %bridgeport | |
721 | try: | |
722 | return os.path.basename(os.readlink(filename)) | |
723 | except: | |
724 | return None | |
725 | ||
726 | def bridge_port_exists(self, bridge, bridgeportname): | |
727 | try: | |
728 | return os.path.exists('/sys/class/net/%s/brif/%s' | |
729 | %(bridge, bridgeportname)) | |
730 | except Exception: | |
731 | return False | |
8e113d63 RP |
732 | |
733 | def bridge_fdb_show_dev(self, dev): | |
734 | try: | |
735 | fdbs = {} | |
736 | output = self.exec_command('bridge fdb show dev %s' %dev) | |
737 | if output: | |
738 | for fdb_entry in output.splitlines(): | |
739 | try: | |
740 | entries = fdb_entry.split() | |
741 | fdbs.setdefault(entries[2], []).append(entries[0]) | |
742 | except: | |
743 | self.logger.debug('%s: invalid fdb line \'%s\'' | |
744 | %(dev, fdb_entry)) | |
745 | pass | |
746 | return fdbs | |
747 | except Exception: | |
748 | return None | |
749 | ||
750 | def is_bridge(self, bridge): | |
751 | return os.path.exists('/sys/class/net/%s/bridge' %bridge) | |
c9bba753 RP |
752 | |
753 | def is_link_up(self, ifacename): | |
754 | ret = False | |
755 | try: | |
756 | flags = self.read_file_oneline('/sys/class/net/%s/flags' %ifacename) | |
757 | iflags = int(flags, 16) | |
758 | if (iflags & 0x0001): | |
759 | ret = True | |
760 | except: | |
761 | ret = False | |
762 | pass | |
763 | return ret | |
f8858144 RP |
764 | |
765 | def ip_route_get_dev(self, prefix): | |
766 | try: | |
767 | output = self.exec_command('ip route get %s' %prefix) | |
768 | if output: | |
769 | rline = output.splitlines()[0] | |
770 | if rline: | |
771 | rattrs = rline.split() | |
772 | return rattrs[rattrs.index('dev') + 1] | |
773 | except Exception, e: | |
774 | self.logger.debug('ip_route_get_dev: failed .. %s' %str(e)) | |
775 | pass | |
776 | return None |