Skip to content

Add support for clvm-ng incremental volume snaps - #14484

Draft
Pearl1594 wants to merge 3 commits into
mainfrom
clvm-ng-bitmap-incsnaps
Draft

Pearl1594 wants to merge 3 commits into
mainfrom
clvm-ng-bitmap-incsnaps

Conversation

@Pearl1594

Copy link
Copy Markdown
Contributor

Description

This PR adds support for incremental volume snapshots for CLVM-NG

Types of changes

  • Breaking change (fix or feature that would cause existing functionality to change)
  • New feature (non-breaking change which adds functionality)
  • Bug fix (non-breaking change which fixes an issue)
  • Enhancement (improves an existing feature and functionality)
  • Cleanup (Code refactoring and cleanup, that may add test cases)
  • Build/CI
  • Test (unit or integration test code)

Feature/Enhancement Scale or Bug Severity

Feature/Enhancement Scale

  • Major
  • Minor

Screenshots (if appropriate):

How Has This Been Tested?

  • 5-deep ROOT incremental chain with data written between snapshots - File-level: write and change files in the guest across three snapshots; revert to the first incremental and to a mid-chain incremental, files as expected
  • Incremental snapshot on a stopped VM (ROOT and data volumes)
  • Incremental snapshot on a running VM with the guest writing
  • Restore snapshots to new volumes, byte-identical to the source
  • Create volume from snapshot; template from snapshot, VM boots
  • Delete a middle snapshot: goes Hidden, chain intact
  • Delete the last child
  • Live VM migration, then incremental snapshot
  • Volume migration across VGs, then incremental snapshot and restore
  • Revert a data volume, then snapshot (new full after revert)
  • ROOT revert after VM migration
  • snapshot.delta.max chain rollover
  • Live resize, then snapshot and restore
  • Hourly snapshot policy
  • Snapshot right after boot with the guest writing, then a stopped incremental identical to the LV
  • Forced stop VM, restart on another host, then incremental snaps

How did you try to break this feature and the system with this change?

@codecov

codecov Bot commented Oct 22, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 3.86%. Comparing base (d6390bd) to head (dde3d73).
⚠️ Report is 34 commits behind head on main.

❗ There is a different number of reports uploaded between BASE (d6390bd) and HEAD (dde3d73). Click for more details.

HEAD has 1 upload less than BASE
Flag BASE (d6390bd) HEAD (dde3d73)
unittests 1 0
Additional details and impacted files
@@              Coverage Diff              @@
##               main   #14484       +/-   ##
=============================================
- Coverage     20.76%    3.86%   -16.91%     
=============================================
  Files          6496      494     -6002     
  Lines        591612    42875   -548737     
  Branches      72585     8136    -64449     
=============================================
- Hits         122851     1657   -121194     
+ Misses       455424    40986   -414438     
+ Partials      13337      232    -13105     
Flag Coverage Δ
uitests 3.86% <ø> (-0.02%) ⬇️
unittests ?

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.

@Pearl1594

Copy link
Copy Markdown
Contributor Author

@blueorangutan package

@blueorangutan

Copy link
Copy Markdown

@Pearl1594 a [SL] Jenkins job has been kicked to build packages. It will be bundled with no SystemVM templates. I'll keep you posted as I make progress.

@blueorangutan

Copy link
Copy Markdown

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

@Pearl1594
Pearl1594 marked this pull request as ready for review October 23, 2026 13:38
Copilot AI balanced review requested due to automatic review settings October 23, 2026 13:38

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warning

Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.

Copilot review overview

3 open issues
What changed in this PR

Adds incremental volume snapshot support for CLVM-NG by treating it similarly to KVM/file-based incremental flows and enforcing secondary-storage requirements in the KVM storage processor.

Changes:

  • Enable incremental snapshot payload/flow for StoragePoolType.CLVM_NG when kvmIncrementalSnapshot is enabled.
  • Adjust snapshot-to-secondary handling to avoid double-backup for CLVM-NG incremental snapshots.
  • Add CLVM-NG incremental validation in KVM snapshot creation (requires secondary storage; supports live/paused cases).
File Description
server/​src/​main/​java/​com/​cloud/​storage/​snapshot/​SnapshotManagerImpl.java Enables CLVM-NG to participate in the incremental snapshot/secondary-store allocation flow and adjusts backup behavior accordingly.
plugins/​hypervisors/​kvm/​src/​main/​java/​com/​cloud/​hypervisor/​kvm/​storage/​KVMStorageProcessor.java Adds CLVM-NG incremental snapshot checks and routes live/stopped snapshot handling through incremental paths using secondary storage.

Review effort: Lite

Comment on lines +2120 to 2123
if (isClvmNgIncremental) {
logger.info("Taking incremental snapshot of CLVM_NG volume [{}] using QCOW2 backup to secondary storage.", volume.getName());
newSnapshot = takeIncrementalVolumeSnapshotOfStoppedVm(snapshotTO, primaryPool, secondaryPool,
imageStoreTo.getUrl(), snapshotName, volume, conn, cmd.getWait());

StoragePoolType poolType = volume.getStoragePoolType();
boolean isClvmNgIncrementalCandidate = StoragePoolType.CLVM_NG == poolType && kvmIncrementalSnapshot.valueIn(clusterId);

}
boolean isLiveClvmNgIncremental = isClvmNgIncremental && (DomainInfo.DomainState.VIR_DOMAIN_RUNNING.equals(state) || DomainInfo.DomainState.VIR_DOMAIN_PAUSED.equals(state));
if ((DomainInfo.DomainState.VIR_DOMAIN_RUNNING.equals(state) && !primaryPool.isExternalSnapshot()) || isLiveClvmNgIncremental) {
if (snapshotTO.isKvmIncrementalSnapshot()) {

@JoaoJandre JoaoJandre left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CLGTM, did not test it

@github-actions

Copy link
Copy Markdown

This pull request has merge conflicts. Dear author, please fix the conflicts and sync your branch with the base branch.

@nvazquez

nvazquez commented Nov 1, 2026

Copy link
Copy Markdown
Contributor

Hi @Pearl1594 can you please address the conflicts?

Copilot AI balanced review requested due to automatic review settings November 1, 2026 21:34

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


if (archiveSnapshotToSecondary) {
if (!isKvmAndFileBasedStorage) {
if (!isKvmAndFileBasedStorage && !isClvmNgIncrementalCandidate) {
Comment on lines +1685 to 1686
if (!isKvmAndFileBasedStorage && !isClvmNgIncrementalCandidate) {
backupSnapshotToSecondary(payload.getAsyncBackup(), snapshotStrategy, snapshotOnPrimary, payload.getZoneIds(), payload.getStoragePoolIds());
}

if (isKvmAndFileBasedStorage && kvmIncrementalSnapshot.valueIn(clusterId)) {
if ((isKvmAndFileBasedStorage || StoragePoolType.CLVM_NG == poolType) && kvmIncrementalSnapshot.valueIn(clusterId)) {
@Pearl1594

Copy link
Copy Markdown
Contributor Author

@blueorangutan package

@blueorangutan

Copy link
Copy Markdown

@Pearl1594 a [SL] Jenkins job has been kicked to build packages. It will be bundled with no SystemVM templates. I'll keep you posted as I make progress.

@github-actions

github-actions Bot commented Nov 1, 2026

Copy link
Copy Markdown

🔴 Test Coverage Grade: D — Marginal

Metric Value
Line coverage 25.86%
Branch coverage 20.19%

Grade Scale

Grade Line Coverage Meaning
🟢 A ≥ 80% Excellent - this code sleeps well at night 😴
🟡 B 60-79% Good - almost there, don't stop now 😉
🟠 C 40-59% Acceptable - your code is wearing a seatbelt, but no airbags 😬
🔴 D 20-39% Marginal - boldly shipping where no test has gone before 🖖
⛔ F < 20% Failing - tests? what tests? 🔥

Branch coverage is shown as a secondary signal. Grade is determined by line coverage.
View full Actions run

@blueorangutan

Copy link
Copy Markdown

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

@RosiKyu

RosiKyu commented Nov 11, 2026

Copy link
Copy Markdown
Collaborator

@blueorangutan package

@RosiKyu RosiKyu self-assigned this Nov 11, 2026
@blueorangutan

Copy link
Copy Markdown

@RosiKyu a [SL] Jenkins job has been kicked to build packages. It will be bundled with no SystemVM templates. I'll keep you posted as I make progress.

@blueorangutan

Copy link
Copy Markdown

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

@RosiKyu RosiKyu left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

QA test results: CLVM_NG incremental snapshots

Build: 24.0.0-shapeblue22703 (PR head 2f1249b), upgraded from 4.23.0.0-shapeblue0. Management server and 4 KVM hosts on Oracle Linux 8. Pools: two CLVM_NG pools and one CLVM pool (lvmlockd + sanlock shared VGs on iSCSI LUNs), one NFS primary pool, NFS secondary storage. Guest: CentOS 5.5.

Result: 14 of 15 tests passed. 1 test failed (minor, error message only, details below).

How each test was checked

Before every snapshot, write a marker file in the guest, e.g. echo "M3 written $(date -u) before snapshot 3" > /root/m3; sync. On data disks, also write a 20 MB random file and note its md5sum. To check a snapshot, create a volume from it, attach it to a second VM, and look for the markers on the raw disk: tr -c "[:print:]" "\n" < /dev/vdX | grep -oE "M[0-9] written .* before snapshot [0-9]". For data disks, mount the disk read only and compare the md5 sums. A restore of snapshot N must contain markers 1 to N and nothing newer. To see whether a snapshot is full or incremental, check snapshot_store_ref: parent_snapshot_id = 0 means full, otherwise it is incremental on that parent (incrementals are also much smaller in physical_size).

Global settings for all tests unless the test says otherwise: kvm.incremental.snapshot=true, snapshot.delta.max=16, snapshot.backup.to.secondary=true.

# Test Steps Expected Result
UPG-01 Upgrade with existing snapshots On 4.23, take 2 ROOT snapshots and 1 DATA snapshot of a running VM on CLVM_NG. Upgrade the management server and agents to the PR build. Set kvm.incremental.snapshot=true and take 3 more ROOT snapshots. Restore all 5. Delete the oldest 4.23 snapshot. The first snapshot after the upgrade is a new full, the next 2 are incrementals. All 5 restores are correct. Deleting the old snapshot does not affect the new chain. PASS
INC-01 Running VM, guest writing Running VM on CLVM_NG: take 4 ROOT snapshots (marker before each), then a 5th while a loop in the guest keeps writing files. Restore all 5. 1 full, then incrementals. Each restore has the markers up to its own snapshot. PASS
INC-02 Stopped VM, ROOT and DATA 3 rounds of: start the VM, write markers on ROOT and DATA, stop the VM, snapshot ROOT and DATA. Restore all 6. Chains correct. Markers and DATA md5 sums match in every restore. PASS
INC-03 Revert to the middle of a chain Stop the VM, revert ROOT to the 2nd of 3 snapshots, start the VM, check files. Write a marker, snapshot, write a marker, snapshot. Restore both. The guest has the data of snapshot 2 only. The next snapshot is a new full, the one after is incremental. Restores have no data from the reverted snapshot 3. PASS
INC-04 Volume and template from an incremental VM stopped: create a volume from the latest incremental DATA snapshot, attach it and the original DATA disk to another VM, compare with md5sum /dev/vdX /dev/vdY. Create a template from an incremental ROOT snapshot and deploy a VM from it. The disks are byte-identical. The VM from the template boots and has the data up to that snapshot. PASS
INC-05 Delete a snapshot in the middle Delete the 3rd snapshot of a 5-snapshot chain. Restore the 4th and the 5th. The 3rd snapshot disappears from listSnapshots, and in the DB it shows as Destroyed / Hidden. The 4th and 5th restore with all markers, including the one from the deleted 3rd. PASS
INC-06 Live migration Take 2 snapshots, live migrate the VM to another host, take 2 more. Restore the last 2. The chain continues after the migration. Restores are correct. PASS
INC-07 Disk moved to the other CLVM_NG pool Running VM: migrateVirtualMachineWithVolume to another host, with the DATA disk moved to the other CLVM_NG pool. Take a snapshot before the move and 2 after. Restore all 3. The VM stays up and its files are intact. The first snapshot after the move is a new full, then incremental. All md5 sums match. PASS
INC-08 snapshot.delta.max rollover Set snapshot.delta.max=3, take 5 snapshots, restore the 3rd and the 5th. Full, 2 incrementals, then a new full. Restores are correct. PASS
INC-09 Paused VM virsh suspend the VM on its host, take a ROOT snapshot, check virsh domstate, virsh resume. Restore the snapshot. The snapshot succeeds as an incremental, and the VM stays paused until it is resumed. The restore has the full history. PASS
INC-10 Backup to secondary off Set snapshot.backup.to.secondary=false, restart the management server, take a ROOT snapshot of a VM on CLVM_NG. A clear error that names the cause, or a clear fallback. No broken snapshot. FAILED (see below)
INC-11 Recurring hourly policy Create an HOURLY snapshot policy on a DATA disk. A cron job in the guest writes a marker 2 minutes before each run. Wait 2 hours, then restore the 2nd snapshot. The 2nd hourly snapshot is incremental on the 1st. The restore has both hourly markers. PASS
INC-12 Regression: plain CLVM pool VM on a CLVM (not CLVM_NG) pool, incremental on: take 3 snapshots, restore 2. All 3 are full snapshots with no checkpoint, as before the PR. Restores are correct. PASS
INC-13 Regression: incremental off on CLVM_NG Set kvm.incremental.snapshot=false, take 3 snapshots. Set it back to true and take 2 more. Restore one from each part. When off: all full, no checkpoint. When back on: a new full, then incremental. Restores are correct. PASS
INC-14 Regression: NFS primary VM on NFS primary, snapshot.delta.max=3: take 4 snapshots, restore the 3rd. Same chain behaviour as before (full, 2 incrementals, new full). Restore is correct. PASS

Failed: INC-10, a CLVM_NG snapshot with backup to secondary off fails with only "internal error"

Severity: Minor. The snapshot is correctly refused and nothing broken is left behind. The snapshot row goes to Error, and no new LV or libvirt checkpoint is created. But the user cannot tell why it failed.

Steps to reproduce

  1. Set kvm.incremental.snapshot=true and snapshot.backup.to.secondary=false, then restart the management server (this setting is not dynamic).
  2. Take a snapshot of the ROOT disk of a running VM on a CLVM_NG pool: createSnapshot volumeid={volume}.
  3. For comparison, do the same for a VM on an NFS primary pool.

Expected result: an error that tells the user what to do (for example: turn snapshot.backup.to.secondary back on, or turn off kvm.incremental.snapshot).

Actual result: the API and UI only show "Failed to create Snapshot due to an internal error". The real reason is only in the management server log, at DEBUG level. That message also says "Please configure secondary storage", but secondary storage is already configured; the actual cause is the snapshot.backup.to.secondary setting. On NFS, the same settings work and the snapshot stays on primary storage.

API response, step 2

inc10-primary-s3: jobstatus 2 {"errorcode": 530, "errortext": "Failed to create Snapshot due to an internal error creating Snapshot for volume cb3ddeb9-9d4b-488f-a129-7613b5ad19ff"}

Management server log, step 2

2026-11-12 15:47:35,402 DEBUG [o.a.c.s.s.DefaultSnapshotStrategy] (Work-Job-Executor-1:[ctx-e248fc59, job-386/job-387, ctx-a8e32c65]) (logid:798c2822) Failed to take snapshot: Incremental snapshots for CLVM_NG require secondary storage. Please configure secondary storage or disable incremental snapshots for volume [ROOT-9].
2026-11-12 15:47:35,631 ERROR [o.a.c.a.c.u.s.CreateSnapshotCmd] (API-Job-Executor-1:[ctx-55582e63, job-386, ctx-51d0aec5]) (logid:798c2822) Failed to create Snapshot due to an internal error creating Snapshot for volume cb3ddeb9-9d4b-488f-a129-7613b5ad19ff java.lang.RuntimeException: Unexpected exception
2026-11-12 15:47:35,635 DEBUG [o.a.c.f.j.i.AsyncJobManagerImpl] (API-Job-Executor-1:[ctx-55582e63, job-386]) (logid:798c2822) Complete async job-386, jobStatus: FAILED, resultCode: 530, result: org.apache.cloudstack.api.response.ExceptionResponse/null/{"uuidList":[],"errorcode":"530","errortext":"Failed to create Snapshot due to an internal error creating Snapshot for volume cb3ddeb9-9d4b-488f-a129-7613b5ad19ff"}

Step 3: NFS volume with the same settings, the snapshot succeeds and stays on primary

| inc10-nfs-primary-only | BackedUp | Primary    | Ready |         0 | 47 |    1874853888 | /mnt/309c71fd-166b-38dd-88b0-bb02aac6a416/snapshots/b8f49ba6-aeab-432c-9c59-7952b129c9ac |

Notes (existing behaviour, not caused by the PR)

  • snapshot.delta.max counts the full snapshot as part of the chain, so 3 gives a full plus 2 incrementals. NFS behaves the same way, and the code is unchanged from main.
  • Moving a VM together with its storage ends the snapshot chain for all of its disks, so the next snapshot of each disk is a full. Same code as in main.
  • migrateVolume with livemigrate=true is refused on KVM by design; migrateVirtualMachineWithVolume has to be used instead.

@RosiKyu RosiKyu left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving. My earlier "Request changes" was a mistake; my full test results are in the review above.

@Pearl1594

Copy link
Copy Markdown
Contributor Author

@blueorangutan test

@blueorangutan

Copy link
Copy Markdown

@Pearl1594 a [SL] Trillian-Jenkins test job (ol8 mgmt + kvm-ol8) has been kicked to run smoke tests

@Pearl1594
Pearl1594 marked this pull request as draft November 12, 2026 20:50
@Pearl1594

Copy link
Copy Markdown
Contributor Author

@RosiKyu I found an issue with bitmap removal for a stopped VM and when the disks have moved:
Bitmap removal picks its host from the VM (its host, or its last host if stopped) and does not ask the CLVM manager which host holds the volume's lock. When the lock is on a different host, qemu-img bitmap --remove runs where the LV is inactive and the snapshot delete fails. Ive added a fix for it. SOrry abt that.

@Pearl1594

Copy link
Copy Markdown
Contributor Author

@blueorangutan package

@blueorangutan

Copy link
Copy Markdown

@Pearl1594 a [SL] Jenkins job has been kicked to build packages. It will be bundled with no SystemVM templates. I'll keep you posted as I make progress.

@sonarqubecloud

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
12.6% Coverage on New Code (required ≥ 40%)

See analysis details on SonarQube Cloud

@blueorangutan

Copy link
Copy Markdown

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

@blueorangutan

Copy link
Copy Markdown

[SF] Trillian test result (tid-17568)
Environment: kvm-ol8 (x2), zone: Advanced Networking with Mgmt server ol8
Total time taken: 68172 seconds
Marvin logs: https://github.com/blueorangutan/acs-prs/releases/download/trillian/pr14484-t17568-kvm-ol8.zip
Smoke tests completed. 156 look OK, 1 have errors, 0 did not run
Only failed and skipped tests results shown below:

Test Result Time (s) Test File
test_uservm_host_control_state Failure 17.36 test_host_control_state.py

This branch has not been deployed

No deployments
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.

6 participants