Search in sources :

Example 1 with StepStatus

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);
    }
}
Also used : DataObject(com.emc.storageos.db.client.model.DataObject) Step(com.emc.storageos.workflow.Workflow.Step) StepStatus(com.emc.storageos.workflow.Workflow.StepStatus) InternalException(com.emc.storageos.svcs.errorhandling.resources.InternalException) DatabaseException(com.emc.storageos.db.exceptions.DatabaseException) DeviceControllerException(com.emc.storageos.exceptions.DeviceControllerException) ControllerException(com.emc.storageos.volumecontroller.ControllerException) LockRetryException(com.emc.storageos.locking.LockRetryException)

Example 2 with StepStatus

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;
}
Also used : ArrayList(java.util.ArrayList) StepStatus(com.emc.storageos.workflow.Workflow.StepStatus) LockRetryException(com.emc.storageos.locking.LockRetryException) InternalException(com.emc.storageos.svcs.errorhandling.resources.InternalException) DatabaseException(com.emc.storageos.db.exceptions.DatabaseException) DeviceControllerException(com.emc.storageos.exceptions.DeviceControllerException) ControllerException(com.emc.storageos.volumecontroller.ControllerException) LockRetryException(com.emc.storageos.locking.LockRetryException)

Example 3 with StepStatus

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);
    }
}
Also used : Task(com.emc.storageos.db.client.model.Task) ArrayList(java.util.ArrayList) StepStatus(com.emc.storageos.workflow.Workflow.StepStatus) URI(java.net.URI) DatabaseException(com.emc.storageos.db.exceptions.DatabaseException) HashSet(java.util.HashSet)

Example 4 with StepStatus

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);
        }
    }
}
Also used : InterProcessLock(org.apache.curator.framework.recipes.locks.InterProcessLock) StepStatus(com.emc.storageos.workflow.Workflow.StepStatus) Step(com.emc.storageos.workflow.Workflow.Step) InternalException(com.emc.storageos.svcs.errorhandling.resources.InternalException) DatabaseException(com.emc.storageos.db.exceptions.DatabaseException) DeviceControllerException(com.emc.storageos.exceptions.DeviceControllerException) ControllerException(com.emc.storageos.volumecontroller.ControllerException) LockRetryException(com.emc.storageos.locking.LockRetryException)

Example 5 with StepStatus

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);
    }
}
Also used : ServiceCoded(com.emc.storageos.svcs.errorhandling.model.ServiceCoded) StepStatus(com.emc.storageos.workflow.Workflow.StepStatus)

Aggregations

StepStatus (com.emc.storageos.workflow.Workflow.StepStatus)11 DatabaseException (com.emc.storageos.db.exceptions.DatabaseException)5 DeviceControllerException (com.emc.storageos.exceptions.DeviceControllerException)4 LockRetryException (com.emc.storageos.locking.LockRetryException)4 InternalException (com.emc.storageos.svcs.errorhandling.resources.InternalException)4 ControllerException (com.emc.storageos.volumecontroller.ControllerException)4 Step (com.emc.storageos.workflow.Workflow.Step)4 ArrayList (java.util.ArrayList)3 HashMap (java.util.HashMap)3 Task (com.emc.storageos.db.client.model.Task)2 URI (java.net.URI)2 HashSet (java.util.HashSet)2 URIQueryResultList (com.emc.storageos.db.client.constraint.URIQueryResultList)1 DataObject (com.emc.storageos.db.client.model.DataObject)1 ServiceCoded (com.emc.storageos.svcs.errorhandling.model.ServiceCoded)1 ServiceError (com.emc.storageos.svcs.errorhandling.model.ServiceError)1 StepState (com.emc.storageos.workflow.Workflow.StepState)1 Date (java.util.Date)1 List (java.util.List)1 Map (java.util.Map)1