Skip to content

fix(vm): prevent volume migration deadlock with staggered disk migrations#2680

Draft
universal-itengineer wants to merge 51 commits into
mainfrom
fix/vmop-evict-volume-migration-deadlock
Draft

fix(vm): prevent volume migration deadlock with staggered disk migrations#2680
universal-itengineer wants to merge 51 commits into
mainfrom
fix/vmop-evict-volume-migration-deadlock

Conversation

@universal-itengineer

Copy link
Copy Markdown
Member

Description

Evicting or migrating a virtual machine that has local disks (including hot-plugged ones) could hang indefinitely: the operation stayed Pending, the disks stayed in Migrating, and empty target volumes piled up.

It happened when the VM's disks entered migration at slightly different moments: the migration was started with only some disks switched to their new target and the rest still on the source. The hypervisor rejects such an incomplete set, and the operation could never recover from it.

The migration now waits until every local disk has joined the set before it starts, so only a complete, valid migration is issued. As a safety net, a migration that never becomes ready to start is failed after a timeout, so its half-prepared disks are rolled back and the leftover target volumes are cleaned up instead of hanging forever.

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

Without this, an Evict of a VM with local + hot-plugged disks can wedge that VM: it keeps running but can no longer be migrated or drained off its node, and orphaned target PersistentVolumeClaims accumulate. Recovery previously required manual cleanup.

What is the expected result?

  1. Create a VM with a local (RWO) root disk and several hot-plugged local disks.
  2. Issue an Evict operation.
  3. Expected: the operation reaches Completed and the VM migrates. It no longer gets stuck in Pending, no disk is left in Migrating, and no orphaned target PVCs remain.
  4. If the VM can never become ready to migrate, the operation now ends as Failed (with a clear message) and its disk migration is reverted, rather than hanging.

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: "Evicting or migrating a virtual machine with local disks no longer gets stuck in Pending, and leftover migration volumes are no longer orphaned."

Isteb4k and others added 30 commits July 7, 2026 13:46
Signed-off-by: Dmitry Rakitin <dmitry.rakitin@flant.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Changelog v1.9.3

Signed-off-by: deckhouse-BOaTswain <89150800+deckhouse-boatswain@users.noreply.github.com>
Co-authored-by: universal-itengineer <universal-itengineer@users.noreply.github.com>
Signed-off-by: Valeriy Khorunzhin <valeriy.khorunzhin@flant.com>
During a restore the VM is stopped inside the maintenance window: its KVVMI and then the KVVM itself are deleted. Bringing the VM back up afterwards relied on an implicit side effect — the recreated KVVM getting RunStrategy=Always at creation time. That path is racy and only covers the always-on policies.

---------

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

Signed-off-by: Maksim Fedotov <maksim.fedotov@flant.com>
Signed-off-by: Maksim Fedotov <maksim.fedotov@flant.com>
Add validation to getIPerfClientReport verifying that the iperf client session spans the virtual machine migration: start time before migration start, end time after migration end, and no more than one zero-byte interval during the process.

Signed-off-by: Dmitry Prytkov <dmitry.prytkov@flant.com>
…refix (#2594)

The d8 v ansible-inventory command read host variables from the bare
provisioning.virtualization.deckhouse.io/ prefix, while the
virtualization-provisioner module reads them from the separate
vars.provisioning.virtualization.deckhouse.io/ prefix. The same VM
annotations produced different inventories depending on the tool.

Read host variables from the vars. prefix to match the module. Groups
still come from provisioning.virtualization.deckhouse.io/groups. Update
the command help, usage examples and the ansible-inventory FAQ.

Signed-off-by: Pavel Tishkov <pavel.tishkov@flant.com>
…d manually (#2596)

fix(vm): fix VM stuck until the child KVVMI in Failed phase is deleted manually

Signed-off-by: Yaroslav Borbat <yaroslav.borbat@flant.com>
Virtualization has no primitive to manage a group of identical virtual machines whose count changes over time. Every "I need N identical VMs and the number varies" scenario — CI runner fleets, VDI desktop pools — has to be solved with orchestration outside the platform: users write their own controllers/scripts to create and delete VirtualMachines, watch their number, recreate lost ones and clean up their disks. This duplicates logic and is error-prone around races and node failures.

This PR introduces VirtualMachinePool (paid editions only, EE/SE+): a namespaced resource that declaratively keeps a requested number of identical VMs and integrates with kubectl scale, HPA and KEDA through the standard scale subresource. Its template is an ordinary VirtualMachineSpec, so a replica is no different from a manually created VM.

The feature is complete and covers:

Replica management — keeps the requested number of identical VMs, replaces lost ones, and reports state in status (replicas, readyReplicas, selector, conditions). It is cache-lag-safe (ReplicaSet-style expectations), so a lagging informer cache cannot double-create replicas.
Scale-down policy — NewestFirst / OldestFirst choose which replica leaves on anonymous scale-down via scale; Explicit forbids anonymous shrink through a webhook, so for "busy" workloads (CI runners, VDI) replicas can only be removed by address.
Addressed removal — the scaleDownWith subresource deletes named replicas and shrinks the pool by that count, instead of letting the controller pick victims.
In-place template propagation — editing the template rolls the change out to existing replicas; disruptive changes wait for a restart and are surfaced via status.restartPendingReplicas.
Reusable disks — per-replica disks are described in virtualDiskTemplates with a reclaim policy: Delete disks belong to the VM and are removed with it; Retain disks belong to the pool, outlive the replica and are reused on the next scale-up, with an optional warm buffer (keep) and TTL garbage-collection.
The resource is available only in paid editions, gated behind the VirtualMachinePool module feature gate (default off, locked off in CE). The API/CRD installs in every edition, but the controller self-gates on the feature gate, so the resource does nothing in CE.

---------

Signed-off-by: Pavel Tishkov <pavel.tishkov@flant.com>
Signed-off-by: Vladislav Panfilov <vladislav.panfilov@flant.com>
Co-authored-by: Vladislav Panfilov <vladislav.panfilov@flant.com>
…ate (#2621)

The VirtualMachinePool feature gate was an opt-in placeholder. As the feature
has not been released yet, drop the gate before release so the resource is
available in EE/SE+ out of the box and stays unavailable in CE — the same model
as VolumeMigration/TargetMigration. Nothing to migrate: the gate never shipped.

- gate becomes locked-on in EE/SE+, locked-off in CE (no user opt-in)
- dropped from the user-facing featureGates enum
- pool webhooks gated by edition in helm instead of the feature-gate list

Controller/webhooks keep self-gating on the now-locked gate.

Signed-off-by: Pavel Tishkov <pavel.tishkov@flant.com>
Adds:

- liveMigration.systemNetworkName ModuleConfig field. When set, VM live-migration traffic is routed over the per-node interfaces of a SystemNetwork CR from the SDN instead of the default node network.
- migrationiface controller resolves the per-node kernel interface name and writes it onto each Node as the annotation virtualization.deckhouse.io/migration-iface. virt-handler reads that annotation at startup and binds its migration proxy to the SystemNetwork's per-node IP.
- ModuleConfig validator to check if SystemNetwork added to liveMigration.systemNetworkName exists.
- New reason for VMOP if migration network is unavailable on nodes.

---------

Signed-off-by: Daniil Loktev <lokt.daniil@gmail.com>
Signed-off-by: Daniil Loktev <70405899+loktev-d@users.noreply.github.com>
Signed-off-by: Vladislav Panfilov <97229646+prismagod@users.noreply.github.com>
Co-authored-by: Vladislav Panfilov <97229646+prismagod@users.noreply.github.com>
Description
Log in to the private RU registry in the release channels workflow and check module versions against registry.deckhouse.ru/deckhouse.

Why do we need it, and what problem does it solve?
Release channel checks need access to artifacts in the RU registry. This keeps the workflow and version check pointed at the registry that contains the expected release artifacts.

---------

Signed-off-by: Nikita Korolev <nikita.korolev@flant.com>
…lose gaps (#2602)

Sizing policy validation errors were confusing and, in some cases, missing. When a VirtualMachineClass sets per-core memory limits, the error reported the allowed values per CPU core, while users configure the total memory — so the suggested numbers matched nothing they could actually set. Messages now report the total memory to set for the current number of cores, name the field to change, and combine multiple violations into a single readable list.

This also fixes cases where the policy was silently not enforced: the CPU cores step was never validated, and a memory / per-core minimum was ignored when no maximum was set.

Signed-off-by: Pavel Tishkov <pavel.tishkov@flant.com>
* docs: add VM launcher update notes to release notes

Signed-off-by: Vladislav Panfilov <vladislav.panfilov@flant.com>
Description
Removes CDI (Containerized Data Importer) from the platform. Disk and image provisioning no longer goes through CDI DataVolumes — the module now owns the whole import pipeline:

The VD/VI controllers create and track importer/uploader pods and target PVCs directly (new source/step pipeline instead of the DataVolume path).
A new PVC populator controller populates target PVCs from DVCR; host-assigned PVC cloning uses nbdcopy; the clone strategy (snapshot vs host-assigned) is picked from the StorageProfile, cross-CSI PVC-to-PVC cloning is forbidden.
The CDI importer sources are slimmed down into a pvc-importer image; import progress is streamed live from the target pod into the resource status.
CDI deployments (cdi-operator, cdi-apiserver, cdi-controller, cdi-cloner) and their templates/RBAC are removed; a module hook cleans up CDI resources on upgrade while keeping the CDI config CRD for safe rollback to main.
e2e: expanded block-device coverage with progress/quota observers, WFFC and cross-CSI scenarios.
Bugs fixed along the way
Progress & phases

Import progress could jump backwards after an importer pod restart (metric reset to 0); it is now monotonic (c2a61a4).
An optimistic "0%" surfaced as an intermediate Pending 0% status; Pending now reports no progress and the Pending→Provisioning transition is atomic (d122212).
Progress flip-flopped between 50%/50.0%; percent formatting is now consistent (00b93a0).
VirtualImage-on-PVC never streamed intermediate progress (hardcoded 0%→50%→100%) and under-reported the produced image size, breaking downstream PVC sizing; both now come from the importer pod (680608b).
A busy importer /metrics endpoint could freeze Status.Progress for the whole scrape budget (d122212).
Import pipeline

Interrupted PVC imports were not resumed for already-created targets (b95b230).
Importer pods for VI/CVI sourced from a VI-on-PVC always mounted the source as a block device, failing with FailedMount on volumeMode: Filesystem PVCs (b79920c).
VirtualImage PVC auto-sizing tripped a false "insufficient size" check (7edb744); status.format was copied from source metadata instead of the actual PVC storage type (0fa1bcf).
VirtualImage WFFC imports hung in the populator flow (0fb3661); smart-clone image PVCs were never bound (8cc0139).
Clone snapshots set a VolumeSnapshotClassName picked by provisioner match, which the d8-snapshot-controller webhook could reject (d59b1e2); replicated PVCs now use the snapshot clone strategy (c2f69e1).
pvc-importer inherited generic provisioning pod limits, throttling imports ~30% and risking OOMKill on huge images; limits raised to CDI parity (b50d7b2).
Importer/uploader/bounder pods are labeled to be excluded from project resource quotas (d122212, and quota guards in 9b7fc55).
A create rejection in a terminating namespace failed the reconcile instead of being surfaced on the Ready condition (c2a61a4).
Migrations & VMOP

The inbound migration limiter leaked slots of VMIs deleted mid-migration, eventually freezing all migrations cluster-wide (43e4f3c).
Affinity changes of VMs with local (RWO) disks never triggered a placement migration: the handler required live-migratability and ignored StorageLiveMigratable (67c29ba).
The workload updater kept creating evict VMOPs that the webhook was guaranteed to deny, flooding controller logs (f6b0ac0).
Why do we need it, and what problem does it solve?
CDI was a heavyweight third-party dependency (4 deployments, its own CRDs and RBAC) used only as a PVC provisioning backend. Owning the import pipeline removes the DataVolume indirection, gives accurate live import progress, allows to fix bugs directly, and cuts the CVE/maintenance surface of the vendored CDI codebase.

What is the expected result?
Creating VirtualDisks/VirtualImages from all sources (HTTP, containerRegistry, ObjectRef, upload, blank) works as before without any CDI components in the cluster; import progress is reported in the resource status. Upgrading from main cleans up CDI resources automatically; rollback to main remains safe.

---------

Signed-off-by: Dmitry Rakitin <dmitry.rakitin@flant.com>
Bump the size of the VirtualDisk restored from a VirtualImage in the VirtualImageCreation e2e suite from 400Mi to 450Mi. The change is in the shared runVirtualMachineFromImageDisk helper (test/e2e/blockdevice/virtual_image_creation.go), so it covers every context that boots a VM from a restored image disk (from a VirtualDisk, from a VirtualDiskSnapshot, from a VirtualImage on DVCR/PVC).

---

Signed-off-by: Dmitry Rakitin <dmitry.rakitin@flant.com>
Integrates the new bpfbridge network binding plugin (kubevirt side: deckhouse/3p-kubevirt#99) into the virtualization module and switches VM network interfaces to it, replacing the classic tap + veth + Linux-bridge datapath used for VM networks.

Signed-off-by: Dmitry Lopatin <dmitry.lopatin@flant.com>
Signed-off-by: Maksim Garmonov <maksim.garmonov@flant.com>
Signed-off-by: suselz <suselz@mail.ru>
Developers debugging the module with Delve had a broken helper. The dlv build/push flow shipped a single hardcoded kubectl patch snippet that assumed every component (virtualization-controller, virtualization-api, virtualization-audit) has the same sidecars (proxy, kube-rbac-proxy). Only the controller actually has them, so applying the printed patch to the api or audit deployment failed — it referenced containers that don't exist there.


---------

Signed-off-by: Daniil Antoshin <daniil.antoshin@flant.com>
…stuck uploads (#2610)

Uploading an image or disk (a VirtualImage, ClusterVirtualImage, or VirtualDisk with an Upload data source) could hang forever in Pending with an empty status and no upload URL, so the image simply could not be uploaded.

It happened when the shared upload host started serving an invalid TLS certificate — for example after another, broken upload resource left the host serving a default self-signed certificate. In that state every upload on the cluster was blocked, not just the broken one, and recovery required manually finding and deleting the offending resource.

The controller now restores the upload host certificate on its own during reconcile, so affected uploads recover automatically, and a not-yet-ready upload endpoint no longer leaves the resource with an empty status.

This PR also restores a disk-specific regression: a recent refactor of the VirtualDisk upload flow dropped the upload-host self-healing for disks, so after the upload host changed (for example when publicDomainTemplate was updated) an in-flight disk upload could get stuck. The self-healing is reinstated for disks, matching how images already behave.



Signed-off-by: Pavel Tishkov <pavel.tishkov@flant.com>
New conntrack sync metrics: d8_virtualization_virtualmachine__conntrack_sync_exports_total, d8_virtualization_virtualmachine__conntrack_sync_exported_bytes_total, d8_virtualization_virtualmachine__conntrack_sync_imports_total, d8_virtualization_virtualmachine__conntrack_sync_hook_waits_total

---------

Signed-off-by: Daniil Loktev <lokt.daniil@gmail.com>
Signed-off-by: Daniil Loktev <70405899+loktev-d@users.noreply.github.com>
Signed-off-by: Daniil Antoshin <daniil.antoshin@flant.com>
* chore(api): integrate crd-enricher into CRD codegen and add CRD examples

Signed-off-by: Vladislav Panfilov <vladislav.panfilov@flant.com>
…#2624)

Description
Virtual machines created without paravirtualization keep all their disks and CD-ROMs on the SATA bus forever, even after enableParavirtualization is turned on and the VM is restarted. The recorded bus of an existing device always took priority over the bus implied by the paravirtualization setting.

Now, when the paravirtualization mode changes, devices move to the bus of the new mode on the restart this change already requires. Devices hot-plugged on a non-default bus (for example USB) keep their bus as before.

---------

Signed-off-by: Nikita Korolev <nikita.korolev@flant.com>
… resources with d8- (#2632)

* fix(observability): default audit TLS cert paths to certificates dir

The mounted secret is at /etc/virtualization-audit/certificates (plural),
but the flag defaults pointed at /etc/virtualization-audit/certificate,
so the server failed to load TLS when started without explicit flags.

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

* refactor(observability): prefix audit log-shipper resources with d8- and drop to-

Rename the ClusterLoggingConfig and ClusterLogDestination objects created for
audit shipping: to-virtualization-audit -> d8-virtualization-audit and
virtualization-audit-* -> d8-virtualization-audit-*, aligning with the d8-
naming convention for module-owned cluster-scoped resources.

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

---------

Signed-off-by: Daniil Antoshin <daniil.antoshin@flant.com>
Signed-off-by: Vladislav Panfilov <vladislav.panfilov@flant.com>
update CODEOWNERS

Signed-off-by: Vladislav Panfilov <vladislav.panfilov@flant.com>
Signed-off-by: Maksim Fedotov <maksim.fedotov@flant.com>
Signed-off-by: Maksim Fedotov <maksim.fedotov@flant.com>
eofff and others added 19 commits July 13, 2026 15:52
Signed-off-by: Valeriy Khorunzhin <valeriy.khorunzhin@flant.com>
Signed-off-by: Vladislav Panfilov <vladislav.panfilov@flant.com>
Signed-off-by: Dmitry Rakitin <dmitry.rakitin@flant.com>
Co-authored-by: Dmitry Rakitin <dmitry.rakitin@flant.com>
Signed-off-by: deckhouse-BOaTswain <89150800+deckhouse-boatswain@users.noreply.github.com>
Co-authored-by: nevermarine <nevermarine@users.noreply.github.com>
Description
Prepares the deckhouse/virtualization module to move from GitHub to GitLab by porting the CI. All development pipelines that ran on GitHub Actions now run on GitLab CI with the same behavior: build and deploy (dev/prod, all editions), lint, tests, secret/CVE/Svace scanning, changelog and backport automation, MR auto-assign, and manual release-channel dispatch.

The root .gitlab-ci.yml is a thin entrypoint; the pipeline lives in .gitlab/ci/**.

Not migrated in this iteration
e2e workflows — out of scope.
Reactive slash-commands / webhook listener — GitLab can't start pipelines from MR comments or label changes natively. Manual and scheduled jobs cover the same surface; a webhook listener is a possible follow-up.
Correctness fixes (from review)
Editions could be built/tagged wrong on release and manual pipelines. Some jobs picked up stray pipeline variables that overrode their own settings (a release tag built the CE prod image as EE; a manual precache pushed the wrong tag). The affected build/scan jobs are now isolated to their own config.
mrs:summary could never run — it expected Node.js on the shell runner, which has none; it now runs in the node container like the other JS job.
Malformed Loop release notification — the message is assembled safely, so a tag or channel value with quotes/newlines can't break or inject into it.
Generated-files check ran too much — the heavy vm-route-forge (bpf2go, docker) check now runs only on vm-route-forge changes, not on every change.

---------

Signed-off-by: Nikita Korolev <nikita.korolev@flant.com>
…ion=false (#2645)

Description
On a VM with enableParavirtualization=false, disks attached via VirtualMachineBlockDeviceAttachment were switched to the SATA bus, even though hot-plugged disks must stay on SCSI. This broke such attachments. The bus of a runtime hot-plugged disk is now preserved regardless of the paravirtualization mode.

---------

Co-authored-by: Ivan Mikheykin <ivan.mikheykin@flant.com>
Signed-off-by: Nikita Korolev <nikita.korolev@flant.com>
…tection (#2659)

Description
ProtectionService.RemoveProtection re-reads a fresh copy of the object and
removes only our finalizer under an optimistic lock, retrying on conflict,
instead of resending the whole (possibly stale) finalizers list.

Why do we need it, and what problem does it solve?
On a terminating PVC the built-in pvc-protection controller strips
kubernetes.io/pvc-protection. Patching from a stale copy resent it, and the
API server rejected the update: no new finalizers can be added if the object is being deleted. The VM deletion handler surfaced this as failed to release PVC protection, never removed the VM cleanup finalizer, and VM deletion hung.

What is the expected result?
Deleting a VM whose block-device PVC is terminating completes cleanly; the
errors above no longer appear in the vm-controller log.

Signed-off-by: Dmitry Rakitin <dmitry.rakitin@flant.com>
- fixed CVEs from #2609

Signed-off-by: Ivan Mikheykin <ivan.mikheykin@flant.com>
…#2638)

Description
The virtualization-dra USB DaemonSet pods never start — they stay in CrashLoopBackOff on every node, so USB device passthrough is unavailable. The main container is privileged and runs with a read-only root filesystem while also bind-mounting the host /sys. Under containerd this combination makes the runtime fail to set up the /sys/fs/cgroup mount on the read-only rootfs, and the container never starts.

A privileged container already gets a read-write /sys from the runtime, so the explicit /sys mount is redundant. It is removed together with its volume and the now-dead /sys entry in the SecurityPolicy host-path allow-list.

-------------

Signed-off-by: Nikita Korolev <nikita.korolev@flant.com>
Signed-off-by: Valeriy Khorunzhin <valeriy.khorunzhin@flant.com>
fix(dra): load compressed usbip kernel modules (xz, gzip)

Pre-installed usbip modules may ship compressed as .ko.xz or .ko.gz
depending on the host distribution, not only .ko or .ko.zst. Resolve all
kernel-supported compression suffixes (zstd, xz, gzip) in the module path
fallback and decompress accordingly before finit_module.

Signed-off-by: Daniil Antoshin <daniil.antoshin@flant.com>
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.

---------

Signed-off-by: Daniil Antoshin <daniil.antoshin@flant.com>
Signed-off-by: Vladislav Panfilov <vladislav.panfilov@flant.com>
Signed-off-by: Dmitry Rakitin <dmitry.rakitin@flant.com>
Co-authored-by: Dmitry Rakitin <dmitry.rakitin@flant.com>
…overy (#2664)

* chore(vmclass): discovered features should be frozen after first discovery

- Use first time discovered feature set to filter availableNodes on later reconciles.
- Add errors in case no availableNodes are present in the cluster.

---------

Signed-off-by: Ivan Mikheykin <ivan.mikheykin@flant.com>
Signed-off-by: deckhouse-BOaTswain <89150800+deckhouse-boatswain@users.noreply.github.com>
Co-authored-by: nevermarine <nevermarine@users.noreply.github.com>
Signed-off-by: Maksim Fedotov <maksim.fedotov@flant.com>
A SecureBoot virtual machine stores its Secure Boot state in a persistent volume, which needs a default StorageClass. On a cluster without one the volume can't be created and the VM never starts — and nothing on the VirtualMachine explained why, the error was only visible on internal objects.

---------

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

Signed-off-by: Valeriy Khorunzhin <valeriy.khorunzhin@flant.com>
…ions

An Evict/migration of a VM with local (RWO) disks could deadlock in Pending
forever, leaving orphaned target PersistentVolumeClaims and VirtualDisks stuck
in Migrating.

Root cause: getReadyTargetPVCs checked disk.Status.Target.PersistentVolumeClaim
(the source PVC, which stays bound until the migration completes) instead of the
migration target. A disk that had not started migrating yet was therefore
counted as ready, so a partial migration set (some disks switched to their
target, others still on source) was applied to the KVVM. KubeVirt rejects such a
set because an RWO volume is left out ("is RWO and not included in the migration
volumes"), so the VM never becomes ready to migrate, the operation never creates
the migration, and the pre-created target PVCs are orphaned.

- Check the migration target PVC in getReadyTargetPVCs so the migration set is
  applied only once every RWO disk has joined it (all-or-nothing).
- Fail the migration operation after readyToMigrateTimeout if the VM never
  becomes ready to migrate, so its half-prepared disk migration is reverted and
  the orphaned target PVCs are cleaned up instead of hanging forever; requeue
  while waiting so the timeout is actually evaluated.

Signed-off-by: Nikita Korolev <nikita.korolev@flant.com>
During a volume migration of a VM with a hot-plugged disk on local
WaitForFirstConsumer storage, the controller could transiently patch the KVVM
volume list while a hotplug attach was still being reconciled. patchVolumes
replaces the volume list but not the disk list, so the running KVVM briefly had
a disk without its volume; KubeVirt rejected the patch and the VM flipped into a
transient "restart required" state, logging an error on every convergence.

Wait for the built spec and the running KVVM to agree on the disk set before
patching volumes. A volume migration never changes the disk set (it only swaps
the PVC a disk points at), so this never blocks a legitimate migration; it only
defers the volume patch while the disk topology is still changing. The disk list
is deliberately not patched, since removing a disk from a running VM would
detach a hot-plugged volume.

Signed-off-by: Nikita Korolev <nikita.korolev@flant.com>
…olume migration

While a volume migration is in progress, KubeVirt transiently reports
RestartRequired (an RWO volume is briefly not yet part of the migration set) and
clears it once the migration converges. The controller treated this as a real
"restart required": it raised the AwaitingRestartToApplyConfiguration condition
and logged "Please restart the virtual machine to synchronize its configuration"
as an error on every volume migration, even though no user action is needed.

Ignore the KVVM RestartRequired condition while updateVolumesStrategy is set to
Migration, so a volume migration no longer produces a spurious restart-required
signal or error log. Genuine restart-required changes are still detected earlier
from the VM spec and are unaffected.

Signed-off-by: Nikita Korolev <nikita.korolev@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.