]>
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 | |
de1ee6b0 | 8 | import re |
fc5e1735 | 9 | import ifupdown.ifupdownflags as ifupdownflags |
a193d8d1 | 10 | from ifupdown.utils import utils |
15ef32ea RP |
11 | from ifupdown.iface import * |
12 | from utilsbase import * | |
13 | from iproute2 import * | |
14 | from cache import * | |
15 | ||
6cb589df | 16 | class 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) |