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

kubeovn / kube-ovn / 14967332477

12 May 2025 08:20AM UTC coverage: 21.616% (-0.1%) from 21.714%
14967332477

Pull #5110

github

zbb88888
use provider network default interface

Signed-off-by: zbb88888 <jmdxjsjgcxy@gmail.com>
Pull Request #5110: enable check vlan conflict

0 of 257 new or added lines in 6 files covered. (0.0%)

218 existing lines in 9 files now uncovered.

10254 of 47436 relevant lines covered (21.62%)

0.25 hits per line

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

0.0
/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 interface{}) {
×
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 interface{}) {
×
51
        oldVpc := oldObj.(*kubeovnv1.Vpc)
×
52
        newVpc := newObj.(*kubeovnv1.Vpc)
×
53

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

×
67
                if newVpc.Annotations == nil {
×
68
                        newVpc.Annotations = make(map[string]string)
×
69
                }
×
70
                newVpc.Annotations[util.VpcLastPolicies] = convertPolicies(oldVpc.Spec.PolicyRoutes)
×
71

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

78
func (c *Controller) enqueueDelVpc(obj interface{}) {
×
79
        vpc := obj.(*kubeovnv1.Vpc)
×
80
        if _, ok := vpc.Labels[util.VpcExternalLabel]; !vpc.Status.Default || !ok {
×
81
                klog.V(3).Infof("enqueue delete vpc %s", vpc.Name)
×
82
                c.delVpcQueue.Add(vpc)
×
83
        }
×
84
}
85

86
func (c *Controller) handleDelVpc(vpc *kubeovnv1.Vpc) error {
×
87
        c.vpcKeyMutex.LockKey(vpc.Name)
×
88
        defer func() { _ = c.vpcKeyMutex.UnlockKey(vpc.Name) }()
×
89
        klog.Infof("handle delete vpc %s", vpc.Name)
×
90

×
91
        // should delete vpc subnets first
×
92
        var err error
×
93
        for _, subnet := range vpc.Status.Subnets {
×
94
                if _, err = c.subnetsLister.Get(subnet); err != nil {
×
95
                        if k8serrors.IsNotFound(err) {
×
96
                                continue
×
97
                        }
98
                        err = fmt.Errorf("failed to get subnet %s for vpc %s: %w", subnet, vpc.Name, err)
×
99
                } else {
×
100
                        err = fmt.Errorf("failed to delete vpc %s, please delete subnet %s first", vpc.Name, subnet)
×
101
                }
×
102
                klog.Error(err)
×
103
                return err
×
104
        }
105

106
        if err := c.deleteVpcLb(vpc); err != nil {
×
107
                klog.Error(err)
×
108
                return err
×
109
        }
×
110

111
        if err := c.handleDelVpcExternalSubnet(vpc.Name, c.config.ExternalGatewaySwitch); err != nil {
×
112
                klog.Errorf("failed to delete external connection for vpc %s, error %v", vpc.Name, err)
×
113
                return err
×
114
        }
×
115

116
        for _, subnet := range vpc.Status.ExtraExternalSubnets {
×
117
                klog.Infof("disconnect external network %s to vpc %s", subnet, vpc.Name)
×
118
                if err := c.handleDelVpcExternalSubnet(vpc.Name, subnet); err != nil {
×
119
                        klog.Error(err)
×
120
                        return err
×
121
                }
×
122
        }
123

124
        if err := c.deleteVpcRouter(vpc.Status.Router); err != nil {
×
125
                klog.Error(err)
×
126
                return err
×
127
        }
×
128

129
        return nil
×
130
}
131

132
func (c *Controller) handleUpdateVpcStatus(key string) error {
×
133
        c.vpcKeyMutex.LockKey(key)
×
134
        defer func() { _ = c.vpcKeyMutex.UnlockKey(key) }()
×
135
        klog.Infof("handle status update for vpc %s", key)
×
136

×
137
        cachedVpc, err := c.vpcsLister.Get(key)
×
138
        if err != nil {
×
139
                if k8serrors.IsNotFound(err) {
×
140
                        return nil
×
141
                }
×
142
                klog.Error(err)
×
143
                return err
×
144
        }
145
        vpc := cachedVpc.DeepCopy()
×
146

×
147
        subnets, defaultSubnet, err := c.getVpcSubnets(vpc)
×
148
        if err != nil {
×
149
                klog.Error(err)
×
150
                return err
×
151
        }
×
152

153
        change := vpc.Status.DefaultLogicalSwitch != defaultSubnet
×
154

×
155
        vpc.Status.DefaultLogicalSwitch = defaultSubnet
×
156
        vpc.Status.Subnets = subnets
×
157

×
158
        if !vpc.Spec.BFDPort.IsEnabled() && !vpc.Status.BFDPort.IsEmpty() {
×
159
                vpc.Status.BFDPort.Clear()
×
160
        }
×
161
        bytes, err := vpc.Status.Bytes()
×
162
        if err != nil {
×
163
                klog.Error(err)
×
164
                return err
×
165
        }
×
166

167
        vpc, err = c.config.KubeOvnClient.KubeovnV1().Vpcs().Patch(context.Background(), vpc.Name, types.MergePatchType, bytes, metav1.PatchOptions{}, "status")
×
168
        if err != nil {
×
169
                klog.Error(err)
×
170
                return err
×
171
        }
×
172

173
        if len(vpc.Status.Subnets) == 0 {
×
174
                klog.Infof("vpc %s has no subnets, add to queue", vpc.Name)
×
175
                c.addOrUpdateVpcQueue.AddAfter(vpc.Name, 5*time.Second)
×
176
        }
×
177

178
        if change {
×
179
                for _, ns := range vpc.Spec.Namespaces {
×
180
                        c.addNamespaceQueue.Add(ns)
×
181
                }
×
182
        }
183

184
        natGws, err := c.vpcNatGatewayLister.List(labels.Everything())
×
185
        if err != nil {
×
186
                klog.Error(err)
×
187
                return err
×
188
        }
×
189
        for _, gw := range natGws {
×
190
                if key == gw.Spec.Vpc {
×
191
                        c.updateVpcSubnetQueue.Add(gw.Name)
×
192
                }
×
193
        }
194
        return nil
×
195
}
196

197
type VpcLoadBalancer struct {
198
        TCPLoadBalancer      string
199
        TCPSessLoadBalancer  string
200
        UDPLoadBalancer      string
201
        UDPSessLoadBalancer  string
202
        SctpLoadBalancer     string
203
        SctpSessLoadBalancer string
204
}
205

206
func (c *Controller) GenVpcLoadBalancer(vpcKey string) *VpcLoadBalancer {
×
207
        if vpcKey == c.config.ClusterRouter || vpcKey == "" {
×
208
                return &VpcLoadBalancer{
×
209
                        TCPLoadBalancer:      c.config.ClusterTCPLoadBalancer,
×
210
                        TCPSessLoadBalancer:  c.config.ClusterTCPSessionLoadBalancer,
×
211
                        UDPLoadBalancer:      c.config.ClusterUDPLoadBalancer,
×
212
                        UDPSessLoadBalancer:  c.config.ClusterUDPSessionLoadBalancer,
×
213
                        SctpLoadBalancer:     c.config.ClusterSctpLoadBalancer,
×
214
                        SctpSessLoadBalancer: c.config.ClusterSctpSessionLoadBalancer,
×
215
                }
×
216
        }
×
217
        return &VpcLoadBalancer{
×
218
                TCPLoadBalancer:      fmt.Sprintf("vpc-%s-tcp-load", vpcKey),
×
219
                TCPSessLoadBalancer:  fmt.Sprintf("vpc-%s-tcp-sess-load", vpcKey),
×
220
                UDPLoadBalancer:      fmt.Sprintf("vpc-%s-udp-load", vpcKey),
×
221
                UDPSessLoadBalancer:  fmt.Sprintf("vpc-%s-udp-sess-load", vpcKey),
×
222
                SctpLoadBalancer:     fmt.Sprintf("vpc-%s-sctp-load", vpcKey),
×
223
                SctpSessLoadBalancer: fmt.Sprintf("vpc-%s-sctp-sess-load", vpcKey),
×
224
        }
×
225
}
226

227
func (c *Controller) addLoadBalancer(vpc string) (*VpcLoadBalancer, error) {
×
228
        vpcLbConfig := c.GenVpcLoadBalancer(vpc)
×
229
        if err := c.initLB(vpcLbConfig.TCPLoadBalancer, string(v1.ProtocolTCP), false); err != nil {
×
230
                return nil, err
×
231
        }
×
232
        if err := c.initLB(vpcLbConfig.TCPSessLoadBalancer, string(v1.ProtocolTCP), true); err != nil {
×
233
                return nil, err
×
234
        }
×
235
        if err := c.initLB(vpcLbConfig.UDPLoadBalancer, string(v1.ProtocolUDP), false); err != nil {
×
236
                return nil, err
×
237
        }
×
238
        if err := c.initLB(vpcLbConfig.UDPSessLoadBalancer, string(v1.ProtocolUDP), true); err != nil {
×
239
                return nil, err
×
240
        }
×
241
        if err := c.initLB(vpcLbConfig.SctpLoadBalancer, string(v1.ProtocolSCTP), false); err != nil {
×
242
                return nil, err
×
243
        }
×
244
        if err := c.initLB(vpcLbConfig.SctpSessLoadBalancer, string(v1.ProtocolSCTP), true); err != nil {
×
245
                return nil, err
×
246
        }
×
247

248
        return vpcLbConfig, nil
×
249
}
250

251
func (c *Controller) handleAddOrUpdateVpc(key string) error {
×
252
        c.vpcKeyMutex.LockKey(key)
×
253
        defer func() { _ = c.vpcKeyMutex.UnlockKey(key) }()
×
254
        klog.Infof("handle add/update vpc %s", key)
×
255

×
256
        cachedVpc, err := c.vpcsLister.Get(key)
×
257
        if err != nil {
×
258
                if k8serrors.IsNotFound(err) {
×
259
                        return nil
×
260
                }
×
261
                klog.Error(err)
×
262
                return err
×
263
        }
264

265
        vpc, err := c.formatVpc(cachedVpc.DeepCopy())
×
266
        if err != nil {
×
267
                klog.Errorf("failed to format vpc %s: %v", key, err)
×
268
                return err
×
269
        }
×
270

271
        if err = c.createVpcRouter(key); err != nil {
×
272
                klog.Errorf("failed to create vpc router for vpc %s: %v", key, err)
×
273
                return err
×
274
        }
×
275

276
        var newPeers []string
×
277
        for _, peering := range vpc.Spec.VpcPeerings {
×
278
                if err = util.CheckCidrs(peering.LocalConnectIP); err != nil {
×
279
                        klog.Errorf("invalid cidr %s", peering.LocalConnectIP)
×
280
                        return err
×
281
                }
×
282

283
                newPeers = append(newPeers, peering.RemoteVpc)
×
284
                if err := c.OVNNbClient.CreatePeerRouterPort(vpc.Name, peering.RemoteVpc, peering.LocalConnectIP); err != nil {
×
285
                        klog.Errorf("create peer router port for vpc %s, %v", vpc.Name, err)
×
286
                        return err
×
287
                }
×
288
        }
289
        for _, oldPeer := range vpc.Status.VpcPeerings {
×
290
                if !slices.Contains(newPeers, oldPeer) {
×
291
                        if err = c.OVNNbClient.DeleteLogicalRouterPort(fmt.Sprintf("%s-%s", vpc.Name, oldPeer)); err != nil {
×
292
                                klog.Errorf("delete peer router port for vpc %s, %v", vpc.Name, err)
×
293
                                return err
×
294
                        }
×
295
                }
296
        }
297

298
        // handle static route
299
        var (
×
300
                staticExistedRoutes []*ovnnb.LogicalRouterStaticRoute
×
301
                staticTargetRoutes  []*kubeovnv1.StaticRoute
×
302
                staticRouteMapping  map[string][]*kubeovnv1.StaticRoute
×
303
        )
×
304

×
305
        staticExistedRoutes, err = c.OVNNbClient.ListLogicalRouterStaticRoutes(vpc.Name, nil, nil, "", nil)
×
306
        if err != nil {
×
307
                klog.Errorf("failed to get vpc %s static route list, %v", vpc.Name, err)
×
308
                return err
×
309
        }
×
310

311
        var externalSubnet *kubeovnv1.Subnet
×
312
        externalSubnetExist := false
×
313
        if c.config.EnableEipSnat {
×
314
                externalSubnet, err = c.subnetsLister.Get(c.config.ExternalGatewaySwitch)
×
315
                if err != nil {
×
316
                        klog.Warningf("enable-eip-snat need external subnet %s to be exist: %v", c.config.ExternalGatewaySwitch, err)
×
317
                } else {
×
318
                        externalSubnetExist = true
×
319
                }
×
320
        }
321

322
        staticRouteMapping = c.getRouteTablesByVpc(vpc)
×
323
        staticTargetRoutes = vpc.Spec.StaticRoutes
×
324
        if vpc.Name == c.config.ClusterRouter {
×
325
                if _, ok := staticRouteMapping[util.MainRouteTable]; !ok {
×
326
                        staticRouteMapping[util.MainRouteTable] = nil
×
327
                }
×
328

329
                joinSubnet, err := c.subnetsLister.Get(c.config.NodeSwitch)
×
330
                if err != nil {
×
331
                        if !k8serrors.IsNotFound(err) {
×
332
                                klog.Errorf("failed to get node switch subnet %s: %v", c.config.NodeSwitch, err)
×
333
                                return err
×
334
                        }
×
335
                        c.addOrUpdateVpcQueue.Add(vpc.Name)
×
336
                        return nil
×
337
                }
338
                gatewayV4, gatewayV6 := util.SplitStringIP(joinSubnet.Spec.Gateway)
×
339
                if gatewayV4 != "" {
×
340
                        for table := range staticRouteMapping {
×
341
                                staticTargetRoutes = append(
×
342
                                        staticTargetRoutes,
×
343
                                        &kubeovnv1.StaticRoute{
×
344
                                                Policy:     kubeovnv1.PolicyDst,
×
345
                                                CIDR:       "0.0.0.0/0",
×
346
                                                NextHopIP:  gatewayV4,
×
347
                                                RouteTable: table,
×
348
                                        },
×
349
                                )
×
350
                        }
×
351
                }
352
                if gatewayV6 != "" {
×
353
                        for table := range staticRouteMapping {
×
354
                                staticTargetRoutes = append(
×
355
                                        staticTargetRoutes,
×
356
                                        &kubeovnv1.StaticRoute{
×
357
                                                Policy:     kubeovnv1.PolicyDst,
×
358
                                                CIDR:       "::/0",
×
359
                                                NextHopIP:  gatewayV6,
×
360
                                                RouteTable: table,
×
361
                                        },
×
362
                                )
×
363
                        }
×
364
                }
365
                if c.config.EnableEipSnat {
×
366
                        cm, err := c.configMapsLister.ConfigMaps(c.config.ExternalGatewayConfigNS).Get(util.ExternalGatewayConfig)
×
367
                        if err == nil {
×
368
                                nextHop := cm.Data["external-gw-addr"]
×
369
                                if nextHop == "" {
×
370
                                        if !externalSubnetExist {
×
371
                                                err = fmt.Errorf("failed to get external subnet %s", c.config.ExternalGatewaySwitch)
×
372
                                                klog.Error(err)
×
373
                                                return err
×
374
                                        }
×
375
                                        nextHop = externalSubnet.Spec.Gateway
×
376
                                        if nextHop == "" {
×
377
                                                klog.Errorf("no available gateway address")
×
378
                                                return errors.New("no available gateway address")
×
379
                                        }
×
380
                                }
381
                                if strings.Contains(nextHop, "/") {
×
382
                                        nextHop = strings.Split(nextHop, "/")[0]
×
383
                                }
×
384

385
                                lr, err := c.OVNNbClient.GetLogicalRouter(vpc.Name, false)
×
386
                                if err != nil {
×
387
                                        klog.Errorf("failed to get logical router %s: %v", vpc.Name, err)
×
388
                                        return err
×
389
                                }
×
390

391
                                for _, nat := range lr.Nat {
×
392
                                        info, err := c.OVNNbClient.GetNATByUUID(nat)
×
393
                                        if err != nil {
×
394
                                                klog.Errorf("failed to get nat ip info for vpc %s, %v", vpc.Name, err)
×
395
                                                return err
×
396
                                        }
×
397
                                        if info.LogicalIP != "" {
×
398
                                                for table := range staticRouteMapping {
×
399
                                                        staticTargetRoutes = append(
×
400
                                                                staticTargetRoutes,
×
401
                                                                &kubeovnv1.StaticRoute{
×
402
                                                                        Policy:     kubeovnv1.PolicySrc,
×
403
                                                                        CIDR:       info.LogicalIP,
×
404
                                                                        NextHopIP:  nextHop,
×
405
                                                                        RouteTable: table,
×
406
                                                                },
×
407
                                                        )
×
408
                                                }
×
409
                                        }
410
                                }
411
                        }
412
                }
413
        }
414

415
        routeNeedDel, routeNeedAdd, err := diffStaticRoute(staticExistedRoutes, staticTargetRoutes)
×
416
        if err != nil {
×
417
                klog.Errorf("failed to diff vpc %s static route, %v", vpc.Name, err)
×
418
                return err
×
419
        }
×
420

421
        for _, item := range routeNeedDel {
×
422
                klog.Infof("vpc %s del static route: %+v", vpc.Name, item)
×
423
                policy := convertPolicy(item.Policy)
×
424
                if err = c.OVNNbClient.DeleteLogicalRouterStaticRoute(vpc.Name, &item.RouteTable, &policy, item.CIDR, item.NextHopIP); err != nil {
×
425
                        klog.Errorf("del vpc %s static route failed, %v", vpc.Name, err)
×
426
                        return err
×
427
                }
×
428
        }
429

430
        for _, item := range routeNeedAdd {
×
431
                if item.BfdID != "" {
×
432
                        klog.Infof("vpc %s add static ecmp route: %+v", vpc.Name, item)
×
433
                        if err = c.OVNNbClient.AddLogicalRouterStaticRoute(
×
434
                                vpc.Name, item.RouteTable, convertPolicy(item.Policy), item.CIDR, &item.BfdID, nil, item.NextHopIP,
×
435
                        ); err != nil {
×
436
                                klog.Errorf("failed to add bfd static route to vpc %s , %v", vpc.Name, err)
×
437
                                return err
×
438
                        }
×
439
                } else {
×
440
                        klog.Infof("vpc %s add static route: %+v", vpc.Name, item)
×
441
                        if err = c.OVNNbClient.AddLogicalRouterStaticRoute(
×
442
                                vpc.Name, item.RouteTable, convertPolicy(item.Policy), item.CIDR, nil, nil, item.NextHopIP,
×
443
                        ); err != nil {
×
444
                                klog.Errorf("failed to add normal static route to vpc %s , %v", vpc.Name, err)
×
445
                                return err
×
446
                        }
×
447
                }
448
        }
449

450
        // handle policy route
451
        var (
×
452
                policyRouteExisted, policyRouteNeedDel, policyRouteNeedAdd []*kubeovnv1.PolicyRoute
×
453
                policyRouteLogical                                         []*ovnnb.LogicalRouterPolicy
×
454
                externalIDs                                                = map[string]string{"vendor": util.CniTypeName}
×
455
        )
×
456

×
457
        if vpc.Name == c.config.ClusterRouter {
×
458
                policyRouteExisted = reversePolicies(vpc.Annotations[util.VpcLastPolicies])
×
459
                // diff list
×
460
                policyRouteNeedDel, policyRouteNeedAdd = diffPolicyRouteWithExisted(policyRouteExisted, vpc.Spec.PolicyRoutes)
×
461
        } else {
×
462
                if vpc.Spec.PolicyRoutes == nil {
×
463
                        // do not clean default vpc policy routes
×
464
                        if err = c.OVNNbClient.ClearLogicalRouterPolicy(vpc.Name); err != nil {
×
465
                                klog.Errorf("clean all vpc %s policy route failed, %v", vpc.Name, err)
×
466
                                return err
×
467
                        }
×
468
                } else {
×
469
                        policyRouteLogical, err = c.OVNNbClient.ListLogicalRouterPolicies(vpc.Name, -1, nil, true)
×
470
                        if err != nil {
×
471
                                klog.Errorf("failed to get vpc %s policy route list, %v", vpc.Name, err)
×
472
                                return err
×
473
                        }
×
474
                        // diff vpc policy route
475
                        policyRouteNeedDel, policyRouteNeedAdd = diffPolicyRouteWithLogical(policyRouteLogical, vpc.Spec.PolicyRoutes)
×
476
                }
477
        }
478
        // delete policies non-exist
479
        for _, item := range policyRouteNeedDel {
×
480
                klog.Infof("delete policy route for router: %s, priority: %d, match %s", vpc.Name, item.Priority, item.Match)
×
481
                if err = c.OVNNbClient.DeleteLogicalRouterPolicy(vpc.Name, item.Priority, item.Match); err != nil {
×
482
                        klog.Errorf("del vpc %s policy route failed, %v", vpc.Name, err)
×
483
                        return err
×
484
                }
×
485
        }
486
        // add new policies
487
        for _, item := range policyRouteNeedAdd {
×
488
                klog.Infof("add policy route for router: %s, match %s, action %s, nexthop %s, externalID %v", c.config.ClusterRouter, item.Match, string(item.Action), item.NextHopIP, externalIDs)
×
489
                if err = c.OVNNbClient.AddLogicalRouterPolicy(vpc.Name, item.Priority, item.Match, string(item.Action), []string{item.NextHopIP}, nil, externalIDs); err != nil {
×
490
                        klog.Errorf("add policy route to vpc %s failed, %v", vpc.Name, err)
×
491
                        return err
×
492
                }
×
493
        }
494

495
        vpc.Status.Router = key
×
496
        vpc.Status.Standby = true
×
497
        vpc.Status.VpcPeerings = newPeers
×
498
        if c.config.EnableLb {
×
499
                vpcLb, err := c.addLoadBalancer(key)
×
500
                if err != nil {
501
                        klog.Error(err)
×
502
                        return err
×
503
                }
×
504
                vpc.Status.TCPLoadBalancer = vpcLb.TCPLoadBalancer
×
505
                vpc.Status.TCPSessionLoadBalancer = vpcLb.TCPSessLoadBalancer
×
506
                vpc.Status.UDPLoadBalancer = vpcLb.UDPLoadBalancer
×
507
                vpc.Status.UDPSessionLoadBalancer = vpcLb.UDPSessLoadBalancer
×
508
                vpc.Status.SctpLoadBalancer = vpcLb.SctpLoadBalancer
×
509
                vpc.Status.SctpSessionLoadBalancer = vpcLb.SctpSessLoadBalancer
×
UNCOV
510
        }
×
511
        bytes, err := vpc.Status.Bytes()
×
512
        if err != nil {
×
513
                klog.Error(err)
×
514
                return err
×
515
        }
×
516
        vpc, err = c.config.KubeOvnClient.KubeovnV1().Vpcs().Patch(context.Background(), vpc.Name, types.MergePatchType, bytes, metav1.PatchOptions{}, "status")
×
517
        if err != nil {
×
518
                klog.Error(err)
519
                return err
×
520
        }
×
UNCOV
521

×
522
        if len(vpc.Annotations) != 0 && strings.ToLower(vpc.Annotations[util.VpcLbAnnotation]) == "on" {
×
523
                if err = c.createVpcLb(vpc); err != nil {
×
524
                        klog.Error(err)
×
525
                        return err
×
526
                }
×
527
        } else if err = c.deleteVpcLb(vpc); err != nil {
×
528
                klog.Error(err)
×
529
                return err
530
        }
×
UNCOV
531

×
532
        subnets, err := c.subnetsLister.List(labels.Everything())
×
533
        if err != nil {
×
534
                klog.Error(err)
×
535
                return err
×
536
        }
×
537
        custVpcEnableExternalMultiHopEcmp := false
×
538
        for _, subnet := range subnets {
×
539
                if subnet.Spec.Vpc == key {
540
                        c.addOrUpdateSubnetQueue.Add(subnet.Name)
×
541
                        if vpc.Name != util.DefaultVpc && vpc.Spec.EnableBfd && subnet.Spec.EnableEcmp {
×
542
                                custVpcEnableExternalMultiHopEcmp = true
×
543
                        }
×
UNCOV
544
                }
×
UNCOV
545
        }
×
UNCOV
546

×
547
        if vpc.Name != util.DefaultVpc {
×
548
                if cachedVpc.Spec.EnableExternal {
×
549
                        if !externalSubnetExist {
×
550
                                err = fmt.Errorf("failed to get external subnet %s", c.config.ExternalGatewaySwitch)
×
551
                                klog.Error(err)
×
552
                                return err
553
                        }
554
                        if externalSubnet.Spec.LogicalGateway {
555
                                klog.Infof("no need to handle external connection for logical gw external subnet %s", c.config.ExternalGatewaySwitch)
×
556
                                return nil
×
557
                        }
×
558
                        if !cachedVpc.Status.EnableExternal {
×
559
                                // connect vpc to default external
×
560
                                klog.Infof("connect external network with vpc %s", vpc.Name)
×
561
                                if err := c.handleAddVpcExternalSubnet(key, c.config.ExternalGatewaySwitch); err != nil {
×
562
                                        klog.Errorf("failed to add default external connection for vpc %s, error %v", key, err)
×
563
                                        return err
×
564
                                }
×
UNCOV
565
                        }
×
566
                        if vpc.Spec.EnableBfd {
×
567
                                // create bfd between lrp and physical switch gw
×
568
                                // bfd status down means current lrp binding chassis node external nic lost external network connectivity
×
569
                                // should switch lrp to another node
×
570
                                lrpEipName := fmt.Sprintf("%s-%s", key, c.config.ExternalGatewaySwitch)
×
571
                                v4ExtGw, _ := util.SplitStringIP(externalSubnet.Spec.Gateway)
×
572
                                // TODO: dualstack
×
573
                                if _, err := c.OVNNbClient.CreateBFD(lrpEipName, v4ExtGw, c.config.BfdMinRx, c.config.BfdMinTx, c.config.BfdDetectMult, nil); err != nil {
574
                                        klog.Error(err)
×
575
                                        return err
×
576
                                }
×
UNCOV
577
                                // TODO: support multi external nic
×
578
                                if custVpcEnableExternalMultiHopEcmp {
×
579
                                        klog.Infof("remove normal static ecmp route for vpc %s", vpc.Name)
×
580
                                        // auto remove normal type static route, if using ecmp based bfd
×
581
                                        if err := c.reconcileCustomVpcDelNormalStaticRoute(vpc.Name); err != nil {
×
582
                                                klog.Errorf("failed to reconcile del vpc %q normal static route", vpc.Name)
×
583
                                                return err
×
584
                                        }
×
585
                                }
UNCOV
586
                        }
×
587
                        if cachedVpc.Spec.ExtraExternalSubnets != nil {
×
588
                                sort.Strings(vpc.Spec.ExtraExternalSubnets)
×
589
                        }
×
UNCOV
590
                        // add external subnets only in spec and delete external subnets only in status
×
591
                        if !slices.Equal(vpc.Spec.ExtraExternalSubnets, vpc.Status.ExtraExternalSubnets) {
×
592
                                for _, subnetStatus := range cachedVpc.Status.ExtraExternalSubnets {
×
593
                                        if !slices.Contains(cachedVpc.Spec.ExtraExternalSubnets, subnetStatus) {
594
                                                klog.Infof("delete external subnet %s connection for vpc %s", subnetStatus, vpc.Name)
595
                                                if err := c.handleDelVpcExternalSubnet(vpc.Name, subnetStatus); err != nil {
×
596
                                                        klog.Errorf("failed to delete external subnet %s connection for vpc %s, error %v", subnetStatus, vpc.Name, err)
×
597
                                                        return err
×
598
                                                }
UNCOV
599
                                        }
×
UNCOV
600
                                }
×
601
                                for _, subnetSpec := range cachedVpc.Spec.ExtraExternalSubnets {
×
602
                                        if !slices.Contains(cachedVpc.Status.ExtraExternalSubnets, subnetSpec) {
×
603
                                                klog.Infof("connect external subnet %s with vpc %s", subnetSpec, vpc.Name)
×
604
                                                if err := c.handleAddVpcExternalSubnet(key, subnetSpec); err != nil {
×
605
                                                        klog.Errorf("failed to add external subnet %s connection for vpc %s, error %v", subnetSpec, key, err)
×
606
                                                        return err
×
607
                                                }
608
                                        }
UNCOV
609
                                }
×
610
                                if err := c.updateVpcAddExternalStatus(key, true); err != nil {
×
611
                                        klog.Errorf("failed to update additional external subnets status, %v", err)
×
612
                                        return err
×
613
                                }
×
UNCOV
614
                        }
×
UNCOV
615
                }
×
616

617
                if !cachedVpc.Spec.EnableBfd && cachedVpc.Status.EnableBfd {
618
                        lrpEipName := fmt.Sprintf("%s-%s", key, c.config.ExternalGatewaySwitch)
×
619
                        if err := c.OVNNbClient.DeleteBFDByDstIP(lrpEipName, ""); err != nil {
×
620
                                klog.Error(err)
×
621
                                return err
×
622
                        }
623
                        if err := c.handleDeleteVpcStaticRoute(key); err != nil {
624
                                klog.Errorf("failed to delete bfd route for vpc %s, error %v", key, err)
625
                                return err
×
626
                        }
×
UNCOV
627
                }
×
UNCOV
628

×
629
                if !cachedVpc.Spec.EnableExternal && cachedVpc.Status.EnableExternal {
×
630
                        // disconnect vpc to default external
×
631
                        if err := c.handleDelVpcExternalSubnet(key, c.config.ExternalGatewaySwitch); err != nil {
×
632
                                klog.Errorf("failed to delete external connection for vpc %s, error %v", key, err)
×
633
                                return err
×
634
                        }
×
635
                }
636

637
                if cachedVpc.Status.ExtraExternalSubnets != nil && !cachedVpc.Spec.EnableExternal {
×
638
                        // disconnect vpc to extra external subnets
×
639
                        for _, subnet := range cachedVpc.Status.ExtraExternalSubnets {
×
640
                                klog.Infof("disconnect external network %s to vpc %s", subnet, vpc.Name)
×
641
                                if err := c.handleDelVpcExternalSubnet(key, subnet); err != nil {
×
642
                                        klog.Error(err)
×
643
                                        return err
644
                                }
UNCOV
645
                        }
×
646
                        if err := c.updateVpcAddExternalStatus(key, false); err != nil {
×
647
                                klog.Errorf("failed to update additional external subnets status, %v", err)
×
648
                                return err
×
649
                        }
×
UNCOV
650
                }
×
UNCOV
651
        }
×
UNCOV
652

×
653
        bfdPortName, bfdPortNodes, err := c.reconcileVpcBfdLRP(vpc)
654
        if err != nil {
×
655
                klog.Error(err)
×
656
                return err
×
657
        }
×
658
        if vpc.Spec.BFDPort == nil || !vpc.Spec.BFDPort.Enabled {
659
                vpc.Status.BFDPort = kubeovnv1.BFDPortStatus{}
660
        } else {
661
                vpc.Status.BFDPort = kubeovnv1.BFDPortStatus{
×
662
                        Name:  bfdPortName,
×
663
                        IP:    vpc.Spec.BFDPort.IP,
×
664
                        Nodes: bfdPortNodes,
×
665
                }
×
666
        }
×
667
        if _, err = c.config.KubeOvnClient.KubeovnV1().Vpcs().
×
668
                UpdateStatus(context.Background(), vpc, metav1.UpdateOptions{}); err != nil {
×
669
                klog.Error(err)
×
670
                return err
×
671
        }
×
UNCOV
672

×
673
        return nil
×
UNCOV
674
}
×
UNCOV
675

×
676
func (c *Controller) reconcileVpcBfdLRP(vpc *kubeovnv1.Vpc) (string, []string, error) {
×
677
        portName := "bfd@" + vpc.Name
×
678
        if vpc.Spec.BFDPort == nil || !vpc.Spec.BFDPort.Enabled {
×
679
                if err := c.OVNNbClient.DeleteLogicalRouterPort(portName); err != nil {
×
680
                        err = fmt.Errorf("failed to delete BFD LRP %s: %w", portName, err)
681
                        klog.Error(err)
×
682
                        return portName, nil, err
683
                }
684
                if err := c.OVNNbClient.DeleteHAChassisGroup(portName); err != nil {
×
685
                        err = fmt.Errorf("failed to delete HA chassis group %s: %w", portName, err)
×
686
                        klog.Error(err)
×
687
                        return portName, nil, err
×
688
                }
×
689
                return portName, nil, nil
×
UNCOV
690
        }
×
UNCOV
691

×
692
        var err error
×
693
        chassisCount := 3
×
694
        selector := labels.Everything()
×
695
        if vpc.Spec.BFDPort.NodeSelector != nil {
×
696
                chassisCount = math.MaxInt
×
697
                if selector, err = metav1.LabelSelectorAsSelector(vpc.Spec.BFDPort.NodeSelector); err != nil {
×
698
                        err = fmt.Errorf("failed to parse node selector %q: %w", vpc.Spec.BFDPort.NodeSelector.String(), err)
699
                        klog.Error(err)
700
                        return portName, nil, err
×
701
                }
×
UNCOV
702
        }
×
UNCOV
703

×
704
        nodes, err := c.nodesLister.List(selector)
×
705
        if err != nil {
×
706
                err = fmt.Errorf("failed to list nodes with selector %q: %w", vpc.Spec.BFDPort.NodeSelector, err)
×
707
                klog.Error(err)
×
708
                return portName, nil, err
×
709
        }
×
710
        if len(nodes) == 0 {
711
                err = fmt.Errorf("no nodes found by selector %q", selector.String())
712
                klog.Error(err)
×
713
                return portName, nil, err
×
714
        }
×
UNCOV
715

×
716
        nodeNames := make([]string, 0, len(nodes))
×
717
        chassisCount = min(chassisCount, len(nodes))
×
718
        chassisNames := make([]string, 0, chassisCount)
×
719
        for _, nodes := range nodes[:chassisCount] {
×
720
                chassis, err := c.OVNSbClient.GetChassisByHost(nodes.Name)
×
721
                if err != nil {
×
722
                        err = fmt.Errorf("failed to get chassis of node %s: %w", nodes.Name, err)
×
723
                        klog.Error(err)
724
                        return portName, nil, err
×
725
                }
×
726
                chassisNames = append(chassisNames, chassis.Name)
×
727
                nodeNames = append(nodeNames, nodes.Name)
×
UNCOV
728
        }
×
UNCOV
729

×
730
        networks := strings.Split(vpc.Spec.BFDPort.IP, ",")
×
731
        if err = c.OVNNbClient.CreateLogicalRouterPort(vpc.Name, portName, "", networks); err != nil {
×
732
                klog.Error(err)
×
733
                return portName, nil, err
×
734
        }
×
735
        if err = c.OVNNbClient.UpdateLogicalRouterPortNetworks(portName, networks); err != nil {
×
736
                klog.Error(err)
737
                return portName, nil, err
738
        }
×
739
        if err = c.OVNNbClient.UpdateLogicalRouterPortOptions(portName, map[string]string{"bfd-only": "true"}); err != nil {
×
740
                klog.Error(err)
×
741
                return portName, nil, err
×
742
        }
×
743
        if err = c.OVNNbClient.CreateHAChassisGroup(portName, chassisNames, map[string]string{"lrp": portName}); err != nil {
×
744
                klog.Error(err)
×
745
                return portName, nil, err
×
746
        }
×
747
        if err = c.OVNNbClient.SetLogicalRouterPortHAChassisGroup(portName, portName); err != nil {
×
748
                klog.Error(err)
×
749
                return portName, nil, err
×
750
        }
×
UNCOV
751

×
752
        return portName, nodeNames, nil
×
UNCOV
753
}
×
UNCOV
754

×
755
func (c *Controller) addPolicyRouteToVpc(vpcName string, policy *kubeovnv1.PolicyRoute, externalIDs map[string]string) error {
×
756
        var (
×
757
                nextHops []string
×
758
                err      error
×
759
        )
760

×
761
        if policy.NextHopIP != "" {
762
                nextHops = strings.Split(policy.NextHopIP, ",")
763
        }
×
UNCOV
764

×
765
        if err = c.OVNNbClient.AddLogicalRouterPolicy(vpcName, policy.Priority, policy.Match, string(policy.Action), nextHops, nil, externalIDs); err != nil {
×
766
                klog.Errorf("add policy route to vpc %s failed, %v", vpcName, err)
×
767
                return err
×
768
        }
×
769
        return nil
×
UNCOV
770
}
×
UNCOV
771

×
772
func buildExternalIDsMapKey(match, action string, priority int) string {
773
        return fmt.Sprintf("%s-%s-%d", match, action, priority)
×
774
}
×
UNCOV
775

×
776
func (c *Controller) batchAddPolicyRouteToVpc(name string, policies []*kubeovnv1.PolicyRoute, externalIDs map[string]map[string]string) error {
×
777
        if len(policies) == 0 {
×
778
                return nil
779
        }
780
        start := time.Now()
×
781
        routerPolicies := make([]*ovnnb.LogicalRouterPolicy, 0, len(policies))
×
782
        for _, policy := range policies {
×
783
                var nextHops []string
784
                if policy.NextHopIP != "" {
×
785
                        nextHops = strings.Split(policy.NextHopIP, ",")
×
786
                }
×
787
                routerPolicies = append(routerPolicies, &ovnnb.LogicalRouterPolicy{
×
788
                        Priority:    policy.Priority,
×
789
                        Nexthops:    nextHops,
×
790
                        Action:      string(policy.Action),
×
791
                        Match:       policy.Match,
×
792
                        ExternalIDs: externalIDs[buildExternalIDsMapKey(policy.Match, string(policy.Action), policy.Priority)],
×
793
                })
×
UNCOV
794
        }
×
UNCOV
795

×
796
        if err := c.OVNNbClient.BatchAddLogicalRouterPolicy(name, routerPolicies...); err != nil {
×
797
                klog.Errorf("batch add policy route to vpc %s failed, %v", name, err)
×
798
                return err
×
799
        }
×
800
        klog.Infof("take to %v batch add policy route to vpc %s policies %d", time.Since(start), name, len(policies))
×
801
        return nil
×
802
}
803

804
func (c *Controller) deletePolicyRouteFromVpc(vpcName string, priority int, match string) error {
×
805
        var (
×
806
                vpc, cachedVpc *kubeovnv1.Vpc
×
807
                err            error
×
808
        )
×
809

×
810
        if err = c.OVNNbClient.DeleteLogicalRouterPolicy(vpcName, priority, match); err != nil {
811
                klog.Error(err)
812
                return err
×
813
        }
×
UNCOV
814

×
815
        cachedVpc, err = c.vpcsLister.Get(vpcName)
×
816
        if err != nil {
×
817
                if k8serrors.IsNotFound(err) {
×
818
                        return nil
×
819
                }
×
820
                klog.Error(err)
×
821
                return err
×
822
        }
823
        vpc = cachedVpc.DeepCopy()
×
824
        // make sure custom policies not be deleted
×
825
        _, err = c.config.KubeOvnClient.KubeovnV1().Vpcs().Update(context.Background(), vpc, metav1.UpdateOptions{})
×
826
        if err != nil {
×
827
                klog.Error(err)
×
828
                return err
×
829
        }
×
830
        return nil
UNCOV
831
}
×
UNCOV
832

×
833
func (c *Controller) batchDeletePolicyRouteFromVpc(name string, policies []*kubeovnv1.PolicyRoute) error {
×
834
        var (
×
835
                vpc, cachedVpc *kubeovnv1.Vpc
×
836
                err            error
×
837
        )
×
838

×
839
        start := time.Now()
840
        routerPolicies := make([]*ovnnb.LogicalRouterPolicy, 0, len(policies))
841
        for _, policy := range policies {
×
842
                routerPolicies = append(routerPolicies, &ovnnb.LogicalRouterPolicy{
×
843
                        Priority: policy.Priority,
×
844
                        Match:    policy.Match,
×
845
                })
×
846
        }
×
UNCOV
847

×
848
        if err = c.OVNNbClient.BatchDeleteLogicalRouterPolicy(name, routerPolicies); err != nil {
×
849
                return err
×
850
        }
×
851
        klog.V(3).Infof("take to %v batch delete policy route from vpc %s policies %d", time.Since(start), name, len(policies))
×
852

×
853
        cachedVpc, err = c.vpcsLister.Get(name)
×
854
        if err != nil {
×
855
                if k8serrors.IsNotFound(err) {
856
                        return nil
×
857
                }
×
858
                klog.Error(err)
×
859
                return err
×
UNCOV
860
        }
×
861
        vpc = cachedVpc.DeepCopy()
×
862
        // make sure custom policies not be deleted
×
863
        _, err = c.config.KubeOvnClient.KubeovnV1().Vpcs().Update(context.Background(), vpc, metav1.UpdateOptions{})
×
864
        if err != nil {
×
865
                klog.Error(err)
×
866
                return err
×
867
        }
×
868
        return nil
UNCOV
869
}
×
UNCOV
870

×
871
func (c *Controller) addStaticRouteToVpc(name string, route *kubeovnv1.StaticRoute) error {
×
872
        if route.BfdID != "" {
×
873
                klog.Infof("vpc %s add static ecmp route: %+v", name, route)
×
874
                if err := c.OVNNbClient.AddLogicalRouterStaticRoute(
×
875
                        name, route.RouteTable, convertPolicy(route.Policy), route.CIDR, &route.BfdID, nil, route.NextHopIP,
×
876
                ); err != nil {
×
877
                        klog.Errorf("failed to add bfd static route to vpc %s , %v", name, err)
878
                        return err
879
                }
×
880
        } else {
×
881
                klog.Infof("vpc %s add static route: %+v", name, route)
×
882
                if err := c.OVNNbClient.AddLogicalRouterStaticRoute(
×
883
                        name, route.RouteTable, convertPolicy(route.Policy), route.CIDR, nil, nil, route.NextHopIP,
×
884
                ); err != nil {
×
885
                        klog.Errorf("failed to add normal static route to vpc %s , %v", name, err)
×
886
                        return err
×
887
                }
×
UNCOV
888
        }
×
889
        return nil
×
UNCOV
890
}
×
UNCOV
891

×
892
func (c *Controller) deleteStaticRouteFromVpc(name, table, cidr, nextHop string, policy kubeovnv1.RoutePolicy) error {
×
893
        var (
×
894
                policyStr string
×
895
                err       error
×
896
        )
897

×
898
        policyStr = convertPolicy(policy)
899
        if err = c.OVNNbClient.DeleteLogicalRouterStaticRoute(name, &table, &policyStr, cidr, nextHop); err != nil {
900
                klog.Errorf("del vpc %s static route failed, %v", name, err)
×
901
                return err
×
902
        }
×
UNCOV
903

×
904
        return nil
×
UNCOV
905
}
×
UNCOV
906

×
907
func (c *Controller) batchDeleteStaticRouteFromVpc(name string, staticRoutes []*kubeovnv1.StaticRoute) error {
×
908
        var (
×
909
                vpc, cachedVpc *kubeovnv1.Vpc
×
910
                err            error
×
911
        )
912
        start := time.Now()
×
913
        routeCount := len(staticRoutes)
914
        delRoutes := make([]*ovnnb.LogicalRouterStaticRoute, 0, routeCount)
915
        for _, sr := range staticRoutes {
×
916
                policyStr := convertPolicy(sr.Policy)
×
917
                newRoute := &ovnnb.LogicalRouterStaticRoute{
×
918
                        RouteTable: sr.RouteTable,
×
919
                        Nexthop:    sr.NextHopIP,
×
920
                        Policy:     &policyStr,
×
921
                        IPPrefix:   sr.CIDR,
×
922
                }
×
923
                delRoutes = append(delRoutes, newRoute)
×
924
        }
×
925
        if err = c.OVNNbClient.BatchDeleteLogicalRouterStaticRoute(name, delRoutes); err != nil {
×
926
                klog.Errorf("batch del vpc %s static route %d failed, %v", name, routeCount, err)
×
927
                return err
×
928
        }
×
929
        klog.V(3).Infof("take to %v batch delete static route from vpc %s static routes %d", time.Since(start), name, len(delRoutes))
×
930

×
931
        cachedVpc, err = c.vpcsLister.Get(name)
×
932
        if err != nil {
×
933
                if k8serrors.IsNotFound(err) {
×
934
                        return nil
×
935
                }
×
936
                klog.Error(err)
×
937
                return err
×
UNCOV
938
        }
×
939
        vpc = cachedVpc.DeepCopy()
×
940
        // make sure custom policies not be deleted
×
941
        _, err = c.config.KubeOvnClient.KubeovnV1().Vpcs().Update(context.Background(), vpc, metav1.UpdateOptions{})
×
942
        if err != nil {
×
943
                klog.Error(err)
×
944
                return err
×
945
        }
×
946
        return nil
UNCOV
947
}
×
UNCOV
948

×
949
func diffPolicyRouteWithExisted(exists, target []*kubeovnv1.PolicyRoute) ([]*kubeovnv1.PolicyRoute, []*kubeovnv1.PolicyRoute) {
×
950
        var (
×
951
                dels, adds []*kubeovnv1.PolicyRoute
×
952
                existsMap  map[string]*kubeovnv1.PolicyRoute
×
953
                key        string
×
954
                ok         bool
×
955
        )
956

957
        existsMap = make(map[string]*kubeovnv1.PolicyRoute, len(exists))
×
958
        for _, item := range exists {
×
959
                existsMap[getPolicyRouteItemKey(item)] = item
×
960
        }
×
UNCOV
961
        // load policies to add
×
962
        for _, item := range target {
×
963
                key = getPolicyRouteItemKey(item)
×
964

×
965
                if _, ok = existsMap[key]; ok {
×
966
                        delete(existsMap, key)
×
967
                } else {
×
968
                        adds = append(adds, item)
×
969
                }
UNCOV
970
        }
×
UNCOV
971
        // load policies to delete
×
972
        for _, item := range existsMap {
×
973
                dels = append(dels, item)
×
974
        }
×
975
        return dels, adds
×
UNCOV
976
}
×
UNCOV
977

×
978
func diffPolicyRouteWithLogical(exists []*ovnnb.LogicalRouterPolicy, target []*kubeovnv1.PolicyRoute) ([]*kubeovnv1.PolicyRoute, []*kubeovnv1.PolicyRoute) {
979
        var (
980
                dels, adds []*kubeovnv1.PolicyRoute
×
981
                existsMap  map[string]*kubeovnv1.PolicyRoute
×
982
                key        string
×
983
                ok         bool
×
984
        )
985
        existsMap = make(map[string]*kubeovnv1.PolicyRoute, len(exists))
986

×
987
        for _, item := range exists {
×
988
                policy := &kubeovnv1.PolicyRoute{
×
989
                        Priority: item.Priority,
×
990
                        Match:    item.Match,
×
991
                        Action:   kubeovnv1.PolicyRouteAction(item.Action),
×
992
                }
×
993
                existsMap[getPolicyRouteItemKey(policy)] = policy
×
994
        }
×
UNCOV
995

×
996
        for _, item := range target {
×
997
                key = getPolicyRouteItemKey(item)
×
998

×
999
                if _, ok = existsMap[key]; ok {
×
1000
                        delete(existsMap, key)
×
1001
                } else {
×
1002
                        adds = append(adds, item)
×
1003
                }
UNCOV
1004
        }
×
UNCOV
1005

×
1006
        for _, item := range existsMap {
×
1007
                dels = append(dels, item)
×
1008
        }
×
1009
        return dels, adds
×
UNCOV
1010
}
×
UNCOV
1011

×
1012
func getPolicyRouteItemKey(item *kubeovnv1.PolicyRoute) (key string) {
1013
        return fmt.Sprintf("%d:%s:%s:%s", item.Priority, item.Match, item.Action, item.NextHopIP)
1014
}
×
UNCOV
1015

×
1016
func diffStaticRoute(exist []*ovnnb.LogicalRouterStaticRoute, target []*kubeovnv1.StaticRoute) (routeNeedDel, routeNeedAdd []*kubeovnv1.StaticRoute, err error) {
×
1017
        existRouteMap := make(map[string]*kubeovnv1.StaticRoute, len(exist))
×
1018
        for _, item := range exist {
1019
                policy := kubeovnv1.PolicyDst
1020
                if item.Policy != nil && *item.Policy == ovnnb.LogicalRouterStaticRoutePolicySrcIP {
×
1021
                        policy = kubeovnv1.PolicySrc
×
1022
                }
×
1023
                route := &kubeovnv1.StaticRoute{
1024
                        Policy:     policy,
×
1025
                        CIDR:       item.IPPrefix,
×
1026
                        NextHopIP:  item.Nexthop,
×
1027
                        RouteTable: item.RouteTable,
×
1028
                        ECMPMode:   util.StaticRouteBfdEcmp,
×
1029
                }
×
1030
                if item.BFD != nil {
×
1031
                        route.BfdID = *item.BFD
×
1032
                }
×
1033
                existRouteMap[getStaticRouteItemKey(route)] = route
×
UNCOV
1034
        }
×
UNCOV
1035

×
1036
        for _, item := range target {
×
1037
                key := getStaticRouteItemKey(item)
×
1038
                if _, ok := existRouteMap[key]; ok {
×
1039
                        delete(existRouteMap, key)
×
1040
                } else {
×
1041
                        routeNeedAdd = append(routeNeedAdd, item)
×
1042
                }
1043
        }
1044
        for _, item := range existRouteMap {
×
1045
                routeNeedDel = append(routeNeedDel, item)
×
1046
        }
×
1047
        return
×
UNCOV
1048
}
×
UNCOV
1049

×
1050
func getStaticRouteItemKey(item *kubeovnv1.StaticRoute) string {
×
1051
        var key string
1052
        if item.Policy == kubeovnv1.PolicyDst {
×
1053
                key = fmt.Sprintf("%s:dst:%s=>%s", item.RouteTable, item.CIDR, item.NextHopIP)
×
1054
        } else {
×
1055
                key = fmt.Sprintf("%s:src:%s=>%s", item.RouteTable, item.CIDR, item.NextHopIP)
×
1056
        }
1057
        return key
UNCOV
1058
}
×
UNCOV
1059

×
1060
func (c *Controller) formatVpc(vpc *kubeovnv1.Vpc) (*kubeovnv1.Vpc, error) {
×
1061
        var changed bool
×
1062
        for _, item := range vpc.Spec.StaticRoutes {
×
1063
                // check policy
×
1064
                if item.Policy == "" {
×
1065
                        item.Policy = kubeovnv1.PolicyDst
×
1066
                        changed = true
1067
                }
1068
                if item.Policy != kubeovnv1.PolicyDst && item.Policy != kubeovnv1.PolicySrc {
×
1069
                        return nil, fmt.Errorf("unknown policy type: %q", item.Policy)
×
1070
                }
×
UNCOV
1071
                // check cidr
×
1072
                if strings.Contains(item.CIDR, "/") {
×
1073
                        if _, _, err := net.ParseCIDR(item.CIDR); err != nil {
×
1074
                                return nil, fmt.Errorf("invalid cidr %q: %w", item.CIDR, err)
×
1075
                        }
×
1076
                } else if ip := net.ParseIP(item.CIDR); ip == nil {
×
1077
                        return nil, fmt.Errorf("invalid ip %q", item.CIDR)
×
1078
                }
×
1079
                // check next hop ip
1080
                if ip := net.ParseIP(item.NextHopIP); ip == nil {
×
1081
                        return nil, fmt.Errorf("invalid next hop ip %q", item.NextHopIP)
×
1082
                }
×
UNCOV
1083
        }
×
UNCOV
1084

×
1085
        for _, route := range vpc.Spec.PolicyRoutes {
×
1086
                if route.Action != kubeovnv1.PolicyRouteActionReroute {
×
1087
                        if route.NextHopIP != "" {
1088
                                route.NextHopIP = ""
×
1089
                                changed = true
×
1090
                        }
×
1091
                } else {
1092
                        // ecmp policy route may reroute to multiple next hop ips
1093
                        for _, ipStr := range strings.Split(route.NextHopIP, ",") {
×
1094
                                if ip := net.ParseIP(ipStr); ip == nil {
×
1095
                                        err := fmt.Errorf("invalid next hop ips: %s", route.NextHopIP)
×
1096
                                        klog.Error(err)
×
1097
                                        return nil, err
×
1098
                                }
×
UNCOV
1099
                        }
×
UNCOV
1100
                }
×
UNCOV
1101
        }
×
UNCOV
1102

×
1103
        if vpc.DeletionTimestamp.IsZero() && !slices.Contains(vpc.GetFinalizers(), util.KubeOVNControllerFinalizer) {
×
1104
                controllerutil.AddFinalizer(vpc, util.KubeOVNControllerFinalizer)
×
1105
                changed = true
×
1106
        }
×
1107

1108
        if !vpc.DeletionTimestamp.IsZero() && len(vpc.Status.Subnets) == 0 {
1109
                controllerutil.RemoveFinalizer(vpc, util.KubeOVNControllerFinalizer)
1110
                changed = true
1111
        }
×
UNCOV
1112

×
1113
        if changed {
×
1114
                newVpc, err := c.config.KubeOvnClient.KubeovnV1().Vpcs().Update(context.Background(), vpc, metav1.UpdateOptions{})
×
1115
                if err != nil {
1116
                        klog.Errorf("failed to update vpc %s: %v", vpc.Name, err)
×
1117
                        return nil, err
×
1118
                }
×
1119
                return newVpc, nil
×
1120
        }
UNCOV
1121

×
1122
        return vpc, nil
×
UNCOV
1123
}
×
UNCOV
1124

×
1125
func convertPolicies(list []*kubeovnv1.PolicyRoute) string {
×
1126
        if list == nil {
×
1127
                return ""
×
1128
        }
1129

1130
        var (
×
1131
                res []byte
1132
                err error
1133
        )
×
1134

×
1135
        if res, err = json.Marshal(list); err != nil {
×
1136
                klog.Errorf("failed to serialize policy routes %v , reason : %v", list, err)
×
1137
                return ""
1138
        }
×
1139
        return string(res)
×
UNCOV
1140
}
×
UNCOV
1141

×
1142
func reversePolicies(origin string) []*kubeovnv1.PolicyRoute {
×
1143
        if origin == "" {
×
1144
                return nil
×
1145
        }
×
UNCOV
1146

×
1147
        var (
×
1148
                list []*kubeovnv1.PolicyRoute
1149
                err  error
1150
        )
×
1151

×
1152
        if err = json.Unmarshal([]byte(origin), &list); err != nil {
×
1153
                klog.Errorf("failed to deserialize policy routes %v , reason : %v", list, err)
×
1154
                return nil
1155
        }
×
1156
        return list
×
UNCOV
1157
}
×
UNCOV
1158

×
1159
func convertPolicy(origin kubeovnv1.RoutePolicy) string {
×
1160
        if origin == kubeovnv1.PolicyDst {
×
1161
                return ovnnb.LogicalRouterStaticRoutePolicyDstIP
×
1162
        }
×
1163
        return ovnnb.LogicalRouterStaticRoutePolicySrcIP
×
UNCOV
1164
}
×
1165

1166
func reversePolicy(origin ovnnb.LogicalRouterStaticRoutePolicy) kubeovnv1.RoutePolicy {
1167
        if origin == ovnnb.LogicalRouterStaticRoutePolicyDstIP {
×
1168
                return kubeovnv1.PolicyDst
×
1169
        }
×
1170
        return kubeovnv1.PolicySrc
×
UNCOV
1171
}
×
1172

1173
func (c *Controller) getVpcSubnets(vpc *kubeovnv1.Vpc) (subnets []string, defaultSubnet string, err error) {
1174
        subnets = []string{}
×
1175
        allSubnets, err := c.subnetsLister.List(labels.Everything())
×
1176
        if err != nil {
×
1177
                klog.Error(err)
×
1178
                return nil, "", err
×
1179
        }
1180

1181
        for _, subnet := range allSubnets {
×
1182
                if subnet.Spec.Vpc != vpc.Name || !subnet.DeletionTimestamp.IsZero() || !isOvnSubnet(subnet) {
×
1183
                        continue
×
UNCOV
1184
                }
×
UNCOV
1185

×
1186
                subnets = append(subnets, subnet.Name)
×
1187
                if subnet.Spec.Default {
×
1188
                        defaultSubnet = subnet.Name
1189
                }
×
UNCOV
1190

×
1191
                if vpc.Name != util.DefaultVpc && vpc.Spec.DefaultSubnet != "" && vpc.Spec.DefaultSubnet == subnet.Name {
×
1192
                        defaultSubnet = vpc.Spec.DefaultSubnet
1193
                }
UNCOV
1194
        }
×
1195
        return
×
UNCOV
1196
}
×
UNCOV
1197

×
1198
// createVpcRouter create router to connect logical switches in vpc
1199
func (c *Controller) createVpcRouter(lr string) error {
×
1200
        if err := c.OVNNbClient.CreateLogicalRouter(lr); err != nil {
×
1201
                klog.Errorf("create logical router %s failed: %v", lr, err)
×
1202
                return err
1203
        }
×
1204

1205
        vpcRouter, err := c.OVNNbClient.GetLogicalRouter(lr, false)
1206
        if err != nil {
1207
                klog.Errorf("get logical router %s failed: %v", lr, err)
×
1208
                return err
×
1209
        }
×
UNCOV
1210

×
1211
        vpcRouter.Options = map[string]string{"always_learn_from_arp_request": "false", "dynamic_neigh_routers": "true", "mac_binding_age_threshold": "300"}
×
1212
        err = c.OVNNbClient.UpdateLogicalRouter(vpcRouter, &vpcRouter.Options)
1213
        if err != nil {
×
1214
                klog.Errorf("update logical router %s failed: %v", lr, err)
×
1215
                return err
×
1216
        }
×
1217
        return nil
×
1218
}
UNCOV
1219

×
UNCOV
1220
// deleteVpcRouter delete router to connect logical switches in vpc
×
1221
func (c *Controller) deleteVpcRouter(lr string) error {
×
1222
        return c.OVNNbClient.DeleteLogicalRouter(lr)
×
1223
}
×
UNCOV
1224

×
1225
func (c *Controller) handleAddVpcExternalSubnet(key, subnet string) error {
×
1226
        cachedSubnet, err := c.subnetsLister.Get(subnet)
1227
        if err != nil {
1228
                klog.Error(err)
1229
                return err
×
1230
        }
×
1231
        lrpEipName := fmt.Sprintf("%s-%s", key, subnet)
×
1232
        cachedEip, err := c.ovnEipsLister.Get(lrpEipName)
1233
        var needCreateEip bool
×
1234
        if err != nil {
×
1235
                if !k8serrors.IsNotFound(err) {
×
1236
                        klog.Error(err)
×
1237
                        return err
×
1238
                }
×
1239
                needCreateEip = true
×
UNCOV
1240
        }
×
1241
        var v4ip, v6ip, mac string
×
1242
        klog.V(3).Infof("create vpc lrp eip %s", lrpEipName)
×
1243
        if needCreateEip {
×
1244
                if v4ip, v6ip, mac, err = c.acquireIPAddress(subnet, lrpEipName, lrpEipName); err != nil {
×
1245
                        klog.Errorf("failed to acquire ip address for lrp eip %s, %v", lrpEipName, err)
×
1246
                        return err
×
1247
                }
×
1248
                if err := c.createOrUpdateOvnEipCR(lrpEipName, subnet, v4ip, v6ip, mac, util.OvnEipTypeLRP); err != nil {
1249
                        klog.Errorf("failed to create ovn eip for lrp %s: %v", lrpEipName, err)
×
1250
                        return err
×
1251
                }
×
1252
        } else {
×
1253
                v4ip = cachedEip.Spec.V4Ip
×
1254
                mac = cachedEip.Spec.MacAddress
×
1255
        }
×
1256
        if v4ip == "" || mac == "" {
×
1257
                err := fmt.Errorf("lrp '%s' ip or mac should not be empty", lrpEipName)
×
1258
                klog.Error(err)
×
1259
                return err
×
1260
        }
×
UNCOV
1261
        // init lrp gw chassis group
×
1262
        chassises := []string{}
×
1263
        sel, _ := metav1.LabelSelectorAsSelector(&metav1.LabelSelector{MatchLabels: map[string]string{util.ExGatewayLabel: "true"}})
×
1264
        gwNodes, err := c.nodesLister.List(sel)
×
1265
        if err != nil {
×
1266
                klog.Errorf("failed to list external gw nodes, %v", err)
×
1267
                return err
×
1268
        }
×
1269
        for _, gwNode := range gwNodes {
1270
                annoChassisName := gwNode.Annotations[util.ChassisAnnotation]
×
1271
                if annoChassisName == "" {
×
1272
                        err := fmt.Errorf("node %s has no chassis annotation, kube-ovn-cni not ready", gwNode.Name)
×
1273
                        klog.Error(err)
×
1274
                        return err
×
1275
                }
×
1276
                klog.Infof("get node %s chassis: %s", gwNode.Name, annoChassisName)
×
1277
                chassis, err := c.OVNSbClient.GetChassis(annoChassisName, false)
×
1278
                if err != nil {
×
1279
                        klog.Errorf("failed to get node %s chassis: %s, %v", gwNode.Name, annoChassisName, err)
×
1280
                        return err
×
1281
                }
×
1282
                chassises = append(chassises, chassis.Name)
×
UNCOV
1283
        }
×
UNCOV
1284

×
1285
        if len(chassises) == 0 {
×
1286
                err := errors.New("no external gw nodes")
×
1287
                klog.Error(err)
×
1288
                return err
×
1289
        }
×
UNCOV
1290

×
1291
        v4ipCidr, err := util.GetIPAddrWithMask(v4ip, cachedSubnet.Spec.CIDRBlock)
1292
        if err != nil {
1293
                klog.Error(err)
×
1294
                return err
×
1295
        }
×
1296
        lspName := fmt.Sprintf("%s-%s", subnet, key)
×
1297
        lrpName := fmt.Sprintf("%s-%s", key, subnet)
×
1298

1299
        if err := c.OVNNbClient.CreateLogicalPatchPort(subnet, key, lspName, lrpName, v4ipCidr, mac, chassises...); err != nil {
×
1300
                klog.Errorf("failed to connect router '%s' to external: %v", key, err)
×
1301
                return err
×
1302
        }
×
UNCOV
1303

×
1304
        cachedVpc, err := c.vpcsLister.Get(key)
×
1305
        if err != nil {
×
1306
                if k8serrors.IsNotFound(err) {
×
1307
                        return nil
×
1308
                }
×
1309
                klog.Errorf("failed to get vpc %s, %v", key, err)
×
1310
                return err
×
1311
        }
1312
        if subnet == c.config.ExternalGatewaySwitch {
×
1313
                vpc := cachedVpc.DeepCopy()
×
1314
                vpc.Status.EnableExternal = cachedVpc.Spec.EnableExternal
×
1315
                bytes, err := vpc.Status.Bytes()
×
1316
                if err != nil {
×
1317
                        klog.Errorf("failed to marshal vpc status: %v", err)
×
1318
                        return err
×
1319
                }
1320
                if _, err = c.config.KubeOvnClient.KubeovnV1().Vpcs().Patch(context.Background(),
×
1321
                        vpc.Name, types.MergePatchType, bytes, metav1.PatchOptions{}, "status"); err != nil {
×
1322
                        err := fmt.Errorf("failed to patch vpc %s status, %w", vpc.Name, err)
×
1323
                        klog.Error(err)
×
1324
                        return err
×
1325
                }
×
UNCOV
1326
        }
×
1327
        if _, err = c.ovnEipsLister.Get(lrpEipName); err != nil {
×
1328
                err := fmt.Errorf("failed to get ovn eip %s, %w", lrpEipName, err)
×
1329
                klog.Error(err)
×
1330
                return err
×
1331
        }
×
1332
        return nil
×
UNCOV
1333
}
×
1334

1335
func (c *Controller) handleDeleteVpcStaticRoute(key string) error {
×
1336
        vpc, err := c.vpcsLister.Get(key)
×
1337
        if err != nil {
×
1338
                if k8serrors.IsNotFound(err) {
×
1339
                        return nil
×
1340
                }
×
1341
                klog.Errorf("failed to get vpc %s, %v", key, err)
1342
                return err
UNCOV
1343
        }
×
1344
        needUpdate := false
×
1345
        newStaticRoutes := make([]*kubeovnv1.StaticRoute, 0, len(vpc.Spec.StaticRoutes))
×
1346
        for _, route := range vpc.Spec.StaticRoutes {
×
1347
                if route.ECMPMode != util.StaticRouteBfdEcmp {
×
1348
                        newStaticRoutes = append(newStaticRoutes, route)
×
1349
                        needUpdate = true
×
1350
                }
×
1351
        }
UNCOV
1352
        // keep non ecmp bfd routes
×
1353
        vpc.Spec.StaticRoutes = newStaticRoutes
×
1354
        if needUpdate {
×
1355
                if _, err = c.config.KubeOvnClient.KubeovnV1().Vpcs().Update(context.Background(), vpc, metav1.UpdateOptions{}); err != nil {
×
1356
                        klog.Errorf("failed to update vpc spec static route %s, %v", vpc.Name, err)
×
1357
                        return err
×
1358
                }
×
1359
        }
1360
        if err = c.patchVpcBfdStatus(vpc.Name); err != nil {
1361
                klog.Errorf("failed to patch vpc %s, %v", vpc.Name, err)
×
1362
                return err
×
1363
        }
×
1364
        return nil
×
UNCOV
1365
}
×
UNCOV
1366

×
1367
func (c *Controller) handleDelVpcExternalSubnet(key, subnet string) error {
1368
        lspName := fmt.Sprintf("%s-%s", subnet, key)
×
1369
        lrpName := fmt.Sprintf("%s-%s", key, subnet)
×
1370
        klog.V(3).Infof("delete vpc lrp %s", lrpName)
×
1371
        if err := c.OVNNbClient.RemoveLogicalPatchPort(lspName, lrpName); err != nil {
×
1372
                klog.Errorf("failed to disconnect router '%s' to external, %v", key, err)
×
1373
                return err
1374
        }
UNCOV
1375

×
1376
        if err := c.config.KubeOvnClient.KubeovnV1().OvnEips().Delete(context.Background(), lrpName, metav1.DeleteOptions{}); err != nil {
×
1377
                if !k8serrors.IsNotFound(err) {
×
1378
                        klog.Errorf("failed to delete ovn eip %s, %v", lrpName, err)
×
1379
                        return err
×
1380
                }
×
UNCOV
1381
        }
×
1382
        if err := c.OVNNbClient.DeleteBFDByDstIP(lrpName, ""); err != nil {
×
1383
                klog.Error(err)
1384
                return err
×
1385
        }
×
1386
        cachedVpc, err := c.vpcsLister.Get(key)
×
1387
        if err != nil {
×
1388
                if k8serrors.IsNotFound(err) {
×
1389
                        return nil
1390
                }
×
1391
                klog.Errorf("failed to get vpc %s, %v", key, err)
×
1392
                return err
×
UNCOV
1393
        }
×
1394
        if subnet == c.config.ExternalGatewaySwitch {
×
1395
                vpc := cachedVpc.DeepCopy()
×
1396
                vpc.Status.EnableExternal = cachedVpc.Spec.EnableExternal
×
1397
                vpc.Status.EnableBfd = cachedVpc.Spec.EnableBfd
×
1398
                bytes, err := vpc.Status.Bytes()
×
1399
                if err != nil {
×
1400
                        klog.Errorf("failed to marshal vpc status: %v", err)
×
1401
                        return err
1402
                }
×
1403
                if _, err = c.config.KubeOvnClient.KubeovnV1().Vpcs().Patch(context.Background(),
×
1404
                        vpc.Name, types.MergePatchType, bytes, metav1.PatchOptions{}, "status"); err != nil {
×
1405
                        if k8serrors.IsNotFound(err) {
×
1406
                                return nil
×
1407
                        }
×
1408
                        klog.Errorf("failed to patch vpc %s, %v", key, err)
×
1409
                        return err
×
UNCOV
1410
                }
×
UNCOV
1411
        }
×
1412
        return nil
×
UNCOV
1413
}
×
UNCOV
1414

×
1415
func (c *Controller) patchVpcBfdStatus(key string) error {
×
1416
        cachedVpc, err := c.vpcsLister.Get(key)
×
1417
        if err != nil {
×
1418
                if k8serrors.IsNotFound(err) {
1419
                        return nil
1420
                }
×
1421
                klog.Errorf("failed to get vpc %s, %v", key, err)
1422
                return err
UNCOV
1423
        }
×
UNCOV
1424

×
1425
        if cachedVpc.Status.EnableBfd != cachedVpc.Spec.EnableBfd {
×
1426
                status := cachedVpc.Status.DeepCopy()
×
1427
                status.EnableExternal = cachedVpc.Spec.EnableExternal
×
1428
                status.EnableBfd = cachedVpc.Spec.EnableBfd
×
1429
                bytes, err := status.Bytes()
×
1430
                if err != nil {
×
1431
                        klog.Errorf("failed to marshal vpc status: %v", err)
1432
                        return err
1433
                }
×
1434
                if _, err = c.config.KubeOvnClient.KubeovnV1().Vpcs().Patch(context.Background(),
×
1435
                        cachedVpc.Name, types.MergePatchType, bytes, metav1.PatchOptions{}, "status"); err != nil {
×
1436
                        klog.Error(err)
×
1437
                        return err
×
1438
                }
×
UNCOV
1439
        }
×
1440
        return nil
×
UNCOV
1441
}
×
UNCOV
1442

×
1443
func (c *Controller) getRouteTablesByVpc(vpc *kubeovnv1.Vpc) map[string][]*kubeovnv1.StaticRoute {
×
1444
        rtbs := make(map[string][]*kubeovnv1.StaticRoute)
×
1445
        for _, route := range vpc.Spec.StaticRoutes {
×
1446
                rtbs[route.RouteTable] = append(rtbs[route.RouteTable], route)
×
1447
        }
1448
        return rtbs
×
1449
}
1450

1451
func (c *Controller) updateVpcAddExternalStatus(key string, addExternalStatus bool) error {
×
1452
        cachedVpc, err := c.vpcsLister.Get(key)
×
1453
        if err != nil {
×
1454
                klog.Errorf("failed to get vpc %s, %v", key, err)
×
1455
                return err
×
1456
        }
×
1457
        vpc := cachedVpc.DeepCopy()
1458
        if addExternalStatus && vpc.Spec.ExtraExternalSubnets != nil {
1459
                sort.Strings(vpc.Spec.ExtraExternalSubnets)
×
1460
                vpc.Status.ExtraExternalSubnets = vpc.Spec.ExtraExternalSubnets
×
1461
        } else {
×
1462
                vpc.Status.ExtraExternalSubnets = nil
×
1463
        }
×
1464
        bytes, err := vpc.Status.Bytes()
×
1465
        if err != nil {
×
1466
                klog.Errorf("failed to get vpc bytes, %v", err)
×
1467
                return err
×
1468
        }
×
1469
        if _, err = c.config.KubeOvnClient.KubeovnV1().Vpcs().Patch(context.Background(),
×
1470
                vpc.Name, types.MergePatchType, bytes, metav1.PatchOptions{}, "status"); err != nil {
×
1471
                klog.Errorf("failed to patch vpc %s, %v", key, err)
×
1472
                return err
×
1473
        }
×
UNCOV
1474

×
1475
        return nil
×
UNCOV
1476
}
×
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