package com.emc.storageos.volumecontroller.impl; import com.emc.storageos.coordinator.client.service.DistributedLockQueueEventListener; import com.emc.storageos.coordinator.client.service.impl.DistributedLockQueueManagerImpl.Event; import com.emc.storageos.db.client.DbClient; import com.emc.storageos.db.client.model.Task; import com.emc.storageos.db.client.model.util.TaskUtils; import com.emc.storageos.db.client.util.NullColumnValueGetter; import com.emc.storageos.workflow.Workflow; import com.emc.storageos.workflow.WorkflowService; import com.google.common.base.Predicate; import com.google.common.collect.Collections2; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Calendar; import java.util.Collection; import java.util.List; /** * Asynchronous event handler for responding to addition/removal events from a running * {@link com.emc.storageos.coordinator.client.service.DistributedLockQueueManager} instance. * * @author Ian Bibby */ public class ControlRequestLockQueueListener implements DistributedLockQueueEventListener<ControlRequest> { private static final Logger log = LoggerFactory.getLogger(ControlRequestLockQueueListener.class); private static final int ORCHESTRATOR_TASK_ID_LENGTH = 36; private DbClient dbClient; private WorkflowService workflowService; public void setDbClient(DbClient dbClient) { this.dbClient = dbClient; } public void setWorkflowService(WorkflowService workflowService) { this.workflowService = workflowService; } @Override public void lockQueueEvent(ControlRequest request, Event event) { if (event == null) { return; } switch (event) { case ADDED: nodeAdded(request); break; case REMOVED: nodeRemoved(request); break; } } /** * Event handler for when a node has been added to the lock queue. * Based on the node data, this method attempts to update the status * of any associated resources, e.g. tasks and workflow steps. * * @param request An instance of ControlRequest. */ public void nodeAdded(ControlRequest request) { log.info("ControlRequest has been queued"); if (request == null) { log.warn("A null ControlRequest was added. Ignoring it."); return; // nothing to do } String id = getLastArg(request.getArg()); if (isWorkflowStepId(id)) { id = findTaskIdFromWorkflowStepId(id); } if (id != null) { List<Task> tasks = TaskUtils.findTasksForRequestId(dbClient, id); updateTasks(tasks, pendingTasksPredicate(), Task.Status.queued, request); } } public void nodeRemoved(ControlRequest request) { log.info("ControlRequest has been de-queued"); if (request == null) { return; // nothing to do } String id = getLastArg(request.getArg()); if (isWorkflowStepId(id)) { id = findTaskIdFromWorkflowStepId(id); } if (id != null) { request.setLockGroup(NullColumnValueGetter.getNullStr()); List<Task> tasks = TaskUtils.findTasksForRequestId(dbClient, id); updateTasks(tasks, queuedTasksPredicate(), Task.Status.pending, request); } } private String getLastArg(Object[] args) { return (String) args[args.length-1]; } /** * Updates tasks of a given status with queueing information. * * @param tasks List of tasks. * @param filter Filter to act only on tasks of a specific status. * @param status New task status to update with. * @param request ControlRequest instance. */ private void updateTasks(List<Task> tasks, Predicate<Task> filter, Task.Status status, ControlRequest request) { Collection<Task> filteredTasks = Collections2.filter(tasks, filter); for (Task task : filteredTasks) { log.info("Found task {} with status {}", task.getId(), task.getStatus()); task.setStatus(status.toString()); if (NullColumnValueGetter.isNotNullValue(request.getLockGroup())) { Calendar cal = Calendar.getInstance(); cal.setTimeInMillis(request.getTimestamp()); task.setQueuedStartTime(cal); task.setQueueName(request.getLockGroup()); } else { task.setQueueName(NullColumnValueGetter.getNullStr()); task.setQueuedStartTime(null); } } dbClient.persistObject(tasks); } private boolean isWorkflowStepId(String id) { return id.length() > ORCHESTRATOR_TASK_ID_LENGTH; } private Predicate<Task> queuedTasksPredicate() { return new Predicate<Task>() { @Override public boolean apply(Task task) { return task.isQueued(); } }; } private Predicate<Task> pendingTasksPredicate() { return new Predicate<Task>() { @Override public boolean apply(Task task) { return task.isPending(); } }; } /** * Given a workflow step id, recursively use the WorkflowService * to find the top-most Workflow which would have the real Task id. * * @param stepId Step id from a workflow that may potentially be nested itself. * @return The real Task id */ private String findTaskIdFromWorkflowStepId(String stepId) { Workflow workflow = workflowService.getWorkflowFromStepId(stepId); if (workflow != null && isWorkflowStepId(workflow.getOrchTaskId())) { return findTaskIdFromWorkflowStepId(workflow.getOrchTaskId()); } return (workflow == null) ? null : workflow.getOrchTaskId(); } }