• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

kubeovn / kube-ovn / 26876176436

03 Jun 2026 09:32AM UTC coverage: 25.627% (-0.01%) from 25.64%
26876176436

push

github

web-flow
PR introduces changes to correctly support multiple network interfaces from same subnet (#6766)

* PR introduces changes to allow kube-ovn-cni and kube-ovn-controller to
correctly handle multiple network attachments from same subnets.

Please refer to GH issue https://github.com/kubeovn/kube-ovn/issues/6762
for more details.

Signed-off-by: Gaurav Mehta <gaurav.mehta@suse.com>

* fine tune annotation lookup to ignore default interface name or eth0 as the case may be when trying to lookup kubeovn annotations

Signed-off-by: Gaurav Mehta <gaurav.mehta@suse.com>

* fix provide name elements lookup

Signed-off-by: Gaurav Mehta <gaurav.mehta@suse.com>

* simplify interface logic to leverage pod annotations to identify if interface name needs to be used for lookup

Signed-off-by: Gaurav Mehta <gaurav.mehta@suse.com>

* include pr review feedback, drop extraneous comments and methods

Signed-off-by: Gaurav Mehta <gaurav.mehta@suse.com>

---------

Signed-off-by: Gaurav Mehta <gaurav.mehta@suse.com>

8 of 95 new or added lines in 4 files covered. (8.42%)

14791 of 57716 relevant lines covered (25.63%)

0.3 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/pkg/daemon/handler.go
1
package daemon
2

3
import (
4
        "context"
5
        "encoding/json"
6
        "fmt"
7
        "net"
8
        "net/http"
9
        "strconv"
10
        "strings"
11
        "time"
12

13
        "github.com/emicklei/go-restful/v3"
14
        v1 "k8s.io/api/core/v1"
15
        k8serrors "k8s.io/apimachinery/pkg/api/errors"
16
        metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
17
        "k8s.io/apimachinery/pkg/labels"
18
        "k8s.io/client-go/kubernetes"
19
        "k8s.io/klog/v2"
20
        kubevirtv1 "kubevirt.io/api/core/v1"
21

22
        kubeovnv1 "github.com/kubeovn/kube-ovn/pkg/apis/kubeovn/v1"
23
        clientset "github.com/kubeovn/kube-ovn/pkg/client/clientset/versioned"
24
        "github.com/kubeovn/kube-ovn/pkg/ovs"
25
        "github.com/kubeovn/kube-ovn/pkg/request"
26
        "github.com/kubeovn/kube-ovn/pkg/util"
27
)
28

29
const (
30
        gatewayCheckModeDisabled = iota
31
        gatewayCheckModePing
32
        gatewayCheckModeArping
33
        gatewayCheckModePingNotConcerned
34
        gatewayCheckModeArpingNotConcerned
35
)
36

37
type cniServerHandler struct {
38
        Config        *Configuration
39
        KubeClient    kubernetes.Interface
40
        KubeOvnClient clientset.Interface
41
        Controller    *Controller
42
}
43

44
func createCniServerHandler(config *Configuration, controller *Controller) *cniServerHandler {
×
45
        csh := &cniServerHandler{KubeClient: config.KubeClient, KubeOvnClient: config.KubeOvnClient, Config: config, Controller: controller}
×
46
        return csh
×
47
}
×
48

NEW
49
func (csh cniServerHandler) providerExists(provider, ifName string) (*kubeovnv1.Subnet, bool) {
×
50
        if util.IsOvnProvider(provider) {
×
51
                return nil, true
×
52
        }
×
53
        subnets, _ := csh.Controller.subnetsLister.List(labels.Everything())
×
NEW
54
        // for multi interface attachments the ifname is included in provider, for example, vm-overlay.default.ovn.net1
×
NEW
55
        // as a result if ifname is set, we need to append it to subnet provider when comparing with request provider
×
NEW
56
        // else no subnet will be found
×
NEW
57
        providerName, _ := strings.CutSuffix(provider, fmt.Sprintf(".%s", ifName))
×
58
        for _, subnet := range subnets {
×
NEW
59
                if subnet.Spec.Provider == providerName {
×
60
                        return subnet.DeepCopy(), true
×
61
                }
×
62
        }
63
        return nil, false
×
64
}
65

66
func (csh cniServerHandler) handleAdd(req *restful.Request, resp *restful.Response) {
×
67
        podRequest := request.CniRequest{}
×
68
        if err := req.ReadEntity(&podRequest); err != nil {
×
69
                errMsg := fmt.Errorf("parse add request failed %w", err)
×
70
                klog.Error(errMsg)
×
71
                if err := resp.WriteHeaderAndEntity(http.StatusBadRequest, request.CniResponse{Err: errMsg.Error()}); err != nil {
×
72
                        klog.Errorf("failed to write response, %v", err)
×
73
                }
×
74
                return
×
75
        }
76
        klog.V(5).Infof("request body is %v", podRequest)
×
NEW
77
        podSubnet, exist := csh.providerExists(podRequest.Provider, podRequest.IfName)
×
78
        if !exist {
×
79
                errMsg := fmt.Errorf("provider %s not bind to any subnet", podRequest.Provider)
×
80
                klog.Error(errMsg)
×
81
                if err := resp.WriteHeaderAndEntity(http.StatusBadRequest, request.CniResponse{Err: errMsg.Error()}); err != nil {
×
82
                        klog.Errorf("failed to write response, %v", err)
×
83
                }
×
84
                return
×
85
        }
86

87
        klog.Infof("add port request: %v", podRequest)
×
88
        if err := csh.validatePodRequest(&podRequest); err != nil {
×
89
                klog.Error(err)
×
90
                if err := resp.WriteHeaderAndEntity(http.StatusBadRequest, request.CniResponse{Err: err.Error()}); err != nil {
×
91
                        klog.Errorf("failed to write response, %v", err)
×
92
                }
×
93
                return
×
94
        }
95

96
        var gatewayCheckMode int
×
97
        var macAddr, ip, ipAddr, cidr, gw, subnet, ingress, egress, ingressBurst, egressBurst, providerNetwork, ifName, nicType, podNicName, vmName, latency, limit, loss, jitter, u2oInterconnectionIP, oldPodName string
×
98
        var routes []request.Route
×
99
        var isDefaultRoute, noIPAM bool
×
100
        var pod *v1.Pod
×
101
        var err error
×
NEW
102

×
NEW
103
        providerWithIfName := fmt.Sprintf("%s.%s", podRequest.Provider, podRequest.IfName)
×
NEW
104
        var appendIfName bool
×
NEW
105

×
106
        for range 20 {
×
107
                if pod, err = csh.Controller.podsLister.Pods(podRequest.PodNamespace).Get(podRequest.PodName); err != nil {
×
108
                        errMsg := fmt.Errorf("get pod %s/%s failed %w", podRequest.PodNamespace, podRequest.PodName, err)
×
109
                        klog.Error(errMsg)
×
110
                        if err := resp.WriteHeaderAndEntity(http.StatusInternalServerError, request.CniResponse{Err: errMsg.Error()}); err != nil {
×
111
                                klog.Errorf("failed to write response, %v", err)
×
112
                        }
×
113
                        return
×
114
                }
115

116
                // in case of multiple nics from same subnet
NEW
117
                _, ok := pod.Annotations[fmt.Sprintf(util.IPAddressAnnotationTemplate, providerWithIfName)]
×
NEW
118
                if ok {
×
NEW
119
                        appendIfName = true
×
NEW
120
                }
×
121

NEW
122
                ip = util.GetAnnotationWithIfNameOverride(pod.Annotations, podRequest.Provider, podRequest.IfName, util.IPAddressAnnotationTemplate, appendIfName)
×
NEW
123
                if ip == "" {
×
124
                        klog.Infof("wait address for pod %s/%s provider %s", podRequest.PodNamespace, podRequest.PodName, podRequest.Provider)
×
125
                        // wait controller assign an address
×
126
                        cniWaitAddressResult.WithLabelValues(nodeName).Inc()
×
127
                        time.Sleep(1 * time.Second)
×
128
                        continue
×
129
                }
130

131
                if err := util.ValidatePodNetwork(pod.Annotations); err != nil {
×
132
                        klog.Errorf("validate pod %s/%s failed, %v", podRequest.PodNamespace, podRequest.PodName, err)
×
133
                        // wait controller assign an address
×
134
                        cniWaitAddressResult.WithLabelValues(nodeName).Inc()
×
135
                        time.Sleep(1 * time.Second)
×
136
                        continue
×
137
                }
138

NEW
139
                cidr = util.GetAnnotationWithIfNameOverride(pod.Annotations, podRequest.Provider, podRequest.IfName, util.CidrAnnotationTemplate, appendIfName)
×
NEW
140
                gw = util.GetAnnotationWithIfNameOverride(pod.Annotations, podRequest.Provider, podRequest.IfName, util.GatewayAnnotationTemplate, appendIfName)
×
NEW
141
                subnet = util.GetAnnotationWithIfNameOverride(pod.Annotations, podRequest.Provider, podRequest.IfName, util.LogicalSwitchAnnotationTemplate, appendIfName)
×
NEW
142
                ingress = util.GetAnnotationWithIfNameOverride(pod.Annotations, podRequest.Provider, podRequest.IfName, util.IngressRateAnnotationTemplate, appendIfName)
×
NEW
143
                egress = util.GetAnnotationWithIfNameOverride(pod.Annotations, podRequest.Provider, podRequest.IfName, util.EgressRateAnnotationTemplate, appendIfName)
×
NEW
144
                ingressBurst = util.GetAnnotationWithIfNameOverride(pod.Annotations, podRequest.Provider, podRequest.IfName, util.IngressBurstAnnotationTemplate, appendIfName)
×
NEW
145
                egressBurst = util.GetAnnotationWithIfNameOverride(pod.Annotations, podRequest.Provider, podRequest.IfName, util.EgressBurstAnnotationTemplate, appendIfName)
×
NEW
146
                latency = util.GetAnnotationWithIfNameOverride(pod.Annotations, podRequest.Provider, podRequest.IfName, util.NetemQosLatencyAnnotationTemplate, appendIfName)
×
NEW
147
                limit = util.GetAnnotationWithIfNameOverride(pod.Annotations, podRequest.Provider, podRequest.IfName, util.NetemQosLimitAnnotationTemplate, appendIfName)
×
NEW
148
                loss = util.GetAnnotationWithIfNameOverride(pod.Annotations, podRequest.Provider, podRequest.IfName, util.NetemQosLossAnnotationTemplate, appendIfName)
×
NEW
149
                jitter = util.GetAnnotationWithIfNameOverride(pod.Annotations, podRequest.Provider, podRequest.IfName, util.NetemQosJitterAnnotationTemplate, appendIfName)
×
NEW
150
                providerNetwork = util.GetAnnotationWithIfNameOverride(pod.Annotations, podRequest.Provider, podRequest.IfName, util.ProviderNetworkTemplate, appendIfName)
×
NEW
151
                vmName = util.GetAnnotationWithIfNameOverride(pod.Annotations, podRequest.Provider, podRequest.IfName, util.VMAnnotationTemplate, appendIfName)
×
152
                ipAddr, noIPAM, err = util.GetIPAddrWithMaskForCNI(ip, cidr)
×
153
                if err != nil {
×
154
                        errMsg := fmt.Errorf("failed to get ip address with mask, %w", err)
×
155
                        klog.Error(errMsg)
×
156
                        if err := resp.WriteHeaderAndEntity(http.StatusInternalServerError, request.CniResponse{Err: errMsg.Error()}); err != nil {
×
157
                                klog.Errorf("failed to write response, %v", err)
×
158
                        }
×
159
                        return
×
160
                }
161

162
                oldPodName = podRequest.PodName
×
163
                if s := pod.Annotations[fmt.Sprintf(util.RoutesAnnotationTemplate, podRequest.Provider)]; s != "" {
×
164
                        if err = json.Unmarshal([]byte(s), &routes); err != nil {
×
165
                                errMsg := fmt.Errorf("invalid routes for pod %s/%s: %w", pod.Namespace, pod.Name, err)
×
166
                                klog.Error(errMsg)
×
167
                                if err = resp.WriteHeaderAndEntity(http.StatusInternalServerError, request.CniResponse{Err: errMsg.Error()}); err != nil {
×
168
                                        klog.Errorf("failed to write response: %v", err)
×
169
                                }
×
170
                                return
×
171
                        }
172
                }
173
                if ifName = podRequest.IfName; ifName == "" {
×
174
                        ifName = "eth0"
×
175
                }
×
176

177
                // To support KubeVirt hotplug dpdk nic, forbidden set the volume name
178
                if podRequest.VhostUserSocketConsumption == util.ConsumptionKubevirt {
×
179
                        podRequest.VhostUserSocketVolumeName = util.VhostUserSocketVolumeName
×
180
                }
×
181

182
                switch {
×
183
                case podRequest.DeviceID != "":
×
184
                        nicType = util.OffloadType
×
185
                case podRequest.VhostUserSocketVolumeName != "":
×
186
                        nicType = util.DpdkType
×
187
                        if err = createShortSharedDir(pod, podRequest.VhostUserSocketVolumeName, podRequest.VhostUserSocketConsumption, csh.Config.KubeletDir); err != nil {
×
188
                                klog.Error(err.Error())
×
189
                                if err = resp.WriteHeaderAndEntity(http.StatusInternalServerError, request.CniResponse{Err: err.Error()}); err != nil {
×
190
                                        klog.Errorf("failed to write response: %v", err)
×
191
                                }
×
192
                                return
×
193
                        }
194
                default:
×
195
                        nicType = pod.Annotations[fmt.Sprintf(util.PodNicAnnotationTemplate, podRequest.Provider)]
×
196
                }
197

198
                switch pod.Annotations[fmt.Sprintf(util.DefaultRouteAnnotationTemplate, podRequest.Provider)] {
×
199
                case "true":
×
200
                        isDefaultRoute = true
×
201
                case "false":
×
202
                        isDefaultRoute = false
×
203
                default:
×
204
                        isDefaultRoute = ifName == "eth0"
×
205
                }
206

207
                if isDefaultRoute && pod.Annotations[fmt.Sprintf(util.RoutedAnnotationTemplate, podRequest.Provider)] != "true" && util.IsOvnProvider(podRequest.Provider) {
×
208
                        klog.Infof("wait route ready for pod %s/%s provider %s", podRequest.PodNamespace, podRequest.PodName, podRequest.Provider)
×
209
                        cniWaitRouteResult.WithLabelValues(nodeName).Inc()
×
210
                        time.Sleep(1 * time.Second)
×
211
                        continue
×
212
                }
213

214
                if vmName != "" {
×
215
                        podRequest.PodName = vmName
×
216
                }
×
217

218
                break
×
219
        }
220

NEW
221
        if util.GetAnnotationWithIfNameOverride(pod.Annotations, podRequest.Provider, podRequest.IfName, util.IPAddressAnnotationTemplate, appendIfName) == "" {
×
222
                err := fmt.Errorf("no address allocated to pod %s/%s provider %s, please see kube-ovn-controller logs to find errors", pod.Namespace, pod.Name, podRequest.Provider)
×
223
                klog.Error(err)
×
224
                if err := resp.WriteHeaderAndEntity(http.StatusInternalServerError, request.CniResponse{Err: err.Error()}); err != nil {
×
225
                        klog.Errorf("failed to write response, %v", err)
×
226
                }
×
227
                return
×
228
        }
229

230
        if subnet == "" && podSubnet != nil {
×
231
                subnet = podSubnet.Name
×
232
        }
×
233
        if !noIPAM {
×
NEW
234
                if err := csh.UpdateIPCR(podRequest, subnet, ip, appendIfName); err != nil {
×
235
                        if err := resp.WriteHeaderAndEntity(http.StatusInternalServerError, request.CniResponse{Err: err.Error()}); err != nil {
×
236
                                klog.Errorf("failed to write response, %v", err)
×
237
                        }
×
238
                        return
×
239
                }
240
        }
241

242
        if isDefaultRoute && pod.Annotations[fmt.Sprintf(util.RoutedAnnotationTemplate, podRequest.Provider)] != "true" && util.IsOvnProvider(podRequest.Provider) {
×
243
                err := fmt.Errorf("route is not ready for pod %s/%s provider %s, please see kube-ovn-controller logs to find errors", pod.Namespace, pod.Name, podRequest.Provider)
×
244
                klog.Error(err)
×
245
                if err := resp.WriteHeaderAndEntity(http.StatusInternalServerError, request.CniResponse{Err: err.Error()}); err != nil {
×
246
                        klog.Errorf("failed to write response, %v", err)
×
247
                }
×
248
                return
×
249
        }
250

251
        var mtu int
×
252
        routes = append(podRequest.Routes, routes...)
×
253
        if strings.HasSuffix(podRequest.Provider, util.OvnProvider) && subnet != "" {
×
254
                podSubnet, err := csh.Controller.subnetsLister.Get(subnet)
×
255
                if err != nil {
×
256
                        errMsg := fmt.Errorf("failed to get subnet %s: %w", subnet, err)
×
257
                        klog.Error(errMsg)
×
258
                        if err = resp.WriteHeaderAndEntity(http.StatusInternalServerError, request.CniResponse{Err: errMsg.Error()}); err != nil {
×
259
                                klog.Errorf("failed to write response: %v", err)
×
260
                        }
×
261
                        return
×
262
                }
263

264
                if podSubnet.Status.U2OInterconnectionIP == "" && podSubnet.Spec.U2OInterconnection {
×
265
                        errMsg := fmt.Errorf("failed to generate u2o ip on subnet %s", podSubnet.Name)
×
266
                        klog.Error(errMsg)
×
267
                        return
×
268
                }
×
269

270
                if podSubnet.Status.U2OInterconnectionIP != "" && podSubnet.Spec.U2OInterconnection {
×
271
                        u2oInterconnectionIP = podSubnet.Status.U2OInterconnectionIP
×
272
                }
×
273

274
                var vmMigration bool
×
275
                subnetHasVlan := podSubnet.Spec.Vlan != ""
×
276
                // skip ping check gateway for pods during live migration
×
277
                if pod.Annotations[kubevirtv1.MigrationJobNameAnnotation] == "" {
×
278
                        if subnetHasVlan && !podSubnet.Spec.LogicalGateway {
×
279
                                if podSubnet.Spec.DisableGatewayCheck {
×
280
                                        gatewayCheckMode = gatewayCheckModeArpingNotConcerned
×
281
                                } else {
×
282
                                        gatewayCheckMode = gatewayCheckModeArping
×
283
                                }
×
284
                        } else {
×
285
                                if podSubnet.Spec.DisableGatewayCheck {
×
286
                                        gatewayCheckMode = gatewayCheckModePingNotConcerned
×
287
                                } else {
×
288
                                        gatewayCheckMode = gatewayCheckModePing
×
289
                                }
×
290
                        }
291
                } else {
×
292
                        vmMigration = true
×
293
                }
×
294
                if pod.Annotations[fmt.Sprintf(util.ActivationStrategyTemplate, podRequest.Provider)] != "" {
×
295
                        gatewayCheckMode = gatewayCheckModeDisabled
×
296
                }
×
297

298
                if podSubnet.Spec.Mtu > 0 {
×
299
                        mtu = int(podSubnet.Spec.Mtu)
×
300
                } else {
×
301
                        if providerNetwork != "" && !podSubnet.Spec.LogicalGateway && !podSubnet.Spec.U2OInterconnection {
×
302
                                node, err := csh.Controller.nodesLister.Get(csh.Config.NodeName)
×
303
                                if err != nil {
×
304
                                        errMsg := fmt.Errorf("failed to get node %s: %w", csh.Config.NodeName, err)
×
305
                                        klog.Error(errMsg)
×
306
                                        if err = resp.WriteHeaderAndEntity(http.StatusInternalServerError, request.CniResponse{Err: errMsg.Error()}); err != nil {
×
307
                                                klog.Errorf("failed to write response: %v", err)
×
308
                                        }
×
309
                                        return
×
310
                                }
311
                                mtuStr := node.Labels[fmt.Sprintf(util.ProviderNetworkMtuTemplate, providerNetwork)]
×
312
                                if mtuStr != "" {
×
313
                                        if mtu, err = strconv.Atoi(mtuStr); err != nil || mtu <= 0 {
×
314
                                                errMsg := fmt.Errorf("failed to parse provider network MTU %s: %w", mtuStr, err)
×
315
                                                klog.Error(errMsg)
×
316
                                                if err = resp.WriteHeaderAndEntity(http.StatusInternalServerError, request.CniResponse{Err: errMsg.Error()}); err != nil {
×
317
                                                        klog.Errorf("failed to write response: %v", err)
×
318
                                                }
×
319
                                                return
×
320
                                        }
321
                                }
322
                        } else {
×
323
                                mtu = csh.Config.MTU
×
324
                        }
×
325
                }
326

327
                // Warn but keep the configured value: the provider-network or subnet
328
                // MTU reflects the real link MTU, and inflating it would push pods
329
                // to emit packets that the underlay cannot carry, breaking IPv4 too.
330
                if mtu > 0 && mtu < util.IPv6MinMTU {
×
331
                        subnetProtocol := util.CheckProtocol(podSubnet.Spec.CIDRBlock)
×
332
                        if subnetProtocol == kubeovnv1.ProtocolIPv6 || subnetProtocol == kubeovnv1.ProtocolDual {
×
333
                                klog.Warningf("subnet %s mtu %d is below the IPv6 minimum %d; IPv6 traffic on pod %s/%s will be dropped", podSubnet.Name, mtu, util.IPv6MinMTU, pod.Namespace, pod.Name)
×
334
                        }
×
335
                }
336

NEW
337
                macAddr = util.GetAnnotationWithIfNameOverride(pod.Annotations, podRequest.Provider, podRequest.IfName, util.MacAddressAnnotationTemplate, appendIfName)
×
338
                klog.Infof("create container interface %s mac %s, ip %s, cidr %s, gw %s, custom routes %v", ifName, macAddr, ipAddr, cidr, gw, routes)
×
339
                podNicName = ifName
×
340

×
341
                var encapIP string
×
342
                if podSubnet.Spec.NodeNetwork != "" {
×
343
                        encapIP, err = csh.Config.GetEncapIPByNetwork(podSubnet.Spec.NodeNetwork)
×
344
                        if err != nil {
×
345
                                errMsg := fmt.Errorf("failed to get encap IP for node network %s: %w", podSubnet.Spec.NodeNetwork, err)
×
346
                                klog.Error(errMsg)
×
347
                                if err = resp.WriteHeaderAndEntity(http.StatusInternalServerError, request.CniResponse{Err: errMsg.Error()}); err != nil {
×
348
                                        klog.Errorf("failed to write response: %v", err)
×
349
                                }
×
350
                                return
×
351
                        }
352
                }
353

354
                var localnetSubnet string
×
355
                if subnetHasVlan && !podSubnet.Spec.LogicalGateway {
×
356
                        localnetSubnet = subnet
×
357
                }
×
358

359
                switch nicType {
×
360
                case util.DpdkType:
×
361
                        err = csh.configureDpdkNic(podRequest.PodName, podRequest.PodNamespace, podRequest.Provider, podRequest.NetNs, podRequest.ContainerID, ifName, macAddr, mtu, ipAddr, gw, ingress, egress, ingressBurst, egressBurst, getShortSharedDir(pod.UID, podRequest.VhostUserSocketVolumeName), podRequest.VhostUserSocketName, podRequest.VhostUserSocketConsumption)
×
362
                        routes = nil
×
363
                default:
×
NEW
364
                        routes, err = csh.configureNic(podRequest.PodName, podRequest.PodNamespace, podRequest.Provider, podRequest.NetNs, podRequest.ContainerID, podRequest.VfDriver, ifName, macAddr, mtu, ipAddr, gw, isDefaultRoute, vmMigration, routes, podRequest.DNS.Nameservers, podRequest.DNS.Search, ingress, egress, ingressBurst, egressBurst, podRequest.DeviceID, latency, limit, loss, jitter, gatewayCheckMode, u2oInterconnectionIP, oldPodName, encapIP, localnetSubnet, appendIfName)
×
365
                }
366
                if err != nil {
×
367
                        errMsg := fmt.Errorf("configure nic %s for pod %s/%s failed: %w", ifName, podRequest.PodName, podRequest.PodNamespace, err)
×
368
                        klog.Error(errMsg)
×
369
                        if err := resp.WriteHeaderAndEntity(http.StatusInternalServerError, request.CniResponse{Err: errMsg.Error()}); err != nil {
×
370
                                klog.Errorf("failed to write response, %v", err)
×
371
                        }
×
372
                        return
×
373
                }
374

375
                ifaceID := ovs.PodNameToPortName(podRequest.PodName, podRequest.PodNamespace, podRequest.Provider)
×
376
                if err = ovs.ConfigInterfaceMirror(csh.Config.EnableMirror, pod.Annotations[fmt.Sprintf(util.MirrorControlAnnotationTemplate, podRequest.Provider)], ifaceID); err != nil {
×
377
                        klog.Errorf("failed mirror to mirror0, %v", err)
×
378
                        return
×
379
                }
×
380

381
                if err = csh.Controller.addEgressConfig(podSubnet, ip); err != nil {
×
382
                        errMsg := fmt.Errorf("failed to add egress configuration: %w", err)
×
383
                        klog.Error(errMsg)
×
384
                        if err = resp.WriteHeaderAndEntity(http.StatusInternalServerError, request.CniResponse{Err: errMsg.Error()}); err != nil {
×
385
                                klog.Errorf("failed to write response, %v", err)
×
386
                        }
×
387
                        return
×
388
                }
389
        } else if len(routes) != 0 {
×
390
                hasDefaultRoute := make(map[string]bool, 2)
×
391
                for _, r := range routes {
×
392
                        if r.Destination == "" {
×
393
                                hasDefaultRoute[util.CheckProtocol(r.Gateway)] = true
×
394
                                continue
×
395
                        }
396
                        if _, cidr, err := net.ParseCIDR(r.Destination); err == nil {
×
397
                                if ones, _ := cidr.Mask.Size(); ones == 0 {
×
398
                                        hasDefaultRoute[util.CheckProtocol(r.Gateway)] = true
×
399
                                }
×
400
                        }
401
                }
402
                if len(hasDefaultRoute) != 0 {
×
403
                        // remove existing default route so other CNI plugins, such as macvlan, can add the new default route correctly
×
404
                        if err = csh.removeDefaultRoute(podRequest.NetNs, hasDefaultRoute[kubeovnv1.ProtocolIPv4], hasDefaultRoute[kubeovnv1.ProtocolIPv6]); err != nil {
×
405
                                errMsg := fmt.Errorf("failed to remove existing default route for interface %s of pod %s/%s: %w", podRequest.IfName, podRequest.PodNamespace, podRequest.PodName, err)
×
406
                                klog.Error(errMsg)
×
407
                                if err = resp.WriteHeaderAndEntity(http.StatusInternalServerError, request.CniResponse{Err: errMsg.Error()}); err != nil {
×
408
                                        klog.Errorf("failed to write response: %v", err)
×
409
                                }
×
410
                                return
×
411
                        }
412
                }
413
        }
414

415
        v4IP, v6IP := util.SplitStringIP(ip)
×
416
        v4CIDR, v6CIDR := util.SplitStringIP(cidr)
×
417
        v4GW, v6GW := util.SplitStringIP(gw)
×
418

×
419
        var ips []request.IPConfig
×
420
        if v4IP != "" {
×
421
                cfg := request.IPConfig{Protocol: kubeovnv1.ProtocolIPv4, IP: v4IP, CIDR: v4CIDR}
×
422
                if isDefaultRoute {
×
423
                        cfg.Gateway = v4GW
×
424
                }
×
425
                ips = append(ips, cfg)
×
426
        }
427
        if v6IP != "" {
×
428
                cfg := request.IPConfig{Protocol: kubeovnv1.ProtocolIPv6, IP: v6IP, CIDR: v6CIDR}
×
429
                if isDefaultRoute {
×
430
                        cfg.Gateway = v6GW
×
431
                }
×
432
                ips = append(ips, cfg)
×
433
        }
434

435
        response := &request.CniResponse{
×
436
                IPs:        ips,
×
437
                MacAddress: macAddr,
×
438
                PodNicName: podNicName,
×
439
                Routes:     routes,
×
440
                Mtu:        mtu,
×
441
        }
×
442
        if err := resp.WriteHeaderAndEntity(http.StatusOK, response); err != nil {
×
443
                klog.Errorf("failed to write response, %v", err)
×
444
        }
×
445
}
446

NEW
447
func (csh cniServerHandler) UpdateIPCR(podRequest request.CniRequest, subnet, ip string, appendIfName bool) error {
×
NEW
448
        klog.V(4).Infof("found subnet %s", subnet)
×
449
        ipCRName := ovs.PodNameToPortName(podRequest.PodName, podRequest.PodNamespace, podRequest.Provider)
×
NEW
450

×
NEW
451
        // for backward compatibility we will check if annotation ip address exists with ifName first and subsequently with ifname
×
NEW
452
        if appendIfName {
×
NEW
453
                ipCRName = fmt.Sprintf("%s.%s", ipCRName, podRequest.IfName)
×
NEW
454
        }
×
455
        for range 20 {
×
456
                ipCR, err := csh.KubeOvnClient.KubeovnV1().IPs().Get(context.Background(), ipCRName, metav1.GetOptions{})
×
457
                if err != nil {
×
458
                        err = fmt.Errorf("failed to get ip crd for %s, %w", ip, err)
×
459
                        // maybe create a backup pod with previous annotations
×
460
                        klog.Error(err)
×
461
                } else if ipCR.Spec.NodeName != csh.Config.NodeName {
×
462
                        ipCR := ipCR.DeepCopy()
×
463
                        if ipCR.Labels == nil {
×
464
                                ipCR.Labels = map[string]string{}
×
465
                        }
×
466
                        ipCR.Spec.NodeName = csh.Config.NodeName
×
467
                        ipCR.Spec.AttachIPs = []string{}
×
468
                        ipCR.Labels[subnet] = ""
×
469
                        ipCR.Labels[util.NodeNameLabel] = csh.Config.NodeName
×
470
                        ipCR.Spec.AttachSubnets = []string{}
×
471
                        ipCR.Spec.AttachMacs = []string{}
×
472
                        if _, err := csh.KubeOvnClient.KubeovnV1().IPs().Update(context.Background(), ipCR, metav1.UpdateOptions{}); err != nil {
×
473
                                err = fmt.Errorf("failed to update ip crd for %s, %w", ip, err)
×
474
                                klog.Error(err)
×
475
                        } else {
×
476
                                return nil
×
477
                        }
×
478
                }
479
                if err != nil {
×
480
                        klog.Warningf("wait pod ip %s to be ready", ipCRName)
×
481
                        time.Sleep(1 * time.Second)
×
482
                } else {
×
483
                        return nil
×
484
                }
×
485
        }
486
        // update ip spec node is not that necessary, so we just log the error
487
        return nil
×
488
}
489

490
func (csh cniServerHandler) handleDel(req *restful.Request, resp *restful.Response) {
×
491
        var podRequest request.CniRequest
×
NEW
492
        var appendIfName bool
×
NEW
493

×
494
        if err := req.ReadEntity(&podRequest); err != nil {
×
495
                errMsg := fmt.Errorf("parse del request failed %w", err)
×
496
                klog.Error(errMsg)
×
497
                if err := resp.WriteHeaderAndEntity(http.StatusBadRequest, request.CniResponse{Err: errMsg.Error()}); err != nil {
×
498
                        klog.Errorf("failed to write response, %v", err)
×
499
                }
×
500
                return
×
501
        }
502

503
        // Try to get the Pod, but if it fails due to not being found, log a warning and continue.
504
        pod, err := csh.Controller.podsLister.Pods(podRequest.PodNamespace).Get(podRequest.PodName)
×
505
        if err != nil && !k8serrors.IsNotFound(err) {
×
506
                errMsg := fmt.Errorf("failed to retrieve Pod %s/%s: %w", podRequest.PodNamespace, podRequest.PodName, err)
×
507
                klog.Error(errMsg)
×
508
                if err := resp.WriteHeaderAndEntity(http.StatusBadRequest, request.CniResponse{Err: errMsg.Error()}); err != nil {
×
509
                        klog.Errorf("failed to write response, %v", err)
×
510
                }
×
511
                return
×
512
        }
513

NEW
514
        providerWithIfName := fmt.Sprintf("%s.%s", podRequest.Provider, podRequest.IfName)
×
515
        if podRequest.NetNs == "" {
×
516
                klog.Infof("skip del port request: %v", podRequest)
×
517
                resp.WriteHeader(http.StatusNoContent)
×
518
                return
×
519
        }
×
520

521
        klog.Infof("del port request: %v", podRequest)
×
522
        if err := csh.validatePodRequest(&podRequest); err != nil {
×
523
                klog.Error(err)
×
524
                if err := resp.WriteHeaderAndEntity(http.StatusBadRequest, request.CniResponse{Err: err.Error()}); err != nil {
×
525
                        klog.Errorf("failed to write response, %v", err)
×
526
                }
×
527
                return
×
528
        }
529

530
        var nicType string
×
531
        var vmName string
×
532

×
533
        // If the Pod was found, process its annotations and labels.
×
534
        if pod != nil {
×
535
                if pod.Annotations != nil && (util.IsOvnProvider(podRequest.Provider) || podRequest.CniType == util.CniTypeName) {
×
NEW
536
                        _, ok := pod.Annotations[fmt.Sprintf(util.IPAddressAnnotationTemplate, providerWithIfName)]
×
NEW
537
                        if ok {
×
NEW
538
                                appendIfName = true
×
NEW
539
                        }
×
NEW
540
                        subnet := util.GetAnnotationWithIfNameOverride(pod.Annotations, podRequest.Provider, podRequest.IfName, util.LogicalSwitchAnnotationTemplate, appendIfName)
×
541
                        if subnet != "" {
×
NEW
542
                                ip := util.GetAnnotationWithIfNameOverride(pod.Annotations, podRequest.Provider, podRequest.IfName, util.IPAddressAnnotationTemplate, appendIfName)
×
543
                                if err = csh.Controller.removeEgressConfig(subnet, ip); err != nil {
×
544
                                        errMsg := fmt.Errorf("failed to remove egress configuration: %w", err)
×
545
                                        klog.Error(errMsg)
×
546
                                        if err = resp.WriteHeaderAndEntity(http.StatusInternalServerError, request.CniResponse{Err: errMsg.Error()}); err != nil {
×
547
                                                klog.Errorf("failed to write response, %v", err)
×
548
                                        }
×
549
                                        return
×
550
                                }
551
                        }
552

553
                        switch {
×
554
                        case podRequest.DeviceID != "":
×
555
                                nicType = util.OffloadType
×
556
                        case podRequest.VhostUserSocketVolumeName != "":
×
557
                                nicType = util.DpdkType
×
558
                                if err = removeShortSharedDir(pod, podRequest.VhostUserSocketVolumeName, podRequest.VhostUserSocketConsumption); err != nil {
×
559
                                        klog.Error(err.Error())
×
560
                                        if err = resp.WriteHeaderAndEntity(http.StatusInternalServerError, request.CniResponse{Err: err.Error()}); err != nil {
×
561
                                                klog.Errorf("failed to write response: %v", err)
×
562
                                        }
×
563
                                        return
×
564
                                }
565
                        default:
×
566
                                nicType = pod.Annotations[fmt.Sprintf(util.PodNicAnnotationTemplate, podRequest.Provider)]
×
567
                        }
568

569
                        vmName = pod.Annotations[fmt.Sprintf(util.VMAnnotationTemplate, podRequest.Provider)]
×
570
                        if vmName != "" {
×
571
                                podRequest.PodName = vmName
×
572
                        }
×
573
                }
574
        } else {
×
575
                // If the Pod is not found, assign a default value.
×
576
                klog.Warningf("Pod %s not found, proceeding with NIC deletion using ContainerID and NetNs", podRequest.PodName)
×
577
                switch {
×
578
                case podRequest.DeviceID != "":
×
579
                        nicType = util.OffloadType
×
580
                case podRequest.VhostUserSocketVolumeName != "":
×
581
                        nicType = util.DpdkType
×
582
                default:
×
583
                        nicType = util.VethType
×
584
                }
585
        }
586

587
        // To support KubeVirt hotplug dpdk nic, forbidden set the volume name
588
        if podRequest.VhostUserSocketConsumption == util.ConsumptionKubevirt {
×
589
                podRequest.VhostUserSocketVolumeName = util.VhostUserSocketVolumeName
×
590
        }
×
591

592
        // Proceed to delete the NIC regardless of whether the Pod was found or not.
593
        err = csh.deleteNic(podRequest.PodName, podRequest.PodNamespace, podRequest.ContainerID, podRequest.NetNs, podRequest.DeviceID, podRequest.IfName, nicType)
×
594
        if err != nil {
×
595
                errMsg := fmt.Errorf("del nic failed %w", err)
×
596
                klog.Error(errMsg)
×
597
                if err := resp.WriteHeaderAndEntity(http.StatusInternalServerError, request.CniResponse{Err: errMsg.Error()}); err != nil {
×
598
                        klog.Errorf("failed to write response, %v", err)
×
599
                }
×
600
                return
×
601
        }
602
        resp.WriteHeader(http.StatusNoContent)
×
603
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc