/* * Copyright (c) 2014 EMC Corporation * All Rights Reserved */ package com.emc.storageos.volumecontroller.impl.hds.prov; import java.net.URI; import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.emc.storageos.db.client.DbClient; import com.emc.storageos.db.client.model.BlockObject; import com.emc.storageos.db.client.model.StorageSystem; import com.emc.storageos.db.client.model.Volume; import com.emc.storageos.db.client.model.Volume.ReplicationState; import com.emc.storageos.db.client.util.NullColumnValueGetter; import com.emc.storageos.exceptions.DeviceControllerErrors; import com.emc.storageos.exceptions.DeviceControllerException; 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.ReplicationInfo; import com.emc.storageos.svcs.errorhandling.model.ServiceError; import com.emc.storageos.volumecontroller.CloneOperations; import com.emc.storageos.volumecontroller.TaskCompleter; import com.emc.storageos.volumecontroller.impl.ControllerServiceImpl; 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.HDSUtils; import com.emc.storageos.volumecontroller.impl.job.QueueJob; import com.emc.storageos.volumecontroller.impl.smis.ReplicationUtils; public class HDSCloneOperations implements CloneOperations { private static final Logger log = LoggerFactory.getLogger(HDSCloneOperations.class); private DbClient dbClient; private HDSApiFactory hdsApiFactory; private HDSProtectionOperations hdsProtectionOperations; public void setDbClient(DbClient dbClient) { this.dbClient = dbClient; } /** * @param hdsApiFactory the hdsApiFactory to set */ public void setHdsApiFactory(HDSApiFactory hdsApiFactory) { this.hdsApiFactory = hdsApiFactory; } /** * 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 * * Note that if createInactive is false, then a subsequent step in the * full copy creation workflow will do a wait for synchronization. This * will split the pair, which makes the clone active. * * @param storageSystem {@link StorageSystem} * @param sourceVolumeURI {@link URI} * @param cloneVolumeURI {@link URI} * @param createInactive {@link Boolean} * @param taskCompleter {@link TaskCompleter} */ @Override public void createSingleClone(StorageSystem storageSystem, URI sourceVolumeURI, URI cloneVolumeURI, Boolean createInactive, TaskCompleter taskCompleter) { log.info("START createSingleClone operation"); Volume cloneVolume = null; 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(); } cloneVolume = dbClient.queryObject(Volume.class, cloneVolumeURI); hdsProtectionOperations.createSecondaryVolumeForClone(storageSystem, sourceVolumeURI, cloneVolume); // Need to fetch clone volume from db to get volume's nativeId cloneVolume = dbClient.queryObject(Volume.class, cloneVolumeURI); hdsProtectionOperations.addDummyLunPath(hdsApiClient, cloneVolume); BlockObject source = BlockObject.fetch(dbClient, sourceVolumeURI); String pairName = hdsProtectionOperations.generatePairName(source, cloneVolume); log.info("Pair Name :{}", pairName); ReplicationInfo replicationInfo = hdsApiProtectionManager. createShadowImagePair(replicationGroupObjectID, pairName, HDSUtils.getSystemArrayType(storageSystem), HDSUtils.getSystemSerialNumber(storageSystem), source.getNativeId(), cloneVolume.getNativeId(), storageSystem.getModel()); log.info("Replication Info object :{}", replicationInfo.toXMLString()); log.info("createInactive :{}", createInactive); cloneVolume.setSyncActive(false); cloneVolume.setReplicaState(ReplicationState.INACTIVE.name()); dbClient.persistObject(cloneVolume); taskCompleter.ready(dbClient); } catch (Exception e) { String errorMsg = String.format(CREATE_ERROR_MSG_FORMAT, sourceVolumeURI, cloneVolumeURI); log.error(errorMsg, e); Volume clone = dbClient.queryObject(Volume.class, cloneVolumeURI); if (clone != null) { clone.setInactive(true); dbClient.persistObject(clone); } ServiceError serviceError = DeviceControllerErrors.hds.methodFailed("createSingleClone", e.getMessage()); taskCompleter.error(dbClient, serviceError); } } /** * Detaches clone volume relationship from source volume. * 1. Delete ShadowImage pair from replicationGroup. * 2. Delete dummyLunPath from secondary volume. * * @param storageSystem {@link StorageSystem} * @param cloneVolumeURI {@link URI} * @param taskCompleter {@link TaskCompleter} */ @Override public void detachSingleClone(StorageSystem storageSystem, URI cloneVolumeURI, TaskCompleter taskCompleter) { URI sourceVolumeURI = null; try { Volume targetVolume = dbClient.queryObject(Volume.class, cloneVolumeURI); sourceVolumeURI = targetVolume.getAssociatedSourceVolume(); Volume sourceVolume = dbClient.queryObject(Volume.class, sourceVolumeURI); hdsProtectionOperations.deleteShadowImagePair(storageSystem, sourceVolume, targetVolume); hdsProtectionOperations.removeDummyLunPath(storageSystem, cloneVolumeURI); ReplicationUtils.removeDetachedFullCopyFromSourceFullCopiesList(targetVolume, dbClient); targetVolume.setReplicaState(ReplicationState.DETACHED.name()); targetVolume.setAssociatedSourceVolume(NullColumnValueGetter.getNullURI()); dbClient.persistObject(targetVolume); if (taskCompleter != null) { taskCompleter.ready(dbClient); } } catch (Exception e) { String errorMsg = String.format(DETACH_ERROR_MSG_FORMAT, cloneVolumeURI, sourceVolumeURI); log.error(errorMsg, e); ServiceError serviceError = DeviceControllerErrors.hds.methodFailed("detachSingleClone", e.getMessage()); if (taskCompleter != null) { taskCompleter.error(dbClient, serviceError); } } } /** * @param storageSystem {@link StorageSystem} StorageSystem instance * @param fullCopy {@link URI} clone's URI * @param completer {@link TaskCompleter} * * @prereq Create full copy as inactive * * @brief Activate Clone Volume */ @Override public void activateSingleClone(StorageSystem storageSystem, URI fullCopy, TaskCompleter completer) { log.info("START activateSingleClone for {}", fullCopy); try { Volume clone = dbClient.queryObject(Volume.class, fullCopy); Volume sourceVolume = dbClient.queryObject(Volume.class, clone.getAssociatedSourceVolume()); hdsProtectionOperations.modifyShadowImagePair(storageSystem, sourceVolume.getNativeId(), clone.getNativeId(), HDSApiProtectionManager.ShadowImageOperationType.split); ControllerServiceImpl.enqueueJob(new QueueJob(new HDSReplicationSyncJob( storageSystem.getId(), sourceVolume.getNativeId(), clone.getNativeId(), ReplicationStatus.SPLIT, completer))); // Update state. clone.setSyncActive(true); clone.setReplicaState(ReplicationState.SYNCHRONIZED.name()); dbClient.persistObject(clone); log.info("FINISH activateSingleClone for {}", fullCopy); } catch (Exception e) { String errorMsg = String.format(ACTIVATE_ERROR_MSG_FORMAT, fullCopy); log.error(errorMsg, e); completer.error(dbClient, DeviceControllerException.exceptions.activateVolumeFullCopyFailed(e)); } } public void setHdsProtectionOperations( HDSProtectionOperations hdsProtectionOperations) { this.hdsProtectionOperations = hdsProtectionOperations; } @Override public void restoreFromSingleClone(StorageSystem storageSystem, URI cloneURI, TaskCompleter taskCompleter) { try { Volume cloneVolume = dbClient.queryObject(Volume.class, cloneURI); Volume sourceVolume = dbClient.queryObject(Volume.class, cloneVolume.getAssociatedSourceVolume()); hdsProtectionOperations.modifyShadowImagePair(storageSystem, sourceVolume.getNativeId(), cloneVolume.getNativeId(), HDSApiProtectionManager.ShadowImageOperationType.restore); ControllerServiceImpl.enqueueJob(new QueueJob(new HDSReplicationSyncJob( storageSystem.getId(), sourceVolume.getNativeId(), cloneVolume .getNativeId(), ReplicationStatus.PAIR, taskCompleter))); log.info("FINISH restoreFromSingleClone for {}", cloneURI); } catch (Exception e) { String errorMsg = String.format(RESTORE_ERROR_MSG_FORMAT, cloneURI); log.error(errorMsg, e); taskCompleter.error(dbClient, DeviceControllerException.exceptions.activateVolumeFullCopyFailed(e)); } } @Override public void fractureSingleClone(StorageSystem storageSystem, URI sourceVolume, URI clone, TaskCompleter completer) { } @Override public void resyncSingleClone(StorageSystem storageSystem, URI cloneURI, TaskCompleter taskCompleter) { try { Volume cloneVolume = dbClient.queryObject(Volume.class, cloneURI); Volume sourceVolume = dbClient.queryObject(Volume.class, cloneVolume.getAssociatedSourceVolume()); hdsProtectionOperations.modifyShadowImagePair(storageSystem, sourceVolume.getNativeId(), cloneVolume.getNativeId(), HDSApiProtectionManager.ShadowImageOperationType.resync); ControllerServiceImpl.enqueueJob(new QueueJob(new HDSReplicationSyncJob( storageSystem.getId(), sourceVolume.getNativeId(), cloneVolume .getNativeId(), ReplicationStatus.PAIR, taskCompleter))); log.info("FINISH resyncSingleClone for {}", cloneURI); } catch (Exception e) { String errorMsg = String.format(RESTORE_ERROR_MSG_FORMAT, cloneURI); log.error(errorMsg, e); taskCompleter.error(dbClient, DeviceControllerException.exceptions.activateVolumeFullCopyFailed(e)); } } @Override public void createGroupClone(StorageSystem storage, List<URI> cloneList, Boolean createInactive, TaskCompleter taskCompleter) throws DeviceControllerException { throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported(); } @Override public void activateGroupClones(StorageSystem storage, List<URI> clone, TaskCompleter taskCompleter) { throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported(); } @Override public void restoreGroupClones(StorageSystem storageSystem, List<URI> clone, TaskCompleter completer) { throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported(); } @Override public void fractureGroupClones(StorageSystem storageSystem, List<URI> clone, TaskCompleter completer) { throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported(); } @Override public void resyncGroupClones(StorageSystem storageSystem, List<URI> clone, TaskCompleter completer) { throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported(); } @Override public void detachGroupClones(StorageSystem storageSystem, List<URI> clone, TaskCompleter completer) { throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported(); } @Override public void establishVolumeCloneGroupRelation(StorageSystem storage, URI sourceVolume, URI clone, TaskCompleter completer) { throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported(); } }