Skip to content

Commit 0c14243

Browse files
committed
add support for provider network vlan interfaces
1 parent e3924e0 commit 0c14243

File tree

10 files changed

+635
-17
lines changed

10 files changed

+635
-17
lines changed

charts/kube-ovn-v2/crds/kube-ovn-crd.yaml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2916,6 +2916,15 @@ spec:
29162916
type: array
29172917
items:
29182918
type: string
2919+
preserveVlanInterfaces:
2920+
type: boolean
2921+
description: Enable automatic detection and preservation of VLAN interfaces
2922+
vlanInterfaces:
2923+
type: array
2924+
items:
2925+
type: string
2926+
pattern: '^[a-zA-Z0-9_-]+\.[0-9]{1,4}$'
2927+
description: Optional explicit list of VLAN interface names to preserve (e.g., eth0.10, bond0.20)
29192928
required:
29202929
- defaultInterface
29212930
status:

charts/kube-ovn/templates/kube-ovn-crd.yaml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2916,6 +2916,15 @@ spec:
29162916
type: array
29172917
items:
29182918
type: string
2919+
preserveVlanInterfaces:
2920+
type: boolean
2921+
description: Enable automatic detection and preservation of VLAN interfaces
2922+
vlanInterfaces:
2923+
type: array
2924+
items:
2925+
type: string
2926+
pattern: '^[a-zA-Z0-9_-]+\.[0-9]{1,4}$'
2927+
description: Optional explicit list of VLAN interface names to preserve (e.g., eth0.10, bond0.20)
29192928
required:
29202929
- defaultInterface
29212930
status:

pkg/apis/kubeovn/v1/provider-network.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@ type ProviderNetworkSpec struct {
3737
NodeSelector *metav1.LabelSelector `json:"nodeSelector,omitempty"`
3838
ExcludeNodes []string `json:"excludeNodes,omitempty"`
3939
ExchangeLinkName bool `json:"exchangeLinkName,omitempty"`
40+
// Enable automatic detection and preservation of existing VLAN interfaces on the default interface
41+
PreserveVlanInterfaces bool `json:"preserveVlanInterfaces,omitempty"`
42+
// Explicit list of VLAN interface names to preserve (e.g., eth0.10, bond0.20)
43+
VlanInterfaces []string `json:"vlanInterfaces,omitempty"`
4044
}
4145

4246
type ProviderNetworkCondition struct {

pkg/daemon/controller.go

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,7 @@ func (c *Controller) initProviderNetwork(pn *kubeovnv1.ProviderNetwork, node *v1
360360
fmt.Sprintf(util.ProviderNetworkInterfaceTemplate, pn.Name): nil,
361361
fmt.Sprintf(util.ProviderNetworkMtuTemplate, pn.Name): nil,
362362
fmt.Sprintf(util.ProviderNetworkExcludeTemplate, pn.Name): nil,
363+
fmt.Sprintf(util.ProviderNetworkVlanIntTemplate, pn.Name): nil,
363364
}
364365

365366
vlans := strset.NewWithSize(len(pn.Status.Vlans) + 1)
@@ -378,10 +379,52 @@ func (c *Controller) initProviderNetwork(pn *kubeovnv1.ProviderNetwork, node *v1
378379
// always add trunk 0 so that the ovs bridge can communicate with the external network
379380
vlans.Add("0")
380381

382+
// VLAN sub-interface handling - use map for efficiency
383+
vlanInterfaceMap := make(map[string]int) // interfaceName -> vlanID
384+
385+
// Process explicitly specified VLAN interfaces
386+
if len(pn.Spec.VlanInterfaces) > 0 {
387+
klog.Infof("Processing %d explicitly specified VLAN interfaces", len(pn.Spec.VlanInterfaces))
388+
for _, vlanIfName := range pn.Spec.VlanInterfaces {
389+
if util.CheckInterfaceExists(vlanIfName) {
390+
// Extract VLAN ID from interface name (e.g., "eth0.10" -> 10)
391+
vlanID, err := util.ExtractVlanIDFromInterface(vlanIfName)
392+
if err != nil {
393+
klog.Warningf("Failed to extract VLAN ID from interface %s: %v", vlanIfName, err)
394+
continue
395+
}
396+
vlanInterfaceMap[vlanIfName] = vlanID
397+
vlans.Add(strconv.Itoa(vlanID))
398+
klog.V(3).Infof("Added explicit VLAN interface %s (VLAN ID %d)", vlanIfName, vlanID)
399+
} else {
400+
klog.Warningf("Explicitly specified VLAN interface %s does not exist, skipping", vlanIfName)
401+
}
402+
}
403+
}
404+
405+
// Auto-detection of additional VLAN interfaces (if enabled)
406+
if pn.Spec.PreserveVlanInterfaces {
407+
klog.Infof("Auto-detecting VLAN interfaces on %s", nic)
408+
vlanIDs := util.DetectVlanInterfaces(nic)
409+
for _, vlanID := range vlanIDs {
410+
vlanIfName := fmt.Sprintf("%s.%d", nic, vlanID)
411+
// Only add if not already explicitly specified
412+
if _, exists := vlanInterfaceMap[vlanIfName]; !exists {
413+
vlanInterfaceMap[vlanIfName] = vlanID
414+
vlans.Add(strconv.Itoa(vlanID))
415+
klog.V(3).Infof("Auto-detected VLAN interface %s (VLAN ID %d)", vlanIfName, vlanID)
416+
} else {
417+
klog.V(3).Infof("VLAN interface %s already explicitly specified, skipping auto-detection", vlanIfName)
418+
}
419+
}
420+
klog.Infof("Auto-detected %d additional VLAN interfaces for %s", len(vlanIDs), nic)
421+
}
422+
381423
var mtu int
382424
var err error
383425
klog.V(3).Infof("ovs init provider network %s", pn.Name)
384-
if mtu, err = c.ovsInitProviderNetwork(pn.Name, nic, vlans.List(), pn.Spec.ExchangeLinkName, c.config.MacLearningFallback); err != nil {
426+
// Configure main interface with ALL VLANs (including detected ones) in trunk
427+
if mtu, err = c.ovsInitProviderNetwork(pn.Name, nic, vlans.List(), pn.Spec.ExchangeLinkName, c.config.MacLearningFallback, vlanInterfaceMap); err != nil {
385428
delete(patch, fmt.Sprintf(util.ProviderNetworkExcludeTemplate, pn.Name))
386429
if err1 := util.PatchLabels(c.config.KubeClient.CoreV1().Nodes(), node.Name, patch); err1 != nil {
387430
klog.Errorf("failed to patch annotations of node %s: %v", node.Name, err1)
@@ -393,6 +436,9 @@ func (c *Controller) initProviderNetwork(pn *kubeovnv1.ProviderNetwork, node *v1
393436
patch[fmt.Sprintf(util.ProviderNetworkReadyTemplate, pn.Name)] = "true"
394437
patch[fmt.Sprintf(util.ProviderNetworkInterfaceTemplate, pn.Name)] = nic
395438
patch[fmt.Sprintf(util.ProviderNetworkMtuTemplate, pn.Name)] = strconv.Itoa(mtu)
439+
if len(vlanInterfaceMap) > 0 {
440+
patch[fmt.Sprintf(util.ProviderNetworkVlanIntTemplate, pn.Name)] = "true"
441+
}
396442
if err = util.PatchLabels(c.config.KubeClient.CoreV1().Nodes(), node.Name, patch); err != nil {
397443
klog.Errorf("failed to patch labels of node %s: %v", node.Name, err)
398444
return err
@@ -481,6 +527,7 @@ func (c *Controller) handleDeleteProviderNetwork(pn *kubeovnv1.ProviderNetwork)
481527
fmt.Sprintf(util.ProviderNetworkInterfaceTemplate, pn.Name): nil,
482528
fmt.Sprintf(util.ProviderNetworkMtuTemplate, pn.Name): nil,
483529
fmt.Sprintf(util.ProviderNetworkExcludeTemplate, pn.Name): nil,
530+
fmt.Sprintf(util.ProviderNetworkVlanIntTemplate, pn.Name): nil,
484531
}
485532
if err = util.PatchLabels(c.config.KubeClient.CoreV1().Nodes(), node.Name, patch); err != nil {
486533
klog.Errorf("failed to patch labels of node %s: %v", node.Name, err)

pkg/daemon/init.go

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -97,8 +97,7 @@ func InitMirror(config *Configuration) error {
9797
return configureEmptyMirror(config.MirrorNic, config.MTU)
9898
}
9999

100-
func (c *Controller) ovsInitProviderNetwork(provider, nic string, trunks []string, exchangeLinkName, macLearningFallback bool) (int, error) {
101-
// create and configure external bridge
100+
func (c *Controller) ovsInitProviderNetwork(provider, nic string, trunks []string, exchangeLinkName, macLearningFallback bool, vlanInterfaceMap map[string]int) (int, error) { // create and configure external bridge
102101
brName := util.ExternalBridgeName(provider)
103102
if exchangeLinkName {
104103
exchanged, err := c.changeProvideNicName(nic, brName)
@@ -112,7 +111,7 @@ func (c *Controller) ovsInitProviderNetwork(provider, nic string, trunks []strin
112111
}
113112

114113
klog.V(3).Infof("configure external bridge %s", brName)
115-
if err := c.configExternalBridge(provider, brName, nic, exchangeLinkName, macLearningFallback); err != nil {
114+
if err := c.configExternalBridge(provider, brName, nic, exchangeLinkName, macLearningFallback, vlanInterfaceMap); err != nil {
116115
errMsg := fmt.Errorf("failed to create and configure external bridge %s: %w", brName, err)
117116
klog.Error(errMsg)
118117
return 0, errMsg
@@ -134,6 +133,15 @@ func (c *Controller) ovsInitProviderNetwork(provider, nic string, trunks []strin
134133
return 0, errMsg
135134
}
136135

136+
// add vlan interfaces to the external bridge
137+
if len(vlanInterfaceMap) > 0 {
138+
if err = c.configProviderVlanInterfaces(vlanInterfaceMap, brName); err != nil {
139+
errMsg := fmt.Errorf("failed to add vlan interfaces to external bridge %s: %w", brName, err)
140+
klog.Error(errMsg)
141+
return 0, errMsg
142+
}
143+
}
144+
137145
return mtu, nil
138146
}
139147

@@ -183,10 +191,19 @@ func (c *Controller) ovsCleanProviderNetwork(provider string) error {
183191
if output != "" {
184192
continue
185193
}
186-
klog.Infof("removing ovs port %s from bridge %s", port, brName)
187-
if err = c.removeProviderNic(port, brName); err != nil {
188-
klog.Errorf("failed to remove port %s from external bridge %s: %v", port, brName, err)
189-
return err
194+
// Check if this is a VLAN internal port (e.g., br-eth0-vlan10)
195+
if matched, vlanID := util.IsVlanInternalPort(port); matched {
196+
klog.Infof("removing VLAN internal port %s (VLAN %d) from bridge %s", port, vlanID, brName)
197+
if err = c.removeProviderVlanInterface(port, brName, vlanID); err != nil {
198+
klog.Errorf("failed to remove VLAN internal port %s from external bridge %s: %v", port, brName, err)
199+
return err
200+
}
201+
} else {
202+
klog.Infof("removing ovs port %s from bridge %s", port, brName)
203+
if err = c.removeProviderNic(port, brName); err != nil {
204+
klog.Errorf("failed to remove port %s from external bridge %s: %v", port, brName, err)
205+
return err
206+
}
190207
}
191208
klog.Infof("ovs port %s has been removed from bridge %s", port, brName)
192209
}

pkg/daemon/ovs.go

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,7 @@ func removeOvnMapping(name, key string) error {
233233
return setOvnMappings(name, mappings)
234234
}
235235

236-
func (c *Controller) configExternalBridge(provider, bridge, nic string, exchangeLinkName, macLearningFallback bool) error {
236+
func (c *Controller) configExternalBridge(provider, bridge, nic string, exchangeLinkName, macLearningFallback bool, vlanInterfaceMap map[string]int) error {
237237
// check if nic exists before configuring external bridge
238238
nicExists, err := linkExists(nic)
239239
if err != nil {
@@ -243,6 +243,8 @@ func (c *Controller) configExternalBridge(provider, bridge, nic string, exchange
243243
return fmt.Errorf("nic %s does not exist", nic)
244244
}
245245

246+
klog.Infof("Configuring external bridge %s for provider %s, nic %s, and vlan interfaces %v", bridge, provider, nic, vlanInterfaceMap)
247+
246248
brExists, err := ovs.BridgeExists(bridge)
247249
if err != nil {
248250
return fmt.Errorf("failed to check OVS bridge existence: %w", err)
@@ -266,16 +268,37 @@ func (c *Controller) configExternalBridge(provider, bridge, nic string, exchange
266268
}
267269
if output != "" {
268270
for port := range strings.SplitSeq(output, "\n") {
269-
if port != nic {
270-
ok, err := ovs.ValidatePortVendor(port)
271-
if err != nil {
272-
return fmt.Errorf("failed to check vendor of port %s: %w", port, err)
271+
// Skip the main NIC or VLAN subinterfaces belonging to it
272+
if port == nic {
273+
klog.Infof("Skipping main NIC port %s on bridge %s", port, bridge)
274+
continue
275+
}
276+
// Check if this port is a VLAN internal port we created (e.g., br-eth0-vlan10)
277+
isVlanInternalPort := false
278+
for vlanIf, vlanID := range vlanInterfaceMap {
279+
expectedInternalPort := fmt.Sprintf("%s-vlan%d", bridge, vlanID)
280+
if port == expectedInternalPort {
281+
klog.Infof("Preserving VLAN internal port %s for VLAN interface %s on bridge %s", port, vlanIf, bridge)
282+
isVlanInternalPort = true
283+
break
273284
}
274-
if ok {
275-
if err = c.removeProviderNic(port, bridge); err != nil {
276-
return fmt.Errorf("failed to remove port %s from OVS bridge %s: %w", port, bridge, err)
277-
}
285+
}
286+
if isVlanInternalPort {
287+
continue
288+
}
289+
290+
ok, err := ovs.ValidatePortVendor(port)
291+
if err != nil {
292+
return fmt.Errorf("failed to check vendor of port %s: %w", port, err)
293+
}
294+
295+
if ok {
296+
klog.Infof("Removing unmanaged port %s from bridge %s", port, bridge)
297+
if err = c.removeProviderNic(port, bridge); err != nil {
298+
return fmt.Errorf("failed to remove port %s from OVS bridge %s: %w", port, bridge, err)
278299
}
300+
} else {
301+
klog.Infof("Port %s on bridge %s has different vendor, skipping removal", port, bridge)
279302
}
280303
}
281304
}

0 commit comments

Comments
 (0)