Skip to content

Commit d87c804

Browse files
authored
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.
1 parent 5c97979 commit d87c804

1 file changed

Lines changed: 15 additions & 57 deletions

File tree

plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java

Lines changed: 15 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,6 @@
5555

5656
import com.ceph.rados.IoCTX;
5757
import com.ceph.rados.Rados;
58-
import com.ceph.rados.exceptions.ErrorCode;
5958
import com.ceph.rados.exceptions.RadosException;
6059
import com.ceph.rbd.Rbd;
6160
import com.ceph.rbd.RbdException;
@@ -95,6 +94,8 @@ public class LibvirtStorageAdaptor implements StorageAdaptor {
9594
private static final int RBD_FEATURE_DEEP_FLATTEN = 32;
9695
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;
9796
private int rbdOrder = 0; /* Order 0 means 4MB blocks (the default) */
97+
/* libvirt's VIR_STORAGE_VOL_DELETE_WITH_SNAPSHOTS, not exposed as a constant by libvirt-java */
98+
private static final int VIR_STORAGE_VOL_DELETE_WITH_SNAPSHOTS = 2;
9899

99100
private static final Set<StoragePoolType> QEMU_IMG_MANAGED_POOL_TYPES = Set.of(StoragePoolType.NetworkFilesystem, StoragePoolType.Filesystem, StoragePoolType.SharedMountPoint);
100101

@@ -1150,61 +1151,6 @@ public boolean deletePhysicalDisk(String uuid, KVMStoragePool pool, Storage.Imag
11501151

11511152
logger.info("Attempting to remove volume " + uuid + " from pool " + pool.getUuid());
11521153

1153-
/**
1154-
* RBD volume can have snapshots and while they exist libvirt
1155-
* can't remove the RBD volume
1156-
*
1157-
* We have to remove those snapshots first
1158-
*/
1159-
if (pool.getType() == StoragePoolType.RBD) {
1160-
try {
1161-
logger.info("Unprotecting and Removing RBD snapshots of image " + pool.getSourceDir() + "/" + uuid + " prior to removing the image");
1162-
1163-
Rados r = new Rados(pool.getAuthUserName());
1164-
r.confSet("mon_host", pool.getSourceHost() + ":" + pool.getSourcePort());
1165-
r.confSet("key", pool.getAuthSecret());
1166-
r.confSet("client_mount_timeout", "30");
1167-
r.connect();
1168-
logger.debug("Successfully connected to Ceph cluster at " + r.confGet("mon_host"));
1169-
1170-
IoCTX io = r.ioCtxCreate(pool.getSourceDir());
1171-
Rbd rbd = new Rbd(io);
1172-
RbdImage image = rbd.open(uuid);
1173-
logger.debug("Fetching list of snapshots of RBD image " + pool.getSourceDir() + "/" + uuid);
1174-
List<RbdSnapInfo> snaps = image.snapList();
1175-
try {
1176-
for (RbdSnapInfo snap : snaps) {
1177-
if (image.snapIsProtected(snap.name)) {
1178-
logger.debug("Unprotecting snapshot " + pool.getSourceDir() + "/" + uuid + "@" + snap.name);
1179-
image.snapUnprotect(snap.name);
1180-
} else {
1181-
logger.debug("Snapshot " + pool.getSourceDir() + "/" + uuid + "@" + snap.name + " is not protected.");
1182-
}
1183-
logger.debug("Removing snapshot " + pool.getSourceDir() + "/" + uuid + "@" + snap.name);
1184-
image.snapRemove(snap.name);
1185-
}
1186-
logger.info("Successfully unprotected and removed any remaining snapshots (" + snaps.size() + ") of "
1187-
+ pool.getSourceDir() + "/" + uuid + " Continuing to remove the RBD image");
1188-
} catch (RbdException e) {
1189-
logger.error("Failed to remove snapshot with exception: " + e.toString() +
1190-
", RBD error: " + ErrorCode.getErrorMessage(e.getReturnValue()));
1191-
throw new CloudRuntimeException(e.toString() + " - " + ErrorCode.getErrorMessage(e.getReturnValue()));
1192-
} finally {
1193-
logger.debug("Closing image and destroying context");
1194-
rbd.close(image);
1195-
r.ioCtxDestroy(io);
1196-
}
1197-
} catch (RadosException e) {
1198-
logger.error("Failed to remove snapshot with exception: " + e.toString() +
1199-
", RBD error: " + ErrorCode.getErrorMessage(e.getReturnValue()));
1200-
throw new CloudRuntimeException(e.toString() + " - " + ErrorCode.getErrorMessage(e.getReturnValue()));
1201-
} catch (RbdException e) {
1202-
logger.error("Failed to remove snapshot with exception: " + e.toString() +
1203-
", RBD error: " + ErrorCode.getErrorMessage(e.getReturnValue()));
1204-
throw new CloudRuntimeException(e.toString() + " - " + ErrorCode.getErrorMessage(e.getReturnValue()));
1205-
}
1206-
}
1207-
12081154
LibvirtStoragePool libvirtPool = (LibvirtStoragePool)pool;
12091155
try {
12101156
StorageVol vol = getVolume(libvirtPool.getPool(), uuid);
@@ -1721,7 +1667,19 @@ private void refreshPool(StoragePool pool) throws LibvirtException {
17211667
}
17221668

17231669
private void deleteVol(LibvirtStoragePool pool, StorageVol vol) throws LibvirtException {
1724-
vol.delete(0);
1670+
/**
1671+
* RBD volumes can have snapshots, and libvirt refuses to remove a volume while
1672+
* they exist. VIR_STORAGE_VOL_DELETE_WITH_SNAPSHOTS tells the RBD storage backend
1673+
* to unprotect and remove any snapshots before removing the volume itself.
1674+
*
1675+
* libvirt-java has no named constant for this flag (added upstream in libvirt 1.2.20,
1676+
* commit 3c7590e0a4), so it's passed as a raw flag value here.
1677+
*/
1678+
int flags = 0;
1679+
if (pool.getType() == StoragePoolType.RBD) {
1680+
flags |= VIR_STORAGE_VOL_DELETE_WITH_SNAPSHOTS;
1681+
}
1682+
vol.delete(flags);
17251683
}
17261684

17271685

0 commit comments

Comments
 (0)