]> git.proxmox.com Git - mirror_ifupdown2.git/blame - addons/bond.py
attribute syntax check using validvals/validrange and keywords
[mirror_ifupdown2.git] / addons / bond.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
7from sets import Set
8from ifupdown.iface import *
9import ifupdownaddons
10from ifupdownaddons.modulebase import moduleBase
6cb589df 11from ifupdownaddons.bondutil import bondutil
15ef32ea 12from ifupdownaddons.iproute2 import iproute2
2864d6f3 13from ifupdown.netlink import netlink
6cb589df 14import ifupdown.policymanager as policymanager
fc5e1735 15import ifupdown.ifupdownflags as ifupdownflags
594fb088 16from ifupdown.utils import utils
15ef32ea 17
6cb589df 18class bond(moduleBase):
15ef32ea
RP
19 """ ifupdown2 addon module to configure bond interfaces """
20 _modinfo = { 'mhelp' : 'bond configuration module',
21 'attrs' : {
22 'bond-use-carrier':
23 {'help' : 'bond use carrier',
594fb088
JF
24 'validvals' : ['yes', 'no', '0', '1'],
25 'default' : 'yes',
26 'example': ['bond-use-carrier yes']},
15ef32ea
RP
27 'bond-num-grat-arp':
28 {'help' : 'bond use carrier',
29 'validrange' : ['0', '255'],
30 'default' : '1',
31 'example' : ['bond-num-grat-arp 1']},
32 'bond-num-unsol-na' :
33 {'help' : 'bond slave devices',
34 'validrange' : ['0', '255'],
35 'default' : '1',
36 'example' : ['bond-num-unsol-na 1']},
37 'bond-xmit-hash-policy' :
38 {'help' : 'bond slave devices',
39 'validvals' : ['layer2', 'layer3+4', 'layer2+3'],
40 'default' : 'layer2',
41 'example' : ['bond-xmit-hash-policy layer2']},
42 'bond-miimon' :
43 {'help' : 'bond miimon',
44 'validrange' : ['0', '255'],
45 'default' : '0',
46 'example' : ['bond-miimon 0']},
47 'bond-mode' :
1a13da93
JF
48 {'help': 'bond mode',
49 'validvals': ['0', 'balance-rr',
50 '1', 'active-backup',
51 '2', 'balance-xor',
52 '3', 'broadcast',
53 '4', '802.3ad',
54 '5', 'balance-tlb',
55 '6', 'balance-alb'],
56 'default': 'balance-rr',
57 'example': ['bond-mode 802.3ad']},
15ef32ea 58 'bond-lacp-rate':
e1601369 59 {'help' : 'bond lacp rate',
15ef32ea
RP
60 'validvals' : ['0', '1'],
61 'default' : '0',
62 'example' : ['bond-lacp-rate 0']},
63 'bond-min-links':
64 {'help' : 'bond min links',
65 'default' : '0',
c6370b56 66 'validrange' : ['0', '255'],
15ef32ea
RP
67 'example' : ['bond-min-links 0']},
68 'bond-ad-sys-priority':
69 {'help' : '802.3ad system priority',
70 'default' : '65535',
c6370b56 71 'validrange' : ['0', '65535'],
1553a881
RP
72 'example' : ['bond-ad-sys-priority 65535'],
73 'deprecated' : True,
74 'new-attribute' : 'bond-ad-actor-sys-prio'},
75 'bond-ad-actor-sys-prio':
76 {'help' : '802.3ad system priority',
77 'default' : '65535',
c6370b56 78 'validrange' : ['0', '65535'],
1553a881 79 'example' : ['bond-ad-actor-sys-prio 65535']},
15ef32ea
RP
80 'bond-ad-sys-mac-addr':
81 {'help' : '802.3ad system mac address',
82 'default' : '00:00:00:00:00:00',
482b2fab 83 'validvals': ['<mac>', ],
1553a881
RP
84 'example' : ['bond-ad-sys-mac-addr 00:00:00:00:00:00'],
85 'deprecated' : True,
86 'new-attribute' : 'bond-ad-actor-system'},
87 'bond-ad-actor-system':
88 {'help' : '802.3ad system mac address',
89 'default' : '00:00:00:00:00:00',
482b2fab 90 'validvals': ['<mac>', ],
1553a881 91 'example' : ['bond-ad-actor-system 00:00:00:00:00:00'],},
7b2469db
WK
92 'bond-lacp-bypass-allow':
93 {'help' : 'allow lacp bypass',
594fb088
JF
94 'validvals' : ['yes', 'no', '0', '1'],
95 'default' : 'no',
96 'example' : ['bond-lacp-bypass-allow no']},
15ef32ea
RP
97 'bond-slaves' :
98 {'help' : 'bond slaves',
99 'required' : True,
c6370b56 100 'multivalue' : True,
482b2fab 101 'validvals': ['<interface-list>'],
15ef32ea
RP
102 'example' : ['bond-slaves swp1 swp2',
103 'bond-slaves glob swp1-2',
fff589ea 104 'bond-slaves regex (swp[1|2)']}}}
15ef32ea 105
1a13da93
JF
106 _bond_mode_num = {'0': 'balance-rr',
107 '1': 'active-backup',
108 '2': 'balance-xor',
109 '3': 'broadcast',
110 '4': '802.3ad',
111 '5': 'balance-tlb',
112 '6': 'balance-alb'}
113
114 _bond_mode_string = {'balance-rr': '0',
115 'active-backup': '1',
116 'balance-xor': '2',
117 'broadcast': '3',
118 '802.3ad': '4',
119 'balance-tlb': '5',
120 'balance-alb': '6'}
121
122 @staticmethod
123 def _get_readable_bond_mode(mode):
124 if mode in bond._bond_mode_num:
125 return bond._bond_mode_num[mode]
126 return mode
127
128 @staticmethod
129 def _get_num_bond_mode(mode):
130 if mode in bond._bond_mode_string:
131 return bond._bond_mode_string[mode]
132 return mode
133
15ef32ea
RP
134 def __init__(self, *args, **kargs):
135 ifupdownaddons.modulebase.moduleBase.__init__(self, *args, **kargs)
136 self.ipcmd = None
6cb589df 137 self.bondcmd = None
15ef32ea
RP
138
139 def _is_bond(self, ifaceobj):
140 if ifaceobj.get_attr_value_first('bond-slaves'):
141 return True
142 return False
143
144 def get_dependent_ifacenames(self, ifaceobj, ifacenames_all=None):
145 """ Returns list of interfaces dependent on ifaceobj """
146
147 if not self._is_bond(ifaceobj):
148 return None
0c8332bc
RP
149 slave_list = self.parse_port_list(ifaceobj.name,
150 ifaceobj.get_attr_value_first(
15ef32ea 151 'bond-slaves'), ifacenames_all)
45ca0b6d 152 ifaceobj.dependency_type = ifaceDependencyType.MASTER_SLAVE
15ef32ea
RP
153 # Also save a copy for future use
154 ifaceobj.priv_data = list(slave_list)
fa6a36a9 155 if ifaceobj.link_type != ifaceLinkType.LINK_NA:
a070c90e 156 ifaceobj.link_type = ifaceLinkType.LINK_MASTER
0a3bee28
ST
157 ifaceobj.link_kind |= ifaceLinkKind.BOND
158 ifaceobj.role |= ifaceRole.MASTER
159
15ef32ea
RP
160 return slave_list
161
162 def get_dependent_ifacenames_running(self, ifaceobj):
163 self._init_command_handlers()
6cb589df 164 return self.bondcmd.get_slaves(ifaceobj.name)
15ef32ea
RP
165
166 def _get_slave_list(self, ifaceobj):
167 """ Returns slave list present in ifaceobj config """
168
169 # If priv data already has slave list use that first.
170 if ifaceobj.priv_data:
171 return ifaceobj.priv_data
172 slaves = ifaceobj.get_attr_value_first('bond-slaves')
173 if slaves:
0c8332bc 174 return self.parse_port_list(ifaceobj.name, slaves)
15ef32ea
RP
175 else:
176 return None
177
1c89fd85
AK
178 def _is_clag_bond(self, ifaceobj):
179 if ifaceobj.get_attr_value_first('bond-slaves'):
180 attrval = ifaceobj.get_attr_value_first('clag-id')
181 if attrval and attrval != '0':
182 return True
183 return False
184
15ef32ea
RP
185 def fetch_attr(self, ifaceobj, attrname):
186 attrval = ifaceobj.get_attr_value_first(attrname)
6cb589df
ST
187 # grab the defaults from the policy file in case the
188 # user did not specify something.
189 policy_default_val = policymanager.policymanager_api.\
190 get_iface_default(module_name=self.__class__.__name__,
191 ifname=ifaceobj.name,
192 attr=attrname)
15ef32ea 193 if attrval:
1a13da93
JF
194 if attrname == 'bond-mode':
195 attrval = bond._get_readable_bond_mode(attrval)
196 if attrval == '802.3ad':
197 dattrname = 'bond-min-links'
198 min_links = ifaceobj.get_attr_value_first(dattrname)
199 if not min_links:
200 min_links = self.bondcmd.get_min_links(ifaceobj.name)
201 if min_links == '0':
202 self.logger.warn('%s: attribute %s'
203 %(ifaceobj.name, dattrname) +
204 ' is set to \'0\'')
6cb589df
ST
205 elif policy_default_val:
206 return policy_default_val
15ef32ea
RP
207 return attrval
208
209 def _apply_master_settings(self, ifaceobj):
210 have_attrs_to_set = 0
c416da6a 211 linkup = False
6cb589df 212 bondcmd_attrmap = OrderedDict([('bond-mode' , 'mode'),
15ef32ea
RP
213 ('bond-miimon' , 'miimon'),
214 ('bond-use-carrier', 'use_carrier'),
215 ('bond-lacp-rate' , 'lacp_rate'),
216 ('bond-xmit-hash-policy' , 'xmit_hash_policy'),
217 ('bond-min-links' , 'min_links'),
218 ('bond-num-grat-arp' , 'num_grat_arp'),
219 ('bond-num-unsol-na' , 'num_unsol_na'),
1553a881
RP
220 ('bond-ad-sys-mac-addr' , 'ad_actor_system'),
221 ('bond-ad-actor-system' , 'ad_actor_system'),
222 ('bond-ad-sys-priority' , 'ad_actor_sys_prio'),
223 ('bond-ad-actor-sys-prio' , 'ad_actor_sys_prio'),
7b2469db 224 ('bond-lacp-bypass-allow', 'lacp_bypass')])
c416da6a 225 linkup = self.ipcmd.is_link_up(ifaceobj.name)
15ef32ea
RP
226 try:
227 # order of attributes set matters for bond, so
228 # construct the list sequentially
229 attrstoset = OrderedDict()
6cb589df 230 for k, dstk in bondcmd_attrmap.items():
15ef32ea
RP
231 v = self.fetch_attr(ifaceobj, k)
232 if v:
233 attrstoset[dstk] = v
234 if not attrstoset:
235 return
594fb088
JF
236
237 # support yes/no attrs
238 utils.support_yesno_attrs(attrstoset, ['use_carrier', 'lacp_bypass'])
239
15ef32ea 240 have_attrs_to_set = 1
6cb589df 241 self.bondcmd.set_attrs(ifaceobj.name, attrstoset,
c416da6a 242 self.ipcmd.link_down if linkup else None)
15ef32ea
RP
243 except:
244 raise
245 finally:
c416da6a 246 if have_attrs_to_set and linkup:
15ef32ea
RP
247 self.ipcmd.link_up(ifaceobj.name)
248
249 def _add_slaves(self, ifaceobj):
250 runningslaves = []
251
252 slaves = self._get_slave_list(ifaceobj)
253 if not slaves:
254 self.logger.debug('%s: no slaves found' %ifaceobj.name)
255 return
256
fc5e1735 257 if not ifupdownflags.flags.PERFMODE:
6cb589df 258 runningslaves = self.bondcmd.get_slaves(ifaceobj.name);
15ef32ea 259
1c89fd85
AK
260 clag_bond = self._is_clag_bond(ifaceobj)
261
15ef32ea 262 for slave in Set(slaves).difference(Set(runningslaves)):
fc5e1735
RP
263 if (not ifupdownflags.flags.PERFMODE and
264 not self.ipcmd.link_exists(slave)):
bf3eda91
RP
265 self.log_error('%s: skipping slave %s, does not exist'
266 %(ifaceobj.name, slave), ifaceobj,
267 raise_error=False)
5828d8c5 268 continue
a070c90e 269 link_up = False
c416da6a 270 if self.ipcmd.is_link_up(slave):
2864d6f3
JF
271 netlink.link_set_updown(slave, "down")
272 link_up = True
1c89fd85
AK
273 # If clag bond place the slave in a protodown state; clagd
274 # will protoup it when it is ready
275 if clag_bond:
4ce08bc7 276 try:
2864d6f3 277 netlink.link_set_protodown(slave, "on")
4ce08bc7 278 except Exception, e:
2864d6f3 279 self.logger.error('%s: %s' % (ifaceobj.name, str(e)))
b48ff1a9 280 self.ipcmd.link_set(slave, 'master', ifaceobj.name)
fa6a36a9 281 if link_up or ifaceobj.link_type != ifaceLinkType.LINK_NA:
f28e72e5 282 try:
2864d6f3 283 netlink.link_set_updown(slave, "up")
f28e72e5 284 except Exception, e:
2864d6f3 285 self.logger.debug('%s: %s' % (ifaceobj.name, str(e)))
f28e72e5 286 pass
15ef32ea 287
bb562af1 288 if runningslaves:
1c89fd85
AK
289 for s in runningslaves:
290 if s not in slaves:
291 self.bondcmd.remove_slave(ifaceobj.name, s)
292 if clag_bond:
4ce08bc7 293 try:
2864d6f3 294 netlink.link_set_protodown(s, "off")
4ce08bc7 295 except Exception, e:
2864d6f3 296 self.logger.error('%s: %s' % (ifaceobj.name, str(e)))
4ce08bc7 297
15ef32ea
RP
298 def _up(self, ifaceobj):
299 try:
300 if not self.ipcmd.link_exists(ifaceobj.name):
6cb589df 301 self.bondcmd.create_bond(ifaceobj.name)
15ef32ea
RP
302 self._apply_master_settings(ifaceobj)
303 self._add_slaves(ifaceobj)
e8b4b06d 304 if ifaceobj.addr_method == 'manual':
2864d6f3 305 netlink.link_set_updown(ifaceobj.name, "up")
15ef32ea 306 except Exception, e:
bf3eda91 307 self.log_error(str(e), ifaceobj)
15ef32ea
RP
308
309 def _down(self, ifaceobj):
310 try:
6cb589df 311 self.bondcmd.delete_bond(ifaceobj.name)
15ef32ea
RP
312 except Exception, e:
313 self.log_warn(str(e))
314
315 def _query_check(self, ifaceobj, ifaceobjcurr):
316 slaves = None
317
6cb589df 318 if not self.bondcmd.bond_exists(ifaceobj.name):
15ef32ea
RP
319 self.logger.debug('bond iface %s' %ifaceobj.name +
320 ' does not exist')
15ef32ea
RP
321 return
322
323 ifaceattrs = self.dict_key_subset(ifaceobj.config,
324 self.get_mod_attrs())
325 if not ifaceattrs: return
326 runningattrs = self._query_running_attrs(ifaceobj.name)
1be3f95b 327
594fb088
JF
328 # support yes/no attributes
329 utils.support_yesno_attrs(runningattrs, ['bond-use-carrier',
330 'bond-lacp-bypass-allow'],
331 ifaceobj=ifaceobj)
332
1a13da93
JF
333 # support for numerical bond-mode
334 mode = ifaceobj.get_attr_value_first('bond-mode')
335 if mode in bond._bond_mode_num:
336 if 'bond-mode' in runningattrs:
337 runningattrs['bond-mode'] = bond._get_num_bond_mode(runningattrs['bond-mode'])
338
15ef32ea
RP
339 for k in ifaceattrs:
340 v = ifaceobj.get_attr_value_first(k)
341 if not v:
342 continue
343 if k == 'bond-slaves':
e1601369 344 slaves = self._get_slave_list(ifaceobj)
15ef32ea
RP
345 continue
346 rv = runningattrs.get(k)
347 if not rv:
348 ifaceobjcurr.update_config_with_status(k, 'None', 1)
349 else:
15ef32ea
RP
350 ifaceobjcurr.update_config_with_status(k, rv,
351 1 if v != rv else 0)
352 runningslaves = runningattrs.get('bond-slaves')
353 if not slaves and not runningslaves:
354 return
355 retslave = 1
356 if slaves and runningslaves:
357 if slaves and runningslaves:
358 difference = set(slaves).symmetric_difference(runningslaves)
359 if not difference:
360 retslave = 0
361 ifaceobjcurr.update_config_with_status('bond-slaves',
362 ' '.join(runningslaves)
363 if runningslaves else 'None', retslave)
364
365 def _query_running_attrs(self, bondname):
366 bondattrs = {'bond-mode' :
6cb589df 367 self.bondcmd.get_mode(bondname),
15ef32ea 368 'bond-miimon' :
6cb589df 369 self.bondcmd.get_miimon(bondname),
15ef32ea 370 'bond-use-carrier' :
6cb589df 371 self.bondcmd.get_use_carrier(bondname),
15ef32ea 372 'bond-lacp-rate' :
6cb589df 373 self.bondcmd.get_lacp_rate(bondname),
15ef32ea 374 'bond-min-links' :
6cb589df 375 self.bondcmd.get_min_links(bondname),
1553a881
RP
376 'bond-ad-actor-system' :
377 self.bondcmd.get_ad_actor_system(bondname),
378 'bond-ad-actor-sys-prio' :
379 self.bondcmd.get_ad_actor_sys_prio(bondname),
15ef32ea 380 'bond-xmit-hash-policy' :
6cb589df 381 self.bondcmd.get_xmit_hash_policy(bondname),
1be3f95b 382 'bond-lacp-bypass-allow' :
1553a881
RP
383 self.bondcmd.get_lacp_bypass_allow(bondname),
384 'bond-num-unsol-na' :
385 self.bondcmd.get_num_unsol_na(bondname),
386 'bond-num-grat-arp' :
387 self.bondcmd.get_num_grat_arp(bondname)}
6cb589df 388 slaves = self.bondcmd.get_slaves(bondname)
15ef32ea
RP
389 if slaves:
390 bondattrs['bond-slaves'] = slaves
391 return bondattrs
392
393 def _query_running(self, ifaceobjrunning):
6cb589df 394 if not self.bondcmd.bond_exists(ifaceobjrunning.name):
15ef32ea
RP
395 return
396 bondattrs = self._query_running_attrs(ifaceobjrunning.name)
397 if bondattrs.get('bond-slaves'):
398 bondattrs['bond-slaves'] = ' '.join(bondattrs.get('bond-slaves'))
399 [ifaceobjrunning.update_config(k, v)
400 for k, v in bondattrs.items()
401 if v and v != self.get_mod_subattr(k, 'default')]
402
403 _run_ops = {'pre-up' : _up,
404 'post-down' : _down,
405 'query-running' : _query_running,
406 'query-checkcurr' : _query_check}
407
408 def get_ops(self):
409 """ returns list of ops supported by this module """
410 return self._run_ops.keys()
411
412 def _init_command_handlers(self):
15ef32ea 413 if not self.ipcmd:
fc5e1735 414 self.ipcmd = iproute2()
6cb589df 415 if not self.bondcmd:
fc5e1735 416 self.bondcmd = bondutil()
15ef32ea 417
84ca006f 418 def run(self, ifaceobj, operation, query_ifaceobj=None, **extra_args):
15ef32ea
RP
419 """ run bond configuration on the interface object passed as argument
420
421 Args:
422 **ifaceobj** (object): iface object
423
424 **operation** (str): any of 'pre-up', 'post-down', 'query-checkcurr',
425 'query-running'
426
427 Kwargs:
428 **query_ifaceobj** (object): query check ifaceobject. This is only
429 valid when op is 'query-checkcurr'. It is an object same as
430 ifaceobj, but contains running attribute values and its config
431 status. The modules can use it to return queried running state
432 of interfaces. status is success if the running state is same
433 as user required state in ifaceobj. error otherwise.
434 """
435 op_handler = self._run_ops.get(operation)
436 if not op_handler:
437 return
438 if operation != 'query-running' and not self._is_bond(ifaceobj):
439 return
440 self._init_command_handlers()
441 if operation == 'query-checkcurr':
442 op_handler(self, ifaceobj, query_ifaceobj)
443 else:
444 op_handler(self, ifaceobj)