/* * Copyright (c) 2015 EMC Corporation * All Rights Reserved */ package com.emc.storageos.db.client.model; import java.beans.Transient; import java.net.URI; import java.util.Calendar; import java.util.List; import java.util.Objects; import java.util.StringTokenizer; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.emc.storageos.svcs.errorhandling.model.ServiceCoded; import com.google.common.base.Function; import com.google.common.collect.Lists; /** * Represents a Resource Task */ @Cf("Task") @NoInactiveIndex // Because ViPR will create/delete lots of this object, this prevents too many tombstones in Decommissioned index public class Task extends DataObject { private static final Logger _log = LoggerFactory.getLogger(Operation.class); private NamedURI resource; private String requestId; private String status; private Integer progress; private String message; private String description; private Integer serviceCode; private Calendar startTime; private Calendar endTime; private String associatedResources; private URI tenant; private boolean completedFlag; private URI workflow; private Calendar queuedStartTime; private String queueName; private StringSet warningMessages; // enumeration of status value public enum Status { queued, pending, ready, error, suspended_no_error, suspended_error; public static Status toStatus(String status) { try { return valueOf(status.toLowerCase()); } catch (Exception e) { throw new IllegalArgumentException("status: " + status + " is not a valid status"); } } } // enumeration of allowed operations public enum AllowedOperations { none_specified, retry_rollback, rollback_only, retry_only; public static AllowedOperations toAllowedOperations(String allowedOperations) { try { return valueOf(allowedOperations.toLowerCase()); } catch (Exception e) { throw new IllegalArgumentException("Operations: " + allowedOperations + " is not a valid allowed operation option"); } } } public Task() { } @NamedRelationIndex(cf = "TaskResource", types = { Volume.class, BlockSnapshot.class, VolumeGroup.class, BlockConsistencyGroup.class, Host.class, ExportGroup.class, FileShare.class, Snapshot.class }) @Name("resource") public NamedURI getResource() { return resource; } public void setResource(NamedURI resource) { if (!Objects.equals(this.resource, resource)) { this.resource = resource; setChanged("resource"); } } @Name("requestId") @AlternateId("TaskRequestIds") public String getRequestId() { return requestId; } public void setRequestId(String requestId) { if (!Objects.equals(this.requestId, requestId)) { this.requestId = requestId; setChanged("requestId"); } } @Name("taskStatus") @AggregatedIndex(cf = "AggregatedIndex", groupBy = "tenant") public String getStatus() { return status; } public void setStatus(String status) { if (!isValidStatus(status)) { throw new IllegalArgumentException("status: " + status + " is not a valid status"); } this.status = status; setChanged("taskStatus"); } @Name("progress") public Integer getProgress() { return progress; } public void setProgress(Integer progress) { this.progress = progress; setChanged("progress"); } @Name("message") public String getMessage() { return message; } public void setMessage(String message) { this.message = message; setChanged("message"); } @Name("description") public String getDescription() { return description; } public void setDescription(String description) { this.description = description; setChanged("description"); } @Name("serviceCode") public Integer getServiceCode() { return serviceCode; } public void setServiceCode(Integer serviceCode) { this.serviceCode = serviceCode; setChanged("serviceCode"); } @Name("startTime") public Calendar getStartTime() { return startTime; } public void setStartTime(Calendar startTime) { this.startTime = startTime; setChanged("startTime"); } @Name("endTime") public Calendar getEndTime() { return endTime; } public void setEndTime(Calendar endTime) { this.endTime = endTime; setChanged("endTime"); if (endTime != null) { setCompletedFlag(true); } } @Name("queuedStartTime") public Calendar getQueuedStartTime() { return queuedStartTime; } public void setQueuedStartTime(Calendar queuedStartTime) { this.queuedStartTime = queuedStartTime; setChanged("queuedStartTime"); } @Name("queueName") public String getQueueName() { return queueName; } public void setQueueName(String queueName) { this.queueName = queueName; setChanged("queueName"); } /** * Used to indicate that a task has completed * * This places an entry into a timeseries index that can later be queried * * Do not call directly, use {@link #setEndTime(java.util.Calendar)} instead */ @Name("completedFlag") @DecommissionedIndex("timeseriesIndex") public Boolean getCompletedFlag() { return completedFlag; } public void setCompletedFlag(Boolean completedFlag) { if (!Objects.equals(this.completedFlag, completedFlag)) { this.completedFlag = completedFlag; setChanged("completedFlag"); } } @Name("associatedResources") public String getAssociatedResources() { return associatedResources; } public void setAssociatedResources(String associatedResources) { this.associatedResources = associatedResources; setChanged("associatedResources"); } @Transient public List<URI> getAssociatedResourcesList() { List<URI> resources = Lists.newArrayList(); if (!StringUtils.isBlank(getAssociatedResources())) { StringTokenizer tokenizer = new StringTokenizer(getAssociatedResources(), ","); while (tokenizer.hasMoreElements()) { resources.add(URI.create(tokenizer.nextToken())); } } return resources; } @Transient public void setAssociatedResourcesList(List<URI> associatedResourcesList) { List<String> resources = Lists.transform(associatedResourcesList, new Function<URI, String>() { @Override public String apply(URI input) { return input.toString(); } }); setAssociatedResources(StringUtils.join(resources.toArray(), ",")); } @Name("tenant") @RelationIndex(type = TenantOrg.class, cf = "RelationIndex") public URI getTenant() { return tenant; } public void setTenant(URI tenant) { if (!Objects.equals(this.tenant, tenant)) { this.tenant = tenant; setChanged("tenant"); } } @Name("workflow") public URI getWorkflow() { return workflow; } public void setWorkflow(URI workflow) { if (!Objects.equals(this.workflow, workflow)) { this.workflow = workflow; setChanged("workflow"); } } /** * This method sets the status of the operation to "ready" * * @return */ public void ready() { ready("Operation completed successfully"); } /** * This method sets the status of the operation to "ready" * * @return */ public void ready(String message) { setMessage(message); setStatus(Status.ready.name()); } public boolean isQueued() { String status = getStatus(); return Status.queued.name().equals(status); } public boolean isPending() { String status = getStatus(); return status != null && status.equals(Status.pending.name()); } public boolean isError() { String status = getStatus(); return status != null && status.equals(Status.error.name()); } public boolean isReady() { String status = getStatus(); return status != null && status.equals(Status.ready.name()); } public boolean isSuspendedError() { String status = getStatus(); return status != null && status.equals(Status.suspended_error.name()); } public boolean isSuspendedNoError() { String status = getStatus(); return status != null && status.equals(Status.suspended_no_error.name()); } /** * This method sets the status of the operation to "error" * * @return */ public void error(ServiceCoded sc) { if (sc != null) { setServiceCode(sc.getServiceCode().getCode()); setMessage(sc.getMessage()); } setStatus(Status.error.name()); if (sc instanceof Exception) { _log.info("Setting operation to error due to an exception"); _log.info("Caused by: ", (Exception) sc); } } /** * Checks to see whether the provided status is one of the defined valid * status(es). * * @param statusStr * - Status String * @return true, if the provided status is valid. otherwise false. */ private boolean isValidStatus(String statusStr) { boolean valid = false; Status[] validStatus = Status.values(); for (Status status : validStatus) { if (status.name().toUpperCase().equals(statusStr.toUpperCase())) { valid = true; break; } } return valid; } /** * Checks to see whether the provided operations is one of the defined valid * allowed operations * * @param allowedOperationsStr * - Allowed operations String * @return true, if the provided operations is valid. otherwise false. */ private boolean isValidAllowedOperations(String opStr) { boolean valid = false; AllowedOperations[] validAllowedOperations = AllowedOperations.values(); for (AllowedOperations op : validAllowedOperations) { if (op.name().toUpperCase().equals(opStr.toUpperCase())) { valid = true; break; } } return valid; } @Name("warningMessages") public StringSet getWarningMessages() { if (warningMessages == null) { return new StringSet(); } return warningMessages; } public void setWarningMessages(StringSet warningMessages) { this.warningMessages = warningMessages; setChanged("warningMessages"); } public void addWarningMessage(String warningMessage) { if (warningMessages == null) { warningMessages = new StringSet(); } warningMessages.add(warningMessage); } }