diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java index eff149d32f1f..44493a186816 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java @@ -2695,8 +2695,19 @@ public Answer createVolumeFromSnapshot(final CopyCommand cmd) { final String snapshotFullPath = snapshot.getPath(); final int index = snapshotFullPath.lastIndexOf("/"); - final String snapshotPath = snapshotFullPath.substring(0, index); - final String snapshotName = snapshotFullPath.substring(index + 1); + final String snapshotPath; + final String snapshotName; + if (index >= 0) { + snapshotPath = snapshotFullPath.substring(0, index); + snapshotName = snapshotFullPath.substring(index + 1); + } else { + if (pool.getPoolType() == StoragePoolType.SharedMountPoint) { + snapshotPath = pool.getPath(); + snapshotName = snapshotFullPath; + } else { + throw new CloudRuntimeException("Invalid snapshot path format: " + snapshotFullPath); + } + } KVMPhysicalDisk disk = null; if (imageStore instanceof NfsTO) { disk = createVolumeFromSnapshotOnNFS(cmd, pool, imageStore, volume, snapshotPath, snapshotName); diff --git a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java index 2e86b8fb7c26..d96ac303d46a 100644 --- a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java @@ -10698,7 +10698,7 @@ public Optional cloneVirtualMachine(CloneVMCmd cmd) throws ResourceAlloc throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create vm snapshot: " + e.getMessage(), e); } - List listSnapshots = vmSnapshotDetailsDao.findDetails(vmSnapshot.getId(), "kvmStorageSnapshot"); + List listSnapshots = vmSnapshotDetailsDao.findDetails(vmSnapshot.getId(), "kvmFileBasedStorageSnapshot"); Integer countOfCloneVM = cmd.getCount(); for (int cnt = 1; cnt <= countOfCloneVM; cnt++) { diff --git a/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java b/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java index b6cb759495a6..9cd3cd36a402 100644 --- a/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java +++ b/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java @@ -105,6 +105,7 @@ import com.cloud.storage.VolumeApiService; import com.cloud.storage.VolumeVO; import com.cloud.storage.dao.DiskOfferingDao; +import com.cloud.storage.dao.StoragePoolTagsDao; import com.cloud.storage.dao.GuestOSDao; import com.cloud.storage.dao.GuestOSHypervisorDao; import com.cloud.storage.dao.SnapshotDao; @@ -250,6 +251,8 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager { @Inject private DiskOfferingDao diskOfferingDao; @Inject + private StoragePoolTagsDao storagePoolTagsDao; + @Inject private ResourceManager resourceManager; @Inject private ResourceLimitService resourceLimitService; @@ -2201,16 +2204,43 @@ private List findInstanceConversionDestinationStoragePoolsInClust Cluster destinationCluster, ServiceOfferingVO serviceOffering, Map dataDiskOfferingMap, DataStoreTO temporaryConvertLocation, boolean forceConvertToPool) { - List poolsList; + List poolsList = new ArrayList<>(); if (!forceConvertToPool) { - Set pools = new HashSet<>(primaryDataStoreDao.findClusterWideStoragePoolsByHypervisorAndPoolType(destinationCluster.getId(), Hypervisor.HypervisorType.KVM, Storage.StoragePoolType.NetworkFilesystem)); - pools.addAll(primaryDataStoreDao.findZoneWideStoragePoolsByHypervisorAndPoolType(destinationCluster.getDataCenterId(), Hypervisor.HypervisorType.KVM, Storage.StoragePoolType.NetworkFilesystem)); - if (pools.isEmpty()) { - String msg = String.format("Cannot find suitable storage pools in the cluster %s for the conversion", destinationCluster.getName()); - logger.error(msg); - throw new CloudRuntimeException(msg); + // Try to find pools based on disk offering tags + Set candidatePoolIds = new HashSet<>(); + if (serviceOffering.getDiskOfferingId() != null) { + DiskOfferingVO diskOffering = diskOfferingDao.findById(serviceOffering.getDiskOfferingId()); + if (diskOffering != null && StringUtils.isNotBlank(diskOffering.getTags())) { + String tag = diskOffering.getTags(); + List ids = storagePoolTagsDao.listPoolIdsByTag(tag); + if (ids != null) { + candidatePoolIds.addAll(ids); + } + } + } + + if (!candidatePoolIds.isEmpty()) { + for (Long poolid : candidatePoolIds) { + StoragePoolVO pool = primaryDataStoreDao.findById(poolid); + if (pool == null) { + continue; + } + poolsList.add(pool); + } + } + logger.info("find poolsList : " + poolsList); + + // Fallback to previous behavior if no tagged pools found + if (poolsList.isEmpty()) { + Set pools = new HashSet<>(primaryDataStoreDao.listPoolsByCluster(destinationCluster.getId())); + pools.addAll(primaryDataStoreDao.findZoneWideStoragePoolsByHypervisor(destinationCluster.getDataCenterId(), Hypervisor.HypervisorType.KVM)); + if (pools.isEmpty()) { + String msg = String.format("Cannot find suitable storage pools in the cluster %s for the conversion", destinationCluster.getName()); + logger.error(msg); + throw new CloudRuntimeException(msg); + } + poolsList.addAll(pools); } - poolsList = new ArrayList<>(pools); } else { DataStore dataStore = dataStoreManager.getDataStore(temporaryConvertLocation.getUuid(), temporaryConvertLocation.getRole()); poolsList = Collections.singletonList(primaryDataStoreDao.findById(dataStore.getId()));