Skip to content

Commit f737e64

Browse files
committed
fix: populate routes to BGP neighbors (Equinix Metal)
Fixes #8267 Also refactor the code so that we don't fail hard on mutiple bonds, but it's not clear still how to attach addresses, as they don't have a interface name field, so for now attaching to the first bond. Fixes #8411 Signed-off-by: Andrey Smirnov <andrey.smirnov@siderolabs.com>
1 parent 19f15a8 commit f737e64

7 files changed

Lines changed: 165 additions & 126 deletions

File tree

internal/app/machined/pkg/runtime/v1alpha1/platform/equinixmetal/equinix.go

Lines changed: 75 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import (
99
"bytes"
1010
"context"
1111
"encoding/json"
12-
stderrors "errors"
1312
"fmt"
1413
"io"
1514
"log"
@@ -71,6 +70,12 @@ type Address struct {
7170
Gateway string `json:"gateway"`
7271
}
7372

73+
// BGPNeighbor holds BGP neighbor info from the equinixmetal metadata.
74+
type BGPNeighbor struct {
75+
AddressFamily int `json:"address_family"`
76+
PeerIPs []string `json:"peer_ips"`
77+
}
78+
7479
const (
7580
// EquinixMetalUserDataEndpoint is the local metadata endpoint for Equinix.
7681
EquinixMetalUserDataEndpoint = "https://metadata.platformequinix.com/userdata"
@@ -122,27 +127,23 @@ func (p *EquinixMetal) ParseMetadata(ctx context.Context, equinixMetadata *Metad
122127
// translate the int returned from bond mode metadata to the type needed by network resources
123128
bondMode := nethelpers.BondMode(uint8(equinixMetadata.Network.Bonding.Mode))
124129

125-
// determine bond name and build list of interfaces enslaved by the bond
126-
bondName := ""
127-
128130
hostInterfaces, err := safe.StateListAll[*network.LinkStatus](ctx, st)
129131
if err != nil {
130132
return nil, fmt.Errorf("error listing host interfaces: %w", err)
131133
}
132134

133-
slaveIndex := 0
135+
bondSlaveIndexes := map[string]int{}
136+
firstBond := ""
134137

135138
for _, iface := range equinixMetadata.Network.Interfaces {
136139
if iface.Bond == "" {
137140
continue
138141
}
139142

140-
if bondName != "" && iface.Bond != bondName {
141-
return nil, stderrors.New("encountered multiple bonds. this is unexpected in the equinix metal platform")
143+
if firstBond == "" {
144+
firstBond = iface.Bond
142145
}
143146

144-
bondName = iface.Bond
145-
146147
found := false
147148

148149
hostInterfaceIter := hostInterfaces.Iterator()
@@ -154,17 +155,20 @@ func (p *EquinixMetal) ParseMetadata(ctx context.Context, equinixMetadata *Metad
154155
if hostInterfaceIter.Value().TypedSpec().PermanentAddr.String() == iface.MAC {
155156
found = true
156157

158+
slaveIndex := bondSlaveIndexes[iface.Bond]
159+
157160
networkConfig.Links = append(networkConfig.Links,
158161
network.LinkSpecSpec{
159162
Name: hostInterfaceIter.Value().Metadata().ID(),
160163
Up: true,
161164
BondSlave: network.BondSlave{
162-
MasterName: bondName,
165+
MasterName: iface.Bond,
163166
SlaveIndex: slaveIndex,
164167
},
165168
ConfigLayer: network.ConfigPlatform,
166169
})
167-
slaveIndex++
170+
171+
bondSlaveIndexes[iface.Bond]++
168172

169173
break
170174
}
@@ -173,39 +177,44 @@ func (p *EquinixMetal) ParseMetadata(ctx context.Context, equinixMetadata *Metad
173177
if !found {
174178
log.Printf("interface with MAC %q wasn't found on the host, adding with the name from metadata", iface.MAC)
175179

180+
slaveIndex := bondSlaveIndexes[iface.Bond]
181+
176182
networkConfig.Links = append(networkConfig.Links,
177183
network.LinkSpecSpec{
178184
ConfigLayer: network.ConfigPlatform,
179185
Name: iface.Name,
180186
Up: true,
181187
BondSlave: network.BondSlave{
182-
MasterName: bondName,
188+
MasterName: iface.Bond,
183189
SlaveIndex: slaveIndex,
184190
},
185191
})
186-
slaveIndex++
192+
193+
bondSlaveIndexes[iface.Bond]++
187194
}
188195
}
189196

190-
bondLink := network.LinkSpecSpec{
191-
ConfigLayer: network.ConfigPlatform,
192-
Name: bondName,
193-
Logical: true,
194-
Up: true,
195-
Kind: network.LinkKindBond,
196-
Type: nethelpers.LinkEther,
197-
BondMaster: network.BondMasterSpec{
198-
Mode: bondMode,
199-
DownDelay: 200,
200-
MIIMon: 100,
201-
UpDelay: 200,
202-
HashPolicy: nethelpers.BondXmitPolicyLayer34,
203-
},
204-
}
197+
for bondName := range bondSlaveIndexes {
198+
bondLink := network.LinkSpecSpec{
199+
ConfigLayer: network.ConfigPlatform,
200+
Name: bondName,
201+
Logical: true,
202+
Up: true,
203+
Kind: network.LinkKindBond,
204+
Type: nethelpers.LinkEther,
205+
BondMaster: network.BondMasterSpec{
206+
Mode: bondMode,
207+
DownDelay: 200,
208+
MIIMon: 100,
209+
UpDelay: 200,
210+
HashPolicy: nethelpers.BondXmitPolicyLayer34,
211+
},
212+
}
205213

206-
networkadapter.BondMasterSpec(&bondLink.BondMaster).FillDefaults()
214+
networkadapter.BondMasterSpec(&bondLink.BondMaster).FillDefaults()
207215

208-
networkConfig.Links = append(networkConfig.Links, bondLink)
216+
networkConfig.Links = append(networkConfig.Links, bondLink)
217+
}
209218

210219
// 2. addresses
211220

@@ -233,7 +242,7 @@ func (p *EquinixMetal) ParseMetadata(ctx context.Context, equinixMetadata *Metad
233242
networkConfig.Addresses = append(networkConfig.Addresses,
234243
network.AddressSpecSpec{
235244
ConfigLayer: network.ConfigPlatform,
236-
LinkName: bondName,
245+
LinkName: firstBond,
237246
Address: ipAddr,
238247
Scope: nethelpers.ScopeGlobal,
239248
Flags: nethelpers.AddressFlags(nethelpers.AddressPermanent),
@@ -249,6 +258,7 @@ func (p *EquinixMetal) ParseMetadata(ctx context.Context, equinixMetadata *Metad
249258
}
250259

251260
// 3. routes
261+
var privateGateway netip.Addr
252262

253263
for _, addr := range equinixMetadata.Network.Addresses {
254264
if !(addr.Enabled && addr.Management) {
@@ -275,7 +285,7 @@ func (p *EquinixMetal) ParseMetadata(ctx context.Context, equinixMetadata *Metad
275285
route := network.RouteSpecSpec{
276286
ConfigLayer: network.ConfigPlatform,
277287
Gateway: gw,
278-
OutLinkName: bondName,
288+
OutLinkName: firstBond,
279289
Table: nethelpers.TableMain,
280290
Protocol: nethelpers.ProtocolStatic,
281291
Type: nethelpers.TypeUnicast,
@@ -298,6 +308,8 @@ func (p *EquinixMetal) ParseMetadata(ctx context.Context, equinixMetadata *Metad
298308
return nil, err
299309
}
300310

311+
privateGateway = gw
312+
301313
dest, err := netip.ParsePrefix(privSubnet)
302314
if err != nil {
303315
return nil, err
@@ -307,7 +319,7 @@ func (p *EquinixMetal) ParseMetadata(ctx context.Context, equinixMetadata *Metad
307319
ConfigLayer: network.ConfigPlatform,
308320
Gateway: gw,
309321
Destination: dest,
310-
OutLinkName: bondName,
322+
OutLinkName: firstBond,
311323
Table: nethelpers.TableMain,
312324
Protocol: nethelpers.ProtocolStatic,
313325
Type: nethelpers.TypeUnicast,
@@ -347,6 +359,36 @@ func (p *EquinixMetal) ParseMetadata(ctx context.Context, equinixMetadata *Metad
347359
ProviderID: fmt.Sprintf("equinixmetal://%s", equinixMetadata.ID),
348360
}
349361

362+
// 6. BGP neighbors
363+
364+
for _, bgpNeighbor := range equinixMetadata.BGPNeighbors {
365+
if bgpNeighbor.AddressFamily != 4 {
366+
continue
367+
}
368+
369+
for _, peerIP := range bgpNeighbor.PeerIPs {
370+
peer, err := netip.ParseAddr(peerIP)
371+
if err != nil {
372+
return nil, err
373+
}
374+
375+
route := network.RouteSpecSpec{
376+
ConfigLayer: network.ConfigPlatform,
377+
Gateway: privateGateway,
378+
Destination: netip.PrefixFrom(peer, 32),
379+
OutLinkName: firstBond,
380+
Table: nethelpers.TableMain,
381+
Protocol: nethelpers.ProtocolStatic,
382+
Type: nethelpers.TypeUnicast,
383+
Family: nethelpers.FamilyInet4,
384+
}
385+
386+
route.Normalize()
387+
388+
networkConfig.Routes = append(networkConfig.Routes, route)
389+
}
390+
}
391+
350392
return networkConfig, nil
351393
}
352394

internal/app/machined/pkg/runtime/v1alpha1/platform/equinixmetal/metadata.go

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,12 @@ package equinixmetal
66

77
// MetadataConfig holds equinixmetal metadata info.
88
type MetadataConfig struct {
9-
ID string `json:"id"`
10-
Hostname string `json:"hostname"`
11-
Plan string `json:"plan"`
12-
Metro string `json:"metro"`
13-
Facility string `json:"facility"`
14-
Network Network `json:"network"`
15-
PrivateSubnets []string `json:"private_subnets"`
9+
ID string `json:"id"`
10+
Hostname string `json:"hostname"`
11+
Plan string `json:"plan"`
12+
Metro string `json:"metro"`
13+
Facility string `json:"facility"`
14+
Network Network `json:"network"`
15+
BGPNeighbors []BGPNeighbor `json:"bgp_neighbors"`
16+
PrivateSubnets []string `json:"private_subnets"`
1617
}

internal/app/machined/pkg/runtime/v1alpha1/platform/equinixmetal/testdata/expected.yaml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,28 @@ routes:
9595
flags: ""
9696
protocol: static
9797
layer: platform
98+
- family: inet4
99+
dst: 169.254.255.1/32
100+
src: ""
101+
gateway: 10.66.142.16
102+
outLinkName: bond0
103+
table: main
104+
scope: global
105+
type: unicast
106+
flags: ""
107+
protocol: static
108+
layer: platform
109+
- family: inet4
110+
dst: 169.254.255.2/32
111+
src: ""
112+
gateway: 10.66.142.16
113+
outLinkName: bond0
114+
table: main
115+
scope: global
116+
type: unicast
117+
flags: ""
118+
protocol: static
119+
layer: platform
98120
hostnames:
99121
- hostname: infra-green-ci
100122
domainname: ""

internal/app/machined/pkg/runtime/v1alpha1/platform/equinixmetal/testdata/metadata.json

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,38 @@
111111
],
112112
"metal_gateways": []
113113
},
114+
"bgp_neighbors": [
115+
{
116+
"address_family": 4,
117+
"customer_as": 65000,
118+
"customer_ip": "10.67.50.1",
119+
"md5_enabled": false,
120+
"md5_password": null,
121+
"multihop": true,
122+
"peer_as": 65530,
123+
"peer_ips": [
124+
"169.254.255.1",
125+
"169.254.255.2"
126+
],
127+
"routes_in": [],
128+
"routes_out": []
129+
},
130+
{
131+
"address_family": 6,
132+
"customer_as": 65000,
133+
"customer_ip": "2604:1380:45e1:5000::1",
134+
"md5_enabled": false,
135+
"md5_password": null,
136+
"multihop": true,
137+
"peer_as": 65530,
138+
"peer_ips": [
139+
"fc00:0000:0000:0000:0000:0000:0000:000e",
140+
"fc00:0000:0000:0000:0000:0000:0000:000f"
141+
],
142+
"routes_in": [],
143+
"routes_out": []
144+
}
145+
],
114146
"api_url": "https://metadata.packet.net",
115147
"phone_home_url": "http://tinkerbell.ny5.packet.net/phone-home",
116148
"user_state_url": "http://tinkerbell.ny5.packet.net/events"

website/content/v1.6/_index.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ no_list: true
44
linkTitle: "Documentation"
55
cascade:
66
type: docs
7-
lastRelease: v1.6.4
8-
kubernetesRelease: "1.29.1"
7+
lastRelease: v1.6.7
8+
kubernetesRelease: "1.29.3"
99
prevKubernetesRelease: "1.28.3"
1010
nvidiaContainerToolkitRelease: "v1.13.5"
1111
nvidiaDriverRelease: "535.129.03"

0 commit comments

Comments
 (0)