Skip to content

Commit e57f451

Browse files
committed
Merge remote-tracking branch 'upstream/master' into merge-2-6-25
2 parents b9dcccb + a7ca957 commit e57f451

12 files changed

+362
-91
lines changed

go-controller/pkg/kubevirt/pod.go

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -355,7 +355,9 @@ func IsPodOwnedByVirtualMachine(pod *corev1.Pod) bool {
355355

356356
// IsPodAllowedForMigration determines whether a given pod is eligible for live migration
357357
func IsPodAllowedForMigration(pod *corev1.Pod, netInfo util.NetInfo) bool {
358-
return IsPodOwnedByVirtualMachine(pod) && netInfo.TopologyType() == ovntypes.Layer2Topology
358+
return IsPodOwnedByVirtualMachine(pod) &&
359+
(netInfo.TopologyType() == ovntypes.Layer2Topology ||
360+
netInfo.TopologyType() == ovntypes.LocalnetTopology)
359361
}
360362

361363
func isTargetPodReady(targetPod *corev1.Pod) bool {
@@ -479,3 +481,38 @@ func DiscoverLiveMigrationStatus(client *factory.WatchFactory, pod *corev1.Pod)
479481
}
480482
return &status, nil
481483
}
484+
485+
func ReconcileIPv4DefaultGatewayAfterLiveMigration(watchFactory *factory.WatchFactory, netInfo util.NetInfo, liveMigrationStatus *LiveMigrationStatus, interfaceName string) error {
486+
if liveMigrationStatus.State != LiveMigrationTargetDomainReady {
487+
return nil
488+
}
489+
490+
targetNode, err := watchFactory.GetNode(liveMigrationStatus.TargetPod.Spec.NodeName)
491+
if err != nil {
492+
return err
493+
}
494+
495+
lrpJoinAddress, err := util.ParseNodeGatewayRouterJoinNetwork(targetNode, netInfo.GetNetworkName())
496+
if err != nil {
497+
return err
498+
}
499+
500+
lrpJoinIPv4, _, err := net.ParseCIDR(lrpJoinAddress.IPv4)
501+
if err != nil {
502+
return err
503+
}
504+
505+
lrpMAC := util.IPAddrToHWAddr(lrpJoinIPv4)
506+
for _, subnet := range netInfo.Subnets() {
507+
gwIP := util.GetNodeGatewayIfAddr(subnet.CIDR).IP.To4()
508+
if gwIP == nil {
509+
continue
510+
}
511+
garp := util.GARP{IP: gwIP, MAC: &lrpMAC}
512+
if err := util.BroadcastGARP(interfaceName, garp); err != nil {
513+
return err
514+
}
515+
}
516+
517+
return nil
518+
}

go-controller/pkg/node/linkmanager/link_network_manager.go

Lines changed: 1 addition & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@ package linkmanager
22

33
import (
44
"fmt"
5-
"net"
6-
"net/netip"
75
"sync"
86
"time"
97

@@ -12,7 +10,6 @@ import (
1210
"k8s.io/klog/v2"
1311
utilnet "k8s.io/utils/net"
1412

15-
"github.com/mdlayher/arp"
1613
"github.com/vishvananda/netlink"
1714
)
1815

@@ -198,7 +195,7 @@ func (c *Controller) syncLink(link netlink.Link) error {
198195
// For IPv4, use arping to try to update other hosts ARP caches, in case this IP was
199196
// previously active on another node
200197
if addressWanted.IP.To4() != nil {
201-
if err = gratuitousArpOverIfaceByName(addressWanted.IP, linkName); err != nil {
198+
if err = util.BroadcastGARP(linkName, util.GARP{IP: addressWanted.IP}); err != nil {
202199
klog.Errorf("Failed to send a GARP for IP %s over interface %s: %v", addressWanted.IP.String(),
203200
linkName, err)
204201
}
@@ -307,29 +304,3 @@ func containsAddress(addresses []netlink.Addr, candidate netlink.Addr) bool {
307304
}
308305
return false
309306
}
310-
311-
func gratuitousArpOverIfaceByName(srcIP net.IP, ifaceName string) error {
312-
iface, err := net.InterfaceByName(ifaceName)
313-
if err != nil {
314-
return fmt.Errorf("failed finding interface '%s': %w", ifaceName, err)
315-
}
316-
317-
c, err := arp.Dial(iface)
318-
if err != nil {
319-
return fmt.Errorf("failed dialing logical switch interface '%s': %w", ifaceName, err)
320-
}
321-
defer c.Close()
322-
addr, err := netip.ParseAddr(srcIP.String())
323-
if err != nil {
324-
return fmt.Errorf("failed converting net.IP to netip.Addr: %w", err)
325-
}
326-
p, err := arp.NewPacket(arp.OperationRequest, iface.HardwareAddr, addr, net.HardwareAddr{0, 0, 0, 0, 0, 0}, addr)
327-
if err != nil {
328-
return fmt.Errorf("failed create GARP: %w", err)
329-
}
330-
err = c.WriteTo(p, net.HardwareAddr{0xff, 0xff, 0xff, 0xff, 0xff, 0xff})
331-
if err != nil {
332-
return fmt.Errorf("failed sending GARP: %w", err)
333-
}
334-
return nil
335-
}

go-controller/pkg/ovn/base_network_controller_pods.go

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -566,15 +566,25 @@ func (bnc *BaseNetworkController) addLogicalPortToNetwork(pod *kapi.Pod, nadName
566566
return nil, nil, nil, false, err
567567
}
568568

569-
// set addresses on the port
570-
// LSP addresses in OVN are a single space-separated value
569+
lsp.Enabled = enable
570+
if lsp.Enabled != nil {
571+
customFields = append(customFields, libovsdbops.LogicalSwitchPortEnabled)
572+
}
573+
571574
addresses = []string{podAnnotation.MAC.String()}
572575
for _, podIfAddr := range podAnnotation.IPs {
573576
addresses[0] = addresses[0] + " " + podIfAddr.IP.String()
574577
}
575578

576-
lsp.Addresses = addresses
577-
customFields = append(customFields, libovsdbops.LogicalSwitchPortAddresses)
579+
// Skip address configuration if LSP is disabled since it will install
580+
// l2 look up flows that harms some topologies
581+
if lsp.Enabled == nil || *lsp.Enabled {
582+
// set addresses on the port
583+
// LSP addresses in OVN are a single space-separated value
584+
585+
lsp.Addresses = addresses
586+
customFields = append(customFields, libovsdbops.LogicalSwitchPortAddresses)
587+
}
578588

579589
// add external ids
580590
lsp.ExternalIDs = map[string]string{"namespace": pod.Namespace, "pod": "true"}
@@ -602,11 +612,6 @@ func (bnc *BaseNetworkController) addLogicalPortToNetwork(pod *kapi.Pod, nadName
602612
if len(lsp.Options) != 0 {
603613
customFields = append(customFields, libovsdbops.LogicalSwitchPortOptions)
604614
}
605-
606-
lsp.Enabled = enable
607-
if lsp.Enabled != nil {
608-
customFields = append(customFields, libovsdbops.LogicalSwitchPortEnabled)
609-
}
610615
ops, err = libovsdbops.CreateOrUpdateLogicalSwitchPortsOnSwitchWithCustomFieldsOps(bnc.nbClient, nil, ls, customFields, lsp)
611616
if err != nil {
612617
return nil, nil, nil, false,

go-controller/pkg/ovn/base_network_controller_secondary.go

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ func (bsnc *BaseSecondaryNetworkController) UpdateSecondaryNetworkResourceCommon
111111
oldPod := oldObj.(*corev1.Pod)
112112
newPod := newObj.(*corev1.Pod)
113113

114-
return bsnc.ensurePodForSecondaryNetwork(newPod, inRetryCache || util.PodScheduled(oldPod) != util.PodScheduled(newPod))
114+
return bsnc.ensurePodForSecondaryNetwork(newPod, shouldAddPort(oldPod, newPod, inRetryCache))
115115

116116
case factory.NamespaceType:
117117
oldNs, newNs := oldObj.(*corev1.Namespace), newObj.(*corev1.Namespace)
@@ -356,7 +356,9 @@ func (bsnc *BaseSecondaryNetworkController) addLogicalPortToNetworkForNAD(pod *c
356356
}
357357

358358
if shouldHandleLiveMigration &&
359-
kubevirtLiveMigrationStatus.IsTargetDomainReady() {
359+
kubevirtLiveMigrationStatus.IsTargetDomainReady() &&
360+
// At localnet there is no source pod remote LSP so it should be skipped
361+
(bsnc.TopologyType() != types.LocalnetTopology || bsnc.isPodScheduledinLocalZone(kubevirtLiveMigrationStatus.SourcePod)) {
360362
ops, err = bsnc.disableLiveMigrationSourceLSPOps(kubevirtLiveMigrationStatus, nadName, ops)
361363
if err != nil {
362364
return fmt.Errorf("failed to create LSP ops for source pod during Live-migration status: %w", err)
@@ -444,7 +446,7 @@ func (bsnc *BaseSecondaryNetworkController) removePodForSecondaryNetwork(pod *co
444446
}
445447

446448
var alreadyProcessed bool
447-
for nadName := range podNetworks {
449+
for nadName, podAnnotation := range podNetworks {
448450
if !bsnc.HasNAD(nadName) {
449451
continue
450452
}
@@ -471,7 +473,7 @@ func (bsnc *BaseSecondaryNetworkController) removePodForSecondaryNetwork(pod *co
471473
}
472474

473475
if kubevirt.IsPodAllowedForMigration(pod, bsnc.GetNetInfo()) {
474-
if err = bsnc.enableSourceLSPFailedLiveMigration(pod, nadName); err != nil {
476+
if err = bsnc.enableSourceLSPFailedLiveMigration(pod, nadName, podAnnotation.MAC, podAnnotation.IPs); err != nil {
475477
return err
476478
}
477479
}
@@ -896,15 +898,34 @@ func (bsnc *BaseSecondaryNetworkController) requireDHCP(pod *corev1.Pod) bool {
896898
bsnc.TopologyType() == types.Layer2Topology
897899
}
898900

899-
func (bsnc *BaseSecondaryNetworkController) setPodLogicalSwitchPortEnabledField(
900-
pod *corev1.Pod, nadName string, ops []ovsdb.Operation, enabled bool) ([]ovsdb.Operation, *nbdb.LogicalSwitchPort, error) {
901+
func (bsnc *BaseSecondaryNetworkController) setPodLogicalSwitchPortAddressesAndEnabledField(
902+
pod *corev1.Pod, nadName string, mac string, ips []string, enabled bool, ops []ovsdb.Operation) ([]ovsdb.Operation, *nbdb.LogicalSwitchPort, error) {
901903
lsp := &nbdb.LogicalSwitchPort{Name: bsnc.GetLogicalPortName(pod, nadName)}
902904
lsp.Enabled = ptr.To(enabled)
905+
customFields := []libovsdbops.ModelUpdateField{
906+
libovsdbops.LogicalSwitchPortEnabled,
907+
libovsdbops.LogicalSwitchPortAddresses,
908+
}
909+
if !enabled {
910+
lsp.Addresses = nil
911+
} else {
912+
if len(mac) == 0 || len(ips) == 0 {
913+
return nil, nil, fmt.Errorf("failed to configure addresses for lsp, missing mac and ips for pod %s", pod.Name)
914+
}
915+
916+
// Remove length
917+
for i, ip := range ips {
918+
ips[i] = strings.Split(ip, "/")[0]
919+
}
920+
921+
lsp.Addresses = []string{
922+
strings.Join(append([]string{mac}, ips...), " "),
923+
}
924+
}
903925
switchName, err := bsnc.getExpectedSwitchName(pod)
904926
if err != nil {
905927
return nil, nil, fmt.Errorf("failed to fetch switch name for pod %s: %w", pod.Name, err)
906928
}
907-
customFields := []libovsdbops.ModelUpdateField{libovsdbops.LogicalSwitchPortEnabled}
908929
ops, err = libovsdbops.UpdateLogicalSwitchPortsOnSwitchWithCustomFieldsOps(bsnc.nbClient, ops, &nbdb.LogicalSwitch{Name: switchName}, customFields, lsp)
909930
if err != nil {
910931
return nil, nil, fmt.Errorf("failed updating logical switch port %+v on switch %s: %w", *lsp, switchName, err)
@@ -916,11 +937,11 @@ func (bsnc *BaseSecondaryNetworkController) disableLiveMigrationSourceLSPOps(
916937
kubevirtLiveMigrationStatus *kubevirt.LiveMigrationStatus,
917938
nadName string, ops []ovsdb.Operation) ([]ovsdb.Operation, error) {
918939
// closing the sourcePod lsp to ensure traffic goes to the now ready targetPod.
919-
ops, _, err := bsnc.setPodLogicalSwitchPortEnabledField(kubevirtLiveMigrationStatus.SourcePod, nadName, ops, false)
940+
ops, _, err := bsnc.setPodLogicalSwitchPortAddressesAndEnabledField(kubevirtLiveMigrationStatus.SourcePod, nadName, "", nil, false, ops)
920941
return ops, err
921942
}
922943

923-
func (bsnc *BaseSecondaryNetworkController) enableSourceLSPFailedLiveMigration(pod *corev1.Pod, nadName string) error {
944+
func (bsnc *BaseSecondaryNetworkController) enableSourceLSPFailedLiveMigration(pod *corev1.Pod, nadName string, mac string, ips []string) error {
924945
kubevirtLiveMigrationStatus, err := kubevirt.DiscoverLiveMigrationStatus(bsnc.watchFactory, pod)
925946
if err != nil {
926947
return fmt.Errorf("failed to discover Live-migration status after pod termination: %w", err)
@@ -931,7 +952,7 @@ func (bsnc *BaseSecondaryNetworkController) enableSourceLSPFailedLiveMigration(p
931952
return nil
932953
}
933954
// make sure sourcePod lsp is enabled if migration failed after DomainReady was set.
934-
ops, sourcePodLsp, err := bsnc.setPodLogicalSwitchPortEnabledField(kubevirtLiveMigrationStatus.SourcePod, nadName, nil, true)
955+
ops, sourcePodLsp, err := bsnc.setPodLogicalSwitchPortAddressesAndEnabledField(kubevirtLiveMigrationStatus.SourcePod, nadName, mac, ips, true, nil)
935956
if err != nil {
936957
return fmt.Errorf("failed to set source Pod lsp to enabled after migration failed: %w", err)
937958
}
@@ -942,3 +963,7 @@ func (bsnc *BaseSecondaryNetworkController) enableSourceLSPFailedLiveMigration(p
942963

943964
return nil
944965
}
966+
967+
func shouldAddPort(oldPod, newPod *corev1.Pod, inRetryCache bool) bool {
968+
return inRetryCache || util.PodScheduled(oldPod) != util.PodScheduled(newPod)
969+
}

go-controller/pkg/ovn/multihoming_test.go

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,9 @@ func (em *secondaryNetworkExpectationMachine) expectedLogicalSwitchesAndPortsWit
158158
lsp := newExpectedSwitchPort(lspUUID, portName, podAddr, pod, ocInfo.bnc, nad)
159159
if expectedPodLspEnabled != nil {
160160
lsp.Enabled = expectedPodLspEnabled[pod.podName]
161+
if lsp.Enabled != nil && !*lsp.Enabled {
162+
lsp.Addresses = nil
163+
}
161164
}
162165

163166
if pod.noIfaceIdVer {
@@ -429,6 +432,13 @@ func enableICFeatureConfig() *config.OVNKubernetesFeatureConfig {
429432
return featConfig
430433
}
431434

435+
func enableNonICFeatureConfig() *config.OVNKubernetesFeatureConfig {
436+
featConfig := minimalFeatureConfig()
437+
featConfig.EnableInterconnect = false
438+
featConfig.EnablePersistentIPs = true
439+
return featConfig
440+
}
441+
432442
type testConfigOpt = func(*testConfiguration)
433443

434444
func icClusterTestConfiguration(opts ...testConfigOpt) testConfiguration {
@@ -443,7 +453,9 @@ func icClusterTestConfiguration(opts ...testConfigOpt) testConfiguration {
443453
}
444454

445455
func nonICClusterTestConfiguration(opts ...testConfigOpt) testConfiguration {
446-
config := testConfiguration{}
456+
config := testConfiguration{
457+
configToOverride: enableNonICFeatureConfig(),
458+
}
447459
for _, opt := range opts {
448460
opt(&config)
449461
}
@@ -464,8 +476,14 @@ func newMultiHomedKubevirtPod(vmName string, liveMigrationInfo liveMigrationPodI
464476
func newMultiHomedPod(testPod testPod, multiHomingConfigs ...secondaryNetInfo) *v1.Pod {
465477
pod := newPod(testPod.namespace, testPod.podName, testPod.nodeName, testPod.podIP)
466478
var secondaryNetworks []nadapi.NetworkSelectionElement
479+
if len(pod.Annotations) == 0 {
480+
pod.Annotations = map[string]string{}
481+
}
467482
for _, multiHomingConf := range multiHomingConfigs {
468483
if multiHomingConf.isPrimary {
484+
if multiHomingConf.ipamClaimReference != "" {
485+
pod.Annotations[util.OvnUDNIPAMClaimName] = multiHomingConf.ipamClaimReference
486+
}
469487
continue // these will be automatically plugged in
470488
}
471489
nadNamePair := strings.Split(multiHomingConf.nadName, "/")
@@ -476,13 +494,14 @@ func newMultiHomedPod(testPod testPod, multiHomingConfigs ...secondaryNetInfo) *
476494
attachmentName = nadNamePair[1]
477495
}
478496
nse := nadapi.NetworkSelectionElement{
479-
Name: attachmentName,
480-
Namespace: ns,
497+
Name: attachmentName,
498+
Namespace: ns,
499+
IPAMClaimReference: multiHomingConf.ipamClaimReference,
481500
}
482501
secondaryNetworks = append(secondaryNetworks, nse)
483502
}
484503
serializedNetworkSelectionElements, _ := json.Marshal(secondaryNetworks)
485-
pod.Annotations = map[string]string{nadapi.NetworkAttachmentAnnot: string(serializedNetworkSelectionElements)}
504+
pod.Annotations[nadapi.NetworkAttachmentAnnot] = string(serializedNetworkSelectionElements)
486505
if config.OVNKubernetesFeature.EnableInterconnect {
487506
dummyOVNNetAnnotations := dummyOVNPodNetworkAnnotations(testPod.secondaryPodInfos, multiHomingConfigs)
488507
if dummyOVNNetAnnotations != "{}" {

go-controller/pkg/ovn/ovn_test.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88

99
"github.com/onsi/ginkgo/v2"
1010

11+
ipamclaimsapi "github.com/k8snetworkplumbingwg/ipamclaims/pkg/crd/ipamclaims/v1alpha1"
1112
fakeipamclaimclient "github.com/k8snetworkplumbingwg/ipamclaims/pkg/crd/ipamclaims/v1alpha1/apis/clientset/versioned/fake"
1213
mnpapi "github.com/k8snetworkplumbingwg/multi-networkpolicy/pkg/apis/k8s.cni.cncf.io/v1beta1"
1314
mnpfake "github.com/k8snetworkplumbingwg/multi-networkpolicy/pkg/client/clientset/versioned/fake"
@@ -127,6 +128,7 @@ func (o *FakeOVN) start(objects ...runtime.Object) {
127128
egressServiceObjects := []runtime.Object{}
128129
apbExternalRouteObjects := []runtime.Object{}
129130
anpObjects := []runtime.Object{}
131+
ipamClaimObjects := []runtime.Object{}
130132
v1Objects := []runtime.Object{}
131133
nads := []nettypes.NetworkAttachmentDefinition{}
132134
nadClient := fakenadclient.NewSimpleClientset()
@@ -158,6 +160,8 @@ func (o *FakeOVN) start(objects ...runtime.Object) {
158160
apbExternalRouteObjects = append(apbExternalRouteObjects, object)
159161
case *anpapi.AdminNetworkPolicyList:
160162
anpObjects = append(anpObjects, object)
163+
case *ipamclaimsapi.IPAMClaimList:
164+
ipamClaimObjects = append(ipamClaimObjects, object)
161165
default:
162166
v1Objects = append(v1Objects, object)
163167
}
@@ -172,7 +176,7 @@ func (o *FakeOVN) start(objects ...runtime.Object) {
172176
MultiNetworkPolicyClient: mnpfake.NewSimpleClientset(multiNetworkPolicyObjects...),
173177
EgressServiceClient: egressservicefake.NewSimpleClientset(egressServiceObjects...),
174178
AdminPolicyRouteClient: adminpolicybasedroutefake.NewSimpleClientset(apbExternalRouteObjects...),
175-
IPAMClaimsClient: fakeipamclaimclient.NewSimpleClientset(),
179+
IPAMClaimsClient: fakeipamclaimclient.NewSimpleClientset(ipamClaimObjects...),
176180
NetworkAttchDefClient: nadClient,
177181
UserDefinedNetworkClient: udnclientfake.NewSimpleClientset(),
178182
}
@@ -410,6 +414,7 @@ func NewOvnController(
410414
EgressServiceClient: ovnClient.EgressServiceClient,
411415
APBRouteClient: ovnClient.AdminPolicyRouteClient,
412416
EgressQoSClient: ovnClient.EgressQoSClient,
417+
IPAMClaimsClient: ovnClient.IPAMClaimsClient,
413418
},
414419
wf,
415420
recorder,
@@ -525,6 +530,7 @@ func (o *FakeOVN) NewSecondaryNetworkController(netattachdef *nettypes.NetworkAt
525530
Kube: kube.Kube{KClient: o.fakeClient.KubeClient},
526531
EIPClient: o.fakeClient.EgressIPClient,
527532
EgressFirewallClient: o.fakeClient.EgressFirewallClient,
533+
IPAMClaimsClient: o.fakeClient.IPAMClaimsClient,
528534
},
529535
o.watcher,
530536
o.fakeRecorder,

0 commit comments

Comments
 (0)