3 # Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved.
4 # Author: Roopa Prabhu, roopa@cumulusnetworks.com
10 from ifupdown2
.lib
.addon
import Addon
12 import ifupdown2
.ifupdown
.ifupdownflags
as ifupdownflags
13 import ifupdown2
.ifupdown
.policymanager
as policymanager
14 import ifupdown2
.ifupdown
.statemanager
as statemanager
16 from ifupdown2
.ifupdown
.iface
import *
17 from ifupdown2
.ifupdown
.utils
import utils
18 from ifupdown2
.ifupdown
.exceptions
import moduleNotSupported
20 from ifupdown2
.ifupdownaddons
.utilsbase
import *
21 from ifupdown2
.ifupdownaddons
.modulebase
import moduleBase
22 except (ImportError, ModuleNotFoundError
):
23 from lib
.addon
import Addon
25 import ifupdown
.ifupdownflags
as ifupdownflags
26 import ifupdown
.policymanager
as policymanager
27 import ifupdown
.statemanager
as statemanager
29 from ifupdown
.iface
import *
30 from ifupdown
.utils
import utils
31 from ifupdown
.exceptions
import moduleNotSupported
33 from ifupdownaddons
.utilsbase
import *
34 from ifupdownaddons
.modulebase
import moduleBase
37 class ethtool(Addon
, moduleBase
):
38 """ ifupdown2 addon module to configure ethtool attributes """
41 "mhelp": "ethtool configuration module for interfaces",
44 "help": "set link speed",
57 "example": ["link-speed 1000"],
58 "default": "varies by platform and port"
61 "help": "set link duplex",
62 "example": ["link-duplex full"],
63 "validvals": ["half", "full"],
67 "help": "set autonegotiation",
68 "example": ["link-autoneg on"],
69 "validvals": ["yes", "no", "on", "off"],
70 "default": "varies by platform and port"
73 "help": "set forward error correction mode",
74 "example": ["link-fec rs"],
75 "validvals": ["rs", "baser", "auto", "off"],
76 "default": "varies by platform and port"
79 'help': 'Generic Receive Offload',
80 'example': ['gro-offload on'],
81 'validvals': ['on', 'off'],
82 'default': 'varies by interface'
85 'help': 'Large Receive Offload',
86 'example': ['lro-offload on'],
87 'validvals': ['on', 'off'],
88 'default': 'varies by interface'
91 'help': 'Generic Segmentation Offload',
92 'example': ['tso-offload on'],
93 'validvals': ['on', 'off'],
94 'default': 'varies by interface'
97 'help': 'TCP Segmentation Offload',
98 'example': ['tso-offload on'],
99 'validvals': ['on', 'off'],
100 'default': 'varies by interface'
103 'help': 'UDP Fragmentation Offload',
104 'example': ['ufo-offload on'],
105 'validvals': ['on', 'off'],
106 'default': 'varies by interface'
109 'help': 'TX Checksum Offload',
110 'example': ['tx-offload on'],
111 'validvals': ['on', 'off'],
112 'default': 'varies by interface'
115 'help': 'RX Checksum Offload',
116 'example': ['rx-offload on'],
117 'validvals': ['on', 'off'],
118 'default': 'varies by interface'
121 'help': 'Ring RX Parameter',
122 'example': ['ring-rx 512'],
123 'validvals': ['max', '<number>'],
124 'default': 'varies by interface'
127 'help': 'Ring TX Parameter',
128 'example': ['ring-tx 512'],
129 'validvals': ['max', '<number>'],
130 'default': 'varies by interface'
135 def __init__(self
, *args
, **kargs
):
137 moduleBase
.__init
__(self
, *args
, **kargs
)
138 if not os
.path
.exists(utils
.ethtool_cmd
):
139 raise moduleNotSupported('module init failed: %s: not found' % utils
.ethtool_cmd
)
140 # keep a list of iface objects who have modified link attributes
141 self
.ifaceobjs_modified_configs
= []
143 self
.feature_cache
= None
145 self
.ethtool_ignore_errors
= policymanager
.policymanager_api
.get_module_globals(
146 module_name
=self
.__class
__.__name
__,
147 attr
='ethtool_ignore_errors'
150 def do_ring_settings(self
, ifaceobj
, attr_name
, option
):
151 # Get the current configuration value and default value for the specified attribute
152 config_val
= ifaceobj
.get_attr_value_first(attr_name
)
153 default_val
= policymanager
.policymanager_api
.get_iface_default(
154 module_name
='ethtool',
155 ifname
=ifaceobj
.name
,
158 # Check which variable to use, config_val > default_val. If none are set, return.
159 value
= config_val
or default_val
164 # Get the maximum value for the specified attribute
165 max_val
= self
.get_max_attr(attr_name
, ifaceobj
)
170 # Get the current running value
171 running_val
= self
.get_running_attr(attr_name
, ifaceobj
)
174 # If the value is the same as the running value, do nothing
175 if value
== running_val
:
178 # Generate the ethtool command
179 cmd
= ('%s --set-ring %s %s %s' %
180 (utils
.ethtool_cmd
, ifaceobj
.name
, option
, value
))
182 # Execute the ethtool command if command is generated
185 utils
.exec_command(cmd
)
186 except Exception as e
:
187 self
.log_error('%s: %s' %(ifaceobj
.name
, str(e
)), ifaceobj
)
189 def do_offload_settings(self
, ifaceobj
, attr_name
, eth_name
):
190 default
= 'default_' + eth_name
191 config_val
= ifaceobj
.get_attr_value_first(attr_name
)
194 saved_ifaceobjs
= statemanager
.statemanager_api
.get_ifaceobjs(ifaceobj
.name
)
196 default_val
= saved_ifaceobjs
[0].get_attr_value_first(default
)
197 if config_val
or default_val
:
200 running_val
= str(self
.get_running_attr(eth_name
, ifaceobj
)).lower()
204 ifaceobj
.config
[default
] = [running_val
]
207 ifaceobj
.config
[default
] = [default_val
]
210 config_val
= default_val
212 if config_val
and config_val
!= running_val
:
214 cmd
= ('%s -K %s %s %s' %
215 (utils
.ethtool_cmd
, ifaceobj
.name
, eth_name
, config_val
))
216 utils
.exec_command(cmd
)
217 except Exception as e
:
218 self
.log_error('%s: %s' %(ifaceobj
.name
, str(e
)), ifaceobj
)
220 self
.ethtool_ignore_errors
= policymanager
.policymanager_api
.get_module_globals(
221 module_name
=self
.__class
__.__name
__,
222 attr
='ethtool_ignore_errors'
225 def do_fec_settings(self
, ifaceobj
):
228 # attribute existed before but we must reset to default
229 config_val
= ifaceobj
.get_attr_value_first('link-fec')
230 default_val
= policymanager
.policymanager_api
.get_iface_default(
231 module_name
='ethtool',
232 ifname
=ifaceobj
.name
,
235 if not default_val
and not config_val
:
236 # there is no point in checking the running config
237 # if we have no default and the user did not have settings
240 # use only lowercase values
241 running_val
= str(self
.get_running_attr('fec', ifaceobj
)).lower()
244 config_val
= config_val
.lower()
246 default_val
= default_val
.lower()
248 # check running values
249 if config_val
and config_val
== running_val
:
252 if not config_val
and default_val
and default_val
== running_val
:
253 # nothing configured but the default is running
256 # if we got this far, we need to change it
257 if config_val
and (config_val
!= running_val
):
258 # if the configured value is not set, set it
259 feccmd
= ' %s %s' % ("encoding", config_val
)
260 elif default_val
and (default_val
!= running_val
):
261 # or if it has a default not equal to running value, set it
262 feccmd
= ' %s %s' % ("encoding", default_val
)
266 feccmd
= ('%s --set-fec %s %s' %
267 (utils
.ethtool_cmd
, ifaceobj
.name
, feccmd
))
268 utils
.exec_command(feccmd
)
269 except Exception as e
:
270 if not self
.ethtool_ignore_errors
:
271 self
.log_error('%s: %s' %(ifaceobj
.name
, str(e
)), ifaceobj
)
275 def do_speed_settings(self
, ifaceobj
, operation
='post_up'):
278 autoneg_to_configure
= None
279 speed_to_configure
= None
280 duplex_to_configure
= None
282 config_speed
= ifaceobj
.get_attr_value_first('link-speed')
283 config_duplex
= ifaceobj
.get_attr_value_first('link-duplex')
284 config_autoneg
= ifaceobj
.get_attr_value_first('link-autoneg')
286 default_speed
= policymanager
.policymanager_api
.get_iface_default(
287 module_name
='ethtool',
288 ifname
=ifaceobj
.name
,
292 default_duplex
= policymanager
.policymanager_api
.get_iface_default(
293 module_name
='ethtool',
294 ifname
=ifaceobj
.name
,
298 default_autoneg
= policymanager
.policymanager_api
.get_iface_default(
299 module_name
='ethtool',
300 ifname
=ifaceobj
.name
,
304 # autoneg wins if provided by user and is on
305 if config_autoneg
and utils
.get_boolean_from_string(config_autoneg
):
306 autoneg_to_configure
= config_autoneg
307 speed_to_configure
= None
308 duplex_to_configure
= None
310 # Any speed settings configured by the user wins
311 autoneg_to_configure
= None
312 speed_to_configure
= config_speed
313 duplex_to_configure
= config_duplex
314 if not config_duplex
:
315 duplex_to_configure
= default_duplex
317 # if user given autoneg config is off, we must respect that and
318 # override any default autoneg config
319 if config_autoneg
and not utils
.get_boolean_from_string(config_autoneg
):
320 default_autoneg
= 'off'
322 if default_autoneg
and utils
.get_boolean_from_string(default_autoneg
):
323 autoneg_to_configure
= utils
.get_onoff_bool(default_autoneg
)
324 speed_to_configure
= None
325 duplex_to_configure
= None
327 autoneg_to_configure
= None
328 speed_to_configure
= default_speed
329 duplex_to_configure
= default_duplex
331 if autoneg_to_configure
:
332 autoneg_to_configure
= utils
.get_onoff_bool(autoneg_to_configure
)
333 # check running values
334 running_val
= self
.get_running_attr('autoneg', ifaceobj
)
335 if autoneg_to_configure
!= running_val
:
336 # if the configured value is not set, set it
337 cmd
+= ' autoneg %s' % autoneg_to_configure
340 if speed_to_configure
:
341 # check running values
342 if utils
.get_boolean_from_string(self
.get_running_attr('autoneg', ifaceobj
) or 'off'):
344 # if we are transitioning from autoneg 'on' to 'off'
345 # don't check running speed
348 running_val
= self
.get_running_attr('speed', ifaceobj
)
349 if force_set
or (speed_to_configure
!= running_val
):
350 # if the configured value is not set, set it
351 cmd
+= ' speed %s' % speed_to_configure
353 if duplex_to_configure
:
354 # check running values
355 running_val
= self
.get_running_attr('duplex', ifaceobj
)
356 if force_set
or (duplex_to_configure
!= running_val
):
357 # if the configured value is not set, set it
358 cmd
+= ' duplex %s' % duplex_to_configure
362 cmd
= ('%s -s %s %s' % (utils
.ethtool_cmd
, ifaceobj
.name
, cmd
))
363 utils
.exec_command(cmd
)
364 except Exception as e
:
365 if not self
.ethtool_ignore_errors
:
366 self
.log_error('%s: %s' % (ifaceobj
.name
, str(e
)), ifaceobj
)
368 def _pre_up(self
, ifaceobj
, operation
='post_up'):
370 _pre_up and _pre_down will reset the layer 2 attributes to default policy
373 if not self
.cache
.link_exists(ifaceobj
.name
):
376 self
.do_speed_settings(ifaceobj
)
377 self
.do_fec_settings(ifaceobj
)
378 self
.do_ring_settings(ifaceobj
, 'ring-rx', 'rx')
379 self
.do_ring_settings(ifaceobj
, 'ring-tx', 'tx')
380 self
.do_offload_settings(ifaceobj
, 'gro-offload', 'gro')
381 self
.do_offload_settings(ifaceobj
, 'lro-offload', 'lro')
382 self
.do_offload_settings(ifaceobj
, 'gso-offload', 'gso')
383 self
.do_offload_settings(ifaceobj
, 'tso-offload', 'tso')
384 self
.do_offload_settings(ifaceobj
, 'ufo-offload', 'ufo')
385 self
.do_offload_settings(ifaceobj
, 'tx-offload', 'tx')
386 self
.do_offload_settings(ifaceobj
, 'rx-offload', 'rx')
388 def _pre_down(self
, ifaceobj
):
389 pass #self._post_up(ifaceobj,operation="_pre_down")
391 def _query_check(self
, ifaceobj
, ifaceobjcurr
):
393 _query_check() needs to compare the configured (or running)
394 attribute with the running attribute.
396 If there is nothing configured, we compare the default attribute with
397 the running attribute and FAIL if they are different.
398 This is because a reboot will lose their running attribute
399 (the default will get set).
401 for attr
in ['speed', 'duplex', 'autoneg', 'fec']:
402 configured
= ifaceobj
.get_attr_value_first('link-%s'%attr
)
403 # if there is nothing configured, do not check
405 if not ifupdownflags
.flags
.WITHDEFAULTS
:
407 default
= policymanager
.policymanager_api
.get_iface_default(
408 module_name
='ethtool',
409 ifname
=ifaceobj
.name
,
411 # if we have no default, do not bother checking
412 # this avoids ethtool calls on virtual interfaces
415 # autoneg comes from ethtool whereas speed and duplex from /sys/class
416 running_attr
= self
.get_running_attr(attr
, ifaceobj
)
420 ifaceobjcurr
.update_config_with_status('link-%s' % attr
,
424 if attr
== 'autoneg':
425 if configured
== 'yes' and running_attr
== 'on':
427 elif configured
== 'no' and running_attr
== 'off':
430 # we make sure we can get a running value first
431 if (running_attr
and configured
and running_attr
== configured
):
432 # PASS since running is what is configured
433 ifaceobjcurr
.update_config_with_status('link-%s'%attr
,
435 elif (running_attr
and configured
and running_attr
!= configured
):
436 # We show a FAIL since it is not the configured or default
437 ifaceobjcurr
.update_config_with_status('link-%s'%attr
,
439 elif (running_attr
and default
and running_attr
== default
):
440 # PASS since running is default
441 ifaceobjcurr
.update_config_with_status('link-%s'%attr
,
443 elif (default
or configured
):
444 # We show a FAIL since it is not the configured or default
445 ifaceobjcurr
.update_config_with_status('link-%s'%attr
,
449 def get_autoneg(self
,ethtool_output
=None):
451 get_autoneg simply calls the ethtool command and parses out
454 ethtool_attrs
= ethtool_output
.split()
455 if ('Auto-negotiation:' in ethtool_attrs
):
456 return(ethtool_attrs
[ethtool_attrs
.index('Auto-negotiation:')+1])
460 def get_fec_encoding(self
,ethtool_output
=None):
462 get_fec_encoding simply calls the ethtool show-fec command and parses out
463 the fec encoding value.
466 for attr
in ethtool_output
.splitlines():
467 if attr
.startswith('Configured FEC encodings:'):
468 fec_attrs
= attr
.split()
469 return(fec_attrs
[fec_attrs
.index('encodings:')+1])
470 except Exception as e
:
471 self
.logger
.debug('ethtool: problems in ethtool set-fec output'
472 ' %s: %s' %(ethtool_output
.splitlines(), str(e
)))
476 def get_offload_setting(self
, ethtool_output
, setting
):
480 for line
in ethtool_output
.splitlines():
491 def get_ring_setting(self
, ethtool_output
, setting
, get_ring_max
=False):
495 settings
= ethtool_output
.split('Current hardware settings:', 1)[0]
497 settings
= ethtool_output
.split('Current hardware settings:', 1)[1]
499 for line
in settings
.splitlines():
500 if line
.startswith(setting
):
501 value
= line
.split(':', 1)[1]
506 def get_attr_value(self
,attr
='',ifaceobj
=None,get_max
=False):
507 if not ifaceobj
or not attr
:
511 if attr
== 'autoneg':
512 output
= utils
.exec_commandl([utils
.ethtool_cmd
, ifaceobj
.name
])
513 attr_value
= self
.get_autoneg(ethtool_output
=output
)
514 elif attr
== 'ring-rx':
515 output
= utils
.exec_command('%s --show-ring %s'%
516 (utils
.ethtool_cmd
, ifaceobj
.name
))
517 attr_value
= self
.get_ring_setting(ethtool_output
=output
, setting
='RX:', get_ring_max
=get_max
)
518 elif attr
== 'ring-tx':
519 output
= utils
.exec_command('%s --show-ring %s'%
520 (utils
.ethtool_cmd
, ifaceobj
.name
))
521 attr_value
= self
.get_ring_setting(ethtool_output
=output
, setting
='TX:', get_ring_max
=get_max
)
523 output
= utils
.exec_command('%s --show-fec %s'%
524 (utils
.ethtool_cmd
, ifaceobj
.name
))
525 attr_value
= self
.get_fec_encoding(ethtool_output
=output
)
527 if not self
.feature_cache
:
528 self
.feature_cache
= utils
.exec_command('%s --show-features %s'%
529 (utils
.ethtool_cmd
, ifaceobj
.name
))
530 attr_value
= self
.get_offload_setting(ethtool_output
=self
.feature_cache
, setting
='generic-receive-offload')
532 if not self
.feature_cache
:
533 self
.feature_cache
= utils
.exec_command('%s --show-features %s'%
534 (utils
.ethtool_cmd
, ifaceobj
.name
))
535 attr_value
= self
.get_offload_setting(ethtool_output
=self
.feature_cache
, setting
='large-receive-offload')
537 if not self
.feature_cache
:
538 self
.feature_cache
= utils
.exec_command('%s --show-features %s'%
539 (utils
.ethtool_cmd
, ifaceobj
.name
))
540 attr_value
= self
.get_offload_setting(ethtool_output
=self
.feature_cache
, setting
='generic-segmentation-offload')
542 if not self
.feature_cache
:
543 self
.feature_cache
= utils
.exec_command('%s --show-features %s'%
544 (utils
.ethtool_cmd
, ifaceobj
.name
))
545 attr_value
= self
.get_offload_setting(ethtool_output
=self
.feature_cache
, setting
='tcp-segmentation-offload')
547 if not self
.feature_cache
:
548 self
.feature_cache
= utils
.exec_command('%s --show-features %s'%
549 (utils
.ethtool_cmd
, ifaceobj
.name
))
550 attr_value
= self
.get_offload_setting(ethtool_output
=self
.feature_cache
, setting
='udp-fragmentation-offload')
552 if not self
.feature_cache
:
553 self
.feature_cache
= utils
.exec_command('%s --show-features %s'%
554 (utils
.ethtool_cmd
, ifaceobj
.name
))
555 attr_value
= self
.get_offload_setting(ethtool_output
=self
.feature_cache
, setting
='rx-checksumming')
557 if not self
.feature_cache
:
558 self
.feature_cache
= utils
.exec_command('%s --show-features %s'%
559 (utils
.ethtool_cmd
, ifaceobj
.name
))
560 attr_value
= self
.get_offload_setting(ethtool_output
=self
.feature_cache
, setting
='tx-checksumming')
562 attr_value
= self
.io
.read_file_oneline('/sys/class/net/%s/%s' % \
563 (ifaceobj
.name
, attr
))
564 except Exception as e
:
565 if not self
.ethtool_ignore_errors
:
566 # for nonexistent interfaces, we get an error (rc = 256 or 19200)
567 self
.logger
.debug('ethtool: problems calling ethtool or reading'
568 ' /sys/class on iface %s for attr %s: %s' %
569 (ifaceobj
.name
, attr
, str(e
)))
572 def get_running_attr(self
,attr
='',ifaceobj
=None):
573 return self
.get_attr_value(attr
=attr
, ifaceobj
=ifaceobj
, get_max
=False)
575 def get_max_attr(self
,attr
='',ifaceobj
=None):
576 return self
.get_attr_value(attr
=attr
, ifaceobj
=ifaceobj
, get_max
=True)
578 def _query_running(self
, ifaceobj
, ifaceobj_getfunc
=None):
580 _query_running looks at the speed and duplex from /sys/class
581 and retreives autoneg from ethtool. We do not report autoneg
582 if speed is not available because this usually means the link is
583 down and the autoneg value is not reliable when the link is down.
585 # do not bother showing swp ifaces that are not up for the speed
586 # duplex and autoneg are not reliable.
587 if not self
.cache
.link_is_up(ifaceobj
.name
):
589 for attr
in ['speed', 'duplex', 'autoneg']:
590 default_val
= policymanager
.policymanager_api
.get_iface_default(
591 module_name
='ethtool',
592 ifname
=ifaceobj
.name
,
594 # do not continue if we have no defaults
595 # this avoids ethtool calls on virtual interfaces
598 running_attr
= self
.get_running_attr(attr
, ifaceobj
)
600 # Only show the link attributes if they differ from defaults
601 # to see the defaults, we should implement another flag (--with-defaults)
602 if default_val
== running_attr
:
605 # do not proceed if speed = 0
606 if attr
== 'speed' and running_attr
and running_attr
== '0':
609 ifaceobj
.update_config('link-%s'%attr
, running_attr
)
613 def _query(self
, ifaceobj
, **kwargs
):
614 """ add default policy attributes supported by the module """
615 for attr
in ['speed', 'duplex', 'autoneg', 'fec']:
616 if ifaceobj
.get_attr_value_first('link-%s'%attr
):
618 default
= policymanager
.policymanager_api
.get_iface_default(
619 module_name
='ethtool',
620 ifname
=ifaceobj
.name
,
621 attr
='link-%s' %attr
)
624 ifaceobj
.update_config('link-%s' %attr
, default
)
626 _run_ops
= {'pre-down' : _pre_down
,
628 'query-checkcurr' : _query_check
,
629 'query-running' : _query_running
,
633 """ returns list of ops supported by this module """
634 return list(self
._run
_ops
.keys())
636 def run(self
, ifaceobj
, operation
, query_ifaceobj
=None, **extra_args
):
637 """ run ethtool configuration on the interface object passed as
641 **ifaceobj** (object): iface object
643 **operation** (str): any of 'post-up', 'query-checkcurr',
646 **query_ifaceobj** (object): query check ifaceobject. This is only
647 valid when op is 'query-checkcurr'. It is an object same as
648 ifaceobj, but contains running attribute values and its config
649 status. The modules can use it to return queried running state
650 of interfaces. status is success if the running state is same
651 as user required state in ifaceobj. error otherwise.
653 if (ifaceobj
.link_kind
or
654 ifaceobj
.link_privflags
& ifaceLinkPrivFlags
.LOOPBACK
):
656 op_handler
= self
._run
_ops
.get(operation
)
659 if operation
== 'query-checkcurr':
660 op_handler(self
, ifaceobj
, query_ifaceobj
)
662 op_handler(self
, ifaceobj
)