Fix for create template from snapshot (for snapshots on primary storage and storage doesn't support create snapshot to template directly) (#11452)

* Fix for create template from snapshot

* code improvements, for create volume from snapshot
This commit is contained in:
Suresh Kumar Anaparti 2025-08-15 22:17:03 +05:30 committed by GitHub
parent ba2d70ab21
commit f671461d4c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 33 additions and 20 deletions

View File

@ -579,14 +579,19 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati
VolumeInfo vol = volFactory.getVolume(volume.getId()); VolumeInfo vol = volFactory.getVolume(volume.getId());
long zoneId = volume.getDataCenterId(); long zoneId = volume.getDataCenterId();
DataStore store = dataStoreMgr.getDataStore(pool.getId(), DataStoreRole.Primary); DataStore store = dataStoreMgr.getDataStore(pool.getId(), DataStoreRole.Primary);
DataStoreRole dataStoreRole = snapshotHelper.getDataStoreRole(snapshot, zoneId); DataStoreRole dataStoreRole = snapshotHelper.getDataStoreRole(snapshot);
SnapshotInfo snapInfo = snapshotFactory.getSnapshotWithRoleAndZone(snapshot.getId(), dataStoreRole, zoneId); SnapshotInfo snapInfo = snapshotFactory.getSnapshotWithRoleAndZone(snapshot.getId(), dataStoreRole, zoneId);
boolean kvmSnapshotOnlyInPrimaryStorage = snapshotHelper.isKvmSnapshotOnlyInPrimaryStorage(snapshot, dataStoreRole);
logger.debug("Creating volume from snapshot, with dataStore role {} and on primary storage: {}", dataStoreRole, kvmSnapshotOnlyInPrimaryStorage);
boolean kvmSnapshotOnlyInPrimaryStorage = snapshotHelper.isKvmSnapshotOnlyInPrimaryStorage(snapshot, dataStoreRole, volume.getDataCenterId());
boolean storageSupportSnapshotToTemplateEnabled = snapshotHelper.isStorageSupportSnapshotToTemplate(snapInfo); boolean storageSupportSnapshotToTemplateEnabled = snapshotHelper.isStorageSupportSnapshotToTemplate(snapInfo);
try { try {
if (!storageSupportSnapshotToTemplateEnabled) { if (storageSupportSnapshotToTemplateEnabled) { // true only for StorPool now [TODO: Update to check storage supports snapshot to volume (DataStoreCapabilities.CAN_CREATE_VOLUME_FROM_SNAPSHOT) - may impact other storages, or StorPool storage type only]
dataStoreRole = snapshotHelper.getDataStoreRole(snapshot, zoneId);
snapInfo = snapshotFactory.getSnapshotWithRoleAndZone(snapshot.getId(), dataStoreRole, zoneId);
kvmSnapshotOnlyInPrimaryStorage = snapshotHelper.isKvmSnapshotOnlyInPrimaryStorage(snapshot, dataStoreRole, zoneId);
logger.debug("Creating volume from snapshot for storage supporting snapshot to template, with dataStore role {} and on primary storage: {}", dataStoreRole, kvmSnapshotOnlyInPrimaryStorage);
} else {
snapInfo = snapshotHelper.backupSnapshotToSecondaryStorageIfNotExists(snapInfo, dataStoreRole, snapshot, kvmSnapshotOnlyInPrimaryStorage); snapInfo = snapshotHelper.backupSnapshotToSecondaryStorageIfNotExists(snapInfo, dataStoreRole, snapshot, kvmSnapshotOnlyInPrimaryStorage);
} }
} catch (CloudRuntimeException e) { } catch (CloudRuntimeException e) {
@ -600,7 +605,7 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati
} }
// don't try to perform a sync if the DataStoreRole of the snapshot is equal to DataStoreRole.Primary // don't try to perform a sync if the DataStoreRole of the snapshot is equal to DataStoreRole.Primary
if (!DataStoreRole.Primary.equals(dataStoreRole) || !storageSupportSnapshotToTemplateEnabled) { if (!DataStoreRole.Primary.equals(dataStoreRole) || (kvmSnapshotOnlyInPrimaryStorage && !storageSupportSnapshotToTemplateEnabled)) {
try { try {
// sync snapshot to region store if necessary // sync snapshot to region store if necessary
DataStore snapStore = snapInfo.getDataStore(); DataStore snapStore = snapInfo.getDataStore();

View File

@ -1692,18 +1692,21 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager,
if (store == null) { if (store == null) {
throw new CloudRuntimeException("cannot find an image store for zone " + zoneId); throw new CloudRuntimeException("cannot find an image store for zone " + zoneId);
} }
AsyncCallFuture<TemplateApiResult> future = null; AsyncCallFuture<TemplateApiResult> future;
if (snapshotId != null) { if (snapshotId != null) {
DataStoreRole dataStoreRole = snapshotHelper.getDataStoreRole(snapshot, zoneId); DataStoreRole dataStoreRole = snapshotHelper.getDataStoreRole(snapshot);
kvmSnapshotOnlyInPrimaryStorage = snapshotHelper.isKvmSnapshotOnlyInPrimaryStorage(snapshot, dataStoreRole, zoneId); kvmSnapshotOnlyInPrimaryStorage = snapshotHelper.isKvmSnapshotOnlyInPrimaryStorage(snapshot, dataStoreRole);
snapInfo = _snapshotFactory.getSnapshotWithRoleAndZone(snapshotId, dataStoreRole, zoneId); snapInfo = _snapshotFactory.getSnapshotWithRoleAndZone(snapshotId, dataStoreRole, zoneId);
boolean kvmIncrementalSnapshot = SnapshotManager.kvmIncrementalSnapshot.valueIn(_hostDao.findClusterIdByVolumeInfo(snapInfo.getBaseVolume())); logger.debug("Creating template from snapshot, with dataStore role {} and on primary storage: {}", dataStoreRole, kvmSnapshotOnlyInPrimaryStorage);
boolean storageSupportsSnapshotToTemplate = snapshotHelper.isStorageSupportSnapshotToTemplate(snapInfo);
if (storageSupportsSnapshotToTemplate) {
dataStoreRole = snapshotHelper.getDataStoreRole(snapshot, zoneId);
kvmSnapshotOnlyInPrimaryStorage = snapshotHelper.isKvmSnapshotOnlyInPrimaryStorage(snapshot, dataStoreRole, zoneId);
snapInfo = _snapshotFactory.getSnapshotWithRoleAndZone(snapshotId, dataStoreRole, zoneId);
logger.debug("Creating template from snapshot for storage supporting snapshot to template, with dataStore role {} and on primary storage: {}", dataStoreRole, kvmSnapshotOnlyInPrimaryStorage);
boolean skipCopyToSecondary = false;
boolean keepOnPrimary = snapshotHelper.isStorageSupportSnapshotToTemplate(snapInfo);
if (keepOnPrimary) {
ImageStoreVO imageStore = _imgStoreDao.findOneByZoneAndProtocol(zoneId, "nfs"); ImageStoreVO imageStore = _imgStoreDao.findOneByZoneAndProtocol(zoneId, "nfs");
if (imageStore == null) { if (imageStore == null) {
throw new CloudRuntimeException(String.format("Could not find an NFS secondary storage pool on zone %s to use as a temporary location " + throw new CloudRuntimeException(String.format("Could not find an NFS secondary storage pool on zone %s to use as a temporary location " +
@ -1713,7 +1716,7 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager,
if (dataStore != null) { if (dataStore != null) {
store = dataStore; store = dataStore;
} }
} else if (dataStoreRole == DataStoreRole.Image) { } else if (dataStoreRole == DataStoreRole.Image || kvmSnapshotOnlyInPrimaryStorage) {
snapInfo = snapshotHelper.backupSnapshotToSecondaryStorageIfNotExists(snapInfo, dataStoreRole, snapshot, kvmSnapshotOnlyInPrimaryStorage); snapInfo = snapshotHelper.backupSnapshotToSecondaryStorageIfNotExists(snapInfo, dataStoreRole, snapshot, kvmSnapshotOnlyInPrimaryStorage);
_accountMgr.checkAccess(caller, null, true, snapInfo); _accountMgr.checkAccess(caller, null, true, snapInfo);
DataStore snapStore = snapInfo.getDataStore(); DataStore snapStore = snapInfo.getDataStore();
@ -1722,6 +1725,8 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager,
store = snapStore; // pick snapshot image store to create template store = snapStore; // pick snapshot image store to create template
} }
} }
boolean kvmIncrementalSnapshot = SnapshotManager.kvmIncrementalSnapshot.valueIn(_hostDao.findClusterIdByVolumeInfo(snapInfo.getBaseVolume()));
if (kvmIncrementalSnapshot && DataStoreRole.Image.equals(dataStoreRole)) { if (kvmIncrementalSnapshot && DataStoreRole.Image.equals(dataStoreRole)) {
snapInfo = snapshotHelper.convertSnapshotIfNeeded(snapInfo); snapInfo = snapshotHelper.convertSnapshotIfNeeded(snapInfo);
} }

View File

@ -156,7 +156,8 @@ public class SnapshotHelper {
public boolean isStorageSupportSnapshotToTemplate(SnapshotInfo snapInfo) { public boolean isStorageSupportSnapshotToTemplate(SnapshotInfo snapInfo) {
if (DataStoreRole.Primary.equals(snapInfo.getDataStore().getRole())) { if (DataStoreRole.Primary.equals(snapInfo.getDataStore().getRole())) {
Map<String, String> capabilities = snapInfo.getDataStore().getDriver().getCapabilities(); Map<String, String> capabilities = snapInfo.getDataStore().getDriver().getCapabilities();
return org.apache.commons.collections4.MapUtils.isNotEmpty(capabilities) && capabilities.containsKey(DataStoreCapabilities.CAN_CREATE_TEMPLATE_FROM_SNAPSHOT.toString()); return org.apache.commons.collections4.MapUtils.isNotEmpty(capabilities)
&& Boolean.parseBoolean(capabilities.get(DataStoreCapabilities.CAN_CREATE_TEMPLATE_FROM_SNAPSHOT.toString()));
} }
return false; return false;
} }
@ -214,22 +215,24 @@ public class SnapshotHelper {
* @return true if hypervisor is {@link HypervisorType#KVM} and data store role is {@link DataStoreRole#Primary} and global setting "snapshot.backup.to.secondary" is false, * @return true if hypervisor is {@link HypervisorType#KVM} and data store role is {@link DataStoreRole#Primary} and global setting "snapshot.backup.to.secondary" is false,
* else false. * else false.
*/ */
public boolean isKvmSnapshotOnlyInPrimaryStorage(Snapshot snapshot, DataStoreRole dataStoreRole) {
return snapshot.getHypervisorType() == Hypervisor.HypervisorType.KVM && dataStoreRole == DataStoreRole.Primary && !backupSnapshotAfterTakingSnapshot;
}
public boolean isKvmSnapshotOnlyInPrimaryStorage(Snapshot snapshot, DataStoreRole dataStoreRole, Long zoneId){ public boolean isKvmSnapshotOnlyInPrimaryStorage(Snapshot snapshot, DataStoreRole dataStoreRole, Long zoneId){
List<SnapshotJoinVO> snapshots = snapshotJoinDao.listBySnapshotIdAndZoneId(zoneId, snapshot.getSnapshotId()); List<SnapshotJoinVO> snapshots = snapshotJoinDao.listBySnapshotIdAndZoneId(zoneId, snapshot.getSnapshotId());
boolean isKvmSnapshotOnlyInPrimaryStorage = snapshots.stream().filter(s -> s.getStoreRole().equals(DataStoreRole.Image)).count() == 0; boolean isKvmSnapshotOnlyInPrimaryStorage = snapshots.stream().noneMatch(s -> s.getStoreRole().equals(DataStoreRole.Image));
return snapshot.getHypervisorType() == Hypervisor.HypervisorType.KVM && dataStoreRole == DataStoreRole.Primary && isKvmSnapshotOnlyInPrimaryStorage; return snapshot.getHypervisorType() == Hypervisor.HypervisorType.KVM && dataStoreRole == DataStoreRole.Primary && isKvmSnapshotOnlyInPrimaryStorage;
} }
public DataStoreRole getDataStoreRole(Snapshot snapshot) { public DataStoreRole getDataStoreRole(Snapshot snapshot) {
SnapshotDataStoreVO snapshotStore = snapshotDataStoreDao.findOneBySnapshotAndDatastoreRole(snapshot.getId(), DataStoreRole.Primary); SnapshotDataStoreVO snapshotStore = snapshotDataStoreDao.findOneBySnapshotAndDatastoreRole(snapshot.getId(), DataStoreRole.Primary);
if (snapshotStore == null) { if (snapshotStore == null) {
return DataStoreRole.Image; return DataStoreRole.Image;
} }
long storagePoolId = snapshotStore.getDataStoreId(); long storagePoolId = snapshotStore.getDataStoreId();
StoragePoolVO storagePoolVO = primaryDataStoreDao.findById(storagePoolId); StoragePoolVO storagePoolVO = primaryDataStoreDao.findById(storagePoolId);
if ((storagePoolTypesToValidateWithBackupSnapshotAfterTakingSnapshot.contains(storagePoolVO.getPoolType()) || snapshot.getHypervisorType() == HypervisorType.KVM) if ((storagePoolTypesToValidateWithBackupSnapshotAfterTakingSnapshot.contains(storagePoolVO.getPoolType()) || snapshot.getHypervisorType() == HypervisorType.KVM)
&& !backupSnapshotAfterTakingSnapshot) { && !backupSnapshotAfterTakingSnapshot) {
@ -237,13 +240,11 @@ public class SnapshotHelper {
} }
DataStore dataStore = dataStorageManager.getDataStore(storagePoolId, DataStoreRole.Primary); DataStore dataStore = dataStorageManager.getDataStore(storagePoolId, DataStoreRole.Primary);
if (dataStore == null) { if (dataStore == null) {
return DataStoreRole.Image; return DataStoreRole.Image;
} }
Map<String, String> mapCapabilities = dataStore.getDriver().getCapabilities(); Map<String, String> mapCapabilities = dataStore.getDriver().getCapabilities();
if (MapUtils.isNotEmpty(mapCapabilities) && BooleanUtils.toBoolean(mapCapabilities.get(DataStoreCapabilities.STORAGE_SYSTEM_SNAPSHOT.toString()))) { if (MapUtils.isNotEmpty(mapCapabilities) && BooleanUtils.toBoolean(mapCapabilities.get(DataStoreCapabilities.STORAGE_SYSTEM_SNAPSHOT.toString()))) {
return DataStoreRole.Primary; return DataStoreRole.Primary;
} }
@ -255,11 +256,13 @@ public class SnapshotHelper {
if (zoneId == null) { if (zoneId == null) {
getDataStoreRole(snapshot); getDataStoreRole(snapshot);
} }
List<SnapshotJoinVO> snapshots = snapshotJoinDao.listBySnapshotIdAndZoneId(zoneId, snapshot.getId()); List<SnapshotJoinVO> snapshots = snapshotJoinDao.listBySnapshotIdAndZoneId(zoneId, snapshot.getId());
boolean snapshotOnPrimary = snapshots.stream().anyMatch(s -> s.getStoreRole().equals(DataStoreRole.Primary)); boolean snapshotOnPrimary = snapshots.stream().anyMatch(s -> s.getStoreRole().equals(DataStoreRole.Primary));
if (snapshotOnPrimary) { if (snapshotOnPrimary) {
return DataStoreRole.Primary; return DataStoreRole.Primary;
} }
return DataStoreRole.Image; return DataStoreRole.Image;
} }
/** /**