Skip to content

Commit 2238d47

Browse files
authored
feat: support static IP/MAC for multiple interfaces on same switch (kubeovn#6060)
When a Pod/VM has multiple interfaces attached to the same logical switch, allow assigning different static IP/MAC to each interface using specific annotations: <nad-name>.<nad-namespace>.kubernetes.io/ip_address.<interface> <nad-name>.<nad-namespace>.kubernetes.io/mac_address.<interface> The flat annotation ovn.kubernetes.io/ip_address is kept as fallback. Signed-off-by: Chris Chiu <chris.chiu@suse.com>
1 parent d74b7c6 commit 2238d47

File tree

1 file changed

+81
-14
lines changed

1 file changed

+81
-14
lines changed

pkg/controller/pod.go

Lines changed: 81 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1770,6 +1770,9 @@ type kubeovnNet struct {
17701770
AllowLiveMigration bool
17711771
IPRequest string
17721772
MacRequest string
1773+
NadName string
1774+
NadNamespace string
1775+
InterfaceName string
17731776
}
17741777

17751778
func (c *Controller) getPodAttachmentNet(pod *v1.Pod) ([]*kubeovnNet, error) {
@@ -1802,14 +1805,23 @@ func (c *Controller) getPodAttachmentNet(pod *v1.Pod) ([]*kubeovnNet, error) {
18021805
// ignore to return all existing subnets to clean its ip crd
18031806
ignoreSubnetNotExist := !pod.DeletionTimestamp.IsZero()
18041807

1808+
nadCounts := make(map[string]int)
1809+
for _, attach := range multusNets {
1810+
nadCounts[fmt.Sprintf("%s/%s", attach.Namespace, attach.Name)]++
1811+
}
1812+
18051813
result := make([]*kubeovnNet, 0, len(multusNets))
18061814
for _, attach := range multusNets {
1815+
nadKey := fmt.Sprintf("%s/%s", attach.Namespace, attach.Name)
18071816
network, err := c.netAttachLister.NetworkAttachmentDefinitions(attach.Namespace).Get(attach.Name)
18081817
if err != nil {
18091818
klog.Errorf("failed to get net-attach-def %s, %v", attach.Name, err)
18101819
if k8serrors.IsNotFound(err) && ignoreSubnetNotExist {
18111820
// NAD deleted before pod, find subnet for cleanup
18121821
providerName := fmt.Sprintf("%s.%s.%s", attach.Name, attach.Namespace, util.OvnProvider)
1822+
if nadCounts[nadKey] > 1 && attach.InterfaceRequest != "" {
1823+
providerName = fmt.Sprintf("%s.%s", providerName, attach.InterfaceRequest)
1824+
}
18131825
subnetName := pod.Annotations[fmt.Sprintf(util.LogicalSwitchAnnotationTemplate, providerName)]
18141826
if subnetName == "" {
18151827
for _, subnet := range subnets {
@@ -1837,10 +1849,13 @@ func (c *Controller) getPodAttachmentNet(pod *v1.Pod) ([]*kubeovnNet, error) {
18371849

18381850
klog.Infof("pod %s/%s net-attach-def %s not found, using subnet %s for cleanup", pod.Namespace, pod.Name, attach.Name, subnetName)
18391851
result = append(result, &kubeovnNet{
1840-
Type: providerTypeIPAM,
1841-
ProviderName: providerName,
1842-
Subnet: subnet,
1843-
IsDefault: util.IsDefaultNet(pod.Annotations[util.DefaultNetworkAnnotation], attach),
1852+
Type: providerTypeIPAM,
1853+
ProviderName: providerName,
1854+
Subnet: subnet,
1855+
IsDefault: util.IsDefaultNet(pod.Annotations[util.DefaultNetworkAnnotation], attach),
1856+
NadName: attach.Name,
1857+
NadNamespace: attach.Namespace,
1858+
InterfaceName: attach.InterfaceRequest,
18441859
})
18451860
continue
18461861
}
@@ -1864,6 +1879,9 @@ func (c *Controller) getPodAttachmentNet(pod *v1.Pod) ([]*kubeovnNet, error) {
18641879
isDefault := util.IsDefaultNet(pod.Annotations[util.DefaultNetworkAnnotation], attach)
18651880

18661881
providerName = fmt.Sprintf("%s.%s.%s", attach.Name, attach.Namespace, util.OvnProvider)
1882+
if nadCounts[nadKey] > 1 && attach.InterfaceRequest != "" {
1883+
providerName = fmt.Sprintf("%s.%s", providerName, attach.InterfaceRequest)
1884+
}
18671885
if pod.Annotations[kubevirtv1.MigrationJobNameAnnotation] != "" {
18681886
allowLiveMigration = true
18691887
}
@@ -1917,18 +1935,24 @@ func (c *Controller) getPodAttachmentNet(pod *v1.Pod) ([]*kubeovnNet, error) {
19171935
AllowLiveMigration: allowLiveMigration,
19181936
MacRequest: attach.MacRequest,
19191937
IPRequest: strings.Join(attach.IPRequest, ","),
1938+
NadName: attach.Name,
1939+
NadNamespace: attach.Namespace,
1940+
InterfaceName: attach.InterfaceRequest,
19201941
}
19211942
result = append(result, ret)
19221943
} else {
19231944
providerName = fmt.Sprintf("%s.%s", attach.Name, attach.Namespace)
19241945
for _, subnet := range subnets {
19251946
if subnet.Spec.Provider == providerName {
19261947
result = append(result, &kubeovnNet{
1927-
Type: providerTypeIPAM,
1928-
ProviderName: providerName,
1929-
Subnet: subnet,
1930-
MacRequest: attach.MacRequest,
1931-
IPRequest: strings.Join(attach.IPRequest, ","),
1948+
Type: providerTypeIPAM,
1949+
ProviderName: providerName,
1950+
Subnet: subnet,
1951+
MacRequest: attach.MacRequest,
1952+
IPRequest: strings.Join(attach.IPRequest, ","),
1953+
NadName: attach.Name,
1954+
NadNamespace: attach.Namespace,
1955+
InterfaceName: attach.InterfaceRequest,
19321956
})
19331957
break
19341958
}
@@ -1993,19 +2017,28 @@ func (c *Controller) acquireAddress(pod *v1.Pod, podNet *kubeovnNet) (string, st
19932017
}
19942018

19952019
var macPointer *string
1996-
if isOvnSubnet(podNet.Subnet) {
2020+
if podNet.NadName != "" && podNet.NadNamespace != "" && podNet.InterfaceName != "" {
2021+
key := perInterfaceMACAnnotationKey(podNet.NadName, podNet.NadNamespace, podNet.InterfaceName)
2022+
if macStr := pod.Annotations[key]; macStr != "" {
2023+
if _, err := net.ParseMAC(macStr); err != nil {
2024+
return "", "", "", podNet.Subnet, err
2025+
}
2026+
macPointer = &macStr
2027+
}
2028+
}
2029+
2030+
if macPointer == nil && isOvnSubnet(podNet.Subnet) {
19972031
annoMAC := pod.Annotations[fmt.Sprintf(util.MacAddressAnnotationTemplate, podNet.ProviderName)]
19982032
if annoMAC != "" {
19992033
if _, err := net.ParseMAC(annoMAC); err != nil {
20002034
return "", "", "", podNet.Subnet, err
20012035
}
20022036
macPointer = &annoMAC
20032037
}
2004-
} else {
2038+
} else if macPointer == nil {
20052039
macPointer = ptr.To("")
20062040
}
20072041

2008-
var err error
20092042
var nsNets []*kubeovnNet
20102043
ippoolStr := pod.Annotations[fmt.Sprintf(util.IPPoolAnnotationTemplate, podNet.ProviderName)]
20112044
subnetStr := pod.Annotations[fmt.Sprintf(util.LogicalSwitchAnnotationTemplate, podNet.ProviderName)]
@@ -2079,6 +2112,14 @@ func (c *Controller) acquireAddress(pod *v1.Pod, podNet *kubeovnNet) (string, st
20792112
// Random allocate
20802113
if pod.Annotations[fmt.Sprintf(util.IPAddressAnnotationTemplate, podNet.ProviderName)] == "" &&
20812114
ippoolStr == "" {
2115+
// check new IP annotation
2116+
if podNet.NadName != "" && podNet.NadNamespace != "" && podNet.InterfaceName != "" {
2117+
annoKey := perInterfaceIPAnnotationKey(podNet.NadName, podNet.NadNamespace, podNet.InterfaceName)
2118+
if ipStr := pod.Annotations[annoKey]; ipStr != "" {
2119+
return c.acquireStaticAddressHelper(pod, podNet, portName, macPointer, ippoolStr, nsNets, isStsPod, key)
2120+
}
2121+
}
2122+
20822123
var skippedAddrs []string
20832124
for {
20842125
ipv4, ipv6, mac, err := c.ipam.GetRandomAddress(key, portName, macPointer, podNet.Subnet.Name, "", skippedAddrs, !podNet.AllowLiveMigration)
@@ -2104,6 +2145,13 @@ func (c *Controller) acquireAddress(pod *v1.Pod, podNet *kubeovnNet) (string, st
21042145
}
21052146
}
21062147

2148+
return c.acquireStaticAddressHelper(pod, podNet, portName, macPointer, ippoolStr, nsNets, isStsPod, key)
2149+
}
2150+
2151+
func (c *Controller) acquireStaticAddressHelper(pod *v1.Pod, podNet *kubeovnNet, portName string, macPointer *string, ippoolStr string, nsNets []*kubeovnNet, isStsPod bool, key string) (string, string, string, *kubeovnv1.Subnet, error) {
2152+
var v4IP, v6IP, mac string
2153+
var err error
2154+
21072155
// The static ip can be assigned from any subnet after ns supports multi subnets
21082156
if nsNets == nil {
21092157
if nsNets, err = c.getNsAvailableSubnets(pod, podNet); err != nil {
@@ -2112,9 +2160,20 @@ func (c *Controller) acquireAddress(pod *v1.Pod, podNet *kubeovnNet) (string, st
21122160
}
21132161
}
21142162

2115-
var v4IP, v6IP, mac string
2116-
21172163
// Static allocate
2164+
if podNet.NadName != "" && podNet.NadNamespace != "" && podNet.InterfaceName != "" {
2165+
key := perInterfaceIPAnnotationKey(podNet.NadName, podNet.NadNamespace, podNet.InterfaceName)
2166+
if ipStr := pod.Annotations[key]; ipStr != "" {
2167+
for _, net := range nsNets {
2168+
v4IP, v6IP, mac, err = c.acquireStaticAddress(key, portName, ipStr, macPointer, net.Subnet.Name, net.AllowLiveMigration)
2169+
if err == nil {
2170+
return v4IP, v6IP, mac, net.Subnet, nil
2171+
}
2172+
}
2173+
return v4IP, v6IP, mac, podNet.Subnet, err
2174+
}
2175+
}
2176+
21182177
if ipStr := pod.Annotations[fmt.Sprintf(util.IPAddressAnnotationTemplate, podNet.ProviderName)]; ipStr != "" {
21192178
for _, net := range nsNets {
21202179
v4IP, v6IP, mac, err = c.acquireStaticAddress(key, portName, ipStr, macPointer, net.Subnet.Name, net.AllowLiveMigration)
@@ -2602,3 +2661,11 @@ func (c *Controller) checkIsPodVpcNatGw(pod *v1.Pod) (bool, string) {
26022661
}
26032662
return isVpcNatGw, vpcGwName
26042663
}
2664+
2665+
func perInterfaceIPAnnotationKey(nadName, nadNamespace, ifaceName string) string {
2666+
return fmt.Sprintf("%s.%s.kubernetes.io/ip_address.%s", nadName, nadNamespace, ifaceName)
2667+
}
2668+
2669+
func perInterfaceMACAnnotationKey(nadName, nadNamespace, ifaceName string) string {
2670+
return fmt.Sprintf("%s.%s.kubernetes.io/mac_address.%s", nadName, nadNamespace, ifaceName)
2671+
}

0 commit comments

Comments
 (0)