]>
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 | from sets import Set | |
8 | from ifupdown.iface import * | |
9 | import ifupdownaddons | |
10 | from ifupdownaddons.modulebase import moduleBase | |
6cb589df | 11 | from ifupdownaddons.bondutil import bondutil |
15ef32ea | 12 | from ifupdownaddons.iproute2 import iproute2 |
2864d6f3 | 13 | from ifupdown.netlink import netlink |
6cb589df | 14 | import ifupdown.policymanager as policymanager |
fc5e1735 | 15 | import ifupdown.ifupdownflags as ifupdownflags |
594fb088 | 16 | from ifupdown.utils import utils |
15ef32ea | 17 | |
6cb589df | 18 | class 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) |