]>
Commit | Line | Data |
---|---|---|
15ef32ea RP |
1 | #!/usr/bin/python |
2 | # | |
d486dd0d | 3 | # Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved. |
15ef32ea RP |
4 | # Author: Roopa Prabhu, roopa@cumulusnetworks.com |
5 | # | |
d486dd0d JF |
6 | |
7 | import os | |
15ef32ea RP |
8 | |
9 | try: | |
d486dd0d JF |
10 | import ifupdown2.ifupdown.ifupdownflags as ifupdownflags |
11 | import ifupdown2.ifupdown.policymanager as policymanager | |
12 | ||
13 | from ifupdown2.ifupdown.iface import * | |
14 | from ifupdown2.ifupdown.utils import utils | |
15 | from ifupdown2.ifupdown.exceptions import moduleNotSupported | |
16 | ||
17 | from ifupdown2.ifupdownaddons.utilsbase import * | |
18 | from ifupdown2.ifupdownaddons.LinkUtils import LinkUtils | |
19 | from ifupdown2.ifupdownaddons.modulebase import moduleBase | |
20 | except ImportError: | |
21 | import ifupdown.ifupdownflags as ifupdownflags | |
22 | import ifupdown.policymanager as policymanager | |
23 | ||
15ef32ea | 24 | from ifupdown.iface import * |
a193d8d1 | 25 | from ifupdown.utils import utils |
d486dd0d JF |
26 | from ifupdown.exceptions import moduleNotSupported |
27 | ||
3d44fbd0 | 28 | from ifupdownaddons.utilsbase import * |
d486dd0d JF |
29 | from ifupdownaddons.LinkUtils import LinkUtils |
30 | from ifupdownaddons.modulebase import moduleBase | |
31 | ||
15ef32ea | 32 | |
3d44fbd0 | 33 | class ethtool(moduleBase,utilsBase): |
15ef32ea RP |
34 | """ ifupdown2 addon module to configure ethtool attributes """ |
35 | ||
36 | _modinfo = {'mhelp' : 'ethtool configuration module for interfaces', | |
37 | 'attrs': { | |
38 | 'link-speed' : | |
39 | {'help' : 'set link speed', | |
a220d2d1 JF |
40 | 'validvals' : ['10', |
41 | '100', | |
a4cf8448 JF |
42 | '1000', |
43 | '10000', | |
44 | '25000', | |
45 | '40000', | |
46 | '50000', | |
47 | '100000'], | |
3d44fbd0 ST |
48 | 'example' : ['link-speed 1000'], |
49 | 'default' : 'varies by platform and port'}, | |
15ef32ea RP |
50 | 'link-duplex' : |
51 | {'help': 'set link duplex', | |
52 | 'example' : ['link-duplex full'], | |
53 | 'validvals' : ['half', 'full'], | |
3d44fbd0 | 54 | 'default' : 'full'}, |
15ef32ea RP |
55 | 'link-autoneg' : |
56 | {'help': 'set autonegotiation', | |
57 | 'example' : ['link-autoneg on'], | |
594fb088 | 58 | 'validvals' : ['yes', 'no', 'on', 'off'], |
d8b6aad0 VSR |
59 | 'default' : 'varies by platform and port'}, |
60 | 'link-fec' : | |
61 | {'help': 'set forward error correction mode', | |
62 | 'example' : ['link-fec rs'], | |
d486dd0d | 63 | 'validvals' : ['rs', 'baser', 'auto', 'off'], |
3d44fbd0 | 64 | 'default' : 'varies by platform and port'}}} |
15ef32ea RP |
65 | |
66 | def __init__(self, *args, **kargs): | |
67 | moduleBase.__init__(self, *args, **kargs) | |
d486dd0d JF |
68 | if not os.path.exists(utils.ethtool_cmd): |
69 | raise moduleNotSupported('module init failed: %s: not found' % utils.ethtool_cmd) | |
15ef32ea | 70 | self.ipcmd = None |
7444feea ST |
71 | # keep a list of iface objects who have modified link attributes |
72 | self.ifaceobjs_modified_configs = [] | |
15ef32ea | 73 | |
d486dd0d | 74 | def do_fec_settings(self, ifaceobj): |
d8b6aad0 | 75 | feccmd = '' |
3d44fbd0 | 76 | |
d486dd0d JF |
77 | # attribute existed before but we must reset to default |
78 | config_val = ifaceobj.get_attr_value_first('link-fec') | |
79 | default_val = policymanager.policymanager_api.get_iface_default( | |
80 | module_name='ethtool', | |
81 | ifname=ifaceobj.name, | |
82 | attr='link-fec') | |
594fb088 | 83 | |
d486dd0d JF |
84 | if not default_val and not config_val: |
85 | # there is no point in checking the running config | |
86 | # if we have no default and the user did not have settings | |
87 | return | |
7444feea | 88 | |
d486dd0d JF |
89 | # check running values |
90 | running_val = self.get_running_attr('fec', ifaceobj) | |
91 | if config_val and config_val == running_val: | |
92 | return | |
7444feea | 93 | |
d486dd0d JF |
94 | if not config_val and default_val and default_val == running_val: |
95 | # nothing configured but the default is running | |
96 | return | |
7444feea | 97 | |
d486dd0d JF |
98 | # if we got this far, we need to change it |
99 | if config_val and (config_val != running_val): | |
100 | # if the configured value is not set, set it | |
101 | feccmd = ' %s %s' % ("encoding", config_val) | |
102 | elif default_val and (default_val != running_val): | |
103 | # or if it has a default not equal to running value, set it | |
104 | feccmd = ' %s %s' % ("encoding", default_val) | |
d8b6aad0 | 105 | |
d486dd0d | 106 | if feccmd: |
15ef32ea | 107 | try: |
d486dd0d JF |
108 | feccmd = ('%s --set-fec %s %s' % |
109 | (utils.ethtool_cmd, ifaceobj.name, feccmd)) | |
110 | utils.exec_command(feccmd) | |
15ef32ea | 111 | except Exception, e: |
bf3eda91 | 112 | self.log_error('%s: %s' %(ifaceobj.name, str(e)), ifaceobj) |
3d44fbd0 ST |
113 | else: |
114 | pass | |
115 | ||
d486dd0d JF |
116 | def do_speed_settings(self, ifaceobj, operation='post_up'): |
117 | cmd = '' | |
118 | ||
119 | autoneg_to_configure = None | |
120 | speed_to_configure = None | |
121 | duplex_to_configure = None | |
122 | ||
123 | config_speed = ifaceobj.get_attr_value_first('link-speed') | |
124 | config_duplex = ifaceobj.get_attr_value_first('link-duplex') | |
125 | config_autoneg = ifaceobj.get_attr_value_first('link-autoneg') | |
126 | ||
127 | default_speed = policymanager.policymanager_api.get_iface_default( | |
128 | module_name='ethtool', | |
129 | ifname=ifaceobj.name, | |
130 | attr='link-speed' | |
131 | ) | |
132 | ||
133 | default_duplex = policymanager.policymanager_api.get_iface_default( | |
134 | module_name='ethtool', | |
135 | ifname=ifaceobj.name, | |
136 | attr='link-duplex' | |
137 | ) | |
138 | ||
139 | default_autoneg = policymanager.policymanager_api.get_iface_default( | |
140 | module_name='ethtool', | |
141 | ifname=ifaceobj.name, | |
142 | attr='link-autoneg' | |
143 | ) | |
144 | ||
145 | # autoneg wins if provided by user and is on | |
146 | if config_autoneg and utils.get_boolean_from_string(config_autoneg): | |
147 | autoneg_to_configure = config_autoneg | |
148 | speed_to_configure = None | |
149 | duplex_to_configure = None | |
150 | elif config_speed: | |
151 | # Any speed settings configured by the user wins | |
152 | autoneg_to_configure = None | |
153 | speed_to_configure = config_speed | |
154 | duplex_to_configure = config_duplex | |
155 | if not config_duplex: | |
156 | duplex_to_configure = default_duplex | |
157 | else: | |
158 | # if user given autoneg config is off, we must respect that and | |
159 | # override any default autoneg config | |
160 | if config_autoneg and not utils.get_boolean_from_string(config_autoneg): | |
161 | default_autoneg = 'off' | |
162 | ||
163 | if default_autoneg and utils.get_boolean_from_string(default_autoneg): | |
164 | autoneg_to_configure = utils.get_onoff_bool(default_autoneg) | |
165 | speed_to_configure = None | |
166 | duplex_to_configure = None | |
167 | else: | |
168 | autoneg_to_configure = None | |
169 | speed_to_configure = default_speed | |
170 | duplex_to_configure = default_duplex | |
171 | ||
172 | if autoneg_to_configure: | |
173 | autoneg_to_configure = utils.get_onoff_bool(autoneg_to_configure) | |
174 | # check running values | |
175 | running_val = self.get_running_attr('autoneg', ifaceobj) | |
176 | if autoneg_to_configure != running_val: | |
177 | # if the configured value is not set, set it | |
178 | cmd += ' autoneg %s' % autoneg_to_configure | |
179 | else: | |
180 | force_set = False | |
181 | if speed_to_configure: | |
182 | # check running values | |
183 | if utils.get_boolean_from_string(self.get_running_attr('autoneg', ifaceobj) or 'off'): | |
184 | cmd = 'autoneg off' | |
185 | # if we are transitioning from autoneg 'on' to 'off' | |
186 | # don't check running speed | |
187 | force_set = True | |
188 | ||
189 | running_val = self.get_running_attr('speed', ifaceobj) | |
190 | if force_set or (speed_to_configure != running_val): | |
191 | # if the configured value is not set, set it | |
192 | cmd += ' speed %s' % speed_to_configure | |
193 | ||
194 | if duplex_to_configure: | |
195 | # check running values | |
196 | running_val = self.get_running_attr('duplex', ifaceobj) | |
197 | if force_set or (duplex_to_configure != running_val): | |
198 | # if the configured value is not set, set it | |
199 | cmd += ' duplex %s' % duplex_to_configure | |
200 | ||
201 | if cmd: | |
d8b6aad0 | 202 | try: |
d486dd0d JF |
203 | cmd = ('%s -s %s %s' % (utils.ethtool_cmd, ifaceobj.name, cmd)) |
204 | utils.exec_command(cmd) | |
d8b6aad0 | 205 | except Exception, e: |
d486dd0d JF |
206 | self.log_error('%s: %s' %(ifaceobj.name, str(e)), ifaceobj, raise_error=False) |
207 | ||
208 | def _pre_up(self, ifaceobj, operation='post_up'): | |
209 | """ | |
210 | _pre_up and _pre_down will reset the layer 2 attributes to default policy | |
211 | settings. | |
212 | """ | |
213 | if not self.ipcmd.link_exists(ifaceobj.name): | |
214 | return | |
215 | ||
216 | self.do_speed_settings(ifaceobj) | |
217 | self.do_fec_settings(ifaceobj) | |
d8b6aad0 | 218 | |
3d44fbd0 ST |
219 | def _pre_down(self, ifaceobj): |
220 | pass #self._post_up(ifaceobj,operation="_pre_down") | |
15ef32ea | 221 | |
6e16e5ae | 222 | def _query_check(self, ifaceobj, ifaceobjcurr): |
e74d01e1 | 223 | """ |
3d44fbd0 ST |
224 | _query_check() needs to compare the configured (or running) |
225 | attribute with the running attribute. | |
226 | ||
227 | If there is nothing configured, we compare the default attribute with | |
228 | the running attribute and FAIL if they are different. | |
229 | This is because a reboot will lose their running attribute | |
230 | (the default will get set). | |
231 | """ | |
d8b6aad0 | 232 | for attr in ['speed', 'duplex', 'autoneg', 'fec']: |
97202602 ST |
233 | configured = ifaceobj.get_attr_value_first('link-%s'%attr) |
234 | # if there is nothing configured, do not check | |
235 | if not configured: | |
6e16e5ae | 236 | if not ifupdownflags.flags.WITHDEFAULTS: |
669b422a | 237 | continue |
3d44fbd0 ST |
238 | default = policymanager.policymanager_api.get_iface_default( |
239 | module_name='ethtool', | |
240 | ifname=ifaceobj.name, | |
241 | attr='link-%s'%attr) | |
8d60ab41 ST |
242 | # if we have no default, do not bother checking |
243 | # this avoids ethtool calls on virtual interfaces | |
244 | if not default: | |
245 | continue | |
246 | # autoneg comes from ethtool whereas speed and duplex from /sys/class | |
247 | running_attr = self.get_running_attr(attr, ifaceobj) | |
b2a87e4e JF |
248 | if not running_attr: |
249 | if not configured: | |
250 | continue | |
251 | ifaceobjcurr.update_config_with_status('link-%s' % attr, | |
252 | 'unknown', 1) | |
3d44fbd0 ST |
253 | continue |
254 | ||
594fb088 JF |
255 | if attr == 'autoneg': |
256 | if configured == 'yes' and running_attr == 'on': | |
257 | running_attr = 'yes' | |
258 | elif configured == 'no' and running_attr == 'off': | |
259 | running_attr = 'no' | |
260 | ||
3d44fbd0 ST |
261 | # we make sure we can get a running value first |
262 | if (running_attr and configured and running_attr == configured): | |
d8b6aad0 | 263 | # PASS since running is what is configured |
3d44fbd0 ST |
264 | ifaceobjcurr.update_config_with_status('link-%s'%attr, |
265 | running_attr, 0) | |
266 | elif (running_attr and configured and running_attr != configured): | |
267 | # We show a FAIL since it is not the configured or default | |
268 | ifaceobjcurr.update_config_with_status('link-%s'%attr, | |
269 | running_attr, 1) | |
270 | elif (running_attr and default and running_attr == default): | |
271 | # PASS since running is default | |
272 | ifaceobjcurr.update_config_with_status('link-%s'%attr, | |
273 | running_attr, 0) | |
274 | elif (default or configured): | |
275 | # We show a FAIL since it is not the configured or default | |
276 | ifaceobjcurr.update_config_with_status('link-%s'%attr, | |
277 | running_attr, 1) | |
278 | return | |
279 | ||
280 | def get_autoneg(self,ethtool_output=None): | |
281 | """ | |
282 | get_autoneg simply calls the ethtool command and parses out | |
283 | the autoneg value. | |
284 | """ | |
285 | ethtool_attrs = ethtool_output.split() | |
286 | if ('Auto-negotiation:' in ethtool_attrs): | |
287 | return(ethtool_attrs[ethtool_attrs.index('Auto-negotiation:')+1]) | |
288 | else: | |
289 | return(None) | |
290 | ||
d8b6aad0 VSR |
291 | def get_fec_encoding(self,ethtool_output=None): |
292 | """ | |
293 | get_fec_encoding simply calls the ethtool show-fec command and parses out | |
294 | the fec encoding value. | |
295 | """ | |
296 | try: | |
297 | for attr in ethtool_output.splitlines(): | |
298 | if attr.startswith('FEC encodings'): | |
299 | fec_attrs = attr.split() | |
6092d545 | 300 | return(fec_attrs[fec_attrs.index(':')+1]) |
d8b6aad0 VSR |
301 | except Exception as e: |
302 | self.logger.debug('ethtool: problems in ethtool set-fec output' | |
303 | ' %s: %s' %(ethtool_output.splitlines(), str(e))) | |
304 | ||
305 | return(None) | |
306 | ||
d2431a3d ST |
307 | def get_running_attr(self,attr='',ifaceobj=None): |
308 | if not ifaceobj or not attr: | |
309 | return | |
310 | running_attr = None | |
311 | try: | |
312 | if attr == 'autoneg': | |
d486dd0d | 313 | output = utils.exec_commandl([utils.ethtool_cmd, ifaceobj.name]) |
d2431a3d | 314 | running_attr = self.get_autoneg(ethtool_output=output) |
d8b6aad0 | 315 | elif attr == 'fec': |
d486dd0d JF |
316 | output = utils.exec_command('%s --show-fec %s'% |
317 | (utils.ethtool_cmd, ifaceobj.name)) | |
d8b6aad0 | 318 | running_attr = self.get_fec_encoding(ethtool_output=output) |
d2431a3d ST |
319 | else: |
320 | running_attr = self.read_file_oneline('/sys/class/net/%s/%s' % \ | |
321 | (ifaceobj.name, attr)) | |
a193d8d1 | 322 | except Exception as e: |
d2431a3d ST |
323 | # for nonexistent interfaces, we get an error (rc = 256 or 19200) |
324 | self.logger.debug('ethtool: problems calling ethtool or reading' | |
a193d8d1 JF |
325 | ' /sys/class on iface %s for attr %s: %s' % |
326 | (ifaceobj.name, attr, str(e))) | |
d2431a3d ST |
327 | return running_attr |
328 | ||
329 | ||
3d44fbd0 ST |
330 | def _query_running(self, ifaceobj, ifaceobj_getfunc=None): |
331 | """ | |
332 | _query_running looks at the speed and duplex from /sys/class | |
333 | and retreives autoneg from ethtool. We do not report autoneg | |
334 | if speed is not available because this usually means the link is | |
335 | down and the autoneg value is not reliable when the link is down. | |
336 | """ | |
337 | # do not bother showing swp ifaces that are not up for the speed | |
338 | # duplex and autoneg are not reliable. | |
339 | if not self.ipcmd.is_link_up(ifaceobj.name): | |
e74d01e1 | 340 | return |
3d44fbd0 | 341 | for attr in ['speed', 'duplex', 'autoneg']: |
8d60ab41 ST |
342 | default_val = policymanager.policymanager_api.get_iface_default( |
343 | module_name='ethtool', | |
344 | ifname=ifaceobj.name, | |
345 | attr='link-%s'%attr) | |
346 | # do not continue if we have no defaults | |
347 | # this avoids ethtool calls on virtual interfaces | |
348 | if not default_val: | |
349 | continue | |
d2431a3d | 350 | running_attr = self.get_running_attr(attr, ifaceobj) |
d8b6aad0 | 351 | |
16e30aab ST |
352 | # Only show the link attributes if they differ from defaults |
353 | # to see the defaults, we should implement another flag (--with-defaults) | |
354 | if default_val == running_attr: | |
355 | continue | |
151d8024 RP |
356 | |
357 | # do not proceed if speed = 0 | |
358 | if attr == 'speed' and running_attr and running_attr == '0': | |
359 | return | |
8d60ab41 | 360 | if running_attr: |
3d44fbd0 | 361 | ifaceobj.update_config('link-%s'%attr, running_attr) |
15ef32ea | 362 | |
15ef32ea RP |
363 | return |
364 | ||
baa909c6 N |
365 | def _query(self, ifaceobj, **kwargs): |
366 | """ add default policy attributes supported by the module """ | |
d8b6aad0 | 367 | for attr in ['speed', 'duplex', 'autoneg', 'fec']: |
baa909c6 N |
368 | if ifaceobj.get_attr_value_first('link-%s'%attr): |
369 | continue | |
370 | default = policymanager.policymanager_api.get_iface_default( | |
371 | module_name='ethtool', | |
372 | ifname=ifaceobj.name, | |
373 | attr='link-%s' %attr) | |
374 | if not default: | |
375 | continue | |
376 | ifaceobj.update_config('link-%s' %attr, default) | |
377 | ||
3d44fbd0 | 378 | _run_ops = {'pre-down' : _pre_down, |
d486dd0d | 379 | 'pre-up' : _pre_up, |
3d44fbd0 | 380 | 'query-checkcurr' : _query_check, |
baa909c6 N |
381 | 'query-running' : _query_running, |
382 | 'query' : _query} | |
15ef32ea RP |
383 | |
384 | def get_ops(self): | |
385 | """ returns list of ops supported by this module """ | |
386 | return self._run_ops.keys() | |
387 | ||
388 | def _init_command_handlers(self): | |
389 | if not self.ipcmd: | |
d486dd0d | 390 | self.ipcmd = LinkUtils() |
15ef32ea | 391 | |
84ca006f | 392 | def run(self, ifaceobj, operation, query_ifaceobj=None, **extra_args): |
15ef32ea RP |
393 | """ run ethtool configuration on the interface object passed as |
394 | argument | |
395 | ||
396 | Args: | |
397 | **ifaceobj** (object): iface object | |
398 | ||
399 | **operation** (str): any of 'post-up', 'query-checkcurr', | |
400 | 'query-running' | |
401 | Kwargs: | |
402 | **query_ifaceobj** (object): query check ifaceobject. This is only | |
403 | valid when op is 'query-checkcurr'. It is an object same as | |
404 | ifaceobj, but contains running attribute values and its config | |
405 | status. The modules can use it to return queried running state | |
406 | of interfaces. status is success if the running state is same | |
407 | as user required state in ifaceobj. error otherwise. | |
408 | """ | |
6ceda69e JF |
409 | if (ifaceobj.link_kind or |
410 | ifaceobj.link_privflags & ifaceLinkPrivFlags.LOOPBACK): | |
d07b5a9f | 411 | return |
15ef32ea RP |
412 | op_handler = self._run_ops.get(operation) |
413 | if not op_handler: | |
414 | return | |
415 | self._init_command_handlers() | |
416 | if operation == 'query-checkcurr': | |
6e16e5ae | 417 | op_handler(self, ifaceobj, query_ifaceobj) |
15ef32ea RP |
418 | else: |
419 | op_handler(self, ifaceobj) |