Skip to content

fix(vm): allow volume migration while restart is pending#2634

Open
danilrwx wants to merge 18 commits into
mainfrom
fix/volume-migration-restart-required
Open

fix(vm): allow volume migration while restart is pending#2634
danilrwx wants to merge 18 commits into
mainfrom
fix/volume-migration-restart-required

Conversation

@danilrwx

@danilrwx danilrwx commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Description

Virtual machines with local (RWO) disks could get stuck unable to migrate whenever they had a pending restart. The "restart required to apply configuration" state can appear on its own after a module upgrade, without any user change; while it was set, volume migration of the VM's local disks was skipped entirely. Migrations initiated by evacuation (node drain), firmware updates and other workload updates then hung indefinitely — target disks were never prepared and the migration stayed Pending.

The same over-broad skip also blocked recovery: if a volume migration was interrupted, the VM could be left running while its disk still pointed at the discarded target, and it could never migrate again.

The pending-restart state and volume migration are unrelated: volume migration only moves a disk between PVCs and never applies the changes that actually require a restart. The skip is now narrowed so that only a genuine structural disk change (a disk added or removed, which may need a restart) waits for the restart, while a real volume migration — and the roll-back of an interrupted one — proceeds regardless of a pending restart.

Why do we need it, and what problem does it solve?

Local-disk VMs could end up in a state where a node cannot be drained and firmware/workload updates never finish, with no user-visible reason — the VM just reports it needs a restart and its migration hangs forever. An interrupted migration could leave the VM permanently unable to migrate. Both dead-ends are removed here, so evacuation, updates and repeated migrations work for local-disk VMs without a manual restart.

What is the expected result?

For a running VM with local (RWO) disks that has the "restart required to apply configuration" condition:

  1. Trigger a migration — drain its node, or start a firmware/workload update (an Evict operation). The volume migration completes instead of staying Pending.
  2. Interrupt a migration mid-flight — the disk returns to its source and the VM can be migrated again immediately.
  3. A structural disk change (adding/removing a disk that requires a restart) still correctly waits for the restart and is applied only after it.

Checklist

  • The code is covered by unit tests.
  • e2e tests passed.
  • Documentation updated according to the changes.
  • Changes were tested in the Kubernetes cluster manually.

Changelog entries

section: vm
type: fix
summary: "Virtual machines with local disks can now be evacuated, updated and re-migrated even while a restart is pending, instead of getting stuck unable to migrate."

danilrwx added 3 commits July 10, 2026 17:10
Volume migration for local (RWO) disks was fully skipped whenever the VM
had the AwaitingRestartToApplyConfiguration condition. This created a
deadlock: a spurious RestartRequired (e.g. memory defaulting drift after
an upgrade) blocked volume migration, so target PVCs never synced, the
migration stayed Pending forever and firmware/workload-update VMOPs hung.

RestartRequired and volume migration are orthogonal: patchVolumes only
touches volumes/affinity/updateVolumesStrategy and never applies
non-live-updatable domain fields. Narrow the early skip to the single
branch that would propagate a structural volume change (added/removed
disk, which may require a restart) to KVVM, so genuine volume migration
proceeds while awaiting a restart.

Signed-off-by: Daniil Antoshin <daniil.antoshin@flant.com>
The previous fix let volume migration proceed under a pending restart, but
still skipped it entirely when the desired volume set also differed
structurally from the running one (a disk added or removed). That meant a
node drain could be blocked by an unrelated pending disk change on a VM.

Keep delaying the structural change until the restart, but no longer block
the migration of already-attached disks: rebuild the volume patch from the
running KVVM structure and only redirect migrating disks to their target
PVCs. A structural change without an active migration still waits for the
restart, unchanged.

Signed-off-by: Daniil Antoshin <daniil.antoshin@flant.com>
Temporary: build 3p-kubevirt from the fix/volume-migration-restart-required
branch to test the volume-migration RestartRequired fix end-to-end. Revert
to a tagged version before merge.

Signed-off-by: Daniil Antoshin <daniil.antoshin@flant.com>
@danilrwx danilrwx force-pushed the fix/volume-migration-restart-required branch from 83a52e0 to 49cb516 Compare July 10, 2026 15:10
@danilrwx danilrwx added this to the v1.10.0 milestone Jul 10, 2026
danilrwx added 15 commits July 10, 2026 18:37
Temporary diagnostic log in getActionIfMigrationInProgress to capture why
a local volume migration reverts under KubeVirt-side RestartRequired.
Revert this commit before merge.

Signed-off-by: Daniil Antoshin <daniil.antoshin@flant.com>
Under a KubeVirt-side RestartRequired, a local (RWO) disk migration could
enter a revert loop: right after the compute migration of a cycle
succeeded and the disk migration completed, a new volume migration was
started because the RWO disk still reports DisksShouldBeMigrating while the
VMOP was being finalized. The new prepare overwrote the disk migration
start timestamp, so isMigrationsMatched no longer held on the next
reconcile, the migration was reverted, and the cleared target made the disk
eligible for migration again.

Fix it in two places:
- getAction: do not start a new volume migration while the current cycle's
  compute migration is finalizing (its end timestamp is set), so the
  restart-induced race window no longer triggers a re-prepare.
- getActionIfMigrationInProgress: if the compute migration already
  succeeded, finalize the disk migration instead of blindly reverting, so a
  concurrent re-prepare cannot drop the already-migrated target.

Also removes the temporary diagnostic log added to investigate this.

Signed-off-by: Daniil Antoshin <daniil.antoshin@flant.com>
The previous guard skipped starting a volume migration whenever
vm.Status.MigrationState had an end timestamp. That state is not cleared
after a migration finishes, so any subsequent migration (e.g. a new Evict
operation) was skipped forever and stuck in
WaitingForVirtualMachineToBeReadyToMigrate.

Compare the compute migration end time against the current operation's
creation time: only skip the re-prepare when the migration that finished
belongs to the operation still being finalized, not a stale one from a
previous migration.

Signed-off-by: Daniil Antoshin <daniil.antoshin@flant.com>
Replace the timestamp-based guard against the volume migration revert loop
with a phase check. A local (RWO) disk always reports DisksShouldBeMigrating,
so getAction would re-prepare a target right after a migration completed
(while the operation was being finalized) — overwriting the disk migration
timestamp, breaking isMigrationsMatched and reverting the migration in a loop
under a pending restart.

Now a new target is prepared only while the operation is in the
WaitingForVirtualMachineToBeReadyToMigrate phase, i.e. while it actually waits
for disks. Once the compute migration has started or finished, no new prepare
is triggered, so the loop cannot form. This also drops the earlier
timestamp comparison and the complete-vs-revert safety net, which relied on a
stale MigrationState and were the ugly part of the previous fix.

Signed-off-by: Daniil Antoshin <daniil.antoshin@flant.com>
Move the structural-change-under-restart handling out of the large
SyncVolumes body into migrateAttachedVolumesDelayingStructural, and inline
the single-use rebase helpers into it. Same behavior: an in-progress volume
migration of the attached disks proceeds while a pending structural change
waits for the restart.

Signed-off-by: Daniil Antoshin <daniil.antoshin@flant.com>
…ructural

The method always returned an empty reconcile.Result (unparam). Return
just error and build the Result at the call site.

Signed-off-by: Daniil Antoshin <daniil.antoshin@flant.com>
…isks

Move the in-progress migrating VMOP lookup into migratingIsWaitingForDisks
so getAction no longer threads the operation through. Same behavior.

Signed-off-by: Daniil Antoshin <daniil.antoshin@flant.com>
Signed-off-by: Daniil Antoshin <daniil.antoshin@flant.com>
Remove migrateAttachedVolumesDelayingStructural. It only served the rare
non-virtio edge case where a structural disk change (add/remove) coincides
with an in-progress migration. Now a structural change under a pending
restart simply waits for the restart; genuine volume migration (unchanged
structure) still proceeds through the branches below.

Signed-off-by: Daniil Antoshin <daniil.antoshin@flant.com>
handleRevert deleted the target PVC and marked the migration failed but
never pointed the disk back at its source PVC (unlike the revert branches in
handleComplete). The disk was left with a dangling/empty target, so the VM
got stuck: no valid target and no new migration could start. Set the active
PVC back to the source on revert so the disk recovers to a consistent state.

Signed-off-by: Daniil Antoshin <daniil.antoshin@flant.com>
The revert did not resolve the stuck state: vd.Status.Target stayed empty
even though handleRevert set it back to the source PVC. The real cause is
elsewhere in the Target lifecycle; reverting to keep history honest while
investigating.

Signed-off-by: Daniil Antoshin <daniil.antoshin@flant.com>
A restart-required VM whose KVVM was left pointing at a dead migration
target PVC (a previous volume migration that failed and was reverted on the
disk side but not on the KVVM) could never recover: the restart-required
guard in SyncVolumes delayed *any* volume-set difference, including the PVC
swap needed to revert KVVM back to the source. Every later migration then
hung in Pending forever.

Only a structural change (a disk added or removed) can require a restart. A
difference that is just a PVC swap on the same set of disks is a volume
migration or its revert and must proceed regardless of restart. Gate the
delay on isStructuralVolumeChange so reverts and migrations are no longer
blocked.

Signed-off-by: Daniil Antoshin <daniil.antoshin@flant.com>
Update the structural-change test to model a real structural change (a disk
added) instead of a same-disk PVC swap, and add a case asserting that a PVC
swap (a migration revert) is applied even while a restart is pending.

Signed-off-by: Daniil Antoshin <daniil.antoshin@flant.com>
Cover the helper directly: added/removed/renamed disks are structural, while
a PVC swap on the same set of volume names (a migration or its revert) is not.

Signed-off-by: Daniil Antoshin <daniil.antoshin@flant.com>
Add an e2e case to RWOVirtualDiskMigration that makes the VM restart-required
via a delayed configuration change and then migrates it. Guards the regression
where volume migration of local disks hung whenever the VM had a pending
restart, and asserts the delayed change is neither lost nor applied without a
restart.

Signed-off-by: Daniil Antoshin <daniil.antoshin@flant.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant