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

kubeovn / kube-ovn / 27749108378

18 Jun 2026 09:10AM UTC coverage: 25.582% (+0.2%) from 25.424%
27749108378

Pull #6901

github

zhangzujian
fix: reconcile vpc bfd ha chassis on node changes

Signed-off-by: zhangzujian <zhangzujian.7@gmail.com>
Pull Request #6901: fix: reconcile VPC BFD HA chassis on node changes

51 of 62 new or added lines in 2 files covered. (82.26%)

1 existing line in 1 file now uncovered.

14544 of 56852 relevant lines covered (25.58%)

0.3 hits per line

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

23.36
/pkg/controller/vpc.go
1
package controller
2

3
import (
4
        "context"
5
        "encoding/json"
6
        "errors"
7
        "fmt"
8
        "maps"
9
        "math"
10
        "net"
11
        "reflect"
12
        "slices"
13
        "sort"
14
        "strings"
15
        "time"
16

17
        v1 "k8s.io/api/core/v1"
18
        k8serrors "k8s.io/apimachinery/pkg/api/errors"
19
        metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
20
        "k8s.io/apimachinery/pkg/labels"
21
        "k8s.io/apimachinery/pkg/types"
22
        "k8s.io/client-go/tools/cache"
23
        "k8s.io/klog/v2"
24
        "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
25

26
        kubeovnv1 "github.com/kubeovn/kube-ovn/pkg/apis/kubeovn/v1"
27
        "github.com/kubeovn/kube-ovn/pkg/ovsdb/ovnnb"
28
        "github.com/kubeovn/kube-ovn/pkg/util"
29
)
30

31
func (c *Controller) enqueueAddVpc(obj any) {
×
32
        vpc := obj.(*kubeovnv1.Vpc)
×
33
        key := cache.MetaObjectToName(vpc).String()
×
34
        if _, ok := vpc.Labels[util.VpcExternalLabel]; !ok {
×
35
                klog.V(3).Infof("enqueue add vpc %s", key)
×
36
                c.addOrUpdateVpcQueue.Add(key)
×
37
        }
×
38
}
39

40
func vpcBFDPortChanged(oldObj, newObj *kubeovnv1.BFDPort) bool {
×
41
        if oldObj == nil && newObj == nil {
×
42
                return false
×
43
        }
×
44
        if oldObj == nil || newObj == nil {
×
45
                return true
×
46
        }
×
47
        return oldObj.Enabled != newObj.Enabled || oldObj.IP != newObj.IP || !reflect.DeepEqual(oldObj.NodeSelector, newObj.NodeSelector)
×
48
}
49

50
func (c *Controller) enqueueUpdateVpc(oldObj, newObj any) {
×
51
        oldVpc := oldObj.(*kubeovnv1.Vpc)
×
52
        newVpc := newObj.(*kubeovnv1.Vpc)
×
53

×
54
        if newVpc.Labels != nil && newVpc.Labels[util.VpcExternalLabel] == "true" {
×
55
                return
×
56
        }
×
57

58
        if !newVpc.DeletionTimestamp.IsZero() ||
×
59
                !slices.Equal(oldVpc.Spec.Namespaces, newVpc.Spec.Namespaces) ||
×
60
                !reflect.DeepEqual(oldVpc.Spec.StaticRoutes, newVpc.Spec.StaticRoutes) ||
×
61
                !reflect.DeepEqual(oldVpc.Spec.PolicyRoutes, newVpc.Spec.PolicyRoutes) ||
×
62
                !reflect.DeepEqual(oldVpc.Spec.VpcPeerings, newVpc.Spec.VpcPeerings) ||
×
63
                kubeOvnAnnotationsChanged(oldVpc.Annotations, newVpc.Annotations) ||
×
64
                !slices.Equal(oldVpc.Spec.ExtraExternalSubnets, newVpc.Spec.ExtraExternalSubnets) ||
×
65
                oldVpc.Spec.EnableExternal != newVpc.Spec.EnableExternal ||
×
66
                oldVpc.Spec.EnableBfd != newVpc.Spec.EnableBfd ||
×
67
                vpcBFDPortChanged(oldVpc.Spec.BFDPort, newVpc.Spec.BFDPort) ||
×
68
                oldVpc.Labels[util.VpcExternalLabel] != newVpc.Labels[util.VpcExternalLabel] ||
×
69
                !slices.Equal(oldVpc.Status.Subnets, newVpc.Status.Subnets) {
×
70
                // TODO:// label VpcExternalLabel replace with spec enable external
×
71

×
72
                // recode last policies
×
73
                c.vpcLastPoliciesMap.Store(newVpc.Name, convertPolicies(oldVpc.Spec.PolicyRoutes))
×
74

×
75
                key := cache.MetaObjectToName(newVpc).String()
×
76
                klog.Infof("enqueue update vpc %s", key)
×
77
                c.addOrUpdateVpcQueue.Add(key)
×
78
        }
×
79
}
80

81
func (c *Controller) enqueueDelVpc(obj any) {
×
82
        var vpc *kubeovnv1.Vpc
×
83
        switch t := obj.(type) {
×
84
        case *kubeovnv1.Vpc:
×
85
                vpc = t
×
86
        case cache.DeletedFinalStateUnknown:
×
87
                v, ok := t.Obj.(*kubeovnv1.Vpc)
×
88
                if !ok {
×
89
                        klog.Warningf("unexpected object type: %T", t.Obj)
×
90
                        return
×
91
                }
×
92
                vpc = v
×
93
        default:
×
94
                klog.Warningf("unexpected type: %T", obj)
×
95
                return
×
96
        }
97

98
        if _, ok := vpc.Labels[util.VpcExternalLabel]; !vpc.Status.Default || !ok {
×
99
                klog.V(3).Infof("enqueue delete vpc %s", vpc.Name)
×
100
                c.delVpcQueue.Add(vpc)
×
101
        }
×
102
}
103

104
func (c *Controller) handleDelVpc(vpc *kubeovnv1.Vpc) error {
×
105
        c.vpcKeyMutex.LockKey(vpc.Name)
×
106
        defer func() { _ = c.vpcKeyMutex.UnlockKey(vpc.Name) }()
×
107
        klog.Infof("handle delete vpc %s", vpc.Name)
×
108

×
109
        // should delete vpc subnets first
×
110
        var err error
×
111
        for _, subnet := range vpc.Status.Subnets {
×
112
                if _, err = c.subnetsLister.Get(subnet); err != nil {
×
113
                        if k8serrors.IsNotFound(err) {
×
114
                                continue
×
115
                        }
116
                        err = fmt.Errorf("failed to get subnet %s for vpc %s: %w", subnet, vpc.Name, err)
×
117
                } else {
×
118
                        err = fmt.Errorf("failed to delete vpc %s, please delete subnet %s first", vpc.Name, subnet)
×
119
                }
×
120
                klog.Error(err)
×
121
                return err
×
122
        }
123

124
        // clean up vpc last policies cached
125
        c.vpcLastPoliciesMap.Delete(vpc.Name)
×
126

×
127
        if err := c.deleteVpcLb(vpc); err != nil {
×
128
                klog.Error(err)
×
129
                return err
×
130
        }
×
131

132
        if err := c.handleDelVpcExternalSubnet(vpc.Name, c.config.ExternalGatewaySwitch); err != nil {
×
133
                klog.Errorf("failed to delete external connection for vpc %s, error %v", vpc.Name, err)
×
134
                return err
×
135
        }
×
136

137
        for _, subnet := range vpc.Status.ExtraExternalSubnets {
×
138
                klog.Infof("disconnect external network %s to vpc %s", subnet, vpc.Name)
×
139
                if err := c.handleDelVpcExternalSubnet(vpc.Name, subnet); err != nil {
×
140
                        klog.Error(err)
×
141
                        return err
×
142
                }
×
143
        }
144

145
        if err := c.deleteVpcRouter(vpc.Status.Router); err != nil {
×
146
                klog.Error(err)
×
147
                return err
×
148
        }
×
149

150
        return nil
×
151
}
152

153
func (c *Controller) handleUpdateVpcStatus(key string) error {
×
154
        c.vpcKeyMutex.LockKey(key)
×
155
        defer func() { _ = c.vpcKeyMutex.UnlockKey(key) }()
×
156
        klog.Infof("handle status update for vpc %s", key)
×
157

×
158
        cachedVpc, err := c.vpcsLister.Get(key)
×
159
        if err != nil {
×
160
                if k8serrors.IsNotFound(err) {
×
161
                        return nil
×
162
                }
×
163
                klog.Error(err)
×
164
                return err
×
165
        }
166
        vpc := cachedVpc.DeepCopy()
×
167

×
168
        subnets, defaultSubnet, err := c.getVpcSubnets(vpc)
×
169
        if err != nil {
×
170
                klog.Error(err)
×
171
                return err
×
172
        }
×
173

174
        change := vpc.Status.DefaultLogicalSwitch != defaultSubnet
×
175

×
176
        vpc.Status.DefaultLogicalSwitch = defaultSubnet
×
177
        vpc.Status.Subnets = subnets
×
178

×
179
        if !vpc.Spec.BFDPort.IsEnabled() && !vpc.Status.BFDPort.IsEmpty() {
×
180
                vpc.Status.BFDPort.Clear()
×
181
        }
×
182
        bytes, err := vpc.Status.Bytes()
×
183
        if err != nil {
×
184
                klog.Error(err)
×
185
                return err
×
186
        }
×
187

188
        vpc, err = c.config.KubeOvnClient.KubeovnV1().Vpcs().Patch(context.Background(), vpc.Name, types.MergePatchType, bytes, metav1.PatchOptions{}, "status")
×
189
        if err != nil {
×
190
                klog.Error(err)
×
191
                return err
×
192
        }
×
193

194
        if len(vpc.Status.Subnets) == 0 {
×
195
                klog.Infof("vpc %s has no subnets, add to queue", vpc.Name)
×
196
                c.addOrUpdateVpcQueue.AddAfter(vpc.Name, 5*time.Second)
×
197
        }
×
198

199
        if change {
×
200
                for _, ns := range vpc.Spec.Namespaces {
×
201
                        c.addNamespaceQueue.Add(ns)
×
202
                }
×
203
        }
204

205
        natGws, err := c.vpcNatGatewayLister.List(labels.Everything())
×
206
        if err != nil {
×
207
                klog.Error(err)
×
208
                return err
×
209
        }
×
210
        for _, gw := range natGws {
×
211
                if key == gw.Spec.Vpc {
×
212
                        c.updateVpcSubnetQueue.Add(gw.Name)
×
213
                }
×
214
        }
215
        return nil
×
216
}
217

218
type VpcLoadBalancer struct {
219
        TCPLoadBalancer      string
220
        TCPSessLoadBalancer  string
221
        UDPLoadBalancer      string
222
        UDPSessLoadBalancer  string
223
        SctpLoadBalancer     string
224
        SctpSessLoadBalancer string
225
}
226

227
func (c *Controller) GenVpcLoadBalancer(vpcKey string) *VpcLoadBalancer {
×
228
        if vpcKey == c.config.ClusterRouter || vpcKey == "" {
×
229
                return &VpcLoadBalancer{
×
230
                        TCPLoadBalancer:      c.config.ClusterTCPLoadBalancer,
×
231
                        TCPSessLoadBalancer:  c.config.ClusterTCPSessionLoadBalancer,
×
232
                        UDPLoadBalancer:      c.config.ClusterUDPLoadBalancer,
×
233
                        UDPSessLoadBalancer:  c.config.ClusterUDPSessionLoadBalancer,
×
234
                        SctpLoadBalancer:     c.config.ClusterSctpLoadBalancer,
×
235
                        SctpSessLoadBalancer: c.config.ClusterSctpSessionLoadBalancer,
×
236
                }
×
237
        }
×
238
        return &VpcLoadBalancer{
×
239
                TCPLoadBalancer:      fmt.Sprintf("vpc-%s-tcp-load", vpcKey),
×
240
                TCPSessLoadBalancer:  fmt.Sprintf("vpc-%s-tcp-sess-load", vpcKey),
×
241
                UDPLoadBalancer:      fmt.Sprintf("vpc-%s-udp-load", vpcKey),
×
242
                UDPSessLoadBalancer:  fmt.Sprintf("vpc-%s-udp-sess-load", vpcKey),
×
243
                SctpLoadBalancer:     fmt.Sprintf("vpc-%s-sctp-load", vpcKey),
×
244
                SctpSessLoadBalancer: fmt.Sprintf("vpc-%s-sctp-sess-load", vpcKey),
×
245
        }
×
246
}
247

248
func (c *Controller) addLoadBalancer(vpc string) (*VpcLoadBalancer, error) {
×
249
        vpcLbConfig := c.GenVpcLoadBalancer(vpc)
×
250
        if err := c.initLB(vpcLbConfig.TCPLoadBalancer, string(v1.ProtocolTCP), false); err != nil {
×
251
                return nil, err
×
252
        }
×
253
        if err := c.initLB(vpcLbConfig.TCPSessLoadBalancer, string(v1.ProtocolTCP), true); err != nil {
×
254
                return nil, err
×
255
        }
×
256
        if err := c.initLB(vpcLbConfig.UDPLoadBalancer, string(v1.ProtocolUDP), false); err != nil {
×
257
                return nil, err
×
258
        }
×
259
        if err := c.initLB(vpcLbConfig.UDPSessLoadBalancer, string(v1.ProtocolUDP), true); err != nil {
×
260
                return nil, err
×
261
        }
×
262
        if err := c.initLB(vpcLbConfig.SctpLoadBalancer, string(v1.ProtocolSCTP), false); err != nil {
×
263
                return nil, err
×
264
        }
×
265
        if err := c.initLB(vpcLbConfig.SctpSessLoadBalancer, string(v1.ProtocolSCTP), true); err != nil {
×
266
                return nil, err
×
267
        }
×
268

269
        return vpcLbConfig, nil
×
270
}
271

272
func (c *Controller) handleAddOrUpdateVpc(key string) error {
1✔
273
        c.vpcKeyMutex.LockKey(key)
1✔
274
        defer func() { _ = c.vpcKeyMutex.UnlockKey(key) }()
2✔
275
        klog.Infof("handle add/update vpc %s", key)
1✔
276

1✔
277
        cachedVpc, err := c.vpcsLister.Get(key)
1✔
278
        if err != nil {
1✔
279
                if k8serrors.IsNotFound(err) {
×
280
                        return nil
×
281
                }
×
282
                klog.Error(err)
×
283
                return err
×
284
        }
285

286
        vpc, err := c.formatVpc(cachedVpc.DeepCopy())
1✔
287
        if err != nil {
1✔
288
                klog.Errorf("failed to format vpc %s: %v", key, err)
×
289
                return err
×
290
        }
×
291

292
        learnFromARPRequest := vpc.Spec.EnableExternal || len(vpc.Spec.VpcPeerings) != 0
1✔
293
        if !learnFromARPRequest {
2✔
294
                for _, subnetName := range vpc.Status.Subnets {
1✔
295
                        subnet, err := c.subnetsLister.Get(subnetName)
×
296
                        if err != nil {
×
297
                                if k8serrors.IsNotFound(err) {
×
298
                                        continue
×
299
                                }
300
                                klog.Errorf("failed to get subnet %s for vpc %s: %v", subnetName, key, err)
×
301
                                return err
×
302
                        }
303
                        if subnet.Spec.Vlan != "" && subnet.Spec.U2OInterconnection {
×
304
                                learnFromARPRequest = true
×
305
                                break
×
306
                        }
307
                }
308
        }
309

310
        if err = c.createVpcRouter(key, learnFromARPRequest); err != nil {
1✔
311
                klog.Errorf("failed to create vpc router for vpc %s: %v", key, err)
×
312
                return err
×
313
        }
×
314

315
        var newPeers []string
1✔
316
        for _, peering := range vpc.Spec.VpcPeerings {
1✔
317
                if err = util.CheckCidrs(peering.LocalConnectIP); err != nil {
×
318
                        klog.Errorf("invalid cidr %s", peering.LocalConnectIP)
×
319
                        return err
×
320
                }
×
321

322
                newPeers = append(newPeers, peering.RemoteVpc)
×
323
                if err := c.OVNNbClient.CreatePeerRouterPort(vpc.Name, peering.RemoteVpc, peering.LocalConnectIP); err != nil {
×
324
                        klog.Errorf("create peer router port for vpc %s, %v", vpc.Name, err)
×
325
                        return err
×
326
                }
×
327
        }
328
        for _, oldPeer := range vpc.Status.VpcPeerings {
1✔
329
                if !slices.Contains(newPeers, oldPeer) {
×
330
                        if err = c.OVNNbClient.DeleteLogicalRouterPort(fmt.Sprintf("%s-%s", vpc.Name, oldPeer)); err != nil {
×
331
                                klog.Errorf("delete peer router port for vpc %s, %v", vpc.Name, err)
×
332
                                return err
×
333
                        }
×
334
                }
335
        }
336

337
        // handle static route
338
        var (
1✔
339
                staticExistedRoutes []*ovnnb.LogicalRouterStaticRoute
1✔
340
                staticTargetRoutes  []*kubeovnv1.StaticRoute
1✔
341
                staticRouteMapping  map[string][]*kubeovnv1.StaticRoute
1✔
342
                externalIDs         = map[string]string{"vendor": util.CniTypeName}
1✔
343
        )
1✔
344

1✔
345
        // only manage static routes which are kube-ovn managed, by filtering for vendor util.CniTypeName
1✔
346
        staticExistedRoutes, err = c.OVNNbClient.ListLogicalRouterStaticRoutes(vpc.Name, nil, nil, "", externalIDs)
1✔
347
        if err != nil {
1✔
348
                klog.Errorf("failed to get vpc %s static route list, %v", vpc.Name, err)
×
349
                return err
×
350
        }
×
351

352
        var externalSubnet *kubeovnv1.Subnet
1✔
353
        externalSubnetExist := false
1✔
354
        externalSubnetGW := ""
1✔
355
        if c.config.EnableEipSnat {
1✔
356
                externalSubnet, err = c.subnetsLister.Get(c.config.ExternalGatewaySwitch)
×
357
                if err != nil {
×
358
                        klog.Warningf("enable-eip-snat need external subnet %s to be exist: %v", c.config.ExternalGatewaySwitch, err)
×
359
                } else {
×
360
                        if !externalSubnet.Spec.LogicalGateway {
×
361
                                // logical gw external subnet can not access external
×
362
                                externalSubnetExist = true
×
363
                                externalSubnetGW = externalSubnet.Spec.Gateway
×
364
                        } else {
×
365
                                klog.Infof("default external subnet %s using logical gw", c.config.ExternalGatewaySwitch)
×
366
                        }
×
367
                }
368
        }
369

370
        staticRouteMapping = c.getRouteTablesByVpc(vpc)
1✔
371
        staticTargetRoutes = vpc.Spec.StaticRoutes
1✔
372
        if vpc.Name == c.config.ClusterRouter {
1✔
373
                if _, ok := staticRouteMapping[util.MainRouteTable]; !ok {
×
374
                        staticRouteMapping[util.MainRouteTable] = nil
×
375
                }
×
376

377
                joinSubnet, err := c.subnetsLister.Get(c.config.NodeSwitch)
×
378
                if err != nil {
×
379
                        if !k8serrors.IsNotFound(err) {
×
380
                                klog.Errorf("failed to get node switch subnet %s: %v", c.config.NodeSwitch, err)
×
381
                                return err
×
382
                        }
×
383
                        c.addOrUpdateVpcQueue.AddAfter(vpc.Name, 1*time.Second)
×
384
                        return nil
×
385
                }
386

387
                // Ensure the join subnet's OVN Logical Switch (and its LRP) has been created
388
                // before adding default routes. Otherwise, OVN northd will warn about unreachable next hops.
389
                if exist, err := c.OVNNbClient.LogicalSwitchExists(c.config.NodeSwitch); err != nil {
×
390
                        klog.Errorf("failed to check logical switch %s existence: %v", c.config.NodeSwitch, err)
×
391
                        return err
×
392
                } else if !exist {
×
393
                        klog.Infof("logical switch %s not ready, requeue vpc %s", c.config.NodeSwitch, vpc.Name)
×
394
                        c.addOrUpdateVpcQueue.AddAfter(vpc.Name, 1*time.Second)
×
395
                        return nil
×
396
                }
×
397
                gatewayV4, gatewayV6 := util.SplitStringIP(joinSubnet.Spec.Gateway)
×
398
                if gatewayV4 != "" {
×
399
                        for table := range staticRouteMapping {
×
400
                                staticTargetRoutes = append(
×
401
                                        staticTargetRoutes,
×
402
                                        &kubeovnv1.StaticRoute{
×
403
                                                Policy:     kubeovnv1.PolicyDst,
×
404
                                                CIDR:       "0.0.0.0/0",
×
405
                                                NextHopIP:  gatewayV4,
×
406
                                                RouteTable: table,
×
407
                                        },
×
408
                                )
×
409
                        }
×
410
                }
411
                if gatewayV6 != "" {
×
412
                        for table := range staticRouteMapping {
×
413
                                staticTargetRoutes = append(
×
414
                                        staticTargetRoutes,
×
415
                                        &kubeovnv1.StaticRoute{
×
416
                                                Policy:     kubeovnv1.PolicyDst,
×
417
                                                CIDR:       "::/0",
×
418
                                                NextHopIP:  gatewayV6,
×
419
                                                RouteTable: table,
×
420
                                        },
×
421
                                )
×
422
                        }
×
423
                }
424
                if c.config.EnableEipSnat {
×
425
                        cm, err := c.configMapsLister.ConfigMaps(c.config.ExternalGatewayConfigNS).Get(util.ExternalGatewayConfig)
×
426
                        if err == nil {
×
427
                                nextHop := cm.Data["external-gw-addr"]
×
428
                                if nextHop == "" {
×
429
                                        if !externalSubnetExist {
×
430
                                                err = fmt.Errorf("failed to get external subnet %s", c.config.ExternalGatewaySwitch)
×
431
                                                klog.Error(err)
×
432
                                                return err
×
433
                                        }
×
434
                                        nextHop = externalSubnet.Spec.Gateway
×
435
                                        if nextHop == "" {
×
436
                                                err := fmt.Errorf("subnet %s has no gateway configuration", externalSubnet.Name)
×
437
                                                klog.Error(err)
×
438
                                                return err
×
439
                                        }
×
440
                                }
441
                                if strings.Contains(nextHop, "/") {
×
442
                                        nextHop = strings.Split(nextHop, "/")[0]
×
443
                                }
×
444

445
                                lr, err := c.OVNNbClient.GetLogicalRouter(vpc.Name, false)
×
446
                                if err != nil {
×
447
                                        klog.Errorf("failed to get logical router %s: %v", vpc.Name, err)
×
448
                                        return err
×
449
                                }
×
450

451
                                for _, nat := range lr.Nat {
×
452
                                        info, err := c.OVNNbClient.GetNATByUUID(nat)
×
453
                                        if err != nil {
×
454
                                                klog.Errorf("failed to get nat ip info for vpc %s, %v", vpc.Name, err)
×
455
                                                return err
×
456
                                        }
×
457
                                        if info.LogicalIP != "" {
×
458
                                                for table := range staticRouteMapping {
×
459
                                                        staticTargetRoutes = append(
×
460
                                                                staticTargetRoutes,
×
461
                                                                &kubeovnv1.StaticRoute{
×
462
                                                                        Policy:     kubeovnv1.PolicySrc,
×
463
                                                                        CIDR:       info.LogicalIP,
×
464
                                                                        NextHopIP:  nextHop,
×
465
                                                                        RouteTable: table,
×
466
                                                                },
×
467
                                                        )
×
468
                                                }
×
469
                                        }
470
                                }
471
                        }
472
                }
473
        } else {
1✔
474
                subnets, err := c.subnetsLister.List(labels.Everything())
1✔
475
                if err != nil {
1✔
476
                        klog.Error(err)
×
477
                        return err
×
478
                }
×
479
                // Add static routes created by addCustomVPCStaticRouteForSubnet
480
                for _, subnet := range subnets {
1✔
481
                        if subnet.Spec.Vpc == key {
×
482
                                v4Gw, v6Gw := util.SplitStringIP(subnet.Spec.Gateway)
×
483
                                v4Cidr, v6Cidr := util.SplitStringIP(subnet.Spec.CIDRBlock)
×
484
                                if v4Gw != "" && v4Cidr != "" {
×
485
                                        staticTargetRoutes = append(staticTargetRoutes, &kubeovnv1.StaticRoute{
×
486
                                                Policy:     kubeovnv1.PolicySrc,
×
487
                                                CIDR:       v4Cidr,
×
488
                                                NextHopIP:  v4Gw,
×
489
                                                RouteTable: subnet.Spec.RouteTable,
×
490
                                        })
×
491
                                }
×
492
                                if v6Gw != "" && v6Cidr != "" {
×
493
                                        staticTargetRoutes = append(staticTargetRoutes, &kubeovnv1.StaticRoute{
×
494
                                                Policy:     kubeovnv1.PolicySrc,
×
495
                                                CIDR:       v6Cidr,
×
496
                                                NextHopIP:  v6Gw,
×
497
                                                RouteTable: subnet.Spec.RouteTable,
×
498
                                        })
×
499
                                }
×
500
                        }
501
                }
502
        }
503

504
        routeNeedDel, routeNeedAdd := diffStaticRoute(staticExistedRoutes, staticTargetRoutes)
1✔
505

1✔
506
        for _, item := range routeNeedDel {
2✔
507
                klog.Infof("vpc %s del static route: %+v", vpc.Name, item)
1✔
508
                policy := convertPolicy(item.Policy)
1✔
509
                if err = c.OVNNbClient.DeleteLogicalRouterStaticRoute(vpc.Name, &item.RouteTable, &policy, item.CIDR, item.NextHopIP); err != nil {
1✔
510
                        klog.Errorf("del vpc %s static route failed, %v", vpc.Name, err)
×
511
                        return err
×
512
                }
×
513
        }
514

515
        for _, item := range routeNeedAdd {
2✔
516
                if item.BfdID != "" {
1✔
517
                        klog.Infof("vpc %s add static ecmp route: %+v", vpc.Name, item)
×
518
                        if err = c.OVNNbClient.AddLogicalRouterStaticRoute(
×
519
                                vpc.Name, item.RouteTable, convertPolicy(item.Policy), item.CIDR, &item.BfdID, externalIDs, item.NextHopIP,
×
520
                        ); err != nil {
×
521
                                klog.Errorf("failed to add bfd static route to vpc %s , %v", vpc.Name, err)
×
522
                                return err
×
523
                        }
×
524
                } else {
1✔
525
                        klog.Infof("vpc %s add static route: %+v", vpc.Name, item)
1✔
526
                        if err = c.OVNNbClient.AddLogicalRouterStaticRoute(
1✔
527
                                vpc.Name, item.RouteTable, convertPolicy(item.Policy), item.CIDR, nil, externalIDs, item.NextHopIP,
1✔
528
                        ); err != nil {
1✔
529
                                klog.Errorf("failed to add normal static route to vpc %s , %v", vpc.Name, err)
×
530
                                return err
×
531
                        }
×
532
                }
533
        }
534

535
        // handle policy route
536
        var (
1✔
537
                policyRouteExisted, policyRouteNeedDel, policyRouteNeedAdd []*kubeovnv1.PolicyRoute
1✔
538
                policyRouteLogical                                         []*ovnnb.LogicalRouterPolicy
1✔
539
        )
1✔
540

1✔
541
        if vpc.Name == c.config.ClusterRouter {
1✔
542
                lastPolicies, _ := c.vpcLastPoliciesMap.Load(vpc.Name)
×
543
                policyRouteExisted = reversePolicies(lastPolicies)
×
544
                // diff list
×
545
                policyRouteNeedDel, policyRouteNeedAdd = diffPolicyRouteWithExisted(policyRouteExisted, vpc.Spec.PolicyRoutes)
×
546
        } else {
1✔
547
                policyRouteLogical, err = c.OVNNbClient.ListLogicalRouterPolicies(vpc.Name, -1, nil, true)
1✔
548
                if err != nil {
1✔
549
                        klog.Errorf("failed to get vpc %s policy route list, %v", vpc.Name, err)
×
550
                        return err
×
551
                }
×
552
                // diff vpc policy route
553
                policyRouteNeedDel, policyRouteNeedAdd = diffPolicyRouteWithLogical(policyRouteLogical, vpc.Spec.PolicyRoutes)
1✔
554
        }
555
        // delete policies non-exist
556
        for _, item := range policyRouteNeedDel {
1✔
557
                klog.Infof("delete policy route for router: %s, priority: %d, match %s", vpc.Name, item.Priority, item.Match)
×
558
                if err = c.OVNNbClient.DeleteLogicalRouterPolicy(vpc.Name, item.Priority, item.Match); err != nil {
×
559
                        klog.Errorf("del vpc %s policy route failed, %v", vpc.Name, err)
×
560
                        return err
×
561
                }
×
562
        }
563
        // add new policies
564
        for _, item := range policyRouteNeedAdd {
2✔
565
                klog.Infof("add policy route for router: %s, match %s, action %s, nexthop %s, externalID %v", vpc.Name, item.Match, string(item.Action), item.NextHopIP, externalIDs)
1✔
566
                if err = c.addPolicyRouteToVpc(vpc.Name, item, externalIDs); err != nil {
1✔
567
                        return err
×
568
                }
×
569
        }
570

571
        vpcSubnets, defaultSubnet, err := c.getVpcSubnets(vpc)
1✔
572
        if err != nil {
1✔
573
                klog.Error(err)
×
574
                return err
×
575
        }
×
576

577
        vpc.Status.Subnets = vpcSubnets
1✔
578
        vpc.Status.DefaultLogicalSwitch = defaultSubnet
1✔
579
        vpc.Status.Router = key
1✔
580
        vpc.Status.Standby = true
1✔
581
        vpc.Status.VpcPeerings = newPeers
1✔
582
        if c.config.EnableLb {
1✔
583
                vpcLb, err := c.addLoadBalancer(key)
×
584
                if err != nil {
×
585
                        klog.Error(err)
×
586
                        return err
×
587
                }
×
588
                vpc.Status.TCPLoadBalancer = vpcLb.TCPLoadBalancer
×
589
                vpc.Status.TCPSessionLoadBalancer = vpcLb.TCPSessLoadBalancer
×
590
                vpc.Status.UDPLoadBalancer = vpcLb.UDPLoadBalancer
×
591
                vpc.Status.UDPSessionLoadBalancer = vpcLb.UDPSessLoadBalancer
×
592
                vpc.Status.SctpLoadBalancer = vpcLb.SctpLoadBalancer
×
593
                vpc.Status.SctpSessionLoadBalancer = vpcLb.SctpSessLoadBalancer
×
594
        }
595
        bytes, err := vpc.Status.Bytes()
1✔
596
        if err != nil {
1✔
597
                klog.Error(err)
×
598
                return err
×
599
        }
×
600
        vpc, err = c.config.KubeOvnClient.KubeovnV1().Vpcs().Patch(context.Background(), vpc.Name, types.MergePatchType, bytes, metav1.PatchOptions{}, "status")
1✔
601
        if err != nil {
1✔
602
                klog.Error(err)
×
603
                return err
×
604
        }
×
605

606
        if len(vpc.Annotations) != 0 && strings.ToLower(vpc.Annotations[util.VpcLbAnnotation]) == "on" {
1✔
607
                if err = c.createVpcLb(vpc); err != nil {
×
608
                        klog.Error(err)
×
609
                        return err
×
610
                }
×
611
        } else if err = c.deleteVpcLb(vpc); err != nil {
1✔
612
                klog.Error(err)
×
613
                return err
×
614
        }
×
615

616
        subnets, err := c.subnetsLister.List(labels.Everything())
1✔
617
        if err != nil {
1✔
618
                klog.Error(err)
×
619
                return err
×
620
        }
×
621
        custVpcEnableExternalEcmp := false
1✔
622
        for _, subnet := range subnets {
1✔
623
                if subnet.Spec.Vpc == key {
×
624
                        // Accelerate subnet update when vpc config is updated.
×
625
                        // In case VPC not set namespaces, subnet will backoff and may take long time to back to ready.
×
626
                        if subnet.Status.IsNotReady() {
×
627
                                c.addOrUpdateSubnetQueue.Add(subnet.Name)
×
628
                        }
×
629
                        if vpc.Name != util.DefaultVpc && vpc.Spec.EnableBfd && subnet.Spec.EnableEcmp {
×
630
                                custVpcEnableExternalEcmp = true
×
631
                        }
×
632
                }
633
        }
634

635
        if vpc.Spec.EnableExternal || vpc.Status.EnableExternal {
1✔
636
                if err = c.handleUpdateVpcExternal(cachedVpc, custVpcEnableExternalEcmp, externalSubnetExist, externalSubnetGW); err != nil {
×
637
                        klog.Errorf("failed to handle update external subnet for vpc %s, %v", key, err)
×
638
                        return err
×
639
                }
×
640
        }
641

642
        bfdPortName, bfdPortNodes, err := c.reconcileVpcBfdLRP(vpc)
1✔
643
        if err != nil {
1✔
644
                klog.Error(err)
×
645
                return err
×
646
        }
×
647
        if vpc.Spec.BFDPort == nil || !vpc.Spec.BFDPort.Enabled {
2✔
648
                vpc.Status.BFDPort = kubeovnv1.BFDPortStatus{}
1✔
649
        } else {
1✔
650
                vpc.Status.BFDPort = kubeovnv1.BFDPortStatus{
×
651
                        Name:  bfdPortName,
×
652
                        IP:    strings.Join(util.SplitTrimmed(vpc.Spec.BFDPort.IP, ","), ","),
×
653
                        Nodes: bfdPortNodes,
×
654
                }
×
655
        }
×
656
        if _, err = c.config.KubeOvnClient.KubeovnV1().Vpcs().
1✔
657
                UpdateStatus(context.Background(), vpc, metav1.UpdateOptions{}); err != nil {
1✔
658
                klog.Error(err)
×
659
                return err
×
660
        }
×
661

662
        return nil
1✔
663
}
664

665
func (c *Controller) handleUpdateVpcExternal(vpc *kubeovnv1.Vpc, custVpcEnableExternalEcmp, defaultExternalSubnetExist bool, externalSubnetGW string) error {
×
666
        if c.config.EnableEipSnat && vpc.Name == util.DefaultVpc {
×
667
                klog.Infof("external_gw handle ovn default external gw %s", vpc.Name)
×
668
                return nil
×
669
        }
×
670

671
        if !vpc.Spec.EnableExternal && !vpc.Status.EnableExternal {
×
672
                // no need to handle external connection
×
673
                return nil
×
674
        }
×
675

676
        // handle any vpc external
677
        if vpc.Spec.EnableExternal && !defaultExternalSubnetExist && vpc.Spec.ExtraExternalSubnets == nil {
×
678
                // at least have a external subnet
×
679
                err := fmt.Errorf("failed to get external subnet for enable external vpc %s", vpc.Name)
×
680
                klog.Error(err)
×
681
                return err
×
682
        }
×
683

684
        if !vpc.Spec.EnableExternal && vpc.Status.EnableExternal {
×
685
                // just del all external subnets connection
×
686
                klog.Infof("disconnect default external subnet %s to vpc %s", c.config.ExternalGatewaySwitch, vpc.Name)
×
687
                if err := c.handleDelVpcExternalSubnet(vpc.Name, c.config.ExternalGatewaySwitch); err != nil {
×
688
                        klog.Errorf("failed to delete external subnet %s connection for vpc %s, error %v", c.config.ExternalGatewaySwitch, vpc.Name, err)
×
689
                        return err
×
690
                }
×
691
                for _, subnet := range vpc.Status.ExtraExternalSubnets {
×
692
                        klog.Infof("disconnect external subnet %s to vpc %s", subnet, vpc.Name)
×
693
                        if err := c.handleDelVpcExternalSubnet(vpc.Name, subnet); err != nil {
×
694
                                klog.Errorf("failed to delete external subnet %s connection for vpc %s, error %v", subnet, vpc.Name, err)
×
695
                                return err
×
696
                        }
×
697
                }
698
        }
699

700
        if vpc.Spec.EnableExternal {
×
701
                if !vpc.Status.EnableExternal {
×
702
                        // just add external connection
×
703
                        if vpc.Spec.ExtraExternalSubnets == nil && defaultExternalSubnetExist {
×
704
                                // only connect default external subnet
×
705
                                klog.Infof("connect default external subnet %s with vpc %s", c.config.ExternalGatewaySwitch, vpc.Name)
×
706
                                if err := c.handleAddVpcExternalSubnet(vpc.Name, c.config.ExternalGatewaySwitch); err != nil {
×
707
                                        klog.Errorf("failed to add external subnet %s connection for vpc %s, error %v", c.config.ExternalGatewaySwitch, vpc.Name, err)
×
708
                                        return err
×
709
                                }
×
710
                        }
711

712
                        // only connect provider network vlan external subnet
713
                        for _, subnet := range vpc.Spec.ExtraExternalSubnets {
×
714
                                klog.Infof("connect external subnet %s with vpc %s", subnet, vpc.Name)
×
715
                                if err := c.handleAddVpcExternalSubnet(vpc.Name, subnet); err != nil {
×
716
                                        klog.Errorf("failed to add external subnet %s connection for vpc %s, error %v", subnet, vpc.Name, err)
×
717
                                        return err
×
718
                                }
×
719
                        }
720
                }
721

722
                // diff to add
723
                for _, subnet := range vpc.Spec.ExtraExternalSubnets {
×
724
                        if !slices.Contains(vpc.Status.ExtraExternalSubnets, subnet) {
×
725
                                klog.Infof("connect external subnet %s with vpc %s", subnet, vpc.Name)
×
726
                                if err := c.handleAddVpcExternalSubnet(vpc.Name, subnet); err != nil {
×
727
                                        klog.Errorf("failed to add external subnet %s connection for vpc %s, error %v", subnet, vpc.Name, err)
×
728
                                        return err
×
729
                                }
×
730
                        }
731
                }
732

733
                // diff to del
734
                for _, subnet := range vpc.Status.ExtraExternalSubnets {
×
735
                        if !slices.Contains(vpc.Spec.ExtraExternalSubnets, subnet) {
×
736
                                klog.Infof("disconnect external subnet %s to vpc %s", subnet, vpc.Name)
×
737
                                if err := c.handleDelVpcExternalSubnet(vpc.Name, subnet); err != nil {
×
738
                                        klog.Errorf("failed to delete external subnet %s connection for vpc %s, error %v", subnet, vpc.Name, err)
×
739
                                        return err
×
740
                                }
×
741
                        }
742
                }
743
        }
744

745
        // custom vpc enable bfd
746
        if vpc.Spec.EnableBfd && vpc.Name != util.DefaultVpc && defaultExternalSubnetExist {
×
747
                // create bfd between lrp and physical switch gw
×
748
                // bfd status down means current lrp binding chassis node external nic lost external network connectivity
×
749
                // should switch lrp to another node
×
750
                lrpEipName := fmt.Sprintf("%s-%s", vpc.Name, c.config.ExternalGatewaySwitch)
×
751
                v4ExtGw, _ := util.SplitStringIP(externalSubnetGW)
×
752
                // TODO: dualstack
×
753
                if _, err := c.OVNNbClient.CreateBFD(lrpEipName, v4ExtGw, c.config.BfdMinRx, c.config.BfdMinTx, c.config.BfdDetectMult, nil); err != nil {
×
754
                        klog.Error(err)
×
755
                        return err
×
756
                }
×
757
                // TODO: support multi external nic
758
                if custVpcEnableExternalEcmp {
×
759
                        klog.Infof("remove normal static ecmp route for vpc %s", vpc.Name)
×
760
                        // auto remove normal type static route, if using ecmp based bfd
×
761
                        if err := c.reconcileCustomVpcDelNormalStaticRoute(vpc.Name); err != nil {
×
762
                                klog.Errorf("failed to reconcile del vpc %q normal static route", vpc.Name)
×
763
                                return err
×
764
                        }
×
765
                }
766
        }
767

768
        if !vpc.Spec.EnableBfd && vpc.Status.EnableBfd {
×
769
                lrpEipName := fmt.Sprintf("%s-%s", vpc.Name, c.config.ExternalGatewaySwitch)
×
770
                if err := c.OVNNbClient.DeleteBFDByDstIP(lrpEipName, ""); err != nil {
×
771
                        klog.Error(err)
×
772
                        return err
×
773
                }
×
774
                if err := c.handleDeleteVpcStaticRoute(vpc.Name); err != nil {
×
775
                        klog.Errorf("failed to delete bfd route for vpc %s, error %v", vpc.Name, err)
×
776
                        return err
×
777
                }
×
778
        }
779

780
        if err := c.updateVpcExternalStatus(vpc.Name); err != nil {
×
781
                klog.Errorf("failed to update vpc external subnets status, %v", err)
×
782
                return err
×
783
        }
×
784
        return nil
×
785
}
786

787
func (c *Controller) reconcileVpcBfdLRP(vpc *kubeovnv1.Vpc) (string, []string, error) {
1✔
788
        portName := "bfd@" + vpc.Name
1✔
789
        if vpc.Spec.BFDPort == nil || !vpc.Spec.BFDPort.Enabled {
2✔
790
                if err := c.OVNNbClient.DeleteLogicalRouterPort(portName); err != nil {
1✔
791
                        err = fmt.Errorf("failed to delete BFD LRP %s: %w", portName, err)
×
792
                        klog.Error(err)
×
793
                        return portName, nil, err
×
794
                }
×
795
                if err := c.OVNNbClient.DeleteHAChassisGroup(portName); err != nil {
1✔
796
                        err = fmt.Errorf("failed to delete HA chassis group %s: %w", portName, err)
×
797
                        klog.Error(err)
×
798
                        return portName, nil, err
×
799
                }
×
800
                return portName, nil, nil
1✔
801
        }
802

803
        var err error
1✔
804
        chassisCount := 3
1✔
805
        selector := labels.Everything()
1✔
806
        if vpc.Spec.BFDPort.NodeSelector != nil {
2✔
807
                chassisCount = math.MaxInt
1✔
808
                if selector, err = metav1.LabelSelectorAsSelector(vpc.Spec.BFDPort.NodeSelector); err != nil {
1✔
809
                        err = fmt.Errorf("failed to parse node selector %q: %w", vpc.Spec.BFDPort.NodeSelector.String(), err)
×
810
                        klog.Error(err)
×
811
                        return portName, nil, err
×
812
                }
×
813
        }
814

815
        nodes, err := c.nodesLister.List(selector)
1✔
816
        if err != nil {
1✔
817
                err = fmt.Errorf("failed to list nodes with selector %q: %w", vpc.Spec.BFDPort.NodeSelector, err)
×
818
                klog.Error(err)
×
819
                return portName, nil, err
×
820
        }
×
821
        if len(nodes) == 0 {
2✔
822
                klog.Warningf("no nodes found by selector %q for BFD LRP %s, clearing HA chassis group", selector.String(), portName)
1✔
823
        }
1✔
824
        readyNodes := make([]*v1.Node, 0, len(nodes))
1✔
825
        for _, node := range nodes {
2✔
826
                if nodeReady(node) {
2✔
827
                        readyNodes = append(readyNodes, node)
1✔
828
                }
1✔
829
        }
830
        if len(nodes) != 0 && len(readyNodes) == 0 {
1✔
NEW
831
                klog.Warningf("no ready nodes found by selector %q for BFD LRP %s, clearing HA chassis group", selector.String(), portName)
×
UNCOV
832
        }
×
833
        sort.Slice(readyNodes, func(i, j int) bool {
2✔
834
                return readyNodes[i].Name < readyNodes[j].Name
1✔
835
        })
1✔
836

837
        nodeNames := make([]string, 0, len(readyNodes))
1✔
838
        chassisCount = min(chassisCount, len(readyNodes))
1✔
839
        chassisNames := make([]string, 0, chassisCount)
1✔
840
        for _, node := range readyNodes[:chassisCount] {
2✔
841
                chassis, err := c.OVNSbClient.GetChassisByHost(node.Name)
1✔
842
                if err != nil {
1✔
843
                        err = fmt.Errorf("failed to get chassis of node %s: %w", node.Name, err)
×
844
                        klog.Error(err)
×
845
                        return portName, nil, err
×
846
                }
×
847
                chassisNames = append(chassisNames, chassis.Name)
1✔
848
                nodeNames = append(nodeNames, node.Name)
1✔
849
        }
850

851
        networks := util.SplitTrimmed(vpc.Spec.BFDPort.IP, ",")
1✔
852
        if err = c.OVNNbClient.CreateLogicalRouterPort(vpc.Name, portName, "", networks); err != nil {
1✔
853
                klog.Error(err)
×
854
                return portName, nil, err
×
855
        }
×
856
        if err = c.OVNNbClient.UpdateLogicalRouterPortNetworks(portName, networks); err != nil {
1✔
857
                klog.Error(err)
×
858
                return portName, nil, err
×
859
        }
×
860
        if err = c.OVNNbClient.UpdateLogicalRouterPortOptions(portName, map[string]string{"bfd-only": "true"}); err != nil {
1✔
861
                klog.Error(err)
×
862
                return portName, nil, err
×
863
        }
×
864
        if err = c.OVNNbClient.CreateHAChassisGroup(portName, chassisNames, map[string]string{"lrp": portName}); err != nil {
1✔
865
                klog.Error(err)
×
866
                return portName, nil, err
×
867
        }
×
868
        if err = c.OVNNbClient.SetLogicalRouterPortHAChassisGroup(portName, portName); err != nil {
1✔
869
                klog.Error(err)
×
870
                return portName, nil, err
×
871
        }
×
872

873
        return portName, nodeNames, nil
1✔
874
}
875

876
func (c *Controller) addPolicyRouteToVpc(vpcName string, policy *kubeovnv1.PolicyRoute, externalIDs map[string]string) error {
1✔
877
        var (
1✔
878
                nextHops []string
1✔
879
                err      error
1✔
880
        )
1✔
881

1✔
882
        if policy.NextHopIP != "" {
2✔
883
                nextHops = util.SplitTrimmed(policy.NextHopIP, ",")
1✔
884
        }
1✔
885

886
        if err = c.OVNNbClient.AddLogicalRouterPolicy(vpcName, policy.Priority, policy.Match, string(policy.Action), nextHops, nil, externalIDs); err != nil {
1✔
887
                klog.Errorf("add policy route to vpc %s failed, %v", vpcName, err)
×
888
                return err
×
889
        }
×
890
        return nil
1✔
891
}
892

893
func buildExternalIDsMapKey(match, action string, priority int) string {
×
894
        return fmt.Sprintf("%s-%s-%d", match, action, priority)
×
895
}
×
896

897
func (c *Controller) batchAddPolicyRouteToVpc(name string, policies []*kubeovnv1.PolicyRoute, externalIDs map[string]map[string]string) error {
×
898
        if len(policies) == 0 {
×
899
                return nil
×
900
        }
×
901
        start := time.Now()
×
902
        routerPolicies := make([]*ovnnb.LogicalRouterPolicy, 0, len(policies))
×
903
        for _, policy := range policies {
×
904
                var nextHops []string
×
905
                if policy.NextHopIP != "" {
×
906
                        nextHops = util.SplitTrimmed(policy.NextHopIP, ",")
×
907
                }
×
908
                routerPolicies = append(routerPolicies, &ovnnb.LogicalRouterPolicy{
×
909
                        Priority:    policy.Priority,
×
910
                        Nexthops:    nextHops,
×
911
                        Action:      string(policy.Action),
×
912
                        Match:       policy.Match,
×
913
                        ExternalIDs: externalIDs[buildExternalIDsMapKey(policy.Match, string(policy.Action), policy.Priority)],
×
914
                })
×
915
        }
916

917
        if err := c.OVNNbClient.BatchAddLogicalRouterPolicy(name, routerPolicies...); err != nil {
×
918
                klog.Errorf("batch add policy route to vpc %s failed, %v", name, err)
×
919
                return err
×
920
        }
×
921
        klog.Infof("take to %v batch add policy route to vpc %s policies %d", time.Since(start), name, len(policies))
×
922
        return nil
×
923
}
924

925
func (c *Controller) deletePolicyRouteFromVpc(vpcName string, priority int, match string) error {
×
926
        if err := c.OVNNbClient.DeleteLogicalRouterPolicy(vpcName, priority, match); err != nil {
×
927
                klog.Error(err)
×
928
                return err
×
929
        }
×
930
        return nil
×
931
}
932

933
func (c *Controller) batchDeletePolicyRouteFromVpc(name string, policies []*kubeovnv1.PolicyRoute) error {
×
934
        start := time.Now()
×
935
        routerPolicies := make([]*ovnnb.LogicalRouterPolicy, 0, len(policies))
×
936
        for _, policy := range policies {
×
937
                routerPolicies = append(routerPolicies, &ovnnb.LogicalRouterPolicy{
×
938
                        Priority: policy.Priority,
×
939
                        Match:    policy.Match,
×
940
                })
×
941
        }
×
942

943
        if err := c.OVNNbClient.BatchDeleteLogicalRouterPolicy(name, routerPolicies); err != nil {
×
944
                return err
×
945
        }
×
946
        klog.V(3).Infof("take to %v batch delete policy route from vpc %s policies %d", time.Since(start), name, len(policies))
×
947
        return nil
×
948
}
949

950
func (c *Controller) addStaticRouteToVpc(name string, route *kubeovnv1.StaticRoute) error {
×
951
        externalIDs := map[string]string{"vendor": util.CniTypeName}
×
952
        if route.BfdID != "" {
×
953
                klog.Infof("vpc %s add static ecmp route: %+v", name, route)
×
954
                if err := c.OVNNbClient.AddLogicalRouterStaticRoute(
×
955
                        name, route.RouteTable, convertPolicy(route.Policy), route.CIDR, &route.BfdID, externalIDs, route.NextHopIP,
×
956
                ); err != nil {
×
957
                        klog.Errorf("failed to add bfd static route to vpc %s , %v", name, err)
×
958
                        return err
×
959
                }
×
960
        } else {
×
961
                klog.Infof("vpc %s add static route: %+v", name, route)
×
962
                if err := c.OVNNbClient.AddLogicalRouterStaticRoute(
×
963
                        name, route.RouteTable, convertPolicy(route.Policy), route.CIDR, nil, externalIDs, route.NextHopIP,
×
964
                ); err != nil {
×
965
                        klog.Errorf("failed to add normal static route to vpc %s , %v", name, err)
×
966
                        return err
×
967
                }
×
968
        }
969
        return nil
×
970
}
971

972
func (c *Controller) deleteStaticRouteFromVpc(name, table, cidr, nextHop string, policy kubeovnv1.RoutePolicy) error {
×
973
        var (
×
974
                policyStr string
×
975
                err       error
×
976
        )
×
977

×
978
        policyStr = convertPolicy(policy)
×
979
        if err = c.OVNNbClient.DeleteLogicalRouterStaticRoute(name, &table, &policyStr, cidr, nextHop); err != nil {
×
980
                klog.Errorf("del vpc %s static route failed, %v", name, err)
×
981
                return err
×
982
        }
×
983

984
        return nil
×
985
}
986

987
func (c *Controller) batchDeleteStaticRouteFromVpc(name string, staticRoutes []*kubeovnv1.StaticRoute) error {
×
988
        start := time.Now()
×
989
        routeCount := len(staticRoutes)
×
990
        delRoutes := make([]*ovnnb.LogicalRouterStaticRoute, 0, routeCount)
×
991
        for _, sr := range staticRoutes {
×
992
                policyStr := convertPolicy(sr.Policy)
×
993
                newRoute := &ovnnb.LogicalRouterStaticRoute{
×
994
                        RouteTable: sr.RouteTable,
×
995
                        Nexthop:    sr.NextHopIP,
×
996
                        Policy:     &policyStr,
×
997
                        IPPrefix:   sr.CIDR,
×
998
                }
×
999
                delRoutes = append(delRoutes, newRoute)
×
1000
        }
×
1001
        if err := c.OVNNbClient.BatchDeleteLogicalRouterStaticRoute(name, delRoutes); err != nil {
×
1002
                klog.Errorf("batch del vpc %s static route %d failed, %v", name, routeCount, err)
×
1003
                return err
×
1004
        }
×
1005
        klog.V(3).Infof("take to %v batch delete static route from vpc %s static routes %d", time.Since(start), name, len(delRoutes))
×
1006
        return nil
×
1007
}
1008

1009
func diffPolicyRouteWithExisted(exists, target []*kubeovnv1.PolicyRoute) ([]*kubeovnv1.PolicyRoute, []*kubeovnv1.PolicyRoute) {
×
1010
        var (
×
1011
                dels, adds []*kubeovnv1.PolicyRoute
×
1012
                existsMap  map[string]*kubeovnv1.PolicyRoute
×
1013
                key        string
×
1014
                ok         bool
×
1015
        )
×
1016

×
1017
        existsMap = make(map[string]*kubeovnv1.PolicyRoute, len(exists))
×
1018
        for _, item := range exists {
×
1019
                existsMap[getPolicyRouteItemKey(item)] = item
×
1020
        }
×
1021
        // load policies to add
1022
        for _, item := range target {
×
1023
                key = getPolicyRouteItemKey(item)
×
1024

×
1025
                if _, ok = existsMap[key]; ok {
×
1026
                        delete(existsMap, key)
×
1027
                } else {
×
1028
                        adds = append(adds, item)
×
1029
                }
×
1030
        }
1031
        // load policies to delete
1032
        for _, item := range existsMap {
×
1033
                dels = append(dels, item)
×
1034
        }
×
1035
        return dels, adds
×
1036
}
1037

1038
func diffPolicyRouteWithLogical(exists []*ovnnb.LogicalRouterPolicy, target []*kubeovnv1.PolicyRoute) ([]*kubeovnv1.PolicyRoute, []*kubeovnv1.PolicyRoute) {
1✔
1039
        var (
1✔
1040
                dels, adds []*kubeovnv1.PolicyRoute
1✔
1041
                existsMap  map[string]*kubeovnv1.PolicyRoute
1✔
1042
                key        string
1✔
1043
                ok         bool
1✔
1044
        )
1✔
1045
        existsMap = make(map[string]*kubeovnv1.PolicyRoute, len(exists))
1✔
1046

1✔
1047
        for _, item := range exists {
2✔
1048
                if item.ExternalIDs["vpc-egress-gateway"] != "" || item.ExternalIDs["subnet"] != "" ||
1✔
1049
                        item.ExternalIDs["isU2ORoutePolicy"] == "true" {
1✔
1050
                        continue
×
1051
                }
1052
                policy := &kubeovnv1.PolicyRoute{
1✔
1053
                        Priority:  item.Priority,
1✔
1054
                        Match:     item.Match,
1✔
1055
                        Action:    kubeovnv1.PolicyRouteAction(item.Action),
1✔
1056
                        NextHopIP: getLogicalPolicyNextHopKey(item),
1✔
1057
                }
1✔
1058
                existsMap[getPolicyRouteItemKey(policy)] = policy
1✔
1059
        }
1060

1061
        for _, item := range target {
2✔
1062
                key = getPolicyRouteItemKey(item)
1✔
1063

1✔
1064
                if _, ok = existsMap[key]; ok {
2✔
1065
                        delete(existsMap, key)
1✔
1066
                } else {
2✔
1067
                        adds = append(adds, item)
1✔
1068
                }
1✔
1069
        }
1070

1071
        for _, item := range existsMap {
1✔
1072
                dels = append(dels, item)
×
1073
        }
×
1074
        return dels, adds
1✔
1075
}
1076

1077
func getPolicyRouteItemKey(item *kubeovnv1.PolicyRoute) (key string) {
1✔
1078
        return fmt.Sprintf("%d:%s:%s:%s", item.Priority, item.Match, item.Action, normalizePolicyRouteNextHops(item.NextHopIP))
1✔
1079
}
1✔
1080

1081
func getLogicalPolicyNextHopKey(item *ovnnb.LogicalRouterPolicy) string {
1✔
1082
        if len(item.Nexthops) > 0 {
2✔
1083
                return normalizePolicyRouteNextHops(strings.Join(item.Nexthops, ","))
1✔
1084
        }
1✔
1085
        if item.Nexthop != nil {
2✔
1086
                return normalizePolicyRouteNextHops(*item.Nexthop)
1✔
1087
        }
1✔
1088
        return ""
×
1089
}
1090

1091
func normalizePolicyRouteNextHops(nextHopIP string) string {
1✔
1092
        if nextHopIP == "" {
2✔
1093
                return ""
1✔
1094
        }
1✔
1095

1096
        nextHops := make([]string, 0)
1✔
1097
        for nextHop := range strings.SplitSeq(nextHopIP, ",") {
2✔
1098
                nextHop = strings.TrimSpace(nextHop)
1✔
1099
                if nextHop != "" {
2✔
1100
                        nextHops = append(nextHops, nextHop)
1✔
1101
                }
1✔
1102
        }
1103
        if len(nextHops) == 0 {
1✔
1104
                return ""
×
1105
        }
×
1106

1107
        slices.Sort(nextHops)
1✔
1108
        return strings.Join(nextHops, ",")
1✔
1109
}
1110

1111
func diffStaticRoute(exist []*ovnnb.LogicalRouterStaticRoute, target []*kubeovnv1.StaticRoute) (routeNeedDel, routeNeedAdd []*kubeovnv1.StaticRoute) {
1✔
1112
        existRouteMap := make(map[string]*kubeovnv1.StaticRoute, len(exist))
1✔
1113
        for _, item := range exist {
2✔
1114
                policy := kubeovnv1.PolicyDst
1✔
1115
                if item.Policy != nil && *item.Policy == ovnnb.LogicalRouterStaticRoutePolicySrcIP {
2✔
1116
                        policy = kubeovnv1.PolicySrc
1✔
1117
                }
1✔
1118
                route := &kubeovnv1.StaticRoute{
1✔
1119
                        Policy:     policy,
1✔
1120
                        CIDR:       item.IPPrefix,
1✔
1121
                        NextHopIP:  item.Nexthop,
1✔
1122
                        RouteTable: item.RouteTable,
1✔
1123
                        ECMPMode:   util.StaticRouteBfdEcmp,
1✔
1124
                }
1✔
1125
                if item.BFD != nil {
1✔
1126
                        route.BfdID = *item.BFD
×
1127
                }
×
1128
                existRouteMap[getStaticRouteItemKey(route)] = route
1✔
1129
        }
1130

1131
        for _, item := range target {
2✔
1132
                key := getStaticRouteItemKey(item)
1✔
1133
                if _, ok := existRouteMap[key]; ok {
2✔
1134
                        delete(existRouteMap, key)
1✔
1135
                } else {
2✔
1136
                        routeNeedAdd = append(routeNeedAdd, item)
1✔
1137
                }
1✔
1138
        }
1139
        for _, item := range existRouteMap {
2✔
1140
                routeNeedDel = append(routeNeedDel, item)
1✔
1141
        }
1✔
1142
        return routeNeedDel, routeNeedAdd
1✔
1143
}
1144

1145
func getStaticRouteItemKey(item *kubeovnv1.StaticRoute) string {
1✔
1146
        var key string
1✔
1147
        if item.Policy == kubeovnv1.PolicyDst {
2✔
1148
                key = fmt.Sprintf("%s:dst:%s=>%s", item.RouteTable, item.CIDR, item.NextHopIP)
1✔
1149
        } else {
2✔
1150
                key = fmt.Sprintf("%s:src:%s=>%s", item.RouteTable, item.CIDR, item.NextHopIP)
1✔
1151
        }
1✔
1152
        return key
1✔
1153
}
1154

1155
func (c *Controller) formatVpc(vpc *kubeovnv1.Vpc) (*kubeovnv1.Vpc, error) {
1✔
1156
        var changed bool
1✔
1157
        for _, item := range vpc.Spec.StaticRoutes {
2✔
1158
                // check policy
1✔
1159
                if item.Policy == "" {
1✔
1160
                        item.Policy = kubeovnv1.PolicyDst
×
1161
                        changed = true
×
1162
                }
×
1163
                if item.Policy != kubeovnv1.PolicyDst && item.Policy != kubeovnv1.PolicySrc {
1✔
1164
                        return nil, fmt.Errorf("unknown policy type: %q", item.Policy)
×
1165
                }
×
1166
                // check cidr
1167
                if strings.Contains(item.CIDR, "/") {
2✔
1168
                        if _, _, err := net.ParseCIDR(item.CIDR); err != nil {
1✔
1169
                                return nil, fmt.Errorf("invalid cidr %q: %w", item.CIDR, err)
×
1170
                        }
×
1171
                } else if ip := net.ParseIP(item.CIDR); ip == nil {
×
1172
                        return nil, fmt.Errorf("invalid ip %q", item.CIDR)
×
1173
                }
×
1174
                // check next hop ip
1175
                if ip := net.ParseIP(item.NextHopIP); ip == nil {
1✔
1176
                        return nil, fmt.Errorf("invalid next hop ip %q", item.NextHopIP)
×
1177
                }
×
1178
        }
1179

1180
        for _, route := range vpc.Spec.PolicyRoutes {
2✔
1181
                if route.Action != kubeovnv1.PolicyRouteActionReroute {
2✔
1182
                        if route.NextHopIP != "" {
1✔
1183
                                route.NextHopIP = ""
×
1184
                                changed = true
×
1185
                        }
×
1186
                } else {
1✔
1187
                        // ecmp policy route may reroute to multiple next hop ips
1✔
1188
                        for ipStr := range strings.SplitSeq(route.NextHopIP, ",") {
2✔
1189
                                if ip := net.ParseIP(ipStr); ip == nil {
1✔
1190
                                        err := fmt.Errorf("invalid next hop ips: %s", route.NextHopIP)
×
1191
                                        klog.Error(err)
×
1192
                                        return nil, err
×
1193
                                }
×
1194
                        }
1195
                }
1196
        }
1197

1198
        if vpc.DeletionTimestamp.IsZero() && !slices.Contains(vpc.GetFinalizers(), util.KubeOVNControllerFinalizer) {
2✔
1199
                controllerutil.AddFinalizer(vpc, util.KubeOVNControllerFinalizer)
1✔
1200
                changed = true
1✔
1201
        }
1✔
1202

1203
        if !vpc.DeletionTimestamp.IsZero() && len(vpc.Status.Subnets) == 0 {
1✔
1204
                controllerutil.RemoveFinalizer(vpc, util.KubeOVNControllerFinalizer)
×
1205
                changed = true
×
1206
        }
×
1207

1208
        if changed {
2✔
1209
                newVpc, err := c.config.KubeOvnClient.KubeovnV1().Vpcs().Update(context.Background(), vpc, metav1.UpdateOptions{})
1✔
1210
                if err != nil {
1✔
1211
                        klog.Errorf("failed to update vpc %s: %v", vpc.Name, err)
×
1212
                        return nil, err
×
1213
                }
×
1214
                return newVpc, nil
1✔
1215
        }
1216

1217
        return vpc, nil
×
1218
}
1219

1220
func convertPolicies(list []*kubeovnv1.PolicyRoute) string {
×
1221
        if list == nil {
×
1222
                return ""
×
1223
        }
×
1224

1225
        var (
×
1226
                res []byte
×
1227
                err error
×
1228
        )
×
1229

×
1230
        if res, err = json.Marshal(list); err != nil {
×
1231
                klog.Errorf("failed to serialize policy routes %v , reason : %v", list, err)
×
1232
                return ""
×
1233
        }
×
1234
        return string(res)
×
1235
}
1236

1237
func reversePolicies(origin string) []*kubeovnv1.PolicyRoute {
×
1238
        if origin == "" {
×
1239
                return nil
×
1240
        }
×
1241

1242
        var (
×
1243
                list []*kubeovnv1.PolicyRoute
×
1244
                err  error
×
1245
        )
×
1246

×
1247
        if err = json.Unmarshal([]byte(origin), &list); err != nil {
×
1248
                klog.Errorf("failed to deserialize policy routes %v , reason : %v", list, err)
×
1249
                return nil
×
1250
        }
×
1251
        return list
×
1252
}
1253

1254
func convertPolicy(origin kubeovnv1.RoutePolicy) string {
1✔
1255
        if origin == kubeovnv1.PolicyDst {
2✔
1256
                return ovnnb.LogicalRouterStaticRoutePolicyDstIP
1✔
1257
        }
1✔
1258
        return ovnnb.LogicalRouterStaticRoutePolicySrcIP
1✔
1259
}
1260

1261
func reversePolicy(origin ovnnb.LogicalRouterStaticRoutePolicy) kubeovnv1.RoutePolicy {
×
1262
        if origin == ovnnb.LogicalRouterStaticRoutePolicyDstIP {
×
1263
                return kubeovnv1.PolicyDst
×
1264
        }
×
1265
        return kubeovnv1.PolicySrc
×
1266
}
1267

1268
func (c *Controller) getVpcSubnets(vpc *kubeovnv1.Vpc) (subnets []string, defaultSubnet string, err error) {
1✔
1269
        subnets = []string{}
1✔
1270
        allSubnets, err := c.subnetsLister.List(labels.Everything())
1✔
1271
        if err != nil {
1✔
1272
                klog.Error(err)
×
1273
                return nil, "", err
×
1274
        }
×
1275

1276
        for _, subnet := range allSubnets {
1✔
1277
                if subnet.Spec.Vpc != vpc.Name || !subnet.DeletionTimestamp.IsZero() || !isOvnSubnet(subnet) {
×
1278
                        continue
×
1279
                }
1280

1281
                subnets = append(subnets, subnet.Name)
×
1282
                if subnet.Spec.Default {
×
1283
                        defaultSubnet = subnet.Name
×
1284
                }
×
1285

1286
                if vpc.Name != util.DefaultVpc && vpc.Spec.DefaultSubnet != "" && vpc.Spec.DefaultSubnet == subnet.Name {
×
1287
                        defaultSubnet = vpc.Spec.DefaultSubnet
×
1288
                }
×
1289
        }
1290
        sort.Strings(subnets)
1✔
1291
        return subnets, defaultSubnet, err
1✔
1292
}
1293

1294
// createVpcRouter create router to connect logical switches in vpc
1295
func (c *Controller) createVpcRouter(lr string, learnFromARPRequest bool) error {
1✔
1296
        if err := c.OVNNbClient.CreateLogicalRouter(lr); err != nil {
1✔
1297
                klog.Errorf("create logical router %s failed: %v", lr, err)
×
1298
                return err
×
1299
        }
×
1300

1301
        vpcRouter, err := c.OVNNbClient.GetLogicalRouter(lr, false)
1✔
1302
        if err != nil {
1✔
1303
                klog.Errorf("get logical router %s failed: %v", lr, err)
×
1304
                return err
×
1305
        }
×
1306

1307
        lrOptions := map[string]string{
1✔
1308
                "mac_binding_age_threshold": "300",
1✔
1309
                "dynamic_neigh_routers":     "true",
1✔
1310
        }
1✔
1311
        if !learnFromARPRequest {
2✔
1312
                lrOptions["always_learn_from_arp_request"] = "false"
1✔
1313
        }
1✔
1314
        if !maps.Equal(vpcRouter.Options, lrOptions) {
2✔
1315
                vpcRouter.Options = lrOptions
1✔
1316
                if err = c.OVNNbClient.UpdateLogicalRouter(vpcRouter, &vpcRouter.Options); err != nil {
1✔
1317
                        klog.Errorf("failed to update options of logical router %s: %v", lr, err)
×
1318
                        return err
×
1319
                }
×
1320
        }
1321

1322
        return nil
1✔
1323
}
1324

1325
// deleteVpcRouter delete router to connect logical switches in vpc
1326
func (c *Controller) deleteVpcRouter(lr string) error {
×
1327
        return c.OVNNbClient.DeleteLogicalRouter(lr)
×
1328
}
×
1329

1330
func (c *Controller) handleAddVpcExternalSubnet(key, subnet string) error {
×
1331
        cachedSubnet, err := c.subnetsLister.Get(subnet)
×
1332
        if err != nil {
×
1333
                klog.Error(err)
×
1334
                return err
×
1335
        }
×
1336
        lrpEipName := fmt.Sprintf("%s-%s", key, subnet)
×
1337
        cachedEip, err := c.ovnEipsLister.Get(lrpEipName)
×
1338
        var needCreateEip bool
×
1339
        if err != nil {
×
1340
                if !k8serrors.IsNotFound(err) {
×
1341
                        klog.Error(err)
×
1342
                        return err
×
1343
                }
×
1344
                needCreateEip = true
×
1345
        }
1346
        var v4ip, v6ip, mac string
×
1347
        klog.V(3).Infof("create vpc lrp eip %s", lrpEipName)
×
1348
        if needCreateEip {
×
1349
                if v4ip, v6ip, mac, err = c.acquireIPAddress(subnet, lrpEipName, lrpEipName); err != nil {
×
1350
                        klog.Errorf("failed to acquire ip address for lrp eip %s, %v", lrpEipName, err)
×
1351
                        return err
×
1352
                }
×
1353
                if err := c.createOrUpdateOvnEipCR(lrpEipName, subnet, v4ip, v6ip, mac, util.OvnEipTypeLRP); err != nil {
×
1354
                        klog.Errorf("failed to create ovn eip for lrp %s: %v", lrpEipName, err)
×
1355
                        return err
×
1356
                }
×
1357
        } else {
×
1358
                v4ip = cachedEip.Spec.V4Ip
×
1359
                v6ip = cachedEip.Spec.V6Ip
×
1360
                mac = cachedEip.Spec.MacAddress
×
1361
        }
×
1362
        if (v4ip == "" && v6ip == "") || mac == "" {
×
1363
                err := fmt.Errorf("lrp '%s' ip or mac should not be empty", lrpEipName)
×
1364
                klog.Error(err)
×
1365
                return err
×
1366
        }
×
1367
        // init lrp gw chassis group
1368
        chassises := []string{}
×
1369
        gwNodes, err := c.nodesLister.List(externalGatewayNodeSelector)
×
1370
        if err != nil {
×
1371
                klog.Errorf("failed to list external gw nodes, %v", err)
×
1372
                return err
×
1373
        }
×
1374
        for _, gwNode := range gwNodes {
×
1375
                annoChassisName := gwNode.Annotations[util.ChassisAnnotation]
×
1376
                if annoChassisName == "" {
×
1377
                        err := fmt.Errorf("node %s has no chassis annotation, kube-ovn-cni not ready", gwNode.Name)
×
1378
                        klog.Error(err)
×
1379
                        return err
×
1380
                }
×
1381
                klog.Infof("get node %s chassis: %s", gwNode.Name, annoChassisName)
×
1382
                chassis, err := c.OVNSbClient.GetChassis(annoChassisName, false)
×
1383
                if err != nil {
×
1384
                        klog.Errorf("failed to get node %s chassis: %s, %v", gwNode.Name, annoChassisName, err)
×
1385
                        return err
×
1386
                }
×
1387
                chassises = append(chassises, chassis.Name)
×
1388
        }
1389

1390
        if len(chassises) == 0 {
×
1391
                err := errors.New("no external gw nodes")
×
1392
                klog.Error(err)
×
1393
                return err
×
1394
        }
×
1395

1396
        ipCidr, err := util.GetIPAddrWithMask(util.GetStringIP(v4ip, v6ip), cachedSubnet.Spec.CIDRBlock)
×
1397
        if err != nil {
×
1398
                klog.Error(err)
×
1399
                return err
×
1400
        }
×
1401
        lspName := fmt.Sprintf("%s-%s", subnet, key)
×
1402
        lrpName := fmt.Sprintf("%s-%s", key, subnet)
×
1403

×
1404
        if err := c.OVNNbClient.CreateLogicalPatchPort(subnet, key, lspName, lrpName, ipCidr, mac, chassises...); err != nil {
×
1405
                klog.Errorf("failed to connect router '%s' to external: %v", key, err)
×
1406
                return err
×
1407
        }
×
1408
        return nil
×
1409
}
1410

1411
func (c *Controller) handleDeleteVpcStaticRoute(key string) error {
×
1412
        vpc, err := c.vpcsLister.Get(key)
×
1413
        if err != nil {
×
1414
                if k8serrors.IsNotFound(err) {
×
1415
                        return nil
×
1416
                }
×
1417
                klog.Errorf("failed to get vpc %s, %v", key, err)
×
1418
                return err
×
1419
        }
1420
        needUpdate := false
×
1421
        newStaticRoutes := make([]*kubeovnv1.StaticRoute, 0, len(vpc.Spec.StaticRoutes))
×
1422
        for _, route := range vpc.Spec.StaticRoutes {
×
1423
                if route.ECMPMode == util.StaticRouteBfdEcmp {
×
1424
                        needUpdate = true
×
1425
                        continue
×
1426
                }
1427
                newStaticRoutes = append(newStaticRoutes, route)
×
1428
        }
1429
        // keep routes except bfd ecmp routes
1430
        if needUpdate {
×
1431
                vpc.Spec.StaticRoutes = newStaticRoutes
×
1432
                if _, err = c.config.KubeOvnClient.KubeovnV1().Vpcs().Update(context.Background(), vpc, metav1.UpdateOptions{}); err != nil {
×
1433
                        klog.Errorf("failed to update vpc spec static route %s, %v", vpc.Name, err)
×
1434
                        return err
×
1435
                }
×
1436
        }
1437
        if err = c.patchVpcBfdStatus(vpc.Name); err != nil {
×
1438
                klog.Errorf("failed to patch vpc %s, %v", vpc.Name, err)
×
1439
                return err
×
1440
        }
×
1441
        return nil
×
1442
}
1443

1444
func (c *Controller) handleDelVpcExternalSubnet(key, subnet string) error {
×
1445
        lspName := fmt.Sprintf("%s-%s", subnet, key)
×
1446
        lrpName := fmt.Sprintf("%s-%s", key, subnet)
×
1447
        klog.Infof("delete vpc lrp %s", lrpName)
×
1448
        if err := c.OVNNbClient.RemoveLogicalPatchPort(lspName, lrpName); err != nil {
×
1449
                klog.Errorf("failed to disconnect router '%s' to external, %v", key, err)
×
1450
                return err
×
1451
        }
×
1452
        if err := c.config.KubeOvnClient.KubeovnV1().OvnEips().Delete(context.Background(), lrpName, metav1.DeleteOptions{}); err != nil {
×
1453
                if !k8serrors.IsNotFound(err) {
×
1454
                        klog.Errorf("failed to delete ovn eip %s, %v", lrpName, err)
×
1455
                        return err
×
1456
                }
×
1457
        }
1458
        if err := c.OVNNbClient.DeleteBFDByDstIP(lrpName, ""); err != nil {
×
1459
                klog.Error(err)
×
1460
                return err
×
1461
        }
×
1462
        return nil
×
1463
}
1464

1465
func (c *Controller) patchVpcBfdStatus(key string) error {
×
1466
        cachedVpc, err := c.vpcsLister.Get(key)
×
1467
        if err != nil {
×
1468
                if k8serrors.IsNotFound(err) {
×
1469
                        return nil
×
1470
                }
×
1471
                klog.Errorf("failed to get vpc %s, %v", key, err)
×
1472
                return err
×
1473
        }
1474

1475
        if cachedVpc.Status.EnableBfd != cachedVpc.Spec.EnableBfd {
×
1476
                status := cachedVpc.Status.DeepCopy()
×
1477
                status.EnableExternal = cachedVpc.Spec.EnableExternal
×
1478
                status.EnableBfd = cachedVpc.Spec.EnableBfd
×
1479
                bytes, err := status.Bytes()
×
1480
                if err != nil {
×
1481
                        klog.Errorf("failed to marshal vpc status: %v", err)
×
1482
                        return err
×
1483
                }
×
1484
                if _, err = c.config.KubeOvnClient.KubeovnV1().Vpcs().Patch(context.Background(),
×
1485
                        cachedVpc.Name, types.MergePatchType, bytes, metav1.PatchOptions{}, "status"); err != nil {
×
1486
                        klog.Error(err)
×
1487
                        return err
×
1488
                }
×
1489
        }
1490
        return nil
×
1491
}
1492

1493
func (c *Controller) getRouteTablesByVpc(vpc *kubeovnv1.Vpc) map[string][]*kubeovnv1.StaticRoute {
1✔
1494
        rtbs := make(map[string][]*kubeovnv1.StaticRoute)
1✔
1495
        for _, route := range vpc.Spec.StaticRoutes {
2✔
1496
                rtbs[route.RouteTable] = append(rtbs[route.RouteTable], route)
1✔
1497
        }
1✔
1498
        return rtbs
1✔
1499
}
1500

1501
func (c *Controller) updateVpcExternalStatus(key string) error {
×
1502
        cachedVpc, err := c.vpcsLister.Get(key)
×
1503
        if err != nil {
×
1504
                klog.Errorf("failed to get vpc %s, %v", key, err)
×
1505
                return err
×
1506
        }
×
1507
        vpc := cachedVpc.DeepCopy()
×
1508
        vpc.Status.EnableExternal = vpc.Spec.EnableExternal
×
1509
        vpc.Status.EnableBfd = vpc.Spec.EnableBfd
×
1510

×
1511
        if vpc.Spec.EnableExternal {
×
1512
                sort.Strings(vpc.Spec.ExtraExternalSubnets)
×
1513
                vpc.Status.ExtraExternalSubnets = vpc.Spec.ExtraExternalSubnets
×
1514
        } else {
×
1515
                vpc.Status.ExtraExternalSubnets = nil
×
1516
        }
×
1517

1518
        bytes, err := vpc.Status.Bytes()
×
1519
        if err != nil {
×
1520
                klog.Errorf("failed to get vpc bytes, %v", err)
×
1521
                return err
×
1522
        }
×
1523
        if _, err = c.config.KubeOvnClient.KubeovnV1().Vpcs().Patch(context.Background(),
×
1524
                vpc.Name, types.MergePatchType, bytes, metav1.PatchOptions{}, "status"); err != nil {
×
1525
                klog.Errorf("failed to patch vpc %s, %v", key, err)
×
1526
                return err
×
1527
        }
×
1528

1529
        return nil
×
1530
}
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