/* * Copyright (c) 2013 EMC Corporation * All Rights Reserved */ package com.emc.storageos.volumecontroller.impl.utils.attrmatchers; import java.net.URI; import java.util.ArrayList; import java.util.HashMap; 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.model.ProtectionSystem; import com.emc.storageos.db.client.model.StoragePool; import com.emc.storageos.db.client.model.StringMap; import com.emc.storageos.db.client.model.StringSet; import com.emc.storageos.db.client.model.VirtualPool; import com.emc.storageos.db.client.util.NullColumnValueGetter; import com.emc.storageos.util.ConnectivityUtil; import com.emc.storageos.volumecontroller.AttributeMatcher; import com.google.common.base.Joiner; /** * ProtectionAttrMatcher is responsible to match all the pools with * the given CoS protection attributes. * */ public class ProtectionAttrMatcher extends AttributeMatcher { private static final Logger _logger = LoggerFactory .getLogger(ProtectionAttrMatcher.class); @Override public List<StoragePool> matchStoragePoolsWithAttributeOn(List<StoragePool> allPools, Map<String, Object> attributeMap, StringBuffer errorMessage) { StringMap rpMap = (StringMap) attributeMap.get(Attributes.recoverpoint_map.name()); _logger.info("Pools matching protection attributes Started {}, {} :", rpMap, Joiner.on("\t").join(getNativeGuidFromPools(allPools))); // If protection is not specified, we can return all remaining pools. if (rpMap == null || rpMap.isEmpty()) { _logger.info("No protection specified, return all remaining pools."); return allPools; } List<StoragePool> matchedPools = new ArrayList<StoragePool>(); Iterator<StoragePool> poolIterator = allPools.iterator(); // Check if MetroPoint has been enabled. Boolean metroPointEnabled = (Boolean) attributeMap.get(Attributes.metropoint.toString()); boolean isRPVPlex = NullColumnValueGetter.isNotNullValue( (String) attributeMap.get(Attributes.high_availability_type.toString())); // Flag to indicate if this is an RP+VPLEX setup with HA as the Source. boolean haAsRPSource = false; if (isRPVPlex && (metroPointEnabled == null || !metroPointEnabled)) { _logger.info("Not a MetroPoint Virtual Pool"); String haVarrayForRPSource = (String) attributeMap.get(Attributes.high_availability_rp.toString()); haAsRPSource = NullColumnValueGetter.isNotNullValue(haVarrayForRPSource); if (haAsRPSource) { _logger.info("Virtual Pool indicates to use HA as the RP Source"); // Get HA Virtual Pool String haVpoolId = (String) attributeMap.get(Attributes.high_availability_vpool .toString()); List<StoragePool> haVpoolPoolList = new ArrayList<StoragePool>(); if (haVpoolId != null) { VirtualPool haVpool = _objectCache.queryObject(VirtualPool.class, URI.create(haVpoolId)); haVpoolPoolList = VirtualPool.getValidStoragePools(haVpool, _objectCache.getDbClient(), true); _logger.info(String.format("HA Virtual Pool exists [%s](%s), consider Storage Pools from it.", haVpool.getLabel(), haVpool.getId())); } else { _logger.info("No HA Virtual Pool exists, consider Storage Pools from main Virtual Pool."); haVpoolPoolList.addAll(allPools); } List<StoragePool> filterPools = new ArrayList<StoragePool>(); _logger.info(String.format("Remove Storage Pools that are not tagged with Virtual Array (%s).", haVarrayForRPSource)); for (StoragePool haPool : haVpoolPoolList) { if (haPool.getTaggedVirtualArrays() != null && !haPool.getTaggedVirtualArrays().contains(haVarrayForRPSource)) { filterPools.add(haPool); } } haVpoolPoolList.removeAll(filterPools); poolIterator = haVpoolPoolList.iterator(); _logger.info("HA Storage Pools to consider: {}", Joiner.on("\t").join(getNativeGuidFromPools(haVpoolPoolList))); } } boolean validProtectionSystems = false; while (poolIterator.hasNext()) { StoragePool storagePool = poolIterator.next(); _logger.info(String.format("Checking storage pool [%s]", storagePool.getLabel())); // If there is a mirror attribute, and the the pool doesn't fit mirror capabilities, // then the pool is not allowed. if (!checkMirrorProtectionValidPool(storagePool, attributeMap)) { _logger.info(String.format("Storage pool [%s] has a mirror attribute, " + "and the the pool doesn't fit mirror capabilities. Disregard pool.", storagePool.getLabel())); continue; } // Get the RP systems for this pool. Set<ProtectionSystem> protectionSystems = ConnectivityUtil.getProtectionSystemsForStoragePool(_objectCache.getDbClient(), storagePool, null, isRPVPlex); // Only pools connected to an RP system can potentially match. if (protectionSystems.isEmpty()) { _logger.info(String.format("Storage pool [%s] not connected to any Protection System. Disregard storage pool.", storagePool.getLabel())); continue; } else { validProtectionSystems = true; } // Scan each RP system and see if the pool is valid, given the target/copy attributes for (ProtectionSystem ps : protectionSystems) { // If there are no virtual arrays associated with this RP system, then the pool // is not allowed if (ps.getVirtualArrays() == null || ps.getVirtualArrays().isEmpty()) { _logger.warn(String.format("Protection System [%s] has no associated virtual arrays. " + "Can not use storage pool [%s]. Please run Protection System discovery.", ps.getLabel(), storagePool.getLabel())); continue; } Map<String, Boolean> validStoragePoolForProtection = new HashMap<String, Boolean>(); // Make sure the target virtual arrays defined are in the list // of virtual arrays for this RP system. If not, move on. for (String targetVarrayId : rpMap.keySet()) { // Assume false to start validStoragePoolForProtection.put(targetVarrayId, Boolean.FALSE); // If the virtual array associated with this storage pool can't be seen by this RP system, // then this pool is not allowed if (!ps.getVirtualArrays().contains(targetVarrayId)) { _logger.info(String.format("Virtual array [%s] of Storage Pool [%s](%s) can't be seen by Protection System [%s](%s). " + "Disregard storage pool.", targetVarrayId, storagePool.getLabel(), storagePool.getId(), ps.getLabel(), ps.getId())); continue; } // If the virtual pool was not specified for this target virtual array, then use the virtual pool // we're in right now. We already know that our target virtual array is in this RP system's // domain, so we'll say yes to this pool. String targetVpoolId = rpMap.get(targetVarrayId); if (NullColumnValueGetter.isNullValue(targetVpoolId)) { _logger.info(String.format("No target virtual pool defined. Using source virtual pool. " + "Storage pool [%s](%s) allowed.", storagePool.getLabel(), storagePool.getId())); validStoragePoolForProtection.put(targetVarrayId, Boolean.TRUE); break; } VirtualPool targetVpool = _objectCache.queryObject(VirtualPool.class, URI.create(targetVpoolId)); List<StoragePool> targetPoolList = VirtualPool.getValidStoragePools(targetVpool, _objectCache.getDbClient(), true); boolean targetVpoolSpecifiesVPlex = VirtualPool.vPoolSpecifiesHighAvailability(targetVpool); // Check the target virtual pool for all valid storage pools. But only consider // storage pools for the target virtual array in question. // // We want to ensure that at least one target storage pool is connected to the same // Protection System as the source/candidate storage pool. If so, we have a match. for (StoragePool tgtPool : targetPoolList) { // Only consider target storage pools for the target varray in question if (tgtPool.getTaggedVirtualArrays() != null && tgtPool.getTaggedVirtualArrays().contains(targetVarrayId)) { _logger.info(String.format("Checking target storage pool [%s](%s)...", tgtPool.getLabel(), tgtPool.getId() )); // Get the RP systems for this target pool Set<ProtectionSystem> targetProtectionSystems = ConnectivityUtil.getProtectionSystemsForStoragePool(_objectCache.getDbClient(), tgtPool, URI.create(targetVarrayId), targetVpoolSpecifiesVPlex); // Check to see if the protection systems line up between the source storage pool // and at least one target storage pool. boolean matchFound = false; for (ProtectionSystem targetProtectionSystem : targetProtectionSystems) { if (targetProtectionSystem != null && targetProtectionSystem.getId().equals(ps.getId())) { _logger.info(String.format("Target storage pool [%s](%s) is connected to same " + "Protection system [%s](%s) as source storage pool [%s](%s). " + "Storage pool allowed.", tgtPool.getLabel(), tgtPool.getId(), ps.getLabel(), ps.getId(), storagePool.getLabel(), storagePool.getId())); validStoragePoolForProtection.put(targetVarrayId, Boolean.TRUE); matchFound = true; break; } } if (matchFound) { // No need to loop through all the target pools, // we found at least one match. break; } } } } // Iterate through the results of valid storage pools, if there are any false entries for this // storage pool, we can not consider it. boolean matchedPool = true; for (Boolean validStoragePool : validStoragePoolForProtection.values()) { if (!validStoragePool) { matchedPool = false; break; } } if (matchedPool) { matchedPools.add(storagePool); } else { _logger.info(String.format("Storage Pool [%s](%s) is not valid for protection.", storagePool.getLabel(), storagePool.getId())); } } } if (!validProtectionSystems) { _logger.warn("No valid protection system could be found for storage pools. " + "Please check data protection systems to ensure they are not inactive or invalid."); } if (haAsRPSource) { if (!matchedPools.isEmpty()) { // If we found at least 1 matched pool using HA as the RP Source, return all pools. matchedPools = allPools; } } if (CollectionUtils.isEmpty(matchedPools)) { errorMessage.append( "No valid protection system could be found for storage pools. " + "Please check data protection systems to ensure they are not inactive or invalid. "); } _logger.info("Pools matching protection attributes ended. " + "Matched pools are: {}", Joiner.on("\t").join(getNativeGuidFromPools(matchedPools))); return matchedPools; } /** * Check if the pool is valid given the Vpool protection attributes * * @param pool * The pool to verify. * * @return true if the pool is valid to use for a protection */ private boolean checkMirrorProtectionValidPool(StoragePool pool, Map<String, Object> attributeMap) { Integer mirrorValue = (Integer) attributeMap.get(Attributes.max_native_continuous_copies.name()); if (mirrorValue != null && mirrorValue != VirtualPool.MAX_DISABLED) { String highAvailabilityType = (String) attributeMap.get(Attributes.high_availability_type.name()); if (highAvailabilityType != null && (VirtualPool.HighAvailabilityType.vplex_local.name().equals(highAvailabilityType) || VirtualPool.HighAvailabilityType.vplex_distributed.name().equals(highAvailabilityType))) { // For VPLEX we don't want to check storage pools copyTypes as it will filter out pools // if the native array do not support mirrors, which doesn't matter for VPLEX // mirrors as we use back-end array to create back-end volumes only. _logger.info("Pool {} is OK, matched with mirror-enabled VPLEX vpool", pool.getLabel()); return true; } StringSet copyTypes = pool.getSupportedCopyTypes(); if (copyTypes != null && copyTypes.contains(StoragePool.CopyTypes.SYNC.name())) { _logger.info("Pool {} is OK, matched with mirror-enabled vpool", pool.getLabel()); return true; } _logger.info("Pool {} was not matched with mirror-enabled vpool", pool.getLabel()); return false; } // Mirror value not set, pool is allowed return true; } @Override protected boolean isAttributeOn(Map<String, Object> attributeMap) { // Check whether Protections is set in CoS or not. // ProtectionsAttrMatcher will be executed only if the protection attribute is set. if (null != attributeMap) { StringMap rpMap = (StringMap) attributeMap.get(Attributes.recoverpoint_map.name()); Integer mirrorValue = (Integer) attributeMap.get(Attributes.max_native_continuous_copies.name()); if ((mirrorValue != null && mirrorValue != VirtualPool.MAX_DISABLED) || (rpMap != null && !rpMap.isEmpty())) { return true; } } return false; } /** * Check to determine if the Storage Pool in question is valid for RP+VPLEX. * * If the Protection System contains the VPLEX in question AND both the Protection System and VPLEX * contain the same varray, then we can consider this pool valid to use for placement. * * @param storagePool The Storage Pool in question, must check to see if RP+VPLEX/MetroPoint * * @return true, if the Storage Pool is valid for RP+VPLEX/MetroPoint */ private boolean validRPVPlexStoragePool(StoragePool storagePool) { // Get the VPLEXs connected to this pool List<String> vplexSystemsForPool = VPlexHighAvailabilityMatcher.getVPlexStorageSystemsForStorageSystem(_objectCache.getDbClient(), storagePool.getStorageDevice(), null); // The Storage Pool needs to have a VPLEX connected to it to be valid, and that's // all we really care about at this point. if (vplexSystemsForPool != null && !vplexSystemsForPool.isEmpty()) { return true; } return false; } }