/*
* Copyright (c) 2008-2015 EMC Corporation
* All Rights Reserved
*/
package com.emc.storageos.api.service.impl.resource.blockingestorchestration;
import java.net.URI;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.emc.storageos.api.service.impl.resource.blockingestorchestration.context.IngestionRequestContext;
import com.emc.storageos.api.service.impl.resource.utils.PropertySetterUtil;
import com.emc.storageos.api.service.impl.resource.utils.VolumeIngestionUtil;
import com.emc.storageos.db.client.constraint.AlternateIdConstraint;
import com.emc.storageos.db.client.model.BlockObject;
import com.emc.storageos.db.client.model.Project;
import com.emc.storageos.db.client.model.RemoteDirectorGroup;
import com.emc.storageos.db.client.model.StringSet;
import com.emc.storageos.db.client.model.StringSetMap;
import com.emc.storageos.db.client.model.VirtualArray;
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.emc.storageos.volumecontroller.impl.smis.srdf.SRDFUtils;
import com.google.common.base.Joiner;
/**
* Remote Replication Ingestion
* S1 - Source Volume ,T1 - Target Volume
* S1 under ViPR control & T1 not yet
* ****************************************
* If user tries to ingest volumes of S1, S1 will be ingested with usable flag bit set to false.
* usable bit set to false indicates, that this volume cannot be used for any provisioning operations
* If we find S1 as source for multiple protected volumes, check whether ALL expected target volumes are already ingested.
* If found true, then create ViPR SRDF links between source and targets, by making them as if these source and targets are created via ViPR
* using SRDF protected VirtualPool, and set usable bit ot TRUE.
* If not, usable bit remains in false state.
* T1 under ViPR control and S1 not yet
* ****************************************
* If user tries to ingest volumes of T1, T1 will be ingested with usable flag bit set to false.
* usable bit set to false indicates, that this volume cannot be used for any provisioning operations
* If we find T1 as target for a source volume, check whether source volume and ALL its expected target volumes are already ingested,
* exclusing the target which we work on.
* If found true, then create ViPR SRDF links between source and targets, by making them as if these source and targets are created via ViPR
* using SRDF protected VirtualPool, and set usable bit ot TRUE.
* If not, usable bit remains in false state.
*/
public class BlockRemoteReplicationIngestOrchestrator extends BlockVolumeIngestOrchestrator {
private static final Logger _logger = LoggerFactory.getLogger(BlockRemoteReplicationIngestOrchestrator.class);
@Override
protected void checkUnmanagedVolumeReplicas(UnManagedVolume unmanagedVolume) {
return;
}
@Override
public <T extends BlockObject> T ingestBlockObjects(IngestionRequestContext requestContext, Class<T> clazz)
throws IngestionException {
UnManagedVolume unManagedVolume = requestContext.getCurrentUnmanagedVolume();
String volumeNativeGuid = unManagedVolume.getNativeGuid().replace(VolumeIngestionUtil.UNMANAGEDVOLUME,
VolumeIngestionUtil.VOLUME);
BlockObject blockObject = VolumeIngestionUtil.checkIfVolumeExistsInDB(volumeNativeGuid, _dbClient);
// validate srdf blockObjects.
validateUnManagedVolumeProperties(unManagedVolume, requestContext.getVarray(unManagedVolume),
requestContext.getVpool(unManagedVolume), requestContext.getProject());
// Check if ingested volume has exportmasks pending for ingestion.
if (isExportIngestionPending(blockObject, unManagedVolume.getId(),
requestContext.getVolumeContext().isVolumeExported())) {
return clazz.cast(blockObject);
}
if (null == blockObject) {
blockObject = super.ingestBlockObjects(requestContext, clazz);
if (null == blockObject) {
_logger.warn("SRDF Volume ingestion failed for unmanagedVolume {}", unManagedVolume.getNativeGuid());
throw IngestionException.exceptions.unmanagedVolumeMasksNotIngested(unManagedVolume.getNativeGuid());
}
} else {
// blockObject already ingested, now just update internalflags &
// srdf relationships. Run this logic always when volume NO_PUBLIC_ACCESS
if (markUnManagedVolumeInactive(requestContext, blockObject)) {
_logger.info("All the related replicas and parent of unManagedVolume {} has been ingested ",
unManagedVolume.getNativeGuid());
unManagedVolume.setInactive(true);
requestContext.getUnManagedVolumesToBeDeleted().add(unManagedVolume);
} else {
_logger.info(
"Not all the parent/replicas of unManagedVolume {} have been ingested , hence marking as internal",
unManagedVolume.getNativeGuid());
blockObject.addInternalFlags(INTERNAL_VOLUME_FLAGS);
}
}
// Decorate blockobjects with SRDF Properties.
decorateBlockObjectWithSRDFProperties(blockObject, unManagedVolume);
return clazz.cast(blockObject);
}
@Override
protected void validateAutoTierPolicy(String autoTierPolicyId, UnManagedVolume unManagedVolume, VirtualPool vPool) {
super.validateAutoTierPolicy(autoTierPolicyId, unManagedVolume, vPool);
}
/**
* Decorates the block objects with srdf properties.
*
* @param blockObject
* @param unManagedVolume
*/
private void decorateBlockObjectWithSRDFProperties(BlockObject blockObject, UnManagedVolume unManagedVolume) {
Volume volume = (Volume) blockObject;
StringSetMap unManagedVolumeInformation = unManagedVolume.getVolumeInformation();
String type = PropertySetterUtil.extractValueFromStringSet(
SupportedVolumeInformation.REMOTE_VOLUME_TYPE.toString(), unManagedVolumeInformation);
if (RemoteMirrorObject.Types.SOURCE.toString().equalsIgnoreCase(type)) {
volume.setPersonality(PersonalityTypes.SOURCE.toString());
} else if (RemoteMirrorObject.Types.TARGET.toString().equalsIgnoreCase(type)) {
volume.setPersonality(PersonalityTypes.TARGET.toString());
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));
}
}
/**
* Validates the UnManagedVolume SRDF Properties.
*
* @param unManagedVolume
* @param virtualArray
* @param virtualPool
*/
private void validateUnManagedVolumeProperties(UnManagedVolume unManagedVolume, VirtualArray virtualArray,
VirtualPool virtualPool, Project project) {
StringSetMap unManagedVolumeInformation = unManagedVolume.getVolumeInformation();
URI rdfGroupId = getRDFGroupBasedOnPersonality(unManagedVolumeInformation);
// To make sure rdfGroup is populated for both R1 & R2 volumes.
if (null == rdfGroupId) {
_logger.warn("SRDF Volume ingestion failed for unmanagedVolume {} as not able to find RDFGroup.",
unManagedVolume.getNativeGuid());
throw IngestionException.exceptions.unmanagedVolumeRDFGroupMissing(unManagedVolume.getNativeGuid());
}
RemoteDirectorGroup rdfGroup = _dbClient.queryObject(RemoteDirectorGroup.class, rdfGroupId);
// name check, "V-<projectname>" or "<projectname>"
StringSet grpNames = SRDFUtils.getQualifyingRDFGroupNames(project);
// Validate the project Name with the unmanaged volume rdfGroup name.
if (null == rdfGroup.getLabel() || !SRDFUtils.containsRaGroupName(grpNames, rdfGroup.getLabel())) {
_logger.warn("SRDF Volume ingestion failed for unmanagedVolume {} due to mismatch in RDF group name",
unManagedVolume.getNativeGuid());
throw IngestionException.exceptions.unmanagedVolumeRDFGroupMismatch(unManagedVolume.getNativeGuid(),
rdfGroup.getLabel(), project.getLabel(), StringUtils.join(grpNames, ","));
}
String type = PropertySetterUtil.extractValueFromStringSet(
SupportedVolumeInformation.REMOTE_VOLUME_TYPE.toString(), unManagedVolumeInformation);
if (null == type) {
_logger.warn("SRDF Volume ingestion failed for unmanagedVolume {}", unManagedVolume.getNativeGuid());
throw IngestionException.exceptions.unmanagedVolumeVolumeTypeNotSet(unManagedVolume.getNativeGuid());
}
_logger.info("Type {} Source Native Guid {}", type, unManagedVolume.getNativeGuid());
if (RemoteMirrorObject.Types.SOURCE.toString().equalsIgnoreCase(type)) {
validateSourceVolumeVarrayWithTargetVPool(unManagedVolume, virtualPool);
} else if (RemoteMirrorObject.Types.TARGET.toString().equalsIgnoreCase(type)) {
validateTargetVolumeVpoolWithSourceVolume(unManagedVolume, virtualArray);
}
}
/**
* Return the rdfGroupId based on the personality.
* For source volume, we will not have RDFGroup hence we should get it from its targets.
*
* @param unManagedVolumeInformation
* @return
*/
private URI getRDFGroupBasedOnPersonality(StringSetMap unManagedVolumeInformation) {
String type = PropertySetterUtil.extractValueFromStringSet(
SupportedVolumeInformation.REMOTE_VOLUME_TYPE.toString(), unManagedVolumeInformation);
URI rdfGroupId = null;
if (RemoteMirrorObject.Types.SOURCE.toString().equalsIgnoreCase(type)) {
StringSet targetUnManagedVolumeGuids = unManagedVolumeInformation.get(SupportedVolumeInformation.REMOTE_MIRRORS
.toString());
if (null != targetUnManagedVolumeGuids && !targetUnManagedVolumeGuids.isEmpty()) {
StringSet targetVolumeNativeGuids = VolumeIngestionUtil.getListofVolumeIds(targetUnManagedVolumeGuids);
List<URI> targetUris = VolumeIngestionUtil.getVolumeUris(targetVolumeNativeGuids, _dbClient);
if (null == targetUris || targetUris.isEmpty()) {
List<URI> unmanagedTargetVolumes = VolumeIngestionUtil.getUnManagedVolumeUris(targetUnManagedVolumeGuids, _dbClient);
for (URI targetUmv : unmanagedTargetVolumes) {
_logger.info("RDFGroup Found using unmanaged Target volume {}", targetUmv);
UnManagedVolume umv = _dbClient.queryObject(UnManagedVolume.class, targetUmv);
rdfGroupId = URI.create(PropertySetterUtil.extractValueFromStringSet(
SupportedVolumeInformation.REMOTE_MIRROR_RDF_GROUP.toString(), umv.getVolumeInformation()));
break;
}
} else {
// If targets are already ingested.
List<Volume> targetVolumes = _dbClient.queryObject(Volume.class, targetUris);
if (null != targetVolumes && !targetVolumes.isEmpty()) {
for (Volume targetVolume : targetVolumes) {
_logger.info("RDFGroup Found for using ingested Target volumes {}.", targetVolume.getNativeGuid());
rdfGroupId = targetVolume.getSrdfGroup();
break;
}
}
}
}
} else if (RemoteMirrorObject.Types.TARGET.toString().equalsIgnoreCase(type)) {
rdfGroupId = URI.create(PropertySetterUtil.extractValueFromStringSet(
SupportedVolumeInformation.REMOTE_MIRROR_RDF_GROUP.toString(), unManagedVolumeInformation));
}
return rdfGroupId;
}
/**
* Validate the SourceVolume VArray details with ingested target volumes
* VArray.
*
* @param unManagedVolume
* @param VirtualPool
* @return
*/
private void validateSourceVolumeVarrayWithTargetVPool(UnManagedVolume unManagedVolume, VirtualPool sourceVPool) {
StringSetMap unManagedVolumeInformation = unManagedVolume.getVolumeInformation();
// find whether all targets are ingested
StringSet targetUnManagedVolumeGuids = unManagedVolumeInformation.get(SupportedVolumeInformation.REMOTE_MIRRORS
.toString());
if (null != targetUnManagedVolumeGuids && !targetUnManagedVolumeGuids.isEmpty()) {
StringSet targetVolumeNativeGuids = VolumeIngestionUtil.getListofVolumeIds(targetUnManagedVolumeGuids);
// check whether target exists
List<URI> targetUris = VolumeIngestionUtil.getVolumeUris(targetVolumeNativeGuids, _dbClient);
if (null == targetUris || targetUris.isEmpty()) {
_logger.info("None of the targets ingested for source volume: {}", unManagedVolume.getNativeGuid());
} else {
List<Volume> targetVolumes = _dbClient.queryObject(Volume.class, targetUris);
for (Volume targetVolume : targetVolumes) {
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()));
throw IngestionException.exceptions.unmanagedSRDFSourceVolumeVArrayMismatch(
unManagedVolume.getLabel(), targetVolume.getVirtualArray().toString());
}
}
}
}
}
/**
* Validate the Target Volume VirtualArray with the Source Volume VPool
* VirtualArray.
*
* @param type
* @param unManagedVolume
* @param virtualArray
*/
private void validateTargetVolumeVpoolWithSourceVolume(UnManagedVolume unManagedVolume, VirtualArray virtualArray) {
String sourceUnManagedVolumeId = PropertySetterUtil.extractValueFromStringSet(
SupportedVolumeInformation.REMOTE_MIRROR_SOURCE_VOLUME.toString(),
unManagedVolume.getVolumeInformation());
String sourceVolumeId = sourceUnManagedVolumeId.replace(VolumeIngestionUtil.UNMANAGEDVOLUME,
VolumeIngestionUtil.VOLUME);
List<URI> sourceUris = _dbClient.queryByConstraint(AlternateIdConstraint.Factory
.getVolumeNativeGuidConstraint(sourceVolumeId));
if (sourceUris.isEmpty()) {
_logger.info("Source {} Not found for target {}", sourceVolumeId, unManagedVolume.getNativeGuid());
} else {
// if source volume is 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.isEmpty() || !settings.containsKey(virtualArray.getId())) {
_logger.info(
"Target Volume's VArray {} is not matching already ingested source volume virtual pool's remote VArray ",
virtualArray.getId());
throw IngestionException.exceptions.unmanagedSRDFTargetVolumeVArrayMismatch(
unManagedVolume.getLabel(), sourceVolume.getVirtualArray().toString());
}
}
}
}