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