/* * Copyright (c) 2015 EMC Corporation * All Rights Reserved */ package com.emc.storageos.volumecontroller.impl.smis.vmax; import static com.emc.storageos.volumecontroller.impl.smis.ReplicationUtils.callEMCRefreshIfRequired; import static com.emc.storageos.volumecontroller.impl.smis.SmisConstants.JOB_COMPLETED_NO_ERROR; 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 javax.cim.UnsignedInteger32; import javax.wbem.WBEMException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.emc.storageos.db.client.model.BlockMirror; 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.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.AbstractMirrorOperations; import com.emc.storageos.volumecontroller.impl.smis.ReplicationUtils; import com.emc.storageos.volumecontroller.impl.smis.SmisConstants; import com.emc.storageos.volumecontroller.impl.smis.SmisConstants.SYNC_TYPE; import com.emc.storageos.volumecontroller.impl.smis.job.SmisBlockCreateCGMirrorJob; import com.emc.storageos.volumecontroller.impl.utils.ConsistencyGroupUtils; import com.google.common.base.Joiner; public class VmaxMirrorOperations extends AbstractMirrorOperations { private static final Logger _log = LoggerFactory.getLogger(VmaxMirrorOperations.class); @Override public void establishVolumeNativeContinuousCopyGroupRelation(StorageSystem storage, URI sourceVolume, URI mirror, TaskCompleter taskCompleter) throws DeviceControllerException { _log.info("establishVolumeNativeContinuousCopyGroupRelation operation START"); try { /** * get groupPath for source volume * get groupPath for mirror * get mirrors belonging to the same Replication Group * get Element synchronizations between volumes and mirrors * call CreateGroupReplicaFromElementSynchronizations */ BlockMirror mirrorObj = _dbClient.queryObject(BlockMirror.class, mirror); Volume volumeObj = _dbClient.queryObject(Volume.class, sourceVolume); CIMObjectPath srcRepSvcPath = _cimPath.getControllerReplicationSvcPath(storage); String volumeGroupName = ConsistencyGroupUtils.getSourceConsistencyGroupName(volumeObj, _dbClient); CIMObjectPath volumeGroupPath = _cimPath.getReplicationGroupPath(storage, volumeGroupName); CIMObjectPath mirrorGroupPath = _cimPath.getReplicationGroupPath(storage, mirrorObj.getReplicationGroupInstance()); CIMObjectPath groupSynchronizedPath = _cimPath.getGroupSynchronized(volumeGroupPath, mirrorGroupPath); CIMInstance syncInstance = _helper.checkExists(storage, groupSynchronizedPath, false, false); if (syncInstance == null) { // List<Volume> volumes = ControllerUtils.getVolumesPartOfCG(sourceVolume.getConsistencyGroup(), _dbClient); // get all mirrors belonging to a Replication Group. There may be multiple mirrors available for a Volume List<BlockMirror> mirrors = ControllerUtils. getMirrorsPartOfReplicationGroup(mirrorObj.getReplicationGroupInstance(), _dbClient); List<CIMObjectPath> elementSynchronizations = new ArrayList<CIMObjectPath>(); for (BlockMirror mirrorObject : mirrors) { Volume volume = _dbClient.queryObject(Volume.class, mirrorObject.getSource()); elementSynchronizations.add(_cimPath.getStorageSynchronized(storage, volume, storage, mirrorObject)); } _log.info("Creating Group synchronization between volume group and mirror group"); CIMArgument[] inArgs = _helper.getCreateGroupReplicaFromElementSynchronizationsForSRDFInputArguments(volumeGroupPath, mirrorGroupPath, elementSynchronizations); CIMArgument[] outArgs = new CIMArgument[5]; _helper.invokeMethod(storage, srcRepSvcPath, SmisConstants.CREATE_GROUP_REPLICA_FROM_ELEMENT_SYNCHRONIZATIONS, inArgs, outArgs); // No Job returned } else { _log.info("Link already established.."); } taskCompleter.ready(_dbClient); } catch (Exception e) { _log.error( "Failed to establish group relation between volume group and mirror group. Volume: {}, Mirror: {}", sourceVolume, mirror); ServiceError serviceError = DeviceControllerException.errors.jobFailed(e); taskCompleter.error(_dbClient, serviceError); } } @Override public void createGroupMirrors(StorageSystem storage, List<URI> mirrorList, Boolean createInactive, TaskCompleter taskCompleter) throws DeviceControllerException { _log.info("createGroupMirrors operation START"); List<BlockMirror> mirrors = null; try { if (!storage.getUsingSmis80()) { throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported(); } mirrors = _dbClient.queryObject(BlockMirror.class, mirrorList); BlockMirror firstMirror = mirrors.get(0); Volume sourceVolume = _dbClient.queryObject(Volume.class, firstMirror.getSource()); String sourceGroupName = ConsistencyGroupUtils.getSourceConsistencyGroupName(sourceVolume, _dbClient); String replicaLabel = ControllerUtils.generateLabel(sourceVolume.getLabel(), firstMirror.getLabel()); // CTRL-5640: ReplicationGroup may not be accessible after provider fail-over. ReplicationUtils.checkReplicationGroupAccessibleOrFail(storage, sourceVolume, _dbClient, _helper, _cimPath); // Create CG mirrors CIMObjectPath job = VmaxGroupOperationsUtils.internalCreateGroupReplica(storage, sourceGroupName, replicaLabel, null, createInactive, taskCompleter, SYNC_TYPE.MIRROR, _dbClient, _helper, _cimPath); ControllerServiceImpl.enqueueJob( new QueueJob(new SmisBlockCreateCGMirrorJob(job, storage.getId(), taskCompleter))); for (BlockMirror mirror : mirrors) { mirror.setSyncState(SynchronizationState.SYNCHRONIZED.name()); } _dbClient.persistObject(mirrors); } catch (Exception e) { _log.error("Problem making SMI-S call: ", e); // Roll back changes if (mirrors != null && !mirrors.isEmpty()) { for (BlockMirror mirrorObj : mirrors) { mirrorObj.setInactive(true); } } _dbClient.persistObject(mirrors); ServiceError error = DeviceControllerErrors.smis.methodFailed("createGroupMirrors", e.getMessage()); taskCompleter.error(_dbClient, error); } _log.info("createGroupMirrors operation END"); } @Override public void fractureGroupMirrors(StorageSystem storage, List<URI> mirrorList, Boolean sync, TaskCompleter taskCompleter) throws DeviceControllerException { _log.info("fractureGroupMirrors operation START"); if (!storage.getUsingSmis80()) { throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported(); } try { callEMCRefreshIfRequired(_dbClient, _helper, storage, mirrorList); CIMObjectPath groupSynchronized = ReplicationUtils.getMirrorGroupSynchronizedPath(storage, mirrorList.get(0), _dbClient, _helper, _cimPath); if (null == _helper.checkExists(storage, groupSynchronized, false, false)) { _log.error("Unable to find group synchronized {}", groupSynchronized.toString()); BlockMirror mirror = _dbClient.queryObject(BlockMirror.class, mirrorList.get(0)); ServiceError error = DeviceControllerErrors.smis.unableToFindSynchPath(mirror.getReplicationGroupInstance()); taskCompleter.error(_dbClient, error); } // If there is any mirror in split state, resume the group to make it consistent. if (_helper.groupHasReplicasInSplitState(storage, mirrorList, BlockMirror.class)) { resumeGroupMirrors(storage, mirrorList); } // Fracture the mirror now. CIMArgument[] fractureCGMirrorInput = _helper.getFractureMirrorInputArgumentsWithCopyState( groupSynchronized, sync); _helper.callModifyReplica(storage, fractureCGMirrorInput); List<BlockMirror> mirrors = _dbClient.queryObject(BlockMirror.class, mirrorList); for (BlockMirror mirror : mirrors) { mirror.setSyncState(SynchronizationState.FRACTURED.name()); } _dbClient.persistObject(mirrors); taskCompleter.ready(_dbClient); } catch (Exception e) { _log.error("Problem making SMI-S call", e); ServiceError error = DeviceControllerException.errors.jobFailed(e); taskCompleter.error(_dbClient, error); } } @Override public void resumeGroupMirrors(StorageSystem storage, List<URI> mirrorList, TaskCompleter taskCompleter) throws DeviceControllerException { _log.info("resumeGroupMirrors operation START"); if (!storage.getUsingSmis80()) { throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported(); } try { callEMCRefreshIfRequired(_dbClient, _helper, storage, mirrorList); CIMObjectPath groupSynchronized = ReplicationUtils.getMirrorGroupSynchronizedPath(storage, mirrorList.get(0), _dbClient, _helper, _cimPath); if (null == _helper.checkExists(storage, groupSynchronized, false, false)) { _log.error("Unable to find group synchronized {}", groupSynchronized.toString()); BlockMirror mirror = _dbClient.queryObject(BlockMirror.class, mirrorList.get(0)); ServiceError error = DeviceControllerErrors.smis.unableToFindSynchPath(mirror.getReplicationGroupInstance()); taskCompleter.error(_dbClient, error); return; } resumeGroupMirrors(storage, mirrorList); taskCompleter.ready(_dbClient); } catch (Exception e) { _log.error("Problem making SMI-S call", e); ServiceError error = DeviceControllerException.errors.jobFailed(e); taskCompleter.error(_dbClient, error); } } /** * Utility to resume Group mirrors. * @param storage * @param mirrorList */ private void resumeGroupMirrors(StorageSystem storage, List<URI> mirrorList) throws WBEMException { CIMObjectPath groupSynchronized = ReplicationUtils.getMirrorGroupSynchronizedPath(storage, mirrorList.get(0), _dbClient, _helper, _cimPath); CIMArgument[] resumeCGMirrorInput = _helper .getResumeSynchronizationInputArgumentsWithCopyState(groupSynchronized); CIMArgument[] outArgs = new CIMArgument[5]; _helper.callModifyReplica(storage, resumeCGMirrorInput, outArgs); List<BlockMirror> mirrors = _dbClient.queryObject(BlockMirror.class, mirrorList); for (BlockMirror mirror : mirrors) { mirror.setSyncState(SynchronizationState.SYNCHRONIZED.name()); } _dbClient.persistObject(mirrors); } @Override public void detachGroupMirrors(StorageSystem storage, List<URI> mirrorList, Boolean deleteGroup, TaskCompleter taskCompleter) throws DeviceControllerException { _log.info("START detach group mirror operation"); if (!storage.getUsingSmis80()) { throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported(); } try { callEMCRefreshIfRequired(_dbClient, _helper, storage, mirrorList); CIMObjectPath groupSynchronized = ReplicationUtils.getMirrorGroupSynchronizedPath(storage, mirrorList.get(0), _dbClient, _helper, _cimPath); if (_helper.checkExists(storage, groupSynchronized, false, false) != null) { CIMArgument[] detachCGMirrorInput = _helper.getDetachSynchronizationInputArguments(groupSynchronized); // Invoke method to detach local mirrors UnsignedInteger32 result = (UnsignedInteger32) _helper.callModifyReplica(storage, detachCGMirrorInput, new CIMArgument[5]); if (JOB_COMPLETED_NO_ERROR.equals(result)) { List<BlockMirror> mirrors = _dbClient.queryObject(BlockMirror.class, mirrorList); if (deleteGroup) { ReplicationUtils.deleteReplicationGroup(storage, mirrors.get(0).getReplicationGroupInstance(), _dbClient, _helper, _cimPath); } // Set mirrors replication group to null for (BlockMirror mirror : mirrors) { if (deleteGroup) { mirror.setConsistencyGroup(NullColumnValueGetter.getNullURI()); mirror.setReplicationGroupInstance(NullColumnValueGetter.getNullStr()); } mirror.setSyncState(NullColumnValueGetter.getNullStr()); } _dbClient.persistObject(mirrors); taskCompleter.ready(_dbClient); } else { String msg = String.format("SMI-S call returned unsuccessfully: %s", result); taskCompleter.error(_dbClient, DeviceControllerException.errors.smis.jobFailed(msg)); } } else { _log.error("Unable to find group synchronized {}", groupSynchronized.toString()); BlockMirror mirror = _dbClient.queryObject(BlockMirror.class, mirrorList.get(0)); ServiceError error = DeviceControllerErrors.smis.unableToFindSynchPath(mirror.getReplicationGroupInstance()); taskCompleter.error(_dbClient, error); } } catch (Exception e) { _log.error("Problem making SMI-S call: ", e); ServiceError error = DeviceControllerException.errors.jobFailed(e); taskCompleter.error(_dbClient, error); } } @Override public void removeMirrorFromDeviceMaskingGroup(StorageSystem system, List<URI> mirrorList, TaskCompleter completer) throws DeviceControllerException { _log.info("removeMirrorFromGroup operation START"); try{ BlockMirror mirror = _dbClient.queryObject(BlockMirror.class, mirrorList.get(0)); CIMObjectPath maskingGroupPath = _cimPath.getMaskingGroupPath(system, mirror.getReplicationGroupInstance(), SmisConstants.MASKING_GROUP_TYPE.SE_DeviceMaskingGroup); List<URI> replicasPartOfGroup = _helper.findVolumesInReplicationGroup(system, maskingGroupPath, mirrorList); if (replicasPartOfGroup.isEmpty()) { _log.info("Mirrors {} already removed from Device Masking Group {}", Joiner.on(", ").join(mirrorList), maskingGroupPath); } else { String[] members = _helper.getBlockObjectAlternateNames(replicasPartOfGroup); CIMObjectPath[] memberPaths = _cimPath.getVolumePaths(system, members); CIMArgument[] inArgs = _helper.getRemoveAndUnmapMaskingGroupMembersInputArguments( maskingGroupPath, memberPaths, system, false); CIMArgument[] outArgs = new CIMArgument[5]; _log.info("Invoking remove mirrors {} from Device Masking Group equivalent to its Replication Group {}", Joiner.on(", ").join(members), mirror.getReplicationGroupInstance()); _helper.invokeMethodSynchronously(system, _cimPath.getControllerConfigSvcPath(system), SmisConstants.REMOVE_MEMBERS, inArgs, outArgs, null); } completer.ready(_dbClient); } catch (Exception e) { _log.error( "Failed to remove mirrors from its Device Masking Group. Mirrors: {}", Joiner.on(", ").join(mirrorList), e); ServiceError serviceError = DeviceControllerException.errors.jobFailed(e); completer.error(_dbClient, serviceError); } _log.info("removeMirrorFromGroup operation END"); } }