/* * 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.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.URIQueryResultList; import com.emc.storageos.db.client.model.ExportGroup; import com.emc.storageos.db.client.model.ExportMask; import com.emc.storageos.db.client.model.FSExportMap; import com.emc.storageos.db.client.model.FileExport; import com.emc.storageos.db.client.model.FileShare; import com.emc.storageos.db.client.model.Initiator; import com.emc.storageos.db.client.model.Network; import com.emc.storageos.db.client.model.NetworkSystem; import com.emc.storageos.db.client.model.StoragePort; import com.emc.storageos.db.client.util.CustomQueryUtility; import com.emc.storageos.db.client.util.NullColumnValueGetter; import com.emc.storageos.db.exceptions.DatabaseException; import com.emc.storageos.networkcontroller.NetworkFCZoneInfo; import com.emc.storageos.svcs.errorhandling.resources.APIException; public class NetworkUtil { private static final Logger _log = LoggerFactory.getLogger(NetworkUtil.class); /** * Given the URI for a Network, obtain its NetworkLite structure. * This is done without instantiating the endpoint data in the Network * by calling DbClient.queryObjectFields, which retrieves only certain * fields from the database. * * @param networkURI * @param client * @return NetworkLite */ static public NetworkLite getNetworkLite(URI networkURI, DbClient client) { List<URI> ids = new ArrayList<URI>(); ids.add(networkURI); Set<String> fieldNames = new HashSet<String>(); fieldNames.addAll(NetworkLite.getColumnNames()); Collection<Network> networks = client.queryObjectFields(Network.class, fieldNames, ids); Iterator<Network> networkIter = networks.iterator(); if (networkIter.hasNext()) { Network network = networkIter.next(); return new NetworkLite(network); } throw DatabaseException.fatals.unableToFindEntity(networkURI); } /** * Get the network that has the endpoint * * @param endpoint the formatted and validated endpoint * @param dbClient an instance if DbClient * @return a reference to the network that contains the endpoint. * Null if the network is not found. * */ public static NetworkLite getEndpointNetworkLite(String endpoint, DbClient dbClient) { return getEndpointNetworkLite(endpoint, dbClient, null); } /** * Get the network that has the endpoint * * @param endpoint the formatted and validated endpoint * @param dbClient an instance if DbClient * @param excludedNetworks do not get network with nativeId in the list * @return a reference to the network that contains the endpoint. * Null if the network is not found. * */ public static NetworkLite getEndpointNetworkLite(String endpoint, DbClient dbClient, Set<String> excludedNetworks) { _log.debug("Finding networklite for endpoint {}", endpoint); URIQueryResultList networkList = new URIQueryResultList(); Iterator<URI> iterator; URI networkUri = null; NetworkLite network; dbClient.queryByConstraint(AlternateIdConstraint.Factory.getEndpointNetworkConstraint(endpoint), networkList); iterator = networkList.iterator(); while (iterator.hasNext()) { networkUri = iterator.next(); network = getNetworkLite(networkUri, dbClient); // vsan id is in the excluded list, skip it if (excludedNetworks != null && excludedNetworks.contains(network.getNativeId())) { continue; } if (network != null && network.getInactive() == false) { _log.info(String.format("endpoint %s in network %s (%s)", endpoint, network.getLabel(), network.getId())); return network; } else { _log.info("networklite {} for endpoint {} was deleted or is inactive", networkUri, endpoint); } } _log.info("networklite could not be found for endpoint {}", endpoint); return null; } /** * Get all the networks that are connected to the endpoint. An endpoint * exists in one and only one network but can be visible by routing * from many other networks. This functions return all the networks * where this endpoint is visible inclusing the one that contains it. * * @param endpoint the formatted and validated endpoint * @param dbClient an instance if DbClient * @return all the networks that are connected to the endpoint. * An empty set if none is found. */ public static Set<NetworkLite> getEndpointAllNetworksLite(String endpoint, DbClient dbClient) { Set<NetworkLite> networks = new HashSet<NetworkLite>(); NetworkLite networkLite = getEndpointNetworkLite(endpoint, dbClient); if (networkLite != null) { networks.add(networkLite); networks.addAll(getNetworkLiteRoutedNetworks(networkLite, dbClient)); } return networks; } /** * Returns an instance of NetworkLite for every network that is routed to the request network. * * @param networkLite the networklite for which the routed networks are requested * @param dbClient an instance of dbClient * @return an instance of NetworkLite for every network that is routed to the request network */ public static Set<NetworkLite> getNetworkLiteRoutedNetworks(NetworkLite networkLite, DbClient dbClient) { if (networkLite != null && networkLite.getRoutedNetworks() != null && !networkLite.getRoutedNetworks().isEmpty()) { return getNetworkLites(networkLite.getRoutedNetworks(), dbClient); } return new HashSet<NetworkLite>(); } /** * Returns the network lites for a collection of network URIs * * @param uris the network URIs in string form * @param dbClient an instance of DbClient * @return the network lites for a collection of network URIs */ public static Set<NetworkLite> getNetworkLites(Collection<String> uris, DbClient dbClient) { Set<NetworkLite> networks = new HashSet<NetworkLite>(); NetworkLite networkLite = null; for (String uri : uris) { networkLite = getNetworkLite(URI.create(uri), dbClient); if (networkLite != null) { networks.add(networkLite); } } return networks; } /** * Returns a NetworkLite set for a collection of network URIs * * @param uris the network URIs in string form * @param dbClient an instance of DbClient * @return the NetworkLite set for a collection of network URIs */ public static Set<NetworkLite> queryNetworkLites(Collection<URI> uris, DbClient dbClient) { Set<NetworkLite> networks = new HashSet<NetworkLite>(); NetworkLite networkLite = null; for (URI uri : uris) { networkLite = getNetworkLite(uri, dbClient); if (networkLite != null) { networks.add(networkLite); } } return networks; } /** * Get the network the endpoint is associated with if any * * @param endpoint * @param dbClient * @return a reference to a network * Assumes endpoint formats have been validated. */ public static Network getEndpointNetwork(String endpoint, DbClient dbClient) { _log.debug("Finding network for endpoint {}", endpoint); URIQueryResultList networkList = new URIQueryResultList(); Iterator<URI> iterator; URI networkUri = null; Network network; dbClient.queryByConstraint(AlternateIdConstraint.Factory.getEndpointNetworkConstraint(endpoint), networkList); iterator = networkList.iterator(); while (iterator.hasNext()) { networkUri = iterator.next(); network = dbClient.queryObject(Network.class, networkUri); if (network != null && network.getInactive() == false) { _log.info("network {} for endpoint {} was found", networkUri, endpoint); return network; } else { _log.info("network {} for endpoint {} was deleted or is inactive", networkUri, endpoint); } } _log.info("network could not be found for endpoint {}", endpoint); return null; } /** * If the endpoint is used in an active Export Group, throws an exception * * @param endpoint endpoint being added * @param dbClient * Assumes endpoint formats have been validated. */ public static void checkNotUsedByActiveExportGroup(String endpoint, DbClient dbClient) { if (endpoint != null && !"".equals(endpoint)) { Initiator initiator = getInitiator(endpoint, dbClient); if (initiator != null && initiator.getId() != null) { if (NetworkUtil.isInitiatorInUse(initiator.getId(), dbClient)) { throw APIException.badRequests.endpointsCannotBeUpdatedActiveExport( endpoint); } } List<StoragePort> ports = findStoragePortsInDB(endpoint, dbClient); for (StoragePort port : ports) { if (port != null && port.getId() != null) { if (NetworkUtil.isBlockStoragePortInUse(port.getId(), dbClient)) { throw APIException.badRequests.endpointsCannotBeUpdatedActiveExport( endpoint); } } } } } /** * Looks up the storage port for a given end point. Returns null if a storage port could not be found. * * @param endPoint * @param dbClient an instance of {@link DbClient} * @return A reference to a port. */ public static StoragePort getStoragePort(String endPoint, DbClient dbClient) { URIQueryResultList portUriList = new URIQueryResultList(); dbClient.queryByConstraint( AlternateIdConstraint.Factory.getStoragePortEndpointConstraint(endPoint), portUriList); Iterator<URI> itr = portUriList.iterator(); while (itr.hasNext()) { StoragePort port = dbClient.queryObject(StoragePort.class, itr.next()); if (port != null && port.getInactive() == false) { return port; } } return null; } /** * Get an initiator as specified by the initiator's network port. * * @param networkPort The initiator's port WWN or IQN. * @return A reference to an initiator. */ public static Initiator getInitiator(String networkPort, DbClient dbClient) { Initiator initiator = null; URIQueryResultList resultsList = new URIQueryResultList(); // find the initiator dbClient.queryByConstraint(AlternateIdConstraint.Factory.getInitiatorPortInitiatorConstraint( networkPort), resultsList); Iterator<URI> resultsIter = resultsList.iterator(); while (resultsIter.hasNext()) { initiator = dbClient.queryObject(Initiator.class, resultsIter.next()); // there should be one initiator, so return as soon as it is found if (initiator != null && !initiator.getInactive()) { return initiator; } } return null; } /** * If any endpoint is used in an active File Export, throws an exception * * @param endpoints endpoints being added * @param varrays endpoints belong to * * Assumes endpoint formats have been validated. */ public static void checkNotUsedByActiveFileExport(String endpoint, DbClient dbClient) { Network network = NetworkUtil.getEndpointNetwork(endpoint, dbClient); if (network != null) { Set<String> netVArrayIds = network.getConnectedVirtualArrays(); if ((netVArrayIds != null) && (!netVArrayIds.isEmpty())) { Iterator<String> netVArrayIdsIter = netVArrayIds.iterator(); while (netVArrayIdsIter.hasNext()) { String varrayId = netVArrayIdsIter.next(); List<FileShare> fileShares = CustomQueryUtility .queryActiveResourcesByConstraint(dbClient, FileShare.class, AlternateIdConstraint.Factory.getConstraint(FileShare.class, "varray", varrayId)); for (FileShare fileShare : fileShares) { FSExportMap fsExports = fileShare.getFsExports(); if (fsExports != null) { Iterator<FileExport> it = fsExports.values().iterator(); while (it.hasNext()) { FileExport fileExport = it.next(); if (fileExport.getClients().contains(endpoint) || fileExport.getStoragePort().contains(endpoint)) { throw APIException.badRequests .endpointsCannotBeUpdatedActiveExport(endpoint); } } } } } } } } /** * Checks if an initiator in use by an export groups * * @param initId the initiator URI being checked * @param dbClient * @return true if the initiator in use by export groups */ public static boolean isInitiatorInUse(URI initId, DbClient dbClient) { if (initId != null) { List<ExportGroup> exportGroups = CustomQueryUtility.queryActiveResourcesByConstraint( dbClient, ExportGroup.class, AlternateIdConstraint.Factory.getConstraint( ExportGroup.class, "initiators", initId.toString())); return (exportGroups != null && !exportGroups.isEmpty()); } return false; } /** * Checks if an port in use by an export masks * * @param portId the port URI being checked * @param dbClient * @return true if the port in use by export masks */ public static boolean isBlockStoragePortInUse(URI portId, DbClient dbClient) { if (portId != null) { List<ExportMask> exportMasks = CustomQueryUtility.queryActiveResourcesByConstraint( dbClient, ExportMask.class, AlternateIdConstraint.Factory.getConstraint( ExportMask.class, "storagePorts", portId.toString())); return (exportMasks != null && !exportMasks.isEmpty()); } return false; } /** * Parses the WWN from the network's Native GUID * * @param network the network * @return the network WWN */ public static String getNetworkWwn(NetworkLite network) { return parseNetworkWwn(network == null ? "" : network.getNativeGuid()); } /** * Parses the WWN from the network's Native GUID * * @param network the network * @return the network WWN */ public static String getNetworkWwn(Network network) { return parseNetworkWwn(network.getNativeGuid()); } private static String parseNetworkWwn(String nativeGuid) { if (nativeGuid == null || nativeGuid.length() == 0) { return ""; } String[] strs = nativeGuid.split("\\+"); if (strs.length > 1) { return strs[strs.length - 1]; } return ""; } /** * Given the initiator and port networks, check that they are connected that is either * in the same network or in different networks but routable. * * @param iniNetwork the initiator network * @param portNet the port network * @return true if the port and initiator network are connected. */ public static boolean checkInitiatorAndPortConnected(NetworkLite iniNetwork, NetworkLite portNet) { if (iniNetwork.getId().equals(portNet.getId())) { _log.info("Both the port and initiator are in the same network {}", iniNetwork.getNativeGuid()); return true; } else if (iniNetwork.hasRoutedNetworks(portNet.getId())) { _log.info("The port and initiators are in different but routed networks: {} and {}", new Object[] { iniNetwork.getNativeGuid(), portNet.getNativeGuid() }); return true; } _log.info("The port and initiator are not connected."); return false; } /** * Given the fabric name for a Network, obtain its NetworkLite structure. * This is done without instantiating the endpoint data in the Network * by calling DbClient.queryObjectFields, which retrieves only certain * fields from the database. * * @param fabricId * @param client * @return NetworkLite */ static public NetworkLite getNetworkLiteByFabricId(String fabricId, String fabricWWN, DbClient client) { if (fabricId != null && fabricId.length() > 0) { URIQueryResultList networkList = new URIQueryResultList(); client.queryByConstraint(AlternateIdConstraint.Factory.getConstraint(Network.class, "nativeId", fabricId), networkList); for (URI uri : networkList) { return getNetworkLite(uri, client); // TODO -need to add check for inactive networks - Need to add code to try using WWN } } return null; } /** * This method returns storage ports if they exist in database. * Returns empty list if a storage port could not be found. * * @param pwwn storage port pwwn * @return storagePort object or null */ public static List<StoragePort> findStoragePortsInDB(String pwwn, DbClient dbClient) { List<StoragePort> ports = new ArrayList<StoragePort>(); _log.info("Looking for storage port {} in database", pwwn); URIQueryResultList portUriList = new URIQueryResultList(); dbClient.queryByConstraint( AlternateIdConstraint.Factory.getStoragePortEndpointConstraint(pwwn), portUriList); Iterator<URI> itr = portUriList.iterator(); while (itr.hasNext()) { StoragePort port = dbClient.queryObject(StoragePort.class, itr.next()); if (port != null && !port.getInactive()) { _log.info("Found storage port {}", pwwn); ports.add(port); } } return ports; } /** * This method returns initiator if it exist in database * Returns null if a initiator could not be found. * * @param pwwn Initiator pwwn * @return initiator object or null */ public static Initiator findInitiatorInDB(String pwwn, DbClient dbClient) { Initiator initiator = null; _log.info("Looking for initiator {} in database", pwwn); URIQueryResultList queryResults = new URIQueryResultList(); dbClient.queryByConstraint( AlternateIdConstraint.Factory.getInitiatorPortInitiatorConstraint(pwwn), queryResults); Iterator<URI> resultsIter = queryResults.iterator(); if (resultsIter.hasNext()) { _log.info("Found initiator {}", pwwn); initiator = dbClient.queryObject(Initiator.class, resultsIter.next()); } return initiator; } /** * Checks if any network systems have been discovered. * * @param dbClient an instance of {@link DbClient} * @return true if at least one active network system exists regardless of the * its discovery or registered status. */ public static boolean areNetworkSystemDiscovered(DbClient dbClient) { // if no network systems are registered, the zoning is not required List<URI> uriNetworkDevices = dbClient.queryByType(NetworkSystem.class, true); if (uriNetworkDevices != null && uriNetworkDevices.iterator().hasNext()) { return true; } _log.info("SAN Zoning is disabled because there are no NetworkSystems"); return false; } /** * Creates a map of initiators grouped and keyed by their network. * Initiators which are not in any network are not returned. * * @param initiators the initiators * @param client * @return a map of network-to-initiators */ public static Map<NetworkLite, List<Initiator>> getInitiatorsByNetwork(Collection<Initiator> initiators, DbClient dbClient) { Map<NetworkLite, List<Initiator>> map = new HashMap<NetworkLite, List<Initiator>>(); NetworkLite network = null; List<Initiator> netInitiators = null; for (Initiator initiator : initiators) { network = NetworkUtil.getEndpointNetworkLite(initiator.getInitiatorPort(), dbClient); if (network != null) { netInitiators = map.get(network); if (netInitiators == null) { netInitiators = new ArrayList<Initiator>(); map.put(network, netInitiators); } netInitiators.add(initiator); } } return map; } /** * Returns the ports in the given network in a port-wwn-to-port map. * * @param networkLite the network * @param ports the ports * @return a map of port-wwn-to-port */ public static Map<String, StoragePort> getPortsInNetworkMap(NetworkLite networkLite, Collection<StoragePort> ports) { Map<String, StoragePort> map = new HashMap<String, StoragePort>(); if (networkLite != null) { for (StoragePort port : ports) { if (port.getNetwork() != null && port.getNetwork().equals(networkLite.getId())) { map.put(port.getPortNetworkId(), port); } } if (map.isEmpty() && networkLite.getRoutedNetworks() != null) { for (StoragePort port : ports) { if (port.getNetwork() != null && networkLite.getRoutedNetworks().contains(port.getNetwork().toString())) { map.put(port.getPortNetworkId(), port); } } } } return map; } /** * Returns a Map of networkURI => [set of endpoints connected]. * * @param dbClient * @param initiators * @return */ public static Map<URI, Set<String>> getNetworkToInitiators(DbClient dbClient, List<Initiator> initiators) { Map<URI, Set<String>> networkToEndPoints = new HashMap<URI, Set<String>>(); for (Initiator initiator : initiators) { Set<NetworkLite> networkLites = getEndpointAllNetworksLite(initiator.getInitiatorPort(), dbClient); if (null == networkLites || networkLites.isEmpty()) { _log.info(String.format("getNetworkToInitiators(%s) -- Initiator is not associated with any network", initiator.getInitiatorPort())); } else { for (NetworkLite networkLite : networkLites) { URI networkUri = networkLite.getId(); _log.info(String.format("Adding initiator, network (%s, %s) to map", initiator.getInitiatorPort(), networkLite.getLabel())); Set<String> endPoints = networkToEndPoints.get(networkUri); if (null == endPoints) { endPoints = new HashSet<String>(); } endPoints.add(initiator.getInitiatorPort()); networkToEndPoints.put(networkUri, endPoints); } } } return networkToEndPoints; } /** * Convenience method to extract all of the FC zone references from a list of NetworkFCZoneInfo objects to a single list. * * @param zoneInfoList zone info list * @return list of FCZoneReference URIs */ public static List<URI> getFCZoneReferences(List<NetworkFCZoneInfo> zoneInfoList) { Set<URI> zoneRefs = new HashSet<>(); if (zoneInfoList != null) { for (NetworkFCZoneInfo zoneInfo : zoneInfoList) { if (!NullColumnValueGetter.isNullURI(zoneInfo.getFcZoneReferenceId())) { zoneRefs.add(zoneInfo.getFcZoneReferenceId()); } } } return new ArrayList<URI>(zoneRefs); } }