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

pearl11594 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 a4263da8aea linstor: Use template's uuid if pool's downloadPath is 
null as resource-name (#11053)
a4263da8aea is described below

commit a4263da8aea93fd12ead37c61489b9e529c0cdbc
Author: ghernadi <gabor.hern...@linbit.com>
AuthorDate: Fri Jul 25 13:51:11 2025 +0200

    linstor: Use template's uuid if pool's downloadPath is null as 
resource-name (#11053)
    
    Also added an integration test for templates from snapshots
---
 plugins/storage/volume/linstor/CHANGELOG.md        |  6 ++
 .../kvm/storage/LinstorStorageAdaptor.java         |  2 +-
 .../driver/LinstorPrimaryDataStoreDriverImpl.java  | 12 +++-
 .../plugins/linstor/test_linstor_volumes.py        | 73 ++++++++++++++++++++--
 4 files changed, 86 insertions(+), 7 deletions(-)

diff --git a/plugins/storage/volume/linstor/CHANGELOG.md 
b/plugins/storage/volume/linstor/CHANGELOG.md
index 2abda3ebc50..a96b7c75b2b 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-07-01]
+
+### Fixed
+
+- Regression in 4.19.3 and 4.21.0 with templates from snapshots
+
 ## [2025-05-07]
 
 ### Added
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 9de2479b20b..4210008f1c0 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
@@ -619,7 +619,7 @@ public class LinstorStorageAdaptor implements 
StorageAdaptor {
             try {
                 templateProps.load(new FileInputStream(propFile.toFile()));
                 String desc = templateProps.getProperty("description");
-                if (desc.startsWith("SystemVM Template")) {
+                if (desc != null && desc.startsWith("SystemVM Template")) {
                     return true;
                 }
             } catch (IOException e) {
diff --git 
a/plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/driver/LinstorPrimaryDataStoreDriverImpl.java
 
b/plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/driver/LinstorPrimaryDataStoreDriverImpl.java
index 3b384831518..71e1b528506 100644
--- 
a/plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/driver/LinstorPrimaryDataStoreDriverImpl.java
+++ 
b/plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/driver/LinstorPrimaryDataStoreDriverImpl.java
@@ -74,12 +74,14 @@ import com.cloud.storage.StorageManager;
 import com.cloud.storage.StoragePool;
 import com.cloud.storage.VMTemplateStoragePoolVO;
 import com.cloud.storage.VMTemplateStorageResourceAssoc;
+import com.cloud.storage.VMTemplateVO;
 import com.cloud.storage.Volume;
 import com.cloud.storage.VolumeDetailVO;
 import com.cloud.storage.VolumeVO;
 import com.cloud.storage.dao.SnapshotDao;
 import com.cloud.storage.dao.SnapshotDetailsDao;
 import com.cloud.storage.dao.SnapshotDetailsVO;
+import com.cloud.storage.dao.VMTemplateDao;
 import com.cloud.storage.dao.VMTemplatePoolDao;
 import com.cloud.storage.dao.VolumeDao;
 import com.cloud.storage.dao.VolumeDetailsDao;
@@ -131,6 +133,7 @@ public class LinstorPrimaryDataStoreDriverImpl implements 
PrimaryDataStoreDriver
     ConfigurationDao _configDao;
     @Inject
     private HostDao _hostDao;
+    @Inject private VMTemplateDao _vmTemplateDao;
 
     private long volumeStatsLastUpdate = 0L;
     private final Map<String, Pair<Long, Long>> volumeStats = new HashMap<>();
@@ -668,8 +671,15 @@ public class LinstorPrimaryDataStoreDriverImpl implements 
PrimaryDataStoreDriver
             storagePoolVO.getId(), csCloneId, null);
 
         if (tmplPoolRef != null) {
-            final String templateRscName = LinstorUtil.RSC_PREFIX + 
tmplPoolRef.getLocalDownloadPath();
+            final String templateRscName;
+            if (tmplPoolRef.getLocalDownloadPath() == null) {
+                VMTemplateVO vmTemplateVO = 
_vmTemplateDao.findById(tmplPoolRef.getTemplateId());
+                templateRscName = LinstorUtil.RSC_PREFIX + 
vmTemplateVO.getUuid();
+            } else {
+                templateRscName = LinstorUtil.RSC_PREFIX + 
tmplPoolRef.getLocalDownloadPath();
+            }
             final String rscName = LinstorUtil.RSC_PREFIX + 
volumeInfo.getUuid();
+
             final DevelopersApi linstorApi = 
LinstorUtil.getLinstorAPI(storagePoolVO.getHostAddress());
 
             try {
diff --git a/test/integration/plugins/linstor/test_linstor_volumes.py 
b/test/integration/plugins/linstor/test_linstor_volumes.py
index a2c7978d801..e0ba15a0499 100644
--- a/test/integration/plugins/linstor/test_linstor_volumes.py
+++ b/test/integration/plugins/linstor/test_linstor_volumes.py
@@ -953,9 +953,72 @@ class TestLinstorVolumes(cloudstackTestCase):
 
         snapshot.delete(self.apiClient)
 
+    @attr(tags=['basic'], required_hardware=False)
+    def test_10_create_template_from_snapshot(self):
+        """
+        Create a template from a snapshot and start an instance from it
+        """
+        self.virtual_machine.stop(self.apiClient)
+
+        volume = list_volumes(
+            self.apiClient,
+            virtualmachineid = self.virtual_machine.id,
+            type = "ROOT",
+            listall = True,
+        )
+        snapshot = Snapshot.create(
+            self.apiClient,
+            volume_id=volume[0].id,
+            account=self.account.name,
+            domainid=self.domain.id,
+        )
+        self.cleanup.append(snapshot)
+
+        self.assertIsNotNone(snapshot, "Could not create snapshot")
+
+        services = {
+            "displaytext": "IntegrationTestTemplate",
+            "name": "int-test-template",
+            "ostypeid": self.template.ostypeid,
+            "ispublic": "true"
+        }
+
+        custom_template = Template.create_from_snapshot(
+            self.apiClient,
+            snapshot,
+            services,
+        )
+        self.cleanup.append(custom_template)
+
+        # create VM from custom template
+        test_virtual_machine = VirtualMachine.create(
+            self.apiClient,
+            self.testdata[TestData.virtualMachine2],
+            accountid=self.account.name,
+            zoneid=self.zone.id,
+            serviceofferingid=self.compute_offering.id,
+            templateid=custom_template.id,
+            domainid=self.domain.id,
+            startvm=False,
+            mode='basic',
+        )
+        self.cleanup.append(test_virtual_machine)
+
+        TestLinstorVolumes._start_vm(test_virtual_machine)
+
+        test_virtual_machine.stop(self.apiClient)
+
+        test_virtual_machine.delete(self.apiClient, True)
+        self.cleanup.remove(test_virtual_machine)
+
+        custom_template.delete(self.apiClient)
+        self.cleanup.remove(custom_template)
+        snapshot.delete(self.apiClient)
+        self.cleanup.remove(snapshot)
+
 
     @attr(tags=['advanced', 'migration'], required_hardware=False)
-    def test_10_migrate_volume_to_same_instance_pool(self):
+    def test_11_migrate_volume_to_same_instance_pool(self):
         """Migrate volume to the same instance pool"""
 
         if not self.testdata[TestData.migrationTests]:
@@ -1088,7 +1151,7 @@ class TestLinstorVolumes(cloudstackTestCase):
         test_virtual_machine.delete(self.apiClient, True)
 
     @attr(tags=['advanced', 'migration'], required_hardware=False)
-    def test_11_migrate_volume_to_distinct_instance_pool(self):
+    def test_12_migrate_volume_to_distinct_instance_pool(self):
         """Migrate volume to distinct instance pool"""
 
         if not self.testdata[TestData.migrationTests]:
@@ -1221,7 +1284,7 @@ class TestLinstorVolumes(cloudstackTestCase):
         test_virtual_machine.delete(self.apiClient, True)
 
     @attr(tags=["basic"], required_hardware=False)
-    def test_12_create_vm_snapshots(self):
+    def test_13_create_vm_snapshots(self):
         """Test to create VM snapshots
         """
         vm = TestLinstorVolumes._start_vm(self.virtual_machine)
@@ -1251,7 +1314,7 @@ class TestLinstorVolumes(cloudstackTestCase):
         )
 
     @attr(tags=["basic"], required_hardware=False)
-    def test_13_revert_vm_snapshots(self):
+    def test_14_revert_vm_snapshots(self):
         """Test to revert VM snapshots
         """
 
@@ -1313,7 +1376,7 @@ class TestLinstorVolumes(cloudstackTestCase):
         )
 
     @attr(tags=["basic"], required_hardware=False)
-    def test_14_delete_vm_snapshots(self):
+    def test_15_delete_vm_snapshots(self):
         """Test to delete vm snapshots
         """
 

Reply via email to