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 # check running values
90 running_val
= self
.get_running_attr('fec', ifaceobj
)
91 if config_val
and config_val
== running_val
:
94 if not config_val
and default_val
and default_val
== running_val
:
95 # nothing configured but the default is running
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
)
108 feccmd
= ('%s --set-fec %s %s' %
109 (utils
.ethtool_cmd
, ifaceobj
.name
, feccmd
))
110 utils
.exec_command(feccmd
)
112 self
.log_error('%s: %s' %(ifaceobj
.name
, str(e
)), ifaceobj
)
116 def do_speed_settings(self
, ifaceobj
, operation
='post_up'):
119 autoneg_to_configure
= None
120 speed_to_configure
= None
121 duplex_to_configure
= None
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')
127 default_speed
= policymanager
.policymanager_api
.get_iface_default(
128 module_name
='ethtool',
129 ifname
=ifaceobj
.name
,
133 default_duplex
= policymanager
.policymanager_api
.get_iface_default(
134 module_name
='ethtool',
135 ifname
=ifaceobj
.name
,
139 default_autoneg
= policymanager
.policymanager_api
.get_iface_default(
140 module_name
='ethtool',
141 ifname
=ifaceobj
.name
,
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
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
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'
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
168 autoneg_to_configure
= None
169 speed_to_configure
= default_speed
170 duplex_to_configure
= default_duplex
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
181 if speed_to_configure
:
182 # check running values
183 if utils
.get_boolean_from_string(self
.get_running_attr('autoneg', ifaceobj
) or 'off'):
185 # if we are transitioning from autoneg 'on' to 'off'
186 # don't check running speed
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
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
203 cmd
= ('%s -s %s %s' % (utils
.ethtool_cmd
, ifaceobj
.name
, cmd
))
204 utils
.exec_command(cmd
)
206 self
.log_error('%s: %s' %(ifaceobj
.name
, str(e
)), ifaceobj
, raise_error
=False)
208 def _pre_up(self
, ifaceobj
, operation
='post_up'):
210 _pre_up and _pre_down will reset the layer 2 attributes to default policy
213 if not self
.ipcmd
.link_exists(ifaceobj
.name
):
216 self
.do_speed_settings(ifaceobj
)
217 self
.do_fec_settings(ifaceobj
)
219 def _pre_down(self
, ifaceobj
):
220 pass #self._post_up(ifaceobj,operation="_pre_down")
222 def _query_check(self
, ifaceobj
, ifaceobjcurr
):
224 _query_check() needs to compare the configured (or running)
225 attribute with the running attribute.
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).
232 for attr
in ['speed', 'duplex', 'autoneg', 'fec']:
233 configured
= ifaceobj
.get_attr_value_first('link-%s'%attr
)
234 # if there is nothing configured, do not check
236 if not ifupdownflags
.flags
.WITHDEFAULTS
:
238 default
= policymanager
.policymanager_api
.get_iface_default(
239 module_name
='ethtool',
240 ifname
=ifaceobj
.name
,
242 # if we have no default, do not bother checking
243 # this avoids ethtool calls on virtual interfaces
246 # autoneg comes from ethtool whereas speed and duplex from /sys/class
247 running_attr
= self
.get_running_attr(attr
, ifaceobj
)
251 ifaceobjcurr
.update_config_with_status('link-%s' % attr
,
255 if attr
== 'autoneg':
256 if configured
== 'yes' and running_attr
== 'on':
258 elif configured
== 'no' and running_attr
== 'off':
261 # we make sure we can get a running value first
262 if (running_attr
and configured
and running_attr
== configured
):
263 # PASS since running is what is configured
264 ifaceobjcurr
.update_config_with_status('link-%s'%attr
,
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
,
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
,
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
,
280 def get_autoneg(self
,ethtool_output
=None):
282 get_autoneg simply calls the ethtool command and parses out
285 ethtool_attrs
= ethtool_output
.split()
286 if ('Auto-negotiation:' in ethtool_attrs
):
287 return(ethtool_attrs
[ethtool_attrs
.index('Auto-negotiation:')+1])
291 def get_fec_encoding(self
,ethtool_output
=None):
293 get_fec_encoding simply calls the ethtool show-fec command and parses out
294 the fec encoding value.
297 for attr
in ethtool_output
.splitlines():
298 if attr
.startswith('FEC encodings'):
299 fec_attrs
= attr
.split()
300 return(fec_attrs
[fec_attrs
.index(':')+1])
301 except Exception as e
:
302 self
.logger
.debug('ethtool: problems in ethtool set-fec output'
303 ' %s: %s' %(ethtool_output
.splitlines(), str(e
)))
307 def get_running_attr(self
,attr
='',ifaceobj
=None):
308 if not ifaceobj
or not attr
:
312 if attr
== 'autoneg':
313 output
= utils
.exec_commandl([utils
.ethtool_cmd
, ifaceobj
.name
])
314 running_attr
= self
.get_autoneg(ethtool_output
=output
)
316 output
= utils
.exec_command('%s --show-fec %s'%
317 (utils
.ethtool_cmd
, ifaceobj
.name
))
318 running_attr
= self
.get_fec_encoding(ethtool_output
=output
)
320 running_attr
= self
.read_file_oneline('/sys/class/net/%s/%s' % \
321 (ifaceobj
.name
, attr
))
322 except Exception as e
:
323 # for nonexistent interfaces, we get an error (rc = 256 or 19200)
324 self
.logger
.debug('ethtool: problems calling ethtool or reading'
325 ' /sys/class on iface %s for attr %s: %s' %
326 (ifaceobj
.name
, attr
, str(e
)))
330 def _query_running(self
, ifaceobj
, ifaceobj_getfunc
=None):
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.
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
):
341 for attr
in ['speed', 'duplex', 'autoneg']:
342 default_val
= policymanager
.policymanager_api
.get_iface_default(
343 module_name
='ethtool',
344 ifname
=ifaceobj
.name
,
346 # do not continue if we have no defaults
347 # this avoids ethtool calls on virtual interfaces
350 running_attr
= self
.get_running_attr(attr
, ifaceobj
)
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
:
357 # do not proceed if speed = 0
358 if attr
== 'speed' and running_attr
and running_attr
== '0':
361 ifaceobj
.update_config('link-%s'%attr
, running_attr
)
365 def _query(self
, ifaceobj
, **kwargs
):
366 """ add default policy attributes supported by the module """
367 for attr
in ['speed', 'duplex', 'autoneg', 'fec']:
368 if ifaceobj
.get_attr_value_first('link-%s'%attr
):
370 default
= policymanager
.policymanager_api
.get_iface_default(
371 module_name
='ethtool',
372 ifname
=ifaceobj
.name
,
373 attr
='link-%s' %attr
)
376 ifaceobj
.update_config('link-%s' %attr
, default
)
378 _run_ops
= {'pre-down' : _pre_down
,
380 'query-checkcurr' : _query_check
,
381 'query-running' : _query_running
,
385 """ returns list of ops supported by this module """
386 return self
._run
_ops
.keys()
388 def _init_command_handlers(self
):
390 self
.ipcmd
= LinkUtils()
392 def run(self
, ifaceobj
, operation
, query_ifaceobj
=None, **extra_args
):
393 """ run ethtool configuration on the interface object passed as
397 **ifaceobj** (object): iface object
399 **operation** (str): any of 'post-up', 'query-checkcurr',
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.
409 if (ifaceobj
.link_kind
or
410 ifaceobj
.link_privflags
& ifaceLinkPrivFlags
.LOOPBACK
):
412 op_handler
= self
._run
_ops
.get(operation
)
415 self
._init
_command
_handlers
()
416 if operation
== 'query-checkcurr':
417 op_handler(self
, ifaceobj
, query_ifaceobj
)
419 op_handler(self
, ifaceobj
)