package org.ovirt.engine.core.bll.job; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.annotation.PostConstruct; import javax.inject.Inject; import javax.inject.Singleton; import org.apache.commons.lang.StringUtils; import org.ovirt.engine.core.bll.CommandBase; import org.ovirt.engine.core.bll.context.CommandContext; import org.ovirt.engine.core.bll.context.EngineContext; import org.ovirt.engine.core.bll.utils.PermissionSubject; import org.ovirt.engine.core.common.HasCorrelationId; 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.job.ExternalSystemType; import org.ovirt.engine.core.common.job.Job; import org.ovirt.engine.core.common.job.JobExecutionStatus; import org.ovirt.engine.core.common.job.Step; import org.ovirt.engine.core.common.job.StepEnum; import org.ovirt.engine.core.common.job.StepSubjectEntity; import org.ovirt.engine.core.common.utils.ExecutionMethod; import org.ovirt.engine.core.common.utils.ValidationUtils; import org.ovirt.engine.core.common.validation.group.PreRun; import org.ovirt.engine.core.compat.Guid; import org.ovirt.engine.core.dal.job.ExecutionMessageDirector; import org.ovirt.engine.core.dao.JobDao; import org.ovirt.engine.core.dao.StepDao; import org.ovirt.engine.core.utils.CorrelationIdTracker; import org.ovirt.engine.core.utils.lock.EngineLock; import org.ovirt.engine.core.utils.log.LoggedUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Provides methods for managing the flow objects the of the command, by the given execution context o the command. * <ul> * <li>Creates an {@code ExecutionContext} instance for {@code CommandBase}.</li> * <li>Creates {@code Job} entities.</li> * <li>Add {@code Step} to a {@code Job}.</li> * <li>Add {@code Step} to a {@code Step} as a sub step.</li> * <li>End step.</li> * <li>End job.</li> * </ul> */ @Singleton public class ExecutionHandler { private final Logger log = LoggerFactory.getLogger(ExecutionHandler.class); private static final List<Class<?>> validationGroups = Arrays.asList(new Class<?>[] { PreRun.class }); private static ExecutionHandler instance; @Deprecated public static ExecutionHandler getInstance() { return instance; } @Inject private JobRepository jobRepository; @Inject private JobDao jobDao; @Inject private StepDao stepDao; @PostConstruct private void init() { instance = this; } /** * Creates and returns an instance of {@link Job} entity. * * @param actionType * The action type the job entity represents. * @param command * The {@code CommandBase} instance which the job entity describes. * @return An initialized {@code Job} instance. */ public static Job createJob(VdcActionType actionType, CommandBase<?> command) { Job job = new Job(); job.setId(Guid.newGuid()); job.setActionType(actionType); job.setDescription(ExecutionMessageDirector.resolveJobMessage(actionType, command.getJobMessageProperties())); job.setJobSubjectEntities(getSubjectEntities(command.getPermissionCheckSubjects())); job.setOwnerId(command.getUserId()); job.setEngineSessionSeqId(command.getSessionSeqId()); job.setStatus(JobExecutionStatus.STARTED); job.setStartTime(new Date()); job.setCorrelationId(command.getCorrelationId()); return job; } private static Map<Guid, VdcObjectType> getSubjectEntities(List<PermissionSubject> permSubjectList) { Map<Guid, VdcObjectType> entities = new HashMap<>(); for (PermissionSubject permSubj : permSubjectList) { if (permSubj.getObjectId() != null && permSubj.getObjectType() != null) { entities.put(permSubj.getObjectId(), permSubj.getObjectType()); } } return entities; } /** * Finalizes a {@code Step} execution by a given context in which the step was performed and by the exit status of * the step. * * @param context * The context in which the {@code Step} was executed. * @param step * The step to finalize. * @param exitStatus * Indicates if the execution described by the step ended successfully or not. */ public void endStep(ExecutionContext context, Step step, boolean exitStatus) { if (context == null) { return; } if (context.isMonitored()) { Job job = context.getJob(); try { if (step != null) { step.markStepEnded(exitStatus); jobRepository.updateStep(step); } if (context.getExecutionMethod() == ExecutionMethod.AsJob && job != null && !exitStatus) { // step failure will cause the job to be marked as failed context.setCompleted(true); job.markJobEnded(false); jobRepository.updateCompletedJobAndSteps(job); } else { Step parentStep = context.getStep(); if (context.getExecutionMethod() == ExecutionMethod.AsStep && parentStep != null) { context.setCompleted(true); if (job != null && !exitStatus) { job.markJobEnded(false); jobRepository.updateCompletedJobAndSteps(job); } } } } catch (Exception e) { log.error("Exception", e); } } } /** * Finalizes a {@code Step} execution which represents a VDSM task. In case of a failure status, the job will not be * marked as failed at this stage, but via executing the {@code CommandBase.endAction} with the proper status by * {@code the AsyncTaskManager}. * * @param stepId * A unique identifier of the step to finalize. * @param exitStatus * The status which the step should be ended with. */ public void endTaskStep(Guid stepId, JobExecutionStatus exitStatus) { try { if (stepId != null) { Step step = jobRepository.getStep(stepId, false); if (step != null) { step.markStepEnded(exitStatus); jobRepository.updateStep(step); } } } catch (Exception e) { log.error("Failed to terminate step '{}' with status '{}': {}", stepId, exitStatus, e.getMessage()); log.debug("Exception", e); } } /** * Prepares the monitoring objects for the command by the default behavior: * <ul> * <li> {@link ExecutionContext} determines how the command should be monitored. By default, non-internal commands * will be associated with {@code Job} to represent the command execution. Internal commands will not be monitored * by default, therefore the {@code ExecutionContext} is created as non-monitored context. * <li> {@link Job} is created for monitored actions * </ul> * * @param command * The created instance of the command (can't be <code>null</code>). * @param actionType * The action type of the command * @param runAsInternal * Indicates if the command should be run as internal action or not */ public void prepareCommandForMonitoring(CommandBase<?> command, VdcActionType actionType, boolean runAsInternal) { ExecutionContext context = command.getExecutionContext(); if (context == null) { context = new ExecutionContext(); } try { boolean isMonitored = shouldMonitorCommand(actionType, runAsInternal); // A monitored job is created for monitored external flows if (isMonitored || context.isJobRequired()) { Job job = getJob(command, actionType); context.setExecutionMethod(ExecutionMethod.AsJob); context.setJob(job); command.setExecutionContext(context); command.setJobId(job.getId()); context.setMonitored(true); } } catch (Exception e) { log.error("Failed to prepare command of type '{}' for monitoring due to error '{}'", actionType.name(), e.getMessage()); log.debug("Exception", e); } } private Job getJob(CommandBase<?> command, VdcActionType actionType) { VdcActionParametersBase params = command.getParameters(); Job job; // if Job is external, we had already created the Job by AddExternalJobCommand, so just get it from DB if (params.getJobId() != null) { job = jobDao.get(params.getJobId()); } else { job = createJob(actionType, command); jobRepository.saveJob(job); } return job; } /** * Determines if a specific action should be monitored by the following criteria: * <ul> * <li>{@code VdcActionType.isMonitored} - defined for a specific action type</li> * <li>{@code isInternal} - By default, only non-internal commands are monitored</li> * </ul> * * @param actionType * The action type * @param isInternal * Indicator of action invocation method * @return true if the command should be monitored, else false. */ private static boolean shouldMonitorCommand(VdcActionType actionType, boolean isInternal) { return actionType.isActionMonitored() && !isInternal; } /** * Adds a {@link Step} entity by the provided context. A {@link Step} will not be created if * {@code ExecutionContext.isMonitored()} returns false. * * @param context * The context of the execution which defines visibility and execution method. * @param stepName * The name of the step. * @param description * A presentation name for the step. If not provided, the presentation name is resolved by the * {@code stepName}. * @return The created instance of the step or {@code null}. */ public Step addStep(ExecutionContext context, StepEnum stepName, String description) { return addStep(context, stepName, description, false); } /** * Adds a {@link Step} entity by the provided context. A {@link Step} will not be created if * {@code ExecutionContext.isMonitored()} returns false. * * @param context * The context of the execution which defines visibility and execution method. * @param stepName * The name of the step. * @param description * A presentation name for the step. If not provided, the presentation name is resolved by the * {@code stepName}. * @param isExternal * Indicates if the step is invoked by a plug-in */ public Step addStep(ExecutionContext context, StepEnum stepName, String description, boolean isExternal) { if (context == null) { return null; } Step step = null; if (context.isMonitored()) { if (description == null) { description = ExecutionMessageDirector.getInstance().getStepMessage(stepName); } try { Job job = context.getJob(); if (context.getExecutionMethod() == ExecutionMethod.AsJob && job != null) { step = job.addStep(stepName, description); try { step.setExternal(isExternal); jobRepository.saveStep(step); } catch (Exception e) { log.error("Failed to save new step '{}' for job '{}', '{}': {}", stepName.name(), job.getId(), job.getActionType().name(), e.getMessage()); log.debug("Exception", e); job.getSteps().remove(step); step = null; } } else { Step contextStep = context.getStep(); if (context.getExecutionMethod() == ExecutionMethod.AsStep && contextStep != null) { step = addSubStep(contextStep, stepName, description, Collections.emptyList()); step.setExternal(isExternal); } } } catch (Exception e) { log.error("Exception", e); } } return step; } /** * Adds a {@link Step} entity which describes a VDSM task by the provided context. A {@link Step} will not be * created if {@code ExecutionContext.isTasksMonitored()} returns false. * * @param context * The context of the execution which defines visibility of tasks. * @param stepName * The name of the step. * @param description * A presentation name for the step. If not provided, the presentation name is resolved by the * {@code stepName}. * @return The created instance of the step or {@code null}. */ public Step addTaskStep(ExecutionContext context, StepEnum stepName, String description, Collection<StepSubjectEntity> stepSubjectEntities) { if (context == null) { return null; } Step step = null; if (context.isTasksMonitored()) { Step parentTaskStep = context.getParentTasksStep(); if (parentTaskStep != null) { step = addSubStep(parentTaskStep, stepName, description, stepSubjectEntities); } } return step; } private Step addSubStep(Step parentStep, StepEnum stepName, String description, Collection<StepSubjectEntity> stepSubjectEntities) { Step step = null; if (parentStep != null) { if (description == null) { description = ExecutionMessageDirector.getInstance().getStepMessage(stepName); } step = parentStep.addStep(stepName, description); try { jobRepository.saveStep(step, stepSubjectEntities); } catch (Exception e) { log.error("Failed to save new step '{}' for step '{}', '{}': {}", stepName.name(), parentStep.getId(), parentStep.getStepType().name(), e.getMessage()); log.debug("Exception", e); parentStep.getSteps().remove(step); step = null; } } return step; } /** * Adds a {@link Step} entity by the provided context as a child step of a given parent step. A {@link Step} will * not be created if {@code ExecutionContext.isMonitored()} returns false. * * @param context * The context of the execution which defines visibility and execution method. * @param parentStep * The parent step which the new step will be added as its child. * @param newStepName * The name of the step. * @param description * A presentation name for the step. If not provided, the presentation name is resolved by the * {@code stepName}. * @return The created instance of the step or {@code null}. */ public Step addSubStep(ExecutionContext context, Step parentStep, StepEnum newStepName, String description) { return addSubStep(context, parentStep, newStepName, description, false); } /** * Adds a {@link Step} entity by the provided context as a child step of a given parent step. A {@link Step} will * not be created if {@code ExecutionContext.isMonitored()} returns false. * * @param context * The context of the execution which defines visibility and execution method. * @param parentStep * The parent step which the new step will be added as its child. * @param newStepName * The name of the step. * @param description * A presentation name for the step. If not provided, the presentation name is resolved by the * {@code stepName}. * @param isExternal * Indicates if the step is invoked by a plug-in */ public Step addSubStep(ExecutionContext context, Step parentStep, StepEnum newStepName, String description, boolean isExternal) { Step step = null; if (context == null || parentStep == null) { return null; } try { if (context.isMonitored()) { if (description == null) { description = ExecutionMessageDirector.getInstance().getStepMessage(newStepName); } if (context.getExecutionMethod() == ExecutionMethod.AsJob) { if (stepDao.exists(parentStep.getId())) { if (parentStep.getJobId().equals(context.getJob().getId())) { step = parentStep.addStep(newStepName, description); } } } else if (context.getExecutionMethod() == ExecutionMethod.AsStep) { step = parentStep.addStep(newStepName, description); } } if (step != null) { step.setExternal(isExternal); jobRepository.saveStep(step); } } catch (Exception e) { log.error("Exception", e); } return step; } /** * Finalizes a {@code Job} execution by a given context in which the job was performed and by the exit status of * the step. If the {@code Job} execution continues beyond the scope of the command, the {@code Job.isAsyncJob()} * should be set to {@code true}. If {@code ExecutionMethod.AsStep} is defined, the current active step can end the * running {@code Job} by setting the {@link ExecutionContext#shouldEndJob()} to * {@code true}. * * @param context * The context of the execution which defines how the job should be ended * @param exitStatus * Indicates if the execution described by the job ended successfully or not. */ public void endJob(ExecutionContext context, boolean exitStatus) { if (context == null) { return; } Job job = context.getJob(); try { if (context.isMonitored()) { if (context.getExecutionMethod() == ExecutionMethod.AsJob && job != null) { if (context.shouldEndJob() || !(job.isAsyncJob() && exitStatus)) { context.setCompleted(true); endJob(exitStatus, job); } } else { Step step = context.getStep(); if (context.getExecutionMethod() == ExecutionMethod.AsStep && step != null) { if (context.shouldEndJob()) { if (job == null) { job = jobRepository.getJob(step.getJobId()); } if (job != null) { context.setCompleted(true); endJob(exitStatus, job); } } } } } } catch (Exception e) { log.error("Exception", e); } } private void endJob(boolean exitStatus, Job job) { job.markJobEnded(exitStatus); try { jobRepository.updateCompletedJobAndSteps(job); } catch (Exception e) { log.error("Failed to end Job '{}', '{}': {}", job.getId(), job.getActionType().name(), e.getMessage()); log.debug("Exception", e); } } /** * Creates a context for execution of internal command as a monitored Job * * @return an execution context as a Job */ public static CommandContext createInternalJobContext() { return createInternalJobContext((EngineLock) null); } public static CommandContext createInternalJobContext(CommandContext commandContext) { return createInternalJobContext(commandContext, null); } /** * Creates a context for execution of internal command as a monitored Job, * the command will release the given lock when it is finished. * * @param lock * The lock which should be released at child command (can be null) * @return an execution context as a Job */ public static CommandContext createInternalJobContext(EngineLock lock) { return modifyContextForInternalJob(new CommandContext(new EngineContext()), lock); } public static CommandContext createInternalJobContext(CommandContext commandContext, EngineLock lock) { return modifyContextForInternalJob(commandContext.clone(), lock); } private static CommandContext modifyContextForInternalJob(CommandContext returnedContext, EngineLock lock) { return returnedContext .withExecutionContext(createMonitoredExecutionContext()) .withLock(lock) .withoutCompensationContext(); } private static ExecutionContext createMonitoredExecutionContext() { ExecutionContext executionContext = new ExecutionContext(); executionContext.setJobRequired(true); executionContext.setMonitored(true); return executionContext; } /** * Creates a default execution context for an inner command which creates VDSM tasks so the tasks will be monitored * under the parent {@code StepEnum.EXECUTING} step. If the parent command is an internal command, its parent task * step is passed to its internal command. * * @param parentContext * The context of the parent command * @return A context by which the internal command should be monitored. */ public static CommandContext createDefaultContextForTasks(CommandContext parentContext) { return createDefaultContextForTasks(parentContext, null); } /** * Creates a default execution context for an inner command which creates VDSM tasks so the tasks will be monitored * under the parent {@code StepEnum.EXECUTING} step. If the parent command is an internal command, its parent task * step is passed to its internal command. * * @param commandContext * The context of the parent command * @param lock * The lock which should be released at child command * @return A context by which the internal command should be monitored. */ public static CommandContext createDefaultContextForTasks(CommandContext commandContext, EngineLock lock) { CommandContext result = commandContext.clone().withLock(lock).withoutCompensationContext(); return result.withExecutionContext(createDefaultContextForTasksImpl(result.getExecutionContext())); } public static void setExecutionContextForTasks(CommandContext commandContext, ExecutionContext executionContext, EngineLock lock) { commandContext.withExecutionContext(createDefaultContextForTasksImpl(executionContext)) .withLock(lock); } private static ExecutionContext createDefaultContextForTasksImpl(ExecutionContext parentExecutionContext) { ExecutionContext executionContext = new ExecutionContext(); if (parentExecutionContext != null) { if (parentExecutionContext.getJob() != null) { Step parentStep = parentExecutionContext.getParentTasksStep(); if (parentStep != null) { executionContext.setParentTasksStep(parentStep); } } else { executionContext.setParentTasksStep(parentExecutionContext.getParentTasksStep()); } executionContext.setStep(parentExecutionContext.getStep()); executionContext.setStepsList(parentExecutionContext.getStepsList()); executionContext.setJob(parentExecutionContext.getJob()); } return executionContext; } /** * Creates {@code ExecutionContext} which defines the context for executing the finalizing step of the job. If the * step exists, it must be part of a job, therefore the {@code Job} entity is being set as part of the context. * * @param stepId * The unique identifier of the step. Must not be {@code null}. * @return The context for monitoring the finalizing step of the job, or {@code null} if no such step. */ public ExecutionContext createFinalizingContext(Guid stepId) { ExecutionContext context = null; try { Step step = jobRepository.getStep(stepId, false); if (step != null && step.getParentStepId() != null) { context = new ExecutionContext(); Step executionStep = jobRepository.getStep(step.getParentStepId(), false); // indicates if a step is monitored at Job level or as an inner step Guid parentStepId = executionStep.getParentStepId(); if (parentStepId == null) { context.setExecutionMethod(ExecutionMethod.AsJob); context.setJob(jobRepository.getJobWithSteps(step.getJobId())); } else { context.setExecutionMethod(ExecutionMethod.AsStep); Step parentStep = jobRepository.getStep(parentStepId, false); parentStep.setSteps(stepDao.getStepsByParentStepId(parentStep.getId())); context.setStep(parentStep); } context.setMonitored(true); } } catch (Exception e) { log.error("Exception", e); } return context; } /** * Method should be called when finalizing the command. The execution step is being ended with success and the * finalization step is started. * * @param executionContext * The context of the job * @return A created instance of the Finalizing step */ public Step startFinalizingStep(ExecutionContext executionContext) { if (executionContext == null) { return null; } Step step = null; try { if (executionContext.getExecutionMethod() == ExecutionMethod.AsJob) { Job job = executionContext.getJob(); if (job != null) { Step executingStep = job.getStep(StepEnum.EXECUTING); Step finalizingStep = job.addStep(StepEnum.FINALIZING, ExecutionMessageDirector.getInstance().getStepMessage(StepEnum.FINALIZING)); if (executingStep != null) { executingStep.markStepEnded(true); jobRepository.updateExistingStepAndSaveNewStep(executingStep, finalizingStep); } else { jobRepository.saveStep(finalizingStep); } } } else if (executionContext.getExecutionMethod() == ExecutionMethod.AsStep) { Step parentStep = executionContext.getStep(); if (parentStep != null) { Step executingStep = parentStep.getStep(StepEnum.EXECUTING); Step finalizingStep = parentStep.addStep(StepEnum.FINALIZING, ExecutionMessageDirector.getInstance() .getStepMessage(StepEnum.FINALIZING)); if (executingStep != null) { executingStep.markStepEnded(true); jobRepository.updateExistingStepAndSaveNewStep(executingStep, finalizingStep); } else { jobRepository.saveStep(finalizingStep); } } } } catch (Exception e) { log.error("Exception", e); } return step; } /** * Updates the step with the id in the external system in which the describe task runs. * * @param step * The step which represents the external task * @param externalId * The id of the task in the external system * @param systemType * The type of the system */ public void updateStepExternalId(Step step, Guid externalId, ExternalSystemType systemType) { if (step != null) { step.getExternalSystem().setId(externalId); step.getExternalSystem().setType(systemType); try { jobRepository.updateStep(step); } catch (Exception e) { log.error("Failed to save step '{}', '{}' for system-type '{}' with id '{}': {}", step.getId(), step.getStepType().name(), systemType.name(), externalId, e.getMessage()); log.debug("Exception", e); } } } /** * Mark the Job as an Async Job which should be terminated by external process to the current command scope. * * @param executionContext * The context which describe the running job. * @param isAsync * indicates if the job should be ended by current action */ public static void setAsyncJob(ExecutionContext executionContext, boolean isAsync) { if (executionContext == null) { return; } Job job = executionContext.getJob(); if (executionContext.getExecutionMethod() == ExecutionMethod.AsJob && job != null) { job.setIsAsyncJob(isAsync); } } /** * Evaluates if a given correlation-ID as part of the parameters is set correctly. If the correlation-ID is null or * empty, a valid correlation-ID will be set. If the correlation-ID exceeds its permitted length, an error return * value will be created and returned. * * @param parameters * The parameters input of the command * @return A {@code null} object emphasis correlation-ID is valid or {@code VdcReturnValueBase} contains the * correlation-ID violation message */ public static VdcReturnValueBase evaluateCorrelationId(HasCorrelationId parameters) { VdcReturnValueBase returnValue = null; String correlationId = parameters.getCorrelationId(); if (StringUtils.isEmpty(correlationId)) { correlationId = CorrelationIdTracker.getCorrelationId(); if (StringUtils.isEmpty(correlationId)) { correlationId = LoggedUtils.getObjectId(parameters); } parameters.setCorrelationId(correlationId); } else { List<String> messages = ValidationUtils.validateInputs(validationGroups, parameters); if (!messages.isEmpty()) { VdcReturnValueBase returnErrorValue = new VdcReturnValueBase(); returnErrorValue.setValid(false); returnErrorValue.getValidationMessages().addAll(messages); return returnErrorValue; } } return returnValue; } /** * If the provided {@link ExecutionContext} execution method is as Step the current step will * be ended with the appropriate exitStatus and so is the FINALIZING {@link Step} if present. * * @param context * The context of the execution * @param exitStatus * Indicates if the execution ended successfully or not. */ public void endFinalizingStepAndCurrentStep(ExecutionContext context, boolean exitStatus) { if (context == null) { return; } try { Step parentStep = context.getStep(); if (context.getExecutionMethod() == ExecutionMethod.AsStep && parentStep != null) { Step finalizingStep = parentStep.getStep(StepEnum.FINALIZING); if (finalizingStep != null) { finalizingStep.markStepEnded(exitStatus); jobRepository.updateStep(finalizingStep); } parentStep.markStepEnded(exitStatus); jobRepository.updateStep(parentStep); } } catch (RuntimeException e) { log.error("Exception", e); } } /** * Finalizes Job with VDSM tasks, as this case requires verification that no other steps are running in order to * close the entire Job * * @param context * The context of the execution which defines how the job should be ended * @param exitStatus * Indicates if the execution described by the job ended successfully or not. */ public void endTaskJobIfNeeded(ExecutionContext context, boolean exitStatus) { if (context == null) { return; } if (context.getExecutionMethod() == ExecutionMethod.AsJob && context.getJob() != null) { endJob(context, exitStatus); } else { Step parentStep = context.getStep(); if (context.getExecutionMethod() == ExecutionMethod.AsStep && parentStep != null) { List<Step> steps = stepDao.getStepsByJobId(parentStep.getJobId()); boolean hasChildStepsRunning = false; for (Step step : steps) { if (step.getStatus() == JobExecutionStatus.STARTED && step.getParentStepId() != null) { hasChildStepsRunning = true; break; } } if (!hasChildStepsRunning) { endJob(exitStatus, jobRepository.getJob(parentStep.getJobId())); } } } } /** * If the provided {@link ExecutionContext} execution method is as Step the current step will * be ended with the appropriate exitStatus and so is the FINALIZING {@link Step} if present. * If needed the job will be ended as well. * * @param context * The context of the execution which defines how the job should be ended * @param exitStatus * Indicates if the execution described by the job ended successfully or not. */ public void endTaskStepAndJob(ExecutionContext context, boolean exitStatus) { endFinalizingStepAndCurrentStep(context, exitStatus); endTaskJobIfNeeded(context, exitStatus); } /** * Checks if a Job has any Step associated with VDSM task * * @param context * The context of the execution stores the Job * @return true if Job has any Step for VDSM Task, else false. */ public boolean checkIfJobHasTasks(ExecutionContext context) { if (context == null || !context.isMonitored()) { return false; } try { Guid jobId = null; if (context.getExecutionMethod() == ExecutionMethod.AsJob && context.getJob() != null) { jobId = context.getJob().getId(); } else if (context.getExecutionMethod() == ExecutionMethod.AsStep && context.getStep() != null) { jobId = context.getStep().getId(); } if (jobId != null) { return jobDao.checkIfJobHasTasks(jobId); } } catch (RuntimeException e) { log.error("Exception", e); } return false; } /** * Updates Job for the same entity for a specific action as completed with a given exit status. * * @param entityId * The entity to search for its jobs * @param actionType * The action type to search for * @param status * The exist status to be set for the job */ public void updateSpecificActionJobCompleted(Guid entityId, VdcActionType actionType, boolean status) { try { List<Job> jobs = jobRepository.getJobsByEntityAndAction(entityId, actionType); for (Job job : jobs) { if (job.getStatus() == JobExecutionStatus.STARTED) { job.markJobEnded(status); } jobRepository.updateCompletedJobAndSteps(job); } } catch (RuntimeException e) { log.error("Exception", e); } } }