/* * Copyright (c) 2015 EMC Corporation * All Rights Reserved */ package com.emc.storageos.volumecontroller.impl.smis.vnx; import static com.emc.storageos.volumecontroller.impl.smis.ReplicationUtils.callEMCRefreshIfRequired; import static com.emc.storageos.volumecontroller.impl.smis.SmisConstants.CREATE_LIST_REPLICA; import static com.emc.storageos.volumecontroller.impl.smis.SmisConstants.JOB; import static java.text.MessageFormat.format; import java.net.URI; import java.util.ArrayList; import java.util.List; import javax.cim.CIMArgument; import javax.cim.CIMInstance; import javax.cim.CIMObjectPath; import org.slf4j.Logger; import org.slf4j.LoggerFactory; 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.svcs.errorhandling.model.ServiceError; import com.emc.storageos.volumecontroller.TaskCompleter; import com.emc.storageos.volumecontroller.impl.ControllerServiceImpl; import com.emc.storageos.volumecontroller.impl.ControllerUtils; import com.emc.storageos.volumecontroller.impl.job.QueueJob; import com.emc.storageos.volumecontroller.impl.smis.AbstractCloneOperations; import com.emc.storageos.volumecontroller.impl.smis.ReplicationUtils; import com.emc.storageos.volumecontroller.impl.smis.SmisConstants; import com.emc.storageos.volumecontroller.impl.smis.job.SmisVnxCreateCGCloneJob; import com.emc.storageos.volumecontroller.impl.utils.ConsistencyGroupUtils; /** * For VNX, clone would be smi-s mirror (Snapview clone) * * */ public class VnxCloneOperations extends AbstractCloneOperations { private static final Logger log = LoggerFactory.getLogger(VnxCloneOperations.class); private static final String MODIFY_GROUP_ERROR = "Failed to modify group full copy."; /** * Should implement create of a clone from a source volume that is part of a * consistency group. * * Implementation note: In this method we will kick of the asynchronous creation * of the target devices required for the CG clones. Upon the successful * device creation, the post operations will take place, which will include the * creation of the target group and the group clone operation. * * @param storage [required] - StorageSystem object representing the array * @param clonetList [required] - clone URI list * @param createInactive whether the clone needs to to be created with sync_active=true/false * @param taskCompleter - TaskCompleter object used for the updating operation status. * @throws DeviceControllerException */ @Override public void createGroupClone(StorageSystem storage, List<URI> cloneList, Boolean createInactive, TaskCompleter taskCompleter) throws DeviceControllerException { log.info("START create group clone operation"); // List of target device ids List<String> targetDeviceIds = null; // The source consistency group name String sourceGroupName = null; try { final Volume first = _dbClient.queryObject(Volume.class, cloneList.get(0)); Volume sourceVolume = _dbClient.queryObject(Volume.class, first.getAssociatedSourceVolume()); sourceGroupName = ConsistencyGroupUtils.getSourceConsistencyGroupName(sourceVolume, _dbClient); if (!ControllerUtils.isNotInRealVNXRG(sourceVolume, _dbClient)) { // CTRL-5640: ReplicationGroup may not be accessible after provider fail-over. ReplicationUtils.checkReplicationGroupAccessibleOrFail(storage, sourceVolume, _dbClient, _helper, _cimPath); } // Group volumes by pool and size List<String> sourceIds = new ArrayList<String>(); List<Volume> clones = _dbClient.queryObject(Volume.class, cloneList); for (Volume clone : clones) { final Volume volume = _dbClient.queryObject(Volume.class, clone.getAssociatedSourceVolume()); sourceIds.add(volume.getNativeId()); } targetDeviceIds = new ArrayList<String>(); for (Volume clone : clones) { final URI poolId = clone.getPool(); Volume source = _dbClient.queryObject(Volume.class, clone.getAssociatedSourceVolume()); // Create target devices final List<String> newDeviceIds = ReplicationUtils.createTargetDevices(storage, sourceGroupName, clone.getLabel(), createInactive, 1, poolId, clone.getCapacity(), source.getThinlyProvisioned(), source, taskCompleter, _dbClient, _helper, _cimPath); targetDeviceIds.addAll(newDeviceIds); } CIMObjectPath[] cloneVolumePaths = _cimPath .getVolumePaths(storage, targetDeviceIds.toArray(new String[targetDeviceIds.size()])); CIMObjectPath[] sourceVolumePaths = _cimPath.getVolumePaths(storage, sourceIds.toArray(new String[sourceIds.size()])); // Create list replica CIMObjectPath replicationSvc = _cimPath.getControllerReplicationSvcPath(storage); CIMArgument[] inArgs = _helper.getCreateListReplicaInputArguments(storage, sourceVolumePaths, cloneVolumePaths); CIMArgument[] outArgs = new CIMArgument[5]; _helper.invokeMethod(storage, replicationSvc, CREATE_LIST_REPLICA, inArgs, outArgs); CIMObjectPath job = _cimPath.getCimObjectPathFromOutputArgs(outArgs, JOB); if (job != null) { ControllerServiceImpl.enqueueJob( new QueueJob(new SmisVnxCreateCGCloneJob(job, storage.getId(), !createInactive, taskCompleter))); } } catch (Exception e) { final String errMsg = format( "An exception occurred when trying to create clones for consistency group {0} on storage system {1}", sourceGroupName, storage.getId()); log.error(errMsg, e); // Roll back changes ReplicationUtils.rollbackCreateReplica(storage, null, targetDeviceIds, taskCompleter, _dbClient, _helper, _cimPath); List<Volume> clones = _dbClient.queryObject(Volume.class, cloneList); for (Volume clone : clones) { clone.setInactive(true); } _dbClient.persistObject(clones); ServiceError error = DeviceControllerErrors.smis.methodFailed("createGroupClones", e.getMessage()); taskCompleter.error(_dbClient, error); } log.info("createGroupClone operation END"); } /** * For VNX clone, activate would be fracture the mirror in smi-s. */ @Override public void activateSingleClone(StorageSystem storageSystem, URI fullCopy, TaskCompleter completer) { log.info("START activateSingleClone for {}", fullCopy); try { Volume clone = _dbClient.queryObject(Volume.class, fullCopy); URI sourceUri = clone.getAssociatedSourceVolume(); BlockObject sourceObj = BlockObject.fetch(_dbClient, sourceUri); StorageSystem sourceSystem = _dbClient.queryObject(StorageSystem.class, sourceObj.getStorageController()); CIMObjectPath syncObject = _cimPath.getStorageSynchronized(sourceSystem, sourceObj, storageSystem, clone); CIMInstance instance = _helper.checkExists(storageSystem, syncObject, false, false); if (instance != null) { fractureReplica(storageSystem, syncObject); clone.setSyncActive(true); clone.setRefreshRequired(true); clone.setReplicaState(ReplicationState.SYNCHRONIZED.name()); _dbClient.persistObject(clone); if (completer != null) { completer.ready(_dbClient); } log.info("FINISH activateSingleClone for {}", fullCopy); } else { String errorMsg = "The clone is already detached. active will not be performed."; log.info(errorMsg); ServiceError error = DeviceControllerErrors.smis.methodFailed("activateSingleClone", errorMsg); if (completer != null) { completer.error(_dbClient, error); } } } catch (Exception e) { String errorMsg = String.format(ACTIVATE_ERROR_MSG_FORMAT, fullCopy); log.error(errorMsg, e); if (completer != null) { completer.error(_dbClient, DeviceControllerException.exceptions.activateVolumeFullCopyFailed(e)); } } log.info("activateSingleClone operation END"); } /** * This interface is for the clone activate in CG, for vnx, it is to Split (Consistent Fracture) the mirror. * * @param storage [required] - StorageSystem object representing the array * @param clones [required] - clone URIs * @param taskCompleter - TaskCompleter object used for the updating operation status. */ @Override public void activateGroupClones(StorageSystem storage, List<URI> clones, TaskCompleter completer) { log.info("activateGroupClones operation START"); try { modifyGroupClones(storage, clones, SmisConstants.SPLIT_VALUE); List<Volume> cloneVols = _dbClient.queryObject(Volume.class, clones); for (Volume clone : cloneVols) { clone.setSyncActive(true); clone.setReplicaState(ReplicationState.SYNCHRONIZED.name()); } _dbClient.persistObject(cloneVols); if (completer != null) { completer.ready(_dbClient); } } catch (Exception e) { log.error(MODIFY_GROUP_ERROR, e); completer.error(_dbClient, DeviceControllerException.exceptions.activateVolumeFullCopyFailed(e)); } log.info("activateGroupClones operation END"); } @Override public void fractureGroupClones(StorageSystem storageSystem, List<URI> clones, TaskCompleter taskCompleter) { log.info("START fractureGroupClone operation"); try { if (_helper.groupHasReplicasInSplitState(storageSystem, clones, Volume.class)) { log.info("Resync group clones with mixed states"); modifyGroupClones(storageSystem, clones, SmisConstants.RESYNC_VALUE); } modifyGroupClones(storageSystem, clones, SmisConstants.SPLIT_VALUE); List<Volume> cloneVols = _dbClient.queryObject(Volume.class, clones); for (Volume clone : cloneVols) { clone.setReplicaState(ReplicationState.SYNCHRONIZED.name()); } _dbClient.persistObject(cloneVols); if (taskCompleter != null) { taskCompleter.ready(_dbClient); } } catch (Exception e) { log.error(MODIFY_GROUP_ERROR, e); taskCompleter.error(_dbClient, DeviceControllerException.exceptions.fractureFullCopyFailed(e)); } log.info("fractureGroupClone operation END"); } @Override public void detachGroupClones(StorageSystem storage, List<URI> clones, TaskCompleter completer) { log.info("START detachGroupClone operation"); try { modifyGroupClones(storage, clones, SmisConstants.DETACH_VALUE); List<Volume> cloneVols = _dbClient.queryObject(Volume.class, clones); for (Volume clone : cloneVols) { ReplicationUtils.removeDetachedFullCopyFromSourceFullCopiesList(clone, _dbClient); clone.setAssociatedSourceVolume(NullColumnValueGetter.getNullURI()); clone.setReplicaState(ReplicationState.DETACHED.name()); } _dbClient.persistObject(cloneVols); if (completer != null) { completer.ready(_dbClient); } } catch (Exception e) { log.error(MODIFY_GROUP_ERROR, e); completer.error(_dbClient, DeviceControllerException.exceptions.detachVolumeFullCopyFailed(e)); } log.info("detachGroupClone operation END"); } @Override public void restoreGroupClones(StorageSystem storage, List<URI> clones, TaskCompleter completer) { log.info("START restoreGroupClone operation"); try { modifyGroupClones(storage, clones, SmisConstants.RESTORE_FROM_REPLICA); completer.ready(_dbClient); } catch (Exception e) { log.error(MODIFY_GROUP_ERROR, e); completer.error(_dbClient, DeviceControllerException.exceptions.restoreVolumeFromFullCopyFailed(e)); } log.info("restoreGroupClone operation END"); } @Override public void resyncGroupClones(StorageSystem storage, List<URI> clones, TaskCompleter completer) { log.info("START resyncGroupClone operation"); try { modifyGroupClones(storage, clones, SmisConstants.RESYNC_VALUE); completer.ready(_dbClient); } catch (Exception e) { log.error(MODIFY_GROUP_ERROR, e); completer.error(_dbClient, DeviceControllerException.exceptions.resynchronizeFullCopyFailed(e)); } log.info("resyncGroupClone operation END"); } /** * It would call modifyListSynchronization for synchronized operations, e.g. fracture, detach, etc * * @param storageSystem * @param clones * @param operationValue * @throws Exception */ @SuppressWarnings("rawtypes") private void modifyGroupClones(StorageSystem storageSystem, List<URI> clones, int operationValue) throws Exception { callEMCRefreshIfRequired(_dbClient, _helper, storageSystem, clones); List<Volume> cloneVols = _dbClient.queryObject(Volume.class, clones); List<CIMObjectPath> syncPaths = new ArrayList<CIMObjectPath>(); for (Volume clone : cloneVols) { URI sourceUri = clone.getAssociatedSourceVolume(); Volume sourceObj = _dbClient.queryObject(Volume.class, sourceUri); CIMObjectPath syncObject = _cimPath.getStorageSynchronized(storageSystem, sourceObj, storageSystem, clone); CIMInstance instance = _helper.checkExists(storageSystem, syncObject, false, false); if (instance != null) { syncPaths.add(syncObject); } else { log.error("Storage synchronized instance is not available for clone {}", clone.getLabel()); throw DeviceControllerException.exceptions.synchronizationInstanceNull(clone.getLabel()); } } CIMArgument[] modifyCGCloneInput = _helper.getModifyListReplicaInputArguments( syncPaths.toArray(new CIMObjectPath[] {}), operationValue); _helper.callModifyListReplica(storageSystem, modifyCGCloneInput); } }