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

kubeovn / kube-ovn / 26731493191

01 Jun 2026 02:08AM UTC coverage: 25.104% (+0.1%) from 24.969%
26731493191

push

github

web-flow
fix(controller): skip in-pod cleanup when VPC NAT gateway CRD is deleted (#6784) (#6798)

Backport of #6784 to release-1.16.

The in-pod cleanup helpers (deleteEipInPod, delEipQoSInPod, deleteFipInPod,
deleteDnatInPod, deleteSnatInPod) used the NAT gateway pod as the termination
sentinel: a missing pod made delEipQoSInPod retry forever (EIP stuck) and made
the others silently skip cleanup even when the gateway was only being recreated.

Introduce the natGwDeleted helper that uses the VpcNatGateway CRD as the
sentinel: CRD gone -> return nil (nothing to clean up); CRD present but pod
absent -> return the error so the reconciler retries until the pod is ready
(logged at klog.V(4) to avoid noise during rolling updates).

Adapted from upstream commit f331bbb07: release-1.16 helpers take a single-arg
getNatGwPod(dp) (no namespace) and lack natGwNamespaceByName /
normalizeSnatInternalCIDR, so the changes were applied manually. Unit tests are
provided in a self-contained vpc_nat_gw_cleanup_test.go.

(cherry picked from commit f331bbb07)

Signed-off-by: Mengxin Liu <liumengxinfly@gmail.com>

63 of 82 new or added lines in 2 files covered. (76.83%)

17 existing lines in 1 file now uncovered.

14226 of 56669 relevant lines covered (25.1%)

0.29 hits per line

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

150
        return nil
×
151
}
152

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

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

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

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

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

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

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

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

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

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

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

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

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

269
        return vpcLbConfig, nil
×
270
}
271

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

662
        return nil
1✔
663
}
664

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

863
        return portName, nodeNames, nil
×
864
}
865

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

1✔
872
        if policy.NextHopIP != "" {
2✔
873
                nextHops = util.SplitTrimmed(policy.NextHopIP, ",")
1✔
874
        }
1✔
875

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

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

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

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

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

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

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

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

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

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

974
        return nil
×
975
}
976

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

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

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

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

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

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

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

1✔
1054
                if _, ok = existsMap[key]; ok {
2✔
1055
                        delete(existsMap, key)
1✔
1056
                } else {
2✔
1057
                        adds = append(adds, item)
1✔
1058
                }
1✔
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) {
1✔
1068
        return fmt.Sprintf("%d:%s:%s:%s", item.Priority, item.Match, item.Action, normalizePolicyRouteNextHops(item.NextHopIP))
1✔
1069
}
1✔
1070

1071
func getLogicalPolicyNextHopKey(item *ovnnb.LogicalRouterPolicy) string {
1✔
1072
        if len(item.Nexthops) > 0 {
2✔
1073
                return normalizePolicyRouteNextHops(strings.Join(item.Nexthops, ","))
1✔
1074
        }
1✔
1075
        if item.Nexthop != nil {
2✔
1076
                return normalizePolicyRouteNextHops(*item.Nexthop)
1✔
1077
        }
1✔
1078
        return ""
×
1079
}
1080

1081
func normalizePolicyRouteNextHops(nextHopIP string) string {
1✔
1082
        if nextHopIP == "" {
2✔
1083
                return ""
1✔
1084
        }
1✔
1085

1086
        nextHops := make([]string, 0)
1✔
1087
        for nextHop := range strings.SplitSeq(nextHopIP, ",") {
2✔
1088
                nextHop = strings.TrimSpace(nextHop)
1✔
1089
                if nextHop != "" {
2✔
1090
                        nextHops = append(nextHops, nextHop)
1✔
1091
                }
1✔
1092
        }
1093
        if len(nextHops) == 0 {
1✔
1094
                return ""
×
1095
        }
×
1096

1097
        slices.Sort(nextHops)
1✔
1098
        return strings.Join(nextHops, ",")
1✔
1099
}
1100

1101
func diffStaticRoute(exist []*ovnnb.LogicalRouterStaticRoute, target []*kubeovnv1.StaticRoute) (routeNeedDel, routeNeedAdd []*kubeovnv1.StaticRoute) {
1✔
1102
        existRouteMap := make(map[string]*kubeovnv1.StaticRoute, len(exist))
1✔
1103
        for _, item := range exist {
2✔
1104
                policy := kubeovnv1.PolicyDst
1✔
1105
                if item.Policy != nil && *item.Policy == ovnnb.LogicalRouterStaticRoutePolicySrcIP {
2✔
1106
                        policy = kubeovnv1.PolicySrc
1✔
1107
                }
1✔
1108
                route := &kubeovnv1.StaticRoute{
1✔
1109
                        Policy:     policy,
1✔
1110
                        CIDR:       item.IPPrefix,
1✔
1111
                        NextHopIP:  item.Nexthop,
1✔
1112
                        RouteTable: item.RouteTable,
1✔
1113
                        ECMPMode:   util.StaticRouteBfdEcmp,
1✔
1114
                }
1✔
1115
                if item.BFD != nil {
1✔
1116
                        route.BfdID = *item.BFD
×
1117
                }
×
1118
                existRouteMap[getStaticRouteItemKey(route)] = route
1✔
1119
        }
1120

1121
        for _, item := range target {
2✔
1122
                key := getStaticRouteItemKey(item)
1✔
1123
                if _, ok := existRouteMap[key]; ok {
2✔
1124
                        delete(existRouteMap, key)
1✔
1125
                } else {
2✔
1126
                        routeNeedAdd = append(routeNeedAdd, item)
1✔
1127
                }
1✔
1128
        }
1129
        for _, item := range existRouteMap {
2✔
1130
                routeNeedDel = append(routeNeedDel, item)
1✔
1131
        }
1✔
1132
        return routeNeedDel, routeNeedAdd
1✔
1133
}
1134

1135
func getStaticRouteItemKey(item *kubeovnv1.StaticRoute) string {
1✔
1136
        var key string
1✔
1137
        if item.Policy == kubeovnv1.PolicyDst {
2✔
1138
                key = fmt.Sprintf("%s:dst:%s=>%s", item.RouteTable, item.CIDR, item.NextHopIP)
1✔
1139
        } else {
2✔
1140
                key = fmt.Sprintf("%s:src:%s=>%s", item.RouteTable, item.CIDR, item.NextHopIP)
1✔
1141
        }
1✔
1142
        return key
1✔
1143
}
1144

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

1170
        for _, route := range vpc.Spec.PolicyRoutes {
2✔
1171
                if route.Action != kubeovnv1.PolicyRouteActionReroute {
2✔
1172
                        if route.NextHopIP != "" {
1✔
1173
                                route.NextHopIP = ""
×
1174
                                changed = true
×
1175
                        }
×
1176
                } else {
1✔
1177
                        // ecmp policy route may reroute to multiple next hop ips
1✔
1178
                        for ipStr := range strings.SplitSeq(route.NextHopIP, ",") {
2✔
1179
                                if ip := net.ParseIP(ipStr); ip == nil {
1✔
1180
                                        err := fmt.Errorf("invalid next hop ips: %s", route.NextHopIP)
×
1181
                                        klog.Error(err)
×
1182
                                        return nil, err
×
1183
                                }
×
1184
                        }
1185
                }
1186
        }
1187

1188
        if vpc.DeletionTimestamp.IsZero() && !slices.Contains(vpc.GetFinalizers(), util.KubeOVNControllerFinalizer) {
2✔
1189
                controllerutil.AddFinalizer(vpc, util.KubeOVNControllerFinalizer)
1✔
1190
                changed = true
1✔
1191
        }
1✔
1192

1193
        if !vpc.DeletionTimestamp.IsZero() && len(vpc.Status.Subnets) == 0 {
1✔
1194
                controllerutil.RemoveFinalizer(vpc, util.KubeOVNControllerFinalizer)
×
1195
                changed = true
×
1196
        }
×
1197

1198
        if changed {
2✔
1199
                newVpc, err := c.config.KubeOvnClient.KubeovnV1().Vpcs().Update(context.Background(), vpc, metav1.UpdateOptions{})
1✔
1200
                if err != nil {
1✔
1201
                        klog.Errorf("failed to update vpc %s: %v", vpc.Name, err)
×
1202
                        return nil, err
×
1203
                }
×
1204
                return newVpc, nil
1✔
1205
        }
1206

1207
        return vpc, nil
×
1208
}
1209

1210
func convertPolicies(list []*kubeovnv1.PolicyRoute) string {
×
1211
        if list == nil {
×
1212
                return ""
×
1213
        }
×
1214

1215
        var (
×
1216
                res []byte
×
1217
                err error
×
1218
        )
×
1219

×
1220
        if res, err = json.Marshal(list); err != nil {
×
1221
                klog.Errorf("failed to serialize policy routes %v , reason : %v", list, err)
×
1222
                return ""
×
1223
        }
×
1224
        return string(res)
×
1225
}
1226

1227
func reversePolicies(origin string) []*kubeovnv1.PolicyRoute {
×
1228
        if origin == "" {
×
1229
                return nil
×
1230
        }
×
1231

1232
        var (
×
1233
                list []*kubeovnv1.PolicyRoute
×
1234
                err  error
×
1235
        )
×
1236

×
1237
        if err = json.Unmarshal([]byte(origin), &list); err != nil {
×
1238
                klog.Errorf("failed to deserialize policy routes %v , reason : %v", list, err)
×
1239
                return nil
×
1240
        }
×
1241
        return list
×
1242
}
1243

1244
func convertPolicy(origin kubeovnv1.RoutePolicy) string {
1✔
1245
        if origin == kubeovnv1.PolicyDst {
2✔
1246
                return ovnnb.LogicalRouterStaticRoutePolicyDstIP
1✔
1247
        }
1✔
1248
        return ovnnb.LogicalRouterStaticRoutePolicySrcIP
1✔
1249
}
1250

1251
func reversePolicy(origin ovnnb.LogicalRouterStaticRoutePolicy) kubeovnv1.RoutePolicy {
×
1252
        if origin == ovnnb.LogicalRouterStaticRoutePolicyDstIP {
×
1253
                return kubeovnv1.PolicyDst
×
1254
        }
×
1255
        return kubeovnv1.PolicySrc
×
1256
}
1257

1258
func (c *Controller) getVpcSubnets(vpc *kubeovnv1.Vpc) (subnets []string, defaultSubnet string, err error) {
1✔
1259
        subnets = []string{}
1✔
1260
        allSubnets, err := c.subnetsLister.List(labels.Everything())
1✔
1261
        if err != nil {
1✔
1262
                klog.Error(err)
×
1263
                return nil, "", err
×
1264
        }
×
1265

1266
        for _, subnet := range allSubnets {
1✔
1267
                if subnet.Spec.Vpc != vpc.Name || !subnet.DeletionTimestamp.IsZero() || !isOvnSubnet(subnet) {
×
1268
                        continue
×
1269
                }
1270

1271
                subnets = append(subnets, subnet.Name)
×
1272
                if subnet.Spec.Default {
×
1273
                        defaultSubnet = subnet.Name
×
1274
                }
×
1275

1276
                if vpc.Name != util.DefaultVpc && vpc.Spec.DefaultSubnet != "" && vpc.Spec.DefaultSubnet == subnet.Name {
×
1277
                        defaultSubnet = vpc.Spec.DefaultSubnet
×
1278
                }
×
1279
        }
1280
        sort.Strings(subnets)
1✔
1281
        return subnets, defaultSubnet, err
1✔
1282
}
1283

1284
// createVpcRouter create router to connect logical switches in vpc
1285
func (c *Controller) createVpcRouter(lr string, learnFromARPRequest bool) error {
1✔
1286
        if err := c.OVNNbClient.CreateLogicalRouter(lr); err != nil {
1✔
1287
                klog.Errorf("create logical router %s failed: %v", lr, err)
×
1288
                return err
×
1289
        }
×
1290

1291
        vpcRouter, err := c.OVNNbClient.GetLogicalRouter(lr, false)
1✔
1292
        if err != nil {
1✔
1293
                klog.Errorf("get logical router %s failed: %v", lr, err)
×
1294
                return err
×
1295
        }
×
1296

1297
        lrOptions := map[string]string{
1✔
1298
                "mac_binding_age_threshold": "300",
1✔
1299
                "dynamic_neigh_routers":     "true",
1✔
1300
        }
1✔
1301
        if !learnFromARPRequest {
2✔
1302
                lrOptions["always_learn_from_arp_request"] = "false"
1✔
1303
        }
1✔
1304
        if !maps.Equal(vpcRouter.Options, lrOptions) {
2✔
1305
                vpcRouter.Options = lrOptions
1✔
1306
                if err = c.OVNNbClient.UpdateLogicalRouter(vpcRouter, &vpcRouter.Options); err != nil {
1✔
1307
                        klog.Errorf("failed to update options of logical router %s: %v", lr, err)
×
1308
                        return err
×
1309
                }
×
1310
        }
1311

1312
        return nil
1✔
1313
}
1314

1315
// deleteVpcRouter delete router to connect logical switches in vpc
1316
func (c *Controller) deleteVpcRouter(lr string) error {
×
1317
        return c.OVNNbClient.DeleteLogicalRouter(lr)
×
1318
}
×
1319

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

1380
        if len(chassises) == 0 {
×
1381
                err := errors.New("no external gw nodes")
×
1382
                klog.Error(err)
×
1383
                return err
×
UNCOV
1384
        }
×
1385

1386
        ipCidr, err := util.GetIPAddrWithMask(util.GetStringIP(v4ip, v6ip), cachedSubnet.Spec.CIDRBlock)
×
1387
        if err != nil {
×
1388
                klog.Error(err)
×
1389
                return err
×
1390
        }
×
1391
        lspName := fmt.Sprintf("%s-%s", subnet, key)
×
1392
        lrpName := fmt.Sprintf("%s-%s", key, subnet)
×
1393

×
1394
        if err := c.OVNNbClient.CreateLogicalPatchPort(subnet, key, lspName, lrpName, ipCidr, mac, chassises...); err != nil {
×
1395
                klog.Errorf("failed to connect router '%s' to external: %v", key, err)
×
1396
                return err
×
1397
        }
×
UNCOV
1398
        return nil
×
1399
}
1400

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

1434
func (c *Controller) handleDelVpcExternalSubnet(key, subnet string) error {
×
1435
        lspName := fmt.Sprintf("%s-%s", subnet, key)
×
1436
        lrpName := fmt.Sprintf("%s-%s", key, subnet)
×
1437
        klog.Infof("delete vpc lrp %s", lrpName)
×
1438
        if err := c.OVNNbClient.RemoveLogicalPatchPort(lspName, lrpName); err != nil {
×
1439
                klog.Errorf("failed to disconnect router '%s' to external, %v", key, err)
×
1440
                return err
×
1441
        }
×
1442
        if err := c.config.KubeOvnClient.KubeovnV1().OvnEips().Delete(context.Background(), lrpName, metav1.DeleteOptions{}); err != nil {
×
1443
                if !k8serrors.IsNotFound(err) {
×
1444
                        klog.Errorf("failed to delete ovn eip %s, %v", lrpName, err)
×
1445
                        return err
×
UNCOV
1446
                }
×
1447
        }
1448
        if err := c.OVNNbClient.DeleteBFDByDstIP(lrpName, ""); err != nil {
×
1449
                klog.Error(err)
×
1450
                return err
×
1451
        }
×
UNCOV
1452
        return nil
×
1453
}
1454

1455
func (c *Controller) patchVpcBfdStatus(key string) error {
×
1456
        cachedVpc, err := c.vpcsLister.Get(key)
×
1457
        if err != nil {
×
1458
                if k8serrors.IsNotFound(err) {
×
1459
                        return nil
×
1460
                }
×
1461
                klog.Errorf("failed to get vpc %s, %v", key, err)
×
UNCOV
1462
                return err
×
1463
        }
1464

1465
        if cachedVpc.Status.EnableBfd != cachedVpc.Spec.EnableBfd {
×
1466
                status := cachedVpc.Status.DeepCopy()
×
1467
                status.EnableExternal = cachedVpc.Spec.EnableExternal
×
1468
                status.EnableBfd = cachedVpc.Spec.EnableBfd
×
1469
                bytes, err := status.Bytes()
×
1470
                if err != nil {
×
1471
                        klog.Errorf("failed to marshal vpc status: %v", err)
×
1472
                        return err
×
1473
                }
×
1474
                if _, err = c.config.KubeOvnClient.KubeovnV1().Vpcs().Patch(context.Background(),
×
1475
                        cachedVpc.Name, types.MergePatchType, bytes, metav1.PatchOptions{}, "status"); err != nil {
×
1476
                        klog.Error(err)
×
1477
                        return err
×
UNCOV
1478
                }
×
1479
        }
UNCOV
1480
        return nil
×
1481
}
1482

1483
func (c *Controller) getRouteTablesByVpc(vpc *kubeovnv1.Vpc) map[string][]*kubeovnv1.StaticRoute {
1✔
1484
        rtbs := make(map[string][]*kubeovnv1.StaticRoute)
1✔
1485
        for _, route := range vpc.Spec.StaticRoutes {
2✔
1486
                rtbs[route.RouteTable] = append(rtbs[route.RouteTable], route)
1✔
1487
        }
1✔
1488
        return rtbs
1✔
1489
}
1490

1491
func (c *Controller) updateVpcExternalStatus(key string) error {
×
1492
        cachedVpc, err := c.vpcsLister.Get(key)
×
1493
        if err != nil {
×
1494
                klog.Errorf("failed to get vpc %s, %v", key, err)
×
1495
                return err
×
1496
        }
×
1497
        vpc := cachedVpc.DeepCopy()
×
1498
        vpc.Status.EnableExternal = vpc.Spec.EnableExternal
×
1499
        vpc.Status.EnableBfd = vpc.Spec.EnableBfd
×
1500

×
1501
        if vpc.Spec.EnableExternal {
×
1502
                sort.Strings(vpc.Spec.ExtraExternalSubnets)
×
1503
                vpc.Status.ExtraExternalSubnets = vpc.Spec.ExtraExternalSubnets
×
1504
        } else {
×
1505
                vpc.Status.ExtraExternalSubnets = nil
×
UNCOV
1506
        }
×
1507

1508
        bytes, err := vpc.Status.Bytes()
×
1509
        if err != nil {
×
1510
                klog.Errorf("failed to get vpc bytes, %v", err)
×
1511
                return err
×
1512
        }
×
1513
        if _, err = c.config.KubeOvnClient.KubeovnV1().Vpcs().Patch(context.Background(),
×
1514
                vpc.Name, types.MergePatchType, bytes, metav1.PatchOptions{}, "status"); err != nil {
×
1515
                klog.Errorf("failed to patch vpc %s, %v", key, err)
×
1516
                return err
×
UNCOV
1517
        }
×
1518

UNCOV
1519
        return nil
×
1520
}
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