use of com.emc.storageos.workflow.Workflow.StepStatus in project coprhd-controller by CoprHD.
the class WorkflowService method loadWorkflow.
/**
* This method sets up the workflow from ZK and DB data using the supplied ZK workflow path.
* The state for each of the Steps is loaded from ZK.
* This is called from updateStepStatus().
*
* @param zkWorkflowPath -- zookeeper path of the Workflow
* @return Workflow -- returns fully reconstructed workflow
* @throws WorkflowNotFound exception if cannot load workflow
*/
private Workflow loadWorkflow(String zkWorkflowPath) throws WorkflowException {
try {
Workflow workflow = (Workflow) _dataManager.getData(zkWorkflowPath, false);
// The stepMap and stepStatusMap can be large; they are saved
// separately in ZK and reconstructed from the database.
workflow._stepMap = new HashMap<String, Step>();
workflow._stepStatusMap = new HashMap<String, StepStatus>();
workflow._service = this;
// Load all the step states.
List<String> children = _dataManager.getChildren(zkWorkflowPath);
for (String child : children) {
String childPath = zkWorkflowPath + "/" + child;
Object stepObj = _dataManager.getData(childPath, false);
if (stepObj == null || false == (stepObj instanceof Step)) {
continue;
}
Step step = (Step) stepObj;
restoreStepDataFromDB(step);
workflow.getStepMap().put(step.stepId, step);
if (step.stepGroup != null) {
if (workflow.getStepGroupMap().get(step.stepGroup) == null) {
workflow.getStepGroupMap().put(step.stepGroup, new HashSet<String>());
}
workflow.getStepGroupMap().get(step.stepGroup).add(step.stepId);
}
StepStatus status = step.status;
workflow._stepStatusMap.put(step.stepId, status);
_log.debug(String.format("Loaded step %s state %s for workflow %s", step.stepId, step.status.state, workflow._orchTaskId));
}
return workflow;
} catch (Exception ex) {
_log.error("Unable to load workflow: " + zkWorkflowPath, ex);
throw WorkflowException.exceptions.workflowNotFound(zkWorkflowPath);
}
}
use of com.emc.storageos.workflow.Workflow.StepStatus in project coprhd-controller by CoprHD.
the class WorkflowService method acquireWorkflowStepLocks.
/**
* Acquires locks on behalf of a workflow step. The locks will be released at the
* end of the step, i.e. when the step is completed. This should only be called
* from within the executing workflow step.
* Note that if the same lock is already held by the workflow, it will not be
* reacquired, and will not be released until the workflow completes.
*
* @param stepId
* - Workflow step id.
* @param lockKeys
* - List of lock keys to be acquired
* @param time
* - Maximum wait time, 0 means poll
* @return
* true if locks acquired, false otherwise
*/
public boolean acquireWorkflowStepLocks(String stepId, List<String> lockKeys, long time) {
boolean gotLocks = false;
try {
Workflow workflow = null;
try {
workflow = loadWorkflowFromStepId(stepId);
} catch (WorkflowException ex) {
_log.warn("Workflow not found for stepId: " + stepId);
}
if (workflow == null) {
return false;
}
Long stepStartTimeSeconds = System.currentTimeMillis();
StepStatus stepStatus = workflow.getStepStatusMap().get(stepId);
if (stepStatus != null && stepStatus.startTime != null) {
stepStartTimeSeconds = stepStatus.startTime.getTime() / MILLISECONDS_IN_SECOND;
}
List<String> locksToAcquire = new ArrayList<String>(lockKeys);
// Remove any locks this workflow has already acquired,
// so as not to acquire them multiple times.
locksToAcquire.removeAll(_ownerLocker.getLocksForOwner(workflow.getWorkflowURI().toString()));
// Also remove all locks already acquired in this step.
locksToAcquire.removeAll(_ownerLocker.getLocksForOwner(stepId));
if (locksToAcquire.isEmpty()) {
return true;
}
gotLocks = _ownerLocker.acquireLocks(locksToAcquire, stepId, stepStartTimeSeconds, time);
} catch (LockRetryException ex) {
_log.info(String.format("Lock retry exception key: %s remaining time %d", ex.getLockIdentifier(), ex.getRemainingWaitTimeSeconds()));
WorkflowStepCompleter.stepQueued(stepId);
throw ex;
} catch (Exception ex) {
_log.info("Exception acquiring WorkflowStep locks: ", ex);
}
return gotLocks;
}
use of com.emc.storageos.workflow.Workflow.StepStatus in project coprhd-controller by CoprHD.
the class WorkflowService method logWorkflow.
/**
* Persist the Cassandra logging record for the Workflow
*
* @param workflow
* @param completed
* - If true, assumes the Workflow has been completed
* (reached a terminal state).
*/
void logWorkflow(Workflow workflow, boolean completed) {
try {
boolean created = false;
com.emc.storageos.db.client.model.Workflow logWorkflow = null;
if (workflow._workflowURI != null) {
logWorkflow = _dbClient.queryObject(com.emc.storageos.db.client.model.Workflow.class, workflow._workflowURI);
} else {
workflow._workflowURI = URIUtil.createId(com.emc.storageos.db.client.model.Workflow.class);
}
// Are we updating or adding?
if (logWorkflow == null) {
created = true;
logWorkflow = new com.emc.storageos.db.client.model.Workflow();
logWorkflow.setId(workflow._workflowURI);
logWorkflow.setCreationTime(Calendar.getInstance());
logWorkflow.setCompleted(false);
}
logWorkflow.setOrchControllerName(workflow._orchControllerName);
logWorkflow.setOrchMethod(workflow._orchMethod);
logWorkflow.setOrchTaskId(workflow._orchTaskId);
logWorkflow.setCompleted(completed);
if (completed) {
// If completed, log the final state and error message.
try {
Map<String, StepStatus> statusMap = workflow.getAllStepStatus();
String[] errorMessage = new String[] { workflow._successMessage };
Workflow.getOverallState(statusMap, errorMessage);
WorkflowState state = workflow.getWorkflowState();
logWorkflow.setCompletionState(state.name());
logWorkflow.setCompletionMessage(errorMessage[0]);
} catch (WorkflowException ex) {
_log.error(ex.getMessage(), ex);
}
}
if (created) {
_dbClient.createObject(logWorkflow);
} else {
_dbClient.updateObject(logWorkflow);
}
if (workflow.getOrchTaskId() != null) {
List<Task> tasks = new ArrayList<>();
if (workflow._taskCompleter != null && workflow._taskCompleter.getId() != null) {
Set<URI> taskIds = new HashSet<>();
// as migrating a non-CG virtual volume.
for (URI resourceId : workflow._taskCompleter.getIds()) {
Task task = TaskUtils.findTaskForRequestId(_dbClient, resourceId, workflow.getOrchTaskId());
if (task != null && !taskIds.contains(task.getId())) {
tasks.add(task);
taskIds.add(task.getId());
}
}
// instance)
for (URI resourceId : workflow._taskCompleter.getIds()) {
Task task = TaskUtils.findTaskForRequestIdAssociatedResource(_dbClient, resourceId, workflow.getOrchTaskId());
if (task != null && !taskIds.contains(task.getId())) {
tasks.add(task);
taskIds.add(task.getId());
}
}
} else {
List<Task> foundTasks = TaskUtils.findTasksForRequestId(_dbClient, workflow.getOrchTaskId());
if (foundTasks != null && !foundTasks.isEmpty()) {
tasks.addAll(foundTasks);
}
}
if (tasks != null && !tasks.isEmpty()) {
for (Task task : tasks) {
task.setWorkflow(workflow.getWorkflowURI());
}
_dbClient.updateObject(tasks);
}
}
} catch (DatabaseException ex) {
_log.error("Cannot persist Cassandra Workflow record " + workflow.getWorkflowURI().toString(), ex);
}
}
use of com.emc.storageos.workflow.Workflow.StepStatus in project coprhd-controller by CoprHD.
the class WorkflowService method updateStepStatus.
/**
* Given a ZK path to a Callback node, get the data which is a StatusUpdateMessage
* and update the appropriate step status.
*
* @param stepId
* -- The Step Id of the step.
* @param state
* @param code
* @param message
* @param automaticRollback
* whether to rollback in case of error at the end of workflow
* @throws WorkflowException
*/
private void updateStepStatus(String stepId, StepState state, ServiceCode code, String message, boolean automaticRollback) throws WorkflowException {
// String path = getZKCallbackPath(stepId);
String workflowPath = getZKStep2WorkflowPath(stepId);
Workflow workflow = null;
boolean workflowDeleted = false;
InterProcessLock lock = null;
try {
// Get the workflow path from ZK
workflowPath = (String) _dataManager.getData(workflowPath, false);
// It is not an error to try and update using a non-existent stepId
if (workflowPath == null) {
return;
}
// Load the Workflow state from ZK
workflow = (Workflow) _dataManager.getData(workflowPath, false);
if (workflow == null) {
WorkflowException ex = WorkflowException.exceptions.workflowNotFound(workflowPath);
_log.info("Workflow not found: " + workflowPath, ex);
throw ex;
}
// Lock the Workflow
lock = lockWorkflow(workflow);
// Load the entire workflow state including the steps
workflow = loadWorkflow(workflow);
if (workflow == null) {
WorkflowException ex = WorkflowException.exceptions.workflowNotFound(workflowPath);
_log.info("Workflow not found: " + workflowPath, ex);
throw ex;
}
synchronized (workflow) {
// Update the StepState structure
StepStatus status = workflow.getStepStatus(stepId);
// something else. There is an exception as WorkflowService calls this for SUSPENDED_NO_ERROR.
if (status.isTerminalState() && !(status.state == StepState.SUSPENDED_NO_ERROR || status.state == StepState.CANCELLED)) {
WorkflowException ex = WorkflowException.exceptions.workflowStepInTerminalState(stepId, status.state.name(), state.name());
_log.error(String.format("Step %s is already in terminal state %s, trying to change to %s which will be ignored", stepId, status.state.toString(), state.toString()), ex);
// are called out of a completer called from the WorkflowService.doWorkflowEndProcessing
return;
}
// If an error is reported, and we're supposed to suspend on error, suspend
// Don't put a workflow in suspended state if we're already in rollback.
Step step = workflow.getStepMap().get(stepId);
if (StepState.ERROR == state && workflow.isSuspendOnError() && !step.isRollbackStep()) {
state = StepState.SUSPENDED_ERROR;
step.suspendStep = false;
}
// SUSPENDED_ERROR step to ERROR
if (step.isRollbackStep()) {
if (step.foundingStepId != null) {
if (workflow.getStepMap().get(step.foundingStepId) != null) {
Step foundingStep = workflow.getStepMap().get(step.foundingStepId);
StepStatus foundingStatus = workflow.getStepStatus(step.foundingStepId);
if (StepState.SUSPENDED_ERROR.equals(foundingStatus.state)) {
foundingStatus.updateState(StepState.ERROR, code, message);
persistWorkflowStep(workflow, foundingStep);
}
}
}
}
_log.info(String.format("Updating workflow step: %s state %s : %s", stepId, state, message));
status.updateState(state, code, message);
// Persist the updated step state
persistWorkflowStep(workflow, step);
// to initiate a totally separate rollback.
try {
if (workflow.allStatesTerminal() && !workflow.isRollbackState() && (workflow._childWorkflows == null || workflow._childWorkflows.isEmpty())) {
InvokeTestFailure.internalOnlyInvokeTestFailure(InvokeTestFailure.ARTIFICIAL_FAILURE_004);
}
} catch (NullPointerException npe) {
// Overwrite the status of the final state
_log.error("Overwriting the state of the final step of a workflow due to artificial failure request", npe);
StepStatus ss = workflow.getStepStatus(stepId);
ss.state = StepState.ERROR;
ss.description = "Artificially thrown exception: " + InvokeTestFailure.ARTIFICIAL_FAILURE_004;
ss.message = "The final step in the workflow was successful, but an artificial failure request is configured to fail the final step to invoke full rollback.";
workflow.getStepStatusMap().put(stepId, ss);
_log.info(String.format("Updating workflow step: %s state %s : %s", stepId, state, ss.message));
WorkflowException ex = WorkflowException.exceptions.workflowInvokedFailure(ss.description);
status.updateState(ss.state, ex.getServiceCode(), ss.message);
step.status = ss;
// Persist the updated step state
persistWorkflowStep(workflow, step);
}
if (status.isTerminalState()) {
// release any step level locks held.
boolean releasedLocks = _ownerLocker.releaseLocks(stepId);
if (!releasedLocks) {
_log.info("Unable to release StepLocks for step: " + stepId);
}
// Check for any blocked steps and unblock them
checkBlockedSteps(workflow, stepId);
}
// Check to see if the workflow might be finished, or need a rollback.
if (workflow.allStatesTerminal()) {
workflowDeleted = doWorkflowEndProcessing(workflow, automaticRollback, lock);
if (workflowDeleted) {
// lock is released by end processing if the workflow is deleted
lock = null;
}
}
}
} catch (Exception ex) {
String exMsg = "Exception processing updateStepStatus stepId: " + stepId + ": " + ex.getMessage();
_log.error(exMsg, ex);
throw new WorkflowException(exMsg, ex);
} finally {
unlockWorkflow(workflow, lock);
if (workflowDeleted) {
deleteWorkflowLock(workflow);
}
}
}
use of com.emc.storageos.workflow.Workflow.StepStatus in project coprhd-controller by CoprHD.
the class NestedWorkflowRollbackHandler method rollbackComplete.
@Override
public void rollbackComplete(Workflow workflow, Object[] args) {
Workflow.WorkflowRollbackHandler originalHandler = (Workflow.WorkflowRollbackHandler) args[indexOfNestedHandler(args)];
if (originalHandler != null) {
originalHandler.initiatingRollback(workflow, args);
}
Map<String, StepStatus> stepToStepStatus = workflow.getStepStatusMap();
boolean rollbackError = false;
StringBuilder builder = new StringBuilder();
for (StepStatus stepStatus : stepToStepStatus.values()) {
if (stepStatus.description.startsWith("Rollback ") && stepStatus.state == StepState.ERROR) {
if (builder.length() > 0) {
builder.append("\n");
}
builder.append(stepStatus.message);
rollbackError = true;
}
}
String parentStepId = (String) args[indexOfParentStepId(args)];
if (rollbackError) {
ServiceCoded coded = WorkflowException.exceptions.innerWorkflowRollbackError(workflow.getWorkflowURI().toString(), builder.toString());
WorkflowStepCompleter.stepFailed(parentStepId, coded);
} else {
WorkflowStepCompleter.stepSucceded(parentStepId);
}
}
Aggregations