use of com.emc.storageos.workflow.Workflow.StepStatus in project coprhd-controller by CoprHD.
the class WorkflowService method printStepStatuses.
private void printStepStatuses(Collection<StepStatus> stepStatuses) {
for (StepStatus status : stepStatuses) {
Date startTime = status.startTime;
Date endTime = status.endTime;
if (startTime != null && endTime != null) {
_log.info(String.format("Step: %s (%s) state: %s message: %s started: %s completed: %s elapsed: %d ms", status.stepId, status.description, status.state, status.message, status.startTime, status.endTime, (status.endTime.getTime() - status.startTime.getTime())));
} else {
_log.info(String.format("Step: %s (%s) state: %s message: %s ", status.stepId, status.description, status.state, status.message));
}
}
}
use of com.emc.storageos.workflow.Workflow.StepStatus in project coprhd-controller by CoprHD.
the class WorkflowService method determineIfRollbackCompleted.
/**
* Returns true if all the Rollback StepStates are SUCCESS.
* Returns false if no Rollback was never initiated or some rollback states did not complete.
*
* @param workflow
* URI
* @return true iff all the Rollback StepStates are SUCCESS.
*/
private boolean determineIfRollbackCompleted(Workflow workflow) {
// If haven't initiated rollback, then return false.
if (workflow.isRollbackState() == false) {
return false;
}
boolean rollbackComplete = true;
Map<String, Step> stepMap = workflow.getStepMap();
for (Step step : stepMap.values()) {
// Do not consider non-rollback steps
if (!step.isRollbackStep()) {
continue;
}
StepStatus status = workflow.getStepStatus(step.stepId);
if (status.isTerminalState() == false || status.state != StepState.SUCCESS) {
_log.info(String.format("Rollback step %s not successful, state %s", step.stepId, status.state.name()));
rollbackComplete = false;
}
}
return rollbackComplete;
}
use of com.emc.storageos.workflow.Workflow.StepStatus in project coprhd-controller by CoprHD.
the class WorkflowService method doWorkflowEndProcessing.
/**
* End of Workflow processing that used to be in WorkflowExecutor.
* Initiates rollback if necessary, does final task completer.
*
* @param workflow
* @param automaticRollback
* @param workflowLock
* -- released only if workflow is deleted
* @return deleted
* @throws DeviceControllerException
*/
private boolean doWorkflowEndProcessing(Workflow workflow, boolean automaticRollback, InterProcessLock workflowLock) throws DeviceControllerException {
Map<String, StepStatus> statusMap = workflow.getStepStatusMap();
// Print out the status of each step into the log.
printStepStatuses(statusMap.values());
// Get the WorkflowState
WorkflowState state = workflow.getWorkflowStateFromSteps();
// Clear the suspend step so we will execute if resumed.
if (statusMap != null && workflow.getSuspendSteps() != null) {
for (Map.Entry<String, StepStatus> statusEntry : statusMap.entrySet()) {
if (statusEntry.getValue() != null && statusEntry.getValue().state != null && (statusEntry.getValue().state == StepState.SUSPENDED_ERROR || statusEntry.getValue().state == StepState.SUSPENDED_NO_ERROR)) {
_log.info("Removing step " + statusEntry.getValue().description + " from the suspended steps list in workflow " + workflow._workflowURI.toString());
URI suspendStepURI = workflow.getStepMap().get(statusEntry.getKey()).workflowStepURI;
workflow.getSuspendSteps().remove(suspendStepURI);
persistWorkflow(workflow);
}
}
}
// Get composite status and status message
if (workflow._successMessage == null) {
workflow._successMessage = String.format("Operation %s for task %s completed successfully", workflow._orchMethod, workflow._orchTaskId);
}
String[] errorMessage = new String[] { workflow._successMessage };
_log.info(String.format("Workflow %s overall state: %s (%s)", workflow.getOrchTaskId(), state, errorMessage[0]));
ServiceError error = Workflow.getOverallServiceError(statusMap);
// Check for user requested terminate
if (state == WorkflowState.ERROR && error == null && workflow.isRollingBackFromSuspend()) {
WorkflowException exception = WorkflowException.exceptions.workflowTerminatedByRequest();
error = ServiceError.buildServiceError(exception.getServiceCode(), exception.getLocalizedMessage());
}
// Initiate rollback if needed.
if (automaticRollback && !workflow.isRollbackState() && (state == WorkflowState.ERROR || state == WorkflowState.SUSPENDED_ERROR)) {
if (workflow.isSuspendOnError()) {
_log.info(String.format("Suspending workflow %s on error, no rollback initiation", workflow.getWorkflowURI()));
state = WorkflowState.SUSPENDED_ERROR;
} else if (initiateRollback(workflow)) {
// Return now, wait until the rollback completions come here again.
workflow.setWorkflowState(WorkflowState.ROLLING_BACK);
persistWorkflow(workflow);
logWorkflow(workflow, true);
_log.info(String.format("Rollback initiated workflow %s", workflow.getWorkflowURI()));
return false;
}
}
// Save the updated workflow state
workflow.setWorkflowState(state);
persistWorkflow(workflow);
logWorkflow(workflow, true);
try {
// Check if rollback completed.
if (workflow.isRollbackState()) {
if (workflow._rollbackHandler != null) {
workflow._rollbackHandler.rollbackComplete(workflow, workflow._rollbackHandlerArgs);
}
}
// Check for workflow completer callback.
if (workflow._callbackHandler != null) {
workflow._callbackHandler.workflowComplete(workflow, workflow._callbackHandlerArgs);
}
// Throw task completer if supplied.
if (workflow._taskCompleter != null) {
switch(state) {
case ERROR:
workflow._taskCompleter.error(_dbClient, _locker, error);
break;
case SUCCESS:
workflow._taskCompleter.ready(_dbClient, _locker);
break;
case SUSPENDED_ERROR:
workflow._taskCompleter.suspendedError(_dbClient, _locker, error);
break;
case SUSPENDED_NO_ERROR:
workflow._taskCompleter.suspendedNoError(_dbClient, _locker);
break;
default:
break;
}
}
} finally {
logWorkflow(workflow, true);
// Release the Workflow's locks, if any.
boolean removed = _ownerLocker.releaseLocks(workflow.getWorkflowURI().toString());
if (!removed) {
_log.error("Unable to release workflow locks for: " + workflow.getWorkflowURI().toString());
}
// Remove the workflow from ZK unless it is suspended (either for an error, or no error)
if (workflow.getWorkflowState() != WorkflowState.SUSPENDED_ERROR && workflow.getWorkflowState() != WorkflowState.SUSPENDED_NO_ERROR) {
removed = false;
if (!workflow._nested) {
// Remove the workflow from ZK unless it is suspended (either for an error, or no error)
unlockWorkflow(workflow, workflowLock);
destroyWorkflow(workflow);
return true;
} else {
if (isExistingWorkflow(workflow)) {
_log.info(String.format("Workflow %s is nested, destruction deferred until parent destroys", workflow.getWorkflowURI()));
}
logWorkflow(workflow, true);
}
}
}
return false;
}
use of com.emc.storageos.workflow.Workflow.StepStatus in project coprhd-controller by CoprHD.
the class WorkflowService method initiateRollback.
/**
* Initiate a rollback of the entire workflow.
*
* @param workflow
* - The workflow to be rolled back.
* @return true if rollback initiated, false if suspended.
*/
public boolean initiateRollback(Workflow workflow) throws WorkflowException {
// Verify all existing steps are in a terminal state.
Map<String, StepStatus> statusMap = workflow.getAllStepStatus();
for (StepStatus status : statusMap.values()) {
if (false == status.isTerminalState()) {
throw new WorkflowException("Step: " + status.stepId + " is not in a terminal state: " + status.state);
}
}
// Make sure all non-cancelled nodes have a rollback method.
// TODO: handle null rollback methods better.
boolean norollback = false;
for (Step step : workflow.getStepMap().values()) {
// Suspended no error steps have not run, treat them as cancelled
if (step.status.state == StepState.SUSPENDED_NO_ERROR) {
step.status.updateState(StepState.CANCELLED, ServiceCode.WORKFLOW_STEP_CANCELLED, "Step cancelled because rollback was initiated");
persistWorkflowStep(workflow, step);
continue;
}
if (step.status.state != StepState.CANCELLED && step.rollbackMethod == null) {
_log.error(String.format("Cannot rollback step %s because it does not have a rollback method", step.stepId));
norollback = true;
}
}
if (norollback) {
return false;
}
_log.info("Generating rollback steps for workflow: " + workflow.getWorkflowURI());
// Going to try and initiate the rollback.
if (workflow._rollbackHandler != null) {
workflow._rollbackHandler.initiatingRollback(workflow, workflow._rollbackHandlerArgs);
}
// Determine the steps that need to be rolled back.
// Maps step original stepId to rollback Step.
Map<String, Step> rollbackStepMap = new HashMap<String, Step>();
// Contains dependencies for the rollback Steps organized into Step Groups..
Map<String, Set<String>> rollbackStepGroupMap = new HashMap<String, Set<String>>();
// Map of step ids or stepGroups names to execution step ids having a dependence on this step/group
Map<String, List<String>> dependenceMap = new HashMap<String, List<String>>();
for (Step step : workflow.getStepMap().values()) {
// Don't process cancelled nodes, they don't need to be rolled back.
if (step.status.state == StepState.CANCELLED) {
continue;
}
// If we have a dependence, put it in the dependence map
if (step.waitFor != null) {
if (dependenceMap.get(step.waitFor) == null) {
dependenceMap.put(step.waitFor, new ArrayList<String>());
}
// Step is dependent on the indicated waitFor
dependenceMap.get(step.waitFor).add(step.stepId);
}
// Compute the corresponding rollback node.
Step rb = step.generateRollbackStep();
rollbackStepMap.put(step.stepId, rb);
}
// or it can be dependent on the stepGroup containing es1.
for (Step executeStep : workflow.getStepMap().values()) {
if (executeStep.status.state == StepState.CANCELLED) {
continue;
}
Step rollbackStep = rollbackStepMap.get(executeStep.stepId);
String stepGroupKey = "_rollback_" + rollbackStep.stepId;
rollbackStepGroupMap.put(stepGroupKey, new HashSet<String>());
// rollback nodes corresponding to the direct dependents of executeStep
List<String> dependentList = dependenceMap.get(executeStep.stepId);
if (dependentList != null) {
for (String dependentId : dependentList) {
Step dependentRollbackStep = rollbackStepMap.get(dependentId);
if (dependentRollbackStep == null) {
continue;
}
rollbackStepGroupMap.get(stepGroupKey).add(dependentRollbackStep.stepId);
}
}
// rollback nodes corresponding to the dependents in the executeStep's stepGroup
dependentList = dependenceMap.get(executeStep.stepGroup);
if (dependentList != null) {
for (String dependentId : dependentList) {
Step dependentRollbackStep = rollbackStepMap.get(dependentId);
if (dependentRollbackStep == null) {
continue;
}
rollbackStepGroupMap.get(stepGroupKey).add(dependentRollbackStep.stepId);
}
}
// If we have dependencies, then set the waitFor to point to our group.
if (false == rollbackStepGroupMap.get(stepGroupKey).isEmpty()) {
rollbackStep.waitFor = stepGroupKey;
}
}
// Print what is being added.
for (Step step : rollbackStepMap.values()) {
_log.info(String.format("Adding rollback node %s (%s) waitFor: %s", step.stepId, step.description, step.waitFor));
}
for (String key : rollbackStepGroupMap.keySet()) {
_log.info(String.format("Adding group %s members %s", key, rollbackStepGroupMap.get(key)));
}
// Add all the rollback Steps and new dependence Groups
for (Step rollbackStep : rollbackStepMap.values()) {
StepStatus status = new StepStatus();
status.stepId = rollbackStep.stepId;
status.state = StepState.CREATED;
status.description = rollbackStep.description;
rollbackStep.status = status;
workflow.getStepMap().put(rollbackStep.stepId, rollbackStep);
workflow.getStepStatusMap().put(rollbackStep.stepId, status);
}
workflow.getStepGroupMap().putAll(rollbackStepGroupMap);
workflow.setRollbackState(true);
workflow.setWorkflowState(WorkflowState.ROLLING_BACK);
// Persist the workflow since we added the rollback groups
persistWorkflow(workflow);
logWorkflow(workflow, true);
// Now queue all the new steps.
for (Step step : rollbackStepMap.values()) {
queueWorkflowStep(workflow, step);
}
return true;
}
use of com.emc.storageos.workflow.Workflow.StepStatus in project coprhd-controller by CoprHD.
the class WorkflowService method isBlocked.
/**
* Determine if a workflow step is blocked. A step is blocked if it has a waitFor clause
* pointing to a step or step group that is not in the SUCCESS state.
* If a pre-requisite step has errored or been cancelled, a CancelledException is thrown.
*
* @param workflow
* Workflow containing the Step
* @param step
* Step checked.
* @return true if the step is blocked waiting on a pre-requiste step to complete, false if runnable now.
* @throws CancelledException
* if a prerequisite step has had an error or has been cancelled
* or if this step (or all steps) should be cancelled because of suspend request.
*/
boolean isBlocked(Workflow workflow, Step step) throws WorkflowException, CancelledException {
// The step cannot be blocked if waitFor is null (which means not specified)
if (step.waitFor == null) {
return false;
}
Map<String, StepStatus> statusMap = new HashMap<String, StepStatus>();
try {
StepStatus status = workflow.getStepStatus(step.waitFor);
statusMap.put(step.waitFor, status);
} catch (WorkflowException ex1) {
try {
statusMap = workflow.getStepGroupStatus(step.waitFor);
} catch (WorkflowException ex2) {
throw new WorkflowException(String.format("Workflow step %s waitFor %s invalid, must be stepId or stepGroup name", step.stepId, step.waitFor));
}
}
String[] errorMessage = new String[1];
StepState state = Workflow.getOverallState(statusMap, errorMessage);
switch(state) {
case SUSPENDED_NO_ERROR:
case SUSPENDED_ERROR:
case CANCELLED:
throw new CancelledException();
case ERROR:
if ((workflow.getRollbackContOnError()) && (workflow.isRollbackState())) {
_log.info("Allowing rollback to continue despite failure in previous rollback step.");
return false;
}
throw new CancelledException();
case SUCCESS:
return false;
case CREATED:
case BLOCKED:
case QUEUED:
case EXECUTING:
default:
return true;
}
}
Aggregations