mirror of
https://github.com/siderolabs/talos.git
synced 2025-12-19 08:21:13 +01:00
feat: add bond slaves ordering
Before this change, we didn't preserve bonded interfaces ordering, which caused problems in some scenarios. Fix this by remembering their position in the original config. Fixes #5207. Signed-off-by: Dmitriy Matrenichev <dmitry.matrenichev@siderolabs.com>
This commit is contained in:
parent
03ef62ad8b
commit
867d38f28f
@ -16,6 +16,7 @@ import (
|
|||||||
|
|
||||||
"github.com/talos-systems/talos/pkg/machinery/config/types/v1alpha1"
|
"github.com/talos-systems/talos/pkg/machinery/config/types/v1alpha1"
|
||||||
"github.com/talos-systems/talos/pkg/machinery/constants"
|
"github.com/talos-systems/talos/pkg/machinery/constants"
|
||||||
|
"github.com/talos-systems/talos/pkg/machinery/ordered"
|
||||||
"github.com/talos-systems/talos/pkg/machinery/resources/network"
|
"github.com/talos-systems/talos/pkg/machinery/resources/network"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -244,13 +245,13 @@ func ParseCmdlineNetwork(cmdline *procfs.Cmdline) (CmdlineNetworking, error) {
|
|||||||
|
|
||||||
linkSpecSpecs = append(linkSpecSpecs, bondLinkSpec)
|
linkSpecSpecs = append(linkSpecSpecs, bondLinkSpec)
|
||||||
|
|
||||||
for _, slave := range bondSlaves {
|
for idx, slave := range bondSlaves {
|
||||||
slaveLinkSpec := network.LinkSpecSpec{
|
slaveLinkSpec := network.LinkSpecSpec{
|
||||||
Name: slave,
|
Name: slave,
|
||||||
Up: true,
|
Up: true,
|
||||||
ConfigLayer: network.ConfigCmdline,
|
ConfigLayer: network.ConfigCmdline,
|
||||||
}
|
}
|
||||||
SetBondSlave(&slaveLinkSpec, bondName)
|
SetBondSlave(&slaveLinkSpec, ordered.MakePair(bondName, idx))
|
||||||
linkSpecSpecs = append(linkSpecSpecs, slaveLinkSpec)
|
linkSpecSpecs = append(linkSpecSpecs, slaveLinkSpec)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -63,14 +63,20 @@ func (suite *CmdlineSuite) TestParse() {
|
|||||||
Up: true,
|
Up: true,
|
||||||
Logical: false,
|
Logical: false,
|
||||||
ConfigLayer: netconfig.ConfigCmdline,
|
ConfigLayer: netconfig.ConfigCmdline,
|
||||||
|
BondSlave: netconfig.BondSlave{
|
||||||
MasterName: "bond0",
|
MasterName: "bond0",
|
||||||
|
SlaveIndex: 0,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "eth1",
|
Name: "eth1",
|
||||||
Up: true,
|
Up: true,
|
||||||
Logical: false,
|
Logical: false,
|
||||||
ConfigLayer: netconfig.ConfigCmdline,
|
ConfigLayer: netconfig.ConfigCmdline,
|
||||||
|
BondSlave: netconfig.BondSlave{
|
||||||
MasterName: "bond0",
|
MasterName: "bond0",
|
||||||
|
SlaveIndex: 1,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -233,14 +239,20 @@ func (suite *CmdlineSuite) TestParse() {
|
|||||||
Up: true,
|
Up: true,
|
||||||
Logical: false,
|
Logical: false,
|
||||||
ConfigLayer: netconfig.ConfigCmdline,
|
ConfigLayer: netconfig.ConfigCmdline,
|
||||||
|
BondSlave: netconfig.BondSlave{
|
||||||
MasterName: "bond1",
|
MasterName: "bond1",
|
||||||
|
SlaveIndex: 0,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "eth4",
|
Name: "eth4",
|
||||||
Up: true,
|
Up: true,
|
||||||
Logical: false,
|
Logical: false,
|
||||||
ConfigLayer: netconfig.ConfigCmdline,
|
ConfigLayer: netconfig.ConfigCmdline,
|
||||||
|
BondSlave: netconfig.BondSlave{
|
||||||
MasterName: "bond1",
|
MasterName: "bond1",
|
||||||
|
SlaveIndex: 1,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -275,14 +287,20 @@ func (suite *CmdlineSuite) TestParse() {
|
|||||||
Up: true,
|
Up: true,
|
||||||
Logical: false,
|
Logical: false,
|
||||||
ConfigLayer: netconfig.ConfigCmdline,
|
ConfigLayer: netconfig.ConfigCmdline,
|
||||||
|
BondSlave: netconfig.BondSlave{
|
||||||
MasterName: "bond1",
|
MasterName: "bond1",
|
||||||
|
SlaveIndex: 0,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "eth4",
|
Name: "eth4",
|
||||||
Up: true,
|
Up: true,
|
||||||
Logical: false,
|
Logical: false,
|
||||||
ConfigLayer: netconfig.ConfigCmdline,
|
ConfigLayer: netconfig.ConfigCmdline,
|
||||||
|
BondSlave: netconfig.BondSlave{
|
||||||
MasterName: "bond1",
|
MasterName: "bond1",
|
||||||
|
SlaveIndex: 1,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@ -18,6 +18,7 @@ import (
|
|||||||
|
|
||||||
talosconfig "github.com/talos-systems/talos/pkg/machinery/config"
|
talosconfig "github.com/talos-systems/talos/pkg/machinery/config"
|
||||||
"github.com/talos-systems/talos/pkg/machinery/nethelpers"
|
"github.com/talos-systems/talos/pkg/machinery/nethelpers"
|
||||||
|
"github.com/talos-systems/talos/pkg/machinery/ordered"
|
||||||
"github.com/talos-systems/talos/pkg/machinery/resources/config"
|
"github.com/talos-systems/talos/pkg/machinery/resources/config"
|
||||||
"github.com/talos-systems/talos/pkg/machinery/resources/network"
|
"github.com/talos-systems/talos/pkg/machinery/resources/network"
|
||||||
)
|
)
|
||||||
@ -269,7 +270,7 @@ func (ctrl *LinkConfigController) parseCmdline(logger *zap.Logger) ([]network.Li
|
|||||||
//nolint:gocyclo
|
//nolint:gocyclo
|
||||||
func (ctrl *LinkConfigController) parseMachineConfiguration(logger *zap.Logger, cfgProvider talosconfig.Provider) []network.LinkSpecSpec {
|
func (ctrl *LinkConfigController) parseMachineConfiguration(logger *zap.Logger, cfgProvider talosconfig.Provider) []network.LinkSpecSpec {
|
||||||
// scan for the bonds
|
// scan for the bonds
|
||||||
bondedLinks := map[string]string{} // mapping physical interface -> bond interface
|
bondedLinks := map[string]ordered.Pair[string, int]{} // mapping physical interface -> bond interface
|
||||||
|
|
||||||
for _, device := range cfgProvider.Machine().Network().Devices() {
|
for _, device := range cfgProvider.Machine().Network().Devices() {
|
||||||
if device.Ignore() {
|
if device.Ignore() {
|
||||||
@ -280,12 +281,12 @@ func (ctrl *LinkConfigController) parseMachineConfiguration(logger *zap.Logger,
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, linkName := range device.Bond().Interfaces() {
|
for idx, linkName := range device.Bond().Interfaces() {
|
||||||
if bondName, exists := bondedLinks[linkName]; exists && bondName != device.Interface() {
|
if bondData, exists := bondedLinks[linkName]; exists && bondData.F1 != device.Interface() {
|
||||||
logger.Sugar().Warnf("link %q is included into more than two bonds", linkName)
|
logger.Sugar().Warnf("link %q is included into more than two bonds", linkName)
|
||||||
}
|
}
|
||||||
|
|
||||||
bondedLinks[linkName] = device.Interface()
|
bondedLinks[linkName] = ordered.MakePair(device.Interface(), idx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -331,7 +332,7 @@ func (ctrl *LinkConfigController) parseMachineConfiguration(logger *zap.Logger,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for slaveName, bondName := range bondedLinks {
|
for slaveName, bondData := range bondedLinks {
|
||||||
if _, exists := linkMap[slaveName]; !exists {
|
if _, exists := linkMap[slaveName]; !exists {
|
||||||
linkMap[slaveName] = &network.LinkSpecSpec{
|
linkMap[slaveName] = &network.LinkSpecSpec{
|
||||||
Name: slaveName,
|
Name: slaveName,
|
||||||
@ -340,7 +341,7 @@ func (ctrl *LinkConfigController) parseMachineConfiguration(logger *zap.Logger,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SetBondSlave(linkMap[slaveName], bondName)
|
SetBondSlave(linkMap[slaveName], bondData)
|
||||||
}
|
}
|
||||||
|
|
||||||
links := make([]network.LinkSpecSpec, 0, len(linkMap))
|
links := make([]network.LinkSpecSpec, 0, len(linkMap))
|
||||||
|
|||||||
@ -316,7 +316,7 @@ func (suite *LinkConfigSuite) TestMachineConfiguration() {
|
|||||||
case "eth2", "eth3":
|
case "eth2", "eth3":
|
||||||
suite.Assert().True(r.TypedSpec().Up)
|
suite.Assert().True(r.TypedSpec().Up)
|
||||||
suite.Assert().False(r.TypedSpec().Logical)
|
suite.Assert().False(r.TypedSpec().Logical)
|
||||||
suite.Assert().Equal("bond0", r.TypedSpec().MasterName)
|
suite.Assert().Equal("bond0", r.TypedSpec().BondSlave.MasterName)
|
||||||
case "bond0":
|
case "bond0":
|
||||||
suite.Assert().True(r.TypedSpec().Up)
|
suite.Assert().True(r.TypedSpec().Up)
|
||||||
suite.Assert().True(r.TypedSpec().Logical)
|
suite.Assert().True(r.TypedSpec().Logical)
|
||||||
|
|||||||
@ -7,6 +7,7 @@ package network
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"sort"
|
||||||
|
|
||||||
"github.com/AlekSi/pointer"
|
"github.com/AlekSi/pointer"
|
||||||
"github.com/cosi-project/runtime/pkg/controller"
|
"github.com/cosi-project/runtime/pkg/controller"
|
||||||
@ -20,6 +21,7 @@ import (
|
|||||||
networkadapter "github.com/talos-systems/talos/internal/app/machined/pkg/adapters/network"
|
networkadapter "github.com/talos-systems/talos/internal/app/machined/pkg/adapters/network"
|
||||||
"github.com/talos-systems/talos/internal/app/machined/pkg/controllers/network/watch"
|
"github.com/talos-systems/talos/internal/app/machined/pkg/controllers/network/watch"
|
||||||
"github.com/talos-systems/talos/pkg/machinery/nethelpers"
|
"github.com/talos-systems/talos/pkg/machinery/nethelpers"
|
||||||
|
"github.com/talos-systems/talos/pkg/machinery/ordered"
|
||||||
"github.com/talos-systems/talos/pkg/machinery/resources/network"
|
"github.com/talos-systems/talos/pkg/machinery/resources/network"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -111,6 +113,8 @@ func (ctrl *LinkSpecController) Run(ctx context.Context, r controller.Runtime, l
|
|||||||
// loop over links and make reconcile decision
|
// loop over links and make reconcile decision
|
||||||
var multiErr *multierror.Error
|
var multiErr *multierror.Error
|
||||||
|
|
||||||
|
SortBonds(list.Items)
|
||||||
|
|
||||||
for _, res := range list.Items {
|
for _, res := range list.Items {
|
||||||
link := res.(*network.LinkSpec) //nolint:forcetypeassert,errcheck
|
link := res.(*network.LinkSpec) //nolint:forcetypeassert,errcheck
|
||||||
|
|
||||||
@ -125,6 +129,27 @@ func (ctrl *LinkSpecController) Run(ctx context.Context, r controller.Runtime, l
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SortBonds sort resources in increasing order, except it places slave interfaces right after the bond
|
||||||
|
// in proper order.
|
||||||
|
func SortBonds(items []resource.Resource) {
|
||||||
|
sort.Slice(items, func(i, j int) bool {
|
||||||
|
left := items[i].Spec().(network.LinkSpecSpec) //nolint:errcheck
|
||||||
|
right := items[j].Spec().(network.LinkSpecSpec) //nolint:errcheck
|
||||||
|
|
||||||
|
l := ordered.MakeTriple(left.Name, 0, "")
|
||||||
|
if left.BondSlave.MasterName != "" {
|
||||||
|
l = ordered.MakeTriple(left.BondSlave.MasterName, left.BondSlave.SlaveIndex, left.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
r := ordered.MakeTriple(right.Name, 0, "")
|
||||||
|
if right.BondSlave.MasterName != "" {
|
||||||
|
r = ordered.MakeTriple(right.BondSlave.MasterName, right.BondSlave.SlaveIndex, right.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
return l.LessThan(r)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func findLink(links []rtnetlink.LinkMessage, name string) *rtnetlink.LinkMessage {
|
func findLink(links []rtnetlink.LinkMessage, name string) *rtnetlink.LinkMessage {
|
||||||
for i, link := range links {
|
for i, link := range links {
|
||||||
if link.Attributes.Name == name {
|
if link.Attributes.Name == name {
|
||||||
@ -341,7 +366,7 @@ func (ctrl *LinkSpecController) syncLink(ctx context.Context, r controller.Runti
|
|||||||
Master: pointer.ToUint32(0),
|
Master: pointer.ToUint32(0),
|
||||||
},
|
},
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return fmt.Errorf("error unslaving link %q under %q: %w", slave.Attributes.Name, link.TypedSpec().MasterName, err)
|
return fmt.Errorf("error unslaving link %q under %q: %w", slave.Attributes.Name, link.TypedSpec().BondSlave.MasterName, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
(*links)[i].Attributes.Master = nil
|
(*links)[i].Attributes.Master = nil
|
||||||
@ -448,8 +473,8 @@ func (ctrl *LinkSpecController) syncLink(ctx context.Context, r controller.Runti
|
|||||||
// sync master index (for links which are bond slaves)
|
// sync master index (for links which are bond slaves)
|
||||||
var masterIndex uint32
|
var masterIndex uint32
|
||||||
|
|
||||||
if link.TypedSpec().MasterName != "" {
|
if link.TypedSpec().BondSlave.MasterName != "" {
|
||||||
if master := findLink(*links, link.TypedSpec().MasterName); master != nil {
|
if master := findLink(*links, link.TypedSpec().BondSlave.MasterName); master != nil {
|
||||||
masterIndex = master.Index
|
masterIndex = master.Index
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -464,12 +489,12 @@ func (ctrl *LinkSpecController) syncLink(ctx context.Context, r controller.Runti
|
|||||||
Master: pointer.ToUint32(masterIndex),
|
Master: pointer.ToUint32(masterIndex),
|
||||||
},
|
},
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return fmt.Errorf("error enslaving/unslaving link %q under %q: %w", link.TypedSpec().Name, link.TypedSpec().MasterName, err)
|
return fmt.Errorf("error enslaving/unslaving link %q under %q: %w", link.TypedSpec().Name, link.TypedSpec().BondSlave.MasterName, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
existing.Attributes.Master = pointer.ToUint32(masterIndex)
|
existing.Attributes.Master = pointer.ToUint32(masterIndex)
|
||||||
|
|
||||||
logger.Info("enslaved/unslaved link", zap.String("parent", link.TypedSpec().MasterName))
|
logger.Info("enslaved/unslaved link", zap.String("parent", link.TypedSpec().BondSlave.MasterName))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -19,6 +19,7 @@ import (
|
|||||||
"github.com/cosi-project/runtime/pkg/state"
|
"github.com/cosi-project/runtime/pkg/state"
|
||||||
"github.com/cosi-project/runtime/pkg/state/impl/inmem"
|
"github.com/cosi-project/runtime/pkg/state/impl/inmem"
|
||||||
"github.com/cosi-project/runtime/pkg/state/impl/namespaced"
|
"github.com/cosi-project/runtime/pkg/state/impl/namespaced"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
"github.com/stretchr/testify/suite"
|
"github.com/stretchr/testify/suite"
|
||||||
"github.com/talos-systems/go-retry/retry"
|
"github.com/talos-systems/go-retry/retry"
|
||||||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||||
@ -376,7 +377,10 @@ func (suite *LinkSpecSuite) TestBond() {
|
|||||||
Kind: "dummy",
|
Kind: "dummy",
|
||||||
Up: true,
|
Up: true,
|
||||||
Logical: true,
|
Logical: true,
|
||||||
|
BondSlave: network.BondSlave{
|
||||||
MasterName: bondName,
|
MasterName: bondName,
|
||||||
|
SlaveIndex: 0,
|
||||||
|
},
|
||||||
ConfigLayer: network.ConfigDefault,
|
ConfigLayer: network.ConfigDefault,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -388,7 +392,10 @@ func (suite *LinkSpecSuite) TestBond() {
|
|||||||
Kind: "dummy",
|
Kind: "dummy",
|
||||||
Up: true,
|
Up: true,
|
||||||
Logical: true,
|
Logical: true,
|
||||||
|
BondSlave: network.BondSlave{
|
||||||
MasterName: bondName,
|
MasterName: bondName,
|
||||||
|
SlaveIndex: 1,
|
||||||
|
},
|
||||||
ConfigLayer: network.ConfigDefault,
|
ConfigLayer: network.ConfigDefault,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -460,7 +467,7 @@ func (suite *LinkSpecSuite) TestBond() {
|
|||||||
// unslave one of the interfaces
|
// unslave one of the interfaces
|
||||||
_, err = suite.state.UpdateWithConflicts(
|
_, err = suite.state.UpdateWithConflicts(
|
||||||
suite.ctx, dummy0.Metadata(), func(r resource.Resource) error {
|
suite.ctx, dummy0.Metadata(), func(r resource.Resource) error {
|
||||||
r.(*network.LinkSpec).TypedSpec().MasterName = ""
|
r.(*network.LinkSpec).TypedSpec().BondSlave.MasterName = ""
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
@ -538,7 +545,10 @@ func (suite *LinkSpecSuite) TestBond8023ad() {
|
|||||||
Kind: "dummy",
|
Kind: "dummy",
|
||||||
Up: true,
|
Up: true,
|
||||||
Logical: true,
|
Logical: true,
|
||||||
|
BondSlave: network.BondSlave{
|
||||||
MasterName: bondName,
|
MasterName: bondName,
|
||||||
|
SlaveIndex: 0,
|
||||||
|
},
|
||||||
ConfigLayer: network.ConfigDefault,
|
ConfigLayer: network.ConfigDefault,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -743,3 +753,72 @@ func (suite *LinkSpecSuite) TearDownTest() {
|
|||||||
func TestLinkSpecSuite(t *testing.T) {
|
func TestLinkSpecSuite(t *testing.T) {
|
||||||
suite.Run(t, new(LinkSpecSuite))
|
suite.Run(t, new(LinkSpecSuite))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSortBonds(t *testing.T) {
|
||||||
|
expectedSlice := []network.LinkSpecSpec{
|
||||||
|
{
|
||||||
|
Name: "A",
|
||||||
|
}, {
|
||||||
|
Name: "G",
|
||||||
|
BondSlave: network.BondSlave{
|
||||||
|
MasterName: "A",
|
||||||
|
SlaveIndex: 0,
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
Name: "C",
|
||||||
|
}, {
|
||||||
|
Name: "E",
|
||||||
|
BondSlave: network.BondSlave{
|
||||||
|
MasterName: "C",
|
||||||
|
SlaveIndex: 0,
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
Name: "F",
|
||||||
|
BondSlave: network.BondSlave{
|
||||||
|
MasterName: "C",
|
||||||
|
SlaveIndex: 1,
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
Name: "B",
|
||||||
|
BondSlave: network.BondSlave{
|
||||||
|
MasterName: "C",
|
||||||
|
SlaveIndex: 2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
seed := time.Now().Unix()
|
||||||
|
rnd := rand.New(rand.NewSource(seed))
|
||||||
|
|
||||||
|
for i := 0; i < 100; i++ {
|
||||||
|
res := toResources(expectedSlice)
|
||||||
|
rnd.Shuffle(len(res), func(i, j int) { res[i], res[j] = res[j], res[i] })
|
||||||
|
netctrl.SortBonds(res)
|
||||||
|
sorted := toSpecs(res)
|
||||||
|
require.Equal(t, expectedSlice, sorted, "failed with seed %d iteration %d", seed, i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func toResources(slice []network.LinkSpecSpec) []resource.Resource {
|
||||||
|
result := make([]resource.Resource, 0, len(slice))
|
||||||
|
|
||||||
|
for _, elem := range slice {
|
||||||
|
link := network.NewLinkSpec(network.NamespaceName, "bar")
|
||||||
|
*link.TypedSpec() = elem
|
||||||
|
|
||||||
|
result = append(result, link)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func toSpecs(slice []resource.Resource) []network.LinkSpecSpec {
|
||||||
|
result := make([]network.LinkSpecSpec, 0, len(slice))
|
||||||
|
|
||||||
|
for _, elem := range slice {
|
||||||
|
v := elem.Spec().(network.LinkSpecSpec) //nolint:errcheck
|
||||||
|
result = append(result, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|||||||
@ -11,6 +11,7 @@ import (
|
|||||||
networkadapter "github.com/talos-systems/talos/internal/app/machined/pkg/adapters/network"
|
networkadapter "github.com/talos-systems/talos/internal/app/machined/pkg/adapters/network"
|
||||||
talosconfig "github.com/talos-systems/talos/pkg/machinery/config"
|
talosconfig "github.com/talos-systems/talos/pkg/machinery/config"
|
||||||
"github.com/talos-systems/talos/pkg/machinery/nethelpers"
|
"github.com/talos-systems/talos/pkg/machinery/nethelpers"
|
||||||
|
"github.com/talos-systems/talos/pkg/machinery/ordered"
|
||||||
"github.com/talos-systems/talos/pkg/machinery/resources/network"
|
"github.com/talos-systems/talos/pkg/machinery/resources/network"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -18,8 +19,11 @@ import (
|
|||||||
const DefaultRouteMetric = 1024
|
const DefaultRouteMetric = 1024
|
||||||
|
|
||||||
// SetBondSlave sets the bond slave spec.
|
// SetBondSlave sets the bond slave spec.
|
||||||
func SetBondSlave(link *network.LinkSpecSpec, bondName string) {
|
func SetBondSlave(link *network.LinkSpecSpec, bond ordered.Pair[string, int]) {
|
||||||
link.MasterName = bondName
|
link.BondSlave = network.BondSlave{
|
||||||
|
MasterName: bond.F1,
|
||||||
|
SlaveIndex: bond.F2,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetBondMaster sets the bond master spec.
|
// SetBondMaster sets the bond master spec.
|
||||||
|
|||||||
@ -117,6 +117,8 @@ func (p *EquinixMetal) ParseMetadata(equinixMetadata *Metadata) (*runtime.Platfo
|
|||||||
return nil, fmt.Errorf("error listing host interfaces: %w", err)
|
return nil, fmt.Errorf("error listing host interfaces: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
slaveIndex := 0
|
||||||
|
|
||||||
for _, iface := range equinixMetadata.Network.Interfaces {
|
for _, iface := range equinixMetadata.Network.Interfaces {
|
||||||
if iface.Bond == "" {
|
if iface.Bond == "" {
|
||||||
continue
|
continue
|
||||||
@ -136,11 +138,15 @@ func (p *EquinixMetal) ParseMetadata(equinixMetadata *Metadata) (*runtime.Platfo
|
|||||||
|
|
||||||
networkConfig.Links = append(networkConfig.Links,
|
networkConfig.Links = append(networkConfig.Links,
|
||||||
network.LinkSpecSpec{
|
network.LinkSpecSpec{
|
||||||
ConfigLayer: network.ConfigPlatform,
|
|
||||||
Name: hostIf.Name,
|
Name: hostIf.Name,
|
||||||
Up: true,
|
Up: true,
|
||||||
|
BondSlave: network.BondSlave{
|
||||||
MasterName: bondName,
|
MasterName: bondName,
|
||||||
|
SlaveIndex: slaveIndex,
|
||||||
|
},
|
||||||
|
ConfigLayer: network.ConfigPlatform,
|
||||||
})
|
})
|
||||||
|
slaveIndex++
|
||||||
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -154,8 +160,12 @@ func (p *EquinixMetal) ParseMetadata(equinixMetadata *Metadata) (*runtime.Platfo
|
|||||||
ConfigLayer: network.ConfigPlatform,
|
ConfigLayer: network.ConfigPlatform,
|
||||||
Name: iface.Name,
|
Name: iface.Name,
|
||||||
Up: true,
|
Up: true,
|
||||||
|
BondSlave: network.BondSlave{
|
||||||
MasterName: bondName,
|
MasterName: bondName,
|
||||||
|
SlaveIndex: slaveIndex,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
slaveIndex++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -33,6 +33,7 @@ links:
|
|||||||
kind: ""
|
kind: ""
|
||||||
type: netrom
|
type: netrom
|
||||||
masterName: bond0
|
masterName: bond0
|
||||||
|
slaveIndex: 1
|
||||||
layer: platform
|
layer: platform
|
||||||
- name: bond0
|
- name: bond0
|
||||||
logical: true
|
logical: true
|
||||||
|
|||||||
61
pkg/machinery/ordered/ordered.go
Normal file
61
pkg/machinery/ordered/ordered.go
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
// 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 ordered
|
||||||
|
|
||||||
|
// Ordered is a constraint that permits any ordered type: any type
|
||||||
|
// that supports the operators < <= >= >.
|
||||||
|
type Ordered interface {
|
||||||
|
~int | ~int8 | ~int16 | ~int32 | ~int64 | ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr | ~float32 | ~float64 | ~string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pair is two element tuple of ordered values.
|
||||||
|
type Pair[T1, T2 Ordered] struct {
|
||||||
|
F1 T1
|
||||||
|
F2 T2
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakePair creates a new Pair.
|
||||||
|
func MakePair[T1, T2 Ordered](v1 T1, v2 T2) Pair[T1, T2] {
|
||||||
|
return Pair[T1, T2]{
|
||||||
|
F1: v1,
|
||||||
|
F2: v2,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compare returns an integer comparing two pairs in natural order.
|
||||||
|
// The result will be 0 if p == other, -1 if p < other, and +1 if p > other.
|
||||||
|
func (p Pair[T1, T2]) Compare(other Pair[T1, T2]) int {
|
||||||
|
if result := cmp(p.F1, other.F1); result != 0 {
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
return cmp(p.F2, other.F2)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MoreThan checks if current pair is bigger than the other.
|
||||||
|
func (p Pair[T1, T2]) MoreThan(other Pair[T1, T2]) bool {
|
||||||
|
return p.Compare(other) == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// LessThan checks if current pair is lesser than the other.
|
||||||
|
func (p Pair[T1, T2]) LessThan(other Pair[T1, T2]) bool {
|
||||||
|
return p.Compare(other) == -1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equal checks if current pair is equal to the other.
|
||||||
|
func (p Pair[T1, T2]) Equal(other Pair[T1, T2]) bool {
|
||||||
|
return p.Compare(other) == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func cmp[T Ordered](a, b T) int {
|
||||||
|
switch {
|
||||||
|
case a == b:
|
||||||
|
return 0
|
||||||
|
case a < b:
|
||||||
|
return -1
|
||||||
|
default:
|
||||||
|
return +1
|
||||||
|
}
|
||||||
|
}
|
||||||
46
pkg/machinery/ordered/ordered_test.go
Normal file
46
pkg/machinery/ordered/ordered_test.go
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
// 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 ordered_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
"math/rand"
|
||||||
|
"sort"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"github.com/talos-systems/talos/pkg/machinery/ordered"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestTriple(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
expectedSlice := []ordered.Triple[int, string, float64]{
|
||||||
|
ordered.MakeTriple(math.MinInt64, "Alpha", 69.0),
|
||||||
|
ordered.MakeTriple(-200, "Alpha", 69.0),
|
||||||
|
ordered.MakeTriple(-200, "Beta", -69.0),
|
||||||
|
ordered.MakeTriple(-200, "Beta", 69.0),
|
||||||
|
ordered.MakeTriple(1, "", 69.0),
|
||||||
|
ordered.MakeTriple(1, "Alpha", 67.0),
|
||||||
|
ordered.MakeTriple(1, "Alpha", 68.0),
|
||||||
|
ordered.MakeTriple(10, "Alpha", 68.0),
|
||||||
|
ordered.MakeTriple(10, "Beta", 68.0),
|
||||||
|
ordered.MakeTriple(math.MaxInt64, "", 69.0),
|
||||||
|
}
|
||||||
|
|
||||||
|
seed := time.Now().Unix()
|
||||||
|
rnd := rand.New(rand.NewSource(seed))
|
||||||
|
|
||||||
|
for i := 0; i < 1000; i++ {
|
||||||
|
a := append([]ordered.Triple[int, string, float64](nil), expectedSlice...)
|
||||||
|
rnd.Shuffle(len(a), func(i, j int) { a[i], a[j] = a[j], a[i] })
|
||||||
|
sort.Slice(a, func(i, j int) bool {
|
||||||
|
return a[i].LessThan(a[j])
|
||||||
|
})
|
||||||
|
require.Equal(t, expectedSlice, a, "failed with seed %d iteration %d", seed, i)
|
||||||
|
}
|
||||||
|
}
|
||||||
48
pkg/machinery/ordered/triple.go
Normal file
48
pkg/machinery/ordered/triple.go
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
// 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 ordered
|
||||||
|
|
||||||
|
// Triple is three element tuple of ordered values.
|
||||||
|
type Triple[T1, T2, T3 Ordered] struct {
|
||||||
|
V1 T1
|
||||||
|
V2 T2
|
||||||
|
V3 T3
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeTriple creates a new Triple.
|
||||||
|
func MakeTriple[T1, T2, T3 Ordered](v1 T1, v2 T2, v3 T3) Triple[T1, T2, T3] {
|
||||||
|
return Triple[T1, T2, T3]{
|
||||||
|
V1: v1,
|
||||||
|
V2: v2,
|
||||||
|
V3: v3,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compare returns an integer comparing two triples in natural order.
|
||||||
|
// The result will be 0 if t == other, -1 if t < other, and +1 if t > other.
|
||||||
|
func (t Triple[T1, T2, T3]) Compare(other Triple[T1, T2, T3]) int {
|
||||||
|
if result := cmp(t.V1, other.V1); result != 0 {
|
||||||
|
return result
|
||||||
|
} else if result := cmp(t.V2, other.V2); result != 0 {
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
return cmp(t.V3, other.V3)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MoreThan checks if current triple is bigger than the other.
|
||||||
|
func (t Triple[T1, T2, T3]) MoreThan(other Triple[T1, T2, T3]) bool {
|
||||||
|
return t.Compare(other) == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// LessThan checks if current triple is lesser than the other.
|
||||||
|
func (t Triple[T1, T2, T3]) LessThan(other Triple[T1, T2, T3]) bool {
|
||||||
|
return t.Compare(other) == -1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equal checks if current triple is equal to the other.
|
||||||
|
func (t Triple[T1, T2, T3]) Equal(other Triple[T1, T2, T3]) bool {
|
||||||
|
return t.Compare(other) == 0
|
||||||
|
}
|
||||||
@ -43,7 +43,7 @@ type LinkSpecSpec struct {
|
|||||||
ParentName string `yaml:"parentName,omitempty"`
|
ParentName string `yaml:"parentName,omitempty"`
|
||||||
|
|
||||||
// MasterName indicates master link for enslaved bonded interfaces.
|
// MasterName indicates master link for enslaved bonded interfaces.
|
||||||
MasterName string `yaml:"masterName,omitempty"`
|
BondSlave BondSlave `yaml:",omitempty,inline"`
|
||||||
|
|
||||||
// These structures are present depending on "Kind" for Logical intefaces.
|
// These structures are present depending on "Kind" for Logical intefaces.
|
||||||
VLAN VLANSpec `yaml:"vlan,omitempty"`
|
VLAN VLANSpec `yaml:"vlan,omitempty"`
|
||||||
@ -54,6 +54,15 @@ type LinkSpecSpec struct {
|
|||||||
ConfigLayer ConfigLayer `yaml:"layer"`
|
ConfigLayer ConfigLayer `yaml:"layer"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BondSlave contains a bond's master name and slave index.
|
||||||
|
type BondSlave struct {
|
||||||
|
// MasterName indicates master link for enslaved bonded interfaces.
|
||||||
|
MasterName string `yaml:"masterName,omitempty"`
|
||||||
|
|
||||||
|
// SlaveIndex indicates a slave's position in bond.
|
||||||
|
SlaveIndex int `yaml:"slaveIndex,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
// DeepCopy generates a deep copy of LinkSpecSpec.
|
// DeepCopy generates a deep copy of LinkSpecSpec.
|
||||||
func (spec LinkSpecSpec) DeepCopy() LinkSpecSpec {
|
func (spec LinkSpecSpec) DeepCopy() LinkSpecSpec {
|
||||||
cp := spec
|
cp := spec
|
||||||
@ -72,51 +81,20 @@ func (spec LinkSpecSpec) DeepCopy() LinkSpecSpec {
|
|||||||
return cp
|
return cp
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
|
||||||
zeroVLAN VLANSpec
|
|
||||||
zeroBondMaster BondMasterSpec
|
|
||||||
)
|
|
||||||
|
|
||||||
// Merge with other, overwriting fields from other if set.
|
// Merge with other, overwriting fields from other if set.
|
||||||
//
|
//
|
||||||
//nolint:gocyclo
|
//nolint:gocyclo
|
||||||
func (spec *LinkSpecSpec) Merge(other *LinkSpecSpec) error {
|
func (spec *LinkSpecSpec) Merge(other *LinkSpecSpec) error {
|
||||||
// prefer Logical, as it is defined for bonds/vlans, etc.
|
// prefer Logical, as it is defined for bonds/vlans, etc.
|
||||||
if other.Logical {
|
updateIfNotZero(&spec.Logical, other.Logical)
|
||||||
spec.Logical = other.Logical
|
updateIfNotZero(&spec.Up, other.Up)
|
||||||
}
|
updateIfNotZero(&spec.MTU, other.MTU)
|
||||||
|
updateIfNotZero(&spec.Kind, other.Kind)
|
||||||
if other.Up {
|
updateIfNotZero(&spec.Type, other.Type)
|
||||||
spec.Up = other.Up
|
updateIfNotZero(&spec.ParentName, other.ParentName)
|
||||||
}
|
updateIfNotZero(&spec.BondSlave, other.BondSlave)
|
||||||
|
updateIfNotZero(&spec.VLAN, other.VLAN)
|
||||||
if other.MTU != 0 {
|
updateIfNotZero(&spec.BondMaster, other.BondMaster)
|
||||||
spec.MTU = other.MTU
|
|
||||||
}
|
|
||||||
|
|
||||||
if other.Kind != "" {
|
|
||||||
spec.Kind = other.Kind
|
|
||||||
}
|
|
||||||
|
|
||||||
if other.Type != 0 {
|
|
||||||
spec.Type = other.Type
|
|
||||||
}
|
|
||||||
|
|
||||||
if other.ParentName != "" {
|
|
||||||
spec.ParentName = other.ParentName
|
|
||||||
}
|
|
||||||
|
|
||||||
if other.MasterName != "" {
|
|
||||||
spec.MasterName = other.MasterName
|
|
||||||
}
|
|
||||||
|
|
||||||
if other.VLAN != zeroVLAN {
|
|
||||||
spec.VLAN = other.VLAN
|
|
||||||
}
|
|
||||||
|
|
||||||
if other.BondMaster != zeroBondMaster {
|
|
||||||
spec.BondMaster = other.BondMaster
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wireguard config should be able to apply non-zero values in earlier config layers which may be zero values in later layers.
|
// Wireguard config should be able to apply non-zero values in earlier config layers which may be zero values in later layers.
|
||||||
// Thus, we handle each Wireguard configuration value discretely.
|
// Thus, we handle each Wireguard configuration value discretely.
|
||||||
@ -133,6 +111,13 @@ func (spec *LinkSpecSpec) Merge(other *LinkSpecSpec) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func updateIfNotZero[T comparable](target *T, source T) {
|
||||||
|
var zero T
|
||||||
|
if source != zero {
|
||||||
|
*target = source
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// NewLinkSpec initializes a LinkSpec resource.
|
// NewLinkSpec initializes a LinkSpec resource.
|
||||||
func NewLinkSpec(namespace resource.Namespace, id resource.ID) *LinkSpec {
|
func NewLinkSpec(namespace resource.Namespace, id resource.ID) *LinkSpec {
|
||||||
return typed.NewResource[LinkSpecSpec, LinkSpecRD](
|
return typed.NewResource[LinkSpecSpec, LinkSpecRD](
|
||||||
|
|||||||
@ -26,7 +26,10 @@ func TestLinkSpecMarshalYAML(t *testing.T) {
|
|||||||
Kind: "eth",
|
Kind: "eth",
|
||||||
Type: nethelpers.LinkEther,
|
Type: nethelpers.LinkEther,
|
||||||
ParentName: "eth1",
|
ParentName: "eth1",
|
||||||
|
BondSlave: network.BondSlave{
|
||||||
MasterName: "bond0",
|
MasterName: "bond0",
|
||||||
|
SlaveIndex: 0,
|
||||||
|
},
|
||||||
VLAN: network.VLANSpec{
|
VLAN: network.VLANSpec{
|
||||||
VID: 25,
|
VID: 25,
|
||||||
Protocol: nethelpers.VLANProtocol8021AD,
|
Protocol: nethelpers.VLANProtocol8021AD,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user