Skip to content

Commit ffd57ec

Browse files
committed
fix metallb underlay lflow rule is deleted unexpected (#5723)
* fix metallb underlay lflow rule is deleted unexpected Signed-off-by: clyi <[email protected]> * add log Signed-off-by: clyi <[email protected]> --------- Signed-off-by: clyi <[email protected]>
1 parent 11debb4 commit ffd57ec

File tree

2 files changed

+167
-34
lines changed

2 files changed

+167
-34
lines changed

pkg/ovs/ovn-nb-load_balancer.go

Lines changed: 113 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -467,68 +467,153 @@ func (c *OVNNbClient) LoadBalancerAddIPPortMapping(lbName, vipEndpoint string, m
467467
return nil
468468
}
469469

470-
// LoadBalancerDeleteIPPortMapping delete load balancer ip port mapping
470+
// LoadBalancerDeleteIPPortMapping deletes IP port mappings for a specific VIP from a load balancer.
471+
// This function ensures that only backend IPs that are no longer referenced by any VIP are removed.
471472
func (c *OVNNbClient) LoadBalancerDeleteIPPortMapping(lbName, vipEndpoint string) error {
472-
var (
473-
ops []ovsdb.Operation
474-
lb *ovnnb.LoadBalancer
475-
mappings map[string]string
476-
vip string
477-
err error
478-
)
473+
lb, err := c.getLoadBalancerForDeletion(lbName)
474+
if err != nil {
475+
klog.Errorf("failed to get load balancer for deletion: %v", err)
476+
return err
477+
}
478+
if lb == nil {
479+
return nil
480+
}
479481

480-
if lb, err = c.GetLoadBalancer(lbName, true); err != nil {
481-
klog.Errorf("failed to get lb health check: %v", err)
482+
targetBackendIPs, err := c.extractBackendIPsFromVIP(lb, vipEndpoint)
483+
if err != nil {
484+
klog.Errorf("failed to extract backend IPs from VIP: %v", err)
482485
return err
483486
}
487+
if len(targetBackendIPs) == 0 {
488+
return nil
489+
}
490+
491+
unusedBackendIPs := c.findUnusedBackendIPs(lb, vipEndpoint, targetBackendIPs)
492+
return c.deleteUnusedIPPortMappings(lbName, vipEndpoint, unusedBackendIPs)
493+
}
494+
495+
// getLoadBalancerForDeletion retrieves the load balancer and performs initial validation
496+
func (c *OVNNbClient) getLoadBalancerForDeletion(lbName string) (*ovnnb.LoadBalancer, error) {
497+
lb, err := c.GetLoadBalancer(lbName, true)
498+
if err != nil {
499+
klog.Errorf("failed to get load balancer %s: %v", lbName, err)
500+
return nil, err
501+
}
484502

485503
if lb == nil {
486-
klog.Infof("lb %s already deleted", lbName)
487-
return nil
504+
klog.Infof("load balancer %s already deleted", lbName)
505+
return nil, nil
488506
}
507+
489508
if len(lb.IPPortMappings) == 0 {
490-
klog.Infof("lb %s has no ip port mapping", lbName)
491-
return nil
509+
klog.Infof("load balancer %s has no IP port mappings", lbName)
510+
return nil, nil
492511
}
493512

494-
if vip, _, err = net.SplitHostPort(vipEndpoint); err != nil {
495-
err := fmt.Errorf("failed to split host port: %w", err)
496-
klog.Error(err)
497-
return err
513+
return lb, nil
514+
}
515+
516+
// extractBackendIPsFromVIP extracts the backend IPs that are used by a specific VIP
517+
func (c *OVNNbClient) extractBackendIPsFromVIP(lb *ovnnb.LoadBalancer, vipEndpoint string) (map[string]bool, error) {
518+
vipBackends, exists := lb.Vips[vipEndpoint]
519+
if !exists {
520+
klog.Infof("VIP %s not found in load balancer", vipEndpoint)
521+
return nil, nil
498522
}
499523

500-
mappings = lb.IPPortMappings
501-
for portIP, portMapVip := range lb.IPPortMappings {
502-
splits := strings.Split(portMapVip, ":")
503-
if len(splits) == 2 && splits[1] == vip {
504-
delete(mappings, portIP)
524+
backendIPs := make(map[string]bool)
525+
526+
for backend := range strings.SplitSeq(vipBackends, ",") {
527+
if backendIP, _, err := net.SplitHostPort(backend); err == nil {
528+
backendIPs[backendIP] = true
505529
}
506530
}
507531

508-
if ops, err = c.LoadBalancerOp(
532+
klog.V(4).Infof("VIP %s uses %d backend IPs: %v", vipEndpoint, len(backendIPs), getMapKeys(backendIPs))
533+
return backendIPs, nil
534+
}
535+
536+
// findUnusedBackendIPs identifies which backend IPs are no longer used by any other VIP
537+
func (c *OVNNbClient) findUnusedBackendIPs(lb *ovnnb.LoadBalancer, targetVIP string, targetBackendIPs map[string]bool) map[string]string {
538+
unusedBackendIPs := make(map[string]string)
539+
540+
for backendIP := range targetBackendIPs {
541+
if !c.isBackendIPStillUsed(lb, targetVIP, backendIP) {
542+
if portMapping, exists := lb.IPPortMappings[backendIP]; exists {
543+
unusedBackendIPs[backendIP] = portMapping
544+
}
545+
}
546+
}
547+
548+
klog.V(4).Infof("Found %d unused backend IPs for VIP %s", len(unusedBackendIPs), targetVIP)
549+
return unusedBackendIPs
550+
}
551+
552+
// isBackendIPStillUsed checks if a backend IP is still referenced by any other VIP
553+
func (c *OVNNbClient) isBackendIPStillUsed(lb *ovnnb.LoadBalancer, targetVIP, backendIP string) bool {
554+
for otherVIP, otherBackends := range lb.Vips {
555+
if otherVIP == targetVIP {
556+
continue
557+
}
558+
559+
for otherBackend := range strings.SplitSeq(otherBackends, ",") {
560+
if otherBackendIP, _, err := net.SplitHostPort(otherBackend); err == nil && otherBackendIP == backendIP {
561+
klog.V(5).Infof("Backend IP %s is still used by VIP %s", backendIP, otherVIP)
562+
return true
563+
}
564+
}
565+
}
566+
567+
klog.V(5).Infof("Backend IP %s is no longer used by any other VIP", backendIP)
568+
return false
569+
}
570+
571+
// deleteUnusedIPPortMappings performs the actual deletion of unused IP port mappings
572+
func (c *OVNNbClient) deleteUnusedIPPortMappings(lbName, vipEndpoint string, unusedBackendIPs map[string]string) error {
573+
if len(unusedBackendIPs) == 0 {
574+
klog.Infof("no unused backend IPs to delete for VIP %s in load balancer %s", vipEndpoint, lbName)
575+
return nil
576+
}
577+
578+
ops, err := c.LoadBalancerOp(
509579
lbName,
510580
func(lb *ovnnb.LoadBalancer) []model.Mutation {
511581
return []model.Mutation{
512582
{
513583
Field: &lb.IPPortMappings,
514-
Value: mappings,
584+
Value: unusedBackendIPs,
515585
Mutator: ovsdb.MutateOperationDelete,
516586
},
517587
}
518588
},
519-
); err != nil {
589+
)
590+
if err != nil {
520591
klog.Error(err)
521-
return fmt.Errorf("failed to generate operations when deleting ip port mapping %s from load balancers %s: %w", vipEndpoint, lbName, err)
592+
return fmt.Errorf("failed to generate operations when deleting IP port mappings for VIP %s from load balancer %s: %w", vipEndpoint, lbName, err)
522593
}
594+
523595
if len(ops) == 0 {
524596
return nil
525597
}
598+
526599
if err = c.Transact("lb-del", ops); err != nil {
527-
return fmt.Errorf("failed to delete ip port mappings %s from load balancer %s: %w", vipEndpoint, lbName, err)
600+
return fmt.Errorf("failed to delete IP port mappings for VIP %s from load balancer %s: %w", vipEndpoint, lbName, err)
528601
}
602+
603+
klog.Infof("successfully deleted %d unused backend IPs for VIP %s from load balancer %s",
604+
len(unusedBackendIPs), vipEndpoint, lbName)
529605
return nil
530606
}
531607

608+
// getMapKeys returns the keys of a map as a slice
609+
func getMapKeys(m map[string]bool) []string {
610+
keys := make([]string, 0, len(m))
611+
for k := range m {
612+
keys = append(keys, k)
613+
}
614+
return keys
615+
}
616+
532617
// LoadBalancerUpdateIPPortMapping update load balancer ip port mapping
533618
func (c *OVNNbClient) LoadBalancerUpdateIPPortMapping(lbName, vipEndpoint string, ipPortMappings map[string]string) error {
534619
ops, err := c.LoadBalancerOp(

pkg/ovs/ovn-nb-load_balancer_test.go

Lines changed: 54 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -780,7 +780,7 @@ func (suite *OvnClientTestSuite) testLoadBalancerDeleteIPPortMapping() {
780780
require.Contains(t, lb.IPPortMappings, backend)
781781
}
782782

783-
err = nbClient.LoadBalancerDeleteIPPortMapping(lbName, vip)
783+
err = nbClient.LoadBalancerDeleteIPPortMapping(lbName, vhost)
784784
require.NoError(t, err)
785785

786786
lb, err = nbClient.GetLoadBalancer(lbName, false)
@@ -806,10 +806,29 @@ func (suite *OvnClientTestSuite) testLoadBalancerDeleteIPPortMapping() {
806806
t.Run("delete ip port mappings from load balancer repeatedly",
807807
func(t *testing.T) {
808808
for vip, backends := range vips {
809-
list := strings.Split(backends, ",")
809+
var (
810+
list []string
811+
vhost, host string
812+
)
813+
list = strings.Split(backends, ",")
810814
mappings = make(map[string]string)
811815

812-
err = nbClient.LoadBalancerDeleteIPPortMapping(lbName, vip)
816+
for _, backend := range list {
817+
host, _, err = net.SplitHostPort(backend)
818+
require.NoError(t, err)
819+
820+
mappings[host] = host
821+
}
822+
823+
vhost, _, err = net.SplitHostPort(vip)
824+
require.NoError(t, err)
825+
err = nbClient.LoadBalancerAddVip(lbName, vhost, list...)
826+
require.NoError(t, err)
827+
828+
err = nbClient.LoadBalancerAddIPPortMapping(lbName, vhost, mappings)
829+
require.NoError(t, err)
830+
831+
err = nbClient.LoadBalancerDeleteIPPortMapping(lbName, vhost)
813832
require.NoError(t, err)
814833

815834
lb, err = nbClient.GetLoadBalancer(lbName, false)
@@ -826,16 +845,45 @@ func (suite *OvnClientTestSuite) testLoadBalancerDeleteIPPortMapping() {
826845
)
827846

828847
vips = map[string]string{
829-
"[fd00:10:96::e86f]:8080": "",
848+
"[fd00:10:96::e86f]:8080": "[fc00::af4:a]:8080,[fc00::af4:b]:8080,[fc00::af4:c]:8080",
830849
}
831850
t.Run("delete ip port mappings from load balancer repeatedly",
832851
func(t *testing.T) {
833-
for vip := range vips {
834-
err = nbClient.LoadBalancerDeleteIPPortMapping(lbName, vip)
852+
for vip, backends := range vips {
853+
var (
854+
list []string
855+
vhost, host string
856+
)
857+
list = strings.Split(backends, ",")
858+
mappings = make(map[string]string)
859+
860+
for _, backend := range list {
861+
host, _, err = net.SplitHostPort(backend)
862+
require.NoError(t, err)
863+
864+
mappings[host] = host
865+
}
866+
867+
vhost, _, err = net.SplitHostPort(vip)
868+
require.NoError(t, err)
869+
err = nbClient.LoadBalancerAddVip(lbName, vhost, list...)
870+
require.NoError(t, err)
871+
872+
err = nbClient.LoadBalancerAddIPPortMapping(lbName, vhost, mappings)
873+
require.NoError(t, err)
874+
875+
err = nbClient.LoadBalancerDeleteIPPortMapping(lbName, vhost)
835876
require.NoError(t, err)
836877

837878
lb, err = nbClient.GetLoadBalancer(lbName, false)
838879
require.NoError(t, err)
880+
881+
for _, backend := range list {
882+
backend, _, err = net.SplitHostPort(backend)
883+
require.NoError(t, err)
884+
885+
require.NotContains(t, lb.IPPortMappings, backend)
886+
}
839887
}
840888
},
841889
)

0 commit comments

Comments
 (0)