Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
72 commits
Select commit Hold shift + click to select a range
26e7c3f
kvm: let libvirt remove RBD snapshots on volume delete (#13763)
wido Sep 8, 2026
b4e123d
kvm: fix RBD exclusive-lock leak that breaks revertSnapshot on Ceph (…
calvix Sep 8, 2026
a397e42
improve test to read and set nas.backup.incremental.enabled for the t…
jmsperu Sep 8, 2026
a56e1ed
kvm: fix restore-and-attach of a backed up volume (#14007)
abh1sar Sep 9, 2026
85554b8
fix: use auth_client_required instead of removed auth_supported for R…
waterWang Sep 10, 2026
72b39c5
kvm: apply rbd_default_data_pool when creating volumes from templates…
bhouse-nexthop Sep 14, 2026
99daffd
Nas backup: Fix mount/unmount error handling and timeout in LibvirtRe…
abh1sar Sep 15, 2026
f8b75f1
backup: remove the powered-off precondition for all backup providers …
weizhouapache Sep 15, 2026
6a908e1
storage: KVM - enable RBD/Ceph volume encryption support (#13556)
calvix Sep 16, 2026
3d22d79
Fix importVM for use with dummy template (#14195)
abh1sar Sep 19, 2026
66e69b5
importVM improvements: RBD support, volume format, and cluster select…
abh1sar Sep 19, 2026
08a706d
kvm: look RBD volumes up through librbd instead of refreshing the poo…
bhouse-nexthop Oct 9, 2026
6fd8dda
smoke test: support ceph storage pools
weizhouapache Oct 29, 2025
e913d82
test: fix test failures
weizhouapache Oct 30, 2025
5bc85a1
test: add volume migration which has been fixed
weizhouapache Dec 9, 2025
dc919fb
test: support Ceph/RBD primary storage in NAS backup smoke test
weizhouapache Sep 8, 2026
6879406
test: fix smoke test failures on xen/vmware
weizhouapache Jul 16, 2026
685225f
test: sleep 10 seconds before attaching volume in test_events_resourc…
weizhouapache Jul 17, 2026
c31f0a6
test: add more time.sleep(10)
weizhouapache Jul 27, 2026
b00be0e
test: add more time.sleep(10) in test_vm_strict_host_tags.py
weizhouapache Jul 28, 2026
1664f24
test: fix test_usage.py
weizhouapache Jul 28, 2026
ddfcfd5
test: move time.sleep(10) in test_host_maintenance.py
weizhouapache Jul 30, 2026
37fd033
test: retry domain deletion in tearDown of test_deploy_vms_in_paralle…
weizhouapache Aug 27, 2026
3a7b273
test: fix tests failures caused by others in drs and host maintenance
weizhouapache Aug 28, 2026
8890c12
test: fix test_10_list_volumes for zone-wide storage pools
weizhouapache Oct 5, 2026
689fb2c
test_host_tags: fix cleanup to handle None host tags
weizhouapache Oct 5, 2026
854a93f
package: Add missing openssl and decompression utilities to KVM agent…
weizhouapache Oct 5, 2026
e64a8d5
kvm: support Host HA on Ceph RBD primary storage
weizhouapache Oct 5, 2026
cfdd7fc
test: support zone-wide storage pools in test_list_storage_pools
weizhouapache Oct 6, 2026
27d316e
KVM: allow cluster-wide storage pools for VM deployment from volumes
weizhouapache Oct 6, 2026
270d76a
KVM: live volume migration on ceph
weizhouapache Oct 7, 2026
3bb8cc0
smoke tests: exercise online volume migration for KVM on RBD
weizhouapache Oct 7, 2026
3a9e609
kvm: address review comments on Host HA for Ceph RBD
weizhouapache Oct 8, 2026
2296fb0
kvm: validate input of the RBD Host HA scripts and the Ceph settings …
weizhouapache Oct 8, 2026
be60d18
kvm: keep the Ceph key out of the logs and process list of the RBD Ho…
weizhouapache Oct 8, 2026
6f92c9e
smoke test: cover RBD/librbd volume encryption in TestVolumeEncryption
weizhouapache Oct 8, 2026
f08f4eb
test: run unmanage/import VM lifecycle test on KVM too
weizhouapache Oct 9, 2026
88b92d6
test: merge the direct download deploy tests into one test
weizhouapache Oct 9, 2026
0771561
kvmvmactivity_rbd.sh: do not depend on python3, and tell failures fro…
weizhouapache Oct 9, 2026
3f60396
kvmvmactivity_rbd.sh: do not decide on an activity state which could …
weizhouapache Oct 9, 2026
ced2070
KVM: support incremental NAS backups of RBD volumes
weizhouapache Oct 12, 2026
0c1c243
test: run the NAS backup smoke test on a cloud-init template
weizhouapache Oct 12, 2026
d970fcb
test: validate a whole backup chain, including across a mid-chain delete
weizhouapache Oct 12, 2026
700d590
test: verify the guest of a VM created from a backup, not just that i…
weizhouapache Oct 12, 2026
f3b0305
test: sync the data to disk before taking the VM snapshot
weizhouapache Oct 9, 2026
937542d
kvm: restore ErrorCode import removed by PR 13763
weizhouapache Oct 12, 2026
d964898
kvm: rewrite the RBD Host HA scripts in Python, with a faster VM-acti…
weizhouapache Oct 13, 2026
d08fe69
kvm: choose the script which checks the VM activity by the type of th…
weizhouapache Oct 13, 2026
2a37171
kvm: use only the Python bindings of librados and librbd in the Host …
weizhouapache Oct 13, 2026
52cfddb
kvm: remove the partial RBD image when a template copy fails
weizhouapache Oct 13, 2026
1635bf0
kvm: test the NAS take-backup arguments with StringUtils and Collecti…
weizhouapache Oct 14, 2026
a950513
nasbackup: carry the backup method separately from the mode
weizhouapache Oct 14, 2026
c521e4c
utils: add RbdUtils for the Ceph monitors of an RBD pool
weizhouapache Oct 14, 2026
3472c0c
kvm: build every Ceph monitor list with RbdUtils
weizhouapache Oct 14, 2026
a4fc494
kvm: build the storage pool's Ceph monitors with RbdUtils
weizhouapache Oct 14, 2026
91ee2e4
kvm: tell a missing object apart from an unreachable cluster in the R…
weizhouapache Oct 14, 2026
2dab8b2
Add ceph-common as dependencies
weizhouapache Oct 14, 2026
a48a4bc
kvm: build the Ceph monitors of the restore backup command with RbdUtils
weizhouapache Oct 14, 2026
320a603
backup: name the rbd method in the TakeBackupCommand comment
weizhouapache Oct 14, 2026
f9b1fb9
kvm: refuse an RBD restore that would destroy a snapshot we did not take
weizhouapache Oct 14, 2026
ae74fa2
kvm: drop the cephx key file from a trap in nasbackup.sh
weizhouapache Oct 14, 2026
c101689
utils: test the escaped colons of an IPv6 Ceph monitor without a port
weizhouapache Oct 15, 2026
bb78bf7
docs: map the commits of the Ceph work to their pull requests
weizhouapache Oct 15, 2026
ae81a0a
KVM: live migrate volumes between RBD, NFS, SharedMountPoint and loca…
weizhouapache Oct 15, 2026
baf10e5
kvm: name the RBD heartbeat objects after the GUID of the host
weizhouapache Oct 16, 2026
b47e6d7
kvm: name the host id of the heartbeat checks heartBeatHostId
weizhouapache Oct 16, 2026
9c858d3
KVM: copy a volume of a SharedMountPoint pool directly between primar…
weizhouapache Oct 19, 2026
27968b3
KVM: do not fail a volume migration whose block copy was pivoted
weizhouapache Oct 19, 2026
5e9be00
KVM: leave the format detection of libvirt volumes and the pool refre…
weizhouapache Oct 19, 2026
6cf6e9d
KVM: do not name an RBD volume copied between primary storages after …
weizhouapache Oct 19, 2026
1541e17
KVM: limit live volume migration to RBD pools and RBD to file based p…
weizhouapache Oct 19, 2026
4a9ff62
commit-pr-map: add the live volume migration commits and PR #14426
weizhouapache Oct 20, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions PendingReleaseNotes
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,12 @@ example.ver.1 > example.ver.2:
which can now be attached to Instances. This is to prevent the Secondary
Storage to grow to enormous sizes as Linux Distributions keep growing in
size while a stripped down Linux should fit on a 2.88MB floppy.

4.23.0.0 > 24.0.0:
* KVM/Ceph: RBD volumes can now be encrypted at rest using native librbd
LUKS2 (<encryption format='luks2' engine='librbd'>), for both data disks
and root disks. Encryption is transparent to the guest and reuses the
existing CloudStack volume-encryption passphrase handling, so no
additional key store is required. Note: attaching an encrypted RBD volume
to a running Instance requires libvirt >= 10.1.0; booting an Instance from
an encrypted RBD root disk works on older libvirt.
1 change: 1 addition & 0 deletions api/src/main/java/com/cloud/host/Host.java
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ public static String[] toStrings(Host.Type... types) {

String HOST_UEFI_ENABLE = "host.uefi.enable";
String HOST_VOLUME_ENCRYPTION = "host.volume.encryption";
String HOST_RBD_VOLUME_ENCRYPTION = "host.volume.encryption.rbd";
String HOST_INSTANCE_CONVERSION = "host.instance.conversion";
String HOST_VDDK_SUPPORT = "host.vddk.support";
String HOST_VDDK_LIB_DIR = "vddk.lib.dir";
Expand Down
44 changes: 43 additions & 1 deletion api/src/main/java/com/cloud/storage/Storage.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

public class Storage {
public static enum ImageFormat {
Expand Down Expand Up @@ -172,7 +173,7 @@ public static enum StoragePoolType {
LVM(false, false, EncryptionSupport.Unsupported), // XenServer local LVM SR
CLVM(true, false, EncryptionSupport.Unsupported),
CLVM_NG(true, false, EncryptionSupport.Hypervisor),
RBD(true, true, EncryptionSupport.Unsupported), // http://libvirt.org/storage.html#StorageBackendRBD
RBD(true, true, EncryptionSupport.Hypervisor), // http://libvirt.org/storage.html#StorageBackendRBD ; encrypted natively by librbd (LUKS2, engine='librbd')
SharedMountPoint(true, true, EncryptionSupport.Hypervisor),
VMFS(true, true, EncryptionSupport.Unsupported), // VMware VMFS storage
PreSetup(true, true, EncryptionSupport.Unsupported), // for XenServer, Storage Pool is set up by customers.
Expand Down Expand Up @@ -225,6 +226,47 @@ public static List<StoragePoolType> getNonSharedStoragePoolTypes() {

}

// pool types whose volumes are files, on KVM
public static final Set<StoragePoolType> KVM_FILE_BASED_POOL_TYPES = Set.of(StoragePoolType.NetworkFilesystem, StoragePoolType.SharedMountPoint,
StoragePoolType.Filesystem);

/**
* True if a volume is migrated between two pools of the same type.
*/
public static boolean arePoolsSameType(StoragePoolType srcType, StoragePoolType destType) {
return srcType != null && srcType.equals(destType);
}

/**
* True if at least one of the pools is file based (NFS, SharedMountPoint or local) on KVM, whether it is the
* source or the destination, and whether the other one is file based as well.
*/
public static boolean isAnyPoolFileBased(StoragePoolType srcType, StoragePoolType destType) {
return KVM_FILE_BASED_POOL_TYPES.contains(srcType) || KVM_FILE_BASED_POOL_TYPES.contains(destType);
}

/**
* True if KVM copies a volume in place with a libvirt block copy: between two RBD pools, or between an RBD pool and
* a file based pool (NFS, SharedMountPoint or local). Both pools are then reachable from the host the Instance runs
* on, so the volume of a running Instance can be copied without moving the Instance.
*/
public static boolean isCopiedInPlaceOnKvm(StoragePoolType srcType, StoragePoolType destType) {
if (!StoragePoolType.RBD.equals(srcType) && !StoragePoolType.RBD.equals(destType)) {
return false;
}
return arePoolsSameType(srcType, destType) || isAnyPoolFileBased(srcType, destType);
}

/**
* True if a volume is migrated between two file based pools (NFS, SharedMountPoint or local) on KVM. Such a volume
* is copied with qemu-img, which reads and writes an encrypted volume with its passphrase, so it is not limited to
* the pools a libvirt block copy can reach. It is the way a volume is moved when it is not in use by a running
* Instance.
*/
public static boolean areBothPoolsFileBased(StoragePoolType srcType, StoragePoolType destType) {
return KVM_FILE_BASED_POOL_TYPES.contains(srcType) && KVM_FILE_BASED_POOL_TYPES.contains(destType);
}

public static enum StorageResourceType {
STORAGE_POOL, STORAGE_HOST, SECONDARY_STORAGE, LOCAL_SECONDARY_STORAGE
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ public class ImportVmCmd extends ImportUnmanagedInstanceCmd {

@Parameter(name = ApiConstants.DISK_PATH,
type = CommandType.STRING,
description = "path of the disk image")
description = "path of the disk image. It is the file name on file based storage pools (NFS, Local, SharedMountPoint), and the image name on RBD storage pools")
private String diskPath;

@Parameter(name = ApiConstants.IMPORT_SOURCE,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ public class ImportVolumeCmd extends BaseAsyncCmd {
@Parameter(name = ApiConstants.PATH,
type = BaseCmd.CommandType.STRING,
required = true,
description = "the path of the volume")
description = "the path of the volume. It is the file name on file based storage pools (NFS, Local, SharedMountPoint), and the image name on RBD storage pools")
private String path;

@Parameter(name = ApiConstants.NAME,
Expand Down
78 changes: 78 additions & 0 deletions api/src/test/java/com/cloud/storage/StorageTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -83,4 +83,82 @@ public void equalityTest() {
Assert.assertTrue(t1.equals(StoragePoolType.NetworkFilesystem));
Assert.assertFalse(t1.equals(StoragePoolType.EXT));
}

@Test
public void arePoolsSameTypeForTwoPoolsOfThatType() {
for (StoragePoolType poolType : StoragePoolType.values()) {
Assert.assertTrue(poolType + " -> " + poolType, Storage.arePoolsSameType(poolType, poolType));
for (StoragePoolType other : StoragePoolType.values()) {
if (other.equals(poolType)) {
continue;
}
Assert.assertFalse(poolType + " -> " + other, Storage.arePoolsSameType(poolType, other));
}
}
}

@Test
public void isAnyPoolFileBasedForOneFileBasedPool() {
for (StoragePoolType fileBased : Storage.KVM_FILE_BASED_POOL_TYPES) {
Assert.assertTrue("RBD -> " + fileBased, Storage.isAnyPoolFileBased(StoragePoolType.RBD, fileBased));
Assert.assertTrue(fileBased + " -> RBD", Storage.isAnyPoolFileBased(fileBased, StoragePoolType.RBD));
for (StoragePoolType otherFileBased : Storage.KVM_FILE_BASED_POOL_TYPES) {
Assert.assertTrue(fileBased + " -> " + otherFileBased, Storage.isAnyPoolFileBased(fileBased, otherFileBased));
}
}
Assert.assertFalse("RBD -> RBD", Storage.isAnyPoolFileBased(StoragePoolType.RBD, StoragePoolType.RBD));
}

@Test
public void isCopiedInPlaceOnKvmForRbdAndForRbdAndFileBasedPools() {
Assert.assertTrue("RBD -> RBD", Storage.isCopiedInPlaceOnKvm(StoragePoolType.RBD, StoragePoolType.RBD));
for (StoragePoolType fileBased : Storage.KVM_FILE_BASED_POOL_TYPES) {
Assert.assertTrue("RBD -> " + fileBased, Storage.isCopiedInPlaceOnKvm(StoragePoolType.RBD, fileBased));
Assert.assertTrue(fileBased + " -> RBD", Storage.isCopiedInPlaceOnKvm(fileBased, StoragePoolType.RBD));
}
}

@Test
public void isCopiedInPlaceOnKvmNotBetweenTwoFileBasedPools() {
for (StoragePoolType srcType : Storage.KVM_FILE_BASED_POOL_TYPES) {
for (StoragePoolType destType : Storage.KVM_FILE_BASED_POOL_TYPES) {
Assert.assertFalse(srcType + " -> " + destType, Storage.isCopiedInPlaceOnKvm(srcType, destType));
}
}
}

@Test
public void isCopiedInPlaceOnKvmNotWithOtherPoolTypes() {
for (StoragePoolType other : StoragePoolType.values()) {
if (StoragePoolType.RBD.equals(other) || Storage.KVM_FILE_BASED_POOL_TYPES.contains(other)) {
continue;
}
Assert.assertFalse(other + " -> " + other, Storage.isCopiedInPlaceOnKvm(other, other));
Assert.assertFalse("RBD -> " + other, Storage.isCopiedInPlaceOnKvm(StoragePoolType.RBD, other));
Assert.assertFalse(other + " -> RBD", Storage.isCopiedInPlaceOnKvm(other, StoragePoolType.RBD));
}
}

@Test
public void areBothPoolsFileBasedBetweenTwoFileBasedPools() {
for (StoragePoolType srcType : Storage.KVM_FILE_BASED_POOL_TYPES) {
for (StoragePoolType destType : Storage.KVM_FILE_BASED_POOL_TYPES) {
Assert.assertTrue(srcType + " -> " + destType, Storage.areBothPoolsFileBased(srcType, destType));
}
}
}

@Test
public void areBothPoolsFileBasedNotWithOtherPoolTypes() {
for (StoragePoolType other : StoragePoolType.values()) {
if (Storage.KVM_FILE_BASED_POOL_TYPES.contains(other)) {
continue;
}
Assert.assertFalse(other + " -> " + other, Storage.areBothPoolsFileBased(other, other));
for (StoragePoolType fileBased : Storage.KVM_FILE_BASED_POOL_TYPES) {
Assert.assertFalse(fileBased + " -> " + other, Storage.areBothPoolsFileBased(fileBased, other));
Assert.assertFalse(other + " -> " + fileBased, Storage.areBothPoolsFileBased(other, fileBased));
}
}
}
}
Loading