/*
* Copyright (c) 2014 EMC Corporation
* All Rights Reserved
*/
package com.emc.storageos.volumecontroller.impl.hds.prov;
import java.net.URI;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.emc.storageos.db.client.DbClient;
import com.emc.storageos.db.client.model.BlockMirror;
import com.emc.storageos.db.client.model.NamedURI;
import com.emc.storageos.db.client.model.StorageSystem;
import com.emc.storageos.db.client.model.SynchronizationState;
import com.emc.storageos.db.client.model.Volume;
import com.emc.storageos.exceptions.DeviceControllerErrors;
import com.emc.storageos.exceptions.DeviceControllerException;
import com.emc.storageos.hds.HDSConstants;
import com.emc.storageos.hds.HDSException;
import com.emc.storageos.hds.api.HDSApiClient;
import com.emc.storageos.hds.api.HDSApiFactory;
import com.emc.storageos.hds.api.HDSApiProtectionManager;
import com.emc.storageos.hds.model.LogicalUnit;
import com.emc.storageos.hds.model.ReplicationInfo;
import com.emc.storageos.svcs.errorhandling.model.ServiceError;
import com.emc.storageos.volumecontroller.TaskCompleter;
import com.emc.storageos.volumecontroller.impl.ControllerServiceImpl;
import com.emc.storageos.volumecontroller.impl.block.taskcompleter.SimpleTaskCompleter;
import com.emc.storageos.volumecontroller.impl.hds.prov.job.HDSBlockMirrorDeleteJob;
import com.emc.storageos.volumecontroller.impl.hds.prov.job.HDSJob;
import com.emc.storageos.volumecontroller.impl.hds.prov.job.HDSReplicationSyncJob;
import com.emc.storageos.volumecontroller.impl.hds.prov.job.HDSReplicationSyncJob.ReplicationStatus;
import com.emc.storageos.volumecontroller.impl.hds.prov.utils.HDSCommandHelper;
import com.emc.storageos.volumecontroller.impl.hds.prov.utils.HDSUtils;
import com.emc.storageos.volumecontroller.impl.job.QueueJob;
import com.emc.storageos.volumecontroller.impl.smis.MirrorOperations;
public class HDSMirrorOperations implements MirrorOperations {
private static final Logger log = LoggerFactory.getLogger(HDSMirrorOperations.class);
private DbClient dbClient;
private HDSApiFactory hdsApiFactory;
private HDSProtectionOperations hdsProtectionOperations;
private HDSCommandHelper hdsCommandHelper;
/**
* 1. Find ReplicationGroup objId from Device Manager
* 2. Check dummy Host Group available on Storage System. if not available create a dummy Host Group name.
* 3. Create a secondary volume and add dummy host group on it.
* 4. create a SI pair.
*
* @param storageSystem {@link StorageSystem}
* @param mirrorVolumeURI {@link URI}
* @param createInactive {@link Boolean}
* @param taskCompleter {@link TaskCompleter}
*/
@Override
public void createSingleVolumeMirror(StorageSystem storageSystem, URI mirrorVolumeURI,
Boolean createInactive, TaskCompleter taskCompleter)
throws DeviceControllerException {
log.info("START createSingleVolumeMirror operation");
try {
HDSApiClient hdsApiClient = hdsApiFactory.getClient(
HDSUtils.getHDSServerManagementServerInfo(storageSystem), storageSystem.getSmisUserName(),
storageSystem.getSmisPassword());
HDSApiProtectionManager hdsApiProtectionManager = hdsApiClient.getHdsApiProtectionManager();
String replicationGroupObjectID = hdsApiClient.getHdsApiProtectionManager().getReplicationGroupObjectId();
if (replicationGroupObjectID == null) {
log.error("Unable to find replication group information/pair management server for pair configuration");
throw HDSException.exceptions.replicationGroupNotAvailable();
}
BlockMirror mirrorObj = dbClient.queryObject(BlockMirror.class, mirrorVolumeURI);
Volume source = dbClient.queryObject(Volume.class, mirrorObj.getSource());
hdsProtectionOperations.createSecondaryVolumeForMirror(storageSystem, source.getId(), mirrorObj);
mirrorObj = dbClient.queryObject(BlockMirror.class, mirrorVolumeURI);
hdsProtectionOperations.addDummyLunPath(hdsApiClient, mirrorObj);
String pairName = hdsProtectionOperations.generatePairName(source, mirrorObj);
log.info("Pair Name :{}", pairName);
ReplicationInfo replicationInfo = hdsApiProtectionManager.
createShadowImagePair(replicationGroupObjectID, pairName,
HDSUtils.getSystemArrayType(storageSystem), HDSUtils.getSystemSerialNumber(storageSystem),
source.getNativeId(), mirrorObj.getNativeId(), storageSystem.getModel());
mirrorObj.setSyncState(SynchronizationState.SYNCHRONIZED.name());
dbClient.persistObject(mirrorObj);
log.info("Replication Info object :{}", replicationInfo.toXMLString());
taskCompleter.ready(dbClient);
} catch (Exception e) {
String errorMsg = String.format(CREATE_ERROR_MSG_FORMAT, mirrorVolumeURI);
log.error(errorMsg, e);
ServiceError serviceError = DeviceControllerErrors.hds.methodFailed("createSingleVolumeMirror",
e.getMessage());
taskCompleter.error(dbClient, serviceError);
}
log.info("FINISHED createSingleVolumeMirror operation");
}
/**
* Split ShadowImage pair
*/
@Override
public void fractureSingleVolumeMirror(StorageSystem storage, URI mirror,
Boolean sync, TaskCompleter taskCompleter)
throws DeviceControllerException {
log.info("fractureSingleVolumeMirror started");
try {
BlockMirror mirrorObj = dbClient.queryObject(BlockMirror.class, mirror);
Volume sourceVolume = dbClient.queryObject(Volume.class, mirrorObj.getSource());
hdsProtectionOperations.modifyShadowImagePair(storage, sourceVolume.getNativeId(),
mirrorObj.getNativeId(), HDSApiProtectionManager.ShadowImageOperationType.split);
HDSJob syncjob = new HDSReplicationSyncJob(
storage.getId(), sourceVolume.getNativeId(),
mirrorObj.getNativeId(), ReplicationStatus.SPLIT, taskCompleter);
hdsCommandHelper.waitForAsyncHDSJob(syncjob);
mirrorObj.setSyncState(SynchronizationState.FRACTURED.name());
dbClient.persistObject(mirrorObj);
taskCompleter.ready(dbClient);
} catch (Exception e) {
log.error("Failed to resume single volume mirror: {}", mirror, e);
ServiceError serviceError = DeviceControllerException.errors.jobFailed(e);
taskCompleter.error(dbClient, serviceError);
}
log.info("fractureSingleVolumeMirror completed");
}
/**
* Resync ShadowImage pair
*/
@Override
public void resumeSingleVolumeMirror(StorageSystem storage, URI mirror,
TaskCompleter taskCompleter) throws DeviceControllerException {
log.info("resumeSingleVolumeMirror started");
try {
BlockMirror mirrorObj = dbClient.queryObject(BlockMirror.class, mirror);
Volume sourceVolume = dbClient.queryObject(Volume.class, mirrorObj.getSource());
hdsProtectionOperations.modifyShadowImagePair(storage, sourceVolume.getNativeId(),
mirrorObj.getNativeId(), HDSApiProtectionManager.ShadowImageOperationType.resync);
HDSJob syncjob = new HDSReplicationSyncJob(
storage.getId(), sourceVolume.getNativeId(),
mirrorObj.getNativeId(), ReplicationStatus.PAIR, taskCompleter);
hdsCommandHelper.waitForAsyncHDSJob(syncjob);
mirrorObj.setSyncState(SynchronizationState.SYNCHRONIZED.name());
dbClient.persistObject(mirrorObj);
taskCompleter.ready(dbClient);
} catch (Exception e) {
log.error("Failed to resume single volume mirror: {}", mirror, e);
ServiceError serviceError = DeviceControllerException.errors.jobFailed(e);
taskCompleter.error(dbClient, serviceError);
}
log.info("resumeSingleVolumeMirror completed");
}
@Override
public void establishVolumeNativeContinuousCopyGroupRelation(StorageSystem storage, URI sourceVolume,
URI mirror, TaskCompleter taskCompleter) throws DeviceControllerException {
throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported();
}
/**
* 1. Delete ShadowImage Pair
* 2. Delete DummyLunPath from secondary volume
*/
@Override
public void detachSingleVolumeMirror(StorageSystem storage, URI mirror,
TaskCompleter taskCompleter) throws DeviceControllerException {
NamedURI sourceVolumeURI = null;
try {
BlockMirror mirrorObj = dbClient.queryObject(BlockMirror.class, mirror);
// TODO needs to sync pair and wait for synchronization here
Volume source = dbClient.queryObject(Volume.class, mirrorObj.getSource());
sourceVolumeURI = mirrorObj.getSource();
boolean status = hdsProtectionOperations.modifyShadowImagePair(storage, source.getNativeId(),
mirrorObj.getNativeId(), HDSApiProtectionManager.ShadowImageOperationType.split);
if (status) {
String taskId = UUID.randomUUID().toString();
TaskCompleter completer = new SimpleTaskCompleter(BlockMirror.class, mirror, taskId);
HDSJob syncjob = new HDSReplicationSyncJob(
storage.getId(), source.getNativeId(),
mirrorObj.getNativeId(), ReplicationStatus.SPLIT, completer);
hdsCommandHelper.waitForAsyncHDSJob(syncjob);
} else {
log.info("Replication info is not available on pair management server");
}
hdsProtectionOperations.deleteShadowImagePair(storage, source, mirrorObj);
hdsProtectionOperations.removeDummyLunPath(storage, mirror);
taskCompleter.ready(dbClient);
} catch (Exception e) {
String errorMsg = String.format(DETACH_ERROR_MSG_FORMAT, mirror,
sourceVolumeURI != null ? sourceVolumeURI.toString() : HDSConstants.SPACE_STR);
log.error(errorMsg, e);
ServiceError serviceError = DeviceControllerErrors.hds.methodFailed("detachSingleVolumeMirror",
e.getMessage());
taskCompleter.error(dbClient, serviceError);
}
}
/**
* Deletes mirror instance from StorageSystem
*/
@Override
public void deleteSingleVolumeMirror(StorageSystem storageSystem, URI mirror,
TaskCompleter taskCompleter) throws DeviceControllerException {
try {
StringBuilder logMsgBuilder = new StringBuilder(String.format(
"Delete Mirror Start - Array:%s", storageSystem.getSerialNumber()));
Set<String> thickLogicalUnitIdList = new HashSet<String>();
Set<String> thinLogicalUnitIdList = new HashSet<String>();
HDSApiClient hdsApiClient = hdsApiFactory.getClient(
HDSUtils.getHDSServerManagementServerInfo(storageSystem),
storageSystem.getSmisUserName(), storageSystem.getSmisPassword());
String systemObjectID = HDSUtils.getSystemObjectID(storageSystem);
BlockMirror mirrorObj = dbClient.queryObject(BlockMirror.class, mirror);
logMsgBuilder.append(String.format("%nMirror:%s", mirrorObj.getLabel()));
String logicalUnitObjectId = HDSUtils.getLogicalUnitObjectId(
mirrorObj.getNativeId(), storageSystem);
LogicalUnit logicalUnit = hdsApiClient.getLogicalUnitInfo(systemObjectID,
logicalUnitObjectId);
if (logicalUnit == null) {
// related volume state (if any) has been deleted. skip
// processing, if already deleted from array.
log.info(String.format("Mirror %s already deleted: ",
mirrorObj.getNativeId()));
// HDSMirrorOperations.removeReferenceFromSourceVolume(dbClient, mirrorObj);
dbClient.markForDeletion(mirrorObj);
} else {
if (mirrorObj.getThinlyProvisioned()) {
thinLogicalUnitIdList.add(logicalUnitObjectId);
} else {
thickLogicalUnitIdList.add(logicalUnitObjectId);
}
log.info(logMsgBuilder.toString());
if (!thickLogicalUnitIdList.isEmpty()) {
String asyncThickLUsJobId = hdsApiClient.deleteThickLogicalUnits(systemObjectID,
thickLogicalUnitIdList, storageSystem.getModel());
if (null != asyncThickLUsJobId) {
ControllerServiceImpl.enqueueJob(new QueueJob(new HDSBlockMirrorDeleteJob(
asyncThickLUsJobId, mirrorObj.getStorageController(),
taskCompleter)));
}
}
if (!thinLogicalUnitIdList.isEmpty()) {
String asyncThinHDSJobId = hdsApiClient.deleteThinLogicalUnits(
systemObjectID, thinLogicalUnitIdList, storageSystem.getModel());
if (null != asyncThinHDSJobId) {
ControllerServiceImpl.enqueueJob(new QueueJob(
new HDSBlockMirrorDeleteJob(asyncThinHDSJobId, mirrorObj
.getStorageController(), taskCompleter)));
}
}
}
log.info("Delete Mirror End - Array: {} Mirror: {}", storageSystem.getSerialNumber(), mirror);
} catch (Exception e) {
log.error("Problem in deleteSingleVolumeMirror: ", e);
ServiceError error = DeviceControllerErrors.hds.methodFailed(
"deleteSingleVolumeMirror", e.getMessage());
taskCompleter.error(dbClient, error);
}
}
public void setDbClient(DbClient dbClient) {
this.dbClient = dbClient;
}
public void setHdsApiFactory(HDSApiFactory hdsApiFactory) {
this.hdsApiFactory = hdsApiFactory;
}
public void setHdsCommandHelper(HDSCommandHelper hdsCommandHelper) {
this.hdsCommandHelper = hdsCommandHelper;
}
public void setHdsProtectionOperations(
HDSProtectionOperations hdsProtectionOperations) {
this.hdsProtectionOperations = hdsProtectionOperations;
}
@Override
public void createGroupMirrors(StorageSystem storage, List<URI> mirrorList, Boolean createInactive, TaskCompleter taskCompleter) {
throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported();
}
@Override
public void fractureGroupMirrors(StorageSystem storage, List<URI> mirrorList, Boolean sync, TaskCompleter taskCompleter) {
throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported();
}
@Override
public void resumeGroupMirrors(StorageSystem storage, List<URI> mirrorList, TaskCompleter taskCompleter) {
throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported();
}
@Override
public void detachGroupMirrors(StorageSystem storage, List<URI> mirrorList, Boolean deleteGroup, TaskCompleter taskCompleter) {
throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported();
}
@Override
public void deleteGroupMirrors(StorageSystem storage, List<URI> mirrorList, TaskCompleter taskCompleter) {
throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported();
}
@Override
public void removeMirrorFromDeviceMaskingGroup(StorageSystem system, List<URI> mirrorList,
TaskCompleter completer) throws DeviceControllerException {
throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported();
}
}