/* * Copyright (c) 2013 EMC Corporation * All Rights Reserved */ package com.emc.storageos.api.service.impl.placement; import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.util.CollectionUtils; import com.emc.storageos.api.service.authorization.PermissionsHelper; import com.emc.storageos.api.service.impl.resource.ArgValidator; import com.emc.storageos.api.service.impl.resource.BlockService; import com.emc.storageos.db.client.DbClient; import com.emc.storageos.db.client.model.BlockConsistencyGroup; import com.emc.storageos.db.client.model.Project; import com.emc.storageos.db.client.model.StoragePool; import com.emc.storageos.db.client.model.StorageSystem; import com.emc.storageos.db.client.model.StringMap; import com.emc.storageos.db.client.model.StringSet; import com.emc.storageos.db.client.model.VirtualArray; import com.emc.storageos.db.client.model.VirtualPool; import com.emc.storageos.db.client.model.Volume; import com.emc.storageos.db.client.model.util.BlockConsistencyGroupUtils; import com.emc.storageos.db.client.util.NullColumnValueGetter; import com.emc.storageos.svcs.errorhandling.resources.APIException; import com.emc.storageos.svcs.errorhandling.resources.BadRequestException; import com.emc.storageos.util.ConnectivityUtil; import com.emc.storageos.volumecontroller.AttributeMatcher; import com.emc.storageos.volumecontroller.Recommendation; import com.emc.storageos.volumecontroller.VPlexRecommendation; import com.emc.storageos.volumecontroller.impl.utils.AttributeMatcherFramework; import com.emc.storageos.volumecontroller.impl.utils.VirtualPoolCapabilityValuesWrapper; public class VPlexScheduler implements Scheduler { public static final Logger _log = LoggerFactory.getLogger(VPlexScheduler.class); private static final String SCHEDULER_NAME = "vplex"; @Autowired protected PermissionsHelper _permissionsHelper = null; private DbClient _dbClient; private StorageScheduler _blockScheduler; private AttributeMatcherFramework _matcherFramework; private PlacementManager _placementManager; public void setBlockScheduler(StorageScheduler blockScheduler) { _blockScheduler = blockScheduler; } public StorageScheduler getBlockScheduler() { return _blockScheduler; } public void setDbClient(DbClient dbClient) { _dbClient = dbClient; } public void setMatcherFramework(AttributeMatcherFramework matcherFramework) { _matcherFramework = matcherFramework; } public void setPlacementManager(PlacementManager placementManager) { _placementManager = placementManager; } /** * {@inheritDoc} */ @Override public List<Recommendation> getRecommendationsForResources(VirtualArray vArray, Project project, VirtualPool vPool, VirtualPoolCapabilityValuesWrapper capabilities) { return getRecommendationsForResources(vArray, project, vPool, capabilities, new HashMap<VpoolUse, List <Recommendation>>()); } public List<Recommendation> getRecommendationsForResources(VirtualArray vArray, Project project, VirtualPool vPool, VirtualPoolCapabilityValuesWrapper capabilities, Map<VpoolUse, List<Recommendation>> currentRecommendations) { _log.info("Getting recommendations for VPlex volume placement"); // Validate the VirtualPool specifies VPlex high availability, which // currently is the only supported means for creating high // availability volumes. if (!VirtualPool.HighAvailabilityType.vplex_distributed.name().equals(vPool.getHighAvailability()) && !VirtualPool.HighAvailabilityType.vplex_local.name().equals(vPool.getHighAvailability())) { throw APIException.badRequests.invalidHighAvailability(vPool.getHighAvailability()); } _log.info("VirtualPool has high availability {}", vPool.getHighAvailability()); Set<URI> vplexSystemsForPlacement = getVPlexSystemsForPlacement(vArray, vPool, capabilities); // Determine if the volume creation request is for HA volumes. boolean isHAVolumeRequest = VirtualPool.HighAvailabilityType.vplex_distributed.name() .equals(vPool.getHighAvailability()); // Get and validate the high availability VirtualArray and VirtualPool. // Note that the HA VirtualPool is optional. When not specified, the // high availability VirtualPool is the passed VirtualPool is use. VirtualPool haVPool = vPool; VirtualArray haVArray = null; StringMap haVaVpMap = vPool.getHaVarrayVpoolMap(); if ((isHAVolumeRequest) && (haVaVpMap != null)) { _log.info("Is HA request and with an HA VirtualArray VirtualPool map"); Iterator<String> vaIter = haVaVpMap.keySet().iterator(); while (vaIter.hasNext()) { String haVaId = vaIter.next(); _log.info("HA VirtualArray is {}", haVaId); if (!haVaId.equals(NullColumnValueGetter.getNullURI().toString())) { _log.info("HA VirtualArray is not a null URI"); haVArray = getVirtualArrayForVolumeCreateRequest(project, URI.create(haVaId)); if (vArray.getId().toString().equals(haVArray.getId().toString())) { throw APIException.badRequests.sameVirtualArrayAndHighAvailabilityArray(); } } // Now get the VirtualPool. String haVpId = haVaVpMap.get(haVaId); _log.info("HA VirtualPool is {}", haVpId); if (!haVpId.equals(NullColumnValueGetter.getNullURI().toString())) { _log.info("HA VirtualPool is not a null URI"); haVPool = BlockService.getVirtualPoolForRequest(project, URI.create(haVpId), _dbClient, _permissionsHelper); } } } // Get the volume placement based on passed parameters. _log.info("VirtualPool: {}, HA VirtualPool: {}", vPool.getId().toString(), haVPool.getId() .toString()); List<Recommendation> recommendations = scheduleStorage( vArray, vplexSystemsForPlacement, null, vPool, isHAVolumeRequest, haVArray, haVPool, capabilities, project, VpoolUse.ROOT, currentRecommendations); return recommendations; } /** * Gets the VPlex storage system(s) associated with the specified consistency * group. * * @param vArray The virtual array specified for the new volume * @param vPool The virtual pool specified for the new volume * @param capabilities The virtual pool capabilities. * @return The VPlex systems for placement */ protected Set<URI> getVPlexSystemsForPlacement(VirtualArray vArray, VirtualPool vPool, VirtualPoolCapabilityValuesWrapper capabilities) { // If a consistency group is specified, then it is created or not // created. It is not in the process of being created based on checks // in the block service. If it is not created, that is fine. However, // if it is created, then it must be for a VPlex storage system. // Further, it must be in the virtual array specified for the volume // create request. In this way, when the volumes are placed, they // will be placed on the VPlex system for the consistency group. BlockConsistencyGroup consistencyGroup = null; Set<URI> vplexSystemsForPlacement = null; URI cgURI = capabilities.getBlockConsistencyGroup(); if (cgURI != null) { consistencyGroup = _permissionsHelper.getObjectById(cgURI, BlockConsistencyGroup.class); } if ((consistencyGroup != null) && (consistencyGroup.created()) && consistencyGroup.getTypes().contains(BlockConsistencyGroup.Types.VPLEX.name())) { // Verify the storage system. List<StorageSystem> vplexStorageSystems = BlockConsistencyGroupUtils.getVPlexStorageSystems(consistencyGroup, _dbClient); if (vplexStorageSystems.isEmpty()) { throw APIException.badRequests.invalidParameterConsistencyGroupNotForVplexStorageSystem(consistencyGroup.getId()); } // The volumes in a VPLEX consistency group must // have the same high availability type. List<Volume> cgVolumes = BlockConsistencyGroupUtils .getActiveVplexVolumesInCG(consistencyGroup, _dbClient, null); Iterator<Volume> cgVolumesIter = cgVolumes.iterator(); if (cgVolumesIter.hasNext()) { Volume cgVolume = cgVolumesIter.next(); VirtualPool cgVolumeVPool = _permissionsHelper.getObjectById( cgVolume.getVirtualPool(), VirtualPool.class); if (!vPool.getHighAvailability().equals( cgVolumeVPool.getHighAvailability())) { throw APIException.badRequests .invalidParameterConsistencyGroupVolumeHasIncorrectHighAvailability( consistencyGroup.getId(), cgVolumeVPool.getHighAvailability()); } } // Verify the virtual array. URI cgVaURI = consistencyGroup.getVirtualArray(); if (!vArray.getId().toString().equals(cgVaURI.toString())) { throw APIException.badRequests.invalidParameterConsistencyGroupVirtualArrayMismatch(consistencyGroup.getId()); } // To satisfy the request, placement must be restricted to the // storage systems connected to the VPlex system for the passed // consistency group. vplexSystemsForPlacement = new HashSet<URI>(); for (StorageSystem vplexSystem : vplexStorageSystems) { vplexSystemsForPlacement.add(vplexSystem.getId()); } } return vplexSystemsForPlacement; } /** * Get recommendations for resource placement based on the passed * parameters. * * @param vArray The VirtualArray in which the resources were requested. * @param project The source volume project * @param sourceVPool The source volume virtual pool * @param mirrorVPool The virtual pool to be used for mirror * @param capabilities The VirtualPool capabilities. * @param vplexStorageSystemURI The URI of the VPLEX system to which resources should be connected * @param excludeStorageSystem The URI of the storage system that needs to be excluded * @param cluster The VPLEX cluster to which resources should be connected */ public List getRecommendationsForMirrors(VirtualArray vArray, Project project, VirtualPool sourceVPool, VirtualPool mirrorVPool, VirtualPoolCapabilityValuesWrapper capabilities, URI vplexStorageSystemURI, URI excludeStorageSystem, String cluster) { _log.info("Getting recommendations for VPlex volume placement"); _log.info("Source VirtualPool has high availability {}", sourceVPool.getHighAvailability()); List<Recommendation> recommendations = scheduleStorageForMirror( vArray, sourceVPool, mirrorVPool, capabilities, vplexStorageSystemURI, excludeStorageSystem, cluster); return recommendations; } /** * Get recommendations for resource placement based on the passed * parameters. * * @param srcVarray The VirtualArray in which the resources were requested. * @param srcVpool Source volume virtual pool * @param mirrorVpool The virtual pool to be used for mirror * @param srcVpool The VirtualPool requested for the source resources. * @param capabilities The VirtualPool capabilities. * @param vplexStorageSystemURI The URI of the VPLEX system to which resources should be connected * @param excludeStorageSystem The URI of the storage system that needs to be excluded * @param cluster The VPLEX cluster to which resources should be connected * * @return A list of VPlexRecommendation instances specifying the * recommended resource placement resources. */ public List<Recommendation> scheduleStorageForMirror(VirtualArray srcVarray, VirtualPool srcVpool, VirtualPool mirrorVpool, VirtualPoolCapabilityValuesWrapper capabilities, URI vplexStorageSystemURI, URI excludeStorageSystem, String cluster) { _log.info("Executing VPlex Mirror placement strategy"); // Initialize the list of recommendations. List<Recommendation> recommendations = new ArrayList<Recommendation>(); // Get all storage pools that match the passed VirtualPool params, // and virtual array. In addition, the pool must // have enough capacity to hold at least one resource of the // requested size. _log.info("Getting placement recommendations for srcVarray {}", srcVarray.getId()); Map<String, Object> attributeMap = new HashMap<String, Object>(); List<StoragePool> allMatchingPools = getMatchingPools(srcVarray, null, excludeStorageSystem, mirrorVpool, capabilities, attributeMap); _log.info("Found {} Matching pools for VirtualArray for the Mirror", allMatchingPools.size()); // If the attribute matcher framework returns an error, then throw that error. // Otherwise, even when there are no pools, let the code return the empty list // of recommendations which is handled by the caller. This code was added for // COP-17666, but resulted in a regression captured by COP-25216. Therefore, // we now only throw an exception if there is an actual error set by a matcher. // If there is no error, returning the empty list will result in the previous // message being displayed. StringBuffer errorMessage = (StringBuffer) attributeMap.get(AttributeMatcher.ERROR_MESSAGE); if ((CollectionUtils.isEmpty(allMatchingPools)) && (errorMessage != null) && (errorMessage.length() != 0)) { throw APIException.badRequests.noStoragePools(srcVarray.getLabel(), srcVpool.getLabel(), errorMessage.toString()); } // Due to VirtualPool attribute matching, we should only get storage // pools on storage systems that are connected to a VPlex // storage system. We find these storage pools and associate // them to the VPlex storage systems to which their storage // systems are connected. Map<String, List<StoragePool>> vplexPoolMapForSrcVarray = sortPoolsByVPlexStorageSystem( allMatchingPools, srcVarray.getId().toString(), cluster); // If only specified VPlex from source volume is desired, filter the vplexPoolMapForSrcVarray // to only use pools from the vplexStorageSystemURI. Iterator<Entry<String, List<StoragePool>>> it = vplexPoolMapForSrcVarray.entrySet().iterator(); if (vplexStorageSystemURI != null) { while (it.hasNext()) { Entry<String, List<StoragePool>> entry = it.next(); String vplexKey = entry.getKey(); URI vplexURI = null; try { vplexURI = URI.create(vplexKey); } catch (IllegalArgumentException ex) { _log.error("Bad VPLEX URI: " + vplexURI); continue; } if (false == vplexStorageSystemURI.equals(vplexURI)) { it.remove(); } } } if (vplexPoolMapForSrcVarray.isEmpty()) { _log.info("No matching pools on storage systems connected to a VPlex"); // There are no matching pools in the source virtual array // on storage systems connected to a VPlex storage system // or there are, but a specific VPlex system was requested // and there are none for that VPlex system. return recommendations; } // The list of potential VPlex storage systems. Set<String> vplexStorageSystemIds = vplexPoolMapForSrcVarray.keySet(); vplexStorageSystemIds = vplexPoolMapForSrcVarray.keySet(); _log.info("{} VPlex storage systems have matching pools", vplexStorageSystemIds.size()); Iterator<String> vplexSystemIdsIter = vplexStorageSystemIds.iterator(); while ((vplexSystemIdsIter.hasNext()) && (recommendations.isEmpty())) { String vplexStorageSystemId = vplexSystemIdsIter.next(); _log.info("Check matching pools for VPlex {}", vplexStorageSystemId); // Check if the resource can be placed on the matching // pools for this VPlex storage system. if (VirtualPool.ProvisioningType.Thin.toString() .equalsIgnoreCase(mirrorVpool.getSupportedProvisioningType())) { capabilities.put(VirtualPoolCapabilityValuesWrapper.THIN_PROVISIONING, Boolean.TRUE); } List<Recommendation> recommendationsForMirrorVarray = _blockScheduler.getRecommendationsForPools( srcVarray.getId().toString(), vplexPoolMapForSrcVarray.get(vplexStorageSystemId), capabilities); if (recommendationsForMirrorVarray.isEmpty()) { _log.info("Matching pools insufficient for placement"); // For this VPlex, the pools for the source varray are // not sufficient, so we need to try another VPlex. continue; } _log.info("Matching pools sufficient for placement"); recommendations.addAll(createVPlexRecommendations(vplexStorageSystemId, srcVarray, srcVpool, recommendationsForMirrorVarray)); continue; } return recommendations; } /** * Get recommendations for resource placement based on the passed * parameters. * * @param srcVarray The VirtualArray in which the resources were requested. * @param requestedVPlexSystems The URIs of the VPlex systems to which * placement should be limited, or null when it doesn't matter. * @param srcStorageSystem The URI of a specific backend storage system to * which the source resource should be limited, or null when it * doesn't matter. * @param srcVpool The VirtualPool requested for the source resources. * @param isHARequest Whether or not HA recommendations are also required. * @param requestedHaVarray The desired HA varray or null when not * specified. * @param haVpool The VirtualPool for the HA resources. * @param capabilities The VirtualPool capabilities. * * @return A list of VPlexRecommendation instances specifying the * recommended resource placement resources. */ public List<Recommendation> scheduleStorage(VirtualArray srcVarray, Set<URI> requestedVPlexSystems, URI srcStorageSystem, VirtualPool srcVpool, boolean isHARequest, VirtualArray requestedHaVarray, VirtualPool haVpool, VirtualPoolCapabilityValuesWrapper capabilities, Project project, VpoolUse vpoolUse, Map<VpoolUse, List<Recommendation>> currentRecommendations) { _log.info("Executing VPlex high availability placement strategy"); if (!isHARequest) { return scheduleStorageForLocalVPLEXVolume(srcVarray, requestedVPlexSystems, srcStorageSystem, srcVpool, capabilities, project, vpoolUse, currentRecommendations); } else { return scheduleStorageForDistributedVPLEXVolume(srcVarray, requestedVPlexSystems, srcStorageSystem, srcVpool, requestedHaVarray, haVpool, capabilities, project, vpoolUse, currentRecommendations); } } /** * Get recommendations for resource placement for local VPLEX volumes. * * @param varray The virtual array in which the resources were requested. * @param requestedVPlexSystems The URIs of the VPlex systems to which * placement should be limited, or null when it doesn't matter. * @param storageSystem The URI of a specific backend storage system to * which the source resource should be limited, or null when it * doesn't matter. * @param vpool The virtual pool requested for the source resources. * @param capabilities The virtual pool capabilities. * * @return A list of VPlexRecommendation instances specifying the * recommended resource placement. */ private List<Recommendation> scheduleStorageForLocalVPLEXVolume( VirtualArray varray, Set<URI> requestedVPlexSystems, URI storageSystem, VirtualPool vpool, VirtualPoolCapabilityValuesWrapper capabilities, Project project, VpoolUse vPoolUse, Map<VpoolUse, List<Recommendation>> currentRecommendations) { _log.info("Executing VPlex high availability placement strategy for Local VPLEX volumes"); // If there is a specified storageSystem, honor that by putting it in the capabilities. // The StorageScheduler.getMatchingPools() routine will honor this request by invocation // of the StorageSystemsMatcher. if (storageSystem != null) { StorageSystem sourceStorageSystem = _dbClient.queryObject(StorageSystem.class, storageSystem); capabilities.put(VirtualPoolCapabilityValuesWrapper.SOURCE_STORAGE_SYSTEM, sourceStorageSystem); } // Initialize the list of recommendations that will be returned List<Recommendation> recommendations = new ArrayList<Recommendation>(); // Call the lower level scheduler to get its baseRecommendations. Scheduler nextScheduler = _placementManager.getNextScheduler( SCHEDULER_NAME, vpool, vPoolUse); _log.info(String.format("Calling next scheduler: %s", nextScheduler.getClass().getSimpleName())); List<Recommendation> baseRecommendations = nextScheduler.getRecommendationsForVpool( varray, project, vpool, vPoolUse, capabilities, currentRecommendations); _log.info(String.format("Received %d recommendations from %s", baseRecommendations.size(), nextScheduler.getClass().getSimpleName())); List<StoragePool> allMatchingPools = _placementManager .getStoragePoolsFromRecommendations(baseRecommendations); _log.info("Found {} matching pools for varray", allMatchingPools.size()); // Sort the matching pools by VPLEX system. Map<String, List<StoragePool>> vplexPoolMapForSrcVarray = getVPlexConnectedMatchingPools(varray, requestedVPlexSystems, capabilities, allMatchingPools); if (vplexPoolMapForSrcVarray.isEmpty()) { _log.info("No matching pools on storage systems connected to a VPlex"); // There are no matching pools in the source virtual array // on storage systems connected to a VPlex storage system // or there are, but a specific VPlex system was requested // and there are none for that VPlex system. return recommendations; } // See if any one VPlex system can utilize all the specified pools. for (Map.Entry<String, List<StoragePool>> entry : vplexPoolMapForSrcVarray.entrySet()) { if (entry.getValue().containsAll(allMatchingPools)) { _log.info(String.format("Generating local recommendations for VPLEX %s", entry.getKey())); recommendations.addAll(createVPlexRecommendations(baseRecommendations, entry.getKey(), varray, vpool)); break; } } if (recommendations.isEmpty()) { _log.info("No single VPLEX could front the entire set of recommendations"); } _placementManager.logRecommendations("VPLEX Local", recommendations); return recommendations; } /** * Get recommendations for resource placement for distributed VLPEX volumes. * * @param srcVarray The virtual array in which the resources were requested. * @param requestedVPlexSystems The URIs of the VPlex systems to which * placement should be limited, or null when it doesn't matter. * @param srcStorageSystem The URI of a specific backend storage system to * which the source resource should be limited, or null when it * doesn't matter. * @param srcVpool The virtual pool requested for the source resources. * @param haVarray The desired HA varray. * @param haVpool The virtual pool for the HA resources. * @param capabilities The virtual pool capabilities. * * @return A list of VPlexRecommendation instances specifying the * recommended resource placement. */ private List<Recommendation> scheduleStorageForDistributedVPLEXVolume( VirtualArray srcVarray, Set<URI> requestedVPlexSystems, URI srcStorageSystem, VirtualPool srcVpool, VirtualArray haVarray, VirtualPool haVpool, VirtualPoolCapabilityValuesWrapper capabilities, Project project, VpoolUse srcVpoolUse, Map<VpoolUse, List<Recommendation>> currentRecommendations) { _log.info("Executing VPlex high availability placement strategy for Distributed VPLEX Volumes."); // Initialize the list of recommendations. List<Recommendation> recommendations = new ArrayList<Recommendation>(); // If there is a specified srcStorageSystem, honor that by putting it in the capabilities. // The StorageScheduler.getMatchingPools() routine will honor this request by invocation // of the StorageSystemsMatcher. if (srcStorageSystem != null) { StorageSystem sourceStorageSystem = _dbClient.queryObject(StorageSystem.class, srcStorageSystem); capabilities.put(VirtualPoolCapabilityValuesWrapper.SOURCE_STORAGE_SYSTEM, sourceStorageSystem); } // Call the lower level scheduler to get its recommendations. Scheduler nextScheduler = _placementManager.getNextScheduler( SCHEDULER_NAME, srcVpool, srcVpoolUse); _log.info(String.format("Calling next scheduler: %s", nextScheduler.getClass().getSimpleName())); List<Recommendation> baseRecommendations = nextScheduler.getRecommendationsForVpool( srcVarray, project, srcVpool, srcVpoolUse, capabilities, currentRecommendations); _log.info(String.format("Received %d recommendations from %s", baseRecommendations.size(), nextScheduler.getClass().getSimpleName())); if (baseRecommendations.isEmpty()) { throw BadRequestException.badRequests.noVplexLocalRecommendationFromSubScheduler( nextScheduler.getClass().getSimpleName(), srcVpool.getLabel(), srcVarray.getLabel()); } _log.info(String.format("Received %d recommendations from %s", baseRecommendations.size(), nextScheduler.getClass().getSimpleName())); List<StoragePool> allMatchingPoolsForSrcVarray = _placementManager .getStoragePoolsFromRecommendations(baseRecommendations); _log.info("Found {} matching pools for source varray", allMatchingPoolsForSrcVarray.size()); URI cgURI = capabilities.getBlockConsistencyGroup(); BlockConsistencyGroup cg = (cgURI == null ? null : _dbClient.queryObject(BlockConsistencyGroup.class, cgURI)); // Sort the matching pools for the source varray by VPLEX system. Map<String, List<StoragePool>> vplexPoolMapForSrcVarray = getVPlexConnectedMatchingPools(srcVarray, requestedVPlexSystems, capabilities, allMatchingPoolsForSrcVarray); if (vplexPoolMapForSrcVarray.isEmpty()) { _log.info("No matching pools on storage systems connected to a VPlex"); // There are no matching pools in the source virtual array // on storage systems connected to a VPlex storage system // or there are, but a specific VPlex system was requested // and there are none for that VPlex system. return recommendations; } // Get all storage pools that match the passed HA VirtualPool params, // and HA virtual array. In addition, the pool must have enough // capacity to hold at least one resource of the requested size. _log.info("Getting all matching pools for HA varray {}", haVarray.getId()); URI haStorageSystem = null; VirtualPoolCapabilityValuesWrapper haCapabilities = new VirtualPoolCapabilityValuesWrapper(capabilities); // Don't look for SRDF in the HA side. haCapabilities.put(VirtualPoolCapabilityValuesWrapper.PERSONALITY, null); // We don't require that the HA side have the same storage controller. haCapabilities.put(VirtualPoolCapabilityValuesWrapper.BLOCK_CONSISTENCY_GROUP, null); Map<String, Object> attributeMap = new HashMap<String, Object>(); List<StoragePool> allMatchingPoolsForHaVarray = getMatchingPools( haVarray, haStorageSystem, haVpool, haCapabilities, attributeMap); if (allMatchingPoolsForHaVarray.isEmpty()) { StringBuffer errorMessage = new StringBuffer(); if (attributeMap.get(AttributeMatcher.ERROR_MESSAGE) != null) { errorMessage = (StringBuffer) attributeMap.get(AttributeMatcher.ERROR_MESSAGE); } throw BadRequestException.badRequests.noMatchingHighAvailabilityStoragePools(haVpool.getLabel(), haVarray.getLabel(), errorMessage.toString()); } _log.info("Found {} matching pools for HA varray", allMatchingPoolsForHaVarray.size()); // Sort the matching pools for the HA varray by VPLEX system. Map<String, List<StoragePool>> vplexPoolMapForHaVarray = sortPoolsByVPlexStorageSystem( allMatchingPoolsForHaVarray, haVarray.getId().toString()); if (vplexPoolMapForHaVarray.isEmpty()) { _log.info("No matching pools on storage systems connected to a VPlex"); // There are no matching pools in the HA virtual array // on storage systems connected to a VPlex storage system. return recommendations; } // Get the list of potential VPlex storage systems for the source // virtual array. Set<String> vplexStorageSystemIds = vplexPoolMapForSrcVarray.keySet(); _log.info("{} VPlex storage systems have matching pools", vplexStorageSystemIds.size()); // Get the possible high availability varrays for each of these // potential VPlex storage system. Map<String, List<String>> vplexHaVarrayMap = ConnectivityUtil.getVPlexVarrays( _dbClient, vplexStorageSystemIds, srcVarray.getId()); // Loop over the potential VPlex storage systems, and attempt // to place the resources. Iterator<String> vplexSystemIdsIter = vplexStorageSystemIds.iterator(); while ((vplexSystemIdsIter.hasNext()) && (recommendations.isEmpty())) { String vplexStorageSystemId = vplexSystemIdsIter.next(); _log.info("Attempting placement on VPlex {}", vplexStorageSystemId); // Check if this VPLEX can satisfy the requested HA varray. List<String> vplexHaVarrays = vplexHaVarrayMap.get(vplexStorageSystemId); if (!vplexHaVarrays.contains(haVarray.getId().toString())) { // It cannot, try the next VPLEX. continue; } // Check if there are HA storage pools for this VPLEX. if (!vplexPoolMapForHaVarray.containsKey(vplexStorageSystemId)) { // There are no HA pools for this VPLEX, try the next. continue; } // Check if the resource can be placed on the matching // pools for this VPlex storage system in the source varray. List<Recommendation> recommendationsForSrcVarray = new ArrayList<Recommendation>(); recommendationsForSrcVarray.addAll( createVPlexRecommendations(baseRecommendations, vplexStorageSystemId, srcVarray, srcVpool)); if (recommendationsForSrcVarray.isEmpty()) { _log.info("Matching pools for source varray insufficient for placement"); // For this VPlex, the pools for the source varray are // not sufficient, so we need to try another VPlex. continue; } // Get the storage systems specified by these recommendations. // We don't want to use these same storage systems on the HA // side when the same system is available to both, else you // could create a distributed volume with both backend volumes // on the same physical array. Set<URI> recommendedSrcSystems = new HashSet<URI>(); for (Recommendation recommendation : recommendationsForSrcVarray) { recommendedSrcSystems.add(recommendation.getSourceStorageSystem()); } // Remove any storage pools on these systems from the list of // matching pools for the HA varray for this VPLEX system. boolean haPoolsLimitedBySrcSelections = false; List<StoragePool> vplexPoolsForHaVarray = new ArrayList<StoragePool>(vplexPoolMapForHaVarray.get(vplexStorageSystemId)); Iterator<StoragePool> vplexPoolsForHaVarrayIter = vplexPoolsForHaVarray.iterator(); while (vplexPoolsForHaVarrayIter.hasNext()) { StoragePool haPool = vplexPoolsForHaVarrayIter.next(); URI poolSystem = haPool.getStorageDevice(); if (recommendedSrcSystems.contains(poolSystem)) { _log.info("Removing pool {} on system {} from consideration for HA placement", haPool.getId(), poolSystem); vplexPoolsForHaVarrayIter.remove(); haPoolsLimitedBySrcSelections = true; } } // Now check if the resource can be placed on the matching // pools for this VPlex storage system in the HA varray. List<Recommendation> recommendationsForHaVarray = getRecommendationsForPools( haVarray.getId().toString(), haVpool, vplexPoolsForHaVarray, capabilities); if (recommendationsForHaVarray.isEmpty()) { _log.info("Matching pools for HA varray insufficient for placement"); if (haPoolsLimitedBySrcSelections) { // If we limited the pools on the HA side and failed to place, // then let's reverse and use all pools on the HA side and limit // the source side. This is certainly not perfect, but at least // will try and use the pools on both sides before giving up. recommendationsForHaVarray = getRecommendationsForPools( haVarray.getId().toString(), haVpool, vplexPoolMapForHaVarray.get(vplexStorageSystemId), capabilities); if (recommendationsForHaVarray.isEmpty()) { // Still can't place them on the HA side. _log.info("Matching pools for HA varray still insufficient for placement"); continue; } else { // Remove the systems from the source side and see // if the source side can still be placed when limited. _log.info("Matching pools for HA varray now sufficient for placement"); } } else { // For this VPlex, the pools for the source varray are // not sufficient, so we need to try another VPlex. continue; } } // We have recommendations for pools in both the source and HA varrays. recommendations.addAll(recommendationsForSrcVarray); recommendations.addAll(createVPlexRecommendations( vplexStorageSystemId, haVarray, haVpool, recommendationsForHaVarray)); _log.info("Done trying to place resources for VPlex."); break; } _placementManager.logRecommendations("VPLEX Distributed", recommendations); return recommendations; } /** * Uses the block scheduler to get the placement recommendations. * * @param varrayId The virtual array id. * @param vpool A reference to the virtual pool * @param candidatePools The list of candidate pools. * @param capabilities The virtual pool capabilities. * * @return The list of placement recommendations. */ private List<Recommendation> getRecommendationsForPools(String varrayId, VirtualPool vpool, List<StoragePool> candidatePools, VirtualPoolCapabilityValuesWrapper capabilities) { VirtualPoolCapabilityValuesWrapper updatedCapabilities = capabilities; if (VirtualPool.ProvisioningType.Thin.toString().equalsIgnoreCase( vpool.getSupportedProvisioningType())) { updatedCapabilities = new VirtualPoolCapabilityValuesWrapper(capabilities); updatedCapabilities.put(VirtualPoolCapabilityValuesWrapper.THIN_PROVISIONING, Boolean.TRUE); } return _blockScheduler.getRecommendationsForPools(varrayId, candidatePools, updatedCapabilities); } /** * Gets the storage pools that are VPlex connected. * * @param srcVarray The source virtual array * @param requestedVPlexSystems VPlex storage systems associated with the specified consistency * group * @param capabilities The virtual pool capabilities * @param allMatchingPools The list of matching pools that needs to be refined * @return Map of VPlex storage systems to connected storage pools */ protected Map<String, List<StoragePool>> getVPlexConnectedMatchingPools(VirtualArray srcVarray, Set<URI> requestedVPlexSystems, VirtualPoolCapabilityValuesWrapper capabilities, List<StoragePool> allMatchingPools) { _log.info("Found {} Matching pools for VirtualArray", allMatchingPools.size()); // Due to VirtualPool attribute matching, we should only get storage // pools on storage systems that are connected to a VPlex // storage system. We find these storage pools and associate // them to the VPlex storage systems to which their storage // systems are connected. Map<String, List<StoragePool>> vplexPoolMapForSrcVarray = sortPoolsByVPlexStorageSystem( allMatchingPools, srcVarray.getId().toString()); // If only specified VPlexes are desired, filter the vplexPoolMapForSrcNH // to only use pools from the requestedVPlexSystems. if (requestedVPlexSystems != null && requestedVPlexSystems.isEmpty() == false) { Iterator<Map.Entry<String, List<StoragePool>>> it = vplexPoolMapForSrcVarray.entrySet().iterator(); while (it.hasNext()) { String vplexKey = it.next().getKey(); URI vplexURI = null; try { vplexURI = new URI(vplexKey); } catch (URISyntaxException ex) { _log.error("Bad VPLEX URI: " + vplexURI); continue; } if (false == requestedVPlexSystems.contains(vplexURI)) { it.remove(); } } } return vplexPoolMapForSrcVarray; } /** * Gets and verifies that the VirtualArray passed in the request is * accessible to the tenant. * * @param project A reference to the project. * @param neighborhoodURI The URI for the VirtualArray. * * @return A reference to the VirtualArray. */ private VirtualArray getVirtualArrayForVolumeCreateRequest(Project project, URI neighborhoodURI) { VirtualArray neighborhood = _dbClient.queryObject(VirtualArray.class, neighborhoodURI); ArgValidator.checkEntity(neighborhood, neighborhoodURI, false); _permissionsHelper.checkTenantHasAccessToVirtualArray(project.getTenantOrg().getURI(), neighborhood); return neighborhood; } /** * Schedule Storage for a VPLEX import operation where we are creating the * HA volume. * * @param srcNH Source Neighborhood * @param vplexs Set<URI> Set of Vplex System URIs that can be used * @param requestedHaNH Optional requested HA Neighborhood. Can be null. * @param cos CoS to be used for new volumes * @param capabilities CoS capabilities to be used for new volume * @return List<Recommendation> */ public List<Recommendation> scheduleStorageForImport(VirtualArray srcNH, Set<URI> vplexs, VirtualArray requestedHaNH, VirtualPool cos, VirtualPoolCapabilityValuesWrapper capabilities) { Set<String> vplexSystemIds = new HashSet<String>(); for (URI vplexURI : vplexs) { vplexSystemIds.add(vplexURI.toString()); } List<Recommendation> recommendations = new ArrayList<Recommendation>(); // For an HA request, get the possible high availability neighborhoods // for each potential VPlex storage system. Map<String, List<String>> vplexHaNHMap = ConnectivityUtil.getVPlexVarrays( _dbClient, vplexSystemIds, srcNH.getId()); for (URI vplexSystemURI : vplexs) { StorageSystem vplexSystem = _dbClient.queryObject(StorageSystem.class, vplexSystemURI); // See if there is an HA varray // for the VPlex that also contains pools suitable to place // the resources. List<String> vplexHaNHIds = vplexHaNHMap.get(vplexSystem.getId().toString()); if (vplexHaNHIds == null) { continue; } _log.info("Found {} HA varrays", vplexHaNHIds.size()); for (String vplexHaNHId : vplexHaNHIds) { _log.info("Check HA varray {}", vplexHaNHId); // If a specific HA varray was specified and this // varray is not it, then skip the varray. if ((requestedHaNH != null) && (!vplexHaNHId.equals(requestedHaNH.getId().toString()))) { _log.info("Not the requested HA varray, skip"); continue; } // Get all storage pools that match the passed CoS params, // protocols, and this HA varray. In addition, the // pool must have enough capacity to hold at least one // resource of the requested size. VirtualArray vplexHaNH = _dbClient.queryObject(VirtualArray.class, URI.create(vplexHaNHId)); Map<String, Object> attributeMap = new HashMap<String, Object>(); List<StoragePool> allMatchingPools = getMatchingPools(vplexHaNH, null, cos, capabilities, attributeMap); _log.info("Found {} matching pools for HA varray", allMatchingPools.size()); // Now from the list of candidate pools, we only want pools // on storage systems that are connected to the VPlex // storage system. We find these storage pools and associate // them to the VPlex storage systems to which their storage // system is connected. Map<String, List<StoragePool>> vplexPoolMapForHaNH = sortPoolsByVPlexStorageSystem(allMatchingPools, vplexHaNHId); // If the HA varray has candidate pools for this // VPlex, see if the candidate pools in this HA // varray are sufficient to place the resources. List<Recommendation> recommendationsForHaNH = new ArrayList<Recommendation>(); if (vplexPoolMapForHaNH.containsKey(vplexSystem.getId().toString())) { _log.info("Found matching pools in HA NH for VPlex {}", vplexSystem.getId()); recommendationsForHaNH = _blockScheduler.getRecommendationsForPools(vplexHaNH.getId().toString(), vplexPoolMapForHaNH.get(vplexSystem.getId().toString()), capabilities); } else { _log.info("No matching pools in HA NH for VPlex {}", vplexSystem.getId()); } recommendations.addAll(createVPlexRecommendations(vplexSystem.getId().toString(), vplexHaNH, cos, recommendationsForHaNH)); } } return recommendations; } /** * Gets all storage pools in the passed varray, satisfying the passed * CoS and capable of holding a resource of the requested size. Additionally * filters the storage pools to those on the storage system with the * passed URI, when the passed storage system is not null. * * @param virtualArray The desired varray. * @param storageSystemURI The desired storage system, or null. * @param virtualPool The required CoS. * @param attributeMap * @return A list of storage pools. */ protected List<StoragePool> getMatchingPools(VirtualArray virtualArray, URI storageSystemURI, VirtualPool virtualPool, VirtualPoolCapabilityValuesWrapper capabilities, Map<String, Object> attributeMap) { return getMatchingPools(virtualArray, storageSystemURI, null, virtualPool, capabilities, attributeMap); } /** * Gets all storage pools in the passed varray, satisfying the passed * CoS and capable of holding a resource of the requested size. Additionally * filters the storage pools to those on the storage system with the * passed URI, when the passed storage system is not null. * * @param varray The desired virtual array. * @param storageSystemURI The desired storage system or null. * @param excludeStorageSystemURI The storage system that should be excluded or null. * @param vpool The required virtual pool. * @param capabilities The virtual pool capabilities. * @param attributeMap * @return A list of storage pools. */ protected List<StoragePool> getMatchingPools(VirtualArray varray, URI storageSystemURI, URI excludeStorageSystemURI, VirtualPool vpool, VirtualPoolCapabilityValuesWrapper capabilities, Map<String, Object> attributeMap) { // If a specific storage system is specified, then enable the storage system matcher // to filter for pools only on that system. if (storageSystemURI != null) { StorageSystem storageSystem = _dbClient.queryObject(StorageSystem.class, storageSystemURI); capabilities.put(VirtualPoolCapabilityValuesWrapper.SOURCE_STORAGE_SYSTEM, storageSystem); } else if (excludeStorageSystemURI != null) { // Otherwise, if an excluded system is provided, enable the exclude storage // system matcher to filter out pools on the excluded system. Note that there // should never be an excluded system when a specific storage system to match // is passed. StorageSystem excludeStorageSystem = _dbClient.queryObject(StorageSystem.class, excludeStorageSystemURI); capabilities.put(VirtualPoolCapabilityValuesWrapper.EXCLUDED_STORAGE_SYSTEM, excludeStorageSystem); } // Now call the block scheduler to get the matching storage pools. List<StoragePool> storagePools = _blockScheduler.getMatchingPools(varray, vpool, capabilities, attributeMap); return storagePools; } /** * Determines if each pool is on storage system that is connected to a VPlex * storage system that has connectivity to the passed varray. If so, the * pool is mapped to that VPlex storage system. * * @param storagePools A list of storage pools. * @param varrayId The varray to which the VPLEX must have connectivity. * * @return A map of storage pools keyed by the VPlex storage system to which * they have connectivity. */ protected Map<String, List<StoragePool>> sortPoolsByVPlexStorageSystem( List<StoragePool> storagePools, String varrayId) { return sortPoolsByVPlexStorageSystem(storagePools, varrayId, null); } /** * Determines if each pool is on storage system that is connected to a VPlex * storage system that has connectivity to the passed varray. If so, the * pool is mapped to that VPlex storage system. * * @param storagePools A list of storage pools. * @param varrayId The varray to which the VPLEX must have connectivity. * @param cluster The VPLEX cluster to which storage system should be connected. * * @return A map of storage pools keyed by the VPlex storage system to which * they have connectivity. */ protected Map<String, List<StoragePool>> sortPoolsByVPlexStorageSystem( List<StoragePool> storagePools, String varrayId, String cluster) { Map<String, List<StoragePool>> vplexPoolMap = new HashMap<String, List<StoragePool>>(); // group the pools by system Map<URI, List<StoragePool>> poolsBySystem = getPoolsBySystem(storagePools); for (URI systemUri : poolsBySystem.keySet()) { // for each system, find the associated vplexes in the requested varray Set<URI> vplexSystemURIs = ConnectivityUtil .getVPlexSystemsAssociatedWithArray(_dbClient, systemUri, new HashSet<String>(Arrays.asList(varrayId)), cluster); for (URI vplexUri : vplexSystemURIs) { StorageSystem vplexSystem = _dbClient.queryObject(StorageSystem.class, vplexUri); String vplexId = vplexUri.toString(); // fill the map if (vplexSystem != null) { if (!vplexPoolMap.containsKey(vplexId)) { List<StoragePool> vplexPoolList = new ArrayList<StoragePool>(); vplexPoolList.addAll(poolsBySystem.get(systemUri)); vplexPoolMap.put(vplexId, vplexPoolList); } else { List<StoragePool> vplexPoolList = vplexPoolMap.get(vplexId); vplexPoolList.addAll(poolsBySystem.get(systemUri)); } } } } return vplexPoolMap; } /** * Creates and returns a map of storage pools grouped by storage system * * @param storagePools a list of storage pools * @return a map of storage pools grouped by storage system */ private Map<URI, List<StoragePool>> getPoolsBySystem(List<StoragePool> storagePools) { Map<URI, List<StoragePool>> map = new HashMap<URI, List<StoragePool>>(); for (StoragePool storagePool : storagePools) { if (!map.containsKey(storagePool.getStorageDevice())) { map.put(storagePool.getStorageDevice(), new ArrayList<StoragePool>()); } map.get(storagePool.getStorageDevice()).add(storagePool); } return map; } /** * Gets the List of storage systems with the passed ids. * * @param storageSystemIds The storage system ids. * * @return A list of the storage systems. */ private List<StorageSystem> getStorageSystemsWithIds(Set<String> storageSystemIds) { List<StorageSystem> storageSystems = new ArrayList<StorageSystem>(); for (String storageSystemId : storageSystemIds) { storageSystems.add(_dbClient.queryObject(StorageSystem.class, URI.create(storageSystemId))); } return storageSystems; } /** * Get the Ids of the varrays for the passed storage pools. * * @param storagePools The list of storage pools. * * @return A list of the ids for the storage pool varrays. */ private List<String> getNeighborhoodsForPools(List<StoragePool> storagePools) { List<String> poolNHIds = new ArrayList<String>(); for (StoragePool storagePool : storagePools) { StringSet nhIds = storagePool.getTaggedVirtualArrays(); if (nhIds == null) { continue; } for (String nhId : nhIds) { if (!poolNHIds.contains(nhId)) { poolNHIds.add(nhId); } } } return poolNHIds; } /** * Sets the Id of the VPlex storage system into the passed recommendations. * * @param vplexStorageSystemId The id of the VPlex storage system. * @param varray The varray for the recommendation. * @param vpool The vpool for the recommendation. * @param recommendations The list of recommendations. */ protected List<VPlexRecommendation> createVPlexRecommendations( String vplexStorageSystemId, VirtualArray varray, VirtualPool vpool, List<Recommendation> recommendations) { List<VPlexRecommendation> vplexRecommendations = new ArrayList<VPlexRecommendation>(); for (Recommendation recommendation : recommendations) { VPlexRecommendation vplexRecommendation = new VPlexRecommendation(); vplexRecommendation.setSourceStorageSystem(recommendation.getSourceStorageSystem()); vplexRecommendation.setSourceStoragePool(recommendation.getSourceStoragePool()); vplexRecommendation.setResourceCount(recommendation.getResourceCount()); //vplexRecommendation.setSourceDevice(URI.create(vplexStorageSystemId)); vplexRecommendation.setVPlexStorageSystem(URI.create(vplexStorageSystemId)); vplexRecommendation.setVirtualArray(varray.getId()); vplexRecommendation.setVirtualPool(vpool); vplexRecommendations.add(vplexRecommendation); } return vplexRecommendations; } protected List<VPlexRecommendation> createVPlexRecommendations( List<Recommendation> baseRecommendations, String vplexStorageSystemId, VirtualArray varray, VirtualPool vpool) { List<VPlexRecommendation> vplexRecommendations = new ArrayList<VPlexRecommendation>(); for (Recommendation recommendation : baseRecommendations) { VPlexRecommendation vplexRecommendation = new VPlexRecommendation(); vplexRecommendation.setSourceStorageSystem(recommendation.getSourceStorageSystem()); vplexRecommendation.setSourceStoragePool(recommendation.getSourceStoragePool()); vplexRecommendation.setResourceCount(recommendation.getResourceCount()); vplexRecommendation.setVPlexStorageSystem(URI.create(vplexStorageSystemId)); vplexRecommendation.setVirtualArray(varray.getId()); vplexRecommendation.setVirtualPool(vpool); vplexRecommendation.setRecommendation(recommendation); vplexRecommendations.add(vplexRecommendation); } return vplexRecommendations; } /** * Gets the HA virtual array if the volume creation request is for HA * volumes. * * @param vArray The source virtual array. * @param project A reference to the project. * @param vPool The HA virtual pool. * @return the HA virtual array */ protected VirtualArray getHaVirtualArray(VirtualArray vArray, Project project, VirtualPool vPool) { // Determine if the volume creation request is for HA volumes. boolean isHAVolumeRequest = VirtualPool.HighAvailabilityType.vplex_distributed.name() .equals(vPool.getHighAvailability()); // Get and validate the high availability VirtualArray. The HA Virtual Array // is optional. When not specified, the high availability VirtualArray will // be selected by the placement logic. VirtualArray haVArray = null; StringMap haVaVpMap = vPool.getHaVarrayVpoolMap(); if ((isHAVolumeRequest) && (haVaVpMap != null)) { _log.info("Is HA request and with an HA VirtualArray VirtualPool map"); for (String haVaId : haVaVpMap.keySet()) { _log.info("HA VirtualArray is {}", haVaId); if (!haVaId.equals(NullColumnValueGetter.getNullURI().toString())) { _log.info("HA VirtualArray is not a null URI"); haVArray = getVirtualArrayForVolumeCreateRequest(project, URI.create(haVaId)); if (vArray.getId().toString().equals(haVArray.getId().toString())) { throw APIException.badRequests.sameVirtualArrayAndHighAvailabilityArray(); } } } } return haVArray; } /** * Gets the HA virtual pool if the volume creation request is for HA * volumes. * * @param vArray The source virtual array. * @param project A reference to the project. * @param vPool The HA virtual pool. * @return the HA virtual pool */ protected VirtualPool getHaVirtualPool(VirtualArray vArray, Project project, VirtualPool vPool) { // Determine if the volume creation request is for HA volumes. boolean isHAVolumeRequest = VirtualPool.HighAvailabilityType.vplex_distributed.name() .equals(vPool.getHighAvailability()); // Get and validate the high availability VirtualPool. This is optional. // If no VirtualPool is specified for the HA VirtualArray, then the // passed VirtualPool is use. VirtualPool haVPool = vPool; StringMap haVaVpMap = vPool.getHaVarrayVpoolMap(); if ((isHAVolumeRequest) && (haVaVpMap != null)) { _log.info("Is HA request and with an HA VirtualArray VirtualPool map"); for (String haVaId : haVaVpMap.keySet()) { _log.info("HA VirtualArray is {}", haVaId); // Now get the VirtualPool. String haVpId = haVaVpMap.get(haVaId); _log.info("HA VirtualPool is {}", haVpId); if (!haVpId.equals(NullColumnValueGetter.getNullURI().toString())) { _log.info("HA VirtualPool is not a null URI"); haVPool = BlockService.getVirtualPoolForRequest(project, URI.create(haVpId), _dbClient, _permissionsHelper); } } } return haVPool; } @Override public List<Recommendation> getRecommendationsForVpool(VirtualArray vArray, Project project, VirtualPool vPool, VpoolUse vPoolUse, VirtualPoolCapabilityValuesWrapper capabilities, Map<VpoolUse, List<Recommendation>> currentRecommendations) { _log.info("Getting recommendations for VPlex volume placement"); // Validate the VirtualPool specifies VPlex high availability, which // currently is the only supported means for creating high // availability volumes. if (!VirtualPool.vPoolSpecifiesHighAvailability(vPool)) { throw APIException.badRequests.invalidHighAvailability(vPool.getHighAvailability()); } _log.info("VirtualPool has high availability {}", vPool.getHighAvailability()); Set<URI> vplexSystemsForPlacement = new HashSet<URI>(); if (vPoolUse == VpoolUse.ROOT) { // For now only validate that we're using the same vplex systems on the ROOT request vplexSystemsForPlacement = getVPlexSystemsForPlacement(vArray, vPool, capabilities); } // Determine if the volume creation request is for HA volumes. boolean isHAVolumeRequest = VirtualPool.vPoolSpecifiesHighAvailabilityDistributed(vPool); // Get and validate the high availability VirtualArray and VirtualPool. // Note that the HA VirtualPool is optional. When not specified, the // high availability VirtualPool is the passed (root) VirtualPool. VirtualPool haVPool = vPool; VirtualArray haVArray = null; StringMap haVaVpMap = vPool.getHaVarrayVpoolMap(); if ((isHAVolumeRequest) && (haVaVpMap != null)) { _log.info("Is HA request and with an HA VirtualArray VirtualPool map"); Iterator<String> vaIter = haVaVpMap.keySet().iterator(); while (vaIter.hasNext()) { String haVaId = vaIter.next(); _log.info("HA VirtualArray is {}", haVaId); if (!haVaId.equals(NullColumnValueGetter.getNullURI().toString())) { _log.info("HA VirtualArray is not a null URI"); haVArray = getVirtualArrayForVolumeCreateRequest(project, URI.create(haVaId)); if (vArray.getId().toString().equals(haVArray.getId().toString())) { throw APIException.badRequests.sameVirtualArrayAndHighAvailabilityArray(); } } // Now get the VirtualPool. String haVpId = haVaVpMap.get(haVaId); _log.info("HA VirtualPool is {}", haVpId); if (!haVpId.equals(NullColumnValueGetter.getNullURI().toString())) { _log.info("HA VirtualPool is not a null URI"); haVPool = BlockService.getVirtualPoolForRequest(project, URI.create(haVpId), _dbClient, _permissionsHelper); } } } // Get the volume placement based on passed parameters. _log.info("VirtualPool: {}, HA VirtualPool: {}", vPool.getId().toString(), haVPool.getId() .toString()); List<Recommendation> recommendations = scheduleStorage( vArray, vplexSystemsForPlacement, null, vPool, isHAVolumeRequest, haVArray, haVPool, capabilities, project, vPoolUse, currentRecommendations); return recommendations; } @Override public String getSchedulerName() { return SCHEDULER_NAME; } @Override public boolean handlesVpool(VirtualPool vPool, VpoolUse vPoolUse) { return VirtualPool.vPoolSpecifiesHighAvailability(vPool); } }