]> git.proxmox.com Git - mirror_ifupdown2.git/blame - ifupdownaddons/bondutil.py
addons: support for new addon module for vrf
[mirror_ifupdown2.git] / ifupdownaddons / bondutil.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
7import os
de1ee6b0 8import re
15ef32ea
RP
9from ifupdown.iface import *
10from utilsbase import *
11from iproute2 import *
12from cache import *
13
6cb589df 14class bondutil(utilsBase):
15ef32ea
RP
15 """ This class contains methods to interact with linux kernel bond
16 related interfaces """
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 self._cache_fill_done:
23 self._bond_linkinfo_fill_all()
24 self._cache_fill_done = True
25
26 def _bond_linkinfo_fill_attrs(self, bondname):
27 try:
28 linkCache.links[bondname]['linkinfo'] = {}
29 except:
30 linkCache.links[bondname] = {'linkinfo': {}}
31
32 try:
33 linkCache.set_attr([bondname, 'linkinfo', 'slaves'],
34 self.read_file_oneline('/sys/class/net/%s/bonding/slaves'
35 %bondname).split())
36 linkCache.set_attr([bondname, 'linkinfo', 'mode'],
37 self.read_file_oneline('/sys/class/net/%s/bonding/mode'
38 %bondname).split()[0])
39 linkCache.set_attr([bondname, 'linkinfo', 'xmit_hash_policy'],
40 self.read_file_oneline(
41 '/sys/class/net/%s/bonding/xmit_hash_policy'
42 %bondname).split()[0])
43 linkCache.set_attr([bondname, 'linkinfo', 'lacp_rate'],
44 self.read_file_oneline('/sys/class/net/%s/bonding/lacp_rate'
45 %bondname).split()[1])
46 linkCache.set_attr([bondname, 'linkinfo', 'ad_sys_priority'],
47 self.read_file_oneline('/sys/class/net/%s/bonding/ad_sys_priority'
48 %bondname))
49 linkCache.set_attr([bondname, 'linkinfo', 'ad_sys_mac_addr'],
50 self.read_file_oneline('/sys/class/net/%s/bonding/ad_sys_mac_addr'
51 %bondname))
52 map(lambda x: linkCache.set_attr([bondname, 'linkinfo', x],
53 self.read_file_oneline('/sys/class/net/%s/bonding/%s'
54 %(bondname, x))),
55 ['use_carrier', 'miimon', 'min_links', 'num_unsol_na',
ff01349d 56 'num_grat_arp', 'lacp_bypass_allow', 'lacp_bypass_period',
137de5d7 57 'lacp_bypass_all_active', 'clag_enable'])
15ef32ea
RP
58 except Exception, e:
59 pass
60
61 def _bond_linkinfo_fill_all(self):
62 bondstr = self.read_file_oneline('/sys/class/net/bonding_masters')
63 if not bondstr:
64 return
65 [self._bond_linkinfo_fill_attrs(b) for b in bondstr.split()]
66
67 def _bond_linkinfo_fill(self, bondname, refresh=False):
68 try:
69 linkCache.get_attr([bondname, 'linkinfo', 'slaves'])
70 return
71 except:
72 pass
73 bondstr = self.read_file_oneline('/sys/class/net/bonding_masters')
74 if (not bondstr or bondname not in bondstr.split()):
75 raise Exception('bond %s not found' %bondname)
76 self._bond_linkinfo_fill_attrs(bondname)
77
78 def _cache_get(self, attrlist, refresh=False):
79 try:
80 if self.DRYRUN:
81 return None
82 if self.CACHE:
6cb589df 83 if not bondutil._cache_fill_done:
15ef32ea 84 self._bond_linkinfo_fill_all()
6cb589df 85 bondutil._cache_fill_done = True
15ef32ea
RP
86 return linkCache.get_attr(attrlist)
87 if not refresh:
88 return linkCache.get_attr(attrlist)
89 self._bond_linkinfo_fill(attrlist[0], refresh)
90 return linkCache.get_attr(attrlist)
91 except Exception, e:
92 self.logger.debug('_cache_get(%s) : [%s]'
93 %(str(attrlist), str(e)))
94 pass
95 return None
96
97 def _cache_check(self, attrlist, value, refresh=False):
98 try:
99 attrvalue = self._cache_get(attrlist, refresh)
100 if attrvalue and attrvalue == value:
101 return True
102 except Exception, e:
103 self.logger.debug('_cache_check(%s) : [%s]'
104 %(str(attrlist), str(e)))
105 pass
106 return False
107
108 def _cache_update(self, attrlist, value):
109 if self.DRYRUN: return
110 try:
111 if attrlist[-1] == 'slaves':
112 linkCache.add_to_attrlist(attrlist, value)
113 return
114 linkCache.add_attr(attrlist, value)
115 except:
116 pass
117
118 def _cache_delete(self, attrlist, value=None):
119 if self.DRYRUN: return
120 try:
121 if attrlist[-1] == 'slaves':
122 linkCache.remove_from_attrlist(attrlist, value)
123 return
124 linkCache.del_attr(attrlist)
125 except:
126 pass
127
128 def _cache_invalidate(self):
129 if self.DRYRUN: return
130 linkCache.invalidate()
131
132 def set_attrs(self, bondname, attrdict, prehook):
133 for attrname, attrval in attrdict.items():
134 if (self._cache_check([bondname, 'linkinfo',
135 attrname], attrval)):
136 continue
137 if (attrname == 'mode' or attrname == 'xmit_hash_policy' or
138 attrname == 'lacp_rate' or attrname == 'min_links'):
139 if prehook:
140 prehook(bondname)
141 try:
b37ec99a
WK
142 if ((attrname not in ['lacp_rate',
143 'lacp_bypass_allow',
144 'lacp_bypass_period',
145 'lacp_bypass_all_active']) or
146 ('mode', '802.3ad') in attrdict.items()):
147 self.write_file('/sys/class/net/%s/bonding/%s'
148 %(bondname, attrname), attrval)
15ef32ea
RP
149 except Exception, e:
150 if self.FORCE:
151 self.logger.warn(str(e))
152 pass
153 else:
154 raise
155
156 def set_use_carrier(self, bondname, use_carrier):
157 if not use_carrier or (use_carrier != '0' and use_carrier != '1'):
158 return
159 if (self._cache_check([bondname, 'linkinfo', 'use_carrier'],
160 use_carrier)):
161 return
162 self.write_file('/sys/class/net/%s' %bondname +
163 '/bonding/use_carrier', use_carrier)
164 self._cache_update([bondname, 'linkinfo',
165 'use_carrier'], use_carrier)
166
167 def get_use_carrier(self, bondname):
168 return self._cache_get([bondname, 'linkinfo', 'use_carrier'])
169
170 def set_xmit_hash_policy(self, bondname, hash_policy, prehook=None):
171 valid_values = ['layer2', 'layer3+4', 'layer2+3']
172 if not hash_policy:
173 return
174 if hash_policy not in valid_values:
175 raise Exception('invalid hash policy value %s' %hash_policy)
176 if (self._cache_check([bondname, 'linkinfo', 'xmit_hash_policy'],
177 hash_policy)):
178 return
179 if prehook:
180 prehook(bondname)
181 self.write_file('/sys/class/net/%s' %bondname +
182 '/bonding/xmit_hash_policy', hash_policy)
183 self._cache_update([bondname, 'linkinfo', 'xmit_hash_policy'],
184 hash_policy)
185
186 def get_xmit_hash_policy(self, bondname):
187 return self._cache_get([bondname, 'linkinfo', 'xmit_hash_policy'])
188
189 def set_miimon(self, bondname, miimon):
190 if (self._cache_check([bondname, 'linkinfo', 'miimon'],
191 miimon)):
192 return
193 self.write_file('/sys/class/net/%s' %bondname +
194 '/bonding/miimon', miimon)
195 self._cache_update([bondname, 'linkinfo', 'miimon'], miimon)
196
197 def get_miimon(self, bondname):
198 return self._cache_get([bondname, 'linkinfo', 'miimon'])
199
ff01349d 200 def set_clag_enable(self, bondname, clag_id):
201 clag_enable = '0' if clag_id == '0' else '1'
202 if self._cache_check([bondname, 'linkinfo', 'clag_enable'],
203 clag_enable) == False:
204 self.write_file('/sys/class/net/%s' %bondname +
205 '/bonding/clag_enable', clag_enable)
206 self._cache_update([bondname, 'linkinfo', 'clag_enable'],
207 clag_enable)
208
209 def get_clag_enable(self, bondname):
210 return self._cache_get([bondname, 'linkinfo', 'clag_enable'])
211
15ef32ea
RP
212 def set_mode(self, bondname, mode, prehook=None):
213 valid_modes = ['balance-rr', 'active-backup', 'balance-xor',
214 'broadcast', '802.3ad', 'balance-tlb', 'balance-alb']
215 if not mode:
216 return
217 if mode not in valid_modes:
218 raise Exception('invalid mode %s' %mode)
219 if (self._cache_check([bondname, 'linkinfo', 'mode'],
220 mode)):
221 return
222 if prehook:
223 prehook(bondname)
224 self.write_file('/sys/class/net/%s' %bondname + '/bonding/mode', mode)
225 self._cache_update([bondname, 'linkinfo', 'mode'], mode)
226
227 def get_mode(self, bondname):
228 return self._cache_get([bondname, 'linkinfo', 'mode'])
229
230 def set_lacp_rate(self, bondname, lacp_rate, prehook=None, posthook=None):
231 if not lacp_rate or (lacp_rate != '0' and lacp_rate != '1'):
232 return
233 if (self._cache_check([bondname, 'linkinfo', 'lacp_rate'],
234 lacp_rate)):
235 return
236 if prehook:
237 prehook(bondname)
238 try:
239 self.write_file('/sys/class/net/%s' %bondname +
240 '/bonding/lacp_rate', lacp_rate)
241 except:
242 raise
243 finally:
244 if posthook:
245 prehook(bondname)
246 self._cache_update([bondname, 'linkinfo',
247 'lacp_rate'], lacp_rate)
248
249 def get_lacp_rate(self, bondname):
250 return self._cache_get([bondname, 'linkinfo', 'lacp_rate'])
251
252 def set_lacp_fallback_allow(self, bondname, allow, prehook=None, posthook=None):
caac3e36
RP
253 if (self._cache_check([bondname, 'linkinfo', 'lacp_bypass_allow'],
254 lacp_bypass_allow)):
15ef32ea
RP
255 return
256 if prehook:
257 prehook(bondname)
258 try:
259 self.write_file('/sys/class/net/%s' %bondname +
caac3e36 260 '/bonding/lacp_bypass_allow', allow)
15ef32ea
RP
261 except:
262 raise
263 finally:
264 if posthook:
265 posthook(bondname)
266 self._cache_update([bondname, 'linkinfo',
caac3e36 267 'lacp_bypass_allow'], allow)
15ef32ea
RP
268
269 def get_lacp_fallback_allow(self, bondname):
caac3e36 270 return self._cache_get([bondname, 'linkinfo', 'lacp_bypass_allow'])
15ef32ea
RP
271
272 def set_lacp_fallback_period(self, bondname, period, prehook=None, posthook=None):
caac3e36
RP
273 if (self._cache_check([bondname, 'linkinfo', 'lacp_bypass_period'],
274 lacp_bypass_period)):
15ef32ea
RP
275 return
276 if prehook:
277 prehook(bondname)
278 try:
279 self.write_file('/sys/class/net/%s' %bondname +
caac3e36 280 '/bonding/lacp_bypass_period', period)
15ef32ea
RP
281 except:
282 raise
283 finally:
284 if posthook:
285 posthook(bondname)
286 self._cache_update([bondname, 'linkinfo',
caac3e36 287 'lacp_bypass_period'], period)
15ef32ea
RP
288
289 def get_lacp_fallback_period(self, bondname):
caac3e36 290 return self._cache_get([bondname, 'linkinfo', 'lacp_bypass_period'])
15ef32ea
RP
291
292 def set_min_links(self, bondname, min_links, prehook=None):
293 if (self._cache_check([bondname, 'linkinfo', 'min_links'],
294 min_links)):
295 return
296 if prehook:
297 prehook(bondname)
298 self.write_file('/sys/class/net/%s/bonding/min_links' %bondname,
299 min_links)
300 self._cache_update([bondname, 'linkinfo', 'min_links'], min_links)
301
302 def get_min_links(self, bondname):
303 return self._cache_get([bondname, 'linkinfo', 'min_links'])
304
305 def set_lacp_fallback_priority(self, bondname, port, val):
caac3e36 306 slavefile = '/sys/class/net/%s/bonding_slave/lacp_bypass_priority' %port
15ef32ea
RP
307 if os.path.exists(slavefile):
308 self.write_file(slavefile, val)
309
310 def get_lacp_fallback_priority(self, bondname):
311 slaves = self.get_slaves(bondname)
312 if not slaves:
313 return slaves
314 prios = []
315 for slave in slaves:
caac3e36 316 priofile = '/sys/class/net/%s/bonding_slave/lacp_bypass_priority' %slave
15ef32ea
RP
317 if os.path.exists(priofile):
318 val = self.read_file_oneline(priofile)
319 if val and val != '0':
320 prio = slave + '=' + val
321 prios.append(prio)
322 prios.sort()
323 prio_str = ' '.join(prios)
324 return prio_str
325
137de5d7
WK
326 def set_lacp_fallback_all_active(self, bondname, useprio, prehook=None, posthook=None):
327 if (self._cache_check([bondname, 'linkinfo', 'lacp_bypass_all_active'],
328 lacp_bypass_all_active)):
48db65f6
WK
329 return
330 if prehook:
331 prehook(bondname)
332 try:
333 self.write_file('/sys/class/net/%s' %bondname +
137de5d7 334 '/bonding/lacp_bypass_all_active', useprio)
48db65f6
WK
335 except:
336 raise
337 finally:
338 if posthook:
339 posthook(bondname)
340 self._cache_update([bondname, 'linkinfo',
137de5d7 341 'lacp_bypass_all_active'], useprio)
48db65f6 342
137de5d7
WK
343 def get_lacp_fallback_all_active(self, bondname):
344 return self._cache_get([bondname, 'linkinfo', 'lacp_bypass_all_active'])
48db65f6 345
15ef32ea
RP
346 def get_ad_sys_mac_addr(self, bondname):
347 return self._cache_get([bondname, 'linkinfo', 'ad_sys_mac_addr'])
348
349 def get_ad_sys_priority(self, bondname):
350 return self._cache_get([bondname, 'linkinfo', 'ad_sys_priority'])
351
352 def enslave_slave(self, bondname, slave, prehook=None, posthook=None):
353 slaves = self._cache_get([bondname, 'linkinfo', 'slaves'])
354 if slaves and slave in slaves: return
355 if prehook:
356 prehook(slave)
357 self.write_file('/sys/class/net/%s' %bondname +
358 '/bonding/slaves', '+' + slave)
359 if posthook:
360 posthook(slave)
361 self._cache_update([bondname, 'linkinfo', 'slaves'], slave)
362
363 def remove_slave(self, bondname, slave):
364 slaves = self._cache_get([bondname, 'linkinfo', 'slaves'])
365 if slave not in slaves:
366 return
367 sysfs_bond_path = ('/sys/class/net/%s' %bondname +
368 '/bonding/slaves')
369 if not os.path.exists(sysfs_bond_path):
370 return
371 self.write_file(sysfs_bond_path, '-' + slave)
372 self._cache_delete([bondname, 'linkinfo', 'slaves'], slave)
373
374 def remove_slaves_all(self, bondname):
375 if not _self._cache_get([bondname, 'linkinfo', 'slaves']):
376 return
377 slaves = None
378 sysfs_bond_path = ('/sys/class/net/%s' %bondname +
379 '/bonding/slaves')
380 ipcmd = iproute2()
381 try:
382 f = open(sysfs_bond_path, 'r')
383 slaves = f.readline().strip().split()
384 f.close()
385 except IOError, e:
386 raise Exception('error reading slaves of bond %s' %bondname
387 + '(' + str(e) + ')')
388 for slave in slaves:
389 ipcmd.ip_link_down(slave)
390 try:
391 self.remove_slave(bondname, slave)
392 except Exception, e:
393 if not self.FORCE:
394 raise Exception('error removing slave %s'
395 %slave + ' from bond %s' %bondname +
396 '(%s)' %str(e))
397 else:
398 pass
399 self._cache_del([bondname, 'linkinfo', 'slaves'])
400
401 def load_bonding_module(self):
402 return self.exec_command('modprobe -q bonding')
403
404 def create_bond(self, bondname):
405 if self.bond_exists(bondname):
406 return
407 sysfs_net = '/sys/class/net/'
408 sysfs_bonding_masters = sysfs_net + 'bonding_masters'
409 if not os.path.exists(sysfs_bonding_masters):
410 self.logger.debug('loading bonding driver')
411 self.load_bonding_module()
412 return True
413 self.write_file(sysfs_bonding_masters, '+' + bondname)
414 self._cache_update([bondname], {})
415
416 def delete_bond(self, bondname):
417 if not os.path.exists('/sys/class/net/%s' %bondname):
418 return
419 self.write_file('/sys/class/net/bonding_masters', '-' + bondname)
420 self._cache_delete([bondname])
421
422 def unset_master(self, bondname):
423 print 'Do nothing yet'
424 return 0
425
426 def get_slaves(self, bondname):
427 slaves = self._cache_get([bondname, 'linkinfo', 'slaves'])
428 if slaves:
429 return list(slaves)
430 slavefile = '/sys/class/net/%s/bonding/slaves' %bondname
431 if os.path.exists(slavefile):
432 buf = self.read_file_oneline(slavefile)
433 if buf:
434 slaves = buf.split()
435 if not slaves:
436 return slaves
437 self._cache_update([bondname, 'linkinfo', 'slaves'], slaves)
438 return list(slaves)
439
440 def bond_slave_exists(self, bond, slave):
441 slaves = self.get_slaves(bond)
442 if not slaves: return False
443 return slave in slaves
444
445 def bond_exists(self, bondname):
446 return os.path.exists('/sys/class/net/%s/bonding' %bondname)