ethtool: Add support of ring

Example:
```yml
---
interfaces:
  - name: enp7s0
    state: up
    ethtool:
      ring:
        rx: 256
        rx-jumbo: 4096
        rx-mini: 4096
        tx: 256
```

The netdevsim in fedora 34 support ring, but will lose ring info after
making changes via NM(the reason is unknown yet).

Integration test case included, but require user to define
`TEST_REAL_NIC` variable to run it. Tested on `virtio_net` NIC.

Unit test case included.

Signed-off-by: Gris Ge <fge@redhat.com>
This commit is contained in:
Gris Ge 2021-05-25 14:26:48 +08:00
parent 411cd6a5ab
commit 1ea97bfddc
8 changed files with 170 additions and 0 deletions

View File

@ -36,6 +36,11 @@ class IfaceEthtool:
self._feature = IfaceEthtoolFeature(
self._info[Ethtool.Feature.CONFIG_SUBTREE]
)
self._ring = None
if self._info.get(Ethtool.Ring.CONFIG_SUBTREE):
self._ring = IfaceEthtoolRing(
self._info[Ethtool.Ring.CONFIG_SUBTREE]
)
@property
def pause(self):
@ -45,6 +50,10 @@ class IfaceEthtool:
def feature(self):
return self._feature
@property
def ring(self):
return self._ring
def canonicalize(self, original_desire):
if self.pause:
self.pause.canonicalize(
@ -54,6 +63,10 @@ class IfaceEthtool:
self.feature.canonicalize(
original_desire.get(Ethtool.Feature.CONFIG_SUBTREE, {})
)
if self.ring:
self.ring.canonicalize(
original_desire.get(Ethtool.Ring.CONFIG_SUBTREE, {})
)
def to_dict(self):
info = {}
@ -61,6 +74,8 @@ class IfaceEthtool:
info[Ethtool.Pause.CONFIG_SUBTREE] = self.pause.to_dict()
if self.feature:
info[Ethtool.Feature.CONFIG_SUBTREE] = self.feature.to_dict()
if self.ring:
info[Ethtool.Ring.CONFIG_SUBTREE] = self.ring.to_dict()
return info
@ -147,3 +162,14 @@ class IfaceEthtoolFeature:
def to_dict(self):
return deepcopy(self._info)
class IfaceEthtoolRing:
def __init__(self, ring_info):
self._info = ring_info
def canonicalize(self, _original_desire):
pass
def to_dict(self):
return deepcopy(self._info)

View File

@ -185,4 +185,22 @@ class EthtoolInfo:
np_features = self._np_ethtool.features
if np_features:
info[Ethtool.Feature.CONFIG_SUBTREE] = np_features.changeable
np_ring = self._np_ethtool.ring
if np_ring:
ring_info = {}
if np_ring.tx is not None:
ring_info[Ethtool.Ring.TX] = np_ring.tx
if np_ring.rx is not None:
ring_info[Ethtool.Ring.RX] = np_ring.rx
if np_ring.rx_jumbo is not None:
ring_info[Ethtool.Ring.RX_JUMBO] = np_ring.rx_jumbo
if np_ring.rx_mini is not None:
ring_info[Ethtool.Ring.RX_MINI] = np_ring.rx_mini
if ring_info:
info[Ethtool.Ring.CONFIG_SUBTREE] = ring_info
return info

View File

@ -22,6 +22,16 @@ from libnmstate.error import NmstateValueError
from .common import NM
from .common import GLib
from libnmstate.schema import Ethtool
_NM_RING_OPT_NAME_MAP = {
Ethtool.Ring.RX: NM.ETHTOOL_OPTNAME_RING_RX,
Ethtool.Ring.RX_JUMBO: NM.ETHTOOL_OPTNAME_RING_RX_JUMBO,
Ethtool.Ring.RX_MINI: NM.ETHTOOL_OPTNAME_RING_RX_MINI,
Ethtool.Ring.TX: NM.ETHTOOL_OPTNAME_RING_TX,
}
def create_ethtool_setting(iface_ethtool, base_con_profile):
nm_setting = None
@ -51,6 +61,15 @@ def create_ethtool_setting(iface_ethtool, base_con_profile):
for kernel_feature_name, value in iface_ethtool.feature.items():
nm_set_feature(nm_setting, kernel_feature_name, value)
if iface_ethtool.ring:
ring_info = iface_ethtool.ring.to_dict()
for prop_name, nm_prop_name in _NM_RING_OPT_NAME_MAP.items():
if prop_name in ring_info:
nm_setting.option_set(
nm_prop_name,
GLib.Variant.new_uint32(ring_info[prop_name]),
)
return nm_setting

View File

@ -473,3 +473,10 @@ class Ethtool:
class Feature:
CONFIG_SUBTREE = "feature"
class Ring:
CONFIG_SUBTREE = "ring"
RX = "rx"
RX_JUMBO = "rx-jumbo"
RX_MINI = "rx-mini"
TX = "tx"

View File

@ -640,6 +640,21 @@ definitions:
type: object
additionalProperties:
type: boolean
ring:
type: object
properties:
tx:
type: integer
minimum: 0
rx:
type: integer
minimum: 0
rx-jumbo:
type: integer
minimum: 0
rx-mini:
type: integer
minimum: 0
interface-team:
rw:
properties:

View File

@ -125,3 +125,19 @@ def test_ethtool_invalid_feature(eth1_up):
}
with pytest.raises(NmstateValueError):
libnmstate.apply({Interface.KEY: [desire_iface_state]})
@pytest.mark.skipif(
not os.environ.get("TEST_REAL_NIC"),
reason="Need to define TEST_REAL_NIC for ethtool ring test",
)
def test_ethtool_ring_set_rx():
desire_iface_state = {
Interface.NAME: os.environ.get("TEST_REAL_NIC"),
Ethtool.CONFIG_SUBTREE: {
Ethtool.Ring.CONFIG_SUBTREE: {Ethtool.Ring.RX: 256}
},
}
libnmstate.apply({Interface.KEY: [desire_iface_state]})
assertlib.assert_state_match({Interface.KEY: [desire_iface_state]})

View File

@ -90,3 +90,31 @@ def test_create_setting_pause_autoneg_off(nm_mock):
],
any_order=False,
)
def test_create_setting_ring(nm_mock):
iface_ethtool = IfaceEthtool(
{
Ethtool.Ring.CONFIG_SUBTREE: {
Ethtool.Ring.RX: 256,
Ethtool.Ring.TX: 1024,
}
}
)
nm_ethtool_setting_mock = nm_mock.SettingEthtool.new.return_value
nm_ethtool.create_ethtool_setting(iface_ethtool, base_con_profile=None)
nm_ethtool_setting_mock.option_set.assert_has_calls(
[
mock.call(
"ring-rx",
GLib.Variant.new_uint32(256),
),
mock.call(
"ring-tx",
GLib.Variant.new_uint32(1024),
),
],
any_order=True,
)

View File

@ -1068,3 +1068,44 @@ class TestEthtool:
)
with pytest.raises(js.ValidationError):
libnmstate.validator.schema_validate(default_data)
def test_valid_ethtool_ring(self, default_data):
default_data[Interface.KEY][0].update(
{
Ethtool.CONFIG_SUBTREE: {
Ethtool.Ring.CONFIG_SUBTREE: {
Ethtool.Ring.RX: 256,
Ethtool.Ring.RX_JUMBO: 4096,
Ethtool.Ring.RX_MINI: 256,
Ethtool.Ring.TX: 256,
}
}
}
)
libnmstate.validator.schema_validate(default_data)
def test_invalid_ethtool_ring_out_of_range(self, default_data):
default_data[Interface.KEY][0].update(
{
Ethtool.CONFIG_SUBTREE: {
Ethtool.Ring.CONFIG_SUBTREE: {
Ethtool.Ring.RX: -1,
}
}
}
)
with pytest.raises(js.ValidationError):
libnmstate.validator.schema_validate(default_data)
def test_invalid_ethtool_ring_not_integer(self, default_data):
default_data[Interface.KEY][0].update(
{
Ethtool.CONFIG_SUBTREE: {
Ethtool.Ring.CONFIG_SUBTREE: {
Ethtool.Ring.RX: False,
}
}
}
)
with pytest.raises(js.ValidationError):
libnmstate.validator.schema_validate(default_data)