package org.ovirt.engine.core.bll.snapshots; import java.util.HashSet; import java.util.Set; import javax.inject.Inject; import org.apache.commons.collections.CollectionUtils; import org.ovirt.engine.core.bll.InternalCommandAttribute; import org.ovirt.engine.core.bll.context.CommandContext; import org.ovirt.engine.core.bll.storage.disk.image.ImagesHandler; import org.ovirt.engine.core.bll.storage.domain.PostDeleteActionHandler; import org.ovirt.engine.core.common.VdcObjectType; import org.ovirt.engine.core.common.action.ImagesContainterParametersBase; import org.ovirt.engine.core.common.asynctasks.AsyncTaskType; import org.ovirt.engine.core.common.businessentities.Snapshot; import org.ovirt.engine.core.common.businessentities.VmBlockJobType; import org.ovirt.engine.core.common.businessentities.storage.DiskImage; import org.ovirt.engine.core.common.businessentities.storage.ImageStatus; import org.ovirt.engine.core.common.job.StepEnum; import org.ovirt.engine.core.common.vdscommands.MergeSnapshotsVDSCommandParameters; import org.ovirt.engine.core.common.vdscommands.VDSCommandType; import org.ovirt.engine.core.common.vdscommands.VDSReturnValue; import org.ovirt.engine.core.compat.Guid; import org.ovirt.engine.core.dal.job.ExecutionMessageDirector; import org.ovirt.engine.core.dao.DiskImageDao; import org.ovirt.engine.core.dao.ImageDao; import org.ovirt.engine.core.dao.SnapshotDao; import org.ovirt.engine.core.dao.StorageDomainDao; import org.ovirt.engine.core.utils.transaction.NoOpTransactionCompletionListener; import org.ovirt.engine.core.utils.transaction.TransactionSupport; @InternalCommandAttribute public class RemoveSnapshotSingleDiskCommand<T extends ImagesContainterParametersBase> extends RemoveSnapshotSingleDiskCommandBase<T> { @Inject private PostDeleteActionHandler postDeleteActionHandler; @Inject private SnapshotDao snapshotDao; @Inject private ImageDao imageDao; @Inject private StorageDomainDao storageDomainDao; @Inject private DiskImageDao diskImageDao; public RemoveSnapshotSingleDiskCommand(T parameters, CommandContext cmdContext) { super(parameters, cmdContext); } @Override protected void executeCommand() { registerRollbackHandler(new CustomTransactionCompletionListener()); Guid storagePoolId = (getDiskImage().getStoragePoolId() != null) ? getDiskImage().getStoragePoolId() : Guid.Empty; Guid storageDomainId = !CollectionUtils.isEmpty(getDiskImage().getStorageIds()) ? getDiskImage().getStorageIds().get(0) : Guid.Empty; Guid taskId = persistAsyncTaskPlaceHolder(getParameters().getParentCommand()); VDSReturnValue vdsReturnValue = mergeSnapshots(storagePoolId, storageDomainId); if (vdsReturnValue != null && vdsReturnValue.getCreationInfo() != null) { getTaskIdList().add(createTask(taskId, vdsReturnValue, storageDomainId)); setSucceeded(vdsReturnValue.getSucceeded()); } else { setSucceeded(false); } } protected VDSReturnValue mergeSnapshots(Guid storagePoolId, Guid storageDomainId) { MergeSnapshotsVDSCommandParameters params = new MergeSnapshotsVDSCommandParameters(storagePoolId, storageDomainId, getVmId(), getDiskImage().getId(), getDiskImage().getImageId(), getDestinationDiskImage().getImageId(), getDiskImage().isWipeAfterDelete(), storageDomainDao.get(storageDomainId).isDiscardAfterDelete()); return runVdsCommand(VDSCommandType.MergeSnapshots, postDeleteActionHandler.fixParameters(params)); } protected Guid createTask(Guid taskId, VDSReturnValue vdsReturnValue, Guid storageDomainId) { String message = ExecutionMessageDirector.resolveStepMessage(StepEnum.MERGE_SNAPSHOTS, getJobMessageProperties()); return super.createTask(taskId, vdsReturnValue.getCreationInfo(), getParameters().getParentCommand(), message, VdcObjectType.Storage, storageDomainId); } @Override protected AsyncTaskType getTaskType() { return AsyncTaskType.mergeSnapshots; } @Override protected void endSuccessfully() { // NOTE: The removal of the images from DB is done here // assuming that there might be situation (related to // tasks failures) in which we will want to preserve the // original state (before the merge-attempt). if (getDestinationDiskImage() != null) { Set<Guid> imagesToUpdate = new HashSet<>(); DiskImage curr = getDestinationDiskImage(); while (!curr.getParentId().equals(getDiskImage().getParentId())) { curr = diskImageDao.getSnapshotById(curr.getParentId()); imagesToUpdate.add(curr.getImageId()); } syncDbRecords(VmBlockJobType.PULL, getImageInfoFromVdsm(getDestinationDiskImage()), imagesToUpdate, true); } if (getParameters().getVmSnapshotId() != null) { lockVmSnapshotsWithWait(getVm()); Snapshot snapshot = snapshotDao.get(getParameters().getVmSnapshotId()); Snapshot snapshotWithoutImage = ImagesHandler.prepareSnapshotConfigWithoutImageSingleImage(snapshot, getParameters().getImageId(), ovfManager); snapshotDao.update(snapshotWithoutImage); if (getSnapshotsEngineLock() != null) { lockManager.releaseLock(getSnapshotsEngineLock()); } } setSucceeded(true); } private class CustomTransactionCompletionListener extends NoOpTransactionCompletionListener { @Override public void onRollback() { TransactionSupport.executeInNewTransaction(() -> { if (!getParameters().isLeaveLocked()) { DiskImage diskImage = getDestinationDiskImage(); if (diskImage != null) { imageDao.updateStatus(diskImage.getImage().getId(), ImageStatus.OK); } unLockImage(); } return null; }); } } }