use of org.apache.curator.framework.recipes.locks.InterProcessLock in project coprhd-controller by CoprHD.
the class WorkflowService method associateToParentWorkflow.
/**
* Associates workflow to a parent (outer) workflow (if any), i.e.
* this Workflow is nested within the outer one.
* Depends on the Workflow's orchestration task id being a step in the outer workflow.
*
* @param workflow
* -- potential nested Workflow
* @return true if a parent association was made.
*/
private boolean associateToParentWorkflow(Workflow workflow) {
try {
String parentPath = getZKStep2WorkflowPath(workflow.getOrchTaskId());
if (_dataManager.checkExists(parentPath) != null) {
// Record our workflow URI as a child in the parent Workflow URI.
// Get the parent workflow path from ZK
parentPath = (String) _dataManager.getData(parentPath, false);
// Load the Workflow state from ZK
if (parentPath != null) {
InterProcessLock parentLock = null;
Workflow parentWorkflow = (Workflow) _dataManager.getData(parentPath, false);
try {
parentLock = lockWorkflow(parentWorkflow);
parentWorkflow = (Workflow) _dataManager.getData(parentPath, false);
parentWorkflow._childWorkflows.add(workflow.getWorkflowURI());
persistWorkflow(parentWorkflow);
} finally {
unlockWorkflow(parentWorkflow, parentLock);
}
}
return true;
}
} catch (Exception ex) {
_log.error(ex.getMessage(), ex);
}
return false;
}
use of org.apache.curator.framework.recipes.locks.InterProcessLock in project coprhd-controller by CoprHD.
the class WorkflowService method executePlan.
/**
* Execute the workflow. It is saved here and control is passed to WorkflowExecutor.
*
* @param workflow
*/
public void executePlan(Workflow workflow) throws WorkflowException {
InterProcessLock lock = null;
try {
if (!workflow.getStepMap().isEmpty()) {
_log.info("Executing workflow plan: " + workflow.getWorkflowURI() + " " + workflow.getOrchTaskId());
workflow.setWorkflowState(WorkflowState.RUNNING);
// Mark steps that should be suspended in the workflow for later.
suspendStepsMatchingProperty(workflow);
// Make sure parent/child relationship is refreshed in case child workflow was created
// before parent was executed
workflow._nested = associateToParentWorkflow(workflow);
persistWorkflow(workflow);
for (Step step : workflow.getStepMap().values()) {
persistWorkflowStep(workflow, step);
}
// Check suspended state and modify states
if (checkSuspendedSteps(workflow)) {
_log.info("Workflow is suspended: " + workflow.getWorkflowURI());
// release any workflow locks
for (Step step : workflow.getStepMap().values()) {
if (step.status.state == StepState.SUSPENDED_NO_ERROR) {
completerStepSuspendedNoError(step.stepId);
}
}
} else {
/**
* Lock the workflow.
*/
lock = lockWorkflow(workflow);
/**
* Queue any steps that have not been queued.
*/
for (Step step : workflow.getStepMap().values()) {
if (step.status.state == StepState.CREATED) {
queueWorkflowStep(workflow, step);
}
}
}
} else {
_log.info("Workflow executed with no steps: " + workflow.getWorkflowURI());
// release any workflow locks
releaseAllWorkflowLocks(workflow);
// If no steps are to process, then just exit properly
if (workflow._taskCompleter != null) {
workflow._taskCompleter.ready(_dbClient);
}
}
} finally {
unlockWorkflow(workflow, lock);
}
}
use of org.apache.curator.framework.recipes.locks.InterProcessLock in project coprhd-controller by CoprHD.
the class WorkflowService method rollbackWorkflow.
/**
* Roll back the workflow
*
* @param uri
* - The workflow URI
*/
private void rollbackWorkflow(URI uri) {
_log.info(String.format("Rollback requested workflow: %s", uri));
Workflow workflow = loadWorkflowFromUri(uri);
if (workflow == null) {
throw WorkflowException.exceptions.workflowNotFound(uri.toString());
}
if (workflow.getWorkflowURI() == null) {
workflow.setWorkflowURI(uri);
logWorkflow(workflow, false);
persistWorkflow(workflow);
}
removeRollbackSteps(workflow);
// See if there are child Workflows that need to be rolled back.
// These are roll-backed first.
Map<String, com.emc.storageos.db.client.model.Workflow> childWFMap = getChildWorkflowsMap(workflow);
for (Entry<String, com.emc.storageos.db.client.model.Workflow> entry : childWFMap.entrySet()) {
String parentStepId = entry.getKey();
Workflow child = loadWorkflowFromUri(entry.getValue().getId());
WorkflowState state = child.getWorkflowState();
switch(state) {
case SUSPENDED_ERROR:
case SUSPENDED_NO_ERROR:
_dbClient.pending(com.emc.storageos.db.client.model.Workflow.class, child.getWorkflowURI(), parentStepId, "rolling back sub-workflow");
rollbackWorkflow(child.getWorkflowURI());
Status status = waitOnOperationComplete(com.emc.storageos.db.client.model.Workflow.class, child.getWorkflowURI(), parentStepId);
_log.info(String.format("Child rollback task %s completed with state %s", child.getWorkflowURI(), status.name()));
break;
default:
continue;
}
}
// Now try to start rollback of top level Workflow
_dbClient.pending(com.emc.storageos.db.client.model.Workflow.class, uri, workflow.getOrchTaskId(), "rolling back top-level-workflow");
InterProcessLock workflowLock = null;
try {
workflowLock = lockWorkflow(workflow);
workflow.setRollingBackFromSuspend(true);
boolean rollBackStarted = initiateRollback(workflow);
if (rollBackStarted) {
_log.info(String.format("Rollback initiated workflow %s", uri));
} else {
// We were unable to initiate rollback, probably because there is no rollback handler somehwere
// Initiate end processing on the workflow, which will release the workflowLock.
doWorkflowEndProcessing(workflow, false, workflowLock);
workflowLock = null;
}
} finally {
unlockWorkflow(workflow, workflowLock);
}
}
use of org.apache.curator.framework.recipes.locks.InterProcessLock 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 org.apache.curator.framework.recipes.locks.InterProcessLock in project coprhd-controller by CoprHD.
the class InterProcessLockHolder method acquire.
public static InterProcessLockHolder acquire(CoordinatorClient client, String lockName, Logger log, int timeoutMillis) throws Exception {
InterProcessLock lock = client.getLock(lockName);
if (!lock.acquire(timeoutMillis, TimeUnit.MILLISECONDS)) {
if (log != null) {
log.info("Failed to take lock {} in {} ms", lockName, timeoutMillis);
}
return null;
}
if (log != null) {
log.info("Acquired lock: {}", lockName);
}
InterProcessLockHolder holder = new InterProcessLockHolder();
holder.lock = lock;
holder.name = lockName;
holder.log = log;
return holder;
}
Aggregations