/* * Copyright (c) 2015 EMC Corporation * All Rights Reserved */ package com.emc.storageos.volumecontroller.impl.smis.srdf; import static com.emc.storageos.db.client.constraint.AlternateIdConstraint.Factory.getVolumeNativeGuidConstraint; import static com.emc.storageos.db.client.constraint.ContainmentConstraint.Factory.getStorageDeviceRemoteGroupsConstraint; import static com.emc.storageos.db.client.constraint.ContainmentConstraint.Factory.getVolumesByConsistencyGroup; import static com.emc.storageos.db.client.util.CommonTransformerFunctions.fctnBlockObjectToNativeID; import static com.google.common.base.Predicates.and; import static com.google.common.base.Predicates.not; import static com.google.common.collect.Collections2.filter; import static com.google.common.collect.Collections2.transform; import static com.google.common.collect.Lists.newArrayList; import java.net.URI; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import javax.cim.CIMInstance; import javax.cim.CIMObjectPath; import javax.wbem.CloseableIterator; import javax.wbem.WBEMException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.emc.storageos.db.client.DbClient; import com.emc.storageos.db.client.URIUtil; import com.emc.storageos.db.client.constraint.AlternateIdConstraint; import com.emc.storageos.db.client.constraint.ContainmentConstraint; import com.emc.storageos.db.client.constraint.URIQueryResultList; import com.emc.storageos.db.client.model.BlockMirror; import com.emc.storageos.db.client.model.BlockSnapshot; import com.emc.storageos.db.client.model.BlockSnapshotSession; import com.emc.storageos.db.client.model.Project; import com.emc.storageos.db.client.model.RemoteDirectorGroup; import com.emc.storageos.db.client.model.RemoteDirectorGroup.SupportedCopyModes; import com.emc.storageos.db.client.model.StorageSystem; import com.emc.storageos.db.client.model.StringSet; import com.emc.storageos.db.client.model.Volume; import com.emc.storageos.db.client.model.Volume.PersonalityTypes; import com.emc.storageos.db.client.model.VpoolRemoteCopyProtectionSettings; import com.emc.storageos.db.client.util.CustomQueryUtility; import com.emc.storageos.db.client.util.NullColumnValueGetter; import com.emc.storageos.plugins.common.Constants; import com.emc.storageos.volumecontroller.impl.smis.CIMObjectPathFactory; import com.emc.storageos.volumecontroller.impl.smis.SRDFOperations; import com.emc.storageos.volumecontroller.impl.smis.SRDFOperations.Mode; import com.emc.storageos.volumecontroller.impl.smis.SmisCommandHelper; import com.emc.storageos.volumecontroller.impl.smis.SmisConstants; import com.emc.storageos.volumecontroller.impl.smis.srdf.collectors.CollectorFactory; import com.emc.storageos.volumecontroller.impl.smis.srdf.collectors.CollectorStrategy; import com.emc.storageos.volumecontroller.impl.smis.srdf.exceptions.RemoteGroupAssociationNotFoundException; import com.google.common.base.Function; import com.google.common.base.Joiner; import com.google.common.base.Predicate; /** * Created by bibbyi1 on 4/15/2015. */ public class SRDFUtils implements SmisConstants { private static final Logger log = LoggerFactory.getLogger(SRDFUtils.class); private DbClient dbClient; private CIMObjectPathFactory cimPath; private SmisCommandHelper helper; public enum SyncDirection { SOURCE_TO_TARGET, TARGET_TO_SOURCE, NONE } public void setDbClient(DbClient dbClient) { this.dbClient = dbClient; } public void setCimObjectPathFactory(CIMObjectPathFactory cimPath) { this.cimPath = cimPath; } public void setHelper(SmisCommandHelper helper) { this.helper = helper; } public CIMInstance getInstance(final CIMObjectPath path, final StorageSystem sourceSystem) { try { return helper.checkExists(sourceSystem, path, false, false); } catch (Exception e) { log.error("Problmem in getInstance: ", e); } return null; } /** * Given a collection of StorageSynchronization instances, filter out the * instances with any state considered "broken" leaving only the active ones. * * This method is useful in determining if any synchronizations as part of a * GroupSynchronized require pausing, as checking the state of a GroupSynchronized * instance directly may report an unhelpful state of "MIXED". * * @param synchronizations Collection of StorageSynchronized instances to filter * @param provider Represents a storage system with references to the StorageSynchronized instances. * @return A collection of zero, one or more active StorageSynchronized paths. */ public Collection<CIMObjectPath> filterActiveLinks(Collection<CIMObjectPath> synchronizations, StorageSystem provider) { return filter(synchronizations, activeLinkPredicate(provider)); } /** * Given a collection of StorageSynchronization instances, filter out the instances * that are considered "active", leaving only the broken ones. * * @param synchronizations Collection of StorageSynchronized instances to filter * @param provider Represents a storage system with references to the StorageSynchronized instances. * @return A collection of zero, one or more broken StorageSynchronized paths. */ public Collection<CIMObjectPath> filterBrokenLinks(Collection<CIMObjectPath> synchronizations, StorageSystem provider) { return filter(synchronizations, not(activeLinkPredicate(provider))); } public boolean isBroken(final CIMInstance syncInstance) { if (null == syncInstance) { return false; } String copyState = syncInstance.getPropertyValue(CP_COPY_STATE).toString(); // Solutions Enabler may report a Split status as Failed Over, for legacy reasons. if (String.valueOf(BROKEN).equalsIgnoreCase(copyState) || String.valueOf(FRACTURED).equalsIgnoreCase(copyState) || String.valueOf(SPLIT).equalsIgnoreCase(copyState) || String.valueOf(SUSPENDED).equalsIgnoreCase(copyState) || String.valueOf(FAILED_OVER).equalsIgnoreCase(copyState)) { return true; } return false; } public CIMObjectPath getGroupSynchronized(final Volume targetVolume, final StorageSystem sourceSystem) { RemoteDirectorGroup group = dbClient.queryObject(RemoteDirectorGroup.class, targetVolume.getSrdfGroup()); CIMObjectPath sourceGroupPath = null; CIMObjectPath targetGroupPath = null; if (!NullColumnValueGetter.isNotNullValue(group.getSourceReplicationGroupName()) || !NullColumnValueGetter.isNotNullValue(group.getTargetReplicationGroupName())) { return null; } else { sourceGroupPath = cimPath.getReplicationGroupObjectPath(sourceSystem, group.getSourceReplicationGroupName()); targetGroupPath = cimPath.getReplicationGroupObjectPath(sourceSystem, group.getTargetReplicationGroupName()); } return cimPath.getGroupSynchronized(sourceGroupPath, targetGroupPath); } public CIMObjectPath getStorageSynchronizedObject(final StorageSystem sourceSystem, final Volume source, final Volume target, final StorageSystem activeProviderSystem) { CloseableIterator<CIMObjectPath> iterator = null; try { // If the Source Provider is down, make use of target provider to // find the Sync Paths. // null check makes the caller not to check liveness for multiple volumes in loop. boolean isSourceActiveNow = (null == activeProviderSystem || URIUtil.identical(activeProviderSystem.getId(), sourceSystem.getId())); String nativeIdToUse = (isSourceActiveNow) ? source.getNativeId() : target.getNativeId(); // Use the activeSystem always. StorageSystem systemToUse = (isSourceActiveNow) ? sourceSystem : activeProviderSystem; if (null != activeProviderSystem) { log.info("sourceSystem, activeProviderSystem: {} {}", sourceSystem.getNativeGuid(), activeProviderSystem.getNativeGuid()); } CIMObjectPath volumePath = cimPath.getVolumePath(systemToUse, nativeIdToUse); if (volumePath == null) { throw new IllegalStateException("Volume not found : " + source.getNativeId()); } log.info("Volume Path {}", volumePath.toString()); iterator = helper.getReference(systemToUse, volumePath, SE_STORAGE_SYNCHRONIZED_SV_SV, null); while (iterator.hasNext()) { CIMObjectPath reference = iterator.next(); if (reference.toString().contains(source.getNativeId()) && reference.toString().contains(target.getNativeId())) { log.info("Storage Synchronized reference {}", reference.toString()); return reference; } } } catch (Exception e) { log.error("Failed to acquire synchronization instance", e); } finally { if (iterator != null) { iterator.close(); } } return null; } public Collection<CIMObjectPath> getStorageSynchronizationsInRemoteGroup(StorageSystem provider, Volume targetVolume) { StorageSystem targetSystem = dbClient.queryObject(StorageSystem.class, targetVolume.getStorageController()); CIMObjectPath objectPath = cimPath.getBlockObjectPath(provider, targetSystem, targetVolume); RemoteDirectorGroup rdfGrp = dbClient.queryObject(RemoteDirectorGroup.class, targetVolume.getSrdfGroup()); CIMObjectPath remoteGroupPath = getRemoteGroupPath(provider, objectPath); List<CIMObjectPath> volumePathsInRemoteGroup = getVolumePathsInRemoteGroup(provider, remoteGroupPath); List<CIMObjectPath> result = new ArrayList<>(); for (CIMObjectPath volumePath : volumePathsInRemoteGroup) { CIMObjectPath storageSync = getStorageSynchronizationFromVolume(provider, volumePath, rdfGrp); result.add(storageSync); } return result; } public Collection<CIMObjectPath> getStorageSynchronizationsInRemoteGroup(StorageSystem provider, RemoteDirectorGroup group) { CIMObjectPath remoteGroupPath = cimPath.getRemoteReplicationCollection(provider, group); List<CIMObjectPath> volumePathsInRemoteGroup = getVolumePathsInRemoteGroup(provider, remoteGroupPath); List<CIMObjectPath> result = new ArrayList<>(); for (CIMObjectPath volumePath : volumePathsInRemoteGroup) { CIMObjectPath storageSync = getStorageSynchronizationFromVolume(provider, volumePath, group); result.add(storageSync); } return result; } public Collection<CIMObjectPath> getSynchronizations(StorageSystem activeProviderSystem, Volume sourceVolume, Volume targetVolume) throws WBEMException { return getSynchronizations(activeProviderSystem, sourceVolume, targetVolume, true); } public Collection<CIMObjectPath> getSynchronizations(StorageSystem activeProviderSystem, Volume sourceVolume, Volume targetVolume, boolean includeGroup) throws WBEMException { StorageSystem sourceSystem = dbClient.queryObject(StorageSystem.class, sourceVolume.getStorageController()); StorageSystem targetSystem = dbClient.queryObject(StorageSystem.class, targetVolume.getStorageController()); List<CIMObjectPath> result = new ArrayList<>(); if (sourceVolume.hasConsistencyGroup() && includeGroup) { result.addAll(getConsistencyGroupSyncPairs(sourceSystem, sourceVolume, targetSystem, targetVolume, activeProviderSystem)); } else { CIMObjectPath objectPath = null; if (Mode.ACTIVE.equals(Mode.valueOf(targetVolume.getSrdfCopyMode()))) { objectPath = getStorageSynchronizationFromVolume(sourceSystem, sourceVolume, targetVolume, activeProviderSystem); } else { objectPath = getStorageSynchronizedObject(sourceSystem, sourceVolume, targetVolume, activeProviderSystem); } if (objectPath != null) { result.add(objectPath); } } return result; } /** * Gets associated ViPR volumes based on its SRDF configuration. * Async/Sync with CG -> All volumes in CG * Async without CG -> All volumes in RDF group * Sync without CG -> Single volume * * @param system The provider system to collect synchronization instances from. * @param target The subject of the association query. * @return A list of Volumes */ public List<Volume> getAssociatedVolumes(StorageSystem system, Volume target) { CollectorFactory collectorFactory = new CollectorFactory(dbClient, this); CollectorStrategy collector = collectorFactory.getCollector(target, false); Collection<CIMObjectPath> syncPaths = collector.collect(system, target); Collection<SynchronizedVolumePair> volumePairs = transform(syncPaths, toSynchronizedVolumePairFn()); Set<URI> volumeURIs = new HashSet<>(); URIQueryResultList results = new URIQueryResultList(); for (SynchronizedVolumePair pair : volumePairs) { dbClient.queryByConstraint(getVolumeNativeGuidConstraint(pair.getSourceGUID()), results); volumeURIs.add(results.iterator().next()); dbClient.queryByConstraint(getVolumeNativeGuidConstraint(pair.getTargetGUID()), results); volumeURIs.add(results.iterator().next()); } return dbClient.queryObject(Volume.class, volumeURIs); } /** * Gets associated ViPR volumes based on the SRDF group * * @param system The provider system to collect synchronization instances from. * @param target The subject of the association query. * @return A list of Volumes */ public List<Volume> getAssociatedVolumesForSRDFGroup(StorageSystem system, RemoteDirectorGroup group) { Collection<CIMObjectPath> syncPaths = getStorageSynchronizationsInRemoteGroup(system, group); Collection<SynchronizedVolumePair> volumePairs = transform(syncPaths, toSynchronizedVolumePairFn()); Set<URI> volumeURIs = new HashSet<>(); URIQueryResultList results = new URIQueryResultList(); for (SynchronizedVolumePair pair : volumePairs) { dbClient.queryByConstraint(getVolumeNativeGuidConstraint(pair.getSourceGUID()), results); volumeURIs.add(results.iterator().next()); dbClient.queryByConstraint(getVolumeNativeGuidConstraint(pair.getTargetGUID()), results); volumeURIs.add(results.iterator().next()); } return dbClient.queryObject(Volume.class, volumeURIs); } public Predicate<? super Volume> volumePersonalityPredicate(final PersonalityTypes personality) { return new Predicate<Volume>() { @Override public boolean apply(Volume input) { return personality.toString().equalsIgnoreCase(input.getPersonality()); } }; } /** * return the targetSystem of the targetvolume. * * @param systemURI * @return */ public StorageSystem getStorageSystem(URI systemURI) { return dbClient.queryObject(StorageSystem.class, systemURI); } /** * Async Without CG : All SRDF operations will be happen for all volumes available on ra group. * Hence we need to change the personalities of the remaining volumes too based on the srdf operation. * * This method returns the remaing source volumes list available on the ra group which belongs to given source and target volumes. * * @param sourceVolume * @param targetVolume * @return */ public List<Volume> getRemainingSourceVolumesForAsyncRAGroup(Volume sourceVolume, Volume targetVolume) { List<Volume> volumeList = new ArrayList<Volume>(); if (sourceVolume != null && targetVolume != null && targetVolume.getSrdfGroup() != null) { RemoteDirectorGroup rdfGroup = dbClient.queryObject(RemoteDirectorGroup.class, targetVolume.getSrdfGroup()); if (rdfGroup != null) { StringSet volumeNativeGUIdList = rdfGroup.getVolumes(); log.info("volumeNativeGUIdList : {}", volumeNativeGUIdList); if (volumeNativeGUIdList != null) { for (String volumeNativeGUId : volumeNativeGUIdList) { log.debug("volume nativeGUId:{}", volumeNativeGUId); URIQueryResultList result = new URIQueryResultList(); dbClient.queryByConstraint(AlternateIdConstraint.Factory .getVolumeNativeGuidConstraint(volumeNativeGUId), result); Iterator<URI> volumeIterator = result.iterator(); if (volumeIterator.hasNext()) { Volume volume = dbClient.queryObject(Volume.class, volumeIterator.next()); if (volume != null && PersonalityTypes.SOURCE.toString().equalsIgnoreCase(volume.getPersonality()) && !volume.getNativeId().equalsIgnoreCase(sourceVolume.getNativeId())) { log.info("Found volume {} in vipr db", volume.getNativeGuid()); volumeList.add(volume); } } } } } } log.info("volume list size {}", volumeList.size()); return volumeList; } /** * Given a target volume, this method acquires both the source and target RemoteDirectorGroup instances * in order to remove from it the nativeGuid's of the target and its parent. * * @param target The target volume to be removed from its RemoteDirectorGroup */ public void removeFromRemoteGroups(Volume target) { RemoteDirectorGroup tgtGroup = dbClient.queryObject(RemoteDirectorGroup.class, target.getSrdfGroup()); RemoteDirectorGroup srcGroup = getAssociatedRemoteDirectorGroup(tgtGroup); Volume source = dbClient.queryObject(Volume.class, target.getSrdfParent().getURI()); List<String> nativeGuids = newArrayList(source.getNativeGuid(), target.getNativeGuid()); removeFromRemoteGroup(tgtGroup, nativeGuids); if (srcGroup != null) { removeFromRemoteGroup(srcGroup, nativeGuids); } } private RemoteDirectorGroup getAssociatedRemoteDirectorGroup(RemoteDirectorGroup group) { URIQueryResultList result = new URIQueryResultList(); try { dbClient.queryByConstraint(getStorageDeviceRemoteGroupsConstraint(group.getRemoteStorageSystemUri()), result); return dbClient.queryObject(RemoteDirectorGroup.class, result.iterator().next()); } catch (Exception e) { String msg = String.format("Failed to get associated RemoteDirectorGroup to %s", group.getNativeGuid()); log.warn(msg, e); } return null; } private void removeFromRemoteGroup(RemoteDirectorGroup group, Collection<String> nativeGuids) { if (group.getVolumes() != null) { for (String nativeGuid : nativeGuids) { group.getVolumes().remove(nativeGuid); } } if (group.getVolumes() == null || group.getVolumes().isEmpty()) { group.setSupportedCopyMode(SupportedCopyModes.ALL.toString()); log.info("RDF Group {} copyMode has been changed to ALL", group.getId()); } dbClient.updateObject(group); } private Function<CIMObjectPath, SynchronizedVolumePair> toSynchronizedVolumePairFn() { return new Function<CIMObjectPath, SynchronizedVolumePair>() { @Override public SynchronizedVolumePair apply(CIMObjectPath input) { return new SynchronizedVolumePair(input); } }; } private List<CIMObjectPath> getVolumePathsInRemoteGroup(StorageSystem provider, CIMObjectPath remoteGroupPath) { CloseableIterator<CIMObjectPath> volumePaths = null; List<CIMObjectPath> result = new ArrayList<>(); try { volumePaths = helper.getAssociatorNames(provider, remoteGroupPath, null, CIM_STORAGE_VOLUME, null, null); while (volumePaths.hasNext()) { result.add(volumePaths.next()); } return result; } catch (WBEMException e) { log.error("getVolumePathsInRemoteGroup WBEMException : ", e); } finally { if (volumePaths != null) { volumePaths.close(); } } return Collections.EMPTY_LIST; } private CIMObjectPath getRemoteGroupPath(StorageSystem provider, CIMObjectPath objectPath) { CloseableIterator<CIMObjectPath> names = null; try { names = helper.getAssociatorNames(provider, objectPath, null, SE_RemoteReplicationCollection, null, null); if (names.hasNext()) { return names.next(); } } catch (WBEMException e) { // TODO Create custom exception throw new RuntimeException("Failed to acquire remote replication collection", e); } finally { if (names != null) { names.close(); } } throw new RemoteGroupAssociationNotFoundException(); } /** * Gets the associated target remote director group * by forming target RDF group's NativeGuid from source group NativeGuid */ public static RemoteDirectorGroup getAssociatedTargetRemoteDirectorGroup(DbClient dbClient, boolean is80Provider, String raGroupId) { // interchange source and target ids & group ids // 8.0.x NativeGuid format in DB // SYMMETRIX+000195700985+REMOTEGROUP+000195700985+60+000195700999+60 // SYMMETRIX+000195700999+REMOTEGROUP+000195700999+60+000195700985+60 // 4.6.x NativeGuid format in DB // SYMMETRIX+000195701573+REMOTEGROUP+000195701505+60+000195701573+60 // SYMMETRIX+000195701505+REMOTEGROUP+000195701505+60+000195701573+60 String targetRaGroupNativeGuid = null; StringBuilder strBuilder = new StringBuilder(); String[] nativeGuidArray = raGroupId.split(Constants.SMIS_PLUS_REGEX); String sourceArray = nativeGuidArray[1]; if (is80Provider) { String targetArray = nativeGuidArray[5]; strBuilder.append(nativeGuidArray[0]).append(Constants.PLUS) .append(targetArray).append(Constants.PLUS) .append(nativeGuidArray[2]).append(Constants.PLUS) .append(targetArray).append(Constants.PLUS) .append(nativeGuidArray[6]).append(Constants.PLUS) .append(sourceArray).append(Constants.PLUS) .append(nativeGuidArray[4]); } else { String targetArray = null; if (nativeGuidArray[3].contains(sourceArray)) { targetArray = nativeGuidArray[5]; } else { targetArray = nativeGuidArray[3]; } strBuilder.append(nativeGuidArray[0]).append(Constants.PLUS) .append(targetArray).append(Constants.PLUS) .append(nativeGuidArray[2]).append(Constants.PLUS) .append(nativeGuidArray[3]).append(Constants.PLUS) .append(nativeGuidArray[6]).append(Constants.PLUS) .append(nativeGuidArray[5]).append(Constants.PLUS) .append(nativeGuidArray[4]); } targetRaGroupNativeGuid = strBuilder.toString(); log.debug("Target RA Group Id : {}", targetRaGroupNativeGuid); RemoteDirectorGroup remoteGroup = getRAGroupFromDB(dbClient, targetRaGroupNativeGuid); if (null == remoteGroup) { log.warn("Target RA Group {} not found", targetRaGroupNativeGuid); return null; } return remoteGroup; } private static RemoteDirectorGroup getRAGroupFromDB(DbClient dbClient, String raGroupNativeGuid) { URIQueryResultList raGroupUris = new URIQueryResultList(); dbClient.queryByConstraint(AlternateIdConstraint.Factory.getRAGroupByNativeGuidConstraint(raGroupNativeGuid), raGroupUris); for (URI raGroupURI : raGroupUris) { RemoteDirectorGroup raGroup = dbClient.queryObject(RemoteDirectorGroup.class, raGroupURI); if (null != raGroup && !raGroup.getInactive()) { return raGroup; } } return null; } /** * Fetch the SRDF Protected target virtual pool uris. * * @return */ public static Set<URI> fetchSRDFTargetVirtualPools(DbClient dbClient) { Set<URI> srdfProtectedTargetVPools = new HashSet<URI>(); try { List<URI> vpoolRemoteSettingURIs = dbClient.queryByType(VpoolRemoteCopyProtectionSettings.class, true); Iterator<VpoolRemoteCopyProtectionSettings> vPoolRemoteSettingsItr = dbClient .queryIterativeObjects(VpoolRemoteCopyProtectionSettings.class, vpoolRemoteSettingURIs, true); while (vPoolRemoteSettingsItr.hasNext()) { VpoolRemoteCopyProtectionSettings rSetting = vPoolRemoteSettingsItr.next(); if (null != rSetting && !NullColumnValueGetter.isNullURI(rSetting.getVirtualPool())) { srdfProtectedTargetVPools.add(rSetting.getVirtualPool()); } } } catch (Exception ex) { log.error("Exception occurred while fetching SRDF enabled virtualpools", ex); } return srdfProtectedTargetVPools; } /** * Checks if a volume has snapshot, snapshot session, or clone or mirror associated. */ private boolean CheckIfVolumeHasReplica(Volume volume) { boolean forceAdd = false; URI volumeURI = volume.getId(); URIQueryResultList list = new URIQueryResultList(); dbClient.queryByConstraint(ContainmentConstraint.Factory .getVolumeSnapshotConstraint(volumeURI), list); Iterator<URI> it = list.iterator(); while (it.hasNext()) { URI snapshotID = it.next(); BlockSnapshot snapshot = dbClient.queryObject(BlockSnapshot.class, snapshotID); if (snapshot != null) { log.debug("There are Snapshot(s) available for volume {}", volumeURI); forceAdd = true; break; } } // Check snapshot sessions also. if (!forceAdd) { List<BlockSnapshotSession> snapSessions = CustomQueryUtility.queryActiveResourcesByConstraint(dbClient, BlockSnapshotSession.class, ContainmentConstraint.Factory.getParentSnapshotSessionConstraint(volumeURI)); if (!snapSessions.isEmpty()) { log.debug("There are snapshot sessions available on volume {}", volumeURI); forceAdd = true; } } if (!forceAdd) { // TODO ignore DETACHED clones? URIQueryResultList cloneList = new URIQueryResultList(); dbClient.queryByConstraint(ContainmentConstraint.Factory .getAssociatedSourceVolumeConstraint(volumeURI), cloneList); Iterator<URI> iter = cloneList.iterator(); while (iter.hasNext()) { URI cloneID = iter.next(); Volume clone = dbClient.queryObject(Volume.class, cloneID); if (clone != null) { log.debug("There are Clone(s) available for volume {}", volumeURI); forceAdd = true; break; } } } if (!forceAdd) { URIQueryResultList mirrorList = new URIQueryResultList(); dbClient.queryByConstraint(ContainmentConstraint.Factory .getVolumeBlockMirrorConstraint(volumeURI), mirrorList); Iterator<URI> itr = mirrorList.iterator(); while (itr.hasNext()) { URI mirrorID = itr.next(); BlockMirror mirror = dbClient.queryObject(BlockMirror.class, mirrorID); if (mirror != null) { log.debug("There are Mirror(s) available for volume {}", volumeURI); forceAdd = true; break; } } } return forceAdd; } /** * Finds remote storage synchronized element for the given volume's cimObjectPath * * @param provider * @param volumePath * @param rdfGroup {@link RemoteDirectorGroup} * @return */ private CIMObjectPath getStorageSynchronizationFromVolume(StorageSystem provider, CIMObjectPath volumePath, RemoteDirectorGroup rdfGroup) { CloseableIterator<CIMObjectPath> references = null; try { references = helper.getReference(provider, volumePath, CIM_STORAGE_SYNCHRONIZED, null); StorageSystem sourceSystem = dbClient.queryObject(StorageSystem.class, rdfGroup.getSourceStorageSystemUri()); StorageSystem targetSystem = dbClient.queryObject(StorageSystem.class, rdfGroup.getRemoteStorageSystemUri()); log.debug("Source serial number {}", sourceSystem.getSerialNumber()); log.debug("target system serial number {}", targetSystem.getSerialNumber()); while (references.hasNext()) { CIMObjectPath storageSynchronized = references.next(); log.debug("storage Synchronized {}", storageSynchronized); if (storageSynchronized.toString().contains(sourceSystem.getSerialNumber()) && storageSynchronized.toString().contains(targetSystem.getSerialNumber())) { return storageSynchronized; } } } catch (WBEMException e) { throw new RuntimeException("Failed to acquire storage synchronization", e); } finally { if (references != null) { references.close(); } } throw new RuntimeException("Failed to acquire storage synchronization"); } private CIMObjectPath getStorageSynchronizationFromVolume(final StorageSystem sourceSystem, final Volume source, final Volume target, final StorageSystem activeProviderSystem) { // If the Source Provider is down, make use of target provider to // find the Sync Paths. // null check makes the caller not to check liveness for multiple volumes in loop. boolean isSourceActiveNow = (null == activeProviderSystem || URIUtil.identical(activeProviderSystem.getId(), sourceSystem.getId())); String nativeIdToUse = (isSourceActiveNow) ? source.getNativeId() : target.getNativeId(); RemoteDirectorGroup rdfGrp = dbClient.queryObject(RemoteDirectorGroup.class, target.getSrdfGroup()); // Use the activeSystem always. StorageSystem systemToUse = (isSourceActiveNow) ? sourceSystem : activeProviderSystem; if (null != activeProviderSystem) { log.info("sourceSystem, activeProviderSystem: {} {}", sourceSystem.getNativeGuid(), activeProviderSystem.getNativeGuid()); } CIMObjectPath volumePath = cimPath.getVolumePath(systemToUse, nativeIdToUse); if (volumePath == null) { throw new IllegalStateException("Volume not found : " + source.getNativeId()); } log.info("Volume Path {}", volumePath.toString()); return getStorageSynchronizationFromVolume(systemToUse, volumePath, rdfGrp); } private Collection<CIMObjectPath> getConsistencyGroupSyncPairs(StorageSystem sourceSystem, Volume source, StorageSystem targetSystem, Volume target, StorageSystem activeProviderSystem) throws WBEMException { List<URI> srcVolumeUris = dbClient.queryByConstraint(getVolumesByConsistencyGroup(source.getConsistencyGroup())); List<Volume> cgSrcVolumes = dbClient.queryObject(Volume.class, srcVolumeUris); Collection<String> srcDevIds = transform(cgSrcVolumes, fctnBlockObjectToNativeID()); List<URI> tgtVolumeUris = dbClient.queryByConstraint(getVolumesByConsistencyGroup(target.getConsistencyGroup())); List<Volume> cgTgtVolumes = dbClient.queryObject(Volume.class, tgtVolumeUris); Collection<String> tgtDevIds = transform(cgTgtVolumes, fctnBlockObjectToNativeID()); // Get the storagesync instances for remote sync/async mirrors List<CIMObjectPath> repPaths = helper.getReplicationRelationships(activeProviderSystem, REMOTE_LOCALITY_VALUE, MIRROR_VALUE, SRDFOperations.Mode.valueOf(target.getSrdfCopyMode()).getMode(), STORAGE_SYNCHRONIZED_VALUE); log.info("Found {} relationships", repPaths.size()); log.info("Looking for System elements on {} with IDs {}", sourceSystem.getNativeGuid(), Joiner.on(',').join(srcDevIds)); log.info("Looking for Synced elements on {} with IDs {}", targetSystem.getNativeGuid(), Joiner.on(',').join(tgtDevIds)); return filter(repPaths, and( cgSyncPairsPredicate(sourceSystem.getNativeGuid(), srcDevIds, CP_SYSTEM_ELEMENT), cgSyncPairsPredicate(targetSystem.getNativeGuid(), tgtDevIds, CP_SYNCED_ELEMENT))); } private Predicate<CIMObjectPath> cgSyncPairsPredicate(final String systemNativeGuid, final Collection<String> nativeIds, final String propertyName) { return new Predicate<CIMObjectPath>() { @Override public boolean apply(CIMObjectPath path) { String el = path.getKeyValue(propertyName).toString(); CIMObjectPath elPath = new CIMObjectPath(el); String elDevId = elPath.getKeyValue(CP_DEVICE_ID).toString(); String elSysName = elPath.getKeyValue(CP_SYSTEM_NAME).toString(). replaceAll(Constants.SMIS80_DELIMITER_REGEX, Constants.PLUS); return elSysName.equalsIgnoreCase(systemNativeGuid) && nativeIds.contains(elDevId); } }; } private Predicate<CIMObjectPath> activeLinkPredicate(final StorageSystem provider) { return new Predicate<CIMObjectPath>() { @Override public boolean apply(CIMObjectPath syncPath) { try { CIMInstance syncInstance = getInstance(syncPath, provider); return !isBroken(syncInstance); // Not broken, so add to the "not paused" collection } catch (Exception e) { log.warn("Failed to determine synchronization state for {}", syncPath, e); return false; } } }; } public static final char REPLACE_RDF_STR_BEFORE = ' '; public static final char REPLACE_RDF_STR_AFTER = '_'; public static final int RDF_GROUP_NAME_MAX_LENGTH = 10; public static final String RDF_GROUP_PREFIX = "V-"; /** * Get the qualifying RDF Group names allowed that we can match against. * "V-<projectname>" or "<projectname>" * * @param project * project requested * @return string of a qualifying name */ public static StringSet getQualifyingRDFGroupNames(final Project project) { StringSet names = new StringSet(); String grpName1 = RDF_GROUP_PREFIX + project.getLabel().replace(REPLACE_RDF_STR_BEFORE, REPLACE_RDF_STR_AFTER); if (grpName1.length() > RDF_GROUP_NAME_MAX_LENGTH) { names.add(grpName1.substring(0, RDF_GROUP_NAME_MAX_LENGTH)); } else { names.add(grpName1); } String grpName2 = project.getLabel().replace(REPLACE_RDF_STR_BEFORE, REPLACE_RDF_STR_AFTER); if (grpName2.length() > RDF_GROUP_NAME_MAX_LENGTH) { names.add(grpName2.substring(0, RDF_GROUP_NAME_MAX_LENGTH)); } else { names.add(grpName2); } String grpName3 = project.getLabel(); if (grpName3.length() > RDF_GROUP_NAME_MAX_LENGTH) { grpName3 = grpName3.substring(0, RDF_GROUP_NAME_MAX_LENGTH); } names.add(grpName3 .trim() .replace(REPLACE_RDF_STR_BEFORE, REPLACE_RDF_STR_AFTER)); return names; } /** * Returns false if the label doesn't available in the grpNames * Primary name check, "V-<projectname>" or "<projectname>" * * @param grpNames list of potential names to match * @param label label desired from project * @return */ public static boolean containsRaGroupName(StringSet grpNames, String label) { // check on each name instead of .contains() as we need to ignore case difference. for (String name : grpNames) { if (name.equalsIgnoreCase(label)) { return true; } } return false; } /** * Need to add all SRDF source volumes id to change the linkStatus and accessState * for Sync/Async with CG. Take care not to add Vplex volume. */ public static void addSRDFCGVolumesForTaskCompleter(Volume sourceVol, DbClient dbClient, List<URI> combined) { if (sourceVol != null && sourceVol.hasConsistencyGroup()) { URIQueryResultList uriQueryResultList = new URIQueryResultList(); dbClient.queryByConstraint(getVolumesByConsistencyGroup(sourceVol.getConsistencyGroup()), uriQueryResultList); Iterator<Volume> volumeIterator = dbClient.queryIterativeObjects(Volume.class, uriQueryResultList); while (volumeIterator.hasNext()) { Volume cgVolume = volumeIterator.next(); URI volumeURI = cgVolume.getId(); if (cgVolume != null && cgVolume.checkForSRDF() && !combined.contains(volumeURI)) { combined.add(volumeURI); } } } } /** * Utility method that returns target volumes for the RDF group * * @param group Reference to RemoteDirectorGroup * @return list of volumes */ public List<URI> getTargetVolumesForRAGroup(RemoteDirectorGroup group) { List<URI> volumeList = new ArrayList<URI>(); log.info("Find Target Volumes from RA group {} {}", group.getLabel(), group.getId()); StringSet volumeNativeGUIdList = group.getVolumes(); log.info("volumeNativeGUIdList : {}", volumeNativeGUIdList); if (volumeNativeGUIdList != null) { for (String volumeNativeGUId : volumeNativeGUIdList) { log.debug("volume nativeGUId:{}", volumeNativeGUId); URIQueryResultList result = new URIQueryResultList(); dbClient.queryByConstraint(AlternateIdConstraint.Factory .getVolumeNativeGuidConstraint(volumeNativeGUId), result); Iterator<URI> volumeIterator = result.iterator(); if (volumeIterator.hasNext()) { Volume volume = dbClient.queryObject(Volume.class, volumeIterator.next()); if (volume != null && PersonalityTypes.SOURCE.toString().equalsIgnoreCase(volume.getPersonality())) { log.info("Found volume {} in vipr db", volume.getNativeGuid()); StringSet srdfTargets = volume.getSrdfTargets(); if (null != srdfTargets && !srdfTargets.isEmpty()) { volumeList.addAll(URIUtil.toURIList(volume.getSrdfTargets())); } } } } } log.info("volume list size {}", volumeList.size()); return volumeList; } /** * Given a source consistency group (SRDF), find the corresponding target CG if there is one. * @param dbClient -- Database handle * @param sourceCG -- URI of source CG * @return -- URI of target CG if found, null otherwise */ public static URI getTargetVolumeCGFromSourceCG(DbClient dbClient, URI sourceCG) { List<Volume> sourceVolumes = CustomQueryUtility.queryActiveResourcesByConstraint(dbClient, Volume.class, AlternateIdConstraint.Factory.getBlockObjectsByConsistencyGroup(sourceCG.toString())); for (Volume sourceVolume : sourceVolumes) { if (sourceVolume.getSrdfTargets() != null) { for (String target : sourceVolume.getSrdfTargets()) { if (NullColumnValueGetter.isNotNullValue(target)) { Volume targetVolume = dbClient.queryObject(Volume.class, URI.create(target)); if (!NullColumnValueGetter.isNullURI(targetVolume.getConsistencyGroup())) { return targetVolume.getConsistencyGroup(); } } } } } return null; } }