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',
47 'example' : ['link-speed 1000'],
48 'default' : 'varies by platform and port'},
50 {'help': 'set link duplex',
51 'example' : ['link-duplex full'],
52 'validvals' : ['half', 'full'],
55 {'help': 'set autonegotiation',
56 'example' : ['link-autoneg on'],
57 'validvals' : ['yes', 'no', 'on', 'off'],
58 'default' : 'varies by platform and port'},
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'}}}
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
)
70 # keep a list of iface objects who have modified link attributes
71 self
.ifaceobjs_modified_configs
= []
73 def do_fec_settings(self
, ifaceobj
):
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',
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
88 # check running values
89 running_val
= self
.get_running_attr('fec', ifaceobj
)
90 if config_val
and config_val
== running_val
:
93 if not config_val
and default_val
and default_val
== running_val
:
94 # nothing configured but the default is running
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
)
107 feccmd
= ('%s --set-fec %s %s' %
108 (utils
.ethtool_cmd
, ifaceobj
.name
, feccmd
))
109 utils
.exec_command(feccmd
)
111 self
.log_error('%s: %s' %(ifaceobj
.name
, str(e
)), ifaceobj
)
115 def do_speed_settings(self
, ifaceobj
, operation
='post_up'):
118 autoneg_to_configure
= None
119 speed_to_configure
= None
120 duplex_to_configure
= None
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')
126 default_speed
= policymanager
.policymanager_api
.get_iface_default(
127 module_name
='ethtool',
128 ifname
=ifaceobj
.name
,
132 default_duplex
= policymanager
.policymanager_api
.get_iface_default(
133 module_name
='ethtool',
134 ifname
=ifaceobj
.name
,
138 default_autoneg
= policymanager
.policymanager_api
.get_iface_default(
139 module_name
='ethtool',
140 ifname
=ifaceobj
.name
,
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
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
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'
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
167 autoneg_to_configure
= None
168 speed_to_configure
= default_speed
169 duplex_to_configure
= default_duplex
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
180 if speed_to_configure
:
181 # check running values
182 if utils
.get_boolean_from_string(self
.get_running_attr('autoneg', ifaceobj
) or 'off'):
184 # if we are transitioning from autoneg 'on' to 'off'
185 # don't check running speed
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
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
202 cmd
= ('%s -s %s %s' % (utils
.ethtool_cmd
, ifaceobj
.name
, cmd
))
203 utils
.exec_command(cmd
)
205 self
.log_error('%s: %s' %(ifaceobj
.name
, str(e
)), ifaceobj
, raise_error
=False)
207 def _pre_up(self
, ifaceobj
, operation
='post_up'):
209 _pre_up and _pre_down will reset the layer 2 attributes to default policy
212 if not self
.ipcmd
.link_exists(ifaceobj
.name
):
215 self
.do_speed_settings(ifaceobj
)
216 self
.do_fec_settings(ifaceobj
)
218 def _pre_down(self
, ifaceobj
):
219 pass #self._post_up(ifaceobj,operation="_pre_down")
221 def _query_check(self
, ifaceobj
, ifaceobjcurr
):
223 _query_check() needs to compare the configured (or running)
224 attribute with the running attribute.
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).
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
235 if not ifupdownflags
.flags
.WITHDEFAULTS
:
237 default
= policymanager
.policymanager_api
.get_iface_default(
238 module_name
='ethtool',
239 ifname
=ifaceobj
.name
,
241 # if we have no default, do not bother checking
242 # this avoids ethtool calls on virtual interfaces
245 # autoneg comes from ethtool whereas speed and duplex from /sys/class
246 running_attr
= self
.get_running_attr(attr
, ifaceobj
)
250 ifaceobjcurr
.update_config_with_status('link-%s' % attr
,
254 if attr
== 'autoneg':
255 if configured
== 'yes' and running_attr
== 'on':
257 elif configured
== 'no' and running_attr
== 'off':
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
,
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
,
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
,
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
,
279 def get_autoneg(self
,ethtool_output
=None):
281 get_autoneg simply calls the ethtool command and parses out
284 ethtool_attrs
= ethtool_output
.split()
285 if ('Auto-negotiation:' in ethtool_attrs
):
286 return(ethtool_attrs
[ethtool_attrs
.index('Auto-negotiation:')+1])
290 def get_fec_encoding(self
,ethtool_output
=None):
292 get_fec_encoding simply calls the ethtool show-fec command and parses out
293 the fec encoding value.
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
)))
306 def get_running_attr(self
,attr
='',ifaceobj
=None):
307 if not ifaceobj
or not attr
:
311 if attr
== 'autoneg':
312 output
= utils
.exec_commandl([utils
.ethtool_cmd
, ifaceobj
.name
])
313 running_attr
= self
.get_autoneg(ethtool_output
=output
)
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
)
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
)))
329 def _query_running(self
, ifaceobj
, ifaceobj_getfunc
=None):
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.
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
):
340 for attr
in ['speed', 'duplex', 'autoneg']:
341 default_val
= policymanager
.policymanager_api
.get_iface_default(
342 module_name
='ethtool',
343 ifname
=ifaceobj
.name
,
345 # do not continue if we have no defaults
346 # this avoids ethtool calls on virtual interfaces
349 running_attr
= self
.get_running_attr(attr
, ifaceobj
)
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
:
356 # do not proceed if speed = 0
357 if attr
== 'speed' and running_attr
and running_attr
== '0':
360 ifaceobj
.update_config('link-%s'%attr
, running_attr
)
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
):
369 default
= policymanager
.policymanager_api
.get_iface_default(
370 module_name
='ethtool',
371 ifname
=ifaceobj
.name
,
372 attr
='link-%s' %attr
)
375 ifaceobj
.update_config('link-%s' %attr
, default
)
377 _run_ops
= {'pre-down' : _pre_down
,
379 'query-checkcurr' : _query_check
,
380 'query-running' : _query_running
,
384 """ returns list of ops supported by this module """
385 return self
._run
_ops
.keys()
387 def _init_command_handlers(self
):
389 self
.ipcmd
= LinkUtils()
391 def run(self
, ifaceobj
, operation
, query_ifaceobj
=None, **extra_args
):
392 """ run ethtool configuration on the interface object passed as
396 **ifaceobj** (object): iface object
398 **operation** (str): any of 'post-up', 'query-checkcurr',
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.
408 if (ifaceobj
.link_kind
or
409 ifaceobj
.link_privflags
& ifaceLinkPrivFlags
.LOOPBACK
):
411 op_handler
= self
._run
_ops
.get(operation
)
414 self
._init
_command
_handlers
()
415 if operation
== 'query-checkcurr':
416 op_handler(self
, ifaceobj
, query_ifaceobj
)
418 op_handler(self
, ifaceobj
)