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

kubeovn / kube-ovn / 26074435105

19 May 2026 03:30AM UTC coverage: 21.533% (+0.3%) from 21.235%
26074435105

push

github

web-flow
feat: backport u2o overlay only routing to release-1.14 (#6750)

* feat: backport u2o overlay only routing

Backport ca6231a54 to release-1.14 without the underlay e2e changes.

Signed-off-by: clyi <clyi@alauda.io>

* fix: sync subnet crd in install script

Signed-off-by: clyi <clyi@alauda.io>

---------

Signed-off-by: clyi <clyi@alauda.io>

132 of 184 new or added lines in 2 files covered. (71.74%)

3 existing lines in 2 files now uncovered.

10828 of 50286 relevant lines covered (21.53%)

0.25 hits per line

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

12.29
/pkg/controller/subnet.go
1
package controller
2

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

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

28
        kubeovnv1 "github.com/kubeovn/kube-ovn/pkg/apis/kubeovn/v1"
29
        "github.com/kubeovn/kube-ovn/pkg/ipam"
30
        "github.com/kubeovn/kube-ovn/pkg/ovs"
31
        "github.com/kubeovn/kube-ovn/pkg/ovsdb/ovnnb"
32
        "github.com/kubeovn/kube-ovn/pkg/util"
33
)
34

35
func (c *Controller) enqueueAddSubnet(obj any) {
×
36
        key := cache.MetaObjectToName(obj.(*kubeovnv1.Subnet)).String()
×
37
        klog.V(3).Infof("enqueue add subnet %s", key)
×
38
        c.addOrUpdateSubnetQueue.Add(key)
×
39
}
×
40

41
func (c *Controller) enqueueDeleteSubnet(obj any) {
×
42
        var subnet *kubeovnv1.Subnet
×
43
        switch t := obj.(type) {
×
44
        case *kubeovnv1.Subnet:
×
45
                subnet = t
×
46
        case cache.DeletedFinalStateUnknown:
×
47
                s, ok := t.Obj.(*kubeovnv1.Subnet)
×
48
                if !ok {
×
49
                        klog.Warningf("unexpected object type: %T", t.Obj)
×
50
                        return
×
51
                }
×
52
                subnet = s
×
53
        default:
×
54
                klog.Warningf("unexpected type: %T", obj)
×
55
                return
×
56
        }
57

58
        klog.V(3).Infof("enqueue delete subnet %s", subnet.Name)
×
59
        c.deleteSubnetQueue.Add(subnet)
×
60
}
61

62
func u2oOverlayOnlyRoutingEnabled(subnet *kubeovnv1.Subnet) bool {
1✔
63
        return subnet != nil && subnet.Spec.U2OFeatures.OverlayOnlyRouting
1✔
64
}
1✔
65

66
func (c *Controller) enqueueUpdateSubnet(oldObj, newObj any) {
×
67
        var usingIPs float64
×
68
        var u2oInterconnIP string
×
69
        oldSubnet := oldObj.(*kubeovnv1.Subnet)
×
70
        newSubnet := newObj.(*kubeovnv1.Subnet)
×
71
        key := cache.MetaObjectToName(newSubnet).String()
×
72

×
73
        // Trigger network policy refresh only if they are enabled, otherwise the lister will be nil
×
74
        if c.npsLister != nil {
×
75
                if newSubnet.Spec.Gateway != oldSubnet.Spec.Gateway || newSubnet.Status.U2OInterconnectionIP != oldSubnet.Status.U2OInterconnectionIP {
×
76
                        policies, err := c.npsLister.List(labels.Everything())
×
77
                        if err != nil {
×
78
                                klog.Errorf("failed to list network policies: %v", err)
×
79
                        } else {
×
80
                                for _, np := range policies {
×
81
                                        c.enqueueAddNp(np)
×
82
                                }
×
83
                        }
84
                }
85
        }
86

87
        if newSubnet.Spec.Protocol == kubeovnv1.ProtocolIPv6 {
×
88
                usingIPs = newSubnet.Status.V6UsingIPs
×
89
        } else {
×
90
                usingIPs = newSubnet.Status.V4UsingIPs
×
91
        }
×
92

93
        u2oInterconnIP = newSubnet.Status.U2OInterconnectionIP
×
94
        if !newSubnet.DeletionTimestamp.IsZero() && (usingIPs == 0 || (usingIPs == 1 && u2oInterconnIP != "")) {
×
95
                c.addOrUpdateSubnetQueue.Add(key)
×
96
                return
×
97
        }
×
98

99
        if oldSubnet.Spec.Vpc != newSubnet.Spec.Vpc &&
×
100
                ((oldSubnet.Spec.Vpc != "" || newSubnet.Spec.Vpc != c.config.ClusterRouter) && (oldSubnet.Spec.Vpc != c.config.ClusterRouter || newSubnet.Spec.Vpc != "")) {
×
101
                // recode last vpc name for subnet
×
102
                if oldSubnet.Spec.Vpc == "" {
×
103
                        c.subnetLastVpcNameMap.Store(newSubnet.Name, c.config.ClusterRouter)
×
104
                } else {
×
105
                        c.subnetLastVpcNameMap.Store(newSubnet.Name, oldSubnet.Spec.Vpc)
×
106
                }
×
107

108
                c.updateVpcStatusQueue.Add(oldSubnet.Spec.Vpc)
×
109
        }
110

111
        if oldSubnet.Spec.U2OInterconnection != newSubnet.Spec.U2OInterconnection {
×
112
                klog.Infof("enqueue update vpc %s triggered by u2o interconnection change of subnet %s", newSubnet.Spec.Vpc, key)
×
113
                c.addOrUpdateVpcQueue.Add(newSubnet.Spec.Vpc)
×
114
        }
×
115

116
        if oldSubnet.Spec.Private != newSubnet.Spec.Private ||
×
117
                oldSubnet.Spec.CIDRBlock != newSubnet.Spec.CIDRBlock ||
×
118
                !slices.Equal(oldSubnet.Spec.AllowSubnets, newSubnet.Spec.AllowSubnets) ||
×
119
                !slices.Equal(oldSubnet.Spec.Namespaces, newSubnet.Spec.Namespaces) ||
×
120
                oldSubnet.Spec.GatewayType != newSubnet.Spec.GatewayType ||
×
NEW
121
                !reflect.DeepEqual(oldSubnet.Spec.U2OFeatures, newSubnet.Spec.U2OFeatures) ||
×
122
                oldSubnet.Spec.GatewayNode != newSubnet.Spec.GatewayNode ||
×
123
                oldSubnet.Spec.LogicalGateway != newSubnet.Spec.LogicalGateway ||
×
124
                oldSubnet.Spec.Gateway != newSubnet.Spec.Gateway ||
×
125
                !slices.Equal(oldSubnet.Spec.ExcludeIps, newSubnet.Spec.ExcludeIps) ||
×
126
                !slices.Equal(oldSubnet.Spec.Vips, newSubnet.Spec.Vips) ||
×
127
                oldSubnet.Spec.Vlan != newSubnet.Spec.Vlan ||
×
128
                oldSubnet.Spec.EnableDHCP != newSubnet.Spec.EnableDHCP ||
×
129
                oldSubnet.Spec.DHCPv4Options != newSubnet.Spec.DHCPv4Options ||
×
130
                oldSubnet.Spec.DHCPv6Options != newSubnet.Spec.DHCPv6Options ||
×
131
                oldSubnet.Spec.EnableIPv6RA != newSubnet.Spec.EnableIPv6RA ||
×
132
                oldSubnet.Spec.IPv6RAConfigs != newSubnet.Spec.IPv6RAConfigs ||
×
133
                oldSubnet.Spec.Protocol != newSubnet.Spec.Protocol ||
×
134
                (oldSubnet.Spec.EnableLb == nil && newSubnet.Spec.EnableLb != nil) ||
×
135
                (oldSubnet.Spec.EnableLb != nil && newSubnet.Spec.EnableLb == nil) ||
×
136
                (oldSubnet.Spec.EnableLb != nil && newSubnet.Spec.EnableLb != nil && *oldSubnet.Spec.EnableLb != *newSubnet.Spec.EnableLb) ||
×
137
                oldSubnet.Spec.EnableEcmp != newSubnet.Spec.EnableEcmp ||
×
138
                !reflect.DeepEqual(oldSubnet.Spec.Acls, newSubnet.Spec.Acls) ||
×
139
                oldSubnet.Spec.U2OInterconnection != newSubnet.Spec.U2OInterconnection ||
×
140
                oldSubnet.Spec.RouteTable != newSubnet.Spec.RouteTable ||
×
141
                oldSubnet.Spec.Vpc != newSubnet.Spec.Vpc ||
×
142
                oldSubnet.Spec.NatOutgoing != newSubnet.Spec.NatOutgoing ||
×
143
                oldSubnet.Spec.EnableMulticastSnoop != newSubnet.Spec.EnableMulticastSnoop ||
×
144
                !reflect.DeepEqual(oldSubnet.Spec.NatOutgoingPolicyRules, newSubnet.Spec.NatOutgoingPolicyRules) ||
×
145
                !reflect.DeepEqual(oldSubnet.Spec.NamespaceSelectors, newSubnet.Spec.NamespaceSelectors) ||
×
146
                (newSubnet.Spec.U2OInterconnection && newSubnet.Spec.U2OInterconnectionIP != "" && oldSubnet.Spec.U2OInterconnectionIP != newSubnet.Spec.U2OInterconnectionIP) {
×
147
                klog.V(3).Infof("enqueue update subnet %s", key)
×
148

×
149
                if oldSubnet.Spec.GatewayType != newSubnet.Spec.GatewayType {
×
150
                        c.recorder.Eventf(newSubnet, v1.EventTypeNormal, "SubnetGatewayTypeChanged",
×
151
                                "subnet gateway type changes from %q to %q", oldSubnet.Spec.GatewayType, newSubnet.Spec.GatewayType)
×
152
                }
×
153

154
                if oldSubnet.Spec.GatewayNode != newSubnet.Spec.GatewayNode {
×
155
                        c.recorder.Eventf(newSubnet, v1.EventTypeNormal, "SubnetGatewayNodeChanged",
×
156
                                "gateway node changes from %q to %q", oldSubnet.Spec.GatewayNode, newSubnet.Spec.GatewayNode)
×
157
                }
×
158

159
                c.addOrUpdateSubnetQueue.Add(key)
×
160
        }
161
}
162

163
func (c *Controller) formatSubnet(subnet *kubeovnv1.Subnet) (*kubeovnv1.Subnet, error) {
1✔
164
        var (
1✔
165
                changed bool
1✔
166
                err     error
1✔
167
        )
1✔
168

1✔
169
        if changed, err = checkSubnetChanged(subnet); err != nil {
1✔
170
                klog.Error(err)
×
171
                return nil, err
×
172
        }
×
173

174
        if subnet.Spec.Provider == "" {
2✔
175
                subnet.Spec.Provider = util.OvnProvider
1✔
176
                changed = true
1✔
177
        }
1✔
178

179
        if subnet.Spec.Vpc == "" {
2✔
180
                if isOvnSubnet(subnet) {
2✔
181
                        subnet.Spec.Vpc = c.config.ClusterRouter
1✔
182
                        changed = true
1✔
183
                }
1✔
184
        }
185

186
        if subnet.Spec.Vpc == c.config.ClusterRouter && subnet.Name != c.config.NodeSwitch {
2✔
187
                // Some format only needed in the default VPC
1✔
188
                if subnet.Spec.GatewayType == "" {
2✔
189
                        subnet.Spec.GatewayType = kubeovnv1.GWDistributedType
1✔
190
                        changed = true
1✔
191
                }
1✔
192
                if subnet.Spec.Default && subnet.Name != c.config.DefaultLogicalSwitch {
1✔
193
                        subnet.Spec.Default = false
×
194
                        changed = true
×
195
                }
×
196
        }
197

198
        if subnet.Spec.EnableLb == nil && subnet.Name != c.config.NodeSwitch {
2✔
199
                changed = true
1✔
200
                subnet.Spec.EnableLb = &c.config.EnableLb
1✔
201
        }
1✔
202
        // set join subnet Spec.EnableLb to nil
203
        if subnet.Spec.EnableLb != nil && subnet.Name == c.config.NodeSwitch {
1✔
204
                changed = true
×
205
                subnet.Spec.EnableLb = nil
×
206
        }
×
207

208
        if subnet.Spec.U2OInterconnectionIP != "" && !subnet.Spec.U2OInterconnection {
1✔
209
                subnet.Spec.U2OInterconnectionIP = ""
×
210
                changed = true
×
211
        }
×
212

213
        if subnet.Spec.Vlan == "" && subnet.Spec.U2OInterconnection {
1✔
214
                subnet.Spec.U2OInterconnection = false
×
215
                changed = true
×
216
        }
×
217

218
        klog.Infof("format subnet %v, changed %v", subnet.Name, changed)
1✔
219
        if changed {
2✔
220
                newSubnet, err := c.config.KubeOvnClient.KubeovnV1().Subnets().Update(context.Background(), subnet, metav1.UpdateOptions{})
1✔
221
                if err != nil {
1✔
222
                        klog.Errorf("failed to update subnet %s, %v", subnet.Name, err)
×
223
                        return nil, err
×
224
                }
×
225
                return newSubnet, nil
1✔
226
        }
227
        return subnet, nil
1✔
228
}
229

230
func (c *Controller) validateSubnetVlan(subnet *kubeovnv1.Subnet) error {
×
231
        if subnet.Spec.Vlan == "" {
×
232
                return nil
×
233
        }
×
234

235
        vlan, err := c.vlansLister.Get(subnet.Spec.Vlan)
×
236
        if err != nil {
×
237
                err = fmt.Errorf("failed to get vlan %s: %w", subnet.Spec.Vlan, err)
×
238
                klog.Error(err)
×
239
                return err
×
240
        }
×
241

242
        if vlan.Status.Conflict {
×
243
                err = fmt.Errorf("subnet %s has invalid conflict vlan %s", subnet.Name, vlan.Name)
×
244
                klog.Error(err)
×
245
                return err
×
246
        }
×
247
        return nil
×
248
}
249

250
func (c *Controller) updateNatOutgoingPolicyRulesStatus(subnet *kubeovnv1.Subnet) error {
×
251
        if subnet.Spec.NatOutgoing {
×
252
                subnet.Status.NatOutgoingPolicyRules = make([]kubeovnv1.NatOutgoingPolicyRuleStatus, len(subnet.Spec.NatOutgoingPolicyRules))
×
253
                for index, rule := range subnet.Spec.NatOutgoingPolicyRules {
×
254
                        jsonRule, err := json.Marshal(rule)
×
255
                        if err != nil {
×
256
                                klog.Error(err)
×
257
                                return err
×
258
                        }
×
259
                        priority := strconv.Itoa(index)
×
260
                        // hash code generate by subnetName, rule and priority
×
261
                        var retBytes []byte
×
262
                        retBytes = append(retBytes, []byte(subnet.Name)...)
×
263
                        retBytes = append(retBytes, []byte(priority)...)
×
264
                        retBytes = append(retBytes, jsonRule...)
×
265
                        result := util.Sha256Hash(retBytes)
×
266

×
267
                        subnet.Status.NatOutgoingPolicyRules[index].RuleID = result[:util.NatPolicyRuleIDLength]
×
268
                        subnet.Status.NatOutgoingPolicyRules[index].Match = rule.Match
×
269
                        subnet.Status.NatOutgoingPolicyRules[index].Action = rule.Action
×
270
                }
271
        } else {
×
272
                subnet.Status.NatOutgoingPolicyRules = []kubeovnv1.NatOutgoingPolicyRuleStatus{}
×
273
        }
×
274

275
        return nil
×
276
}
277

278
func checkSubnetChanged(subnet *kubeovnv1.Subnet) (bool, error) {
1✔
279
        var (
1✔
280
                changed, ret bool
1✔
281
                err          error
1✔
282
        )
1✔
283

1✔
284
        // changed value may be overlapped, so use ret to record value
1✔
285
        if changed, err = checkAndUpdateCIDR(subnet); err != nil {
1✔
286
                klog.Error(err)
×
287
                return changed, err
×
288
        }
×
289
        if changed {
2✔
290
                ret = true
1✔
291
        }
1✔
292

293
        if changed, err = checkAndUpdateGateway(subnet); err != nil {
1✔
294
                klog.Error(err)
×
295
                return changed, err
×
296
        }
×
297
        if changed {
2✔
298
                ret = true
1✔
299
        }
1✔
300

301
        if changed = checkAndUpdateExcludeIPs(subnet); changed {
2✔
302
                ret = true
1✔
303
        }
1✔
304

305
        if subnet.Spec.Protocol != util.CheckProtocol(subnet.Spec.CIDRBlock) {
2✔
306
                subnet.Spec.Protocol = util.CheckProtocol(subnet.Spec.CIDRBlock)
1✔
307
                ret = true
1✔
308
        }
1✔
309

310
        return ret, nil
1✔
311
}
312

313
func checkAndUpdateCIDR(subnet *kubeovnv1.Subnet) (bool, error) {
1✔
314
        var (
1✔
315
                changed    bool
1✔
316
                cidrBlocks []string
1✔
317
        )
1✔
318

1✔
319
        for cidr := range strings.SplitSeq(subnet.Spec.CIDRBlock, ",") {
2✔
320
                _, ipNet, err := net.ParseCIDR(cidr)
1✔
321
                if err != nil {
1✔
322
                        klog.Error(err)
×
323
                        return false, fmt.Errorf("subnet %s cidr %s is invalid", subnet.Name, cidr)
×
324
                }
×
325
                if ipNet.String() != cidr {
2✔
326
                        changed = true
1✔
327
                }
1✔
328
                cidrBlocks = append(cidrBlocks, ipNet.String())
1✔
329
        }
330
        subnet.Spec.CIDRBlock = strings.Join(cidrBlocks, ",")
1✔
331
        return changed, nil
1✔
332
}
333

334
func checkAndUpdateGateway(subnet *kubeovnv1.Subnet) (bool, error) {
1✔
335
        var (
1✔
336
                changed bool
1✔
337
                gw      string
1✔
338
                err     error
1✔
339
        )
1✔
340

1✔
341
        switch {
1✔
342
        case subnet.Spec.Gateway == "":
1✔
343
                gw, err = util.GetGwByCidr(subnet.Spec.CIDRBlock)
1✔
344
        case subnet.Spec.Protocol == kubeovnv1.ProtocolDual && util.CheckProtocol(subnet.Spec.Gateway) != util.CheckProtocol(subnet.Spec.CIDRBlock):
×
345
                gw, err = util.AppendGwByCidr(subnet.Spec.Gateway, subnet.Spec.CIDRBlock)
×
346
        default:
1✔
347
                gw = subnet.Spec.Gateway
1✔
348
        }
349
        if err != nil {
1✔
350
                klog.Error(err)
×
351
                return false, err
×
352
        }
×
353
        if subnet.Spec.Gateway != gw {
2✔
354
                subnet.Spec.Gateway = gw
1✔
355
                changed = true
1✔
356
        }
1✔
357

358
        return changed, nil
1✔
359
}
360

361
// this func must be called after subnet.Spec.Gateway is valued
362
func checkAndUpdateExcludeIPs(subnet *kubeovnv1.Subnet) bool {
1✔
363
        var (
1✔
364
                changed    bool
1✔
365
                excludeIPs []string
1✔
366
        )
1✔
367
        excludeIPs = append(excludeIPs, strings.Split(subnet.Spec.Gateway, ",")...)
1✔
368
        sort.Strings(excludeIPs)
1✔
369
        if len(subnet.Spec.ExcludeIps) == 0 {
2✔
370
                subnet.Spec.ExcludeIps = excludeIPs
1✔
371
                changed = true
1✔
372
        } else {
2✔
373
                changed = checkAndFormatsExcludeIPs(subnet)
1✔
374
                for _, gw := range excludeIPs {
2✔
375
                        gwExists := false
1✔
376
                        for _, excludeIP := range subnet.Spec.ExcludeIps {
2✔
377
                                if util.ContainsIPs(excludeIP, gw) {
2✔
378
                                        gwExists = true
1✔
379
                                        break
1✔
380
                                }
381
                        }
382
                        if !gwExists {
1✔
383
                                subnet.Spec.ExcludeIps = append(subnet.Spec.ExcludeIps, gw)
×
384
                                sort.Strings(subnet.Spec.ExcludeIps)
×
385
                                changed = true
×
386
                        }
×
387
                }
388
        }
389
        return changed
1✔
390
}
391

392
func (c *Controller) syncSubnetFinalizer(cl client.Client) error {
×
393
        // migrate depreciated finalizer to new finalizer
×
394
        subnets := &kubeovnv1.SubnetList{}
×
395
        return migrateFinalizers(cl, subnets, func(i int) (client.Object, client.Object) {
×
396
                if i < 0 || i >= len(subnets.Items) {
×
397
                        return nil, nil
×
398
                }
×
399
                return subnets.Items[i].DeepCopy(), subnets.Items[i].DeepCopy()
×
400
        })
401
}
402

403
func (c *Controller) handleSubnetFinalizer(subnet *kubeovnv1.Subnet) (*kubeovnv1.Subnet, bool, error) {
×
404
        if subnet.DeletionTimestamp.IsZero() && !slices.Contains(subnet.GetFinalizers(), util.KubeOVNControllerFinalizer) {
×
405
                newSubnet := subnet.DeepCopy()
×
406
                controllerutil.AddFinalizer(newSubnet, util.KubeOVNControllerFinalizer)
×
407
                patch, err := util.GenerateMergePatchPayload(subnet, newSubnet)
×
408
                if err != nil {
×
409
                        klog.Errorf("failed to generate patch payload for subnet '%s', %v", subnet.Name, err)
×
410
                        return newSubnet, false, err
×
411
                }
×
412
                patchSubnet, err := c.config.KubeOvnClient.KubeovnV1().Subnets().Patch(context.Background(), subnet.Name, types.MergePatchType, patch, metav1.PatchOptions{}, "")
×
413
                if err != nil {
×
414
                        klog.Errorf("failed to add finalizer to subnet %s, %v", subnet.Name, err)
×
415
                        return patchSubnet, false, err
×
416
                }
×
417
                // wait local cache ready
418
                time.Sleep(1 * time.Second)
×
419
                return patchSubnet, false, nil
×
420
        }
421

422
        usingIPs := subnet.Status.V4UsingIPs
×
423
        if util.CheckProtocol(subnet.Spec.CIDRBlock) == kubeovnv1.ProtocolIPv6 {
×
424
                usingIPs = subnet.Status.V6UsingIPs
×
425
        }
×
426

427
        u2oInterconnIP := subnet.Status.U2OInterconnectionIP
×
428
        if !subnet.DeletionTimestamp.IsZero() && (usingIPs == 0 || (usingIPs == 1 && u2oInterconnIP != "")) {
×
429
                newSubnet := subnet.DeepCopy()
×
430
                controllerutil.RemoveFinalizer(newSubnet, util.KubeOVNControllerFinalizer)
×
431
                patch, err := util.GenerateMergePatchPayload(subnet, newSubnet)
×
432
                if err != nil {
×
433
                        klog.Errorf("failed to generate patch payload for subnet '%s', %v", subnet.Name, err)
×
434
                        return newSubnet, false, err
×
435
                }
×
436
                if _, err := c.config.KubeOvnClient.KubeovnV1().Subnets().Patch(context.Background(), subnet.Name,
×
437
                        types.MergePatchType, patch, metav1.PatchOptions{}, ""); err != nil {
×
438
                        klog.Errorf("failed to remove finalizer from subnet %s, %v", subnet.Name, err)
×
439
                        return newSubnet, false, err
×
440
                }
×
441
                return newSubnet, true, nil
×
442
        }
443
        return subnet, false, nil
×
444
}
445

446
func (c Controller) patchSubnetStatus(subnet *kubeovnv1.Subnet, reason, errStr string) error {
×
447
        if errStr != "" {
×
448
                subnet.Status.SetError(reason, errStr)
×
449
                if reason == "ValidateLogicalSwitchFailed" {
×
450
                        subnet.Status.NotValidated(reason, errStr)
×
451
                } else {
×
452
                        subnet.Status.Validated(reason, "")
×
453
                }
×
454
                subnet.Status.NotReady(reason, errStr)
×
455
                c.recorder.Eventf(subnet, v1.EventTypeWarning, reason, errStr)
×
456
        } else {
×
457
                subnet.Status.Validated(reason, "")
×
458
                c.recorder.Eventf(subnet, v1.EventTypeNormal, reason, errStr)
×
459
                if reason == "SetPrivateLogicalSwitchSuccess" ||
×
460
                        reason == "ResetLogicalSwitchAclSuccess" ||
×
461
                        reason == "ReconcileCentralizedGatewaySuccess" ||
×
462
                        reason == "SetNonOvnSubnetSuccess" {
×
463
                        subnet.Status.Ready(reason, "")
×
464
                }
×
465
        }
466

467
        bytes, err := subnet.Status.Bytes()
×
468
        if err != nil {
×
469
                klog.Error(err)
×
470
                return err
×
471
        }
×
472
        if _, err := c.config.KubeOvnClient.KubeovnV1().Subnets().Patch(context.Background(), subnet.Name, types.MergePatchType, bytes, metav1.PatchOptions{}, "status"); err != nil {
×
473
                klog.Errorf("failed to patch status for subnet %s, %v", subnet.Name, err)
×
474
                return err
×
475
        }
×
476
        return nil
×
477
}
478

479
func (c *Controller) validateVpcBySubnet(subnet *kubeovnv1.Subnet) (*kubeovnv1.Vpc, error) {
×
480
        vpc, err := c.vpcsLister.Get(subnet.Spec.Vpc)
×
481
        if err != nil {
×
482
                klog.Errorf("failed to get subnet's vpc '%s', %v", subnet.Spec.Vpc, err)
×
483
                return vpc, err
×
484
        }
×
485

486
        if !vpc.Status.Standby {
×
487
                err = fmt.Errorf("the vpc '%s' not standby yet", vpc.Name)
×
488
                klog.Error(err)
×
489
                return vpc, err
×
490
        }
×
491

492
        if !vpc.Status.Default {
×
493
                for _, ns := range subnet.Spec.Namespaces {
×
494
                        if !slices.Contains(vpc.Spec.Namespaces, ns) {
×
495
                                err = fmt.Errorf("namespace '%s' is out of range to custom vpc '%s'", ns, vpc.Name)
×
496
                                klog.Error(err)
×
497
                                return vpc, err
×
498
                        }
×
499
                }
500
        } else {
×
501
                vpcs, err := c.vpcsLister.List(labels.Everything())
×
502
                if err != nil {
×
503
                        klog.Errorf("failed to list vpc, %v", err)
×
504
                        return vpc, err
×
505
                }
×
506
                lastVpcName, _ := c.subnetLastVpcNameMap.Load(subnet.Name)
×
507
                for _, vpc := range vpcs {
×
508
                        if (lastVpcName == "" && subnet.Spec.Vpc != vpc.Name || lastVpcName != "" && lastVpcName != vpc.Name) &&
×
509
                                !vpc.Status.Default && util.IsStringsOverlap(vpc.Spec.Namespaces, subnet.Spec.Namespaces) {
×
510
                                err = fmt.Errorf("namespaces %v are overlap with vpc '%s'", subnet.Spec.Namespaces, vpc.Name)
×
511
                                klog.Error(err)
×
512
                                return vpc, err
×
513
                        }
×
514
                }
515
        }
516
        return vpc, nil
×
517
}
518

519
func (c *Controller) checkSubnetConflict(subnet *kubeovnv1.Subnet) error {
×
520
        subnetList, err := c.subnetsLister.List(labels.Everything())
×
521
        if err != nil {
×
522
                klog.Errorf("failed to list subnets %v", err)
×
523
                return err
×
524
        }
×
525

526
        for _, sub := range subnetList {
×
527
                if sub.Spec.Vpc != subnet.Spec.Vpc || sub.Spec.Vlan != subnet.Spec.Vlan || sub.Name == subnet.Name {
×
528
                        continue
×
529
                }
530

531
                if util.CIDROverlap(sub.Spec.CIDRBlock, subnet.Spec.CIDRBlock) {
×
532
                        err = fmt.Errorf("subnet %s cidr %s is conflict with subnet %s cidr %s", subnet.Name, subnet.Spec.CIDRBlock, sub.Name, sub.Spec.CIDRBlock)
×
533
                        klog.Error(err)
×
534
                        if err = c.patchSubnetStatus(subnet, "ValidateLogicalSwitchFailed", err.Error()); err != nil {
×
535
                                klog.Error(err)
×
536
                                return err
×
537
                        }
×
538
                        return err
×
539
                }
540

541
                if subnet.Spec.ExternalEgressGateway != "" && sub.Spec.ExternalEgressGateway != "" &&
×
542
                        subnet.Spec.PolicyRoutingTableID == sub.Spec.PolicyRoutingTableID {
×
543
                        err = fmt.Errorf("subnet %s policy routing table ID %d is conflict with subnet %s policy routing table ID %d", subnet.Name, subnet.Spec.PolicyRoutingTableID, sub.Name, sub.Spec.PolicyRoutingTableID)
×
544
                        klog.Error(err)
×
545
                        if err = c.patchSubnetStatus(subnet, "ValidateLogicalSwitchFailed", err.Error()); err != nil {
×
546
                                klog.Error(err)
×
547
                                return err
×
548
                        }
×
549
                        return err
×
550
                }
551
        }
552

553
        if subnet.Spec.Vlan == "" && subnet.Spec.Vpc == c.config.ClusterRouter {
×
554
                nodes, err := c.nodesLister.List(labels.Everything())
×
555
                if err != nil {
×
556
                        klog.Errorf("failed to list nodes: %v", err)
×
557
                        return err
×
558
                }
×
559
                for _, node := range nodes {
×
560
                        for _, addr := range node.Status.Addresses {
×
561
                                if addr.Type == v1.NodeInternalIP && util.CIDRContainIP(subnet.Spec.CIDRBlock, addr.Address) {
×
562
                                        err = fmt.Errorf("subnet %s cidr %s conflict with node %s address %s", subnet.Name, subnet.Spec.CIDRBlock, node.Name, addr.Address)
×
563
                                        klog.Error(err)
×
564
                                        if err = c.patchSubnetStatus(subnet, "ValidateLogicalSwitchFailed", err.Error()); err != nil {
×
565
                                                klog.Error(err)
×
566
                                                return err
×
567
                                        }
×
568
                                        return err
×
569
                                }
570
                        }
571
                }
572
        }
573
        return nil
×
574
}
575

576
func (c *Controller) updateSubnetDHCPOption(subnet *kubeovnv1.Subnet, needRouter bool) error {
×
577
        var mtu int
×
578
        if subnet.Spec.Mtu > 0 {
×
579
                mtu = int(subnet.Spec.Mtu)
×
580
        } else {
×
581
                mtu = util.DefaultMTU
×
582
                if subnet.Spec.Vlan == "" {
×
583
                        switch c.config.NetworkType {
×
584
                        case util.NetworkTypeVlan:
×
585
                                // default to geneve
×
586
                                fallthrough
×
587
                        case util.NetworkTypeGeneve:
×
588
                                mtu -= util.GeneveHeaderLength
×
589
                        case util.NetworkTypeVxlan:
×
590
                                mtu -= util.VxlanHeaderLength
×
591
                        case util.NetworkTypeStt:
×
592
                                mtu -= util.SttHeaderLength
×
593
                        default:
×
594
                                return fmt.Errorf("invalid network type: %s", c.config.NetworkType)
×
595
                        }
596
                }
597
        }
598

599
        dhcpOptionsUUIDs, err := c.OVNNbClient.UpdateDHCPOptions(subnet, mtu)
×
600
        if err != nil {
×
601
                klog.Errorf("failed to update dhcp options for switch %s, %v", subnet.Name, err)
×
602
                return err
×
603
        }
×
604

605
        vpc, err := c.vpcsLister.Get(subnet.Spec.Vpc)
×
606
        if err != nil {
×
607
                klog.Errorf("failed to get subnet's vpc '%s', %v", subnet.Spec.Vpc, err)
×
608
                return err
×
609
        }
×
610

611
        if needRouter {
×
612
                lrpName := fmt.Sprintf("%s-%s", vpc.Status.Router, subnet.Name)
×
613
                if err := c.OVNNbClient.UpdateLogicalRouterPortRA(lrpName, subnet.Spec.IPv6RAConfigs, subnet.Spec.EnableIPv6RA); err != nil {
×
614
                        klog.Errorf("update ipv6 ra configs for logical router port %s, %v", lrpName, err)
×
615
                        return err
×
616
                }
×
617
        }
618

619
        if subnet.Status.DHCPv4OptionsUUID != dhcpOptionsUUIDs.DHCPv4OptionsUUID || subnet.Status.DHCPv6OptionsUUID != dhcpOptionsUUIDs.DHCPv6OptionsUUID {
×
620
                subnet.Status.DHCPv4OptionsUUID = dhcpOptionsUUIDs.DHCPv4OptionsUUID
×
621
                subnet.Status.DHCPv6OptionsUUID = dhcpOptionsUUIDs.DHCPv6OptionsUUID
×
622
                bytes, err := subnet.Status.Bytes()
×
623
                if err != nil {
×
624
                        klog.Error(err)
×
625
                        return err
×
626
                }
×
627
                if _, err := c.config.KubeOvnClient.KubeovnV1().Subnets().Patch(context.Background(), subnet.Name, types.MergePatchType, bytes, metav1.PatchOptions{}, "status"); err != nil {
×
628
                        klog.Errorf("patch subnet %s dhcp options failed: %v", subnet.Name, err)
×
629
                        return err
×
630
                }
×
631
        }
632

633
        return nil
×
634
}
635

636
func (c *Controller) handleAddOrUpdateSubnet(key string) error {
×
637
        c.subnetKeyMutex.LockKey(key)
×
638
        defer func() { _ = c.subnetKeyMutex.UnlockKey(key) }()
×
639

640
        cachedSubnet, err := c.subnetsLister.Get(key)
×
641
        if err != nil {
×
642
                if k8serrors.IsNotFound(err) {
×
643
                        return nil
×
644
                }
×
645
                klog.Error(err)
×
646
                return err
×
647
        }
648
        klog.V(3).Infof("handle add or update subnet %s", cachedSubnet.Name)
×
649
        subnet := cachedSubnet.DeepCopy()
×
650
        subnet, err = c.formatSubnet(subnet)
×
651
        if err != nil {
×
652
                err := fmt.Errorf("failed to format subnet %s, %w", key, err)
×
653
                klog.Error(err)
×
654
                return err
×
655
        }
×
656

657
        err = c.validateSubnetVlan(subnet)
×
658
        if err != nil {
×
659
                err := fmt.Errorf("failed to validate vlan for subnet %s, %w", key, err)
×
660
                klog.Error(err)
×
661
                if err = c.patchSubnetStatus(subnet, "ValidateSubnetVlanFailed", err.Error()); err != nil {
×
662
                        klog.Error(err)
×
663
                        return err
×
664
                }
×
665
                return err
×
666
        }
667

668
        if err = util.ValidateSubnet(*subnet); err != nil {
×
669
                klog.Errorf("failed to validate subnet %s, %v", subnet.Name, err)
×
670
                if err = c.patchSubnetStatus(subnet, "ValidateLogicalSwitchFailed", err.Error()); err != nil {
×
671
                        klog.Error(err)
×
672
                        return err
×
673
                }
×
674
                return err
×
675
        }
676
        if err = c.patchSubnetStatus(subnet, "ValidateLogicalSwitchSuccess", ""); err != nil {
×
677
                klog.Error(err)
×
678
                return err
×
679
        }
×
680

681
        if err := c.ipam.AddOrUpdateSubnet(subnet.Name, subnet.Spec.CIDRBlock, subnet.Spec.Gateway, subnet.Spec.ExcludeIps); err != nil {
×
682
                klog.Error(err)
×
683
                return err
×
684
        }
×
685

686
        // availableIPStr valued from ipam, so leave update subnet.status after ipam process
687
        if subnet.Spec.Protocol == kubeovnv1.ProtocolDual {
×
688
                subnet, err = c.calcDualSubnetStatusIP(subnet)
×
689
        } else {
×
690
                subnet, err = c.calcSubnetStatusIP(subnet)
×
691
        }
×
692
        if err != nil {
×
693
                klog.Errorf("calculate subnet %s used ip failed, %v", cachedSubnet.Name, err)
×
694
                return err
×
695
        }
×
696

697
        subnet, deleted, err := c.handleSubnetFinalizer(subnet)
×
698
        if err != nil {
×
699
                klog.Errorf("handle subnet finalizer failed %v", err)
×
700
                return err
×
701
        }
×
702
        if deleted {
×
703
                return nil
×
704
        }
×
705

706
        if !isOvnSubnet(subnet) {
×
707
                // subnet provider is not ovn, and vpc is empty, should not reconcile
×
708
                if err = c.patchSubnetStatus(subnet, "SetNonOvnSubnetSuccess", ""); err != nil {
×
709
                        klog.Error(err)
×
710
                        return err
×
711
                }
×
712

713
                subnet.Status.EnsureStandardConditions()
×
714
                klog.Infof("non ovn subnet %s is ready", subnet.Name)
×
715
                return nil
×
716
        }
717

718
        // This validate should be processed after isOvnSubnet, since maybe there's no vpc for subnet not managed by kube-ovn
719
        vpc, err := c.validateVpcBySubnet(subnet)
×
720
        if err != nil {
×
721
                klog.Errorf("failed to get subnet's vpc '%s', %v", subnet.Spec.Vpc, err)
×
722
                return err
×
723
        }
×
724
        _, isMcastQuerierChanged, err := c.reconcileSubnetSpecialIPs(subnet)
×
725
        if err != nil {
×
726
                klog.Errorf("failed to reconcile subnet %s Custom IPs %v", subnet.Name, err)
×
727
                return err
×
728
        }
×
729
        if err := c.checkSubnetConflict(subnet); err != nil {
×
730
                klog.Errorf("failed to check subnet %s, %v", subnet.Name, err)
×
731
                return err
×
732
        }
×
733

734
        needRouter := subnet.Spec.Vlan == "" || subnet.Spec.LogicalGateway ||
×
735
                (subnet.Status.U2OInterconnectionIP != "" && subnet.Spec.U2OInterconnection)
×
736
        // 1. overlay subnet, should add lrp, lrp ip is subnet gw
×
737
        // 2. underlay subnet use logical gw, should add lrp, lrp ip is subnet gw
×
738
        randomAllocateGW := !subnet.Spec.LogicalGateway && vpc.Spec.EnableExternal && subnet.Name == c.config.ExternalGatewaySwitch
×
739
        // 3. underlay subnet use physical gw, vpc has eip, lrp managed in vpc process, lrp ip is random allocation, not subnet gw
×
740

×
741
        gateway := subnet.Spec.Gateway
×
742
        var gatewayMAC string
×
743
        if subnet.Status.U2OInterconnectionIP != "" && subnet.Spec.U2OInterconnection {
×
744
                gateway = subnet.Status.U2OInterconnectionIP
×
745
                gatewayMAC = subnet.Status.U2OInterconnectionMAC
×
746
        }
×
747

748
        if err := c.clearOldU2OResource(subnet); err != nil {
×
749
                klog.Errorf("clear subnet %s old u2o resource failed: %v", subnet.Name, err)
×
750
                return err
×
751
        }
×
752

753
        // create or update logical switch
754
        if err := c.OVNNbClient.CreateLogicalSwitch(subnet.Name, vpc.Status.Router, subnet.Spec.CIDRBlock, gateway, gatewayMAC, needRouter, randomAllocateGW); err != nil {
×
755
                klog.Errorf("create logical switch %s: %v", subnet.Name, err)
×
756
                return err
×
757
        }
×
758

759
        // Record the gateway MAC in ipam if router port exists
760
        if needRouter {
×
761
                routerPortName := ovs.LogicalRouterPortName(vpc.Status.Router, subnet.Name)
×
762
                if lrp, err := c.OVNNbClient.GetLogicalRouterPort(routerPortName, true); err == nil && lrp != nil && lrp.MAC != "" {
×
763
                        if err := c.ipam.RecordGatewayMAC(subnet.Name, lrp.MAC); err != nil {
×
764
                                klog.Warningf("failed to record gateway MAC %s for subnet %s: %v", lrp.MAC, subnet.Name, err)
×
765
                        }
×
766
                } else {
×
767
                        klog.V(3).Infof("router port %s not found or has no MAC, skipping gateway MAC record", routerPortName)
×
768
                }
×
769
        }
770

771
        if isMcastQuerierChanged {
×
772
                if err := c.handleMcastQuerierChange(subnet); err != nil {
×
773
                        klog.Errorf("failed to handle mcast querier IP change for subnet %s: %v", subnet.Name, err)
×
774
                        return err
×
775
                }
×
776
        }
777

778
        subnet.Status.EnsureStandardConditions()
×
779

×
780
        if err := c.updateSubnetDHCPOption(subnet, needRouter); err != nil {
×
781
                klog.Errorf("failed to update subnet %s dhcpOptions: %v", subnet.Name, err)
×
782
                return err
×
783
        }
×
784

785
        if c.config.EnableLb && subnet.Name != c.config.NodeSwitch {
×
786
                lbs := []string{
×
787
                        vpc.Status.TCPLoadBalancer,
×
788
                        vpc.Status.TCPSessionLoadBalancer,
×
789
                        vpc.Status.UDPLoadBalancer,
×
790
                        vpc.Status.UDPSessionLoadBalancer,
×
791
                        vpc.Status.SctpLoadBalancer,
×
792
                        vpc.Status.SctpSessionLoadBalancer,
×
793
                }
×
794
                if subnet.Spec.EnableLb != nil && *subnet.Spec.EnableLb {
×
795
                        if err := c.OVNNbClient.LogicalSwitchUpdateLoadBalancers(subnet.Name, ovsdb.MutateOperationInsert, lbs...); err != nil {
×
796
                                if err = c.patchSubnetStatus(subnet, "AddLbToLogicalSwitchFailed", err.Error()); err != nil {
×
797
                                        klog.Error(err)
×
798
                                        return err
×
799
                                }
×
800
                                klog.Error(err)
×
801
                                return err
×
802
                        }
803
                } else {
×
804
                        if err := c.OVNNbClient.LogicalSwitchUpdateLoadBalancers(subnet.Name, ovsdb.MutateOperationDelete, lbs...); err != nil {
×
805
                                klog.Errorf("remove load-balancer from subnet %s failed: %v", subnet.Name, err)
×
806
                                return err
×
807
                        }
×
808
                }
809
        }
810

811
        if err := c.reconcileSubnet(subnet); err != nil {
×
812
                klog.Errorf("reconcile subnet for %s failed, %v", subnet.Name, err)
×
813
                return err
×
814
        }
×
815

816
        subnet.Status.U2OInterconnectionVPC = ""
×
817
        if subnet.Spec.U2OInterconnection {
×
818
                subnet.Status.U2OInterconnectionVPC = vpc.Status.Router
×
819
        }
×
820

821
        if err = c.updateNatOutgoingPolicyRulesStatus(subnet); err != nil {
×
822
                klog.Errorf("failed to update NAT outgoing policy status for subnet %s: %v", subnet.Name, err)
×
823
                return err
×
824
        }
×
825

826
        if subnet.Spec.Private {
×
827
                if err := c.OVNNbClient.SetLogicalSwitchPrivate(subnet.Name, subnet.Spec.CIDRBlock, c.config.NodeSwitchCIDR, subnet.Spec.AllowSubnets); err != nil {
×
828
                        if err = c.patchSubnetStatus(subnet, "SetPrivateLogicalSwitchFailed", err.Error()); err != nil {
×
829
                                klog.Error(err)
×
830
                                return err
×
831
                        }
×
832
                        klog.Error(err)
×
833
                        return err
×
834
                }
835

836
                if err = c.patchSubnetStatus(subnet, "SetPrivateLogicalSwitchSuccess", ""); err != nil {
×
837
                        klog.Error(err)
×
838
                        return err
×
839
                }
×
840
        } else {
×
841
                // clear acl when direction is ""
×
842
                if err = c.OVNNbClient.DeleteAcls(subnet.Name, logicalSwitchKey, "", nil); err != nil {
×
843
                        if err = c.patchSubnetStatus(subnet, "ResetLogicalSwitchAclFailed", err.Error()); err != nil {
×
844
                                klog.Error(err)
×
845
                                return err
×
846
                        }
×
847
                        klog.Error(err)
×
848
                        return err
×
849
                }
850

851
                if err = c.patchSubnetStatus(subnet, "ResetLogicalSwitchAclSuccess", ""); err != nil {
×
852
                        klog.Error(err)
×
853
                        return err
×
854
                }
×
855
        }
856

857
        if err := c.OVNNbClient.UpdateLogicalSwitchACL(subnet.Name, subnet.Spec.CIDRBlock, subnet.Spec.Acls, subnet.Spec.AllowEWTraffic); err != nil {
×
858
                if err = c.patchSubnetStatus(subnet, "SetLogicalSwitchAclsFailed", err.Error()); err != nil {
×
859
                        klog.Error(err)
×
860
                        return err
×
861
                }
×
862
                klog.Error(err)
×
863
                return err
×
864
        }
865

866
        c.updateVpcStatusQueue.Add(subnet.Spec.Vpc)
×
867

×
868
        ippools, err := c.ippoolLister.List(labels.Everything())
×
869
        if err != nil {
×
870
                klog.Errorf("failed to list ippools: %v", err)
×
871
                return err
×
872
        }
×
873

874
        for _, p := range ippools {
×
875
                if p.Spec.Subnet == subnet.Name {
×
876
                        c.addOrUpdateIPPoolQueue.Add(p.Name)
×
877
                }
×
878
        }
879

880
        return nil
×
881
}
882

883
func (c *Controller) handleUpdateSubnetStatus(key string) error {
×
884
        c.subnetKeyMutex.LockKey(key)
×
885
        defer func() { _ = c.subnetKeyMutex.UnlockKey(key) }()
×
886

887
        cachedSubnet, err := c.subnetsLister.Get(key)
×
888
        subnet := cachedSubnet.DeepCopy()
×
889
        if err != nil {
×
890
                if k8serrors.IsNotFound(err) {
×
891
                        return nil
×
892
                }
×
893
                klog.Error(err)
×
894
                return err
×
895
        }
896

897
        ippools, err := c.ippoolLister.List(labels.Everything())
×
898
        if err != nil {
×
899
                klog.Errorf("failed to list ippool: %v", err)
×
900
                return err
×
901
        }
×
902
        for _, p := range ippools {
×
903
                if p.Spec.Subnet == subnet.Name {
×
904
                        c.updateIPPoolStatusQueue.Add(p.Name)
×
905
                }
×
906
        }
907

908
        if util.CheckProtocol(subnet.Spec.CIDRBlock) == kubeovnv1.ProtocolDual {
×
909
                if _, err := c.calcDualSubnetStatusIP(subnet); err != nil {
×
910
                        klog.Error(err)
×
911
                        return err
×
912
                }
×
913
        } else {
×
914
                if _, err = c.calcSubnetStatusIP(subnet); err != nil {
×
915
                        klog.Error(err)
×
916
                        return err
×
917
                }
×
918
        }
919

920
        if err := c.checkSubnetUsingIPs(subnet); err != nil {
×
921
                klog.Errorf("inconsistency detected in status of subnet %s : %v", subnet.Name, err)
×
922
                return err
×
923
        }
×
924
        return nil
×
925
}
926

927
func (c *Controller) handleDeleteLogicalSwitch(key string) (err error) {
×
928
        c.ipam.DeleteSubnet(key)
×
929

×
930
        exist, err := c.OVNNbClient.LogicalSwitchExists(key)
×
931
        if err != nil {
×
932
                klog.Errorf("check logical switch %s exist: %v", key, err)
×
933
                return err
×
934
        }
×
935

936
        // not found, skip
937
        if !exist {
×
938
                return nil
×
939
        }
×
940

941
        // clear acl when direction is ""
942
        if err = c.OVNNbClient.DeleteAcls(key, logicalSwitchKey, "", nil); err != nil {
×
943
                klog.Errorf("clear logical switch %s acls: %v", key, err)
×
944
                return err
×
945
        }
×
946

947
        if err = c.OVNNbClient.DeleteDHCPOptions(key, kubeovnv1.ProtocolDual); err != nil {
×
948
                klog.Errorf("failed to delete dhcp options of logical switch %s %v", key, err)
×
949
                return err
×
950
        }
×
951

952
        if err = c.OVNNbClient.DeleteLogicalSwitch(key); err != nil {
×
953
                klog.Errorf("delete logical switch %s: %v", key, err)
×
954
                return err
×
955
        }
×
956

957
        nss, err := c.namespacesLister.List(labels.Everything())
×
958
        if err != nil {
×
959
                klog.Errorf("failed to list namespaces, %v", err)
×
960
                return err
×
961
        }
×
962

963
        // re-annotate namespace
964
        for _, ns := range nss {
×
965
                annotations := ns.GetAnnotations()
×
966
                if annotations == nil {
×
967
                        continue
×
968
                }
969

970
                if slices.Contains(strings.Split(annotations[util.LogicalSwitchAnnotation], ","), key) {
×
971
                        c.enqueueAddNamespace(ns)
×
972
                }
×
973
        }
974

975
        return c.delLocalnet(key)
×
976
}
977

978
func (c *Controller) handleDeleteSubnet(subnet *kubeovnv1.Subnet) error {
×
979
        c.subnetKeyMutex.LockKey(subnet.Name)
×
980
        defer func() { _ = c.subnetKeyMutex.UnlockKey(subnet.Name) }()
×
981

982
        c.updateVpcStatusQueue.Add(subnet.Spec.Vpc)
×
983
        klog.Infof("delete u2o interconnection policy route for subnet %s", subnet.Name)
×
984
        if err := c.deletePolicyRouteForU2OInterconn(subnet); err != nil {
×
985
                klog.Errorf("failed to delete policy route for underlay to overlay subnet interconnection %s, %v", subnet.Name, err)
×
986
                return err
×
987
        }
×
988
        if subnet.Spec.Vpc != c.config.ClusterRouter {
×
989
                if err := c.deleteStaticRouteForU2OInterconn(subnet); err != nil {
×
990
                        klog.Errorf("failed to delete static route for underlay to overlay subnet interconnection %s, %v", subnet.Name, err)
×
991
                        return err
×
992
                }
×
993
        }
994

995
        u2oInterconnName := fmt.Sprintf(util.U2OInterconnName, subnet.Spec.Vpc, subnet.Name)
×
996
        if err := c.config.KubeOvnClient.KubeovnV1().IPs().Delete(context.Background(), u2oInterconnName, metav1.DeleteOptions{}); err != nil {
×
997
                if !k8serrors.IsNotFound(err) {
×
998
                        klog.Errorf("failed to delete ip %s, %v", u2oInterconnName, err)
×
999
                        return err
×
1000
                }
×
1001
        }
1002

1003
        if subnet.Spec.Vpc != c.config.ClusterRouter {
×
1004
                if err := c.deleteCustomVPCPolicyRoutesForSubnet(subnet); err != nil {
×
1005
                        klog.Errorf("failed to delete custom vpc routes subnet %s, %v", subnet.Name, err)
×
1006
                        return err
×
1007
                }
×
1008
        }
1009

1010
        klog.Infof("delete policy route for %s subnet %s", subnet.Spec.GatewayType, subnet.Name)
×
1011
        if err := c.deletePolicyRouteByGatewayType(subnet, subnet.Spec.GatewayType, true); err != nil {
×
1012
                klog.Errorf("failed to delete policy route for overlay subnet %s, %v", subnet.Name, err)
×
1013
                return err
×
1014
        }
×
1015

1016
        err := c.handleDeleteLogicalSwitch(subnet.Name)
×
1017
        if err != nil {
×
1018
                klog.Errorf("failed to delete logical switch %s %v", subnet.Name, err)
×
1019
                return err
×
1020
        }
×
1021

1022
        var router string
×
1023
        vpc, err := c.vpcsLister.Get(subnet.Spec.Vpc)
×
1024
        if err != nil {
×
1025
                if !k8serrors.IsNotFound(err) {
×
1026
                        klog.Errorf("get vpc %s: %v", vpc.Name, err)
×
1027
                        return err
×
1028
                }
×
1029
                router = c.config.ClusterRouter
×
1030
        } else {
×
1031
                router = vpc.Status.Router
×
1032
        }
×
1033

1034
        lspName := fmt.Sprintf("%s-%s", subnet.Name, router)
×
1035
        lrpName := fmt.Sprintf("%s-%s", router, subnet.Name)
×
1036
        if err = c.OVNNbClient.RemoveLogicalPatchPort(lspName, lrpName); err != nil {
×
1037
                klog.Errorf("delete router port %s and %s:%v", lspName, lrpName, err)
×
1038
                return err
×
1039
        }
×
1040

1041
        vlans, err := c.vlansLister.List(labels.Everything())
×
1042
        if err != nil && !k8serrors.IsNotFound(err) {
×
1043
                klog.Errorf("failed to list vlans: %v", err)
×
1044
                return err
×
1045
        }
×
1046

1047
        for _, vlan := range vlans {
×
1048
                if err = c.updateVlanStatusForSubnetDeletion(vlan, subnet.Name); err != nil {
×
1049
                        klog.Error(err)
×
1050
                        return err
×
1051
                }
×
1052
        }
1053

1054
        // clean up subnet last vpc name cached
1055
        c.subnetLastVpcNameMap.Delete(subnet.Name)
×
1056

×
1057
        return nil
×
1058
}
1059

1060
func (c *Controller) updateVlanStatusForSubnetDeletion(vlan *kubeovnv1.Vlan, subnet string) error {
×
1061
        if !slices.Contains(vlan.Status.Subnets, subnet) {
×
1062
                return nil
×
1063
        }
×
1064

1065
        newVlan := vlan.DeepCopy()
×
1066
        newVlan.Status.Subnets = util.RemoveString(newVlan.Status.Subnets, subnet)
×
1067
        _, err := c.config.KubeOvnClient.KubeovnV1().Vlans().UpdateStatus(context.Background(), newVlan, metav1.UpdateOptions{})
×
1068
        if err != nil {
×
1069
                klog.Errorf("failed to update status of vlan %s: %v", vlan.Name, err)
×
1070
                return err
×
1071
        }
×
1072

1073
        return nil
×
1074
}
1075

1076
func (c *Controller) reconcileSubnet(subnet *kubeovnv1.Subnet) error {
×
1077
        if err := c.reconcileNamespaces(subnet); err != nil {
×
1078
                klog.Errorf("reconcile namespaces for subnet %s failed, %v", subnet.Name, err)
×
1079
                return err
×
1080
        }
×
1081

1082
        if err := c.reconcileRouteTableForSubnet(subnet); err != nil {
×
1083
                klog.Errorf("reconcile route table for subnet %s failed, %v", subnet.Name, err)
×
1084
                return err
×
1085
        }
×
1086

1087
        if subnet.Spec.Vpc == c.config.ClusterRouter {
×
1088
                if err := c.reconcileOvnDefaultVpcRoute(subnet); err != nil {
×
1089
                        klog.Errorf("reconcile default vpc ovn route for subnet %s failed: %v", subnet.Name, err)
×
1090
                        return err
×
1091
                }
×
1092
        } else if err := c.reconcileCustomVpcStaticRoute(subnet); err != nil {
×
1093
                klog.Errorf("reconcile custom vpc ovn route for subnet %s failed: %v", subnet.Name, err)
×
1094
                return err
×
1095
        }
×
1096

1097
        if err := c.reconcileVlan(subnet); err != nil {
×
1098
                klog.Errorf("reconcile vlan for subnet %s failed, %v", subnet.Name, err)
×
1099
                return err
×
1100
        }
×
1101

1102
        if err := c.reconcileVips(subnet); err != nil {
×
1103
                klog.Errorf("reconcile vips for subnet %s failed, %v", subnet.Name, err)
×
1104
                return err
×
1105
        }
×
1106
        return nil
×
1107
}
1108

1109
func (c *Controller) reconcileVips(subnet *kubeovnv1.Subnet) error {
1✔
1110
        /* get all virtual port belongs to this logical switch */
1✔
1111
        lsps, err := c.OVNNbClient.ListLogicalSwitchPorts(true, map[string]string{logicalSwitchKey: subnet.Name}, func(lsp *ovnnb.LogicalSwitchPort) bool {
1✔
1112
                return lsp.Type == "virtual"
×
1113
        })
×
1114
        if err != nil {
1✔
1115
                klog.Errorf("failed to find virtual port for subnet %s: %v", subnet.Name, err)
×
1116
                return err
×
1117
        }
×
1118

1119
        /* filter all invalid virtual port */
1120
        existVips := make(map[string]string) // key is vip, value is port name
1✔
1121
        for _, lsp := range lsps {
2✔
1122
                vip, ok := lsp.Options["virtual-ip"]
1✔
1123
                if !ok {
1✔
1124
                        continue // ignore vip which is empty
×
1125
                }
1126

1127
                if net.ParseIP(vip) == nil {
1✔
1128
                        continue // ignore invalid vip
×
1129
                }
1130

1131
                existVips[vip] = lsp.Name
1✔
1132
        }
1133

1134
        /* filter virtual port to be added and old virtual port to be deleted */
1135
        var newVips []string
1✔
1136
        for _, vip := range subnet.Spec.Vips {
2✔
1137
                if _, ok := existVips[vip]; !ok {
2✔
1138
                        // new virtual port to be added
1✔
1139
                        newVips = append(newVips, vip)
1✔
1140
                } else {
2✔
1141
                        // delete old virtual port that do not need to be deleted
1✔
1142
                        delete(existVips, vip)
1✔
1143
                }
1✔
1144
        }
1145

1146
        // delete old virtual ports
1147
        for _, lspName := range existVips {
2✔
1148
                if err = c.OVNNbClient.DeleteLogicalSwitchPort(lspName); err != nil {
1✔
1149
                        klog.Errorf("delete virtual port %s lspName from logical switch %s: %v", lspName, subnet.Name, err)
×
1150
                        return err
×
1151
                }
×
1152
        }
1153

1154
        // add new virtual port
1155
        if err = c.OVNNbClient.CreateVirtualLogicalSwitchPorts(subnet.Name, newVips...); err != nil {
1✔
1156
                klog.Errorf("create virtual port with vips %v from logical switch %s: %v", newVips, subnet.Name, err)
×
1157
                return err
×
1158
        }
×
1159

1160
        c.syncVirtualPortsQueue.Add(subnet.Name)
1✔
1161
        return nil
1✔
1162
}
1163

1164
func (c *Controller) syncVirtualPort(key string) error {
1✔
1165
        subnet, err := c.subnetsLister.Get(key)
1✔
1166
        if err != nil {
1✔
1167
                if k8serrors.IsNotFound(err) {
×
1168
                        return nil
×
1169
                }
×
1170
                klog.Errorf("failed to get subnet %s, %v", key, err)
×
1171
                return err
×
1172
        }
1173
        if len(subnet.Spec.Vips) == 0 {
1✔
1174
                return nil
×
1175
        }
×
1176

1177
        externalIDs := map[string]string{
1✔
1178
                logicalSwitchKey: subnet.Name,
1✔
1179
                "attach-vips":    "true",
1✔
1180
        }
1✔
1181

1✔
1182
        lsps, err := c.OVNNbClient.ListNormalLogicalSwitchPorts(true, externalIDs)
1✔
1183
        if err != nil {
1✔
1184
                klog.Errorf("list logical switch %s ports: %v", subnet.Name, err)
×
1185
                return err
×
1186
        }
×
1187

1188
        for _, vip := range subnet.Spec.Vips {
2✔
1189
                if !util.CIDRContainIP(subnet.Spec.CIDRBlock, vip) {
1✔
1190
                        klog.Errorf("vip %s is out of range to subnet %s", vip, subnet.Name)
×
1191
                        continue
×
1192
                }
1193

1194
                var virtualParents []string
1✔
1195
                for _, lsp := range lsps {
2✔
1196
                        vips, ok := lsp.ExternalIDs["vips"]
1✔
1197
                        if !ok {
1✔
1198
                                continue // ignore vips which is empty
×
1199
                        }
1200

1201
                        if strings.Contains(vips, vip) {
2✔
1202
                                virtualParents = append(virtualParents, lsp.Name)
1✔
1203
                        }
1✔
1204
                }
1205

1206
                // logical switch port has no valid vip
1207
                if len(virtualParents) == 0 {
2✔
1208
                        continue
1✔
1209
                }
1210

1211
                if err = c.OVNNbClient.SetLogicalSwitchPortVirtualParents(subnet.Name, strings.Join(virtualParents, ","), vip); err != nil {
1✔
1212
                        klog.Errorf("set vip %s virtual parents %v: %v", vip, virtualParents, err)
×
1213
                        return err
×
1214
                }
×
1215
        }
1216

1217
        return nil
1✔
1218
}
1219

1220
func (c *Controller) reconcileNamespaces(subnet *kubeovnv1.Subnet) error {
×
1221
        var (
×
1222
                namespaces []*v1.Namespace
×
1223
                err        error
×
1224
        )
×
1225

×
1226
        // 1. get all namespaces should be updated
×
1227
        expectNss, err := c.getNamespacesBySelector(subnet.Spec.NamespaceSelectors)
×
1228
        if err != nil {
×
1229
                klog.Errorf("failed to list namespaces by selector, %v", err)
×
1230
                return err
×
1231
        }
×
1232
        for _, ns := range subnet.Spec.Namespaces {
×
1233
                if !slices.Contains(expectNss, ns) {
×
1234
                        expectNss = append(expectNss, ns)
×
1235
                }
×
1236
        }
1237

1238
        // 2. update namespaces
1239
        for _, expectNs := range expectNss {
×
1240
                checkNs, err := c.namespacesLister.Get(expectNs)
×
1241
                if err != nil {
×
1242
                        if k8serrors.IsNotFound(err) {
×
1243
                                continue
×
1244
                        }
1245
                        klog.Error(err)
×
1246
                        return err
×
1247
                }
1248
                if checkNs.Annotations != nil && slices.Contains(strings.Split(checkNs.Annotations[util.LogicalSwitchAnnotation], ","), subnet.Name) {
×
1249
                        // when subnet cidr changed, the ns annotation with the subnet should be updated
×
1250
                        if !slices.Contains(strings.Split(checkNs.Annotations[util.CidrAnnotation], ";"), subnet.Spec.CIDRBlock) {
×
1251
                                c.addNamespaceQueue.Add(checkNs.Name)
×
1252
                        }
×
1253
                        continue
×
1254
                }
1255
                c.addNamespaceQueue.Add(expectNs)
×
1256
        }
1257

1258
        // 3. update unbind namespace annotation
1259
        if namespaces, err = c.namespacesLister.List(labels.Everything()); err != nil {
×
1260
                klog.Errorf("failed to list namespaces, %v", err)
×
1261
                return err
×
1262
        }
×
1263

1264
        for _, ns := range namespaces {
×
1265
                if ns.Annotations != nil && slices.Contains(strings.Split(ns.Annotations[util.LogicalSwitchAnnotation], ","), subnet.Name) {
×
1266
                        if slices.Contains(expectNss, ns.Name) {
×
1267
                                continue
×
1268
                        }
1269
                        // ns deleted from subnet.Spec.Namespaces or subnet delete namespaceSelectors which match the checked namespace
1270
                        c.addNamespaceQueue.Add(ns.Name)
×
1271
                }
1272
        }
1273

1274
        return nil
×
1275
}
1276

1277
func (c *Controller) getNamespacesBySelector(nsSelectors []metav1.LabelSelector) ([]string, error) {
×
1278
        var expectNss []string
×
1279
        for _, nsSelector := range nsSelectors {
×
1280
                matchSelector, err := metav1.LabelSelectorAsSelector(&nsSelector)
×
1281
                if err != nil {
×
1282
                        klog.Errorf("failed to convert label selector, %v", err)
×
1283
                        return expectNss, err
×
1284
                }
×
1285

1286
                nss, err := c.namespacesLister.List(matchSelector)
×
1287
                if err != nil {
×
1288
                        klog.Errorf("failed to list namespaces by selector, %v", err)
×
1289
                        return expectNss, err
×
1290
                }
×
1291
                for _, ns := range nss {
×
1292
                        expectNss = append(expectNss, ns.Name)
×
1293
                }
×
1294
        }
1295
        return expectNss, nil
×
1296
}
1297

1298
func (c *Controller) reconcileCustomVpcBfdStaticRoute(vpcName, subnetName string) error {
×
1299
        // vpc enable bfd and subnet enable ecmp
×
1300
        // use static ecmp route with bfd
×
1301
        ovnEips, err := c.ovnEipsLister.List(labels.SelectorFromSet(labels.Set{util.OvnEipTypeLabel: util.OvnEipTypeLSP}))
×
1302
        if err != nil {
×
1303
                klog.Errorf("failed to list node external ovn eip, %v", err)
×
1304
                return err
×
1305
        }
×
1306
        if len(ovnEips) < 2 {
×
1307
                err := fmt.Errorf("ecmp route with bfd for HA, which need two %s type eips at least, has %d", util.OvnEipTypeLSP, len(ovnEips))
×
1308
                klog.Error(err)
×
1309
                return err
×
1310
        }
×
1311

1312
        subnet, err := c.subnetsLister.Get(subnetName)
×
1313
        if err != nil {
×
1314
                klog.Errorf("failed to get subnet %s, %v", subnetName, err)
×
1315
                return err
×
1316
        }
×
1317
        cachedVpc, err := c.vpcsLister.Get(vpcName)
×
1318
        if err != nil {
×
1319
                if k8serrors.IsNotFound(err) {
×
1320
                        return nil
×
1321
                }
×
1322
                klog.Errorf("failed to get vpc %s, %v", vpcName, err)
×
1323
                return err
×
1324
        }
1325

1326
        var (
×
1327
                needUpdate, v4Exist bool
×
1328
                lrpEipName          string
×
1329
        )
×
1330

×
1331
        lrpEipName = fmt.Sprintf("%s-%s", vpcName, c.config.ExternalGatewaySwitch)
×
1332
        lrpEip, err := c.ovnEipsLister.Get(lrpEipName)
×
1333
        if err != nil {
×
1334
                err := fmt.Errorf("failed to get lrp eip %s, %w", lrpEipName, err)
×
1335
                klog.Error(err)
×
1336
                return err
×
1337
        }
×
1338
        if !lrpEip.Status.Ready || lrpEip.Status.V4Ip == "" {
×
1339
                err := fmt.Errorf("lrp eip %q not ready", lrpEipName)
×
1340
                klog.Error(err)
×
1341
                return err
×
1342
        }
×
1343
        vpc := cachedVpc.DeepCopy()
×
1344

×
1345
        for _, eip := range ovnEips {
×
1346
                if !eip.Status.Ready || eip.Status.V4Ip == "" {
×
1347
                        err := fmt.Errorf("ovn eip %q not ready", eip.Name)
×
1348
                        klog.Error(err)
×
1349
                        return err
×
1350
                }
×
1351
                bfd, err := c.OVNNbClient.CreateBFD(lrpEipName, eip.Status.V4Ip, c.config.BfdMinRx, c.config.BfdMinTx, c.config.BfdDetectMult, nil)
×
1352
                if err != nil {
×
1353
                        klog.Error(err)
×
1354
                        return err
×
1355
                }
×
1356
                // TODO:// support v6
1357
                v4Exist = false
×
1358
                for _, route := range vpc.Spec.StaticRoutes {
×
1359
                        if route.Policy == kubeovnv1.PolicySrc &&
×
1360
                                route.NextHopIP == eip.Status.V4Ip &&
×
1361
                                route.ECMPMode == util.StaticRouteBfdEcmp &&
×
1362
                                route.CIDR == subnet.Spec.CIDRBlock &&
×
1363
                                route.RouteTable == subnet.Spec.RouteTable {
×
1364
                                v4Exist = true
×
1365
                                break
×
1366
                        }
1367
                }
1368
                if !v4Exist {
×
1369
                        // add ecmp type static route with bfd
×
1370
                        route := &kubeovnv1.StaticRoute{
×
1371
                                Policy:     kubeovnv1.PolicySrc,
×
1372
                                CIDR:       subnet.Spec.CIDRBlock,
×
1373
                                NextHopIP:  eip.Status.V4Ip,
×
1374
                                ECMPMode:   util.StaticRouteBfdEcmp,
×
1375
                                BfdID:      bfd.UUID,
×
1376
                                RouteTable: subnet.Spec.RouteTable,
×
1377
                        }
×
1378
                        klog.Infof("add ecmp bfd static route %v", route)
×
1379
                        vpc.Spec.StaticRoutes = append(vpc.Spec.StaticRoutes, route)
×
1380
                        needUpdate = true
×
1381
                }
×
1382
        }
1383
        if needUpdate {
×
1384
                if _, err = c.config.KubeOvnClient.KubeovnV1().Vpcs().Update(context.Background(), vpc, metav1.UpdateOptions{}); err != nil {
×
1385
                        klog.Errorf("failed to update vpc spec static route %s, %v", vpc.Name, err)
×
1386
                        return err
×
1387
                }
×
1388
                if err = c.patchVpcBfdStatus(vpc.Name); err != nil {
×
1389
                        klog.Errorf("failed to patch vpc %s, %v", vpc.Name, err)
×
1390
                        return err
×
1391
                }
×
1392
        }
1393
        return nil
×
1394
}
1395

1396
func (c *Controller) reconcileCustomVpcDelNormalStaticRoute(vpcName string) error {
×
1397
        // normal static route is prior than ecmp bfd static route
×
1398
        // if use ecmp bfd static route, normal static route should not exist
×
1399
        defaultExternalSubnet, err := c.subnetsLister.Get(c.config.ExternalGatewaySwitch)
×
1400
        if err != nil {
×
1401
                klog.Errorf("failed to get default external switch subnet %s: %v", c.config.ExternalGatewaySwitch, err)
×
1402
                return err
×
1403
        }
×
1404
        gatewayV4, gatewayV6 := util.SplitStringIP(defaultExternalSubnet.Spec.Gateway)
×
1405
        needUpdate := false
×
1406
        vpc, err := c.vpcsLister.Get(vpcName)
×
1407
        if err != nil {
×
1408
                if k8serrors.IsNotFound(err) {
×
1409
                        return nil
×
1410
                }
×
1411
                klog.Errorf("failed to get vpc %s, %v", vpcName, err)
×
1412
                return err
×
1413
        }
1414
        routeTotal := len(vpc.Spec.StaticRoutes)
×
1415
        routes := make([]*kubeovnv1.StaticRoute, 0, routeTotal)
×
1416
        for _, route := range vpc.Spec.StaticRoutes {
×
1417
                if route.Policy == kubeovnv1.PolicyDst &&
×
1418
                        (route.NextHopIP == gatewayV4 || route.NextHopIP == gatewayV6) &&
×
1419
                        (route.CIDR == "0.0.0.0/0" || route.CIDR == "::/0") {
×
1420
                        klog.Infof("in order to use ecmp bfd route, need remove normal static route %v", route)
×
1421
                        needUpdate = true
×
1422
                } else {
×
1423
                        routes = append(routes, route)
×
1424
                }
×
1425
        }
1426

1427
        if needUpdate {
×
1428
                vpc.Spec.StaticRoutes = routes
×
1429
                if _, err = c.config.KubeOvnClient.KubeovnV1().Vpcs().Update(context.Background(), vpc, metav1.UpdateOptions{}); err != nil {
×
1430
                        klog.Errorf("failed to update vpc spec static route %s, %v", vpc.Name, err)
×
1431
                        return err
×
1432
                }
×
1433
        }
1434

1435
        if err = c.patchVpcBfdStatus(vpc.Name); err != nil {
×
1436
                klog.Errorf("failed to patch vpc %s, %v", vpc.Name, err)
×
1437
                return err
×
1438
        }
×
1439

1440
        return nil
×
1441
}
1442

1443
func (c *Controller) reconcileDistributedSubnetRouteInDefaultVpc(subnet *kubeovnv1.Subnet) error {
×
1444
        if subnet.Spec.GatewayNode != "" || subnet.Status.ActivateGateway != "" {
×
1445
                klog.Infof("delete old centralized policy route for subnet %s", subnet.Name)
×
1446
                if err := c.deletePolicyRouteForCentralizedSubnet(subnet); err != nil {
×
1447
                        klog.Errorf("failed to delete policy route for subnet %s, %v", subnet.Name, err)
×
1448
                        return err
×
1449
                }
×
1450

1451
                subnet.Spec.GatewayNode = ""
×
1452
                if _, err := c.config.KubeOvnClient.KubeovnV1().Subnets().Update(context.Background(), subnet, metav1.UpdateOptions{}); err != nil {
×
1453
                        klog.Errorf("failed to remove gatewayNode or activateGateway from subnet %s, %v", subnet.Name, err)
×
1454
                        return err
×
1455
                }
×
1456
                subnet.Status.ActivateGateway = ""
×
1457
                if err := c.patchSubnetStatus(subnet, "ChangeToDistributedGw", ""); err != nil {
×
1458
                        klog.Error(err)
×
1459
                        return err
×
1460
                }
×
1461
        }
1462

1463
        nodes, err := c.nodesLister.List(labels.Everything())
×
1464
        if err != nil {
×
1465
                klog.Errorf("failed to list nodes: %v", err)
×
1466
                return err
×
1467
        }
×
1468
        for _, node := range nodes {
×
1469
                if err = c.createPortGroupForDistributedSubnet(node, subnet); err != nil {
×
1470
                        klog.Errorf("failed to create port group %v", err)
×
1471
                        return err
×
1472
                }
×
1473
                if node.Annotations[util.AllocatedAnnotation] != "true" {
×
1474
                        klog.Warningf("node %s has not been successfully initialized, skip adding policy route for subnet %s", node.Name, subnet.Name)
×
1475
                        continue
×
1476
                }
1477
                nodeIP, err := getNodeTunlIP(node)
×
1478
                if err != nil {
×
1479
                        klog.Errorf("failed to get node %s tunnel ip, %v", node.Name, err)
×
1480
                        return err
×
1481
                }
×
1482
                nextHop := getNextHopByTunnelIP(nodeIP)
×
1483
                v4IP, v6IP := util.SplitStringIP(nextHop)
×
1484
                if err = c.addPolicyRouteForDistributedSubnet(subnet, node.Name, v4IP, v6IP); err != nil {
×
1485
                        klog.Errorf("failed to add policy router for node %s and subnet %s: %v", node.Name, subnet.Name, err)
×
1486
                        return err
×
1487
                }
×
1488
        }
1489

1490
        portGroups, err := c.OVNNbClient.ListPortGroups(map[string]string{"subnet": subnet.Name, "node": "", networkPolicyKey: ""})
×
1491
        if err != nil {
×
1492
                klog.Errorf("failed to list port groups for subnet %s: %v", subnet.Name, err)
×
1493
                return err
×
1494
        }
×
1495

1496
        pods, err := c.podsLister.Pods(metav1.NamespaceAll).List(labels.Everything())
×
1497
        if err != nil {
×
1498
                klog.Errorf("failed to list pods %v", err)
×
1499
                return err
×
1500
        }
×
1501
        for _, pod := range pods {
×
1502
                if !isPodAlive(pod) || pod.Spec.NodeName == "" {
×
1503
                        continue
×
1504
                }
1505

1506
                podNets, err := c.getPodKubeovnNets(pod)
×
1507
                if err != nil {
×
1508
                        klog.Errorf("failed to get pod nets %v", err)
×
1509
                        continue
×
1510
                }
1511

1512
                podPorts := make([]string, 0, 1)
×
1513
                for _, podNet := range podNets {
×
1514
                        if !isOvnSubnet(podNet.Subnet) {
×
1515
                                continue
×
1516
                        }
1517

1518
                        if pod.Annotations[fmt.Sprintf(util.IPAddressAnnotationTemplate, podNet.ProviderName)] == "" || pod.Annotations[fmt.Sprintf(util.LogicalSwitchAnnotationTemplate, podNet.ProviderName)] != subnet.Name {
×
1519
                                continue
×
1520
                        }
1521

1522
                        podName := c.getNameByPod(pod)
×
1523
                        portName := ovs.PodNameToPortName(podName, pod.Namespace, podNet.ProviderName)
×
1524
                        podPorts = append(podPorts, portName)
×
1525
                }
1526

1527
                pgName := getOverlaySubnetsPortGroupName(subnet.Name, pod.Spec.NodeName)
×
1528
                portsToAdd := make([]string, 0, len(podPorts))
×
1529
                for _, port := range podPorts {
×
1530
                        exist, err := c.OVNNbClient.LogicalSwitchPortExists(port)
×
1531
                        if err != nil {
×
1532
                                klog.Error(err)
×
1533
                                return err
×
1534
                        }
×
1535

1536
                        if !exist {
×
1537
                                klog.Errorf("lsp does not exist for pod %v, please delete the pod and retry", port)
×
1538
                                continue
×
1539
                        }
1540

1541
                        portsToAdd = append(portsToAdd, port)
×
1542
                }
1543

1544
                // remove lsp from other port groups
1545
                // we need to do this because the pod, e.g. a sts/vm, can be rescheduled to another node
1546
                for _, pg := range portGroups {
×
1547
                        if pg.Name == pgName {
×
1548
                                continue
×
1549
                        }
1550
                        if err = c.OVNNbClient.PortGroupRemovePorts(pg.Name, podPorts...); err != nil {
×
1551
                                klog.Errorf("remove ports from port group %s: %v", pg.Name, err)
×
1552
                                return err
×
1553
                        }
×
1554
                }
1555
                // add ports to the port group
1556
                if err = c.OVNNbClient.PortGroupAddPorts(pgName, portsToAdd...); err != nil {
×
1557
                        klog.Errorf("add ports to port group %s: %v", pgName, err)
×
1558
                        return err
×
1559
                }
×
1560
        }
1561
        return nil
×
1562
}
1563

1564
func (c *Controller) reconcileDefaultCentralizedSubnetRouteInDefaultVpc(subnet *kubeovnv1.Subnet) error {
×
1565
        // check if activateGateway still ready
×
1566
        if subnet.Status.ActivateGateway != "" && util.GatewayContains(subnet.Spec.GatewayNode, subnet.Status.ActivateGateway) {
×
1567
                node, err := c.nodesLister.Get(subnet.Status.ActivateGateway)
×
1568
                if err == nil && nodeReady(node) {
×
1569
                        klog.Infof("subnet %s uses the old activate gw %s", subnet.Name, node.Name)
×
1570

×
1571
                        nodeTunlIPAddr, err := getNodeTunlIP(node)
×
1572
                        if err != nil {
×
1573
                                klog.Errorf("failed to get gatewayNode tunnel ip for subnet %s", subnet.Name)
×
1574
                                return err
×
1575
                        }
×
1576
                        nextHop := getNextHopByTunnelIP(nodeTunlIPAddr)
×
1577
                        if err = c.addPolicyRouteForCentralizedSubnet(subnet, subnet.Status.ActivateGateway, nil, strings.Split(nextHop, ",")); err != nil {
×
1578
                                klog.Errorf("failed to add active-backup policy route for centralized subnet %s: %v", subnet.Name, err)
×
1579
                                return err
×
1580
                        }
×
1581
                        return nil
×
1582
                }
1583
        }
1584

1585
        klog.Info("find a new activate node")
×
1586
        // need a new activate gateway
×
1587
        newActivateNode := ""
×
1588
        var nodeTunlIPAddr []net.IP
×
1589
        for gw := range strings.SplitSeq(subnet.Spec.GatewayNode, ",") {
×
1590
                // the format of gatewayNodeStr can be like 'kube-ovn-worker:172.18.0.2, kube-ovn-control-plane:172.18.0.3', which consists of node name and designative egress ip
×
1591
                if strings.Contains(gw, ":") {
×
1592
                        gw = strings.TrimSpace(strings.Split(gw, ":")[0])
×
1593
                } else {
×
1594
                        gw = strings.TrimSpace(gw)
×
1595
                }
×
1596
                node, err := c.nodesLister.Get(gw)
×
1597
                if err == nil && nodeReady(node) {
×
1598
                        newActivateNode = node.Name
×
1599
                        nodeTunlIPAddr, err = getNodeTunlIP(node)
×
1600
                        if err != nil {
×
1601
                                klog.Error(err)
×
1602
                                return err
×
1603
                        }
×
1604
                        klog.Infof("subnet %s uses a new activate gw %s", subnet.Name, node.Name)
×
1605
                        break
×
1606
                }
1607
        }
1608
        if newActivateNode == "" {
×
1609
                klog.Warningf("all gateways of subnet %s are not ready", subnet.Name)
×
1610
                subnet.Status.ActivateGateway = newActivateNode
×
1611
                if err := c.patchSubnetStatus(subnet, "NoActiveGatewayFound", fmt.Sprintf("subnet %s gws are not ready", subnet.Name)); err != nil {
×
1612
                        klog.Error(err)
×
1613
                        return err
×
1614
                }
×
1615

1616
                return fmt.Errorf("subnet %s gws are not ready", subnet.Name)
×
1617
        }
1618

1619
        nextHop := getNextHopByTunnelIP(nodeTunlIPAddr)
×
1620
        klog.Infof("subnet %s configure new gateway node, nextHop %s", subnet.Name, nextHop)
×
1621
        if err := c.addPolicyRouteForCentralizedSubnet(subnet, newActivateNode, nil, strings.Split(nextHop, ",")); err != nil {
×
1622
                klog.Errorf("failed to add policy route for active-backup centralized subnet %s: %v", subnet.Name, err)
×
1623
                return err
×
1624
        }
×
1625
        subnet.Status.ActivateGateway = newActivateNode
×
1626
        if err := c.patchSubnetStatus(subnet, "ReconcileCentralizedGatewaySuccess", ""); err != nil {
×
1627
                klog.Error(err)
×
1628
                return err
×
1629
        }
×
1630

1631
        klog.Infof("delete old distributed policy route for subnet %s", subnet.Name)
×
1632
        if err := c.deletePolicyRouteByGatewayType(subnet, kubeovnv1.GWDistributedType, false); err != nil {
×
1633
                klog.Errorf("failed to delete policy route for overlay subnet %s, %v", subnet.Name, err)
×
1634
                return err
×
1635
        }
×
1636
        return nil
×
1637
}
1638

1639
func (c *Controller) reconcileEcmpCentralizedSubnetRouteInDefaultVpc(subnet *kubeovnv1.Subnet) error {
×
1640
        // centralized subnet, enable ecmp, add ecmp policy route
×
1641
        var (
×
1642
                gatewayNodes = strings.Split(subnet.Spec.GatewayNode, ",")
×
1643
                nodeV4IPs    = make([]string, 0, len(gatewayNodes))
×
1644
                nodeV6IPs    = make([]string, 0, len(gatewayNodes))
×
1645
                nameV4IPMap  = make(map[string]string, len(gatewayNodes))
×
1646
                nameV6IPMap  = make(map[string]string, len(gatewayNodes))
×
1647
        )
×
1648

×
1649
        for _, gw := range gatewayNodes {
×
1650
                // the format of gatewayNodeStr can be like 'kube-ovn-worker:172.18.0.2, kube-ovn-control-plane:172.18.0.3', which consists of node name and designative egress ip
×
1651
                if strings.Contains(gw, ":") {
×
1652
                        gw = strings.TrimSpace(strings.Split(gw, ":")[0])
×
1653
                } else {
×
1654
                        gw = strings.TrimSpace(gw)
×
1655
                }
×
1656

1657
                node, err := c.nodesLister.Get(gw)
×
1658
                if err != nil {
×
1659
                        klog.Errorf("failed to get gw node %s, %v", gw, err)
×
1660
                        continue
×
1661
                }
1662

1663
                if nodeReady(node) {
×
1664
                        nexthopNodeIP := strings.TrimSpace(node.Annotations[util.IPAddressAnnotation])
×
1665
                        if nexthopNodeIP == "" {
×
1666
                                klog.Errorf("gateway node %v has no ip annotation", node.Name)
×
1667
                                continue
×
1668
                        }
1669
                        nexthopV4, nexthopV6 := util.SplitStringIP(nexthopNodeIP)
×
1670
                        if nexthopV4 != "" {
×
1671
                                nameV4IPMap[node.Name] = nexthopV4
×
1672
                                nodeV4IPs = append(nodeV4IPs, nexthopV4)
×
1673
                        }
×
1674
                        if nexthopV6 != "" {
×
1675
                                nameV6IPMap[node.Name] = nexthopV6
×
1676
                                nodeV6IPs = append(nodeV6IPs, nexthopV6)
×
1677
                        }
×
1678
                } else {
×
1679
                        klog.Errorf("gateway node %v is not ready", gw)
×
1680
                }
×
1681
        }
1682

1683
        v4CIDR, v6CIDR := util.SplitStringIP(subnet.Spec.CIDRBlock)
×
1684
        if nodeV4IPs != nil && v4CIDR != "" {
×
1685
                klog.Infof("delete old distributed policy route for subnet %s", subnet.Name)
×
1686
                if err := c.deletePolicyRouteByGatewayType(subnet, kubeovnv1.GWDistributedType, false); err != nil {
×
1687
                        klog.Errorf("failed to delete policy route for overlay subnet %s, %v", subnet.Name, err)
×
1688
                        return err
×
1689
                }
×
1690
                klog.Infof("subnet %s configure ecmp policy route, nexthops %v", subnet.Name, nodeV4IPs)
×
1691
                if err := c.updatePolicyRouteForCentralizedSubnet(subnet.Name, v4CIDR, nodeV4IPs, nameV4IPMap); err != nil {
×
1692
                        klog.Errorf("failed to add v4 ecmp policy route for centralized subnet %s: %v", subnet.Name, err)
×
1693
                        return err
×
1694
                }
×
1695
        }
1696
        if nodeV6IPs != nil && v6CIDR != "" {
×
1697
                klog.Infof("delete old distributed policy route for subnet %s", subnet.Name)
×
1698
                if err := c.deletePolicyRouteByGatewayType(subnet, kubeovnv1.GWDistributedType, false); err != nil {
×
1699
                        klog.Errorf("failed to delete policy route for overlay subnet %s, %v", subnet.Name, err)
×
1700
                        return err
×
1701
                }
×
1702
                klog.Infof("subnet %s configure ecmp policy route, nexthops %v", subnet.Name, nodeV6IPs)
×
1703
                if err := c.updatePolicyRouteForCentralizedSubnet(subnet.Name, v6CIDR, nodeV6IPs, nameV6IPMap); err != nil {
×
1704
                        klog.Errorf("failed to add v6 ecmp policy route for centralized subnet %s: %v", subnet.Name, err)
×
1705
                        return err
×
1706
                }
×
1707
        }
1708
        return nil
×
1709
}
1710

1711
func (c *Controller) reconcileOvnDefaultVpcRoute(subnet *kubeovnv1.Subnet) error {
×
1712
        if subnet.Name == c.config.NodeSwitch {
×
1713
                if err := c.addCommonRoutesForSubnet(subnet); err != nil {
×
1714
                        klog.Error(err)
×
1715
                        return err
×
1716
                }
×
1717
                return nil
×
1718
        }
1719

1720
        if subnet.Spec.Vlan != "" && !subnet.Spec.LogicalGateway {
×
1721
                // physical switch provide gw for this underlay subnet
×
1722
                pods, err := c.podsLister.Pods(metav1.NamespaceAll).List(labels.Everything())
×
1723
                if err != nil {
×
1724
                        klog.Errorf("failed to list pods %v", err)
×
1725
                        return err
×
1726
                }
×
1727
                for _, pod := range pods {
×
1728
                        if pod.Annotations[util.LogicalSwitchAnnotation] == subnet.Name && pod.Annotations[util.IPAddressAnnotation] != "" {
×
1729
                                if err := c.deleteStaticRoute(
×
1730
                                        pod.Annotations[util.IPAddressAnnotation], c.config.ClusterRouter, subnet.Spec.RouteTable); err != nil {
×
1731
                                        klog.Errorf("failed to delete static route %v", err)
×
1732
                                        return err
×
1733
                                }
×
1734
                        }
1735
                }
1736

1737
                if !subnet.Spec.LogicalGateway && subnet.Name != c.config.ExternalGatewaySwitch && !subnet.Spec.U2OInterconnection {
×
1738
                        lspName := fmt.Sprintf("%s-%s", subnet.Name, c.config.ClusterRouter)
×
1739
                        klog.Infof("delete logical switch port %s", lspName)
×
1740
                        if err := c.OVNNbClient.DeleteLogicalSwitchPort(lspName); err != nil {
×
1741
                                klog.Errorf("failed to delete lsp %s-%s, %v", subnet.Name, c.config.ClusterRouter, err)
×
1742
                                return err
×
1743
                        }
×
1744
                        lrpName := fmt.Sprintf("%s-%s", c.config.ClusterRouter, subnet.Name)
×
1745
                        klog.Infof("delete logical router port %s", lrpName)
×
1746
                        if err := c.OVNNbClient.DeleteLogicalRouterPort(lrpName); err != nil {
×
1747
                                klog.Errorf("failed to delete lrp %s: %v", lrpName, err)
×
1748
                                return err
×
1749
                        }
×
1750
                }
1751

1752
                if subnet.Spec.U2OInterconnection && subnet.Status.U2OInterconnectionIP != "" {
×
1753
                        if err := c.addPolicyRouteForU2OInterconn(subnet); err != nil {
×
1754
                                klog.Errorf("failed to add policy route for underlay to overlay subnet interconnection %s %v", subnet.Name, err)
×
1755
                                return err
×
1756
                        }
×
1757
                } else {
×
1758
                        if err := c.deletePolicyRouteForU2OInterconn(subnet); err != nil {
×
1759
                                klog.Errorf("failed to delete policy route for underlay to overlay subnet interconnection %s, %v", subnet.Name, err)
×
1760
                                return err
×
1761
                        }
×
1762
                }
1763

1764
                if (!c.config.EnableLb || (subnet.Spec.EnableLb == nil || !*subnet.Spec.EnableLb)) &&
×
1765
                        subnet.Spec.U2OInterconnection && subnet.Status.U2OInterconnectionIP != "" {
×
1766
                        if err := c.addPolicyRouteForU2ONoLoadBalancer(subnet); err != nil {
×
1767
                                klog.Errorf("failed to add policy route for underlay to overlay subnet interconnection without enabling loadbalancer %s %v", subnet.Name, err)
×
1768
                                return err
×
1769
                        }
×
1770
                } else {
×
1771
                        if err := c.deletePolicyRouteForU2ONoLoadBalancer(subnet); err != nil {
×
1772
                                klog.Errorf("failed to delete policy route for underlay to overlay subnet interconnection without enabling loadbalancer %s, %v", subnet.Name, err)
×
1773
                                return err
×
1774
                        }
×
1775
                }
1776
        } else {
×
1777
                // It's difficult to update policy route when subnet cidr is changed, add check for cidr changed situation
×
1778
                if err := c.reconcilePolicyRouteForCidrChangedSubnet(subnet, true); err != nil {
×
1779
                        klog.Error(err)
×
1780
                        return err
×
1781
                }
×
1782

1783
                if err := c.addCommonRoutesForSubnet(subnet); err != nil {
×
1784
                        klog.Error(err)
×
1785
                        return err
×
1786
                }
×
1787

1788
                // distributed subnet, only add distributed policy route
1789
                if subnet.Spec.GatewayType == kubeovnv1.GWDistributedType {
×
1790
                        if err := c.reconcileDistributedSubnetRouteInDefaultVpc(subnet); err != nil {
×
1791
                                klog.Error(err)
×
1792
                                return err
×
1793
                        }
×
1794
                } else {
×
1795
                        // centralized subnet
×
1796
                        if subnet.Spec.GatewayNode == "" {
×
1797
                                subnet.Status.NotReady("NoReadyGateway", "")
×
1798
                                if err := c.patchSubnetStatus(subnet, "NoReadyGateway", ""); err != nil {
×
1799
                                        klog.Error(err)
×
1800
                                        return err
×
1801
                                }
×
1802
                                err := fmt.Errorf("subnet %s Spec.GatewayNode field must be specified for centralized gateway type", subnet.Name)
×
1803
                                klog.Error(err)
×
1804
                                return err
×
1805
                        }
1806

1807
                        gwNodeExists := c.checkGwNodeExists(subnet.Spec.GatewayNode)
×
1808
                        if !gwNodeExists {
×
1809
                                klog.Errorf("failed to init centralized gateway for subnet %s, no gateway node exists", subnet.Name)
×
1810
                                return errors.New("failed to add ecmp policy route, no gateway node exists")
×
1811
                        }
×
1812

1813
                        if err := c.reconcilePolicyRouteForCidrChangedSubnet(subnet, false); err != nil {
×
1814
                                klog.Error(err)
×
1815
                                return err
×
1816
                        }
×
1817

1818
                        if subnet.Spec.EnableEcmp {
×
1819
                                if err := c.reconcileEcmpCentralizedSubnetRouteInDefaultVpc(subnet); err != nil {
×
1820
                                        klog.Error(err)
×
1821
                                        return err
×
1822
                                }
×
1823
                        } else {
×
1824
                                if err := c.reconcileDefaultCentralizedSubnetRouteInDefaultVpc(subnet); err != nil {
×
1825
                                        klog.Error(err)
×
1826
                                        return err
×
1827
                                }
×
1828
                        }
1829
                }
1830
        }
1831
        return nil
×
1832
}
1833

1834
func (c *Controller) reconcileCustomVpcStaticRoute(subnet *kubeovnv1.Subnet) error {
×
1835
        // in custom vpc, subnet gw type is unmeaning
×
1836
        // 1. vpc out to public network through vpc nat gw pod, the static route is auto managed by admin user
×
1837
        // 2. vpc out to public network through ovn nat lrp, whose nexthop rely on bfd ecmp, the vpc spec bfd static route is auto managed here
×
1838
        // 3. vpc out to public network through ovn nat lrp, without bfd ecmp, the vpc spec static route is auto managed here
×
1839

×
1840
        vpc, err := c.vpcsLister.Get(subnet.Spec.Vpc)
×
1841
        if err != nil {
×
1842
                if k8serrors.IsNotFound(err) {
×
1843
                        return nil
×
1844
                }
×
1845
                klog.Errorf("failed to get vpc %s, %v", subnet.Spec.Vpc, err)
×
1846
                return err
×
1847
        }
1848

1849
        if vpc.Spec.EnableExternal && vpc.Spec.EnableBfd && subnet.Spec.EnableEcmp {
×
1850
                klog.Infof("add bfd and external static ecmp route for vpc %s, subnet %s", vpc.Name, subnet.Name)
×
1851
                // handle vpc static route
×
1852
                // use static ecmp route with bfd
×
1853
                // bfd ecmp static route depend on subnet cidr
×
1854
                if err := c.reconcileCustomVpcBfdStaticRoute(vpc.Name, subnet.Name); err != nil {
×
1855
                        klog.Errorf("failed to reconcile vpc %q bfd static route", vpc.Name)
×
1856
                        return err
×
1857
                }
×
1858
        }
1859

1860
        if subnet.Spec.Vlan == "" || subnet.Spec.LogicalGateway || subnet.Spec.U2OInterconnection {
×
1861
                if err = c.addCustomVPCStaticRouteForSubnet(subnet); err != nil {
×
1862
                        klog.Errorf("failed to add static route for underlay to overlay subnet interconnection %s %v", subnet.Name, err)
×
1863
                        return err
×
1864
                }
×
1865
                if err = c.addCustomVPCPolicyRoutesForSubnet(subnet); err != nil {
×
1866
                        klog.Error(err)
×
1867
                        return err
×
1868
                }
×
1869
        }
1870

1871
        if subnet.Spec.Vlan != "" && !subnet.Spec.LogicalGateway && subnet.Spec.U2OInterconnection && subnet.Status.U2OInterconnectionIP != "" {
×
1872
                if err := c.addPolicyRouteForU2OInterconn(subnet); err != nil {
×
1873
                        klog.Errorf("failed to add policy route for underlay to overlay subnet interconnection %s %v", subnet.Name, err)
×
1874
                        return err
×
1875
                }
×
1876
        }
1877

1878
        return nil
×
1879
}
1880

1881
func (c *Controller) deleteStaticRoute(ip, router, routeTable string) error {
×
1882
        for ipStr := range strings.SplitSeq(ip, ",") {
×
1883
                if err := c.deleteStaticRouteFromVpc(
×
1884
                        router,
×
1885
                        routeTable,
×
1886
                        ipStr,
×
1887
                        "",
×
1888
                        kubeovnv1.PolicyDst,
×
1889
                ); err != nil {
×
1890
                        klog.Errorf("failed to delete static route %s, %v", ipStr, err)
×
1891
                        return err
×
1892
                }
×
1893
        }
1894

1895
        return nil
×
1896
}
1897

1898
func (c *Controller) reconcileVlan(subnet *kubeovnv1.Subnet) error {
×
1899
        if subnet.Spec.Vlan == "" {
×
1900
                return nil
×
1901
        }
×
1902
        klog.Infof("reconcile vlan %v", subnet.Spec.Vlan)
×
1903

×
1904
        vlan, err := c.vlansLister.Get(subnet.Spec.Vlan)
×
1905
        if err != nil {
×
1906
                klog.Errorf("failed to get vlan %s: %v", subnet.Spec.Vlan, err)
×
1907
                return err
×
1908
        }
×
1909
        if vlan.Status.Conflict {
×
1910
                err = fmt.Errorf("subnet %s has invalid conflict vlan %s", subnet.Name, vlan.Name)
×
1911
                klog.Error(err)
×
1912
                return err
×
1913
        }
×
1914

1915
        localnetPort := ovs.GetLocalnetName(subnet.Name)
×
1916
        if err := c.OVNNbClient.CreateLocalnetLogicalSwitchPort(subnet.Name, localnetPort, vlan.Spec.Provider, subnet.Spec.CIDRBlock, vlan.Spec.ID); err != nil {
×
1917
                klog.Errorf("create localnet port for subnet %s: %v", subnet.Name, err)
×
1918
                return err
×
1919
        }
×
1920

1921
        if !slices.Contains(vlan.Status.Subnets, subnet.Name) {
×
1922
                newVlan := vlan.DeepCopy()
×
1923
                newVlan.Status.Subnets = append(newVlan.Status.Subnets, subnet.Name)
×
1924
                _, err = c.config.KubeOvnClient.KubeovnV1().Vlans().UpdateStatus(context.Background(), newVlan, metav1.UpdateOptions{})
×
1925
                if err != nil {
×
1926
                        klog.Errorf("failed to update status of vlan %s: %v", vlan.Name, err)
×
1927
                        return err
×
1928
                }
×
1929
        }
1930

1931
        return nil
×
1932
}
1933

1934
func (c *Controller) reconcileSubnetSpecialIPs(subnet *kubeovnv1.Subnet) (bool, bool, error) {
×
1935
        isU2OIPChanged := false
×
1936
        isMcastQuerierIPChanged := false
×
1937
        var err error
×
1938

×
1939
        // reconcile u2o IP
×
1940
        if subnet.Spec.Vlan != "" && !subnet.Spec.LogicalGateway {
×
1941
                u2oInterconnName := fmt.Sprintf(util.U2OInterconnName, subnet.Spec.Vpc, subnet.Name)
×
1942
                u2oInterconnLrpName := fmt.Sprintf("%s-%s", subnet.Spec.Vpc, subnet.Name)
×
1943
                var v4ip, v6ip string
×
1944
                if subnet.Spec.U2OInterconnection {
×
1945
                        v4ip, v6ip, _, err = c.acquireU2OIP(subnet, u2oInterconnName, u2oInterconnLrpName)
×
1946
                        if err != nil {
×
1947
                                return isU2OIPChanged, isMcastQuerierIPChanged, err
×
1948
                        }
×
1949

1950
                        if v4ip != "" || v6ip != "" {
×
1951
                                isU2OIPChanged = true
×
1952
                        }
×
1953
                } else if subnet.Status.U2OInterconnectionIP != "" {
×
1954
                        err = c.releaseU2OIP(subnet, u2oInterconnName)
×
1955
                        if err != nil {
×
1956
                                return isU2OIPChanged, isMcastQuerierIPChanged, err
×
1957
                        }
×
1958
                        isU2OIPChanged = true
×
1959
                }
1960

1961
                if isU2OIPChanged {
×
1962
                        klog.Infof("reconcile underlay subnet %s to overlay interconnection with U2OInterconnection %v U2OInterconnectionIP %s",
×
1963
                                subnet.Name, subnet.Spec.U2OInterconnection, subnet.Status.U2OInterconnectionIP)
×
1964
                }
×
1965
        }
1966

1967
        // reconcile mcast querier IP
1968
        if subnet.Spec.EnableMulticastSnoop {
×
1969
                isMcastQuerierIPChanged, err = c.acquireMcastQuerierIP(subnet)
×
1970
                if err != nil {
×
1971
                        return isU2OIPChanged, isMcastQuerierIPChanged, err
×
1972
                }
×
1973
        } else {
×
1974
                isMcastQuerierIPChanged, err = c.releaseMcastQuerierIP(subnet)
×
1975
                if err != nil {
×
1976
                        return isU2OIPChanged, isMcastQuerierIPChanged, err
×
1977
                }
×
1978
        }
1979

1980
        // caculate subnet status
1981
        if isU2OIPChanged || isMcastQuerierIPChanged {
×
1982
                if subnet.Spec.Protocol == kubeovnv1.ProtocolDual {
×
1983
                        if _, err := c.calcDualSubnetStatusIP(subnet); err != nil {
×
1984
                                klog.Error(err)
×
1985
                                return isU2OIPChanged, isMcastQuerierIPChanged, err
×
1986
                        }
×
1987
                } else {
×
1988
                        if _, err := c.calcSubnetStatusIP(subnet); err != nil {
×
1989
                                klog.Error(err)
×
1990
                                return isU2OIPChanged, isMcastQuerierIPChanged, err
×
1991
                        }
×
1992
                }
1993
        }
1994

1995
        return isU2OIPChanged, isMcastQuerierIPChanged, nil
×
1996
}
1997

1998
func (c *Controller) acquireU2OIP(subnet *kubeovnv1.Subnet, u2oInterconnName, u2oInterconnLrpName string) (string, string, string, error) {
×
1999
        var v4ip, v6ip, mac string
×
2000
        var err error
×
2001
        if subnet.Spec.U2OInterconnectionIP == "" && (subnet.Status.U2OInterconnectionIP == "" || subnet.Status.U2OInterconnectionMAC == "") {
×
2002
                v4ip, v6ip, mac, err = c.acquireIPAddress(subnet.Name, u2oInterconnName, u2oInterconnLrpName)
×
2003
                if err != nil {
×
2004
                        klog.Errorf("failed to acquire underlay to overlay interconnection ip address for subnet %s, %v", subnet.Name, err)
×
2005
                        return "", "", "", err
×
2006
                }
×
2007
        } else if subnet.Spec.U2OInterconnectionIP != "" && subnet.Status.U2OInterconnectionIP != subnet.Spec.U2OInterconnectionIP {
×
2008
                if subnet.Status.U2OInterconnectionIP != "" {
×
2009
                        klog.Infof("release underlay to overlay interconnection ip address %s for subnet %s", subnet.Status.U2OInterconnectionIP, subnet.Name)
×
2010
                        c.ipam.ReleaseAddressByPod(u2oInterconnName, subnet.Name)
×
2011
                }
×
2012
                v4ip, v6ip, mac, err = c.acquireStaticIPAddress(subnet.Name, u2oInterconnName, u2oInterconnLrpName, subnet.Spec.U2OInterconnectionIP, nil)
×
2013
                if err != nil {
×
2014
                        klog.Errorf("failed to acquire static underlay to overlay interconnection ip address for subnet %s, %v", subnet.Name, err)
×
2015
                        return "", "", "", err
×
2016
                }
×
2017
        }
2018
        if v4ip != "" || v6ip != "" {
×
2019
                switch subnet.Spec.Protocol {
×
2020
                case kubeovnv1.ProtocolIPv4:
×
2021
                        subnet.Status.U2OInterconnectionIP = v4ip
×
2022
                case kubeovnv1.ProtocolIPv6:
×
2023
                        subnet.Status.U2OInterconnectionIP = v6ip
×
2024
                case kubeovnv1.ProtocolDual:
×
2025
                        subnet.Status.U2OInterconnectionIP = fmt.Sprintf("%s,%s", v4ip, v6ip)
×
2026
                }
2027
                err = c.createOrUpdateIPCR("", u2oInterconnName, subnet.Status.U2OInterconnectionIP, mac, subnet.Name, "default", "", "")
×
2028
                if err != nil {
×
2029
                        klog.Errorf("failed to create or update IPs of %s : %v", u2oInterconnLrpName, err)
×
2030
                        return "", "", "", err
×
2031
                }
×
2032
                subnet.Status.U2OInterconnectionMAC = mac
×
2033
        }
2034
        return v4ip, v6ip, mac, nil
×
2035
}
2036

2037
func (c *Controller) releaseU2OIP(subnet *kubeovnv1.Subnet, u2oInterconnName string) error {
×
2038
        klog.Infof("release underlay to overlay interconnection ip address %s for subnet %s", subnet.Status.U2OInterconnectionIP, subnet.Name)
×
2039
        c.ipam.ReleaseAddressByPod(u2oInterconnName, subnet.Name)
×
2040
        subnet.Status.U2OInterconnectionIP = ""
×
2041
        subnet.Status.U2OInterconnectionMAC = ""
×
2042
        subnet.Status.U2OInterconnectionVPC = ""
×
2043

×
2044
        err := c.config.KubeOvnClient.KubeovnV1().IPs().Delete(context.Background(), u2oInterconnName, metav1.DeleteOptions{})
×
2045
        if err != nil && !k8serrors.IsNotFound(err) {
×
2046
                klog.Errorf("failed to delete ip %s, %v", u2oInterconnName, err)
×
2047
                return err
×
2048
        }
×
2049

2050
        return nil
×
2051
}
2052

2053
func (c *Controller) acquireMcastQuerierIP(subnet *kubeovnv1.Subnet) (bool, error) {
×
2054
        isMcastQuerierChanged := false
×
2055
        mcastQuerierLspName := fmt.Sprintf(util.McastQuerierName, subnet.Name)
×
2056
        var v4ip, v6ip, mac string
×
2057
        var err error
×
2058

×
2059
        if subnet.Status.McastQuerierIP == "" || subnet.Status.McastQuerierMAC == "" {
×
2060
                v4ip, v6ip, mac, err = c.acquireIPAddress(subnet.Name, mcastQuerierLspName, mcastQuerierLspName)
×
2061
                if err != nil {
×
2062
                        klog.Errorf("failed to acquire mcast querier ip address for subnet %s, %v", subnet.Name, err)
×
2063
                        return isMcastQuerierChanged, err
×
2064
                }
×
2065
        }
2066

2067
        if v4ip != "" || v6ip != "" {
×
2068
                switch subnet.Spec.Protocol {
×
2069
                case kubeovnv1.ProtocolIPv4:
×
2070
                        subnet.Status.McastQuerierIP = v4ip
×
2071
                case kubeovnv1.ProtocolIPv6:
×
2072
                        subnet.Status.McastQuerierIP = v6ip
×
2073
                case kubeovnv1.ProtocolDual:
×
2074
                        subnet.Status.McastQuerierIP = fmt.Sprintf("%s,%s", v4ip, v6ip)
×
2075
                }
2076

2077
                err := c.createOrUpdateIPCR("", mcastQuerierLspName, subnet.Status.McastQuerierIP, mac, subnet.Name, "default", "", "")
×
2078
                if err != nil {
×
2079
                        klog.Errorf("failed to create or update IPs of %s : %v", mcastQuerierLspName, err)
×
2080
                        return isMcastQuerierChanged, err
×
2081
                }
×
2082

2083
                subnet.Status.McastQuerierMAC = mac
×
2084
                klog.Infof("reconcile subnet %s mcast querier IP %s mac %s",
×
2085
                        subnet.Name, subnet.Status.McastQuerierIP, subnet.Status.McastQuerierMAC)
×
2086
                isMcastQuerierChanged = true
×
2087
        }
2088

2089
        return isMcastQuerierChanged, nil
×
2090
}
2091

2092
func (c *Controller) releaseMcastQuerierIP(subnet *kubeovnv1.Subnet) (bool, error) {
×
2093
        isMcastQuerierChanged := false
×
2094
        if subnet.Status.McastQuerierIP != "" {
×
2095
                mcastQuerierLspName := fmt.Sprintf(util.McastQuerierName, subnet.Name)
×
2096
                klog.Infof("release mcast querier ip address %s for subnet %s", subnet.Status.McastQuerierIP, subnet.Name)
×
2097
                c.ipam.ReleaseAddressByPod(mcastQuerierLspName, subnet.Name)
×
2098
                subnet.Status.McastQuerierIP = ""
×
2099
                subnet.Status.McastQuerierMAC = ""
×
2100

×
2101
                if err := c.config.KubeOvnClient.KubeovnV1().IPs().Delete(context.Background(), mcastQuerierLspName, metav1.DeleteOptions{}); err != nil {
×
2102
                        if !k8serrors.IsNotFound(err) {
×
2103
                                klog.Errorf("failed to delete ip %s, %v", mcastQuerierLspName, err)
×
2104
                                return isMcastQuerierChanged, err
×
2105
                        }
×
2106
                }
2107
                isMcastQuerierChanged = true
×
2108
                klog.Infof("reconcile subnet %s mcast querier IP %s mac %s",
×
2109
                        subnet.Name, subnet.Status.McastQuerierIP, subnet.Status.McastQuerierMAC)
×
2110
        }
2111
        return isMcastQuerierChanged, nil
×
2112
}
2113

2114
func (c *Controller) calcDualSubnetStatusIP(subnet *kubeovnv1.Subnet) (*kubeovnv1.Subnet, error) {
×
2115
        if err := util.CheckCidrs(subnet.Spec.CIDRBlock); err != nil {
×
2116
                return nil, err
×
2117
        }
×
2118
        // Get the number of pods, not ips. For one pod with two ip(v4 & v6) in dual-stack, num of Items is 1
2119
        podUsedIPs, err := c.ipsLister.List(labels.SelectorFromSet(labels.Set{subnet.Name: ""}))
×
2120
        if err != nil {
×
2121
                klog.Error(err)
×
2122
                return nil, err
×
2123
        }
×
2124
        var lenIP, lenVip, lenIptablesEip, lenOvnEip int
×
2125
        lenIP = len(podUsedIPs)
×
2126
        usingIPNums := lenIP
×
2127

×
2128
        // TODO:// replace ExcludeIps with ip pool and gw to avoid later loop
×
2129
        noGWExcludeIPs := []string{}
×
2130
        v4gw, v6gw := util.SplitStringIP(subnet.Spec.Gateway)
×
2131
        for _, excludeIP := range subnet.Spec.ExcludeIps {
×
2132
                if v4gw == excludeIP || v6gw == excludeIP {
×
2133
                        // no need to compare gateway ip with pod ip
×
2134
                        continue
×
2135
                }
2136
                noGWExcludeIPs = append(noGWExcludeIPs, excludeIP)
×
2137
        }
2138
        if noGWExcludeIPs != nil {
×
2139
                for _, podUsedIP := range podUsedIPs {
×
2140
                        for _, excludeIP := range noGWExcludeIPs {
×
2141
                                if util.ContainsIPs(excludeIP, podUsedIP.Spec.V4IPAddress) || util.ContainsIPs(excludeIP, podUsedIP.Spec.V6IPAddress) {
×
2142
                                        // This ip cr is allocated from subnet.spec.excludeIPs, do not count it as usingIPNums
×
2143
                                        usingIPNums--
×
2144
                                        break
×
2145
                                }
2146
                        }
2147
                }
2148
        }
2149

2150
        // subnet.Spec.ExcludeIps contains both v4 and v6 addresses
2151
        v4ExcludeIPs, v6ExcludeIPs := util.SplitIpsByProtocol(subnet.Spec.ExcludeIps)
×
2152
        // gateway always in excludeIPs
×
2153
        cidrBlocks := strings.Split(subnet.Spec.CIDRBlock, ",")
×
2154
        v4toSubIPs := util.ExpandExcludeIPs(v4ExcludeIPs, cidrBlocks[0])
×
2155
        v6toSubIPs := util.ExpandExcludeIPs(v6ExcludeIPs, cidrBlocks[1])
×
2156
        _, v4CIDR, _ := net.ParseCIDR(cidrBlocks[0])
×
2157
        _, v6CIDR, _ := net.ParseCIDR(cidrBlocks[1])
×
2158
        v4availableIPs := util.AddressCount(v4CIDR) - util.CountIPNums(v4toSubIPs)
×
2159
        v6availableIPs := util.AddressCount(v6CIDR) - util.CountIPNums(v6toSubIPs)
×
2160

×
2161
        usingIPs := float64(usingIPNums)
×
2162

×
2163
        vips, err := c.virtualIpsLister.List(labels.SelectorFromSet(labels.Set{
×
2164
                util.SubnetNameLabel: subnet.Name,
×
2165
                util.IPReservedLabel: "",
×
2166
        }))
×
2167
        if err != nil {
×
2168
                klog.Error(err)
×
2169
                return nil, err
×
2170
        }
×
2171
        lenVip = len(vips)
×
2172
        usingIPs += float64(lenVip)
×
2173

×
2174
        if !isOvnSubnet(subnet) {
×
2175
                eips, err := c.iptablesEipsLister.List(
×
2176
                        labels.SelectorFromSet(labels.Set{util.SubnetNameLabel: subnet.Name}))
×
2177
                if err != nil {
×
2178
                        klog.Error(err)
×
2179
                        return nil, err
×
2180
                }
×
2181
                lenIptablesEip = len(eips)
×
2182
                usingIPs += float64(lenIptablesEip)
×
2183
        }
2184
        if subnet.Spec.Vlan != "" {
×
2185
                ovnEips, err := c.ovnEipsLister.List(labels.SelectorFromSet(labels.Set{
×
2186
                        util.SubnetNameLabel: subnet.Name,
×
2187
                }))
×
2188
                if err != nil {
×
2189
                        klog.Error(err)
×
2190
                        return nil, err
×
2191
                }
×
2192
                lenOvnEip = len(ovnEips)
×
2193
                usingIPs += float64(lenOvnEip)
×
2194
        }
2195

2196
        v4availableIPs -= usingIPs
×
2197
        if v4availableIPs < 0 {
×
2198
                v4availableIPs = 0
×
2199
        }
×
2200
        v6availableIPs -= usingIPs
×
2201
        if v6availableIPs < 0 {
×
2202
                v6availableIPs = 0
×
2203
        }
×
2204

2205
        v4UsingIPStr, v6UsingIPStr, v4AvailableIPStr, v6AvailableIPStr := c.ipam.GetSubnetIPRangeString(subnet.Name, subnet.Spec.ExcludeIps)
×
2206

×
2207
        if subnet.Status.V4AvailableIPs == v4availableIPs &&
×
2208
                subnet.Status.V6AvailableIPs == v6availableIPs &&
×
2209
                subnet.Status.V4UsingIPs == usingIPs &&
×
2210
                subnet.Status.V6UsingIPs == usingIPs &&
×
2211
                subnet.Status.V4UsingIPRange == v4UsingIPStr &&
×
2212
                subnet.Status.V6UsingIPRange == v6UsingIPStr &&
×
2213
                subnet.Status.V4AvailableIPRange == v4AvailableIPStr &&
×
2214
                subnet.Status.V6AvailableIPRange == v6AvailableIPStr {
×
2215
                return subnet, nil
×
2216
        }
×
2217

2218
        subnet.Status.V4AvailableIPs = v4availableIPs
×
2219
        subnet.Status.V6AvailableIPs = v6availableIPs
×
2220
        subnet.Status.V4UsingIPs = usingIPs
×
2221
        subnet.Status.V6UsingIPs = usingIPs
×
2222
        subnet.Status.V4UsingIPRange = v4UsingIPStr
×
2223
        subnet.Status.V6UsingIPRange = v6UsingIPStr
×
2224
        subnet.Status.V4AvailableIPRange = v4AvailableIPStr
×
2225
        subnet.Status.V6AvailableIPRange = v6AvailableIPStr
×
2226
        bytes, err := subnet.Status.Bytes()
×
2227
        if err != nil {
×
2228
                klog.Error(err)
×
2229
                return nil, err
×
2230
        }
×
2231
        newSubnet, err := c.config.KubeOvnClient.KubeovnV1().Subnets().Patch(context.Background(), subnet.Name, types.MergePatchType, bytes, metav1.PatchOptions{}, "status")
×
2232
        return newSubnet, err
×
2233
}
2234

2235
func (c *Controller) calcSubnetStatusIP(subnet *kubeovnv1.Subnet) (*kubeovnv1.Subnet, error) {
×
2236
        _, cidr, err := net.ParseCIDR(subnet.Spec.CIDRBlock)
×
2237
        if err != nil {
×
2238
                klog.Error(err)
×
2239
                return nil, err
×
2240
        }
×
2241
        var lenIP, lenVip, lenIptablesEip, lenOvnEip int
×
2242
        podUsedIPs, err := c.ipsLister.List(labels.SelectorFromSet(labels.Set{subnet.Name: ""}))
×
2243
        if err != nil {
×
2244
                klog.Error(err)
×
2245
                return nil, err
×
2246
        }
×
2247
        lenIP = len(podUsedIPs)
×
2248
        usingIPNums := lenIP
×
2249

×
2250
        // TODO:// replace ExcludeIps with ip pool and gw to avoid later loop
×
2251
        noGWExcludeIPs := []string{}
×
2252
        v4gw, v6gw := util.SplitStringIP(subnet.Spec.Gateway)
×
2253
        for _, excludeIP := range subnet.Spec.ExcludeIps {
×
2254
                if v4gw == excludeIP || v6gw == excludeIP {
×
2255
                        // no need to compare gateway ip with pod ip
×
2256
                        continue
×
2257
                }
2258
                noGWExcludeIPs = append(noGWExcludeIPs, excludeIP)
×
2259
        }
2260
        if noGWExcludeIPs != nil {
×
2261
                for _, podUsedIP := range podUsedIPs {
×
2262
                        for _, excludeIP := range noGWExcludeIPs {
×
2263
                                if util.ContainsIPs(excludeIP, podUsedIP.Spec.V4IPAddress) || util.ContainsIPs(excludeIP, podUsedIP.Spec.V6IPAddress) {
×
2264
                                        // This ip cr is allocated from subnet.spec.excludeIPs, do not count it as usingIPNums
×
2265
                                        usingIPNums--
×
2266
                                        break
×
2267
                                }
2268
                        }
2269
                }
2270
        }
2271

2272
        // gateway always in excludeIPs
2273
        toSubIPs := util.ExpandExcludeIPs(subnet.Spec.ExcludeIps, subnet.Spec.CIDRBlock)
×
2274
        availableIPs := util.AddressCount(cidr) - util.CountIPNums(toSubIPs)
×
2275
        usingIPs := float64(usingIPNums)
×
2276
        vips, err := c.virtualIpsLister.List(labels.SelectorFromSet(labels.Set{
×
2277
                util.SubnetNameLabel: subnet.Name,
×
2278
                util.IPReservedLabel: "",
×
2279
        }))
×
2280
        if err != nil {
×
2281
                klog.Error(err)
×
2282
                return nil, err
×
2283
        }
×
2284
        lenVip = len(vips)
×
2285
        usingIPs += float64(lenVip)
×
2286
        if !isOvnSubnet(subnet) {
×
2287
                eips, err := c.iptablesEipsLister.List(
×
2288
                        labels.SelectorFromSet(labels.Set{util.SubnetNameLabel: subnet.Name}))
×
2289
                if err != nil {
×
2290
                        klog.Error(err)
×
2291
                        return nil, err
×
2292
                }
×
2293
                lenIptablesEip = len(eips)
×
2294
                usingIPs += float64(lenIptablesEip)
×
2295
        }
2296
        if subnet.Spec.Vlan != "" {
×
2297
                ovnEips, err := c.ovnEipsLister.List(labels.SelectorFromSet(labels.Set{
×
2298
                        util.SubnetNameLabel: subnet.Name,
×
2299
                }))
×
2300
                if err != nil {
×
2301
                        klog.Error(err)
×
2302
                        return nil, err
×
2303
                }
×
2304
                lenOvnEip = len(ovnEips)
×
2305
                usingIPs += float64(lenOvnEip)
×
2306
        }
2307

2308
        availableIPs -= usingIPs
×
2309
        if availableIPs < 0 {
×
2310
                availableIPs = 0
×
2311
        }
×
2312

2313
        v4UsingIPStr, v6UsingIPStr, v4AvailableIPStr, v6AvailableIPStr := c.ipam.GetSubnetIPRangeString(subnet.Name, subnet.Spec.ExcludeIps)
×
2314
        cachedFloatFields := [4]float64{
×
2315
                subnet.Status.V4AvailableIPs,
×
2316
                subnet.Status.V4UsingIPs,
×
2317
                subnet.Status.V6AvailableIPs,
×
2318
                subnet.Status.V6UsingIPs,
×
2319
        }
×
2320
        cachedStringFields := [4]string{
×
2321
                subnet.Status.V4UsingIPRange,
×
2322
                subnet.Status.V4AvailableIPRange,
×
2323
                subnet.Status.V6UsingIPRange,
×
2324
                subnet.Status.V6AvailableIPRange,
×
2325
        }
×
2326

×
2327
        if subnet.Spec.Protocol == kubeovnv1.ProtocolIPv4 {
×
2328
                subnet.Status.V4AvailableIPs = availableIPs
×
2329
                subnet.Status.V4UsingIPs = usingIPs
×
2330
                subnet.Status.V4UsingIPRange = v4UsingIPStr
×
2331
                subnet.Status.V4AvailableIPRange = v4AvailableIPStr
×
2332
                subnet.Status.V6AvailableIPs = 0
×
2333
                subnet.Status.V6UsingIPs = 0
×
2334
        } else {
×
2335
                subnet.Status.V6AvailableIPs = availableIPs
×
2336
                subnet.Status.V6UsingIPs = usingIPs
×
2337
                subnet.Status.V6UsingIPRange = v6UsingIPStr
×
2338
                subnet.Status.V6AvailableIPRange = v6AvailableIPStr
×
2339
                subnet.Status.V4AvailableIPs = 0
×
2340
                subnet.Status.V4UsingIPs = 0
×
2341
        }
×
2342
        if cachedFloatFields == [4]float64{
×
2343
                subnet.Status.V4AvailableIPs,
×
2344
                subnet.Status.V4UsingIPs,
×
2345
                subnet.Status.V6AvailableIPs,
×
2346
                subnet.Status.V6UsingIPs,
×
2347
        } && cachedStringFields == [4]string{
×
2348
                subnet.Status.V4UsingIPRange,
×
2349
                subnet.Status.V4AvailableIPRange,
×
2350
                subnet.Status.V6UsingIPRange,
×
2351
                subnet.Status.V6AvailableIPRange,
×
2352
        } {
×
2353
                return subnet, nil
×
2354
        }
×
2355

2356
        bytes, err := subnet.Status.Bytes()
×
2357
        if err != nil {
×
2358
                klog.Error(err)
×
2359
                return nil, err
×
2360
        }
×
2361
        newSubnet, err := c.config.KubeOvnClient.KubeovnV1().Subnets().Patch(context.Background(), subnet.Name, types.MergePatchType, bytes, metav1.PatchOptions{}, "status")
×
2362
        return newSubnet, err
×
2363
}
2364

2365
func (c *Controller) checkSubnetUsingIPs(subnet *kubeovnv1.Subnet) error {
×
2366
        if subnet.Status.V4UsingIPs != 0 && subnet.Status.V4UsingIPRange == "" {
×
2367
                err := fmt.Errorf("subnet %s has %.0f v4 ip in use, while the v4 using ip range is empty", subnet.Name, subnet.Status.V4UsingIPs)
×
2368
                klog.Error(err)
×
2369
                return err
×
2370
        }
×
2371
        if subnet.Status.V6UsingIPs != 0 && subnet.Status.V6UsingIPRange == "" {
×
2372
                err := fmt.Errorf("subnet %s has %.0f v6 ip in use, while the v6 using ip range is empty", subnet.Name, subnet.Status.V6UsingIPs)
×
2373
                klog.Error(err)
×
2374
                return err
×
2375
        }
×
2376
        return nil
×
2377
}
2378

2379
func isOvnSubnet(subnet *kubeovnv1.Subnet) bool {
1✔
2380
        return subnet != nil && util.IsOvnProvider(subnet.Spec.Provider)
1✔
2381
}
1✔
2382

2383
func checkAndFormatsExcludeIPs(subnet *kubeovnv1.Subnet) bool {
1✔
2384
        var excludeIPs []string
1✔
2385
        mapIPs := make(map[string]*ipam.IPRange, len(subnet.Spec.ExcludeIps))
1✔
2386
        for _, excludeIP := range subnet.Spec.ExcludeIps {
2✔
2387
                if _, ok := mapIPs[excludeIP]; !ok {
2✔
2388
                        ips := strings.Split(excludeIP, "..")
1✔
2389
                        start, _ := ipam.NewIP(ips[0])
1✔
2390
                        end := start
1✔
2391
                        if len(ips) != 1 {
1✔
2392
                                end, _ = ipam.NewIP(ips[1])
×
2393
                        }
×
2394
                        mapIPs[excludeIP] = ipam.NewIPRange(start, end)
1✔
2395
                }
2396
        }
2397
        newMap := filterRepeatIPRange(mapIPs)
1✔
2398
        for _, v := range newMap {
2✔
2399
                if v.Start().Equal(v.End()) {
2✔
2400
                        excludeIPs = append(excludeIPs, v.Start().String())
1✔
2401
                } else {
1✔
2402
                        excludeIPs = append(excludeIPs, v.Start().String()+".."+v.End().String())
×
2403
                }
×
2404
        }
2405
        sort.Strings(excludeIPs)
1✔
2406
        if !slices.Equal(subnet.Spec.ExcludeIps, excludeIPs) {
1✔
2407
                klog.V(3).Infof("excludeips before format is %v, after format is %v", subnet.Spec.ExcludeIps, excludeIPs)
×
2408
                subnet.Spec.ExcludeIps = excludeIPs
×
2409
                return true
×
2410
        }
×
2411
        return false
1✔
2412
}
2413

2414
func filterRepeatIPRange(mapIPs map[string]*ipam.IPRange) map[string]*ipam.IPRange {
1✔
2415
        for ka, a := range mapIPs {
2✔
2416
                for kb, b := range mapIPs {
2✔
2417
                        if ka == kb && a == b {
2✔
2418
                                continue
1✔
2419
                        }
2420

2421
                        if b.End().LessThan(a.Start()) || b.Start().GreaterThan(a.End()) {
2✔
2422
                                continue
1✔
2423
                        }
2424

2425
                        if (a.Start().Equal(b.Start()) || a.Start().GreaterThan(b.Start())) &&
×
2426
                                (a.End().Equal(b.End()) || a.End().LessThan(b.End())) {
×
2427
                                delete(mapIPs, ka)
×
2428
                                continue
×
2429
                        }
2430

2431
                        if (a.Start().Equal(b.Start()) || a.Start().GreaterThan(b.Start())) &&
×
2432
                                a.End().GreaterThan(b.End()) {
×
2433
                                delete(mapIPs, ka)
×
2434
                                mapIPs[kb] = ipam.NewIPRange(b.Start(), a.End())
×
2435
                                continue
×
2436
                        }
2437

2438
                        if (a.End().Equal(b.End()) || a.End().LessThan(b.End())) &&
×
2439
                                a.Start().LessThan(b.Start()) {
×
2440
                                delete(mapIPs, ka)
×
2441
                                mapIPs[kb] = ipam.NewIPRange(a.Start(), b.End())
×
2442
                                continue
×
2443
                        }
2444

2445
                        // a contains b
2446
                        mapIPs[kb] = a
×
2447
                        delete(mapIPs, ka)
×
2448
                }
2449
        }
2450
        return mapIPs
1✔
2451
}
2452

2453
func (c *Controller) checkGwNodeExists(gatewayNode string) bool {
×
2454
        found := false
×
2455
        for gwName := range strings.SplitSeq(gatewayNode, ",") {
×
2456
                // the format of gatewayNode can be like 'kube-ovn-worker:172.18.0.2, kube-ovn-control-plane:172.18.0.3', which consists of node name and designative egress ip
×
2457
                if strings.Contains(gwName, ":") {
×
2458
                        gwName = strings.TrimSpace(strings.Split(gwName, ":")[0])
×
2459
                } else {
×
2460
                        gwName = strings.TrimSpace(gwName)
×
2461
                }
×
2462

2463
                gwNode, err := c.nodesLister.Get(gwName)
×
2464
                if err != nil {
×
2465
                        if k8serrors.IsNotFound(err) {
×
2466
                                klog.Errorf("gw node %s does not exist, %v", gwName, err)
×
2467
                                continue
×
2468
                        }
2469
                }
2470
                if gwNode != nil {
×
2471
                        found = true
×
2472
                        break
×
2473
                }
2474
        }
2475
        return found
×
2476
}
2477

2478
func getIPSuffix(protocol string) string {
1✔
2479
        if protocol == kubeovnv1.ProtocolIPv6 {
1✔
NEW
2480
                return "ip6"
×
NEW
2481
        }
×
2482
        return "ip4"
1✔
2483
}
2484

2485
func buildPolicyRouteExternalIDs(subnetName string, extraIDs map[string]string) map[string]string {
1✔
2486
        externalIDs := map[string]string{
1✔
2487
                "vendor": util.CniTypeName,
1✔
2488
                "subnet": subnetName,
1✔
2489
        }
1✔
2490
        maps.Copy(externalIDs, extraIDs)
1✔
2491
        return externalIDs
1✔
2492
}
1✔
2493

2494
func (c *Controller) addCommonRoutesForSubnet(subnet *kubeovnv1.Subnet) error {
×
2495
        for cidr := range strings.SplitSeq(subnet.Spec.CIDRBlock, ",") {
×
2496
                if cidr == "" {
×
2497
                        continue
×
2498
                }
2499

2500
                var gateway string
×
2501
                protocol := util.CheckProtocol(cidr)
×
2502
                for gw := range strings.SplitSeq(subnet.Spec.Gateway, ",") {
×
2503
                        if util.CheckProtocol(gw) == protocol {
×
2504
                                gateway = gw
×
2505
                                break
×
2506
                        }
2507
                }
2508
                if gateway == "" {
×
2509
                        return fmt.Errorf("failed to get gateway of CIDR %s", cidr)
×
2510
                }
×
2511

2512
                // policy route
2513
                af := 4
×
2514
                if protocol == kubeovnv1.ProtocolIPv6 {
×
2515
                        af = 6
×
2516
                }
×
2517

2518
                var (
×
2519
                        match       = fmt.Sprintf("ip%d.dst == %s", af, cidr)
×
2520
                        action      = kubeovnv1.PolicyRouteActionAllow
×
2521
                        externalIDs = map[string]string{"vendor": util.CniTypeName, "subnet": subnet.Name}
×
2522
                )
×
2523
                klog.Infof("add common policy route for router: %s, match %s, action %s, externalID %v", subnet.Spec.Vpc, match, action, externalIDs)
×
2524
                if err := c.addPolicyRouteToVpc(
×
2525
                        subnet.Spec.Vpc,
×
2526
                        &kubeovnv1.PolicyRoute{
×
2527
                                Priority: util.SubnetRouterPolicyPriority,
×
2528
                                Match:    match,
×
2529
                                Action:   action,
×
2530
                        },
×
2531
                        externalIDs,
×
2532
                ); err != nil {
×
2533
                        klog.Errorf("failed to add logical router policy for CIDR %s of subnet %s: %v", cidr, subnet.Name, err)
×
2534
                        return err
×
2535
                }
×
2536
        }
2537
        return nil
×
2538
}
2539

2540
func getOverlaySubnetsPortGroupName(subnetName, nodeName string) string {
×
2541
        return strings.ReplaceAll(fmt.Sprintf("%s.%s", subnetName, nodeName), "-", ".")
×
2542
}
×
2543

2544
func (c *Controller) createPortGroupForDistributedSubnet(node *v1.Node, subnet *kubeovnv1.Subnet) error {
×
2545
        if subnet.Spec.Vlan != "" && !subnet.Spec.LogicalGateway {
×
2546
                return nil
×
2547
        }
×
2548
        if subnet.Spec.Vpc != c.config.ClusterRouter || subnet.Name == c.config.NodeSwitch {
×
2549
                return nil
×
2550
        }
×
2551

2552
        pgName := getOverlaySubnetsPortGroupName(subnet.Name, node.Name)
×
2553
        externalIDs := map[string]string{
×
2554
                "subnet":         subnet.Name,
×
2555
                "node":           node.Name,
×
2556
                "vendor":         util.CniTypeName,
×
2557
                networkPolicyKey: subnet.Name + "/" + node.Name,
×
2558
        }
×
2559
        if err := c.OVNNbClient.CreatePortGroup(pgName, externalIDs); err != nil {
×
2560
                klog.Errorf("create port group for subnet %s and node %s: %v", subnet.Name, node.Name, err)
×
2561
                return err
×
2562
        }
×
2563

2564
        return nil
×
2565
}
2566

2567
func (c *Controller) updatePolicyRouteForCentralizedSubnet(subnetName, cidr string, nextHops []string, nameIPMap map[string]string) error {
×
2568
        ipSuffix := "ip4"
×
2569
        if util.CheckProtocol(cidr) == kubeovnv1.ProtocolIPv6 {
×
2570
                ipSuffix = "ip6"
×
2571
        }
×
2572

2573
        var (
×
2574
                match  = fmt.Sprintf("%s.src == %s", ipSuffix, cidr)
×
2575
                action = kubeovnv1.PolicyRouteActionReroute
×
2576
                // there's no way to update policy route when gatewayNode changed for subnet, so delete and readd policy route
×
2577
                // The delete operation is processed in AddPolicyRoute if the policy route is inconsistent, so no need delete here
×
2578
                externalIDs = map[string]string{
×
2579
                        "vendor": util.CniTypeName,
×
2580
                        "subnet": subnetName,
×
2581
                }
×
2582
        )
×
2583
        // It's difficult to delete policy route when delete node,
×
2584
        // add map nodeName:nodeIP to external_ids to help process when delete node
×
2585
        maps.Copy(externalIDs, nameIPMap)
×
2586
        klog.Infof("add policy route for router: %s, match %s, action %s, nexthops %v, externalID %s", c.config.ClusterRouter, match, action, nextHops, externalIDs)
×
2587
        if err := c.addPolicyRouteToVpc(
×
2588
                c.config.ClusterRouter,
×
2589
                &kubeovnv1.PolicyRoute{
×
2590
                        Priority:  util.GatewayRouterPolicyPriority,
×
2591
                        Match:     match,
×
2592
                        Action:    action,
×
2593
                        NextHopIP: strings.Join(nextHops, ","),
×
2594
                },
×
2595
                externalIDs,
×
2596
        ); err != nil {
×
2597
                klog.Errorf("failed to add policy route for centralized subnet %s: %v", subnetName, err)
×
2598
                return err
×
2599
        }
×
2600
        return nil
×
2601
}
2602

2603
func (c *Controller) addPolicyRouteForCentralizedSubnet(subnet *kubeovnv1.Subnet, nodeName string, ipNameMap map[string]string, nodeIPs []string) error {
×
2604
        for _, nodeIP := range nodeIPs {
×
2605
                // node v4ip v6ip
×
2606
                for cidrBlock := range strings.SplitSeq(subnet.Spec.CIDRBlock, ",") {
×
2607
                        if util.CheckProtocol(cidrBlock) != util.CheckProtocol(nodeIP) {
×
2608
                                continue
×
2609
                        }
2610
                        // Check for repeat policy route is processed in AddPolicyRoute
2611

2612
                        var nextHops []string
×
2613
                        nameIPMap := map[string]string{}
×
2614
                        nextHops = append(nextHops, nodeIP)
×
2615
                        tmpName := nodeName
×
2616
                        if nodeName == "" {
×
2617
                                tmpName = ipNameMap[nodeIP]
×
2618
                        }
×
2619
                        nameIPMap[tmpName] = nodeIP
×
2620
                        if err := c.updatePolicyRouteForCentralizedSubnet(subnet.Name, cidrBlock, nextHops, nameIPMap); err != nil {
×
2621
                                klog.Error(err)
×
2622
                                return err
×
2623
                        }
×
2624
                }
2625
        }
2626
        return nil
×
2627
}
2628

2629
func (c *Controller) deletePolicyRouteForCentralizedSubnet(subnet *kubeovnv1.Subnet) error {
×
2630
        for cidr := range strings.SplitSeq(subnet.Spec.CIDRBlock, ",") {
×
2631
                ipSuffix := "ip4"
×
2632
                if util.CheckProtocol(cidr) == kubeovnv1.ProtocolIPv6 {
×
2633
                        ipSuffix = "ip6"
×
2634
                }
×
2635
                match := fmt.Sprintf("%s.src == %s", ipSuffix, cidr)
×
2636
                klog.Infof("delete policy route for router: %s, priority: %d, match %s", c.config.ClusterRouter, util.GatewayRouterPolicyPriority, match)
×
2637
                if err := c.deletePolicyRouteFromVpc(c.config.ClusterRouter, util.GatewayRouterPolicyPriority, match); err != nil {
×
2638
                        klog.Errorf("failed to delete policy route for centralized subnet %s: %v", subnet.Name, err)
×
2639
                        return err
×
2640
                }
×
2641
        }
2642
        return nil
×
2643
}
2644

2645
func (c *Controller) addPolicyRouteForDistributedSubnet(subnet *kubeovnv1.Subnet, nodeName, nodeIPv4, nodeIPv6 string) error {
×
2646
        if subnet.Spec.Vlan != "" && !subnet.Spec.LogicalGateway {
×
2647
                return nil
×
2648
        }
×
2649
        if subnet.Spec.Vpc != c.config.ClusterRouter || subnet.Name == c.config.NodeSwitch {
×
2650
                return nil
×
2651
        }
×
2652

2653
        pgName := getOverlaySubnetsPortGroupName(subnet.Name, nodeName)
×
2654
        for cidrBlock := range strings.SplitSeq(subnet.Spec.CIDRBlock, ",") {
×
2655
                ipSuffix, nodeIP := "ip4", nodeIPv4
×
2656
                if util.CheckProtocol(cidrBlock) == kubeovnv1.ProtocolIPv6 {
×
2657
                        ipSuffix, nodeIP = "ip6", nodeIPv6
×
2658
                }
×
2659
                if nodeIP == "" {
×
2660
                        continue
×
2661
                }
2662

2663
                var (
×
2664
                        pgAs        = fmt.Sprintf("%s_%s", pgName, ipSuffix)
×
2665
                        match       = fmt.Sprintf("%s.src == $%s", ipSuffix, pgAs)
×
2666
                        action      = kubeovnv1.PolicyRouteActionReroute
×
2667
                        externalIDs = map[string]string{
×
2668
                                "vendor": util.CniTypeName,
×
2669
                                "subnet": subnet.Name,
×
2670
                                "node":   nodeName,
×
2671
                        }
×
2672
                )
×
2673

×
2674
                klog.Infof("add policy route for router: %s, match %s, action %s, externalID %v", c.config.ClusterRouter, match, action, externalIDs)
×
2675
                if err := c.addPolicyRouteToVpc(
×
2676
                        c.config.ClusterRouter,
×
2677
                        &kubeovnv1.PolicyRoute{
×
2678
                                Priority:  util.GatewayRouterPolicyPriority,
×
2679
                                Match:     match,
×
2680
                                Action:    action,
×
2681
                                NextHopIP: nodeIP,
×
2682
                        },
×
2683
                        externalIDs,
×
2684
                ); err != nil {
×
2685
                        klog.Errorf("failed to add logical router policy for port-group address-set %s: %v", pgAs, err)
×
2686
                        return err
×
2687
                }
×
2688
        }
2689
        return nil
×
2690
}
2691

2692
func (c *Controller) deletePolicyRouteForDistributedSubnet(subnet *kubeovnv1.Subnet, nodeName string) error {
×
2693
        pgName := getOverlaySubnetsPortGroupName(subnet.Name, nodeName)
×
2694
        for cidrBlock := range strings.SplitSeq(subnet.Spec.CIDRBlock, ",") {
×
2695
                ipSuffix := "ip4"
×
2696
                if util.CheckProtocol(cidrBlock) == kubeovnv1.ProtocolIPv6 {
×
2697
                        ipSuffix = "ip6"
×
2698
                }
×
2699
                pgAs := fmt.Sprintf("%s_%s", pgName, ipSuffix)
×
2700
                match := fmt.Sprintf("%s.src == $%s", ipSuffix, pgAs)
×
2701
                klog.Infof("delete policy route for router: %s, priority: %d, match: %q", c.config.ClusterRouter, util.GatewayRouterPolicyPriority, match)
×
2702
                if err := c.deletePolicyRouteFromVpc(c.config.ClusterRouter, util.GatewayRouterPolicyPriority, match); err != nil {
×
2703
                        klog.Errorf("failed to delete policy route for subnet %s: %v", subnet.Name, err)
×
2704
                        return err
×
2705
                }
×
2706
        }
2707
        return nil
×
2708
}
2709

2710
func (c *Controller) deletePolicyRouteByGatewayType(subnet *kubeovnv1.Subnet, gatewayType string, isDelete bool) error {
×
2711
        if (subnet.Spec.Vlan != "" && !subnet.Spec.LogicalGateway) || subnet.Spec.Vpc != c.config.ClusterRouter {
×
2712
                return nil
×
2713
        }
×
2714

2715
        for cidr := range strings.SplitSeq(subnet.Spec.CIDRBlock, ",") {
×
2716
                if cidr == "" || !isDelete {
×
2717
                        continue
×
2718
                }
2719

2720
                af := 4
×
2721
                if util.CheckProtocol(cidr) == kubeovnv1.ProtocolIPv6 {
×
2722
                        af = 6
×
2723
                }
×
2724
                match := fmt.Sprintf("ip%d.dst == %s", af, cidr)
×
2725
                klog.Infof("delete policy route for router: %s, priority: %d, match %s", c.config.ClusterRouter, util.SubnetRouterPolicyPriority, match)
×
2726
                if err := c.deletePolicyRouteFromVpc(c.config.ClusterRouter, util.SubnetRouterPolicyPriority, match); err != nil {
×
2727
                        klog.Errorf("failed to delete logical router policy for CIDR %s of subnet %s: %v", cidr, subnet.Name, err)
×
2728
                        return err
×
2729
                }
×
2730
        }
2731
        if subnet.Name == c.config.NodeSwitch {
×
2732
                return nil
×
2733
        }
×
2734

2735
        if gatewayType == kubeovnv1.GWDistributedType {
×
2736
                nodes, err := c.nodesLister.List(labels.Everything())
×
2737
                if err != nil {
×
2738
                        klog.Errorf("list nodes: %v", err)
×
2739
                        return err
×
2740
                }
×
2741
                for _, node := range nodes {
×
2742
                        pgName := getOverlaySubnetsPortGroupName(subnet.Name, node.Name)
×
2743
                        if err = c.OVNNbClient.DeletePortGroup(pgName); err != nil {
×
2744
                                klog.Errorf("delete port group for subnet %s and node %s: %v", subnet.Name, node.Name, err)
×
2745
                                return err
×
2746
                        }
×
2747

2748
                        if err = c.deletePolicyRouteForDistributedSubnet(subnet, node.Name); err != nil {
×
2749
                                klog.Errorf("delete policy route for subnet %s and node %s: %v", subnet.Name, node.Name, err)
×
2750
                                return err
×
2751
                        }
×
2752
                }
2753
        }
2754

2755
        if gatewayType == kubeovnv1.GWCentralizedType {
×
2756
                klog.Infof("delete policy route for centralized subnet %s", subnet.Name)
×
2757
                if err := c.deletePolicyRouteForCentralizedSubnet(subnet); err != nil {
×
2758
                        klog.Errorf("delete policy route for subnet %s: %v", subnet.Name, err)
×
2759
                        return err
×
2760
                }
×
2761
        }
2762

2763
        return nil
×
2764
}
2765

2766
func (c *Controller) addPolicyRouteForU2OInterconn(subnet *kubeovnv1.Subnet) error {
1✔
2767
        v4Gw, v6Gw := util.SplitStringIP(subnet.Spec.Gateway)
1✔
2768
        overlayOnly := u2oOverlayOnlyRoutingEnabled(subnet)
1✔
2769

1✔
2770
        externalIDs := buildPolicyRouteExternalIDs(subnet.Name, map[string]string{"isU2ORoutePolicy": "true"})
1✔
2771

1✔
2772
        u2oExcludeIP4Ag := strings.ReplaceAll(fmt.Sprintf(util.U2OExcludeIPAg, subnet.Name, "ip4"), "-", ".")
1✔
2773
        u2oExcludeIP6Ag := strings.ReplaceAll(fmt.Sprintf(util.U2OExcludeIPAg, subnet.Name, "ip6"), "-", ".")
1✔
2774

1✔
2775
        nodes, err := c.nodesLister.List(labels.Everything())
1✔
2776
        if err != nil {
1✔
2777
                klog.Errorf("failed to list nodes: %v", err)
×
2778
                return err
×
2779
        }
×
2780

2781
        var nodesIPv4, nodesIPv6 []string
1✔
2782
        for _, node := range nodes {
1✔
2783
                nodeIPv4, nodeIPv6 := util.GetNodeInternalIP(*node)
×
2784

×
2785
                if nodeIPv4 != "" {
×
2786
                        nodesIPv4 = append(nodesIPv4, nodeIPv4)
×
2787
                }
×
2788
                if nodeIPv6 != "" {
×
2789
                        nodesIPv6 = append(nodesIPv6, nodeIPv6)
×
2790
                }
×
2791
        }
2792

2793
        if err := c.OVNNbClient.CreateAddressSet(u2oExcludeIP4Ag, externalIDs); err != nil {
1✔
2794
                klog.Errorf("create address set %s: %v", u2oExcludeIP4Ag, err)
×
2795
                return err
×
2796
        }
×
2797

2798
        if err := c.OVNNbClient.CreateAddressSet(u2oExcludeIP6Ag, externalIDs); err != nil {
1✔
2799
                klog.Errorf("create address set %s: %v", u2oExcludeIP6Ag, err)
×
2800
                return err
×
2801
        }
×
2802

2803
        if len(nodesIPv4) > 0 {
1✔
2804
                if err := c.OVNNbClient.AddressSetUpdateAddress(u2oExcludeIP4Ag, nodesIPv4...); err != nil {
×
2805
                        klog.Errorf("set v4 address set %s with address %v: %v", u2oExcludeIP4Ag, nodesIPv4, err)
×
2806
                        return err
×
2807
                }
×
2808
        }
2809

2810
        if len(nodesIPv6) > 0 {
1✔
2811
                if err := c.OVNNbClient.AddressSetUpdateAddress(u2oExcludeIP6Ag, nodesIPv6...); err != nil {
×
2812
                        klog.Errorf("set v6 address set %s with address %v: %v", u2oExcludeIP6Ag, nodesIPv6, err)
×
2813
                        return err
×
2814
                }
×
2815
        }
2816

2817
        overlayCIDRs4Ag, overlayCIDRs6Ag := u2oOverlayCIDRsAddressSetNames(subnet.Spec.Vpc)
1✔
2818
        if overlayOnly {
2✔
2819
                if _, _, err := c.syncU2OOverlayCIDRsAddressSet(subnet.Spec.Vpc, ""); err != nil {
1✔
NEW
2820
                        return err
×
NEW
2821
                }
×
2822
        }
2823

2824
        overlayOnlyExternalIDs := buildPolicyRouteExternalIDs(subnet.Name, map[string]string{
1✔
2825
                "isU2ORoutePolicy":            "true",
1✔
2826
                "isU2OOverlayOnlyRoutePolicy": "true",
1✔
2827
        })
1✔
2828
        desiredPolicies := make(map[string]struct{})
1✔
2829

1✔
2830
        for cidrBlock := range strings.SplitSeq(subnet.Spec.CIDRBlock, ",") {
2✔
2831
                ipSuffix := getIPSuffix(util.CheckProtocol(cidrBlock))
1✔
2832
                nextHop := v4Gw
1✔
2833
                u2oExcludeIPAs := u2oExcludeIP4Ag
1✔
2834
                overlayCIDRsAg := overlayCIDRs4Ag
1✔
2835
                if ipSuffix == "ip6" {
1✔
2836
                        nextHop = v6Gw
×
NEW
2837
                        u2oExcludeIPAs = u2oExcludeIP6Ag
×
NEW
2838
                        overlayCIDRsAg = overlayCIDRs6Ag
×
UNCOV
2839
                }
×
2840

2841
                match1 := fmt.Sprintf("%s.dst == %s", ipSuffix, cidrBlock)
1✔
2842
                match2 := fmt.Sprintf("%s.dst == $%s && %s.src == %s", ipSuffix, u2oExcludeIPAs, ipSuffix, cidrBlock)
1✔
2843
                match3 := fmt.Sprintf("%s.src == %s", ipSuffix, cidrBlock)
1✔
2844
                matchSameSubnet := fmt.Sprintf("%s.src == %s && %s.dst == %s", ipSuffix, cidrBlock, ipSuffix, cidrBlock)
1✔
2845
                matchOverlayToUnderlay := fmt.Sprintf("%s.src == $%s && %s.dst == %s", ipSuffix, overlayCIDRsAg, ipSuffix, cidrBlock)
1✔
2846

1✔
2847
                action := kubeovnv1.PolicyRouteActionAllow
1✔
2848
                if overlayOnly {
2✔
2849
                        klog.Infof("add u2o overlay only policy for router: %s, match %s, action %s", subnet.Spec.Vpc, matchOverlayToUnderlay, action)
1✔
2850
                        if err := c.addPolicyRouteToVpc(
1✔
2851
                                subnet.Spec.Vpc,
1✔
2852
                                &kubeovnv1.PolicyRoute{
1✔
2853
                                        Priority: util.U2OSubnetPolicyPriority,
1✔
2854
                                        Match:    matchOverlayToUnderlay,
1✔
2855
                                        Action:   action,
1✔
2856
                                },
1✔
2857
                                overlayOnlyExternalIDs,
1✔
2858
                        ); err != nil {
1✔
NEW
2859
                                klog.Errorf("failed to add u2o overlay to underlay policy for subnet %s %v", subnet.Name, err)
×
NEW
2860
                                return err
×
NEW
2861
                        }
×
2862
                        desiredPolicies[logicalRouterPolicyKey(util.U2OSubnetPolicyPriority, matchOverlayToUnderlay)] = struct{}{}
1✔
2863
                }
2864

2865
                if !overlayOnly && subnet.Spec.Vpc == c.config.ClusterRouter {
1✔
2866
                        klog.Infof("add u2o interconnection policy for router: %s, match %s, action %s", subnet.Spec.Vpc, match1, action)
×
2867
                        if err := c.addPolicyRouteToVpc(
×
2868
                                subnet.Spec.Vpc,
×
2869
                                &kubeovnv1.PolicyRoute{
×
2870
                                        Priority: util.U2OSubnetPolicyPriority,
×
2871
                                        Match:    match1,
×
2872
                                        Action:   action,
×
2873
                                },
×
2874
                                externalIDs,
×
2875
                        ); err != nil {
×
2876
                                klog.Errorf("failed to add u2o interconnection policy1 for subnet %s %v", subnet.Name, err)
×
2877
                                return err
×
2878
                        }
×
NEW
2879
                        desiredPolicies[logicalRouterPolicyKey(util.U2OSubnetPolicyPriority, match1)] = struct{}{}
×
2880
                }
2881

2882
                if overlayOnly || subnet.Spec.Vpc == c.config.ClusterRouter {
2✔
2883
                        action = kubeovnv1.PolicyRouteActionReroute
1✔
2884
                        klog.Infof("add u2o interconnection policy for router: %s, match %s, action %s", subnet.Spec.Vpc, match2, action)
1✔
2885
                        currentExternalIDs := externalIDs
1✔
2886
                        if overlayOnly {
2✔
2887
                                currentExternalIDs = overlayOnlyExternalIDs
1✔
2888
                        }
1✔
2889
                        if err := c.addPolicyRouteToVpc(
1✔
2890
                                subnet.Spec.Vpc,
1✔
2891
                                &kubeovnv1.PolicyRoute{
1✔
2892
                                        Priority:  util.SubnetRouterPolicyPriority,
1✔
2893
                                        Match:     match2,
1✔
2894
                                        Action:    action,
1✔
2895
                                        NextHopIP: nextHop,
1✔
2896
                                },
1✔
2897
                                currentExternalIDs,
1✔
2898
                        ); err != nil {
1✔
2899
                                klog.Errorf("failed to add u2o interconnection policy2 for subnet %s %v", subnet.Name, err)
×
2900
                                return err
×
2901
                        }
×
2902
                        desiredPolicies[logicalRouterPolicyKey(util.SubnetRouterPolicyPriority, match2)] = struct{}{}
1✔
2903
                }
2904

2905
                if overlayOnly {
2✔
2906
                        klog.Infof("add u2o overlay only same-subnet policy for router: %s, match %s, action %s", subnet.Spec.Vpc, matchSameSubnet, kubeovnv1.PolicyRouteActionAllow)
1✔
2907
                        if err := c.addPolicyRouteToVpc(
1✔
2908
                                subnet.Spec.Vpc,
1✔
2909
                                &kubeovnv1.PolicyRoute{
1✔
2910
                                        Priority: util.U2OSameSubnetPolicyPriority,
1✔
2911
                                        Match:    matchSameSubnet,
1✔
2912
                                        Action:   kubeovnv1.PolicyRouteActionAllow,
1✔
2913
                                },
1✔
2914
                                overlayOnlyExternalIDs,
1✔
2915
                        ); err != nil {
1✔
NEW
2916
                                klog.Errorf("failed to add u2o overlay only same-subnet policy for subnet %s %v", subnet.Name, err)
×
NEW
2917
                                return err
×
NEW
2918
                        }
×
2919
                        desiredPolicies[logicalRouterPolicyKey(util.U2OSameSubnetPolicyPriority, matchSameSubnet)] = struct{}{}
1✔
2920
                }
2921

2922
                action = kubeovnv1.PolicyRouteActionReroute
1✔
2923
                physicalGatewayPolicyPriority := util.GatewayRouterPolicyPriority
1✔
2924
                if overlayOnly {
2✔
2925
                        physicalGatewayPolicyPriority = util.U2OPhysicalGatewayPolicyPriority
1✔
2926
                }
1✔
2927
                klog.Infof("add u2o interconnection policy for router: %s, match %s, action %s, nexthop %s", subnet.Spec.Vpc, match3, action, nextHop)
1✔
2928
                if err := c.addPolicyRouteToVpc(
1✔
2929
                        subnet.Spec.Vpc,
1✔
2930
                        &kubeovnv1.PolicyRoute{
1✔
2931
                                Priority:  physicalGatewayPolicyPriority,
1✔
2932
                                Match:     match3,
1✔
2933
                                Action:    action,
1✔
2934
                                NextHopIP: nextHop,
1✔
2935
                        },
1✔
2936
                        externalIDs,
1✔
2937
                ); err != nil {
1✔
2938
                        klog.Errorf("failed to add u2o interconnection policy3 for subnet %s %v", subnet.Name, err)
×
2939
                        return err
×
2940
                }
×
2941
                desiredPolicies[logicalRouterPolicyKey(physicalGatewayPolicyPriority, match3)] = struct{}{}
1✔
2942
        }
2943
        if err := c.deleteStaleU2ORoutePolicies(subnet, desiredPolicies); err != nil {
1✔
NEW
2944
                return err
×
NEW
2945
        }
×
2946
        return nil
1✔
2947
}
2948

2949
func logicalRouterPolicyKey(priority int, match string) string {
1✔
2950
        return fmt.Sprintf("%d/%s", priority, match)
1✔
2951
}
1✔
2952

2953
func u2oOverlayCIDRsAddressSetNames(vpcName string) (string, string) {
1✔
2954
        v4Name := strings.ReplaceAll(fmt.Sprintf(util.U2OOverlayCIDRs, vpcName, "ip4"), "-", ".")
1✔
2955
        v6Name := strings.ReplaceAll(fmt.Sprintf(util.U2OOverlayCIDRs, vpcName, "ip6"), "-", ".")
1✔
2956
        return v4Name, v6Name
1✔
2957
}
1✔
2958

2959
func u2oOverlayCIDRsAddressSetExternalIDs(vpcName string) map[string]string {
1✔
2960
        return map[string]string{
1✔
2961
                "isU2OOverlayCIDRs": "true",
1✔
2962
                "vpc":               vpcName,
1✔
2963
        }
1✔
2964
}
1✔
2965

2966
func (c *Controller) syncU2OOverlayCIDRsAddressSet(vpcName, excludeSubnet string) (v4CIDRs, v6CIDRs []string, err error) {
1✔
2967
        if vpcName == "" {
1✔
NEW
2968
                return nil, nil, nil
×
NEW
2969
        }
×
2970

2971
        v4Name, v6Name := u2oOverlayCIDRsAddressSetNames(vpcName)
1✔
2972
        externalIDs := u2oOverlayCIDRsAddressSetExternalIDs(vpcName)
1✔
2973
        if v4CIDRs, v6CIDRs, err = c.buildU2OOverlayCIDRs(vpcName, excludeSubnet); err != nil {
1✔
NEW
2974
                return nil, nil, err
×
NEW
2975
        }
×
2976
        if err := c.OVNNbClient.CreateAddressSet(v4Name, externalIDs); err != nil {
1✔
NEW
2977
                klog.Errorf("create address set %s: %v", v4Name, err)
×
NEW
2978
                return nil, nil, err
×
NEW
2979
        }
×
2980
        if err := c.OVNNbClient.CreateAddressSet(v6Name, externalIDs); err != nil {
1✔
NEW
2981
                klog.Errorf("create address set %s: %v", v6Name, err)
×
NEW
2982
                return nil, nil, err
×
NEW
2983
        }
×
2984
        if err := c.OVNNbClient.AddressSetUpdateAddress(v4Name, v4CIDRs...); err != nil {
1✔
NEW
2985
                klog.Errorf("set v4 address set %s with address %v: %v", v4Name, v4CIDRs, err)
×
NEW
2986
                return nil, nil, err
×
NEW
2987
        }
×
2988
        if err := c.OVNNbClient.AddressSetUpdateAddress(v6Name, v6CIDRs...); err != nil {
1✔
NEW
2989
                klog.Errorf("set v6 address set %s with address %v: %v", v6Name, v6CIDRs, err)
×
NEW
2990
                return nil, nil, err
×
NEW
2991
        }
×
2992
        return v4CIDRs, v6CIDRs, nil
1✔
2993
}
2994

2995
func (c *Controller) buildU2OOverlayCIDRs(vpcName, excludeSubnet string) (v4CIDRs, v6CIDRs []string, err error) {
1✔
2996
        subnets, err := c.subnetsLister.List(labels.Everything())
1✔
2997
        if err != nil {
1✔
NEW
2998
                klog.Errorf("failed to list subnets: %v", err)
×
NEW
2999
                return nil, nil, err
×
NEW
3000
        }
×
3001

3002
        for _, subnet := range subnets {
2✔
3003
                if subnet.Name == excludeSubnet || subnet.Spec.Vpc != vpcName || subnet.Spec.Vlan != "" {
2✔
3004
                        continue
1✔
3005
                }
3006
                for cidr := range strings.SplitSeq(subnet.Spec.CIDRBlock, ",") {
2✔
3007
                        switch util.CheckProtocol(cidr) {
1✔
3008
                        case kubeovnv1.ProtocolIPv4:
1✔
3009
                                v4CIDRs = append(v4CIDRs, cidr)
1✔
3010
                        case kubeovnv1.ProtocolIPv6:
1✔
3011
                                v6CIDRs = append(v6CIDRs, cidr)
1✔
3012
                        }
3013
                }
3014
        }
3015
        return v4CIDRs, v6CIDRs, nil
1✔
3016
}
3017

3018
func (c *Controller) deleteStaleU2ORoutePolicies(subnet *kubeovnv1.Subnet, desiredPolicies map[string]struct{}) error {
1✔
3019
        lr := subnet.Status.U2OInterconnectionVPC
1✔
3020
        if lr == "" {
1✔
NEW
3021
                lr = subnet.Spec.Vpc
×
NEW
3022
        }
×
3023

3024
        logicalRouter, err := c.OVNNbClient.GetLogicalRouter(lr, true)
1✔
3025
        if err == nil && logicalRouter == nil {
1✔
NEW
3026
                klog.Infof("logical router %s already deleted", lr)
×
NEW
3027
                return nil
×
NEW
3028
        }
×
3029

3030
        policies, err := c.OVNNbClient.ListLogicalRouterPolicies(lr, -1, map[string]string{
1✔
3031
                "isU2ORoutePolicy": "true",
1✔
3032
                "vendor":           util.CniTypeName,
1✔
3033
                "subnet":           subnet.Name,
1✔
3034
        }, true)
1✔
3035
        if err != nil {
1✔
NEW
3036
                klog.Errorf("failed to list u2o logical router policies: %v", err)
×
NEW
3037
                return err
×
NEW
3038
        }
×
3039
        for _, policy := range policies {
1✔
NEW
3040
                if policy.ExternalIDs["isU2ONoLBRoutePolicy"] == "true" {
×
NEW
3041
                        continue
×
3042
                }
NEW
3043
                if _, ok := desiredPolicies[logicalRouterPolicyKey(policy.Priority, policy.Match)]; ok {
×
NEW
3044
                        continue
×
3045
                }
NEW
3046
                klog.Infof("delete stale u2o policy for router %s with match %s priority %d", lr, policy.Match, policy.Priority)
×
NEW
3047
                if err := c.OVNNbClient.DeleteLogicalRouterPolicyByUUID(lr, policy.UUID); err != nil {
×
NEW
3048
                        klog.Errorf("failed to delete stale u2o policy for subnet %s: %v", subnet.Name, err)
×
NEW
3049
                        return err
×
NEW
3050
                }
×
3051
        }
3052
        return nil
1✔
3053
}
3054

3055
func (c *Controller) deletePolicyRouteForU2OInterconn(subnet *kubeovnv1.Subnet) error {
×
3056
        logicalRouter, err := c.OVNNbClient.GetLogicalRouter(subnet.Spec.Vpc, true)
×
3057
        if err == nil && logicalRouter == nil {
×
3058
                klog.Infof("logical router %s already deleted", subnet.Spec.Vpc)
×
3059
                return nil
×
3060
        }
×
3061
        policies, err := c.OVNNbClient.ListLogicalRouterPolicies(subnet.Spec.Vpc, -1, map[string]string{
×
3062
                "isU2ORoutePolicy": "true",
×
3063
                "vendor":           util.CniTypeName,
×
3064
                "subnet":           subnet.Name,
×
3065
        }, true)
×
3066
        if err != nil {
×
3067
                klog.Errorf("failed to list logical router policies: %v", err)
×
3068
                return err
×
3069
        }
×
3070
        if len(policies) == 0 {
×
3071
                return nil
×
3072
        }
×
3073

3074
        lr := subnet.Status.U2OInterconnectionVPC
×
3075
        if lr == "" {
×
3076
                // old version field U2OInterconnectionVPC may be "" and then use subnet.Spec.Vpc
×
3077
                lr = subnet.Spec.Vpc
×
3078
        }
×
3079

3080
        for _, policy := range policies {
×
3081
                klog.Infof("delete u2o interconnection policy for router %s with match %s priority %d", lr, policy.Match, policy.Priority)
×
3082
                if err = c.OVNNbClient.DeleteLogicalRouterPolicyByUUID(lr, policy.UUID); err != nil {
×
3083
                        klog.Errorf("failed to delete u2o interconnection policy for subnet %s: %v", subnet.Name, err)
×
3084
                        return err
×
3085
                }
×
3086
        }
3087

3088
        u2oExcludeIP4Ag := strings.ReplaceAll(fmt.Sprintf(util.U2OExcludeIPAg, subnet.Name, "ip4"), "-", ".")
×
3089
        u2oExcludeIP6Ag := strings.ReplaceAll(fmt.Sprintf(util.U2OExcludeIPAg, subnet.Name, "ip6"), "-", ".")
×
3090

×
3091
        if err := c.OVNNbClient.DeleteAddressSet(u2oExcludeIP4Ag); err != nil {
×
3092
                klog.Errorf("delete address set %s: %v", u2oExcludeIP4Ag, err)
×
3093
                return err
×
3094
        }
×
3095

3096
        if err := c.OVNNbClient.DeleteAddressSet(u2oExcludeIP6Ag); err != nil {
×
3097
                klog.Errorf("delete address set %s: %v", u2oExcludeIP6Ag, err)
×
3098
                return err
×
3099
        }
×
3100

3101
        return nil
×
3102
}
3103

3104
func (c *Controller) addCustomVPCStaticRouteForSubnet(subnet *kubeovnv1.Subnet) error {
×
3105
        if subnet.Spec.Vpc == "" {
×
3106
                return nil
×
3107
        }
×
3108

3109
        var v4Gw, v6Gw, v4Cidr, v6Cidr string
×
3110
        for gw := range strings.SplitSeq(subnet.Spec.Gateway, ",") {
×
3111
                switch util.CheckProtocol(gw) {
×
3112
                case kubeovnv1.ProtocolIPv4:
×
3113
                        v4Gw = gw
×
3114
                case kubeovnv1.ProtocolIPv6:
×
3115
                        v6Gw = gw
×
3116
                }
3117
        }
3118

3119
        for cidr := range strings.SplitSeq(subnet.Spec.CIDRBlock, ",") {
×
3120
                if util.CheckProtocol(cidr) == kubeovnv1.ProtocolIPv4 {
×
3121
                        v4Cidr = cidr
×
3122
                } else {
×
3123
                        v6Cidr = cidr
×
3124
                }
×
3125
        }
3126

3127
        if v4Gw != "" && v4Cidr != "" {
×
3128
                if err := c.addStaticRouteToVpc(
×
3129
                        subnet.Spec.Vpc,
×
3130
                        &kubeovnv1.StaticRoute{
×
3131
                                Policy:    kubeovnv1.PolicySrc,
×
3132
                                CIDR:      v4Cidr,
×
3133
                                NextHopIP: v4Gw,
×
3134
                        },
×
3135
                ); err != nil {
×
3136
                        klog.Errorf("failed to add static route, %v", err)
×
3137
                        return err
×
3138
                }
×
3139
        }
3140

3141
        if v6Gw != "" && v6Cidr != "" {
×
3142
                if err := c.addStaticRouteToVpc(
×
3143
                        subnet.Spec.Vpc,
×
3144
                        &kubeovnv1.StaticRoute{
×
3145
                                Policy:    kubeovnv1.PolicySrc,
×
3146
                                CIDR:      v6Cidr,
×
3147
                                NextHopIP: v6Gw,
×
3148
                        },
×
3149
                ); err != nil {
×
3150
                        klog.Errorf("failed to add static route, %v", err)
×
3151
                        return err
×
3152
                }
×
3153
        }
3154
        return nil
×
3155
}
3156

3157
func (c *Controller) deleteStaticRouteForU2OInterconn(subnet *kubeovnv1.Subnet) error {
×
3158
        if subnet.Spec.Vpc == "" {
×
3159
                return nil
×
3160
        }
×
3161

3162
        var v4Gw, v6Gw, v4Cidr, v6Cidr string
×
3163
        for gw := range strings.SplitSeq(subnet.Spec.Gateway, ",") {
×
3164
                switch util.CheckProtocol(gw) {
×
3165
                case kubeovnv1.ProtocolIPv4:
×
3166
                        v4Gw = gw
×
3167
                case kubeovnv1.ProtocolIPv6:
×
3168
                        v6Gw = gw
×
3169
                }
3170
        }
3171

3172
        for cidr := range strings.SplitSeq(subnet.Spec.CIDRBlock, ",") {
×
3173
                if util.CheckProtocol(cidr) == kubeovnv1.ProtocolIPv4 {
×
3174
                        v4Cidr = cidr
×
3175
                } else {
×
3176
                        v6Cidr = cidr
×
3177
                }
×
3178
        }
3179

3180
        if v4Gw != "" && v4Cidr != "" {
×
3181
                if err := c.deleteStaticRouteFromVpc(
×
3182
                        subnet.Spec.Vpc,
×
3183
                        subnet.Spec.RouteTable,
×
3184
                        v4Cidr,
×
3185
                        v4Gw,
×
3186
                        kubeovnv1.PolicySrc,
×
3187
                ); err != nil {
×
3188
                        klog.Errorf("failed to add static route, %v", err)
×
3189
                        return err
×
3190
                }
×
3191
        }
3192

3193
        if v6Gw != "" && v6Cidr != "" {
×
3194
                if err := c.deleteStaticRouteFromVpc(
×
3195
                        subnet.Spec.Vpc,
×
3196
                        subnet.Spec.RouteTable,
×
3197
                        v6Cidr,
×
3198
                        v6Gw,
×
3199
                        kubeovnv1.PolicySrc,
×
3200
                ); err != nil {
×
3201
                        klog.Errorf("failed to delete static route, %v", err)
×
3202
                        return err
×
3203
                }
×
3204
        }
3205
        return nil
×
3206
}
3207

3208
func (c *Controller) reconcileRouteTableForSubnet(subnet *kubeovnv1.Subnet) error {
×
3209
        if subnet.Spec.Vlan != "" && !subnet.Spec.U2OInterconnection {
×
3210
                return nil
×
3211
        }
×
3212

3213
        routerPortName := ovs.LogicalRouterPortName(subnet.Spec.Vpc, subnet.Name)
×
3214
        lrp, err := c.OVNNbClient.GetLogicalRouterPort(routerPortName, false)
×
3215
        if err != nil {
×
3216
                klog.Error(err)
×
3217
                return err
×
3218
        }
×
3219

3220
        rtb := lrp.Options["route_table"]
×
3221

×
3222
        // no need to update
×
3223
        if rtb == subnet.Spec.RouteTable {
×
3224
                return nil
×
3225
        }
×
3226

3227
        klog.Infof("reconcile route table %q for subnet %s", subnet.Spec.RouteTable, subnet.Name)
×
3228
        opt := map[string]string{"route_table": subnet.Spec.RouteTable}
×
3229
        if err = c.OVNNbClient.UpdateLogicalRouterPortOptions(routerPortName, opt); err != nil {
×
3230
                klog.Errorf("failed to set route table of logical router port %s to %s: %v", routerPortName, subnet.Spec.RouteTable, err)
×
3231
                return err
×
3232
        }
×
3233

3234
        return nil
×
3235
}
3236

3237
func (c *Controller) addCustomVPCPolicyRoutesForSubnet(subnet *kubeovnv1.Subnet) error {
×
3238
        return c.addCommonRoutesForSubnet(subnet)
×
3239
}
×
3240

3241
func (c *Controller) deleteCustomVPCPolicyRoutesForSubnet(subnet *kubeovnv1.Subnet) error {
×
3242
        logicalRouter, err := c.OVNNbClient.GetLogicalRouter(subnet.Spec.Vpc, true)
×
3243
        if err == nil && logicalRouter == nil {
×
3244
                klog.Infof("logical router %s already deleted", subnet.Spec.Vpc)
×
3245
                return nil
×
3246
        }
×
3247
        for cidr := range strings.SplitSeq(subnet.Spec.CIDRBlock, ",") {
×
3248
                af := 4
×
3249
                if util.CheckProtocol(cidr) == kubeovnv1.ProtocolIPv6 {
×
3250
                        af = 6
×
3251
                }
×
3252
                match := fmt.Sprintf("ip%d.dst == %s", af, cidr)
×
3253
                klog.Infof("delete policy route for router: %s, priority: %d, match %s", subnet.Spec.Vpc, util.SubnetRouterPolicyPriority, match)
×
3254
                if err := c.deletePolicyRouteFromVpc(subnet.Spec.Vpc, util.SubnetRouterPolicyPriority, match); err != nil {
×
3255
                        klog.Errorf("failed to delete logical router policy for CIDR %s of subnet %s: %v", cidr, subnet.Name, err)
×
3256
                        return err
×
3257
                }
×
3258
        }
3259
        return nil
×
3260
}
3261

3262
func (c *Controller) clearOldU2OResource(subnet *kubeovnv1.Subnet) error {
×
3263
        if subnet.Status.U2OInterconnectionVPC != "" &&
×
3264
                (!subnet.Spec.U2OInterconnection || (subnet.Spec.U2OInterconnection && subnet.Status.U2OInterconnectionVPC != subnet.Spec.Vpc)) {
×
3265
                // remove old u2o lsp and lrp first
×
3266
                lspName := fmt.Sprintf("%s-%s", subnet.Name, subnet.Status.U2OInterconnectionVPC)
×
3267
                lrpName := fmt.Sprintf("%s-%s", subnet.Status.U2OInterconnectionVPC, subnet.Name)
×
3268
                klog.Infof("clean subnet %s old u2o resource with lsp %s lrp %s", subnet.Name, lspName, lrpName)
×
3269
                if err := c.OVNNbClient.DeleteLogicalSwitchPort(lspName); err != nil {
×
3270
                        klog.Errorf("failed to delete u2o logical switch port %s: %v", lspName, err)
×
3271
                        return err
×
3272
                }
×
3273

3274
                if err := c.OVNNbClient.DeleteLogicalRouterPort(lrpName); err != nil {
×
3275
                        klog.Errorf("failed to delete u2o logical router port %s: %v", lrpName, err)
×
3276
                        return err
×
3277
                }
×
3278

3279
                if err := c.deletePolicyRouteForU2OInterconn(subnet); err != nil {
×
3280
                        klog.Errorf("failed to delete u2o policy route for u2o connection %s: %v", subnet.Name, err)
×
3281
                        return err
×
3282
                }
×
3283

3284
                if subnet.Status.U2OInterconnectionVPC != c.config.ClusterRouter {
×
3285
                        if err := c.deleteStaticRouteForU2OInterconn(subnet); err != nil {
×
3286
                                klog.Errorf("failed to delete u2o static route for u2o connection %s: %v", subnet.Name, err)
×
3287
                                return err
×
3288
                        }
×
3289
                }
3290
        }
3291
        return nil
×
3292
}
3293

3294
func (c *Controller) reconcilePolicyRouteForCidrChangedSubnet(subnet *kubeovnv1.Subnet, isCommonRoute bool) error {
×
3295
        var match string
×
3296
        var priority int
×
3297

×
3298
        if isCommonRoute {
×
3299
                priority = util.SubnetRouterPolicyPriority
×
3300
        } else {
×
3301
                priority = util.GatewayRouterPolicyPriority
×
3302
        }
×
3303

3304
        policies, err := c.OVNNbClient.ListLogicalRouterPolicies(subnet.Spec.Vpc, priority, map[string]string{
×
3305
                "vendor": util.CniTypeName,
×
3306
                "subnet": subnet.Name,
×
3307
        }, true)
×
3308
        if err != nil {
×
3309
                klog.Errorf("failed to list logical router policies: %v", err)
×
3310
                return err
×
3311
        }
×
3312
        if len(policies) == 0 {
×
3313
                return nil
×
3314
        }
×
3315

3316
        for _, policy := range policies {
×
3317
                policyProtocol := kubeovnv1.ProtocolIPv4
×
3318
                if strings.Contains(policy.Match, "ip6") {
×
3319
                        policyProtocol = kubeovnv1.ProtocolIPv6
×
3320
                }
×
3321

3322
                for cidr := range strings.SplitSeq(subnet.Spec.CIDRBlock, ",") {
×
3323
                        if cidr == "" {
×
3324
                                continue
×
3325
                        }
3326
                        if policyProtocol != util.CheckProtocol(cidr) {
×
3327
                                continue
×
3328
                        }
3329

3330
                        af := 4
×
3331
                        if util.CheckProtocol(cidr) == kubeovnv1.ProtocolIPv6 {
×
3332
                                af = 6
×
3333
                        }
×
3334

3335
                        if isCommonRoute {
×
3336
                                match = fmt.Sprintf("ip%d.dst == %s", af, cidr)
×
3337
                        } else {
×
3338
                                if subnet.Spec.GatewayType == kubeovnv1.GWCentralizedType {
×
3339
                                        match = fmt.Sprintf("ip%d.src == %s", af, cidr)
×
3340
                                } else {
×
3341
                                        // distributed subnet does not need process gateway route policy
×
3342
                                        continue
×
3343
                                }
3344
                        }
3345

3346
                        if policy.Match != match {
×
3347
                                klog.Infof("delete old policy route for subnet %s with match %s priority %d, new match %v", subnet.Name, policy.Match, policy.Priority, match)
×
3348
                                if err = c.OVNNbClient.DeleteLogicalRouterPolicyByUUID(subnet.Spec.Vpc, policy.UUID); err != nil {
×
3349
                                        klog.Errorf("failed to delete policy route for subnet %s: %v", subnet.Name, err)
×
3350
                                        return err
×
3351
                                }
×
3352
                        }
3353
                }
3354
        }
3355
        return nil
×
3356
}
3357

3358
func (c *Controller) addPolicyRouteForU2ONoLoadBalancer(subnet *kubeovnv1.Subnet) error {
×
3359
        nodes, err := c.nodesLister.List(labels.Everything())
×
3360
        if err != nil {
×
3361
                klog.Errorf("failed to list nodes: %v", err)
×
3362
                return err
×
3363
        }
×
3364
        for _, node := range nodes {
×
3365
                pgName := getOverlaySubnetsPortGroupName(subnet.Name, node.Name)
×
3366
                if err := c.OVNNbClient.CreatePortGroup(pgName, map[string]string{logicalRouterKey: subnet.Spec.Vpc, logicalSwitchKey: subnet.Name, u2oKey: "true"}); err != nil {
×
3367
                        klog.Errorf("failed to create u2o port group for subnet %s and node %s: %v", subnet.Name, node.Name, err)
×
3368
                        return err
×
3369
                }
×
3370
                key := util.NodeLspName(node.Name)
×
3371
                ip, err := c.ipsLister.Get(key)
×
3372
                if err != nil {
×
3373
                        if k8serrors.IsNotFound(err) {
×
3374
                                return nil
×
3375
                        }
×
3376
                        klog.Error(err)
×
3377
                        return err
×
3378
                }
3379
                v4Svc, v6Svc := util.SplitStringIP(c.config.ServiceClusterIPRange)
×
3380
                for cidrBlock := range strings.SplitSeq(subnet.Spec.CIDRBlock, ",") {
×
3381
                        ipSuffix, nodeIP, svcCIDR := "ip4", ip.Spec.V4IPAddress, v4Svc
×
3382
                        if util.CheckProtocol(cidrBlock) == kubeovnv1.ProtocolIPv6 {
×
3383
                                ipSuffix, nodeIP, svcCIDR = "ip6", ip.Spec.V6IPAddress, v6Svc
×
3384
                        }
×
3385
                        if nodeIP == "" || svcCIDR == "" {
×
3386
                                continue
×
3387
                        }
3388

3389
                        var (
×
3390
                                pgAs        = fmt.Sprintf("%s_%s", pgName, ipSuffix)
×
3391
                                match       = fmt.Sprintf("%s.src == $%s && %s.dst == %s", ipSuffix, pgAs, ipSuffix, svcCIDR)
×
3392
                                action      = kubeovnv1.PolicyRouteActionReroute
×
3393
                                externalIDs = map[string]string{
×
3394
                                        "vendor":               util.CniTypeName,
×
3395
                                        "subnet":               subnet.Name,
×
3396
                                        "isU2ORoutePolicy":     "true",
×
3397
                                        "isU2ONoLBRoutePolicy": "true",
×
3398
                                        "node":                 node.Name,
×
3399
                                }
×
3400
                        )
×
3401

×
3402
                        klog.Infof("add u2o interconnection policy without enabling loadbalancer for router: %s, match %s, action %s, nexthop %s", subnet.Spec.Vpc, match, action, nodeIP)
×
3403
                        if err := c.addPolicyRouteToVpc(
×
3404
                                c.config.ClusterRouter,
×
3405
                                &kubeovnv1.PolicyRoute{
×
3406
                                        Priority:  util.U2OSubnetPolicyPriority,
×
3407
                                        Match:     match,
×
3408
                                        Action:    action,
×
3409
                                        NextHopIP: nodeIP,
×
3410
                                },
×
3411
                                externalIDs,
×
3412
                        ); err != nil {
×
3413
                                klog.Errorf("failed to add logical router policy for port-group address-set %s: %v", pgAs, err)
×
3414
                                return err
×
3415
                        }
×
3416
                }
3417
        }
3418
        lsps, err := c.OVNNbClient.ListNormalLogicalSwitchPorts(true, map[string]string{logicalSwitchKey: subnet.Name})
×
3419
        if err != nil {
×
3420
                klog.Errorf("failed to list normal lsps for subnet %s: %v", subnet.Name, err)
×
3421
                return err
×
3422
        }
×
3423
        for _, lsp := range lsps {
×
3424
                ip, err := c.ipsLister.Get(lsp.Name)
×
3425
                if err != nil {
×
3426
                        if k8serrors.IsNotFound(err) {
×
3427
                                return nil
×
3428
                        }
×
3429
                        klog.Error(err)
×
3430
                        return err
×
3431
                }
3432
                pgName := getOverlaySubnetsPortGroupName(subnet.Name, ip.Spec.NodeName)
×
3433
                if err = c.OVNNbClient.PortGroupAddPorts(pgName, lsp.Name); err != nil {
×
3434
                        klog.Errorf("failed to add port to u2o port group %s: %v", pgName, err)
×
3435
                        return err
×
3436
                }
×
3437
        }
3438
        return nil
×
3439
}
3440

3441
func (c *Controller) deletePolicyRouteForU2ONoLoadBalancer(subnet *kubeovnv1.Subnet) error {
×
3442
        logicalRouter, err := c.OVNNbClient.GetLogicalRouter(subnet.Spec.Vpc, true)
×
3443
        if err == nil && logicalRouter == nil {
×
3444
                klog.Infof("logical router %s already deleted", subnet.Spec.Vpc)
×
3445
                return nil
×
3446
        }
×
3447
        policies, err := c.OVNNbClient.ListLogicalRouterPolicies(subnet.Spec.Vpc, -1, map[string]string{
×
3448
                "isU2ONoLBRoutePolicy": "true",
×
3449
                "vendor":               util.CniTypeName,
×
3450
                "subnet":               subnet.Name,
×
3451
        }, true)
×
3452
        if err != nil {
×
3453
                klog.Errorf("failed to list logical router policies: %v", err)
×
3454
                return err
×
3455
        }
×
3456

3457
        lr := subnet.Status.U2OInterconnectionVPC
×
3458
        if lr == "" {
×
3459
                // old version field U2OInterconnectionVPC may be "" and then use subnet.Spec.Vpc
×
3460
                lr = subnet.Spec.Vpc
×
3461
        }
×
3462

3463
        for _, policy := range policies {
×
3464
                klog.Infof("delete u2o interconnection policy without enabling loadbalancer for router %s with match %s priority %d", lr, policy.Match, policy.Priority)
×
3465
                if err = c.OVNNbClient.DeleteLogicalRouterPolicyByUUID(lr, policy.UUID); err != nil {
×
3466
                        klog.Errorf("failed to delete u2o interconnection policy for subnet %s: %v", subnet.Name, err)
×
3467
                        return err
×
3468
                }
×
3469
        }
3470

3471
        pgs, err := c.OVNNbClient.ListPortGroups(map[string]string{logicalRouterKey: subnet.Spec.Vpc, logicalSwitchKey: subnet.Name, u2oKey: "true"})
×
3472
        if err != nil {
×
3473
                klog.Errorf("failed to list u2o port groups with u2oKey is true for subnet %s: %v", subnet.Name, err)
×
3474
                return err
×
3475
        }
×
3476
        for _, pg := range pgs {
×
3477
                klog.Infof("delete u2o port group %s for subnet %s", pg.Name, subnet.Name)
×
3478
                if err = c.OVNNbClient.DeletePortGroup(pg.Name); err != nil {
×
3479
                        klog.Errorf("failed to delete u2o port group for subnet %s: %v", subnet.Name, err)
×
3480
                        return err
×
3481
                }
×
3482
        }
3483
        return nil
×
3484
}
3485

3486
func (c *Controller) findSubnetByNetworkAttachmentDefinition(ns, name string, subnets []*kubeovnv1.Subnet) (*kubeovnv1.Subnet, error) {
×
3487
        nadClient := c.config.AttachNetClient.K8sCniCncfIoV1().NetworkAttachmentDefinitions(ns)
×
3488
        nad, err := nadClient.Get(context.Background(), name, metav1.GetOptions{})
×
3489
        if err != nil {
×
3490
                klog.Errorf("failed to get net-attach-def %s/%s: %v", ns, name, err)
×
3491
                return nil, err
×
3492
        }
×
3493
        netCfg, err := loadNetConf([]byte(nad.Spec.Config))
×
3494
        if err != nil {
×
3495
                klog.Errorf("failed to parse config of net-attach-def %s/%s: %v", ns, name, err)
×
3496
                return nil, err
×
3497
        }
×
3498

3499
        var provider string
×
3500
        if netCfg.Conf.Type == util.CniTypeName {
×
3501
                provider = fmt.Sprintf("%s.%s.%s", name, ns, util.OvnProvider)
×
3502
        } else {
×
3503
                provider = fmt.Sprintf("%s.%s", name, ns)
×
3504
        }
×
3505
        var subnet *kubeovnv1.Subnet
×
3506
        for _, s := range subnets {
×
3507
                if s.Spec.Provider == provider {
×
3508
                        subnet = s.DeepCopy()
×
3509
                        break
×
3510
                }
3511
        }
3512
        if subnet == nil {
×
3513
                err = fmt.Errorf("failed to get subnet for net-attach-def %s/%s", ns, name)
×
3514
                klog.Error(err)
×
3515
                return nil, err
×
3516
        }
×
3517

3518
        return subnet, nil
×
3519
}
3520

3521
func (c *Controller) handleMcastQuerierChange(subnet *kubeovnv1.Subnet) error {
×
3522
        if subnet.Spec.EnableMulticastSnoop {
×
3523
                multicastSnoopFlag := map[string]string{
×
3524
                        "mcast_snoop":   "true",
×
3525
                        "mcast_querier": "true",
×
3526
                        "mcast_ip4_src": subnet.Status.McastQuerierIP,
×
3527
                        "mcast_eth_src": subnet.Status.McastQuerierMAC,
×
3528
                }
×
3529
                mcastQuerierLspName := fmt.Sprintf(util.McastQuerierName, subnet.Name)
×
3530
                if err := c.OVNNbClient.CreateLogicalSwitchPort(subnet.Name, mcastQuerierLspName, subnet.Status.McastQuerierIP, subnet.Status.McastQuerierMAC, mcastQuerierLspName, "default", false, "", "", false, nil, ""); err != nil {
×
3531
                        err = fmt.Errorf("failed to create mcast querier lsp %s: %w", mcastQuerierLspName, err)
×
3532
                        klog.Error(err)
×
3533
                        return err
×
3534
                }
×
3535

3536
                if err := c.OVNNbClient.LogicalSwitchUpdateOtherConfig(subnet.Name, ovsdb.MutateOperationInsert, multicastSnoopFlag); err != nil {
×
3537
                        klog.Errorf("enable logical switch multicast snoop %s: %v", subnet.Name, err)
×
3538
                        return err
×
3539
                }
×
3540
        } else {
×
3541
                lss, err := c.OVNNbClient.ListLogicalSwitch(false, func(ls *ovnnb.LogicalSwitch) bool {
×
3542
                        return ls.Name == subnet.Name
×
3543
                })
×
3544
                if err != nil || len(lss) == 0 {
×
3545
                        klog.Errorf("failed to list logical switch %s: %v", subnet.Name, err)
×
3546
                        return err
×
3547
                }
×
3548

3549
                multicastSnoopFlag := map[string]string{
×
3550
                        "mcast_snoop":   lss[0].OtherConfig["mcast_snoop"],
×
3551
                        "mcast_querier": lss[0].OtherConfig["mcast_querier"],
×
3552
                        "mcast_ip4_src": lss[0].OtherConfig["mcast_ip4_src"],
×
3553
                        "mcast_eth_src": lss[0].OtherConfig["mcast_eth_src"],
×
3554
                }
×
3555
                mcastQuerierLspName := fmt.Sprintf(util.McastQuerierName, subnet.Name)
×
3556
                if err := c.OVNNbClient.LogicalSwitchUpdateOtherConfig(subnet.Name, ovsdb.MutateOperationDelete, multicastSnoopFlag); err != nil {
×
3557
                        klog.Errorf("disable logical switch multicast snoop %s: %v", subnet.Name, err)
×
3558
                        return err
×
3559
                }
×
3560

3561
                if err := c.OVNNbClient.DeleteLogicalSwitchPort(mcastQuerierLspName); err != nil {
×
3562
                        err = fmt.Errorf("failed to delete mcast querier lsp %s: %w", mcastQuerierLspName, err)
×
3563
                        klog.Error(err)
×
3564
                        return err
×
3565
                }
×
3566
        }
3567
        return nil
×
3568
}
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