mirror of
https://github.com/siderolabs/talos.git
synced 2025-08-08 07:37:06 +02:00
Support showing current feature state, and changing features on the fly. The output and interface should be similar to `ethtool`. We don't support legacy feature names. ``` node: 172.20.0.5 metadata: namespace: network type: EthernetStatuses.net.talos.dev id: enp0s2 version: 2 owner: network.EthernetStatusController phase: running created: 2025-02-10T11:40:32Z updated: 2025-02-10T11:40:32Z spec: linkState: true port: Other duplex: Unknown rings: rx-max: 256 tx-max: 256 rx: 256 tx: 256 tx-push: false rx-push: false features: tx-scatter-gather: on tx-checksum-ipv4: off [fixed] tx-checksum-ip-generic: on tx-checksum-ipv6: off [fixed] highdma: on [fixed] tx-scatter-gather-fraglist: off [fixed] tx-vlan-hw-insert: off [fixed] rx-vlan-hw-parse: off [fixed] rx-vlan-filter: on [fixed] vlan-challenged: off [fixed] tx-generic-segmentation: on rx-gro: on rx-lro: off [fixed] tx-tcp-segmentation: on tx-gso-robust: on [fixed] tx-tcp-ecn-segmentation: on tx-tcp-mangleid-segmentation: off tx-tcp6-segmentation: on tx-fcoe-segmentation: off [fixed] tx-gre-segmentation: off [fixed] tx-gre-csum-segmentation: off [fixed] tx-ipxip4-segmentation: off [fixed] tx-ipxip6-segmentation: off [fixed] tx-udp_tnl-segmentation: off [fixed] tx-udp_tnl-csum-segmentation: off [fixed] tx-gso-partial: off [fixed] tx-tunnel-remcsum-segmentation: off [fixed] tx-sctp-segmentation: off [fixed] tx-esp-segmentation: off [fixed] tx-udp-segmentation: off tx-gso-list: off [fixed] tx-checksum-fcoe-crc: off [fixed] tx-checksum-sctp: off [fixed] rx-ntuple-filter: off [fixed] rx-hashing: off [fixed] rx-checksum: on [fixed] tx-nocache-copy: off loopback: off [fixed] rx-fcs: off [fixed] rx-all: off [fixed] tx-vlan-stag-hw-insert: off [fixed] rx-vlan-stag-hw-parse: off [fixed] rx-vlan-stag-filter: off [fixed] l2-fwd-offload: off [fixed] hw-tc-offload: off [fixed] esp-hw-offload: off [fixed] esp-tx-csum-hw-offload: off [fixed] rx-udp_tunnel-port-offload: off [fixed] tls-hw-tx-offload: off [fixed] tls-hw-rx-offload: off [fixed] rx-gro-hw: on tls-hw-record: off [fixed] rx-gro-list: off macsec-hw-offload: off [fixed] rx-udp-gro-forwarding: off hsr-tag-ins-offload: off [fixed] hsr-tag-rm-offload: off [fixed] hsr-fwd-offload: off [fixed] hsr-dup-offload: off [fixed] ``` Signed-off-by: Andrey Smirnov <andrey.smirnov@siderolabs.com>
200 lines
6.1 KiB
Go
200 lines
6.1 KiB
Go
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
|
|
//go:build integration_api
|
|
|
|
package api
|
|
|
|
import (
|
|
"context"
|
|
"os"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/cosi-project/runtime/pkg/resource"
|
|
"github.com/cosi-project/runtime/pkg/resource/rtestutils"
|
|
"github.com/cosi-project/runtime/pkg/safe"
|
|
"github.com/siderolabs/go-pointer"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"gopkg.in/yaml.v3"
|
|
|
|
"github.com/siderolabs/talos/internal/integration/base"
|
|
"github.com/siderolabs/talos/pkg/machinery/client"
|
|
networkconfig "github.com/siderolabs/talos/pkg/machinery/config/types/network"
|
|
"github.com/siderolabs/talos/pkg/machinery/resources/network"
|
|
)
|
|
|
|
// EthernetSuite ...
|
|
type EthernetSuite struct {
|
|
base.APISuite
|
|
|
|
ctx context.Context //nolint:containedctx
|
|
ctxCancel context.CancelFunc
|
|
}
|
|
|
|
// SuiteName ...
|
|
func (suite *EthernetSuite) SuiteName() string {
|
|
return "api.EthernetSuite"
|
|
}
|
|
|
|
// SetupTest ...
|
|
func (suite *EthernetSuite) SetupTest() {
|
|
suite.ctx, suite.ctxCancel = context.WithTimeout(context.Background(), 1*time.Minute)
|
|
|
|
if suite.Cluster == nil || suite.Cluster.Provisioner() != base.ProvisionerQEMU {
|
|
suite.T().Skip("skipping ethernet test since provisioner is not qemu")
|
|
}
|
|
}
|
|
|
|
// TearDownTest ...
|
|
func (suite *EthernetSuite) TearDownTest() {
|
|
if suite.ctxCancel != nil {
|
|
suite.ctxCancel()
|
|
}
|
|
}
|
|
|
|
func getFeatureStatus(t *testing.T, features network.EthernetFeatureStatusList, name string) bool {
|
|
t.Helper()
|
|
|
|
for _, f := range features {
|
|
if f.Name == name {
|
|
switch f.Status {
|
|
case "on":
|
|
return true
|
|
case "off":
|
|
return false
|
|
default:
|
|
require.Fail(t, "unexpected feature status: %s", f.Status)
|
|
}
|
|
}
|
|
}
|
|
|
|
require.Fail(t, "feature %s not found", name)
|
|
|
|
panic("unreachable")
|
|
}
|
|
|
|
// TestEthernetConfig verifies changing Ethernet settings.
|
|
func (suite *EthernetSuite) TestEthernetConfig() {
|
|
// pick up a random node to test the Ethernet on, and use it throughout the test
|
|
node := suite.RandomDiscoveredNodeInternalIP()
|
|
|
|
suite.T().Logf("testing Ethernet on node %s", node)
|
|
|
|
// build a Talos API context which is tied to the node
|
|
nodeCtx := client.WithNode(suite.ctx, node)
|
|
|
|
// pick a Ethernet links
|
|
ethStatuses, err := safe.StateListAll[*network.EthernetStatus](nodeCtx, suite.Client.COSI)
|
|
suite.Require().NoError(err)
|
|
|
|
var (
|
|
linkName string
|
|
prevRingConfig *network.EthernetRingsStatus
|
|
prevFeatures network.EthernetFeatureStatusList
|
|
)
|
|
|
|
for ethStatus := range ethStatuses.All() {
|
|
if ethStatus.TypedSpec().Rings != nil && ethStatus.TypedSpec().Rings.RXMax != nil {
|
|
linkName = ethStatus.Metadata().ID()
|
|
prevRingConfig = ethStatus.TypedSpec().Rings
|
|
prevFeatures = ethStatus.TypedSpec().Features
|
|
|
|
marshaled, err := resource.MarshalYAML(ethStatus)
|
|
suite.Require().NoError(err)
|
|
|
|
out, err := yaml.Marshal(marshaled)
|
|
suite.Require().NoError(err)
|
|
|
|
suite.T().Logf("found link %s with: %s", linkName, string(out))
|
|
|
|
break
|
|
}
|
|
}
|
|
|
|
suite.Require().NotEmpty(linkName, "no link provides RX rings")
|
|
suite.Require().NotEmpty(prevFeatures, "no link provides features")
|
|
|
|
suite.Run("Rings", func() {
|
|
if os.Getenv("CI") != "" {
|
|
suite.T().Skip("skipping ethtool test in CI, as QEMU version doesn't support updating RX rings for virtio")
|
|
}
|
|
|
|
// first, adjust RX rings to be 50% of what it was before
|
|
newRX := pointer.SafeDeref(prevRingConfig.RXMax) / 2
|
|
|
|
suite.T().Logf("testing RX rings on link %s: %d -> %d", linkName, pointer.SafeDeref(prevRingConfig.RX), newRX)
|
|
|
|
cfgDocument := networkconfig.NewEthernetConfigV1Alpha1(linkName)
|
|
cfgDocument.RingsConfig = &networkconfig.EthernetRingsConfig{
|
|
RX: pointer.To(newRX),
|
|
}
|
|
suite.PatchMachineConfig(nodeCtx, cfgDocument)
|
|
|
|
// now EthernetStatus should reflect the new RX rings
|
|
rtestutils.AssertResource(nodeCtx, suite.T(), suite.Client.COSI, linkName,
|
|
func(ethStatus *network.EthernetStatus, asrt *assert.Assertions) {
|
|
asrt.Equal(newRX, pointer.SafeDeref(ethStatus.TypedSpec().Rings.RX))
|
|
},
|
|
)
|
|
|
|
// now, let's revert the RX rings to what it was before
|
|
cfgDocument.RingsConfig.RX = prevRingConfig.RX
|
|
|
|
suite.PatchMachineConfig(nodeCtx, cfgDocument)
|
|
|
|
// now EthernetStatus should reflect the new RX rings
|
|
rtestutils.AssertResource(nodeCtx, suite.T(), suite.Client.COSI, linkName,
|
|
func(ethStatus *network.EthernetStatus, asrt *assert.Assertions) {
|
|
asrt.Equal(pointer.SafeDeref(prevRingConfig.RX), pointer.SafeDeref(ethStatus.TypedSpec().Rings.RX))
|
|
},
|
|
)
|
|
|
|
// remove the config document
|
|
suite.RemoveMachineConfigDocuments(nodeCtx, cfgDocument.MetaKind)
|
|
})
|
|
|
|
suite.Run("Features", func() {
|
|
const featureName = "tx-tcp-segmentation"
|
|
|
|
// get the initial state
|
|
initialState := getFeatureStatus(suite.T(), prevFeatures, featureName)
|
|
|
|
suite.T().Logf("testing feature %s on link %s: %v -> %v", featureName, linkName, initialState, !initialState)
|
|
|
|
cfgDocument := networkconfig.NewEthernetConfigV1Alpha1(linkName)
|
|
cfgDocument.FeaturesConfig = map[string]bool{
|
|
featureName: !initialState,
|
|
}
|
|
suite.PatchMachineConfig(nodeCtx, cfgDocument)
|
|
|
|
// now EthernetStatus should reflect the new feature status
|
|
rtestutils.AssertResource(nodeCtx, suite.T(), suite.Client.COSI, linkName,
|
|
func(ethStatus *network.EthernetStatus, asrt *assert.Assertions) {
|
|
asrt.Equal(!initialState, getFeatureStatus(suite.T(), ethStatus.TypedSpec().Features, featureName))
|
|
},
|
|
)
|
|
|
|
// now, let's revert the RX rings to what it was before
|
|
cfgDocument.FeaturesConfig[featureName] = initialState
|
|
|
|
suite.PatchMachineConfig(nodeCtx, cfgDocument)
|
|
|
|
// now EthernetStatus should reflect the old feature status
|
|
rtestutils.AssertResource(nodeCtx, suite.T(), suite.Client.COSI, linkName,
|
|
func(ethStatus *network.EthernetStatus, asrt *assert.Assertions) {
|
|
asrt.Equal(initialState, getFeatureStatus(suite.T(), ethStatus.TypedSpec().Features, featureName))
|
|
},
|
|
)
|
|
|
|
// remove the config document
|
|
suite.RemoveMachineConfigDocuments(nodeCtx, cfgDocument.MetaKind)
|
|
})
|
|
}
|
|
|
|
func init() {
|
|
allSuites = append(allSuites, new(EthernetSuite))
|
|
}
|