Skip to content

Commit

Permalink
Merge pull request #115 from tyll/ethtool
Browse files Browse the repository at this point in the history
Support ethtool -K|--features|--offload
  • Loading branch information
tyll authored Jun 14, 2019
2 parents e37e9d7 + 70aa987 commit d5891d4
Show file tree
Hide file tree
Showing 12 changed files with 547 additions and 4 deletions.
62 changes: 62 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,68 @@ The IP configuration supports the following options:

**Note:** Slaves to the bridge, bond or team devices cannot specify `ip` settings.

### `ethtool`

The ethtool settings allow to enable or disable varios features. The names
correspond to the names used by the `ethtool` utility. Depending on the actual
kernel and device, changing some features might not be supported.

```yaml
ethtool:
features:
esp-hw-offload: yes|no # optional
esp-tx-csum-hw-offload: yes|no # optional
fcoe-mtu: yes|no # optional
gro: yes|no # optional
gso: yes|no # optional
highdma: yes|no # optional
hw-tc-offload: yes|no # optional
l2-fwd-offload: yes|no # optional
loopback: yes|no # optional
lro: yes|no # optional
ntuple: yes|no # optional
rx: yes|no # optional
rx-all: yes|no # optional
rx-fcs: yes|no # optional
rx-gro-hw: yes|no # optional
rx-udp_tunnel-port-offload: yes|no # optional
rx-vlan-filter: yes|no # optional
rx-vlan-stag-filter: yes|no # optional
rx-vlan-stag-hw-parse: yes|no # optional
rxhash: yes|no # optional
rxvlan: yes|no # optional
sg: yes|no # optional
tls-hw-record: yes|no # optional
tls-hw-tx-offload: yes|no # optional
tso: yes|no # optional
tx: yes|no # optional
tx-checksum-fcoe-crc: yes|no # optional
tx-checksum-ip-generic: yes|no # optional
tx-checksum-ipv4: yes|no # optional
tx-checksum-ipv6: yes|no # optional
tx-checksum-sctp: yes|no # optional
tx-esp-segmentation: yes|no # optional
tx-fcoe-segmentation: yes|no # optional
tx-gre-csum-segmentation: yes|no # optional
tx-gre-segmentation: yes|no # optional
tx-gso-partial: yes|no # optional
tx-gso-robust: yes|no # optional
tx-ipxip4-segmentation: yes|no # optional
tx-ipxip6-segmentation: yes|no # optional
tx-nocache-copy: yes|no # optional
tx-scatter-gather: yes|no # optional
tx-scatter-gather-fraglist: yes|no # optional
tx-sctp-segmentation: yes|no # optional
tx-tcp-ecn-segmentation: yes|no # optional
tx-tcp-mangleid-segmentation: yes|no # optional
tx-tcp-segmentation: yes|no # optional
tx-tcp6-segmentation: yes|no # optional
tx-udp-segmentation: yes|no # optional
tx-udp_tnl-csum-segmentation: yes|no # optional
tx-udp_tnl-segmentation: yes|no # optional
tx-vlan-stag-hw-insert: yes|no # optional
txvlan: yes|no # optional
```
Examples of Options
-------------------
Expand Down
14 changes: 14 additions & 0 deletions examples/ethtool-features-default.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# SPDX-License-Identifier: BSD-3-Clause
---
- hosts: all
tasks:
- include_role:
name: linux-system-roles.network
vars:
network_connections:
- name: "{{ network_interface_name1 }}"
state: up
type: ethernet
ip:
dhcp4: "no"
auto6: "no"
19 changes: 19 additions & 0 deletions examples/ethtool-features.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# SPDX-License-Identifier: BSD-3-Clause
---
- hosts: all
tasks:
- include_role:
name: linux-system-roles.network
vars:
network_connections:
- name: "{{ network_interface_name1 }}"
state: up
type: ethernet
ip:
dhcp4: "no"
auto6: "no"
ethtool:
features:
gro: "no"
gso: "yes"
tx-sctp-segmentation: "no"
79 changes: 75 additions & 4 deletions library/network_connections.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

# pylint: disable=import-error
from ansible.module_utils.network_lsr.utils import Util
from ansible.module_utils.network_lsr import nm_provider

DOCUMENTATION = """
---
Expand Down Expand Up @@ -361,16 +362,39 @@ def ifcfg_create(
if connection["mtu"]:
ifcfg["MTU"] = str(connection["mtu"])

ethtool_options = ""
if "ethernet" in connection:
if connection["ethernet"]["autoneg"] is not None:
if connection["ethernet"]["autoneg"]:
s = "autoneg on"
ethtool_options = "autoneg on"
else:
s = "autoneg off speed %s duplex %s" % (
ethtool_options = "autoneg off speed %s duplex %s" % (
connection["ethernet"]["speed"],
connection["ethernet"]["duplex"],
)
ifcfg["ETHTOOL_OPTS"] = s

ethtool_features = connection["ethtool"]["features"]
configured_features = []
for feature, setting in ethtool_features.items():
value = ""
if setting:
value = "on"
elif setting is not None:
value = "off"

if value:
configured_features.append("%s %s" % (feature, value))

if configured_features:
if ethtool_options:
ethtool_options += " ; "
ethtool_options += "-K %s %s" % (
connection["interface_name"],
" ".join(configured_features),
)

if ethtool_options:
ifcfg["ETHTOOL_OPTS"] = ethtool_options

if connection["master"] is not None:
m = ArgUtil.connection_find_master(connection["master"], connections, idx)
Expand Down Expand Up @@ -839,6 +863,20 @@ def connection_create(self, connections, idx, connection_current=None):
NM.SETTING_WIRED_SPEED, connection["ethernet"]["speed"]
)

if hasattr(NM, "SettingEthtool"):
s_ethtool = self.connection_ensure_setting(con, NM.SettingEthtool)

for feature, setting in connection["ethtool"]["features"].items():
nm_feature = nm_provider.get_nm_ethtool_feature(feature)

if setting is None:
if nm_feature:
s_ethtool.set_feature(nm_feature, NM.Ternary.DEFAULT)
elif setting:
s_ethtool.set_feature(nm_feature, NM.Ternary.TRUE)
else:
s_ethtool.set_feature(nm_feature, NM.Ternary.FALSE)

if connection["mtu"]:
if connection["type"] == "infiniband":
s_infiniband = self.connection_ensure_setting(con, NM.SettingInfiniband)
Expand Down Expand Up @@ -1830,7 +1868,9 @@ def run_prepare(self):
Cmd.run_prepare(self)

names = {}
for connection in self.connections:
for idx, connection in enumerate(self.connections):
self._check_ethtool_setting_support(idx, connection)

name = connection["name"]
if not name:
assert connection["persistent_state"] == "absent"
Expand Down Expand Up @@ -1872,6 +1912,37 @@ def finish_transaction(self):
finally:
self._checkpoint = None

def _check_ethtool_setting_support(self, idx, connection):
""" Check if SettingEthtool support is needed and available
If any feature is specified, the SettingEthtool setting needs to be
available. Also NM needs to know about each specified setting. Do not
check if NM knows about any defaults.
"""
NM = Util.NM()

# If the profile is not completely specified, for example if only the
# runtime change is specified, the ethtool subtree might be missing.
# Then no checks are required.
if "ethtool" not in connection:
return

ethtool_features = connection["ethtool"]["features"]
specified_features = dict(
[(k, v) for k, v in ethtool_features.items() if v is not None]
)

if specified_features and not hasattr(NM, "SettingEthtool"):
self.log_fatal(idx, "ethtool.features specified but not supported by NM")

for feature, setting in specified_features.items():
nm_feature = nm_provider.get_nm_ethtool_feature(feature)
if not nm_feature:
self.log_fatal(
idx, "ethtool feature %s specified but not support by NM" % feature
)

def run_action_absent(self, idx):
seen = set()
name = self.connections[idx]["name"]
Expand Down
87 changes: 87 additions & 0 deletions module_utils/network_lsr/argument_validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -521,6 +521,92 @@ def _validate_post(self, value, name, result):
return result


class ArgValidator_DictEthtool(ArgValidatorDict):
def __init__(self):
ArgValidatorDict.__init__(
self,
name="ethtool",
nested=[ArgValidator_DictEthtoolFeatures()],
default_value=ArgValidator.MISSING,
)

self.default_value = dict(
[(k, v.default_value) for k, v in self.nested.items()]
)


class ArgValidator_DictEthtoolFeatures(ArgValidatorDict):
# List of features created with:
# nmcli connection modify "virbr0" ethtool.feature- on |& \
# sed -e 's_[,:]_\n_g' | \ # split output in newlines
# grep ^\ f | \ # select only lines starting with " f"
# tr -d " ." | \ # remove spaces and fullstops
# sed -e 's,feature-,ArgValidatorBool(",' \ # add Python code
# -e 's/$/", default_value=None)],/'
def __init__(self):
ArgValidatorDict.__init__(
self,
name="features",
nested=[
ArgValidatorBool("esp-hw-offload", default_value=None),
ArgValidatorBool("esp-tx-csum-hw-offload", default_value=None),
ArgValidatorBool("fcoe-mtu", default_value=None),
ArgValidatorBool("gro", default_value=None),
ArgValidatorBool("gso", default_value=None),
ArgValidatorBool("highdma", default_value=None),
ArgValidatorBool("hw-tc-offload", default_value=None),
ArgValidatorBool("l2-fwd-offload", default_value=None),
ArgValidatorBool("loopback", default_value=None),
ArgValidatorBool("lro", default_value=None),
ArgValidatorBool("ntuple", default_value=None),
ArgValidatorBool("rx", default_value=None),
ArgValidatorBool("rxhash", default_value=None),
ArgValidatorBool("rxvlan", default_value=None),
ArgValidatorBool("rx-all", default_value=None),
ArgValidatorBool("rx-fcs", default_value=None),
ArgValidatorBool("rx-gro-hw", default_value=None),
ArgValidatorBool("rx-udp_tunnel-port-offload", default_value=None),
ArgValidatorBool("rx-vlan-filter", default_value=None),
ArgValidatorBool("rx-vlan-stag-filter", default_value=None),
ArgValidatorBool("rx-vlan-stag-hw-parse", default_value=None),
ArgValidatorBool("sg", default_value=None),
ArgValidatorBool("tls-hw-record", default_value=None),
ArgValidatorBool("tls-hw-tx-offload", default_value=None),
ArgValidatorBool("tso", default_value=None),
ArgValidatorBool("tx", default_value=None),
ArgValidatorBool("txvlan", default_value=None),
ArgValidatorBool("tx-checksum-fcoe-crc", default_value=None),
ArgValidatorBool("tx-checksum-ipv4", default_value=None),
ArgValidatorBool("tx-checksum-ipv6", default_value=None),
ArgValidatorBool("tx-checksum-ip-generic", default_value=None),
ArgValidatorBool("tx-checksum-sctp", default_value=None),
ArgValidatorBool("tx-esp-segmentation", default_value=None),
ArgValidatorBool("tx-fcoe-segmentation", default_value=None),
ArgValidatorBool("tx-gre-csum-segmentation", default_value=None),
ArgValidatorBool("tx-gre-segmentation", default_value=None),
ArgValidatorBool("tx-gso-partial", default_value=None),
ArgValidatorBool("tx-gso-robust", default_value=None),
ArgValidatorBool("tx-ipxip4-segmentation", default_value=None),
ArgValidatorBool("tx-ipxip6-segmentation", default_value=None),
ArgValidatorBool("tx-nocache-copy", default_value=None),
ArgValidatorBool("tx-scatter-gather", default_value=None),
ArgValidatorBool("tx-scatter-gather-fraglist", default_value=None),
ArgValidatorBool("tx-sctp-segmentation", default_value=None),
ArgValidatorBool("tx-tcp6-segmentation", default_value=None),
ArgValidatorBool("tx-tcp-ecn-segmentation", default_value=None),
ArgValidatorBool("tx-tcp-mangleid-segmentation", default_value=None),
ArgValidatorBool("tx-tcp-segmentation", default_value=None),
ArgValidatorBool("tx-udp-segmentation", default_value=None),
ArgValidatorBool("tx-udp_tnl-csum-segmentation", default_value=None),
ArgValidatorBool("tx-udp_tnl-segmentation", default_value=None),
ArgValidatorBool("tx-vlan-stag-hw-insert", default_value=None),
],
)
self.default_value = dict(
[(k, v.default_value) for k, v in self.nested.items()]
)


class ArgValidator_DictBond(ArgValidatorDict):

VALID_MODES = [
Expand Down Expand Up @@ -668,6 +754,7 @@ def __init__(self):
ArgValidatorBool("ignore_errors", default_value=None),
ArgValidator_DictIP(),
ArgValidator_DictEthernet(),
ArgValidator_DictEthtool(),
ArgValidator_DictBond(),
ArgValidator_DictInfiniband(),
ArgValidator_DictVlan(),
Expand Down
23 changes: 23 additions & 0 deletions module_utils/network_lsr/nm_provider.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# SPDX-License-Identifier: BSD-3-Clause
""" Support for NetworkManager aka the NM provider """

# pylint: disable=import-error, no-name-in-module
from ansible.module_utils.network_lsr.utils import Util

ETHTOOL_FEATURE_PREFIX = "ETHTOOL_OPTNAME_FEATURE_"


def get_nm_ethtool_feature(name):
"""
Translate ethtool feature into Network Manager name
:param name: Name of the feature
:type name: str
:returns: Name of the feature to be used by `NM.SettingEthtool.set_feature()`
:rtype: str
"""

name = ETHTOOL_FEATURE_PREFIX + name.upper().replace("-", "_")

feature = getattr(Util.NM(), name, None)
return feature
2 changes: 2 additions & 0 deletions tests/ensure_non_running_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
"tests_unit.yml",
"tests_vlan_mtu_initscripts.yml",
"tests_vlan_mtu_nm.yml",
"tests_ethtool_features_initscripts.yml",
"tests_ethtool_features_nm.yml",
]

OTHER_PLAYBOOK = """
Expand Down
Loading

0 comments on commit d5891d4

Please sign in to comment.