package org.ovirt.engine.core.bll;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import javax.inject.Inject;
import org.ovirt.engine.core.bll.context.CommandContext;
import org.ovirt.engine.core.common.action.AttachDetachVmDiskParameters;
import org.ovirt.engine.core.common.action.LockProperties;
import org.ovirt.engine.core.common.action.LockProperties.Scope;
import org.ovirt.engine.core.common.action.RestoreAllSnapshotsParameters;
import org.ovirt.engine.core.common.action.UpdateVmVersionParameters;
import org.ovirt.engine.core.common.action.VdcActionType;
import org.ovirt.engine.core.common.action.VdcReturnValueBase;
import org.ovirt.engine.core.common.action.VmOperationParameterBase;
import org.ovirt.engine.core.common.businessentities.Snapshot.SnapshotType;
import org.ovirt.engine.core.common.businessentities.SnapshotActionEnum;
import org.ovirt.engine.core.common.businessentities.storage.DiskImage;
import org.ovirt.engine.core.common.businessentities.storage.DiskVmElement;
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.compat.Guid;
import org.ovirt.engine.core.dao.DiskImageDao;
import org.ovirt.engine.core.dao.SnapshotDao;
@InternalCommandAttribute
public class RestoreStatelessVmCommand<T extends VmOperationParameterBase> extends VmCommand<T> {
@Inject
private DiskImageDao diskImageDao;
@Inject
private SnapshotDao snapshotDao;
/**
* Constructor for command creation when compensation is applied on startup
*/
protected RestoreStatelessVmCommand(Guid commandId) {
super(commandId);
}
public RestoreStatelessVmCommand(T parameters, CommandContext cmdContext) {
super(parameters, cmdContext);
}
@Override
protected void executeCommand() {
VdcReturnValueBase result =
runInternalActionWithTasksContext(
VdcActionType.UpdateVmVersion,
buildUpdateVmVersionParameters());
// if it fail because of validate, its safe to restore the snapshot
// and the vm will still be usable with previous version
if (!result.getSucceeded() && !result.isValid()) {
log.warn("Couldn't update VM '{}' ({}) version from it's template, continue with restoring stateless snapshot.",
getVm().getName(),
getVmId());
setSucceeded(restoreInitialState());
}
else {
setSucceeded(result.getSucceeded());
}
}
private UpdateVmVersionParameters buildUpdateVmVersionParameters() {
UpdateVmVersionParameters parameters = new UpdateVmVersionParameters(getVmId());
// the VM is already locked by this command during the execute phase of UpdateVmVersion
parameters.setLockVm(false);
return parameters;
}
private boolean restoreInitialState() {
Guid statelessVmSnapshotId = getVmSnapshotIdForType(SnapshotType.STATELESS);
if (statelessVmSnapshotId == null) {
return true;
}
List<DiskImage> statelessDiskSnapshots = getDiskSnapshotsForVmSnapshot(statelessVmSnapshotId);
if (!detachDisksNotPartOfStatelessSnapshot(statelessDiskSnapshots)) {
return false;
}
return runInternalActionWithTasksContext(VdcActionType.RestoreAllSnapshots,
buildRestoreAllSnapshotsParameters(statelessDiskSnapshots),
getLock()).getSucceeded();
}
private boolean detachDisksNotPartOfStatelessSnapshot(List<DiskImage> statelessDiskSnapshots) {
Guid activeVmSnapshotId = getVmSnapshotIdForType(SnapshotType.ACTIVE);
List<DiskImage> activeDiskSnapshots = getDiskSnapshotsForVmSnapshot(activeVmSnapshotId);
Set<Guid> disksWithStatelessSnapshot =
statelessDiskSnapshots.stream().map(DiskImage::getId).collect(Collectors.toSet());
for (DiskImage activeDiskSnapshot : activeDiskSnapshots) {
if (!disksWithStatelessSnapshot.contains(activeDiskSnapshot.getId())) {
VdcReturnValueBase returnValue = runInternalAction (
VdcActionType.DetachDiskFromVm,
buildDetachDetachVmDiskParameters(activeDiskSnapshot));
if (!returnValue.getSucceeded()) {
log.error("Could not restore stateless VM {} due to a failure to detach Disk {}",
getVmId(), activeDiskSnapshot.getId());
return false;
}
}
}
return true;
}
private AttachDetachVmDiskParameters buildDetachDetachVmDiskParameters(DiskImage activeDiskSnapshot) {
return new AttachDetachVmDiskParameters(
new DiskVmElement(activeDiskSnapshot.getId(), getVmId()),
false,
false);
}
private Guid getVmSnapshotIdForType(SnapshotType type) {
return snapshotDao.getId(getVmId(), type);
}
private List<DiskImage> getDiskSnapshotsForVmSnapshot(Guid snapshotId) {
return diskImageDao.getAllSnapshotsForVmSnapshot(snapshotId);
}
private RestoreAllSnapshotsParameters buildRestoreAllSnapshotsParameters(List<DiskImage> imagesList) {
RestoreAllSnapshotsParameters restoreParameters = new RestoreAllSnapshotsParameters(getVm().getId(), SnapshotActionEnum.RESTORE_STATELESS);
restoreParameters.setShouldBeLogged(false);
restoreParameters.setImages(imagesList);
return restoreParameters;
}
@Override
protected LockProperties applyLockProperties(LockProperties lockProperties) {
return lockProperties.withScope(Scope.Execution);
}
@Override
protected Map<String, Pair<String, String>> getExclusiveLocks() {
return Collections.singletonMap(getVmId().toString(),
LockMessagesMatchUtil.makeLockingPair(LockingGroup.VM, EngineMessage.ACTION_TYPE_FAILED_OBJECT_LOCKED));
}
}