This is an automated email from the ASF dual-hosted git repository.

weizhouapache pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/cloudstack.git


The following commit(s) were added to refs/heads/main by this push:
     new d87c8049aae kvm: let libvirt remove RBD snapshots on volume delete 
(#13763)
d87c8049aae is described below

commit d87c8049aae4be06e33ded7cf2dd6e61db0eea5b
Author: Wido den Hollander <[email protected]>
AuthorDate: Tue Sep 8 14:12:47 2026 +0200

    kvm: let libvirt remove RBD snapshots on volume delete (#13763)
    
    LibvirtStorageAdaptor#deletePhysicalDisk manually connected to Ceph via
    librados/librbd to unprotect and remove every snapshot of an RBD image
    before asking libvirt to delete the volume. libvirt's RBD storage
    backend has supported VIR_STORAGE_VOL_DELETE_WITH_SNAPSHOTS since
    1.2.20, which does the same unprotect/remove internally. Pass that
    flag instead and drop the manual cleanup.
---
 .../kvm/storage/LibvirtStorageAdaptor.java         | 72 +++++-----------------
 1 file changed, 15 insertions(+), 57 deletions(-)

diff --git 
a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java
 
b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java
index 4bfac31b68f..794b88165aa 100644
--- 
a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java
+++ 
b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java
@@ -55,7 +55,6 @@ import org.libvirt.StorageVol;
 
 import com.ceph.rados.IoCTX;
 import com.ceph.rados.Rados;
-import com.ceph.rados.exceptions.ErrorCode;
 import com.ceph.rados.exceptions.RadosException;
 import com.ceph.rbd.Rbd;
 import com.ceph.rbd.RbdException;
@@ -95,6 +94,8 @@ public class LibvirtStorageAdaptor implements StorageAdaptor {
     private static final int RBD_FEATURE_DEEP_FLATTEN = 32;
     public static final int RBD_FEATURES = RBD_FEATURE_LAYERING + 
RBD_FEATURE_EXCLUSIVE_LOCK + RBD_FEATURE_OBJECT_MAP + RBD_FEATURE_FAST_DIFF + 
RBD_FEATURE_DEEP_FLATTEN;
     private int rbdOrder = 0; /* Order 0 means 4MB blocks (the default) */
+    /* libvirt's VIR_STORAGE_VOL_DELETE_WITH_SNAPSHOTS, not exposed as a 
constant by libvirt-java */
+    private static final int VIR_STORAGE_VOL_DELETE_WITH_SNAPSHOTS = 2;
 
     private static final Set<StoragePoolType> QEMU_IMG_MANAGED_POOL_TYPES = 
Set.of(StoragePoolType.NetworkFilesystem, StoragePoolType.Filesystem, 
StoragePoolType.SharedMountPoint);
 
@@ -1150,61 +1151,6 @@ public class LibvirtStorageAdaptor implements 
StorageAdaptor {
 
         logger.info("Attempting to remove volume " + uuid + " from pool " + 
pool.getUuid());
 
-        /**
-         * RBD volume can have snapshots and while they exist libvirt
-         * can't remove the RBD volume
-         *
-         * We have to remove those snapshots first
-         */
-        if (pool.getType() == StoragePoolType.RBD) {
-            try {
-                logger.info("Unprotecting and Removing RBD snapshots of image 
" + pool.getSourceDir() + "/" + uuid + " prior to removing the image");
-
-                Rados r = new Rados(pool.getAuthUserName());
-                r.confSet("mon_host", pool.getSourceHost() + ":" + 
pool.getSourcePort());
-                r.confSet("key", pool.getAuthSecret());
-                r.confSet("client_mount_timeout", "30");
-                r.connect();
-                logger.debug("Successfully connected to Ceph cluster at " + 
r.confGet("mon_host"));
-
-                IoCTX io = r.ioCtxCreate(pool.getSourceDir());
-                Rbd rbd = new Rbd(io);
-                RbdImage image = rbd.open(uuid);
-                logger.debug("Fetching list of snapshots of RBD image " + 
pool.getSourceDir() + "/" + uuid);
-                List<RbdSnapInfo> snaps = image.snapList();
-                try {
-                    for (RbdSnapInfo snap : snaps) {
-                        if (image.snapIsProtected(snap.name)) {
-                            logger.debug("Unprotecting snapshot " + 
pool.getSourceDir() + "/" + uuid + "@" + snap.name);
-                            image.snapUnprotect(snap.name);
-                        } else {
-                            logger.debug("Snapshot " + pool.getSourceDir() + 
"/" + uuid + "@" + snap.name + " is not protected.");
-                        }
-                        logger.debug("Removing snapshot " + 
pool.getSourceDir() + "/" + uuid + "@" + snap.name);
-                        image.snapRemove(snap.name);
-                    }
-                    logger.info("Successfully unprotected and removed any 
remaining snapshots (" + snaps.size() + ") of "
-                        + pool.getSourceDir() + "/" + uuid + " Continuing to 
remove the RBD image");
-                } catch (RbdException e) {
-                    logger.error("Failed to remove snapshot with exception: " 
+ e.toString() +
-                        ", RBD error: " + 
ErrorCode.getErrorMessage(e.getReturnValue()));
-                    throw new CloudRuntimeException(e.toString() + " - " + 
ErrorCode.getErrorMessage(e.getReturnValue()));
-                } finally {
-                    logger.debug("Closing image and destroying context");
-                    rbd.close(image);
-                    r.ioCtxDestroy(io);
-                }
-            } catch (RadosException e) {
-                logger.error("Failed to remove snapshot with exception: " + 
e.toString() +
-                    ", RBD error: " + 
ErrorCode.getErrorMessage(e.getReturnValue()));
-                throw new CloudRuntimeException(e.toString() + " - " + 
ErrorCode.getErrorMessage(e.getReturnValue()));
-            } catch (RbdException e) {
-                logger.error("Failed to remove snapshot with exception: " + 
e.toString() +
-                    ", RBD error: " + 
ErrorCode.getErrorMessage(e.getReturnValue()));
-                throw new CloudRuntimeException(e.toString() + " - " + 
ErrorCode.getErrorMessage(e.getReturnValue()));
-            }
-        }
-
         LibvirtStoragePool libvirtPool = (LibvirtStoragePool)pool;
         try {
             StorageVol vol = getVolume(libvirtPool.getPool(), uuid);
@@ -1721,7 +1667,19 @@ public class LibvirtStorageAdaptor implements 
StorageAdaptor {
     }
 
     private void deleteVol(LibvirtStoragePool pool, StorageVol vol) throws 
LibvirtException {
-        vol.delete(0);
+        /**
+         * RBD volumes can have snapshots, and libvirt refuses to remove a 
volume while
+         * they exist. VIR_STORAGE_VOL_DELETE_WITH_SNAPSHOTS tells the RBD 
storage backend
+         * to unprotect and remove any snapshots before removing the volume 
itself.
+         *
+         * libvirt-java has no named constant for this flag (added upstream in 
libvirt 1.2.20,
+         * commit 3c7590e0a4), so it's passed as a raw flag value here.
+         */
+        int flags = 0;
+        if (pool.getType() == StoragePoolType.RBD) {
+            flags |= VIR_STORAGE_VOL_DELETE_WITH_SNAPSHOTS;
+        }
+        vol.delete(flags);
     }
 
 

Reply via email to