Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ethtool execution and state module functions for pause #63129

Merged
merged 3 commits into from
Dec 6, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog/63128.added
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add ethtool execution and state module functions for pause
138 changes: 137 additions & 1 deletion salt/modules/ethtool.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,11 @@
:platform: linux
"""


import logging
import os

import salt.utils.path
from salt.exceptions import CommandExecutionError

try:
import ethtool
Expand Down Expand Up @@ -299,3 +302,136 @@ def set_offload(devname, **kwargs):
return "Not supported"

return show_offload(devname)


def _ethtool_command(devname, *args, **kwargs):
"""
Helper function to build an ethtool command
"""
ethtool = salt.utils.path.which("ethtool")
if not ethtool:
raise CommandExecutionError("Command 'ethtool' cannot be found")
switches = " ".join(arg for arg in args)
params = " ".join("{} {}".format(key, val) for key, val in kwargs.items())
cmd = "{} {} {} {}".format(ethtool, switches, devname, params).strip()
ret = __salt__["cmd.run"](cmd, ignore_retcode=True).splitlines()
if ret and ret[0].startswith("Cannot"):
raise CommandExecutionError(ret[0])
return ret


def _validate_params(valid_params, kwargs):
"""
Helper function to validate parameters to ethtool commands. Boolean values
will be transformed into ``on`` and ``off`` to match expected syntax.
"""
validated = {}
for key, val in kwargs.items():
key = key.lower()
if key in valid_params:
if val is True:
val = "on"
elif val is False:
val = "off"
validated[key] = val
if not validated:
raise CommandExecutionError(
"None of the valid parameters were provided: {}".format(valid_params)
)
return validated


def show_pause(devname):
"""
nicholasmhughes marked this conversation as resolved.
Show resolved Hide resolved
Queries the specified network device for associated pause information

CLI Example:

.. code-block:: bash

salt '*' ethtool.show_pause <devname>
"""
data = {}

content = _ethtool_command(devname, "-a")

for line in content[1:]:
if line.strip():
(key, value) = (s.strip() for s in line.split(":", 1))
data[key] = value == "on"

return data


def set_pause(devname, **kwargs):
"""
Changes the pause parameters of the specified network device

CLI Example:

.. code-block:: bash

salt '*' ethtool.set_pause <devname> autoneg=off rx=off tx=off
"""
valid_params = ["autoneg", "rx", "tx"]
params = _validate_params(valid_params, kwargs)
ret = _ethtool_command(devname, "-A", **params)
if not ret:
return True
return ret


def show_features(devname):
"""
Queries the specified network device for associated feature information

CLI Example:

.. code-block:: bash

salt '*' ethtool.show_feature <devname>
"""
data = {}

content = _ethtool_command(devname, "-k")

for line in content[1:]:
if ":" in line:
key, value = (s.strip() for s in line.strip().split(":", 1))
fixed = "fixed" in value
if fixed:
value = value.split()[0].strip()
data[key.strip()] = {"on": value == "on", "fixed": fixed}

return data


def set_feature(devname, **kwargs):
"""
Changes the feature parameters of the specified network device

CLI Example:

.. code-block:: bash

salt '*' ethtool.set_feature <devname> sg=off
"""
valid_params = [
"rx",
"tx",
"sg",
"tso",
"ufo",
"gso",
"gro",
"lro",
"rxvlan",
"txvlan",
"ntuple",
"rxhash",
]
params = _validate_params(valid_params, kwargs)
ret = _ethtool_command(devname, "-K", **params)
if not ret:
return True
return os.linesep.join(ret)
82 changes: 82 additions & 0 deletions salt/states/ethtool.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@

import logging

from salt.exceptions import CommandExecutionError

# Set up logging
log = logging.getLogger(__name__)

Expand Down Expand Up @@ -310,3 +312,83 @@ def offload(name, **kwargs):
return ret

return ret


def pause(name, **kwargs):
"""
Manage pause parameters of network device

name
Interface name to apply pause parameters

.. code-block:: yaml

eth0:
ethtool.pause:
- name: eth0
- autoneg: off
- rx: off
- tx: off

"""
ret = {
"name": name,
"changes": {},
"result": True,
"comment": "Network device {} pause parameters are up to date.".format(name),
}
apply_pause = False

# Get current pause parameters
try:
old = __salt__["ethtool.show_pause"](name)
except CommandExecutionError:
ret["result"] = False
ret["comment"] = "Device {} pause parameters are not supported".format(name)
return ret

# map ethtool command input to output text
pause_map = {
"autoneg": "Autonegotiate",
"rx": "RX",
"tx": "RX",
}

# Process changes
new = {}
diff = []

for key, value in kwargs.items():
key = key.lower()
if key in pause_map:
if value != old[pause_map[key]]:
new.update({key: value})
if value is True:
value = "on"
elif value is False:
value = "off"
diff.append("{}: {}".format(key, value))

if not new:
return ret

# Dry run
if __opts__["test"]:
ret["result"] = None
ret["comment"] = "Device {} pause parameters are set to be updated:\n{}".format(
name, "\n".join(diff)
)
return ret

# Apply pause parameters
try:
__salt__["ethtool.set_pause"](name, **new)
# Prepare return output
ret["comment"] = "Device {} pause parameters updated.".format(name)
ret["changes"]["ethtool_pause"] = "\n".join(diff)
except CommandExecutionError as exc:
ret["result"] = False
ret["comment"] = str(exc)
return ret

return ret
Loading