/*
* Copyright (c) 2015 EMC Corporation
* All Rights Reserved
*/
package com.emc.storageos.api.service.impl.resource;
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.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.constraint.ContainmentConstraint;
import com.emc.storageos.db.client.constraint.URIQueryResultList;
import com.emc.storageos.db.client.model.DiscoveredDataObject;
import com.emc.storageos.db.client.model.DiscoveredDataObject.DiscoveryStatus;
import com.emc.storageos.db.client.model.Host;
import com.emc.storageos.db.client.model.Initiator;
import com.emc.storageos.db.client.model.Network;
import com.emc.storageos.db.client.model.StoragePort;
import com.emc.storageos.db.client.model.StorageSystem;
import com.emc.storageos.db.client.model.StringMap;
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.util.NullColumnValueGetter;
import com.emc.storageos.svcs.errorhandling.resources.APIException;
import com.emc.storageos.svcs.errorhandling.resources.InternalException;
public class VplexExportGroupServiceApiImpl extends
AbstractExportGroupServiceApiImpl {
private static final Logger _log = LoggerFactory
.getLogger(VplexExportGroupServiceApiImpl.class);
/**
* Validate varray ports during Export Group Create. If varray
* contains ports from both Vplex Cluster 1 and Cluster 2 thats an invalid
* network configuration. It could be one or more network within a varray.
* If initiators of a host are in two networks then vplex storage ports from
* both networks are taken into account to check if ports belong to both
* Vplex Cluster 1 and Cluster 2.
*
* @param storageSystemURIs vplex storageSystem URIs
* @param varray source VirtualArray
* @param allHosts
* @throws InternalException
*/
@Override
public void validateVarrayStoragePorts(Set<URI> storageSystemURIs,
VirtualArray varray, List<URI> allHosts)
throws InternalException {
try {
// Get VirtualArray Storage ports by Network.
Map<Network, Set<StoragePort>> networkToPortsMap = getVirtualArrayTaggedPortsByNework(varray
.getId());
Map<URI, Set<URI>> vplexCluster1ports = new HashMap<URI, Set<URI>>();
Map<URI, Set<URI>> vplexCluster2ports = new HashMap<URI, Set<URI>>();
Map<URI, StorageSystem> storageSystems = new HashMap<URI, StorageSystem>();
// Iterate over all vplex storage systems and add results to above three maps
// Separate cluster1 and cluster 2 ports of the provided vplex storageSystemURIs
for (URI uri : storageSystemURIs) {
StorageSystem storageSystem = _dbClient.queryObject(StorageSystem.class,
uri);
URIQueryResultList storagePortURIs = new URIQueryResultList();
_dbClient.queryByConstraint(ContainmentConstraint.Factory
.getStorageDeviceStoragePortConstraint(storageSystem.getId()),
storagePortURIs);
final String cluster1 = "1";
final String cluster2 = "2";
Set<URI> cluster1StoragePorts = new HashSet<URI>();
Set<URI> cluster2StoragePorts = new HashSet<URI>();
Iterator<URI> storagePortsIter = storagePortURIs.iterator();
while (storagePortsIter.hasNext()) {
URI storagePortURI = storagePortsIter.next();
StoragePort storagePort = _dbClient.queryObject(StoragePort.class,
storagePortURI);
if (storagePort != null
&& !storagePort.getInactive()
&& storagePort.getRegistrationStatus().equals(
DiscoveredDataObject.RegistrationStatus.REGISTERED.name())
&& !storagePort.getDiscoveryStatus().equalsIgnoreCase(
DiscoveryStatus.NOTVISIBLE.name())) {
// Port Group has value like director-1-1-A, first number
// after director- in this string determines vplex cluster
if (storagePort.getPortGroup() != null) {
String[] tokens = storagePort.getPortGroup().split("-");
if (cluster1.equals(tokens[1])) {
cluster1StoragePorts.add(storagePort.getId());
} else if (cluster2.equals(tokens[1])) {
cluster2StoragePorts.add(storagePort.getId());
} else {
_log.warn("Could not determine cluster for storageport:"
+ storagePort.getPortNetworkId() + " "
+ storagePort.getId() + " Port group is:"
+ storagePort.getPortGroup());
}
} else {
_log.warn("Could not determine cluster for storageport:"
+ storagePort.getPortNetworkId() + " " + storagePort.getId());
}
}
}
vplexCluster1ports.put(uri, cluster1StoragePorts);
vplexCluster2ports.put(uri, cluster2StoragePorts);
storageSystems.put(uri, storageSystem);
}
for (URI hostUri : allHosts) {
Map<URI, StoragePort> networkStoragePortsForHost = getNetworkTaggedPortsForHost(
hostUri, networkToPortsMap);
// Validate if storage ports seen by the host belong to both the
// clusters of the vplex.
for (URI uri : storageSystemURIs) {
Set<URI> intersection1 = new HashSet<URI>(networkStoragePortsForHost.keySet());
Set<URI> intersection2 = new HashSet<URI>(networkStoragePortsForHost.keySet());
intersection1.retainAll(vplexCluster1ports.get(uri));
intersection2.retainAll(vplexCluster2ports.get(uri));
// Ports should only be either in intersection1 or intersection2,
// if we have ports in both then its a mix ports from cluster 1 and cluster 2
if (!intersection1.isEmpty() && !intersection2.isEmpty()) {
Map<URI, String> cluster1Ports = new HashMap<URI, String>();
Map<URI, String> cluster2Ports = new HashMap<URI, String>();
// Get port information so that we can print in log cluster 1 and cluster 2 ports
// which belong in the same varray
for (URI uriIntersection1 : intersection1) {
if (networkStoragePortsForHost.get(uriIntersection1) != null) {
cluster1Ports.put(uriIntersection1,
networkStoragePortsForHost.get(uriIntersection1).getPortNetworkId());
}
}
for (URI uriIntersection2 : intersection2) {
if (networkStoragePortsForHost.get(uriIntersection2) != null) {
cluster2Ports.put(uriIntersection2,
networkStoragePortsForHost.get(uriIntersection2).getPortNetworkId());
}
}
Host host = _dbClient.queryObject(Host.class, hostUri);
_log.error("Varray "
+ varray.getLabel()
+ " has storageports from Cluster 1 and Cluster 2 of the Vplex "
+ storageSystems.get(uri).getLabel() + " "
+ storageSystems.get(uri).getId().toString()
+ ". This is detected for the host " + host.getHostName()
+ "\n Cluster 1 storageports in varray are"
+ cluster1Ports
+ "\n Cluster 2 storageports in varray are"
+ cluster2Ports);
throw APIException.badRequests
.invalidVarrayNetworkConfiguration(varray.getLabel(), storageSystems.get(uri).getLabel());
}
}
}
_log.info("Done validating vplex cluster 1 and 2 ports for the Varray:" + varray.getLabel());
} catch (InternalException ex) {
_log.error(ex.getLocalizedMessage());
throw (ex);
}
}
/**
* This method returns all StoragePorts from networks containing initiators
* of a host.
*
* @param hostUri
* @param networkToPortsMap map of network to storageport set
*/
private Map<URI, StoragePort> getNetworkTaggedPortsForHost(URI hostUri,
Map<Network, Set<StoragePort>> networkToPortsMap) {
URIQueryResultList initiatorURIs = new URIQueryResultList();
_dbClient.queryByConstraint(ContainmentConstraint.Factory
.getContainedObjectsConstraint(hostUri, Initiator.class, "host"),
initiatorURIs);
Map<URI, StoragePort> networkStoragePorts = new HashMap<URI, StoragePort>();
Set<StoragePort> storagePorts = new HashSet<StoragePort>();
Iterator<URI> initiatorURIsIter = initiatorURIs.iterator();
while (initiatorURIsIter.hasNext()) {
URI initiatorURI = initiatorURIs.iterator().next();
Initiator initiator = _dbClient.queryObject(Initiator.class, initiatorURI);
if (!initiator.getInactive() && initiator.getRegistrationStatus().equals(
DiscoveredDataObject.RegistrationStatus.REGISTERED.name())) {
for (Map.Entry<Network, Set<StoragePort>> entry : networkToPortsMap
.entrySet()) {
if (entry.getKey().retrieveEndpoints().contains(initiator.getInitiatorPort())) {
storagePorts.addAll(entry.getValue());
}
}
}
}
for (StoragePort storagePort : storagePorts) {
networkStoragePorts.put(storagePort.getId(), storagePort);
}
return networkStoragePorts;
}
/**
* This methods looks for tagged virtual array ports if it belongs to a
* network then adds it to a map.
*
* @param varray virtual array uri
* @return a map of network to storageport set
*/
private Map<Network, Set<StoragePort>> getVirtualArrayTaggedPortsByNework(URI varray) {
Map<URI, Set<StoragePort>> registeredNetworkStoragePorts = new HashMap<URI, Set<StoragePort>>();
URIQueryResultList storagePortURIs = new URIQueryResultList();
_dbClient.queryByConstraint(AlternateIdConstraint.Factory
.getVirtualArrayStoragePortsConstraint(varray.toString()),
storagePortURIs);
Iterator<URI> storagePortURIsIter = storagePortURIs.iterator();
while (storagePortURIsIter.hasNext()) {
URI storagePortURI = storagePortURIsIter.next();
StoragePort storagePort = _dbClient.queryObject(StoragePort.class,
storagePortURI);
if (storagePort != null
&& !storagePort.getInactive()
&& !NullColumnValueGetter.isNullURI(storagePort.getNetwork())
&& storagePort.getRegistrationStatus().equals(
DiscoveredDataObject.RegistrationStatus.REGISTERED.name())
&& !storagePort.getDiscoveryStatus().equalsIgnoreCase(
DiscoveryStatus.NOTVISIBLE.name())) {
if (registeredNetworkStoragePorts.get(storagePort.getNetwork()) == null) {
Set<StoragePort> storageports = new HashSet<StoragePort>();
storageports.add(storagePort);
registeredNetworkStoragePorts.put(storagePort.getNetwork(),
storageports);
} else {
registeredNetworkStoragePorts.get(storagePort.getNetwork()).add(
storagePort);
}
}
}
Map<Network, Set<StoragePort>> networkToStoragePortsMap = new HashMap<Network, Set<StoragePort>>();
for (Map.Entry<URI, Set<StoragePort>> entry : registeredNetworkStoragePorts
.entrySet()) {
Network network = _dbClient.queryObject(Network.class, entry.getKey());
networkToStoragePortsMap.put(network, entry.getValue());
}
return networkToStoragePortsMap;
}
/**
* Determines the virtual arrays associated with a VPLEX volume.
*
* @param volume The VPLEX volume
* @param vplexSystem The VPLEX storage system
* @param dbClient A reference to the database client.
*
* @return A list of URIs of the virtual array URIs.
*/
public static List<URI> getVolumeVirtualArrays(Volume volume,
StorageSystem vplexSystem, DbClient dbClient) {
List<URI> varrayURIs = new ArrayList<URI>();
// For volumes created in ViPR we could use the varrays specified
// for the associated backend volumes. However, for ingested
// volumes, the associated volumes will not be set. We know
// one varray is the varray for the VPLEX volume itself, so
// add it to the list.
varrayURIs.add(volume.getVirtualArray());
// If the volume is a distributed volume, then the other varray
// is the HA varray specified in the vpool for the volume.
String volumeId = volume.getId().toString();
URI vpoolURI = volume.getVirtualPool();
if (vpoolURI != null) {
VirtualPool vpool = dbClient.queryObject(VirtualPool.class, vpoolURI);
if (vpool != null) {
String vplexHA = vpool.getHighAvailability();
if (VirtualPool.HighAvailabilityType.vplex_distributed.name().equals(vplexHA)) {
StringMap haVarrayMap = vpool.getHaVarrayVpoolMap();
if ((haVarrayMap != null) && (!haVarrayMap.isEmpty())) {
varrayURIs.add(URI.create(haVarrayMap.keySet().iterator().next()));
} else {
_log.error("HA varray not set in vpool {} for VPLEX volume {}", vpoolURI, volumeId);
}
}
} else {
_log.error("Could not find virtual pool {} for VPLEX volume {}", vpoolURI, volumeId);
}
} else {
_log.error("Virtual pool is not set for VPLEX volume {}", volumeId);
}
return varrayURIs;
}
}