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