/**
  * We group the tasks by the root command id, so that we can identify the commands that were
  * partially submitted to vdsm
  */
 private Map<Guid, List<AsyncTask>> groupTasksByRootCommandId(List<AsyncTask> tasksInDB) {
   Map<Guid, List<AsyncTask>> rootCommandIdToCommandsMap = new HashMap<>();
   for (AsyncTask task : tasksInDB) {
     MultiValueMapUtils.addToMap(task.getRootCommandId(), task, rootCommandIdToCommandsMap);
   }
   return rootCommandIdToCommandsMap;
 }
 // NOTE - any change to the logic here and in the async tasks managed command requires inspection
 // of
 // CommandExecutor.handleUnmanagedCommands()
 public void initAsyncTaskManager() {
   tasksInDbAfterRestart = new ConcurrentHashMap<>();
   Map<Guid, List<AsyncTask>> rootCommandIdToTasksMap =
       groupTasksByRootCommandId(coco.getAllAsyncTasksFromDb());
   int numberOfCommandsPartiallyExecuted = 0;
   for (Entry<Guid, List<AsyncTask>> entry : rootCommandIdToTasksMap.entrySet()) {
     if (isPartiallyExecutedCommand(rootCommandIdToTasksMap.get(entry.getKey()))) {
       log.info("Root Command '{}' has partially executed task.", entry.getKey());
       numberOfCommandsPartiallyExecuted++;
     }
   }
   irsBrokerLatch = new CountDownLatch(numberOfCommandsPartiallyExecuted);
   for (Entry<Guid, List<AsyncTask>> entry : rootCommandIdToTasksMap.entrySet()) {
     if (isPartiallyExecutedCommand(rootCommandIdToTasksMap.get(entry.getKey()))) {
       log.info("Root Command '{}' has partially executed tasks.", entry.getKey());
       handlePartiallyExecuteTasksOfCommand(rootCommandIdToTasksMap.get(entry.getKey()));
     }
     for (AsyncTask task : entry.getValue()) {
       if (!isPartiallyExecutedTask(task)) {
         tasksInDbAfterRestart.putIfAbsent(task.getStoragePoolId(), new ArrayList<>());
         tasksInDbAfterRestart.get(task.getStoragePoolId()).add(task);
       }
     }
   }
   try {
     irsBrokerLatch.await();
     log.info("Initialization of AsyncTaskManager completed successfully.");
   } catch (InterruptedException e) {
   }
 }
  /**
   * If a command is partially submitted to vdsm, the empty place holders can be removed from the
   * database and we poll for the completion of tasks that were already submitted. Once the tasks
   * that were submitted finish execution, they are marked to be failed by adding them to the
   * partiallyCompletedCommandTasks list. If none of the tasks were submitted to vdsm, the empty
   * place holders are deleted from the database and we endAction on the command with failure
   */
  private void handlePartiallyExecutedTaskOfCommand(final AsyncTask task) {

    if (hasEmptyVdsmId(task)) {
      removeTaskFromDbByTaskId(task.getTaskId());
      return;
    }
    partiallyCompletedCommandTasks.put(task.getVdsmTaskId(), task);
  }
 /**
  * Constructs a task based on creation info (task type and task parameters as retrieved from the
  * vdsm). Use in order to construct tasks when service is initializing.
  *
  * @param coco Handle to command coordinator
  * @param creationInfo The Asyc Task Creation info
  */
 public static SPMAsyncTask construct(
     CommandCoordinator coco, AsyncTaskCreationInfo creationInfo) {
   AsyncTask asyncTask = coco.getByVdsmTaskId(creationInfo.getVdsmTaskId());
   if (asyncTask == null || asyncTask.getActionParameters() == null) {
     asyncTask =
         new AsyncTask(
             AsyncTaskResultEnum.success,
             AsyncTaskStatusEnum.running,
             Guid.Empty,
             creationInfo.getVdsmTaskId(),
             creationInfo.getStepId(),
             creationInfo.getStoragePoolID(),
             creationInfo.getTaskType(),
             getCommandEntity(
                 coco, asyncTask == null ? Guid.newGuid() : asyncTask.getRootCommandId()),
             getCommandEntity(
                 coco, asyncTask == null ? Guid.newGuid() : asyncTask.getCommandId()));
     creationInfo.setTaskType(AsyncTaskType.unknown);
   }
   AsyncTaskParameters asyncTaskParams = new AsyncTaskParameters(creationInfo, asyncTask);
   return construct(coco, creationInfo.getTaskType(), asyncTaskParams, true);
 }
  /**
   * Retrieves from the specified storage pool the tasks that exist on it and adds them to the
   * manager.
   *
   * @param sp the storage pool to retrieve running tasks from
   */
  public void addStoragePoolExistingTasks(StoragePool sp) {
    List<AsyncTaskCreationInfo> currPoolTasks = null;
    try {
      currPoolTasks = coco.getAllTasksInfo(sp.getId());
    } catch (RuntimeException e) {
      log.error(
          "Getting existing tasks on Storage Pool '{}' failed: {}", sp.getName(), e.getMessage());
      log.debug("Exception", e);
    }

    if (currPoolTasks != null && currPoolTasks.size() > 0) {
      synchronized (this) {
        final List<SPMTask> newlyAddedTasks = new ArrayList<>();

        for (AsyncTaskCreationInfo creationInfo : currPoolTasks) {
          creationInfo.setStoragePoolID(sp.getId());
          if (!_tasks.containsKey(creationInfo.getVdsmTaskId())) {
            try {
              SPMTask task;
              if (partiallyCompletedCommandTasks.containsKey(creationInfo.getVdsmTaskId())) {
                AsyncTask asyncTaskInDb =
                    partiallyCompletedCommandTasks.get(creationInfo.getVdsmTaskId());
                task = coco.construct(creationInfo, asyncTaskInDb);
                if (task.getEntitiesMap() == null) {
                  task.setEntitiesMap(new HashMap<>());
                }
                partiallyCompletedCommandTasks.remove(task.getVdsmTaskId());
                // mark it as a task of a partially completed command
                // Will result in failure of the command
                task.setPartiallyCompletedCommandTask(true);
              } else {
                task = coco.construct(creationInfo);
              }
              addTaskToManager(task);
              newlyAddedTasks.add(task);
            } catch (Exception e) {
              log.error(
                  "Failed to load task of type '{}' with id '{}': {}.",
                  creationInfo.getTaskType(),
                  creationInfo.getVdsmTaskId(),
                  ExceptionUtils.getRootCauseMessage(e));
              log.debug("Exception", e);
            }
          }
        }

        TransactionSupport.executeInNewTransaction(
            () -> {
              for (SPMTask task : newlyAddedTasks) {
                AsyncTaskUtils.addOrUpdateTaskInDB(task);
              }
              return null;
            });

        for (SPMTask task : newlyAddedTasks) {
          startPollingTask(task.getVdsmTaskId());
        }

        log.info(
            "Discovered {} tasks on Storage Pool '{}', {} added to manager.",
            currPoolTasks.size(),
            sp.getName(),
            newlyAddedTasks.size());
      }
    } else {
      log.info("Discovered no tasks on Storage Pool '{}'", sp.getName());
    }

    List<AsyncTask> tasksInDForStoragePool = tasksInDbAfterRestart.get(sp.getId());
    if (tasksInDForStoragePool != null) {
      for (AsyncTask task : tasksInDForStoragePool) {
        if (!_tasks.containsKey(task.getVdsmTaskId())) {
          coco.removeByVdsmTaskId(task.getVdsmTaskId());
        }
      }
    }

    // Either the tasks were only in DB - so they were removed from db, or they are polled -
    // in any case no need to hold them in the map that represents the tasksInDbAfterRestart
    tasksInDbAfterRestart.remove(sp.getId());
  }
 public void logAndFailPartiallySubmittedTaskOfCommand(final AsyncTask task, String message) {
   log.info(
       "Failing partially submitted task AsyncTaskType '{}': Task '{}' Parent Command '{}'",
       task.getTaskType(),
       task.getTaskId(),
       task.getActionType());
   task.getTaskParameters().setTaskGroupSuccess(false);
   if (task.getActionType() == VdcActionType.Unknown) {
     removeTaskFromDbByTaskId(task.getTaskId());
     log.info(
         "Not calling endAction for partially submitted task and AsyncTaskType '{}': Task '{}' Parent Command '{}'",
         task.getTaskType(),
         task.getTaskId(),
         task.getActionType());
     return;
   }
   log.info(
       "Calling updateTask for partially submitted task and AsyncTaskType '{}': Task '{}' Parent Command"
           + " '{}' Parameters class '{}'",
       task.getTaskType(),
       task.getTaskId(),
       task.getActionType());
   AsyncTaskCreationInfo creationInfo =
       new AsyncTaskCreationInfo(Guid.Empty, task.getTaskType(), task.getStoragePoolId());
   SPMTask spmTask = coco.construct(creationInfo, task);
   AsyncTaskStatus failureStatus = new AsyncTaskStatus();
   failureStatus.setStatus(AsyncTaskStatusEnum.finished);
   failureStatus.setResult(AsyncTaskResultEnum.failure);
   failureStatus.setMessage(message);
   spmTask.setState(AsyncTaskState.Ended);
   spmTask.setLastTaskStatus(failureStatus);
   spmTask.updateTask(failureStatus);
 }
 private static boolean isPartiallyExecutedTask(AsyncTask task) {
   return !task.getChildCmdEntity().isExecuted();
 }
 private static boolean hasEmptyVdsmId(AsyncTask task) {
   return Guid.Empty.equals(task.getVdsmTaskId());
 }