diff --git a/.yamllint.yml b/.yamllint.yml index bd4bad0..b7d0a48 100644 --- a/.yamllint.yml +++ b/.yamllint.yml @@ -22,3 +22,4 @@ rules: /tests/tests_wireless_plugin_installation_nm.yml /tests/tests_team_plugin_installation_nm.yml /tests/tests_ethtool_coalesce_initscripts.yml + /tests/tests_ethtool_ring_initscripts.yml diff --git a/README.md b/README.md index 699d92b..f73d621 100644 --- a/README.md +++ b/README.md @@ -436,6 +436,17 @@ The ethtool settings allow to enable or disable various features. The names correspond to the names used by the `ethtool` utility. Depending on the actual kernel and device, changing some options might not be supported. +The ethtool configuration supports the following options: + +- `ring` + + Changes the `rx`/`tx` `ring` parameters of the specified network device. The list + of supported `ring` parameters is: + - `rx` - Changes the number of ring entries for the Rx ring. + - `rx-jumbo` - Changes the number of ring entries for the Rx Jumbo ring. + - `rx-mini` - Changes the number of ring entries for the Rx Mini ring. + - `tx` - Changes the number of ring entries for the Tx ring. + ```yaml ethtool: features: @@ -514,6 +525,11 @@ kernel and device, changing some options might not be supported. tx_usecs_high: 0 # optional mininum=0 maximum=0xffffffff tx_usecs_irq: 0 # optional mininum=0 maximum=0xffffffff tx_usecs_low: 0 # optional mininum=0 maximum=0xffffffff + ring: + rx: 0 # optional mininum=0 maximum=0xffffffff + rx_jumbo: 0 # optional mininum=0 maximum=0xffffffff + rx_mini: 0 # optional mininum=0 maximum=0xffffffff + tx: 0 # optional mininum=0 maximum=0xffffffff ``` ### `ieee802_1x` diff --git a/examples/ethtool_ring.yml b/examples/ethtool_ring.yml new file mode 100644 index 0000000..fbf6fe9 --- /dev/null +++ b/examples/ethtool_ring.yml @@ -0,0 +1,20 @@ +# 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: + ring: + rx: 128 + rx_jumbo: 128 + rx_mini: 128 + tx: 128 diff --git a/library/network_connections.py b/library/network_connections.py index 6096ebc..fc113b7 100644 --- a/library/network_connections.py +++ b/library/network_connections.py @@ -428,6 +428,19 @@ class IfcfgUtil: connection["interface_name"], " ".join(configured_coalesce), ) + ethtool_ring = connection["ethtool"]["ring"] + configured_ring = [] + for ring, setting in ethtool_ring.items(): + if setting is not None: + configured_ring.append("%s %s" % (ring.replace("_", "-"), setting)) + + if configured_ring: + if ethtool_options: + ethtool_options += " ; " + ethtool_options += "-G %s %s" % ( + connection["interface_name"], + " ".join(configured_ring), + ) if ethtool_options: ifcfg["ETHTOOL_OPTS"] = ethtool_options @@ -948,6 +961,14 @@ class NMUtil: s_ethtool.option_set(nm_coalesce, None) else: s_ethtool.option_set_uint32(nm_coalesce, int(setting)) + for ring, setting in connection["ethtool"]["ring"].items(): + nm_ring = nm_provider.get_nm_ethtool_ring(ring) + + if nm_ring: + if setting is None: + s_ethtool.option_set(nm_ring, None) + else: + s_ethtool.option_set_uint32(nm_ring, setting) if connection["mtu"]: if connection["type"] == "infiniband": @@ -2005,6 +2026,7 @@ class Cmd_nm(Cmd): ethtool_dict = { "features": nm_provider.get_nm_ethtool_feature, "coalesce": nm_provider.get_nm_ethtool_coalesce, + "ring": nm_provider.get_nm_ethtool_ring, } for ethtool_key, nm_get_name_fcnt in ethtool_dict.items(): diff --git a/module_utils/network_lsr/argument_validator.py b/module_utils/network_lsr/argument_validator.py index 92e07b8..a5c36df 100644 --- a/module_utils/network_lsr/argument_validator.py +++ b/module_utils/network_lsr/argument_validator.py @@ -711,6 +711,7 @@ class ArgValidator_DictEthtool(ArgValidatorDict): nested=[ ArgValidator_DictEthtoolFeatures(), ArgValidator_DictEthtoolCoalesce(), + ArgValidator_DictEthtoolRing(), ], default_value=ArgValidator.MISSING, ) @@ -1008,6 +1009,31 @@ class ArgValidator_DictEthtoolCoalesce(ArgValidatorDict): ) +class ArgValidator_DictEthtoolRing(ArgValidatorDict): + def __init__(self): + ArgValidatorDict.__init__( + self, + name="ring", + nested=[ + ArgValidatorNum( + "rx", val_min=0, val_max=UINT32_MAX, default_value=None + ), + ArgValidatorNum( + "rx_jumbo", val_min=0, val_max=UINT32_MAX, default_value=None + ), + ArgValidatorNum( + "rx_mini", val_min=0, val_max=UINT32_MAX, default_value=None + ), + ArgValidatorNum( + "tx", val_min=0, val_max=UINT32_MAX, default_value=None + ), + ], + ) + self.default_value = dict( + [(k, v.default_value) for k, v in self.nested.items()] + ) + + class ArgValidator_DictBond(ArgValidatorDict): VALID_MODES = [ diff --git a/module_utils/network_lsr/nm_provider.py b/module_utils/network_lsr/nm_provider.py index d6168eb..bf10279 100644 --- a/module_utils/network_lsr/nm_provider.py +++ b/module_utils/network_lsr/nm_provider.py @@ -10,6 +10,7 @@ from ansible.module_utils.network_lsr.utils import Util # noqa:E501 ETHTOOL_FEATURE_PREFIX = "ETHTOOL_OPTNAME_FEATURE_" ETHTOOL_COALESCE_PREFIX = "ETHTOOL_OPTNAME_COALESCE_" +ETHTOOL_RING_PREFIX = "ETHTOOL_OPTNAME_RING_" def get_nm_ethtool_feature(name): @@ -42,3 +43,18 @@ def get_nm_ethtool_coalesce(name): coalesce = getattr(Util.NM(), name, None) return coalesce + + +def get_nm_ethtool_ring(name): + """ + Translate ethtool ring option into NetworkManager attribute name + :param name: Name of the ring + :type name: str + :returns: Name of the setting to be used by `NM.SettingEthtool.set_ring()` + :rtype: str + """ + + name = ETHTOOL_RING_PREFIX + name.upper() + + ring = getattr(Util.NM(), name, None) + return ring diff --git a/tests/playbooks/tests_ethtool_ring.yml b/tests/playbooks/tests_ethtool_ring.yml new file mode 100644 index 0000000..00b0460 --- /dev/null +++ b/tests/playbooks/tests_ethtool_ring.yml @@ -0,0 +1,194 @@ +# SPDX-License-Identifier: BSD-3-Clause +--- +- hosts: all + vars: + interface: testnic1 + type: veth + tasks: + - debug: + msg: "this is: playbooks/tests_ethtool_ring.yml" + tags: + - always + + - name: "INIT: Ethtool ring tests" + debug: + msg: "##################################################" + - include_tasks: tasks/show_interfaces.yml + - include_tasks: tasks/manage_test_interface.yml + vars: + state: present + - include_tasks: tasks/assert_device_present.yml + - name: Install ethtool (test dependency) + package: + name: ethtool + state: present + + - block: + - name: >- + TEST: I can create a profile without any ring option. + debug: + msg: "##################################################" + - import_role: + name: linux-system-roles.network + vars: + network_connections: + - name: "{{ interface }}" + type: ethernet + state: up + ip: + dhcp4: no + auto6: no + + - name: Get profile's ring options + command: nmcli -g ethtool.ring-rx c show {{ interface }} + register: no_ring_nm + when: + - network_provider == "nm" + - name: "ASSERT: The profile does not contain ring options" + assert: + that: no_ring_nm.stdout | length == 0 + when: + - network_provider == "nm" + - name: Get profile's ring options + command: + grep ETHTOOL /etc/sysconfig/network-scripts/ifcfg-{{ interface }} + register: no_ring_initscripts + ignore_errors: true + when: + - network_provider == "initscripts" + - name: "ASSERT: The profile does not contain ring options" + assert: + that: no_ring_initscripts.stdout | length == 0 + when: + - network_provider == "initscripts" + + - name: >- + TEST: I can set rx. + debug: + msg: "##################################################" + - import_role: + name: linux-system-roles.network + vars: + network_connections: + - name: "{{ interface }}" + type: ethernet + state: up + ip: + dhcp4: no + auto6: no + ethtool: + ring: + rx: 128 + rx_jumbo: 128 + rx_mini: 128 + tx: 128 + + - name: Get profile's ethtool.ring-rx options + command: nmcli -g ethtool.ring-rx c show {{ interface }} + register: with_ring_rx + when: + - network_provider == "nm" + - name: Assert ethtool.ring-rx option set in profile + assert: + that: with_ring_rx.stdout == '128' + when: + - network_provider == "nm" + - name: Get profile's ethtool.ring-rx-jumbo options + command: nmcli -g ethtool.ring-rx-jumbo c show {{ interface }} + register: with_ring_rx_jumbo + when: + - network_provider == "nm" + - name: Assert ethtool.ring-rx-jumbo option set in profile + assert: + that: with_ring_rx_jumbo.stdout == '128' + when: + - network_provider == "nm" + - name: Get profile's ethtool.ring-rx-mini options + command: nmcli -g ethtool.ring-rx-mini c show {{ interface }} + register: with_ring_rx_mini + when: + - network_provider == "nm" + - name: Assert ethtool.ring-rx-mini option set in profile + assert: + that: with_ring_rx_mini.stdout == '128' + when: + - network_provider == "nm" + - name: Get profile's ethtool.ring-tx options + command: nmcli -g ethtool.ring-tx c show {{ interface }} + register: with_ring_tx + when: + - network_provider == "nm" + - name: Assert ethtool.ring-tx option set in profile + assert: + that: with_ring_tx.stdout == '128' + when: + - network_provider == "nm" + + - name: Get profile's ethtool.ring options + command: + grep ETHTOOL /etc/sysconfig/network-scripts/ifcfg-{{ interface }} + register: with_ring + ignore_errors: true + when: + - network_provider == "initscripts" + - name: Assert ethtool.ring option set in profile + assert: + that: + - '"rx 128" in with_ring.stdout' + - '"rx-jumbo 128" in with_ring.stdout' + - '"rx-mini 128" in with_ring.stdout' + - '"tx 128" in with_ring.stdout' + when: + - network_provider == "initscripts" + + - name: "TEST: I can clear ring options" + debug: + msg: "##################################################" + - import_role: + name: linux-system-roles.network + vars: + network_connections: + - name: "{{ interface }}" + type: ethernet + state: up + ip: + dhcp4: no + auto6: no + + - name: Get profile's ring options + command: nmcli -g ethtool.ring-rx c show {{ interface }} + register: clear_ring_nm + when: + - network_provider == "nm" + - name: "ASSERT: The profile does reset ring options" + assert: + that: clear_ring_nm.stdout | length == 0 + when: + - network_provider == "nm" + - name: Get profile's ring options + command: + grep ETHTOOL /etc/sysconfig/network-scripts/ifcfg-{{ interface }} + register: clear_ring_initscripts + ignore_errors: true + when: + - network_provider == "initscripts" + - name: "ASSERT: The profile does reset ring options" + assert: + that: clear_ring_initscripts.stdout | length == 0 + when: + - network_provider == "initscripts" + + always: + - block: + - import_role: + name: linux-system-roles.network + vars: + network_connections: + - name: "{{ interface }}" + persistent_state: absent + ignore_errors: true + - include_tasks: tasks/manage_test_interface.yml + vars: + state: absent + tags: + - "tests::cleanup" diff --git a/tests/tests_ethtool_ring_initscripts.yml b/tests/tests_ethtool_ring_initscripts.yml new file mode 100644 index 0000000..356ca2c --- /dev/null +++ b/tests/tests_ethtool_ring_initscripts.yml @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: BSD-3-Clause +# This file was generated by ensure_provider_tests.py +--- +- hosts: all + name: Run playbook 'playbooks/tests_ethtool_ring.yml' with initscripts as provider + tasks: + - name: Set network provider to 'initscripts' + set_fact: + network_provider: initscripts + tags: + - always + +- import_playbook: playbooks/tests_ethtool_ring.yml diff --git a/tests/tests_ethtool_ring_nm.yml b/tests/tests_ethtool_ring_nm.yml new file mode 100644 index 0000000..0a7ba83 --- /dev/null +++ b/tests/tests_ethtool_ring_nm.yml @@ -0,0 +1,37 @@ +# SPDX-License-Identifier: BSD-3-Clause +# This file was generated by ensure_provider_tests.py +--- +# set network provider and gather facts +- hosts: all + name: Run playbook 'playbooks/tests_ethtool_ring.yml' with nm as provider + tasks: + - name: Set network provider to 'nm' + set_fact: + network_provider: nm + tags: + - always + + - block: + - name: Install NetworkManager + package: + name: NetworkManager + state: present + - name: Get NetworkManager version + command: rpm -q --qf "%{version}" NetworkManager + args: + warn: false + register: NetworkManager_version + when: true + when: + - ansible_distribution_major_version != '6' + tags: + - always + + +# The test requires or should run with NetworkManager, therefore it cannot run +# on RHEL/CentOS 6 +- import_playbook: playbooks/tests_ethtool_ring.yml + when: + - ansible_distribution_major_version != '6' + + - NetworkManager_version.stdout is version('1.25.2', '>=') diff --git a/tests/unit/test_network_connections.py b/tests/unit/test_network_connections.py index a500292..1b24193 100644 --- a/tests/unit/test_network_connections.py +++ b/tests/unit/test_network_connections.py @@ -140,9 +140,18 @@ ETHTOOL_COALESCE_DEFAULTS = { "tx_usecs_low": None, } +ETHTOOL_RING_DEFAULTS = { + "rx": None, + "rx_jumbo": None, + "rx_mini": None, + "tx": None, +} + + ETHTOOL_DEFAULTS = { "features": ETHTOOL_FEATURES_DEFAULTS, "coalesce": ETHTOOL_COALESCE_DEFAULTS, + "ring": ETHTOOL_RING_DEFAULTS, } ETHERNET_DEFAULTS = {"autoneg": None, "duplex": None, "speed": 0}