Skip to content

Commit

Permalink
podNetworkController: Disable LSP when VM migration target pod is ready
Browse files Browse the repository at this point in the history
currently all pods have the LSP.Enabled option set to true by default.
During migration, it makes sense to handoff the network traffic from the
source to target pod as soon as the target pod is up and ready for
traffic.
The handoff is done by closing the LSP of the source port.
Doing this significantly reduces the migration downtime.

This traffic handoff optimization is currently restricted to VM with
only secondary interfaces, and layer2 topology.

Signed-off-by: Ram Lavi <ralavi@redhat.com>
  • Loading branch information
RamLavi committed Oct 14, 2024
1 parent d54979e commit 3af1f6b
Show file tree
Hide file tree
Showing 2 changed files with 94 additions and 0 deletions.
75 changes: 75 additions & 0 deletions go-controller/pkg/kubevirt/pod.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@ import (
"github.com/ovn-org/ovn-kubernetes/go-controller/pkg/util"
)

// IsVMPod will return true if the pod belongs to kubevirt
func IsVMPod(pod *corev1.Pod) bool {
_, ok := pod.Labels[kubevirtv1.VirtualMachineNameLabel]
return ok
}

// IsPodLiveMigratable will return true if the pod belongs
// to kubevirt and should use the live migration features
func IsPodLiveMigratable(pod *corev1.Pod) bool {
Expand Down Expand Up @@ -343,3 +349,72 @@ func ZoneContainsPodSubnetOrUntracked(watchFactory *factory.WatchFactory, lsMana
// to a node
return hostSubnets, !util.IsContainedInAnyCIDR(annotation.IPs[0], hostSubnets...), nil
}

func podDuringMigration(vmPods []*corev1.Pod) bool {
return len(vmPods) > 0
}

func isSourcePodAlreadyStale(pod *corev1.Pod, vmPods []*corev1.Pod) bool {
if util.PodCompleted(pod) {
return true
}
for _, vmPod := range vmPods {
if vmPod.CreationTimestamp.After(pod.CreationTimestamp.Time) {
return true
}
}

return false
}

func getTargetPod(vmPods []*corev1.Pod) *corev1.Pod {
targetPodNominee := vmPods[0]
for i := 1; i < len(vmPods); i++ {
if vmPods[i].CreationTimestamp.After(targetPodNominee.CreationTimestamp.Time) {
targetPodNominee = vmPods[i]
}
}
return targetPodNominee
}

func isTargetPodReady(targetPod *corev1.Pod) bool {
if targetPod == nil {
return false
}

// This annotation only appears on live migration scenarios, and it signals
// that target VM pod is ready to receive traffic, so we can route
// traffic to it.
targetReadyTimestamp := targetPod.Annotations[kubevirtv1.MigrationTargetReadyTimestamp]

// VM is ready to receive traffic
return targetReadyTimestamp != ""
}

func filterNotComplete(vmPods []*corev1.Pod) []*corev1.Pod {
var notCompletePods []*corev1.Pod
for _, vmPod := range vmPods {
if !util.PodCompleted(vmPod) {
notCompletePods = append(notCompletePods, vmPod)
}
}

return notCompletePods
}

func IsMigrationReadyForTrafficHandoff(client *factory.WatchFactory, pod *corev1.Pod) (bool, error) {
vmPods, err := findVMRelatedPods(client, pod)
if err != nil {
return false, fmt.Errorf("failed finding related pods for pod %s/%s when checking live migration left overs: %v", pod.Namespace, pod.Name, err)
}
vmPods = filterNotComplete(vmPods)

if !podDuringMigration(vmPods) {
return false, nil
}

if isSourcePodAlreadyStale(pod, vmPods) && isTargetPodReady(getTargetPod(vmPods)) {
return true, nil
}
return false, nil
}
19 changes: 19 additions & 0 deletions go-controller/pkg/ovn/base_network_controller_pods.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/klog/v2"
"k8s.io/utils/ptr"

libovsdbclient "github.com/ovn-org/libovsdb/client"
"github.com/ovn-org/libovsdb/ovsdb"
Expand Down Expand Up @@ -588,6 +589,20 @@ func (bnc *BaseNetworkController) addLogicalPortToNetwork(pod *kapi.Pod, nadName
}
}

lsp.Enabled = ptr.To(true)
if kubevirt.IsVMPod(pod) &&
isAllowedForMigration(bnc.IsSecondary(), bnc.IsPrimaryNetwork(), bnc.isLayer2Interconnect()) {
migrationProcessReady, err := kubevirt.IsMigrationReadyForTrafficHandoff(bnc.watchFactory, pod)
if err != nil {
return nil, nil, nil, false, err
}

if migrationProcessReady || util.PodWantsHostNetwork(pod) {
// Perform Traffic handoff by disabling src pod LSP
lsp.Enabled = ptr.To(false)
}
}

ops, err = libovsdbops.CreateOrUpdateLogicalSwitchPortsOnSwitchOps(bnc.nbClient, nil, ls, lsp)
if err != nil {
return nil, nil, nil, false,
Expand Down Expand Up @@ -1162,3 +1177,7 @@ func (bnc *BaseNetworkController) wasPodReleasedBeforeStartup(uid, nad string) b
}
return bnc.releasedPodsBeforeStartup[nad].Has(uid)
}

func isAllowedForMigration(isSecondary, isPrimaryNetwork, isl2Topology bool) bool {
return isSecondary && !isPrimaryNetwork && isl2Topology
}

0 comments on commit 3af1f6b

Please sign in to comment.