From 5d52802001d4fef74fe2d47e436bfd52dda7617b Mon Sep 17 00:00:00 2001 From: Artem Chernyshev Date: Wed, 4 May 2022 16:44:12 +0300 Subject: [PATCH] feat: add more hardware information to the link status resources The new fields will be used by the network device selector. Signed-off-by: Artem Chernyshev --- .../pkg/controllers/network/link_status.go | 13 ++++ .../controllers/network/link_status_test.go | 55 +++++++++++++ pkg/machinery/nethelpers/device.go | 78 +++++++++++++++++++ .../resources/network/link_status.go | 3 + .../v1.1/learn-more/networking-resources.md | 31 ++++++++ 5 files changed, 180 insertions(+) create mode 100644 pkg/machinery/nethelpers/device.go diff --git a/internal/app/machined/pkg/controllers/network/link_status.go b/internal/app/machined/pkg/controllers/network/link_status.go index e7d43ab50..ca7b5d302 100644 --- a/internal/app/machined/pkg/controllers/network/link_status.go +++ b/internal/app/machined/pkg/controllers/network/link_status.go @@ -213,6 +213,19 @@ func (ctrl *LinkStatusController) reconcile(ctx context.Context, r controller.Ru status.Duplex = nethelpers.Duplex(ethtool.Unknown) } + var deviceInfo *nethelpers.DeviceInfo + + deviceInfo, err = nethelpers.GetDeviceInfo(link.Attributes.Name) + if err != nil { + logger.Warn("failure getting device information from /sys/class/net/*", zap.Error(err), zap.String("link", link.Attributes.Name)) + } + + if deviceInfo != nil { + status.BusPath = deviceInfo.BusPath + status.Driver = deviceInfo.Driver + status.PCIID = deviceInfo.PCIID + } + switch status.Kind { case network.LinkKindVLAN: if err = networkadapter.VLANSpec(&status.VLAN).Decode(link.Attributes.Info.Data); err != nil { diff --git a/internal/app/machined/pkg/controllers/network/link_status_test.go b/internal/app/machined/pkg/controllers/network/link_status_test.go index 4f8c85a57..f152f77f1 100644 --- a/internal/app/machined/pkg/controllers/network/link_status_test.go +++ b/internal/app/machined/pkg/controllers/network/link_status_test.go @@ -7,10 +7,12 @@ package network_test import ( "context" + "errors" "fmt" "log" "math/rand" "net" + "strings" "sync" "testing" "time" @@ -125,6 +127,59 @@ func (suite *LinkStatusSuite) assertNoInterface(id string) error { return nil } +func (suite *LinkStatusSuite) TestInterfaceHwInfo() { + errNoInterfaces := fmt.Errorf("no suitable interfaces found") + + err := retry.Constant(5*time.Second, retry.WithUnits(100*time.Millisecond)).Retry( + func() error { + resources, err := suite.state.List( + suite.ctx, + resource.NewMetadata(network.NamespaceName, network.LinkStatusType, "", resource.VersionUndefined), + ) + suite.Require().NoError(err) + + for _, res := range resources.Items { + spec := res.(*network.LinkStatus).TypedSpec() //nolint:errcheck,forcetypeassert + + if !spec.Physical() { + continue + } + + if spec.Type != nethelpers.LinkEther { + continue + } + + emptyFields := []string{} + + for key, value := range map[string]string{ + "hw addr": spec.HardwareAddr.String(), + "driver": spec.Driver, + "bus path": spec.BusPath, + "PCI id": spec.PCIID, + } { + if value == "" { + emptyFields = append(emptyFields, key) + } + } + + if len(emptyFields) > 0 { + return fmt.Errorf("the interface %s has the following fields empty: %s", res.Metadata().ID(), strings.Join(emptyFields, ", ")) + } + + return nil + } + + return retry.ExpectedError(errNoInterfaces) + }, + ) + + if errors.Is(err, errNoInterfaces) { + suite.T().Skip(err.Error()) + } + + suite.Require().NoError(err) +} + func (suite *LinkStatusSuite) TestLoopbackInterface() { suite.Assert().NoError( retry.Constant(10*time.Second, retry.WithUnits(100*time.Millisecond)).Retry( diff --git a/pkg/machinery/nethelpers/device.go b/pkg/machinery/nethelpers/device.go new file mode 100644 index 000000000..aabce630f --- /dev/null +++ b/pkg/machinery/nethelpers/device.go @@ -0,0 +1,78 @@ +// 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/. + +package nethelpers + +import ( + "bytes" + "io/ioutil" + "os" + "path/filepath" + "strings" +) + +// DeviceInfo contains device hardware information that can be read from /sys/. +type DeviceInfo struct { + BusPath string + PCIID string + Driver string +} + +// GetDeviceInfo get additional device information by reading /sys/ directory. +//nolint:gocyclo +func GetDeviceInfo(deviceName string) (*DeviceInfo, error) { + path := filepath.Join("/sys/class/net/", deviceName, "/device/") + + readFile := func(path string) (string, error) { + f, err := os.Open(path) + if err != nil { + return "", err + } + + res, err := ioutil.ReadAll(f) + if err != nil { + return "", err + } + + return string(bytes.TrimSpace(res)), nil + } + + _, err := os.Stat(path) + if err != nil { + if os.IsNotExist(err) { + return &DeviceInfo{}, nil + } + + return nil, err + } + + ueventContents, err := readFile(filepath.Join(path, "uevent")) + if err != nil { + return nil, err + } + + if ueventContents == "" { + return &DeviceInfo{}, nil + } + + device := &DeviceInfo{} + + for _, line := range strings.Split(ueventContents, "\n") { + key, value, found := strings.Cut(line, "=") + if !found { + continue + } + + switch key { + case "DRIVER": + device.Driver = value + case "PCI_ID": + device.PCIID = value + case "PCI_SLOT_NAME": + device.BusPath = value + } + } + + return device, nil +} diff --git a/pkg/machinery/resources/network/link_status.go b/pkg/machinery/resources/network/link_status.go index 8cc51b45c..644d252b1 100644 --- a/pkg/machinery/resources/network/link_status.go +++ b/pkg/machinery/resources/network/link_status.go @@ -34,6 +34,9 @@ type LinkStatusSpec struct { OperationalState nethelpers.OperationalState `yaml:"operationalState"` Kind string `yaml:"kind"` SlaveKind string `yaml:"slaveKind"` + BusPath string `yaml:"busPath,omitempty"` + PCIID string `yaml:"pciID,omitempty"` + Driver string `yaml:"driver,omitempty"` // Fields coming from ethtool API. LinkState bool `yaml:"linkState"` SpeedMegabits int `yaml:"speedMbit,omitempty"` diff --git a/website/content/v1.1/learn-more/networking-resources.md b/website/content/v1.1/learn-more/networking-resources.md index 5f664ae4e..df025a493 100644 --- a/website/content/v1.1/learn-more/networking-resources.md +++ b/website/content/v1.1/learn-more/networking-resources.md @@ -99,6 +99,37 @@ NODE NAMESPACE TYPE ID VERSION RESOLVERS 172.20.0.2 network ResolverStatus resolvers 2 ["8.8.8.8","1.1.1.1"] ``` +```sh +$ talosctl get links -o yaml +node: 172.20.0.2 +metadata: + namespace: network + type: LinkStatuses.net.talos.dev + id: eth0 + version: 2 + owner: network.LinkStatusController + phase: running + created: 2021-06-29T20:23:18Z + updated: 2021-06-29T20:23:18Z +spec: + index: 4 + type: ether + linkIndex: 0 + flags: UP,BROADCAST,RUNNING,MULTICAST,LOWER_UP + hardwareAddr: 4e:95:8e:8f:e4:47 + broadcastAddr: ff:ff:ff:ff:ff:ff + mtu: 1500 + queueDisc: pfifo_fast + operationalState: up + kind: "" + slaveKind: "" + driver: virtio_net + linkState: true + speedMbit: 4294967295 + port: Other + duplex: Unknown +``` + ## Inspecting Configuration The desired networking configuration is combined from multiple sources and presented