]> git.proxmox.com Git - mirror_ifupdown2.git/blob - addons/ifenslave.py
Move ifupdown2addons into ifupdown2 pacakge
[mirror_ifupdown2.git] / addons / ifenslave.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 from sets import Set
8 from ifupdown.iface import *
9 import ifupdownaddons
10 from ifupdownaddons.modulebase import moduleBase
11 from ifupdownaddons.ifenslaveutil import ifenslaveutil
12 from ifupdownaddons.iproute2 import iproute2
13
14 class ifenslave(moduleBase):
15 """ ifupdown2 addon module to configure bond interfaces """
16 _modinfo = { 'mhelp' : 'bond configuration module',
17 'attrs' : {
18 'bond-use-carrier':
19 {'help' : 'bond use carrier',
20 'validvals' : ['0', '1'],
21 'default' : '1',
22 'example': ['bond-use-carrier 1']},
23 'bond-num-grat-arp':
24 {'help' : 'bond use carrier',
25 'validrange' : ['0', '255'],
26 'default' : '1',
27 'example' : ['bond-num-grat-arp 1']},
28 'bond-num-unsol-na' :
29 {'help' : 'bond slave devices',
30 'validrange' : ['0', '255'],
31 'default' : '1',
32 'example' : ['bond-num-unsol-na 1']},
33 'bond-xmit-hash-policy' :
34 {'help' : 'bond slave devices',
35 'validvals' : ['layer2', 'layer3+4', 'layer2+3'],
36 'default' : 'layer2',
37 'example' : ['bond-xmit-hash-policy layer2']},
38 'bond-miimon' :
39 {'help' : 'bond miimon',
40 'validrange' : ['0', '255'],
41 'default' : '0',
42 'example' : ['bond-miimon 0']},
43 'bond-mode' :
44 {'help' : 'bond mode',
45 'validvals' : ['balance-rr', 'active-backup',
46 'balance-xor', 'broadcast', '802.3ad',
47 'balance-tlb', 'balance-alb'],
48 'default' : 'balance-rr',
49 'example' : ['bond-mode 802.3ad']},
50 'bond-lacp-rate':
51 {'help' : 'bond use carrier',
52 'validvals' : ['0', '1'],
53 'default' : '0',
54 'example' : ['bond-lacp-rate 0']},
55 'bond-min-links':
56 {'help' : 'bond min links',
57 'default' : '0',
58 'example' : ['bond-min-links 0']},
59 'bond-ad-sys-priority':
60 {'help' : '802.3ad system priority',
61 'default' : '65535',
62 'example' : ['bond-ad-sys-priority 65535']},
63 'bond-ad-sys-mac-addr':
64 {'help' : '802.3ad system mac address',
65 'default' : '00:00:00:00:00:00',
66 'example' : ['bond-ad-sys-mac-addr 00:00:00:00:00:00']},
67 'bond-lacp-fallback-allow':
68 {'help' : 'allow lacp fall back',
69 'validvals' : ['0', '1'],
70 'default' : '0',
71 'example' : ['bond-lacp-fallback-allow 0']},
72 'bond-lacp-fallback-period':
73 {'help' : 'grace period (seconds) for lacp fall back',
74 'validrange' : ['0', '100'],
75 'default' : '90',
76 'example' : ['bond-lacp-fallback-period 100']},
77 'bond-lacp-fallback-priority':
78 {'help' : 'slave priority for lacp fall back',
79 'example' : ['bond-lacp-fallback-priority swp1=1 swp2=1 swp3=2']},
80 'bond-slaves' :
81 {'help' : 'bond slaves',
82 'required' : True,
83 'example' : ['bond-slaves swp1 swp2',
84 'bond-slaves glob swp1-2',
85 'bond-slaves regex (swp[1|2)']}}}
86
87 def __init__(self, *args, **kargs):
88 ifupdownaddons.modulebase.moduleBase.__init__(self, *args, **kargs)
89 self.ipcmd = None
90 self.ifenslavecmd = None
91
92 def _is_bond(self, ifaceobj):
93 if ifaceobj.get_attr_value_first('bond-slaves'):
94 return True
95 return False
96
97 def get_dependent_ifacenames(self, ifaceobj, ifacenames_all=None):
98 """ Returns list of interfaces dependent on ifaceobj """
99
100 if not self._is_bond(ifaceobj):
101 return None
102 slave_list = self.parse_port_list(ifaceobj.get_attr_value_first(
103 'bond-slaves'), ifacenames_all)
104
105 # Also save a copy for future use
106 ifaceobj.priv_data = list(slave_list)
107 return slave_list
108
109 def get_dependent_ifacenames_running(self, ifaceobj):
110 self._init_command_handlers()
111 return self.ifenslavecmd.get_slaves(ifaceobj.name)
112
113 def _get_slave_list(self, ifaceobj):
114 """ Returns slave list present in ifaceobj config """
115
116 # If priv data already has slave list use that first.
117 if ifaceobj.priv_data:
118 return ifaceobj.priv_data
119 slaves = ifaceobj.get_attr_value_first('bond-slaves')
120 if slaves:
121 return self.parse_port_list(slaves)
122 else:
123 return None
124
125 def fetch_attr(self, ifaceobj, attrname):
126 attrval = ifaceobj.get_attr_value_first(attrname)
127 if attrval:
128 msg = ('%s: invalid value %s for attr %s.'
129 %(ifaceobj.name, attrval, attrname))
130 optiondict = self.get_mod_attr(attrname)
131 if not optiondict:
132 return None
133 validvals = optiondict.get('validvals')
134 if validvals and attrval not in validvals:
135 raise Exception(msg + ' Valid values are %s' %str(validvals))
136 validrange = optiondict.get('validrange')
137 if validrange:
138 if (int(attrval) < int(validrange[0]) or
139 int(attrval) > int(validrange[1])):
140 raise Exception(msg + ' Valid range is [%s,%s]'
141 %(validrange[0], validrange[1]))
142 return attrval
143
144 def _apply_master_settings(self, ifaceobj):
145 have_attrs_to_set = 0
146 ifenslavecmd_attrmap = OrderedDict([('bond-mode' , 'mode'),
147 ('bond-miimon' , 'miimon'),
148 ('bond-use-carrier', 'use_carrier'),
149 ('bond-lacp-rate' , 'lacp_rate'),
150 ('bond-xmit-hash-policy' , 'xmit_hash_policy'),
151 ('bond-min-links' , 'min_links'),
152 ('bond-num-grat-arp' , 'num_grat_arp'),
153 ('bond-num-unsol-na' , 'num_unsol_na'),
154 ('bond-ad-sys-mac-addr' , 'ad_sys_mac_addr'),
155 ('bond-ad-sys-priority' , 'ad_sys_priority'),
156 ('bond-lacp-fallback-allow', 'lacp_fallback_allow'),
157 ('bond-lacp-fallback-period', 'lacp_fallback_period')])
158 linkstatus = self.ipcmd.link_get_status(ifaceobj.name)
159 if not linkstatus:
160 # assume link status is 'UP'
161 linkstatus = 'UP'
162 try:
163 # order of attributes set matters for bond, so
164 # construct the list sequentially
165 attrstoset = OrderedDict()
166 for k, dstk in ifenslavecmd_attrmap.items():
167 v = self.fetch_attr(ifaceobj, k)
168 if v:
169 attrstoset[dstk] = v
170 if not attrstoset:
171 return
172 have_attrs_to_set = 1
173 self.ifenslavecmd.set_attrs(ifaceobj.name, attrstoset,
174 self.ipcmd.link_down if linkstatus == 'UP' else None)
175 except:
176 raise
177 finally:
178 if have_attrs_to_set and linkstatus == 'UP':
179 self.ipcmd.link_up(ifaceobj.name)
180
181 def _add_slaves(self, ifaceobj):
182 runningslaves = []
183
184 slaves = self._get_slave_list(ifaceobj)
185 if not slaves:
186 self.logger.debug('%s: no slaves found' %ifaceobj.name)
187 return
188
189 if not self.PERFMODE:
190 runningslaves = self.ifenslavecmd.get_slaves(ifaceobj.name);
191 if runningslaves:
192 # Delete active slaves not in the new slave list
193 [ self.ifenslavecmd.remove_slave(ifaceobj.name, s)
194 for s in runningslaves if s not in slaves ]
195
196 for slave in Set(slaves).difference(Set(runningslaves)):
197 if (not self.PERFMODE and
198 not self.ipcmd.link_exists(slave)):
199 self.log_warn('%s: skipping slave %s, does not exist'
200 %(ifaceobj.name, slave))
201 continue
202 self.ifenslavecmd.enslave_slave(ifaceobj.name, slave,
203 prehook=self.ipcmd.link_down,
204 posthook=self.ipcmd.link_up)
205
206 def _apply_slaves_lacp_fallback_prio(self, ifaceobj):
207 slaves = self.ifenslavecmd.get_slaves(ifaceobj.name)
208 attrval = ifaceobj.get_attr_value_first('bond-lacp-fallback-priority')
209 if attrval:
210 portlist = self.parse_port_list(attrval)
211 if not portlist:
212 self.log_warn('%s: could not parse \'%s %s\''
213 %(ifaceobj.name, attrname, attrval))
214 return
215
216 for p in portlist:
217 try:
218 (port, val) = p.split('=')
219 if port not in slaves:
220 self.log_warn('%s: skipping slave %s, does not exist'
221 %(ifaceobj.name, port))
222 continue
223 slaves.remove(port)
224 self.ifenslavecmd.set_lacp_fallback_priority(ifaceobj.name, port, val)
225 except Exception, e:
226 self.log_warn('%s: failed to set lacp_fallback_priority %s (%s)'
227 %(ifaceobj.name, port, str(e)))
228
229 for p in slaves:
230 try:
231 self.ifenslavecmd.set_lacp_fallback_priority(ifaceobj.name, p, '0')
232 except Exception, e:
233 self.log_warn('%s: failed to clear lacp_fallback_priority %s (%s)'
234 %(ifaceobj.name, p, str(e)))
235
236
237 def _up(self, ifaceobj):
238 try:
239 if not self.ipcmd.link_exists(ifaceobj.name):
240 self.ifenslavecmd.create_bond(ifaceobj.name)
241 self._apply_master_settings(ifaceobj)
242 self._add_slaves(ifaceobj)
243 self._apply_slaves_lacp_fallback_prio(ifaceobj)
244 except Exception, e:
245 self.log_error(str(e))
246
247 def _down(self, ifaceobj):
248 try:
249 self.ifenslavecmd.delete_bond(ifaceobj.name)
250 except Exception, e:
251 self.log_warn(str(e))
252
253 def _query_check(self, ifaceobj, ifaceobjcurr):
254 slaves = None
255
256 if not self.ifenslavecmd.bond_exists(ifaceobj.name):
257 self.logger.debug('bond iface %s' %ifaceobj.name +
258 ' does not exist')
259 ifaceobjcurr.status = ifaceStatus.NOTFOUND
260 return
261
262 ifaceattrs = self.dict_key_subset(ifaceobj.config,
263 self.get_mod_attrs())
264 if not ifaceattrs: return
265 runningattrs = self._query_running_attrs(ifaceobj.name)
266 for k in ifaceattrs:
267 v = ifaceobj.get_attr_value_first(k)
268 if not v:
269 continue
270 if k == 'bond-slaves':
271 slaves = v.split()
272 continue
273 rv = runningattrs.get(k)
274 if not rv:
275 ifaceobjcurr.update_config_with_status(k, 'None', 1)
276 else:
277 if k == 'bond-lacp-fallback-priority':
278 prios = v.split()
279 prios.sort()
280 prio_str = ' '.join(prios)
281 ifaceobjcurr.update_config_with_status(k, rv,
282 1 if prio_str != rv else 0)
283 continue
284
285 ifaceobjcurr.update_config_with_status(k, rv,
286 1 if v != rv else 0)
287 runningslaves = runningattrs.get('bond-slaves')
288 if not slaves and not runningslaves:
289 return
290 retslave = 1
291 if slaves and runningslaves:
292 if slaves and runningslaves:
293 difference = set(slaves).symmetric_difference(runningslaves)
294 if not difference:
295 retslave = 0
296 ifaceobjcurr.update_config_with_status('bond-slaves',
297 ' '.join(runningslaves)
298 if runningslaves else 'None', retslave)
299
300 def _query_running_attrs(self, bondname):
301 bondattrs = {'bond-mode' :
302 self.ifenslavecmd.get_mode(bondname),
303 'bond-miimon' :
304 self.ifenslavecmd.get_miimon(bondname),
305 'bond-use-carrier' :
306 self.ifenslavecmd.get_use_carrier(bondname),
307 'bond-lacp-rate' :
308 self.ifenslavecmd.get_lacp_rate(bondname),
309 'bond-min-links' :
310 self.ifenslavecmd.get_min_links(bondname),
311 'bond-ad-sys-mac-addr' :
312 self.ifenslavecmd.get_ad_sys_mac_addr(bondname),
313 'bond-ad-sys-priority' :
314 self.ifenslavecmd.get_ad_sys_priority(bondname),
315 'bond-xmit-hash-policy' :
316 self.ifenslavecmd.get_xmit_hash_policy(bondname),
317 'bond-lacp-fallback-allow' :
318 self.ifenslavecmd.get_lacp_fallback_allow(bondname),
319 'bond-lacp-fallback-period' :
320 self.ifenslavecmd.get_lacp_fallback_period(bondname),
321 'bond-lacp-fallback-priority' :
322 self.ifenslavecmd.get_lacp_fallback_priority(bondname)}
323 slaves = self.ifenslavecmd.get_slaves(bondname)
324 if slaves:
325 bondattrs['bond-slaves'] = slaves
326 return bondattrs
327
328 def _query_running(self, ifaceobjrunning):
329 if not self.ifenslavecmd.bond_exists(ifaceobjrunning.name):
330 return
331 bondattrs = self._query_running_attrs(ifaceobjrunning.name)
332 if bondattrs.get('bond-slaves'):
333 bondattrs['bond-slaves'] = ' '.join(bondattrs.get('bond-slaves'))
334 [ifaceobjrunning.update_config(k, v)
335 for k, v in bondattrs.items()
336 if v and v != self.get_mod_subattr(k, 'default')]
337
338 _run_ops = {'pre-up' : _up,
339 'post-down' : _down,
340 'query-running' : _query_running,
341 'query-checkcurr' : _query_check}
342
343 def get_ops(self):
344 """ returns list of ops supported by this module """
345 return self._run_ops.keys()
346
347 def _init_command_handlers(self):
348 flags = self.get_flags()
349 if not self.ipcmd:
350 self.ipcmd = iproute2(**flags)
351 if not self.ifenslavecmd:
352 self.ifenslavecmd = ifenslaveutil(**flags)
353
354 def run(self, ifaceobj, operation, query_ifaceobj=None):
355 """ run bond configuration on the interface object passed as argument
356
357 Args:
358 **ifaceobj** (object): iface object
359
360 **operation** (str): any of 'pre-up', 'post-down', 'query-checkcurr',
361 'query-running'
362
363 Kwargs:
364 **query_ifaceobj** (object): query check ifaceobject. This is only
365 valid when op is 'query-checkcurr'. It is an object same as
366 ifaceobj, but contains running attribute values and its config
367 status. The modules can use it to return queried running state
368 of interfaces. status is success if the running state is same
369 as user required state in ifaceobj. error otherwise.
370 """
371 op_handler = self._run_ops.get(operation)
372 if not op_handler:
373 return
374 if operation != 'query-running' and not self._is_bond(ifaceobj):
375 return
376 self._init_command_handlers()
377 if operation == 'query-checkcurr':
378 op_handler(self, ifaceobj, query_ifaceobj)
379 else:
380 op_handler(self, ifaceobj)