package org.ovirt.engine.core.bll.tasks;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.ovirt.engine.core.bll.Backend;
import org.ovirt.engine.core.bll.CommandBase;
import org.ovirt.engine.core.bll.interfaces.BackendInternal;
import org.ovirt.engine.core.bll.job.ExecutionHandler;
import org.ovirt.engine.core.bll.tasks.interfaces.CommandCoordinator;
import org.ovirt.engine.core.bll.tasks.interfaces.SPMTask;
import org.ovirt.engine.core.common.VdcObjectType;
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.AsyncTaskCreationInfo;
import org.ovirt.engine.core.common.asynctasks.AsyncTaskParameters;
import org.ovirt.engine.core.common.asynctasks.AsyncTaskType;
import org.ovirt.engine.core.common.businessentities.AsyncTask;
import org.ovirt.engine.core.common.businessentities.AsyncTaskResultEnum;
import org.ovirt.engine.core.common.businessentities.AsyncTaskStatus;
import org.ovirt.engine.core.common.businessentities.AsyncTaskStatusEnum;
import org.ovirt.engine.core.common.businessentities.CommandEntity;
import org.ovirt.engine.core.common.job.ExternalSystemType;
import org.ovirt.engine.core.common.job.Step;
import org.ovirt.engine.core.common.job.StepEnum;
import org.ovirt.engine.core.common.vdscommands.SPMTaskGuidBaseVDSCommandParameters;
import org.ovirt.engine.core.common.vdscommands.VDSCommandType;
import org.ovirt.engine.core.compat.CommandStatus;
import org.ovirt.engine.core.compat.Guid;
import org.ovirt.engine.core.compat.TransactionScopeOption;
import org.ovirt.engine.core.dal.dbbroker.DbFacade;
import org.ovirt.engine.core.utils.threadpool.ThreadPoolUtil;
import org.ovirt.engine.core.utils.transaction.TransactionSupport;
import org.slf4j.Logger;
public class CoCoAsyncTaskHelper {
private final CommandCoordinator coco;
CoCoAsyncTaskHelper(CommandCoordinator coco) {
this.coco = coco;
}
/**
* Use this method in order to create task in the AsyncTaskManager in a safe way. If you use
* this method within a certain command, make sure that the command implemented the
* ConcreteCreateTask method.
*
* @param asyncTaskCreationInfo
* info to send to AsyncTaskManager when creating the task.
* @param parentCommand
* VdcActionType of the command that its EndAction we want to invoke when tasks are finished.
* @param entitiesMap
* Map of entities that are associated with the task
* @return Guid of the created task.
*/
public Guid createTask(Guid taskId,
CommandBase<?> command,
AsyncTaskCreationInfo asyncTaskCreationInfo,
VdcActionType parentCommand,
String description,
Map<Guid, VdcObjectType> entitiesMap) {
Step taskStep =
ExecutionHandler.getInstance().addTaskStep(command.getExecutionContext(),
StepEnum.getStepNameByTaskType(asyncTaskCreationInfo.getTaskType()),
description,
command.getCommandStepSubjectEntities());
command.getExecutionContext().setStep(taskStep);
if (taskStep != null) {
asyncTaskCreationInfo.setStepId(taskStep.getId());
}
SPMAsyncTask task = concreteCreateTask(taskId, command, asyncTaskCreationInfo, parentCommand);
task.setEntitiesMap(entitiesMap);
AsyncTaskUtils.addOrUpdateTaskInDB(task);
getAsyncTaskManager().lockAndAddTaskToManager(task);
Guid vdsmTaskId = task.getVdsmTaskId();
ExecutionHandler.getInstance().updateStepExternalId(taskStep, vdsmTaskId, ExternalSystemType.VDSM);
return vdsmTaskId;
}
public SPMAsyncTask createTask(AsyncTaskType taskType, AsyncTaskParameters taskParameters) {
return AsyncTaskFactory.construct(coco, taskType, taskParameters, false);
}
/**
* Create the {@link SPMAsyncTask} object to be run
* @param taskId the id of the async task place holder in the database
* @param asyncTaskCreationInfo Info on how to create the task
* @param parentCommand The type of command issuing the task
* @return An {@link SPMAsyncTask} object representing the task to be run
*/
public SPMAsyncTask concreteCreateTask(
Guid taskId,
CommandBase<?> command,
AsyncTaskCreationInfo asyncTaskCreationInfo,
VdcActionType parentCommand) {
AsyncTaskParameters p =
new AsyncTaskParameters(asyncTaskCreationInfo,
getAsyncTask(taskId, command, asyncTaskCreationInfo, parentCommand));
p.setEntityInfo(command.getParameters().getEntityInfo());
return createTask(internalGetTaskType(command), p);
}
public void revertTasks(CommandBase<?> command) {
if (command.getParameters().getVdsmTaskIds() != null) {
// list to send to the pollTasks method
ArrayList<Guid> taskIdAsList = new ArrayList<>();
for (Guid taskId : command.getParameters().getVdsmTaskIds()) {
taskIdAsList.add(taskId);
ArrayList<AsyncTaskStatus> tasksStatuses = getAsyncTaskManager().pollTasks(
taskIdAsList);
// call revert task only if ended successfully
if (tasksStatuses.get(0).getTaskEndedSuccessfully()) {
getBackend().getResourceManager().runVdsCommand(
VDSCommandType.SPMRevertTask,
new SPMTaskGuidBaseVDSCommandParameters(
command.getStoragePool().getId(), taskId));
}
taskIdAsList.clear();
}
}
}
public void cancelTasks(final CommandBase<?> command, final Logger log) {
if (command.hasTasks()) {
ThreadPoolUtil.execute(() -> {
log.info("Rollback for command '{}'", command.getClass().getName());
try {
getAsyncTaskManager().cancelTasks(command.getReturnValue().getVdsmTaskIdList());
} catch (Exception e) {
log.error("Failed to cancel tasks for command '{}'",
command.getClass().getName());
}
});
}
}
public List<AsyncTask> getAllAsyncTasksFromDb(CommandCoordinator coco) {
List<AsyncTask> asyncTasks = DbFacade.getInstance().getAsyncTaskDao().getAll();
for (AsyncTask asyncTask : asyncTasks) {
asyncTask.setRootCmdEntity(getCommandEntity(asyncTask.getRootCommandId()));
asyncTask.setChildCmdEntity(getCommandEntity(asyncTask.getCommandId()));
}
return asyncTasks;
}
private CommandEntity getCommandEntity(Guid cmdId) {
CommandEntity cmdEntity = coco.getCommandEntity(cmdId);
if (cmdEntity == null) {
cmdEntity = coco.createCommandEntity(cmdId, VdcActionType.Unknown, new VdcActionParametersBase());
}
return cmdEntity;
}
/**
* This method is always called from with in a transaction
*/
public void saveAsyncTaskToDb(final AsyncTask asyncTask) {
TransactionSupport.executeInScope(TransactionScopeOption.Required, () -> {
DbFacade.getInstance().getAsyncTaskDao().save(asyncTask);
coco.persistCommand(asyncTask.getRootCmdEntity());
coco.persistCommand(asyncTask.getChildCmdEntity());
return null;
});
}
public AsyncTask getAsyncTaskFromDb(Guid asyncTaskId) {
AsyncTask asyncTask = DbFacade.getInstance().getAsyncTaskDao().get(asyncTaskId);
if (asyncTask != null) {
asyncTask.setRootCmdEntity(getCommandEntity(asyncTask.getRootCommandId()));
asyncTask.setChildCmdEntity(getCommandEntity(asyncTask.getCommandId()));
}
return asyncTask;
}
public int removeTaskFromDbByTaskId(final Guid taskId) throws RuntimeException {
return TransactionSupport.executeInScope(TransactionScopeOption.Required, () -> {
AsyncTask asyncTask = DbFacade.getInstance().getAsyncTaskDao().get(taskId);
int retVal = DbFacade.getInstance().getAsyncTaskDao().remove(taskId);
if (shouldRemoveCommand(asyncTask)) {
coco.removeCommand(asyncTask.getCommandId());
if (!coco.hasCommandEntitiesWithRootCommandId(asyncTask.getRootCommandId())) {
coco.removeCommand(asyncTask.getRootCommandId());
}
}
return retVal;
});
}
private boolean shouldRemoveCommand(AsyncTask asyncTask) {
if (asyncTask == null || Guid.isNullOrEmpty(asyncTask.getCommandId())) {
return false;
}
CommandEntity cmdEntity = coco.getCommandEntity(asyncTask.getCommandId());
CommandEntity parentEntity = null;
if (cmdEntity != null) {
parentEntity = coco.getCommandEntity(cmdEntity.getParentCommandId());
}
return cmdEntity != null && !cmdEntity.isCallbackEnabled() &&
(parentEntity == null || !parentEntity.isCallbackEnabled());
}
public AsyncTask getByVdsmTaskId(Guid vdsmTaskId) {
AsyncTask asyncTask = DbFacade.getInstance().getAsyncTaskDao().getByVdsmTaskId(vdsmTaskId);
if (asyncTask != null) {
asyncTask.setRootCmdEntity(getCommandEntity(asyncTask.getRootCommandId()));
asyncTask.setChildCmdEntity(getCommandEntity(asyncTask.getCommandId()));
}
return asyncTask;
}
public int removeByVdsmTaskId(final Guid vdsmTaskId) {
return TransactionSupport.executeInScope(TransactionScopeOption.Required, () -> {
AsyncTask asyncTask = DbFacade.getInstance().getAsyncTaskDao().getByVdsmTaskId(vdsmTaskId);
int retVal = DbFacade.getInstance().getAsyncTaskDao().removeByVdsmTaskId(vdsmTaskId);
if (shouldRemoveCommand(asyncTask)) {
coco.removeCommand(asyncTask.getCommandId());
if (!coco.hasCommandEntitiesWithRootCommandId(asyncTask.getRootCommandId())) {
coco.removeCommand(asyncTask.getRootCommandId());
}
}
return retVal;
});
}
public void addOrUpdateTaskInDB(final AsyncTask asyncTask) {
TransactionSupport.executeInScope(TransactionScopeOption.Required, () -> {
if (asyncTask.getChildCmdEntity().getRootCommandId() != null &&
!asyncTask.getChildCmdEntity().getRootCommandId().equals(asyncTask.getChildCmdEntity().getId())) {
coco.persistCommand(asyncTask.getRootCmdEntity());
}
coco.persistCommand(asyncTask.getChildCmdEntity());
DbFacade.getInstance().getAsyncTaskDao().saveOrUpdate(asyncTask);
return null;
});
}
public AsyncTask getAsyncTask(Guid taskId,
CommandBase<?> command,
AsyncTaskCreationInfo asyncTaskCreationInfo,
VdcActionType parentCommand) {
AsyncTask asyncTask = null;
if (!taskId.equals(Guid.Empty)) {
asyncTask = getAsyncTaskFromDb(taskId);
}
if (asyncTask != null) {
VdcActionParametersBase parentParameters = command.getParentParameters() == null ?
command.getParentParameters(parentCommand) : command.getParentParameters();
Guid parentCommandId =
parentParameters == null ? Guid.Empty : parentParameters.getCommandId();
if (VdcActionType.Unknown.equals(command.getParameters().getCommandType())) {
command.getParameters().setCommandType(command.getActionType());
}
asyncTask.setActionType(parentCommand);
asyncTask.setVdsmTaskId(asyncTaskCreationInfo.getVdsmTaskId());
asyncTask.setActionParameters(parentParameters);
asyncTask.setTaskParameters(command.getParameters());
asyncTask.setStepId(asyncTaskCreationInfo.getStepId());
asyncTask.setCommandId(command.getCommandId());
asyncTask.setRootCmdEntity(getParentCommandEntity(parentCommandId,
parentCommand,
parentParameters));
asyncTask.setChildCmdEntity(getChildCommandEntity(command,
parentCommand));
asyncTask.setStoragePoolId(asyncTaskCreationInfo.getStoragePoolID());
asyncTask.setTaskType(asyncTaskCreationInfo.getTaskType());
asyncTask.setCommandStatus(command.getCommandStatus());
asyncTask.setCommandType(command.getParameters().getCommandType());
} else {
asyncTask = createAsyncTask(command, asyncTaskCreationInfo, parentCommand);
}
return asyncTask;
}
public AsyncTask createAsyncTask(
CommandBase<?> command,
AsyncTaskCreationInfo asyncTaskCreationInfo,
VdcActionType parentCommand) {
VdcActionParametersBase parentParameters = command.getParentParameters() == null ?
command.getParentParameters(parentCommand) : command.getParentParameters();
Guid parentCommandId =
parentParameters == null ? Guid.Empty : parentParameters.getCommandId();
if (VdcActionType.Unknown.equals(command.getParameters().getCommandType())) {
command.getParameters().setCommandType(command.getActionType());
}
AsyncTask asyncTask = new AsyncTask(AsyncTaskResultEnum.success,
AsyncTaskStatusEnum.running,
command.getUserId(),
asyncTaskCreationInfo.getVdsmTaskId(),
asyncTaskCreationInfo.getStepId(),
asyncTaskCreationInfo.getStoragePoolID(),
asyncTaskCreationInfo.getTaskType(),
getParentCommandEntity(parentCommandId,
parentCommand,
parentParameters),
getChildCommandEntity(command, parentCommand));
return asyncTask;
}
private CommandEntity getChildCommandEntity(CommandBase<?> command, VdcActionType parentCommand) {
CommandEntity cmdEntity = coco.getCommandEntity(command.getCommandId());
if (cmdEntity == null) {
command.persistCommand(parentCommand, command.getCallback() != null);
}
return coco.getCommandEntity(command.getCommandId());
}
private CommandEntity getParentCommandEntity(Guid cmdId,
VdcActionType actionType,
VdcActionParametersBase parameters) {
CommandEntity cmdEntity = coco.getCommandEntity(cmdId);
if (cmdEntity == null) {
cmdEntity = coco.createCommandEntity(cmdId, actionType, parameters);
if (!Guid.isNullOrEmpty(cmdId)) {
cmdEntity.setCommandStatus(CommandStatus.ACTIVE);
coco.persistCommand(cmdEntity);
}
}
return cmdEntity;
}
public VdcReturnValueBase endAction(SPMTask task) {
AsyncTask dbAsyncTask = task.getParameters().getDbAsyncTask();
VdcActionType actionType = getEndActionType(dbAsyncTask);
VdcActionParametersBase parameters = dbAsyncTask.getActionParameters();
CommandBase<?> command = CommandHelper.buildCommand(actionType, parameters,
coco.retrieveCommandContext(dbAsyncTask.getRootCommandId()).getExecutionContext(),
coco.getCommandStatus(dbAsyncTask.getCommandId()));
return new DecoratedCommand<>(command).endAction();
}
private VdcActionType getEndActionType(AsyncTask dbAsyncTask) {
VdcActionType commandType = dbAsyncTask.getActionParameters().getCommandType();
if (!VdcActionType.Unknown.equals(commandType)) {
return commandType;
}
return dbAsyncTask.getActionType();
}
/**
* @return The type of task that should be created for this command.
* Commands that do not create async tasks should throw a
* {@link UnsupportedOperationException}
*
*/
private AsyncTaskType internalGetTaskType(CommandBase<?> command) {
return command.getAsyncTaskType();
}
private AsyncTaskManager getAsyncTaskManager() {
return AsyncTaskManager.getInstance();
}
private BackendInternal getBackend() {
return Backend.getInstance();
}
}