/*
* Copyright (c) 2015-2016 EMC Corporation
* All Rights Reserved
*/
package com.emc.storageos.volumecontroller.impl.isilon.job;
import java.io.Serializable;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.emc.storageos.db.client.model.StorageSystem;
import com.emc.storageos.exceptions.DeviceControllerErrors;
import com.emc.storageos.isilon.restapi.IsilonApi;
import com.emc.storageos.isilon.restapi.IsilonException;
import com.emc.storageos.isilon.restapi.IsilonSyncPolicy;
import com.emc.storageos.isilon.restapi.IsilonSyncPolicy.JobState;
import com.emc.storageos.isilon.restapi.IsilonSyncPolicyReport;
import com.emc.storageos.isilon.restapi.IsilonSyncTargetPolicy;
import com.emc.storageos.isilon.restapi.IsilonSyncTargetPolicy.FOFB_STATES;
import com.emc.storageos.svcs.errorhandling.model.ServiceError;
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 IsilonSyncJobFailover extends Job implements Serializable {
private static final Logger _logger = LoggerFactory.getLogger(IsilonSyncIQJob.class);
private static final long ERROR_TRACKING_LIMIT = 2 * 60 * 60 * 1000; // tracking limit for transient errors. set for 2 hours
protected String _jobName;
protected URI _storageSystemUri;
protected TaskCompleter _taskCompleter;
protected List<String> _jobIds = new ArrayList<String>();
protected long _error_tracking_time = 0L;
protected JobStatus _status = JobStatus.IN_PROGRESS;
// status of job.updateStatus() execution
protected JobStatus _postProcessingStatus = JobStatus.SUCCESS;
protected Map<String, Object> _map = new HashMap<String, Object>();
protected JobPollResult _pollResult = new JobPollResult();
protected String _errorDescription = null;
public IsilonSyncJobFailover(String jobId, URI storageSystemUri, TaskCompleter taskCompleter, String jobName) {
this._storageSystemUri = storageSystemUri;
this._taskCompleter = taskCompleter;
this._jobName = jobName;
this._jobIds.add(jobId);
}
@Override
public JobPollResult poll(JobContext jobContext, long trackingPeriodInMillis) {
String currentJob = _jobIds.get(0);
try {
IsilonApi isiApiClient = getIsilonRestClient(jobContext);
if (isiApiClient == null) {
String errorMessage = "No Isilon REST API client found for: " + _storageSystemUri;
processTransientError(currentJob, trackingPeriodInMillis, errorMessage, null);
} else {
_pollResult.setJobName(_jobName);
_pollResult.setJobId(_taskCompleter.getOpId());
IsilonSyncTargetPolicy policy = null;
IsilonSyncPolicy.JobState policyState = null;
policy = isiApiClient.getTargetReplicationPolicy(currentJob);
policyState = policy.getLastJobState();
if (policyState != null && policyState.equals(JobState.running) ||
policy.equals(FOFB_STATES.enabling_writes)) {
_status = JobStatus.IN_PROGRESS;
} else if (policyState != null && policyState.equals(JobState.finished)) {
_status = JobStatus.SUCCESS;
_pollResult.setJobPercentComplete(100);
_logger.info("IsilonSyncIQJob: {} succeeded", currentJob);
} else if (policyState != null && policyState.equals(JobState.failed)) {
_errorDescription = isiGetReportErrMsg(isiApiClient.getTargetReplicationPolicyReports(currentJob).getList());
_pollResult.setJobPercentComplete(100);
_pollResult.setErrorDescription(_errorDescription);
_status = JobStatus.FAILED;
_logger.error("IsilonSyncIQJob: {} failed; Details: {}", currentJob, _errorDescription);
}
}
} 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);
return _pollResult;
}
@Override
public TaskCompleter getTaskCompleter() {
return _taskCompleter;
}
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.isilon.jobFailed(_errorDescription);
_taskCompleter.error(jobContext.getDbClient(), error);
}
}
public void setErrorStatus(String errorDescription) {
_status = JobStatus.FATAL_ERROR;
_errorDescription = errorDescription;
}
public void setErrorTrackingTime(long trackingTime) {
_error_tracking_time = trackingTime;
}
/**
* Get Isilon API client
*
* @param jobContext
* @return
*/
public IsilonApi getIsilonRestClient(JobContext jobContext) {
StorageSystem device = jobContext.getDbClient().queryObject(StorageSystem.class, _storageSystemUri);
if (jobContext.getIsilonApiFactory() != null) {
IsilonApi isilonAPI;
URI deviceURI;
try {
deviceURI = new URI("https", null, device.getIpAddress(), device.getPortNumber(), "/", null, null);
} catch (URISyntaxException ex) {
throw IsilonException.exceptions.errorCreatingServerURL(device.getIpAddress(), device.getPortNumber(), ex);
}
// get rest client
if (device.getUsername() != null && !device.getUsername().isEmpty()) {
isilonAPI = jobContext.getIsilonApiFactory().getRESTClient(deviceURI, device.getUsername(), device.getPassword());
} else {
isilonAPI = jobContext.getIsilonApiFactory().getRESTClient(deviceURI);
}
return isilonAPI;
}
return null;
}
protected 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 Isilon SyncIQ Job - Name: %s, ID: %s, Desc: %s Status: %s",
_jobName, jobId, _errorDescription, _status), ex);
} else {
_logger.error(String.format("Error while processing Isilon SyncIQ - 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 Isilon SyncIQ 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 Isilon SyncIQ - Name: %s, ID: %s. Set status to %s .",
_jobName, jobId, _status));
}
}
protected String isiGetReportErrMsg(List<IsilonSyncPolicyReport> policyReports) {
String errorMessage = "";
for (IsilonSyncPolicyReport report : policyReports) {
if (report.getState().equals(JobState.failed) || report.getState().equals(JobState.needs_attention)) {
errorMessage = report.getErrors()[0];
break;
} else {
continue;
}
}
return errorMessage;
}
protected IsilonSyncPolicyReport isiGetReportErr(List<IsilonSyncPolicyReport> policyReports) {
for (IsilonSyncPolicyReport report : policyReports) {
if (report.getState().equals(JobState.failed) || report.getState().equals(JobState.needs_attention)) {
return report;
} else {
continue;
}
}
return null;
}
}