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

kubeovn / kube-ovn / 22316478569

23 Feb 2026 05:05PM UTC coverage: 23.069% (+0.01%) from 23.057%
22316478569

Pull #6323

github

oilbeater
fix: avoid OVN northd "No path for static route" warning on startup

During controller startup, default gateway static routes (0.0.0.0/0 and
::/0) were added to the OVN logical router before the join subnet's
Logical Router Port was created, causing OVN northd to warn about
unreachable next hops.

This fix ensures routes are only added after the join subnet's OVN
Logical Switch and LRP exist:

1. Remove addNodeGatewayStaticRoute() call from syncNodeRoutes() which
   runs during init phase before any OVN logical switches are created.

2. Add LogicalSwitchExists check in handleAddOrUpdateVpc() to requeue
   the VPC if the join subnet's logical switch is not yet ready, avoiding
   the race between VPC worker and Subnet worker.

3. Remove redundant addNodeGatewayStaticRoute() call from handleAddNode()
   since handleAddOrUpdateVpc() already manages default route reconciliation
   as part of VPC route diffing. Also remove the now-unused function.

Signed-off-by: Mengxin Liu <liumengxinfly@gmail.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Mengxin Liu <liumengxinfly@gmail.com>
Pull Request #6323: fix: avoid OVN northd "No path for static route" warning on startup

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

2 existing lines in 1 file now uncovered.

12533 of 54328 relevant lines covered (23.07%)

0.27 hits per line

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

16.25
/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
                        }
×
NEW
383
                        c.addOrUpdateVpcQueue.AddAfter(vpc.Name, 1*time.Second)
×
NEW
384
                        return nil
×
385
                }
386

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

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

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

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

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

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

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

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

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

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

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

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

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

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

663
        return nil
1✔
664
}
665

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

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

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

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

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

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

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

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

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

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

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

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

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

816
        nodes, err := c.nodesLister.List(selector)
×
817
        if err != nil {
×
818
                err = fmt.Errorf("failed to list nodes with selector %q: %w", vpc.Spec.BFDPort.NodeSelector, err)
×
819
                klog.Error(err)
×
820
                return portName, nil, err
×
821
        }
×
822
        if len(nodes) == 0 {
×
823
                err = fmt.Errorf("no nodes found by selector %q", selector.String())
×
824
                klog.Error(err)
×
825
                return portName, nil, err
×
826
        }
×
827

828
        nodeNames := make([]string, 0, len(nodes))
×
829
        chassisCount = min(chassisCount, len(nodes))
×
830
        chassisNames := make([]string, 0, chassisCount)
×
831
        for _, node := range nodes[:chassisCount] {
×
832
                chassis, err := c.OVNSbClient.GetChassisByHost(node.Name)
×
833
                if err != nil {
×
834
                        err = fmt.Errorf("failed to get chassis of node %s: %w", node.Name, err)
×
835
                        klog.Error(err)
×
836
                        return portName, nil, err
×
837
                }
×
838
                chassisNames = append(chassisNames, chassis.Name)
×
839
                nodeNames = append(nodeNames, node.Name)
×
840
        }
841

842
        networks := strings.Split(vpc.Spec.BFDPort.IP, ",")
×
843
        if err = c.OVNNbClient.CreateLogicalRouterPort(vpc.Name, portName, "", networks); err != nil {
×
844
                klog.Error(err)
×
845
                return portName, nil, err
×
846
        }
×
847
        if err = c.OVNNbClient.UpdateLogicalRouterPortNetworks(portName, networks); err != nil {
×
848
                klog.Error(err)
×
849
                return portName, nil, err
×
850
        }
×
851
        if err = c.OVNNbClient.UpdateLogicalRouterPortOptions(portName, map[string]string{"bfd-only": "true"}); err != nil {
×
852
                klog.Error(err)
×
853
                return portName, nil, err
×
854
        }
×
855
        if err = c.OVNNbClient.CreateHAChassisGroup(portName, chassisNames, map[string]string{"lrp": portName}); err != nil {
×
856
                klog.Error(err)
×
857
                return portName, nil, err
×
858
        }
×
859
        if err = c.OVNNbClient.SetLogicalRouterPortHAChassisGroup(portName, portName); err != nil {
×
860
                klog.Error(err)
×
861
                return portName, nil, err
×
862
        }
×
863

864
        return portName, nodeNames, nil
×
865
}
866

867
func (c *Controller) addPolicyRouteToVpc(vpcName string, policy *kubeovnv1.PolicyRoute, externalIDs map[string]string) error {
×
868
        var (
×
869
                nextHops []string
×
870
                err      error
×
871
        )
×
872

×
873
        if policy.NextHopIP != "" {
×
874
                nextHops = strings.Split(policy.NextHopIP, ",")
×
875
        }
×
876

877
        if err = c.OVNNbClient.AddLogicalRouterPolicy(vpcName, policy.Priority, policy.Match, string(policy.Action), nextHops, nil, externalIDs); err != nil {
×
878
                klog.Errorf("add policy route to vpc %s failed, %v", vpcName, err)
×
879
                return err
×
880
        }
×
881
        return nil
×
882
}
883

884
func buildExternalIDsMapKey(match, action string, priority int) string {
×
885
        return fmt.Sprintf("%s-%s-%d", match, action, priority)
×
886
}
×
887

888
func (c *Controller) batchAddPolicyRouteToVpc(name string, policies []*kubeovnv1.PolicyRoute, externalIDs map[string]map[string]string) error {
×
889
        if len(policies) == 0 {
×
890
                return nil
×
891
        }
×
892
        start := time.Now()
×
893
        routerPolicies := make([]*ovnnb.LogicalRouterPolicy, 0, len(policies))
×
894
        for _, policy := range policies {
×
895
                var nextHops []string
×
896
                if policy.NextHopIP != "" {
×
897
                        nextHops = strings.Split(policy.NextHopIP, ",")
×
898
                }
×
899
                routerPolicies = append(routerPolicies, &ovnnb.LogicalRouterPolicy{
×
900
                        Priority:    policy.Priority,
×
901
                        Nexthops:    nextHops,
×
902
                        Action:      string(policy.Action),
×
903
                        Match:       policy.Match,
×
904
                        ExternalIDs: externalIDs[buildExternalIDsMapKey(policy.Match, string(policy.Action), policy.Priority)],
×
905
                })
×
906
        }
907

908
        if err := c.OVNNbClient.BatchAddLogicalRouterPolicy(name, routerPolicies...); err != nil {
×
909
                klog.Errorf("batch add policy route to vpc %s failed, %v", name, err)
×
910
                return err
×
911
        }
×
912
        klog.Infof("take to %v batch add policy route to vpc %s policies %d", time.Since(start), name, len(policies))
×
913
        return nil
×
914
}
915

916
func (c *Controller) deletePolicyRouteFromVpc(vpcName string, priority int, match string) error {
×
917
        if err := c.OVNNbClient.DeleteLogicalRouterPolicy(vpcName, priority, match); err != nil {
×
918
                klog.Error(err)
×
919
                return err
×
920
        }
×
921
        return nil
×
922
}
923

924
func (c *Controller) batchDeletePolicyRouteFromVpc(name string, policies []*kubeovnv1.PolicyRoute) error {
×
925
        start := time.Now()
×
926
        routerPolicies := make([]*ovnnb.LogicalRouterPolicy, 0, len(policies))
×
927
        for _, policy := range policies {
×
928
                routerPolicies = append(routerPolicies, &ovnnb.LogicalRouterPolicy{
×
929
                        Priority: policy.Priority,
×
930
                        Match:    policy.Match,
×
931
                })
×
932
        }
×
933

934
        if err := c.OVNNbClient.BatchDeleteLogicalRouterPolicy(name, routerPolicies); err != nil {
×
935
                return err
×
936
        }
×
937
        klog.V(3).Infof("take to %v batch delete policy route from vpc %s policies %d", time.Since(start), name, len(policies))
×
938
        return nil
×
939
}
940

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

963
func (c *Controller) deleteStaticRouteFromVpc(name, table, cidr, nextHop string, policy kubeovnv1.RoutePolicy) error {
×
964
        var (
×
965
                policyStr string
×
966
                err       error
×
967
        )
×
968

×
969
        policyStr = convertPolicy(policy)
×
970
        if err = c.OVNNbClient.DeleteLogicalRouterStaticRoute(name, &table, &policyStr, cidr, nextHop); err != nil {
×
971
                klog.Errorf("del vpc %s static route failed, %v", name, err)
×
972
                return err
×
973
        }
×
974

975
        return nil
×
976
}
977

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

1000
func diffPolicyRouteWithExisted(exists, target []*kubeovnv1.PolicyRoute) ([]*kubeovnv1.PolicyRoute, []*kubeovnv1.PolicyRoute) {
×
1001
        var (
×
1002
                dels, adds []*kubeovnv1.PolicyRoute
×
1003
                existsMap  map[string]*kubeovnv1.PolicyRoute
×
1004
                key        string
×
1005
                ok         bool
×
1006
        )
×
1007

×
1008
        existsMap = make(map[string]*kubeovnv1.PolicyRoute, len(exists))
×
1009
        for _, item := range exists {
×
1010
                existsMap[getPolicyRouteItemKey(item)] = item
×
1011
        }
×
1012
        // load policies to add
1013
        for _, item := range target {
×
1014
                key = getPolicyRouteItemKey(item)
×
1015

×
1016
                if _, ok = existsMap[key]; ok {
×
1017
                        delete(existsMap, key)
×
1018
                } else {
×
1019
                        adds = append(adds, item)
×
1020
                }
×
1021
        }
1022
        // load policies to delete
1023
        for _, item := range existsMap {
×
1024
                dels = append(dels, item)
×
1025
        }
×
1026
        return dels, adds
×
1027
}
1028

1029
func diffPolicyRouteWithLogical(exists []*ovnnb.LogicalRouterPolicy, target []*kubeovnv1.PolicyRoute) ([]*kubeovnv1.PolicyRoute, []*kubeovnv1.PolicyRoute) {
1✔
1030
        var (
1✔
1031
                dels, adds []*kubeovnv1.PolicyRoute
1✔
1032
                existsMap  map[string]*kubeovnv1.PolicyRoute
1✔
1033
                key        string
1✔
1034
                ok         bool
1✔
1035
        )
1✔
1036
        existsMap = make(map[string]*kubeovnv1.PolicyRoute, len(exists))
1✔
1037

1✔
1038
        for _, item := range exists {
1✔
1039
                if item.ExternalIDs["vpc-egress-gateway"] != "" || item.ExternalIDs["subnet"] != "" ||
×
1040
                        item.ExternalIDs["isU2ORoutePolicy"] == "true" {
×
1041
                        continue
×
1042
                }
1043
                policy := &kubeovnv1.PolicyRoute{
×
1044
                        Priority: item.Priority,
×
1045
                        Match:    item.Match,
×
1046
                        Action:   kubeovnv1.PolicyRouteAction(item.Action),
×
1047
                }
×
1048
                existsMap[getPolicyRouteItemKey(policy)] = policy
×
1049
        }
1050

1051
        for _, item := range target {
1✔
1052
                key = getPolicyRouteItemKey(item)
×
1053

×
1054
                if _, ok = existsMap[key]; ok {
×
1055
                        delete(existsMap, key)
×
1056
                } else {
×
1057
                        adds = append(adds, item)
×
1058
                }
×
1059
        }
1060

1061
        for _, item := range existsMap {
1✔
1062
                dels = append(dels, item)
×
1063
        }
×
1064
        return dels, adds
1✔
1065
}
1066

1067
func getPolicyRouteItemKey(item *kubeovnv1.PolicyRoute) (key string) {
×
1068
        return fmt.Sprintf("%d:%s:%s:%s", item.Priority, item.Match, item.Action, item.NextHopIP)
×
1069
}
×
1070

1071
func diffStaticRoute(exist []*ovnnb.LogicalRouterStaticRoute, target []*kubeovnv1.StaticRoute) (routeNeedDel, routeNeedAdd []*kubeovnv1.StaticRoute) {
1✔
1072
        existRouteMap := make(map[string]*kubeovnv1.StaticRoute, len(exist))
1✔
1073
        for _, item := range exist {
2✔
1074
                policy := kubeovnv1.PolicyDst
1✔
1075
                if item.Policy != nil && *item.Policy == ovnnb.LogicalRouterStaticRoutePolicySrcIP {
2✔
1076
                        policy = kubeovnv1.PolicySrc
1✔
1077
                }
1✔
1078
                route := &kubeovnv1.StaticRoute{
1✔
1079
                        Policy:     policy,
1✔
1080
                        CIDR:       item.IPPrefix,
1✔
1081
                        NextHopIP:  item.Nexthop,
1✔
1082
                        RouteTable: item.RouteTable,
1✔
1083
                        ECMPMode:   util.StaticRouteBfdEcmp,
1✔
1084
                }
1✔
1085
                if item.BFD != nil {
1✔
1086
                        route.BfdID = *item.BFD
×
1087
                }
×
1088
                existRouteMap[getStaticRouteItemKey(route)] = route
1✔
1089
        }
1090

1091
        for _, item := range target {
2✔
1092
                key := getStaticRouteItemKey(item)
1✔
1093
                if _, ok := existRouteMap[key]; ok {
2✔
1094
                        delete(existRouteMap, key)
1✔
1095
                } else {
2✔
1096
                        routeNeedAdd = append(routeNeedAdd, item)
1✔
1097
                }
1✔
1098
        }
1099
        for _, item := range existRouteMap {
2✔
1100
                routeNeedDel = append(routeNeedDel, item)
1✔
1101
        }
1✔
1102
        return routeNeedDel, routeNeedAdd
1✔
1103
}
1104

1105
func getStaticRouteItemKey(item *kubeovnv1.StaticRoute) string {
1✔
1106
        var key string
1✔
1107
        if item.Policy == kubeovnv1.PolicyDst {
2✔
1108
                key = fmt.Sprintf("%s:dst:%s=>%s", item.RouteTable, item.CIDR, item.NextHopIP)
1✔
1109
        } else {
2✔
1110
                key = fmt.Sprintf("%s:src:%s=>%s", item.RouteTable, item.CIDR, item.NextHopIP)
1✔
1111
        }
1✔
1112
        return key
1✔
1113
}
1114

1115
func (c *Controller) formatVpc(vpc *kubeovnv1.Vpc) (*kubeovnv1.Vpc, error) {
1✔
1116
        var changed bool
1✔
1117
        for _, item := range vpc.Spec.StaticRoutes {
2✔
1118
                // check policy
1✔
1119
                if item.Policy == "" {
1✔
1120
                        item.Policy = kubeovnv1.PolicyDst
×
1121
                        changed = true
×
1122
                }
×
1123
                if item.Policy != kubeovnv1.PolicyDst && item.Policy != kubeovnv1.PolicySrc {
1✔
1124
                        return nil, fmt.Errorf("unknown policy type: %q", item.Policy)
×
1125
                }
×
1126
                // check cidr
1127
                if strings.Contains(item.CIDR, "/") {
2✔
1128
                        if _, _, err := net.ParseCIDR(item.CIDR); err != nil {
1✔
1129
                                return nil, fmt.Errorf("invalid cidr %q: %w", item.CIDR, err)
×
1130
                        }
×
1131
                } else if ip := net.ParseIP(item.CIDR); ip == nil {
×
1132
                        return nil, fmt.Errorf("invalid ip %q", item.CIDR)
×
1133
                }
×
1134
                // check next hop ip
1135
                if ip := net.ParseIP(item.NextHopIP); ip == nil {
1✔
1136
                        return nil, fmt.Errorf("invalid next hop ip %q", item.NextHopIP)
×
1137
                }
×
1138
        }
1139

1140
        for _, route := range vpc.Spec.PolicyRoutes {
1✔
1141
                if route.Action != kubeovnv1.PolicyRouteActionReroute {
×
1142
                        if route.NextHopIP != "" {
×
1143
                                route.NextHopIP = ""
×
1144
                                changed = true
×
1145
                        }
×
1146
                } else {
×
1147
                        // ecmp policy route may reroute to multiple next hop ips
×
1148
                        for ipStr := range strings.SplitSeq(route.NextHopIP, ",") {
×
1149
                                if ip := net.ParseIP(ipStr); ip == nil {
×
1150
                                        err := fmt.Errorf("invalid next hop ips: %s", route.NextHopIP)
×
1151
                                        klog.Error(err)
×
1152
                                        return nil, err
×
1153
                                }
×
1154
                        }
1155
                }
1156
        }
1157

1158
        if vpc.DeletionTimestamp.IsZero() && !slices.Contains(vpc.GetFinalizers(), util.KubeOVNControllerFinalizer) {
2✔
1159
                controllerutil.AddFinalizer(vpc, util.KubeOVNControllerFinalizer)
1✔
1160
                changed = true
1✔
1161
        }
1✔
1162

1163
        if !vpc.DeletionTimestamp.IsZero() && len(vpc.Status.Subnets) == 0 {
1✔
1164
                controllerutil.RemoveFinalizer(vpc, util.KubeOVNControllerFinalizer)
×
1165
                changed = true
×
1166
        }
×
1167

1168
        if changed {
2✔
1169
                newVpc, err := c.config.KubeOvnClient.KubeovnV1().Vpcs().Update(context.Background(), vpc, metav1.UpdateOptions{})
1✔
1170
                if err != nil {
1✔
1171
                        klog.Errorf("failed to update vpc %s: %v", vpc.Name, err)
×
1172
                        return nil, err
×
1173
                }
×
1174
                return newVpc, nil
1✔
1175
        }
1176

1177
        return vpc, nil
×
1178
}
1179

1180
func convertPolicies(list []*kubeovnv1.PolicyRoute) string {
×
1181
        if list == nil {
×
1182
                return ""
×
1183
        }
×
1184

1185
        var (
×
1186
                res []byte
×
1187
                err error
×
1188
        )
×
1189

×
1190
        if res, err = json.Marshal(list); err != nil {
×
1191
                klog.Errorf("failed to serialize policy routes %v , reason : %v", list, err)
×
1192
                return ""
×
1193
        }
×
1194
        return string(res)
×
1195
}
1196

1197
func reversePolicies(origin string) []*kubeovnv1.PolicyRoute {
×
1198
        if origin == "" {
×
1199
                return nil
×
1200
        }
×
1201

1202
        var (
×
1203
                list []*kubeovnv1.PolicyRoute
×
1204
                err  error
×
1205
        )
×
1206

×
1207
        if err = json.Unmarshal([]byte(origin), &list); err != nil {
×
1208
                klog.Errorf("failed to deserialize policy routes %v , reason : %v", list, err)
×
1209
                return nil
×
1210
        }
×
1211
        return list
×
1212
}
1213

1214
func convertPolicy(origin kubeovnv1.RoutePolicy) string {
1✔
1215
        if origin == kubeovnv1.PolicyDst {
2✔
1216
                return ovnnb.LogicalRouterStaticRoutePolicyDstIP
1✔
1217
        }
1✔
1218
        return ovnnb.LogicalRouterStaticRoutePolicySrcIP
1✔
1219
}
1220

1221
func reversePolicy(origin ovnnb.LogicalRouterStaticRoutePolicy) kubeovnv1.RoutePolicy {
×
1222
        if origin == ovnnb.LogicalRouterStaticRoutePolicyDstIP {
×
1223
                return kubeovnv1.PolicyDst
×
1224
        }
×
1225
        return kubeovnv1.PolicySrc
×
1226
}
1227

1228
func (c *Controller) getVpcSubnets(vpc *kubeovnv1.Vpc) (subnets []string, defaultSubnet string, err error) {
1✔
1229
        subnets = []string{}
1✔
1230
        allSubnets, err := c.subnetsLister.List(labels.Everything())
1✔
1231
        if err != nil {
1✔
1232
                klog.Error(err)
×
1233
                return nil, "", err
×
1234
        }
×
1235

1236
        for _, subnet := range allSubnets {
1✔
1237
                if subnet.Spec.Vpc != vpc.Name || !subnet.DeletionTimestamp.IsZero() || !isOvnSubnet(subnet) {
×
1238
                        continue
×
1239
                }
1240

1241
                subnets = append(subnets, subnet.Name)
×
1242
                if subnet.Spec.Default {
×
1243
                        defaultSubnet = subnet.Name
×
1244
                }
×
1245

1246
                if vpc.Name != util.DefaultVpc && vpc.Spec.DefaultSubnet != "" && vpc.Spec.DefaultSubnet == subnet.Name {
×
1247
                        defaultSubnet = vpc.Spec.DefaultSubnet
×
1248
                }
×
1249
        }
1250
        sort.Strings(subnets)
1✔
1251
        return subnets, defaultSubnet, err
1✔
1252
}
1253

1254
// createVpcRouter create router to connect logical switches in vpc
1255
func (c *Controller) createVpcRouter(lr string, learnFromARPRequest bool) error {
1✔
1256
        if err := c.OVNNbClient.CreateLogicalRouter(lr); err != nil {
1✔
1257
                klog.Errorf("create logical router %s failed: %v", lr, err)
×
1258
                return err
×
1259
        }
×
1260

1261
        vpcRouter, err := c.OVNNbClient.GetLogicalRouter(lr, false)
1✔
1262
        if err != nil {
1✔
1263
                klog.Errorf("get logical router %s failed: %v", lr, err)
×
1264
                return err
×
1265
        }
×
1266

1267
        lrOptions := map[string]string{
1✔
1268
                "mac_binding_age_threshold": "300",
1✔
1269
                "dynamic_neigh_routers":     "true",
1✔
1270
        }
1✔
1271
        if !learnFromARPRequest {
2✔
1272
                lrOptions["always_learn_from_arp_request"] = "false"
1✔
1273
        }
1✔
1274
        if !maps.Equal(vpcRouter.Options, lrOptions) {
2✔
1275
                vpcRouter.Options = lrOptions
1✔
1276
                if err = c.OVNNbClient.UpdateLogicalRouter(vpcRouter, &vpcRouter.Options); err != nil {
1✔
1277
                        klog.Errorf("failed to update options of logical router %s: %v", lr, err)
×
1278
                        return err
×
1279
                }
×
1280
        }
1281

1282
        return nil
1✔
1283
}
1284

1285
// deleteVpcRouter delete router to connect logical switches in vpc
1286
func (c *Controller) deleteVpcRouter(lr string) error {
×
1287
        return c.OVNNbClient.DeleteLogicalRouter(lr)
×
1288
}
×
1289

1290
func (c *Controller) handleAddVpcExternalSubnet(key, subnet string) error {
×
1291
        cachedSubnet, err := c.subnetsLister.Get(subnet)
×
1292
        if err != nil {
×
1293
                klog.Error(err)
×
1294
                return err
×
1295
        }
×
1296
        lrpEipName := fmt.Sprintf("%s-%s", key, subnet)
×
1297
        cachedEip, err := c.ovnEipsLister.Get(lrpEipName)
×
1298
        var needCreateEip bool
×
1299
        if err != nil {
×
1300
                if !k8serrors.IsNotFound(err) {
×
1301
                        klog.Error(err)
×
1302
                        return err
×
1303
                }
×
1304
                needCreateEip = true
×
1305
        }
1306
        var v4ip, v6ip, mac string
×
1307
        klog.V(3).Infof("create vpc lrp eip %s", lrpEipName)
×
1308
        if needCreateEip {
×
1309
                if v4ip, v6ip, mac, err = c.acquireIPAddress(subnet, lrpEipName, lrpEipName); err != nil {
×
1310
                        klog.Errorf("failed to acquire ip address for lrp eip %s, %v", lrpEipName, err)
×
1311
                        return err
×
1312
                }
×
1313
                if err := c.createOrUpdateOvnEipCR(lrpEipName, subnet, v4ip, v6ip, mac, util.OvnEipTypeLRP); err != nil {
×
1314
                        klog.Errorf("failed to create ovn eip for lrp %s: %v", lrpEipName, err)
×
1315
                        return err
×
1316
                }
×
1317
        } else {
×
1318
                v4ip = cachedEip.Spec.V4Ip
×
1319
                mac = cachedEip.Spec.MacAddress
×
1320
        }
×
1321
        if v4ip == "" || mac == "" {
×
1322
                err := fmt.Errorf("lrp '%s' ip or mac should not be empty", lrpEipName)
×
1323
                klog.Error(err)
×
1324
                return err
×
1325
        }
×
1326
        // init lrp gw chassis group
1327
        chassises := []string{}
×
1328
        gwNodes, err := c.nodesLister.List(externalGatewayNodeSelector)
×
1329
        if err != nil {
×
1330
                klog.Errorf("failed to list external gw nodes, %v", err)
×
1331
                return err
×
1332
        }
×
1333
        for _, gwNode := range gwNodes {
×
1334
                annoChassisName := gwNode.Annotations[util.ChassisAnnotation]
×
1335
                if annoChassisName == "" {
×
1336
                        err := fmt.Errorf("node %s has no chassis annotation, kube-ovn-cni not ready", gwNode.Name)
×
1337
                        klog.Error(err)
×
1338
                        return err
×
1339
                }
×
1340
                klog.Infof("get node %s chassis: %s", gwNode.Name, annoChassisName)
×
1341
                chassis, err := c.OVNSbClient.GetChassis(annoChassisName, false)
×
1342
                if err != nil {
×
1343
                        klog.Errorf("failed to get node %s chassis: %s, %v", gwNode.Name, annoChassisName, err)
×
1344
                        return err
×
1345
                }
×
1346
                chassises = append(chassises, chassis.Name)
×
1347
        }
1348

1349
        if len(chassises) == 0 {
×
1350
                err := errors.New("no external gw nodes")
×
1351
                klog.Error(err)
×
1352
                return err
×
1353
        }
×
1354

1355
        v4ipCidr, err := util.GetIPAddrWithMask(v4ip, cachedSubnet.Spec.CIDRBlock)
×
1356
        if err != nil {
×
1357
                klog.Error(err)
×
1358
                return err
×
1359
        }
×
1360
        lspName := fmt.Sprintf("%s-%s", subnet, key)
×
1361
        lrpName := fmt.Sprintf("%s-%s", key, subnet)
×
1362

×
1363
        if err := c.OVNNbClient.CreateLogicalPatchPort(subnet, key, lspName, lrpName, v4ipCidr, mac, chassises...); err != nil {
×
1364
                klog.Errorf("failed to connect router '%s' to external: %v", key, err)
×
1365
                return err
×
1366
        }
×
1367
        return nil
×
1368
}
1369

1370
func (c *Controller) handleDeleteVpcStaticRoute(key string) error {
×
1371
        vpc, err := c.vpcsLister.Get(key)
×
1372
        if err != nil {
×
1373
                if k8serrors.IsNotFound(err) {
×
1374
                        return nil
×
1375
                }
×
1376
                klog.Errorf("failed to get vpc %s, %v", key, err)
×
1377
                return err
×
1378
        }
1379
        needUpdate := false
×
1380
        newStaticRoutes := make([]*kubeovnv1.StaticRoute, 0, len(vpc.Spec.StaticRoutes))
×
1381
        for _, route := range vpc.Spec.StaticRoutes {
×
1382
                if route.ECMPMode == util.StaticRouteBfdEcmp {
×
1383
                        needUpdate = true
×
1384
                        continue
×
1385
                }
1386
                newStaticRoutes = append(newStaticRoutes, route)
×
1387
        }
1388
        // keep routes except bfd ecmp routes
1389
        if needUpdate {
×
1390
                vpc.Spec.StaticRoutes = newStaticRoutes
×
1391
                if _, err = c.config.KubeOvnClient.KubeovnV1().Vpcs().Update(context.Background(), vpc, metav1.UpdateOptions{}); err != nil {
×
1392
                        klog.Errorf("failed to update vpc spec static route %s, %v", vpc.Name, err)
×
1393
                        return err
×
1394
                }
×
1395
        }
1396
        if err = c.patchVpcBfdStatus(vpc.Name); err != nil {
×
1397
                klog.Errorf("failed to patch vpc %s, %v", vpc.Name, err)
×
1398
                return err
×
1399
        }
×
1400
        return nil
×
1401
}
1402

1403
func (c *Controller) handleDelVpcExternalSubnet(key, subnet string) error {
×
1404
        lspName := fmt.Sprintf("%s-%s", subnet, key)
×
1405
        lrpName := fmt.Sprintf("%s-%s", key, subnet)
×
1406
        klog.Infof("delete vpc lrp %s", lrpName)
×
1407
        if err := c.OVNNbClient.RemoveLogicalPatchPort(lspName, lrpName); err != nil {
×
1408
                klog.Errorf("failed to disconnect router '%s' to external, %v", key, err)
×
1409
                return err
×
1410
        }
×
1411
        if err := c.config.KubeOvnClient.KubeovnV1().OvnEips().Delete(context.Background(), lrpName, metav1.DeleteOptions{}); err != nil {
×
1412
                if !k8serrors.IsNotFound(err) {
×
1413
                        klog.Errorf("failed to delete ovn eip %s, %v", lrpName, err)
×
1414
                        return err
×
1415
                }
×
1416
        }
1417
        if err := c.OVNNbClient.DeleteBFDByDstIP(lrpName, ""); err != nil {
×
1418
                klog.Error(err)
×
1419
                return err
×
1420
        }
×
1421
        return nil
×
1422
}
1423

1424
func (c *Controller) patchVpcBfdStatus(key string) error {
×
1425
        cachedVpc, err := c.vpcsLister.Get(key)
×
1426
        if err != nil {
×
1427
                if k8serrors.IsNotFound(err) {
×
1428
                        return nil
×
1429
                }
×
1430
                klog.Errorf("failed to get vpc %s, %v", key, err)
×
1431
                return err
×
1432
        }
1433

1434
        if cachedVpc.Status.EnableBfd != cachedVpc.Spec.EnableBfd {
×
1435
                status := cachedVpc.Status.DeepCopy()
×
1436
                status.EnableExternal = cachedVpc.Spec.EnableExternal
×
1437
                status.EnableBfd = cachedVpc.Spec.EnableBfd
×
1438
                bytes, err := status.Bytes()
×
1439
                if err != nil {
×
1440
                        klog.Errorf("failed to marshal vpc status: %v", err)
×
1441
                        return err
×
1442
                }
×
1443
                if _, err = c.config.KubeOvnClient.KubeovnV1().Vpcs().Patch(context.Background(),
×
1444
                        cachedVpc.Name, types.MergePatchType, bytes, metav1.PatchOptions{}, "status"); err != nil {
×
1445
                        klog.Error(err)
×
1446
                        return err
×
1447
                }
×
1448
        }
1449
        return nil
×
1450
}
1451

1452
func (c *Controller) getRouteTablesByVpc(vpc *kubeovnv1.Vpc) map[string][]*kubeovnv1.StaticRoute {
1✔
1453
        rtbs := make(map[string][]*kubeovnv1.StaticRoute)
1✔
1454
        for _, route := range vpc.Spec.StaticRoutes {
2✔
1455
                rtbs[route.RouteTable] = append(rtbs[route.RouteTable], route)
1✔
1456
        }
1✔
1457
        return rtbs
1✔
1458
}
1459

1460
func (c *Controller) updateVpcExternalStatus(key string) error {
×
1461
        cachedVpc, err := c.vpcsLister.Get(key)
×
1462
        if err != nil {
×
1463
                klog.Errorf("failed to get vpc %s, %v", key, err)
×
1464
                return err
×
1465
        }
×
1466
        vpc := cachedVpc.DeepCopy()
×
1467
        vpc.Status.EnableExternal = vpc.Spec.EnableExternal
×
1468
        vpc.Status.EnableBfd = vpc.Spec.EnableBfd
×
1469

×
1470
        if vpc.Spec.EnableExternal {
×
1471
                sort.Strings(vpc.Spec.ExtraExternalSubnets)
×
1472
                vpc.Status.ExtraExternalSubnets = vpc.Spec.ExtraExternalSubnets
×
1473
        } else {
×
1474
                vpc.Status.ExtraExternalSubnets = nil
×
1475
        }
×
1476

1477
        bytes, err := vpc.Status.Bytes()
×
1478
        if err != nil {
×
1479
                klog.Errorf("failed to get vpc bytes, %v", err)
×
1480
                return err
×
1481
        }
×
1482
        if _, err = c.config.KubeOvnClient.KubeovnV1().Vpcs().Patch(context.Background(),
×
1483
                vpc.Name, types.MergePatchType, bytes, metav1.PatchOptions{}, "status"); err != nil {
×
1484
                klog.Errorf("failed to patch vpc %s, %v", key, err)
×
1485
                return err
×
1486
        }
×
1487

1488
        return nil
×
1489
}
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