3 # Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved.
4 # Author: Roopa Prabhu, roopa@cumulusnetworks.com
10 import ifupdown2
.ifupdown
.ifupdownflags
as ifupdownflags
11 import ifupdown2
.ifupdown
.policymanager
as policymanager
13 from ifupdown2
.ifupdown
.iface
import *
14 from ifupdown2
.ifupdown
.utils
import utils
15 from ifupdown2
.ifupdown
.exceptions
import moduleNotSupported
17 from ifupdown2
.ifupdownaddons
.utilsbase
import *
18 from ifupdown2
.ifupdownaddons
.LinkUtils
import LinkUtils
19 from ifupdown2
.ifupdownaddons
.modulebase
import moduleBase
21 import ifupdown
.ifupdownflags
as ifupdownflags
22 import ifupdown
.policymanager
as policymanager
24 from ifupdown
.iface
import *
25 from ifupdown
.utils
import utils
26 from ifupdown
.exceptions
import moduleNotSupported
28 from ifupdownaddons
.utilsbase
import *
29 from ifupdownaddons
.LinkUtils
import LinkUtils
30 from ifupdownaddons
.modulebase
import moduleBase
33 class ethtool(moduleBase
,utilsBase
):
34 """ ifupdown2 addon module to configure ethtool attributes """
36 _modinfo
= {'mhelp' : 'ethtool configuration module for interfaces',
39 {'help' : 'set link speed',
48 'example' : ['link-speed 1000'],
49 'default' : 'varies by platform and port'},
51 {'help': 'set link duplex',
52 'example' : ['link-duplex full'],
53 'validvals' : ['half', 'full'],
56 {'help': 'set autonegotiation',
57 'example' : ['link-autoneg on'],
58 'validvals' : ['yes', 'no', 'on', 'off'],
59 'default' : 'varies by platform and port'},
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'}}}
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
)
71 # keep a list of iface objects who have modified link attributes
72 self
.ifaceobjs_modified_configs
= []
74 def do_fec_settings(self
, ifaceobj
):
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',
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
89 # use only lowercase values
90 running_val
= str(self
.get_running_attr('fec', ifaceobj
)).lower()
93 config_val
= config_val
.lower()
95 default_val
= default_val
.lower()
97 if running_val
in ["none", "notsupported"]:
98 # None and NotSupported ethtool FEC values mean "off"
101 # check running values
102 if config_val
and config_val
== running_val
:
105 if not config_val
and default_val
and default_val
== running_val
:
106 # nothing configured but the default is running
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
)
119 feccmd
= ('%s --set-fec %s %s' %
120 (utils
.ethtool_cmd
, ifaceobj
.name
, feccmd
))
121 utils
.exec_command(feccmd
)
123 self
.log_error('%s: %s' %(ifaceobj
.name
, str(e
)), ifaceobj
)
127 def do_speed_settings(self
, ifaceobj
, operation
='post_up'):
130 autoneg_to_configure
= None
131 speed_to_configure
= None
132 duplex_to_configure
= None
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')
138 default_speed
= policymanager
.policymanager_api
.get_iface_default(
139 module_name
='ethtool',
140 ifname
=ifaceobj
.name
,
144 default_duplex
= policymanager
.policymanager_api
.get_iface_default(
145 module_name
='ethtool',
146 ifname
=ifaceobj
.name
,
150 default_autoneg
= policymanager
.policymanager_api
.get_iface_default(
151 module_name
='ethtool',
152 ifname
=ifaceobj
.name
,
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
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
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'
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
179 autoneg_to_configure
= None
180 speed_to_configure
= default_speed
181 duplex_to_configure
= default_duplex
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
192 if speed_to_configure
:
193 # check running values
194 if utils
.get_boolean_from_string(self
.get_running_attr('autoneg', ifaceobj
) or 'off'):
196 # if we are transitioning from autoneg 'on' to 'off'
197 # don't check running speed
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
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
214 cmd
= ('%s -s %s %s' % (utils
.ethtool_cmd
, ifaceobj
.name
, cmd
))
215 utils
.exec_command(cmd
)
217 self
.log_error('%s: %s' %(ifaceobj
.name
, str(e
)), ifaceobj
, raise_error
=False)
219 def _pre_up(self
, ifaceobj
, operation
='post_up'):
221 _pre_up and _pre_down will reset the layer 2 attributes to default policy
224 if not self
.ipcmd
.link_exists(ifaceobj
.name
):
227 self
.do_speed_settings(ifaceobj
)
228 self
.do_fec_settings(ifaceobj
)
230 def _pre_down(self
, ifaceobj
):
231 pass #self._post_up(ifaceobj,operation="_pre_down")
233 def _query_check(self
, ifaceobj
, ifaceobjcurr
):
235 _query_check() needs to compare the configured (or running)
236 attribute with the running attribute.
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).
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
247 if not ifupdownflags
.flags
.WITHDEFAULTS
:
249 default
= policymanager
.policymanager_api
.get_iface_default(
250 module_name
='ethtool',
251 ifname
=ifaceobj
.name
,
253 # if we have no default, do not bother checking
254 # this avoids ethtool calls on virtual interfaces
257 # autoneg comes from ethtool whereas speed and duplex from /sys/class
258 running_attr
= self
.get_running_attr(attr
, ifaceobj
)
262 ifaceobjcurr
.update_config_with_status('link-%s' % attr
,
266 if attr
== 'autoneg':
267 if configured
== 'yes' and running_attr
== 'on':
269 elif configured
== 'no' and running_attr
== 'off':
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
,
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
,
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
,
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
,
291 def get_autoneg(self
,ethtool_output
=None):
293 get_autoneg simply calls the ethtool command and parses out
296 ethtool_attrs
= ethtool_output
.split()
297 if ('Auto-negotiation:' in ethtool_attrs
):
298 return(ethtool_attrs
[ethtool_attrs
.index('Auto-negotiation:')+1])
302 def get_fec_encoding(self
,ethtool_output
=None):
304 get_fec_encoding simply calls the ethtool show-fec command and parses out
305 the fec encoding value.
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
)))
318 def get_running_attr(self
,attr
='',ifaceobj
=None):
319 if not ifaceobj
or not attr
:
323 if attr
== 'autoneg':
324 output
= utils
.exec_commandl([utils
.ethtool_cmd
, ifaceobj
.name
])
325 running_attr
= self
.get_autoneg(ethtool_output
=output
)
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
)
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
)))
341 def _query_running(self
, ifaceobj
, ifaceobj_getfunc
=None):
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.
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
):
352 for attr
in ['speed', 'duplex', 'autoneg']:
353 default_val
= policymanager
.policymanager_api
.get_iface_default(
354 module_name
='ethtool',
355 ifname
=ifaceobj
.name
,
357 # do not continue if we have no defaults
358 # this avoids ethtool calls on virtual interfaces
361 running_attr
= self
.get_running_attr(attr
, ifaceobj
)
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
:
368 # do not proceed if speed = 0
369 if attr
== 'speed' and running_attr
and running_attr
== '0':
372 ifaceobj
.update_config('link-%s'%attr
, running_attr
)
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
):
381 default
= policymanager
.policymanager_api
.get_iface_default(
382 module_name
='ethtool',
383 ifname
=ifaceobj
.name
,
384 attr
='link-%s' %attr
)
387 ifaceobj
.update_config('link-%s' %attr
, default
)
389 _run_ops
= {'pre-down' : _pre_down
,
391 'query-checkcurr' : _query_check
,
392 'query-running' : _query_running
,
396 """ returns list of ops supported by this module """
397 return self
._run
_ops
.keys()
399 def _init_command_handlers(self
):
401 self
.ipcmd
= LinkUtils()
403 def run(self
, ifaceobj
, operation
, query_ifaceobj
=None, **extra_args
):
404 """ run ethtool configuration on the interface object passed as
408 **ifaceobj** (object): iface object
410 **operation** (str): any of 'post-up', 'query-checkcurr',
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.
420 if (ifaceobj
.link_kind
or
421 ifaceobj
.link_privflags
& ifaceLinkPrivFlags
.LOOPBACK
):
423 op_handler
= self
._run
_ops
.get(operation
)
426 self
._init
_command
_handlers
()
427 if operation
== 'query-checkcurr':
428 op_handler(self
, ifaceobj
, query_ifaceobj
)
430 op_handler(self
, ifaceobj
)