/* * Copyright (c) 2015 EMC Corporation * All Rights Reserved */ package com.emc.storageos.volumecontroller.impl; 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.coordinator.client.service.CoordinatorClient; import com.emc.storageos.db.client.DbClient; 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.Network; import com.emc.storageos.db.client.model.StoragePool; import com.emc.storageos.db.client.model.StoragePort; import com.emc.storageos.db.client.model.StorageSystem; import com.emc.storageos.db.client.model.StringSet; import com.emc.storageos.db.client.model.StoragePort.PortType; import com.emc.storageos.util.ConnectivityUtil; /** * This is a helper class to update the implicit associations between varrays and * storage pools. These associations exist when a varray has one or more transport * zones with end points that map to storage arrays. The pools of these arrays are * considered implicitly associated with the varray. This is because volumes can * be created and exported to hosts in these varrays. These associations are changed * when networks are added/remove to varrays, storage ports are discovered, * registered and de-registered, and when new pools are discovered. <em>Note</em> removal * of storage ports and storage pools is not yet supported. * */ public class StoragePoolAssociationHelper { private static final Logger _log = LoggerFactory.getLogger(StoragePoolAssociationHelper.class); /** * Update the storage pools that are affected by the network update. These are the pools * in storage systems that have ports in the network. * * When this call is made, the update already took place. Further the list of endpoints and * varrays are filtered to what effectively added and not what was requested by the user. * * @param network the network that was updated * @param addVarrays the varrays that were added to the network if any, otherwise null * @param remVarray the varrays that were removed from the network if any, otherwise null * @param ports the ports that were changed in the network if any, otherwise null * @param remPorts the ports that were removed to the network if any, otherwise null */ public static void handleNetworkUpdated(Network network, Collection<URI> addVarrays, Collection<URI> remVarray, Collection<StoragePort> ports, Collection<StoragePort> remPorts, DbClient dbClient, CoordinatorClient coordinator) { StoragePortAssociationHelper.runUpdatePortAssociationsProcess(ports, remPorts, dbClient, coordinator, null); } /** * Method to get Storage Systems from Ports * * @param ports * @param remPorts * @return */ public static HashSet<URI> getStorageSytemsFromPorts(Collection<StoragePort> ports, Collection<StoragePort> remPorts) { Map<URI, List<StoragePort>> systemsMap = getPortsBySystem(ports); Map<URI, List<StoragePort>> remPortsSystemsMap = getPortsBySystem(remPorts); // get all the system that were affected and update their virtual arrays HashSet<URI> systemsToProcess = new HashSet<URI>(remPortsSystemsMap.keySet()); systemsToProcess.addAll(systemsMap.keySet()); return systemsToProcess; } /** * Extract Storage Pools from Storage Systems the ports belonging to. * * @param dbClient * @param ports * @param remPorts * @return */ public static List<StoragePool> getStoragePoolsFromPorts(DbClient dbClient, Collection<StoragePort> ports, Collection<StoragePort> remPorts) { return getStoragePoolsFromPorts(dbClient, ports, remPorts, false); } /** * Extract Storage Pools from Storage Systems the ports belonging to. * * @param dbClient * @param ports * @param remPorts * @param getVplexConnected * @return */ public static List<StoragePool> getStoragePoolsFromPorts(DbClient dbClient, Collection<StoragePort> ports, Collection<StoragePort> remPorts, boolean getVplexConnected) { List<StoragePool> storagePools = new ArrayList<StoragePool>(); HashSet<URI> systemsToProcess = new HashSet<URI>(); HashSet<URI> portSystems = getStorageSytemsFromPorts(ports, remPorts); for (URI systemUri : portSystems) { if (getVplexConnected) { StorageSystem system = dbClient.queryObject(StorageSystem.class, systemUri); if (DiscoveredDataObject.Type.vplex.name().equals(system.getSystemType())) { Set<URI> connectedSystemURIs = ConnectivityUtil .getStorageSystemAssociationsByNetwork(dbClient, systemUri, PortType.backend); for (URI connectedSystemURI : connectedSystemURIs) { systemsToProcess.add(connectedSystemURI); } } else { systemsToProcess.addAll(portSystems); } } else { systemsToProcess.addAll(portSystems); } } for (URI systemUri : systemsToProcess) { URIQueryResultList storagePoolURIs = new URIQueryResultList(); dbClient.queryByConstraint(ContainmentConstraint.Factory .getStorageDeviceStoragePoolConstraint(systemUri), storagePoolURIs); while (storagePoolURIs.iterator().hasNext()) { URI storagePoolURI = storagePoolURIs.iterator().next(); StoragePool storagePool = dbClient.queryObject(StoragePool.class, storagePoolURI); if (storagePool != null && !storagePool.getInactive()) { storagePools.add(storagePool); } } } return storagePools; } /** * Given the changes made to a list of ports' varray associations, update the pools associations. * * @param ports the ports for which the varray associations were updated * @param remPorts the ports for which the varray associations were cleared * @param dbClient an instance of db client * @param coordinator an instance of coordinator service */ public static void updateVArrayRelations(Collection<StoragePort> ports, Collection<StoragePort> remPorts, DbClient dbClient, CoordinatorClient coordinator) { // get all the system that were affected and update their virtual arrays HashSet<URI> systemsToProcess = getStorageSytemsFromPorts(ports, remPorts); // this is very crude still because it resets the connections for all the systems // what is needed is: // If neighborhoods were changed, update all the systems // If neighborhoods not changed, to get the removed, added and existing systems. // only act on systems that exist only in add and only in remove. for (URI systemUri : systemsToProcess) { updateSystemVarrays(systemUri, dbClient); } } /** * When the ports-varray associations change, for vplex system, update the system-varray association. * For other storage systems, update the pools-varrays associations. * * @param systemUri the system where the varray association has changed * @param dbClient an instance of dbClient */ private static void updateSystemVarrays(URI systemUri, DbClient dbClient) { StorageSystem system = dbClient.queryObject(StorageSystem.class, systemUri); if (!system.getInactive()) { _log.info("Updating the virtual arrays for storage system {}", system.getNativeGuid()); if (ConnectivityUtil.isAVPlex(system)) { StringSet varrays = getSystemConnectedVarrays(systemUri, PortType.frontend.name(), dbClient); _log.info("vplex system {} varrays will be set to {}", system.getNativeGuid(), varrays); if (system.getVirtualArrays() == null) { system.setVirtualArrays(varrays); } else { system.getVirtualArrays().replace(varrays); } dbClient.updateAndReindexObject(system); _log.info("Updated vplex system {} varrays to {}", system.getNativeGuid(), varrays); } else { StringSet varrays = getSystemConnectedVarrays(systemUri, null, dbClient); _log.info("The pools of storage system {} varrays will be set to {}", system.getNativeGuid(), varrays); List<StoragePool> pools = getSystemPools(systemUri, dbClient); for (StoragePool pool : pools) { pool.replaceConnectedVirtualArray(varrays); _log.info("Updated storage pool {} varrays to {}", pool.getNativeGuid(), varrays); } dbClient.updateAndReindexObject(pools); } } } /** * When a pool is newly discovered, set its varray associations. * * @param systemUri the pool's system * @param storagePools the pool to be updated * @param dbClient an instance of db client */ public static void setStoragePoolVarrays(URI systemUri, List<StoragePool> storagePools, DbClient dbClient) { StringSet varrayURIs = getSystemConnectedVarrays(systemUri, null, dbClient); for (StoragePool storagePool : storagePools) { if (storagePool.getStorageDevice().equals(systemUri)) { storagePool.replaceConnectedVirtualArray(varrayURIs); } else { _log.error("Pool {} does not belong to storage system {} and will not be updated", storagePool.getNativeGuid(), systemUri); } } dbClient.updateAndReindexObject(storagePools); // now ensure any RP systems are getting their varray connections updated ConnectivityUtil.updateRpSystemsConnectivity( java.util.Collections.singletonList(systemUri), dbClient); } /** * Create a map of storage system to ports. The logic of associating/dis-associating * needs to look at the aggregate data to make decisions. * * @param portSet the list of ports * @return */ private static Map<URI, List<StoragePort>> getPortsBySystem(Collection<StoragePort> portSet) { Map<URI, List<StoragePort>> systemPorts = new HashMap<URI, List<StoragePort>>(); if (portSet != null) { List<StoragePort> ports = null; for (StoragePort port : portSet) { ports = (ArrayList<StoragePort>) systemPorts.get(port.getStorageDevice()); if (ports == null) { ports = new ArrayList<StoragePort>(); systemPorts.put(port.getStorageDevice(), ports); } ports.add(port); } } return systemPorts; } private static List<StoragePort> getSystemPorts(URI systemUri, DbClient dbClient) { List<StoragePort> ports = new ArrayList<StoragePort>(); URIQueryResultList storagePortURIs = new URIQueryResultList(); dbClient.queryByConstraint(ContainmentConstraint.Factory .getStorageDeviceStoragePortConstraint(systemUri), 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()) { ports.add(storagePort); } else { _log.error("Can't find storage port {} in the database " + "or the port is marked for deletion", storagePortURI); } } return ports; } private static List<StoragePool> getSystemPools(URI systemUri, DbClient dbClient) { List<StoragePool> pools = new ArrayList<StoragePool>(); URIQueryResultList storagePoolsURIs = new URIQueryResultList(); dbClient.queryByConstraint(ContainmentConstraint.Factory .getContainedObjectsConstraint(systemUri, StoragePool.class, "storageDevice"), storagePoolsURIs); Iterator<URI> storagePortURIsIter = storagePoolsURIs.iterator(); while (storagePortURIsIter.hasNext()) { URI storagePoolURI = storagePortURIsIter.next(); StoragePool storagePool = dbClient.queryObject(StoragePool.class, storagePoolURI); if (storagePool != null && !storagePool.getInactive()) { pools.add(storagePool); } else { _log.error("Can't find storage pool {} in the database " + "or the pool is marked for deletion", storagePoolURI); } } return pools; } /** * Returns a list of varrays that have connectivity to the storage * system. Connectivity is determined by the presence in one of the * varray's networks of an end point that corresponds to a * storage port that was discovered on the array. * * @param systemUri the storage system uri * @param portType frontend or backend. Return all types when this param is null * @param dbClient and instance of {@link DbClient} * @return a list of varray URIs */ private static StringSet getSystemConnectedVarrays(URI systemUri, String portType, DbClient dbClient) { List<StoragePort> ports = getSystemPorts(systemUri, dbClient); StringSet varrays = new StringSet(); for (StoragePort port : ports) { if (port.getTaggedVirtualArrays() != null && (portType == null || port.getPortType().equals(portType))) { varrays.addAll(port.getTaggedVirtualArrays()); } } return varrays; } /** * Returns a list of varrays that have connectivity to a vplex system * * @param systemUri the vplex system uri * @param dbClient and instance of {@link DbClient} * @return a list of varray URIs */ public static StringSet getVplexSystemConnectedVarrays(URI systemUri, DbClient dbClient) { return getSystemConnectedVarrays(systemUri, PortType.frontend.name(), dbClient); } }