From 0b8b3567771fbe796926dc9a6e904e7102535170 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20Nohlg=C3=A5rd?= Date: Mon, 14 Oct 2024 13:12:53 +0200 Subject: [PATCH] feat: add BridgePort property to network machine configuration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Allow putting a device into a bridge from device configuration. Signed-off-by: Joakim NohlgÄrd Signed-off-by: Andrey Smirnov --- .../definitions/network/network.proto | 2 +- .../pkg/controllers/network/link_config.go | 18 ++++++-- .../controllers/network/link_config_test.go | 23 ++++++++++ .../pkg/controllers/network/network.go | 17 ++++--- .../definitions/network/network.pb.go | 2 +- pkg/machinery/config/config/machine.go | 6 +++ .../config/schemas/config.schema.json | 20 ++++++++ .../types/v1alpha1/v1alpha1_examples.go | 6 +++ .../types/v1alpha1/v1alpha1_provider.go | 18 ++++++++ .../config/types/v1alpha1/v1alpha1_types.go | 12 +++++ .../types/v1alpha1/v1alpha1_types_doc.go | 46 +++++++++++++++++-- pkg/machinery/resources/network/link_spec.go | 2 +- website/content/v1.9/reference/api.md | 2 +- .../configuration/v1alpha1/config.md | 44 ++++++++++++++++++ .../content/v1.9/schemas/config.schema.json | 20 ++++++++ 15 files changed, 218 insertions(+), 20 deletions(-) diff --git a/api/resource/definitions/network/network.proto b/api/resource/definitions/network/network.proto index f482ef4872..9ee2bf2d81 100755 --- a/api/resource/definitions/network/network.proto +++ b/api/resource/definitions/network/network.proto @@ -74,7 +74,7 @@ message BridgeMasterSpec { BridgeVLANSpec vlan = 2; } -// BridgeSlave contains a bond's master name and slave index. +// BridgeSlave contains the name of the master bridge of a bridged interface message BridgeSlave { string master_name = 1; } diff --git a/internal/app/machined/pkg/controllers/network/link_config.go b/internal/app/machined/pkg/controllers/network/link_config.go index a90f2bca1f..7f1e589f59 100644 --- a/internal/app/machined/pkg/controllers/network/link_config.go +++ b/internal/app/machined/pkg/controllers/network/link_config.go @@ -280,10 +280,6 @@ func (ctrl *LinkConfigController) processDevicesConfiguration(logger *zap.Logger continue } - if device.Bond() == nil && device.Bridge() == nil { - continue - } - if device.Bond() != nil { for idx, linkName := range device.Bond().Interfaces() { if bondData, exists := bondedLinks[linkName]; exists && bondData.F1 != device.Interface() { @@ -315,6 +311,20 @@ func (ctrl *LinkConfigController) processDevicesConfiguration(logger *zap.Logger bridgedLinks[linkName] = device.Interface() } } + + if device.BridgePort() != nil { + if bridgeName, exists := bridgedLinks[device.Interface()]; exists && bridgeName != device.BridgePort().Master() { + logger.Sugar().Warnf("link %q is included in both bridges %q and %q", device.Interface(), + bridgeName, device.BridgePort().Master()) + } + + if bondData, exists := bondedLinks[device.Interface()]; exists { + logger.Sugar().Warnf("link %q is included into both bond %q and bridge %q", device.Interface(), + bondData.F1, device.BridgePort().Master()) + } + + bridgedLinks[device.Interface()] = device.BridgePort().Master() + } } linkMap := map[string]*network.LinkSpecSpec{} diff --git a/internal/app/machined/pkg/controllers/network/link_config_test.go b/internal/app/machined/pkg/controllers/network/link_config_test.go index f0b4afa25a..006e6a8bab 100644 --- a/internal/app/machined/pkg/controllers/network/link_config_test.go +++ b/internal/app/machined/pkg/controllers/network/link_config_test.go @@ -214,6 +214,12 @@ func (suite *LinkConfigSuite) TestMachineConfiguration() { DeviceInterface: "eth5", DeviceAddresses: []string{"192.168.0.43/24"}, }, + { + DeviceInterface: "eth8", + DeviceBridgePort: &v1alpha1.BridgePort{ + BridgePortMaster: "br1", + }, + }, { DeviceInterface: "br0", DeviceBridge: &v1alpha1.Bridge{ @@ -223,6 +229,10 @@ func (suite *LinkConfigSuite) TestMachineConfiguration() { }, }, }, + { + DeviceInterface: "br1", + DeviceBridge: &v1alpha1.Bridge{}, + }, { DeviceInterface: "br0", DeviceBridge: &v1alpha1.Bridge{ @@ -287,9 +297,11 @@ func (suite *LinkConfigSuite) TestMachineConfiguration() { "configuration/eth3", "configuration/eth6", "configuration/eth7", + "configuration/eth8", "configuration/bond0", "configuration/bond1", "configuration/br0", + "configuration/br1", "configuration/dummy0", "configuration/wireguard0", }, func(r *network.LinkSpec, asrt *assert.Assertions) { @@ -346,6 +358,10 @@ func (suite *LinkConfigSuite) TestMachineConfiguration() { asrt.True(r.TypedSpec().Up) asrt.False(r.TypedSpec().Logical) asrt.Equal("br0", r.TypedSpec().BridgeSlave.MasterName) + case "eth8": + asrt.True(r.TypedSpec().Up) + asrt.False(r.TypedSpec().Logical) + asrt.Equal("br1", r.TypedSpec().BridgeSlave.MasterName) case "br0": asrt.True(r.TypedSpec().Up) asrt.True(r.TypedSpec().Logical) @@ -353,6 +369,13 @@ func (suite *LinkConfigSuite) TestMachineConfiguration() { asrt.Equal(network.LinkKindBridge, r.TypedSpec().Kind) asrt.True(r.TypedSpec().BridgeMaster.STP.Enabled) asrt.True(r.TypedSpec().BridgeMaster.VLAN.FilteringEnabled) + case "br1": + asrt.True(r.TypedSpec().Up) + asrt.True(r.TypedSpec().Logical) + asrt.Equal(nethelpers.LinkEther, r.TypedSpec().Type) + asrt.Equal(network.LinkKindBridge, r.TypedSpec().Kind) + asrt.True(r.TypedSpec().BridgeMaster.STP.Enabled) + asrt.False(r.TypedSpec().BridgeMaster.VLAN.FilteringEnabled) case "wireguard0": asrt.True(r.TypedSpec().Up) asrt.True(r.TypedSpec().Logical) diff --git a/internal/app/machined/pkg/controllers/network/network.go b/internal/app/machined/pkg/controllers/network/network.go index 87ff0aa686..881299cb83 100644 --- a/internal/app/machined/pkg/controllers/network/network.go +++ b/internal/app/machined/pkg/controllers/network/network.go @@ -128,13 +128,16 @@ func SetBridgeMaster(link *network.LinkSpecSpec, bridge talosconfig.Bridge) erro link.Logical = true link.Kind = network.LinkKindBridge link.Type = nethelpers.LinkEther - link.BridgeMaster = network.BridgeMasterSpec{ - STP: network.STPSpec{ - Enabled: bridge.STP().Enabled(), - }, - VLAN: network.BridgeVLANSpec{ - FilteringEnabled: bridge.VLAN().FilteringEnabled(), - }, + + if bridge != nil { + link.BridgeMaster = network.BridgeMasterSpec{ + STP: network.STPSpec{ + Enabled: bridge.STP().Enabled(), + }, + VLAN: network.BridgeVLANSpec{ + FilteringEnabled: bridge.VLAN().FilteringEnabled(), + }, + } } return nil diff --git a/pkg/machinery/api/resource/definitions/network/network.pb.go b/pkg/machinery/api/resource/definitions/network/network.pb.go index 33338633c2..84c029134c 100644 --- a/pkg/machinery/api/resource/definitions/network/network.pb.go +++ b/pkg/machinery/api/resource/definitions/network/network.pb.go @@ -575,7 +575,7 @@ func (x *BridgeMasterSpec) GetVlan() *BridgeVLANSpec { return nil } -// BridgeSlave contains a bond's master name and slave index. +// BridgeSlave contains the name of the master bridge of a bridged interface type BridgeSlave struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache diff --git a/pkg/machinery/config/config/machine.go b/pkg/machinery/config/config/machine.go index aaafa1b07f..5fb5dfb08f 100644 --- a/pkg/machinery/config/config/machine.go +++ b/pkg/machinery/config/config/machine.go @@ -157,6 +157,7 @@ type Device interface { Routes() []Route Bond() Bond Bridge() Bridge + BridgePort() BridgePort Vlans() []Vlan MTU() int DHCP() bool @@ -261,6 +262,11 @@ type Bridge interface { VLAN() BridgeVLAN } +// BridgePort contains the options for a bridge port. +type BridgePort interface { + Master() string +} + // Vlan represents vlan settings for a device. type Vlan interface { Addresses() []string diff --git a/pkg/machinery/config/schemas/config.schema.json b/pkg/machinery/config/schemas/config.schema.json index 8d739ba5d1..97c0c0a11e 100644 --- a/pkg/machinery/config/schemas/config.schema.json +++ b/pkg/machinery/config/schemas/config.schema.json @@ -916,6 +916,19 @@ "additionalProperties": false, "type": "object" }, + "v1alpha1.BridgePort": { + "properties": { + "master": { + "type": "string", + "title": "master", + "description": "The name of the bridge master interface\n", + "markdownDescription": "The name of the bridge master interface", + "x-intellij-html-description": "\u003cp\u003eThe name of the bridge master interface\u003c/p\u003e\n" + } + }, + "additionalProperties": false, + "type": "object" + }, "v1alpha1.BridgeVLAN": { "properties": { "vlanFiltering": { @@ -1482,6 +1495,13 @@ "markdownDescription": "Bridge specific options.", "x-intellij-html-description": "\u003cp\u003eBridge specific options.\u003c/p\u003e\n" }, + "bridgePort": { + "$ref": "#/$defs/v1alpha1.BridgePort", + "title": "bridgePort", + "description": "Configure this device as a bridge port.\nThis can be used to dynamically assign network interfaces to a bridge.\n", + "markdownDescription": "Configure this device as a bridge port.\nThis can be used to dynamically assign network interfaces to a bridge.", + "x-intellij-html-description": "\u003cp\u003eConfigure this device as a bridge port.\nThis can be used to dynamically assign network interfaces to a bridge.\u003c/p\u003e\n" + }, "vlans": { "items": { "$ref": "#/$defs/v1alpha1.Vlan" diff --git a/pkg/machinery/config/types/v1alpha1/v1alpha1_examples.go b/pkg/machinery/config/types/v1alpha1/v1alpha1_examples.go index 6b6e3331ca..c2695792ab 100644 --- a/pkg/machinery/config/types/v1alpha1/v1alpha1_examples.go +++ b/pkg/machinery/config/types/v1alpha1/v1alpha1_examples.go @@ -527,6 +527,12 @@ func networkConfigBridgeExample() *Bridge { } } +func networkConfigDynamicBridgePortsExample() *BridgePort { + return &BridgePort{ + BridgePortMaster: "br0", + } +} + func networkConfigDHCPOptionsExample() *DHCPOptions { return &DHCPOptions{ DHCPRouteMetric: 1024, diff --git a/pkg/machinery/config/types/v1alpha1/v1alpha1_provider.go b/pkg/machinery/config/types/v1alpha1/v1alpha1_provider.go index 45b4cd2369..f9406aa0e7 100644 --- a/pkg/machinery/config/types/v1alpha1/v1alpha1_provider.go +++ b/pkg/machinery/config/types/v1alpha1/v1alpha1_provider.go @@ -664,6 +664,15 @@ func (d *Device) Bridge() config.Bridge { return d.DeviceBridge } +// BridgePort implements the MachineNetwork interface. +func (d *Device) BridgePort() config.BridgePort { + if d.DeviceBridgePort == nil { + return nil + } + + return d.DeviceBridgePort +} + // Vlans implements the MachineNetwork interface. func (d *Device) Vlans() []config.Vlan { return xslices.Map(d.DeviceVlans, func(v *Vlan) config.Vlan { return v }) @@ -1076,6 +1085,15 @@ func (b *Bridge) VLAN() config.BridgeVLAN { return b.BridgeVLAN } +// Master implements the config.BridgePort interface. +func (b *BridgePort) Master() string { + if b == nil { + return "" + } + + return b.BridgePortMaster +} + // Addresses implements the MachineNetwork interface. func (v *Vlan) Addresses() []string { switch { diff --git a/pkg/machinery/config/types/v1alpha1/v1alpha1_types.go b/pkg/machinery/config/types/v1alpha1/v1alpha1_types.go index 52c3ae1ecf..0e1ffe938f 100644 --- a/pkg/machinery/config/types/v1alpha1/v1alpha1_types.go +++ b/pkg/machinery/config/types/v1alpha1/v1alpha1_types.go @@ -1747,6 +1747,12 @@ type Device struct { // examples: // - value: networkConfigBridgeExample() DeviceBridge *Bridge `yaml:"bridge,omitempty"` + // description: | + // Configure this device as a bridge port. + // This can be used to dynamically assign network interfaces to a bridge. + // examples: + // - value: networkConfigDynamicBridgePortsExample() + DeviceBridgePort *BridgePort `yaml:"bridgePort,omitempty"` // description: VLAN specific options. DeviceVlans VlanList `yaml:"vlans,omitempty"` // description: | @@ -2006,6 +2012,12 @@ type Bridge struct { BridgeVLAN *BridgeVLAN `yaml:"vlan,omitempty"` } +// BridgePort contains settings for assigning a link to a bridge interface. +type BridgePort struct { + // description: The name of the bridge master interface + BridgePortMaster string `yaml:"master,omitempty"` +} + // VlanList is a list of *Vlan structures with overridden merge process. // //docgen:alias diff --git a/pkg/machinery/config/types/v1alpha1/v1alpha1_types_doc.go b/pkg/machinery/config/types/v1alpha1/v1alpha1_types_doc.go index 7035ffd379..42e1385e00 100644 --- a/pkg/machinery/config/types/v1alpha1/v1alpha1_types_doc.go +++ b/pkg/machinery/config/types/v1alpha1/v1alpha1_types_doc.go @@ -2407,6 +2407,13 @@ func (Device) Doc() *encoder.Doc { Description: "Bridge specific options.", Comments: [3]string{"" /* encoder.HeadComment */, "Bridge specific options." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, + { + Name: "bridgePort", + Type: "BridgePort", + Note: "", + Description: "Configure this device as a bridge port.\nThis can be used to dynamically assign network interfaces to a bridge.", + Comments: [3]string{"" /* encoder.HeadComment */, "Configure this device as a bridge port." /* encoder.LineComment */, "" /* encoder.FootComment */}, + }, { Name: "vlans", Type: "[]Vlan", @@ -2475,11 +2482,12 @@ func (Device) Doc() *encoder.Doc { doc.Fields[4].AddExample("", networkConfigRoutesExample()) doc.Fields[5].AddExample("", networkConfigBondExample()) doc.Fields[6].AddExample("", networkConfigBridgeExample()) - doc.Fields[9].AddExample("", true) - doc.Fields[12].AddExample("", networkConfigDHCPOptionsExample()) - doc.Fields[13].AddExample("wireguard server example", networkConfigWireguardHostExample()) - doc.Fields[13].AddExample("wireguard peer example", networkConfigWireguardPeerExample()) - doc.Fields[14].AddExample("layer2 vip example", networkConfigVIPLayer2Example()) + doc.Fields[7].AddExample("", networkConfigDynamicBridgePortsExample()) + doc.Fields[10].AddExample("", true) + doc.Fields[13].AddExample("", networkConfigDHCPOptionsExample()) + doc.Fields[14].AddExample("wireguard server example", networkConfigWireguardHostExample()) + doc.Fields[14].AddExample("wireguard peer example", networkConfigWireguardPeerExample()) + doc.Fields[15].AddExample("layer2 vip example", networkConfigVIPLayer2Example()) return doc } @@ -3036,6 +3044,33 @@ func (Bridge) Doc() *encoder.Doc { return doc } +func (BridgePort) Doc() *encoder.Doc { + doc := &encoder.Doc{ + Type: "BridgePort", + Comments: [3]string{"" /* encoder.HeadComment */, "BridgePort contains settings for assigning a link to a bridge interface." /* encoder.LineComment */, "" /* encoder.FootComment */}, + Description: "BridgePort contains settings for assigning a link to a bridge interface.", + AppearsIn: []encoder.Appearance{ + { + TypeName: "Device", + FieldName: "bridgePort", + }, + }, + Fields: []encoder.Doc{ + { + Name: "master", + Type: "string", + Note: "", + Description: "The name of the bridge master interface", + Comments: [3]string{"" /* encoder.HeadComment */, "The name of the bridge master interface" /* encoder.LineComment */, "" /* encoder.FootComment */}, + }, + }, + } + + doc.AddExample("", networkConfigDynamicBridgePortsExample()) + + return doc +} + func (Vlan) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "Vlan", @@ -4127,6 +4162,7 @@ func GetFileDoc() *encoder.FileDoc { STP{}.Doc(), BridgeVLAN{}.Doc(), Bridge{}.Doc(), + BridgePort{}.Doc(), Vlan{}.Doc(), Route{}.Doc(), RegistryMirrorConfig{}.Doc(), diff --git a/pkg/machinery/resources/network/link_spec.go b/pkg/machinery/resources/network/link_spec.go index 3c0689f6f6..a84a457da7 100644 --- a/pkg/machinery/resources/network/link_spec.go +++ b/pkg/machinery/resources/network/link_spec.go @@ -72,7 +72,7 @@ type BondSlave struct { SlaveIndex int `yaml:"slaveIndex,omitempty" protobuf:"2"` } -// BridgeSlave contains a bond's master name and slave index. +// BridgeSlave contains the name of the master bridge of a bridged interface // //gotagsrewrite:gen type BridgeSlave struct { diff --git a/website/content/v1.9/reference/api.md b/website/content/v1.9/reference/api.md index bc619594be..7e04d3eae1 100644 --- a/website/content/v1.9/reference/api.md +++ b/website/content/v1.9/reference/api.md @@ -3368,7 +3368,7 @@ BridgeMasterSpec describes bridge settings if Kind == "bridge". ### BridgeSlave -BridgeSlave contains a bond's master name and slave index. +BridgeSlave contains the name of the master bridge of a bridged interface | Field | Type | Label | Description | diff --git a/website/content/v1.9/reference/configuration/v1alpha1/config.md b/website/content/v1.9/reference/configuration/v1alpha1/config.md index 283e334f1b..0020d398ea 100644 --- a/website/content/v1.9/reference/configuration/v1alpha1/config.md +++ b/website/content/v1.9/reference/configuration/v1alpha1/config.md @@ -204,6 +204,10 @@ network: # stp: # enabled: true # Whether Spanning Tree Protocol (STP) is enabled. + # # Configure this device as a bridge port. + # bridgePort: + # master: br0 # The name of the bridge master interface + # # Indicates if DHCP should be used to configure the interface. # dhcp: true @@ -775,6 +779,10 @@ machine: # stp: # enabled: true # Whether Spanning Tree Protocol (STP) is enabled. + # # Configure this device as a bridge port. + # bridgePort: + # master: br0 # The name of the bridge master interface + # # Indicates if DHCP should be used to configure the interface. # dhcp: true @@ -886,6 +894,10 @@ interfaces: # stp: # enabled: true # Whether Spanning Tree Protocol (STP) is enabled. + # # Configure this device as a bridge port. + # bridgePort: + # master: br0 # The name of the bridge master interface + # # Indicates if DHCP should be used to configure the interface. # dhcp: true @@ -1006,6 +1018,10 @@ machine: # stp: # enabled: true # Whether Spanning Tree Protocol (STP) is enabled. + # # Configure this device as a bridge port. + # bridgePort: + # master: br0 # The name of the bridge master interface + # # Indicates if DHCP should be used to configure the interface. # dhcp: true @@ -1098,6 +1114,10 @@ bridge: stp: enabled: true # Whether Spanning Tree Protocol (STP) is enabled. {{< /highlight >}} | | +|`bridgePort` |BridgePort |
Configure this device as a bridge port.This can be used to dynamically assign network interfaces to a bridge.
Show example(s){{< highlight yaml >}} +bridgePort: + master: br0 # The name of the bridge master interface +{{< /highlight >}}
| | |`vlans` |[]Vlan |VLAN specific options. | | |`mtu` |int |
The interface's MTU.If used in combination with DHCP, this will override any MTU settings returned from DHCP server.
| | |`dhcp` |bool |
Indicates if DHCP should be used to configure the interface.The following DHCP options are supported:

- `OptionClasslessStaticRoute`
- `OptionDomainNameServer`
- `OptionDNSDomainSearchList`
- `OptionHostName`
Show example(s){{< highlight yaml >}} @@ -1401,6 +1421,30 @@ BridgeVLAN contains the various options for configuring the VLAN properties of a +##### bridgePort {#Config.machine.network.interfaces..bridgePort} + +BridgePort contains settings for assigning a link to a bridge interface. + + + +{{< highlight yaml >}} +machine: + network: + interfaces: + - bridgePort: + master: br0 # The name of the bridge master interface +{{< /highlight >}} + + +| Field | Type | Description | Value(s) | +|-------|------|-------------|----------| +|`master` |string |The name of the bridge master interface | | + + + + + + ##### vlans[] {#Config.machine.network.interfaces..vlans.} Vlan represents vlan settings for a device. diff --git a/website/content/v1.9/schemas/config.schema.json b/website/content/v1.9/schemas/config.schema.json index 8d739ba5d1..97c0c0a11e 100644 --- a/website/content/v1.9/schemas/config.schema.json +++ b/website/content/v1.9/schemas/config.schema.json @@ -916,6 +916,19 @@ "additionalProperties": false, "type": "object" }, + "v1alpha1.BridgePort": { + "properties": { + "master": { + "type": "string", + "title": "master", + "description": "The name of the bridge master interface\n", + "markdownDescription": "The name of the bridge master interface", + "x-intellij-html-description": "\u003cp\u003eThe name of the bridge master interface\u003c/p\u003e\n" + } + }, + "additionalProperties": false, + "type": "object" + }, "v1alpha1.BridgeVLAN": { "properties": { "vlanFiltering": { @@ -1482,6 +1495,13 @@ "markdownDescription": "Bridge specific options.", "x-intellij-html-description": "\u003cp\u003eBridge specific options.\u003c/p\u003e\n" }, + "bridgePort": { + "$ref": "#/$defs/v1alpha1.BridgePort", + "title": "bridgePort", + "description": "Configure this device as a bridge port.\nThis can be used to dynamically assign network interfaces to a bridge.\n", + "markdownDescription": "Configure this device as a bridge port.\nThis can be used to dynamically assign network interfaces to a bridge.", + "x-intellij-html-description": "\u003cp\u003eConfigure this device as a bridge port.\nThis can be used to dynamically assign network interfaces to a bridge.\u003c/p\u003e\n" + }, "vlans": { "items": { "$ref": "#/$defs/v1alpha1.Vlan"