From d28366eb5037a12e0f53eda568c9f392947928f1 Mon Sep 17 00:00:00 2001 From: Slavka Peleva Date: Wed, 18 Oct 2023 10:33:59 +0300 Subject: [PATCH] Create snapshot from VM snapshot for NFS/Local storage --- .../main/java/com/cloud/storage/Snapshot.java | 2 +- .../motion/AncientDataMotionStrategy.java | 18 ++++++++- .../kvm/storage/KVMStorageProcessor.java | 40 ++++++++++++------- .../storage/snapshot/SnapshotManagerImpl.java | 40 +++++++++++++++++-- 4 files changed, 78 insertions(+), 22 deletions(-) diff --git a/api/src/main/java/com/cloud/storage/Snapshot.java b/api/src/main/java/com/cloud/storage/Snapshot.java index 5b25843f48b0..fc919e442b2e 100644 --- a/api/src/main/java/com/cloud/storage/Snapshot.java +++ b/api/src/main/java/com/cloud/storage/Snapshot.java @@ -26,7 +26,7 @@ public interface Snapshot extends ControlledEntity, Identity, InternalIdentity, StateObject { public enum Type { - MANUAL, RECURRING, TEMPLATE, HOURLY, DAILY, WEEKLY, MONTHLY, GROUP; + MANUAL, RECURRING, TEMPLATE, HOURLY, DAILY, WEEKLY, MONTHLY, GROUP, FROM_GROUP; private int max = 8; public void setMax(int max) { diff --git a/engine/storage/datamotion/src/main/java/org/apache/cloudstack/storage/motion/AncientDataMotionStrategy.java b/engine/storage/datamotion/src/main/java/org/apache/cloudstack/storage/motion/AncientDataMotionStrategy.java index 1d463cad7eac..e450addb2617 100644 --- a/engine/storage/datamotion/src/main/java/org/apache/cloudstack/storage/motion/AncientDataMotionStrategy.java +++ b/engine/storage/datamotion/src/main/java/org/apache/cloudstack/storage/motion/AncientDataMotionStrategy.java @@ -66,10 +66,13 @@ import com.cloud.host.Host; import com.cloud.hypervisor.Hypervisor; import com.cloud.storage.DataStoreRole; +import com.cloud.storage.Snapshot.Type; +import com.cloud.storage.SnapshotVO; import com.cloud.storage.StorageManager; import com.cloud.storage.Storage.StoragePoolType; import com.cloud.storage.StoragePool; import com.cloud.storage.VolumeVO; +import com.cloud.storage.dao.SnapshotDao; import com.cloud.storage.dao.VolumeDao; import com.cloud.utils.NumbersUtil; import com.cloud.utils.db.DB; @@ -97,6 +100,8 @@ public class AncientDataMotionStrategy implements DataMotionStrategy { @Inject StorageManager storageManager; + @Inject + SnapshotDao snapshotDao; @Override public StrategyPriority canHandle(DataObject srcData, DataObject destData) { @@ -583,8 +588,8 @@ protected Answer copySnapshot(DataObject srcData, DataObject destData) { fullSnapshot = snapshotFullBackup; } Map options = new HashMap(); - options.put("fullSnapshot", fullSnapshot.toString()); - options.put(SnapshotInfo.BackupSnapshotAfterTakingSnapshot.key(), String.valueOf(SnapshotInfo.BackupSnapshotAfterTakingSnapshot.value())); + + addCommandOptions(snapshotInfo, fullSnapshot, options); boolean encryptionRequired = anyVolumeRequiresEncryption(srcData, destData); Answer answer = null; @@ -631,6 +636,15 @@ protected Answer copySnapshot(DataObject srcData, DataObject destData) { } + private void addCommandOptions(SnapshotInfo snapshotInfo, Boolean fullSnapshot, Map options) { + SnapshotVO snap = snapshotDao.findById(snapshotInfo.getSnapshotId()); + if (snap != null && Type.FROM_GROUP.name().equals(snap.getTypeDescription())) { + options.put("typeDescription", snap.getTypeDescription()); + } + options.put("fullSnapshot", fullSnapshot.toString()); + options.put(SnapshotInfo.BackupSnapshotAfterTakingSnapshot.key(), String.valueOf(SnapshotInfo.BackupSnapshotAfterTakingSnapshot.value())); + } + @Override public void copyAsync(Map volumeMap, VirtualMachineTO vmTo, Host srcHost, Host destHost, AsyncCompletionCallback callback) { CopyCommandResult result = new CopyCommandResult(null, null); 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 ae0fa637bf07..f7ec09ca50f4 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 @@ -1014,9 +1014,9 @@ public Answer backupSnapshot(final CopyCommand cmd) { command.add("-b", isCreatedFromVmSnapshot ? snapshotDisk.getPath() : snapshot.getPath()); command.add(NAME_OPTION, snapshotName); command.add("-p", snapshotDestPath); - if (isCreatedFromVmSnapshot) { - descName = UUID.randomUUID().toString(); - } + + descName = UUID.randomUUID().toString(); + command.add("-t", descName); final String result = command.execute(); if (result != null) { @@ -1041,18 +1041,7 @@ public Answer backupSnapshot(final CopyCommand cmd) { if (isCreatedFromVmSnapshot) { s_logger.debug("Ignoring removal of vm snapshot on primary as this snapshot is created from vm snapshot"); } else if (primaryPool.getType() != StoragePoolType.RBD) { - String snapshotPath = snapshot.getPath(); - String backupSnapshotAfterTakingSnapshot = cmd.getOptions() == null ? null : cmd.getOptions().get(SnapshotInfo.BackupSnapshotAfterTakingSnapshot.key()); - - if (backupSnapshotAfterTakingSnapshot == null || BooleanUtils.toBoolean(backupSnapshotAfterTakingSnapshot)) { - try { - Files.deleteIfExists(Paths.get(snapshotPath)); - } catch (IOException ex) { - s_logger.error(String.format("Failed to delete snapshot [%s] on primary storage [%s].", snapshotPath, primaryPool.getUuid()), ex); - } - } else { - s_logger.debug(String.format("This backup is temporary, not deleting snapshot [%s] on primary storage [%s]", snapshotPath, primaryPool.getUuid())); - } + deleteSnapshotOnPrimary(cmd, snapshot, primaryPool); } try { @@ -1064,6 +1053,27 @@ public Answer backupSnapshot(final CopyCommand cmd) { } } } + + private void deleteSnapshotOnPrimary(final CopyCommand cmd, final SnapshotObjectTO snapshot, + KVMStoragePool primaryPool) { + String snapshotPath = snapshot.getPath(); + String backupSnapshotAfterTakingSnapshot = null; + boolean deleteSnapshotOnPrimary = true; + if (cmd.getOptions() != null) { + backupSnapshotAfterTakingSnapshot = cmd.getOptions().get(SnapshotInfo.BackupSnapshotAfterTakingSnapshot.key()); + deleteSnapshotOnPrimary = cmd.getOptions().get("typeDescription") == null; + } + + if ((backupSnapshotAfterTakingSnapshot == null || BooleanUtils.toBoolean(backupSnapshotAfterTakingSnapshot)) && deleteSnapshotOnPrimary) { + try { + Files.deleteIfExists(Paths.get(snapshotPath)); + } catch (IOException ex) { + s_logger.error(String.format("Failed to delete snapshot [%s] on primary storage [%s].", snapshotPath, primaryPool.getUuid()), ex); + } + } else { + s_logger.debug(String.format("This backup is temporary, not deleting snapshot [%s] on primary storage [%s]", snapshotPath, primaryPool.getUuid())); + } + } protected synchronized void attachOrDetachISO(final Connect conn, final String vmName, String isoPath, final boolean isAttach, Map params) throws LibvirtException, InternalErrorException { DiskDef iso = new DiskDef(); diff --git a/server/src/main/java/com/cloud/storage/snapshot/SnapshotManagerImpl.java b/server/src/main/java/com/cloud/storage/snapshot/SnapshotManagerImpl.java index bd8811b2a157..aeb095b6ecfd 100755 --- a/server/src/main/java/com/cloud/storage/snapshot/SnapshotManagerImpl.java +++ b/server/src/main/java/com/cloud/storage/snapshot/SnapshotManagerImpl.java @@ -109,6 +109,7 @@ import com.cloud.storage.SnapshotVO; import com.cloud.storage.Storage; import com.cloud.storage.Storage.ImageFormat; +import com.cloud.storage.Storage.StoragePoolType; import com.cloud.storage.StorageManager; import com.cloud.storage.StoragePool; import com.cloud.storage.VMTemplateVO; @@ -150,8 +151,10 @@ import com.cloud.vm.VirtualMachine.State; import com.cloud.vm.dao.UserVmDao; import com.cloud.vm.snapshot.VMSnapshot; +import com.cloud.vm.snapshot.VMSnapshotDetailsVO; import com.cloud.vm.snapshot.VMSnapshotVO; import com.cloud.vm.snapshot.dao.VMSnapshotDao; +import com.cloud.vm.snapshot.dao.VMSnapshotDetailsDao; @Component public class SnapshotManagerImpl extends MutualExclusiveIdsManagerBase implements SnapshotManager, SnapshotApiService, Configurable { @@ -221,6 +224,10 @@ public class SnapshotManagerImpl extends MutualExclusiveIdsManagerBase implement protected SnapshotHelper snapshotHelper; @Inject DataCenterDao dataCenterDao; + @Inject + VMSnapshotDetailsDao vmSnapshotDetailsDao; + @Inject + SnapshotDataFactory snapshotDataFactory; private int _totalRetries; private int _pauseInterval; @@ -497,12 +504,12 @@ public Snapshot backupSnapshotFromVmSnapshot(Long snapshotId, Long vmId, Long vo SnapshotInfo snapshotInfo = this.snapshotFactory.getSnapshot(snapshotId, store); snapshotInfo = (SnapshotInfo)store.create(snapshotInfo); SnapshotDataStoreVO snapshotOnPrimaryStore = this._snapshotStoreDao.findByStoreSnapshot(store.getRole(), store.getId(), snapshot.getId()); - snapshotOnPrimaryStore.setState(ObjectInDataStoreStateMachine.State.Ready); - snapshotOnPrimaryStore.setInstallPath(vmSnapshot.getName()); - _snapshotStoreDao.update(snapshotOnPrimaryStore.getId(), snapshotOnPrimaryStore); + + StoragePoolVO storagePool = _storagePoolDao.findById(store.getId()); + updateSnapshotInfo(volumeId, vmSnapshotId, vmSnapshot, snapshot, snapshotOnPrimaryStore, storagePool); + snapshot.setState(Snapshot.State.CreatedOnPrimary); _snapshotDao.update(snapshot.getId(), snapshot); - snapshotInfo = this.snapshotFactory.getSnapshot(snapshotId, store); Long snapshotOwnerId = vm.getAccountId(); @@ -519,10 +526,35 @@ public Snapshot backupSnapshotFromVmSnapshot(Long snapshotId, Long vmId, Long vo _resourceLimitMgr.decrementResourceCount(snapshotOwnerId, ResourceType.snapshot); _resourceLimitMgr.decrementResourceCount(snapshotOwnerId, ResourceType.secondary_storage, new Long(volume.getSize())); throw new CloudRuntimeException("Failed to backup snapshot from vm snapshot", e); + } finally { + if (snapshotOnPrimaryStore != null) { + _snapshotStoreDao.remove(snapshotOnPrimaryStore.getId()); + } } return snapshotInfo; } + private void updateSnapshotInfo(Long volumeId, Long vmSnapshotId, VMSnapshotVO vmSnapshot, SnapshotVO snapshot, + SnapshotDataStoreVO snapshotOnPrimaryStore, StoragePoolVO storagePool) { + if ((storagePool.getPoolType() == StoragePoolType.NetworkFilesystem || storagePool.getPoolType() == StoragePoolType.Filesystem) && vmSnapshot.getType() == VMSnapshot.Type.Disk) { + List vmSnapshotDetails = vmSnapshotDetailsDao.findDetails(vmSnapshotId, "kvmStorageSnapshot"); + for (VMSnapshotDetailsVO vmSnapshotDetailsVO : vmSnapshotDetails) { + SnapshotInfo sInfo = snapshotDataFactory.getSnapshot(Long.parseLong(vmSnapshotDetailsVO.getValue()), DataStoreRole.Primary); + if (sInfo.getVolumeId() == volumeId) { + snapshotOnPrimaryStore.setState(ObjectInDataStoreStateMachine.State.Ready); + snapshotOnPrimaryStore.setInstallPath(sInfo.getPath()); + _snapshotStoreDao.update(snapshotOnPrimaryStore.getId(), snapshotOnPrimaryStore); + snapshot.setTypeDescription(Type.FROM_GROUP.name()); + snapshot.setSnapshotType((short)Type.FROM_GROUP.ordinal()); + } + } + } else { + snapshotOnPrimaryStore.setState(ObjectInDataStoreStateMachine.State.Ready); + snapshotOnPrimaryStore.setInstallPath(vmSnapshot.getName()); + _snapshotStoreDao.update(snapshotOnPrimaryStore.getId(), snapshotOnPrimaryStore); + } + } + @Override public SnapshotVO getParentSnapshot(VolumeInfo volume) { long preId = _snapshotDao.getLastSnapshot(volume.getId(), DataStoreRole.Primary);