package org.ovirt.engine.core.bll.storage.dr;
import java.util.Collections;
import java.util.HashMap;
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.CommandBase;
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.SerialChildCommandsExecutionCallback;
import org.ovirt.engine.core.bll.SerialChildExecutingCommand;
import org.ovirt.engine.core.bll.context.CommandContext;
import org.ovirt.engine.core.bll.tasks.CommandCoordinatorUtil;
import org.ovirt.engine.core.bll.tasks.interfaces.CommandCallback;
import org.ovirt.engine.core.bll.utils.PermissionSubject;
import org.ovirt.engine.core.common.AuditLogType;
import org.ovirt.engine.core.common.VdcObjectType;
import org.ovirt.engine.core.common.action.CreateAllSnapshotsFromVmParameters;
import org.ovirt.engine.core.common.action.GlusterStorageSyncCommandParameters;
import org.ovirt.engine.core.common.action.GlusterStorageSyncCommandParameters.DRStep;
import org.ovirt.engine.core.common.action.LockProperties;
import org.ovirt.engine.core.common.action.LockProperties.Scope;
import org.ovirt.engine.core.common.action.RemoveSnapshotParameters;
import org.ovirt.engine.core.common.action.VdcActionParametersBase.EndProcedure;
import org.ovirt.engine.core.common.action.VdcActionType;
import org.ovirt.engine.core.common.action.VdcReturnValueBase;
import org.ovirt.engine.core.common.action.gluster.GlusterVolumeGeoRepSessionParameters;
import org.ovirt.engine.core.common.businessentities.Snapshot.SnapshotType;
import org.ovirt.engine.core.common.businessentities.VM;
import org.ovirt.engine.core.common.businessentities.gluster.GlusterGeoRepSession;
import org.ovirt.engine.core.common.errors.EngineMessage;
import org.ovirt.engine.core.common.locks.LockingGroup;
import org.ovirt.engine.core.common.queries.IdQueryParameters;
import org.ovirt.engine.core.common.queries.VdcQueryType;
import org.ovirt.engine.core.common.utils.Pair;
import org.ovirt.engine.core.compat.Guid;
import org.ovirt.engine.core.dao.gluster.GlusterGeoRepDao;
@NonTransactiveCommandAttribute
public class GlusterStorageSyncCommand<T extends GlusterStorageSyncCommandParameters> extends CommandBase<T> implements SerialChildExecutingCommand {
private static final String DR_SNAPSHOT_NAME_SUFFIX = "-TMPDR";
@Inject
private GlusterGeoRepDao geoRepDao;
private GlusterGeoRepSession geoRepSession;
public GlusterStorageSyncCommand(T parameters, CommandContext cmdContext) {
super(parameters, cmdContext);
setStorageDomainId(getParameters().getStorageDomainId());
}
private GlusterGeoRepSession getSession() {
if (geoRepSession == null) {
geoRepSession = geoRepDao.getById(getParameters().getGeoRepSessionId());
}
return geoRepSession;
}
@Override
protected LockProperties applyLockProperties(LockProperties lockProperties) {
return lockProperties.withScope(Scope.Command);
}
@Override
protected Map<String, Pair<String, String>> getExclusiveLocks() {
return Collections.singletonMap(getStorageDomainId().toString(),
LockMessagesMatchUtil.makeLockingPair(LockingGroup.GLUSTER_STORAGE_DOMAIN_SYNC,
new LockMessage(EngineMessage.ERROR_STORAGE_DOMAIN_SYNC_EXISTS)));
}
@Override
protected void executeCommand() {
// Get list of running VMs that have disks on storage domain
List<VM> vms =
runInternalQuery(VdcQueryType.GetVmsByStorageDomain, new IdQueryParameters(getStorageDomain().getId()))
.getReturnValue();
// Snapshot the VMs
Map<Guid, Guid> vmIdSnapshotIdMap = new HashMap<>();
for (VM vm : vms) {
try {
Future<VdcReturnValueBase> future = CommandCoordinatorUtil.executeAsyncCommand(
VdcActionType.CreateAllSnapshotsFromVm,
getCreateSnapshotParameters(vm),
cloneContextAndDetachFromParent());
vmIdSnapshotIdMap.put(vm.getId(), future.get().getActionReturnValue());
} catch (InterruptedException | ExecutionException e) {
log.error("Error creating VM snapshot for VM with id '{}', name '{}' for DR sync",
vm.getId(),
vm.getName(),
e.getMessage());
log.debug("Exception", e);
endWithFailure();
getParameters().setTaskGroupSuccess(false);
}
}
getParameters().setVmIdSnapshotIds(vmIdSnapshotIdMap);
getParameters().setNextStep(DRStep.GEO_REP);
persistCommandIfNeeded();
setSucceeded(true);
}
@Override
public List getPermissionCheckSubjects() {
return Collections.singletonList(new PermissionSubject(getStorageDomainId(),
VdcObjectType.Storage,
getActionType().getActionGroup()));
}
@Override
protected void endWithFailure() {
removeDRSnapshots();
super.endWithFailure();
}
private void removeDRSnapshots() {
for (Map.Entry<Guid, Guid> entry : getParameters().getVmIdSnapshotIds().entrySet()) {
RemoveSnapshotParameters removeSnapshotParameters = new RemoveSnapshotParameters(entry.getValue(),
entry.getKey());
removeSnapshotParameters.setParentCommand(getActionType());
removeSnapshotParameters.setEndProcedure(EndProcedure.COMMAND_MANAGED);
removeSnapshotParameters.setParentParameters(getParameters());
removeSnapshotParameters.setNeedsLocking(false);
CommandCoordinatorUtil.executeAsyncCommand(VdcActionType.RemoveSnapshot,
removeSnapshotParameters,
cloneContextAndDetachFromParent());
}
}
@Override
public AuditLogType getAuditLogTypeValue() {
switch (getActionState()) {
case EXECUTE:
return AuditLogType.GLUSTER_STORAGE_DOMAIN_SYNC_STARTED;
case END_SUCCESS:
return AuditLogType.GLUSTER_STORAGE_DOMAIN_SYNCED;
default:
return AuditLogType.GLUSTER_STORAGE_DOMAIN_SYNC_FAILED;
}
}
protected CreateAllSnapshotsFromVmParameters getCreateSnapshotParameters(VM vm) {
CreateAllSnapshotsFromVmParameters params = new CreateAllSnapshotsFromVmParameters(vm.getId(),
vm.getName() + getStorageDomain().getName() + DR_SNAPSHOT_NAME_SUFFIX,
false);
params.setParentCommand(getActionType());
params.setSnapshotType(SnapshotType.REGULAR);
params.setParentParameters(getParameters());
params.setDisks(vm.getDiskList());
params.setNeedsLocking(false);
params.setEndProcedure(EndProcedure.COMMAND_MANAGED);
return params;
}
@Override
public boolean performNextOperation(int completedChildCount) {
if (getParameters().getNextStep() == null) {
return false;
}
switch (getParameters().getNextStep()) {
case GEO_REP:
GlusterVolumeGeoRepSessionParameters parameters =
new GlusterVolumeGeoRepSessionParameters(getSession().getMasterVolumeId(),
getSession().getId());
parameters.setEndProcedure(EndProcedure.COMMAND_MANAGED);
parameters.setParentCommand(getActionType());
parameters.setParentParameters(getParameters());
getParameters().setNextStep(DRStep.REMOVE_TMP_SNAPSHOTS);
runInternalActionWithTasksContext(VdcActionType.GlusterStorageGeoRepSyncInternal, parameters);
persistCommandIfNeeded();
break;
case REMOVE_TMP_SNAPSHOTS:
removeDRSnapshots();
getParameters().setNextStep(null);
persistCommandIfNeeded();
break;
}
return true;
}
@Override
public CommandCallback getCallback() {
return new SerialChildCommandsExecutionCallback();
}
}