/* * Copyright (c) 2013 EMC Corporation * All Rights Reserved */ package com.emc.storageos.volumecontroller.impl.utils.attrmatchers; import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; 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 org.springframework.util.CollectionUtils; 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.DiscoveredDataObject.RegistrationStatus; import com.emc.storageos.db.client.model.StoragePool; import com.emc.storageos.db.client.model.StoragePort; import com.emc.storageos.db.client.model.StorageProtocol; import com.emc.storageos.db.client.model.VirtualArray; import com.emc.storageos.db.client.model.StorageProtocol.Block; import com.emc.storageos.db.client.model.StorageProtocol.File; import com.emc.storageos.db.client.model.StorageProtocol.Transport; 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.EndpointUtility; import com.emc.storageos.db.client.util.NullColumnValueGetter; import com.emc.storageos.model.valid.Endpoint; import com.emc.storageos.volumecontroller.AttributeMatcher; import com.google.common.base.Joiner; import com.google.common.collect.Sets; /** * ProtocolAttrMatcher is responsible to match all the pool protocols with * the given protocols. * */ public class ProtocolsAttrMatcher extends AttributeMatcher { private static final Logger _logger = LoggerFactory .getLogger(ProtocolsAttrMatcher.class); @Override public List<StoragePool> matchStoragePoolsWithAttributeOn(List<StoragePool> allPools, Map<String, Object> attributeMap, StringBuffer errorMessage) { List<StoragePool> matchedPools = new ArrayList<StoragePool>(); @SuppressWarnings("unchecked") Set<String> protocolsRequested = (Set<String>) attributeMap.get(Attributes.protocols.toString()); _logger.info("Pools Matching Protocols Started {}, {} :", protocolsRequested, Joiner.on("\t").join(getNativeGuidFromPools(allPools))); Iterator<StoragePool> poolIterator = allPools.iterator(); while (poolIterator.hasNext()) { StoragePool pool = poolIterator.next(); boolean isPoolMatched = false; if (null != pool.getProtocols()) { Set<String> protocolsNotMatched = Sets.difference(protocolsRequested, pool.getProtocols()); if (protocolsNotMatched.isEmpty() || isFilePoolMatchedRequestedProtocols(pool, protocolsRequested)) { matchedPools.add(pool); isPoolMatched = true; } } if (!isPoolMatched) { _logger.info("Ignoring pool {} id: {} as it doesn't support protocols.", pool.getNativeGuid(), pool.getNativeGuid()); } } getNetworkMatchingPoolsForVnxe(matchedPools, protocolsRequested, attributeMap); _logger.info("Pools Matching Protocols Ended: {}", Joiner.on("\t").join(getNativeGuidFromPools(matchedPools))); if (CollectionUtils.isEmpty(matchedPools)) { errorMessage.append(String.format("No matching storage pool found for the protocols %s. ", protocolsRequested)); _logger.error(errorMessage.toString()); } return matchedPools; } @Override protected boolean isAttributeOn(Map<String, Object> attributeMap) { // ProtocolsAttrMatcher will be executed only if there are protocols. return attributeMap != null && attributeMap.containsKey(Attributes.protocols.toString()); } @Override public Map<String, Set<String>> getAvailableAttribute(List<StoragePool> neighborhoodPools, URI varrayId) { try { Map<String, Set<String>> availableAttrMap = new HashMap<String, Set<String>>(1); Set<String> availableAttrValues = new HashSet<String>(); Map<URI, Set<String>> arrayProtocolsMap = new HashMap<URI, Set<String>>(); for (StoragePool pool : neighborhoodPools) { StringSet protocols = pool.getProtocols(); if (null != protocols && !protocols.isEmpty()) { URI arrayUri = pool.getStorageDevice(); Set<String> existProtocols = arrayProtocolsMap.get(arrayUri); if (existProtocols != null) { existProtocols.addAll(protocols); } else { arrayProtocolsMap.put(arrayUri, protocols); } } } if (!arrayProtocolsMap.isEmpty()) { availableAttrValues = getNetworkSupportedProtocols(arrayProtocolsMap, varrayId); } if (!availableAttrValues.isEmpty()) { availableAttrMap.put(Attributes.protocols.toString(), availableAttrValues); return availableAttrMap; } } catch (Exception e) { _logger.error("Exception occurred while getting available attributes using ProtocolsMatcher.", e); } return Collections.emptyMap(); } /** * Returns if the storage pool's protocol matches requested protocols when * the storagePool's protocol is set to NFS_OR_CIFS * * @param pool storagePool * @param protocolsRequested requested protocols * @return if storagePool's protocol matches the requested protocols */ private boolean isFilePoolMatchedRequestedProtocols(StoragePool pool, Set<String> protocolsRequested) { boolean isMatched = false; StringSet poolProtocols = pool.getProtocols(); if (poolProtocols.contains(StorageProtocol.File.NFS_OR_CIFS.name()) && protocolsRequested.size() == 1) { Iterator<String> it = protocolsRequested.iterator(); String protocol = it.next(); if (protocol.equalsIgnoreCase(StorageProtocol.File.NFS.name()) || protocol.equalsIgnoreCase(StorageProtocol.File.CIFS.name()) || protocol.equalsIgnoreCase(StorageProtocol.File.NFSv4.name()) ) { isMatched = true; } } return isMatched; } /** * Get supported protocols by the storage pools and storage ports in the varray * * @param arrayProtocolsMap supported pool protocols per array * @param varrayId * @return the set of protocols supported */ private Set<String> getNetworkSupportedProtocols(Map<URI, Set<String>> arrayProtocolsMap, URI varrayId) { Set<String> protocols = new HashSet<String>(); for (Map.Entry<URI, Set<String>> entry : arrayProtocolsMap.entrySet()) { URI arrayUri = entry.getKey(); Set<String> poolProtocols = entry.getValue(); if (poolProtocols.contains(File.NFS_OR_CIFS.name())) { poolProtocols.remove(File.NFS_OR_CIFS.name()); poolProtocols.add(File.NFS.name()); poolProtocols.add(File.CIFS.name()); } Set<String> portProtocols = new HashSet<String>(); URIQueryResultList storagePortURIs = new URIQueryResultList(); _objectCache.getDbClient().queryByConstraint( ContainmentConstraint.Factory.getStorageDeviceStoragePortConstraint(arrayUri), storagePortURIs); Iterator<URI> storagePortsIter = storagePortURIs.iterator(); while (storagePortsIter.hasNext()) { URI storagePortURI = storagePortsIter.next(); StoragePort storagePort = _objectCache.queryObject(StoragePort.class, storagePortURI); // only usable storage port will be checked Set<String> varrays = new HashSet<String>(); varrays.add(varrayId.toString()); if (!isPortUsable(storagePort, varrays)) { continue; } portProtocols.addAll(transport2Protocol(storagePort.getTransportType())); if (portProtocols.containsAll(poolProtocols)) { break; } } poolProtocols.retainAll(portProtocols); protocols.addAll(poolProtocols); } return protocols; } /** * Convert storage port transport type to related protocols. * * @param transportType * @return */ private Set<String> transport2Protocol(String transport) { Set<String> protocols = new HashSet<String>(); if (transport == null) { return protocols; } if (Transport.IP.name().equals(transport)) { protocols.add(Block.iSCSI.name()); protocols.add(Block.RBD.name()); protocols.add(File.NFS.name()); protocols.add(File.CIFS.name()); protocols.add(File.NFSv4.name()); } if (Transport.Ethernet.name().equals(transport)) { protocols.add(Block.FCoE.name()); return protocols; } if (Transport.FC.name().equals(transport)) { protocols.add(Block.FC.name()); return protocols; } if (Transport.ScaleIO.name().equals(transport)) { protocols.add(Block.ScaleIO.name()); return protocols; } return protocols; } /** * VNXe supports both of file and block, and storage pools supports both block and file too. * This method will check on the network storage ports type to match up the requested protocols. * e.g. if the requested protocols are for file, and there is no file storage ports * in the networks, it would remove the storage pools from the matchedPools list. * * @param matchedPools * @param protocolRequested * @param attributeMap * @return */ private List<StoragePool> getNetworkMatchingPoolsForVnxe(List<StoragePool> matchedPools, Set<String> protocolRequested, Map<String, Object> attributeMap) { Map<URI, Set<StoragePool>> arrayPoolMap = new HashMap<URI, Set<StoragePool>>(); for (StoragePool pool : matchedPools) { URI arrayId = pool.getStorageDevice(); if (arrayId == null) { continue; } StorageSystem storageSystem = _objectCache.queryObject(StorageSystem.class, arrayId); if (storageSystem == null) { continue; } String systemType = storageSystem.getSystemType(); if (systemType != null && (systemType.equalsIgnoreCase(SystemType.vnxe.name()) ||systemType.equalsIgnoreCase(SystemType.unity.name()))) { // only check on VNXe and VNXUnity pools Set<StoragePool> arrayPools = arrayPoolMap.get(arrayId); if (arrayPools != null) { arrayPools.add(pool); } else { Set<StoragePool> pools = new HashSet<StoragePool>(); pools.add(pool); arrayPoolMap.put(arrayId, pools); } } } if (arrayPoolMap.isEmpty()) { return matchedPools; } @SuppressWarnings("unchecked") Set<String> vArrays = (Set<String>) attributeMap.get(Attributes.varrays.toString()); _logger.info("Checking for VNXe matching pools."); for (Map.Entry<URI, Set<StoragePool>> entry : arrayPoolMap.entrySet()) { URI arrayUri = entry.getKey(); URIQueryResultList storagePortURIs = new URIQueryResultList(); _objectCache.getDbClient().queryByConstraint( ContainmentConstraint.Factory.getStorageDeviceStoragePortConstraint(arrayUri), storagePortURIs); Iterator<URI> storagePortsIter = storagePortURIs.iterator(); boolean isMatching = false; while (storagePortsIter.hasNext()) { URI storagePortURI = storagePortsIter.next(); StoragePort storagePort = _objectCache.queryObject(StoragePort.class, storagePortURI); // only usable storage port will be checked if (!isPortUsable(storagePort, vArrays)) { continue; } String tranportType = storagePort.getTransportType(); String endpoint = storagePort.getPortNetworkId(); if (protocolRequested.contains(Block.FC.name())) { if (tranportType.equalsIgnoreCase(Transport.FC.name()) && EndpointUtility.isValidEndpoint(endpoint, Endpoint.EndpointType.WWN)) { isMatching = true; break; } } else if (protocolRequested.contains(Block.iSCSI.name())) { if (tranportType.equalsIgnoreCase(Transport.IP.name()) && EndpointUtility.isValidEndpoint(endpoint, Endpoint.EndpointType.SAN)) { isMatching = true; break; } } else if (protocolRequested.contains(File.CIFS.name()) || protocolRequested.contains(File.NFS.name())) { if (tranportType.equalsIgnoreCase(Transport.IP.name()) && EndpointUtility.isValidEndpoint(endpoint, Endpoint.EndpointType.IP)) { isMatching = true; break; } } } if (!isMatching) { _logger.info("Removing all storage pools of the array: {} from the matching pools.", arrayUri); matchedPools.removeAll(arrayPoolMap.get(arrayUri)); } } return matchedPools; } /** * Check if the storagePort belongs to any of the varrays, and usable * * @param storagePort * @param varrays * @return */ private boolean isPortUsable(StoragePort storagePort, Set<String> varrays) { boolean isUsable = true; if (storagePort == null || storagePort.getInactive() || storagePort.getTaggedVirtualArrays() == null || NullColumnValueGetter.isNullURI(storagePort.getNetwork()) || !RegistrationStatus.REGISTERED.toString().equalsIgnoreCase( storagePort.getRegistrationStatus()) || (StoragePort.OperationalStatus.valueOf(storagePort.getOperationalStatus())) .equals(StoragePort.OperationalStatus.NOT_OK) || !DiscoveredDataObject.CompatibilityStatus.COMPATIBLE.name() .equals(storagePort.getCompatibilityStatus()) || !DiscoveryStatus.VISIBLE.name().equals( storagePort.getDiscoveryStatus())) { isUsable = false; } else { StringSet portVarrays = storagePort.getTaggedVirtualArrays(); portVarrays.retainAll(varrays); if (portVarrays.isEmpty()) { // the storage port does not belongs to any varrays isUsable = false; } } return isUsable; } }