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