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

kubeovn / kube-ovn / 26729668873

01 Jun 2026 12:57AM UTC coverage: 24.185% (-0.004%) from 24.189%
26729668873

push

github

oilbeater
fix(controller): populate v6ip from cached EIP in handleAddVpcExternalSubnet (#6799)

When the OvnEip already exists (controller restart/requeue), the else
branch only read V4Ip and MacAddress but not V6Ip. This caused
GetIPAddrWithMask to fail with 'ip should be dualstack' when the
external subnet is dualstack, as it received only the v4 address
against a dualstack CIDR.

Signed-off-by: rohan-kluisz <Rohan.Meshram@Kluisz.ai>
(cherry picked from commit 8bd070c1c)

0 of 4 new or added lines in 1 file covered. (0.0%)

2 existing lines in 1 file now uncovered.

13270 of 54868 relevant lines covered (24.19%)

0.28 hits per line

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

20.81
/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
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.Add(vpc.Name)
×
384
                        return nil
×
385
                }
386
                gatewayV4, gatewayV6 := util.SplitStringIP(joinSubnet.Spec.Gateway)
×
387
                if gatewayV4 != "" {
×
388
                        for table := range staticRouteMapping {
×
389
                                staticTargetRoutes = append(
×
390
                                        staticTargetRoutes,
×
391
                                        &kubeovnv1.StaticRoute{
×
392
                                                Policy:     kubeovnv1.PolicyDst,
×
393
                                                CIDR:       "0.0.0.0/0",
×
394
                                                NextHopIP:  gatewayV4,
×
395
                                                RouteTable: table,
×
396
                                        },
×
397
                                )
×
398
                        }
×
399
                }
400
                if gatewayV6 != "" {
×
401
                        for table := range staticRouteMapping {
×
402
                                staticTargetRoutes = append(
×
403
                                        staticTargetRoutes,
×
404
                                        &kubeovnv1.StaticRoute{
×
405
                                                Policy:     kubeovnv1.PolicyDst,
×
406
                                                CIDR:       "::/0",
×
407
                                                NextHopIP:  gatewayV6,
×
408
                                                RouteTable: table,
×
409
                                        },
×
410
                                )
×
411
                        }
×
412
                }
413
                if c.config.EnableEipSnat {
×
414
                        cm, err := c.configMapsLister.ConfigMaps(c.config.ExternalGatewayConfigNS).Get(util.ExternalGatewayConfig)
×
415
                        if err == nil {
×
416
                                nextHop := cm.Data["external-gw-addr"]
×
417
                                if nextHop == "" {
×
418
                                        if !externalSubnetExist {
×
419
                                                err = fmt.Errorf("failed to get external subnet %s", c.config.ExternalGatewaySwitch)
×
420
                                                klog.Error(err)
×
421
                                                return err
×
422
                                        }
×
423
                                        nextHop = externalSubnet.Spec.Gateway
×
424
                                        if nextHop == "" {
×
425
                                                err := fmt.Errorf("subnet %s has no gateway configuration", externalSubnet.Name)
×
426
                                                klog.Error(err)
×
427
                                                return err
×
428
                                        }
×
429
                                }
430
                                if strings.Contains(nextHop, "/") {
×
431
                                        nextHop = strings.Split(nextHop, "/")[0]
×
432
                                }
×
433

434
                                lr, err := c.OVNNbClient.GetLogicalRouter(vpc.Name, false)
×
435
                                if err != nil {
×
436
                                        klog.Errorf("failed to get logical router %s: %v", vpc.Name, err)
×
437
                                        return err
×
438
                                }
×
439

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

493
        routeNeedDel, routeNeedAdd := diffStaticRoute(staticExistedRoutes, staticTargetRoutes)
1✔
494

1✔
495
        for _, item := range routeNeedDel {
2✔
496
                klog.Infof("vpc %s del static route: %+v", vpc.Name, item)
1✔
497
                policy := convertPolicy(item.Policy)
1✔
498
                if err = c.OVNNbClient.DeleteLogicalRouterStaticRoute(vpc.Name, &item.RouteTable, &policy, item.CIDR, item.NextHopIP); err != nil {
1✔
499
                        klog.Errorf("del vpc %s static route failed, %v", vpc.Name, err)
×
500
                        return err
×
501
                }
×
502
        }
503

504
        for _, item := range routeNeedAdd {
2✔
505
                if item.BfdID != "" {
1✔
506
                        klog.Infof("vpc %s add static ecmp route: %+v", vpc.Name, item)
×
507
                        if err = c.OVNNbClient.AddLogicalRouterStaticRoute(
×
508
                                vpc.Name, item.RouteTable, convertPolicy(item.Policy), item.CIDR, &item.BfdID, externalIDs, item.NextHopIP,
×
509
                        ); err != nil {
×
510
                                klog.Errorf("failed to add bfd static route to vpc %s , %v", vpc.Name, err)
×
511
                                return err
×
512
                        }
×
513
                } else {
1✔
514
                        klog.Infof("vpc %s add static route: %+v", vpc.Name, item)
1✔
515
                        if err = c.OVNNbClient.AddLogicalRouterStaticRoute(
1✔
516
                                vpc.Name, item.RouteTable, convertPolicy(item.Policy), item.CIDR, nil, externalIDs, item.NextHopIP,
1✔
517
                        ); err != nil {
1✔
518
                                klog.Errorf("failed to add normal static route to vpc %s , %v", vpc.Name, err)
×
519
                                return err
×
520
                        }
×
521
                }
522
        }
523

524
        // handle policy route
525
        var (
1✔
526
                policyRouteExisted, policyRouteNeedDel, policyRouteNeedAdd []*kubeovnv1.PolicyRoute
1✔
527
                policyRouteLogical                                         []*ovnnb.LogicalRouterPolicy
1✔
528
        )
1✔
529

1✔
530
        if vpc.Name == c.config.ClusterRouter {
1✔
531
                lastPolicies, _ := c.vpcLastPoliciesMap.Load(vpc.Name)
×
532
                policyRouteExisted = reversePolicies(lastPolicies)
×
533
                // diff list
×
534
                policyRouteNeedDel, policyRouteNeedAdd = diffPolicyRouteWithExisted(policyRouteExisted, vpc.Spec.PolicyRoutes)
×
535
        } else {
1✔
536
                policyRouteLogical, err = c.OVNNbClient.ListLogicalRouterPolicies(vpc.Name, -1, nil, true)
1✔
537
                if err != nil {
1✔
538
                        klog.Errorf("failed to get vpc %s policy route list, %v", vpc.Name, err)
×
539
                        return err
×
540
                }
×
541
                // diff vpc policy route
542
                policyRouteNeedDel, policyRouteNeedAdd = diffPolicyRouteWithLogical(policyRouteLogical, vpc.Spec.PolicyRoutes)
1✔
543
        }
544
        // delete policies non-exist
545
        for _, item := range policyRouteNeedDel {
1✔
546
                klog.Infof("delete policy route for router: %s, priority: %d, match %s", vpc.Name, item.Priority, item.Match)
×
547
                if err = c.OVNNbClient.DeleteLogicalRouterPolicy(vpc.Name, item.Priority, item.Match); err != nil {
×
548
                        klog.Errorf("del vpc %s policy route failed, %v", vpc.Name, err)
×
549
                        return err
×
550
                }
×
551
        }
552
        // add new policies
553
        for _, item := range policyRouteNeedAdd {
2✔
554
                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✔
555
                if err = c.addPolicyRouteToVpc(vpc.Name, item, externalIDs); err != nil {
1✔
556
                        return err
×
557
                }
×
558
        }
559

560
        vpcSubnets, defaultSubnet, err := c.getVpcSubnets(vpc)
1✔
561
        if err != nil {
1✔
562
                klog.Error(err)
×
563
                return err
×
564
        }
×
565

566
        vpc.Status.Subnets = vpcSubnets
1✔
567
        vpc.Status.DefaultLogicalSwitch = defaultSubnet
1✔
568
        vpc.Status.Router = key
1✔
569
        vpc.Status.Standby = true
1✔
570
        vpc.Status.VpcPeerings = newPeers
1✔
571
        if c.config.EnableLb {
1✔
572
                vpcLb, err := c.addLoadBalancer(key)
×
573
                if err != nil {
×
574
                        klog.Error(err)
×
575
                        return err
×
576
                }
×
577
                vpc.Status.TCPLoadBalancer = vpcLb.TCPLoadBalancer
×
578
                vpc.Status.TCPSessionLoadBalancer = vpcLb.TCPSessLoadBalancer
×
579
                vpc.Status.UDPLoadBalancer = vpcLb.UDPLoadBalancer
×
580
                vpc.Status.UDPSessionLoadBalancer = vpcLb.UDPSessLoadBalancer
×
581
                vpc.Status.SctpLoadBalancer = vpcLb.SctpLoadBalancer
×
582
                vpc.Status.SctpSessionLoadBalancer = vpcLb.SctpSessLoadBalancer
×
583
        }
584
        bytes, err := vpc.Status.Bytes()
1✔
585
        if err != nil {
1✔
586
                klog.Error(err)
×
587
                return err
×
588
        }
×
589
        vpc, err = c.config.KubeOvnClient.KubeovnV1().Vpcs().Patch(context.Background(), vpc.Name, types.MergePatchType, bytes, metav1.PatchOptions{}, "status")
1✔
590
        if err != nil {
1✔
591
                klog.Error(err)
×
592
                return err
×
593
        }
×
594

595
        if len(vpc.Annotations) != 0 && strings.ToLower(vpc.Annotations[util.VpcLbAnnotation]) == "on" {
1✔
596
                if err = c.createVpcLb(vpc); err != nil {
×
597
                        klog.Error(err)
×
598
                        return err
×
599
                }
×
600
        } else if err = c.deleteVpcLb(vpc); err != nil {
1✔
601
                klog.Error(err)
×
602
                return err
×
603
        }
×
604

605
        subnets, err := c.subnetsLister.List(labels.Everything())
1✔
606
        if err != nil {
1✔
607
                klog.Error(err)
×
608
                return err
×
609
        }
×
610
        custVpcEnableExternalEcmp := false
1✔
611
        for _, subnet := range subnets {
1✔
612
                if subnet.Spec.Vpc == key {
×
613
                        // Accelerate subnet update when vpc config is updated.
×
614
                        // In case VPC not set namespaces, subnet will backoff and may take long time to back to ready.
×
615
                        if subnet.Status.IsNotReady() {
×
616
                                c.addOrUpdateSubnetQueue.Add(subnet.Name)
×
617
                        }
×
618
                        if vpc.Name != util.DefaultVpc && vpc.Spec.EnableBfd && subnet.Spec.EnableEcmp {
×
619
                                custVpcEnableExternalEcmp = true
×
620
                        }
×
621
                }
622
        }
623

624
        if vpc.Spec.EnableExternal || vpc.Status.EnableExternal {
1✔
625
                if err = c.handleUpdateVpcExternal(cachedVpc, custVpcEnableExternalEcmp, externalSubnetExist, externalSubnetGW); err != nil {
×
626
                        klog.Errorf("failed to handle update external subnet for vpc %s, %v", key, err)
×
627
                        return err
×
628
                }
×
629
        }
630

631
        bfdPortName, bfdPortNodes, err := c.reconcileVpcBfdLRP(vpc)
1✔
632
        if err != nil {
1✔
633
                klog.Error(err)
×
634
                return err
×
635
        }
×
636
        if vpc.Spec.BFDPort == nil || !vpc.Spec.BFDPort.Enabled {
2✔
637
                vpc.Status.BFDPort = kubeovnv1.BFDPortStatus{}
1✔
638
        } else {
1✔
639
                vpc.Status.BFDPort = kubeovnv1.BFDPortStatus{
×
640
                        Name:  bfdPortName,
×
641
                        IP:    vpc.Spec.BFDPort.IP,
×
642
                        Nodes: bfdPortNodes,
×
643
                }
×
644
        }
×
645
        if _, err = c.config.KubeOvnClient.KubeovnV1().Vpcs().
1✔
646
                UpdateStatus(context.Background(), vpc, metav1.UpdateOptions{}); err != nil {
1✔
647
                klog.Error(err)
×
648
                return err
×
649
        }
×
650

651
        return nil
1✔
652
}
653

654
func (c *Controller) handleUpdateVpcExternal(vpc *kubeovnv1.Vpc, custVpcEnableExternalEcmp, defaultExternalSubnetExist bool, externalSubnetGW string) error {
×
655
        if c.config.EnableEipSnat && vpc.Name == util.DefaultVpc {
×
656
                klog.Infof("external_gw handle ovn default external gw %s", vpc.Name)
×
657
                return nil
×
658
        }
×
659

660
        if !vpc.Spec.EnableExternal && !vpc.Status.EnableExternal {
×
661
                // no need to handle external connection
×
662
                return nil
×
663
        }
×
664

665
        // handle any vpc external
666
        if vpc.Spec.EnableExternal && !defaultExternalSubnetExist && vpc.Spec.ExtraExternalSubnets == nil {
×
667
                // at least have a external subnet
×
668
                err := fmt.Errorf("failed to get external subnet for enable external vpc %s", vpc.Name)
×
669
                klog.Error(err)
×
670
                return err
×
671
        }
×
672

673
        if !vpc.Spec.EnableExternal && vpc.Status.EnableExternal {
×
674
                // just del all external subnets connection
×
675
                klog.Infof("disconnect default external subnet %s to vpc %s", c.config.ExternalGatewaySwitch, vpc.Name)
×
676
                if err := c.handleDelVpcExternalSubnet(vpc.Name, c.config.ExternalGatewaySwitch); err != nil {
×
677
                        klog.Errorf("failed to delete external subnet %s connection for vpc %s, error %v", c.config.ExternalGatewaySwitch, vpc.Name, err)
×
678
                        return err
×
679
                }
×
680
                for _, subnet := range vpc.Status.ExtraExternalSubnets {
×
681
                        klog.Infof("disconnect external subnet %s to vpc %s", subnet, vpc.Name)
×
682
                        if err := c.handleDelVpcExternalSubnet(vpc.Name, subnet); err != nil {
×
683
                                klog.Errorf("failed to delete external subnet %s connection for vpc %s, error %v", subnet, vpc.Name, err)
×
684
                                return err
×
685
                        }
×
686
                }
687
        }
688

689
        if vpc.Spec.EnableExternal {
×
690
                if !vpc.Status.EnableExternal {
×
691
                        // just add external connection
×
692
                        if vpc.Spec.ExtraExternalSubnets == nil && defaultExternalSubnetExist {
×
693
                                // only connect default external subnet
×
694
                                klog.Infof("connect default external subnet %s with vpc %s", c.config.ExternalGatewaySwitch, vpc.Name)
×
695
                                if err := c.handleAddVpcExternalSubnet(vpc.Name, c.config.ExternalGatewaySwitch); err != nil {
×
696
                                        klog.Errorf("failed to add external subnet %s connection for vpc %s, error %v", c.config.ExternalGatewaySwitch, vpc.Name, err)
×
697
                                        return err
×
698
                                }
×
699
                        }
700

701
                        // only connect provider network vlan external subnet
702
                        for _, subnet := range vpc.Spec.ExtraExternalSubnets {
×
703
                                klog.Infof("connect external subnet %s with vpc %s", subnet, vpc.Name)
×
704
                                if err := c.handleAddVpcExternalSubnet(vpc.Name, subnet); err != nil {
×
705
                                        klog.Errorf("failed to add external subnet %s connection for vpc %s, error %v", subnet, vpc.Name, err)
×
706
                                        return err
×
707
                                }
×
708
                        }
709
                }
710

711
                // diff to add
712
                for _, subnet := range vpc.Spec.ExtraExternalSubnets {
×
713
                        if !slices.Contains(vpc.Status.ExtraExternalSubnets, subnet) {
×
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 del
723
                for _, subnet := range vpc.Status.ExtraExternalSubnets {
×
724
                        if !slices.Contains(vpc.Spec.ExtraExternalSubnets, subnet) {
×
725
                                klog.Infof("disconnect external subnet %s to vpc %s", subnet, vpc.Name)
×
726
                                if err := c.handleDelVpcExternalSubnet(vpc.Name, subnet); err != nil {
×
727
                                        klog.Errorf("failed to delete external subnet %s connection for vpc %s, error %v", subnet, vpc.Name, err)
×
728
                                        return err
×
729
                                }
×
730
                        }
731
                }
732
        }
733

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

757
        if !vpc.Spec.EnableBfd && vpc.Status.EnableBfd {
×
758
                lrpEipName := fmt.Sprintf("%s-%s", vpc.Name, c.config.ExternalGatewaySwitch)
×
759
                if err := c.OVNNbClient.DeleteBFDByDstIP(lrpEipName, ""); err != nil {
×
760
                        klog.Error(err)
×
761
                        return err
×
762
                }
×
763
                if err := c.handleDeleteVpcStaticRoute(vpc.Name); err != nil {
×
764
                        klog.Errorf("failed to delete bfd route for vpc %s, error %v", vpc.Name, err)
×
765
                        return err
×
766
                }
×
767
        }
768

769
        if err := c.updateVpcExternalStatus(vpc.Name); err != nil {
×
770
                klog.Errorf("failed to update vpc external subnets status, %v", err)
×
771
                return err
×
772
        }
×
773
        return nil
×
774
}
775

776
func (c *Controller) reconcileVpcBfdLRP(vpc *kubeovnv1.Vpc) (string, []string, error) {
1✔
777
        portName := "bfd@" + vpc.Name
1✔
778
        if vpc.Spec.BFDPort == nil || !vpc.Spec.BFDPort.Enabled {
2✔
779
                if err := c.OVNNbClient.DeleteLogicalRouterPort(portName); err != nil {
1✔
780
                        err = fmt.Errorf("failed to delete BFD LRP %s: %w", portName, err)
×
781
                        klog.Error(err)
×
782
                        return portName, nil, err
×
783
                }
×
784
                if err := c.OVNNbClient.DeleteHAChassisGroup(portName); err != nil {
1✔
785
                        err = fmt.Errorf("failed to delete HA chassis group %s: %w", portName, err)
×
786
                        klog.Error(err)
×
787
                        return portName, nil, err
×
788
                }
×
789
                return portName, nil, nil
1✔
790
        }
791

792
        var err error
×
793
        chassisCount := 3
×
794
        selector := labels.Everything()
×
795
        if vpc.Spec.BFDPort.NodeSelector != nil {
×
796
                chassisCount = math.MaxInt
×
797
                if selector, err = metav1.LabelSelectorAsSelector(vpc.Spec.BFDPort.NodeSelector); err != nil {
×
798
                        err = fmt.Errorf("failed to parse node selector %q: %w", vpc.Spec.BFDPort.NodeSelector.String(), err)
×
799
                        klog.Error(err)
×
800
                        return portName, nil, err
×
801
                }
×
802
        }
803

804
        nodes, err := c.nodesLister.List(selector)
×
805
        if err != nil {
×
806
                err = fmt.Errorf("failed to list nodes with selector %q: %w", vpc.Spec.BFDPort.NodeSelector, err)
×
807
                klog.Error(err)
×
808
                return portName, nil, err
×
809
        }
×
810
        if len(nodes) == 0 {
×
811
                err = fmt.Errorf("no nodes found by selector %q", selector.String())
×
812
                klog.Error(err)
×
813
                return portName, nil, err
×
814
        }
×
815

816
        nodeNames := make([]string, 0, len(nodes))
×
817
        chassisCount = min(chassisCount, len(nodes))
×
818
        chassisNames := make([]string, 0, chassisCount)
×
819
        for _, node := range nodes[:chassisCount] {
×
820
                chassis, err := c.OVNSbClient.GetChassisByHost(node.Name)
×
821
                if err != nil {
×
822
                        err = fmt.Errorf("failed to get chassis of node %s: %w", node.Name, err)
×
823
                        klog.Error(err)
×
824
                        return portName, nil, err
×
825
                }
×
826
                chassisNames = append(chassisNames, chassis.Name)
×
827
                nodeNames = append(nodeNames, node.Name)
×
828
        }
829

830
        networks := strings.Split(vpc.Spec.BFDPort.IP, ",")
×
831
        if err = c.OVNNbClient.CreateLogicalRouterPort(vpc.Name, portName, "", networks); err != nil {
×
832
                klog.Error(err)
×
833
                return portName, nil, err
×
834
        }
×
835
        if err = c.OVNNbClient.UpdateLogicalRouterPortNetworks(portName, networks); err != nil {
×
836
                klog.Error(err)
×
837
                return portName, nil, err
×
838
        }
×
839
        if err = c.OVNNbClient.UpdateLogicalRouterPortOptions(portName, map[string]string{"bfd-only": "true"}); err != nil {
×
840
                klog.Error(err)
×
841
                return portName, nil, err
×
842
        }
×
843
        if err = c.OVNNbClient.CreateHAChassisGroup(portName, chassisNames, map[string]string{"lrp": portName}); err != nil {
×
844
                klog.Error(err)
×
845
                return portName, nil, err
×
846
        }
×
847
        if err = c.OVNNbClient.SetLogicalRouterPortHAChassisGroup(portName, portName); err != nil {
×
848
                klog.Error(err)
×
849
                return portName, nil, err
×
850
        }
×
851

852
        return portName, nodeNames, nil
×
853
}
854

855
func (c *Controller) addPolicyRouteToVpc(vpcName string, policy *kubeovnv1.PolicyRoute, externalIDs map[string]string) error {
1✔
856
        var (
1✔
857
                nextHops []string
1✔
858
                err      error
1✔
859
        )
1✔
860

1✔
861
        if policy.NextHopIP != "" {
2✔
862
                nextHops = strings.Split(policy.NextHopIP, ",")
1✔
863
        }
1✔
864

865
        if err = c.OVNNbClient.AddLogicalRouterPolicy(vpcName, policy.Priority, policy.Match, string(policy.Action), nextHops, nil, externalIDs); err != nil {
1✔
866
                klog.Errorf("add policy route to vpc %s failed, %v", vpcName, err)
×
867
                return err
×
868
        }
×
869
        return nil
1✔
870
}
871

872
func buildExternalIDsMapKey(match, action string, priority int) string {
×
873
        return fmt.Sprintf("%s-%s-%d", match, action, priority)
×
874
}
×
875

876
func (c *Controller) batchAddPolicyRouteToVpc(name string, policies []*kubeovnv1.PolicyRoute, externalIDs map[string]map[string]string) error {
×
877
        if len(policies) == 0 {
×
878
                return nil
×
879
        }
×
880
        start := time.Now()
×
881
        routerPolicies := make([]*ovnnb.LogicalRouterPolicy, 0, len(policies))
×
882
        for _, policy := range policies {
×
883
                var nextHops []string
×
884
                if policy.NextHopIP != "" {
×
885
                        nextHops = strings.Split(policy.NextHopIP, ",")
×
886
                }
×
887
                routerPolicies = append(routerPolicies, &ovnnb.LogicalRouterPolicy{
×
888
                        Priority:    policy.Priority,
×
889
                        Nexthops:    nextHops,
×
890
                        Action:      string(policy.Action),
×
891
                        Match:       policy.Match,
×
892
                        ExternalIDs: externalIDs[buildExternalIDsMapKey(policy.Match, string(policy.Action), policy.Priority)],
×
893
                })
×
894
        }
895

896
        if err := c.OVNNbClient.BatchAddLogicalRouterPolicy(name, routerPolicies...); err != nil {
×
897
                klog.Errorf("batch add policy route to vpc %s failed, %v", name, err)
×
898
                return err
×
899
        }
×
900
        klog.Infof("take to %v batch add policy route to vpc %s policies %d", time.Since(start), name, len(policies))
×
901
        return nil
×
902
}
903

904
func (c *Controller) deletePolicyRouteFromVpc(vpcName string, priority int, match string) error {
×
905
        if err := c.OVNNbClient.DeleteLogicalRouterPolicy(vpcName, priority, match); err != nil {
×
906
                klog.Error(err)
×
907
                return err
×
908
        }
×
909
        return nil
×
910
}
911

912
func (c *Controller) batchDeletePolicyRouteFromVpc(name string, policies []*kubeovnv1.PolicyRoute) error {
×
913
        start := time.Now()
×
914
        routerPolicies := make([]*ovnnb.LogicalRouterPolicy, 0, len(policies))
×
915
        for _, policy := range policies {
×
916
                routerPolicies = append(routerPolicies, &ovnnb.LogicalRouterPolicy{
×
917
                        Priority: policy.Priority,
×
918
                        Match:    policy.Match,
×
919
                })
×
920
        }
×
921

922
        if err := c.OVNNbClient.BatchDeleteLogicalRouterPolicy(name, routerPolicies); err != nil {
×
923
                return err
×
924
        }
×
925
        klog.V(3).Infof("take to %v batch delete policy route from vpc %s policies %d", time.Since(start), name, len(policies))
×
926
        return nil
×
927
}
928

929
func (c *Controller) addStaticRouteToVpc(name string, route *kubeovnv1.StaticRoute) error {
×
930
        externalIDs := map[string]string{"vendor": util.CniTypeName}
×
931
        if route.BfdID != "" {
×
932
                klog.Infof("vpc %s add static ecmp route: %+v", name, route)
×
933
                if err := c.OVNNbClient.AddLogicalRouterStaticRoute(
×
934
                        name, route.RouteTable, convertPolicy(route.Policy), route.CIDR, &route.BfdID, externalIDs, route.NextHopIP,
×
935
                ); err != nil {
×
936
                        klog.Errorf("failed to add bfd static route to vpc %s , %v", name, err)
×
937
                        return err
×
938
                }
×
939
        } else {
×
940
                klog.Infof("vpc %s add static route: %+v", name, route)
×
941
                if err := c.OVNNbClient.AddLogicalRouterStaticRoute(
×
942
                        name, route.RouteTable, convertPolicy(route.Policy), route.CIDR, nil, externalIDs, route.NextHopIP,
×
943
                ); err != nil {
×
944
                        klog.Errorf("failed to add normal static route to vpc %s , %v", name, err)
×
945
                        return err
×
946
                }
×
947
        }
948
        return nil
×
949
}
950

951
func (c *Controller) deleteStaticRouteFromVpc(name, table, cidr, nextHop string, policy kubeovnv1.RoutePolicy) error {
×
952
        var (
×
953
                policyStr string
×
954
                err       error
×
955
        )
×
956

×
957
        policyStr = convertPolicy(policy)
×
958
        if err = c.OVNNbClient.DeleteLogicalRouterStaticRoute(name, &table, &policyStr, cidr, nextHop); err != nil {
×
959
                klog.Errorf("del vpc %s static route failed, %v", name, err)
×
960
                return err
×
961
        }
×
962

963
        return nil
×
964
}
965

966
func (c *Controller) batchDeleteStaticRouteFromVpc(name string, staticRoutes []*kubeovnv1.StaticRoute) error {
×
967
        start := time.Now()
×
968
        routeCount := len(staticRoutes)
×
969
        delRoutes := make([]*ovnnb.LogicalRouterStaticRoute, 0, routeCount)
×
970
        for _, sr := range staticRoutes {
×
971
                policyStr := convertPolicy(sr.Policy)
×
972
                newRoute := &ovnnb.LogicalRouterStaticRoute{
×
973
                        RouteTable: sr.RouteTable,
×
974
                        Nexthop:    sr.NextHopIP,
×
975
                        Policy:     &policyStr,
×
976
                        IPPrefix:   sr.CIDR,
×
977
                }
×
978
                delRoutes = append(delRoutes, newRoute)
×
979
        }
×
980
        if err := c.OVNNbClient.BatchDeleteLogicalRouterStaticRoute(name, delRoutes); err != nil {
×
981
                klog.Errorf("batch del vpc %s static route %d failed, %v", name, routeCount, err)
×
982
                return err
×
983
        }
×
984
        klog.V(3).Infof("take to %v batch delete static route from vpc %s static routes %d", time.Since(start), name, len(delRoutes))
×
985
        return nil
×
986
}
987

988
func diffPolicyRouteWithExisted(exists, target []*kubeovnv1.PolicyRoute) ([]*kubeovnv1.PolicyRoute, []*kubeovnv1.PolicyRoute) {
×
989
        var (
×
990
                dels, adds []*kubeovnv1.PolicyRoute
×
991
                existsMap  map[string]*kubeovnv1.PolicyRoute
×
992
                key        string
×
993
                ok         bool
×
994
        )
×
995

×
996
        existsMap = make(map[string]*kubeovnv1.PolicyRoute, len(exists))
×
997
        for _, item := range exists {
×
998
                existsMap[getPolicyRouteItemKey(item)] = item
×
999
        }
×
1000
        // load policies to add
1001
        for _, item := range target {
×
1002
                key = getPolicyRouteItemKey(item)
×
1003

×
1004
                if _, ok = existsMap[key]; ok {
×
1005
                        delete(existsMap, key)
×
1006
                } else {
×
1007
                        adds = append(adds, item)
×
1008
                }
×
1009
        }
1010
        // load policies to delete
1011
        for _, item := range existsMap {
×
1012
                dels = append(dels, item)
×
1013
        }
×
1014
        return dels, adds
×
1015
}
1016

1017
func diffPolicyRouteWithLogical(exists []*ovnnb.LogicalRouterPolicy, target []*kubeovnv1.PolicyRoute) ([]*kubeovnv1.PolicyRoute, []*kubeovnv1.PolicyRoute) {
1✔
1018
        var (
1✔
1019
                dels, adds []*kubeovnv1.PolicyRoute
1✔
1020
                existsMap  map[string]*kubeovnv1.PolicyRoute
1✔
1021
                key        string
1✔
1022
                ok         bool
1✔
1023
        )
1✔
1024
        existsMap = make(map[string]*kubeovnv1.PolicyRoute, len(exists))
1✔
1025

1✔
1026
        for _, item := range exists {
2✔
1027
                if item.ExternalIDs["vpc-egress-gateway"] != "" || item.ExternalIDs["subnet"] != "" ||
1✔
1028
                        item.ExternalIDs["isU2ORoutePolicy"] == "true" {
1✔
1029
                        continue
×
1030
                }
1031
                policy := &kubeovnv1.PolicyRoute{
1✔
1032
                        Priority:  item.Priority,
1✔
1033
                        Match:     item.Match,
1✔
1034
                        Action:    kubeovnv1.PolicyRouteAction(item.Action),
1✔
1035
                        NextHopIP: getLogicalPolicyNextHopKey(item),
1✔
1036
                }
1✔
1037
                existsMap[getPolicyRouteItemKey(policy)] = policy
1✔
1038
        }
1039

1040
        for _, item := range target {
2✔
1041
                key = getPolicyRouteItemKey(item)
1✔
1042

1✔
1043
                if _, ok = existsMap[key]; ok {
2✔
1044
                        delete(existsMap, key)
1✔
1045
                } else {
2✔
1046
                        adds = append(adds, item)
1✔
1047
                }
1✔
1048
        }
1049

1050
        for _, item := range existsMap {
1✔
1051
                dels = append(dels, item)
×
1052
        }
×
1053
        return dels, adds
1✔
1054
}
1055

1056
func getPolicyRouteItemKey(item *kubeovnv1.PolicyRoute) (key string) {
1✔
1057
        return fmt.Sprintf("%d:%s:%s:%s", item.Priority, item.Match, item.Action, normalizePolicyRouteNextHops(item.NextHopIP))
1✔
1058
}
1✔
1059

1060
func getLogicalPolicyNextHopKey(item *ovnnb.LogicalRouterPolicy) string {
1✔
1061
        if len(item.Nexthops) > 0 {
2✔
1062
                return normalizePolicyRouteNextHops(strings.Join(item.Nexthops, ","))
1✔
1063
        }
1✔
1064
        if item.Nexthop != nil {
2✔
1065
                return normalizePolicyRouteNextHops(*item.Nexthop)
1✔
1066
        }
1✔
1067
        return ""
×
1068
}
1069

1070
func normalizePolicyRouteNextHops(nextHopIP string) string {
1✔
1071
        if nextHopIP == "" {
2✔
1072
                return ""
1✔
1073
        }
1✔
1074

1075
        nextHops := make([]string, 0)
1✔
1076
        for nextHop := range strings.SplitSeq(nextHopIP, ",") {
2✔
1077
                nextHop = strings.TrimSpace(nextHop)
1✔
1078
                if nextHop != "" {
2✔
1079
                        nextHops = append(nextHops, nextHop)
1✔
1080
                }
1✔
1081
        }
1082
        if len(nextHops) == 0 {
1✔
1083
                return ""
×
1084
        }
×
1085

1086
        slices.Sort(nextHops)
1✔
1087
        return strings.Join(nextHops, ",")
1✔
1088
}
1089

1090
func diffStaticRoute(exist []*ovnnb.LogicalRouterStaticRoute, target []*kubeovnv1.StaticRoute) (routeNeedDel, routeNeedAdd []*kubeovnv1.StaticRoute) {
1✔
1091
        existRouteMap := make(map[string]*kubeovnv1.StaticRoute, len(exist))
1✔
1092
        for _, item := range exist {
2✔
1093
                policy := kubeovnv1.PolicyDst
1✔
1094
                if item.Policy != nil && *item.Policy == ovnnb.LogicalRouterStaticRoutePolicySrcIP {
2✔
1095
                        policy = kubeovnv1.PolicySrc
1✔
1096
                }
1✔
1097
                route := &kubeovnv1.StaticRoute{
1✔
1098
                        Policy:     policy,
1✔
1099
                        CIDR:       item.IPPrefix,
1✔
1100
                        NextHopIP:  item.Nexthop,
1✔
1101
                        RouteTable: item.RouteTable,
1✔
1102
                        ECMPMode:   util.StaticRouteBfdEcmp,
1✔
1103
                }
1✔
1104
                if item.BFD != nil {
1✔
1105
                        route.BfdID = *item.BFD
×
1106
                }
×
1107
                existRouteMap[getStaticRouteItemKey(route)] = route
1✔
1108
        }
1109

1110
        for _, item := range target {
2✔
1111
                key := getStaticRouteItemKey(item)
1✔
1112
                if _, ok := existRouteMap[key]; ok {
2✔
1113
                        delete(existRouteMap, key)
1✔
1114
                } else {
2✔
1115
                        routeNeedAdd = append(routeNeedAdd, item)
1✔
1116
                }
1✔
1117
        }
1118
        for _, item := range existRouteMap {
2✔
1119
                routeNeedDel = append(routeNeedDel, item)
1✔
1120
        }
1✔
1121
        return routeNeedDel, routeNeedAdd
1✔
1122
}
1123

1124
func getStaticRouteItemKey(item *kubeovnv1.StaticRoute) string {
1✔
1125
        var key string
1✔
1126
        if item.Policy == kubeovnv1.PolicyDst {
2✔
1127
                key = fmt.Sprintf("%s:dst:%s=>%s", item.RouteTable, item.CIDR, item.NextHopIP)
1✔
1128
        } else {
2✔
1129
                key = fmt.Sprintf("%s:src:%s=>%s", item.RouteTable, item.CIDR, item.NextHopIP)
1✔
1130
        }
1✔
1131
        return key
1✔
1132
}
1133

1134
func (c *Controller) formatVpc(vpc *kubeovnv1.Vpc) (*kubeovnv1.Vpc, error) {
1✔
1135
        var changed bool
1✔
1136
        for _, item := range vpc.Spec.StaticRoutes {
2✔
1137
                // check policy
1✔
1138
                if item.Policy == "" {
1✔
1139
                        item.Policy = kubeovnv1.PolicyDst
×
1140
                        changed = true
×
1141
                }
×
1142
                if item.Policy != kubeovnv1.PolicyDst && item.Policy != kubeovnv1.PolicySrc {
1✔
1143
                        return nil, fmt.Errorf("unknown policy type: %q", item.Policy)
×
1144
                }
×
1145
                // check cidr
1146
                if strings.Contains(item.CIDR, "/") {
2✔
1147
                        if _, _, err := net.ParseCIDR(item.CIDR); err != nil {
1✔
1148
                                return nil, fmt.Errorf("invalid cidr %q: %w", item.CIDR, err)
×
1149
                        }
×
1150
                } else if ip := net.ParseIP(item.CIDR); ip == nil {
×
1151
                        return nil, fmt.Errorf("invalid ip %q", item.CIDR)
×
1152
                }
×
1153
                // check next hop ip
1154
                if ip := net.ParseIP(item.NextHopIP); ip == nil {
1✔
1155
                        return nil, fmt.Errorf("invalid next hop ip %q", item.NextHopIP)
×
1156
                }
×
1157
        }
1158

1159
        for _, route := range vpc.Spec.PolicyRoutes {
2✔
1160
                if route.Action != kubeovnv1.PolicyRouteActionReroute {
2✔
1161
                        if route.NextHopIP != "" {
1✔
1162
                                route.NextHopIP = ""
×
1163
                                changed = true
×
1164
                        }
×
1165
                } else {
1✔
1166
                        // ecmp policy route may reroute to multiple next hop ips
1✔
1167
                        for ipStr := range strings.SplitSeq(route.NextHopIP, ",") {
2✔
1168
                                if ip := net.ParseIP(ipStr); ip == nil {
1✔
1169
                                        err := fmt.Errorf("invalid next hop ips: %s", route.NextHopIP)
×
1170
                                        klog.Error(err)
×
1171
                                        return nil, err
×
1172
                                }
×
1173
                        }
1174
                }
1175
        }
1176

1177
        if vpc.DeletionTimestamp.IsZero() && !slices.Contains(vpc.GetFinalizers(), util.KubeOVNControllerFinalizer) {
2✔
1178
                controllerutil.AddFinalizer(vpc, util.KubeOVNControllerFinalizer)
1✔
1179
                changed = true
1✔
1180
        }
1✔
1181

1182
        if !vpc.DeletionTimestamp.IsZero() && len(vpc.Status.Subnets) == 0 {
1✔
1183
                controllerutil.RemoveFinalizer(vpc, util.KubeOVNControllerFinalizer)
×
1184
                changed = true
×
1185
        }
×
1186

1187
        if changed {
2✔
1188
                newVpc, err := c.config.KubeOvnClient.KubeovnV1().Vpcs().Update(context.Background(), vpc, metav1.UpdateOptions{})
1✔
1189
                if err != nil {
1✔
1190
                        klog.Errorf("failed to update vpc %s: %v", vpc.Name, err)
×
1191
                        return nil, err
×
1192
                }
×
1193
                return newVpc, nil
1✔
1194
        }
1195

1196
        return vpc, nil
×
1197
}
1198

1199
func convertPolicies(list []*kubeovnv1.PolicyRoute) string {
×
1200
        if list == nil {
×
1201
                return ""
×
1202
        }
×
1203

1204
        var (
×
1205
                res []byte
×
1206
                err error
×
1207
        )
×
1208

×
1209
        if res, err = json.Marshal(list); err != nil {
×
1210
                klog.Errorf("failed to serialize policy routes %v , reason : %v", list, err)
×
1211
                return ""
×
1212
        }
×
1213
        return string(res)
×
1214
}
1215

1216
func reversePolicies(origin string) []*kubeovnv1.PolicyRoute {
×
1217
        if origin == "" {
×
1218
                return nil
×
1219
        }
×
1220

1221
        var (
×
1222
                list []*kubeovnv1.PolicyRoute
×
1223
                err  error
×
1224
        )
×
1225

×
1226
        if err = json.Unmarshal([]byte(origin), &list); err != nil {
×
1227
                klog.Errorf("failed to deserialize policy routes %v , reason : %v", list, err)
×
1228
                return nil
×
1229
        }
×
1230
        return list
×
1231
}
1232

1233
func convertPolicy(origin kubeovnv1.RoutePolicy) string {
1✔
1234
        if origin == kubeovnv1.PolicyDst {
2✔
1235
                return ovnnb.LogicalRouterStaticRoutePolicyDstIP
1✔
1236
        }
1✔
1237
        return ovnnb.LogicalRouterStaticRoutePolicySrcIP
1✔
1238
}
1239

1240
func reversePolicy(origin ovnnb.LogicalRouterStaticRoutePolicy) kubeovnv1.RoutePolicy {
×
1241
        if origin == ovnnb.LogicalRouterStaticRoutePolicyDstIP {
×
1242
                return kubeovnv1.PolicyDst
×
1243
        }
×
1244
        return kubeovnv1.PolicySrc
×
1245
}
1246

1247
func (c *Controller) getVpcSubnets(vpc *kubeovnv1.Vpc) (subnets []string, defaultSubnet string, err error) {
1✔
1248
        subnets = []string{}
1✔
1249
        allSubnets, err := c.subnetsLister.List(labels.Everything())
1✔
1250
        if err != nil {
1✔
1251
                klog.Error(err)
×
1252
                return nil, "", err
×
1253
        }
×
1254

1255
        for _, subnet := range allSubnets {
1✔
1256
                if subnet.Spec.Vpc != vpc.Name || !subnet.DeletionTimestamp.IsZero() || !isOvnSubnet(subnet) {
×
1257
                        continue
×
1258
                }
1259

1260
                subnets = append(subnets, subnet.Name)
×
1261
                if subnet.Spec.Default {
×
1262
                        defaultSubnet = subnet.Name
×
1263
                }
×
1264

1265
                if vpc.Name != util.DefaultVpc && vpc.Spec.DefaultSubnet != "" && vpc.Spec.DefaultSubnet == subnet.Name {
×
1266
                        defaultSubnet = vpc.Spec.DefaultSubnet
×
1267
                }
×
1268
        }
1269
        sort.Strings(subnets)
1✔
1270
        return subnets, defaultSubnet, err
1✔
1271
}
1272

1273
// createVpcRouter create router to connect logical switches in vpc
1274
func (c *Controller) createVpcRouter(lr string, learnFromARPRequest bool) error {
1✔
1275
        if err := c.OVNNbClient.CreateLogicalRouter(lr); err != nil {
1✔
1276
                klog.Errorf("create logical router %s failed: %v", lr, err)
×
1277
                return err
×
1278
        }
×
1279

1280
        vpcRouter, err := c.OVNNbClient.GetLogicalRouter(lr, false)
1✔
1281
        if err != nil {
1✔
1282
                klog.Errorf("get logical router %s failed: %v", lr, err)
×
1283
                return err
×
1284
        }
×
1285

1286
        lrOptions := map[string]string{
1✔
1287
                "mac_binding_age_threshold": "300",
1✔
1288
                "dynamic_neigh_routers":     "true",
1✔
1289
        }
1✔
1290
        if !learnFromARPRequest {
2✔
1291
                lrOptions["always_learn_from_arp_request"] = "false"
1✔
1292
        }
1✔
1293
        if !maps.Equal(vpcRouter.Options, lrOptions) {
2✔
1294
                vpcRouter.Options = lrOptions
1✔
1295
                if err = c.OVNNbClient.UpdateLogicalRouter(vpcRouter, &vpcRouter.Options); err != nil {
1✔
1296
                        klog.Errorf("failed to update options of logical router %s: %v", lr, err)
×
1297
                        return err
×
1298
                }
×
1299
        }
1300

1301
        return nil
1✔
1302
}
1303

1304
// deleteVpcRouter delete router to connect logical switches in vpc
1305
func (c *Controller) deleteVpcRouter(lr string) error {
×
1306
        return c.OVNNbClient.DeleteLogicalRouter(lr)
×
1307
}
×
1308

1309
func (c *Controller) handleAddVpcExternalSubnet(key, subnet string) error {
×
1310
        cachedSubnet, err := c.subnetsLister.Get(subnet)
×
1311
        if err != nil {
×
1312
                klog.Error(err)
×
1313
                return err
×
1314
        }
×
1315
        lrpEipName := fmt.Sprintf("%s-%s", key, subnet)
×
1316
        cachedEip, err := c.ovnEipsLister.Get(lrpEipName)
×
1317
        var needCreateEip bool
×
1318
        if err != nil {
×
1319
                if !k8serrors.IsNotFound(err) {
×
1320
                        klog.Error(err)
×
1321
                        return err
×
1322
                }
×
1323
                needCreateEip = true
×
1324
        }
1325
        var v4ip, v6ip, mac string
×
1326
        klog.V(3).Infof("create vpc lrp eip %s", lrpEipName)
×
1327
        if needCreateEip {
×
1328
                if v4ip, v6ip, mac, err = c.acquireIPAddress(subnet, lrpEipName, lrpEipName); err != nil {
×
1329
                        klog.Errorf("failed to acquire ip address for lrp eip %s, %v", lrpEipName, err)
×
1330
                        return err
×
1331
                }
×
1332
                if err := c.createOrUpdateOvnEipCR(lrpEipName, subnet, v4ip, v6ip, mac, util.OvnEipTypeLRP); err != nil {
×
1333
                        klog.Errorf("failed to create ovn eip for lrp %s: %v", lrpEipName, err)
×
1334
                        return err
×
1335
                }
×
1336
        } else {
×
1337
                v4ip = cachedEip.Spec.V4Ip
×
NEW
1338
                v6ip = cachedEip.Spec.V6Ip
×
1339
                mac = cachedEip.Spec.MacAddress
×
1340
        }
×
NEW
1341
        if (v4ip == "" && v6ip == "") || mac == "" {
×
1342
                err := fmt.Errorf("lrp '%s' ip or mac should not be empty", lrpEipName)
×
1343
                klog.Error(err)
×
1344
                return err
×
1345
        }
×
1346
        // init lrp gw chassis group
1347
        chassises := []string{}
×
1348
        gwNodes, err := c.nodesLister.List(externalGatewayNodeSelector)
×
1349
        if err != nil {
×
1350
                klog.Errorf("failed to list external gw nodes, %v", err)
×
1351
                return err
×
1352
        }
×
1353
        for _, gwNode := range gwNodes {
×
1354
                annoChassisName := gwNode.Annotations[util.ChassisAnnotation]
×
1355
                if annoChassisName == "" {
×
1356
                        err := fmt.Errorf("node %s has no chassis annotation, kube-ovn-cni not ready", gwNode.Name)
×
1357
                        klog.Error(err)
×
1358
                        return err
×
1359
                }
×
1360
                klog.Infof("get node %s chassis: %s", gwNode.Name, annoChassisName)
×
1361
                chassis, err := c.OVNSbClient.GetChassis(annoChassisName, false)
×
1362
                if err != nil {
×
1363
                        klog.Errorf("failed to get node %s chassis: %s, %v", gwNode.Name, annoChassisName, err)
×
1364
                        return err
×
1365
                }
×
1366
                chassises = append(chassises, chassis.Name)
×
1367
        }
1368

1369
        if len(chassises) == 0 {
×
1370
                err := errors.New("no external gw nodes")
×
1371
                klog.Error(err)
×
1372
                return err
×
1373
        }
×
1374

NEW
1375
        ipCidr, err := util.GetIPAddrWithMask(util.GetStringIP(v4ip, v6ip), cachedSubnet.Spec.CIDRBlock)
×
1376
        if err != nil {
×
1377
                klog.Error(err)
×
1378
                return err
×
1379
        }
×
1380
        lspName := fmt.Sprintf("%s-%s", subnet, key)
×
1381
        lrpName := fmt.Sprintf("%s-%s", key, subnet)
×
1382

×
NEW
1383
        if err := c.OVNNbClient.CreateLogicalPatchPort(subnet, key, lspName, lrpName, ipCidr, mac, chassises...); err != nil {
×
1384
                klog.Errorf("failed to connect router '%s' to external: %v", key, err)
×
1385
                return err
×
1386
        }
×
1387
        return nil
×
1388
}
1389

1390
func (c *Controller) handleDeleteVpcStaticRoute(key string) error {
×
1391
        vpc, err := c.vpcsLister.Get(key)
×
1392
        if err != nil {
×
1393
                if k8serrors.IsNotFound(err) {
×
1394
                        return nil
×
1395
                }
×
1396
                klog.Errorf("failed to get vpc %s, %v", key, err)
×
1397
                return err
×
1398
        }
1399
        needUpdate := false
×
1400
        newStaticRoutes := make([]*kubeovnv1.StaticRoute, 0, len(vpc.Spec.StaticRoutes))
×
1401
        for _, route := range vpc.Spec.StaticRoutes {
×
1402
                if route.ECMPMode == util.StaticRouteBfdEcmp {
×
1403
                        needUpdate = true
×
1404
                        continue
×
1405
                }
1406
                newStaticRoutes = append(newStaticRoutes, route)
×
1407
        }
1408
        // keep routes except bfd ecmp routes
1409
        if needUpdate {
×
1410
                vpc.Spec.StaticRoutes = newStaticRoutes
×
1411
                if _, err = c.config.KubeOvnClient.KubeovnV1().Vpcs().Update(context.Background(), vpc, metav1.UpdateOptions{}); err != nil {
×
1412
                        klog.Errorf("failed to update vpc spec static route %s, %v", vpc.Name, err)
×
1413
                        return err
×
1414
                }
×
1415
        }
1416
        if err = c.patchVpcBfdStatus(vpc.Name); err != nil {
×
1417
                klog.Errorf("failed to patch vpc %s, %v", vpc.Name, err)
×
1418
                return err
×
1419
        }
×
1420
        return nil
×
1421
}
1422

1423
func (c *Controller) handleDelVpcExternalSubnet(key, subnet string) error {
×
1424
        lspName := fmt.Sprintf("%s-%s", subnet, key)
×
1425
        lrpName := fmt.Sprintf("%s-%s", key, subnet)
×
1426
        klog.Infof("delete vpc lrp %s", lrpName)
×
1427
        if err := c.OVNNbClient.RemoveLogicalPatchPort(lspName, lrpName); err != nil {
×
1428
                klog.Errorf("failed to disconnect router '%s' to external, %v", key, err)
×
1429
                return err
×
1430
        }
×
1431
        if err := c.config.KubeOvnClient.KubeovnV1().OvnEips().Delete(context.Background(), lrpName, metav1.DeleteOptions{}); err != nil {
×
1432
                if !k8serrors.IsNotFound(err) {
×
1433
                        klog.Errorf("failed to delete ovn eip %s, %v", lrpName, err)
×
1434
                        return err
×
1435
                }
×
1436
        }
1437
        if err := c.OVNNbClient.DeleteBFDByDstIP(lrpName, ""); err != nil {
×
1438
                klog.Error(err)
×
1439
                return err
×
1440
        }
×
1441
        return nil
×
1442
}
1443

1444
func (c *Controller) patchVpcBfdStatus(key string) error {
×
1445
        cachedVpc, err := c.vpcsLister.Get(key)
×
1446
        if err != nil {
×
1447
                if k8serrors.IsNotFound(err) {
×
1448
                        return nil
×
1449
                }
×
1450
                klog.Errorf("failed to get vpc %s, %v", key, err)
×
1451
                return err
×
1452
        }
1453

1454
        if cachedVpc.Status.EnableBfd != cachedVpc.Spec.EnableBfd {
×
1455
                status := cachedVpc.Status.DeepCopy()
×
1456
                status.EnableExternal = cachedVpc.Spec.EnableExternal
×
1457
                status.EnableBfd = cachedVpc.Spec.EnableBfd
×
1458
                bytes, err := status.Bytes()
×
1459
                if err != nil {
×
1460
                        klog.Errorf("failed to marshal vpc status: %v", err)
×
1461
                        return err
×
1462
                }
×
1463
                if _, err = c.config.KubeOvnClient.KubeovnV1().Vpcs().Patch(context.Background(),
×
1464
                        cachedVpc.Name, types.MergePatchType, bytes, metav1.PatchOptions{}, "status"); err != nil {
×
1465
                        klog.Error(err)
×
1466
                        return err
×
1467
                }
×
1468
        }
1469
        return nil
×
1470
}
1471

1472
func (c *Controller) getRouteTablesByVpc(vpc *kubeovnv1.Vpc) map[string][]*kubeovnv1.StaticRoute {
1✔
1473
        rtbs := make(map[string][]*kubeovnv1.StaticRoute)
1✔
1474
        for _, route := range vpc.Spec.StaticRoutes {
2✔
1475
                rtbs[route.RouteTable] = append(rtbs[route.RouteTable], route)
1✔
1476
        }
1✔
1477
        return rtbs
1✔
1478
}
1479

1480
func (c *Controller) updateVpcExternalStatus(key string) error {
×
1481
        cachedVpc, err := c.vpcsLister.Get(key)
×
1482
        if err != nil {
×
1483
                klog.Errorf("failed to get vpc %s, %v", key, err)
×
1484
                return err
×
1485
        }
×
1486
        vpc := cachedVpc.DeepCopy()
×
1487
        vpc.Status.EnableExternal = vpc.Spec.EnableExternal
×
1488
        vpc.Status.EnableBfd = vpc.Spec.EnableBfd
×
1489

×
1490
        if vpc.Spec.EnableExternal {
×
1491
                sort.Strings(vpc.Spec.ExtraExternalSubnets)
×
1492
                vpc.Status.ExtraExternalSubnets = vpc.Spec.ExtraExternalSubnets
×
1493
        } else {
×
1494
                vpc.Status.ExtraExternalSubnets = nil
×
1495
        }
×
1496

1497
        bytes, err := vpc.Status.Bytes()
×
1498
        if err != nil {
×
1499
                klog.Errorf("failed to get vpc bytes, %v", err)
×
1500
                return err
×
1501
        }
×
1502
        if _, err = c.config.KubeOvnClient.KubeovnV1().Vpcs().Patch(context.Background(),
×
1503
                vpc.Name, types.MergePatchType, bytes, metav1.PatchOptions{}, "status"); err != nil {
×
1504
                klog.Errorf("failed to patch vpc %s, %v", key, err)
×
1505
                return err
×
1506
        }
×
1507

1508
        return nil
×
1509
}
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