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

kubeovn / kube-ovn / 28942026866

08 Jul 2026 12:19PM UTC coverage: 27.431% (-0.05%) from 27.477%
28942026866

Pull #6697

github

web-flow
Merge branch 'master' into fix-vpc-external
Pull Request #6697: fix(vpc-external): reconcile vpc when gw or pn changes

6 of 171 new or added lines in 3 files covered. (3.51%)

6 existing lines in 2 files now uncovered.

16688 of 60836 relevant lines covered (27.43%)

0.32 hits per line

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

17.75
/pkg/controller/node.go
1
package controller
2

3
import (
4
        "context"
5
        "errors"
6
        "fmt"
7
        "maps"
8
        "slices"
9
        "strconv"
10
        "strings"
11
        "time"
12

13
        goping "github.com/prometheus-community/pro-bing"
14
        "github.com/scylladb/go-set/strset"
15
        v1 "k8s.io/api/core/v1"
16
        k8serrors "k8s.io/apimachinery/pkg/api/errors"
17
        metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
18
        "k8s.io/apimachinery/pkg/labels"
19
        "k8s.io/apimachinery/pkg/types"
20
        "k8s.io/client-go/tools/cache"
21
        "k8s.io/klog/v2"
22

23
        kubeovnv1 "github.com/kubeovn/kube-ovn/pkg/apis/kubeovn/v1"
24
        "github.com/kubeovn/kube-ovn/pkg/ovs"
25
        "github.com/kubeovn/kube-ovn/pkg/util"
26
)
27

28
type ErrChassisNotFound struct {
29
        Chassis string
30
        Node    string
31
}
32

33
func (e *ErrChassisNotFound) Error() string {
×
34
        return fmt.Sprintf("chassis %s not found for node %s", e.Chassis, e.Node)
×
35
}
×
36

37
func (c *Controller) enqueueAddNode(obj any) {
1✔
38
        node := obj.(*v1.Node)
1✔
39
        key := cache.MetaObjectToName(node).String()
1✔
40
        klog.V(3).Infof("enqueue add node %s", key)
1✔
41
        c.addNodeQueue.Add(key)
1✔
42
        c.enqueueVpcBFDPortByNodeChange(nil, node)
1✔
43
}
1✔
44

45
// enqueueExternalVpcsForReconcile re-queues all custom VPCs that use external subnets
46
// so their LRP gateway-chassis lists are kept in sync with external-gateway nodes.
NEW
47
func (c *Controller) enqueueExternalVpcsForReconcile() {
×
NEW
48
        vpcs, err := c.vpcsLister.List(labels.Everything())
×
NEW
49
        if err != nil {
×
NEW
50
                klog.Errorf("failed to list vpcs for external reconcile: %v", err)
×
NEW
51
                return
×
NEW
52
        }
×
NEW
53
        for _, vpc := range vpcs {
×
NEW
54
                if _, ok := vpc.Labels[util.VpcExternalLabel]; ok {
×
NEW
55
                        continue
×
56
                }
NEW
57
                if vpc.Spec.EnableExternal || vpc.Status.EnableExternal {
×
NEW
58
                        c.addOrUpdateVpcQueue.Add(vpc.Name)
×
NEW
59
                }
×
60
        }
61
}
62

63
func nodeReady(node *v1.Node) bool {
1✔
64
        var ready, networkUnavailable bool
1✔
65
        for _, c := range node.Status.Conditions {
2✔
66
                switch c.Type {
1✔
67
                case v1.NodeReady:
1✔
68
                        ready = c.Status == v1.ConditionTrue
1✔
69
                case v1.NodeNetworkUnavailable:
×
70
                        networkUnavailable = c.Status == v1.ConditionTrue
×
71
                }
72
        }
73
        return ready && !networkUnavailable
1✔
74
}
75

76
func kubeOvnAnnotationsChanged(oldAnnotations, newAnnotations map[string]string) bool {
1✔
77
        filterKubeOvnAnnotations := func(annotations map[string]string) map[string]string {
2✔
78
                filtered := make(map[string]string)
1✔
79
                for key, value := range annotations {
2✔
80
                        if strings.Contains(key, ".kubernetes.io/") {
2✔
81
                                filtered[key] = value
1✔
82
                        }
1✔
83
                }
84
                return filtered
1✔
85
        }
86

87
        return !maps.Equal(filterKubeOvnAnnotations(oldAnnotations), filterKubeOvnAnnotations(newAnnotations))
1✔
88
}
89

90
func (c *Controller) listVpcBFDPorts() ([]*kubeovnv1.Vpc, error) {
1✔
91
        if c.vpcIndexer == nil {
1✔
92
                return c.vpcsLister.List(labels.Everything())
×
93
        }
×
94

95
        objs, err := c.vpcIndexer.ByIndex(IndexVpcByBFDPort, IndexVpcBFDPortEnabled)
1✔
96
        if err != nil {
1✔
97
                return nil, err
×
98
        }
×
99
        vpcs := make([]*kubeovnv1.Vpc, 0, len(objs))
1✔
100
        for _, obj := range objs {
2✔
101
                vpc, ok := obj.(*kubeovnv1.Vpc)
1✔
102
                if !ok {
1✔
103
                        klog.Warningf("unexpected object type in VPC BFDPort indexer: %T", obj)
×
104
                        continue
×
105
                }
106
                vpcs = append(vpcs, vpc)
1✔
107
        }
108
        return vpcs, nil
1✔
109
}
110

111
func (c *Controller) enqueueVpcBFDPortByNodeChange(oldNode, newNode *v1.Node) {
1✔
112
        vpcs, err := c.listVpcBFDPorts()
1✔
113
        if err != nil {
1✔
114
                klog.Errorf("failed to list VPC BFD ports for node update: %v", err)
×
115
                return
×
116
        }
×
117

118
        for _, vpc := range vpcs {
2✔
119
                if vpc.Labels != nil && vpc.Labels[util.VpcExternalLabel] == "true" {
1✔
120
                        continue
×
121
                }
122
                if !vpc.Spec.BFDPort.IsEnabled() {
1✔
123
                        continue
×
124
                }
125

126
                selector := labels.Everything()
1✔
127
                if vpc.Spec.BFDPort.NodeSelector != nil {
2✔
128
                        selector, err = metav1.LabelSelectorAsSelector(vpc.Spec.BFDPort.NodeSelector)
1✔
129
                        if err != nil {
1✔
130
                                klog.Errorf("failed to parse BFD port node selector of vpc %s: %v", vpc.Name, err)
×
131
                                c.addOrUpdateVpcQueue.Add(vpc.Name)
×
132
                                continue
×
133
                        }
134
                }
135

136
                oldMatched := oldNode != nil && selector.Matches(labels.Set(oldNode.Labels))
1✔
137
                newMatched := newNode != nil && selector.Matches(labels.Set(newNode.Labels))
1✔
138
                if oldMatched || newMatched {
2✔
139
                        klog.V(3).Infof("enqueue update vpc %s for BFD port triggered by node change", vpc.Name)
1✔
140
                        c.addOrUpdateVpcQueue.Add(vpc.Name)
1✔
141
                }
1✔
142
        }
143
}
144

145
func (c *Controller) enqueueUpdateNode(oldObj, newObj any) {
1✔
146
        oldNode := oldObj.(*v1.Node)
1✔
147
        newNode := newObj.(*v1.Node)
1✔
148
        nodeReadyChanged := nodeReady(oldNode) != nodeReady(newNode)
1✔
149
        nodeLabelsChanged := !maps.Equal(oldNode.Labels, newNode.Labels)
1✔
150

1✔
151
        key := cache.MetaObjectToName(newNode).String()
1✔
152
        if nodeReadyChanged || kubeOvnAnnotationsChanged(oldNode.Annotations, newNode.Annotations) {
2✔
153
                if len(newNode.Annotations) == 0 || newNode.Annotations[util.AllocatedAnnotation] != "true" {
2✔
154
                        klog.V(3).Infof("enqueue add node %s", key)
1✔
155
                        c.addNodeQueue.Add(key)
1✔
156
                } else {
1✔
157
                        klog.V(3).Infof("enqueue update node %s", key)
×
158
                        c.updateNodeQueue.Add(key)
×
159
                }
×
160
        } else if nodeLabelsChanged && newNode.Annotations[util.AllocatedAnnotation] == "true" {
2✔
161
                klog.V(3).Infof("enqueue update node %s for label change", key)
1✔
162
                c.updateNodeQueue.Add(key)
1✔
163
        }
1✔
164
        if nodeReadyChanged || nodeLabelsChanged {
2✔
165
                c.enqueueVpcBFDPortByNodeChange(oldNode, newNode)
1✔
166
        }
1✔
167
        if oldNode.Labels[util.ExGatewayLabel] != newNode.Labels[util.ExGatewayLabel] {
1✔
NEW
168
                c.enqueueExternalVpcsForReconcile()
×
NEW
169
        }
×
170
}
171

172
func (c *Controller) enqueueDeleteNode(obj any) {
1✔
173
        var node *v1.Node
1✔
174
        switch t := obj.(type) {
1✔
175
        case *v1.Node:
×
176
                node = t
×
177
        case cache.DeletedFinalStateUnknown:
1✔
178
                n, ok := t.Obj.(*v1.Node)
1✔
179
                if !ok {
1✔
180
                        klog.Warningf("unexpected object type: %T", t.Obj)
×
181
                        return
×
182
                }
×
183
                node = n
1✔
184
        default:
×
185
                klog.Warningf("unexpected type: %T", obj)
×
186
                return
×
187
        }
188

189
        key := cache.MetaObjectToName(node).String()
1✔
190
        klog.V(3).Infof("enqueue delete node %s", key)
1✔
191
        c.deletingNodeObjMap.Store(key, node)
1✔
192
        c.deleteNodeQueue.Add(key)
1✔
193
        c.enqueueVpcBFDPortByNodeChange(node, nil)
1✔
194
}
195

196
func nodeUnderlayAddressSetName(node string, af int) string {
×
197
        return fmt.Sprintf("node_%s_underlay_v%d", strings.ReplaceAll(node, "-", "_"), af)
×
198
}
×
199

200
func (c *Controller) handleAddNode(key string) error {
×
201
        c.nodeKeyMutex.LockKey(key)
×
202
        defer func() { _ = c.nodeKeyMutex.UnlockKey(key) }()
×
203

204
        cachedNode, err := c.nodesLister.Get(key)
×
205
        if err != nil {
×
206
                if k8serrors.IsNotFound(err) {
×
207
                        return nil
×
208
                }
×
209
                klog.Errorf("failed to get node %s: %v", key, err)
×
210
                return err
×
211
        }
212
        node := cachedNode.DeepCopy()
×
213
        klog.Infof("handle add node %s", node.Name)
×
214

×
215
        subnets, err := c.subnetsLister.List(labels.Everything())
×
216
        if err != nil {
×
217
                klog.Errorf("failed to list subnets: %v", err)
×
218
                return err
×
219
        }
×
220

221
        nodeIPv4, nodeIPv6 := util.GetNodeInternalIP(*node)
×
222
        for _, subnet := range subnets {
×
223
                if subnet.Spec.Vpc != c.config.ClusterRouter {
×
224
                        continue
×
225
                }
226

227
                v4, v6 := util.SplitStringIP(subnet.Spec.CIDRBlock)
×
228
                if subnet.Spec.Vlan == "" && (util.CIDRContainIP(v4, nodeIPv4) || util.CIDRContainIP(v6, nodeIPv6)) {
×
229
                        msg := fmt.Sprintf("internal IP address of node %s is in CIDR of subnet %s, this may result in network issues", node.Name, subnet.Name)
×
230
                        klog.Warning(msg)
×
231
                        c.recorder.Eventf(&v1.Node{ObjectMeta: metav1.ObjectMeta{Name: node.Name, UID: types.UID(node.Name)}}, v1.EventTypeWarning, "NodeAddressConflictWithSubnet", "%s", msg)
×
232
                        break
×
233
                }
234
        }
235

236
        if err = c.handleNodeAnnotationsForProviderNetworks(node); err != nil {
×
237
                klog.Errorf("failed to handle annotations of node %s for provider networks: %v", node.Name, err)
×
238
                return err
×
239
        }
×
240

241
        subnet, err := c.subnetsLister.Get(c.config.NodeSwitch)
×
242
        if err != nil {
×
243
                klog.Errorf("failed to get node subnet: %v", err)
×
244
                return err
×
245
        }
×
246

247
        var v4IP, v6IP, mac string
×
248

×
249
        portName := util.NodeLspName(key)
×
250
        if node.Annotations[util.AllocatedAnnotation] == "true" && node.Annotations[util.IPAddressAnnotation] != "" && node.Annotations[util.MacAddressAnnotation] != "" {
×
251
                macStr := node.Annotations[util.MacAddressAnnotation]
×
252
                v4IP, v6IP, mac, err = c.ipam.GetStaticAddress(portName, portName, node.Annotations[util.IPAddressAnnotation],
×
253
                        &macStr, node.Annotations[util.LogicalSwitchAnnotation], true)
×
254
                if err != nil {
×
255
                        klog.Errorf("failed to alloc static ip addrs for node %v: %v", node.Name, err)
×
256
                        return err
×
257
                }
×
258
        } else {
×
259
                v4IP, v6IP, mac, err = c.ipam.GetRandomAddress(portName, portName, nil, c.config.NodeSwitch, "", nil, true)
×
260
                if err != nil {
×
261
                        klog.Errorf("failed to alloc random ip addrs for node %v: %v", node.Name, err)
×
262
                        return err
×
263
                }
×
264

265
                // Clean up potentially existing logical switch ports to avoid leftover issues from previous failed configurations
266
                if err := c.OVNNbClient.DeleteLogicalSwitchPort(portName); err != nil {
×
267
                        klog.Errorf("failed to delete stale logical switch port %s: %v", portName, err)
×
268
                        return err
×
269
                }
×
270
                klog.Infof("deleted stale logical switch port %s", portName)
×
271
        }
272

273
        ipStr := util.GetStringIP(v4IP, v6IP)
×
274
        if err := c.OVNNbClient.CreateBareLogicalSwitchPort(c.config.NodeSwitch, portName, ipStr, mac); err != nil {
×
275
                klog.Errorf("failed to create logical switch port %s: %v", portName, err)
×
276
                return err
×
277
        }
×
278

279
        for ip := range strings.SplitSeq(ipStr, ",") {
×
280
                if ip == "" {
×
281
                        continue
×
282
                }
283

284
                nodeIP, af := nodeIPv4, 4
×
285
                protocol := util.CheckProtocol(ip)
×
286
                if protocol == kubeovnv1.ProtocolIPv6 {
×
287
                        nodeIP, af = nodeIPv6, 6
×
288
                }
×
289
                if nodeIP != "" {
×
290
                        var (
×
291
                                match       = fmt.Sprintf("ip%d.dst == %s", af, nodeIP)
×
292
                                action      = kubeovnv1.PolicyRouteActionReroute
×
293
                                externalIDs = map[string]string{
×
294
                                        "vendor":         util.CniTypeName,
×
295
                                        "node":           node.Name,
×
296
                                        "address-family": strconv.Itoa(af),
×
297
                                }
×
298
                        )
×
299
                        klog.Infof("add policy route for router: %s, match %s, action %s, nexthop %s, externalID %v", c.config.ClusterRouter, match, action, ip, externalIDs)
×
300
                        if err = c.addPolicyRouteToVpc(
×
301
                                c.config.ClusterRouter,
×
302
                                &kubeovnv1.PolicyRoute{
×
303
                                        Priority:  util.NodeRouterPolicyPriority,
×
304
                                        Match:     match,
×
305
                                        Action:    action,
×
306
                                        NextHopIP: ip,
×
307
                                },
×
308
                                externalIDs,
×
309
                        ); err != nil {
×
310
                                klog.Errorf("failed to add logical router policy for node %s: %v", node.Name, err)
×
311
                                return err
×
312
                        }
×
313

314
                        dnsIPs := make([]string, 0, len(c.config.NodeLocalDNSIPs))
×
315
                        for _, ip := range c.config.NodeLocalDNSIPs {
×
316
                                if util.CheckProtocol(ip) == protocol {
×
317
                                        dnsIPs = append(dnsIPs, ip)
×
318
                                }
×
319
                        }
320

321
                        if err = c.addPolicyRouteForLocalDNSCacheOnNode(dnsIPs, portName, ip, node.Name, af); err != nil {
×
322
                                klog.Errorf("failed to add policy route for node %s: %v", node.Name, err)
×
323
                                return err
×
324
                        }
×
325
                }
326
        }
327

328
        patch := util.KVPatch{
×
329
                util.IPAddressAnnotation:     ipStr,
×
330
                util.MacAddressAnnotation:    mac,
×
331
                util.CidrAnnotation:          subnet.Spec.CIDRBlock,
×
332
                util.GatewayAnnotation:       subnet.Spec.Gateway,
×
333
                util.LogicalSwitchAnnotation: c.config.NodeSwitch,
×
334
                util.AllocatedAnnotation:     "true",
×
335
                util.PortNameAnnotation:      portName,
×
336
        }
×
337
        if err = util.PatchAnnotations(c.config.KubeClient.CoreV1().Nodes(), node.Name, patch); err != nil {
×
338
                klog.Errorf("failed to update annotations of node %s: %v", node.Name, err)
×
339
                return err
×
340
        }
×
341

342
        if err := c.createOrUpdateIPCR("", "", ipStr, mac, c.config.NodeSwitch, "", node.Name, ""); err != nil {
×
343
                klog.Errorf("failed to create or update IPs %s: %v", portName, err)
×
344
                return err
×
345
        }
×
346

347
        for _, subnet := range subnets {
×
348
                if (subnet.Spec.Vlan != "" && !subnet.Spec.LogicalGateway) || subnet.Spec.Vpc != c.config.ClusterRouter || subnet.Name == c.config.NodeSwitch || subnet.Spec.GatewayType != kubeovnv1.GWDistributedType {
×
349
                        continue
×
350
                }
351
                if err = c.createPortGroupForDistributedSubnet(node, subnet); err != nil {
×
352
                        klog.Errorf("failed to create port group for node %s and subnet %s: %v", node.Name, subnet.Name, err)
×
353
                        return err
×
354
                }
×
355
        }
356
        c.distributedSubnetNeedSync.Store(true)
×
357

×
358
        // ovn acl doesn't support address_set name with '-', so replace '-' by '.'
×
359
        pgName := strings.ReplaceAll(portName, "-", ".")
×
360
        if err = c.OVNNbClient.CreatePortGroup(pgName, map[string]string{"node": node.Name, networkPolicyKey: "node" + "/" + key}); err != nil {
×
361
                klog.Errorf("create port group %s for node %s: %v", pgName, key, err)
×
362
                return err
×
363
        }
×
364

365
        if err := c.addPolicyRouteForCentralizedSubnetOnNode(node, ipStr); err != nil {
×
366
                klog.Errorf("failed to add policy route for node %s, %v", key, err)
×
367
                return err
×
368
        }
×
369

370
        if err := c.UpdateChassisTag(node); err != nil {
×
371
                klog.Errorf("failed to update chassis tag for node %s: %v", node.Name, err)
×
372
                return err
×
373
        }
×
374

375
        if err := c.retryDelDupChassis(util.ChassisRetryMaxTimes, util.ChassisControllerRetryInterval, c.cleanDuplicatedChassis, node); err != nil {
×
376
                klog.Errorf("failed to clean duplicated chassis for node %s: %v", node.Name, err)
×
377
                return err
×
378
        }
×
NEW
379
        if _, ok := node.Labels[util.ExGatewayLabel]; ok {
×
NEW
380
                c.enqueueExternalVpcsForReconcile()
×
NEW
381
        }
×
UNCOV
382
        return nil
×
383
}
384

385
func (c *Controller) handleNodeAnnotationsForProviderNetworks(node *v1.Node) error {
×
386
        providerNetworks, err := c.providerNetworksLister.List(labels.Everything())
×
387
        if err != nil && !k8serrors.IsNotFound(err) {
×
388
                klog.Errorf("failed to list provider networks: %v", err)
×
389
                return err
×
390
        }
×
391

392
        for _, pn := range providerNetworks {
×
393
                excludeAnno := fmt.Sprintf(util.ProviderNetworkExcludeTemplate, pn.Name)
×
394
                interfaceAnno := fmt.Sprintf(util.ProviderNetworkInterfaceTemplate, pn.Name)
×
395

×
396
                var newPn *kubeovnv1.ProviderNetwork
×
397
                excluded, err := util.IsNodeExcludedFromProviderNetwork(node, pn)
×
398
                if err != nil {
×
399
                        klog.Error(err)
×
400
                        return err
×
401
                }
×
402

403
                // Handle node annotation for exclusion (only when nodeSelector is not set)
404
                if !excluded && pn.Spec.NodeSelector == nil && len(node.Annotations) != 0 && node.Annotations[excludeAnno] == "true" {
×
405
                        newPn = pn.DeepCopy()
×
406
                        newPn.Spec.ExcludeNodes = append(newPn.Spec.ExcludeNodes, node.Name)
×
407
                        excluded = true
×
408
                }
×
409

410
                var customInterface string
×
411
                for _, v := range pn.Spec.CustomInterfaces {
×
412
                        if slices.Contains(v.Nodes, node.Name) {
×
413
                                customInterface = v.Interface
×
414
                                break
×
415
                        }
416
                }
417
                if customInterface == "" && len(node.Annotations) != 0 {
×
418
                        if customInterface = node.Annotations[interfaceAnno]; customInterface != "" {
×
419
                                if newPn == nil {
×
420
                                        newPn = pn.DeepCopy()
×
421
                                }
×
422
                                var index *int
×
423
                                for i := range newPn.Spec.CustomInterfaces {
×
424
                                        if newPn.Spec.CustomInterfaces[i].Interface == customInterface {
×
425
                                                index = &i
×
426
                                                break
×
427
                                        }
428
                                }
429
                                if index != nil {
×
430
                                        newPn.Spec.CustomInterfaces[*index].Nodes = append(newPn.Spec.CustomInterfaces[*index].Nodes, node.Name)
×
431
                                } else {
×
432
                                        ci := kubeovnv1.CustomInterface{Interface: customInterface, Nodes: []string{node.Name}}
×
433
                                        newPn.Spec.CustomInterfaces = append(newPn.Spec.CustomInterfaces, ci)
×
434
                                }
×
435
                        }
436
                }
437

438
                if newPn != nil {
×
439
                        if newPn, err = c.config.KubeOvnClient.KubeovnV1().ProviderNetworks().Update(context.Background(), newPn, metav1.UpdateOptions{}); err != nil {
×
440
                                klog.Errorf("failed to update provider network %s: %v", pn.Name, err)
×
441
                                return err
×
442
                        }
×
443
                }
444

445
                if len(node.Annotations) != 0 {
×
446
                        patch := util.KVPatch{excludeAnno: nil, interfaceAnno: nil}
×
447
                        if err = util.PatchAnnotations(c.config.KubeClient.CoreV1().Nodes(), node.Name, patch); err != nil {
×
448
                                klog.Errorf("failed to patch node %s: %v", node.Name, err)
×
449
                                return err
×
450
                        }
×
451
                }
452

453
                if excluded {
×
454
                        if newPn == nil {
×
455
                                newPn = pn.DeepCopy()
×
456
                        } else {
×
457
                                newPn = newPn.DeepCopy()
×
458
                        }
×
459

460
                        if newPn.Status.EnsureNodeStandardConditions(node.Name) {
×
461
                                _, err = c.config.KubeOvnClient.KubeovnV1().ProviderNetworks().UpdateStatus(context.Background(), newPn, metav1.UpdateOptions{})
×
462
                                if err != nil {
×
463
                                        klog.Errorf("failed to update status of provider network %s: %v", pn.Name, err)
×
464
                                        return err
×
465
                                }
×
466
                        }
467
                }
468
        }
469

470
        return nil
×
471
}
472

473
func (c *Controller) handleDeleteNode(key string) (err error) {
×
474
        c.nodeKeyMutex.LockKey(key)
×
475
        defer func() {
×
476
                _ = c.nodeKeyMutex.UnlockKey(key)
×
477
                if err == nil {
×
478
                        c.deletingNodeObjMap.Delete(key)
×
479
                }
×
480
        }()
481
        klog.Infof("handle delete node %s", key)
×
482

×
483
        node, ok := c.deletingNodeObjMap.Load(key)
×
484
        if !ok {
×
485
                return nil
×
486
        }
×
487
        n, _ := c.nodesLister.Get(key)
×
488
        if n != nil && n.UID != node.UID {
×
489
                klog.Warningf("Node %s is adding, skip the node delete handler, but it may leave some gc resources behind", key)
×
490
                return nil
×
491
        }
×
NEW
492
        if err = c.deleteNode(key); err != nil {
×
NEW
493
                return err
×
NEW
494
        }
×
NEW
495
        if _, ok := node.Labels[util.ExGatewayLabel]; ok {
×
NEW
496
                c.enqueueExternalVpcsForReconcile()
×
NEW
497
        }
×
NEW
498
        return nil
×
499
}
500

501
func (c *Controller) deleteNode(key string) error {
×
502
        portName := util.NodeLspName(key)
×
503
        klog.Infof("delete logical switch port %s", portName)
×
504
        if err := c.OVNNbClient.DeleteLogicalSwitchPort(portName); err != nil {
×
505
                klog.Errorf("failed to delete node switch port %s: %v", portName, err)
×
506
                return err
×
507
        }
×
508
        if err := c.OVNSbClient.DeleteChassisByHost(key); err != nil {
×
509
                klog.Errorf("failed to delete chassis for node %s: %v", key, err)
×
510
                return err
×
511
        }
×
512

513
        for _, af := range [...]int{4, 6} {
×
514
                if err := c.deletePolicyRouteForLocalDNSCacheOnNode(key, af); err != nil {
×
515
                        klog.Error(err)
×
516
                        return err
×
517
                }
×
518
        }
519

520
        // ovn acl doesn't support address_set name with '-', so replace '-' by '.'
521
        pgName := strings.ReplaceAll(portName, "-", ".")
×
522
        if err := c.OVNNbClient.DeletePortGroup(pgName); err != nil {
×
523
                klog.Errorf("delete port group %s for node: %v", portName, err)
×
524
                return err
×
525
        }
×
526

527
        if err := c.deletePolicyRouteForNode(key, portName); err != nil {
×
528
                klog.Errorf("failed to delete policy route for node %s: %v", key, err)
×
529
                return err
×
530
        }
×
531

532
        if err := c.OVNNbClient.DeleteAddressSet(nodeUnderlayAddressSetName(key, 4)); err != nil {
×
533
                klog.Errorf("failed to delete address set for node %s: %v", key, err)
×
534
                return err
×
535
        }
×
536
        if err := c.OVNNbClient.DeleteAddressSet(nodeUnderlayAddressSetName(key, 6)); err != nil {
×
537
                klog.Errorf("failed to delete address set for node %s: %v", key, err)
×
538
                return err
×
539
        }
×
540

541
        klog.Infof("release node port %s", portName)
×
542
        c.ipam.ReleaseAddressByPod(portName, c.config.NodeSwitch)
×
543

×
544
        providerNetworks, err := c.providerNetworksLister.List(labels.Everything())
×
545
        if err != nil && !k8serrors.IsNotFound(err) {
×
546
                klog.Errorf("failed to list provider networks: %v", err)
×
547
                return err
×
548
        }
×
549

550
        for _, pn := range providerNetworks {
×
551
                if err = c.updateProviderNetworkForNodeDeletion(pn, key); err != nil {
×
552
                        klog.Error(err)
×
553
                        return err
×
554
                }
×
555
        }
556
        klog.Infof("delete node ip %s", portName)
×
557
        if err = c.config.KubeOvnClient.KubeovnV1().IPs().Delete(context.Background(), portName, metav1.DeleteOptions{}); err != nil && !k8serrors.IsNotFound(err) {
×
558
                return err
×
559
        }
×
560

561
        return nil
×
562
}
563

564
func (c *Controller) updateProviderNetworkForNodeDeletion(pn *kubeovnv1.ProviderNetwork, node string) error {
×
565
        // update provider network status
×
566
        var needUpdate bool
×
567
        newPn := pn.DeepCopy()
×
568
        if slices.Contains(newPn.Status.ReadyNodes, node) {
×
569
                newPn.Status.ReadyNodes = util.RemoveString(newPn.Status.ReadyNodes, node)
×
570
                needUpdate = true
×
571
        }
×
572
        if newPn.Status.RemoveNodeConditions(node) {
×
573
                needUpdate = true
×
574
        }
×
575
        if needUpdate {
×
576
                var err error
×
577
                newPn, err = c.config.KubeOvnClient.KubeovnV1().ProviderNetworks().UpdateStatus(context.Background(), newPn, metav1.UpdateOptions{})
×
578
                if err != nil {
×
579
                        klog.Errorf("failed to update status of provider network %s: %v", pn.Name, err)
×
580
                        return err
×
581
                }
×
582
        }
583

584
        // update provider network spec
585
        pn, newPn = newPn, nil
×
586
        if excludeNodes := util.RemoveString(pn.Spec.ExcludeNodes, node); len(excludeNodes) != len(pn.Spec.ExcludeNodes) {
×
587
                newPn = pn.DeepCopy()
×
588
                newPn.Spec.ExcludeNodes = excludeNodes
×
589
        }
×
590

591
        var changed bool
×
592
        customInterfaces := make([]kubeovnv1.CustomInterface, 0, len(pn.Spec.CustomInterfaces))
×
593
        for _, ci := range pn.Spec.CustomInterfaces {
×
594
                nodes := util.RemoveString(ci.Nodes, node)
×
595
                if !changed {
×
596
                        changed = len(nodes) == 0 || len(nodes) != len(ci.Nodes)
×
597
                }
×
598
                if len(nodes) != 0 {
×
599
                        customInterfaces = append(customInterfaces, kubeovnv1.CustomInterface{Interface: ci.Interface, Nodes: nodes})
×
600
                }
×
601
        }
602
        if changed {
×
603
                newPn = pn.DeepCopy()
×
604
                newPn.Spec.CustomInterfaces = customInterfaces
×
605
        }
×
606
        if newPn != nil {
×
607
                if _, err := c.config.KubeOvnClient.KubeovnV1().ProviderNetworks().Update(context.Background(), newPn, metav1.UpdateOptions{}); err != nil {
×
608
                        klog.Errorf("failed to update provider network %s: %v", pn.Name, err)
×
609
                        return err
×
610
                }
×
611
        }
612

613
        return nil
×
614
}
615

616
func (c *Controller) handleUpdateNode(key string) error {
×
617
        c.nodeKeyMutex.LockKey(key)
×
618
        defer func() { _ = c.nodeKeyMutex.UnlockKey(key) }()
×
619
        klog.Infof("handle update node %s", key)
×
620

×
621
        node, err := c.nodesLister.Get(key)
×
622
        if err != nil {
×
623
                if k8serrors.IsNotFound(err) {
×
624
                        return nil
×
625
                }
×
626
                klog.Errorf("failed to get node %s: %v", key, err)
×
627
                return err
×
628
        }
629

630
        if err = c.handleNodeAnnotationsForProviderNetworks(node); err != nil {
×
631
                klog.Errorf("failed to handle annotations of node %s for provider networks: %v", node.Name, err)
×
632
                return err
×
633
        }
×
634

635
        subnets, err := c.subnetsLister.List(labels.Everything())
×
636
        if err != nil {
×
637
                klog.Errorf("failed to get subnets %v", err)
×
638
                return err
×
639
        }
×
640

641
        if err := c.UpdateChassisTag(node); err != nil {
×
642
                klog.Errorf("failed to update chassis tag for node %s: %v", node.Name, err)
×
643
                return err
×
644
        }
×
645
        if err := c.retryDelDupChassis(util.ChassisRetryMaxTimes, util.ChassisControllerRetryInterval, c.cleanDuplicatedChassis, node); err != nil {
×
646
                klog.Errorf("failed to clean duplicated chassis for node %s: %v", node.Name, err)
×
647
                return err
×
648
        }
×
649

650
        c.distributedSubnetNeedSync.Store(true)
×
651

×
652
        for _, cachedSubnet := range subnets {
×
653
                if cachedSubnet.Spec.GatewayType != kubeovnv1.GWCentralizedType {
×
654
                        continue
×
655
                }
656

657
                // For subnets using GatewayNodeSelectors, always trigger reconciliation
658
                // when node labels change, since the node might have been added or removed
659
                // from the gateway list
660
                if cachedSubnet.Spec.GatewayNode == "" && len(cachedSubnet.Spec.GatewayNodeSelectors) > 0 {
×
661
                        c.addOrUpdateSubnetQueue.Add(cachedSubnet.Name)
×
662
                        continue
×
663
                }
664

665
                if util.GatewayContains(cachedSubnet.Spec.GatewayNode, node.Name) {
×
666
                        c.subnetKeyMutex.LockKey(cachedSubnet.Name)
×
667
                        err = func() error {
×
668
                                defer func() { _ = c.subnetKeyMutex.UnlockKey(cachedSubnet.Name) }()
×
669
                                return c.reconcileOvnDefaultVpcRoute(cachedSubnet.DeepCopy())
×
670
                        }()
671
                        if err != nil {
×
672
                                klog.Error(err)
×
673
                                return err
×
674
                        }
×
675
                }
676
        }
677

678
        return nil
×
679
}
680

681
func (c *Controller) syncDistributedSubnetRoutes() {
×
682
        if !c.distributedSubnetNeedSync.Swap(false) {
×
683
                return
×
684
        }
×
685

686
        klog.V(3).Infoln("start to sync distributed subnet routes")
×
687
        subnets, err := c.subnetsLister.List(labels.Everything())
×
688
        if err != nil {
×
689
                klog.Errorf("failed to list subnets: %v", err)
×
690
                c.distributedSubnetNeedSync.Store(true)
×
691
                return
×
692
        }
×
693

694
        for _, subnet := range subnets {
×
695
                if subnet.Spec.Vpc != c.config.ClusterRouter ||
×
696
                        subnet.Name == c.config.NodeSwitch ||
×
697
                        subnet.Spec.GatewayType != kubeovnv1.GWDistributedType ||
×
698
                        (subnet.Spec.Vlan != "" && !subnet.Spec.LogicalGateway) {
×
699
                        continue
×
700
                }
701
                if err := c.reconcileDistributedSubnetRouteInDefaultVpc(subnet); err != nil {
×
702
                        klog.Errorf("failed to reconcile distributed subnet %s route: %v", subnet.Name, err)
×
703
                        c.distributedSubnetNeedSync.Store(true)
×
704
                }
×
705
        }
706
}
707

708
func (c *Controller) checkSubnetGateway() {
×
709
        if err := c.checkSubnetGatewayNode(); err != nil {
×
710
                klog.Errorf("failed to check subnet gateway node: %v", err)
×
711
        }
×
712
}
713

714
func (c *Controller) checkSubnetGatewayNode() error {
×
715
        klog.V(3).Infoln("start to check subnet gateway node")
×
716
        subnetList, err := c.subnetsLister.List(labels.Everything())
×
717
        if err != nil {
×
718
                klog.Errorf("failed to list subnets: %v", err)
×
719
                return err
×
720
        }
×
721
        nodes, err := c.nodesLister.List(labels.Everything())
×
722
        if err != nil {
×
723
                klog.Errorf("failed to list nodes: %v", err)
×
724
                return err
×
725
        }
×
726

727
        for _, subnet := range subnetList {
×
728
                if (subnet.Spec.Vlan != "" && (subnet.Spec.U2OInterconnection || !subnet.Spec.LogicalGateway)) ||
×
729
                        subnet.Spec.Vpc != c.config.ClusterRouter ||
×
730
                        subnet.Name == c.config.NodeSwitch ||
×
731
                        subnet.Spec.GatewayNode == "" ||
×
732
                        subnet.Spec.GatewayType != kubeovnv1.GWCentralizedType ||
×
733
                        !subnet.Spec.EnableEcmp {
×
734
                        continue
×
735
                }
736
                gwNodes := strings.Split(subnet.Spec.GatewayNode, ",")
×
737
                if len(gwNodes) < 2 {
×
738
                        continue
×
739
                }
740

741
                for cidrBlock := range strings.SplitSeq(subnet.Spec.CIDRBlock, ",") {
×
742
                        skipCIDR := false
×
743
                        for _, node := range nodes {
×
744
                                if skipCIDR {
×
745
                                        break
×
746
                                }
747

748
                                ipStr := node.Annotations[util.IPAddressAnnotation]
×
749
                                for ip := range strings.SplitSeq(ipStr, ",") {
×
750
                                        if util.CheckProtocol(cidrBlock) != util.CheckProtocol(ip) {
×
751
                                                continue
×
752
                                        }
753

754
                                        isGateway := util.GatewayContains(subnet.Spec.GatewayNode, node.Name)
×
755
                                        pingSucceeded := false
×
756
                                        nodeIsReady := nodeReady(node)
×
757
                                        if isGateway {
×
758
                                                pinger, err := goping.NewPinger(ip)
×
759
                                                if err != nil {
×
760
                                                        return fmt.Errorf("failed to init pinger, %w", err)
×
761
                                                }
×
762
                                                pinger.SetPrivileged(true)
×
763

×
764
                                                count := 5
×
765
                                                pinger.Count = count
×
766
                                                pinger.Timeout = time.Duration(count) * time.Second
×
767
                                                pinger.Interval = 1 * time.Second
×
768

×
769
                                                pinger.OnRecv = func(_ *goping.Packet) {
×
770
                                                        pingSucceeded = true
×
771
                                                        pinger.Stop()
×
772
                                                }
×
773
                                                if err = pinger.Run(); err != nil {
×
774
                                                        klog.Errorf("failed to run pinger for destination %s: %v", ip, err)
×
775
                                                        return err
×
776
                                                }
×
777
                                                if pingSucceeded {
×
778
                                                        klog.V(3).Infof("succeeded to ping %s ip %s on node %s", util.NodeNic, ip, node.Name)
×
779
                                                }
×
780
                                        }
781

782
                                        err = func() error {
×
783
                                                c.subnetKeyMutex.LockKey(subnet.Name)
×
784
                                                defer func() { _ = c.subnetKeyMutex.UnlockKey(subnet.Name) }()
×
785

786
                                                nextHops, nameIPMap, err := c.getPolicyRouteParams(cidrBlock, util.GatewayRouterPolicyPriority)
×
787
                                                if err != nil {
×
788
                                                        klog.Errorf("failed to get ecmp policy route paras for subnet %s: %v", subnet.Name, err)
×
789
                                                        skipCIDR = true
×
790
                                                        return nil
×
791
                                                }
×
792

793
                                                exist := nameIPMap[node.Name] == ip
×
794
                                                if isGateway {
×
795
                                                        if !pingSucceeded || !nodeIsReady {
×
796
                                                                if exist {
×
797
                                                                        if !pingSucceeded {
×
798
                                                                                klog.Warningf("failed to ping %s ip %s on node %s", util.NodeNic, ip, node.Name)
×
799
                                                                        }
×
800
                                                                        if !nodeIsReady {
×
801
                                                                                klog.Warningf("node %s is not ready", node.Name)
×
802
                                                                        }
×
803
                                                                        klog.Warningf("delete ecmp policy route for node %s ip %s", node.Name, ip)
×
804
                                                                        nextHops.Remove(ip)
×
805
                                                                        delete(nameIPMap, node.Name)
×
806
                                                                        klog.Infof("update policy route for centralized subnet %s, nextHops %s", subnet.Name, nextHops)
×
807
                                                                        if err = c.updatePolicyRouteForCentralizedSubnet(subnet.Name, cidrBlock, nextHops.List(), nameIPMap); err != nil {
×
808
                                                                                klog.Errorf("failed to delete ecmp policy route for subnet %s on node %s, %v", subnet.Name, node.Name, err)
×
809
                                                                                return err
×
810
                                                                        }
×
811
                                                                }
812
                                                        } else if !exist {
×
813
                                                                nextHops.Add(ip)
×
814
                                                                if nameIPMap == nil {
×
815
                                                                        nameIPMap = make(map[string]string, 1)
×
816
                                                                }
×
817
                                                                nameIPMap[node.Name] = ip
×
818
                                                                klog.Infof("update policy route for centralized subnet %s, nextHops %s", subnet.Name, nextHops)
×
819
                                                                if err = c.updatePolicyRouteForCentralizedSubnet(subnet.Name, cidrBlock, nextHops.List(), nameIPMap); err != nil {
×
820
                                                                        klog.Errorf("failed to add ecmp policy route for subnet %s on node %s, %v", subnet.Name, node.Name, err)
×
821
                                                                        return err
×
822
                                                                }
×
823
                                                        }
824
                                                } else if exist {
×
825
                                                        klog.Infof("subnet %s gateway nodes does not contain node %s, delete policy route for node ip %s", subnet.Name, node.Name, ip)
×
826
                                                        nextHops.Remove(ip)
×
827
                                                        delete(nameIPMap, node.Name)
×
828
                                                        klog.Infof("update policy route for centralized subnet %s, nextHops %s", subnet.Name, nextHops)
×
829
                                                        if err = c.updatePolicyRouteForCentralizedSubnet(subnet.Name, cidrBlock, nextHops.List(), nameIPMap); err != nil {
×
830
                                                                klog.Errorf("failed to delete ecmp policy route for subnet %s on node %s, %v", subnet.Name, node.Name, err)
×
831
                                                                return err
×
832
                                                        }
×
833
                                                }
834

835
                                                return nil
×
836
                                        }()
837
                                        if err != nil {
×
838
                                                return err
×
839
                                        }
×
840
                                        if skipCIDR {
×
841
                                                break
×
842
                                        }
843
                                }
844
                        }
845
                }
846
        }
847

848
        return nil
×
849
}
850

851
func (c *Controller) cleanDuplicatedChassis(node *v1.Node) error {
1✔
852
        // if multi chassis has the same node name, delete all of them
1✔
853
        _, err := c.OVNSbClient.GetChassisByHost(node.Name)
1✔
854
        if err == nil {
2✔
855
                return nil
1✔
856
        }
1✔
857

858
        if !errors.Is(err, ovs.ErrOneNodeMultiChassis) {
2✔
859
                klog.Errorf("failed to get chassis for node %s: %v", node.Name, err)
1✔
860
                return err
1✔
861
        }
1✔
862

863
        klog.Warningf("node %s has multiple chassis, deleting all", node.Name)
1✔
864
        if err := c.OVNSbClient.DeleteChassisByHost(node.Name); err != nil {
2✔
865
                klog.Errorf("failed to delete chassis for node %s: %v", node.Name, err)
1✔
866
                return err
1✔
867
        }
1✔
868
        return nil
1✔
869
}
870

871
func (c *Controller) retryDelDupChassis(attempts, sleep int, f func(node *v1.Node) error, node *v1.Node) error {
×
872
        for i := range attempts {
×
873
                err := f(node)
×
874
                if err == nil {
×
875
                        return nil
×
876
                }
×
877
                klog.Errorf("failed to delete duplicated chassis for node %s: %v", node.Name, err)
×
878
                if i < attempts-1 {
×
879
                        time.Sleep(time.Duration(sleep) * time.Second)
×
880
                }
×
881
        }
882
        errMsg := errors.New("exhausting all attempts")
×
883
        klog.Error(errMsg)
×
884
        return errMsg
×
885
}
886

887
func (c *Controller) fetchPodsOnNode(nodeName string) ([]string, error) {
1✔
888
        objs, err := c.podIndexer.ByIndex(IndexPodByNode, nodeName)
1✔
889
        if err != nil {
1✔
890
                return nil, err
×
891
        }
×
892
        ports := make([]string, 0, len(objs))
1✔
893
        for _, obj := range objs {
1✔
894
                pod := obj.(*v1.Pod)
×
895
                if pod.Spec.HostNetwork || !isPodAlive(pod) {
×
896
                        continue
×
897
                }
898

899
                if pod.Annotations[util.LogicalRouterAnnotation] != c.config.ClusterRouter {
×
900
                        subnetName := pod.Annotations[util.LogicalSwitchAnnotation]
×
901
                        if subnetName == "" {
×
902
                                klog.V(4).Infof("Pod %s/%s is not on cluster router and has no logical switch annotation, skipping for VLAN check.", pod.Namespace, pod.Name)
×
903
                                continue
×
904
                        }
905

906
                        subnet, err := c.subnetsLister.Get(subnetName)
×
907
                        if err != nil {
×
908
                                klog.Errorf("failed to get subnet %s: %v", subnetName, err)
×
909
                                return nil, err
×
910
                        }
×
911

912
                        if subnet.Spec.Vlan == "" {
×
913
                                continue
×
914
                        }
915
                }
916

917
                podName := c.getNameByPod(pod)
×
918

×
919
                podNets, err := c.getPodKubeovnNets(pod)
×
920
                if err != nil {
×
921
                        klog.Errorf("failed to get pod nets %v", err)
×
922
                        return nil, err
×
923
                }
×
924

925
                for _, podNet := range podNets {
×
926
                        if !isOvnSubnet(podNet.Subnet) {
×
927
                                continue
×
928
                        }
929

930
                        if pod.Annotations != nil && pod.Annotations[fmt.Sprintf(util.AllocatedAnnotationTemplate, podNet.ProviderName)] == "true" {
×
931
                                ports = append(ports, ovs.PodNameToPortName(podName, pod.Namespace, podNet.ProviderName))
×
932
                        }
×
933
                }
934
        }
935
        return ports, nil
1✔
936
}
937

938
func (c *Controller) CheckNodePortGroup() {
×
939
        if err := c.checkAndUpdateNodePortGroup(); err != nil {
×
940
                klog.Errorf("check node port group status: %v", err)
×
941
        }
×
942
}
943

944
func (c *Controller) checkAndUpdateNodePortGroup() error {
1✔
945
        klog.V(3).Infoln("start to check node port-group status")
1✔
946
        var networkPolicyExists bool
1✔
947
        if c.config.EnableNP {
1✔
948
                networkPolicyExists = len(c.npIndexer.ListKeys()) != 0
×
949
        }
×
950

951
        nodes, err := c.nodesLister.List(labels.Everything())
1✔
952
        if err != nil {
1✔
953
                klog.Errorf("list nodes: %v", err)
×
954
                return err
×
955
        }
×
956

957
        for _, node := range nodes {
2✔
958
                // The port-group should already created when add node
1✔
959
                pgName := strings.ReplaceAll(node.Annotations[util.PortNameAnnotation], "-", ".")
1✔
960
                if pgName == "" {
2✔
961
                        klog.V(2).Infof("node %s does not have port name annotation, skip port group update", node.Name)
1✔
962
                        continue
1✔
963
                }
964

965
                // use join IP only when no internal IP exists
966
                nodeIPv4, nodeIPv6 := util.GetNodeInternalIP(*node)
1✔
967
                joinIP := node.Annotations[util.IPAddressAnnotation]
1✔
968
                joinIPv4, joinIPv6 := util.SplitStringIP(joinIP)
1✔
969
                if nodeIPv4 == "" {
1✔
970
                        nodeIPv4 = joinIPv4
×
971
                }
×
972
                if nodeIPv6 == "" {
2✔
973
                        nodeIPv6 = joinIPv6
1✔
974
                }
1✔
975
                nodeIP := strings.Trim(fmt.Sprintf("%s,%s", nodeIPv4, nodeIPv6), ",")
1✔
976

1✔
977
                nodePorts, err := c.fetchPodsOnNode(node.Name)
1✔
978
                if err != nil {
1✔
979
                        klog.Errorf("fetch pods for node %v: %v", node.Name, err)
×
980
                        return err
×
981
                }
×
982

983
                if err = c.OVNNbClient.PortGroupSetPorts(pgName, nodePorts); err != nil {
1✔
984
                        klog.Errorf("failed to set ports of port group %s: %v", pgName, err)
×
985
                        return err
×
986
                }
×
987

988
                if networkPolicyExists {
1✔
989
                        if err := c.OVNNbClient.CreateNodeACL(pgName, nodeIP, joinIP); err != nil {
×
990
                                klog.Errorf("create node acl for node pg %s: %v", pgName, err)
×
991
                        }
×
992
                } else {
1✔
993
                        // clear all acl
1✔
994
                        if err = c.OVNNbClient.DeleteAcls(pgName, portGroupKey, "", nil); err != nil {
1✔
995
                                klog.Errorf("delete node acl for node pg %s: %v", pgName, err)
×
996
                        }
×
997
                }
998
        }
999

1000
        return nil
1✔
1001
}
1002

1003
func (c *Controller) UpdateChassisTag(node *v1.Node) error {
×
1004
        annoChassisName := node.Annotations[util.ChassisAnnotation]
×
1005
        if annoChassisName == "" {
×
1006
                // kube-ovn-cni not ready to set chassis
×
1007
                return nil
×
1008
        }
×
1009
        chassis, err := c.OVNSbClient.GetChassis(annoChassisName, true)
×
1010
        if err != nil {
×
1011
                klog.Errorf("failed to get chassis %s for node %s: %v", annoChassisName, node.Name, err)
×
1012
                return err
×
1013
        }
×
1014
        if chassis == nil {
×
1015
                klog.Infof("chassis %q not registered for node %s, do chassis gc once", annoChassisName, node.Name)
×
1016
                // chassis name conflict, do GC
×
1017
                if err = c.gcChassis(); err != nil {
×
1018
                        klog.Errorf("failed to gc chassis: %v", err)
×
1019
                        return err
×
1020
                }
×
1021
                err = &ErrChassisNotFound{Chassis: annoChassisName, Node: node.Name}
×
1022
                klog.Error(err)
×
1023
                return err
×
1024
        }
1025

1026
        if chassis.ExternalIDs == nil || chassis.ExternalIDs["vendor"] != util.CniTypeName {
×
1027
                klog.Infof("init tag %s for node %s chassis %s", util.CniTypeName, node.Name, chassis.Name)
×
1028
                if err = c.OVNSbClient.UpdateChassisTag(chassis.Name, node.Name); err != nil {
×
1029
                        err := fmt.Errorf("failed to init chassis tag, %w", err)
×
1030
                        klog.Error(err)
×
1031
                        return err
×
1032
                }
×
1033
        }
1034
        return nil
×
1035
}
1036

1037
func (c *Controller) getPolicyRouteParams(cidr string, priority int) (*strset.Set, map[string]string, error) {
1✔
1038
        ipSuffix := "ip4"
1✔
1039
        if util.CheckProtocol(cidr) == kubeovnv1.ProtocolIPv6 {
1✔
1040
                ipSuffix = "ip6"
×
1041
        }
×
1042
        match := fmt.Sprintf("%s.src == %s", ipSuffix, cidr)
1✔
1043
        policyList, err := c.OVNNbClient.GetLogicalRouterPolicy(c.config.ClusterRouter, priority, match, true)
1✔
1044
        if err != nil {
1✔
1045
                klog.Errorf("failed to get logical router policy: %v", err)
×
1046
                return nil, nil, err
×
1047
        }
×
1048
        if len(policyList) == 0 {
1✔
1049
                return strset.New(), map[string]string{}, nil
×
1050
        }
×
1051
        return strset.New(policyList[0].Nexthops...), maps.Clone(policyList[0].ExternalIDs), nil
1✔
1052
}
1053

1054
func (c *Controller) deletePolicyRouteForNode(nodeName, portName string) error {
×
1055
        subnets, err := c.subnetsLister.List(labels.Everything())
×
1056
        if err != nil {
×
1057
                klog.Errorf("get subnets: %v", err)
×
1058
                return err
×
1059
        }
×
1060

1061
        addresses := c.ipam.GetPodAddress(portName)
×
1062
        for _, addr := range addresses {
×
1063
                if addr.IP == "" {
×
1064
                        continue
×
1065
                }
1066
                klog.Infof("deleting logical router policy with nexthop %q from %s for node %s", addr.IP, c.config.ClusterRouter, nodeName)
×
1067
                if err = c.OVNNbClient.DeleteLogicalRouterPolicyByNexthop(c.config.ClusterRouter, util.NodeRouterPolicyPriority, addr.IP); err != nil {
×
1068
                        klog.Errorf("failed to delete logical router policy with nexthop %q from %s for node %s: %v", addr.IP, c.config.ClusterRouter, nodeName, err)
×
1069
                        return err
×
1070
                }
×
1071
        }
1072

1073
        for _, subnet := range subnets {
×
1074
                if (subnet.Spec.Vlan != "" && !subnet.Spec.LogicalGateway) || subnet.Spec.Vpc != c.config.ClusterRouter || subnet.Name == c.config.NodeSwitch {
×
1075
                        continue
×
1076
                }
1077

1078
                if subnet.Spec.GatewayType == kubeovnv1.GWDistributedType {
×
1079
                        pgName := getOverlaySubnetsPortGroupName(subnet.Name, nodeName)
×
1080
                        if err = c.OVNNbClient.DeletePortGroup(pgName); err != nil {
×
1081
                                klog.Errorf("delete port group for subnet %s and node %s: %v", subnet.Name, nodeName, err)
×
1082
                                return err
×
1083
                        }
×
1084

1085
                        klog.Infof("delete policy route for distributed subnet %s, node %s", subnet.Name, nodeName)
×
1086
                        if err = c.deletePolicyRouteForDistributedSubnet(subnet, nodeName); err != nil {
×
1087
                                klog.Errorf("delete policy route for subnet %s and node %s: %v", subnet.Name, nodeName, err)
×
1088
                                return err
×
1089
                        }
×
1090
                }
1091

1092
                if subnet.Spec.GatewayType == kubeovnv1.GWCentralizedType {
×
1093
                        c.subnetKeyMutex.LockKey(subnet.Name)
×
1094
                        err = func() error {
×
1095
                                defer func() { _ = c.subnetKeyMutex.UnlockKey(subnet.Name) }()
×
1096
                                if subnet.Spec.EnableEcmp {
×
1097
                                        for cidrBlock := range strings.SplitSeq(subnet.Spec.CIDRBlock, ",") {
×
1098
                                                nextHops, nameIPMap, err := c.getPolicyRouteParams(cidrBlock, util.GatewayRouterPolicyPriority)
×
1099
                                                if err != nil {
×
1100
                                                        klog.Errorf("get ecmp policy route paras for subnet %v, error %v", subnet.Name, err)
×
1101
                                                        continue
×
1102
                                                }
1103

1104
                                                exist := false
×
1105
                                                if _, ok := nameIPMap[nodeName]; ok {
×
1106
                                                        exist = true
×
1107
                                                }
×
1108

1109
                                                if exist {
×
1110
                                                        nextHops.Remove(nameIPMap[nodeName])
×
1111
                                                        delete(nameIPMap, nodeName)
×
1112

×
1113
                                                        if nextHops.Size() == 0 {
×
1114
                                                                klog.Infof("delete policy route for centralized subnet %s, nextHops %s", subnet.Name, nextHops)
×
1115
                                                                if err := c.deletePolicyRouteForCentralizedSubnet(subnet); err != nil {
×
1116
                                                                        klog.Errorf("failed to delete policy route for centralized subnet %s, %v", subnet.Name, err)
×
1117
                                                                        return err
×
1118
                                                                }
×
1119
                                                        } else {
×
1120
                                                                klog.Infof("update policy route for centralized subnet %s, nextHops %s", subnet.Name, nextHops)
×
1121
                                                                if err = c.updatePolicyRouteForCentralizedSubnet(subnet.Name, cidrBlock, nextHops.List(), nameIPMap); err != nil {
×
1122
                                                                        klog.Errorf("failed to update policy route for subnet %s on node %s, %v", subnet.Name, nodeName, err)
×
1123
                                                                        return err
×
1124
                                                                }
×
1125
                                                        }
1126
                                                }
1127
                                        }
1128
                                } else {
×
1129
                                        klog.Infof("reconcile policy route for centralized subnet %s", subnet.Name)
×
1130
                                        if err := c.reconcileDefaultCentralizedSubnetRouteInDefaultVpc(subnet); err != nil {
×
1131
                                                klog.Errorf("failed to delete policy route for centralized subnet %s, %v", subnet.Name, err)
×
1132
                                                return err
×
1133
                                        }
×
1134
                                }
1135
                                return nil
×
1136
                        }()
1137
                        if err != nil {
×
1138
                                return err
×
1139
                        }
×
1140
                }
1141
        }
1142
        return nil
×
1143
}
1144

1145
func (c *Controller) addPolicyRouteForCentralizedSubnetOnNode(node *v1.Node, nodeIP string) error {
×
1146
        subnets, err := c.subnetsLister.List(labels.Everything())
×
1147
        if err != nil {
×
1148
                klog.Errorf("failed to get subnets %v", err)
×
1149
                return err
×
1150
        }
×
1151

1152
        for _, subnet := range subnets {
×
1153
                if (subnet.Spec.Vlan != "" && !subnet.Spec.LogicalGateway) || subnet.Spec.Vpc != c.config.ClusterRouter || subnet.Name == c.config.NodeSwitch || subnet.Spec.GatewayType != kubeovnv1.GWCentralizedType {
×
1154
                        continue
×
1155
                }
1156
                c.subnetKeyMutex.LockKey(subnet.Name)
×
1157
                err = func() error {
×
1158
                        defer func() { _ = c.subnetKeyMutex.UnlockKey(subnet.Name) }()
×
1159
                        nodeName := node.Name
×
1160
                        if subnet.Spec.EnableEcmp {
×
1161
                                if !util.GatewayContains(subnet.Spec.GatewayNode, nodeName) &&
×
1162
                                        (subnet.Spec.GatewayNode != "" || !util.MatchLabelSelectors(subnet.Spec.GatewayNodeSelectors, node.Labels)) {
×
1163
                                        return nil
×
1164
                                }
×
1165

1166
                                for nextHop := range strings.SplitSeq(nodeIP, ",") {
×
1167
                                        for cidrBlock := range strings.SplitSeq(subnet.Spec.CIDRBlock, ",") {
×
1168
                                                if util.CheckProtocol(cidrBlock) != util.CheckProtocol(nextHop) {
×
1169
                                                        continue
×
1170
                                                }
1171

1172
                                                nextHops, nameIPMap, err := c.getPolicyRouteParams(cidrBlock, util.GatewayRouterPolicyPriority)
×
1173
                                                if err != nil {
×
1174
                                                        klog.Errorf("get ecmp policy route paras for subnet %v, error %v", subnet.Name, err)
×
1175
                                                        continue
×
1176
                                                }
1177
                                                if nameIPMap[nodeName] == nextHop {
×
1178
                                                        continue
×
1179
                                                }
1180

1181
                                                nextHops.Add(nextHop)
×
1182
                                                if nameIPMap == nil {
×
1183
                                                        nameIPMap = make(map[string]string, 1)
×
1184
                                                }
×
1185
                                                nameIPMap[nodeName] = nextHop
×
1186
                                                klog.Infof("update policy route for centralized subnet %s, nextHops %s", subnet.Name, nextHops)
×
1187
                                                if err = c.updatePolicyRouteForCentralizedSubnet(subnet.Name, cidrBlock, nextHops.List(), nameIPMap); err != nil {
×
1188
                                                        klog.Errorf("failed to update policy route for subnet %s on node %s, %v", subnet.Name, nodeName, err)
×
1189
                                                        return err
×
1190
                                                }
×
1191
                                        }
1192
                                }
1193
                        } else {
×
1194
                                if subnet.Status.ActivateGateway != nodeName {
×
1195
                                        return nil
×
1196
                                }
×
1197
                                klog.Infof("add policy route for centralized subnet %s, on node %s, ip %s", subnet.Name, nodeName, nodeIP)
×
1198
                                if err = c.addPolicyRouteForCentralizedSubnet(subnet, nodeName, nil, strings.Split(nodeIP, ",")); err != nil {
×
1199
                                        klog.Errorf("failed to add active-backup policy route for centralized subnet %s: %v", subnet.Name, err)
×
1200
                                        return err
×
1201
                                }
×
1202
                        }
1203
                        return nil
×
1204
                }()
1205
                if err != nil {
×
1206
                        return err
×
1207
                }
×
1208
        }
1209
        return nil
×
1210
}
1211

1212
func (c *Controller) addPolicyRouteForLocalDNSCacheOnNode(dnsIPs []string, nodePortName, nodeIP, nodeName string, af int) error {
1✔
1213
        if len(dnsIPs) == 0 {
1✔
1214
                return c.deletePolicyRouteForLocalDNSCacheOnNode(nodeName, af)
×
1215
        }
×
1216

1217
        var (
1✔
1218
                externalIDs = map[string]string{
1✔
1219
                        "vendor":          util.CniTypeName,
1✔
1220
                        "node":            nodeName,
1✔
1221
                        "address-family":  strconv.Itoa(af),
1✔
1222
                        "isLocalDnsCache": "true",
1✔
1223
                }
1✔
1224
                pgAs     = strings.ReplaceAll(fmt.Sprintf("%s_ip%d", nodePortName, af), "-", ".")
1✔
1225
                action   = kubeovnv1.PolicyRouteActionReroute
1✔
1226
                nextHops = []string{nodeIP}
1✔
1227
        )
1✔
1228
        matches := strset.NewWithSize(len(dnsIPs))
1✔
1229
        for _, ip := range dnsIPs {
2✔
1230
                matches.Add(fmt.Sprintf("ip%d.src == $%s && ip%d.dst == %s", af, pgAs, af, ip))
1✔
1231
        }
1✔
1232

1233
        policies, err := c.OVNNbClient.ListLogicalRouterPolicies(c.config.ClusterRouter, -1, externalIDs, true)
1✔
1234
        if err != nil {
1✔
1235
                klog.Errorf("failed to list logical router policies for node %q af %d: %v", nodeName, af, err)
×
1236
                return err
×
1237
        }
×
1238

1239
        for _, policy := range policies {
2✔
1240
                if policy.Priority == util.NodeRouterPolicyPriority && policy.Action == string(action) && slices.Equal(policy.Nexthops, nextHops) && matches.Has(policy.Match) {
2✔
1241
                        matches.Remove(policy.Match)
1✔
1242
                        continue
1✔
1243
                }
1244
                // delete unused policy router policy
1245
                klog.Infof("deleting logical router policy by UUID %s", policy.UUID)
1✔
1246
                if err = c.OVNNbClient.DeleteLogicalRouterPolicyByUUID(c.config.ClusterRouter, policy.UUID); err != nil {
1✔
1247
                        klog.Errorf("failed to delete logical router policy by UUID %s: %v", policy.UUID, err)
×
1248
                        return err
×
1249
                }
×
1250
        }
1251

1252
        for _, match := range matches.List() {
2✔
1253
                klog.Infof("add node local dns cache policy route for router %s: match %q, action %q, nexthop %q, externalID %v", c.config.ClusterRouter, match, action, nodeIP, externalIDs)
1✔
1254
                if err := c.addPolicyRouteToVpc(
1✔
1255
                        c.config.ClusterRouter,
1✔
1256
                        &kubeovnv1.PolicyRoute{
1✔
1257
                                Priority:  util.NodeRouterPolicyPriority,
1✔
1258
                                Match:     match,
1✔
1259
                                Action:    action,
1✔
1260
                                NextHopIP: nodeIP,
1✔
1261
                        },
1✔
1262
                        externalIDs,
1✔
1263
                ); err != nil {
1✔
1264
                        klog.Errorf("failed to add logical router policy for node %s: %v", nodeName, err)
×
1265
                        return err
×
1266
                }
×
1267
        }
1268

1269
        return nil
1✔
1270
}
1271

1272
func (c *Controller) deletePolicyRouteForLocalDNSCacheOnNode(nodeName string, af int) error {
×
1273
        policies, err := c.OVNNbClient.ListLogicalRouterPolicies(c.config.ClusterRouter, -1, map[string]string{
×
1274
                "vendor":          util.CniTypeName,
×
1275
                "node":            nodeName,
×
1276
                "address-family":  strconv.Itoa(af),
×
1277
                "isLocalDnsCache": "true",
×
1278
        }, true)
×
1279
        if err != nil {
×
1280
                klog.Errorf("failed to list logical router policies: %v", err)
×
1281
                return err
×
1282
        }
×
1283
        if len(policies) == 0 {
×
1284
                return nil
×
1285
        }
×
1286

1287
        for _, policy := range policies {
×
1288
                klog.Infof("delete node local dns cache policy route for router %s with match %s", c.config.ClusterRouter, policy.Match)
×
1289

×
1290
                if err := c.OVNNbClient.DeleteLogicalRouterPolicyByUUID(c.config.ClusterRouter, policy.UUID); err != nil {
×
1291
                        klog.Errorf("failed to delete policy route for node local dns in router %s with match %s: %v", c.config.ClusterRouter, policy.Match, err)
×
1292
                        return err
×
1293
                }
×
1294
        }
1295
        return nil
×
1296
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc