/* * Copyright (c) 2008-2014 EMC Corporation * All Rights Reserved */ package com.emc.storageos.api.service.impl.resource.utils; import java.net.URI; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.emc.storageos.db.client.DbClient; import com.emc.storageos.db.client.constraint.AlternateIdConstraint; import com.emc.storageos.db.client.model.BlockObject; import com.emc.storageos.db.client.model.DataObject; import com.emc.storageos.db.client.model.DataObject.Flag; import com.emc.storageos.db.client.model.NamedURI; import com.emc.storageos.db.client.model.StringSet; import com.emc.storageos.db.client.model.StringSetMap; import com.emc.storageos.db.client.model.VirtualPool; 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.model.UnManagedDiscoveredObjects.UnManagedVolume; import com.emc.storageos.db.client.model.UnManagedDiscoveredObjects.UnManagedVolume.SupportedVolumeInformation; import com.emc.storageos.volumecontroller.impl.plugins.discovery.smis.processor.detailedDiscovery.RemoteMirrorObject; import com.google.common.base.Joiner; public class RemoteReplicationIngestor { private static final Logger _logger = LoggerFactory .getLogger(RemoteReplicationIngestor.class); private static final DataObject.Flag[] INTERNAL_VOLUME_FLAGS = new DataObject.Flag[] { Flag.INTERNAL_OBJECT, Flag.PARTIALLY_INGESTED, Flag.NO_METERING }; /** * If unmanaged volume is a Target Volume, then 1. Find if source is ingested 2. If yes, then * find whether expected targets of this source had been ingested already excluding the current * target. 3. If yes, establish links between source and targets. 4. If not,then make sure * unmanaged volume hasn't been deleted. * * @param unManagedVolume * @param volume * @param unManagedVolumes * @param type * @return */ @SuppressWarnings("deprecation") private static boolean runRemoteReplicationStepsOnTarget(UnManagedVolume unManagedVolume, Volume volume, List<UnManagedVolume> unManagedVolumes, String type, DbClient dbClient) { boolean removeUnManagedVolume = false; StringSetMap unManagedVolumeInformation = unManagedVolume.getVolumeInformation(); String sourceUnManagedVolumeId = PropertySetterUtil.extractValueFromStringSet( SupportedVolumeInformation.REMOTE_MIRROR_SOURCE_VOLUME.toString(), unManagedVolumeInformation); _logger.info("Type {} Source Native Guid {}", type, sourceUnManagedVolumeId); String sourceVolumeId = sourceUnManagedVolumeId.replace(VolumeIngestionUtil.UNMANAGEDVOLUME, VolumeIngestionUtil.VOLUME); List<URI> sourceUris = dbClient.queryByConstraint(AlternateIdConstraint.Factory.getVolumeNativeGuidConstraint(sourceVolumeId)); String copyMode = PropertySetterUtil.extractValueFromStringSet(SupportedVolumeInformation.REMOTE_COPY_MODE.toString(), unManagedVolumeInformation); String raGroup = PropertySetterUtil.extractValueFromStringSet(SupportedVolumeInformation.REMOTE_MIRROR_RDF_GROUP.toString(), unManagedVolumeInformation); volume.setSrdfCopyMode(copyMode); volume.setSrdfGroup(URI.create(raGroup)); if (sourceUris.isEmpty()) { _logger.info("Source {} Not found for target {}", sourceVolumeId, volume.getId()); } else { // check whether all targets of the source are ingested List<URI> sourceUnmanagedUris = dbClient.queryByConstraint(AlternateIdConstraint.Factory .getVolumeInfoNativeIdConstraint(sourceUnManagedVolumeId)); if (!sourceUnmanagedUris.isEmpty()) { UnManagedVolume sourceUnManagedVolume = dbClient.queryObject(UnManagedVolume.class, sourceUnmanagedUris.get(0)); if (null != sourceUnManagedVolume) { StringSet targetUnManagedVolumeGuids = sourceUnManagedVolume.getVolumeInformation().get( SupportedVolumeInformation.REMOTE_MIRRORS.toString()); if (null != targetUnManagedVolumeGuids && !targetUnManagedVolumeGuids.isEmpty()) { StringSet targetVolumeNativeGuids = VolumeIngestionUtil.getListofVolumeIds(targetUnManagedVolumeGuids); List<URI> targetUris = VolumeIngestionUtil.getVolumeUris(targetVolumeNativeGuids, dbClient); targetUris.add(volume.getId()); _logger.info("Expected targets Size {} , found {} ", targetUnManagedVolumeGuids.size(), targetUris.size()); _logger.debug("Expected Targets {} : Found {}", Joiner.on("\t").join(targetVolumeNativeGuids), Joiner.on("\t") .join(targetUris)); List<Volume> modifiedVolumes = new ArrayList<Volume>(); if (targetUris.size() == targetUnManagedVolumeGuids.size()) { // if all other targets are ingested, then Volume sourceVolume = dbClient.queryObject(Volume.class, sourceUris.get(0)); // check whether the source Volume's VPool is actually having this target Volume's varray // specified as remote VirtualPool sourceVPool = dbClient.queryObject(VirtualPool.class, sourceVolume.getVirtualPool()); Map<URI, VpoolRemoteCopyProtectionSettings> settings = sourceVPool.getRemoteProtectionSettings(sourceVPool, dbClient); if (null == settings || settings.size() == 0 || !settings.containsKey(volume.getVirtualArray())) { _logger.info( "Target Volume's VArray {} is not matching already ingested source volume virtual pool's remote VArray ", volume.getVirtualArray()); return false; } sourceVolume.setSrdfTargets(VolumeIngestionUtil.convertUrisToStrings(targetUris)); _logger.info("Clearing internal flag for source volume {} found", sourceVolume.getNativeGuid()); sourceVolume.clearInternalFlags(INTERNAL_VOLUME_FLAGS); _logger.debug("Set srdf target for source volume {} found", sourceVolume.getId()); modifiedVolumes.add(sourceVolume); // source unmanagedVolume sourceUnManagedVolume.setInactive(true); unManagedVolumes.add(sourceUnManagedVolume); // this target unmanaged volume volume.setSrdfParent(new NamedURI(sourceVolume.getId(), sourceVolume.getLabel())); _logger.debug("target volume set parent", volume.getId()); removeUnManagedVolume = true; // handle other target volumes List<Volume> targetVolumes = dbClient.queryObject(Volume.class, targetUris); for (Volume targetVolume : targetVolumes) { _logger.debug("Set parent for remaining target volume {}", targetVolume.getId()); targetVolume.setSrdfParent(new NamedURI(sourceVolume.getId(), sourceVolume.getLabel())); targetVolume.clearInternalFlags(INTERNAL_VOLUME_FLAGS); } modifiedVolumes.addAll(targetVolumes); // target unmanaged volumes List<UnManagedVolume> targetUnManagedVolumes = dbClient.queryObject(UnManagedVolume.class, VolumeIngestionUtil.getUnManagedVolumeUris(targetUnManagedVolumeGuids, dbClient)); for (UnManagedVolume targetUnManagedVol : targetUnManagedVolumes) { _logger.debug("Set Target unmanaged volume inactive {}", targetUnManagedVol.getId()); targetUnManagedVol.setInactive(true); unManagedVolumes.add(targetUnManagedVol); } dbClient.persistObject(modifiedVolumes); _logger.info("Target Volume successfully ingested with remote replication links", volume.getNativeGuid()); } else { // set volume flag to false _logger.info("Expected Targets not found for source Volume {}", sourceUnManagedVolumeId); } } else { _logger.info( "Targets information not found on source volume {}." + "This could happen when parallel ingests are tried or the actual volume got deleted on array.", sourceUnManagedVolumeId); } } } } return removeUnManagedVolume; } /** * If unmanaged volume is of type Source, then check if all its target volumes are already * ingested. if yes, establish links. * * @param unManagedVolume * @param srcVolume * @param unManagedVolumes * @param type * @return */ private static boolean runRemoteReplicationStepsOnSource(UnManagedVolume unManagedVolume, Volume srcVolume, List<UnManagedVolume> unManagedVolumes, String type, DbClient dbClient) { boolean removeUnManagedVolume = false; StringSetMap unManagedVolumeInformation = unManagedVolume.getVolumeInformation(); // find whether all targets are ingested StringSet targetUnManagedVolumeGuids = unManagedVolumeInformation.get(SupportedVolumeInformation.REMOTE_MIRRORS.toString()); _logger.info("Type : {} --> Source Volume {}", type, srcVolume.getNativeGuid()); if (null != targetUnManagedVolumeGuids && !targetUnManagedVolumeGuids.isEmpty()) { StringSet targetVolumeNativeGuids = VolumeIngestionUtil.getListofVolumeIds(targetUnManagedVolumeGuids); // check whether target exists List<URI> targetUris = VolumeIngestionUtil.getVolumeUris(targetVolumeNativeGuids, dbClient); _logger.info("Expected targets : {} -->Found Target URIs : {}", targetUnManagedVolumeGuids.size(), targetUris.size()); _logger.debug("Expected Targets {} : Found {}", Joiner.on("\t").join(targetVolumeNativeGuids), Joiner.on("\t").join(targetUris)); if (targetUris.size() != targetUnManagedVolumeGuids.size()) { _logger.info("Found Target Volumes still not ingested.Skipping Remote Replication Link establishment."); } else { List<Volume> targetVolumes = dbClient.queryObject(Volume.class, targetUris); for (Volume targetVolume : targetVolumes) { // Get the Source Volume's remote VArray and compare the same with target's Virtual Array. VirtualPool sourceVPool = dbClient.queryObject(VirtualPool.class, srcVolume.getVirtualPool()); Map<URI, VpoolRemoteCopyProtectionSettings> settings = sourceVPool.getRemoteProtectionSettings(sourceVPool, dbClient); if (null == settings || settings.size() == 0 || !settings.containsKey(targetVolume.getVirtualArray())) { _logger.info( "Target Volume's VArray {} is not matching already ingested source volume virtual pool's remote VArray {}", targetVolume.getVirtualArray(), Joiner.on(",").join(settings.keySet())); // remove the target from processing .so that links will never get established targetUris.remove(targetVolume.getId()); } else { // for each target set its srdf parent ; copyMode and // raGroup should be updated as part of target ingestion. _logger.info("Set parent for volume {}", targetVolume.getId()); targetVolume.setSrdfParent(new NamedURI(srcVolume.getId(), srcVolume.getLabel())); targetVolume.clearInternalFlags(INTERNAL_VOLUME_FLAGS); } } if (!targetUris.isEmpty()) { // set targets from source srcVolume.setSrdfTargets(VolumeIngestionUtil.convertUrisToStrings(targetUris)); dbClient.persistObject(targetVolumes); // can remove unmanaged volume removeUnManagedVolume = true; // setting target unmanaged volumes inactive List<UnManagedVolume> targetUnManagedVolumes = dbClient.queryObject(UnManagedVolume.class, VolumeIngestionUtil.getUnManagedVolumeUris(targetUnManagedVolumeGuids, dbClient)); for (UnManagedVolume targetUnManagedVol : targetUnManagedVolumes) { if (!targetUris.contains(targetUnManagedVol.getId())) { _logger.info("Setting unmanaged target inactive {}", targetUnManagedVol.getId()); targetUnManagedVol.setInactive(true); unManagedVolumes.add(targetUnManagedVol); } else { _logger.info("Skipping deletion of unmanaged volume {} as remote links are not established", targetUnManagedVol.getId()); } } _logger.info("Source Volume successfully ingested with remote replication links", srcVolume.getNativeGuid()); } else { _logger.info("Source Volume failed to ingest with remote replication links", srcVolume.getNativeGuid()); } } } return removeUnManagedVolume; } public static boolean runRemoteReplicationStepsOnPartiallyIngestedVolume(UnManagedVolume unManagedVolume, BlockObject bo, List<UnManagedVolume> unManagedVolumes, DbClient dbClient) { Volume volume = (Volume) bo; StringSetMap unManagedVolumeInformation = unManagedVolume.getVolumeInformation(); boolean remoteLinksEstablished = false; String type = PropertySetterUtil.extractValueFromStringSet(SupportedVolumeInformation.REMOTE_VOLUME_TYPE.toString(), unManagedVolumeInformation); if (null == type) { return true; } if (RemoteMirrorObject.Types.SOURCE.toString().equalsIgnoreCase(type)) { volume.setPersonality(PersonalityTypes.SOURCE.toString()); remoteLinksEstablished = runRemoteReplicationStepsOnSource(unManagedVolume, volume, unManagedVolumes, type, dbClient); } else if (RemoteMirrorObject.Types.TARGET.toString().equalsIgnoreCase(type)) { volume.setPersonality(PersonalityTypes.TARGET.toString()); remoteLinksEstablished = runRemoteReplicationStepsOnTarget(unManagedVolume, volume, unManagedVolumes, type, dbClient); } return remoteLinksEstablished; } }