]> git.proxmox.com Git - mirror_ifupdown2.git/blob - ifupdownaddons/ifenslaveutil.py
addons: tunnel: Fix (re)creation of tunnelsof any kind.
[mirror_ifupdown2.git] / ifupdownaddons / ifenslaveutil.py
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 import re
9 from ifupdown.iface import *
10 from utilsbase import *
11 from iproute2 import *
12 from cache import *
13
14 class ifenslaveutil(utilsBase):
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',
56 'num_grat_arp', 'lacp_bypass_allow', 'lacp_bypass_period',
57 'clag_enable'])
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:
83 if not ifenslaveutil._cache_fill_done:
84 self._bond_linkinfo_fill_all()
85 ifenslaveutil._cache_fill_done = True
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:
142 self.write_file('/sys/class/net/%s/bonding/%s'
143 %(bondname, attrname), attrval)
144 except Exception, e:
145 if self.FORCE:
146 self.logger.warn(str(e))
147 pass
148 else:
149 raise
150
151 def set_use_carrier(self, bondname, use_carrier):
152 if not use_carrier or (use_carrier != '0' and use_carrier != '1'):
153 return
154 if (self._cache_check([bondname, 'linkinfo', 'use_carrier'],
155 use_carrier)):
156 return
157 self.write_file('/sys/class/net/%s' %bondname +
158 '/bonding/use_carrier', use_carrier)
159 self._cache_update([bondname, 'linkinfo',
160 'use_carrier'], use_carrier)
161
162 def get_use_carrier(self, bondname):
163 return self._cache_get([bondname, 'linkinfo', 'use_carrier'])
164
165 def set_xmit_hash_policy(self, bondname, hash_policy, prehook=None):
166 valid_values = ['layer2', 'layer3+4', 'layer2+3']
167 if not hash_policy:
168 return
169 if hash_policy not in valid_values:
170 raise Exception('invalid hash policy value %s' %hash_policy)
171 if (self._cache_check([bondname, 'linkinfo', 'xmit_hash_policy'],
172 hash_policy)):
173 return
174 if prehook:
175 prehook(bondname)
176 self.write_file('/sys/class/net/%s' %bondname +
177 '/bonding/xmit_hash_policy', hash_policy)
178 self._cache_update([bondname, 'linkinfo', 'xmit_hash_policy'],
179 hash_policy)
180
181 def get_xmit_hash_policy(self, bondname):
182 return self._cache_get([bondname, 'linkinfo', 'xmit_hash_policy'])
183
184 def set_miimon(self, bondname, miimon):
185 if (self._cache_check([bondname, 'linkinfo', 'miimon'],
186 miimon)):
187 return
188 self.write_file('/sys/class/net/%s' %bondname +
189 '/bonding/miimon', miimon)
190 self._cache_update([bondname, 'linkinfo', 'miimon'], miimon)
191
192 def get_miimon(self, bondname):
193 return self._cache_get([bondname, 'linkinfo', 'miimon'])
194
195 def set_clag_enable(self, bondname, clag_id):
196 clag_enable = '0' if clag_id == '0' else '1'
197 if self._cache_check([bondname, 'linkinfo', 'clag_enable'],
198 clag_enable) == False:
199 self.write_file('/sys/class/net/%s' %bondname +
200 '/bonding/clag_enable', clag_enable)
201 self._cache_update([bondname, 'linkinfo', 'clag_enable'],
202 clag_enable)
203
204 def get_clag_enable(self, bondname):
205 return self._cache_get([bondname, 'linkinfo', 'clag_enable'])
206
207 def set_mode(self, bondname, mode, prehook=None):
208 valid_modes = ['balance-rr', 'active-backup', 'balance-xor',
209 'broadcast', '802.3ad', 'balance-tlb', 'balance-alb']
210 if not mode:
211 return
212 if mode not in valid_modes:
213 raise Exception('invalid mode %s' %mode)
214 if (self._cache_check([bondname, 'linkinfo', 'mode'],
215 mode)):
216 return
217 if prehook:
218 prehook(bondname)
219 self.write_file('/sys/class/net/%s' %bondname + '/bonding/mode', mode)
220 self._cache_update([bondname, 'linkinfo', 'mode'], mode)
221
222 def get_mode(self, bondname):
223 return self._cache_get([bondname, 'linkinfo', 'mode'])
224
225 def set_lacp_rate(self, bondname, lacp_rate, prehook=None, posthook=None):
226 if not lacp_rate or (lacp_rate != '0' and lacp_rate != '1'):
227 return
228 if (self._cache_check([bondname, 'linkinfo', 'lacp_rate'],
229 lacp_rate)):
230 return
231 if prehook:
232 prehook(bondname)
233 try:
234 self.write_file('/sys/class/net/%s' %bondname +
235 '/bonding/lacp_rate', lacp_rate)
236 except:
237 raise
238 finally:
239 if posthook:
240 prehook(bondname)
241 self._cache_update([bondname, 'linkinfo',
242 'lacp_rate'], lacp_rate)
243
244 def get_lacp_rate(self, bondname):
245 return self._cache_get([bondname, 'linkinfo', 'lacp_rate'])
246
247 def set_lacp_fallback_allow(self, bondname, allow, prehook=None, posthook=None):
248 if (self._cache_check([bondname, 'linkinfo', 'lacp_bypass_allow'],
249 lacp_bypass_allow)):
250 return
251 if prehook:
252 prehook(bondname)
253 try:
254 self.write_file('/sys/class/net/%s' %bondname +
255 '/bonding/lacp_bypass_allow', allow)
256 except:
257 raise
258 finally:
259 if posthook:
260 posthook(bondname)
261 self._cache_update([bondname, 'linkinfo',
262 'lacp_bypass_allow'], allow)
263
264 def get_lacp_fallback_allow(self, bondname):
265 return self._cache_get([bondname, 'linkinfo', 'lacp_bypass_allow'])
266
267 def set_lacp_fallback_period(self, bondname, period, prehook=None, posthook=None):
268 if (self._cache_check([bondname, 'linkinfo', 'lacp_bypass_period'],
269 lacp_bypass_period)):
270 return
271 if prehook:
272 prehook(bondname)
273 try:
274 self.write_file('/sys/class/net/%s' %bondname +
275 '/bonding/lacp_bypass_period', period)
276 except:
277 raise
278 finally:
279 if posthook:
280 posthook(bondname)
281 self._cache_update([bondname, 'linkinfo',
282 'lacp_bypass_period'], period)
283
284 def get_lacp_fallback_period(self, bondname):
285 return self._cache_get([bondname, 'linkinfo', 'lacp_bypass_period'])
286
287 def set_min_links(self, bondname, min_links, prehook=None):
288 if (self._cache_check([bondname, 'linkinfo', 'min_links'],
289 min_links)):
290 return
291 if prehook:
292 prehook(bondname)
293 self.write_file('/sys/class/net/%s/bonding/min_links' %bondname,
294 min_links)
295 self._cache_update([bondname, 'linkinfo', 'min_links'], min_links)
296
297 def get_min_links(self, bondname):
298 return self._cache_get([bondname, 'linkinfo', 'min_links'])
299
300 def set_lacp_fallback_priority(self, bondname, port, val):
301 slavefile = '/sys/class/net/%s/bonding_slave/lacp_bypass_priority' %port
302 if os.path.exists(slavefile):
303 self.write_file(slavefile, val)
304
305 def get_lacp_fallback_priority(self, bondname):
306 slaves = self.get_slaves(bondname)
307 if not slaves:
308 return slaves
309 prios = []
310 for slave in slaves:
311 priofile = '/sys/class/net/%s/bonding_slave/lacp_bypass_priority' %slave
312 if os.path.exists(priofile):
313 val = self.read_file_oneline(priofile)
314 if val and val != '0':
315 prio = slave + '=' + val
316 prios.append(prio)
317 prios.sort()
318 prio_str = ' '.join(prios)
319 return prio_str
320
321 def get_ad_sys_mac_addr(self, bondname):
322 return self._cache_get([bondname, 'linkinfo', 'ad_sys_mac_addr'])
323
324 def get_ad_sys_priority(self, bondname):
325 return self._cache_get([bondname, 'linkinfo', 'ad_sys_priority'])
326
327 def enslave_slave(self, bondname, slave, prehook=None, posthook=None):
328 slaves = self._cache_get([bondname, 'linkinfo', 'slaves'])
329 if slaves and slave in slaves: return
330 if prehook:
331 prehook(slave)
332 self.write_file('/sys/class/net/%s' %bondname +
333 '/bonding/slaves', '+' + slave)
334 if posthook:
335 posthook(slave)
336 self._cache_update([bondname, 'linkinfo', 'slaves'], slave)
337
338 def remove_slave(self, bondname, slave):
339 slaves = self._cache_get([bondname, 'linkinfo', 'slaves'])
340 if slave not in slaves:
341 return
342 sysfs_bond_path = ('/sys/class/net/%s' %bondname +
343 '/bonding/slaves')
344 if not os.path.exists(sysfs_bond_path):
345 return
346 self.write_file(sysfs_bond_path, '-' + slave)
347 self._cache_delete([bondname, 'linkinfo', 'slaves'], slave)
348
349 def remove_slaves_all(self, bondname):
350 if not _self._cache_get([bondname, 'linkinfo', 'slaves']):
351 return
352 slaves = None
353 sysfs_bond_path = ('/sys/class/net/%s' %bondname +
354 '/bonding/slaves')
355 ipcmd = iproute2()
356 try:
357 f = open(sysfs_bond_path, 'r')
358 slaves = f.readline().strip().split()
359 f.close()
360 except IOError, e:
361 raise Exception('error reading slaves of bond %s' %bondname
362 + '(' + str(e) + ')')
363 for slave in slaves:
364 ipcmd.ip_link_down(slave)
365 try:
366 self.remove_slave(bondname, slave)
367 except Exception, e:
368 if not self.FORCE:
369 raise Exception('error removing slave %s'
370 %slave + ' from bond %s' %bondname +
371 '(%s)' %str(e))
372 else:
373 pass
374 self._cache_del([bondname, 'linkinfo', 'slaves'])
375
376 def load_bonding_module(self):
377 return self.exec_command('modprobe -q bonding')
378
379 def create_bond(self, bondname):
380 if self.bond_exists(bondname):
381 return
382 sysfs_net = '/sys/class/net/'
383 sysfs_bonding_masters = sysfs_net + 'bonding_masters'
384 if not os.path.exists(sysfs_bonding_masters):
385 self.logger.debug('loading bonding driver')
386 self.load_bonding_module()
387 return True
388 self.write_file(sysfs_bonding_masters, '+' + bondname)
389 self._cache_update([bondname], {})
390
391 def delete_bond(self, bondname):
392 if not os.path.exists('/sys/class/net/%s' %bondname):
393 return
394 self.write_file('/sys/class/net/bonding_masters', '-' + bondname)
395 self._cache_delete([bondname])
396
397 def unset_master(self, bondname):
398 print 'Do nothing yet'
399 return 0
400
401 def get_slaves(self, bondname):
402 slaves = self._cache_get([bondname, 'linkinfo', 'slaves'])
403 if slaves:
404 return list(slaves)
405 slavefile = '/sys/class/net/%s/bonding/slaves' %bondname
406 if os.path.exists(slavefile):
407 buf = self.read_file_oneline(slavefile)
408 if buf:
409 slaves = buf.split()
410 if not slaves:
411 return slaves
412 self._cache_update([bondname, 'linkinfo', 'slaves'], slaves)
413 return list(slaves)
414
415 def bond_slave_exists(self, bond, slave):
416 slaves = self.get_slaves(bond)
417 if not slaves: return False
418 return slave in slaves
419
420 def bond_exists(self, bondname):
421 return os.path.exists('/sys/class/net/%s/bonding' %bondname)