/* * Copyright (c) 2014 EMC Corporation * All Rights Reserved */ package com.emc.storageos.volumecontroller.impl.vnxe.job; import java.io.Serializable; import java.net.URI; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.slf4j.LoggerFactory; import org.slf4j.Logger; import com.emc.storageos.db.client.DbClient; import com.emc.storageos.db.client.model.DiscoveredDataObject.Type; import com.emc.storageos.db.client.model.StoragePool; import com.emc.storageos.db.client.model.StorageSystem; import com.emc.storageos.exceptions.DeviceControllerErrors; import com.emc.storageos.svcs.errorhandling.model.ServiceError; import com.emc.storageos.vnxe.VNXeApiClient; import com.emc.storageos.vnxe.VNXeUtils; import com.emc.storageos.vnxe.models.MessageOut; import com.emc.storageos.vnxe.models.VNXeCommandJob; import com.emc.storageos.vnxe.models.VNXePool; import com.emc.storageos.volumecontroller.Job; import com.emc.storageos.volumecontroller.JobContext; import com.emc.storageos.volumecontroller.TaskCompleter; import com.emc.storageos.volumecontroller.impl.JobPollResult; public class VNXeJob extends Job implements Serializable { private static final long serialVersionUID = 1L; private static final Logger _logger = LoggerFactory.getLogger(VNXeJob.class); private static final long ERROR_TRACKING_LIMIT = 60 * 1000; // tracking limit for transient errors. set for 2 hours public static int KBYTES = 1024; // private static final long ERROR_TRACKING_LIMIT = 2*60*60*1000; private JobPollResult _pollResult = new JobPollResult(); protected List<String> _jobIds = new ArrayList<String>(); protected URI _storageSystemUri; protected TaskCompleter _taskCompleter; protected String _jobName; protected String _errorDescription = null; protected long _error_tracking_time = 0L; protected JobStatus _status = JobStatus.IN_PROGRESS; protected boolean _isSuccess = false; protected Map<String, Object> _map = new HashMap<String, Object>(); public VNXeJob(String jobId, URI storageSystemUri, TaskCompleter taskCompleter, String jobName) { _jobIds.add(jobId); _storageSystemUri = storageSystemUri; _taskCompleter = taskCompleter; _jobName = jobName; } public VNXeJob(List<String> jobIds, URI storageSystemUri, TaskCompleter taskCompleter, String jobName) { _jobIds = jobIds; _storageSystemUri = storageSystemUri; _taskCompleter = taskCompleter; _jobName = jobName; } @Override public JobPollResult poll(JobContext jobContext, long trackingPeriodInMillis) { String currentJob = _jobIds.get(0); try { _logger.info("VNXeJob: Looking up job: id {}", _jobIds.get(0)); VNXeApiClient vnxeApiClient = getVNXeClient(jobContext); StringBuilder prettyMsg = new StringBuilder(); if (vnxeApiClient == null) { String errorMessage = "No VNXe client found for: " + _storageSystemUri; processTransientError(currentJob, trackingPeriodInMillis, errorMessage, null); } else { _pollResult.setJobName(_jobName); _pollResult.setJobId(_taskCompleter.getOpId()); int completeCount = 0; boolean isSuccess = true; StringBuilder msg = new StringBuilder(); for (String jobId : _jobIds) { currentJob = jobId; VNXeCommandJob jobResult = vnxeApiClient.getJob(jobId); MessageOut msgOut = jobResult.getMessageOut(); int progressPct = jobResult.getProgressPct(); int state = jobResult.getState(); if (state == VNXeCommandJob.JobStatusEnum.FAILED.getValue()) { completeCount++; isSuccess = false; msg.append("Async task failed for jobID "); msg.append(jobId); if (msgOut != null) { msg.append(" " + msgOut.getMessage()); String cleanMsg = msgOut.getMessage().split("\\(Error Code")[0]; prettyMsg.append(cleanMsg); } continue; } if (progressPct == 100 && state != VNXeCommandJob.JobStatusEnum.RUNNING.getValue()) { completeCount++; if (state != VNXeCommandJob.JobStatusEnum.COMPLETED.getValue()) { msg.append("Async task failed for jobID "); msg.append(jobId); if (msgOut != null) { msg.append(" " + msgOut.getMessage()); String cleanMsg = msgOut.getMessage().split("\\(Error Code")[0]; prettyMsg.append(cleanMsg); } } } } if (completeCount == _jobIds.size()) { // all completed _pollResult.setJobPercentComplete(100); if (isSuccess) { _status = JobStatus.SUCCESS; _logger.info("Job: {} succeeded", _jobName); } else { _status = JobStatus.FAILED; _errorDescription = prettyMsg.toString(); _logger.info(msg.toString()); } } else { _pollResult.setJobPercentComplete(100 * completeCount / _jobIds.size()); } } } catch (Exception e) { processTransientError(currentJob, trackingPeriodInMillis, e.getMessage(), e); } finally { try { updateStatus(jobContext); } catch (Exception e) { setErrorStatus(e.getMessage()); _logger.error("Problem while trying to update status", e); } } _pollResult.setJobStatus(_status); _pollResult.setErrorDescription(_errorDescription); return _pollResult; } public void updateStatus(JobContext jobContext) throws Exception { if (_status == JobStatus.SUCCESS) { _taskCompleter.ready(jobContext.getDbClient()); } else if (_status == JobStatus.FAILED || _status == JobStatus.FATAL_ERROR) { ServiceError error = DeviceControllerErrors.vnxe.jobFailed(_errorDescription,""); _taskCompleter.error(jobContext.getDbClient(), error); } } private void processTransientError(String jobId, long trackingInterval, String errorMessage, Exception ex) { _status = JobStatus.ERROR; _errorDescription = errorMessage; if (ex != null) { _logger.error(String.format("Error while processing VNXeJob - Name: %s, ID: %s, Desc: %s Status: %s", _jobName, jobId, _errorDescription, _status), ex); } else { _logger.error(String.format("Error while processing VNXeJob - Name: %s, ID: %s, Desc: %s Status: %s", _jobName, jobId, _errorDescription, _status)); } // Check if job tracking limit was reached. Set status to FAILED in such a case. setErrorTrackingTime(_error_tracking_time + trackingInterval); _logger.info(String.format("Tracking time of VNXeJob in transient error status - %s, Name: %s, ID: %s. Status %s .", _error_tracking_time, _jobName, jobId, _status)); if (_error_tracking_time > ERROR_TRACKING_LIMIT) { _status = JobStatus.FATAL_ERROR; _logger.error(String.format("Reached tracking time limit for VNXeJob - Name: %s, ID: %s. Set status to %s .", _jobName, jobId, _status)); } } public void setErrorTrackingTime(long trackingTime) { _error_tracking_time = trackingTime; } public void setErrorStatus(String errorDescription) { _status = JobStatus.FATAL_ERROR; _errorDescription = errorDescription; } public List<String> getJobIds() { return _jobIds; } public void setJobId(List<String> _jobIds) { this._jobIds = _jobIds; } public URI getStorageSystemUri() { return _storageSystemUri; } public void setStorageSystemUri(URI _storageSystemUri) { this._storageSystemUri = _storageSystemUri; } public TaskCompleter getTaskCompleter() { return _taskCompleter; } public void setTaskCompleter(TaskCompleter _taskCompleter) { this._taskCompleter = _taskCompleter; } public String getJobName() { return _jobName; } public void setJobName(String _jobName) { this._jobName = _jobName; } public boolean getIsSuccess() { return _isSuccess; } public void setIsSuccess(boolean success) { _isSuccess = success; } /** * Get VNXe API client * * @param jobContext * @return */ public VNXeApiClient getVNXeClient(JobContext jobContext) { VNXeApiClient vnxeApiClient = null; StorageSystem storageSystem = jobContext.getDbClient().queryObject(StorageSystem.class, _storageSystemUri); if (Type.unity.name().equalsIgnoreCase(storageSystem.getSystemType())) { vnxeApiClient = jobContext.getVNXeApiClientFactory().getUnityClient( storageSystem.getIpAddress(), storageSystem.getPortNumber(), storageSystem.getUsername(), storageSystem.getPassword()); } else { vnxeApiClient = jobContext.getVNXeApiClientFactory().getClient( storageSystem.getIpAddress(), storageSystem.getPortNumber(), storageSystem.getUsername(), storageSystem.getPassword()); } return vnxeApiClient; } /** * Update storage pool capacity * * @param dbClient * @param vnxeApiClient * @param storagePoolUri * @param reservedCapacityVolumesIds The volumes reserved capacity in the pool that needs to be removed */ public static void updateStoragePoolCapacity(DbClient dbClient, VNXeApiClient vnxeApiClient, URI storagePoolUri, List<String> reservedCapacityVolumeIds) { StoragePool storagePool = dbClient.queryObject(StoragePool.class, storagePoolUri); if (reservedCapacityVolumeIds != null && !reservedCapacityVolumeIds.isEmpty()) { storagePool.removeReservedCapacityForVolumes(reservedCapacityVolumeIds); } String poolNativeId = storagePool.getNativeId(); VNXePool pool = vnxeApiClient.getPool(poolNativeId); storagePool.setFreeCapacity(VNXeUtils.convertDoubleSizeToViPRLong(pool.getSizeFree())); storagePool.setSubscribedCapacity(VNXeUtils.convertDoubleSizeToViPRLong(pool.getSizeSubscribed())); dbClient.updateObject(storagePool); } }