/*
* Copyright (c) 2015 EMC Corporation
* All Rights Reserved
*/
package com.emc.storageos.filereplicationcontroller;
import static java.util.Arrays.asList;
import java.net.URI;
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.DbClient;
import com.emc.storageos.db.client.model.FileShare;
import com.emc.storageos.db.client.model.StorageSystem;
import com.emc.storageos.exceptions.DeviceControllerException;
import com.emc.storageos.fileorchestrationcontroller.FileDescriptor;
import com.emc.storageos.fileorchestrationcontroller.FileOrchestrationInterface;
import com.emc.storageos.svcs.errorhandling.model.ServiceError;
import com.emc.storageos.svcs.errorhandling.resources.InternalException;
import com.emc.storageos.volumecontroller.ControllerException;
import com.emc.storageos.volumecontroller.FileStorageDevice;
import com.emc.storageos.volumecontroller.TaskCompleter;
import com.emc.storageos.volumecontroller.impl.file.FileMirrorRollbackCompleter;
import com.emc.storageos.volumecontroller.impl.file.MirrorFileCreateTaskCompleter;
import com.emc.storageos.volumecontroller.impl.file.RemoteFileMirrorOperation;
import com.emc.storageos.workflow.Workflow;
import com.emc.storageos.workflow.WorkflowException;
import com.emc.storageos.workflow.WorkflowService;
import com.emc.storageos.workflow.WorkflowStepCompleter;
/**
* FileReplicationDeviceController-specific Controller implementation with support for file Orchestration.
*/
public class FileReplicationDeviceController implements FileOrchestrationInterface, FileReplicationController {
private static final Logger log = LoggerFactory.getLogger(FileReplicationDeviceController.class);
private WorkflowService workflowService;
private DbClient dbClient;
private Map<String, FileStorageDevice> devices;
private static final String CREATE_FILE_MIRRORS_STEP = "CREATE_FILE_MIRRORS_STEP";
private static final String CREATE_FILE_MIRROR_PAIR_METH = "createMirrorSession";
private static final String ROLLBACK_MIRROR_LINKS_METHOD = "rollbackMirrorFileShareStep";
private static final String CREATE_FILE_MIRRORS_STEP_DESC = "Create MirrorFileShare Link";
private static final String ROLLBACK_METHOD_NULL = "rollbackMethodNull";
/**
* Calls to remote mirror operations on devices
*
* @param storageSystem
* @return
*/
private RemoteFileMirrorOperation getRemoteMirrorDevice(StorageSystem storageSystem) {
return (RemoteFileMirrorOperation) devices.get(storageSystem.getSystemType());
}
public WorkflowService getWorkflowService() {
return workflowService;
}
public void setWorkflowService(final WorkflowService workflowService) {
this.workflowService = workflowService;
}
public DbClient getDbClient() {
return dbClient;
}
public void setDbClient(final DbClient dbClient) {
this.dbClient = dbClient;
}
public Map<String, FileStorageDevice> getDevices() {
return devices;
}
public void setDevices(final Map<String, FileStorageDevice> devices) {
this.devices = devices;
}
/**
* Create mirror session or link between source fileshare and target fileshare
*/
@Override
public String addStepsForCreateFileSystems(Workflow workflow,
String waitFor, List<FileDescriptor> filesystems, String taskId)
throws InternalException {
List<FileDescriptor> fileDescriptors = FileDescriptor.filterByType(filesystems,
new FileDescriptor.Type[] { FileDescriptor.Type.FILE_MIRROR_SOURCE,
FileDescriptor.Type.FILE_MIRROR_TARGET, FileDescriptor.Type.FILE_EXISTING_MIRROR_SOURCE },
new FileDescriptor.Type[] {});
if (fileDescriptors.isEmpty()) {
log.info("No Create Mirror Steps required");
return waitFor;
}
log.info("Adding Create Mirror steps for create fileshares");
// Create replication relationships
waitFor = createElementReplicaSteps(workflow, waitFor, fileDescriptors);
return waitFor = CREATE_FILE_MIRRORS_STEP;
}
/**
* Expand source file share and target fileshare
*/
@Override
public String addStepsForExpandFileSystems(Workflow workflow,
String waitFor, List<FileDescriptor> fileDescriptors, String taskId)
throws InternalException {
// TBD
return null;
}
/**
* Create Replication Session Step
*
* @param workflow
* @param waitFor
* @param fileDescriptors
* @return
*/
private String createElementReplicaSteps(final Workflow workflow, String waitFor,
final List<FileDescriptor> fileDescriptors) {
log.info("START create element replica steps");
List<FileDescriptor> sourceDescriptors = FileDescriptor.filterByType(fileDescriptors, FileDescriptor.Type.FILE_MIRROR_SOURCE,
FileDescriptor.Type.FILE_EXISTING_MIRROR_SOURCE);
Map<URI, FileShare> uriFileShareMap = queryFileShares(fileDescriptors);
// call to create mirror session
String newWaitFor = createFileMirrorSession(workflow, waitFor, sourceDescriptors, uriFileShareMap);
return newWaitFor;
}
/**
* Create Mirror Work Flow Step - creates replication session between source and target
*
* @param workflow
* @param waitFor
* @param sourceDescriptors
* @param uriFileShareMap
* @return
*/
protected String createFileMirrorSession(Workflow workflow, String waitFor, List<FileDescriptor> sourceDescriptors,
Map<URI, FileShare> uriFileShareMap) {
for (FileDescriptor sourceDescriptor : sourceDescriptors) {
FileShare source = uriFileShareMap.get(sourceDescriptor.getFsURI());
for (String targetStr : source.getMirrorfsTargets()) {
URI targetURI = URI.create(targetStr);
StorageSystem system = dbClient.queryObject(StorageSystem.class,
source.getStorageDevice());
Workflow.Method createMethod = createMirrorFilePairStep(system.getId(),
source.getId(), targetURI, null);
Workflow.Method rollbackMethod = rollbackMirrorFilePairMethod(system.getId(),
source.getId(), targetURI);
// Ensure CreateElementReplica steps are executed sequentially (CQ613404)
waitFor = workflow.createStep(CREATE_FILE_MIRRORS_STEP,
CREATE_FILE_MIRRORS_STEP_DESC, waitFor, system.getId(),
system.getSystemType(), getClass(), createMethod, rollbackMethod, null);
}
}
return waitFor = CREATE_FILE_MIRRORS_STEP;
}
private Workflow.Method createMirrorFilePairStep(
URI systemURI, URI sourceURI, URI targetURI, URI vpoolChangeUri) {
return new Workflow.Method(CREATE_FILE_MIRROR_PAIR_METH,
systemURI, sourceURI, targetURI, vpoolChangeUri);
}
/**
* Call to Create Mirror session on Storage Device
*
* @param systemURI
* @param sourceURI
* @param targetURI
* @param vpoolChangeUri
* @param opId
* @return
*/
public boolean createMirrorSession(
URI systemURI, URI sourceURI, URI targetURI, URI vpoolChangeUri, String opId) {
log.info("Create Mirror Session between source and Target Pair");
TaskCompleter completer = null;
try {
WorkflowStepCompleter.stepExecuting(opId);
StorageSystem system = getStorageSystem(systemURI);
completer = new MirrorFileCreateTaskCompleter(sourceURI, targetURI, vpoolChangeUri, opId);
getRemoteMirrorDevice(system).doCreateMirrorLink(system, sourceURI, targetURI, completer);
log.info("Source: {}", sourceURI);
log.info("Target: {}", targetURI);
log.info("OpId: {}", opId);
} catch (Exception e) {
ServiceError error = DeviceControllerException.errors.jobFailed(e);
if (null != completer) {
completer.error(dbClient, error);
}
WorkflowStepCompleter.stepFailed(opId, error);
return false;
}
return true;
}
// roll back mirror session
private Workflow.Method rollbackMirrorFilePairMethod(final URI systemURI, final URI sourceURI,
final URI targetURI) {
return rollbackMirrorFilePairStep(systemURI, asList(sourceURI), asList(targetURI));
}
private Workflow.Method rollbackMirrorFilePairStep(final URI systemURI, final List<URI> sourceURIs,
final List<URI> targetURIs) {
return new Workflow.Method(ROLLBACK_MIRROR_LINKS_METHOD, systemURI, sourceURIs, targetURIs);
}
/**
* Roll back Mirror session between source and target
*
* @param systemURI
* @param sourceURIs
* @param targetURIs
* @param opId
* @return
*/
public boolean rollbackMirrorFileShareStep(URI systemURI, List<URI> sourceURIs,
List<URI> targetURIs, String opId) {
log.info("START rollback Mirror links");
TaskCompleter completer = null;
try {
WorkflowStepCompleter.stepExecuting(opId);
StorageSystem system = getStorageSystem(systemURI);
completer = new FileMirrorRollbackCompleter(sourceURIs, opId);
getRemoteMirrorDevice(system).doRollbackMirrorLink(system, sourceURIs, targetURIs, completer, opId);
} catch (Exception e) {
log.error("Ignoring exception while rolling back Mirror sources: {}", sourceURIs, e);
// Succeed here, to allow other rollbacks to run
if (null != completer) {
completer.ready(dbClient);
}
WorkflowStepCompleter.stepSucceded(opId);
return false;
}
return true;
}
@Override
public void performNativeContinuousCopies(URI storage, URI sourceFileShare,
List<URI> mirrorURIs, String opType, String opId) throws ControllerException {
}
/**
* Convenience method to build a Map of URI's to their respective fileshares based on a List of
* FileDescriptor.
*
* @param fileShareDescriptors List of fileshare descriptors
* @return Map of URI to FileShare
*/
private Map<URI, FileShare> queryFileShares(final List<FileDescriptor> fileShareDescriptors) {
List<URI> fileShareURIs = FileDescriptor.getFileSystemURIs(fileShareDescriptors);
List<FileShare> fileShares = dbClient.queryObject(FileShare.class, fileShareURIs);
Map<URI, FileShare> fileShareMap = new HashMap<URI, FileShare>();
for (FileShare fileShare : fileShares) {
if (fileShare != null) {
fileShareMap.put(fileShare.getId(), fileShare);
}
}
return fileShareMap;
}
private StorageSystem getStorageSystem(final URI systemURI) {
return dbClient.queryObject(StorageSystem.class, systemURI);
}
private static final String FAILOVER_MIRROR_FILESHARE_METH = "failoverMirrorFilePair";
private static final String RESYNC_MIRROR_FILESHARE_METH = "resyncPrepMirrorFilePair";
private static final String START_MIRROR_FILESHARE_METH = "startPrepMirrorFilePair";
private static final String CANCEL_MIRROR_FILESHARE_METH = "cancelPrepMirrorFilePair";
// resyncPrep -step
public static Workflow.Method
resyncPrepMirrorPairMeth(URI primarysystemURI, URI targetSystemURI, URI fileshareURI, String policyName) {
return new Workflow.Method(RESYNC_MIRROR_FILESHARE_METH, primarysystemURI, targetSystemURI, fileshareURI, policyName);
}
// start Mirror -step
public static Workflow.Method
startMirrorPairMeth(URI storage, URI fsURI, String policyName) {
return new Workflow.Method(START_MIRROR_FILESHARE_METH, storage, fsURI, policyName);
}
// failover Mirror -step
public static Workflow.Method
faioverMirrorPairMeth(URI storage, URI fsURI, String policyName) {
return new Workflow.Method(FAILOVER_MIRROR_FILESHARE_METH, storage, fsURI, policyName);
}
// Cancel Mirror Policy -step
public static Workflow.Method
cancelMirrorPairMeth(URI storage, URI fsURI, String policyName) {
return new Workflow.Method(CANCEL_MIRROR_FILESHARE_METH, storage, fsURI, policyName);
}
/**
* Creates a rollback workflow method that does nothing, but allows rollback
* to continue to prior steps back up the workflow chain.
*
* @return A workflow method
*/
public Workflow.Method rollbackMethodNullMethod() {
return new Workflow.Method(ROLLBACK_METHOD_NULL);
}
/**
* A rollback workflow method that does nothing, but allows rollback
* to continue to prior steps back up the workflow chain. Can be and is
* used in workflows in other controllers that invoke operations on this
* block controller. If the block operation happens to fail, this no-op
* rollback method is invoked. It says the rollback step succeeded,
* which will then allow other rollback operations to execute for other
* workflow steps executed by the other controller.
*
* @param stepId The id of the step being rolled back.
*
* @throws WorkflowException
*/
public void rollbackMethodNull(String stepId) throws WorkflowException {
WorkflowStepCompleter.stepSucceded(stepId);
}
public String gerneratePolicyName(StorageSystem system, FileShare fileShareTarget) {
return fileShareTarget.getLabel();
}
/**
* Common method used to create Controller methods that would be executed by workflow service
*
* @param workflow
* @param stepGroup
* @param waitFor - String
* @param methodName - Name of the method to be executed
* @param stepId - String unique id of the step
* @param stepDescription - String description of the step
* @param storage - URI of the StorageSystem
* @param args - Parameters of the method that has to be executed by workflow
* @return waitForStep
*/
public String createMethod(Workflow workflow, String stepGroup, String waitFor, String methodName, String stepId,
String stepDescription, URI storage, Object[] args) {
StorageSystem system = this.dbClient.queryObject(StorageSystem.class, storage);
Workflow.Method method = new Workflow.Method(methodName, args);
String waitForStep = workflow.createStep(stepGroup, stepDescription, waitFor, storage, system.getSystemType(), getClass(), method,
null, stepId);
return waitForStep;
}
}