Skip to content

KVM/VMware: warm/change-tracking migration and direct VDDK import into Ceph/RBD and Linstor, plus importVm adoption of existing RBD root volumes#13656

Open
andrijapanicsb wants to merge 15 commits into
apache:mainfrom
andrijapanicsb:kvm-rbd-vmware-migration
Open

KVM/VMware: warm/change-tracking migration and direct VDDK import into Ceph/RBD and Linstor, plus importVm adoption of existing RBD root volumes#13656
andrijapanicsb wants to merge 15 commits into
apache:mainfrom
andrijapanicsb:kvm-rbd-vmware-migration

Conversation

@andrijapanicsb

@andrijapanicsb andrijapanicsb commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Description

This PR makes Ceph/RBD and Linstor first-class targets for KVM VM onboarding and adds an operator-driven warm VMware→KVM migration path. It bundles the following (Ceph/RBD plus Linstor as block-storage destinations):

1. VMware CBT → native RBD/qcow2 warm migration to KVM

An operator-driven, warm VMware-to-KVM migration built on VDDK and VMware Changed Block Tracking (CBT). It maintains a source-equivalent KVM-side replica kept current through repeated delta/sync cycles, then finalizes the guest with virt-v2v in-place at cutover — bounding downtime to the cutover window instead of a full cold copy.

  • Targets both filesystem (qcow2) and native Ceph/RBD (raw image) primary storage.
  • Full lifecycle as async operations with persisted migration state (migration / cycle / disk records), preflight validation, cancel/cleanup, and Import/Export UI integration.
  • New admin API commands: startVmwareCbtMigration, syncVmwareCbtMigration, cutoverVmwareCbtMigration, cancelVmwareCbtMigration, deleteVmwareCbtMigration, listVmwareCbtMigrations, checkVmwareCbtMigrationPrerequisites.
  • Server orchestration in VmwareCbtMigrationManagerImpl (+ VmwareCbtMigrationServiceImpl, cutover policy), backed by new VOs/DAOs and a DB schema migration.
  • KVM agent: libvirt CBT command wrappers (prepare / sync / cutover / RBD-probe) driving VDDK + qemu + virt-v2v, plus agent commands/answers and a sync-plan.
  • UI: new VMware CBT Migrations view and additions to Import/Manage Instances under Tools.
  • Docs: docs/vmware-cbt/ (architecture + README) and docs/vmware-cbt-migration.md.

2. Direct VDDK VMware import into Ceph/RBD

Extends the existing VDDK + virt-v2v VMware→KVM import path so the converted disk is written straight into an RBD pool as a raw image, with in-place virt-v2v finalization (no intermediate qcow2-on-NFS staging copy). Adds host-capability detection for qemu-img RBD support, RBD qemu-copy, virt-v2v in-place, and direct-RBD import — advertised via ReadyCommand and reconciled by the agent manager.

3. importVm adoption of an existing RBD (Ceph) root volume

Extends the KVM unmanaged-import / manage-volume flow so an existing raw RBD image can be adopted directly as a VM ROOT volume. Adds RBD-aware format handling in VolumeOrchestrator (RBD ⇒ RAW instead of the hardcoded QCOW2) and the RBD branch in the libvirt volume-inspection path (LibvirtCheckVolumeCommandWrapper), so CheckVolumeCommand succeeds on RBD instead of failing with "Disk not found or is invalid".

4. Consolidate KVM host-capability probe names across the RBD features

Because features (1) and (2) introduced host-capability keys independently, this commit unifies them under feature-neutral names that describe what is probed, and drops a redundant alias (host.rbd.qemu.copy.support, identical to host.qemu.rbd.support) and its delegate.

5. Linstor (DRBD) primary storage as a migration destination

Extends all three VMware→KVM paths above — staged import, direct VDDK import, and warm CBT migration — so the converted disks can also land on Linstor primary storage, not just Ceph/RBD. A generic RAW_BLOCK_DEVICE target type covers "write RAW into a host-local block device provided by the storage adaptor", so the same code paths serve Linstor (and lay groundwork for other block backends).

  • The KVM agent pre-creates each Linstor resource at source capacity (unlike RBD, qemu-img cannot create a DRBD device), copies with qemu-img convert -n — or nbdcopy when the host has it — into the local /dev/drbd/… device, and finalizes with virt-v2v-in-place fed a <disk type='block'> domain XML (no qemu-nbd bridge needed for a local device).
  • Warm CBT delta cycles stream each changed extent from the nbdkit/VDDK source window straight into the same device window (qemu-img convert -n -S 0), avoiding a temp-file round-trip.
  • Server orchestration adds Linstor to the staged / direct-VDDK / CBT destination pool types and validates that the conversion host is a LINSTOR satellite connected to the pool.
  • LINSTOR resource-name limits (48 chars, no rename) are respected with short deterministic names; imported volumes keep the name as their recorded path.
  • Also drops an unsupported -O flag from the virt-v2v-in-place invocation (fixes in-place finalization on virt-v2v < 2.5, e.g. Ubuntu 24.04) — which benefits the RBD path too.

Types of changes

  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Bug fix
  • Enhancement

Feature/Enhancement Scale

  • Major
  • Minor

How Has This Been Tested?

  • Unit tests for all new and touched wrappers/managers/orchestration:
    LibvirtImportConvertedInstanceCommandWrapperTest, LibvirtConvertInstanceCommandWrapperTest,
    LibvirtCheckConvertInstanceCommandWrapperTest, LibvirtCheckVolumeCommandWrapperTest,
    the LibvirtVmwareCbt* suites, UnmanagedVMsManagerImplTest,
    VolumeImportUnmanageManagerImplTest, the server-side VmwareCbt* suites,
    and VolumeOrchestratorTest — all passing.

  • Manual/lab validation of warm CBT migration and direct-RBD import against a Ceph/RBD KVM cluster.

  • Linstor destination validated byte-identical on a live 3-node LINSTOR/DRBD lab against a real VMware source (cold direct-VDDK and warm CBT; qemu-img compare = images identical), and end-to-end through a live importVm that produced a Stopped CloudStack VM with its root and data disks as Linstor volumes backed by DRBD resources UpToDate on all three nodes. New Linstor unit tests across the destination gate lists, host selection, the convert/import/CBT wrappers, listPhysicalDisks, and the block-device delta/nbdcopy paths — all passing.

Operator-driven warm VMware-to-KVM migration built on VDDK and VMware
Changed Block Tracking (CBT). Keeps a source-equivalent KVM-side replica
current through delta cycles, then finalizes with virt-v2v in-place at
cutover. Supports both filesystem (qcow2) and native Ceph/RBD (raw image)
primary storage targets, with preflight validation, async lifecycle
operations, persisted migration state, and Import/Export UI integration.

Signed-off-by: Andrija Panic <andrija.panic@gmail.com>
Extends the existing VDDK + virt-v2v VMware-to-KVM import path so the
converted disk can be written straight into an RBD pool as a raw image,
with in-place virt-v2v finalization. Adds host-capability detection for
qemu-img RBD support, RBD qemu copy, virt-v2v in-place, and direct RBD
import, advertised via ReadyCommand and reconciled by the agent manager.

Signed-off-by: Andrija Panic <andrija.panic@gmail.com>
Extends the KVM unmanaged import / manage-volume flow so an existing raw
RBD image can be adopted directly as a VM root volume, including the
RBD-format check in the volume orchestrator and the libvirt volume
inspection path.

Signed-off-by: Andrija Panic <andrija.panic@gmail.com>
Unifies the host-capability keys that the CBT warm-migration and VDDK
direct-RBD-import features introduced independently under different
names, using feature-neutral names that describe what is probed:
  host.vmware.cbt.support                     -> host.vddk.blockcopy.support
  host.vmware.cbt.in.place.finalization.support -> host.vddk.blockcopy.inplace.finalization.support
  host.vmware.cbt.rbd.support                 -> host.vddk.blockcopy.rbd.support
  host.qemu.img.rbd.support                   -> host.qemu.rbd.support
  host.virt.v2v.inplace.support / host.virtv2v.in.place.version -> host.virtv2v.inplace.*
Drops the redundant host.rbd.qemu.copy.support alias (identical to
host.qemu.rbd.support) and its hostSupportsRbdQemuCopy() delegate.

Signed-off-by: Andrija Panic <andrija.panic@gmail.com>
@codecov

codecov Bot commented Jul 20, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 37.10194% with 2943 lines in your changes missing coverage. Please review.
✅ Project coverage is 19.75%. Comparing base (fc83241) to head (096aa39).

Files with missing lines Patch % Lines
.../vmware/manager/VmwareCbtMigrationServiceImpl.java 0.28% 354 Missing and 1 partial ⚠️
.../wrapper/LibvirtConvertInstanceCommandWrapper.java 5.36% 279 Missing and 3 partials ⚠️
...c/main/java/com/cloud/vm/VmwareCbtMigrationVO.java 9.45% 201 Missing ⚠️
...i/command/admin/vm/StartVmwareCbtMigrationCmd.java 0.00% 140 Missing ⚠️
...ervisor/kvm/resource/LibvirtComputingResource.java 8.49% 138 Missing and 2 partials ⚠️
...wrapper/LibvirtVmwareCbtCutoverCommandWrapper.java 77.88% 82 Missing and 37 partials ⚠️
...wrapper/LibvirtVmwareCbtPrepareCommandWrapper.java 61.37% 83 Missing and 29 partials ⚠️
...ce/wrapper/LibvirtVmwareCbtSyncCommandWrapper.java 67.06% 82 Missing and 28 partials ⚠️
.../apache/cloudstack/vm/UnmanagedVMsManagerImpl.java 52.86% 92 Missing and 15 partials ⚠️
.../response/VmwareCbtMigrationPreflightResponse.java 0.00% 97 Missing ⚠️
... and 56 more
Additional details and impacted files
@@             Coverage Diff              @@
##               main   #13656      +/-   ##
============================================
+ Coverage     19.64%   19.75%   +0.10%     
- Complexity    19789    20154     +365     
============================================
  Files          6368     6420      +52     
  Lines        575109   581895    +6786     
  Branches      70371    71170     +799     
============================================
+ Hits         112994   114945    +1951     
- Misses       449829   454473    +4644     
- Partials      12286    12477     +191     
Flag Coverage Δ
uitests 3.39% <ø> (-0.02%) ⬇️
unittests 21.03% <37.10%> (+0.11%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@andrijapanicsb

Copy link
Copy Markdown
Contributor Author

@blueorangutan package kvm

@blueorangutan

Copy link
Copy Markdown

@andrijapanicsb a [SL] Jenkins job has been kicked to build packages. It will be bundled with kvm SystemVM template(s). I'll keep you posted as I make progress.

@blueorangutan

Copy link
Copy Markdown

Packaging result [SF]: ✔️ el8 ✔️ el9 ✔️ el10 ✔️ debian ✔️ suse15. SL-JID 18626

@andrijapanicsb andrijapanicsb added this to the 4.24.0 milestone Jul 21, 2026
@andrijapanicsb andrijapanicsb changed the title KVM/VMware: warm CBT migration and direct VDDK import into Ceph/RBD, plus importVm adoption of existing RBD root volumes KVM/VMware: warm/change tracking migration and direct VDDK import into Ceph/RBD, plus importVm adoption of existing RBD root volumes Jul 21, 2026
andrijapanicsb and others added 4 commits July 21, 2026 06:58
…isk import

The "Manage Instances" source dropdown offered "Import QCOW2 image from
Local/Shared Storage", but importVm now adopts an existing ROOT disk from
Filesystem, NetworkFilesystem, SharedMountPoint and RBD (raw) pools, so the
QCOW2-only wording is misleading. Rename both entries and their wizard
title/description to "Import Instance using existing ROOT disk from
Local/Shared Storage", across the en, pt_BR and te locales.

Signed-off-by: Andrija Panic <andrija.panic@gmail.com>
docs/vmware-cbt/architecture.md and docs/vmware-cbt/README.md were added
without the Apache license header, which failed the RAT license check and the
pre-commit insert-license hook. Add the standard ASF header (identical to the
one already present in docs/vmware-cbt-migration.md).

Signed-off-by: Andrija Panic <andrija.panic@gmail.com>
…ey names

State Apache CloudStack 4.24.0.0 as the target release (schema delivered via the
4.24.0.0 upgrade path, with a note that the DDL currently rides in
schema-42210to42300.sql pending the main version bump), and replace the
pre-consolidation host-capability key names with the current
host.vddk.blockcopy.* / host.virtv2v.inplace.version names.

Signed-off-by: Andrija Panic <andrija.panic@gmail.com>
Staged VMware-to-KVM imports (virt-v2v conversion to an NFS temporary
location followed by a copy to the destination pool) can now target
Linstor primary storage:

- Linstor added to the staged-conversion destination pool types, so
  destination pool discovery considers cluster- and zone-wide Linstor
  pools alongside NFS and RBD.
- The import host is validated to have access to each Linstor
  destination pool (it must be a LINSTOR satellite connected to the
  pool); the RBD qemu-copy capability check is preserved and the
  validation renamed to validateStagedImportHostSupport.
- The KVM-side converted-disk move already dispatches by pool type to
  LinstorStorageAdaptor.copyPhysicalDisk, which spawns the resource
  from the pool's resource group and qemu-img converts the qcow2 into
  the DRBD block device; converted-disk metadata for Linstor pools now
  reports the pool UUID and volume name instead of parsing NFS mounts
  (Linstor pools have no local mount path).

The imported volume keeps the copy-time UUID as its path; the Linstor
driver addresses existing volumes by path, so later volume lifecycle
operations (attach, resize, snapshot, delete) work unchanged.
Extends the direct (non-staged) VDDK import path, previously available
only for Ceph/RBD, to Linstor primary storage. With usevddk=true and
forceconverttopool=true on a Linstor conversion pool, the KVM host
copies each VMware disk over nbdkit/VDDK straight into a pre-created
DRBD block device and finalizes it with virt-v2v in place - no NFS
staging and no intermediate qcow2 copy.

Server side:
- Linstor added to the VDDK direct-conversion pool allow-list; the
  RBD/Linstor "requires usevddk" validation is shared.
- Conversion host selection for Linstor requires VDDK support,
  in-place virt-v2v support, and access to the Linstor pool (the host
  must be a LINSTOR satellite); explicit host selections are validated
  for pool access as well.
- CheckConvertInstanceCommand carries a new in-place finalization
  check so unsupported hosts fail fast with a clear message.
- The powered-off, non-cloned source VM requirement now applies to any
  direct block-storage import.

KVM agent side:
- The direct conversion routine handles both targets: for Linstor it
  pre-creates each resource at the source capacity through the storage
  adaptor (qemu-img cannot create DRBD devices, unlike RBD images),
  copies with qemu-img convert -n to the local DRBD device, and feeds
  virt-v2v-in-place a <disk type='block'> domain XML - no qemu URI or
  RBD-style network disks needed.
- Storage access is probed with a 4 MiB test volume and a qemu-io
  write/read round-trip before any data transfer.
- Linstor disk names use a shorter '-dNN' suffix: LINSTOR resource
  names are capped at 48 characters, which the RBD '-disk-NNN' scheme
  would exceed.
- The forced-conversion import short-circuit accepts Linstor temporary
  locations, backed by a new LinstorStorageAdaptor.listPhysicalDisks
  implementation (resource definitions filtered by the pool's resource
  group), replacing the previous UnsupportedOperationException.
Extends the VMware CBT warm-migration framework, previously limited to
qcow2 file and Ceph/RBD targets, with a generic raw-block-device target
type backed by Linstor primary storage. The initial full sync, the
incremental CBT delta cycles and the powered-off cutover all write
straight into the DRBD block device of a pre-created Linstor volume.

Target classification and orchestration:
- New VmwareCbtTargetStorageType.RAW_BLOCK_DEVICE; Linstor pools
  classify as supported block-device targets that require in-place
  virt-v2v finalization (the qcow2 fallback cannot write to devices).
- Linstor added to the CBT-compatible pool types for explicit and
  implicit destination pool selection.
- Conversion host selection requires VMware CBT migration support,
  in-place finalization support and access to the Linstor pool (the
  host must be a LINSTOR satellite); both auto-selection filtering and
  explicit host validation enforce it.
- The destination storage probe now covers block-device pools: the
  agent creates a 4 MiB volume through the storage adaptor, round-trips
  a qemu-io write/read on the local device and deletes it.

Naming: block-device target volumes are named cbt-<mig8>-<diskId>,
where mig8 is the first eight characters of the migration UUID. LINSTOR
resource names are limited to 48 characters (the volume name gets a
cs- prefix as a LINSTOR resource), so the RBD-style names carrying the
full migration UUID do not fit. The short marker keeps cleanup guarded:
both the management server and the agent only delete volumes whose
names start with the marker derived from the migration UUID.

Agent-side data path:
- Initial sync pre-creates each Linstor volume at source capacity via
  the storage adaptor (qemu-img cannot create DRBD devices) and runs
  the nbdkit/VDDK captive copy with qemu-img convert -n to the local
  device path.
- Delta cycles resolve the device path and patch changed extents with
  the existing qemu-io write mechanism; raw device targets are accepted
  by disk validation.
- Cutover feeds virt-v2v-in-place a <disk type='block'> domain XML with
  the local device path - the qemu-nbd localhost bridges required for
  RBD are unnecessary for block devices.
- Cleanup deletes only marker-guarded volumes through the adaptor.

The finalized volumes are imported through the existing shared path;
imported Linstor volumes keep the target name as their volume path,
which the Linstor driver uses to address existing volumes.
Covers the release note for the three import modes (staged, direct
VDDK, CBT warm migration), the operator guide's destination-specific
host requirements and operational notes (satellite/node naming, single
Primary writer, diskful replica preference, DRBD size rounding,
heartbeat fencing interaction, retained volume names), and the
as-built design guide's storage-target section (RAW_BLOCK_DEVICE
mapping, pre-created devices, convert -n, block-disk XML finalization,
48-character resource-name constraint and the short cleanup marker).
Found during live validation on an Ubuntu 24.04 LINSTOR lab node:
virt-v2v-in-place 2.4.0 does not know the -O (write updated output
XML) option, which only exists from virt-v2v 2.5 on (EL9 ships 2.5.x,
where the RBD flows were originally validated). Both the VMware CBT
cutover finalization and the direct VDDK import finalization passed -O
unconditionally, so otherwise fully capable hosts failed the in-place
step at the very end of a migration.

Nothing ever consumed the output XML - both wrappers created a
temporary file for it and deleted it unread - so the option and the
temporary files are removed rather than gated on a version probe.
Verified live: virt-v2v-in-place 2.4.0 with the same libvirt XML
converts a guest on a DRBD block device successfully without -O.
The CBT delta sync copied each changed extent in two hops: qemu-img
convert the nbdkit source window into a temporary raw file on local
disk, then qemu-io write that file into the target at the same offset.
For a raw block-device target (Linstor/DRBD) that doubles the local
I/O and needs scratch space proportional to the changed set.

For RAW_BLOCK_DEVICE targets the changed extent is now streamed from
the nbdkit source window straight into the same target device window in
a single qemu-img convert (raw source image-opts -> raw host_device
target image-opts at matching offset/size) - no temp file, no scratch
directory, no second process per chunk.

-S 0 disables zero/sparse skipping so blocks the source cleared to zero
are actually overwritten in the target, preserving delta correctness
(a full copy can skip zeros because the target starts zeroed; a delta
into an already-populated device cannot). -n keeps the pre-created
device.

The qcow2-file and RBD paths are unchanged - qcow2 cannot be addressed
by a raw offset window, and the RBD URI target keeps the proven qemu-io
path.

Validated on a 3-node LINSTOR lab against a real VMware source: after a
full baseline sync, 144 CBT changed extents applied through the new
direct-copy script produced a device byte-identical to a full read of
the delta snapshot (qemu-img compare: images are identical).
… available

The cold direct-VDDK import and the VMware CBT initial full sync copy an
entire disk from an nbdkit/VDDK source into a target. For a local raw
block-device target (Linstor/DRBD) this is now done with nbdcopy (from
libnbd) when the host has it, falling back to qemu-img convert otherwise.
nbdcopy keeps many requests in flight and is what virt-v2v itself uses
for this step, so it is typically faster than a single-connection
qemu-img convert.

Scope and safety:
- Only the two full-disk copy paths, and only for RAW_BLOCK_DEVICE
  targets whose target is a local device path nbdcopy can write directly.
  The RBD URI target and qcow2 file targets keep qemu-img convert
  (nbdcopy cannot address an rbd: URI without a bridge, and is raw-only).
- The CBT delta cycles are unchanged: they copy a caller-supplied list of
  scattered changed extents at fixed offsets, which is range-addressed
  work nbdcopy does not model - qemu-img/qemu-io remain correct there.
- Availability is probed locally on the agent (nbdcopy --version); it is a
  pure optimization with an automatic fallback, so no server-side gating
  or new host capability is needed.
- Correct on a freshly spawned thin device: nbdcopy skips source holes,
  and the device reads back as zeros, matching qemu-img convert's sparse
  behavior.

Validated on a 3-node LINSTOR lab against a real VMware source: a 16 GiB
disk copied via the new nbdcopy path is byte-identical to a full VDDK read
of the source (qemu-img compare: images are identical), and ran about 20%
faster than qemu-img convert on the same disk over network NBD transport.
@andrijapanicsb
andrijapanicsb requested a review from rp- as a code owner July 21, 2026 07:40
@andrijapanicsb andrijapanicsb changed the title KVM/VMware: warm/change tracking migration and direct VDDK import into Ceph/RBD, plus importVm adoption of existing RBD root volumes KVM/VMware: warm/change-tracking migration and direct VDDK import into Ceph/RBD and Linstor, plus importVm adoption of existing RBD root volumes Jul 21, 2026
@andrijapanicsb
andrijapanicsb requested review from rp- and removed request for rp- July 21, 2026 08:55
@andrijapanicsb

Copy link
Copy Markdown
Contributor Author

@blueorangutan package kvm

@blueorangutan

Copy link
Copy Markdown

@andrijapanicsb a [SL] Jenkins job has been kicked to build packages. It will be bundled with kvm SystemVM template(s). I'll keep you posted as I make progress.

A standalone design/validation document covering the Linstor-destination
portion of this series, written so LINSTOR/DRBD maintainers can review
the storage-side behavior: the RAW_BLOCK_DEVICE target model,
pre-created resources with qemu-img convert -n / nbdcopy, in-place
finalization on the local DRBD device, resource-naming constraints,
delta-copy semantics, host prerequisites, the support matrix, and the
lab/end-to-end validation performed.
@blueorangutan

Copy link
Copy Markdown

Packaging result [SF]: ✔️ el8 ✔️ el9 ✔️ el10 ✔️ debian ✔️ suse15. SL-JID 18633

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants