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 <artem.chernyshev@talos-systems.com>
This commit is contained in:
Artem Chernyshev 2022-05-04 16:44:12 +03:00
parent 2ff6db749a
commit 5d52802001
No known key found for this signature in database
GPG Key ID: 9B9D0328B57B443F
5 changed files with 180 additions and 0 deletions

View File

@ -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 {

View File

@ -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(

View File

@ -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
}

View File

@ -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"`

View File

@ -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