Skip to content

Commit

Permalink
feat: support network bond device selectors
Browse files Browse the repository at this point in the history
Fixes #6756

Signed-off-by: Thomas Way <thomas@6f.io>
Signed-off-by: Andrey Smirnov <andrey.smirnov@talos-systems.com>
  • Loading branch information
uhthomas authored and smira committed Mar 31, 2023
1 parent cbab12e commit 7ffabe0
Show file tree
Hide file tree
Showing 19 changed files with 452 additions and 166 deletions.
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ require (
github.com/BurntSushi/toml v1.2.1
github.com/aws/aws-sdk-go v1.44.232
github.com/beevik/ntp v0.3.0
github.com/benbjohnson/clock v1.1.0
github.com/cenkalti/backoff/v4 v4.2.0
github.com/containerd/cgroups v1.1.0
github.com/containerd/containerd v1.6.19
Expand All @@ -40,7 +41,7 @@ require (
github.com/containernetworking/plugins v1.2.0
github.com/coreos/go-iptables v0.6.0
github.com/coreos/go-semver v0.3.1
github.com/cosi-project/runtime v0.3.0-alpha.10
github.com/cosi-project/runtime v0.3.0
github.com/docker/distribution v2.8.1+incompatible
github.com/docker/docker v23.0.2+incompatible
github.com/docker/go-connections v0.4.0
Expand Down Expand Up @@ -155,7 +156,6 @@ require (
github.com/ProtonMail/gopenpgp/v2 v2.5.2 // indirect
github.com/adrg/xdg v0.4.0 // indirect
github.com/armon/circbuf v0.0.0-20190214190532-5111143e8da2 // indirect
github.com/benbjohnson/clock v1.1.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect
github.com/blang/semver/v4 v4.0.0 // indirect
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -456,8 +456,8 @@ github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cosi-project/runtime v0.3.0-alpha.10 h1:WMk620imAuRTDeEPkwyAXu6lR59FdJNq9CpaWu5lsU0=
github.com/cosi-project/runtime v0.3.0-alpha.10/go.mod h1:BBFzt+ANf4BSlVGIbemVuz+Hql8F3tveWaWoErUi0lY=
github.com/cosi-project/runtime v0.3.0 h1:f8++A7HUu7pQv9G3IhQworfA4TFLdzGWl3W+jLQF3Oo=
github.com/cosi-project/runtime v0.3.0/go.mod h1:BBFzt+ANf4BSlVGIbemVuz+Hql8F3tveWaWoErUi0lY=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
Expand Down
17 changes: 17 additions & 0 deletions hack/release.toml
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,23 @@ Example:
```
talos.environment=http_proxy=http://proxy.example.com:8080 talos.environment=https_proxy=http://proxy.example.com:8080
```
"""

[notes.bonding]
title = "Bond Device Selectors"
description="""\
Bond links can now be described using device selectors instead of explicit device names:
```yaml
machine:
network:
interfaces:
- interface: bond0
bond:
deviceSelectors:
- hardwareAddr: '00:50:56:*'
- hardwareAddr: '00:50:57:9c:2c:2d'
```
"""

[make_deps]
Expand Down
93 changes: 75 additions & 18 deletions internal/app/machined/pkg/controllers/network/device_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@ import (

"github.com/cosi-project/runtime/pkg/controller"
"github.com/cosi-project/runtime/pkg/resource"
"github.com/cosi-project/runtime/pkg/safe"
"github.com/cosi-project/runtime/pkg/state"
glob "github.com/ryanuber/go-glob"
"github.com/siderolabs/gen/slices"
"github.com/siderolabs/go-pointer"
"go.uber.org/zap"

Expand Down Expand Up @@ -79,7 +81,7 @@ func (ctrl *DeviceConfigController) Run(ctx context.Context, r controller.Runtim
case <-r.EventCh():
}

links, err := r.List(ctx, resource.NewMetadata(network.NamespaceName, network.LinkStatusType, "", resource.VersionUndefined))
links, err := safe.ReaderList[*network.LinkStatus](ctx, r, resource.NewMetadata(network.NamespaceName, network.LinkStatusType, "", resource.VersionUndefined))
if err != nil {
return err
}
Expand All @@ -88,23 +90,24 @@ func (ctrl *DeviceConfigController) Run(ctx context.Context, r controller.Runtim

var cfgProvider talosconfig.Provider

cfg, err := r.Get(ctx, resource.NewMetadata(config.NamespaceName, config.MachineConfigType, config.V1Alpha1ID, resource.VersionUndefined))
cfg, err := safe.ReaderGet[*config.MachineConfig](ctx, r, resource.NewMetadata(config.NamespaceName, config.MachineConfigType, config.V1Alpha1ID, resource.VersionUndefined))
if err != nil {
if !state.IsNotFoundError(err) {
return fmt.Errorf("error getting config: %w", err)
}
} else {
cfgProvider = cfg.(*config.MachineConfig).Config()
cfgProvider = cfg.Config()
}

if cfgProvider != nil {
selectedInterfaces := map[string]struct{}{}

for index, device := range cfgProvider.Machine().Network().Devices() {
if device.Selector() != nil {
device = device.(*v1alpha1.Device).DeepCopy()
dev := device.(*v1alpha1.Device).DeepCopy()
device = dev

err = ctrl.getDeviceBySelector(device, links.Items)
err = ctrl.getDeviceBySelector(dev, links)
if err != nil {
logger.Warn("failed to select an interface for a device", zap.Error(err))

Expand All @@ -118,7 +121,19 @@ func (ctrl *DeviceConfigController) Run(ctx context.Context, r controller.Runtim
selectedInterfaces[device.Interface()] = struct{}{}
}

id := fmt.Sprintf("%s/%d", device.Interface(), index)
if device.Bond() != nil && len(device.Bond().Selectors()) > 0 {
dev := device.(*v1alpha1.Device).DeepCopy()
device = dev

err = ctrl.expandBondSelector(dev, links)
if err != nil {
logger.Warn("failed to select interfaces for a bond device", zap.Error(err))

continue
}
}

id := fmt.Sprintf("%s/%03d", device.Interface(), index)

touchedIDs[id] = struct{}{}

Expand Down Expand Up @@ -156,13 +171,58 @@ func (ctrl *DeviceConfigController) Run(ctx context.Context, r controller.Runtim
}
}

func (ctrl *DeviceConfigController) getDeviceBySelector(device talosconfig.Device, links []resource.Resource) error {
func (ctrl *DeviceConfigController) getDeviceBySelector(device *v1alpha1.Device, links safe.List[*network.LinkStatus]) error {
selector := device.Selector()

for _, link := range links {
linkStatus := link.(*network.LinkStatus).TypedSpec() //nolint:forcetypeassert,errcheck
matches := ctrl.selectDevices(selector, links)
if len(matches) == 0 {
return fmt.Errorf("no matching network device for defined selector: %+v", selector)
}

link := matches[0]

device.DeviceInterface = link.Metadata().ID()

return nil
}

func (ctrl *DeviceConfigController) expandBondSelector(device *v1alpha1.Device, links safe.List[*network.LinkStatus]) error {
var matches []*network.LinkStatus

for _, selector := range device.Bond().Selectors() {
matches = append(matches,
// filter out bond device itself, as it will inherit the MAC address of the first link
slices.Filter(
ctrl.selectDevices(selector, links),
func(link *network.LinkStatus) bool {
return link.Metadata().ID() != device.Interface()
})...)
}

device.DeviceBond.BondInterfaces = slices.Map(matches, func(link *network.LinkStatus) string { return link.Metadata().ID() })

if len(device.DeviceBond.BondInterfaces) == 0 {
return fmt.Errorf("no matching network device for defined bond selectors: %v",
slices.Map(device.Bond().Selectors(),
func(selector talosconfig.NetworkDeviceSelector) string {
return fmt.Sprintf("%+v", selector)
},
),
)
}

device.DeviceBond.BondDeviceSelectors = nil

matches := false
return nil
}

func (ctrl *DeviceConfigController) selectDevices(selector talosconfig.NetworkDeviceSelector, links safe.List[*network.LinkStatus]) []*network.LinkStatus {
var result []*network.LinkStatus

for iter := safe.IteratorFromList(links); iter.Next(); {
linkStatus := iter.Value().TypedSpec()

match := false

for _, pair := range [][]string{
{selector.HardwareAddress(), linkStatus.HardwareAddr.String()},
Expand All @@ -175,21 +235,18 @@ func (ctrl *DeviceConfigController) getDeviceBySelector(device talosconfig.Devic
}

if !glob.Glob(pair[0], pair[1]) {
matches = false
match = false

break
}

matches = true
match = true
}

if matches {
dev := device.(*v1alpha1.Device) //nolint:errcheck,forcetypeassert
dev.DeviceInterface = link.Metadata().ID()

return nil
if match {
result = append(result, iter.Value())
}
}

return fmt.Errorf("no matching network device for defined selector: %v", selector)
return result
}
Loading

0 comments on commit 7ffabe0

Please sign in to comment.