/*
* Copyright (c) 2015 EMC Corporation
* All Rights Reserved
*/
package com.emc.storageos.volumecontroller.impl.xtremio;
import java.net.URI;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang3.StringUtils;
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.BlockObject;
import com.emc.storageos.db.client.model.BlockSnapshot;
import com.emc.storageos.db.client.model.DataObject.Flag;
import com.emc.storageos.db.client.model.HostInterface;
import com.emc.storageos.db.client.model.Initiator;
import com.emc.storageos.db.client.model.StoragePool;
import com.emc.storageos.db.client.model.StoragePort;
import com.emc.storageos.db.client.model.StoragePort.TransportType;
import com.emc.storageos.db.client.model.StorageSystem;
import com.emc.storageos.db.client.model.StringMap;
import com.emc.storageos.db.client.model.StringSet;
import com.emc.storageos.db.client.model.StringSetMap;
import com.emc.storageos.db.client.model.Volume;
import com.emc.storageos.db.client.model.ZoneInfo;
import com.emc.storageos.db.client.model.ZoneInfoMap;
import com.emc.storageos.db.client.model.UnManagedDiscoveredObjects.UnManagedConsistencyGroup;
import com.emc.storageos.db.client.model.UnManagedDiscoveredObjects.UnManagedExportMask;
import com.emc.storageos.db.client.model.UnManagedDiscoveredObjects.UnManagedVolume;
import com.emc.storageos.db.client.model.UnManagedDiscoveredObjects.UnManagedVolume.SupportedVolumeCharacterstics;
import com.emc.storageos.db.client.model.UnManagedDiscoveredObjects.UnManagedVolume.SupportedVolumeInformation;
import com.emc.storageos.networkcontroller.impl.NetworkDeviceController;
import com.emc.storageos.plugins.AccessProfile;
import com.emc.storageos.plugins.common.Constants;
import com.emc.storageos.plugins.common.PartitionManager;
import com.emc.storageos.util.ExportUtils;
import com.emc.storageos.util.NetworkUtil;
import com.emc.storageos.volumecontroller.impl.NativeGUIDGenerator;
import com.emc.storageos.volumecontroller.impl.utils.DiscoveryUtils;
import com.emc.storageos.volumecontroller.impl.xtremio.prov.utils.XtremIOProvUtils;
import com.emc.storageos.vplexcontroller.VPlexControllerUtils;
import com.emc.storageos.xtremio.restapi.XtremIOClient;
import com.emc.storageos.xtremio.restapi.XtremIOClientFactory;
import com.emc.storageos.xtremio.restapi.XtremIOConstants;
import com.emc.storageos.xtremio.restapi.model.response.XtremIOConsistencyGroup;
import com.emc.storageos.xtremio.restapi.model.response.XtremIOInitiator;
import com.emc.storageos.xtremio.restapi.model.response.XtremIOObjectInfo;
import com.emc.storageos.xtremio.restapi.model.response.XtremIOVolume;
import com.google.common.base.Joiner;
import com.google.common.collect.Lists;
public class XtremIOUnManagedVolumeDiscoverer {
private static final String RP_SNAPSHOT_CRITERIA3 = "_SMP";
private static final String RP_SNAPSHOT_CRITERIA2 = ".RP_";
private static final String RP_SNAPSHOT_CRITERIA1 = "._RP_";
private static final Logger log = LoggerFactory.getLogger(XtremIOUnManagedVolumeDiscoverer.class);
private static final String UNMANAGED_VOLUME = "UnManagedVolume";
private static final String UNMANAGED_EXPORT_MASK = "UnManagedExportMask";
private static final String UNMANAGED_CONSISTENCY_GROUP = "UnManagedConsistencyGroup";
private static final String TRUE = "true";
private static final String FALSE = "false";
List<UnManagedVolume> unManagedVolumesToCreate = null;
List<UnManagedVolume> unManagedVolumesToUpdate = null;
Set<URI> allCurrentUnManagedVolumeUris = new HashSet<URI>();
private final Set<URI> allCurrentUnManagedCgURIs = new HashSet<URI>();
private List<UnManagedConsistencyGroup> unManagedCGToUpdate = null;
private List<UnManagedExportMask> unManagedExportMasksToCreate = null;
private List<UnManagedExportMask> unManagedExportMasksToUpdate = null;
private final Set<URI> allCurrentUnManagedExportMaskUris = new HashSet<URI>();
private Map<String, UnManagedConsistencyGroup> unManagedCGToUpdateMap = null;
private Set<String> unSupportedCG = null;
private XtremIOClientFactory xtremioRestClientFactory;
private NetworkDeviceController networkDeviceController;
public void setXtremioRestClientFactory(XtremIOClientFactory xtremioRestClientFactory) {
this.xtremioRestClientFactory = xtremioRestClientFactory;
}
public void setNetworkDeviceController(
NetworkDeviceController networkDeviceController) {
this.networkDeviceController = networkDeviceController;
}
private StringSet discoverVolumeSnaps(StorageSystem system, List<List<Object>> snapDetails, String parentGUID,
StringSet parentMatchedVPools, XtremIOClient xtremIOClient, String xioClusterName, DbClient dbClient,
Map<String, List<UnManagedVolume>> igUnmanagedVolumesMap, Map<String, StringSet> igKnownVolumesMap) throws Exception {
StringSet snaps = new StringSet();
Object snapNameToProcess;
for (List<Object> snapDetail : snapDetails) {
// This can't be null
snapNameToProcess = snapDetail.get(1);
if ((null == snapNameToProcess || snapNameToProcess.toString().length() == 0) || null == snapDetail.get(2)) {
log.warn("Skipping snapshot as it is null for volume {}", parentGUID);
continue;
}
// If this name is a trigger/match for RP automated snapshots, ignore it as well
String snapName = (String) snapNameToProcess;
if ((snapName.contains(RP_SNAPSHOT_CRITERIA1) || snapName.contains(RP_SNAPSHOT_CRITERIA2))
&& snapName.contains(RP_SNAPSHOT_CRITERIA3)) {
log.warn("Skipping snapshot {} because it is internal to RP for volume {}", snapName, parentGUID);
continue;
}
XtremIOVolume snap = xtremIOClient.getSnapShotDetails(snapNameToProcess.toString(), xioClusterName);
UnManagedVolume unManagedVolume = null;
boolean isExported = !snap.getLunMaps().isEmpty();
String managedSnapNativeGuid = NativeGUIDGenerator.generateNativeGuidForVolumeOrBlockSnapShot(
system.getNativeGuid(), snap.getVolInfo().get(0));
BlockSnapshot viprSnap = DiscoveryUtils.checkBlockSnapshotExistsInDB(dbClient, managedSnapNativeGuid);
if (null != viprSnap) {
log.info("Skipping snapshot {} as it is already managed by ViPR", managedSnapNativeGuid);
// Check if the xtremIO snap is exported. If yes, we need to store it to add to unmanaged
// export masks.
if (isExported) {
populateKnownVolsMap(snap, viprSnap, igKnownVolumesMap);
}
snaps.add(managedSnapNativeGuid);
continue;
}
String unManagedVolumeNatvieGuid = NativeGUIDGenerator.generateNativeGuidForPreExistingVolume(
system.getNativeGuid(), snap.getVolInfo().get(0));
unManagedVolume = DiscoveryUtils.checkUnManagedVolumeExistsInDB(dbClient,
unManagedVolumeNatvieGuid);
unManagedVolume = createUnManagedVolume(unManagedVolume, unManagedVolumeNatvieGuid, snap, igUnmanagedVolumesMap, system, null,
dbClient);
populateSnapInfo(unManagedVolume, snap, parentGUID, parentMatchedVPools);
snaps.add(unManagedVolumeNatvieGuid);
allCurrentUnManagedVolumeUris.add(unManagedVolume.getId());
// determine the the snapsets associated with the snap and then determine if the snap set
// is associated with a cg, if it is process the snap same as any unmanaged volume
if (snap.getSnapSetList() != null && !snap.getSnapSetList().isEmpty()) {
Object snapSetNameToProcess = null;
if (snap.getSnapSetList().size() > 1) {
// snaps that belong to multiple snap sets are not supported in ViPR
log.warn("Skipping snapshot {} as it belongs to multiple snapSets and this is not supported", snap.getVolInfo().get(0));
// add all the snap sets that this volume belongs to to the list that are unsupported for ingestion
for (List<Object> snapSet : snap.getSnapSetList()) {
snapSetNameToProcess = snapSet.get(1);
log.warn("Skipping SnapSet {} as it contains volumes belonging to multiple CGs and this is not supported",
snapSetNameToProcess.toString());
}
} else {
for (List<Object> snapSet : snap.getSnapSetList()) {
snapSetNameToProcess = snapSet.get(1);
XtremIOConsistencyGroup snapSetDetails = xtremIOClient.getSnapshotSetDetails(snapSetNameToProcess.toString(),
xioClusterName);
if (snapSetDetails != null && snapSetDetails.getCgName() != null) {
log.info("Snapshot {} belongs to consistency group {} on the array",
snapSetNameToProcess.toString(), snapSetDetails.getCgName());
addObjectToUnManagedConsistencyGroup(xtremIOClient, unManagedVolume, snapSetDetails.getCgName(), system,
xioClusterName, dbClient);
} else {
log.info("Snapshot {} does not belong to a consistency group.",
snapSetNameToProcess.toString());
}
}
}
}
}
return snaps;
}
public void discoverUnManagedObjects(AccessProfile accessProfile, DbClient dbClient,
PartitionManager partitionManager) throws Exception {
log.info("Started discovery of UnManagedVolumes for system {}", accessProfile.getSystemId());
StorageSystem storageSystem = dbClient.queryObject(StorageSystem.class,
accessProfile.getSystemId());
XtremIOClient xtremIOClient = XtremIOProvUtils.getXtremIOClient(dbClient, storageSystem, xtremioRestClientFactory);
unManagedVolumesToCreate = new ArrayList<UnManagedVolume>();
unManagedVolumesToUpdate = new ArrayList<UnManagedVolume>();
unManagedCGToUpdateMap = new HashMap<String, UnManagedConsistencyGroup>();
// get the storage pool associated with the xtremio system
StoragePool storagePool = XtremIOProvUtils.getXtremIOStoragePool(storageSystem.getId(), dbClient);
if (storagePool == null) {
log.error("Skipping unmanaged volume discovery as the volume storage pool doesn't exist in ViPR");
return;
}
Map<String, List<UnManagedVolume>> igUnmanagedVolumesMap = new HashMap<String, List<UnManagedVolume>>();
Map<String, StringSet> igKnownVolumesMap = new HashMap<String, StringSet>();
Map<String, Map<String,Integer>> volumeIGHLUMap = new HashMap<String, Map<String,Integer>>();
String xioClusterName = xtremIOClient.getClusterDetails(storageSystem.getSerialNumber()).getName();
// get the xtremio volume links and process them in batches
List<XtremIOObjectInfo> volLinks = xtremIOClient.getXtremIOVolumeLinks(xioClusterName);
// Get the volume details
List<List<XtremIOObjectInfo>> volume_partitions = Lists.partition(volLinks, Constants.DEFAULT_PARTITION_SIZE);
// Set containing cgs that cannot be ingested, for now that
// means they contain volumes which belong to more than one cg
unSupportedCG = new HashSet<String>();
for (List<XtremIOObjectInfo> partition : volume_partitions) {
List<XtremIOVolume> volumes = xtremIOClient.getXtremIOVolumesForLinks(partition, xioClusterName);
for (XtremIOVolume volume : volumes) {
try {
// If the volume is a snap don't process it. We will get the snap info from the
// volumes later
if (volume.getAncestoVolInfo() != null && !volume.getAncestoVolInfo().isEmpty()) {
log.debug("Skipping volume {} as it is a snap", volume.getVolInfo().get(0));
continue;
}
// check if cgs are null before trying to access, older versions of
// the XtremIO REST client do not return cgs as part of volume response
// flag indicating the volume is part of a cg
boolean hasCGs = false;
if (volume.getConsistencyGroups() != null && !volume.getConsistencyGroups().isEmpty()) {
hasCGs = true;
if (volume.getConsistencyGroups().size() > 1) {
// volumes that belong to multiple CGs are not supported in ViPR
log.warn("Skipping volume {} as it belongs to multiple CGs and this is not supported",
volume.getVolInfo().get(0));
// add all the CGs that this volume belongs to to the list that are unsupported for ingestion
for (List<Object> cg : volume.getConsistencyGroups()) {
Object cgNameToProcess = cg.get(1);
unSupportedCG.add(cgNameToProcess.toString());
log.warn("Skipping CG {} as it contains volumes belonging to multiple CGs and this is not supported",
cgNameToProcess.toString());
}
continue;
}
}
UnManagedVolume unManagedVolume = null;
boolean isExported = !volume.getLunMaps().isEmpty();
boolean hasSnaps = !volume.getSnaps().isEmpty();
String managedVolumeNativeGuid = NativeGUIDGenerator.generateNativeGuidForVolumeOrBlockSnapShot(
storageSystem.getNativeGuid(), volume.getVolInfo().get(0));
Volume viprVolume = DiscoveryUtils.checkStorageVolumeExistsInDB(dbClient, managedVolumeNativeGuid);
if (null != viprVolume) {
log.info("Skipping volume {} as it is already managed by ViPR", managedVolumeNativeGuid);
// Check if the xtremIO vol is exported. If yes, we need to store it to add to unmanaged
// export masks.
if (isExported) {
populateKnownVolsMap(volume, viprVolume, igKnownVolumesMap);
}
// retrieve snap info to be processed later
if (hasSnaps) {
StringSet vpoolUriSet = new StringSet();
vpoolUriSet.add(viprVolume.getVirtualPool().toString());
discoverVolumeSnaps(storageSystem, volume.getSnaps(), viprVolume.getNativeGuid(), vpoolUriSet,
xtremIOClient, xioClusterName, dbClient, igUnmanagedVolumesMap, igKnownVolumesMap);
}
continue;
}
String unManagedVolumeNatvieGuid = NativeGUIDGenerator.generateNativeGuidForPreExistingVolume(
storageSystem.getNativeGuid(), volume.getVolInfo().get(0));
// retrieve snap info to be processed later
unManagedVolume = DiscoveryUtils.checkUnManagedVolumeExistsInDB(dbClient,
unManagedVolumeNatvieGuid);
unManagedVolume = createUnManagedVolume(unManagedVolume, unManagedVolumeNatvieGuid, volume, igUnmanagedVolumesMap,
storageSystem, storagePool, dbClient);
// if the volume is associated with a CG, set up the unmanaged CG
if (hasCGs) {
for (List<Object> cg : volume.getConsistencyGroups()) {
// retrieve what should be the first and only consistency group from the list
// volumes belonging to multiple cgs are not supported and were excluded above
Object cgNameToProcess = cg.get(1);
addObjectToUnManagedConsistencyGroup(xtremIOClient, unManagedVolume, cgNameToProcess.toString(),
storageSystem, xioClusterName, dbClient);
}
} else {
// Make sure the unManagedVolume object does not contain CG information from previous discovery
unManagedVolume.getVolumeCharacterstics().put(
SupportedVolumeCharacterstics.IS_VOLUME_ADDED_TO_CONSISTENCYGROUP.toString(), Boolean.FALSE.toString());
// set the uri of the unmanaged CG in the unmanaged volume object to empty
unManagedVolume.getVolumeInformation().put(SupportedVolumeInformation.UNMANAGED_CONSISTENCY_GROUP_URI.toString(),
"");
}
if (isExported) {
Map<String, Integer> igHLUMap = new HashMap<String, Integer>();
for (List<Object> lunMapEntries : volume.getLunMaps()) {
Double hlu = (Double) lunMapEntries.get(2);
log.info("Found HLU {}", hlu);
List<Object> igDetails = (List<Object>) lunMapEntries.get(0);
igHLUMap.put(igDetails.get(1).toString(), Integer.valueOf(hlu.intValue())); // key value IG-HLU
}
if (!igHLUMap.isEmpty()) {
volumeIGHLUMap.put(unManagedVolumeNatvieGuid, igHLUMap);
}
}
boolean hasReplicas = false;
if (hasSnaps) {
StringSet parentMatchedVPools = unManagedVolume.getSupportedVpoolUris();
StringSet discoveredSnaps = discoverVolumeSnaps(storageSystem, volume.getSnaps(), unManagedVolumeNatvieGuid,
parentMatchedVPools, xtremIOClient, xioClusterName, dbClient, igUnmanagedVolumesMap, igKnownVolumesMap);
if (!discoveredSnaps.isEmpty()) {
hasReplicas = true;
// set the HAS_REPLICAS property
unManagedVolume.getVolumeCharacterstics().put(SupportedVolumeCharacterstics.HAS_REPLICAS.toString(),
TRUE);
StringSetMap unManagedVolumeInformation = unManagedVolume.getVolumeInformation();
if (unManagedVolumeInformation.containsKey(SupportedVolumeInformation.SNAPSHOTS.toString())) {
log.debug("Snaps :" + Joiner.on("\t").join(discoveredSnaps));
if (null != discoveredSnaps && discoveredSnaps.isEmpty()) {
// replace with empty string set doesn't work, hence added explicit code to remove all
unManagedVolumeInformation.get(
SupportedVolumeInformation.SNAPSHOTS.toString()).clear();
} else {
// replace with new StringSet
unManagedVolumeInformation.get(
SupportedVolumeInformation.SNAPSHOTS.toString()).replace(discoveredSnaps);
log.info("Replaced snaps :" + Joiner.on("\t").join(unManagedVolumeInformation.get(
SupportedVolumeInformation.SNAPSHOTS.toString())));
}
} else {
unManagedVolumeInformation.put(
SupportedVolumeInformation.SNAPSHOTS.toString(), discoveredSnaps);
}
}
}
if (!hasReplicas) {
unManagedVolume.getVolumeCharacterstics().put(SupportedVolumeCharacterstics.HAS_REPLICAS.toString(), FALSE);
StringSet snapshots = unManagedVolume.getVolumeInformation().get(SupportedVolumeInformation.SNAPSHOTS.toString());
if (snapshots != null && !snapshots.isEmpty()) {
unManagedVolume.getVolumeInformation().get(SupportedVolumeInformation.SNAPSHOTS.toString()).clear();
}
}
allCurrentUnManagedVolumeUris.add(unManagedVolume.getId());
} catch (Exception ex) {
log.error("Error processing XIO volume {}", volume, ex);
}
}
}
if (!unManagedCGToUpdateMap.isEmpty()) {
unManagedCGToUpdate = new ArrayList<UnManagedConsistencyGroup>(unManagedCGToUpdateMap.values());
partitionManager.updateAndReIndexInBatches(unManagedCGToUpdate,
unManagedCGToUpdate.size(), dbClient, UNMANAGED_CONSISTENCY_GROUP);
unManagedCGToUpdate.clear();
}
if (!unManagedVolumesToCreate.isEmpty()) {
partitionManager.insertInBatches(unManagedVolumesToCreate,
Constants.DEFAULT_PARTITION_SIZE, dbClient, UNMANAGED_VOLUME);
unManagedVolumesToCreate.clear();
}
if (!unManagedVolumesToUpdate.isEmpty()) {
partitionManager.updateAndReIndexInBatches(unManagedVolumesToUpdate,
Constants.DEFAULT_PARTITION_SIZE, dbClient, UNMANAGED_VOLUME);
unManagedVolumesToUpdate.clear();
}
// Process those active unmanaged volume objects available in database but not in newly discovered items, to mark them inactive.
DiscoveryUtils.markInActiveUnManagedVolumes(storageSystem, allCurrentUnManagedVolumeUris, dbClient, partitionManager);
// Process those active unmanaged consistency group objects available in database but not in newly discovered items, to mark them
// inactive.
DiscoveryUtils.performUnManagedConsistencyGroupsBookKeeping(storageSystem, allCurrentUnManagedCgURIs, dbClient, partitionManager);
// Next discover the unmanaged export masks
discoverUnmanagedExportMasks(storageSystem.getId(), igUnmanagedVolumesMap, igKnownVolumesMap, xtremIOClient, xioClusterName,
dbClient, partitionManager, volumeIGHLUMap);
}
private void populateKnownVolsMap(XtremIOVolume vol, BlockObject viprObj, Map<String, StringSet> igKnownVolumesMap) {
for (List<Object> lunMapEntries : vol.getLunMaps()) {
@SuppressWarnings("unchecked")
// This can't be null
List<Object> igDetails = (List<Object>) lunMapEntries.get(0);
if (null == igDetails.get(1) || null == lunMapEntries.get(2)) {
log.warn("IG Name is null in returned lun map response for snap {}", vol.toString());
continue;
}
String igNameToProcess = (String) igDetails.get(1);
StringSet knownVolumes = igKnownVolumesMap.get(igNameToProcess);
if (knownVolumes == null) {
knownVolumes = new StringSet();
igKnownVolumesMap.put(igNameToProcess, knownVolumes);
}
knownVolumes.add(viprObj.getId().toString());
}
}
private void populateSnapInfo(UnManagedVolume unManagedVolume, XtremIOVolume xioSnap, String parentVolumeNatvieGuid,
StringSet parentMatchedVPools) {
unManagedVolume.getVolumeCharacterstics().put(SupportedVolumeCharacterstics.IS_SNAP_SHOT.toString(), TRUE);
StringSet parentVol = new StringSet();
parentVol.add(parentVolumeNatvieGuid);
unManagedVolume.getVolumeInformation().put(SupportedVolumeInformation.LOCAL_REPLICA_SOURCE_VOLUME.toString(), parentVol);
StringSet isSyncActive = new StringSet();
isSyncActive.add(TRUE);
unManagedVolume.getVolumeInformation().put(SupportedVolumeInformation.IS_SYNC_ACTIVE.toString(), isSyncActive);
StringSet isReadOnly = new StringSet();
Boolean readOnly = XtremIOConstants.XTREMIO_READ_ONLY_TYPE.equals(xioSnap.getSnapshotType()) ? Boolean.TRUE : Boolean.FALSE;
isReadOnly.add(readOnly.toString());
unManagedVolume.getVolumeInformation().put(SupportedVolumeInformation.IS_READ_ONLY.toString(), isReadOnly);
StringSet techType = new StringSet();
techType.add(BlockSnapshot.TechnologyType.NATIVE.toString());
unManagedVolume.getVolumeInformation().put(SupportedVolumeInformation.TECHNOLOGY_TYPE.toString(), techType);
if (xioSnap.getSnapSetList() != null && !xioSnap.getSnapSetList().isEmpty()) {
StringSet snapsetName = new StringSet();
List<Object> snapsetDetails = xioSnap.getSnapSetList().get(0);
snapsetName.add(snapsetDetails.get(1).toString());
unManagedVolume.getVolumeInformation().put(SupportedVolumeInformation.SNAPSHOT_CONSISTENCY_GROUP_NAME.toString(),
snapsetName);
}
log.debug("Matched Pools : {}", Joiner.on("\t").join(parentMatchedVPools));
if (null == parentMatchedVPools || parentMatchedVPools.isEmpty()) {
// Clearn all vpools as no matching vpools found.
unManagedVolume.getSupportedVpoolUris().clear();
} else {
// replace with new StringSet
unManagedVolume.getSupportedVpoolUris().replace(parentMatchedVPools);
log.info("Replaced Pools :{}", Joiner.on("\t").join(unManagedVolume.getSupportedVpoolUris()));
}
}
/**
* Group existing known initiators by Host.
* For each HostName in HostGroup
* 1. Get List of IGs associated with Host
* 2. For each IG, get unmanaged and managed volumes
* 3. create/update mask with volumes/initiators/ports
*
* @param systemId
* @param igUnmanagedVolumesMap IgName--Unmanaged volume list
* @param igKnownVolumesMap IgName -- managed volume list
* @param xtremIOClient
* @param dbClient
* @param partitionManager
* @param volumeIGHLUMap
* @throws Exception
*/
private void discoverUnmanagedExportMasks(URI systemId, Map<String, List<UnManagedVolume>> igUnmanagedVolumesMap,
Map<String, StringSet> igKnownVolumesMap, XtremIOClient xtremIOClient, String xioClusterName,
DbClient dbClient, PartitionManager partitionManager, Map<String, Map<String, Integer>> volumeIGHLUMap)
throws Exception {
unManagedExportMasksToCreate = new ArrayList<UnManagedExportMask>();
unManagedExportMasksToUpdate = new ArrayList<UnManagedExportMask>();
List<UnManagedVolume> unManagedExportVolumesToUpdate = new ArrayList<UnManagedVolume>();
// In XtremIO, the volumes are exposed through all the storage ports.
// Get all the storage ports of xtremIO to be added as known ports in the unmanaged export mask
// If the host ports are FC, then all add all FC storage ports to the mask
// else add all IP ports
StringSet knownFCStoragePortUris = new StringSet();
StringSet knownIPStoragePortUris = new StringSet();
List<StoragePort> matchedFCPorts = new ArrayList<StoragePort>();
URIQueryResultList storagePortURIs = new URIQueryResultList();
dbClient.queryByConstraint(
ContainmentConstraint.Factory.getStorageDeviceStoragePortConstraint(systemId),
storagePortURIs);
Iterator<URI> portsItr = storagePortURIs.iterator();
while (portsItr.hasNext()) {
URI storagePortURI = portsItr.next();
StoragePort port = dbClient.queryObject(StoragePort.class, storagePortURI);
if (TransportType.FC.toString().equals(port.getTransportType())) {
knownFCStoragePortUris.add(storagePortURI.toString());
matchedFCPorts.add(port);
} else if (TransportType.IP.toString().equals(port.getTransportType())) {
knownIPStoragePortUris.add(storagePortURI.toString());
}
}
// Group all the initiators and their initiator groups based on ViPR host.
// To be used for constructing unmanaged export masks
Map<String, List<Initiator>> hostInitiatorsMap = new HashMap<String, List<Initiator>>();
Map<String, Set<String>> hostIGNamesMap = new HashMap<String, Set<String>>();
List<XtremIOInitiator> initiators = xtremIOClient.getXtremIOInitiatorsInfo(xioClusterName);
for (XtremIOInitiator initiator : initiators) {
String initiatorNetworkId = initiator.getPortAddress();
// check if a host initiator exists for this id
Initiator knownInitiator = NetworkUtil.getInitiator(initiatorNetworkId, dbClient);
if (knownInitiator == null) {
// TODO need to think of ways to handle unknown initiators
continue;
}
// Special case for RP: group masks by cluster.
String hostName = knownInitiator.checkInternalFlags(Flag.RECOVERPOINT) ? knownInitiator.getClusterName() : knownInitiator
.getHostName();
if (hostName != null && !hostName.isEmpty()) {
log.info(" found an initiator in ViPR on host " + hostName);
String igName = initiator.getInitiatorGroup().get(1);
List<Initiator> hostInitiators = hostInitiatorsMap.get(hostName);
Set<String> hostIGNames = hostIGNamesMap.get(hostName);
if (hostInitiators == null) {
hostInitiators = new ArrayList<Initiator>();
hostInitiatorsMap.put(hostName, hostInitiators);
}
if (hostIGNames == null) {
hostIGNames = new HashSet<String>();
hostIGNamesMap.put(hostName, hostIGNames);
}
hostInitiators.add(knownInitiator);
hostIGNames.add(igName);
}
}
// create export mask per vipr host
for (String hostname : hostInitiatorsMap.keySet()) {
StringSet knownIniSet = new StringSet();
StringSet knownNetworkIdSet = new StringSet();
StringSet knownVolumeSet = new StringSet();
List<Initiator> matchedFCInitiators = new ArrayList<Initiator>();
List<Initiator> hostInitiators = hostInitiatorsMap.get(hostname);
Set<String> hostIGs = hostIGNamesMap.get(hostname);
Map<String, List<String>> rpVolumeSnapMap = new HashMap<String, List<String>>();
Map<String, UnManagedVolume> rpVolumeMap = new HashMap<String, UnManagedVolume>();
boolean isRpBackendMask = false;
if (ExportUtils.checkIfInitiatorsForRP(hostInitiators)) {
log.info("host {} contains RP initiators, "
+ "so this mask contains RP protected volumes", hostname);
isRpBackendMask = true;
}
boolean isVplexBackendMask = false;
for (Initiator hostInitiator : hostInitiators) {
if (!isVplexBackendMask && VPlexControllerUtils.isVplexInitiator(hostInitiator, dbClient)) {
log.info("host {} contains VPLEX backend ports, "
+ "so this mask contains VPLEX backend volumes", hostname);
isVplexBackendMask = true;
}
knownIniSet.add(hostInitiator.getId().toString());
knownNetworkIdSet.add(hostInitiator.getInitiatorPort());
if (HostInterface.Protocol.FC.toString().equals(hostInitiator.getProtocol())) {
matchedFCInitiators.add(hostInitiator);
}
}
UnManagedExportMask mask = getUnManagedExportMask(hostInitiators.get(0).getInitiatorPort(), dbClient, systemId);
mask.setStorageSystemUri(systemId);
// set the host name as the mask name
mask.setMaskName(hostname);
allCurrentUnManagedExportMaskUris.add(mask.getId());
for (String igName : hostIGs) {
StringSet knownVols = igKnownVolumesMap.get(igName);
if (knownVols != null) {
knownVolumeSet.addAll(knownVols);
}
List<UnManagedVolume> hostUnManagedVols = igUnmanagedVolumesMap.get(igName);
if (hostUnManagedVols != null) {
for (UnManagedVolume hostUnManagedVol : hostUnManagedVols) {
hostUnManagedVol.getInitiatorNetworkIds().addAll(knownNetworkIdSet);
hostUnManagedVol.getInitiatorUris().addAll(knownIniSet);
hostUnManagedVol.getUnmanagedExportMasks().add(mask.getId().toString());
String nativeGuid = hostUnManagedVol.getNativeGuid();
Map<String, Integer> igHLUMap = volumeIGHLUMap.get(nativeGuid);
if (igHLUMap != null) {
Integer hlu = igHLUMap.get(igName);
if (hlu != null) {
StringSet hostHlu = new StringSet();
hostHlu.add(hostname + "=" + hlu);
StringSet existingHostHlu = (StringSet) hostUnManagedVol.getVolumeInformation().get(
SupportedVolumeInformation.HLU_TO_EXPORT_MASK_NAME_MAP.toString());
if (existingHostHlu != null) {
hostHlu.addAll(existingHostHlu);
}
hostUnManagedVol.getVolumeInformation().put(SupportedVolumeInformation.HLU_TO_EXPORT_MASK_NAME_MAP.toString(), hostHlu);
}
}
if (isVplexBackendMask) {
log.info("marking unmanaged Xtremio volume {} as a VPLEX backend volume",
hostUnManagedVol.getLabel());
hostUnManagedVol.putVolumeCharacterstics(
SupportedVolumeCharacterstics.IS_VPLEX_BACKEND_VOLUME.toString(),
TRUE);
}
// RP processing for this volume in this mask. By default, the RP characteristics are set to false, so
// here we check if the volume is in any non-RP mask. If so, it's exported to something other than RP, and
// we need to mark that so we can do an exported-volume ingestion. Secondly if any mask associated with this
// volume IS associated with RP, we want to mark that volume as RP enabled.
if (!isRpBackendMask) {
log.info("unmanaged volume {} is exported to something other than RP. Marking IS_NONRP_EXPORTED.",
hostUnManagedVol.forDisplay());
hostUnManagedVol.putVolumeCharacterstics(
SupportedVolumeCharacterstics.IS_NONRP_EXPORTED.toString(),
TRUE);
} else {
log.info("unmanaged volume {} is an RP volume", hostUnManagedVol.getLabel());
hostUnManagedVol.putVolumeCharacterstics(
SupportedVolumeCharacterstics.IS_RECOVERPOINT_ENABLED.toString(),
TRUE);
rpVolumeMap.put(hostUnManagedVol.getNativeGuid(), hostUnManagedVol);
}
if (hostUnManagedVol != null) {
mask.getUnmanagedVolumeUris().add(hostUnManagedVol.getId().toString());
unManagedExportVolumesToUpdate.add(hostUnManagedVol);
}
}
}
}
mask.replaceNewWithOldResources(knownIniSet, knownNetworkIdSet, knownVolumeSet,
!matchedFCInitiators.isEmpty() ? knownFCStoragePortUris : knownIPStoragePortUris);
updateZoningMap(mask, matchedFCInitiators, matchedFCPorts);
if (!unManagedExportMasksToCreate.isEmpty()) {
partitionManager.insertInBatches(unManagedExportMasksToCreate,
Constants.DEFAULT_PARTITION_SIZE, dbClient, UNMANAGED_EXPORT_MASK);
unManagedExportMasksToCreate.clear();
}
if (!unManagedExportMasksToUpdate.isEmpty()) {
partitionManager.updateInBatches(unManagedExportMasksToUpdate,
Constants.DEFAULT_PARTITION_SIZE, dbClient, UNMANAGED_EXPORT_MASK);
unManagedExportMasksToUpdate.clear();
}
if (!unManagedExportVolumesToUpdate.isEmpty()) {
partitionManager.updateAndReIndexInBatches(unManagedExportVolumesToUpdate,
Constants.DEFAULT_PARTITION_SIZE, dbClient, UNMANAGED_VOLUME);
unManagedExportVolumesToUpdate.clear();
}
DiscoveryUtils.markInActiveUnManagedExportMask(systemId, allCurrentUnManagedExportMaskUris, dbClient, partitionManager);
}
}
private void updateZoningMap(UnManagedExportMask mask, List<Initiator> initiators, List<StoragePort> storagePorts) {
ZoneInfoMap zoningMap = networkDeviceController.getInitiatorsZoneInfoMap(initiators, storagePorts);
for (ZoneInfo zoneInfo : zoningMap.values()) {
log.info("Found zone: {} for initiator {} and port {}", new Object[] { zoneInfo.getZoneName(),
zoneInfo.getInitiatorWwn(), zoneInfo.getPortWwn() });
}
mask.setZoningMap(zoningMap);
}
private UnManagedExportMask getUnManagedExportMask(String knownInitiatorNetworkId, DbClient dbClient, URI systemURI) {
URIQueryResultList result = new URIQueryResultList();
dbClient.queryByConstraint(AlternateIdConstraint.Factory
.getUnManagedExportMaskKnownInitiatorConstraint(knownInitiatorNetworkId), result);
UnManagedExportMask uem = null;
Iterator<URI> it = result.iterator();
while (it.hasNext()) {
UnManagedExportMask potentialUem = dbClient.queryObject(UnManagedExportMask.class, it.next());
// Check whether the uem belongs to the same storage system. This to avoid in picking up the
// vplex uem.
if (URIUtil.identical(potentialUem.getStorageSystemUri(), systemURI)) {
uem = potentialUem;
unManagedExportMasksToUpdate.add(uem);
break;
}
}
if (uem != null && !uem.getInactive()) {
// clean up collections (we'll be refreshing them)
uem.getKnownInitiatorUris().clear();
uem.getKnownInitiatorNetworkIds().clear();
uem.getKnownStoragePortUris().clear();
uem.getKnownVolumeUris().clear();
uem.getUnmanagedInitiatorNetworkIds().clear();
uem.getUnmanagedStoragePortNetworkIds().clear();
uem.getUnmanagedVolumeUris().clear();
} else {
uem = new UnManagedExportMask();
uem.setId(URIUtil.createId(UnManagedExportMask.class));
unManagedExportMasksToCreate.add(uem);
}
return uem;
}
/**
* Creates a new UnManagedConsistencyGroup object in the database
*
* @param unManagedCGNativeGuid - nativeGuid of the unmanaged consistency group
* @param consistencyGroup - xtremio consistency group returned from REST client
* @param storageSystemURI - storage system of the consistency group
* @param dbClient - database client
* @return the new UnManagedConsistencyGroup object
*/
private UnManagedConsistencyGroup createUnManagedCG(String unManagedCGNativeGuid,
XtremIOConsistencyGroup consistencyGroup, URI storageSystemURI, DbClient dbClient) {
UnManagedConsistencyGroup unManagedCG = new UnManagedConsistencyGroup();
unManagedCG.setId(URIUtil.createId(UnManagedConsistencyGroup.class));
unManagedCG.setLabel(consistencyGroup.getName());
unManagedCG.setName(consistencyGroup.getName());
unManagedCG.setNativeGuid(unManagedCGNativeGuid);
unManagedCG.setStorageSystemUri(storageSystemURI);
unManagedCG.setNumberOfVols(consistencyGroup.getNumOfVols());
dbClient.createObject(unManagedCG);
return unManagedCG;
}
/**
* Creates a new UnManagedVolume with the given arguments.
*
* @param unManagedVolumeNativeGuid
* @param volume
* @param system
* @param pool
* @param dbClient
* @return
*/
private UnManagedVolume createUnManagedVolume(UnManagedVolume unManagedVolume, String unManagedVolumeNativeGuid,
XtremIOVolume volume, Map<String, List<UnManagedVolume>> igVolumesMap, StorageSystem system,
StoragePool pool, DbClient dbClient) {
boolean created = false;
StringSetMap unManagedVolumeInformation = null;
Map<String, String> unManagedVolumeCharacteristics = null;
if (null == unManagedVolume) {
unManagedVolume = new UnManagedVolume();
unManagedVolume.setId(URIUtil.createId(UnManagedVolume.class));
unManagedVolume.setNativeGuid(unManagedVolumeNativeGuid);
unManagedVolume.setStorageSystemUri(system.getId());
if (pool != null) {
unManagedVolume.setStoragePoolUri(pool.getId());
}
created = true;
unManagedVolumeInformation = new StringSetMap();
unManagedVolumeCharacteristics = new HashMap<String, String>();
} else {
unManagedVolumeInformation = unManagedVolume.getVolumeInformation();
unManagedVolumeCharacteristics = unManagedVolume.getVolumeCharacterstics();
}
unManagedVolume.setLabel(volume.getVolInfo().get(1));
Boolean isVolumeExported = false;
if (!volume.getLunMaps().isEmpty()) {
// clear the previous unmanaged export masks, initiators if any. The latest export masks will be updated later.
unManagedVolume.getUnmanagedExportMasks().clear();
unManagedVolume.getInitiatorNetworkIds().clear();
unManagedVolume.getInitiatorUris().clear();
isVolumeExported = true;
for (List<Object> lunMapEntries : volume.getLunMaps()) {
@SuppressWarnings("unchecked")
// This can't be null
List<Object> igDetails = (List<Object>) lunMapEntries.get(0);
if (null == igDetails.get(1) || null == lunMapEntries.get(2)) {
log.warn("IG Name is null in returned lun map response for volume {}", volume.toString());
continue;
}
String igNameToProcess = (String) igDetails.get(1);
List<UnManagedVolume> igVolumes = igVolumesMap.get(igNameToProcess);
if (igVolumes == null) {
igVolumes = new ArrayList<UnManagedVolume>();
igVolumesMap.put(igNameToProcess, igVolumes);
}
igVolumes.add(unManagedVolume);
}
}
unManagedVolumeCharacteristics.put(SupportedVolumeCharacterstics.IS_VOLUME_EXPORTED.toString(), isVolumeExported.toString());
// Set these default to false. The individual storage discovery will change them if needed.
unManagedVolumeCharacteristics.put(SupportedVolumeCharacterstics.IS_NONRP_EXPORTED.toString(), FALSE);
unManagedVolumeCharacteristics.put(SupportedVolumeCharacterstics.IS_RECOVERPOINT_ENABLED.toString(), FALSE);
StringSet deviceLabel = new StringSet();
deviceLabel.add(volume.getVolInfo().get(1));
unManagedVolumeInformation.put(SupportedVolumeInformation.DEVICE_LABEL.toString(),
deviceLabel);
String volumeWWN = volume.getWwn().isEmpty() ? volume.getVolInfo().get(0) : volume.getWwn();
unManagedVolume.setWwn(volumeWWN);
StringSet systemTypes = new StringSet();
systemTypes.add(system.getSystemType());
StringSet accessState = new StringSet();
accessState.add(Volume.VolumeAccessState.READWRITE.getState());
unManagedVolumeInformation.put(SupportedVolumeInformation.ACCESS.toString(), accessState);
StringSet provCapacity = new StringSet();
provCapacity.add(String.valueOf(Long.parseLong(volume.getAllocatedCapacity()) * 1024));
unManagedVolumeInformation.put(SupportedVolumeInformation.PROVISIONED_CAPACITY.toString(),
provCapacity);
StringSet allocatedCapacity = new StringSet();
allocatedCapacity.add(String.valueOf(Long.parseLong(volume.getAllocatedCapacity()) * 1024));
unManagedVolumeInformation.put(SupportedVolumeInformation.ALLOCATED_CAPACITY.toString(),
allocatedCapacity);
unManagedVolumeInformation.put(SupportedVolumeInformation.SYSTEM_TYPE.toString(),
systemTypes);
StringSet nativeId = new StringSet();
nativeId.add(volume.getVolInfo().get(0));
unManagedVolumeInformation.put(SupportedVolumeInformation.NATIVE_ID.toString(),
nativeId);
unManagedVolumeCharacteristics.put(
SupportedVolumeCharacterstics.IS_INGESTABLE.toString(), TRUE);
unManagedVolumeCharacteristics.put(SupportedVolumeCharacterstics.IS_THINLY_PROVISIONED.toString(),
TRUE);
// Set up default MAXIMUM_IO_BANDWIDTH and MAXIMUM_IOPS
StringSet bwValues = new StringSet();
bwValues.add("0");
if (unManagedVolumeInformation.get(SupportedVolumeInformation.EMC_MAXIMUM_IO_BANDWIDTH.toString()) == null) {
unManagedVolumeInformation.put(SupportedVolumeInformation.EMC_MAXIMUM_IO_BANDWIDTH.toString(), bwValues);
} else {
unManagedVolumeInformation.get(SupportedVolumeInformation.EMC_MAXIMUM_IO_BANDWIDTH.toString()).replace(
bwValues);
}
StringSet iopsVal = new StringSet();
iopsVal.add("0");
if (unManagedVolumeInformation.get(SupportedVolumeInformation.EMC_MAXIMUM_IOPS.toString()) == null) {
unManagedVolumeInformation.put(SupportedVolumeInformation.EMC_MAXIMUM_IOPS.toString(), iopsVal);
} else {
unManagedVolumeInformation.get(SupportedVolumeInformation.EMC_MAXIMUM_IOPS.toString()).replace(iopsVal);
}
if (null != pool) {
unManagedVolume.setStoragePoolUri(pool.getId());
StringSet pools = new StringSet();
pools.add(pool.getId().toString());
unManagedVolumeInformation.put(SupportedVolumeInformation.STORAGE_POOL.toString(), pools);
StringSet driveTypes = pool.getSupportedDriveTypes();
if (null != driveTypes) {
unManagedVolumeInformation.put(
SupportedVolumeInformation.DISK_TECHNOLOGY.toString(),
driveTypes);
}
StringSet matchedVPools = DiscoveryUtils.getMatchedVirtualPoolsForPool(dbClient, pool.getId(),
unManagedVolumeCharacteristics.get(SupportedVolumeCharacterstics.IS_THINLY_PROVISIONED.toString()),
unManagedVolume);
log.debug("Matched Pools : {}", Joiner.on("\t").join(matchedVPools));
if (null == matchedVPools || matchedVPools.isEmpty()) {
// clear all existing supported vpools.
unManagedVolume.getSupportedVpoolUris().clear();
} else {
// replace with new StringSet
unManagedVolume.getSupportedVpoolUris().replace(matchedVPools);
log.info("Replaced Pools : {}", Joiner.on("\t").join(unManagedVolume.getSupportedVpoolUris()));
}
}
unManagedVolume.setVolumeInformation(unManagedVolumeInformation);
if (unManagedVolume.getVolumeCharacterstics() == null) {
unManagedVolume.setVolumeCharacterstics(new StringMap());
}
unManagedVolume.getVolumeCharacterstics().replace(unManagedVolumeCharacteristics);
if (created) {
unManagedVolumesToCreate.add(unManagedVolume);
} else {
unManagedVolumesToUpdate.add(unManagedVolume);
}
return unManagedVolume;
}
/**
* Adds the passed in unmanaged volume or unmanaged snapshot
* to an unmanaged consistency group object
*
* @param xtremIOClient - connection to xtremio REST interface
* @param unManagedVolume - either and unmanaged volume or unmanaged snapshot
* associated with a consistency group
* @param cgNameToProcess - consistency group being processed
* @param storageSystem - storage system the objects are on
* @param xioClusterName - name of the xtremio cluster being managed by the xtremio XMS
* @param dbClient - dbclient
* @throws Exception
*/
private void addObjectToUnManagedConsistencyGroup(XtremIOClient xtremIOClient, UnManagedVolume unManagedVolume,
String cgNameToProcess, StorageSystem storageSystem, String xioClusterName, DbClient dbClient) throws Exception {
// Check if the current CG is in the list of unsupported CGs
if (!unSupportedCG.isEmpty() && unSupportedCG.contains(cgNameToProcess.toString())) {
// leave the for loop and do nothing
log.warn("Skipping CG {} as it contains volumes belonging to multiple CGs and this is not supported",
cgNameToProcess.toString());
return;
}
log.info("Unmanaged volume {} belongs to consistency group {} on the array", unManagedVolume.getLabel(), cgNameToProcess);
// Update the unManagedVolume object with CG information
unManagedVolume.getVolumeCharacterstics().put(SupportedVolumeCharacterstics.IS_VOLUME_ADDED_TO_CONSISTENCYGROUP.toString(),
Boolean.TRUE.toString());
// Get the unmanaged CG details from the array
XtremIOConsistencyGroup xioCG = xtremIOClient.getConsistencyGroupDetails(cgNameToProcess.toString(), xioClusterName);
// determine the native guid for the unmanaged CG (storage system id + xio cg guid)
String unManagedCGNativeGuid = NativeGUIDGenerator.generateNativeGuidForCG(storageSystem.getNativeGuid(), xioCG.getGuid());
// determine if the unmanaged CG already exists in the unManagedCGToUpdateMap or in the database
// if the the unmanaged CG is not in either create a new one
UnManagedConsistencyGroup unManagedCG = null;
if (unManagedCGToUpdateMap.containsKey(unManagedCGNativeGuid)) {
unManagedCG = unManagedCGToUpdateMap.get(unManagedCGNativeGuid);
log.info("Unmanaged consistency group {} was previously added to the unManagedCGToUpdateMap", unManagedCG.getLabel());
} else {
unManagedCG = DiscoveryUtils.checkUnManagedCGExistsInDB(dbClient, unManagedCGNativeGuid);
if (null == unManagedCG) {
// unmanaged CG does not exist in the database, create it
unManagedCG = createUnManagedCG(unManagedCGNativeGuid, xioCG, storageSystem.getId(), dbClient);
log.info("Created unmanaged consistency group: {}", unManagedCG.getId().toString());
} else {
log.info("Unmanaged consistency group {} was previously added to the database", unManagedCG.getLabel());
// clean out the list of unmanaged volumes if this unmanaged cg was already
// in the database and its first time being used in this discovery operation
// the list should be re-populated by the current discovery operation
log.info("Cleaning out unmanaged volume map from unmanaged consistency group: {}", unManagedCG.getLabel());
unManagedCG.getUnManagedVolumesMap().clear();
}
}
log.info("Adding unmanaged volume {} to unmanaged consistency group {}", unManagedVolume.getLabel(), unManagedCG.getLabel());
// set the uri of the unmanaged CG in the unmanaged volume object
unManagedVolume.getVolumeInformation().put(SupportedVolumeInformation.UNMANAGED_CONSISTENCY_GROUP_URI.toString(),
unManagedCG.getId().toString());
// add the unmanaged volume object to the unmanaged CG
unManagedCG.getUnManagedVolumesMap().put(unManagedVolume.getNativeGuid(), unManagedVolume.getId().toString());
// add the unmanaged CG to the map of unmanaged CGs to be updated in the database once all volumes have been processed
unManagedCGToUpdateMap.put(unManagedCGNativeGuid, unManagedCG);
// add the unmanaged CG to the current set of CGs being discovered on the array. This is for book keeping later.
allCurrentUnManagedCgURIs.add(unManagedCG.getId());
}
}