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