/* * Copyright (c) 2015 EMC Corporation * All Rights Reserved */ package com.emc.storageos.util; import java.net.URI; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; 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.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.AbstractChangeTrackingSet; import com.emc.storageos.db.client.model.DiscoveredDataObject; import com.emc.storageos.db.client.model.DiscoveredDataObject.DiscoveryStatus; import com.emc.storageos.db.client.model.DiscoveredDataObject.RegistrationStatus; import com.emc.storageos.db.client.model.ExportMask; import com.emc.storageos.db.client.model.Initiator; import com.emc.storageos.db.client.model.ProtectionSystem; import com.emc.storageos.db.client.model.RPSiteArray; import com.emc.storageos.db.client.model.StoragePool; import com.emc.storageos.db.client.model.StoragePort; import com.emc.storageos.db.client.model.StoragePort.PortType; import com.emc.storageos.db.client.model.StorageSystem; import com.emc.storageos.db.client.model.StringSet; import com.emc.storageos.db.client.model.VirtualPool.SystemType; import com.emc.storageos.db.client.util.CustomQueryUtility; import com.emc.storageos.db.client.util.NullColumnValueGetter; import com.emc.storageos.db.client.util.StringSetUtil; import com.emc.storageos.networkcontroller.impl.NetworkAssociationHelper; import com.emc.storageos.volumecontroller.impl.StoragePoolAssociationHelper; import com.emc.storageos.volumecontroller.impl.utils.attrmatchers.VPlexHighAvailabilityMatcher; import com.emc.storageos.vplex.api.VPlexApiConstants; import com.google.common.base.Joiner; public class ConnectivityUtil { /** * */ // Return values from getVPlexClusterOfPort public static final String CLUSTER1 = VPlexApiConstants.CLUSTER_1_ID; public static final String CLUSTER2 = VPlexApiConstants.CLUSTER_2_ID; public static final String CLUSTER_UNKNOWN = "unknown-cluster"; private static final int PING_TIMEOUT = 30 * 1000; // 30 second timeout for ping public static enum StorageSystemType { BLOCK, FILE } private static Logger _log = LoggerFactory.getLogger(ConnectivityUtil.class); /** * Determines whether or not the passed in Storage System in a VPLEX by checking * the System Type. * * @param system The Storage System to check * @return boolean value of whether or not this is a VPLEX */ public static boolean isAVPlex(StorageSystem system) { return (system.getSystemType().equals(DiscoveredDataObject.Type.vplex.name())); } /** * Determines if the passed storage port is on a VPLEX storage system. * * @param storagePort A reference to a storage port. * @param dbClient Reference to a DB client. * * @return true if the port is a VPLEX port, false otherwise. */ public static boolean isAVplexPort(StoragePort storagePort, DbClient dbClient) { StorageSystem storagePortSystem = dbClient.queryObject(StorageSystem.class, storagePort.getStorageDevice()); boolean isAVplexPort = ConnectivityUtil.isAVPlex(storagePortSystem); if (isAVplexPort) { _log.info("Storage port {} is a VPLEX port", storagePort.getId()); } return isAVplexPort; } /** * Determines if the passed VPLEX storage port can be assigned the passed * virtual array. Presumes the passed storage port is a VPLEX storage port. * * @param storagePort A reference to a VPLEX storage port. * @param varrayId The id of a virtual array. * @param dbClient Reference to a DB client. * * @return true if the storage port can be assigned to the passed virtual * array, false otherwise. */ public static boolean vplexPortCanBeAssignedToVirtualArray(StoragePort storagePort, String varrayId, DbClient dbClient) { boolean canBeAssigned = true; // Get the VPLEX storage system id. URI vplexSystemURI = storagePort.getStorageDevice(); _log.info("Storage port {} VPLEX is {}", storagePort.getId(), vplexSystemURI); // Get the VPLEX cluster id for the passed port String portClusterId = getVplexClusterOfPort(storagePort); _log.info("Storage port VPLEX cluster id is {}", portClusterId); // Get all storage ports manually assigned, rather than tagged, to this // virtual array. Use manually assigned here. The user could have just // assigned the network containing the port to one or more virtual // arrays and is now assigning specific ports to specific varrays. In // this case, other VPLEX ports could be implicitly connected to the // virtual arrays and will be subsequently assigned. URIQueryResultList queryResults = new URIQueryResultList(); dbClient.queryByConstraint(AlternateIdConstraint.Factory .getAssignedVirtualArrayStoragePortsConstraint(varrayId), queryResults); Iterator<URI> resultsIter = queryResults.iterator(); while (resultsIter.hasNext()) { StoragePort virtualArrayPort = dbClient.queryObject(StoragePort.class, resultsIter.next()); URI virtualArrayPortSystemURI = virtualArrayPort.getStorageDevice(); _log.info("Storage port {} storage system is {}", virtualArrayPort.getId(), virtualArrayPortSystemURI); if (!virtualArrayPortSystemURI.equals(vplexSystemURI)) { continue; } _log.info("Storage ports are on the same VPLEX"); String virtualArrayPortClusterId = getVplexClusterOfPort(virtualArrayPort); _log.info("Virtual array storage port VPLEX cluster id is {}", virtualArrayPortClusterId); if (!portClusterId.equals(virtualArrayPortClusterId)) { // The virtual array already contains a port from the other // cluster of the VPLEX, so this virtual array cannot be // assigned to this port. canBeAssigned = false; break; } } return canBeAssigned; } /** * Returns the Virtual Storage Array URIs for a given VPLEX system. * * @param dbClient -- Used by static method. * @param vplexSystemURI * @return List<URI> of Neighborhoods associated with this VPLEX. */ public static List<URI> getVPlexSystemVarrays(DbClient dbClient, URI vplexSystemURI) { Set<String> vplexSystems = new HashSet<String>(); vplexSystems.add(vplexSystemURI.toString()); Map<String, List<String>> vplexToVarrays = getVPlexVarrays(dbClient, vplexSystems, null); List<URI> result = new ArrayList<URI>(); List<String> varrays = vplexToVarrays.get(vplexSystemURI.toString()); if (varrays != null) { for (String varray : varrays) { result.add(URI.create(varray)); } } return result; } /** * Gets a list of the high availability Virtual Storage Arrays for the passed VPlex * storage systems. * * @param dbClient - Used to access DB by static method * @param vplexStorageSystemIds A set of VPlex storage system ids. * @param excludeVarray A varray to exclude from the list (or null to ignore). * @return A map of the high availability neighborhoods for each VPlex * storage system. */ public static Map<String, List<String>> getVPlexVarrays( DbClient dbClient, Set<String> vplexStorageSystemIds, URI excludeVarray) { // Initialize the map. Map<String, List<String>> vplexVarrayIdMap = new HashMap<String, List<String>>(); // For all the requested storage systems... for (String vplexSystemId : vplexStorageSystemIds) { StringSet set = StoragePoolAssociationHelper.getVplexSystemConnectedVarrays(URI.create(vplexSystemId), dbClient); set.remove(excludeVarray); vplexVarrayIdMap.put(vplexSystemId, new ArrayList<String>(set)); } return vplexVarrayIdMap; } /** * @see #getStorageSystemAssociationsByNetwork(DbClient, URI, com.emc.storageos.db.client.model.StoragePort.PortType, String) * @param dbClient * @param seedURI -- the StorageSystem we wish to find associations for * @param PortType (frontend or backend) to be matched against on seed array * @return Set<URI> -- StorageSystems of all types (VPlex/VNX/VMAX/etc.) sharing one or * more network and virtual array with the Seed. */ public static Set<URI> getStorageSystemAssociationsByNetwork( DbClient dbClient, URI seedURI, StoragePort.PortType seedPortType) { return getStorageSystemAssociationsByNetwork(dbClient, seedURI, seedPortType, null, null, null); } /** * Finds the associations of a seed storage system to others by determining they both have * StoragePorts in a common Network and virtual array. The method will not return an association to * the seed array. Chooses frontend or backend ports on associated systems as appropriate. * * @param dbClient * @param seedURI -- the StorageSystem we wish to find associations for * @param PortType (frontend or backend) to be matched against on seed array * @param systemType an optional filter when the caller wants a specific type of associated * storage system, for example vplex. Null if all associated systems should be returned. * @param varrayUris an optional filter when the caller wants the system to be associated to the * in specific varrays. Null if the association can be on any matched varray. * @param cluster an optional filter to limit if the caller wants the system to be associated * in a specific vplex cluster * @return Set<URI> -- StorageSystems of all types (VPlex/VNX/VMAX/etc.) sharing one or * more network and virtual array with the Seed. */ public static Set<URI> getStorageSystemAssociationsByNetwork( DbClient dbClient, URI seedURI, StoragePort.PortType seedPortType, String systemType, Set<String> varrayUris, String vplexCluster) { _log.info("Checking storage system association for array {}", seedURI); // The results to be returned Set<URI> associatedSystemURIs = new HashSet<URI>(); // A map for holding systems already retrieved from db to avoid unnecessary db hits Map<URI, StorageSystem> systemsMap = new HashMap<URI, StorageSystem>(); // get all the ports in the storage system of the specified type, grouped by network Map<URI, List<StoragePort>> networkToPortMap = getStoragePortsOfType(dbClient, seedURI, seedPortType); // Add code here to include routed networks in the list // of networks for which we're looping for (URI networkUri : networkToPortMap.keySet()) { _log.info("Checking storage system association via network {}", networkUri); // get all the varrays for the seed storage system ports for this network StringSet networkVarrays = getStoragePortsVarrays(networkToPortMap.get(networkUri)); // check some ports are in some varrays, or, when a specific varray is desired, check some ports are in it if (networkVarrays.isEmpty() || (varrayUris != null && Collections.disjoint(networkVarrays, varrayUris))) { _log.info("There are no varrays in this network or no varrays that matched the required ones."); continue; } // find the varrays that the ports should be in. If no varray is specified, match to any of the system varrays StringSet matchingVarrays = new StringSet(); if (varrayUris != null) { matchingVarrays.addAll(varrayUris); } else { matchingVarrays.addAll(networkVarrays); } _log.info("Matching varrays in this network {}", matchingVarrays); // TODO - This is really expensive and I probably should look for other ways List<StoragePort> ports = NetworkAssociationHelper. getNetworkConnectedStoragePorts(networkUri.toString(), dbClient); StorageSystem system = null; for (StoragePort port : ports) { if (port == null || port.getInactive() == true || port.getStorageDevice() == null) { continue; } if (port.getStorageDevice().equals(seedURI)) { continue; } if (associatedSystemURIs.contains(port.getStorageDevice())) { continue; } if (!DiscoveredDataObject.CompatibilityStatus.COMPATIBLE.name() .equals(port.getCompatibilityStatus())) { continue; } if (false == port.getRegistrationStatus() .equals(StoragePort.RegistrationStatus.REGISTERED.name())) { continue; } if (!DiscoveryStatus.VISIBLE.name().equals(port.getDiscoveryStatus())) { continue; } // Look the system in the saved systems map before querying the db if (systemsMap.containsKey(port.getStorageDevice())) { system = systemsMap.get(port.getStorageDevice()); } else { system = dbClient.queryObject( StorageSystem.class, port.getStorageDevice()); systemsMap.put(system.getId(), system); } // If a specified type of system is required, filter by it if (systemType == null || system.getSystemType().equals(systemType)) { StoragePort.PortType portType = isAVPlex(system) ? StoragePort.PortType.backend : StoragePort.PortType.frontend; if (port.getPortType().equals(portType.toString()) && port.getTaggedVirtualArrays() != null && !Collections.disjoint(matchingVarrays, port.getTaggedVirtualArrays())) { if (vplexCluster != null) { if (vplexCluster.equals(getVplexClusterOfPort(port))) { _log.info("Storage system {} is associated to vplex cluster {} via port {}", new Object[] { system.getNativeGuid(), vplexCluster, port.getNativeGuid() }); associatedSystemURIs.add(system.getId()); } } else { _log.info(String.format("Storage system %s is associated via port %s", system.getNativeGuid(), port.getNativeGuid())); associatedSystemURIs.add(system.getId()); } } } } } return associatedSystemURIs; } /** * Returns the set of virtual arrays the storage ports are in. This is the union * of all the ports' virtual arrays. * * @param storagePorts a list of storage ports. * @return the union of all the tagged virtual arrays of all the ports. */ public static StringSet getStoragePortsVarrays(List<StoragePort> storagePorts) { StringSet varrays = new StringSet(); for (StoragePort port : storagePorts) { if (port.getTaggedVirtualArrays() != null) { varrays.addAll(port.getTaggedVirtualArrays()); } } return varrays; } /** * Given a storage array URI, find all the VPlex systems associated with the array. Vplex * systems are associated when the storage array can be a backend system of the vplex. * This requires a connectivity such that backend ports on the vplex and frontend ports * on the storage array are in the same network and virtual array. This condition is necessary * to create the backend export group between the storage array and vplex. * * @param dbClient and instance of dbClient. * @param arrayURI the storage array whose vplex associations are requested. * @return a list of vplex devices that can use the storage array to create vplex volumes. */ public static Set<URI> getVPlexSystemsAssociatedWithArray(DbClient dbClient, URI arrayURI) { Set<URI> associations = getStorageSystemAssociationsByNetwork(dbClient, arrayURI, StoragePort.PortType.frontend, DiscoveredDataObject.Type.vplex.name(), null, null); return associations; } /** * Given a storage array URI, find all the VPlex systems associated with the array. Vplex * systems are associated when the storage array can be a backend system of the vplex. * This requires a connectivity such that backend ports on the vplex and frontend ports * on the storage array are in the same network and virtual array. This condition is necessary * to create the backend export group between the storage array and vplex. * * @param dbClient and instance of dbClient. * @param arrayURI the storage array whose vplex associations are requested. * @param varrayUris an optional filter to limit the results by vplexes connected to the storage * array in specific varrays * @param cluster an optional filter to limit the results of vplexes connected to the storage * array in a specific vplex cluster * @return a list of vplex devices that can use the storage array to create vplex volumes. */ public static Set<URI> getVPlexSystemsAssociatedWithArray(DbClient dbClient, URI arrayURI, Set<String> varrayUris, String cluster) { Set<URI> associations = getStorageSystemAssociationsByNetwork(dbClient, arrayURI, StoragePort.PortType.frontend, DiscoveredDataObject.Type.vplex.name(), varrayUris, cluster); return associations; } /** * Retrieve all the storage ports of a given StorageSystem and type in a Map by Network URI. * * @param storage -- StorageSystem URI * @param type -- frontend or backend (note normal array ports are frontend). * @return Map<URI, List<StoragePort>> -- A map of the Network URI the Storage Ports in that Network */ public static Map<URI, List<StoragePort>> getStoragePortsOfType(DbClient dbClient, URI storage, StoragePort.PortType type) { Map<URI, List<StoragePort>> tzURItoStoragePortsMap = new HashMap<URI, List<StoragePort>>(); List<StoragePort> ports = getStoragePortsForSystem(dbClient, storage); for (StoragePort port : ports) { if (port.getPortType().equals(type.name())) { URI transportZoneURI = port.getNetwork(); // Don't use the port if not assigned to a transport zone if (NullColumnValueGetter.isNullURI(transportZoneURI)) { continue; } if (tzURItoStoragePortsMap.get(transportZoneURI) == null) { tzURItoStoragePortsMap.put(transportZoneURI, new ArrayList<StoragePort>()); } tzURItoStoragePortsMap.get(transportZoneURI).add(port); } } return tzURItoStoragePortsMap; } /** * Retrieve all the storage ports of a given StorageSystem and type in a Map by Network URI. * * @param storage -- StorageSystem URI * @param type -- frontend or backend (note normal array ports are frontend). * @param varrayURI -- URI of varray that must be in port's tagged varrays * @return Map<URI, List<StoragePort>> -- A map of the Network URI the Storage Ports in that Network */ public static Map<URI, List<StoragePort>> getStoragePortsOfTypeAndVArray( DbClient dbClient, URI storage, StoragePort.PortType type, URI varrayURI) { String varray = varrayURI.toString(); Map<URI, List<StoragePort>> netURIToStoragePortsMap = getStoragePortsOfType(dbClient, storage, type); // Filter out any ports that do not include the varray in their tagged list Set<URI> networks = new HashSet<URI>(netURIToStoragePortsMap.keySet()); for (URI network : networks) { List<StoragePort> ports = netURIToStoragePortsMap.get(network); List<StoragePort> filteredPorts = new ArrayList<StoragePort>(); for (StoragePort port : ports) { if (port.getTaggedVirtualArrays() != null && port.getTaggedVirtualArrays().contains(varray)) { filteredPorts.add(port); } } // Update the result map with the filtered ports, or // remove a network entry if no remaining ports in that network if (!filteredPorts.isEmpty()) { netURIToStoragePortsMap.put(network, filteredPorts); } else { netURIToStoragePortsMap.remove(network); } } return netURIToStoragePortsMap; } /** * Retrieve all the storage ports of a given StorageSystem and type. * * @param storage -- StorageSystem URI * @return List<StoragePort> -- A list of the the Storage Ports in that Network */ public static List<StoragePort> getStoragePortsForSystem(DbClient dbClient, URI storage) { List<StoragePort> ports = new ArrayList<StoragePort>(); URIQueryResultList storagePortURIs = new URIQueryResultList(); dbClient.queryByConstraint( ContainmentConstraint.Factory.getStorageDeviceStoragePortConstraint(storage), storagePortURIs); Iterator<URI> storagePortsIter = storagePortURIs.iterator(); while (storagePortsIter.hasNext()) { URI portURI = storagePortsIter.next(); StoragePort port = dbClient.queryObject(StoragePort.class, portURI); if (port == null || port.getInactive() == true) { continue; } if (!DiscoveredDataObject.CompatibilityStatus.COMPATIBLE.name() .equals(port.getCompatibilityStatus())) { continue; } if (false == port.getRegistrationStatus() .equals(StoragePort.RegistrationStatus.REGISTERED.name())) { continue; } if (!DiscoveryStatus.VISIBLE.name().equals(port.getDiscoveryStatus())) { continue; } ports.add(port); } return ports; } /** * Get all protection systems associated with an array. * * @param dbClient - db client * @param storageSystem - storage array * @return list of URIs corresponding to rp systems */ public static Set<URI> getProtectionSystemsAssociatedWithArray( DbClient dbClient, URI storageSystem) { Set<URI> rpSystemIds = new HashSet<URI>(); // Get the Source Storage System in question StorageSystem sourceStorageSystem = dbClient.queryObject( StorageSystem.class, storageSystem); // Get all the RPSiteArrays associated to this Storage System URIQueryResultList sitelist = new URIQueryResultList(); dbClient.queryByConstraint(AlternateIdConstraint.Factory .getRPSiteArrayByStorageSystemConstraint(storageSystem.toString()), sitelist); Iterator<URI> it = sitelist.iterator(); while (it.hasNext()) { URI rpSiteArrayId = it.next(); RPSiteArray rpSiteArray = dbClient.queryObject(RPSiteArray.class, rpSiteArrayId); if (rpSiteArray != null) { // Find source RPSiteArrays that qualify and get the Protection System if (sourceStorageSystem.getId().equals( rpSiteArray.getStorageSystem())) { rpSystemIds.add(rpSiteArray.getRpProtectionSystem()); } } } return rpSystemIds; } /** * Gets a list of the RP Protection systems for the passed pool. * * @param dbClient Reference to a DB client * @param storagePool Reference to a storage pool * @param varrayId Optional, filter by varray * @param isRPVPlex Optional, specifies whether or not this is an RP+VPLEX/MetroPoint request * * @return A list of the RP protection systems. */ public static Set<ProtectionSystem> getProtectionSystemsForStoragePool(DbClient dbClient, StoragePool storagePool, URI varrayId, boolean isRPVPlex) { Set<ProtectionSystem> rpSystems = new HashSet<ProtectionSystem>(); Set<ProtectionSystem> rpSystemsWithIsolatedVarrayEntry = new HashSet<ProtectionSystem>(); List<String> isolatedRPSites = new ArrayList<String>(); List<String> storageSystems = new ArrayList<String>(); _log.info(String.format("Find Protection Systems using the Storage System of Storage pool [%s]", storagePool.getLabel())); // If this is a RP+VPLEX/MetroPoint request, we need only consider Protection Systems associated to // the VPLEX. if (isRPVPlex) { _log.info(String.format("RP+VPlex/MetroPoint - only consider Protection Systems associated to the VPLEX.")); // Get the VPLEXs associated with the Storage Pool List<String> vplexSystemsForPool = VPlexHighAvailabilityMatcher.getVPlexStorageSystemsForStorageSystem(dbClient, storagePool.getStorageDevice(), null); // Loop through all the VPLEXs for (String vplexId : vplexSystemsForPool) { if (varrayId != null) { // Find the VPLEX(s) that are associated to this varray List<URI> vplexVarrays = ConnectivityUtil .getVPlexSystemVarrays(dbClient, URI.create(vplexId)); if (vplexVarrays.contains(varrayId)) { _log.info(String.format("Candidate VPLEX Storage System [%s]", vplexId)); storageSystems.add(vplexId); } } else { _log.info(String.format("Candidate VPLEX Storage System [%s]", vplexId)); storageSystems.add(vplexId); } } } else { _log.info(String.format("Candidate Storage System [%s]", storagePool.getStorageDevice().toASCIIString())); storageSystems.add(storagePool.getStorageDevice().toASCIIString()); } // Find all the Protection Systems for these Storage Systems for (String storageSystemId : storageSystems) { Set<URI> rpSystemURIs = ConnectivityUtil. getProtectionSystemsAssociatedWithArray(dbClient, URI.create(storageSystemId)); for (URI uri : rpSystemURIs) { ProtectionSystem ps = dbClient.queryObject(ProtectionSystem.class, uri); // Make sure the ProtectionSystem is active if (ps != null && !ps.getInactive()) { // We could be isolating the varray in question to specific RPA clusters/sites. // If there is an entry for this varray in the siteAssignedVirtualArrays field // we need to honour that isolation and only return Protection Systems // with an entry for that varray. boolean varrayHasBeenIsolated = false; if (varrayId != null) { if (ps.getSiteAssignedVirtualArrays() != null && !ps.getSiteAssignedVirtualArrays().isEmpty()) { // Loop over all entries to see if this Protection System has an entry for this varray to be isolated for (Map.Entry<String, AbstractChangeTrackingSet<String>> entry : ps.getSiteAssignedVirtualArrays().entrySet()) { // Check to see if this entry contains the varray if (entry.getValue().contains(varrayId.toString())) { // This varray has been isolated to a RP cluster/site varrayHasBeenIsolated = true; isolatedRPSites.add(entry.getKey()); break; } } } } _log.info(String.format("Found Protection System [%s]", ps.getLabel())); if (varrayHasBeenIsolated) { rpSystemsWithIsolatedVarrayEntry.add(ps); } else { rpSystems.add(ps); } } else { _log.info(String.format("Excluding ProtectionSystem %s because it is inactive or invalid.", uri)); } } } // If we have any RP Protection Systems with an entry indicating that we are isolating this varray, // then we can only return Protection Systems that have an isolation entry. // The default with no isolation entries, is to return all Protection Systems that have // connectivity. if (!rpSystemsWithIsolatedVarrayEntry.isEmpty()) { // Only use RP Systems that have an entry for this varray, // we will ignore the others. rpSystems = rpSystemsWithIsolatedVarrayEntry; StringBuffer logMsg = new StringBuffer(); logMsg.append(String.format("Varray [%s] has been isolated to these RP Sites: %s %n", varrayId.toString(), Joiner.on(',').join(isolatedRPSites))); logMsg.append("Therefore only these Protection Systems can be used: "); for (ProtectionSystem ps : rpSystems) { logMsg.append(ps.getLabel() + " "); } _log.info(logMsg.toString()); } return rpSystems; } /** * Get all of the virtual arrays associated with the RP system. RP System -> * Storage System -> Storage Pools -> Virtual Arrays * * @param dbClient - db client * @param rpSystemId - URI of RP system * @return list of virtual array URIs */ public static List<URI> getRPSystemVirtualArrays(DbClient dbClient, URI rpSystemId) { Set<URI> virtualArrayIdSet = new HashSet<URI>(); List<URI> virtualArrayIdList = new ArrayList<URI>(); // Get the rp system's array mappings from the RP client URIQueryResultList sitelist = new URIQueryResultList(); dbClient.queryByConstraint(AlternateIdConstraint.Factory .getRPSiteArrayProtectionSystemConstraint(rpSystemId.toString()), sitelist); Iterator<URI> it = sitelist.iterator(); List<String> serialNumberSiteName = new ArrayList<String>(); while (it.hasNext()) { URI rpSiteArrayId = it.next(); RPSiteArray siteArray = dbClient.queryObject(RPSiteArray.class, rpSiteArrayId); if (!serialNumberSiteName.contains(siteArray.getArraySerialNumber() + siteArray.getRpSiteName())) { findAllVirtualArraysForRPSiteArray(dbClient, siteArray, virtualArrayIdSet); serialNumberSiteName.add(siteArray.getArraySerialNumber() + siteArray.getRpSiteName()); } } // Convert to a list virtualArrayIdList.addAll(virtualArrayIdSet); return virtualArrayIdList; } /** * Find all the associated VSA URIs for the passed in RPSiteArray * * @param dbClient * @param siteArray * @param ids virtual array ids collected * @return void */ private static void findAllVirtualArraysForRPSiteArray( DbClient dbClient, RPSiteArray siteArray, Collection<URI> ids) { if (siteArray != null) { // Find all the Storage Pools associated to this RPSiteArray URIQueryResultList storagePoolURIs = new URIQueryResultList(); dbClient.queryByConstraint(ContainmentConstraint.Factory .getStorageDeviceStoragePoolConstraint(siteArray .getStorageSystem()), storagePoolURIs); Iterator<URI> storagePoolIter = storagePoolURIs.iterator(); while (storagePoolIter.hasNext()) { URI storagePoolURI = storagePoolIter.next(); StoragePool storagePool = dbClient.queryObject( StoragePool.class, storagePoolURI); // For each Storage Pool get all the connected VSAs if (storagePool != null && !storagePool.getInactive() && storagePool.getConnectedVirtualArrays() != null) { for (String vArrayId : storagePool .getConnectedVirtualArrays()) { ids.add(URI.create(vArrayId)); } } } // If the rpsite array storage system is vplex check virtual array // connectivity to rpsite using front end storage ports StorageSystem storageSystem = dbClient.queryObject(StorageSystem.class, siteArray.getStorageSystem()); if (storageSystem != null && isAVPlex(storageSystem)) { Map<URI, List<StoragePort>> storagePortMap = ConnectivityUtil.getStoragePortsOfType(dbClient, storageSystem.getId(), PortType.frontend); for (Map.Entry<URI, List<StoragePort>> storagePortEntry : storagePortMap.entrySet()) { for (StoragePort storagePort : storagePortEntry.getValue()) { // For each Storage Port get all the connected VSAs if (storagePort.getConnectedVirtualArrays() != null && !storagePort.getConnectedVirtualArrays().isEmpty() && !ids.containsAll(URIUtil.toURIList(storagePort.getConnectedVirtualArrays()))) { _log.info(String.format("Vplex System [%s] has connectvity to RP Site [%s]", storageSystem.getLabel(), siteArray.getRpSiteName())); ids.addAll(URIUtil.toURIList(storagePort.getConnectedVirtualArrays())); } if (storagePort.getAssignedVirtualArrays() != null && !storagePort.getAssignedVirtualArrays().isEmpty() && !ids.containsAll(URIUtil.toURIList(storagePort.getAssignedVirtualArrays()))) { _log.info(String.format("Vplex System [%s] has connectvity to RP Site [%s]", storageSystem.getLabel(), siteArray.getRpSiteName())); ids.addAll(URIUtil.toURIList(storagePort.getAssignedVirtualArrays())); } } } } } } /** * Get all of the storage pools associated with the RP System * * @param dbClient db client * @param rpSystemId URI of the RP system * @return list of storage pool URIs */ public static List<URI> getRPSystemStoragePools(DbClient dbClient, URI rpSystemId) { List<URI> poolIds = new ArrayList<URI>(); // Get the rp system's array mappings from the RP client URIQueryResultList sitelist = new URIQueryResultList(); dbClient.queryByConstraint(AlternateIdConstraint.Factory .getRPSiteArrayProtectionSystemConstraint(rpSystemId.toString()), sitelist); Iterator<URI> it = sitelist.iterator(); while (it.hasNext()) { URI rpSiteArrayId = it.next(); RPSiteArray siteArray = dbClient.queryObject(RPSiteArray.class, rpSiteArrayId); if (siteArray != null && !siteArray.getInactive()) { // Find all the Storage Pools associated to this RPSiteArray URIQueryResultList storagePoolURIs = new URIQueryResultList(); dbClient.queryByConstraint(ContainmentConstraint.Factory .getStorageDeviceStoragePoolConstraint(siteArray .getStorageSystem()), storagePoolURIs); Iterator<URI> storagePoolIter = storagePoolURIs.iterator(); while (storagePoolIter.hasNext()) { URI storagePoolURI = storagePoolIter.next(); poolIds.add(storagePoolURI); } } } return poolIds; } /** * Refreshes the connected virtual arrays for the Recover Point (RP) systems that have sites for the storage systems. * This function: * <ol> * <li>Finds all the RP systems that have sites in one or more of the storage systems</li> * <li>for each RP system, find all the virtual arrays for all their connected storage systems' pools.</li> * </ol> * * @param storageSystemUris the storage systems that have some change in their varray associations * @param dbClient an instance of {@link DbClient} */ public static void updateRpSystemsConnectivity(Collection<URI> storageSystemUris, DbClient dbClient) { if (storageSystemUris.isEmpty()) { return; } List<URI> connectedRPSystems = getConnectedRPSystems(storageSystemUris, dbClient); List<ProtectionSystem> rpSystems = dbClient.queryObject(ProtectionSystem.class, connectedRPSystems); for (ProtectionSystem rpSystem : rpSystems) { updateRpSystemConnectivity(rpSystem, dbClient); } } /** * Refreshes the list of connected varrays for an RP system. * * @param rpSystem */ public static void updateRpSystemConnectivity(ProtectionSystem rpSystem, DbClient dbClient) { if (rpSystem.getInactive()) { return; } if (rpSystem.getVirtualArrays() == null) { rpSystem.setVirtualArrays(new StringSet()); } rpSystem.getVirtualArrays().replace( StringSetUtil.uriListToSet(getRPSystemVirtualArrays(dbClient, rpSystem.getId()))); dbClient.updateAndReindexObject(rpSystem); } /** * Queries and return a list of RP systems that have sites in one or more of the storage systems * * @param storageSystemUris a list of storage systems URIs * @param dbClient an instance of {@link DbClient} * @return a list of RP systems that have sites for in one or more of the storage systems */ private static List<URI> getConnectedRPSystems(Collection<URI> storageSystemUris, DbClient dbClient) { List<URI> rpSystems = new ArrayList<URI>(); for (URI storageSystemUri : storageSystemUris) { List<RPSiteArray> sites = CustomQueryUtility.queryActiveResourcesByRelation(dbClient, storageSystemUri, RPSiteArray.class, "storageSystem"); for (RPSiteArray site : sites) { if (!rpSystems.contains(site.getRpProtectionSystem())) { rpSystems.add(site.getRpProtectionSystem()); } } } return rpSystems; } /** * Returns all the varrays the initiator can have exports in. An initiator can have exports * to all storage ports that are in its network and all those that are in a network * that is routed to the initiator network. The list varrays is all the varrays containing * one or more such ports * * @param initiatorId the initiator port WWN * @param dbClient an instance of DbClient * @return all the varrays the initiator can have exports in. */ public static Set<String> getInitiatorVarrays(String initiatorId, DbClient dbClient) { Set<String> varrayIds = new HashSet<String>(); Set<NetworkLite> networks = NetworkUtil.getEndpointAllNetworksLite(initiatorId, dbClient); for (NetworkLite network : networks) { if (network.registered()) { varrayIds.addAll(network.fetchAllVirtualArrays()); } } return varrayIds; } /** * Checks the connectivity between a network and a group of networks. * If the network is either in the collection or is routed to a network * in the collection, this method returns true * * @param networkUri the network being checked for connectivity * @param networkUris the networks being checked * @param dbClient an instance of dbClient * @return true the network is either in the collection or is routed to a network * in the collection */ public static boolean checkNetworkConnectedToAtLeastOneNetwork(URI networkUri, Collection<URI> networkUris, DbClient dbClient) { if (NullColumnValueGetter.isNullURI(networkUri) == false) { NetworkLite networkLite = NetworkUtil.getNetworkLite(networkUri, dbClient); if (networkLite != null) { return networkLite.connectedToAtLeastOneNetwork(networkUris); } } return false; } /** * Returns cluster1 or cluster2 according to which cluster a VPLEX StoragePort * is in. Only works for VPLEX ports. * * @param vplexPort StoragePort * @return "1" or "2". Returns "unknown-cluster" if error. * * TODO: Move this method to VPlexUtil */ public static String getVplexClusterOfPort(StoragePort vplexPort) { // after director- in this string determines vplex cluster if (vplexPort.getPortGroup() != null) { String[] tokens = vplexPort.getPortGroup().split("-"); if (CLUSTER1.equals(tokens[1])) { return CLUSTER1; } else if (CLUSTER2.equals(tokens[1])) { return CLUSTER2; } else { _log.warn("Could not determine cluster for storageport:" + vplexPort.getPortNetworkId() + " " + vplexPort.getId() + " Port group is:" + vplexPort.getPortGroup()); } } else { _log.warn("Could not determine cluster for storageport:" + vplexPort.getPortNetworkId() + " " + vplexPort.getId()); } return CLUSTER_UNKNOWN; } /** * This method returns the VPLEX cluster information for the virtual array. The assumption here is that the passed * varrayURI will not have ports from both VPLEX clusters. This is true when its called for the varray * which has VPLEX volume created on it. Until VPLEX volume create is attempted on the varray we cannot use this * method as user can just assign the network and varray could get ports from both the VPLEX clusters * * @param varrayURI The URI of the virtaul array * @param vplexStorageSystemURI The URI of the VPLEX storage system * @param dbClient dbclient * @return "1" or "2". Returns "unknown-cluster" if error. * * TODO: Move this method to VPlexUtil */ public static String getVplexClusterForVarray(URI varrayURI, URI vplexStorageSystemURI, DbClient dbClient) { String vplexCluster = CLUSTER_UNKNOWN; URIQueryResultList storagePortURIs = new URIQueryResultList(); dbClient .queryByConstraint(AlternateIdConstraint.Factory .getVirtualArrayStoragePortsConstraint(varrayURI.toString()), storagePortURIs); for (URI uri : storagePortURIs) { StoragePort storagePort = dbClient.queryObject(StoragePort.class, uri); if ((storagePort != null) && DiscoveredDataObject.CompatibilityStatus.COMPATIBLE.name().equals( storagePort.getCompatibilityStatus()) && (RegistrationStatus.REGISTERED.toString().equals(storagePort .getRegistrationStatus())) && DiscoveryStatus.VISIBLE.toString().equals(storagePort.getDiscoveryStatus())) { if (storagePort.getStorageDevice().equals(vplexStorageSystemURI)) { // Assumption is this varray cannot have mix of CLuster 1 // and Cluster 2 ports from VPLEX so getting // cluster information from one of the VPLEX port should // work. vplexCluster = getVplexClusterOfPort(storagePort); break; } } } return vplexCluster; } /** * This method returns the VPLEX cluster location for the List of StoragePort URIs. The assumption here is that the passed * List of StoragePorts URIs will not contain ports from both VPLEX clusters. The first port that passes all conditions * will determine the VPLEX cluster id returned. * * @param storagePortUris the List of StoragePort URIs to chck * @param vplexStorageSystemUri The URI of the VPLEX storage system * @param dbClient a reference to the database client * @return "1" or "2". Returns "unknown-cluster" if error. */ public static String getVplexClusterForStoragePortUris(List<URI> storagePortUris, URI vplexStorageSystemUri, DbClient dbClient) { String vplexCluster = CLUSTER_UNKNOWN; if (storagePortUris != null) { for (URI uri : storagePortUris) { StoragePort storagePort = dbClient.queryObject(StoragePort.class, uri); if ((storagePort != null) && DiscoveredDataObject.CompatibilityStatus.COMPATIBLE.name().equals( storagePort.getCompatibilityStatus()) && (RegistrationStatus.REGISTERED.toString().equals(storagePort .getRegistrationStatus())) && DiscoveryStatus.VISIBLE.toString().equals(storagePort.getDiscoveryStatus())) { if (storagePort.getStorageDevice().equals(vplexStorageSystemUri)) { // Assumption is this ExportMask cannot have mix of Cluster 1 // and Cluster 2 ports from VPLEX so getting // cluster information from one of the VPLEX port should // work. vplexCluster = getVplexClusterOfPort(storagePort); break; } } } } return vplexCluster; } /** * Given an initiator and StorageSystem object, find if the initiator is connect to the StorageSystem * through one of its network associations. Optionally checks that the port is in one of the specified varrays. * * @param initiator [in] Initiator object representing the initiator for which we would like * to check the connection. * @param storageSystem [in] StorageSystem object representing the array we want to check the connection to. * @param varrayURIs [in] [optional] If not null, will check that the ports tagged varrays intersect the passed varrays * @param dbClient [in] DbClient * @return true - iff, there is at least one network found that is shared by the initiator and one of the * StorageSystems StoragePorts. */ public static boolean isInitiatorConnectedToStorageSystem(Initiator initiator, StorageSystem storageSystem, List<URI> varrayURIs, DbClient dbClient) { Set<String> varrays = new HashSet<String>(); if (varrayURIs != null) { for (URI varray : varrayURIs) { varrays.add(varray.toString()); } } if (initiator == null || storageSystem == null || dbClient == null) { _log.info(String.format("isInitiatorConnectedToStorageSystem - Invalid parameters")); return false; } _log.info(String.format("isInitiatorConnectedToStorageSystem(%s, %s) -- Entered", initiator.getInitiatorPort(), storageSystem.getNativeGuid())); NetworkLite networkLite = NetworkUtil.getEndpointNetworkLite(initiator.getInitiatorPort(), dbClient); if (networkLite == null) { _log.info(String.format("isInitiatorConnectedToStorageSystem(%s, %s) -- Initiator is not associated with any network", initiator.getInitiatorPort(), storageSystem.getNativeGuid())); return false; } URI networkUri = networkLite.getId(); List<StoragePort> ports = NetworkAssociationHelper. getNetworkConnectedStoragePorts(networkUri.toString(), dbClient); _log.info(String.format("isInitiatorConnectedToStorageSystem(%s, %s) -- Checking for port connections on %s network", initiator.getInitiatorPort(), storageSystem.getNativeGuid(), networkLite.getLabel())); for (StoragePort port : ports) { if (storageSystem.getId().equals(port.getStorageDevice())) { if (varrays.isEmpty() || (port.getTaggedVirtualArrays() != null && !Collections.disjoint(varrays, port.getTaggedVirtualArrays()))) { _log.info(String .format("isInitiatorConnectedToStorageSystem(%s, %s) -- Found one port in the same network as initiator, %s (%s). Returning true.", initiator.getInitiatorPort(), storageSystem.getNativeGuid(), port.getNativeGuid(), port.getId())); return true; } } } _log.info(String .format("isInitiatorConnectedToStorageSystem(%s, %s) -- Could not find any ports in the same networks as the initiator. Returning false.", initiator.getInitiatorPort(), storageSystem.getNativeGuid())); return false; } /** * Find a storage system based on its serial number. Keep in mind, we may * have 2 storage systems with the same serial number if both block and file * are registered. Use the fileOnly param to distinguish between the * system type that's wanted. * * @param serialNumber * StorageSystem serial number * @param dbClient * DB Client * @param fileOnly * true if only file storage systems should be considered, * false otherwise. * @return The storage system URI found or null if none found */ public static URI findStorageSystemBySerialNumber(String serialNumber, DbClient dbClient, StorageSystemType systemType) { URI foundStorageSystemURI = null; // Find the storage system ID associated with this serial number URIQueryResultList result = new URIQueryResultList(); dbClient.queryByConstraint(AlternateIdConstraint.Factory .getStorageDeviceSerialNumberConstraint(serialNumber), result); if (result != null && result.iterator() != null && result.iterator().hasNext()) { Iterator<URI> resultItr = result.iterator(); while (resultItr.hasNext()) { foundStorageSystemURI = resultItr.next(); if (foundStorageSystemURI != null) { StorageSystem storageSystem = dbClient.queryObject(StorageSystem.class, foundStorageSystemURI); boolean isFileTypeSystem = SystemType.isFileTypeSystem(storageSystem.getSystemType()); if ((systemType == StorageSystemType.BLOCK && !isFileTypeSystem) || (systemType == StorageSystemType.FILE && isFileTypeSystem)) { // If file system types only has been specified and this is a file system type then // use that. Otherwise, if non-file system types has been specified and this is // not a file system type then use that. break; } } } } // Sometimes we have a serial number from the RP appliances, and for the most part, that works // with a Constraint Query, but in the case of VPLEX, the serial number object for distributed // VPLEX clusters will contain two serial numbers, not just one. So we need a long-form // way of finding those VPLEXs as well. if (foundStorageSystemURI == null) { // Get all the Storage System IDs List<URI> storageSystemIDs = dbClient.queryByType(StorageSystem.class, true); // Get all the Storage System objects loaded List<StorageSystem> storageSystems = null; if (storageSystemIDs != null) { storageSystems = dbClient.queryObject(StorageSystem.class, storageSystemIDs); } if (storageSystems != null && !storageSystems.isEmpty()) { for (StorageSystem storageSystem : storageSystems) { if (NullColumnValueGetter.isNotNullValue(storageSystem.getSerialNumber()) && storageSystem.getSerialNumber().contains(serialNumber)) { foundStorageSystemURI = storageSystem.getId(); break; } } } } return foundStorageSystemURI; } }