package org.ovirt.engine.core.bll.snapshots;
import static org.ovirt.engine.core.bll.storage.disk.image.DisksFilter.ONLY_ACTIVE;
import static org.ovirt.engine.core.bll.storage.disk.image.DisksFilter.ONLY_NOT_SHAREABLE;
import static org.ovirt.engine.core.bll.storage.disk.image.DisksFilter.ONLY_SNAPABLE;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import javax.inject.Inject;
import org.ovirt.engine.core.bll.Backend;
import org.ovirt.engine.core.bll.ConcurrentChildCommandsExecutionCallback;
import org.ovirt.engine.core.bll.DisableInPrepareMode;
import org.ovirt.engine.core.bll.LockMessage;
import org.ovirt.engine.core.bll.LockMessagesMatchUtil;
import org.ovirt.engine.core.bll.NonTransactiveCommandAttribute;
import org.ovirt.engine.core.bll.VmCommand;
import org.ovirt.engine.core.bll.context.CommandContext;
import org.ovirt.engine.core.bll.job.ExecutionHandler;
import org.ovirt.engine.core.bll.memory.LiveSnapshotMemoryImageBuilder;
import org.ovirt.engine.core.bll.memory.MemoryImageBuilder;
import org.ovirt.engine.core.bll.memory.MemoryStorageHandler;
import org.ovirt.engine.core.bll.memory.MemoryUtils;
import org.ovirt.engine.core.bll.memory.NullableMemoryImageBuilder;
import org.ovirt.engine.core.bll.memory.StatelessSnapshotMemoryImageBuilder;
import org.ovirt.engine.core.bll.quota.QuotaConsumptionParameter;
import org.ovirt.engine.core.bll.quota.QuotaStorageConsumptionParameter;
import org.ovirt.engine.core.bll.quota.QuotaStorageDependent;
import org.ovirt.engine.core.bll.storage.disk.image.DisksFilter;
import org.ovirt.engine.core.bll.storage.disk.image.ImagesHandler;
import org.ovirt.engine.core.bll.tasks.CommandCoordinatorUtil;
import org.ovirt.engine.core.bll.tasks.interfaces.CommandCallback;
import org.ovirt.engine.core.bll.utils.VmOverheadCalculator;
import org.ovirt.engine.core.bll.validator.VmValidator;
import org.ovirt.engine.core.bll.validator.storage.CinderDisksValidator;
import org.ovirt.engine.core.bll.validator.storage.DiskImagesValidator;
import org.ovirt.engine.core.bll.validator.storage.MultipleStorageDomainsValidator;
import org.ovirt.engine.core.bll.validator.storage.StoragePoolValidator;
import org.ovirt.engine.core.common.AuditLogType;
import org.ovirt.engine.core.common.FeatureSupported;
import org.ovirt.engine.core.common.VdcObjectType;
import org.ovirt.engine.core.common.action.CreateAllSnapshotsFromVmParameters;
import org.ovirt.engine.core.common.action.CreateCinderSnapshotParameters;
import org.ovirt.engine.core.common.action.ImagesActionsParametersBase;
import org.ovirt.engine.core.common.action.LockProperties;
import org.ovirt.engine.core.common.action.LockProperties.Scope;
import org.ovirt.engine.core.common.action.RemoveMemoryVolumesParameters;
import org.ovirt.engine.core.common.action.VdcActionParametersBase;
import org.ovirt.engine.core.common.action.VdcActionType;
import org.ovirt.engine.core.common.action.VdcReturnValueBase;
import org.ovirt.engine.core.common.asynctasks.EntityInfo;
import org.ovirt.engine.core.common.businessentities.Snapshot;
import org.ovirt.engine.core.common.businessentities.Snapshot.SnapshotStatus;
import org.ovirt.engine.core.common.businessentities.Snapshot.SnapshotType;
import org.ovirt.engine.core.common.businessentities.StorageDomain;
import org.ovirt.engine.core.common.businessentities.VMStatus;
import org.ovirt.engine.core.common.businessentities.storage.CinderDisk;
import org.ovirt.engine.core.common.businessentities.storage.Disk;
import org.ovirt.engine.core.common.businessentities.storage.DiskImage;
import org.ovirt.engine.core.common.businessentities.storage.DiskStorageType;
import org.ovirt.engine.core.common.errors.EngineError;
import org.ovirt.engine.core.common.errors.EngineException;
import org.ovirt.engine.core.common.errors.EngineMessage;
import org.ovirt.engine.core.common.locks.LockingGroup;
import org.ovirt.engine.core.common.utils.Pair;
import org.ovirt.engine.core.common.validation.group.CreateEntity;
import org.ovirt.engine.core.common.vdscommands.SnapshotVDSCommandParameters;
import org.ovirt.engine.core.common.vdscommands.VDSCommandType;
import org.ovirt.engine.core.common.vdscommands.VDSReturnValue;
import org.ovirt.engine.core.common.vdscommands.VdsAndVmIDVDSParametersBase;
import org.ovirt.engine.core.compat.Guid;
import org.ovirt.engine.core.compat.TransactionScopeOption;
import org.ovirt.engine.core.dao.DiskDao;
import org.ovirt.engine.core.dao.ImageDao;
import org.ovirt.engine.core.dao.SnapshotDao;
import org.ovirt.engine.core.dao.VmStaticDao;
import org.ovirt.engine.core.utils.transaction.TransactionSupport;
@NonTransactiveCommandAttribute(forceCompensation = true)
@DisableInPrepareMode
public class CreateAllSnapshotsFromVmCommand<T extends CreateAllSnapshotsFromVmParameters> extends VmCommand<T> implements QuotaStorageDependent {
private List<DiskImage> cachedSelectedActiveDisks;
private List<DiskImage> cachedImagesDisks;
private Guid cachedStorageDomainId = Guid.Empty;
private String cachedSnapshotIsBeingTakenMessage;
private Guid newActiveSnapshotId = Guid.newGuid();
private MemoryImageBuilder memoryBuilder;
@Inject
private VmOverheadCalculator vmOverheadCalculator;
@Inject
private MemoryStorageHandler memoryStorageHandler;
@Inject
private VmStaticDao vmStaticDao;
@Inject
private DiskDao diskDao;
@Inject
private ImageDao imageDao;
@Inject
private SnapshotDao snapshotDao;
public CreateAllSnapshotsFromVmCommand(Guid commandId) {
super(commandId);
}
@Override
protected LockProperties applyLockProperties(LockProperties lockProperties) {
return lockProperties.withScope(Scope.Command);
}
public CreateAllSnapshotsFromVmCommand(T parameters, CommandContext commandContext) {
super(parameters, commandContext);
}
@Override
public void init() {
getParameters().setUseCinderCommandCallback(isCinderDisksExist());
getParameters().setEntityInfo(new EntityInfo(VdcObjectType.VM, getVmId()));
setSnapshotName(getParameters().getDescription());
setStoragePoolId(getVm() != null ? getVm().getStoragePoolId() : null);
}
@Override
public Map<String, String> getJobMessageProperties() {
if (jobProperties == null) {
jobProperties = super.getJobMessageProperties();
jobProperties.put(VdcObjectType.Snapshot.name().toLowerCase(), getParameters().getDescription());
}
return jobProperties;
}
protected List<DiskImage> getDiskImagesForVm() {
List<Disk> disks = diskDao.getAllForVm(getVmId());
List<DiskImage> allDisks = new ArrayList<>(getDiskImages(disks));
allDisks.addAll(ImagesHandler.getCinderLeafImages(disks));
return allDisks;
}
private List<DiskImage> getDiskImages(List<Disk> disks) {
if (cachedImagesDisks == null) {
cachedImagesDisks = DisksFilter.filterImageDisks(disks, ONLY_NOT_SHAREABLE,
ONLY_SNAPABLE, ONLY_ACTIVE);
}
return cachedImagesDisks;
}
/**
* Filter all allowed snapshot disks.
* @return list of disks to be snapshot.
*/
protected List<DiskImage> getDisksList() {
if (cachedSelectedActiveDisks == null) {
List<DiskImage> imagesAndCinderForVm = getDiskImagesForVm();
// Get disks from the specified parameters or according to the VM
if (getParameters().getDisks() == null) {
cachedSelectedActiveDisks = imagesAndCinderForVm;
}
else {
// Get selected images from 'DiskImagesForVm' to ensure disks entities integrity
// (i.e. only images' IDs and Cinders' IDs are relevant).
cachedSelectedActiveDisks = ImagesHandler.imagesIntersection(imagesAndCinderForVm, getParameters().getDisks());
}
}
return cachedSelectedActiveDisks;
}
protected List<DiskImage> getDisksListForChecks() {
List<DiskImage> disksListForChecks = getDisksList();
if (getParameters().getDiskIdsToIgnoreInChecks().isEmpty()) {
return disksListForChecks;
}
List<DiskImage> toReturn = new LinkedList<>();
for (DiskImage diskImage : disksListForChecks) {
if (!getParameters().getDiskIdsToIgnoreInChecks().contains(diskImage.getId())) {
toReturn.add(diskImage);
}
}
return toReturn;
}
private boolean validateStorage() {
List<DiskImage> vmDisksList = getDisksListForChecks();
vmDisksList = ImagesHandler.getDisksDummiesForStorageAllocations(vmDisksList);
List<DiskImage> allDisks = new ArrayList<>(vmDisksList);
List<DiskImage> memoryDisksList = null;
if (getParameters().isSaveMemory()) {
memoryDisksList = MemoryUtils.createDiskDummies(vmOverheadCalculator.getSnapshotMemorySizeInBytes(getVm()),
MemoryUtils.METADATA_SIZE_IN_BYTES);
if (Guid.Empty.equals(getStorageDomainIdForVmMemory(memoryDisksList))) {
return failValidation(EngineMessage.ACTION_TYPE_FAILED_NO_SUITABLE_DOMAIN_FOUND);
}
allDisks.addAll(memoryDisksList);
}
MultipleStorageDomainsValidator sdValidator = createMultipleStorageDomainsValidator(allDisks);
if (!validate(sdValidator.allDomainsExistAndActive())
|| !validate(sdValidator.allDomainsWithinThresholds())
|| !validateCinder()) {
return false;
}
if (memoryDisksList == null) { //no memory volumes
return validate(sdValidator.allDomainsHaveSpaceForNewDisks(vmDisksList));
}
return validate(sdValidator.allDomainsHaveSpaceForAllDisks(vmDisksList, memoryDisksList));
}
public boolean validateCinder() {
List<CinderDisk> cinderDisks = DisksFilter.filterCinderDisks(diskDao.getAllForVm(getVmId()));
if (!cinderDisks.isEmpty()) {
CinderDisksValidator cinderDisksValidator = getCinderDisksValidator(cinderDisks);
return validate(cinderDisksValidator.validateCinderDiskSnapshotsLimits());
}
return true;
}
protected CinderDisksValidator getCinderDisksValidator(List<CinderDisk> cinderDisks) {
return new CinderDisksValidator(cinderDisks);
}
protected MemoryImageBuilder getMemoryImageBuilder() {
if (memoryBuilder == null) {
memoryBuilder = createMemoryImageBuilder();
}
return memoryBuilder;
}
private void incrementVmGeneration() {
vmStaticDao.incrementDbGeneration(getVm().getId());
}
@Override
protected void executeVmCommand() {
Guid createdSnapshotId = updateActiveSnapshotId();
setActionReturnValue(createdSnapshotId);
MemoryImageBuilder memoryImageBuilder = getMemoryImageBuilder();
freezeVm();
createSnapshotsForDisks();
memoryImageBuilder.build();
addSnapshotToDB(createdSnapshotId, memoryImageBuilder);
fastForwardDisksToActiveSnapshot();
setSucceeded(true);
}
private Guid updateActiveSnapshotId() {
final Snapshot activeSnapshot = snapshotDao.get(getVmId(), SnapshotType.ACTIVE);
final Guid activeSnapshotId = activeSnapshot.getId();
TransactionSupport.executeInScope(TransactionScopeOption.Required, () -> {
getCompensationContext().snapshotEntity(activeSnapshot);
snapshotDao.updateId(activeSnapshotId, newActiveSnapshotId);
activeSnapshot.setId(newActiveSnapshotId);
getCompensationContext().snapshotNewEntity(activeSnapshot);
getCompensationContext().stateChanged();
return null;
});
return activeSnapshotId;
}
public Guid getStorageDomainIdForVmMemory(List<DiskImage> memoryDisksList) {
if (cachedStorageDomainId.equals(Guid.Empty) && getVm() != null) {
StorageDomain storageDomain = memoryStorageHandler.findStorageDomainForMemory(
getVm().getStoragePoolId(), memoryDisksList, getDisksList(), getVm());
if (storageDomain != null) {
cachedStorageDomainId = storageDomain.getId();
}
}
return cachedStorageDomainId;
}
private MemoryImageBuilder createMemoryImageBuilder() {
if (!isMemorySnapshotSupported()) {
return new NullableMemoryImageBuilder();
}
if (getParameters().getSnapshotType() == SnapshotType.STATELESS) {
return new StatelessSnapshotMemoryImageBuilder(getVm());
}
if (getParameters().isSaveMemory() && isLiveSnapshotApplicable()) {
return new LiveSnapshotMemoryImageBuilder(getVm(), cachedStorageDomainId, getStoragePool(),
this, vmOverheadCalculator);
}
return new NullableMemoryImageBuilder();
}
private Snapshot addSnapshotToDB(Guid snapshotId, MemoryImageBuilder memoryImageBuilder) {
// Reset cachedSelectedActiveDisks so new Cinder volumes can be fetched when calling getDisksList.
cachedSelectedActiveDisks = null;
return getSnapshotsManager().addSnapshot(snapshotId,
getParameters().getDescription(),
SnapshotStatus.LOCKED,
getParameters().getSnapshotType(),
getVm(),
true,
memoryImageBuilder.getVolumeStringRepresentation(),
getDisksList(),
getCompensationContext());
}
private void createSnapshotsForDisks() {
for (DiskImage disk : getDisksList()) {
if (disk.getDiskStorageType() == DiskStorageType.CINDER) {
CreateCinderSnapshotParameters params = buildChildCommandParameters(disk);
params.setQuotaId(disk.getQuotaId());
Future<VdcReturnValueBase> future = CommandCoordinatorUtil.executeAsyncCommand(
VdcActionType.CreateCinderSnapshot,
params,
cloneContext().withoutCompensationContext().withoutLock());
try {
VdcReturnValueBase vdcReturnValueBase = future.get();
if (!vdcReturnValueBase.getSucceeded()) {
log.error("Error creating snapshot for Cinder disk '{}'", disk.getDiskAlias());
throw new EngineException(EngineError.CINDER_ERROR, "Failed to create snapshot!");
}
} catch (InterruptedException | ExecutionException e) {
log.error("Error creating snapshot for Cinder disk '{}': {}", disk.getDiskAlias(), e.getMessage());
throw new EngineException(EngineError.CINDER_ERROR, "Failed to create snapshot!");
}
continue;
}
VdcReturnValueBase vdcReturnValue = Backend.getInstance().runInternalAction(
VdcActionType.CreateSnapshot,
buildCreateSnapshotParameters(disk),
ExecutionHandler.createDefaultContextForTasks(getContext()));
if (vdcReturnValue.getSucceeded()) {
getTaskIdList().addAll(vdcReturnValue.getInternalVdsmTaskIdList());
} else {
throw new EngineException(vdcReturnValue.getFault().getError(),
"Failed to create snapshot!");
}
}
}
private CreateCinderSnapshotParameters buildChildCommandParameters(DiskImage cinderDisk) {
CreateCinderSnapshotParameters createParams =
new CreateCinderSnapshotParameters(((CinderDisk) diskDao.get(cinderDisk.getId())).getImageId());
createParams.setVmSnapshotId(newActiveSnapshotId);
createParams.setStorageDomainId(cinderDisk.getStorageIds().get(0));
createParams.setDescription(getParameters().getDescription());
createParams.setSnapshotType(getParameters().getSnapshotType());
createParams.setParentCommand(getActionType());
createParams.setParentParameters(getParameters());
return createParams;
}
private void fastForwardDisksToActiveSnapshot() {
if (getParameters().getDisks() != null) {
// Remove disks included in snapshot
List<DiskImage> diskImagesToUpdate = ImagesHandler.imagesSubtract(getDiskImagesForVm(), getParameters().getDisks());
// Fast-forward non-included disks to active snapshot
for (DiskImage diskImage : diskImagesToUpdate) {
imageDao.updateImageVmSnapshotId(diskImage.getImageId(), newActiveSnapshotId);
}
}
}
private ImagesActionsParametersBase buildCreateSnapshotParameters(DiskImage image) {
ImagesActionsParametersBase result = new ImagesActionsParametersBase(image.getImageId());
result.setDescription(getParameters().getDescription());
result.setSessionId(getParameters().getSessionId());
result.setQuotaId(image.getQuotaId());
result.setDiskProfileId(image.getDiskProfileId());
result.setVmSnapshotId(newActiveSnapshotId);
result.setEntityInfo(getParameters().getEntityInfo());
result.setParentCommand(getActionType());
result.setParentParameters(getParameters());
if (getParameters().getDiskIdsToIgnoreInChecks().contains(image.getId())) {
result.setLeaveLocked(true);
}
return result;
}
private boolean shouldPerformLiveSnapshot(Snapshot snapshot) {
return isLiveSnapshotApplicable() && snapshot != null &&
(snapshotWithMemory(snapshot) || !getDisksList().isEmpty());
}
private boolean snapshotWithMemory(Snapshot snapshot) {
return getParameters().isSaveMemory() && snapshot.containsMemory();
}
@Override
protected void endVmCommand() {
Snapshot createdSnapshot = snapshotDao.get(getVmId(), getParameters().getSnapshotType(), SnapshotStatus.LOCKED);
// if the snapshot was not created in the DB
// the command should also be handled as a failure
boolean taskGroupSucceeded = createdSnapshot != null && getParameters().getTaskGroupSuccess();
boolean liveSnapshotRequired = shouldPerformLiveSnapshot(createdSnapshot);
boolean liveSnapshotSucceeded = false;
if (taskGroupSucceeded) {
snapshotDao.updateStatus(createdSnapshot.getId(), SnapshotStatus.OK);
if (liveSnapshotRequired) {
liveSnapshotSucceeded = performLiveSnapshot(createdSnapshot);
} else {
// If the created snapshot contains memory, remove the memory volumes as
// they are not going to be in use since no live snapshot is created
if (snapshotWithMemory(createdSnapshot)) {
logMemorySavingFailed();
snapshotDao.removeMemoryFromSnapshot(createdSnapshot.getId());
removeMemoryVolumesOfSnapshot(createdSnapshot);
}
}
} else {
if (createdSnapshot != null) {
revertToActiveSnapshot(createdSnapshot.getId());
// If the removed snapshot contained memory, remove the memory volumes
// Note that the memory volumes might not have been created
if (snapshotWithMemory(createdSnapshot)) {
removeMemoryVolumesOfSnapshot(createdSnapshot);
}
} else {
log.warn("No snapshot was created for VM '{}' which is in LOCKED status", getVmId());
}
}
incrementVmGeneration();
thawVm();
endActionOnDisks();
setSucceeded(taskGroupSucceeded && (!liveSnapshotRequired || liveSnapshotSucceeded));
getReturnValue().setEndActionTryAgain(false);
}
private void logMemorySavingFailed() {
addCustomValue("SnapshotName", getSnapshotName());
addCustomValue("VmName", getVmName());
auditLogDirector.log(this, AuditLogType.USER_CREATE_LIVE_SNAPSHOT_NO_MEMORY_FAILURE);
}
private void removeMemoryVolumesOfSnapshot(Snapshot snapshot) {
VdcReturnValueBase retVal = runInternalAction(
VdcActionType.RemoveMemoryVolumes,
new RemoveMemoryVolumesParameters(snapshot.getMemoryVolume(), getVmId()), cloneContextAndDetachFromParent());
if (!retVal.getSucceeded()) {
log.error("Failed to remove memory volumes of snapshot '{}' ({})",
snapshot.getDescription(), snapshot.getId());
}
}
protected boolean isLiveSnapshotApplicable() {
return getParameters().getParentCommand() != VdcActionType.RunVm && getVm() != null
&& (getVm().isRunning() || getVm().getStatus() == VMStatus.Paused) && getVm().getRunOnVds() != null;
}
@Override
protected List<VdcActionParametersBase> getParametersForChildCommand() {
List<VdcActionParametersBase> sortedList = getParameters().getImagesParameters();
Collections.sort(sortedList, new Comparator<VdcActionParametersBase>() {
@Override
public int compare(VdcActionParametersBase o1, VdcActionParametersBase o2) {
if (o1 instanceof ImagesActionsParametersBase && o2 instanceof ImagesActionsParametersBase) {
return ((ImagesActionsParametersBase) o1).getDestinationImageId()
.compareTo(((ImagesActionsParametersBase) o2).getDestinationImageId());
}
return 0;
}
});
return sortedList;
}
/**
* Perform live snapshot on the host that the VM is running on. If the snapshot fails, and the error is
* unrecoverable then the {@link CreateAllSnapshotsFromVmParameters#getTaskGroupSuccess()} will return false.
*
* @param snapshot
* Snapshot to revert to being active, in case of rollback.
*/
protected boolean performLiveSnapshot(final Snapshot snapshot) {
try {
TransactionSupport.executeInScope(TransactionScopeOption.Suppress, () -> {
runVdsCommand(VDSCommandType.Snapshot, buildLiveSnapshotParameters(snapshot));
return null;
});
} catch (EngineException e) {
handleVdsLiveSnapshotFailure(e);
return false;
}
return true;
}
private SnapshotVDSCommandParameters buildLiveSnapshotParameters(Snapshot snapshot) {
List<Disk> pluggedDisksForVm = diskDao.getAllForVm(getVm().getId(), true);
List<DiskImage> filteredPluggedDisksForVm = DisksFilter.filterImageDisks(pluggedDisksForVm,
ONLY_SNAPABLE, ONLY_ACTIVE);
// 'filteredPluggedDisks' should contain only disks from 'getDisksList()' that are plugged to the VM.
List<DiskImage> filteredPluggedDisks = ImagesHandler.imagesIntersection(filteredPluggedDisksForVm, getDisksList());
SnapshotVDSCommandParameters parameters = new SnapshotVDSCommandParameters(
getVm().getRunOnVds(), getVm().getId(), filteredPluggedDisks);
if (isMemorySnapshotSupported()) {
parameters.setMemoryVolume(snapshot.getMemoryVolume());
}
parameters.setVmFrozen(shouldFreezeOrThawVm());
return parameters;
}
/**
* Check if Memory Snapshot is supported
*/
private boolean isMemorySnapshotSupported() {
return FeatureSupported.isMemorySnapshotSupportedByArchitecture(
getVm().getClusterArch(), getVm().getCompatibilityVersion());
}
/**
* Freezing the VM is needed for live snapshot with Cinder disks.
*/
private void freezeVm() {
if (!shouldFreezeOrThawVm()) {
return;
}
VDSReturnValue returnValue;
try {
auditLogDirector.log(this, AuditLogType.FREEZE_VM_INITIATED);
returnValue = runVdsCommand(VDSCommandType.Freeze, new VdsAndVmIDVDSParametersBase(
getVds().getId(), getVmId()));
} catch (EngineException e) {
handleFreezeVmFailure(e);
return;
}
if (returnValue.getSucceeded()) {
auditLogDirector.log(this, AuditLogType.FREEZE_VM_SUCCESS);
} else {
handleFreezeVmFailure(new EngineException(EngineError.freezeErr));
}
}
/**
* VM thaw is needed if the VM was frozen.
*/
private void thawVm() {
if (!shouldFreezeOrThawVm()) {
return;
}
VDSReturnValue returnValue;
try {
returnValue = runVdsCommand(VDSCommandType.Thaw, new VdsAndVmIDVDSParametersBase(
getVds().getId(), getVmId()));
} catch (EngineException e) {
handleThawVmFailure(e);
return;
}
if (!returnValue.getSucceeded()) {
handleThawVmFailure(new EngineException(EngineError.thawErr));
}
}
private boolean shouldFreezeOrThawVm() {
return isLiveSnapshotApplicable() && isCinderDisksExist();
}
private boolean isCinderDisksExist() {
return !DisksFilter.filterCinderDisks(getDisksList()).isEmpty();
}
private void handleVmFailure(EngineException e, AuditLogType auditLogType, String warnMessage) {
log.warn(warnMessage, e.getMessage());
log.debug("Exception", e);
addCustomValue("SnapshotName", getSnapshotName());
addCustomValue("VmName", getVmName());
updateCallStackFromThrowable(e);
auditLogDirector.log(this, auditLogType);
}
private void handleVdsLiveSnapshotFailure(EngineException e) {
handleVmFailure(e, AuditLogType.USER_CREATE_LIVE_SNAPSHOT_FINISHED_FAILURE,
"Could not perform live snapshot due to error, VM will still be configured to the new created"
+ " snapshot: {}");
}
private void handleFreezeVmFailure(EngineException e) {
handleVmFailure(e, AuditLogType.FAILED_TO_FREEZE_VM,
"Could not freeze VM guest filesystems due to an error: {}");
}
private void handleThawVmFailure(EngineException e) {
handleVmFailure(e, AuditLogType.FAILED_TO_THAW_VM,
"Could not thaw VM guest filesystems due to an error: {}");
}
/**
* Return the given snapshot ID's snapshot to be the active snapshot. The snapshot with the given ID is removed
* in the process.
*
* @param createdSnapshotId
* The snapshot ID to return to being active.
*/
protected void revertToActiveSnapshot(Guid createdSnapshotId) {
if (createdSnapshotId != null) {
snapshotDao.remove(createdSnapshotId);
snapshotDao.updateId(snapshotDao.getId(getVmId(), SnapshotType.ACTIVE), createdSnapshotId);
}
setSucceeded(false);
}
@Override
public AuditLogType getAuditLogTypeValue() {
switch (getActionState()) {
case EXECUTE:
return getSucceeded() ? AuditLogType.USER_CREATE_SNAPSHOT : AuditLogType.USER_FAILED_CREATE_SNAPSHOT;
case END_SUCCESS:
return getSucceeded() ? AuditLogType.USER_CREATE_SNAPSHOT_FINISHED_SUCCESS
: AuditLogType.USER_CREATE_SNAPSHOT_FINISHED_FAILURE;
default:
return AuditLogType.USER_CREATE_SNAPSHOT_FINISHED_FAILURE;
}
}
@Override
protected boolean validate() {
if (getVm() == null) {
addValidationMessage(EngineMessage.ACTION_TYPE_FAILED_VM_NOT_FOUND);
return false;
}
if (!canRunActionOnNonManagedVm()) {
return false;
}
if (!isSpecifiedDisksExist(getParameters().getDisks())) {
return false;
}
// Initialize validators.
VmValidator vmValidator = createVmValidator();
StoragePoolValidator spValidator = createStoragePoolValidator();
DiskImagesValidator diskImagesValidatorForChain =
createDiskImageValidator(DisksFilter.filterImageDisks(getDisksList(), ONLY_NOT_SHAREABLE,
ONLY_SNAPABLE, ONLY_ACTIVE));
if (!(validateVM(vmValidator) && validate(spValidator.isUp())
&& validate(vmValidator.vmNotIlegal())
&& validate(vmValidator.vmNotLocked())
&& validate(snapshotsValidator.vmNotDuringSnapshot(getVmId()))
&& validate(snapshotsValidator.vmNotInPreview(getVmId()))
&& validate(vmValidator.vmNotDuringMigration())
&& validate(vmValidator.vmNotRunningStateless())
&& validate(diskImagesValidatorForChain.diskImagesHaveNotExceededMaxNumberOfVolumesInImageChain())
&& (!getParameters().isSaveMemory() || validate(vmValidator.vmNotHavingPciPassthroughDevices())))) {
return false;
}
List<DiskImage> disksList = getDisksListForChecks();
if (disksList.size() > 0) {
DiskImagesValidator diskImagesValidator = createDiskImageValidator(disksList);
if (!(validate(diskImagesValidator.diskImagesNotLocked())
&& validate(diskImagesValidator.diskImagesNotIllegal()))) {
return false;
}
}
return validateStorage();
}
protected StoragePoolValidator createStoragePoolValidator() {
return new StoragePoolValidator(getStoragePool());
}
protected DiskImagesValidator createDiskImageValidator(List<DiskImage> disksList) {
return new DiskImagesValidator(disksList);
}
protected VmValidator createVmValidator() {
return new VmValidator(getVm());
}
protected boolean validateVM(VmValidator vmValidator) {
return validate(vmValidator.vmNotSavingRestoring()) &&
validate(vmValidator.validateVmStatusUsingMatrix(VdcActionType.CreateAllSnapshotsFromVm));
}
private boolean isSpecifiedDisksExist(List<DiskImage> disks) {
if (disks == null || disks.isEmpty()) {
return true;
}
DiskImagesValidator diskImagesValidator = createDiskImageValidator(disks);
if (!validate(diskImagesValidator.diskImagesNotExist())) {
return false;
}
return true;
}
protected MultipleStorageDomainsValidator createMultipleStorageDomainsValidator(Collection<DiskImage> disksList) {
return new MultipleStorageDomainsValidator(getVm().getStoragePoolId(),
ImagesHandler.getAllStorageIdsForImageIds(disksList));
}
@Override
protected void setActionMessageParameters() {
addValidationMessage(EngineMessage.VAR__ACTION__CREATE);
addValidationMessage(EngineMessage.VAR__TYPE__SNAPSHOT);
}
@Override
protected VdcActionType getChildActionType() {
return VdcActionType.CreateSnapshot;
}
@Override
protected List<Class<?>> getValidationGroups() {
addValidationGroup(CreateEntity.class);
return super.getValidationGroups();
}
@Override
protected Map<String, Pair<String, String>> getExclusiveLocks() {
return getParameters().isNeedsLocking() ?
Collections.singletonMap(getVmId().toString(),
LockMessagesMatchUtil.makeLockingPair(LockingGroup.VM, getSnapshotIsBeingTakenForVmMessage()))
: null;
}
private String getSnapshotIsBeingTakenForVmMessage() {
if (cachedSnapshotIsBeingTakenMessage == null) {
cachedSnapshotIsBeingTakenMessage =
new LockMessage(EngineMessage.ACTION_TYPE_FAILED_SNAPSHOT_IS_BEING_TAKEN_FOR_VM)
.withOptional("VmName", getVmName())
.toString();
}
return cachedSnapshotIsBeingTakenMessage;
}
@Override
public List<QuotaConsumptionParameter> getQuotaStorageConsumptionParameters() {
List<QuotaConsumptionParameter> list = new ArrayList<>();
for (DiskImage disk : getDisksList()) {
list.add(new QuotaStorageConsumptionParameter(
disk.getQuotaId(),
null,
QuotaConsumptionParameter.QuotaAction.CONSUME,
disk.getStorageIds().get(0),
disk.getActualSize()));
}
return list;
}
@Override
public CommandCallback getCallback() {
return new ConcurrentChildCommandsExecutionCallback();
}
}