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

kubeovn / kube-ovn / 30198864379

26 Jul 2026 10:46AM UTC coverage: 30.05% (+2.0%) from 28.029%
30198864379

push

github

web-flow
controller, daemon: record pod network lifecycle events (#6989)

Signed-off-by: zhangzujian <zhangzujian.7@gmail.com>

239 of 298 new or added lines in 3 files covered. (80.2%)

4 existing lines in 1 file now uncovered.

18391 of 61202 relevant lines covered (30.05%)

0.35 hits per line

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

45.41
/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 {
1✔
45
        csh := &cniServerHandler{KubeClient: config.KubeClient, KubeOvnClient: config.KubeOvnClient, Config: config, Controller: controller}
1✔
46
        return csh
1✔
47
}
1✔
48

49
func podForCNIEvent(pod *v1.Pod, podRequest *request.CniRequest) *v1.Pod {
1✔
50
        if pod != nil {
2✔
51
                return pod
1✔
52
        }
1✔
53
        return &v1.Pod{
1✔
54
                TypeMeta: metav1.TypeMeta{APIVersion: "v1", Kind: "Pod"},
1✔
55
                ObjectMeta: metav1.ObjectMeta{
1✔
56
                        Namespace: podRequest.PodNamespace,
1✔
57
                        Name:      podRequest.PodName,
1✔
58
                },
1✔
59
        }
1✔
60
}
61

62
func cniEventInterfaceNames(containerID, ifName string) []string {
1✔
63
        ifNames := []string{ifName}
1✔
64
        if ifName == "" {
2✔
65
                ifNames = append(ifNames, "eth0")
1✔
66
        }
1✔
67

68
        var names []string
1✔
69
        for _, name := range ifNames {
2✔
70
                if name == "eth0" {
2✔
71
                        if len(containerID) >= 12 {
2✔
72
                                names = append(names, containerID[:12]+"_h", containerID[:12]+"_c")
1✔
73
                        }
1✔
74
                        continue
1✔
75
                }
76
                if strings.HasPrefix(name, "pod") && len(name) == 14 {
1✔
NEW
77
                        name = name[3 : len(name)-4]
×
NEW
78
                }
×
79
                prefixLen := 12 - len(name)
1✔
80
                if prefixLen < 0 || len(containerID) < prefixLen {
2✔
81
                        continue
1✔
82
                }
83
                prefix := containerID[:prefixLen] + "_" + name
1✔
84
                names = append(names, prefix+"_h", prefix+"_c")
1✔
85
        }
86
        return names
1✔
87
}
88

89
func (csh cniServerHandler) recordCNIPodEvent(pod *v1.Pod, podRequest *request.CniRequest, eventType, reason, message string) {
1✔
90
        ifName := podRequest.IfName
1✔
91
        if ifName == "" {
2✔
92
                ifName = "eth0"
1✔
93
        }
1✔
94
        details := []string{podRequest.ContainerID, podRequest.NetNs, podRequest.DeviceID}
1✔
95
        details = append(details, cniEventInterfaceNames(podRequest.ContainerID, podRequest.IfName)...)
1✔
96
        for _, detail := range details {
2✔
97
                if detail != "" {
2✔
98
                        message = strings.ReplaceAll(message, detail, "<redacted>")
1✔
99
                }
1✔
100
        }
101
        csh.Controller.recorder.Eventf(pod, eventType, reason, "%s provider=%s interface=%s node=%s", message, podRequest.Provider, ifName, csh.Config.NodeName)
1✔
102
}
103

104
// gatewayForCNIIPFamily returns only the gateway entries matching the address
105
// families configured on the container interface. A single-family pod can still
106
// carry a dual-stack subnet gateway annotation, but CNI route and gateway checks
107
// must use only gateways that the interface can actually reach.
108
func gatewayForCNIIPFamily(ipAddr, gateway string) string {
1✔
109
        if ipAddr == "" || gateway == "" || util.CheckProtocol(gateway) != kubeovnv1.ProtocolDual {
2✔
110
                return gateway
1✔
111
        }
1✔
112

113
        gateways := util.SplitTrimmed(gateway, ",")
1✔
114
        filtered := make([]string, 0, len(gateways))
1✔
115
        for _, ip := range util.SplitTrimmed(ipAddr, ",") {
2✔
116
                ipProtocol := util.CheckProtocol(ip)
1✔
117
                for _, gw := range gateways {
2✔
118
                        if util.CheckProtocol(gw) == ipProtocol {
2✔
119
                                filtered = append(filtered, gw)
1✔
120
                                break
1✔
121
                        }
122
                }
123
        }
124
        if len(filtered) == 0 {
1✔
125
                klog.Warningf("failed to match gateway %q with ip address %q, keep original gateway", gateway, ipAddr)
×
126
                return gateway
×
127
        }
×
128
        return strings.Join(filtered, ",")
1✔
129
}
130

131
func (csh cniServerHandler) providerExists(provider, ifName string) (*kubeovnv1.Subnet, bool) {
1✔
132
        if provider == "" || provider == util.OvnProvider {
2✔
133
                return nil, true
1✔
134
        }
1✔
135
        subnets, err := csh.Controller.subnetsLister.List(labels.Everything())
1✔
136
        if err != nil {
2✔
137
                klog.Errorf("failed to list subnets while checking provider %s: %v", provider, err)
1✔
138
                return nil, true
1✔
139
        }
1✔
140
        // for multi interface attachments the ifname is included in provider, for example, vm-overlay.default.ovn.net1
141
        // as a result if ifname is set, we need to append it to subnet provider when comparing with request provider
142
        // else no subnet will be found
143
        providerName, _ := strings.CutSuffix(provider, fmt.Sprintf(".%s", ifName))
1✔
144
        for _, subnet := range subnets {
2✔
145
                if subnet.Spec.Provider == providerName {
2✔
146
                        return subnet.DeepCopy(), true
1✔
147
                }
1✔
148
        }
149
        return nil, false
1✔
150
}
151

152
// isMacOnlyAllocation reports whether the controller allocated only a MAC address
153
// for the given provider/interface (BYO-DHCP / external DHCP on an underlay subnet
154
// without a CIDR): the pod is marked allocated and has a MAC, but no IP or CIDR.
155
// Such NICs must not block on the address/route wait loops in handleAdd.
156
func isMacOnlyAllocation(annotations map[string]string, provider, ifName string, appendIfName bool) bool {
1✔
157
        return util.GetAnnotationWithIfNameOverride(annotations, provider, ifName, util.IPAddressAnnotationTemplate, appendIfName) == "" &&
1✔
158
                util.GetAnnotationWithIfNameOverride(annotations, provider, ifName, util.AllocatedAnnotationTemplate, appendIfName) == "true" &&
1✔
159
                util.GetAnnotationWithIfNameOverride(annotations, provider, ifName, util.MacAddressAnnotationTemplate, appendIfName) != "" &&
1✔
160
                util.GetAnnotationWithIfNameOverride(annotations, provider, ifName, util.CidrAnnotationTemplate, appendIfName) == ""
1✔
161
}
1✔
162

163
func (csh cniServerHandler) handleAdd(req *restful.Request, resp *restful.Response) {
1✔
164
        podRequest := request.CniRequest{}
1✔
165
        if err := req.ReadEntity(&podRequest); err != nil {
2✔
166
                errMsg := fmt.Errorf("parse add request failed %w", err)
1✔
167
                klog.Error(errMsg)
1✔
168
                if err := resp.WriteHeaderAndEntity(http.StatusBadRequest, request.CniResponse{Err: errMsg.Error()}); err != nil {
1✔
169
                        klog.Errorf("failed to write response, %v", err)
×
170
                }
×
171
                return
1✔
172
        }
173
        eventPod := podForCNIEvent(nil, &podRequest)
1✔
174
        recordFailure := func(stage string, err error) {
2✔
175
                csh.recordCNIPodEvent(eventPod, &podRequest, v1.EventTypeWarning, "PodNetworkConfigureFailed", fmt.Sprintf("stage=%s error=%v", stage, err))
1✔
176
        }
1✔
177
        klog.V(5).Infof("request body is %v", podRequest)
1✔
178
        podSubnet, exist := csh.providerExists(podRequest.Provider, podRequest.IfName)
1✔
179
        if !exist {
1✔
180
                errMsg := fmt.Errorf("provider %s is not bound to any subnet", podRequest.Provider)
×
181
                klog.Error(errMsg)
×
NEW
182
                recordFailure("provider", errMsg)
×
183
                if err := resp.WriteHeaderAndEntity(http.StatusBadRequest, request.CniResponse{Err: errMsg.Error()}); err != nil {
×
184
                        klog.Errorf("failed to write response, %v", err)
×
185
                }
×
186
                return
×
187
        }
188

189
        klog.Infof("add port request: %v", podRequest)
1✔
190
        if err := csh.validatePodRequest(&podRequest); err != nil {
1✔
191
                klog.Error(err)
×
NEW
192
                recordFailure("validate-request", err)
×
193
                if err := resp.WriteHeaderAndEntity(http.StatusBadRequest, request.CniResponse{Err: err.Error()}); err != nil {
×
194
                        klog.Errorf("failed to write response, %v", err)
×
195
                }
×
196
                return
×
197
        }
198

199
        var gatewayCheckMode int
1✔
200
        var macAddr, ip, ipAddr, cidr, gw, subnet, ingress, egress, ingressBurst, egressBurst, providerNetwork, ifName, nicType, podNicName, vmName, latency, limit, loss, jitter, u2oInterconnectionIP, oldPodName string
1✔
201
        var routes []request.Route
1✔
202
        var isDefaultRoute, noIPAM, macOnly bool
1✔
203
        var pod *v1.Pod
1✔
204
        var err error
1✔
205

1✔
206
        providerWithIfName := fmt.Sprintf("%s.%s", podRequest.Provider, podRequest.IfName)
1✔
207
        var appendIfName bool
1✔
208

1✔
209
        for range 20 {
2✔
210
                if pod, err = csh.Controller.podsLister.Pods(podRequest.PodNamespace).Get(podRequest.PodName); err != nil {
2✔
211
                        errMsg := fmt.Errorf("get pod %s/%s failed %w", podRequest.PodNamespace, podRequest.PodName, err)
1✔
212
                        klog.Error(errMsg)
1✔
213
                        recordFailure("get-pod", errMsg)
1✔
214
                        if err := resp.WriteHeaderAndEntity(http.StatusInternalServerError, request.CniResponse{Err: errMsg.Error()}); err != nil {
1✔
215
                                klog.Errorf("failed to write response, %v", err)
×
216
                        }
×
217
                        return
1✔
218
                }
219
                eventPod = pod
1✔
220

1✔
221
                // in case of multiple nics from same subnet
1✔
222
                _, ok := pod.Annotations[fmt.Sprintf(util.IPAddressAnnotationTemplate, providerWithIfName)]
1✔
223
                if ok {
1✔
224
                        appendIfName = true
×
225
                }
×
226

227
                ip = util.GetAnnotationWithIfNameOverride(pod.Annotations, podRequest.Provider, podRequest.IfName, util.IPAddressAnnotationTemplate, appendIfName)
1✔
228
                // MAC-only mode (BYO-DHCP / external DHCP): for an underlay subnet without a CIDR,
1✔
229
                // the controller allocates only a MAC address, leaving the IP and CIDR annotations
1✔
230
                // empty while still marking the pod as allocated. Such pods must not block on the
1✔
231
                // address/route wait loops below.
1✔
232
                macOnly = isMacOnlyAllocation(pod.Annotations, podRequest.Provider, podRequest.IfName, appendIfName)
1✔
233
                if ip == "" && !macOnly {
1✔
234
                        klog.Infof("wait address for pod %s/%s provider %s", podRequest.PodNamespace, podRequest.PodName, podRequest.Provider)
×
235
                        // wait controller assign an address
×
236
                        cniWaitAddressResult.WithLabelValues(nodeName).Inc()
×
237
                        time.Sleep(1 * time.Second)
×
238
                        continue
×
239
                }
240

241
                if err := util.ValidatePodNetwork(pod.Annotations); err != nil {
1✔
242
                        klog.Errorf("validate pod %s/%s failed, %v", podRequest.PodNamespace, podRequest.PodName, err)
×
243
                        // wait controller assign an address
×
244
                        cniWaitAddressResult.WithLabelValues(nodeName).Inc()
×
245
                        time.Sleep(1 * time.Second)
×
246
                        continue
×
247
                }
248

249
                cidr = util.GetAnnotationWithIfNameOverride(pod.Annotations, podRequest.Provider, podRequest.IfName, util.CidrAnnotationTemplate, appendIfName)
1✔
250
                gw = util.GetAnnotationWithIfNameOverride(pod.Annotations, podRequest.Provider, podRequest.IfName, util.GatewayAnnotationTemplate, appendIfName)
1✔
251
                subnet = util.GetAnnotationWithIfNameOverride(pod.Annotations, podRequest.Provider, podRequest.IfName, util.LogicalSwitchAnnotationTemplate, appendIfName)
1✔
252
                ingress = util.GetAnnotationWithIfNameOverride(pod.Annotations, podRequest.Provider, podRequest.IfName, util.IngressRateAnnotationTemplate, appendIfName)
1✔
253
                egress = util.GetAnnotationWithIfNameOverride(pod.Annotations, podRequest.Provider, podRequest.IfName, util.EgressRateAnnotationTemplate, appendIfName)
1✔
254
                ingressBurst = util.GetAnnotationWithIfNameOverride(pod.Annotations, podRequest.Provider, podRequest.IfName, util.IngressBurstAnnotationTemplate, appendIfName)
1✔
255
                egressBurst = util.GetAnnotationWithIfNameOverride(pod.Annotations, podRequest.Provider, podRequest.IfName, util.EgressBurstAnnotationTemplate, appendIfName)
1✔
256
                latency = util.GetAnnotationWithIfNameOverride(pod.Annotations, podRequest.Provider, podRequest.IfName, util.NetemQosLatencyAnnotationTemplate, appendIfName)
1✔
257
                limit = util.GetAnnotationWithIfNameOverride(pod.Annotations, podRequest.Provider, podRequest.IfName, util.NetemQosLimitAnnotationTemplate, appendIfName)
1✔
258
                loss = util.GetAnnotationWithIfNameOverride(pod.Annotations, podRequest.Provider, podRequest.IfName, util.NetemQosLossAnnotationTemplate, appendIfName)
1✔
259
                jitter = util.GetAnnotationWithIfNameOverride(pod.Annotations, podRequest.Provider, podRequest.IfName, util.NetemQosJitterAnnotationTemplate, appendIfName)
1✔
260
                providerNetwork = util.GetAnnotationWithIfNameOverride(pod.Annotations, podRequest.Provider, podRequest.IfName, util.ProviderNetworkTemplate, appendIfName)
1✔
261
                vmName = util.GetAnnotationWithIfNameOverride(pod.Annotations, podRequest.Provider, podRequest.IfName, util.VMAnnotationTemplate, appendIfName)
1✔
262
                ipAddr, noIPAM, err = util.GetIPAddrWithMaskForCNI(ip, cidr)
1✔
263
                if err != nil {
1✔
264
                        errMsg := fmt.Errorf("failed to get ip address with mask, %w", err)
×
265
                        klog.Error(errMsg)
×
NEW
266
                        recordFailure("parse-address", errMsg)
×
267
                        if err := resp.WriteHeaderAndEntity(http.StatusInternalServerError, request.CniResponse{Err: errMsg.Error()}); err != nil {
×
268
                                klog.Errorf("failed to write response, %v", err)
×
269
                        }
×
270
                        return
×
271
                }
272
                gw = gatewayForCNIIPFamily(ipAddr, gw)
1✔
273

1✔
274
                oldPodName = podRequest.PodName
1✔
275
                if s := pod.Annotations[fmt.Sprintf(util.RoutesAnnotationTemplate, podRequest.Provider)]; s != "" {
1✔
276
                        if err = json.Unmarshal([]byte(s), &routes); err != nil {
×
277
                                errMsg := fmt.Errorf("invalid routes for pod %s/%s: %w", pod.Namespace, pod.Name, err)
×
278
                                klog.Error(errMsg)
×
NEW
279
                                recordFailure("parse-routes", errMsg)
×
280
                                if err = resp.WriteHeaderAndEntity(http.StatusInternalServerError, request.CniResponse{Err: errMsg.Error()}); err != nil {
×
281
                                        klog.Errorf("failed to write response: %v", err)
×
282
                                }
×
283
                                return
×
284
                        }
285
                }
286
                if ifName = podRequest.IfName; ifName == "" {
1✔
287
                        ifName = "eth0"
×
288
                }
×
289

290
                // To support KubeVirt hotplug dpdk nic, forbidden set the volume name
291
                if podRequest.VhostUserSocketConsumption == util.ConsumptionKubevirt {
1✔
292
                        podRequest.VhostUserSocketVolumeName = util.VhostUserSocketVolumeName
×
293
                }
×
294

295
                switch {
1✔
296
                case podRequest.DeviceID != "":
×
297
                        nicType = util.OffloadType
×
298
                case podRequest.VhostUserSocketVolumeName != "":
×
299
                        nicType = util.DpdkType
×
300
                        if err = createShortSharedDir(pod, podRequest.VhostUserSocketVolumeName, podRequest.VhostUserSocketConsumption, csh.Config.KubeletDir); err != nil {
×
301
                                klog.Error(err.Error())
×
NEW
302
                                recordFailure("prepare-dpdk", err)
×
303
                                if err = resp.WriteHeaderAndEntity(http.StatusInternalServerError, request.CniResponse{Err: err.Error()}); err != nil {
×
304
                                        klog.Errorf("failed to write response: %v", err)
×
305
                                }
×
306
                                return
×
307
                        }
308
                default:
1✔
309
                        nicType = pod.Annotations[fmt.Sprintf(util.PodNicAnnotationTemplate, podRequest.Provider)]
1✔
310
                }
311

312
                switch pod.Annotations[fmt.Sprintf(util.DefaultRouteAnnotationTemplate, podRequest.Provider)] {
1✔
313
                case "true":
×
314
                        isDefaultRoute = true
×
315
                case "false":
×
316
                        isDefaultRoute = false
×
317
                default:
1✔
318
                        isDefaultRoute = ifName == "eth0"
1✔
319
                }
320

321
                if !macOnly && isDefaultRoute && pod.Annotations[fmt.Sprintf(util.RoutedAnnotationTemplate, podRequest.Provider)] != "true" && util.IsOvnProvider(podRequest.Provider) {
1✔
322
                        klog.Infof("wait route ready for pod %s/%s provider %s", podRequest.PodNamespace, podRequest.PodName, podRequest.Provider)
×
323
                        cniWaitRouteResult.WithLabelValues(nodeName).Inc()
×
324
                        time.Sleep(1 * time.Second)
×
325
                        continue
×
326
                }
327

328
                if vmName != "" {
1✔
329
                        podRequest.PodName = vmName
×
330
                }
×
331

332
                break
1✔
333
        }
334

335
        if !macOnly && util.GetAnnotationWithIfNameOverride(pod.Annotations, podRequest.Provider, podRequest.IfName, util.IPAddressAnnotationTemplate, appendIfName) == "" {
1✔
336
                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)
×
337
                klog.Error(err)
×
NEW
338
                recordFailure("wait-address", err)
×
339
                if err := resp.WriteHeaderAndEntity(http.StatusInternalServerError, request.CniResponse{Err: err.Error()}); err != nil {
×
340
                        klog.Errorf("failed to write response, %v", err)
×
341
                }
×
342
                return
×
343
        }
344

345
        if subnet == "" && podSubnet != nil {
1✔
346
                subnet = podSubnet.Name
×
347
        }
×
348
        if !noIPAM {
2✔
349
                if err := csh.UpdateIPCR(podRequest, subnet, ip, appendIfName); err != nil {
1✔
NEW
350
                        recordFailure("update-ip-cr", err)
×
351
                        if err := resp.WriteHeaderAndEntity(http.StatusInternalServerError, request.CniResponse{Err: err.Error()}); err != nil {
×
352
                                klog.Errorf("failed to write response, %v", err)
×
353
                        }
×
354
                        return
×
355
                }
356
        }
357

358
        if !macOnly && isDefaultRoute && pod.Annotations[fmt.Sprintf(util.RoutedAnnotationTemplate, podRequest.Provider)] != "true" && util.IsOvnProvider(podRequest.Provider) {
1✔
359
                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)
×
360
                klog.Error(err)
×
NEW
361
                recordFailure("wait-route", err)
×
362
                if err := resp.WriteHeaderAndEntity(http.StatusInternalServerError, request.CniResponse{Err: err.Error()}); err != nil {
×
363
                        klog.Errorf("failed to write response, %v", err)
×
364
                }
×
365
                return
×
366
        }
367

368
        var mtu int
1✔
369
        routes = append(podRequest.Routes, routes...)
1✔
370
        if strings.HasSuffix(podRequest.Provider, util.OvnProvider) && subnet != "" {
1✔
371
                podSubnet, err := csh.Controller.subnetsLister.Get(subnet)
×
372
                if err != nil {
×
373
                        errMsg := fmt.Errorf("failed to get subnet %s: %w", subnet, err)
×
374
                        klog.Error(errMsg)
×
NEW
375
                        recordFailure("get-subnet", errMsg)
×
376
                        if err = resp.WriteHeaderAndEntity(http.StatusInternalServerError, request.CniResponse{Err: errMsg.Error()}); err != nil {
×
377
                                klog.Errorf("failed to write response: %v", err)
×
378
                        }
×
379
                        return
×
380
                }
381

382
                if podSubnet.Status.U2OInterconnectionIP == "" && podSubnet.Spec.U2OInterconnection {
×
383
                        errMsg := fmt.Errorf("failed to generate u2o ip on subnet %s", podSubnet.Name)
×
384
                        klog.Error(errMsg)
×
NEW
385
                        recordFailure("u2o-address", errMsg)
×
386
                        return
×
387
                }
×
388

389
                if podSubnet.Status.U2OInterconnectionIP != "" && podSubnet.Spec.U2OInterconnection {
×
390
                        u2oInterconnectionIP = podSubnet.Status.U2OInterconnectionIP
×
391
                }
×
392

393
                var vmMigration bool
×
394
                subnetHasVlan := podSubnet.Spec.Vlan != ""
×
395
                // skip ping check gateway for pods during live migration
×
396
                if pod.Annotations[kubevirtv1.MigrationJobNameAnnotation] == "" {
×
397
                        if subnetHasVlan && !podSubnet.Spec.LogicalGateway {
×
398
                                if podSubnet.Spec.DisableGatewayCheck {
×
399
                                        gatewayCheckMode = gatewayCheckModeArpingNotConcerned
×
400
                                } else {
×
401
                                        gatewayCheckMode = gatewayCheckModeArping
×
402
                                }
×
403
                        } else {
×
404
                                if podSubnet.Spec.DisableGatewayCheck {
×
405
                                        gatewayCheckMode = gatewayCheckModePingNotConcerned
×
406
                                } else {
×
407
                                        gatewayCheckMode = gatewayCheckModePing
×
408
                                }
×
409
                        }
410
                } else {
×
411
                        vmMigration = true
×
412
                }
×
413
                if pod.Annotations[fmt.Sprintf(util.ActivationStrategyTemplate, podRequest.Provider)] != "" {
×
414
                        gatewayCheckMode = gatewayCheckModeDisabled
×
415
                }
×
416

417
                if podSubnet.Spec.Mtu > 0 {
×
418
                        mtu = int(podSubnet.Spec.Mtu)
×
419
                } else {
×
420
                        if providerNetwork != "" && !podSubnet.Spec.LogicalGateway && !podSubnet.Spec.U2OInterconnection {
×
421
                                node, err := csh.Controller.nodesLister.Get(csh.Config.NodeName)
×
422
                                if err != nil {
×
423
                                        errMsg := fmt.Errorf("failed to get node %s: %w", csh.Config.NodeName, err)
×
424
                                        klog.Error(errMsg)
×
NEW
425
                                        recordFailure("get-node", errMsg)
×
426
                                        if err = resp.WriteHeaderAndEntity(http.StatusInternalServerError, request.CniResponse{Err: errMsg.Error()}); err != nil {
×
427
                                                klog.Errorf("failed to write response: %v", err)
×
428
                                        }
×
429
                                        return
×
430
                                }
431
                                mtuStr := node.Labels[fmt.Sprintf(util.ProviderNetworkMtuTemplate, providerNetwork)]
×
432
                                if mtuStr != "" {
×
433
                                        var errMsg error
×
434
                                        if mtu, err = strconv.Atoi(mtuStr); err != nil {
×
435
                                                errMsg = fmt.Errorf("failed to parse provider network MTU %s: %w", mtuStr, err)
×
436
                                        } else if mtu <= 0 {
×
437
                                                errMsg = fmt.Errorf("invalid provider network MTU %q: must be a positive integer", mtuStr)
×
438
                                        }
×
439
                                        if errMsg != nil {
×
440
                                                klog.Error(errMsg)
×
NEW
441
                                                recordFailure("mtu", errMsg)
×
442
                                                if err = resp.WriteHeaderAndEntity(http.StatusInternalServerError, request.CniResponse{Err: errMsg.Error()}); err != nil {
×
443
                                                        klog.Errorf("failed to write response: %v", err)
×
444
                                                }
×
445
                                                return
×
446
                                        }
447
                                }
448
                        } else {
×
449
                                mtu = csh.Config.MTU
×
450
                        }
×
451
                }
452

453
                // Warn but keep the configured value: the provider-network or subnet
454
                // MTU reflects the real link MTU, and inflating it would push pods
455
                // to emit packets that the underlay cannot carry, breaking IPv4 too.
456
                if mtu > 0 && mtu < util.IPv6MinMTU {
×
457
                        subnetProtocol := util.CheckProtocol(podSubnet.Spec.CIDRBlock)
×
458
                        if subnetProtocol == kubeovnv1.ProtocolIPv6 || subnetProtocol == kubeovnv1.ProtocolDual {
×
459
                                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)
×
460
                        }
×
461
                }
462

463
                macAddr = util.GetAnnotationWithIfNameOverride(pod.Annotations, podRequest.Provider, podRequest.IfName, util.MacAddressAnnotationTemplate, appendIfName)
×
464
                klog.Infof("create container interface %s mac %s, ip %s, cidr %s, gw %s, custom routes %v", ifName, macAddr, ipAddr, cidr, gw, routes)
×
465
                podNicName = ifName
×
466

×
467
                var encapIP string
×
468
                if podSubnet.Spec.NodeNetwork != "" {
×
469
                        encapIP, err = csh.Config.GetEncapIPByNetwork(podSubnet.Spec.NodeNetwork)
×
470
                        if err != nil {
×
471
                                errMsg := fmt.Errorf("failed to get encap IP for node network %s: %w", podSubnet.Spec.NodeNetwork, err)
×
472
                                klog.Error(errMsg)
×
NEW
473
                                recordFailure("encap-ip", errMsg)
×
474
                                if err = resp.WriteHeaderAndEntity(http.StatusInternalServerError, request.CniResponse{Err: errMsg.Error()}); err != nil {
×
475
                                        klog.Errorf("failed to write response: %v", err)
×
476
                                }
×
477
                                return
×
478
                        }
479
                }
480

481
                var localnetSubnet string
×
482
                if subnetHasVlan && !podSubnet.Spec.LogicalGateway {
×
483
                        localnetSubnet = subnet
×
484
                }
×
485

486
                switch nicType {
×
487
                case util.DpdkType:
×
488
                        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)
×
489
                        routes = nil
×
490
                default:
×
491
                        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)
×
492
                }
493
                if err != nil {
×
494
                        errMsg := fmt.Errorf("configure nic %s for pod %s/%s failed: %w", ifName, podRequest.PodName, podRequest.PodNamespace, err)
×
495
                        klog.Error(errMsg)
×
NEW
496
                        recordFailure("configure-nic", errMsg)
×
497
                        if err := resp.WriteHeaderAndEntity(http.StatusInternalServerError, request.CniResponse{Err: errMsg.Error()}); err != nil {
×
498
                                klog.Errorf("failed to write response, %v", err)
×
499
                        }
×
500
                        return
×
501
                }
502

503
                ifaceID := ovs.PodNameToPortName(podRequest.PodName, podRequest.PodNamespace, podRequest.Provider)
×
504
                if err = ovs.ConfigInterfaceMirror(csh.Config.EnableMirror, pod.Annotations[fmt.Sprintf(util.MirrorControlAnnotationTemplate, podRequest.Provider)], ifaceID); err != nil {
×
505
                        klog.Errorf("failed mirror to mirror0, %v", err)
×
NEW
506
                        recordFailure("mirror", err)
×
507
                        return
×
508
                }
×
509

510
                if err = csh.Controller.addEgressConfig(podSubnet, ip); err != nil {
×
511
                        errMsg := fmt.Errorf("failed to add egress configuration: %w", err)
×
512
                        klog.Error(errMsg)
×
NEW
513
                        recordFailure("egress", errMsg)
×
514
                        if err = resp.WriteHeaderAndEntity(http.StatusInternalServerError, request.CniResponse{Err: errMsg.Error()}); err != nil {
×
515
                                klog.Errorf("failed to write response, %v", err)
×
516
                        }
×
517
                        return
×
518
                }
519
        } else if len(routes) != 0 {
1✔
520
                hasDefaultRoute := make(map[string]bool, 2)
×
521
                for _, r := range routes {
×
522
                        if r.Destination == "" {
×
523
                                hasDefaultRoute[util.CheckProtocol(r.Gateway)] = true
×
524
                                continue
×
525
                        }
526
                        if _, cidr, err := net.ParseCIDR(r.Destination); err == nil {
×
527
                                if ones, _ := cidr.Mask.Size(); ones == 0 {
×
528
                                        hasDefaultRoute[util.CheckProtocol(r.Gateway)] = true
×
529
                                }
×
530
                        }
531
                }
532
                if len(hasDefaultRoute) != 0 {
×
533
                        // remove existing default route so other CNI plugins, such as macvlan, can add the new default route correctly
×
534
                        if err = csh.removeDefaultRoute(podRequest.NetNs, hasDefaultRoute[kubeovnv1.ProtocolIPv4], hasDefaultRoute[kubeovnv1.ProtocolIPv6]); err != nil {
×
535
                                errMsg := fmt.Errorf("failed to remove existing default route for interface %s of pod %s/%s: %w", podRequest.IfName, podRequest.PodNamespace, podRequest.PodName, err)
×
536
                                klog.Error(errMsg)
×
NEW
537
                                recordFailure("remove-default-route", errMsg)
×
538
                                if err = resp.WriteHeaderAndEntity(http.StatusInternalServerError, request.CniResponse{Err: errMsg.Error()}); err != nil {
×
539
                                        klog.Errorf("failed to write response: %v", err)
×
540
                                }
×
541
                                return
×
542
                        }
543
                }
544
        }
545

546
        v4IP, v6IP := util.SplitStringIP(ip)
1✔
547
        v4CIDR, v6CIDR := util.SplitStringIP(cidr)
1✔
548
        v4GW, v6GW := util.SplitStringIP(gw)
1✔
549

1✔
550
        var ips []request.IPConfig
1✔
551
        if v4IP != "" {
2✔
552
                cfg := request.IPConfig{Protocol: kubeovnv1.ProtocolIPv4, IP: v4IP, CIDR: v4CIDR}
1✔
553
                if isDefaultRoute {
1✔
554
                        cfg.Gateway = v4GW
×
555
                }
×
556
                ips = append(ips, cfg)
1✔
557
        }
558
        if v6IP != "" {
1✔
559
                cfg := request.IPConfig{Protocol: kubeovnv1.ProtocolIPv6, IP: v6IP, CIDR: v6CIDR}
×
560
                if isDefaultRoute {
×
561
                        cfg.Gateway = v6GW
×
562
                }
×
563
                ips = append(ips, cfg)
×
564
        }
565

566
        response := &request.CniResponse{
1✔
567
                IPs:        ips,
1✔
568
                MacAddress: macAddr,
1✔
569
                PodNicName: podNicName,
1✔
570
                Routes:     routes,
1✔
571
                Mtu:        mtu,
1✔
572
        }
1✔
573
        eventMAC := macAddr
1✔
574
        if eventMAC == "" {
2✔
575
                eventMAC = util.GetAnnotationWithIfNameOverride(pod.Annotations, podRequest.Provider, podRequest.IfName, util.MacAddressAnnotationTemplate, appendIfName)
1✔
576
        }
1✔
577
        csh.recordCNIPodEvent(eventPod, &podRequest, v1.EventTypeNormal, "PodNetworkConfigured", fmt.Sprintf("subnet=%s ip=%s mac=%s", subnet, ip, eventMAC))
1✔
578
        if err := resp.WriteHeaderAndEntity(http.StatusOK, response); err != nil {
1✔
579
                klog.Errorf("failed to write response, %v", err)
×
580
        }
×
581
}
582

583
func (csh cniServerHandler) UpdateIPCR(podRequest request.CniRequest, subnet, ip string, appendIfName bool) error {
1✔
584
        klog.V(4).Infof("found subnet %s", subnet)
1✔
585
        ipCRName := ovs.PodNameToPortName(podRequest.PodName, podRequest.PodNamespace, podRequest.Provider)
1✔
586

1✔
587
        // for backward compatibility we will check if annotation ip address exists with ifName first and subsequently with ifname
1✔
588
        if appendIfName {
1✔
589
                ipCRName = fmt.Sprintf("%s.%s", ipCRName, podRequest.IfName)
×
590
        }
×
591
        for range 20 {
2✔
592
                ipCR, err := csh.KubeOvnClient.KubeovnV1().IPs().Get(context.Background(), ipCRName, metav1.GetOptions{})
1✔
593
                if err != nil {
1✔
594
                        err = fmt.Errorf("failed to get ip crd for %s, %w", ip, err)
×
595
                        // maybe create a backup pod with previous annotations
×
596
                        klog.Error(err)
×
597
                } else if ipCR.Spec.NodeName != csh.Config.NodeName {
1✔
598
                        ipCR := ipCR.DeepCopy()
×
599
                        if ipCR.Labels == nil {
×
600
                                ipCR.Labels = map[string]string{}
×
601
                        }
×
602
                        ipCR.Spec.NodeName = csh.Config.NodeName
×
603
                        ipCR.Spec.AttachIPs = []string{}
×
604
                        ipCR.Labels[subnet] = ""
×
605
                        ipCR.Labels[util.NodeNameLabel] = csh.Config.NodeName
×
606
                        ipCR.Spec.AttachSubnets = []string{}
×
607
                        ipCR.Spec.AttachMacs = []string{}
×
608
                        if _, err := csh.KubeOvnClient.KubeovnV1().IPs().Update(context.Background(), ipCR, metav1.UpdateOptions{}); err != nil {
×
609
                                err = fmt.Errorf("failed to update ip crd for %s, %w", ip, err)
×
610
                                klog.Error(err)
×
611
                        } else {
×
612
                                return nil
×
613
                        }
×
614
                }
615
                if err != nil {
1✔
616
                        klog.Warningf("wait pod ip %s to be ready", ipCRName)
×
617
                        time.Sleep(1 * time.Second)
×
618
                } else {
1✔
619
                        return nil
1✔
620
                }
1✔
621
        }
622
        // update ip spec node is not that necessary, so we just log the error
623
        return nil
×
624
}
625

626
func (csh cniServerHandler) handleDel(req *restful.Request, resp *restful.Response) {
1✔
627
        var podRequest request.CniRequest
1✔
628
        var appendIfName bool
1✔
629

1✔
630
        if err := req.ReadEntity(&podRequest); err != nil {
2✔
631
                errMsg := fmt.Errorf("parse del request failed %w", err)
1✔
632
                klog.Error(errMsg)
1✔
633
                if err := resp.WriteHeaderAndEntity(http.StatusBadRequest, request.CniResponse{Err: errMsg.Error()}); err != nil {
1✔
634
                        klog.Errorf("failed to write response, %v", err)
×
635
                }
×
636
                return
1✔
637
        }
638
        eventPod := podForCNIEvent(nil, &podRequest)
1✔
639
        recordFailure := func(stage string, err error) {
2✔
640
                csh.recordCNIPodEvent(eventPod, &podRequest, v1.EventTypeWarning, "PodNetworkRemoveFailed", fmt.Sprintf("stage=%s error=%v", stage, err))
1✔
641
        }
1✔
642

643
        // Try to get the Pod, but if it fails due to not being found, log a warning and continue.
644
        pod, err := csh.Controller.podsLister.Pods(podRequest.PodNamespace).Get(podRequest.PodName)
1✔
645
        if err != nil && !k8serrors.IsNotFound(err) {
1✔
646
                errMsg := fmt.Errorf("failed to retrieve Pod %s/%s: %w", podRequest.PodNamespace, podRequest.PodName, err)
×
647
                klog.Error(errMsg)
×
NEW
648
                recordFailure("get-pod", errMsg)
×
649
                if err := resp.WriteHeaderAndEntity(http.StatusBadRequest, request.CniResponse{Err: errMsg.Error()}); err != nil {
×
650
                        klog.Errorf("failed to write response, %v", err)
×
651
                }
×
652
                return
×
653
        }
654
        if pod != nil {
2✔
655
                eventPod = pod
1✔
656
        }
1✔
657

658
        providerWithIfName := fmt.Sprintf("%s.%s", podRequest.Provider, podRequest.IfName)
1✔
659
        if podRequest.NetNs == "" {
2✔
660
                klog.Infof("skip del port request: %v", podRequest)
1✔
661
                resp.WriteHeader(http.StatusNoContent)
1✔
662
                return
1✔
663
        }
1✔
664

665
        klog.Infof("del port request: %v", podRequest)
1✔
666
        if err := csh.validatePodRequest(&podRequest); err != nil {
1✔
667
                klog.Error(err)
×
NEW
668
                recordFailure("validate-request", err)
×
669
                if err := resp.WriteHeaderAndEntity(http.StatusBadRequest, request.CniResponse{Err: err.Error()}); err != nil {
×
670
                        klog.Errorf("failed to write response, %v", err)
×
671
                }
×
672
                return
×
673
        }
674

675
        var nicType string
1✔
676
        var vmName string
1✔
677

1✔
678
        // If the Pod was found, process its annotations and labels.
1✔
679
        if pod != nil {
2✔
680
                if pod.Annotations != nil && (util.IsOvnProvider(podRequest.Provider) || podRequest.CniType == util.CniTypeName) {
2✔
681
                        _, ok := pod.Annotations[fmt.Sprintf(util.IPAddressAnnotationTemplate, providerWithIfName)]
1✔
682
                        if ok {
1✔
683
                                appendIfName = true
×
684
                        }
×
685
                        subnet := util.GetAnnotationWithIfNameOverride(pod.Annotations, podRequest.Provider, podRequest.IfName, util.LogicalSwitchAnnotationTemplate, appendIfName)
1✔
686
                        if subnet != "" {
1✔
687
                                ip := util.GetAnnotationWithIfNameOverride(pod.Annotations, podRequest.Provider, podRequest.IfName, util.IPAddressAnnotationTemplate, appendIfName)
×
688
                                if err = csh.Controller.removeEgressConfig(subnet, ip); err != nil {
×
689
                                        errMsg := fmt.Errorf("failed to remove egress configuration: %w", err)
×
690
                                        klog.Error(errMsg)
×
NEW
691
                                        recordFailure("remove-egress", errMsg)
×
692
                                        if err = resp.WriteHeaderAndEntity(http.StatusInternalServerError, request.CniResponse{Err: errMsg.Error()}); err != nil {
×
693
                                                klog.Errorf("failed to write response, %v", err)
×
694
                                        }
×
695
                                        return
×
696
                                }
697
                        }
698

699
                        switch {
1✔
700
                        case podRequest.DeviceID != "":
×
701
                                nicType = util.OffloadType
×
702
                        case podRequest.VhostUserSocketVolumeName != "":
×
703
                                nicType = util.DpdkType
×
704
                                if err = removeShortSharedDir(pod, podRequest.VhostUserSocketVolumeName, podRequest.VhostUserSocketConsumption); err != nil {
×
705
                                        klog.Error(err.Error())
×
NEW
706
                                        recordFailure("remove-dpdk-dir", err)
×
707
                                        if err = resp.WriteHeaderAndEntity(http.StatusInternalServerError, request.CniResponse{Err: err.Error()}); err != nil {
×
708
                                                klog.Errorf("failed to write response: %v", err)
×
709
                                        }
×
710
                                        return
×
711
                                }
712
                        default:
1✔
713
                                nicType = pod.Annotations[fmt.Sprintf(util.PodNicAnnotationTemplate, podRequest.Provider)]
1✔
714
                        }
715

716
                        vmName = pod.Annotations[fmt.Sprintf(util.VMAnnotationTemplate, podRequest.Provider)]
1✔
717
                        if vmName != "" {
2✔
718
                                podRequest.PodName = vmName
1✔
719
                        }
1✔
720
                }
721
        } else {
1✔
722
                // If the Pod is not found, assign a default value.
1✔
723
                klog.Warningf("Pod %s not found, proceeding with NIC deletion using ContainerID and NetNs", podRequest.PodName)
1✔
724
                switch {
1✔
725
                case podRequest.DeviceID != "":
×
726
                        nicType = util.OffloadType
×
727
                case podRequest.VhostUserSocketVolumeName != "":
×
728
                        nicType = util.DpdkType
×
729
                default:
1✔
730
                        nicType = util.VethType
1✔
731
                }
732
        }
733

734
        // To support KubeVirt hotplug dpdk nic, forbidden set the volume name
735
        if podRequest.VhostUserSocketConsumption == util.ConsumptionKubevirt {
1✔
736
                podRequest.VhostUserSocketVolumeName = util.VhostUserSocketVolumeName
×
737
        }
×
738

739
        // Proceed to delete the NIC regardless of whether the Pod was found or not.
740
        err = csh.deleteNic(podRequest.PodName, podRequest.PodNamespace, podRequest.ContainerID, podRequest.NetNs, podRequest.DeviceID, podRequest.IfName, nicType)
1✔
741
        if err != nil {
2✔
742
                errMsg := fmt.Errorf("del nic failed %w", err)
1✔
743
                klog.Error(errMsg)
1✔
744
                recordFailure("delete-nic", errMsg)
1✔
745
                if err := resp.WriteHeaderAndEntity(http.StatusInternalServerError, request.CniResponse{Err: errMsg.Error()}); err != nil {
1✔
746
                        klog.Errorf("failed to write response, %v", err)
×
747
                }
×
748
                return
1✔
749
        }
750
        csh.recordCNIPodEvent(eventPod, &podRequest, v1.EventTypeNormal, "PodNetworkRemoved", "removed pod network")
1✔
751
        resp.WriteHeader(http.StatusNoContent)
1✔
752
}
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