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

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


The following commit(s) were added to refs/heads/4.19 by this push:
     new a208db54ea8 linstor: use sparse/discard qemu-img convert on thin 
devices (#11787)
a208db54ea8 is described below

commit a208db54ea84a8e3eda406990671c49efc90ef42
Author: Rene Peinthor <[email protected]>
AuthorDate: Mon Oct 6 09:10:53 2025 +0200

    linstor: use sparse/discard qemu-img convert on thin devices (#11787)
---
 plugins/storage/volume/linstor/CHANGELOG.md        |  6 ++++
 .../LinstorRevertBackupSnapshotCommandWrapper.java | 21 ++++++++++--
 .../kvm/storage/LinstorStorageAdaptor.java         | 38 +---------------------
 .../storage/datastore/util/LinstorUtil.java        | 34 +++++++++++++++++++
 4 files changed, 59 insertions(+), 40 deletions(-)

diff --git a/plugins/storage/volume/linstor/CHANGELOG.md 
b/plugins/storage/volume/linstor/CHANGELOG.md
index c0991a9aa2b..7da3516955d 100644
--- a/plugins/storage/volume/linstor/CHANGELOG.md
+++ b/plugins/storage/volume/linstor/CHANGELOG.md
@@ -5,6 +5,12 @@ All notable changes to Linstor CloudStack plugin will be 
documented in this file
 The format is based on [Keep a 
Changelog](https://keepachangelog.com/en/1.0.0/),
 and this project adheres to [Semantic 
Versioning](https://semver.org/spec/v2.0.0.html).
 
+## [2025-10-03]
+
+### Changed
+
+- Revert qcow2 snapshot now use sparse/discard options to convert on thin 
devices.
+
 ## [2025-08-05]
 
 ### Fixed
diff --git 
a/plugins/storage/volume/linstor/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LinstorRevertBackupSnapshotCommandWrapper.java
 
b/plugins/storage/volume/linstor/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LinstorRevertBackupSnapshotCommandWrapper.java
index 511b5a40ca8..98b8bf0bb78 100644
--- 
a/plugins/storage/volume/linstor/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LinstorRevertBackupSnapshotCommandWrapper.java
+++ 
b/plugins/storage/volume/linstor/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LinstorRevertBackupSnapshotCommandWrapper.java
@@ -26,13 +26,16 @@ import 
com.cloud.hypervisor.kvm.storage.KVMStoragePoolManager;
 import com.cloud.resource.CommandWrapper;
 import com.cloud.resource.ResourceWrapper;
 import com.cloud.storage.Storage;
+import com.cloud.utils.script.Script;
 import org.apache.cloudstack.storage.command.CopyCmdAnswer;
+import org.apache.cloudstack.storage.datastore.util.LinstorUtil;
 import org.apache.cloudstack.storage.to.SnapshotObjectTO;
 import org.apache.cloudstack.storage.to.VolumeObjectTO;
 import org.apache.cloudstack.utils.qemu.QemuImg;
 import org.apache.cloudstack.utils.qemu.QemuImgException;
 import org.apache.cloudstack.utils.qemu.QemuImgFile;
 import org.apache.log4j.Logger;
+import org.joda.time.Duration;
 import org.libvirt.LibvirtException;
 
 @ResourceWrapper(handles = LinstorRevertBackupSnapshotCommand.class)
@@ -41,12 +44,23 @@ public final class LinstorRevertBackupSnapshotCommandWrapper
 {
     private static final Logger s_logger = 
Logger.getLogger(LinstorRevertBackupSnapshotCommandWrapper.class);
 
-    private void convertQCow2ToRAW(final String srcPath, final String dstPath, 
int waitMilliSeconds)
+    private void convertQCow2ToRAW(
+            KVMStoragePool pool, final String srcPath, final String dstUuid, 
int waitMilliSeconds)
         throws LibvirtException, QemuImgException
     {
+        final String dstPath = pool.getPhysicalDisk(dstUuid).getPath();
         final QemuImgFile srcQemuFile = new QemuImgFile(
             srcPath, QemuImg.PhysicalDiskFormat.QCOW2);
-        final QemuImg qemu = new QemuImg(waitMilliSeconds);
+        boolean zeroedDevice = LinstorUtil.resourceSupportZeroBlocks(pool, 
LinstorUtil.RSC_PREFIX + dstUuid);
+        if (zeroedDevice)
+        {
+            // blockdiscard the device to ensure the device is filled with 
zeroes
+            Script blkDiscardScript = new Script("blkdiscard", 
Duration.millis(waitMilliSeconds));
+            blkDiscardScript.add("-f");
+            blkDiscardScript.add(dstPath);
+            blkDiscardScript.execute();
+        }
+        final QemuImg qemu = new QemuImg(waitMilliSeconds, zeroedDevice, true);
         final QemuImgFile dstFile = new QemuImgFile(dstPath, 
QemuImg.PhysicalDiskFormat.RAW);
         qemu.convert(srcQemuFile, dstFile);
     }
@@ -73,8 +87,9 @@ public final class LinstorRevertBackupSnapshotCommandWrapper
                 srcDataStore.getUrl() + File.separator + srcFile.getParent());
 
             convertQCow2ToRAW(
+                linstorPool,
                 secondaryPool.getLocalPath() + File.separator + 
srcFile.getName(),
-                linstorPool.getPhysicalDisk(dst.getPath()).getPath(),
+                dst.getPath(),
                 cmd.getWaitInMillSeconds());
 
             final VolumeObjectTO dstVolume = new VolumeObjectTO();
diff --git 
a/plugins/storage/volume/linstor/src/main/java/com/cloud/hypervisor/kvm/storage/LinstorStorageAdaptor.java
 
b/plugins/storage/volume/linstor/src/main/java/com/cloud/hypervisor/kvm/storage/LinstorStorageAdaptor.java
index 4210008f1c0..c269878c808 100644
--- 
a/plugins/storage/volume/linstor/src/main/java/com/cloud/hypervisor/kvm/storage/LinstorStorageAdaptor.java
+++ 
b/plugins/storage/volume/linstor/src/main/java/com/cloud/hypervisor/kvm/storage/LinstorStorageAdaptor.java
@@ -30,7 +30,6 @@ import javax.annotation.Nonnull;
 import com.cloud.storage.Storage;
 import com.cloud.utils.exception.CloudRuntimeException;
 import com.cloud.utils.script.Script;
-
 import org.apache.cloudstack.storage.datastore.util.LinstorUtil;
 import org.apache.cloudstack.utils.qemu.QemuImg;
 import org.apache.cloudstack.utils.qemu.QemuImgException;
@@ -56,7 +55,6 @@ import com.linbit.linstor.api.model.ResourceGroupSpawn;
 import com.linbit.linstor.api.model.ResourceMakeAvailable;
 import com.linbit.linstor.api.model.ResourceWithVolumes;
 import com.linbit.linstor.api.model.StoragePool;
-import com.linbit.linstor.api.model.Volume;
 import com.linbit.linstor.api.model.VolumeDefinition;
 
 import java.io.File;
@@ -570,40 +568,6 @@ public class LinstorStorageAdaptor implements 
StorageAdaptor {
         return copyPhysicalDisk(disk, name, destPool, timeout, null, null, 
null);
     }
 
-    /**
-     * Checks if all diskful resource are on a zeroed block device.
-     * @param destPool Linstor pool to use
-     * @param resName Linstor resource name
-     * @return true if all resources are on a provider with zeroed blocks.
-     */
-    private boolean resourceSupportZeroBlocks(KVMStoragePool destPool, String 
resName) {
-        final DevelopersApi api = getLinstorAPI(destPool);
-
-        try {
-            List<ResourceWithVolumes> resWithVols = api.viewResources(
-                    Collections.emptyList(),
-                    Collections.singletonList(resName),
-                    Collections.emptyList(),
-                    Collections.emptyList(),
-                    null,
-                    null);
-
-            if (resWithVols != null) {
-                return resWithVols.stream()
-                        .allMatch(res -> {
-                            Volume vol0 = res.getVolumes().get(0);
-                            return vol0 != null && (vol0.getProviderKind() == 
ProviderKind.LVM_THIN ||
-                                    vol0.getProviderKind() == ProviderKind.ZFS 
||
-                                    vol0.getProviderKind() == 
ProviderKind.ZFS_THIN ||
-                                    vol0.getProviderKind() == 
ProviderKind.DISKLESS);
-                        } );
-            }
-        } catch (ApiException apiExc) {
-            s_logger.error(apiExc.getMessage());
-        }
-        return false;
-    }
-
     /**
      * Checks if the given disk is the SystemVM template, by checking its 
properties file in the same directory.
      * The initial systemvm template resource isn't created on the management 
server, but
@@ -674,7 +638,7 @@ public class LinstorStorageAdaptor implements 
StorageAdaptor {
         destFile.setFormat(dstDisk.getFormat());
         destFile.setSize(disk.getVirtualSize());
 
-        boolean zeroedDevice = resourceSupportZeroBlocks(destPools, 
getLinstorRscName(name));
+        boolean zeroedDevice = 
LinstorUtil.resourceSupportZeroBlocks(destPools, getLinstorRscName(name));
         try {
             final QemuImg qemu = new QemuImg(timeout, zeroedDevice, true);
             qemu.convert(srcFile, destFile);
diff --git 
a/plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/util/LinstorUtil.java
 
b/plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/util/LinstorUtil.java
index 60d06590006..9a6151efafc 100644
--- 
a/plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/util/LinstorUtil.java
+++ 
b/plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/util/LinstorUtil.java
@@ -42,6 +42,7 @@ import java.util.Map;
 import java.util.Optional;
 import java.util.stream.Collectors;
 
+import com.cloud.hypervisor.kvm.storage.KVMStoragePool;
 import com.cloud.utils.Pair;
 import com.cloud.utils.exception.CloudRuntimeException;
 import org.apache.log4j.Logger;
@@ -430,4 +431,37 @@ public class LinstorUtil {
     public static boolean isRscDiskless(ResourceWithVolumes rsc) {
         return rsc.getFlags() != null && 
rsc.getFlags().contains(ApiConsts.FLAG_DISKLESS);
     }
+
+    /**
+     * Checks if all diskful resource are on a zeroed block device.
+     * @param pool Linstor pool to use
+     * @param resName Linstor resource name
+     * @return true if all resources are on a provider with zeroed blocks.
+     */
+    public static boolean resourceSupportZeroBlocks(KVMStoragePool pool, 
String resName) {
+        final DevelopersApi api = getLinstorAPI(pool.getSourceHost());
+        try {
+            List<ResourceWithVolumes> resWithVols = api.viewResources(
+                    Collections.emptyList(),
+                    Collections.singletonList(resName),
+                    Collections.emptyList(),
+                    Collections.emptyList(),
+                    null,
+                    null);
+
+            if (resWithVols != null) {
+                return resWithVols.stream()
+                        .allMatch(res -> {
+                            Volume vol0 = res.getVolumes().get(0);
+                            return vol0 != null && (vol0.getProviderKind() == 
ProviderKind.LVM_THIN ||
+                                    vol0.getProviderKind() == ProviderKind.ZFS 
||
+                                    vol0.getProviderKind() == 
ProviderKind.ZFS_THIN ||
+                                    vol0.getProviderKind() == 
ProviderKind.DISKLESS);
+                        } );
+            }
+        } catch (ApiException apiExc) {
+            s_logger.error(apiExc.getMessage());
+        }
+        return false;
+    }
 }

Reply via email to