From bc588e324c1533072a7b239063af5cf2f08bccc7 Mon Sep 17 00:00:00 2001 From: Dmitry Lopatin Date: Thu, 11 Jun 2026 11:59:16 +0300 Subject: [PATCH 1/4] fix(network): skip template network sync for main-only VMs Do not run the network template synchronization path for virtual machines that only use the implicit Main network. The network hotplug reconciliation introduced a networksOutOfSync path that materialized the implicit default pod network in the KubeVirt VM template for already running VMs. Older virt-controller versions can observe that [] -> [default] template diff during module rollout and mark the VM with RestartRequired, which DVP then exposes as AwaitingRestartToApplyConfiguration. The same path also caused a misleading ConfigurationApplied=False message about waiting for SDN even though there are no additional SDN-managed networks. Skip the out-of-sync network reconciliation and readiness/update path for Main-only VMs. Additional networks still use the existing SDN readiness flow. Signed-off-by: Dmitry Lopatin --- .../pkg/controller/vm/internal/sync_kvvm.go | 42 ++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/images/virtualization-artifact/pkg/controller/vm/internal/sync_kvvm.go b/images/virtualization-artifact/pkg/controller/vm/internal/sync_kvvm.go index a4401040f4..32df09a562 100644 --- a/images/virtualization-artifact/pkg/controller/vm/internal/sync_kvvm.go +++ b/images/virtualization-artifact/pkg/controller/vm/internal/sync_kvvm.go @@ -962,7 +962,15 @@ func (h *SyncKvvmHandler) networksOutOfSync(ctx context.Context, s state.Virtual if kvvm == nil { return false, nil } - filteredVM, err := filterReadyNetworks(ctx, s.Client(), s.VirtualMachine().Current()) + vm := s.VirtualMachine().Current() + kvvmi, err := s.KVVMI(ctx) + if err != nil { + return false, err + } + if hasOnlyDefaultNetwork(vm) && !hasActiveAdditionalInterfaces(kvvm) && isKVVMIRunning(kvvmi) { + return false, nil + } + filteredVM, err := filterReadyNetworks(ctx, s.Client(), vm) if err != nil { return false, err } @@ -989,7 +997,39 @@ func (h *SyncKvvmHandler) networksOutOfSync(ctx context.Context, s state.Virtual return false, nil } +func isKVVMIRunning(kvvmi *virtv1.VirtualMachineInstance) bool { + return kvvmi != nil && kvvmi.Status.Phase == virtv1.Running +} + +func hasActiveAdditionalInterfaces(kvvm *virtv1.VirtualMachine) bool { + if kvvm == nil || kvvm.Spec.Template == nil { + return false + } + for _, iface := range kvvm.Spec.Template.Spec.Domain.Devices.Interfaces { + if iface.Name == network.NameDefaultInterface || iface.State == virtv1.InterfaceStateAbsent { + continue + } + return true + } + return false +} + func (h *SyncKvvmHandler) applyNetworkReadinessSync(ctx context.Context, s state.VirtualMachineState) error { + vm := s.VirtualMachine().Current() + if hasOnlyDefaultNetwork(vm) { + kvvm, err := s.KVVM(ctx) + if err != nil { + return fmt.Errorf("find the internal virtual machine: %w", err) + } + kvvmi, err := s.KVVMI(ctx) + if err != nil { + return fmt.Errorf("find the internal virtual machine instance: %w", err) + } + if !hasActiveAdditionalInterfaces(kvvm) && isKVVMIRunning(kvvmi) { + return nil + } + } + desired, err := h.patchPodNetworkAnnotation(ctx, s) if err != nil { return fmt.Errorf("patch pod network annotation: %w", err) From f66a2bf7076255603a9a5aa49ac2a3211ab337d1 Mon Sep 17 00:00:00 2001 From: Dmitry Lopatin Date: Sun, 14 Jun 2026 16:32:53 +0300 Subject: [PATCH 2/4] docs(network): explain implicit default network drift guard Signed-off-by: Dmitry Lopatin --- .../pkg/controller/vm/internal/sync_kvvm.go | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/images/virtualization-artifact/pkg/controller/vm/internal/sync_kvvm.go b/images/virtualization-artifact/pkg/controller/vm/internal/sync_kvvm.go index 32df09a562..df6b09e72f 100644 --- a/images/virtualization-artifact/pkg/controller/vm/internal/sync_kvvm.go +++ b/images/virtualization-artifact/pkg/controller/vm/internal/sync_kvvm.go @@ -967,6 +967,13 @@ func (h *SyncKvvmHandler) networksOutOfSync(ctx context.Context, s state.Virtual if err != nil { return false, err } + // For Main-only VMs, the default pod network may be implicit in the KVVM + // template but explicit in the running VMI: KubeVirt defaulting adds it to + // the VMI spec when the instance starts. If we later "fix" the KVVM template + // from [] to [default] while the VM is already Running, KubeVirt treats that + // template diff as a non-live-updatable network change and sets RestartRequired. + // When there are no active additional interfaces, this is only an implicit-vs- + // explicit default-network drift, so do not reconcile it. if hasOnlyDefaultNetwork(vm) && !hasActiveAdditionalInterfaces(kvvm) && isKVVMIRunning(kvvmi) { return false, nil } @@ -1025,6 +1032,12 @@ func (h *SyncKvvmHandler) applyNetworkReadinessSync(ctx context.Context, s state if err != nil { return fmt.Errorf("find the internal virtual machine instance: %w", err) } + // For Main-only VMs, KubeVirt may have already defaulted the pod network into + // the running VMI while the KVVM template still has no explicit interfaces. + // Waiting for SDN and updating the template would materialize [] -> [default] + // after the VM is Running, which KubeVirt can report as RestartRequired. + // If there are no active additional interfaces, there is nothing SDN-managed + // to wait for or sync, so skip the network readiness/update path. if !hasActiveAdditionalInterfaces(kvvm) && isKVVMIRunning(kvvmi) { return nil } From 28ebd1b72a1ccc6357da320d3b0695ba449cdd1f Mon Sep 17 00:00:00 2001 From: Dmitry Lopatin Date: Mon, 15 Jun 2026 10:38:30 +0300 Subject: [PATCH 3/4] ++ fix condition message Signed-off-by: Dmitry Lopatin --- .../pkg/controller/vm/internal/sync_kvvm.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/virtualization-artifact/pkg/controller/vm/internal/sync_kvvm.go b/images/virtualization-artifact/pkg/controller/vm/internal/sync_kvvm.go index df6b09e72f..d566a39744 100644 --- a/images/virtualization-artifact/pkg/controller/vm/internal/sync_kvvm.go +++ b/images/virtualization-artifact/pkg/controller/vm/internal/sync_kvvm.go @@ -234,7 +234,7 @@ func (h *SyncKvvmHandler) Handle(ctx context.Context, s state.VirtualMachineStat cbConfApplied. Status(metav1.ConditionFalse). Reason(vmcondition.ReasonConfigurationNotApplied). - Message("Waiting for SDN to configure network interfaces on the pod.") + Message("Waiting for SDN to configure network interfaces.") case kvvmSyncErr != nil: h.recorder.Event(current, corev1.EventTypeWarning, v1alpha2.ReasonErrVmNotSynced, kvvmSyncErr.Error()) cbConfApplied. From a03e915199f524f95a7f155e018bc561e711261c Mon Sep 17 00:00:00 2001 From: Dmitry Lopatin Date: Mon, 15 Jun 2026 10:49:39 +0300 Subject: [PATCH 4/4] ++ fix message in event Signed-off-by: Dmitry Lopatin --- .../pkg/controller/vm/internal/sync_kvvm.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/virtualization-artifact/pkg/controller/vm/internal/sync_kvvm.go b/images/virtualization-artifact/pkg/controller/vm/internal/sync_kvvm.go index d566a39744..7002b31ec6 100644 --- a/images/virtualization-artifact/pkg/controller/vm/internal/sync_kvvm.go +++ b/images/virtualization-artifact/pkg/controller/vm/internal/sync_kvvm.go @@ -777,7 +777,7 @@ func (h *SyncKvvmHandler) applyVMChangesToKVVM(ctx context.Context, s state.Virt return fmt.Errorf("unable to check pod network status: %w", err) } if !ready { - msg := "Waiting for SDN to configure network interfaces on the pod" + msg := "Waiting for SDN to configure network interfaces" log.Info(msg) h.recorder.Event(current, corev1.EventTypeNormal, v1alpha2.ReasonVMChangesApplied, msg) return errWaitForNetworkReady